This is a fairly quiet release. We don't have any patches to the core

framework. The only patch that can even be considered "core" adds another
 clk_get() variant. The rest of the changes are in drivers for various SoCs, and
 we have a few bits for ARM shmobile architecture code (dts and mach) due to the
 dependency we're breaking between shmobile architecture code and its clk
 driver. Those shmobile bits have also been pulled into arm-soc tree. Here's the
 summary:
 
 Core:
 
  - Support for devm_get_clk_from_child() used with DT bindings that have
    subnodes with the 'clocks' property
 
 New Drivers:
 
  - Allwinner A64 (sun50i)
  - i.MX imx6ull
  - Socionext's UniPhier SoC CPUs
  - Mediatek MT2701 SoCs
  - Rockchip rk1108 SoCs
  - Qualcomm MSM8994/MSM8992 SoCS
  - Qualcomm RPM Clocks
  - Hisilicon Hi3516CV300 and Hi3798CV200 CRG
  - Oxford Semiconductor OX820 and OX810SE SoCs
  - Renesas RZ/G1M and RZ/GIE SoCs
  - Renesas R-Car RST driver for mode pin states
 
 Updates:
 
  - Four Allwinner SoCs are migrated to the new style clk driver
  - Rockchip rk3399,rk3066 PLL optimizations
  - i.MX LVDS display glitch fixes and AV PLL precision improvements
  - Qualcomm MSM8996 GPU GDSCs, hw controlled GDSCs, and Alpha PLL support
  - Explicit demodularization of always builtin drivers
  - Freescale Qoriq ls1012a and ls1046a support
  - Exynos 5433 parent typo fix and critical clock tagging
  - Renesas r8a7743/r8a7745 CPG
  - Renesas R-Car M3-W CSI2/VIN/SYS-DMAC/(H)SCIF/I2C/DRIF/gfx support
  - stm32f4* LSI, LSE, RTC, and QSPI clocks
  - pxa27x and pxa25x cpufreq as clks
  - TI omap36xx sprz319 advisory 2.1 workaround
  - Broadcom bcm2835 rate change propogation to PLLH_AUX from VEC
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABCAAGBQJYT1GXAAoJEK0CiJfG5JUlQ1QP/iOaWnE8TBLK/lOKPte2rw8U
 1rw2SDQ8gEJBGIVbZZsnOq6Pp1sVKrJ/7S9ybBeSHoOVb5iTCVAB4wG5uqdLLUGs
 4cHj4Vtge7xSxPLCh6YzawS0SjbtbYp1KXHBygGB2COIF53zphkmuM74gZ+l3dcz
 TMkfbIvwm8ISvNjc7tRpjhzf6+XUTIVRJ6UZPMnir08lTmDqHz7mouY7nUxlbWOy
 lOlF725RoBSa4LcBt+nZcNZ7Cu8eajFneeE87YiLdM4aS/VYm1Ajs9KzZYIRM3R6
 mznmiSDwCWTOzU4CsPSdcxGXePzyPrkDvRGWED2qHXNwWQ7Asbtm5pxDKEJ+rj8L
 LoB60z20d5PP0zJeiSwnr3XOgp95gW6vduAngu094O7FDZV7yY90wENIphQqgHaU
 5nVEPYWTK3lrxAShadpHvnyZI5A621QbNYzAoCAM/jf5xa3JW+AbkERmO/RyEsTO
 s6gAKX9H4WiQsEHrmBpJ+VsVVmlT5fhCtqskohaEqFg9CaVaxXvTPzN2fO9KtbDC
 M6JPycE9qgu08TWTyJr9xGDGBh0mKP+7ffxpj1x1gVT59HmCLAGTMEvMuHAfKCfW
 vz6qPjWW4KnqwAY0JvDZy1y37YQMytA5PVidV/XsEM7WLnmbutTCEbmwIx8eUNGv
 NaEpc4l2hvKLwCo4w0J+
 =RXZr
 -----END PGP SIGNATURE-----

Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux

Pull clk updates from Stephen Boyd:
 "This is a fairly quiet release. We don't have any patches to the core
  framework. The only patch that can even be considered "core" adds
  another clk_get() variant. The rest of the changes are in drivers for
  various SoCs, and we have a few bits for ARM shmobile architecture
  code (dts and mach) due to the dependency we're breaking between
  shmobile architecture code and its clk driver. Those shmobile bits
  have also been pulled into arm-soc tree. Here's the summary:

  Core:

   - Support for devm_get_clk_from_child() used with DT bindings that
     have subnodes with the 'clocks' property

  New Drivers:

   - Allwinner A64 (sun50i)
   - i.MX imx6ull
   - Socionext's UniPhier SoC CPUs
   - Mediatek MT2701 SoCs
   - Rockchip rk1108 SoCs
   - Qualcomm MSM8994/MSM8992 SoCS
   - Qualcomm RPM Clocks
   - Hisilicon Hi3516CV300 and Hi3798CV200 CRG
   - Oxford Semiconductor OX820 and OX810SE SoCs
   - Renesas RZ/G1M and RZ/GIE SoCs
   - Renesas R-Car RST driver for mode pin states

  Updates:

   - Four Allwinner SoCs are migrated to the new style clk driver
   - Rockchip rk3399,rk3066 PLL optimizations
   - i.MX LVDS display glitch fixes and AV PLL precision improvements
   - Qualcomm MSM8996 GPU GDSCs, hw controlled GDSCs, and Alpha PLL
     support
   - Explicit demodularization of always builtin drivers
   - Freescale Qoriq ls1012a and ls1046a support
   - Exynos 5433 parent typo fix and critical clock tagging
   - Renesas r8a7743/r8a7745 CPG
   - Renesas R-Car M3-W CSI2/VIN/SYS-DMAC/(H)SCIF/I2C/DRIF/gfx support
   - stm32f4* LSI, LSE, RTC, and QSPI clocks
   - pxa27x and pxa25x cpufreq as clks
   - TI omap36xx sprz319 advisory 2.1 workaround
   - Broadcom bcm2835 rate change propogation to PLLH_AUX from VEC"

* tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (150 commits)
  clk: bcm: Fix 'maybe-uninitialized' warning in bcm2835_clock_choose_div_and_prate()
  clk: add devm_get_clk_from_child() API
  clk: st: clk-flexgen: Unmap region obtained by of_iomap
  clk: keystone: pll: Unmap region obtained by of_iomap
  clk:mmp:clk-of-mmp2: Free memory and Unmap region obtained by kzalloc and of_iomap
  clk:mmp:clk-of-pxa910: Free memory and Unmap region obtained by kzmalloc and of_iomap
  clk: mmp: clk-of-pxa1928: Free memory obtained by kzalloc
  clk: cdce925: Fix limit check
  clk: bcm: Make COMMON_CLK_IPROC into a library
  clk: qoriq: added ls1012a clock configuration
  clk: ti: dra7: fix "failed to lookup clock node gmac_gmii_ref_clk_div" boot message
  clk: bcm: Allow rate change propagation to PLLH_AUX on VEC clock
  clk: bcm: Support rate change propagation on bcm2835 clocks
  clk: bcm2835: Avoid overwriting the div info when disabling a pll_div clk
  clk: ti: omap36xx: Work around sprz319 advisory 2.1
  clk: clk-wm831x: fix a logic error
  clk: uniphier: add cpufreq data for LD11, LD20 SoCs
  clk: uniphier: add CPU-gear change (cpufreq) support
  clk: qcom: Put venus core0/1 gdscs to hw control mode
  clk: qcom: gdsc: Add support for gdscs with HW control
  ...
This commit is contained in:
Linus Torvalds 2016-12-13 08:54:27 -08:00
commit b8d2798f32
165 changed files with 12579 additions and 781 deletions

View File

@ -79,7 +79,7 @@ Required Properties:
Input clocks for fsys clock controller:
- oscclk
- sclk_ufs_mphy
- div_aclk_fsys_200
- aclk_fsys_200
- sclk_pcie_100_fsys
- sclk_ufsunipro_fsys
- sclk_mmc2_fsys
@ -104,6 +104,10 @@ Required Properties:
- sclk_decon_tv_vclk_disp
- aclk_disp_333
Input clocks for audio clock controller:
- oscclk
- fout_aud_pll
Input clocks for bus0 clock controller:
- aclk_bus0_400
@ -235,7 +239,7 @@ Example 2: Examples of clock controller nodes are listed below.
clock-names = "oscclk",
"sclk_ufs_mphy",
"div_aclk_fsys_200",
"aclk_fsys_200",
"sclk_pcie_100_fsys",
"sclk_ufsunipro_fsys",
"sclk_mmc2_fsys",
@ -245,7 +249,7 @@ Example 2: Examples of clock controller nodes are listed below.
"sclk_usbdrd30_fsys";
clocks = <&xxti>,
<&cmu_cpif CLK_SCLK_UFS_MPHY>,
<&cmu_top CLK_DIV_ACLK_FSYS_200>,
<&cmu_top CLK_ACLK_FSYS_200>,
<&cmu_top CLK_SCLK_PCIE_100_FSYS>,
<&cmu_top CLK_SCLK_UFSUNIPRO_FSYS>,
<&cmu_top CLK_SCLK_MMC2_FSYS>,
@ -297,6 +301,9 @@ Example 2: Examples of clock controller nodes are listed below.
compatible = "samsung,exynos5433-cmu-aud";
reg = <0x114c0000 0x0b04>;
#clock-cells = <1>;
clock-names = "oscclk", "fout_aud_pll";
clocks = <&xxti>, <&cmu_top CLK_FOUT_AUD_PLL>;
};
cmu_bus0: clock-controller@13600000 {

View File

@ -1,7 +1,7 @@
* Hisilicon Hi3519 Clock and Reset Generator(CRG)
* HiSilicon Clock and Reset Generator(CRG)
The Hi3519 CRG module provides clock and reset signals to various
controllers within the SoC.
The CRG module provides clock and reset signals to various
modules within the SoC.
This binding uses the following bindings:
Documentation/devicetree/bindings/clock/clock-bindings.txt
@ -10,7 +10,11 @@ This binding uses the following bindings:
Required Properties:
- compatible: should be one of the following.
- "hisilicon,hi3519-crg" - controller compatible with Hi3519 SoC.
- "hisilicon,hi3516cv300-crg"
- "hisilicon,hi3516cv300-sysctrl"
- "hisilicon,hi3519-crg"
- "hisilicon,hi3798cv200-crg"
- "hisilicon,hi3798cv200-sysctrl"
- reg: physical base address of the controller and length of memory mapped
region.

View File

@ -5,22 +5,15 @@ Please also refer to clock-bindings.txt in this directory for common clock
bindings usage.
Required properties:
- compatible: Should be "oxsemi,ox810se-stdclk"
- compatible: For OX810SE, should be "oxsemi,ox810se-stdclk"
For OX820, should be "oxsemi,ox820-stdclk"
- #clock-cells: 1, see below
Parent node should have the following properties :
- compatible: Should be "oxsemi,ox810se-sys-ctrl", "syscon", "simple-mfd"
For OX810SE, the clock indices are :
- 0: LEON
- 1: DMA_SGDMA
- 2: CIPHER
- 3: SATA
- 4: AUDIO
- 5: USBMPH
- 6: ETHA
- 7: PCIA
- 8: NAND
- compatible: For OX810SE, should be
"oxsemi,ox810se-sys-ctrl", "syscon", "simple-mfd"
For OX820, should be
"oxsemi,ox820-sys-ctrl", "syscon", "simple-mfd"
example:

View File

@ -14,6 +14,7 @@ Required properties :
"qcom,gcc-msm8974"
"qcom,gcc-msm8974pro"
"qcom,gcc-msm8974pro-ac"
"qcom,gcc-msm8994"
"qcom,gcc-msm8996"
"qcom,gcc-mdm9615"

View File

@ -0,0 +1,37 @@
Qualcomm RPM Clock Controller Binding
------------------------------------------------
The RPM is a dedicated hardware engine for managing the shared
SoC resources in order to keep the lowest power profile. It
communicates with other hardware subsystems via shared memory
and accepts clock requests, aggregates the requests and turns
the clocks on/off or scales them on demand.
Required properties :
- compatible : shall contain only one of the following. The generic
compatible "qcom,rpmcc" should be also included.
"qcom,rpmcc-msm8916", "qcom,rpmcc"
"qcom,rpmcc-apq8064", "qcom,rpmcc"
- #clock-cells : shall contain 1
Example:
smd {
compatible = "qcom,smd";
rpm {
interrupts = <0 168 1>;
qcom,ipc = <&apcs 8 0>;
qcom,smd-edge = <15>;
rpm_requests {
compatible = "qcom,rpm-msm8916";
qcom,smd-channels = "rpm_requests";
rpmcc: clock-controller {
compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc";
#clock-cells = <1>;
};
};
};
};

View File

@ -13,6 +13,8 @@ They provide the following functionalities:
Required Properties:
- compatible: Must be one of:
- "renesas,r8a7743-cpg-mssr" for the r8a7743 SoC (RZ/G1M)
- "renesas,r8a7745-cpg-mssr" for the r8a7745 SoC (RZ/G1E)
- "renesas,r8a7795-cpg-mssr" for the r8a7795 SoC (R-Car H3)
- "renesas,r8a7796-cpg-mssr" for the r8a7796 SoC (R-Car M3-W)
@ -22,8 +24,9 @@ Required Properties:
- clocks: References to external parent clocks, one entry for each entry in
clock-names
- clock-names: List of external parent clock names. Valid names are:
- "extal" (r8a7795, r8a7796)
- "extal" (r8a7743, r8a7745, r8a7795, r8a7796)
- "extalr" (r8a7795, r8a7796)
- "usb_extal" (r8a7743, r8a7745)
- #clock-cells: Must be 2
- For CPG core clocks, the two clock specifier cells must be "CPG_CORE"

View File

@ -0,0 +1,59 @@
* Rockchip RK1108 Clock and Reset Unit
The RK1108 clock controller generates and supplies clock to various
controllers within the SoC and also implements a reset controller for SoC
peripherals.
Required Properties:
- compatible: should be "rockchip,rk1108-cru"
- reg: physical base address of the controller and length of memory mapped
region.
- #clock-cells: should be 1.
- #reset-cells: should be 1.
Optional Properties:
- rockchip,grf: phandle to the syscon managing the "general register files"
If missing pll rates are not changeable, due to the missing pll lock status.
Each clock is assigned an identifier and client nodes can use this identifier
to specify the clock which they consume. All available clocks are defined as
preprocessor macros in the dt-bindings/clock/rk1108-cru.h headers and can be
used in device tree sources. Similar macros exist for the reset sources in
these files.
External clocks:
There are several clocks that are generated outside the SoC. It is expected
that they are defined using standard clock bindings with following
clock-output-names:
- "xin24m" - crystal input - required,
- "ext_vip" - external VIP clock - optional
- "ext_i2s" - external I2S clock - optional
- "ext_gmac" - external GMAC clock - optional
- "hdmiphy" - external clock input derived from HDMI PHY - optional
- "usbphy" - external clock input derived from USB PHY - optional
Example: Clock controller node:
cru: cru@20200000 {
compatible = "rockchip,rk1108-cru";
reg = <0x20200000 0x1000>;
rockchip,grf = <&grf>;
#clock-cells = <1>;
#reset-cells = <1>;
};
Example: UART controller node that consumes the clock generated by the clock
controller:
uart0: serial@10230000 {
compatible = "rockchip,rk1108-uart", "snps,dw-apb-uart";
reg = <0x10230000 0x100>;
interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&cru SCLK_UART0>;
};

View File

@ -7,7 +7,9 @@ Please refer to clock-bindings.txt for common clock controller binding usage.
Please also refer to reset.txt for common reset controller binding usage.
Required properties:
- compatible: Should be "st,stm32f42xx-rcc"
- compatible: Should be:
"st,stm32f42xx-rcc"
"st,stm32f469-rcc"
- reg: should be register base and length as documented in the
datasheet
- #reset-cells: 1, see below

View File

@ -7,6 +7,7 @@ Required properties :
- "allwinner,sun8i-a23-ccu"
- "allwinner,sun8i-a33-ccu"
- "allwinner,sun8i-h3-ccu"
- "allwinner,sun50i-a64-ccu"
- reg: Must contain the registers base address and length
- clocks: phandle to the oscillators feeding the CCU. Two are needed:

View File

@ -0,0 +1,37 @@
DT bindings for the Renesas R-Car and RZ/G Reset Controllers
The R-Car and RZ/G Reset Controllers provide reset control, and implement the
following functions:
- Latching of the levels on mode pins when PRESET# is negated,
- Mode monitoring register,
- Reset control of peripheral devices (on R-Car Gen1),
- Watchdog timer (on R-Car Gen1),
- Register-based reset control and boot address registers for the various CPU
cores (on R-Car Gen2 and Gen3, and on RZ/G).
Required properties:
- compatible: Should be
- "renesas,<soctype>-reset-wdt" for R-Car Gen1,
- "renesas,<soctype>-rst" for R-Car Gen2 and Gen3, and RZ/G
Examples with soctypes are:
- "renesas,r8a7743-rst" (RZ/G1M)
- "renesas,r8a7745-rst" (RZ/G1E)
- "renesas,r8a7778-reset-wdt" (R-Car M1A)
- "renesas,r8a7779-reset-wdt" (R-Car H1)
- "renesas,r8a7790-rst" (R-Car H2)
- "renesas,r8a7791-rst" (R-Car M2-W)
- "renesas,r8a7792-rst" (R-Car V2H
- "renesas,r8a7793-rst" (R-Car M2-N)
- "renesas,r8a7794-rst" (R-Car E2)
- "renesas,r8a7795-rst" (R-Car H3)
- "renesas,r8a7796-rst" (R-Car M3-W)
- reg: Address start and address range for the device.
Example:
rst: reset-controller@e6160000 {
compatible = "renesas,r8a7795-rst";
reg = <0 0xe6160000 0 0x0200>;
};

View File

@ -626,4 +626,9 @@ R8A7778_CLK_SRU_SRC8
"sru-src6", "sru-src7", "sru-src8";
};
};
rst: reset-controller@ffcc0000 {
compatible = "renesas,r8a7778-reset-wdt";
reg = <0xffcc0000 0x40>;
};
};

View File

@ -590,6 +590,11 @@ R8A7779_CLK_MMC1 R8A7779_CLK_MMC0
};
};
rst: reset-controller@ffcc0000 {
compatible = "renesas,r8a7779-reset-wdt";
reg = <0xffcc0000 0x48>;
};
sysc: system-controller@ffd85000 {
compatible = "renesas,r8a7779-sysc";
reg = <0xffd85000 0x0200>;

View File

@ -1471,6 +1471,11 @@ R8A7790_CLK_SCU_SRC4 R8A7790_CLK_SCU_SRC3 R8A7790_CLK_SCU_SRC2 R8A7790_CLK_SCU_S
};
};
rst: reset-controller@e6160000 {
compatible = "renesas,r8a7790-rst";
reg = <0 0xe6160000 0 0x0100>;
};
sysc: system-controller@e6180000 {
compatible = "renesas,r8a7790-sysc";
reg = <0 0xe6180000 0 0x0200>;

View File

@ -1482,6 +1482,11 @@ R8A7791_CLK_SCIFA3 R8A7791_CLK_SCIFA4 R8A7791_CLK_SCIFA5
};
};
rst: reset-controller@e6160000 {
compatible = "renesas,r8a7791-rst";
reg = <0 0xe6160000 0 0x0100>;
};
sysc: system-controller@e6180000 {
compatible = "renesas,r8a7791-sysc";
reg = <0 0xe6180000 0 0x0200>;

View File

@ -118,6 +118,11 @@ IRQ_TYPE_LEVEL_LOW)>,
IRQ_TYPE_LEVEL_LOW)>;
};
rst: reset-controller@e6160000 {
compatible = "renesas,r8a7792-rst";
reg = <0 0xe6160000 0 0x0100>;
};
sysc: system-controller@e6180000 {
compatible = "renesas,r8a7792-sysc";
reg = <0 0xe6180000 0 0x0200>;

View File

@ -1279,6 +1279,11 @@ R8A7793_CLK_SCIFA3 R8A7793_CLK_SCIFA4 R8A7793_CLK_SCIFA5
};
};
rst: reset-controller@e6160000 {
compatible = "renesas,r8a7793-rst";
reg = <0 0xe6160000 0 0x0100>;
};
sysc: system-controller@e6180000 {
compatible = "renesas,r8a7793-sysc";
reg = <0 0xe6180000 0 0x0200>;

View File

@ -1375,6 +1375,11 @@ R8A7794_CLK_SCIFA3 R8A7794_CLK_SCIFA4 R8A7794_CLK_SCIFA5
};
};
rst: reset-controller@e6160000 {
compatible = "renesas,r8a7794-rst";
reg = <0 0xe6160000 0 0x0100>;
};
sysc: system-controller@e6180000 {
compatible = "renesas,r8a7794-sysc";
reg = <0 0xe6180000 0 0x0200>;

View File

@ -15,7 +15,6 @@
* GNU General Public License for more details.
*/
#include <linux/clk/renesas.h>
#include <linux/io.h>
#include <linux/irqchip.h>
@ -23,19 +22,6 @@
#include "common.h"
#define MODEMR 0xffcc0020
static void __init r8a7778_timer_init(void)
{
u32 mode;
void __iomem *modemr = ioremap_nocache(MODEMR, 4);
BUG_ON(!modemr);
mode = ioread32(modemr);
iounmap(modemr);
r8a7778_clocks_init(mode);
}
#define INT2SMSKCR0 0x82288 /* 0xfe782288 */
#define INT2SMSKCR1 0x8228c /* 0xfe78228c */
@ -70,6 +56,5 @@ DT_MACHINE_START(R8A7778_DT, "Generic R8A7778 (Flattened Device Tree)")
.init_early = shmobile_init_delay,
.init_irq = r8a7778_init_irq_dt,
.init_late = shmobile_init_late,
.init_time = r8a7778_timer_init,
.dt_compat = r8a7778_compat_dt,
MACHINE_END

View File

@ -14,8 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk/renesas.h>
#include <linux/clocksource.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
@ -76,30 +74,6 @@ static void __init r8a7779_init_irq_dt(void)
__raw_writel(0x003fee3f, INT2SMSKCR4);
}
#define MODEMR 0xffcc0020
static u32 __init r8a7779_read_mode_pins(void)
{
static u32 mode;
static bool mode_valid;
if (!mode_valid) {
void __iomem *modemr = ioremap_nocache(MODEMR, PAGE_SIZE);
BUG_ON(!modemr);
mode = ioread32(modemr);
iounmap(modemr);
mode_valid = true;
}
return mode;
}
static void __init r8a7779_init_time(void)
{
r8a7779_clocks_init(r8a7779_read_mode_pins());
clocksource_probe();
}
static const char *const r8a7779_compat_dt[] __initconst = {
"renesas,r8a7779",
NULL,
@ -109,7 +83,6 @@ DT_MACHINE_START(R8A7779_DT, "Generic R8A7779 (Flattened Device Tree)")
.smp = smp_ops(r8a7779_smp_ops),
.map_io = r8a7779_map_io,
.init_early = shmobile_init_delay,
.init_time = r8a7779_init_time,
.init_irq = r8a7779_init_irq_dt,
.init_late = shmobile_init_late,
.dt_compat = r8a7779_compat_dt,

View File

@ -15,7 +15,7 @@
* GNU General Public License for more details.
*/
#include <linux/clk/renesas.h>
#include <linux/clk-provider.h>
#include <linux/clocksource.h>
#include <linux/device.h>
#include <linux/dma-contiguous.h>
@ -71,7 +71,6 @@ static unsigned int __init get_extal_freq(void)
void __init rcar_gen2_timer_init(void)
{
u32 mode = rcar_gen2_read_mode_pins();
#ifdef CONFIG_ARM_ARCH_TIMER
void __iomem *base;
u32 freq;
@ -130,7 +129,7 @@ void __init rcar_gen2_timer_init(void)
iounmap(base);
#endif /* CONFIG_ARM_ARCH_TIMER */
rcar_gen2_clocks_init(mode);
of_clk_init(NULL);
clocksource_probe();
}

View File

@ -321,6 +321,11 @@ cpg: clock-controller@e6150000 {
#power-domain-cells = <0>;
};
rst: reset-controller@e6160000 {
compatible = "renesas,r8a7795-rst";
reg = <0 0xe6160000 0 0x0200>;
};
sysc: system-controller@e6180000 {
compatible = "renesas,r8a7795-sysc";
reg = <0 0xe6180000 0 0x0400>;

View File

@ -233,6 +233,11 @@ cpg: clock-controller@e6150000 {
#power-domain-cells = <0>;
};
rst: reset-controller@e6160000 {
compatible = "renesas,r8a7796-rst";
reg = <0 0xe6160000 0 0x0200>;
};
sysc: system-controller@e6180000 {
compatible = "renesas,r8a7796-sysc";
reg = <0 0xe6180000 0 0x0400>;

View File

@ -33,7 +33,7 @@ source "drivers/clk/versatile/Kconfig"
config COMMON_CLK_MAX77686
tristate "Clock driver for Maxim 77620/77686/77802 MFD"
depends on MFD_MAX77686 || MFD_MAX77620
depends on MFD_MAX77686 || MFD_MAX77620 || COMPILE_TEST
---help---
This driver supports Maxim 77620/77686/77802 crystal oscillator
clock.
@ -119,7 +119,7 @@ config COMMON_CLK_CS2000_CP
config COMMON_CLK_S2MPS11
tristate "Clock driver for S2MPS1X/S5M8767 MFD"
depends on MFD_SEC_CORE
depends on MFD_SEC_CORE || COMPILE_TEST
---help---
This driver supports S2MPS11/S2MPS14/S5M8767 crystal oscillator
clock. These multi-function devices have two (S2MPS14) or three

View File

@ -1,7 +1,6 @@
config CLK_BCM_63XX
bool "Broadcom BCM63xx clock support"
depends on ARCH_BCM_63XX || COMPILE_TEST
depends on COMMON_CLK
select COMMON_CLK_IPROC
default ARCH_BCM_63XX
help
@ -11,27 +10,22 @@ config CLK_BCM_63XX
config CLK_BCM_KONA
bool "Broadcom Kona CCU clock support"
depends on ARCH_BCM_MOBILE || COMPILE_TEST
depends on COMMON_CLK
default y
default ARCH_BCM_MOBILE
help
Enable common clock framework support for Broadcom SoCs
using "Kona" style clock control units, including those
in the BCM281xx and BCM21664 families.
config COMMON_CLK_IPROC
bool "Broadcom iProc clock support"
depends on ARCH_BCM_IPROC || ARCH_BCM_63XX || COMPILE_TEST
depends on COMMON_CLK
default ARCH_BCM_IPROC
bool
help
Enable common clock framework support for Broadcom SoCs
based on the iProc architecture
if COMMON_CLK_IPROC
config CLK_BCM_CYGNUS
bool "Broadcom Cygnus clock support"
depends on ARCH_BCM_CYGNUS || COMPILE_TEST
select COMMON_CLK_IPROC
default ARCH_BCM_CYGNUS
help
Enable common clock framework support for the Broadcom Cygnus SoC
@ -39,6 +33,7 @@ config CLK_BCM_CYGNUS
config CLK_BCM_NSP
bool "Broadcom Northstar/Northstar Plus clock support"
depends on ARCH_BCM_5301X || ARCH_BCM_NSP || COMPILE_TEST
select COMMON_CLK_IPROC
default ARCH_BCM_5301X || ARCH_BCM_NSP
help
Enable common clock framework support for the Broadcom Northstar and
@ -47,8 +42,7 @@ config CLK_BCM_NSP
config CLK_BCM_NS2
bool "Broadcom Northstar 2 clock support"
depends on ARCH_BCM_IPROC || COMPILE_TEST
select COMMON_CLK_IPROC
default ARCH_BCM_IPROC
help
Enable common clock framework support for the Broadcom Northstar 2 SoC
endif

View File

@ -436,6 +436,9 @@ struct bcm2835_clock_data {
const char *const *parents;
int num_mux_parents;
/* Bitmap encoding which parents accept rate change propagation. */
unsigned int set_rate_parent;
u32 ctl_reg;
u32 div_reg;
@ -751,7 +754,9 @@ static void bcm2835_pll_divider_off(struct clk_hw *hw)
cprman_write(cprman, data->cm_reg,
(cprman_read(cprman, data->cm_reg) &
~data->load_mask) | data->hold_mask);
cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE);
cprman_write(cprman, data->a2w_reg,
cprman_read(cprman, data->a2w_reg) |
A2W_PLL_CHANNEL_DISABLE);
spin_unlock(&cprman->regs_lock);
}
@ -1015,10 +1020,60 @@ bcm2835_clk_is_pllc(struct clk_hw *hw)
return strncmp(clk_hw_get_name(hw), "pllc", 4) == 0;
}
static unsigned long bcm2835_clock_choose_div_and_prate(struct clk_hw *hw,
int parent_idx,
unsigned long rate,
u32 *div,
unsigned long *prate)
{
struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
struct bcm2835_cprman *cprman = clock->cprman;
const struct bcm2835_clock_data *data = clock->data;
unsigned long best_rate = 0;
u32 curdiv, mindiv, maxdiv;
struct clk_hw *parent;
parent = clk_hw_get_parent_by_index(hw, parent_idx);
if (!(BIT(parent_idx) & data->set_rate_parent)) {
*prate = clk_hw_get_rate(parent);
*div = bcm2835_clock_choose_div(hw, rate, *prate, true);
return bcm2835_clock_rate_from_divisor(clock, *prate,
*div);
}
if (data->frac_bits)
dev_warn(cprman->dev,
"frac bits are not used when propagating rate change");
/* clamp to min divider of 2 if we're dealing with a mash clock */
mindiv = data->is_mash_clock ? 2 : 1;
maxdiv = BIT(data->int_bits) - 1;
/* TODO: Be smart, and only test a subset of the available divisors. */
for (curdiv = mindiv; curdiv <= maxdiv; curdiv++) {
unsigned long tmp_rate;
tmp_rate = clk_hw_round_rate(parent, rate * curdiv);
tmp_rate /= curdiv;
if (curdiv == mindiv ||
(tmp_rate > best_rate && tmp_rate <= rate))
best_rate = tmp_rate;
if (best_rate == rate)
break;
}
*div = curdiv << CM_DIV_FRAC_BITS;
*prate = curdiv * best_rate;
return best_rate;
}
static int bcm2835_clock_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
struct clk_hw *parent, *best_parent = NULL;
bool current_parent_is_pllc;
unsigned long rate, best_rate = 0;
@ -1046,9 +1101,8 @@ static int bcm2835_clock_determine_rate(struct clk_hw *hw,
if (bcm2835_clk_is_pllc(parent) && !current_parent_is_pllc)
continue;
prate = clk_hw_get_rate(parent);
div = bcm2835_clock_choose_div(hw, req->rate, prate, true);
rate = bcm2835_clock_rate_from_divisor(clock, prate, div);
rate = bcm2835_clock_choose_div_and_prate(hw, i, req->rate,
&div, &prate);
if (rate > best_rate && rate <= req->rate) {
best_parent = parent;
best_prate = prate;
@ -1260,6 +1314,13 @@ static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman,
init.name = data->name;
init.flags = data->flags | CLK_IGNORE_UNUSED;
/*
* Pass the CLK_SET_RATE_PARENT flag if we are allowed to propagate
* rate changes on at least of the parents.
*/
if (data->set_rate_parent)
init.flags |= CLK_SET_RATE_PARENT;
if (data->is_vpu_clock) {
init.ops = &bcm2835_vpu_clock_clk_ops;
} else {
@ -1596,7 +1657,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.a2w_reg = A2W_PLLH_AUX,
.load_mask = CM_PLLH_LOADAUX,
.hold_mask = 0,
.fixed_divider = 10),
.fixed_divider = 1),
[BCM2835_PLLH_PIX] = REGISTER_PLL_DIV(
.name = "pllh_pix",
.source_pll = "pllh",
@ -1800,7 +1861,12 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.ctl_reg = CM_VECCTL,
.div_reg = CM_VECDIV,
.int_bits = 4,
.frac_bits = 0),
.frac_bits = 0,
/*
* Allow rate change propagation only on PLLH_AUX which is
* assigned index 7 in the parent array.
*/
.set_rate_parent = BIT(7)),
/* dsi clocks */
[BCM2835_CLOCK_DSI0E] = REGISTER_PER_CLK(

View File

@ -216,7 +216,7 @@ static int cdce925_pll_prepare(struct clk_hw *hw)
nn = n * BIT(p);
/* q = int(nn/m) */
q = nn / m;
if ((q < 16) || (1 > 64)) {
if ((q < 16) || (q > 63)) {
pr_debug("%s invalid q=%d\n", __func__, q);
return -EINVAL;
}

View File

@ -53,3 +53,24 @@ void devm_clk_put(struct device *dev, struct clk *clk)
WARN_ON(ret);
}
EXPORT_SYMBOL(devm_clk_put);
struct clk *devm_get_clk_from_child(struct device *dev,
struct device_node *np, const char *con_id)
{
struct clk **ptr, *clk;
ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
clk = of_clk_get_by_name(np, con_id);
if (!IS_ERR(clk)) {
*ptr = clk;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return clk;
}
EXPORT_SYMBOL(devm_get_clk_from_child);

View File

@ -145,8 +145,8 @@ struct clk_hw *clk_hw_register_gate(struct device *dev, const char *name,
init.name = name;
init.ops = &clk_gate_ops;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = (parent_name ? &parent_name: NULL);
init.num_parents = (parent_name ? 1 : 0);
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
/* struct clk_gate assignments */
gate->reg = reg;

View File

@ -20,31 +20,43 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/stringify.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <dt-bindings/clock/oxsemi,ox810se.h>
#include <dt-bindings/clock/oxsemi,ox820.h>
/* Standard regmap gate clocks */
struct clk_oxnas {
struct clk_oxnas_gate {
struct clk_hw hw;
signed char bit;
unsigned int bit;
struct regmap *regmap;
};
struct oxnas_stdclk_data {
struct clk_hw_onecell_data *onecell_data;
struct clk_oxnas_gate **gates;
unsigned int ngates;
struct clk_oxnas_pll **plls;
unsigned int nplls;
};
/* Regmap offsets */
#define CLK_STAT_REGOFFSET 0x24
#define CLK_SET_REGOFFSET 0x2c
#define CLK_CLR_REGOFFSET 0x30
static inline struct clk_oxnas *to_clk_oxnas(struct clk_hw *hw)
static inline struct clk_oxnas_gate *to_clk_oxnas_gate(struct clk_hw *hw)
{
return container_of(hw, struct clk_oxnas, hw);
return container_of(hw, struct clk_oxnas_gate, hw);
}
static int oxnas_clk_is_enabled(struct clk_hw *hw)
static int oxnas_clk_gate_is_enabled(struct clk_hw *hw)
{
struct clk_oxnas *std = to_clk_oxnas(hw);
struct clk_oxnas_gate *std = to_clk_oxnas_gate(hw);
int ret;
unsigned int val;
@ -55,29 +67,29 @@ static int oxnas_clk_is_enabled(struct clk_hw *hw)
return val & BIT(std->bit);
}
static int oxnas_clk_enable(struct clk_hw *hw)
static int oxnas_clk_gate_enable(struct clk_hw *hw)
{
struct clk_oxnas *std = to_clk_oxnas(hw);
struct clk_oxnas_gate *std = to_clk_oxnas_gate(hw);
regmap_write(std->regmap, CLK_SET_REGOFFSET, BIT(std->bit));
return 0;
}
static void oxnas_clk_disable(struct clk_hw *hw)
static void oxnas_clk_gate_disable(struct clk_hw *hw)
{
struct clk_oxnas *std = to_clk_oxnas(hw);
struct clk_oxnas_gate *std = to_clk_oxnas_gate(hw);
regmap_write(std->regmap, CLK_CLR_REGOFFSET, BIT(std->bit));
}
static const struct clk_ops oxnas_clk_ops = {
.enable = oxnas_clk_enable,
.disable = oxnas_clk_disable,
.is_enabled = oxnas_clk_is_enabled,
static const struct clk_ops oxnas_clk_gate_ops = {
.enable = oxnas_clk_gate_enable,
.disable = oxnas_clk_gate_disable,
.is_enabled = oxnas_clk_gate_is_enabled,
};
static const char *const oxnas_clk_parents[] = {
static const char *const osc_parents[] = {
"oscillator",
};
@ -85,63 +97,138 @@ static const char *const eth_parents[] = {
"gmacclk",
};
#define DECLARE_STD_CLKP(__clk, __parent) \
static const struct clk_init_data clk_##__clk##_init = { \
.name = __stringify(__clk), \
.ops = &oxnas_clk_ops, \
.parent_names = __parent, \
.num_parents = ARRAY_SIZE(__parent), \
#define OXNAS_GATE(_name, _bit, _parents) \
struct clk_oxnas_gate _name = { \
.bit = (_bit), \
.hw.init = &(struct clk_init_data) { \
.name = #_name, \
.ops = &oxnas_clk_gate_ops, \
.parent_names = _parents, \
.num_parents = ARRAY_SIZE(_parents), \
.flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
}, \
}
#define DECLARE_STD_CLK(__clk) DECLARE_STD_CLKP(__clk, oxnas_clk_parents)
static OXNAS_GATE(ox810se_leon, 0, osc_parents);
static OXNAS_GATE(ox810se_dma_sgdma, 1, osc_parents);
static OXNAS_GATE(ox810se_cipher, 2, osc_parents);
static OXNAS_GATE(ox810se_sata, 4, osc_parents);
static OXNAS_GATE(ox810se_audio, 5, osc_parents);
static OXNAS_GATE(ox810se_usbmph, 6, osc_parents);
static OXNAS_GATE(ox810se_etha, 7, eth_parents);
static OXNAS_GATE(ox810se_pciea, 8, osc_parents);
static OXNAS_GATE(ox810se_nand, 9, osc_parents);
/* Hardware Bit - Clock association */
struct clk_oxnas_init_data {
unsigned long bit;
const struct clk_init_data *clk_init;
static struct clk_oxnas_gate *ox810se_gates[] = {
&ox810se_leon,
&ox810se_dma_sgdma,
&ox810se_cipher,
&ox810se_sata,
&ox810se_audio,
&ox810se_usbmph,
&ox810se_etha,
&ox810se_pciea,
&ox810se_nand,
};
/* Clk init data declaration */
DECLARE_STD_CLK(leon);
DECLARE_STD_CLK(dma_sgdma);
DECLARE_STD_CLK(cipher);
DECLARE_STD_CLK(sata);
DECLARE_STD_CLK(audio);
DECLARE_STD_CLK(usbmph);
DECLARE_STD_CLKP(etha, eth_parents);
DECLARE_STD_CLK(pciea);
DECLARE_STD_CLK(nand);
static OXNAS_GATE(ox820_leon, 0, osc_parents);
static OXNAS_GATE(ox820_dma_sgdma, 1, osc_parents);
static OXNAS_GATE(ox820_cipher, 2, osc_parents);
static OXNAS_GATE(ox820_sd, 3, osc_parents);
static OXNAS_GATE(ox820_sata, 4, osc_parents);
static OXNAS_GATE(ox820_audio, 5, osc_parents);
static OXNAS_GATE(ox820_usbmph, 6, osc_parents);
static OXNAS_GATE(ox820_etha, 7, eth_parents);
static OXNAS_GATE(ox820_pciea, 8, osc_parents);
static OXNAS_GATE(ox820_nand, 9, osc_parents);
static OXNAS_GATE(ox820_ethb, 10, eth_parents);
static OXNAS_GATE(ox820_pcieb, 11, osc_parents);
static OXNAS_GATE(ox820_ref600, 12, osc_parents);
static OXNAS_GATE(ox820_usbdev, 13, osc_parents);
/* Table index is clock indice */
static const struct clk_oxnas_init_data clk_oxnas_init[] = {
[0] = {0, &clk_leon_init},
[1] = {1, &clk_dma_sgdma_init},
[2] = {2, &clk_cipher_init},
/* Skip & Do not touch to DDR clock */
[3] = {4, &clk_sata_init},
[4] = {5, &clk_audio_init},
[5] = {6, &clk_usbmph_init},
[6] = {7, &clk_etha_init},
[7] = {8, &clk_pciea_init},
[8] = {9, &clk_nand_init},
static struct clk_oxnas_gate *ox820_gates[] = {
&ox820_leon,
&ox820_dma_sgdma,
&ox820_cipher,
&ox820_sd,
&ox820_sata,
&ox820_audio,
&ox820_usbmph,
&ox820_etha,
&ox820_pciea,
&ox820_nand,
&ox820_etha,
&ox820_pciea,
&ox820_ref600,
&ox820_usbdev,
};
struct clk_oxnas_data {
struct clk_oxnas clk_oxnas[ARRAY_SIZE(clk_oxnas_init)];
struct clk_onecell_data onecell_data[ARRAY_SIZE(clk_oxnas_init)];
struct clk *clks[ARRAY_SIZE(clk_oxnas_init)];
static struct clk_hw_onecell_data ox810se_hw_onecell_data = {
.hws = {
[CLK_810_LEON] = &ox810se_leon.hw,
[CLK_810_DMA_SGDMA] = &ox810se_dma_sgdma.hw,
[CLK_810_CIPHER] = &ox810se_cipher.hw,
[CLK_810_SATA] = &ox810se_sata.hw,
[CLK_810_AUDIO] = &ox810se_audio.hw,
[CLK_810_USBMPH] = &ox810se_usbmph.hw,
[CLK_810_ETHA] = &ox810se_etha.hw,
[CLK_810_PCIEA] = &ox810se_pciea.hw,
[CLK_810_NAND] = &ox810se_nand.hw,
},
.num = ARRAY_SIZE(ox810se_gates),
};
static struct clk_hw_onecell_data ox820_hw_onecell_data = {
.hws = {
[CLK_820_LEON] = &ox820_leon.hw,
[CLK_820_DMA_SGDMA] = &ox820_dma_sgdma.hw,
[CLK_820_CIPHER] = &ox820_cipher.hw,
[CLK_820_SD] = &ox820_sd.hw,
[CLK_820_SATA] = &ox820_sata.hw,
[CLK_820_AUDIO] = &ox820_audio.hw,
[CLK_820_USBMPH] = &ox820_usbmph.hw,
[CLK_820_ETHA] = &ox820_etha.hw,
[CLK_820_PCIEA] = &ox820_pciea.hw,
[CLK_820_NAND] = &ox820_nand.hw,
[CLK_820_ETHB] = &ox820_ethb.hw,
[CLK_820_PCIEB] = &ox820_pcieb.hw,
[CLK_820_REF600] = &ox820_ref600.hw,
[CLK_820_USBDEV] = &ox820_usbdev.hw,
},
.num = ARRAY_SIZE(ox820_gates),
};
static struct oxnas_stdclk_data ox810se_stdclk_data = {
.onecell_data = &ox810se_hw_onecell_data,
.gates = ox810se_gates,
.ngates = ARRAY_SIZE(ox810se_gates),
};
static struct oxnas_stdclk_data ox820_stdclk_data = {
.onecell_data = &ox820_hw_onecell_data,
.gates = ox820_gates,
.ngates = ARRAY_SIZE(ox820_gates),
};
static const struct of_device_id oxnas_stdclk_dt_ids[] = {
{ .compatible = "oxsemi,ox810se-stdclk", &ox810se_stdclk_data },
{ .compatible = "oxsemi,ox820-stdclk", &ox820_stdclk_data },
{ }
};
static int oxnas_stdclk_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct clk_oxnas_data *clk_oxnas;
const struct oxnas_stdclk_data *data;
const struct of_device_id *id;
struct regmap *regmap;
int ret;
int i;
clk_oxnas = devm_kzalloc(&pdev->dev, sizeof(*clk_oxnas), GFP_KERNEL);
if (!clk_oxnas)
return -ENOMEM;
id = of_match_device(oxnas_stdclk_dt_ids, &pdev->dev);
if (!id)
return -ENODEV;
data = id->data;
regmap = syscon_node_to_regmap(of_get_parent(np));
if (IS_ERR(regmap)) {
@ -149,32 +236,23 @@ static int oxnas_stdclk_probe(struct platform_device *pdev)
return PTR_ERR(regmap);
}
for (i = 0; i < ARRAY_SIZE(clk_oxnas_init); i++) {
struct clk_oxnas *_clk;
for (i = 0 ; i < data->ngates ; ++i)
data->gates[i]->regmap = regmap;
_clk = &clk_oxnas->clk_oxnas[i];
_clk->bit = clk_oxnas_init[i].bit;
_clk->hw.init = clk_oxnas_init[i].clk_init;
_clk->regmap = regmap;
for (i = 0; i < data->onecell_data->num; i++) {
if (!data->onecell_data->hws[i])
continue;
clk_oxnas->clks[i] =
devm_clk_register(&pdev->dev, &_clk->hw);
if (WARN_ON(IS_ERR(clk_oxnas->clks[i])))
return PTR_ERR(clk_oxnas->clks[i]);
ret = devm_clk_hw_register(&pdev->dev,
data->onecell_data->hws[i]);
if (ret)
return ret;
}
clk_oxnas->onecell_data->clks = clk_oxnas->clks;
clk_oxnas->onecell_data->clk_num = ARRAY_SIZE(clk_oxnas_init);
return of_clk_add_provider(np, of_clk_src_onecell_get,
clk_oxnas->onecell_data);
return of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
data->onecell_data);
}
static const struct of_device_id oxnas_stdclk_dt_ids[] = {
{ .compatible = "oxsemi,ox810se-stdclk" },
{ }
};
static struct platform_driver oxnas_stdclk_driver = {
.probe = oxnas_stdclk_probe,
.driver = {

View File

@ -266,6 +266,39 @@ static const struct clockgen_muxinfo ls1043a_hwa2 = {
},
};
static const struct clockgen_muxinfo ls1046a_hwa1 = {
{
{},
{},
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV3 },
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV4 },
{ CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 },
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
},
};
static const struct clockgen_muxinfo ls1046a_hwa2 = {
{
{},
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
{},
{},
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
},
};
static const struct clockgen_muxinfo ls1012a_cmux = {
{
[0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
{},
[2] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
}
};
static const struct clockgen_muxinfo t1023_hwa1 = {
{
{},
@ -488,6 +521,31 @@ static const struct clockgen_chipinfo chipinfo[] = {
.pll_mask = 0x07,
.flags = CG_PLL_8BIT,
},
{
.compat = "fsl,ls1046a-clockgen",
.init_periph = t2080_init_periph,
.cmux_groups = {
&t1040_cmux
},
.hwaccel = {
&ls1046a_hwa1, &ls1046a_hwa2
},
.cmux_to_group = {
0, -1
},
.pll_mask = 0x07,
.flags = CG_PLL_8BIT,
},
{
.compat = "fsl,ls1012a-clockgen",
.cmux_groups = {
&ls1012a_cmux
},
.cmux_to_group = {
0, -1
},
.pll_mask = 0x03,
},
{
.compat = "fsl,ls2080a-clockgen",
.cmux_groups = {
@ -1273,8 +1331,10 @@ static void __init clockgen_init(struct device_node *np)
CLK_OF_DECLARE(qoriq_clockgen_1, "fsl,qoriq-clockgen-1.0", clockgen_init);
CLK_OF_DECLARE(qoriq_clockgen_2, "fsl,qoriq-clockgen-2.0", clockgen_init);
CLK_OF_DECLARE(qoriq_clockgen_ls1012a, "fsl,ls1012a-clockgen", clockgen_init);
CLK_OF_DECLARE(qoriq_clockgen_ls1021a, "fsl,ls1021a-clockgen", clockgen_init);
CLK_OF_DECLARE(qoriq_clockgen_ls1043a, "fsl,ls1043a-clockgen", clockgen_init);
CLK_OF_DECLARE(qoriq_clockgen_ls1046a, "fsl,ls1046a-clockgen", clockgen_init);
CLK_OF_DECLARE(qoriq_clockgen_ls2080a, "fsl,ls2080a-clockgen", clockgen_init);
/* Legacy nodes */

View File

@ -19,10 +19,14 @@
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#define STM32F4_RCC_PLLCFGR 0x04
#define STM32F4_RCC_CFGR 0x08
@ -31,6 +35,8 @@
#define STM32F4_RCC_AHB3ENR 0x38
#define STM32F4_RCC_APB1ENR 0x40
#define STM32F4_RCC_APB2ENR 0x44
#define STM32F4_RCC_BDCR 0x70
#define STM32F4_RCC_CSR 0x74
struct stm32f4_gate_data {
u8 offset;
@ -40,7 +46,7 @@ struct stm32f4_gate_data {
unsigned long flags;
};
static const struct stm32f4_gate_data stm32f4_gates[] __initconst = {
static const struct stm32f4_gate_data stm32f429_gates[] __initconst = {
{ STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" },
@ -120,26 +126,113 @@ static const struct stm32f4_gate_data stm32f4_gates[] __initconst = {
{ STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" },
};
/*
* MAX_CLKS is the maximum value in the enumeration below plus the combined
* hweight of stm32f42xx_gate_map (plus one).
*/
#define MAX_CLKS 74
static const struct stm32f4_gate_data stm32f469_gates[] __initconst = {
{ STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 20, "ccmdatam", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" },
{ STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" },
enum { SYSTICK, FCLK };
{ STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" },
{ STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" },
{ STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" },
{ STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" },
{ STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" },
{ STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div",
CLK_IGNORE_UNUSED },
{ STM32F4_RCC_AHB3ENR, 1, "qspi", "ahb_div",
CLK_IGNORE_UNUSED },
{ STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" },
{ STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" },
{ STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" },
{ STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" },
{ STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" },
{ STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" },
{ STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" },
{ STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" },
{ STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" },
{ STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 17, "uart2", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 18, "uart3", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 19, "uart4", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 20, "uart5", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 21, "i2c1", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 22, "i2c2", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 23, "i2c3", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 30, "uart7", "apb1_div" },
{ STM32F4_RCC_APB1ENR, 31, "uart8", "apb1_div" },
{ STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" },
{ STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" },
{ STM32F4_RCC_APB2ENR, 4, "usart1", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 5, "usart6", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 11, "sdio", "pll48" },
{ STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" },
{ STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" },
{ STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" },
{ STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" },
{ STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" },
};
enum { SYSTICK, FCLK, CLK_LSI, CLK_LSE, CLK_HSE_RTC, CLK_RTC, END_PRIMARY_CLK };
/*
* This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx
* have gate bits associated with them. Its combined hweight is 71.
*/
static const u64 stm32f42xx_gate_map[] = { 0x000000f17ef417ffull,
0x0000000000000001ull,
0x04777f33f6fec9ffull };
#define MAX_GATE_MAP 3
static const u64 stm32f42xx_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull,
0x0000000000000001ull,
0x04777f33f6fec9ffull };
static const u64 stm32f46xx_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull,
0x0000000000000003ull,
0x0c777f33f6fec9ffull };
static const u64 *stm32f4_gate_map;
static struct clk_hw **clks;
static struct clk_hw *clks[MAX_CLKS];
static DEFINE_SPINLOCK(stm32f4_clk_lock);
static void __iomem *base;
static struct regmap *pdrm;
/*
* "Multiplier" device for APBx clocks.
*
@ -256,15 +349,15 @@ static void stm32f4_rcc_register_pll(const char *hse_clk, const char *hsi_clk)
*/
static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary)
{
u64 table[ARRAY_SIZE(stm32f42xx_gate_map)];
u64 table[MAX_GATE_MAP];
if (primary == 1) {
if (WARN_ON(secondary > FCLK))
if (WARN_ON(secondary >= END_PRIMARY_CLK))
return -EINVAL;
return secondary;
}
memcpy(table, stm32f42xx_gate_map, sizeof(table));
memcpy(table, stm32f4_gate_map, sizeof(table));
/* only bits set in table can be used as indices */
if (WARN_ON(secondary >= BITS_PER_BYTE * sizeof(table) ||
@ -276,7 +369,7 @@ static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary)
table[BIT_ULL_WORD(secondary)] &=
GENMASK_ULL(secondary % BITS_PER_LONG_LONG, 0);
return FCLK + hweight64(table[0]) +
return END_PRIMARY_CLK - 1 + hweight64(table[0]) +
(BIT_ULL_WORD(secondary) >= 1 ? hweight64(table[1]) : 0) +
(BIT_ULL_WORD(secondary) >= 2 ? hweight64(table[2]) : 0);
}
@ -292,6 +385,212 @@ stm32f4_rcc_lookup_clk(struct of_phandle_args *clkspec, void *data)
return clks[i];
}
#define to_rgclk(_rgate) container_of(_rgate, struct stm32_rgate, gate)
static inline void disable_power_domain_write_protection(void)
{
if (pdrm)
regmap_update_bits(pdrm, 0x00, (1 << 8), (1 << 8));
}
static inline void enable_power_domain_write_protection(void)
{
if (pdrm)
regmap_update_bits(pdrm, 0x00, (1 << 8), (0 << 8));
}
static inline void sofware_reset_backup_domain(void)
{
unsigned long val;
val = readl(base + STM32F4_RCC_BDCR);
writel(val | BIT(16), base + STM32F4_RCC_BDCR);
writel(val & ~BIT(16), base + STM32F4_RCC_BDCR);
}
struct stm32_rgate {
struct clk_gate gate;
u8 bit_rdy_idx;
};
#define RTC_TIMEOUT 1000000
static int rgclk_enable(struct clk_hw *hw)
{
struct clk_gate *gate = to_clk_gate(hw);
struct stm32_rgate *rgate = to_rgclk(gate);
u32 reg;
int ret;
disable_power_domain_write_protection();
clk_gate_ops.enable(hw);
ret = readl_relaxed_poll_timeout_atomic(gate->reg, reg,
reg & rgate->bit_rdy_idx, 1000, RTC_TIMEOUT);
enable_power_domain_write_protection();
return ret;
}
static void rgclk_disable(struct clk_hw *hw)
{
clk_gate_ops.disable(hw);
}
static int rgclk_is_enabled(struct clk_hw *hw)
{
return clk_gate_ops.is_enabled(hw);
}
static const struct clk_ops rgclk_ops = {
.enable = rgclk_enable,
.disable = rgclk_disable,
.is_enabled = rgclk_is_enabled,
};
static struct clk_hw *clk_register_rgate(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 bit_idx, u8 bit_rdy_idx,
u8 clk_gate_flags, spinlock_t *lock)
{
struct stm32_rgate *rgate;
struct clk_init_data init = { NULL };
struct clk_hw *hw;
int ret;
rgate = kzalloc(sizeof(*rgate), GFP_KERNEL);
if (!rgate)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &rgclk_ops;
init.flags = flags;
init.parent_names = &parent_name;
init.num_parents = 1;
rgate->bit_rdy_idx = bit_rdy_idx;
rgate->gate.lock = lock;
rgate->gate.reg = reg;
rgate->gate.bit_idx = bit_idx;
rgate->gate.hw.init = &init;
hw = &rgate->gate.hw;
ret = clk_hw_register(dev, hw);
if (ret) {
kfree(rgate);
hw = ERR_PTR(ret);
}
return hw;
}
static int cclk_gate_enable(struct clk_hw *hw)
{
int ret;
disable_power_domain_write_protection();
ret = clk_gate_ops.enable(hw);
enable_power_domain_write_protection();
return ret;
}
static void cclk_gate_disable(struct clk_hw *hw)
{
disable_power_domain_write_protection();
clk_gate_ops.disable(hw);
enable_power_domain_write_protection();
}
static int cclk_gate_is_enabled(struct clk_hw *hw)
{
return clk_gate_ops.is_enabled(hw);
}
static const struct clk_ops cclk_gate_ops = {
.enable = cclk_gate_enable,
.disable = cclk_gate_disable,
.is_enabled = cclk_gate_is_enabled,
};
static u8 cclk_mux_get_parent(struct clk_hw *hw)
{
return clk_mux_ops.get_parent(hw);
}
static int cclk_mux_set_parent(struct clk_hw *hw, u8 index)
{
int ret;
disable_power_domain_write_protection();
sofware_reset_backup_domain();
ret = clk_mux_ops.set_parent(hw, index);
enable_power_domain_write_protection();
return ret;
}
static const struct clk_ops cclk_mux_ops = {
.get_parent = cclk_mux_get_parent,
.set_parent = cclk_mux_set_parent,
};
static struct clk_hw *stm32_register_cclk(struct device *dev, const char *name,
const char * const *parent_names, int num_parents,
void __iomem *reg, u8 bit_idx, u8 shift, unsigned long flags,
spinlock_t *lock)
{
struct clk_hw *hw;
struct clk_gate *gate;
struct clk_mux *mux;
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate) {
hw = ERR_PTR(-EINVAL);
goto fail;
}
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux) {
kfree(gate);
hw = ERR_PTR(-EINVAL);
goto fail;
}
gate->reg = reg;
gate->bit_idx = bit_idx;
gate->flags = 0;
gate->lock = lock;
mux->reg = reg;
mux->shift = shift;
mux->mask = 3;
mux->flags = 0;
hw = clk_hw_register_composite(dev, name, parent_names, num_parents,
&mux->hw, &cclk_mux_ops,
NULL, NULL,
&gate->hw, &cclk_gate_ops,
flags);
if (IS_ERR(hw)) {
kfree(gate);
kfree(mux);
}
fail:
return hw;
}
static const char *sys_parents[] __initdata = { "hsi", NULL, "pll" };
static const struct clk_div_table ahb_div_table[] = {
@ -308,10 +607,46 @@ static const struct clk_div_table apb_div_table[] = {
{ 0 },
};
static const char *rtc_parents[4] = {
"no-clock", "lse", "lsi", "hse-rtc"
};
struct stm32f4_clk_data {
const struct stm32f4_gate_data *gates_data;
const u64 *gates_map;
int gates_num;
};
static const struct stm32f4_clk_data stm32f429_clk_data = {
.gates_data = stm32f429_gates,
.gates_map = stm32f42xx_gate_map,
.gates_num = ARRAY_SIZE(stm32f429_gates),
};
static const struct stm32f4_clk_data stm32f469_clk_data = {
.gates_data = stm32f469_gates,
.gates_map = stm32f46xx_gate_map,
.gates_num = ARRAY_SIZE(stm32f469_gates),
};
static const struct of_device_id stm32f4_of_match[] = {
{
.compatible = "st,stm32f42xx-rcc",
.data = &stm32f429_clk_data
},
{
.compatible = "st,stm32f469-rcc",
.data = &stm32f469_clk_data
},
{}
};
static void __init stm32f4_rcc_init(struct device_node *np)
{
const char *hse_clk;
int n;
const struct of_device_id *match;
const struct stm32f4_clk_data *data;
base = of_iomap(np, 0);
if (!base) {
@ -319,6 +654,25 @@ static void __init stm32f4_rcc_init(struct device_node *np)
return;
}
pdrm = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
if (IS_ERR(pdrm)) {
pdrm = NULL;
pr_warn("%s: Unable to get syscfg\n", __func__);
}
match = of_match_node(stm32f4_of_match, np);
if (WARN_ON(!match))
return;
data = match->data;
clks = kmalloc_array(data->gates_num + END_PRIMARY_CLK,
sizeof(*clks), GFP_KERNEL);
if (!clks)
goto fail;
stm32f4_gate_map = data->gates_map;
hse_clk = of_clk_get_parent_name(np, 0);
clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0,
@ -351,11 +705,15 @@ static void __init stm32f4_rcc_init(struct device_node *np)
clks[FCLK] = clk_hw_register_fixed_factor(NULL, "fclk", "ahb_div",
0, 1, 1);
for (n = 0; n < ARRAY_SIZE(stm32f4_gates); n++) {
const struct stm32f4_gate_data *gd = &stm32f4_gates[n];
unsigned int secondary =
8 * (gd->offset - STM32F4_RCC_AHB1ENR) + gd->bit_idx;
int idx = stm32f4_rcc_lookup_clk_idx(0, secondary);
for (n = 0; n < data->gates_num; n++) {
const struct stm32f4_gate_data *gd;
unsigned int secondary;
int idx;
gd = &data->gates_data[n];
secondary = 8 * (gd->offset - STM32F4_RCC_AHB1ENR) +
gd->bit_idx;
idx = stm32f4_rcc_lookup_clk_idx(0, secondary);
if (idx < 0)
goto fail;
@ -371,9 +729,44 @@ static void __init stm32f4_rcc_init(struct device_node *np)
}
}
clks[CLK_LSI] = clk_register_rgate(NULL, "lsi", "clk-lsi", 0,
base + STM32F4_RCC_CSR, 0, 2, 0, &stm32f4_clk_lock);
if (IS_ERR(clks[CLK_LSI])) {
pr_err("Unable to register lsi clock\n");
goto fail;
}
clks[CLK_LSE] = clk_register_rgate(NULL, "lse", "clk-lse", 0,
base + STM32F4_RCC_BDCR, 0, 2, 0, &stm32f4_clk_lock);
if (IS_ERR(clks[CLK_LSE])) {
pr_err("Unable to register lse clock\n");
goto fail;
}
clks[CLK_HSE_RTC] = clk_hw_register_divider(NULL, "hse-rtc", "clk-hse",
0, base + STM32F4_RCC_CFGR, 16, 5, 0,
&stm32f4_clk_lock);
if (IS_ERR(clks[CLK_HSE_RTC])) {
pr_err("Unable to register hse-rtc clock\n");
goto fail;
}
clks[CLK_RTC] = stm32_register_cclk(NULL, "rtc", rtc_parents, 4,
base + STM32F4_RCC_BDCR, 15, 8, 0, &stm32f4_clk_lock);
if (IS_ERR(clks[CLK_RTC])) {
pr_err("Unable to register rtc clock\n");
goto fail;
}
of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL);
return;
fail:
kfree(clks);
iounmap(base);
}
CLK_OF_DECLARE(stm32f4_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init);
CLK_OF_DECLARE(stm32f42xx_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init);
CLK_OF_DECLARE(stm32f46xx_rcc, "st,stm32f469-rcc", stm32f4_rcc_init);

View File

@ -243,7 +243,7 @@ static int wm831x_clkout_is_prepared(struct clk_hw *hw)
if (ret < 0) {
dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n",
ret);
return true;
return false;
}
return (ret & WM831X_CLKOUT_ENA) != 0;

View File

@ -1,3 +1,11 @@
config COMMON_CLK_HI3516CV300
tristate "HI3516CV300 Clock Driver"
depends on ARCH_HISI || COMPILE_TEST
select RESET_HISI
default ARCH_HISI
help
Build the clock driver for hi3516cv300.
config COMMON_CLK_HI3519
tristate "Hi3519 Clock Driver"
depends on ARCH_HISI || COMPILE_TEST
@ -6,6 +14,14 @@ config COMMON_CLK_HI3519
help
Build the clock driver for hi3519.
config COMMON_CLK_HI3798CV200
tristate "Hi3798CV200 Clock Driver"
depends on ARCH_HISI || COMPILE_TEST
select RESET_HISI
default ARCH_HISI
help
Build the clock driver for hi3798cv200.
config COMMON_CLK_HI6220
bool "Hi6220 Clock Driver"
depends on ARCH_HISI || COMPILE_TEST
@ -23,5 +39,6 @@ config RESET_HISI
config STUB_CLK_HI6220
bool "Hi6220 Stub Clock Driver"
depends on COMMON_CLK_HI6220 && MAILBOX
default ARCH_HISI
help
Build the Hisilicon Hi6220 stub clock driver.

View File

@ -7,7 +7,9 @@ obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o
obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o
obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o
obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o
obj-$(CONFIG_COMMON_CLK_HI3516CV300) += crg-hi3516cv300.o
obj-$(CONFIG_COMMON_CLK_HI3519) += clk-hi3519.o
obj-$(CONFIG_COMMON_CLK_HI3798CV200) += crg-hi3798cv200.o
obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o
obj-$(CONFIG_RESET_HISI) += reset.o
obj-$(CONFIG_STUB_CLK_HI6220) += clk-hi6220-stub.o

View File

@ -0,0 +1,330 @@
/*
* Hi3516CV300 Clock and Reset Generator Driver
*
* Copyright (c) 2016 HiSilicon Technologies Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <dt-bindings/clock/hi3516cv300-clock.h>
#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include "clk.h"
#include "crg.h"
#include "reset.h"
/* hi3516CV300 core CRG */
#define HI3516CV300_INNER_CLK_OFFSET 64
#define HI3516CV300_FIXED_3M 65
#define HI3516CV300_FIXED_6M 66
#define HI3516CV300_FIXED_24M 67
#define HI3516CV300_FIXED_49P5 68
#define HI3516CV300_FIXED_50M 69
#define HI3516CV300_FIXED_83P3M 70
#define HI3516CV300_FIXED_99M 71
#define HI3516CV300_FIXED_100M 72
#define HI3516CV300_FIXED_148P5M 73
#define HI3516CV300_FIXED_198M 74
#define HI3516CV300_FIXED_297M 75
#define HI3516CV300_UART_MUX 76
#define HI3516CV300_FMC_MUX 77
#define HI3516CV300_MMC0_MUX 78
#define HI3516CV300_MMC1_MUX 79
#define HI3516CV300_MMC2_MUX 80
#define HI3516CV300_MMC3_MUX 81
#define HI3516CV300_PWM_MUX 82
#define HI3516CV300_CRG_NR_CLKS 128
static const struct hisi_fixed_rate_clock hi3516cv300_fixed_rate_clks[] = {
{ HI3516CV300_FIXED_3M, "3m", NULL, 0, 3000000, },
{ HI3516CV300_FIXED_6M, "6m", NULL, 0, 6000000, },
{ HI3516CV300_FIXED_24M, "24m", NULL, 0, 24000000, },
{ HI3516CV300_FIXED_49P5, "49.5m", NULL, 0, 49500000, },
{ HI3516CV300_FIXED_50M, "50m", NULL, 0, 50000000, },
{ HI3516CV300_FIXED_83P3M, "83.3m", NULL, 0, 83300000, },
{ HI3516CV300_FIXED_99M, "99m", NULL, 0, 99000000, },
{ HI3516CV300_FIXED_100M, "100m", NULL, 0, 100000000, },
{ HI3516CV300_FIXED_148P5M, "148.5m", NULL, 0, 148500000, },
{ HI3516CV300_FIXED_198M, "198m", NULL, 0, 198000000, },
{ HI3516CV300_FIXED_297M, "297m", NULL, 0, 297000000, },
{ HI3516CV300_APB_CLK, "apb", NULL, 0, 50000000, },
};
static const char *const uart_mux_p[] = {"24m", "6m"};
static const char *const fmc_mux_p[] = {
"24m", "83.3m", "148.5m", "198m", "297m"
};
static const char *const mmc_mux_p[] = {"49.5m"};
static const char *const mmc2_mux_p[] = {"99m", "49.5m"};
static const char *const pwm_mux_p[] = {"3m", "50m", "24m", "24m"};
static u32 uart_mux_table[] = {0, 1};
static u32 fmc_mux_table[] = {0, 1, 2, 3, 4};
static u32 mmc_mux_table[] = {0};
static u32 mmc2_mux_table[] = {0, 2};
static u32 pwm_mux_table[] = {0, 1, 2, 3};
static const struct hisi_mux_clock hi3516cv300_mux_clks[] = {
{ HI3516CV300_UART_MUX, "uart_mux", uart_mux_p, ARRAY_SIZE(uart_mux_p),
CLK_SET_RATE_PARENT, 0xe4, 19, 1, 0, uart_mux_table, },
{ HI3516CV300_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p),
CLK_SET_RATE_PARENT, 0xc0, 2, 3, 0, fmc_mux_table, },
{ HI3516CV300_MMC0_MUX, "mmc0_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p),
CLK_SET_RATE_PARENT, 0xc4, 4, 2, 0, mmc_mux_table, },
{ HI3516CV300_MMC1_MUX, "mmc1_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p),
CLK_SET_RATE_PARENT, 0xc4, 12, 2, 0, mmc_mux_table, },
{ HI3516CV300_MMC2_MUX, "mmc2_mux", mmc2_mux_p, ARRAY_SIZE(mmc2_mux_p),
CLK_SET_RATE_PARENT, 0xc4, 20, 2, 0, mmc2_mux_table, },
{ HI3516CV300_MMC3_MUX, "mmc3_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p),
CLK_SET_RATE_PARENT, 0xc8, 4, 2, 0, mmc_mux_table, },
{ HI3516CV300_PWM_MUX, "pwm_mux", pwm_mux_p, ARRAY_SIZE(pwm_mux_p),
CLK_SET_RATE_PARENT, 0x38, 2, 2, 0, pwm_mux_table, },
};
static const struct hisi_gate_clock hi3516cv300_gate_clks[] = {
{ HI3516CV300_UART0_CLK, "clk_uart0", "uart_mux", CLK_SET_RATE_PARENT,
0xe4, 15, 0, },
{ HI3516CV300_UART1_CLK, "clk_uart1", "uart_mux", CLK_SET_RATE_PARENT,
0xe4, 16, 0, },
{ HI3516CV300_UART2_CLK, "clk_uart2", "uart_mux", CLK_SET_RATE_PARENT,
0xe4, 17, 0, },
{ HI3516CV300_SPI0_CLK, "clk_spi0", "100m", CLK_SET_RATE_PARENT,
0xe4, 13, 0, },
{ HI3516CV300_SPI1_CLK, "clk_spi1", "100m", CLK_SET_RATE_PARENT,
0xe4, 14, 0, },
{ HI3516CV300_FMC_CLK, "clk_fmc", "fmc_mux", CLK_SET_RATE_PARENT,
0xc0, 1, 0, },
{ HI3516CV300_MMC0_CLK, "clk_mmc0", "mmc0_mux", CLK_SET_RATE_PARENT,
0xc4, 1, 0, },
{ HI3516CV300_MMC1_CLK, "clk_mmc1", "mmc1_mux", CLK_SET_RATE_PARENT,
0xc4, 9, 0, },
{ HI3516CV300_MMC2_CLK, "clk_mmc2", "mmc2_mux", CLK_SET_RATE_PARENT,
0xc4, 17, 0, },
{ HI3516CV300_MMC3_CLK, "clk_mmc3", "mmc3_mux", CLK_SET_RATE_PARENT,
0xc8, 1, 0, },
{ HI3516CV300_ETH_CLK, "clk_eth", NULL, 0, 0xec, 1, 0, },
{ HI3516CV300_DMAC_CLK, "clk_dmac", NULL, 0, 0xd8, 5, 0, },
{ HI3516CV300_PWM_CLK, "clk_pwm", "pwm_mux", CLK_SET_RATE_PARENT,
0x38, 1, 0, },
{ HI3516CV300_USB2_BUS_CLK, "clk_usb2_bus", NULL, 0, 0xb8, 0, 0, },
{ HI3516CV300_USB2_OHCI48M_CLK, "clk_usb2_ohci48m", NULL, 0,
0xb8, 1, 0, },
{ HI3516CV300_USB2_OHCI12M_CLK, "clk_usb2_ohci12m", NULL, 0,
0xb8, 2, 0, },
{ HI3516CV300_USB2_OTG_UTMI_CLK, "clk_usb2_otg_utmi", NULL, 0,
0xb8, 3, 0, },
{ HI3516CV300_USB2_HST_PHY_CLK, "clk_usb2_hst_phy", NULL, 0,
0xb8, 4, 0, },
{ HI3516CV300_USB2_UTMI0_CLK, "clk_usb2_utmi0", NULL, 0, 0xb8, 5, 0, },
{ HI3516CV300_USB2_PHY_CLK, "clk_usb2_phy", NULL, 0, 0xb8, 7, 0, },
};
static struct hisi_clock_data *hi3516cv300_clk_register(
struct platform_device *pdev)
{
struct hisi_clock_data *clk_data;
int ret;
clk_data = hisi_clk_alloc(pdev, HI3516CV300_CRG_NR_CLKS);
if (!clk_data)
return ERR_PTR(-ENOMEM);
ret = hisi_clk_register_fixed_rate(hi3516cv300_fixed_rate_clks,
ARRAY_SIZE(hi3516cv300_fixed_rate_clks), clk_data);
if (ret)
return ERR_PTR(ret);
ret = hisi_clk_register_mux(hi3516cv300_mux_clks,
ARRAY_SIZE(hi3516cv300_mux_clks), clk_data);
if (ret)
goto unregister_fixed_rate;
ret = hisi_clk_register_gate(hi3516cv300_gate_clks,
ARRAY_SIZE(hi3516cv300_gate_clks), clk_data);
if (ret)
goto unregister_mux;
ret = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, &clk_data->clk_data);
if (ret)
goto unregister_gate;
return clk_data;
unregister_gate:
hisi_clk_unregister_gate(hi3516cv300_gate_clks,
ARRAY_SIZE(hi3516cv300_gate_clks), clk_data);
unregister_mux:
hisi_clk_unregister_mux(hi3516cv300_mux_clks,
ARRAY_SIZE(hi3516cv300_mux_clks), clk_data);
unregister_fixed_rate:
hisi_clk_unregister_fixed_rate(hi3516cv300_fixed_rate_clks,
ARRAY_SIZE(hi3516cv300_fixed_rate_clks), clk_data);
return ERR_PTR(ret);
}
static void hi3516cv300_clk_unregister(struct platform_device *pdev)
{
struct hisi_crg_dev *crg = platform_get_drvdata(pdev);
of_clk_del_provider(pdev->dev.of_node);
hisi_clk_unregister_gate(hi3516cv300_gate_clks,
ARRAY_SIZE(hi3516cv300_gate_clks), crg->clk_data);
hisi_clk_unregister_mux(hi3516cv300_mux_clks,
ARRAY_SIZE(hi3516cv300_mux_clks), crg->clk_data);
hisi_clk_unregister_fixed_rate(hi3516cv300_fixed_rate_clks,
ARRAY_SIZE(hi3516cv300_fixed_rate_clks), crg->clk_data);
}
static const struct hisi_crg_funcs hi3516cv300_crg_funcs = {
.register_clks = hi3516cv300_clk_register,
.unregister_clks = hi3516cv300_clk_unregister,
};
/* hi3516CV300 sysctrl CRG */
#define HI3516CV300_SYSCTRL_NR_CLKS 16
static const char *wdt_mux_p[] __initconst = { "3m", "apb" };
static u32 wdt_mux_table[] = {0, 1};
static const struct hisi_mux_clock hi3516cv300_sysctrl_mux_clks[] = {
{ HI3516CV300_WDT_CLK, "wdt", wdt_mux_p, ARRAY_SIZE(wdt_mux_p),
CLK_SET_RATE_PARENT, 0x0, 23, 1, 0, wdt_mux_table, },
};
static struct hisi_clock_data *hi3516cv300_sysctrl_clk_register(
struct platform_device *pdev)
{
struct hisi_clock_data *clk_data;
int ret;
clk_data = hisi_clk_alloc(pdev, HI3516CV300_SYSCTRL_NR_CLKS);
if (!clk_data)
return ERR_PTR(-ENOMEM);
ret = hisi_clk_register_mux(hi3516cv300_sysctrl_mux_clks,
ARRAY_SIZE(hi3516cv300_sysctrl_mux_clks), clk_data);
if (ret)
return ERR_PTR(ret);
ret = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, &clk_data->clk_data);
if (ret)
goto unregister_mux;
return clk_data;
unregister_mux:
hisi_clk_unregister_mux(hi3516cv300_sysctrl_mux_clks,
ARRAY_SIZE(hi3516cv300_sysctrl_mux_clks), clk_data);
return ERR_PTR(ret);
}
static void hi3516cv300_sysctrl_clk_unregister(struct platform_device *pdev)
{
struct hisi_crg_dev *crg = platform_get_drvdata(pdev);
of_clk_del_provider(pdev->dev.of_node);
hisi_clk_unregister_mux(hi3516cv300_sysctrl_mux_clks,
ARRAY_SIZE(hi3516cv300_sysctrl_mux_clks),
crg->clk_data);
}
static const struct hisi_crg_funcs hi3516cv300_sysctrl_funcs = {
.register_clks = hi3516cv300_sysctrl_clk_register,
.unregister_clks = hi3516cv300_sysctrl_clk_unregister,
};
static const struct of_device_id hi3516cv300_crg_match_table[] = {
{
.compatible = "hisilicon,hi3516cv300-crg",
.data = &hi3516cv300_crg_funcs
},
{
.compatible = "hisilicon,hi3516cv300-sysctrl",
.data = &hi3516cv300_sysctrl_funcs
},
{ }
};
MODULE_DEVICE_TABLE(of, hi3516cv300_crg_match_table);
static int hi3516cv300_crg_probe(struct platform_device *pdev)
{
struct hisi_crg_dev *crg;
crg = devm_kmalloc(&pdev->dev, sizeof(*crg), GFP_KERNEL);
if (!crg)
return -ENOMEM;
crg->funcs = of_device_get_match_data(&pdev->dev);
if (!crg->funcs)
return -ENOENT;
crg->rstc = hisi_reset_init(pdev);
if (!crg->rstc)
return -ENOMEM;
crg->clk_data = crg->funcs->register_clks(pdev);
if (IS_ERR(crg->clk_data)) {
hisi_reset_exit(crg->rstc);
return PTR_ERR(crg->clk_data);
}
platform_set_drvdata(pdev, crg);
return 0;
}
static int hi3516cv300_crg_remove(struct platform_device *pdev)
{
struct hisi_crg_dev *crg = platform_get_drvdata(pdev);
hisi_reset_exit(crg->rstc);
crg->funcs->unregister_clks(pdev);
return 0;
}
static struct platform_driver hi3516cv300_crg_driver = {
.probe = hi3516cv300_crg_probe,
.remove = hi3516cv300_crg_remove,
.driver = {
.name = "hi3516cv300-crg",
.of_match_table = hi3516cv300_crg_match_table,
},
};
static int __init hi3516cv300_crg_init(void)
{
return platform_driver_register(&hi3516cv300_crg_driver);
}
core_initcall(hi3516cv300_crg_init);
static void __exit hi3516cv300_crg_exit(void)
{
platform_driver_unregister(&hi3516cv300_crg_driver);
}
module_exit(hi3516cv300_crg_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("HiSilicon Hi3516CV300 CRG Driver");

View File

@ -0,0 +1,337 @@
/*
* Hi3798CV200 Clock and Reset Generator Driver
*
* Copyright (c) 2016 HiSilicon Technologies Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <dt-bindings/clock/histb-clock.h>
#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include "clk.h"
#include "crg.h"
#include "reset.h"
/* hi3798CV200 core CRG */
#define HI3798CV200_INNER_CLK_OFFSET 64
#define HI3798CV200_FIXED_24M 65
#define HI3798CV200_FIXED_25M 66
#define HI3798CV200_FIXED_50M 67
#define HI3798CV200_FIXED_75M 68
#define HI3798CV200_FIXED_100M 69
#define HI3798CV200_FIXED_150M 70
#define HI3798CV200_FIXED_200M 71
#define HI3798CV200_FIXED_250M 72
#define HI3798CV200_FIXED_300M 73
#define HI3798CV200_FIXED_400M 74
#define HI3798CV200_MMC_MUX 75
#define HI3798CV200_ETH_PUB_CLK 76
#define HI3798CV200_ETH_BUS_CLK 77
#define HI3798CV200_ETH_BUS0_CLK 78
#define HI3798CV200_ETH_BUS1_CLK 79
#define HI3798CV200_COMBPHY1_MUX 80
#define HI3798CV200_CRG_NR_CLKS 128
static const struct hisi_fixed_rate_clock hi3798cv200_fixed_rate_clks[] = {
{ HISTB_OSC_CLK, "clk_osc", NULL, 0, 24000000, },
{ HISTB_APB_CLK, "clk_apb", NULL, 0, 100000000, },
{ HISTB_AHB_CLK, "clk_ahb", NULL, 0, 200000000, },
{ HI3798CV200_FIXED_24M, "24m", NULL, 0, 24000000, },
{ HI3798CV200_FIXED_25M, "25m", NULL, 0, 25000000, },
{ HI3798CV200_FIXED_50M, "50m", NULL, 0, 50000000, },
{ HI3798CV200_FIXED_75M, "75m", NULL, 0, 75000000, },
{ HI3798CV200_FIXED_100M, "100m", NULL, 0, 100000000, },
{ HI3798CV200_FIXED_150M, "150m", NULL, 0, 150000000, },
{ HI3798CV200_FIXED_200M, "200m", NULL, 0, 200000000, },
{ HI3798CV200_FIXED_250M, "250m", NULL, 0, 250000000, },
};
static const char *const mmc_mux_p[] = {
"100m", "50m", "25m", "200m", "150m" };
static u32 mmc_mux_table[] = {0, 1, 2, 3, 6};
static const char *const comphy1_mux_p[] = {
"100m", "25m"};
static u32 comphy1_mux_table[] = {2, 3};
static struct hisi_mux_clock hi3798cv200_mux_clks[] = {
{ HI3798CV200_MMC_MUX, "mmc_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p),
CLK_SET_RATE_PARENT, 0xa0, 8, 3, 0, mmc_mux_table, },
{ HI3798CV200_COMBPHY1_MUX, "combphy1_mux",
comphy1_mux_p, ARRAY_SIZE(comphy1_mux_p),
CLK_SET_RATE_PARENT, 0x188, 10, 2, 0, comphy1_mux_table, },
};
static const struct hisi_gate_clock hi3798cv200_gate_clks[] = {
/* UART */
{ HISTB_UART2_CLK, "clk_uart2", "75m",
CLK_SET_RATE_PARENT, 0x68, 4, 0, },
/* I2C */
{ HISTB_I2C0_CLK, "clk_i2c0", "clk_apb",
CLK_SET_RATE_PARENT, 0x6C, 4, 0, },
{ HISTB_I2C1_CLK, "clk_i2c1", "clk_apb",
CLK_SET_RATE_PARENT, 0x6C, 8, 0, },
{ HISTB_I2C2_CLK, "clk_i2c2", "clk_apb",
CLK_SET_RATE_PARENT, 0x6C, 12, 0, },
{ HISTB_I2C3_CLK, "clk_i2c3", "clk_apb",
CLK_SET_RATE_PARENT, 0x6C, 16, 0, },
{ HISTB_I2C4_CLK, "clk_i2c4", "clk_apb",
CLK_SET_RATE_PARENT, 0x6C, 20, 0, },
/* SPI */
{ HISTB_SPI0_CLK, "clk_spi0", "clk_apb",
CLK_SET_RATE_PARENT, 0x70, 0, 0, },
/* SDIO */
{ HISTB_SDIO0_BIU_CLK, "clk_sdio0_biu", "200m",
CLK_SET_RATE_PARENT, 0x9c, 0, 0, },
{ HISTB_SDIO0_CIU_CLK, "clk_sdio0_ciu", "mmc_mux",
CLK_SET_RATE_PARENT, 0x9c, 1, 0, },
/* EMMC */
{ HISTB_MMC_BIU_CLK, "clk_mmc_biu", "200m",
CLK_SET_RATE_PARENT, 0xa0, 0, 0, },
{ HISTB_MMC_CIU_CLK, "clk_mmc_ciu", "mmc_mux",
CLK_SET_RATE_PARENT, 0xa0, 1, 0, },
/* PCIE*/
{ HISTB_PCIE_BUS_CLK, "clk_pcie_bus", "200m",
CLK_SET_RATE_PARENT, 0x18c, 0, 0, },
{ HISTB_PCIE_SYS_CLK, "clk_pcie_sys", "100m",
CLK_SET_RATE_PARENT, 0x18c, 1, 0, },
{ HISTB_PCIE_PIPE_CLK, "clk_pcie_pipe", "250m",
CLK_SET_RATE_PARENT, 0x18c, 2, 0, },
{ HISTB_PCIE_AUX_CLK, "clk_pcie_aux", "24m",
CLK_SET_RATE_PARENT, 0x18c, 3, 0, },
/* Ethernet */
{ HI3798CV200_ETH_PUB_CLK, "clk_pub", NULL,
CLK_SET_RATE_PARENT, 0xcc, 5, 0, },
{ HI3798CV200_ETH_BUS_CLK, "clk_bus", "clk_pub",
CLK_SET_RATE_PARENT, 0xcc, 0, 0, },
{ HI3798CV200_ETH_BUS0_CLK, "clk_bus_m0", "clk_bus",
CLK_SET_RATE_PARENT, 0xcc, 1, 0, },
{ HI3798CV200_ETH_BUS1_CLK, "clk_bus_m1", "clk_bus",
CLK_SET_RATE_PARENT, 0xcc, 2, 0, },
{ HISTB_ETH0_MAC_CLK, "clk_mac0", "clk_bus_m0",
CLK_SET_RATE_PARENT, 0xcc, 3, 0, },
{ HISTB_ETH0_MACIF_CLK, "clk_macif0", "clk_bus_m0",
CLK_SET_RATE_PARENT, 0xcc, 24, 0, },
{ HISTB_ETH1_MAC_CLK, "clk_mac1", "clk_bus_m1",
CLK_SET_RATE_PARENT, 0xcc, 4, 0, },
{ HISTB_ETH1_MACIF_CLK, "clk_macif1", "clk_bus_m1",
CLK_SET_RATE_PARENT, 0xcc, 25, 0, },
/* COMBPHY1 */
{ HISTB_COMBPHY1_CLK, "clk_combphy1", "combphy1_mux",
CLK_SET_RATE_PARENT, 0x188, 8, 0, },
};
static struct hisi_clock_data *hi3798cv200_clk_register(
struct platform_device *pdev)
{
struct hisi_clock_data *clk_data;
int ret;
clk_data = hisi_clk_alloc(pdev, HI3798CV200_CRG_NR_CLKS);
if (!clk_data)
return ERR_PTR(-ENOMEM);
ret = hisi_clk_register_fixed_rate(hi3798cv200_fixed_rate_clks,
ARRAY_SIZE(hi3798cv200_fixed_rate_clks),
clk_data);
if (ret)
return ERR_PTR(ret);
ret = hisi_clk_register_mux(hi3798cv200_mux_clks,
ARRAY_SIZE(hi3798cv200_mux_clks),
clk_data);
if (ret)
goto unregister_fixed_rate;
ret = hisi_clk_register_gate(hi3798cv200_gate_clks,
ARRAY_SIZE(hi3798cv200_gate_clks),
clk_data);
if (ret)
goto unregister_mux;
ret = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, &clk_data->clk_data);
if (ret)
goto unregister_gate;
return clk_data;
unregister_fixed_rate:
hisi_clk_unregister_fixed_rate(hi3798cv200_fixed_rate_clks,
ARRAY_SIZE(hi3798cv200_fixed_rate_clks),
clk_data);
unregister_mux:
hisi_clk_unregister_mux(hi3798cv200_mux_clks,
ARRAY_SIZE(hi3798cv200_mux_clks),
clk_data);
unregister_gate:
hisi_clk_unregister_gate(hi3798cv200_gate_clks,
ARRAY_SIZE(hi3798cv200_gate_clks),
clk_data);
return ERR_PTR(ret);
}
static void hi3798cv200_clk_unregister(struct platform_device *pdev)
{
struct hisi_crg_dev *crg = platform_get_drvdata(pdev);
of_clk_del_provider(pdev->dev.of_node);
hisi_clk_unregister_gate(hi3798cv200_gate_clks,
ARRAY_SIZE(hi3798cv200_gate_clks),
crg->clk_data);
hisi_clk_unregister_mux(hi3798cv200_mux_clks,
ARRAY_SIZE(hi3798cv200_mux_clks),
crg->clk_data);
hisi_clk_unregister_fixed_rate(hi3798cv200_fixed_rate_clks,
ARRAY_SIZE(hi3798cv200_fixed_rate_clks),
crg->clk_data);
}
static const struct hisi_crg_funcs hi3798cv200_crg_funcs = {
.register_clks = hi3798cv200_clk_register,
.unregister_clks = hi3798cv200_clk_unregister,
};
/* hi3798CV200 sysctrl CRG */
#define HI3798CV200_SYSCTRL_NR_CLKS 16
static const struct hisi_gate_clock hi3798cv200_sysctrl_gate_clks[] = {
{ HISTB_IR_CLK, "clk_ir", "100m",
CLK_SET_RATE_PARENT, 0x48, 4, 0, },
{ HISTB_TIMER01_CLK, "clk_timer01", "24m",
CLK_SET_RATE_PARENT, 0x48, 6, 0, },
{ HISTB_UART0_CLK, "clk_uart0", "75m",
CLK_SET_RATE_PARENT, 0x48, 10, 0, },
};
static struct hisi_clock_data *hi3798cv200_sysctrl_clk_register(
struct platform_device *pdev)
{
struct hisi_clock_data *clk_data;
int ret;
clk_data = hisi_clk_alloc(pdev, HI3798CV200_SYSCTRL_NR_CLKS);
if (!clk_data)
return ERR_PTR(-ENOMEM);
ret = hisi_clk_register_gate(hi3798cv200_sysctrl_gate_clks,
ARRAY_SIZE(hi3798cv200_sysctrl_gate_clks),
clk_data);
if (ret)
return ERR_PTR(ret);
ret = of_clk_add_provider(pdev->dev.of_node,
of_clk_src_onecell_get, &clk_data->clk_data);
if (ret)
goto unregister_gate;
return clk_data;
unregister_gate:
hisi_clk_unregister_gate(hi3798cv200_sysctrl_gate_clks,
ARRAY_SIZE(hi3798cv200_sysctrl_gate_clks),
clk_data);
return ERR_PTR(ret);
}
static void hi3798cv200_sysctrl_clk_unregister(struct platform_device *pdev)
{
struct hisi_crg_dev *crg = platform_get_drvdata(pdev);
of_clk_del_provider(pdev->dev.of_node);
hisi_clk_unregister_gate(hi3798cv200_sysctrl_gate_clks,
ARRAY_SIZE(hi3798cv200_sysctrl_gate_clks),
crg->clk_data);
}
static const struct hisi_crg_funcs hi3798cv200_sysctrl_funcs = {
.register_clks = hi3798cv200_sysctrl_clk_register,
.unregister_clks = hi3798cv200_sysctrl_clk_unregister,
};
static const struct of_device_id hi3798cv200_crg_match_table[] = {
{ .compatible = "hisilicon,hi3798cv200-crg",
.data = &hi3798cv200_crg_funcs },
{ .compatible = "hisilicon,hi3798cv200-sysctrl",
.data = &hi3798cv200_sysctrl_funcs },
{ }
};
MODULE_DEVICE_TABLE(of, hi3798cv200_crg_match_table);
static int hi3798cv200_crg_probe(struct platform_device *pdev)
{
struct hisi_crg_dev *crg;
crg = devm_kmalloc(&pdev->dev, sizeof(*crg), GFP_KERNEL);
if (!crg)
return -ENOMEM;
crg->funcs = of_device_get_match_data(&pdev->dev);
if (!crg->funcs)
return -ENOENT;
crg->rstc = hisi_reset_init(pdev);
if (!crg->rstc)
return -ENOMEM;
crg->clk_data = crg->funcs->register_clks(pdev);
if (IS_ERR(crg->clk_data)) {
hisi_reset_exit(crg->rstc);
return PTR_ERR(crg->clk_data);
}
platform_set_drvdata(pdev, crg);
return 0;
}
static int hi3798cv200_crg_remove(struct platform_device *pdev)
{
struct hisi_crg_dev *crg = platform_get_drvdata(pdev);
hisi_reset_exit(crg->rstc);
crg->funcs->unregister_clks(pdev);
return 0;
}
static struct platform_driver hi3798cv200_crg_driver = {
.probe = hi3798cv200_crg_probe,
.remove = hi3798cv200_crg_remove,
.driver = {
.name = "hi3798cv200-crg",
.of_match_table = hi3798cv200_crg_match_table,
},
};
static int __init hi3798cv200_crg_init(void)
{
return platform_driver_register(&hi3798cv200_crg_driver);
}
core_initcall(hi3798cv200_crg_init);
static void __exit hi3798cv200_crg_exit(void)
{
platform_driver_unregister(&hi3798cv200_crg_driver);
}
module_exit(hi3798cv200_crg_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("HiSilicon Hi3798CV200 CRG Driver");

View File

@ -0,0 +1,34 @@
/*
* HiSilicon Clock and Reset Driver Header
*
* Copyright (c) 2016 HiSilicon Limited.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __HISI_CRG_H
#define __HISI_CRG_H
struct hisi_clock_data;
struct hisi_reset_controller;
struct hisi_crg_funcs {
struct hisi_clock_data* (*register_clks)(struct platform_device *pdev);
void (*unregister_clks)(struct platform_device *pdev);
};
struct hisi_crg_dev {
struct hisi_clock_data *clk_data;
struct hisi_reset_controller *rstc;
const struct hisi_crg_funcs *funcs;
};
#endif /* __HISI_CRG_H */

View File

@ -156,10 +156,267 @@ static struct clk ** const uart_clks[] __initconst = {
NULL
};
static int ldb_di_sel_by_clock_id(int clock_id)
{
switch (clock_id) {
case IMX6QDL_CLK_PLL5_VIDEO_DIV:
if (clk_on_imx6q() &&
imx_get_soc_revision() == IMX_CHIP_REVISION_1_0)
return -ENOENT;
return 0;
case IMX6QDL_CLK_PLL2_PFD0_352M:
return 1;
case IMX6QDL_CLK_PLL2_PFD2_396M:
return 2;
case IMX6QDL_CLK_MMDC_CH1_AXI:
return 3;
case IMX6QDL_CLK_PLL3_USB_OTG:
return 4;
default:
return -ENOENT;
}
}
static void of_assigned_ldb_sels(struct device_node *node,
unsigned int *ldb_di0_sel,
unsigned int *ldb_di1_sel)
{
struct of_phandle_args clkspec;
int index, rc, num_parents;
int parent, child, sel;
num_parents = of_count_phandle_with_args(node, "assigned-clock-parents",
"#clock-cells");
for (index = 0; index < num_parents; index++) {
rc = of_parse_phandle_with_args(node, "assigned-clock-parents",
"#clock-cells", index, &clkspec);
if (rc < 0) {
/* skip empty (null) phandles */
if (rc == -ENOENT)
continue;
else
return;
}
if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) {
pr_err("ccm: parent clock %d not in ccm\n", index);
return;
}
parent = clkspec.args[0];
rc = of_parse_phandle_with_args(node, "assigned-clocks",
"#clock-cells", index, &clkspec);
if (rc < 0)
return;
if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) {
pr_err("ccm: child clock %d not in ccm\n", index);
return;
}
child = clkspec.args[0];
if (child != IMX6QDL_CLK_LDB_DI0_SEL &&
child != IMX6QDL_CLK_LDB_DI1_SEL)
continue;
sel = ldb_di_sel_by_clock_id(parent);
if (sel < 0) {
pr_err("ccm: invalid ldb_di%d parent clock: %d\n",
child == IMX6QDL_CLK_LDB_DI1_SEL, parent);
continue;
}
if (child == IMX6QDL_CLK_LDB_DI0_SEL)
*ldb_di0_sel = sel;
if (child == IMX6QDL_CLK_LDB_DI1_SEL)
*ldb_di1_sel = sel;
}
}
#define CCM_CCDR 0x04
#define CCM_CCSR 0x0c
#define CCM_CS2CDR 0x2c
#define CCDR_MMDC_CH1_MASK BIT(16)
#define CCSR_PLL3_SW_CLK_SEL BIT(0)
#define CS2CDR_LDB_DI0_CLK_SEL_SHIFT 9
#define CS2CDR_LDB_DI1_CLK_SEL_SHIFT 12
static void __init imx6q_mmdc_ch1_mask_handshake(void __iomem *ccm_base)
{
unsigned int reg;
reg = readl_relaxed(ccm_base + CCM_CCDR);
reg |= CCDR_MMDC_CH1_MASK;
writel_relaxed(reg, ccm_base + CCM_CCDR);
}
/*
* The only way to disable the MMDC_CH1 clock is to move it to pll3_sw_clk
* via periph2_clk2_sel and then to disable pll3_sw_clk by selecting the
* bypass clock source, since there is no CG bit for mmdc_ch1.
*/
static void mmdc_ch1_disable(void __iomem *ccm_base)
{
unsigned int reg;
clk_set_parent(clk[IMX6QDL_CLK_PERIPH2_CLK2_SEL],
clk[IMX6QDL_CLK_PLL3_USB_OTG]);
/*
* Handshake with mmdc_ch1 module must be masked when changing
* periph2_clk_sel.
*/
clk_set_parent(clk[IMX6QDL_CLK_PERIPH2], clk[IMX6QDL_CLK_PERIPH2_CLK2]);
/* Disable pll3_sw_clk by selecting the bypass clock source */
reg = readl_relaxed(ccm_base + CCM_CCSR);
reg |= CCSR_PLL3_SW_CLK_SEL;
writel_relaxed(reg, ccm_base + CCM_CCSR);
}
static void mmdc_ch1_reenable(void __iomem *ccm_base)
{
unsigned int reg;
/* Enable pll3_sw_clk by disabling the bypass */
reg = readl_relaxed(ccm_base + CCM_CCSR);
reg &= ~CCSR_PLL3_SW_CLK_SEL;
writel_relaxed(reg, ccm_base + CCM_CCSR);
clk_set_parent(clk[IMX6QDL_CLK_PERIPH2], clk[IMX6QDL_CLK_PERIPH2_PRE]);
}
/*
* We have to follow a strict procedure when changing the LDB clock source,
* otherwise we risk introducing a glitch that can lock up the LDB divider.
* Things to keep in mind:
*
* 1. The current and new parent clock inputs to the mux must be disabled.
* 2. The default clock input for ldb_di0/1_clk_sel is mmdc_ch1_axi, which
* has no CG bit.
* 3. pll2_pfd2_396m can not be gated if it is used as memory clock.
* 4. In the RTL implementation of the LDB_DI_CLK_SEL muxes the top four
* options are in one mux and the PLL3 option along with three unused
* inputs is in a second mux. There is a third mux with two inputs used
* to decide between the first and second 4-port mux:
*
* pll5_video_div 0 --|\
* pll2_pfd0_352m 1 --| |_
* pll2_pfd2_396m 2 --| | `-|\
* mmdc_ch1_axi 3 --|/ | |
* | |--
* pll3_usb_otg 4 --|\ | |
* 5 --| |_,-|/
* 6 --| |
* 7 --|/
*
* The ldb_di0/1_clk_sel[1:0] bits control both 4-port muxes at the same time.
* The ldb_di0/1_clk_sel[2] bit controls the 2-port mux. The code below
* switches the parent to the bottom mux first and then manipulates the top
* mux to ensure that no glitch will enter the divider.
*/
static void init_ldb_clks(struct device_node *np, void __iomem *ccm_base)
{
unsigned int reg;
unsigned int sel[2][4];
int i;
reg = readl_relaxed(ccm_base + CCM_CS2CDR);
sel[0][0] = (reg >> CS2CDR_LDB_DI0_CLK_SEL_SHIFT) & 7;
sel[1][0] = (reg >> CS2CDR_LDB_DI1_CLK_SEL_SHIFT) & 7;
sel[0][3] = sel[0][2] = sel[0][1] = sel[0][0];
sel[1][3] = sel[1][2] = sel[1][1] = sel[1][0];
of_assigned_ldb_sels(np, &sel[0][3], &sel[1][3]);
for (i = 0; i < 2; i++) {
/* Warn if a glitch might have been introduced already */
if (sel[i][0] != 3) {
pr_warn("ccm: ldb_di%d_sel already changed from reset value: %d\n",
i, sel[i][0]);
}
if (sel[i][0] == sel[i][3])
continue;
/* Only switch to or from pll2_pfd2_396m if it is disabled */
if ((sel[i][0] == 2 || sel[i][3] == 2) &&
(clk_get_parent(clk[IMX6QDL_CLK_PERIPH_PRE]) ==
clk[IMX6QDL_CLK_PLL2_PFD2_396M])) {
pr_err("ccm: ldb_di%d_sel: couldn't disable pll2_pfd2_396m\n",
i);
sel[i][3] = sel[i][2] = sel[i][1] = sel[i][0];
continue;
}
/* First switch to the bottom mux */
sel[i][1] = sel[i][0] | 4;
/* Then configure the top mux before switching back to it */
sel[i][2] = sel[i][3] | 4;
pr_debug("ccm: switching ldb_di%d_sel: %d->%d->%d->%d\n", i,
sel[i][0], sel[i][1], sel[i][2], sel[i][3]);
}
if (sel[0][0] == sel[0][3] && sel[1][0] == sel[1][3])
return;
mmdc_ch1_disable(ccm_base);
for (i = 1; i < 4; i++) {
reg = readl_relaxed(ccm_base + CCM_CS2CDR);
reg &= ~((7 << CS2CDR_LDB_DI0_CLK_SEL_SHIFT) |
(7 << CS2CDR_LDB_DI1_CLK_SEL_SHIFT));
reg |= ((sel[0][i] << CS2CDR_LDB_DI0_CLK_SEL_SHIFT) |
(sel[1][i] << CS2CDR_LDB_DI1_CLK_SEL_SHIFT));
writel_relaxed(reg, ccm_base + CCM_CS2CDR);
}
mmdc_ch1_reenable(ccm_base);
}
#define CCM_ANALOG_PLL_VIDEO 0xa0
#define CCM_ANALOG_PFD_480 0xf0
#define CCM_ANALOG_PFD_528 0x100
#define PLL_ENABLE BIT(13)
#define PFD0_CLKGATE BIT(7)
#define PFD1_CLKGATE BIT(15)
#define PFD2_CLKGATE BIT(23)
#define PFD3_CLKGATE BIT(31)
static void disable_anatop_clocks(void __iomem *anatop_base)
{
unsigned int reg;
/* Make sure PLL2 PFDs 0-2 are gated */
reg = readl_relaxed(anatop_base + CCM_ANALOG_PFD_528);
/* Cannot gate PFD2 if pll2_pfd2_396m is the parent of MMDC clock */
if (clk_get_parent(clk[IMX6QDL_CLK_PERIPH_PRE]) ==
clk[IMX6QDL_CLK_PLL2_PFD2_396M])
reg |= PFD0_CLKGATE | PFD1_CLKGATE;
else
reg |= PFD0_CLKGATE | PFD1_CLKGATE | PFD2_CLKGATE;
writel_relaxed(reg, anatop_base + CCM_ANALOG_PFD_528);
/* Make sure PLL3 PFDs 0-3 are gated */
reg = readl_relaxed(anatop_base + CCM_ANALOG_PFD_480);
reg |= PFD0_CLKGATE | PFD1_CLKGATE | PFD2_CLKGATE | PFD3_CLKGATE;
writel_relaxed(reg, anatop_base + CCM_ANALOG_PFD_480);
/* Make sure PLL5 is disabled */
reg = readl_relaxed(anatop_base + CCM_ANALOG_PLL_VIDEO);
reg &= ~PLL_ENABLE;
writel_relaxed(reg, anatop_base + CCM_ANALOG_PLL_VIDEO);
}
static void __init imx6q_clocks_init(struct device_node *ccm_node)
{
struct device_node *np;
void __iomem *base;
void __iomem *anatop_base, *base;
int i;
int ret;
@ -172,7 +429,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk[IMX6QDL_CLK_ANACLK2] = imx_obtain_fixed_clock("anaclk2", 0);
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
base = of_iomap(np, 0);
anatop_base = base = of_iomap(np, 0);
WARN_ON(!base);
/* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */
@ -330,8 +587,20 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk[IMX6QDL_CLK_GPU3D_SHADER_SEL] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels));
clk[IMX6QDL_CLK_IPU1_SEL] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
clk[IMX6QDL_CLK_IPU2_SEL] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
disable_anatop_clocks(anatop_base);
imx6q_mmdc_ch1_mask_handshake(base);
/*
* The LDB_DI0/1_SEL muxes are registered read-only due to a hardware
* bug. Set the muxes to the requested values before registering the
* ldb_di_sel clocks.
*/
init_ldb_clks(np, base);
clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_ldb("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_ldb("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels));
clk[IMX6QDL_CLK_IPU1_DI0_PRE_SEL] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);
clk[IMX6QDL_CLK_IPU1_DI1_PRE_SEL] = imx_clk_mux_flags("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);
clk[IMX6QDL_CLK_IPU2_DI0_PRE_SEL] = imx_clk_mux_flags("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);
@ -582,12 +851,6 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk_register_clkdev(clk[IMX6QDL_CLK_ENET_REF], "enet_ref", NULL);
if ((imx_get_soc_revision() != IMX_CHIP_REVISION_1_0) ||
clk_on_imx6dl()) {
clk_set_parent(clk[IMX6QDL_CLK_LDB_DI0_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
clk_set_parent(clk[IMX6QDL_CLK_LDB_DI1_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]);
}
clk_set_rate(clk[IMX6QDL_CLK_PLL3_PFD1_540M], 540000000);
if (clk_on_imx6dl())
clk_set_parent(clk[IMX6QDL_CLK_IPU1_SEL], clk[IMX6QDL_CLK_PLL3_PFD1_540M]);

View File

@ -64,6 +64,10 @@ static const char *perclk_sels[] = { "ipg", "osc", };
static const char *lcdif_sels[] = { "lcdif_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", };
static const char *csi_sels[] = { "osc", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", };
static const char *sim_sels[] = { "sim_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", };
/* epdc_pre_sels, epdc_sels, esai_sels only exists on i.MX6ULL */
static const char *epdc_pre_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd2_508m", };
static const char *esai_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll5_video_div", "pll3_usb_otg", };
static const char *epdc_sels[] = { "epdc_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", };
static struct clk *clks[IMX6UL_CLK_END];
static struct clk_onecell_data clk_data;
@ -102,6 +106,17 @@ static u32 share_count_audio;
static u32 share_count_sai1;
static u32 share_count_sai2;
static u32 share_count_sai3;
static u32 share_count_esai;
static inline int clk_on_imx6ul(void)
{
return of_machine_is_compatible("fsl,imx6ul");
}
static inline int clk_on_imx6ull(void)
{
return of_machine_is_compatible("fsl,imx6ull");
}
static void __init imx6ul_clocks_init(struct device_node *ccm_node)
{
@ -238,12 +253,19 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_QSPI1_SEL] = imx_clk_mux("qspi1_sel", base + 0x1c, 7, 3, qspi1_sels, ARRAY_SIZE(qspi1_sels));
clks[IMX6UL_CLK_PERCLK_SEL] = imx_clk_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels));
clks[IMX6UL_CLK_CAN_SEL] = imx_clk_mux("can_sel", base + 0x20, 8, 2, can_sels, ARRAY_SIZE(can_sels));
if (clk_on_imx6ull())
clks[IMX6ULL_CLK_ESAI_SEL] = imx_clk_mux("esai_sel", base + 0x20, 19, 2, esai_sels, ARRAY_SIZE(esai_sels));
clks[IMX6UL_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels));
clks[IMX6UL_CLK_ENFC_SEL] = imx_clk_mux("enfc_sel", base + 0x2c, 15, 3, enfc_sels, ARRAY_SIZE(enfc_sels));
clks[IMX6UL_CLK_LDB_DI0_SEL] = imx_clk_mux("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di0_sels, ARRAY_SIZE(ldb_di0_sels));
clks[IMX6UL_CLK_SPDIF_SEL] = imx_clk_mux("spdif_sel", base + 0x30, 20, 2, spdif_sels, ARRAY_SIZE(spdif_sels));
clks[IMX6UL_CLK_SIM_PRE_SEL] = imx_clk_mux("sim_pre_sel", base + 0x34, 15, 3, sim_pre_sels, ARRAY_SIZE(sim_pre_sels));
clks[IMX6UL_CLK_SIM_SEL] = imx_clk_mux("sim_sel", base + 0x34, 9, 3, sim_sels, ARRAY_SIZE(sim_sels));
if (clk_on_imx6ul()) {
clks[IMX6UL_CLK_SIM_PRE_SEL] = imx_clk_mux("sim_pre_sel", base + 0x34, 15, 3, sim_pre_sels, ARRAY_SIZE(sim_pre_sels));
clks[IMX6UL_CLK_SIM_SEL] = imx_clk_mux("sim_sel", base + 0x34, 9, 3, sim_sels, ARRAY_SIZE(sim_sels));
} else if (clk_on_imx6ull()) {
clks[IMX6ULL_CLK_EPDC_PRE_SEL] = imx_clk_mux("epdc_pre_sel", base + 0x34, 15, 3, epdc_pre_sels, ARRAY_SIZE(epdc_pre_sels));
clks[IMX6ULL_CLK_EPDC_SEL] = imx_clk_mux("epdc_sel", base + 0x34, 9, 3, epdc_sels, ARRAY_SIZE(epdc_sels));
}
clks[IMX6UL_CLK_ECSPI_SEL] = imx_clk_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels));
clks[IMX6UL_CLK_LCDIF_PRE_SEL] = imx_clk_mux("lcdif_pre_sel", base + 0x38, 15, 3, lcdif_pre_sels, ARRAY_SIZE(lcdif_pre_sels));
clks[IMX6UL_CLK_LCDIF_SEL] = imx_clk_mux("lcdif_sel", base + 0x38, 9, 3, lcdif_sels, ARRAY_SIZE(lcdif_sels));
@ -276,6 +298,10 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_SAI3_PODF] = imx_clk_divider("sai3_podf", "sai3_pred", base + 0x28, 16, 6);
clks[IMX6UL_CLK_SAI1_PRED] = imx_clk_divider("sai1_pred", "sai1_sel", base + 0x28, 6, 3);
clks[IMX6UL_CLK_SAI1_PODF] = imx_clk_divider("sai1_podf", "sai1_pred", base + 0x28, 0, 6);
if (clk_on_imx6ull()) {
clks[IMX6ULL_CLK_ESAI_PRED] = imx_clk_divider("esai_pred", "esai_sel", base + 0x28, 9, 3);
clks[IMX6ULL_CLK_ESAI_PODF] = imx_clk_divider("esai_podf", "esai_pred", base + 0x28, 25, 3);
}
clks[IMX6UL_CLK_ENFC_PRED] = imx_clk_divider("enfc_pred", "enfc_sel", base + 0x2c, 18, 3);
clks[IMX6UL_CLK_ENFC_PODF] = imx_clk_divider("enfc_podf", "enfc_pred", base + 0x2c, 21, 6);
clks[IMX6UL_CLK_SAI2_PRED] = imx_clk_divider("sai2_pred", "sai2_sel", base + 0x2c, 6, 3);
@ -298,9 +324,15 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_APBHDMA] = imx_clk_gate2("apbh_dma", "bch_podf", base + 0x68, 4);
clks[IMX6UL_CLK_ASRC_IPG] = imx_clk_gate2_shared("asrc_ipg", "ahb", base + 0x68, 6, &share_count_asrc);
clks[IMX6UL_CLK_ASRC_MEM] = imx_clk_gate2_shared("asrc_mem", "ahb", base + 0x68, 6, &share_count_asrc);
clks[IMX6UL_CLK_CAAM_MEM] = imx_clk_gate2("caam_mem", "ahb", base + 0x68, 8);
clks[IMX6UL_CLK_CAAM_ACLK] = imx_clk_gate2("caam_aclk", "ahb", base + 0x68, 10);
clks[IMX6UL_CLK_CAAM_IPG] = imx_clk_gate2("caam_ipg", "ipg", base + 0x68, 12);
if (clk_on_imx6ul()) {
clks[IMX6UL_CLK_CAAM_MEM] = imx_clk_gate2("caam_mem", "ahb", base + 0x68, 8);
clks[IMX6UL_CLK_CAAM_ACLK] = imx_clk_gate2("caam_aclk", "ahb", base + 0x68, 10);
clks[IMX6UL_CLK_CAAM_IPG] = imx_clk_gate2("caam_ipg", "ipg", base + 0x68, 12);
} else if (clk_on_imx6ull()) {
clks[IMX6ULL_CLK_DCP_CLK] = imx_clk_gate2("dcp", "ahb", base + 0x68, 10);
clks[IMX6UL_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x68, 12);
clks[IMX6UL_CLK_ENET_AHB] = imx_clk_gate2("enet_ahb", "ahb", base + 0x68, 12);
}
clks[IMX6UL_CLK_CAN1_IPG] = imx_clk_gate2("can1_ipg", "ipg", base + 0x68, 14);
clks[IMX6UL_CLK_CAN1_SERIAL] = imx_clk_gate2("can1_serial", "can_podf", base + 0x68, 16);
clks[IMX6UL_CLK_CAN2_IPG] = imx_clk_gate2("can2_ipg", "ipg", base + 0x68, 18);
@ -309,7 +341,10 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_GPT2_SERIAL] = imx_clk_gate2("gpt2_serial", "perclk", base + 0x68, 26);
clks[IMX6UL_CLK_UART2_IPG] = imx_clk_gate2("uart2_ipg", "ipg", base + 0x68, 28);
clks[IMX6UL_CLK_UART2_SERIAL] = imx_clk_gate2("uart2_serial", "uart_podf", base + 0x68, 28);
clks[IMX6UL_CLK_AIPSTZ3] = imx_clk_gate2("aips_tz3", "ahb", base + 0x68, 30);
if (clk_on_imx6ul())
clks[IMX6UL_CLK_AIPSTZ3] = imx_clk_gate2("aips_tz3", "ahb", base + 0x68, 30);
else if (clk_on_imx6ull())
clks[IMX6UL_CLK_AIPSTZ3] = imx_clk_gate2("aips_tz3", "ahb", base + 0x80, 18);
/* CCGR1 */
clks[IMX6UL_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_podf", base + 0x6c, 0);
@ -328,6 +363,11 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_UART4_SERIAL] = imx_clk_gate2("uart4_serail", "uart_podf", base + 0x6c, 24);
/* CCGR2 */
if (clk_on_imx6ull()) {
clks[IMX6ULL_CLK_ESAI_EXTAL] = imx_clk_gate2_shared("esai_extal", "esai_podf", base + 0x70, 0, &share_count_esai);
clks[IMX6ULL_CLK_ESAI_IPG] = imx_clk_gate2_shared("esai_ipg", "ahb", base + 0x70, 0, &share_count_esai);
clks[IMX6ULL_CLK_ESAI_MEM] = imx_clk_gate2_shared("esai_mem", "ahb", base + 0x70, 0, &share_count_esai);
}
clks[IMX6UL_CLK_CSI] = imx_clk_gate2("csi", "csi_podf", base + 0x70, 2);
clks[IMX6UL_CLK_I2C1] = imx_clk_gate2("i2c1", "perclk", base + 0x70, 6);
clks[IMX6UL_CLK_I2C2] = imx_clk_gate2("i2c2", "perclk", base + 0x70, 8);
@ -340,8 +380,13 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
/* CCGR3 */
clks[IMX6UL_CLK_UART5_IPG] = imx_clk_gate2("uart5_ipg", "ipg", base + 0x74, 2);
clks[IMX6UL_CLK_UART5_SERIAL] = imx_clk_gate2("uart5_serial", "uart_podf", base + 0x74, 2);
clks[IMX6UL_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x74, 4);
clks[IMX6UL_CLK_ENET_AHB] = imx_clk_gate2("enet_ahb", "ahb", base + 0x74, 4);
if (clk_on_imx6ul()) {
clks[IMX6UL_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x74, 4);
clks[IMX6UL_CLK_ENET_AHB] = imx_clk_gate2("enet_ahb", "ahb", base + 0x74, 4);
} else if (clk_on_imx6ull()) {
clks[IMX6ULL_CLK_EPDC_ACLK] = imx_clk_gate2("epdc_aclk", "axi", base + 0x74, 4);
clks[IMX6ULL_CLK_EPDC_PIX] = imx_clk_gate2("epdc_pix", "epdc_podf", base + 0x74, 4);
}
clks[IMX6UL_CLK_UART6_IPG] = imx_clk_gate2("uart6_ipg", "ipg", base + 0x74, 6);
clks[IMX6UL_CLK_UART6_SERIAL] = imx_clk_gate2("uart6_serial", "uart_podf", base + 0x74, 6);
clks[IMX6UL_CLK_LCDIF_PIX] = imx_clk_gate2("lcdif_pix", "lcdif_podf", base + 0x74, 10);
@ -385,8 +430,10 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clks[IMX6UL_CLK_USBOH3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0);
clks[IMX6UL_CLK_USDHC1] = imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2);
clks[IMX6UL_CLK_USDHC2] = imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4);
clks[IMX6UL_CLK_SIM1] = imx_clk_gate2("sim1", "sim_sel", base + 0x80, 6);
clks[IMX6UL_CLK_SIM2] = imx_clk_gate2("sim2", "sim_sel", base + 0x80, 8);
if (clk_on_imx6ul()) {
clks[IMX6UL_CLK_SIM1] = imx_clk_gate2("sim1", "sim_sel", base + 0x80, 6);
clks[IMX6UL_CLK_SIM2] = imx_clk_gate2("sim2", "sim_sel", base + 0x80, 8);
}
clks[IMX6UL_CLK_EIM] = imx_clk_gate2("eim", "eim_slow_podf", base + 0x80, 10);
clks[IMX6UL_CLK_PWM8] = imx_clk_gate2("pwm8", "perclk", base + 0x80, 16);
clks[IMX6UL_CLK_UART8_IPG] = imx_clk_gate2("uart8_ipg", "ipg", base + 0x80, 14);
@ -441,7 +488,10 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
}
clk_set_parent(clks[IMX6UL_CLK_CAN_SEL], clks[IMX6UL_CLK_PLL3_60M]);
clk_set_parent(clks[IMX6UL_CLK_SIM_PRE_SEL], clks[IMX6UL_CLK_PLL3_USB_OTG]);
if (clk_on_imx6ul())
clk_set_parent(clks[IMX6UL_CLK_SIM_PRE_SEL], clks[IMX6UL_CLK_PLL3_USB_OTG]);
else if (clk_on_imx6ull())
clk_set_parent(clks[IMX6ULL_CLK_EPDC_PRE_SEL], clks[IMX6UL_CLK_PLL3_PFD2]);
clk_set_parent(clks[IMX6UL_CLK_ENFC_SEL], clks[IMX6UL_CLK_PLL2_PFD2]);
}

View File

@ -234,6 +234,7 @@ static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long max_rate = parent_rate * 54;
u32 div;
u32 mfn, mfd = 1000000;
u32 max_mfd = 0x3FFFFFFF;
u64 temp64;
if (rate > max_rate)
@ -241,6 +242,9 @@ static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate,
else if (rate < min_rate)
rate = min_rate;
if (parent_rate <= max_mfd)
mfd = parent_rate;
div = rate / parent_rate;
temp64 = (u64) (rate - div * parent_rate);
temp64 *= mfd;
@ -262,11 +266,15 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long max_rate = parent_rate * 54;
u32 val, div;
u32 mfn, mfd = 1000000;
u32 max_mfd = 0x3FFFFFFF;
u64 temp64;
if (rate < min_rate || rate > max_rate)
return -EINVAL;
if (parent_rate <= max_mfd)
mfd = parent_rate;
div = rate / parent_rate;
temp64 = (u64) (rate - div * parent_rate);
temp64 *= mfd;

View File

@ -75,6 +75,14 @@ static inline struct clk *imx_clk_fixed(const char *name, int rate)
return clk_register_fixed_rate(NULL, name, NULL, 0, rate);
}
static inline struct clk *imx_clk_mux_ldb(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parents, int num_parents)
{
return clk_register_mux(NULL, name, parents, num_parents,
CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, reg,
shift, width, CLK_MUX_READ_ONLY, &imx_ccm_lock);
}
static inline struct clk *imx_clk_fixed_factor(const char *name,
const char *parent, unsigned int mult, unsigned int div)
{

View File

@ -154,7 +154,7 @@ static struct clk *clk_register_pll(struct device *dev,
}
/**
* _of_clk_init - PLL initialisation via DT
* _of_pll_clk_init - PLL initialisation via DT
* @node: device tree node for this clock
* @pllctrl: If true, lower 6 bits of multiplier is in pllm register of
* pll controller, else it is in the control register0(bit 11-6)
@ -235,7 +235,7 @@ CLK_OF_DECLARE(keystone_pll_clock, "ti,keystone,pll-clock",
of_keystone_pll_clk_init);
/**
* of_keystone_pll_main_clk_init - Main PLL initialisation DT wrapper
* of_keystone_main_pll_clk_init - Main PLL initialisation DT wrapper
* @node: device tree node for this clock
*/
static void __init of_keystone_main_pll_clk_init(struct device_node *node)
@ -267,25 +267,30 @@ static void __init of_pll_div_clk_init(struct device_node *node)
parent_name = of_clk_get_parent_name(node, 0);
if (!parent_name) {
pr_err("%s: missing parent clock\n", __func__);
iounmap(reg);
return;
}
if (of_property_read_u32(node, "bit-shift", &shift)) {
pr_err("%s: missing 'shift' property\n", __func__);
iounmap(reg);
return;
}
if (of_property_read_u32(node, "bit-mask", &mask)) {
pr_err("%s: missing 'bit-mask' property\n", __func__);
iounmap(reg);
return;
}
clk = clk_register_divider(NULL, clk_name, parent_name, 0, reg, shift,
mask, 0, NULL);
if (clk)
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
else
} else {
pr_err("%s: error registering divider %s\n", __func__, clk_name);
iounmap(reg);
}
}
CLK_OF_DECLARE(pll_divider_clock, "ti,keystone,pll-divider-clock", of_pll_div_clk_init);

View File

@ -6,6 +6,49 @@ config COMMON_CLK_MEDIATEK
---help---
Mediatek SoCs' clock support.
config COMMON_CLK_MT2701
bool "Clock driver for Mediatek MT2701"
select COMMON_CLK_MEDIATEK
default ARCH_MEDIATEK
---help---
This driver supports Mediatek MT2701 basic clocks.
config COMMON_CLK_MT2701_MMSYS
bool "Clock driver for Mediatek MT2701 mmsys"
select COMMON_CLK_MT2701
---help---
This driver supports Mediatek MT2701 mmsys clocks.
config COMMON_CLK_MT2701_IMGSYS
bool "Clock driver for Mediatek MT2701 imgsys"
select COMMON_CLK_MT2701
---help---
This driver supports Mediatek MT2701 imgsys clocks.
config COMMON_CLK_MT2701_VDECSYS
bool "Clock driver for Mediatek MT2701 vdecsys"
select COMMON_CLK_MT2701
---help---
This driver supports Mediatek MT2701 vdecsys clocks.
config COMMON_CLK_MT2701_HIFSYS
bool "Clock driver for Mediatek MT2701 hifsys"
select COMMON_CLK_MT2701
---help---
This driver supports Mediatek MT2701 hifsys clocks.
config COMMON_CLK_MT2701_ETHSYS
bool "Clock driver for Mediatek MT2701 ethsys"
select COMMON_CLK_MT2701
---help---
This driver supports Mediatek MT2701 ethsys clocks.
config COMMON_CLK_MT2701_BDPSYS
bool "Clock driver for Mediatek MT2701 bdpsys"
select COMMON_CLK_MT2701
---help---
This driver supports Mediatek MT2701 bdpsys clocks.
config COMMON_CLK_MT8135
bool "Clock driver for Mediatek MT8135"
depends on ARCH_MEDIATEK || COMPILE_TEST

View File

@ -1,4 +1,11 @@
obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o
obj-$(CONFIG_RESET_CONTROLLER) += reset.o
obj-$(CONFIG_COMMON_CLK_MT2701) += clk-mt2701.o
obj-$(CONFIG_COMMON_CLK_MT2701_BDPSYS) += clk-mt2701-bdp.o
obj-$(CONFIG_COMMON_CLK_MT2701_ETHSYS) += clk-mt2701-eth.o
obj-$(CONFIG_COMMON_CLK_MT2701_HIFSYS) += clk-mt2701-hif.o
obj-$(CONFIG_COMMON_CLK_MT2701_IMGSYS) += clk-mt2701-img.o
obj-$(CONFIG_COMMON_CLK_MT2701_MMSYS) += clk-mt2701-mm.o
obj-$(CONFIG_COMMON_CLK_MT2701_VDECSYS) += clk-mt2701-vdec.o
obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o

View File

@ -61,6 +61,22 @@ static void mtk_cg_clr_bit(struct clk_hw *hw)
regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit));
}
static void mtk_cg_set_bit_no_setclr(struct clk_hw *hw)
{
struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
u32 cgbit = BIT(cg->bit);
regmap_update_bits(cg->regmap, cg->sta_ofs, cgbit, cgbit);
}
static void mtk_cg_clr_bit_no_setclr(struct clk_hw *hw)
{
struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
u32 cgbit = BIT(cg->bit);
regmap_update_bits(cg->regmap, cg->sta_ofs, cgbit, 0);
}
static int mtk_cg_enable(struct clk_hw *hw)
{
mtk_cg_clr_bit(hw);
@ -85,6 +101,30 @@ static void mtk_cg_disable_inv(struct clk_hw *hw)
mtk_cg_clr_bit(hw);
}
static int mtk_cg_enable_no_setclr(struct clk_hw *hw)
{
mtk_cg_clr_bit_no_setclr(hw);
return 0;
}
static void mtk_cg_disable_no_setclr(struct clk_hw *hw)
{
mtk_cg_set_bit_no_setclr(hw);
}
static int mtk_cg_enable_inv_no_setclr(struct clk_hw *hw)
{
mtk_cg_set_bit_no_setclr(hw);
return 0;
}
static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw)
{
mtk_cg_clr_bit_no_setclr(hw);
}
const struct clk_ops mtk_clk_gate_ops_setclr = {
.is_enabled = mtk_cg_bit_is_cleared,
.enable = mtk_cg_enable,
@ -97,6 +137,18 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
.disable = mtk_cg_disable_inv,
};
const struct clk_ops mtk_clk_gate_ops_no_setclr = {
.is_enabled = mtk_cg_bit_is_cleared,
.enable = mtk_cg_enable_no_setclr,
.disable = mtk_cg_disable_no_setclr,
};
const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = {
.is_enabled = mtk_cg_bit_is_set,
.enable = mtk_cg_enable_inv_no_setclr,
.disable = mtk_cg_disable_inv_no_setclr,
};
struct clk *mtk_clk_register_gate(
const char *name,
const char *parent_name,

View File

@ -36,6 +36,8 @@ static inline struct mtk_clk_gate *to_mtk_clk_gate(struct clk_hw *hw)
extern const struct clk_ops mtk_clk_gate_ops_setclr;
extern const struct clk_ops mtk_clk_gate_ops_setclr_inv;
extern const struct clk_ops mtk_clk_gate_ops_no_setclr;
extern const struct clk_ops mtk_clk_gate_ops_no_setclr_inv;
struct clk *mtk_clk_register_gate(
const char *name,

View File

@ -0,0 +1,138 @@
/*
* Copyright (c) 2014 MediaTek Inc.
* Author: Shunli Wang <shunli.wang@mediatek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
#include "clk-gate.h"
#include <dt-bindings/clock/mt2701-clk.h>
static const struct mtk_gate_regs bdp0_cg_regs = {
.set_ofs = 0x0104,
.clr_ofs = 0x0108,
.sta_ofs = 0x0100,
};
static const struct mtk_gate_regs bdp1_cg_regs = {
.set_ofs = 0x0114,
.clr_ofs = 0x0118,
.sta_ofs = 0x0110,
};
#define GATE_BDP0(_id, _name, _parent, _shift) { \
.id = _id, \
.name = _name, \
.parent_name = _parent, \
.regs = &bdp0_cg_regs, \
.shift = _shift, \
.ops = &mtk_clk_gate_ops_setclr_inv, \
}
#define GATE_BDP1(_id, _name, _parent, _shift) { \
.id = _id, \
.name = _name, \
.parent_name = _parent, \
.regs = &bdp1_cg_regs, \
.shift = _shift, \
.ops = &mtk_clk_gate_ops_setclr_inv, \
}
static const struct mtk_gate bdp_clks[] = {
GATE_BDP0(CLK_BDP_BRG_BA, "brg_baclk", "mm_sel", 0),
GATE_BDP0(CLK_BDP_BRG_DRAM, "brg_dram", "mm_sel", 1),
GATE_BDP0(CLK_BDP_LARB_DRAM, "larb_dram", "mm_sel", 2),
GATE_BDP0(CLK_BDP_WR_VDI_PXL, "wr_vdi_pxl", "hdmi_0_deep340m", 3),
GATE_BDP0(CLK_BDP_WR_VDI_DRAM, "wr_vdi_dram", "mm_sel", 4),
GATE_BDP0(CLK_BDP_WR_B, "wr_bclk", "mm_sel", 5),
GATE_BDP0(CLK_BDP_DGI_IN, "dgi_in", "dpi1_sel", 6),
GATE_BDP0(CLK_BDP_DGI_OUT, "dgi_out", "dpi1_sel", 7),
GATE_BDP0(CLK_BDP_FMT_MAST_27, "fmt_mast_27", "dpi1_sel", 8),
GATE_BDP0(CLK_BDP_FMT_B, "fmt_bclk", "mm_sel", 9),
GATE_BDP0(CLK_BDP_OSD_B, "osd_bclk", "mm_sel", 10),
GATE_BDP0(CLK_BDP_OSD_DRAM, "osd_dram", "mm_sel", 11),
GATE_BDP0(CLK_BDP_OSD_AGENT, "osd_agent", "osd_sel", 12),
GATE_BDP0(CLK_BDP_OSD_PXL, "osd_pxl", "dpi1_sel", 13),
GATE_BDP0(CLK_BDP_RLE_B, "rle_bclk", "mm_sel", 14),
GATE_BDP0(CLK_BDP_RLE_AGENT, "rle_agent", "mm_sel", 15),
GATE_BDP0(CLK_BDP_RLE_DRAM, "rle_dram", "mm_sel", 16),
GATE_BDP0(CLK_BDP_F27M, "f27m", "di_sel", 17),
GATE_BDP0(CLK_BDP_F27M_VDOUT, "f27m_vdout", "di_sel", 18),
GATE_BDP0(CLK_BDP_F27_74_74, "f27_74_74", "di_sel", 19),
GATE_BDP0(CLK_BDP_F2FS, "f2fs", "di_sel", 20),
GATE_BDP0(CLK_BDP_F2FS74_148, "f2fs74_148", "di_sel", 21),
GATE_BDP0(CLK_BDP_FB, "fbclk", "mm_sel", 22),
GATE_BDP0(CLK_BDP_VDO_DRAM, "vdo_dram", "mm_sel", 23),
GATE_BDP0(CLK_BDP_VDO_2FS, "vdo_2fs", "di_sel", 24),
GATE_BDP0(CLK_BDP_VDO_B, "vdo_bclk", "mm_sel", 25),
GATE_BDP0(CLK_BDP_WR_DI_PXL, "wr_di_pxl", "di_sel", 26),
GATE_BDP0(CLK_BDP_WR_DI_DRAM, "wr_di_dram", "mm_sel", 27),
GATE_BDP0(CLK_BDP_WR_DI_B, "wr_di_bclk", "mm_sel", 28),
GATE_BDP0(CLK_BDP_NR_PXL, "nr_pxl", "nr_sel", 29),
GATE_BDP0(CLK_BDP_NR_DRAM, "nr_dram", "mm_sel", 30),
GATE_BDP0(CLK_BDP_NR_B, "nr_bclk", "mm_sel", 31),
GATE_BDP1(CLK_BDP_RX_F, "rx_fclk", "hadds2_fbclk", 0),
GATE_BDP1(CLK_BDP_RX_X, "rx_xclk", "clk26m", 1),
GATE_BDP1(CLK_BDP_RXPDT, "rxpdtclk", "hdmi_0_pix340m", 2),
GATE_BDP1(CLK_BDP_RX_CSCL_N, "rx_cscl_n", "clk26m", 3),
GATE_BDP1(CLK_BDP_RX_CSCL, "rx_cscl", "clk26m", 4),
GATE_BDP1(CLK_BDP_RX_DDCSCL_N, "rx_ddcscl_n", "hdmi_scl_rx", 5),
GATE_BDP1(CLK_BDP_RX_DDCSCL, "rx_ddcscl", "hdmi_scl_rx", 6),
GATE_BDP1(CLK_BDP_RX_VCO, "rx_vcoclk", "hadds2pll_294m", 7),
GATE_BDP1(CLK_BDP_RX_DP, "rx_dpclk", "hdmi_0_pll340m", 8),
GATE_BDP1(CLK_BDP_RX_P, "rx_pclk", "hdmi_0_pll340m", 9),
GATE_BDP1(CLK_BDP_RX_M, "rx_mclk", "hadds2pll_294m", 10),
GATE_BDP1(CLK_BDP_RX_PLL, "rx_pllclk", "hdmi_0_pix340m", 11),
GATE_BDP1(CLK_BDP_BRG_RT_B, "brg_rt_bclk", "mm_sel", 12),
GATE_BDP1(CLK_BDP_BRG_RT_DRAM, "brg_rt_dram", "mm_sel", 13),
GATE_BDP1(CLK_BDP_LARBRT_DRAM, "larbrt_dram", "mm_sel", 14),
GATE_BDP1(CLK_BDP_TMDS_SYN, "tmds_syn", "hdmi_0_pll340m", 15),
GATE_BDP1(CLK_BDP_HDMI_MON, "hdmi_mon", "hdmi_0_pll340m", 16),
};
static const struct of_device_id of_match_clk_mt2701_bdp[] = {
{ .compatible = "mediatek,mt2701-bdpsys", },
{}
};
static int clk_mt2701_bdp_probe(struct platform_device *pdev)
{
struct clk_onecell_data *clk_data;
int r;
struct device_node *node = pdev->dev.of_node;
clk_data = mtk_alloc_clk_data(CLK_BDP_NR);
mtk_clk_register_gates(node, bdp_clks, ARRAY_SIZE(bdp_clks),
clk_data);
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
if (r)
dev_err(&pdev->dev,
"could not register clock provider: %s: %d\n",
pdev->name, r);
return r;
}
static struct platform_driver clk_mt2701_bdp_drv = {
.probe = clk_mt2701_bdp_probe,
.driver = {
.name = "clk-mt2701-bdp",
.of_match_table = of_match_clk_mt2701_bdp,
},
};
builtin_platform_driver(clk_mt2701_bdp_drv);

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2014 MediaTek Inc.
* Author: Shunli Wang <shunli.wang@mediatek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
#include "clk-gate.h"
#include <dt-bindings/clock/mt2701-clk.h>
static const struct mtk_gate_regs eth_cg_regs = {
.sta_ofs = 0x0030,
};
#define GATE_ETH(_id, _name, _parent, _shift) { \
.id = _id, \
.name = _name, \
.parent_name = _parent, \
.regs = &eth_cg_regs, \
.shift = _shift, \
.ops = &mtk_clk_gate_ops_no_setclr_inv, \
}
static const struct mtk_gate eth_clks[] = {
GATE_ETH(CLK_ETHSYS_HSDMA, "hsdma_clk", "ethif_sel", 5),
GATE_ETH(CLK_ETHSYS_ESW, "esw_clk", "ethpll_500m_ck", 6),
GATE_ETH(CLK_ETHSYS_GP2, "gp2_clk", "trgpll", 7),
GATE_ETH(CLK_ETHSYS_GP1, "gp1_clk", "ethpll_500m_ck", 8),
GATE_ETH(CLK_ETHSYS_PCM, "pcm_clk", "ethif_sel", 11),
GATE_ETH(CLK_ETHSYS_GDMA, "gdma_clk", "ethif_sel", 14),
GATE_ETH(CLK_ETHSYS_I2S, "i2s_clk", "ethif_sel", 17),
GATE_ETH(CLK_ETHSYS_CRYPTO, "crypto_clk", "ethif_sel", 29),
};
static const struct of_device_id of_match_clk_mt2701_eth[] = {
{ .compatible = "mediatek,mt2701-ethsys", },
{}
};
static int clk_mt2701_eth_probe(struct platform_device *pdev)
{
struct clk_onecell_data *clk_data;
int r;
struct device_node *node = pdev->dev.of_node;
clk_data = mtk_alloc_clk_data(CLK_ETHSYS_NR);
mtk_clk_register_gates(node, eth_clks, ARRAY_SIZE(eth_clks),
clk_data);
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
if (r)
dev_err(&pdev->dev,
"could not register clock provider: %s: %d\n",
pdev->name, r);
return r;
}
static struct platform_driver clk_mt2701_eth_drv = {
.probe = clk_mt2701_eth_probe,
.driver = {
.name = "clk-mt2701-eth",
.of_match_table = of_match_clk_mt2701_eth,
},
};
builtin_platform_driver(clk_mt2701_eth_drv);

View File

@ -0,0 +1,81 @@
/*
* Copyright (c) 2014 MediaTek Inc.
* Author: Shunli Wang <shunli.wang@mediatek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
#include "clk-gate.h"
#include <dt-bindings/clock/mt2701-clk.h>
static const struct mtk_gate_regs hif_cg_regs = {
.sta_ofs = 0x0030,
};
#define GATE_HIF(_id, _name, _parent, _shift) { \
.id = _id, \
.name = _name, \
.parent_name = _parent, \
.regs = &hif_cg_regs, \
.shift = _shift, \
.ops = &mtk_clk_gate_ops_no_setclr_inv, \
}
static const struct mtk_gate hif_clks[] = {
GATE_HIF(CLK_HIFSYS_USB0PHY, "usb0_phy_clk", "ethpll_500m_ck", 21),
GATE_HIF(CLK_HIFSYS_USB1PHY, "usb1_phy_clk", "ethpll_500m_ck", 22),
GATE_HIF(CLK_HIFSYS_PCIE0, "pcie0_clk", "ethpll_500m_ck", 24),
GATE_HIF(CLK_HIFSYS_PCIE1, "pcie1_clk", "ethpll_500m_ck", 25),
GATE_HIF(CLK_HIFSYS_PCIE2, "pcie2_clk", "ethpll_500m_ck", 26),
};
static const struct of_device_id of_match_clk_mt2701_hif[] = {
{ .compatible = "mediatek,mt2701-hifsys", },
{}
};
static int clk_mt2701_hif_probe(struct platform_device *pdev)
{
struct clk_onecell_data *clk_data;
int r;
struct device_node *node = pdev->dev.of_node;
clk_data = mtk_alloc_clk_data(CLK_HIFSYS_NR);
mtk_clk_register_gates(node, hif_clks, ARRAY_SIZE(hif_clks),
clk_data);
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
if (r) {
dev_err(&pdev->dev,
"could not register clock provider: %s: %d\n",
pdev->name, r);
return r;
}
mtk_register_reset_controller(node, 1, 0x34);
return 0;
}
static struct platform_driver clk_mt2701_hif_drv = {
.probe = clk_mt2701_hif_probe,
.driver = {
.name = "clk-mt2701-hif",
.of_match_table = of_match_clk_mt2701_hif,
},
};
builtin_platform_driver(clk_mt2701_hif_drv);

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2014 MediaTek Inc.
* Author: Shunli Wang <shunli.wang@mediatek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
#include "clk-gate.h"
#include <dt-bindings/clock/mt2701-clk.h>
static const struct mtk_gate_regs img_cg_regs = {
.set_ofs = 0x0004,
.clr_ofs = 0x0008,
.sta_ofs = 0x0000,
};
#define GATE_IMG(_id, _name, _parent, _shift) { \
.id = _id, \
.name = _name, \
.parent_name = _parent, \
.regs = &img_cg_regs, \
.shift = _shift, \
.ops = &mtk_clk_gate_ops_setclr, \
}
static const struct mtk_gate img_clks[] = {
GATE_IMG(CLK_IMG_SMI_COMM, "img_smi_comm", "mm_sel", 0),
GATE_IMG(CLK_IMG_RESZ, "img_resz", "mm_sel", 1),
GATE_IMG(CLK_IMG_JPGDEC_SMI, "img_jpgdec_smi", "mm_sel", 5),
GATE_IMG(CLK_IMG_JPGDEC, "img_jpgdec", "mm_sel", 6),
GATE_IMG(CLK_IMG_VENC_LT, "img_venc_lt", "mm_sel", 8),
GATE_IMG(CLK_IMG_VENC, "img_venc", "mm_sel", 9),
};
static const struct of_device_id of_match_clk_mt2701_img[] = {
{ .compatible = "mediatek,mt2701-imgsys", },
{}
};
static int clk_mt2701_img_probe(struct platform_device *pdev)
{
struct clk_onecell_data *clk_data;
int r;
struct device_node *node = pdev->dev.of_node;
clk_data = mtk_alloc_clk_data(CLK_IMG_NR);
mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks),
clk_data);
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
if (r)
dev_err(&pdev->dev,
"could not register clock provider: %s: %d\n",
pdev->name, r);
return r;
}
static struct platform_driver clk_mt2701_img_drv = {
.probe = clk_mt2701_img_probe,
.driver = {
.name = "clk-mt2701-img",
.of_match_table = of_match_clk_mt2701_img,
},
};
builtin_platform_driver(clk_mt2701_img_drv);

View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2014 MediaTek Inc.
* Author: Shunli Wang <shunli.wang@mediatek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
#include "clk-gate.h"
#include <dt-bindings/clock/mt2701-clk.h>
static const struct mtk_gate_regs disp0_cg_regs = {
.set_ofs = 0x0104,
.clr_ofs = 0x0108,
.sta_ofs = 0x0100,
};
static const struct mtk_gate_regs disp1_cg_regs = {
.set_ofs = 0x0114,
.clr_ofs = 0x0118,
.sta_ofs = 0x0110,
};
#define GATE_DISP0(_id, _name, _parent, _shift) { \
.id = _id, \
.name = _name, \
.parent_name = _parent, \
.regs = &disp0_cg_regs, \
.shift = _shift, \
.ops = &mtk_clk_gate_ops_setclr, \
}
#define GATE_DISP1(_id, _name, _parent, _shift) { \
.id = _id, \
.name = _name, \
.parent_name = _parent, \
.regs = &disp1_cg_regs, \
.shift = _shift, \
.ops = &mtk_clk_gate_ops_setclr, \
}
static const struct mtk_gate mm_clks[] = {
GATE_DISP0(CLK_MM_SMI_COMMON, "mm_smi_comm", "mm_sel", 0),
GATE_DISP0(CLK_MM_SMI_LARB0, "mm_smi_larb0", "mm_sel", 1),
GATE_DISP0(CLK_MM_CMDQ, "mm_cmdq", "mm_sel", 2),
GATE_DISP0(CLK_MM_MUTEX, "mm_mutex", "mm_sel", 3),
GATE_DISP0(CLK_MM_DISP_COLOR, "mm_disp_color", "mm_sel", 4),
GATE_DISP0(CLK_MM_DISP_BLS, "mm_disp_bls", "mm_sel", 5),
GATE_DISP0(CLK_MM_DISP_WDMA, "mm_disp_wdma", "mm_sel", 6),
GATE_DISP0(CLK_MM_DISP_RDMA, "mm_disp_rdma", "mm_sel", 7),
GATE_DISP0(CLK_MM_DISP_OVL, "mm_disp_ovl", "mm_sel", 8),
GATE_DISP0(CLK_MM_MDP_TDSHP, "mm_mdp_tdshp", "mm_sel", 9),
GATE_DISP0(CLK_MM_MDP_WROT, "mm_mdp_wrot", "mm_sel", 10),
GATE_DISP0(CLK_MM_MDP_WDMA, "mm_mdp_wdma", "mm_sel", 11),
GATE_DISP0(CLK_MM_MDP_RSZ1, "mm_mdp_rsz1", "mm_sel", 12),
GATE_DISP0(CLK_MM_MDP_RSZ0, "mm_mdp_rsz0", "mm_sel", 13),
GATE_DISP0(CLK_MM_MDP_RDMA, "mm_mdp_rdma", "mm_sel", 14),
GATE_DISP0(CLK_MM_MDP_BLS_26M, "mm_mdp_bls_26m", "pwm_sel", 15),
GATE_DISP0(CLK_MM_CAM_MDP, "mm_cam_mdp", "mm_sel", 16),
GATE_DISP0(CLK_MM_FAKE_ENG, "mm_fake_eng", "mm_sel", 17),
GATE_DISP0(CLK_MM_MUTEX_32K, "mm_mutex_32k", "rtc_sel", 18),
GATE_DISP0(CLK_MM_DISP_RDMA1, "mm_disp_rdma1", "mm_sel", 19),
GATE_DISP0(CLK_MM_DISP_UFOE, "mm_disp_ufoe", "mm_sel", 20),
GATE_DISP1(CLK_MM_DSI_ENGINE, "mm_dsi_eng", "mm_sel", 0),
GATE_DISP1(CLK_MM_DSI_DIG, "mm_dsi_dig", "dsi0_lntc_dsi", 1),
GATE_DISP1(CLK_MM_DPI_DIGL, "mm_dpi_digl", "dpi0_sel", 2),
GATE_DISP1(CLK_MM_DPI_ENGINE, "mm_dpi_eng", "mm_sel", 3),
GATE_DISP1(CLK_MM_DPI1_DIGL, "mm_dpi1_digl", "dpi1_sel", 4),
GATE_DISP1(CLK_MM_DPI1_ENGINE, "mm_dpi1_eng", "mm_sel", 5),
GATE_DISP1(CLK_MM_TVE_OUTPUT, "mm_tve_output", "tve_sel", 6),
GATE_DISP1(CLK_MM_TVE_INPUT, "mm_tve_input", "dpi0_sel", 7),
GATE_DISP1(CLK_MM_HDMI_PIXEL, "mm_hdmi_pixel", "dpi1_sel", 8),
GATE_DISP1(CLK_MM_HDMI_PLL, "mm_hdmi_pll", "hdmi_sel", 9),
GATE_DISP1(CLK_MM_HDMI_AUDIO, "mm_hdmi_audio", "apll_sel", 10),
GATE_DISP1(CLK_MM_HDMI_SPDIF, "mm_hdmi_spdif", "apll_sel", 11),
GATE_DISP1(CLK_MM_TVE_FMM, "mm_tve_fmm", "mm_sel", 14),
};
static const struct of_device_id of_match_clk_mt2701_mm[] = {
{ .compatible = "mediatek,mt2701-mmsys", },
{}
};
static int clk_mt2701_mm_probe(struct platform_device *pdev)
{
struct clk_onecell_data *clk_data;
int r;
struct device_node *node = pdev->dev.of_node;
clk_data = mtk_alloc_clk_data(CLK_MM_NR);
mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks),
clk_data);
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
if (r)
dev_err(&pdev->dev,
"could not register clock provider: %s: %d\n",
pdev->name, r);
return r;
}
static struct platform_driver clk_mt2701_mm_drv = {
.probe = clk_mt2701_mm_probe,
.driver = {
.name = "clk-mt2701-mm",
.of_match_table = of_match_clk_mt2701_mm,
},
};
builtin_platform_driver(clk_mt2701_mm_drv);

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2014 MediaTek Inc.
* Author: Shunli Wang <shunli.wang@mediatek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include "clk-mtk.h"
#include "clk-gate.h"
#include <dt-bindings/clock/mt2701-clk.h>
static const struct mtk_gate_regs vdec0_cg_regs = {
.set_ofs = 0x0000,
.clr_ofs = 0x0004,
.sta_ofs = 0x0000,
};
static const struct mtk_gate_regs vdec1_cg_regs = {
.set_ofs = 0x0008,
.clr_ofs = 0x000c,
.sta_ofs = 0x0008,
};
#define GATE_VDEC0(_id, _name, _parent, _shift) { \
.id = _id, \
.name = _name, \
.parent_name = _parent, \
.regs = &vdec0_cg_regs, \
.shift = _shift, \
.ops = &mtk_clk_gate_ops_setclr_inv, \
}
#define GATE_VDEC1(_id, _name, _parent, _shift) { \
.id = _id, \
.name = _name, \
.parent_name = _parent, \
.regs = &vdec1_cg_regs, \
.shift = _shift, \
.ops = &mtk_clk_gate_ops_setclr_inv, \
}
static const struct mtk_gate vdec_clks[] = {
GATE_VDEC0(CLK_VDEC_CKGEN, "vdec_cken", "vdec_sel", 0),
GATE_VDEC1(CLK_VDEC_LARB, "vdec_larb_cken", "mm_sel", 0),
};
static const struct of_device_id of_match_clk_mt2701_vdec[] = {
{ .compatible = "mediatek,mt2701-vdecsys", },
{}
};
static int clk_mt2701_vdec_probe(struct platform_device *pdev)
{
struct clk_onecell_data *clk_data;
int r;
struct device_node *node = pdev->dev.of_node;
clk_data = mtk_alloc_clk_data(CLK_VDEC_NR);
mtk_clk_register_gates(node, vdec_clks, ARRAY_SIZE(vdec_clks),
clk_data);
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
if (r)
dev_err(&pdev->dev,
"could not register clock provider: %s: %d\n",
pdev->name, r);
return r;
}
static struct platform_driver clk_mt2701_vdec_drv = {
.probe = clk_mt2701_vdec_probe,
.driver = {
.name = "clk-mt2701-vdec",
.of_match_table = of_match_clk_mt2701_vdec,
},
};
builtin_platform_driver(clk_mt2701_vdec_drv);

File diff suppressed because it is too large Load Diff

View File

@ -58,6 +58,9 @@ void mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks,
for (i = 0; i < num; i++) {
const struct mtk_fixed_clk *rc = &clks[i];
if (clk_data && !IS_ERR_OR_NULL(clk_data->clks[rc->id]))
continue;
clk = clk_register_fixed_rate(NULL, rc->name, rc->parent, 0,
rc->rate);
@ -81,6 +84,9 @@ void mtk_clk_register_factors(const struct mtk_fixed_factor *clks,
for (i = 0; i < num; i++) {
const struct mtk_fixed_factor *ff = &clks[i];
if (clk_data && !IS_ERR_OR_NULL(clk_data->clks[ff->id]))
continue;
clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name,
CLK_SET_RATE_PARENT, ff->mult, ff->div);
@ -116,6 +122,9 @@ int mtk_clk_register_gates(struct device_node *node,
for (i = 0; i < num; i++) {
const struct mtk_gate *gate = &clks[i];
if (!IS_ERR_OR_NULL(clk_data->clks[gate->id]))
continue;
clk = mtk_clk_register_gate(gate->name, gate->parent_name,
regmap,
gate->regs->set_ofs,
@ -232,6 +241,9 @@ void mtk_clk_register_composites(const struct mtk_composite *mcs,
for (i = 0; i < num; i++) {
const struct mtk_composite *mc = &mcs[i];
if (clk_data && !IS_ERR_OR_NULL(clk_data->clks[mc->id]))
continue;
clk = mtk_clk_register_composite(mc, base, lock);
if (IS_ERR(clk)) {
@ -244,3 +256,31 @@ void mtk_clk_register_composites(const struct mtk_composite *mcs,
clk_data->clks[mc->id] = clk;
}
}
void mtk_clk_register_dividers(const struct mtk_clk_divider *mcds,
int num, void __iomem *base, spinlock_t *lock,
struct clk_onecell_data *clk_data)
{
struct clk *clk;
int i;
for (i = 0; i < num; i++) {
const struct mtk_clk_divider *mcd = &mcds[i];
if (clk_data && !IS_ERR_OR_NULL(clk_data->clks[mcd->id]))
continue;
clk = clk_register_divider(NULL, mcd->name, mcd->parent_name,
mcd->flags, base + mcd->div_reg, mcd->div_shift,
mcd->div_width, mcd->clk_divider_flags, lock);
if (IS_ERR(clk)) {
pr_err("Failed to register clk %s: %ld\n",
mcd->name, PTR_ERR(clk));
continue;
}
if (clk_data)
clk_data->clks[mcd->id] = clk;
}
}

View File

@ -87,7 +87,8 @@ struct mtk_composite {
* In case the rate change propagation to parent clocks is undesirable,
* this macro allows to specify the clock flags manually.
*/
#define MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, _gate, _flags) { \
#define MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, \
_gate, _flags) { \
.id = _id, \
.name = _name, \
.mux_reg = _reg, \
@ -106,7 +107,8 @@ struct mtk_composite {
* parent clock by default.
*/
#define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) \
MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, _gate, CLK_SET_RATE_PARENT)
MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, \
_gate, CLK_SET_RATE_PARENT)
#define MUX(_id, _name, _parents, _reg, _shift, _width) { \
.id = _id, \
@ -121,7 +123,8 @@ struct mtk_composite {
.flags = CLK_SET_RATE_PARENT, \
}
#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) { \
#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, \
_div_width, _div_shift) { \
.id = _id, \
.parent = _parent, \
.name = _name, \
@ -156,12 +159,40 @@ struct mtk_gate {
const struct clk_ops *ops;
};
int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks,
int num, struct clk_onecell_data *clk_data);
int mtk_clk_register_gates(struct device_node *node,
const struct mtk_gate *clks, int num,
struct clk_onecell_data *clk_data);
struct mtk_clk_divider {
int id;
const char *name;
const char *parent_name;
unsigned long flags;
u32 div_reg;
unsigned char div_shift;
unsigned char div_width;
unsigned char clk_divider_flags;
const struct clk_div_table *clk_div_table;
};
#define DIV_ADJ(_id, _name, _parent, _reg, _shift, _width) { \
.id = _id, \
.name = _name, \
.parent_name = _parent, \
.div_reg = _reg, \
.div_shift = _shift, \
.div_width = _width, \
}
void mtk_clk_register_dividers(const struct mtk_clk_divider *mcds,
int num, void __iomem *base, spinlock_t *lock,
struct clk_onecell_data *clk_data);
struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num);
#define HAVE_RST_BAR BIT(0)
#define PLL_AO BIT(1)
struct mtk_pll_div_table {
u32 div;

View File

@ -301,6 +301,7 @@ static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data,
pll->data = data;
init.name = data->name;
init.flags = (data->flags & PLL_AO) ? CLK_IS_CRITICAL : 0;
init.ops = &mtk_pll_ops;
init.parent_names = &parent_name;
init.num_parents = 1;

View File

@ -309,19 +309,19 @@ static void __init mmp2_clk_init(struct device_node *np)
pxa_unit->mpmu_base = of_iomap(np, 0);
if (!pxa_unit->mpmu_base) {
pr_err("failed to map mpmu registers\n");
return;
goto free_memory;
}
pxa_unit->apmu_base = of_iomap(np, 1);
if (!pxa_unit->apmu_base) {
pr_err("failed to map apmu registers\n");
return;
goto unmap_mpmu_region;
}
pxa_unit->apbc_base = of_iomap(np, 2);
if (!pxa_unit->apbc_base) {
pr_err("failed to map apbc registers\n");
return;
goto unmap_apmu_region;
}
mmp_clk_init(np, &pxa_unit->unit, MMP2_NR_CLKS);
@ -333,6 +333,15 @@ static void __init mmp2_clk_init(struct device_node *np)
mmp2_axi_periph_clk_init(pxa_unit);
mmp2_clk_reset_init(np, pxa_unit);
return;
unmap_apmu_region:
iounmap(pxa_unit->apmu_base);
unmap_mpmu_region:
iounmap(pxa_unit->mpmu_base);
free_memory:
kfree(pxa_unit);
}
CLK_OF_DECLARE(mmp2_clk, "marvell,mmp2-clock", mmp2_clk_init);

View File

@ -216,6 +216,7 @@ static void __init pxa1928_mpmu_clk_init(struct device_node *np)
pxa_unit->mpmu_base = of_iomap(np, 0);
if (!pxa_unit->mpmu_base) {
pr_err("failed to map mpmu registers\n");
kfree(pxa_unit);
return;
}
@ -234,6 +235,7 @@ static void __init pxa1928_apmu_clk_init(struct device_node *np)
pxa_unit->apmu_base = of_iomap(np, 0);
if (!pxa_unit->apmu_base) {
pr_err("failed to map apmu registers\n");
kfree(pxa_unit);
return;
}
@ -254,6 +256,7 @@ static void __init pxa1928_apbc_clk_init(struct device_node *np)
pxa_unit->apbc_base = of_iomap(np, 0);
if (!pxa_unit->apbc_base) {
pr_err("failed to map apbc registers\n");
kfree(pxa_unit);
return;
}

View File

@ -278,25 +278,25 @@ static void __init pxa910_clk_init(struct device_node *np)
pxa_unit->mpmu_base = of_iomap(np, 0);
if (!pxa_unit->mpmu_base) {
pr_err("failed to map mpmu registers\n");
return;
goto free_memory;
}
pxa_unit->apmu_base = of_iomap(np, 1);
if (!pxa_unit->apmu_base) {
pr_err("failed to map apmu registers\n");
return;
goto unmap_mpmu_region;
}
pxa_unit->apbc_base = of_iomap(np, 2);
if (!pxa_unit->apbc_base) {
pr_err("failed to map apbc registers\n");
return;
goto unmap_apmu_region;
}
pxa_unit->apbcp_base = of_iomap(np, 3);
if (!pxa_unit->apbcp_base) {
pr_err("failed to map apbcp registers\n");
return;
goto unmap_apbc_region;
}
mmp_clk_init(np, &pxa_unit->unit, PXA910_NR_CLKS);
@ -308,6 +308,17 @@ static void __init pxa910_clk_init(struct device_node *np)
pxa910_axi_periph_clk_init(pxa_unit);
pxa910_clk_reset_init(np, pxa_unit);
return;
unmap_apbc_region:
iounmap(pxa_unit->apbc_base);
unmap_apmu_region:
iounmap(pxa_unit->apmu_base);
unmap_mpmu_region:
iounmap(pxa_unit->mpmu_base);
free_memory:
kfree(pxa_unit);
}
CLK_OF_DECLARE(pxa910_clk, "marvell,pxa910-clock", pxa910_clk_init);

View File

@ -14,7 +14,7 @@
#include <linux/clk-provider.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
@ -135,34 +135,17 @@ static int ap806_syscon_clk_probe(struct platform_device *pdev)
return ret;
}
static int ap806_syscon_clk_remove(struct platform_device *pdev)
{
of_clk_del_provider(pdev->dev.of_node);
clk_unregister_fixed_factor(ap806_clks[3]);
clk_unregister_fixed_rate(ap806_clks[2]);
clk_unregister_fixed_rate(ap806_clks[1]);
clk_unregister_fixed_rate(ap806_clks[0]);
return 0;
}
static const struct of_device_id ap806_syscon_of_match[] = {
{ .compatible = "marvell,ap806-system-controller", },
{ }
};
MODULE_DEVICE_TABLE(of, armada8k_pcie_of_match);
static struct platform_driver ap806_syscon_driver = {
.probe = ap806_syscon_clk_probe,
.remove = ap806_syscon_clk_remove,
.driver = {
.name = "marvell-ap806-system-controller",
.of_match_table = ap806_syscon_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(ap806_syscon_driver);
MODULE_DESCRIPTION("Marvell AP806 System Controller driver");
MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
MODULE_LICENSE("GPL");
builtin_platform_driver(ap806_syscon_driver);

View File

@ -30,7 +30,7 @@
#include <linux/clk-provider.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
@ -87,7 +87,7 @@ struct cp110_gate_clk {
u8 bit_idx;
};
#define to_cp110_gate_clk(clk) container_of(clk, struct cp110_gate_clk, hw)
#define to_cp110_gate_clk(hw) container_of(hw, struct cp110_gate_clk, hw)
static int cp110_gate_enable(struct clk_hw *hw)
{
@ -123,13 +123,14 @@ static const struct clk_ops cp110_gate_ops = {
.is_enabled = cp110_gate_is_enabled,
};
static struct clk *cp110_register_gate(const char *name,
const char *parent_name,
struct regmap *regmap, u8 bit_idx)
static struct clk_hw *cp110_register_gate(const char *name,
const char *parent_name,
struct regmap *regmap, u8 bit_idx)
{
struct cp110_gate_clk *gate;
struct clk *clk;
struct clk_hw *hw;
struct clk_init_data init;
int ret;
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate)
@ -146,39 +147,37 @@ static struct clk *cp110_register_gate(const char *name,
gate->bit_idx = bit_idx;
gate->hw.init = &init;
clk = clk_register(NULL, &gate->hw);
if (IS_ERR(clk))
hw = &gate->hw;
ret = clk_hw_register(NULL, hw);
if (ret) {
kfree(gate);
hw = ERR_PTR(ret);
}
return clk;
return hw;
}
static void cp110_unregister_gate(struct clk *clk)
static void cp110_unregister_gate(struct clk_hw *hw)
{
struct clk_hw *hw;
hw = __clk_get_hw(clk);
if (!hw)
return;
clk_unregister(clk);
clk_hw_unregister(hw);
kfree(to_cp110_gate_clk(hw));
}
static struct clk *cp110_of_clk_get(struct of_phandle_args *clkspec, void *data)
static struct clk_hw *cp110_of_clk_get(struct of_phandle_args *clkspec,
void *data)
{
struct clk_onecell_data *clk_data = data;
struct clk_hw_onecell_data *clk_data = data;
unsigned int type = clkspec->args[0];
unsigned int idx = clkspec->args[1];
if (type == CP110_CLK_TYPE_CORE) {
if (idx > CP110_MAX_CORE_CLOCKS)
return ERR_PTR(-EINVAL);
return clk_data->clks[idx];
return clk_data->hws[idx];
} else if (type == CP110_CLK_TYPE_GATABLE) {
if (idx > CP110_MAX_GATABLE_CLOCKS)
return ERR_PTR(-EINVAL);
return clk_data->clks[CP110_MAX_CORE_CLOCKS + idx];
return clk_data->hws[CP110_MAX_CORE_CLOCKS + idx];
}
return ERR_PTR(-EINVAL);
@ -189,8 +188,8 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev)
struct regmap *regmap;
struct device_node *np = pdev->dev.of_node;
const char *ppv2_name, *apll_name, *core_name, *eip_name, *nand_name;
struct clk_onecell_data *cp110_clk_data;
struct clk *clk, **cp110_clks;
struct clk_hw_onecell_data *cp110_clk_data;
struct clk_hw *hw, **cp110_clks;
u32 nand_clk_ctrl;
int i, ret;
@ -203,80 +202,75 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev)
if (ret)
return ret;
cp110_clks = devm_kcalloc(&pdev->dev, sizeof(struct clk *),
CP110_CLK_NUM, GFP_KERNEL);
if (!cp110_clks)
return -ENOMEM;
cp110_clk_data = devm_kzalloc(&pdev->dev,
sizeof(*cp110_clk_data),
cp110_clk_data = devm_kzalloc(&pdev->dev, sizeof(*cp110_clk_data) +
sizeof(struct clk_hw *) * CP110_CLK_NUM,
GFP_KERNEL);
if (!cp110_clk_data)
return -ENOMEM;
cp110_clk_data->clks = cp110_clks;
cp110_clk_data->clk_num = CP110_CLK_NUM;
cp110_clks = cp110_clk_data->hws;
cp110_clk_data->num = CP110_CLK_NUM;
/* Register the APLL which is the root of the clk tree */
/* Register the APLL which is the root of the hw tree */
of_property_read_string_index(np, "core-clock-output-names",
CP110_CORE_APLL, &apll_name);
clk = clk_register_fixed_rate(NULL, apll_name, NULL, 0,
1000 * 1000 * 1000);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
hw = clk_hw_register_fixed_rate(NULL, apll_name, NULL, 0,
1000 * 1000 * 1000);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto fail0;
}
cp110_clks[CP110_CORE_APLL] = clk;
cp110_clks[CP110_CORE_APLL] = hw;
/* PPv2 is APLL/3 */
of_property_read_string_index(np, "core-clock-output-names",
CP110_CORE_PPV2, &ppv2_name);
clk = clk_register_fixed_factor(NULL, ppv2_name, apll_name, 0, 1, 3);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
hw = clk_hw_register_fixed_factor(NULL, ppv2_name, apll_name, 0, 1, 3);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto fail1;
}
cp110_clks[CP110_CORE_PPV2] = clk;
cp110_clks[CP110_CORE_PPV2] = hw;
/* EIP clock is APLL/2 */
of_property_read_string_index(np, "core-clock-output-names",
CP110_CORE_EIP, &eip_name);
clk = clk_register_fixed_factor(NULL, eip_name, apll_name, 0, 1, 2);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
hw = clk_hw_register_fixed_factor(NULL, eip_name, apll_name, 0, 1, 2);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto fail2;
}
cp110_clks[CP110_CORE_EIP] = clk;
cp110_clks[CP110_CORE_EIP] = hw;
/* Core clock is EIP/2 */
of_property_read_string_index(np, "core-clock-output-names",
CP110_CORE_CORE, &core_name);
clk = clk_register_fixed_factor(NULL, core_name, eip_name, 0, 1, 2);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
hw = clk_hw_register_fixed_factor(NULL, core_name, eip_name, 0, 1, 2);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto fail3;
}
cp110_clks[CP110_CORE_CORE] = clk;
cp110_clks[CP110_CORE_CORE] = hw;
/* NAND can be either APLL/2.5 or core clock */
of_property_read_string_index(np, "core-clock-output-names",
CP110_CORE_NAND, &nand_name);
if (nand_clk_ctrl & NF_CLOCK_SEL_400_MASK)
clk = clk_register_fixed_factor(NULL, nand_name,
apll_name, 0, 2, 5);
hw = clk_hw_register_fixed_factor(NULL, nand_name,
apll_name, 0, 2, 5);
else
clk = clk_register_fixed_factor(NULL, nand_name,
core_name, 0, 1, 1);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
hw = clk_hw_register_fixed_factor(NULL, nand_name,
core_name, 0, 1, 1);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto fail4;
}
cp110_clks[CP110_CORE_NAND] = clk;
cp110_clks[CP110_CORE_NAND] = hw;
for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) {
const char *parent, *name;
@ -335,16 +329,16 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev)
break;
}
clk = cp110_register_gate(name, parent, regmap, i);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
hw = cp110_register_gate(name, parent, regmap, i);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto fail_gate;
}
cp110_clks[CP110_MAX_CORE_CLOCKS + i] = clk;
cp110_clks[CP110_MAX_CORE_CLOCKS + i] = hw;
}
ret = of_clk_add_provider(np, cp110_of_clk_get, cp110_clk_data);
ret = of_clk_add_hw_provider(np, cp110_of_clk_get, cp110_clk_data);
if (ret)
goto fail_clk_add;
@ -355,65 +349,36 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev)
fail_clk_add:
fail_gate:
for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) {
clk = cp110_clks[CP110_MAX_CORE_CLOCKS + i];
hw = cp110_clks[CP110_MAX_CORE_CLOCKS + i];
if (clk)
cp110_unregister_gate(clk);
if (hw)
cp110_unregister_gate(hw);
}
clk_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]);
clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]);
fail4:
clk_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]);
clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]);
fail3:
clk_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]);
clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]);
fail2:
clk_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]);
clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]);
fail1:
clk_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]);
clk_hw_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]);
fail0:
return ret;
}
static int cp110_syscon_clk_remove(struct platform_device *pdev)
{
struct clk **cp110_clks = platform_get_drvdata(pdev);
int i;
of_clk_del_provider(pdev->dev.of_node);
for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) {
struct clk *clk = cp110_clks[CP110_MAX_CORE_CLOCKS + i];
if (clk)
cp110_unregister_gate(clk);
}
clk_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]);
clk_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]);
clk_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]);
clk_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]);
clk_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]);
return 0;
}
static const struct of_device_id cp110_syscon_of_match[] = {
{ .compatible = "marvell,cp110-system-controller0", },
{ }
};
MODULE_DEVICE_TABLE(of, armada8k_pcie_of_match);
static struct platform_driver cp110_syscon_driver = {
.probe = cp110_syscon_clk_probe,
.remove = cp110_syscon_clk_remove,
.driver = {
.name = "marvell-cp110-system-controller0",
.of_match_table = cp110_syscon_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(cp110_syscon_driver);
MODULE_DESCRIPTION("Marvell CP110 System Controller 0 driver");
MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
MODULE_LICENSE("GPL");
builtin_platform_driver(cp110_syscon_driver);

View File

@ -277,12 +277,15 @@ static void __init lpc18xx_ccu_init(struct device_node *np)
}
clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
if (!clk_data) {
iounmap(reg_base);
return;
}
clk_data->num = of_property_count_strings(np, "clock-names");
clk_data->name = kcalloc(clk_data->num, sizeof(char *), GFP_KERNEL);
if (!clk_data->name) {
iounmap(reg_base);
kfree(clk_data);
return;
}

View File

@ -1282,13 +1282,13 @@ static struct clk_hw_proto clk_hw_proto[LPC32XX_CLK_HW_MAX] = {
LPC32XX_DEFINE_MUX(PWM1_MUX, PWMCLK_CTRL, 1, 0x1, NULL, 0),
LPC32XX_DEFINE_DIV(PWM1_DIV, PWMCLK_CTRL, 4, 4, NULL,
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO),
CLK_DIVIDER_ONE_BASED),
LPC32XX_DEFINE_GATE(PWM1_GATE, PWMCLK_CTRL, 0, 0),
LPC32XX_DEFINE_COMPOSITE(PWM1, PWM1_MUX, PWM1_DIV, PWM1_GATE),
LPC32XX_DEFINE_MUX(PWM2_MUX, PWMCLK_CTRL, 3, 0x1, NULL, 0),
LPC32XX_DEFINE_DIV(PWM2_DIV, PWMCLK_CTRL, 8, 4, NULL,
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO),
CLK_DIVIDER_ONE_BASED),
LPC32XX_DEFINE_GATE(PWM2_GATE, PWMCLK_CTRL, 2, 0),
LPC32XX_DEFINE_COMPOSITE(PWM2, PWM2_MUX, PWM2_DIV, PWM2_GATE),
@ -1335,8 +1335,7 @@ static struct clk_hw_proto clk_hw_proto[LPC32XX_CLK_HW_MAX] = {
LPC32XX_DEFINE_GATE(USB_DIV_GATE, USB_CTRL, 17, 0),
LPC32XX_DEFINE_COMPOSITE(USB_DIV, _NULL, USB_DIV_DIV, USB_DIV_GATE),
LPC32XX_DEFINE_DIV(SD_DIV, MS_CTRL, 0, 4, NULL,
CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO),
LPC32XX_DEFINE_DIV(SD_DIV, MS_CTRL, 0, 4, NULL, CLK_DIVIDER_ONE_BASED),
LPC32XX_DEFINE_CLK(SD_GATE, MS_CTRL, BIT(5) | BIT(9), BIT(5) | BIT(9),
0x0, BIT(5) | BIT(9), 0x0, 0x0, clk_mask_ops),
LPC32XX_DEFINE_COMPOSITE(SD, _NULL, SD_DIV, SD_GATE),
@ -1478,6 +1477,20 @@ static struct clk * __init lpc32xx_clk_register(u32 id)
return clk;
}
static void __init lpc32xx_clk_div_quirk(u32 reg, u32 div_mask, u32 gate)
{
u32 val;
regmap_read(clk_regmap, reg, &val);
if (!(val & div_mask)) {
val &= ~gate;
val |= BIT(__ffs(div_mask));
}
regmap_update_bits(clk_regmap, reg, gate | div_mask, val);
}
static void __init lpc32xx_clk_init(struct device_node *np)
{
unsigned int i;
@ -1517,6 +1530,17 @@ static void __init lpc32xx_clk_init(struct device_node *np)
return;
}
/*
* Divider part of PWM and MS clocks requires a quirk to avoid
* a misinterpretation of formally valid zero value in register
* bitfield, which indicates another clock gate. Instead of
* adding complexity to a gate clock ensure that zero value in
* divider clock is never met in runtime.
*/
lpc32xx_clk_div_quirk(LPC32XX_CLKPWR_PWMCLK_CTRL, 0xf0, BIT(0));
lpc32xx_clk_div_quirk(LPC32XX_CLKPWR_PWMCLK_CTRL, 0xf00, BIT(2));
lpc32xx_clk_div_quirk(LPC32XX_CLKPWR_MS_CTRL, 0xf, BIT(5) | BIT(9));
for (i = 1; i < LPC32XX_CLK_MAX; i++) {
clk[i] = lpc32xx_clk_register(i);
if (IS_ERR(clk[i])) {

View File

@ -18,7 +18,27 @@
#include <dt-bindings/clock/pxa-clock.h>
#include "clk-pxa.h"
DEFINE_SPINLOCK(lock);
#define KHz 1000
#define MHz (1000 * 1000)
#define MDREFR_K0DB4 (1 << 29) /* SDCLK0 Divide by 4 Control/Status */
#define MDREFR_K2FREE (1 << 25) /* SDRAM Free-Running Control */
#define MDREFR_K1FREE (1 << 24) /* SDRAM Free-Running Control */
#define MDREFR_K0FREE (1 << 23) /* SDRAM Free-Running Control */
#define MDREFR_SLFRSH (1 << 22) /* SDRAM Self-Refresh Control/Status */
#define MDREFR_APD (1 << 20) /* SDRAM/SSRAM Auto-Power-Down Enable */
#define MDREFR_K2DB2 (1 << 19) /* SDCLK2 Divide by 2 Control/Status */
#define MDREFR_K2RUN (1 << 18) /* SDCLK2 Run Control/Status */
#define MDREFR_K1DB2 (1 << 17) /* SDCLK1 Divide by 2 Control/Status */
#define MDREFR_K1RUN (1 << 16) /* SDCLK1 Run Control/Status */
#define MDREFR_E1PIN (1 << 15) /* SDCKE1 Level Control/Status */
#define MDREFR_K0DB2 (1 << 14) /* SDCLK0 Divide by 2 Control/Status */
#define MDREFR_K0RUN (1 << 13) /* SDCLK0 Run Control/Status */
#define MDREFR_E0PIN (1 << 12) /* SDCKE0 Level Control/Status */
#define MDREFR_DB2_MASK (MDREFR_K2DB2 | MDREFR_K1DB2)
#define MDREFR_DRI_MASK 0xFFF
static DEFINE_SPINLOCK(pxa_clk_lock);
static struct clk *pxa_clocks[CLK_MAX];
static struct clk_onecell_data onecell_data = {
@ -89,7 +109,7 @@ int __init clk_pxa_cken_init(const struct desc_clk_cken *clks, int nb_clks)
pxa_clk->lp = clks[i].lp;
pxa_clk->hp = clks[i].hp;
pxa_clk->gate = clks[i].gate;
pxa_clk->gate.lock = &lock;
pxa_clk->gate.lock = &pxa_clk_lock;
clk = clk_register_composite(NULL, clks[i].name,
clks[i].parent_names, 2,
&pxa_clk->hw, &cken_mux_ops,
@ -106,3 +126,124 @@ void __init clk_pxa_dt_common_init(struct device_node *np)
{
of_clk_add_provider(np, of_clk_src_onecell_get, &onecell_data);
}
void pxa2xx_core_turbo_switch(bool on)
{
unsigned long flags;
unsigned int unused, clkcfg;
local_irq_save(flags);
asm("mrc p14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
clkcfg &= ~CLKCFG_TURBO & ~CLKCFG_HALFTURBO;
if (on)
clkcfg |= CLKCFG_TURBO;
clkcfg |= CLKCFG_FCS;
asm volatile(
" b 2f\n"
" .align 5\n"
"1: mcr p14, 0, %1, c6, c0, 0\n"
" b 3f\n"
"2: b 1b\n"
"3: nop\n"
: "=&r" (unused)
: "r" (clkcfg)
: );
local_irq_restore(flags);
}
void pxa2xx_cpll_change(struct pxa2xx_freq *freq,
u32 (*mdrefr_dri)(unsigned int), void __iomem *mdrefr,
void __iomem *cccr)
{
unsigned int clkcfg = freq->clkcfg;
unsigned int unused, preset_mdrefr, postset_mdrefr;
unsigned long flags;
local_irq_save(flags);
/* Calculate the next MDREFR. If we're slowing down the SDRAM clock
* we need to preset the smaller DRI before the change. If we're
* speeding up we need to set the larger DRI value after the change.
*/
preset_mdrefr = postset_mdrefr = readl(mdrefr);
if ((preset_mdrefr & MDREFR_DRI_MASK) > mdrefr_dri(freq->membus_khz)) {
preset_mdrefr = (preset_mdrefr & ~MDREFR_DRI_MASK);
preset_mdrefr |= mdrefr_dri(freq->membus_khz);
}
postset_mdrefr =
(postset_mdrefr & ~MDREFR_DRI_MASK) |
mdrefr_dri(freq->membus_khz);
/* If we're dividing the memory clock by two for the SDRAM clock, this
* must be set prior to the change. Clearing the divide must be done
* after the change.
*/
if (freq->div2) {
preset_mdrefr |= MDREFR_DB2_MASK;
postset_mdrefr |= MDREFR_DB2_MASK;
} else {
postset_mdrefr &= ~MDREFR_DB2_MASK;
}
/* Set new the CCCR and prepare CLKCFG */
writel(freq->cccr, cccr);
asm volatile(
" ldr r4, [%1]\n"
" b 2f\n"
" .align 5\n"
"1: str %3, [%1] /* preset the MDREFR */\n"
" mcr p14, 0, %2, c6, c0, 0 /* set CLKCFG[FCS] */\n"
" str %4, [%1] /* postset the MDREFR */\n"
" b 3f\n"
"2: b 1b\n"
"3: nop\n"
: "=&r" (unused)
: "r" (mdrefr), "r" (clkcfg), "r" (preset_mdrefr),
"r" (postset_mdrefr)
: "r4", "r5");
local_irq_restore(flags);
}
int pxa2xx_determine_rate(struct clk_rate_request *req,
struct pxa2xx_freq *freqs, int nb_freqs)
{
int i, closest_below = -1, closest_above = -1;
unsigned long rate;
for (i = 0; i < nb_freqs; i++) {
rate = freqs[i].cpll;
if (rate == req->rate)
break;
if (rate < req->min_rate)
continue;
if (rate > req->max_rate)
continue;
if (rate <= req->rate)
closest_below = i;
if ((rate >= req->rate) && (closest_above == -1))
closest_above = i;
}
req->best_parent_hw = NULL;
if (i < nb_freqs) {
rate = req->rate;
} else if (closest_below >= 0) {
rate = freqs[closest_below].cpll;
} else if (closest_above >= 0) {
rate = freqs[closest_above].cpll;
} else {
pr_debug("%s(rate=%lu) no match\n", __func__, req->rate);
return -EINVAL;
}
pr_debug("%s(rate=%lu) rate=%lu\n", __func__, req->rate, rate);
req->rate = rate;
return 0;
}

View File

@ -13,6 +13,11 @@
#ifndef _CLK_PXA_
#define _CLK_PXA_
#define CLKCFG_TURBO 0x1
#define CLKCFG_FCS 0x2
#define CLKCFG_HALFTURBO 0x4
#define CLKCFG_FASTBUS 0x8
#define PARENTS(name) \
static const char *const name ## _parents[] __initconst
#define MUX_RO_RATE_RO_OPS(name, clk_name) \
@ -35,9 +40,9 @@
NULL, NULL, CLK_GET_RATE_NOCACHE); \
}
#define RATE_RO_OPS(name, clk_name) \
#define RATE_RO_OPS(name, clk_name) \
static struct clk_hw name ## _rate_hw; \
static struct clk_ops name ## _rate_ops = { \
static const struct clk_ops name ## _rate_ops = { \
.recalc_rate = name ## _get_rate, \
}; \
static struct clk * __init clk_register_ ## name(void) \
@ -50,6 +55,41 @@
NULL, NULL, CLK_GET_RATE_NOCACHE); \
}
#define RATE_OPS(name, clk_name) \
static struct clk_hw name ## _rate_hw; \
static struct clk_ops name ## _rate_ops = { \
.recalc_rate = name ## _get_rate, \
.set_rate = name ## _set_rate, \
.determine_rate = name ## _determine_rate, \
}; \
static struct clk * __init clk_register_ ## name(void) \
{ \
return clk_register_composite(NULL, clk_name, \
name ## _parents, \
ARRAY_SIZE(name ## _parents), \
NULL, NULL, \
&name ## _rate_hw, &name ## _rate_ops, \
NULL, NULL, CLK_GET_RATE_NOCACHE); \
}
#define MUX_OPS(name, clk_name, flags) \
static struct clk_hw name ## _mux_hw; \
static const struct clk_ops name ## _mux_ops = { \
.get_parent = name ## _get_parent, \
.set_parent = name ## _set_parent, \
.determine_rate = name ## _determine_rate, \
}; \
static struct clk * __init clk_register_ ## name(void) \
{ \
return clk_register_composite(NULL, clk_name, \
name ## _parents, \
ARRAY_SIZE(name ## _parents), \
&name ## _mux_hw, &name ## _mux_ops, \
NULL, NULL, \
NULL, NULL, \
CLK_GET_RATE_NOCACHE | flags); \
}
/*
* CKEN clock type
* This clock takes it source from 2 possible parents :
@ -95,7 +135,15 @@ struct desc_clk_cken {
PXA_CKEN(dev_id, con_id, name, parents, 1, 1, 1, 1, \
NULL, cken_reg, cken_bit, flag)
static int dummy_clk_set_parent(struct clk_hw *hw, u8 index)
struct pxa2xx_freq {
unsigned long cpll;
unsigned int membus_khz;
unsigned int cccr;
unsigned int div2;
unsigned int clkcfg;
};
static inline int dummy_clk_set_parent(struct clk_hw *hw, u8 index)
{
return 0;
}
@ -105,4 +153,11 @@ extern void clkdev_pxa_register(int ckid, const char *con_id,
extern int clk_pxa_cken_init(const struct desc_clk_cken *clks, int nb_clks);
void clk_pxa_dt_common_init(struct device_node *np);
void pxa2xx_core_turbo_switch(bool on);
void pxa2xx_cpll_change(struct pxa2xx_freq *freq,
u32 (*mdrefr_dri)(unsigned int), void __iomem *mdrefr,
void __iomem *cccr);
int pxa2xx_determine_rate(struct clk_rate_request *req,
struct pxa2xx_freq *freqs, int nb_freqs);
#endif

View File

@ -18,6 +18,7 @@
#include <linux/io.h>
#include <linux/of.h>
#include <mach/pxa2xx-regs.h>
#include <mach/smemc.h>
#include <dt-bindings/clock/pxa-clock.h>
#include "clk-pxa.h"
@ -30,6 +31,17 @@ enum {
PXA_CORE_TURBO,
};
#define PXA25x_CLKCFG(T) \
(CLKCFG_FCS | \
((T) ? CLKCFG_TURBO : 0))
#define PXA25x_CCCR(N2, M, L) (N2 << 7 | M << 5 | L)
#define MDCNFG_DRAC2(mdcnfg) (((mdcnfg) >> 21) & 0x3)
#define MDCNFG_DRAC0(mdcnfg) (((mdcnfg) >> 5) & 0x3)
/* Define the refresh period in mSec for the SDRAM and the number of rows */
#define SDRAM_TREF 64 /* standard 64ms SDRAM */
/*
* Various clock factors driven by the CCCR register.
*/
@ -48,6 +60,34 @@ static const char * const get_freq_khz[] = {
"core", "run", "cpll", "memory"
};
static int get_sdram_rows(void)
{
static int sdram_rows;
unsigned int drac2 = 0, drac0 = 0;
u32 mdcnfg;
if (sdram_rows)
return sdram_rows;
mdcnfg = readl_relaxed(MDCNFG);
if (mdcnfg & (MDCNFG_DE2 | MDCNFG_DE3))
drac2 = MDCNFG_DRAC2(mdcnfg);
if (mdcnfg & (MDCNFG_DE0 | MDCNFG_DE1))
drac0 = MDCNFG_DRAC0(mdcnfg);
sdram_rows = 1 << (11 + max(drac0, drac2));
return sdram_rows;
}
static u32 mdrefr_dri(unsigned int freq_khz)
{
u32 interval = freq_khz * SDRAM_TREF / get_sdram_rows();
return interval / 32;
}
/*
* Get the clock frequency as reflected by CCCR and the turbo flag.
* We assume these values have been applied via a fcs.
@ -139,6 +179,21 @@ static struct desc_clk_cken pxa25x_clocks[] __initdata = {
clk_pxa25x_memory_parents, 0),
};
/*
* In this table, PXA25x_CCCR(N2, M, L) has the following meaning, where :
* - freq_cpll = n * m * L * 3.6864 MHz
* - n = N2 / 2
* - m = 2^(M - 1), where 1 <= M <= 3
* - l = L_clk_mult[L], ie. { 0, 27, 32, 36, 40, 45, 0, }[L]
*/
static struct pxa2xx_freq pxa25x_freqs[] = {
/* CPU MEMBUS CCCR DIV2 CCLKCFG */
{ 99532800, 99500, PXA25x_CCCR(2, 1, 1), 1, PXA25x_CLKCFG(1)},
{199065600, 99500, PXA25x_CCCR(4, 1, 1), 0, PXA25x_CLKCFG(1)},
{298598400, 99500, PXA25x_CCCR(3, 2, 1), 0, PXA25x_CLKCFG(1)},
{398131200, 99500, PXA25x_CCCR(4, 2, 1), 0, PXA25x_CLKCFG(1)},
};
static u8 clk_pxa25x_core_get_parent(struct clk_hw *hw)
{
unsigned long clkcfg;
@ -151,13 +206,24 @@ static u8 clk_pxa25x_core_get_parent(struct clk_hw *hw)
return PXA_CORE_RUN;
}
static unsigned long clk_pxa25x_core_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
static int clk_pxa25x_core_set_parent(struct clk_hw *hw, u8 index)
{
return parent_rate;
if (index > PXA_CORE_TURBO)
return -EINVAL;
pxa2xx_core_turbo_switch(index == PXA_CORE_TURBO);
return 0;
}
static int clk_pxa25x_core_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
return __clk_mux_determine_rate(hw, req);
}
PARENTS(clk_pxa25x_core) = { "run", "cpll" };
MUX_RO_RATE_RO_OPS(clk_pxa25x_core, "core");
MUX_OPS(clk_pxa25x_core, "core", CLK_SET_RATE_PARENT);
static unsigned long clk_pxa25x_run_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
@ -182,17 +248,42 @@ static unsigned long clk_pxa25x_cpll_get_rate(struct clk_hw *hw,
m = M_clk_mult[(cccr >> 5) & 0x03];
n2 = N2_clk_mult[(cccr >> 7) & 0x07];
if (t)
return m * l * n2 * parent_rate / 2;
return m * l * parent_rate;
return m * l * n2 * parent_rate / 2;
}
static int clk_pxa25x_cpll_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
return pxa2xx_determine_rate(req, pxa25x_freqs,
ARRAY_SIZE(pxa25x_freqs));
}
static int clk_pxa25x_cpll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
int i;
pr_debug("%s(rate=%lu parent_rate=%lu)\n", __func__, rate, parent_rate);
for (i = 0; i < ARRAY_SIZE(pxa25x_freqs); i++)
if (pxa25x_freqs[i].cpll == rate)
break;
if (i >= ARRAY_SIZE(pxa25x_freqs))
return -EINVAL;
pxa2xx_cpll_change(&pxa25x_freqs[i], mdrefr_dri, MDREFR, CCCR);
return 0;
}
PARENTS(clk_pxa25x_cpll) = { "osc_3_6864mhz" };
RATE_RO_OPS(clk_pxa25x_cpll, "cpll");
RATE_OPS(clk_pxa25x_cpll, "cpll");
static void __init pxa25x_register_core(void)
{
clk_register_clk_pxa25x_cpll();
clk_register_clk_pxa25x_run();
clkdev_pxa_register(CLK_NONE, "cpll", NULL,
clk_register_clk_pxa25x_cpll());
clkdev_pxa_register(CLK_NONE, "run", NULL,
clk_register_clk_pxa25x_run());
clkdev_pxa_register(CLK_CORE, "core", NULL,
clk_register_clk_pxa25x_core());
}
@ -214,7 +305,8 @@ static void __init pxa25x_base_clocks_init(void)
{
pxa25x_register_plls();
pxa25x_register_core();
clk_register_clk_pxa25x_memory();
clkdev_pxa_register(CLK_NONE, "system_bus", NULL,
clk_register_clk_pxa25x_memory());
}
#define DUMMY_CLK(_con_id, _dev_id, _parent) \

View File

@ -17,6 +17,8 @@
#include <linux/clkdev.h>
#include <linux/of.h>
#include <mach/smemc.h>
#include <dt-bindings/clock/pxa-clock.h>
#include "clk-pxa.h"
@ -45,11 +47,52 @@ enum {
PXA_MEM_RUN,
};
#define PXA27x_CLKCFG(B, HT, T) \
(CLKCFG_FCS | \
((B) ? CLKCFG_FASTBUS : 0) | \
((HT) ? CLKCFG_HALFTURBO : 0) | \
((T) ? CLKCFG_TURBO : 0))
#define PXA27x_CCCR(A, L, N2) (A << 25 | N2 << 7 | L)
#define MDCNFG_DRAC2(mdcnfg) (((mdcnfg) >> 21) & 0x3)
#define MDCNFG_DRAC0(mdcnfg) (((mdcnfg) >> 5) & 0x3)
/* Define the refresh period in mSec for the SDRAM and the number of rows */
#define SDRAM_TREF 64 /* standard 64ms SDRAM */
static const char * const get_freq_khz[] = {
"core", "run", "cpll", "memory",
"system_bus"
};
static int get_sdram_rows(void)
{
static int sdram_rows;
unsigned int drac2 = 0, drac0 = 0;
u32 mdcnfg;
if (sdram_rows)
return sdram_rows;
mdcnfg = readl_relaxed(MDCNFG);
if (mdcnfg & (MDCNFG_DE2 | MDCNFG_DE3))
drac2 = MDCNFG_DRAC2(mdcnfg);
if (mdcnfg & (MDCNFG_DE0 | MDCNFG_DE1))
drac0 = MDCNFG_DRAC0(mdcnfg);
sdram_rows = 1 << (11 + max(drac0, drac2));
return sdram_rows;
}
static u32 mdrefr_dri(unsigned int freq_khz)
{
u32 interval = freq_khz * SDRAM_TREF / get_sdram_rows();
return (interval - 31) / 32;
}
/*
* Get the clock frequency as reflected by CCSR and the turbo flag.
* We assume these values have been applied via a fcs.
@ -145,6 +188,42 @@ static struct desc_clk_cken pxa27x_clocks[] __initdata = {
};
/*
* PXA270 definitions
*
* For the PXA27x:
* Control variables are A, L, 2N for CCCR; B, HT, T for CLKCFG.
*
* A = 0 => memory controller clock from table 3-7,
* A = 1 => memory controller clock = system bus clock
* Run mode frequency = 13 MHz * L
* Turbo mode frequency = 13 MHz * L * N
* System bus frequency = 13 MHz * L / (B + 1)
*
* In CCCR:
* A = 1
* L = 16 oscillator to run mode ratio
* 2N = 6 2 * (turbo mode to run mode ratio)
*
* In CCLKCFG:
* B = 1 Fast bus mode
* HT = 0 Half-Turbo mode
* T = 1 Turbo mode
*
* For now, just support some of the combinations in table 3-7 of
* PXA27x Processor Family Developer's Manual to simplify frequency
* change sequences.
*/
static struct pxa2xx_freq pxa27x_freqs[] = {
{104000000, 104000, PXA27x_CCCR(1, 8, 2), 0, PXA27x_CLKCFG(1, 0, 1) },
{156000000, 104000, PXA27x_CCCR(1, 8, 3), 0, PXA27x_CLKCFG(1, 0, 1) },
{208000000, 208000, PXA27x_CCCR(0, 16, 2), 1, PXA27x_CLKCFG(0, 0, 1) },
{312000000, 208000, PXA27x_CCCR(1, 16, 3), 1, PXA27x_CLKCFG(1, 0, 1) },
{416000000, 208000, PXA27x_CCCR(1, 16, 4), 1, PXA27x_CLKCFG(1, 0, 1) },
{520000000, 208000, PXA27x_CCCR(1, 16, 5), 1, PXA27x_CLKCFG(1, 0, 1) },
{624000000, 208000, PXA27x_CCCR(1, 16, 6), 1, PXA27x_CLKCFG(1, 0, 1) },
};
static unsigned long clk_pxa27x_cpll_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
@ -162,10 +241,35 @@ static unsigned long clk_pxa27x_cpll_get_rate(struct clk_hw *hw,
L = l * parent_rate;
N = (L * n2) / 2;
return t ? N : L;
return N;
}
static int clk_pxa27x_cpll_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
return pxa2xx_determine_rate(req, pxa27x_freqs,
ARRAY_SIZE(pxa27x_freqs));
}
static int clk_pxa27x_cpll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
int i;
pr_debug("%s(rate=%lu parent_rate=%lu)\n", __func__, rate, parent_rate);
for (i = 0; i < ARRAY_SIZE(pxa27x_freqs); i++)
if (pxa27x_freqs[i].cpll == rate)
break;
if (i >= ARRAY_SIZE(pxa27x_freqs))
return -EINVAL;
pxa2xx_cpll_change(&pxa27x_freqs[i], mdrefr_dri, MDREFR, CCCR);
return 0;
}
PARENTS(clk_pxa27x_cpll) = { "osc_13mhz" };
RATE_RO_OPS(clk_pxa27x_cpll, "cpll");
RATE_OPS(clk_pxa27x_cpll, "cpll");
static unsigned long clk_pxa27x_lcd_base_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
@ -217,31 +321,10 @@ static void __init pxa27x_register_plls(void)
clk_register_fixed_factor(NULL, "ppll_312mhz", "osc_13mhz", 0, 24, 1);
}
static unsigned long clk_pxa27x_core_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
unsigned long clkcfg;
unsigned int t, ht, b, osc_forced;
unsigned long ccsr = readl(CCSR);
osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
t = clkcfg & (1 << 0);
ht = clkcfg & (1 << 2);
b = clkcfg & (1 << 3);
if (osc_forced)
return parent_rate;
if (ht)
return parent_rate / 2;
else
return parent_rate;
}
static u8 clk_pxa27x_core_get_parent(struct clk_hw *hw)
{
unsigned long clkcfg;
unsigned int t, ht, b, osc_forced;
unsigned int t, ht, osc_forced;
unsigned long ccsr = readl(CCSR);
osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
@ -251,14 +334,30 @@ static u8 clk_pxa27x_core_get_parent(struct clk_hw *hw)
asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
t = clkcfg & (1 << 0);
ht = clkcfg & (1 << 2);
b = clkcfg & (1 << 3);
if (ht || t)
return PXA_CORE_TURBO;
return PXA_CORE_RUN;
}
static int clk_pxa27x_core_set_parent(struct clk_hw *hw, u8 index)
{
if (index > PXA_CORE_TURBO)
return -EINVAL;
pxa2xx_core_turbo_switch(index == PXA_CORE_TURBO);
return 0;
}
static int clk_pxa27x_core_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
return __clk_mux_determine_rate(hw, req);
}
PARENTS(clk_pxa27x_core) = { "osc_13mhz", "run", "cpll" };
MUX_RO_RATE_RO_OPS(clk_pxa27x_core, "core");
MUX_OPS(clk_pxa27x_core, "core", CLK_SET_RATE_PARENT);
static unsigned long clk_pxa27x_run_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
@ -273,9 +372,10 @@ RATE_RO_OPS(clk_pxa27x_run, "run");
static void __init pxa27x_register_core(void)
{
clk_register_clk_pxa27x_cpll();
clk_register_clk_pxa27x_run();
clkdev_pxa_register(CLK_NONE, "cpll", NULL,
clk_register_clk_pxa27x_cpll());
clkdev_pxa_register(CLK_NONE, "run", NULL,
clk_register_clk_pxa27x_run());
clkdev_pxa_register(CLK_CORE, "core", NULL,
clk_register_clk_pxa27x_core());
}
@ -294,9 +394,9 @@ static unsigned long clk_pxa27x_system_bus_get_rate(struct clk_hw *hw,
if (osc_forced)
return parent_rate;
if (b)
return parent_rate / 2;
else
return parent_rate;
else
return parent_rate / 2;
}
static u8 clk_pxa27x_system_bus_get_parent(struct clk_hw *hw)
@ -385,8 +485,10 @@ static void __init pxa27x_base_clocks_init(void)
{
pxa27x_register_plls();
pxa27x_register_core();
clk_register_clk_pxa27x_system_bus();
clk_register_clk_pxa27x_memory();
clkdev_pxa_register(CLK_NONE, "system_bus", NULL,
clk_register_clk_pxa27x_system_bus());
clkdev_pxa_register(CLK_NONE, "memory", NULL,
clk_register_clk_pxa27x_memory());
clk_register_clk_pxa27x_lcd_base();
}

View File

@ -2,6 +2,9 @@ config QCOM_GDSC
bool
select PM_GENERIC_DOMAINS if PM
config QCOM_RPMCC
bool
config COMMON_CLK_QCOM
tristate "Support for Qualcomm's clock controllers"
depends on OF
@ -9,6 +12,32 @@ config COMMON_CLK_QCOM
select REGMAP_MMIO
select RESET_CONTROLLER
config QCOM_CLK_RPM
tristate "RPM based Clock Controller"
depends on COMMON_CLK_QCOM && MFD_QCOM_RPM
select QCOM_RPMCC
help
The RPM (Resource Power Manager) is a dedicated hardware engine for
managing the shared SoC resources in order to keep the lowest power
profile. It communicates with other hardware subsystems via shared
memory and accepts clock requests, aggregates the requests and turns
the clocks on/off or scales them on demand.
Say Y if you want to support the clocks exposed by the RPM on
platforms such as apq8064, msm8660, msm8960 etc.
config QCOM_CLK_SMD_RPM
tristate "RPM over SMD based Clock Controller"
depends on COMMON_CLK_QCOM && QCOM_SMD_RPM
select QCOM_RPMCC
help
The RPM (Resource Power Manager) is a dedicated hardware engine for
managing the shared SoC resources in order to keep the lowest power
profile. It communicates with other hardware subsystems via shared
memory and accepts clock requests, aggregates the requests and turns
the clocks on/off or scales them on demand.
Say Y if you want to support the clocks exposed by the RPM on
platforms such as apq8016, apq8084, msm8974 etc.
config APQ_GCC_8084
tristate "APQ8084 Global Clock Controller"
select QCOM_GDSC
@ -132,6 +161,14 @@ config MSM_MMCC_8974
Say Y if you want to support multimedia devices such as display,
graphics, video encode/decode, camera, etc.
config MSM_GCC_8994
tristate "MSM8994 Global Clock Controller"
depends on COMMON_CLK_QCOM
help
Support for the global clock controller on msm8994 devices.
Say Y if you want to use peripheral devices such as UART, SPI,
i2c, USB, UFS, SD/eMMC, PCIe, etc.
config MSM_GCC_8996
tristate "MSM8996 Global Clock Controller"
select QCOM_GDSC

View File

@ -24,8 +24,11 @@ obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o
obj-$(CONFIG_MSM_GCC_8916) += gcc-msm8916.o
obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o
obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
obj-$(CONFIG_MSM_GCC_8994) += gcc-msm8994.o
obj-$(CONFIG_MSM_GCC_8996) += gcc-msm8996.o
obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o

View File

@ -18,17 +18,21 @@
#include <linux/delay.h>
#include "clk-alpha-pll.h"
#include "common.h"
#define PLL_MODE 0x00
# define PLL_OUTCTRL BIT(0)
# define PLL_BYPASSNL BIT(1)
# define PLL_RESET_N BIT(2)
# define PLL_OFFLINE_REQ BIT(7)
# define PLL_LOCK_COUNT_SHIFT 8
# define PLL_LOCK_COUNT_MASK 0x3f
# define PLL_BIAS_COUNT_SHIFT 14
# define PLL_BIAS_COUNT_MASK 0x3f
# define PLL_VOTE_FSM_ENA BIT(20)
# define PLL_FSM_ENA BIT(20)
# define PLL_VOTE_FSM_RESET BIT(21)
# define PLL_OFFLINE_ACK BIT(28)
# define PLL_ACTIVE_FLAG BIT(30)
# define PLL_LOCK_DET BIT(31)
@ -46,6 +50,7 @@
#define PLL_USER_CTL_U 0x14
#define PLL_CONFIG_CTL 0x18
#define PLL_CONFIG_CTL_U 0x20
#define PLL_TEST_CTL 0x1c
#define PLL_TEST_CTL_U 0x20
#define PLL_STATUS 0x24
@ -55,6 +60,7 @@
*/
#define ALPHA_REG_BITWIDTH 40
#define ALPHA_BITWIDTH 32
#define ALPHA_16BIT_MASK 0xffff
#define to_clk_alpha_pll(_hw) container_of(to_clk_regmap(_hw), \
struct clk_alpha_pll, clkr)
@ -62,9 +68,10 @@
#define to_clk_alpha_pll_postdiv(_hw) container_of(to_clk_regmap(_hw), \
struct clk_alpha_pll_postdiv, clkr)
static int wait_for_pll(struct clk_alpha_pll *pll)
static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
const char *action)
{
u32 val, mask, off;
u32 val, off;
int count;
int ret;
const char *name = clk_hw_get_name(&pll->clkr.hw);
@ -74,26 +81,148 @@ static int wait_for_pll(struct clk_alpha_pll *pll)
if (ret)
return ret;
if (val & PLL_VOTE_FSM_ENA)
mask = PLL_ACTIVE_FLAG;
else
mask = PLL_LOCK_DET;
/* Wait for pll to enable. */
for (count = 100; count > 0; count--) {
ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
if (ret)
return ret;
if ((val & mask) == mask)
if (inverse && !(val & mask))
return 0;
else if ((val & mask) == mask)
return 0;
udelay(1);
}
WARN(1, "%s didn't enable after voting for it!\n", name);
WARN(1, "%s failed to %s!\n", name, action);
return -ETIMEDOUT;
}
#define wait_for_pll_enable_active(pll) \
wait_for_pll(pll, PLL_ACTIVE_FLAG, 0, "enable")
#define wait_for_pll_enable_lock(pll) \
wait_for_pll(pll, PLL_LOCK_DET, 0, "enable")
#define wait_for_pll_disable(pll) \
wait_for_pll(pll, PLL_ACTIVE_FLAG, 1, "disable")
#define wait_for_pll_offline(pll) \
wait_for_pll(pll, PLL_OFFLINE_ACK, 0, "offline")
void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
const struct alpha_pll_config *config)
{
u32 val, mask;
u32 off = pll->offset;
regmap_write(regmap, off + PLL_L_VAL, config->l);
regmap_write(regmap, off + PLL_ALPHA_VAL, config->alpha);
regmap_write(regmap, off + PLL_CONFIG_CTL, config->config_ctl_val);
regmap_write(regmap, off + PLL_CONFIG_CTL_U, config->config_ctl_hi_val);
val = config->main_output_mask;
val |= config->aux_output_mask;
val |= config->aux2_output_mask;
val |= config->early_output_mask;
val |= config->pre_div_val;
val |= config->post_div_val;
val |= config->vco_val;
mask = config->main_output_mask;
mask |= config->aux_output_mask;
mask |= config->aux2_output_mask;
mask |= config->early_output_mask;
mask |= config->pre_div_mask;
mask |= config->post_div_mask;
mask |= config->vco_mask;
regmap_update_bits(regmap, off + PLL_USER_CTL, mask, val);
if (pll->flags & SUPPORTS_FSM_MODE)
qcom_pll_set_fsm_mode(regmap, off + PLL_MODE, 6, 0);
}
static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw)
{
int ret;
u32 val, off;
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
off = pll->offset;
ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
if (ret)
return ret;
val |= PLL_FSM_ENA;
if (pll->flags & SUPPORTS_OFFLINE_REQ)
val &= ~PLL_OFFLINE_REQ;
ret = regmap_write(pll->clkr.regmap, off + PLL_MODE, val);
if (ret)
return ret;
/* Make sure enable request goes through before waiting for update */
mb();
return wait_for_pll_enable_active(pll);
}
static void clk_alpha_pll_hwfsm_disable(struct clk_hw *hw)
{
int ret;
u32 val, off;
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
off = pll->offset;
ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
if (ret)
return;
if (pll->flags & SUPPORTS_OFFLINE_REQ) {
ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
PLL_OFFLINE_REQ, PLL_OFFLINE_REQ);
if (ret)
return;
ret = wait_for_pll_offline(pll);
if (ret)
return;
}
/* Disable hwfsm */
ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
PLL_FSM_ENA, 0);
if (ret)
return;
wait_for_pll_disable(pll);
}
static int pll_is_enabled(struct clk_hw *hw, u32 mask)
{
int ret;
u32 val, off;
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
off = pll->offset;
ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
if (ret)
return ret;
return !!(val & mask);
}
static int clk_alpha_pll_hwfsm_is_enabled(struct clk_hw *hw)
{
return pll_is_enabled(hw, PLL_ACTIVE_FLAG);
}
static int clk_alpha_pll_is_enabled(struct clk_hw *hw)
{
return pll_is_enabled(hw, PLL_LOCK_DET);
}
static int clk_alpha_pll_enable(struct clk_hw *hw)
{
int ret;
@ -112,7 +241,7 @@ static int clk_alpha_pll_enable(struct clk_hw *hw)
ret = clk_enable_regmap(hw);
if (ret)
return ret;
return wait_for_pll(pll);
return wait_for_pll_enable_active(pll);
}
/* Skip if already enabled */
@ -136,7 +265,7 @@ static int clk_alpha_pll_enable(struct clk_hw *hw)
if (ret)
return ret;
ret = wait_for_pll(pll);
ret = wait_for_pll_enable_lock(pll);
if (ret)
return ret;
@ -234,9 +363,14 @@ clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
regmap_read(pll->clkr.regmap, off + PLL_USER_CTL, &ctl);
if (ctl & PLL_ALPHA_EN) {
regmap_read(pll->clkr.regmap, off + PLL_ALPHA_VAL, &low);
regmap_read(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, &high);
a = (u64)high << 32 | low;
a >>= ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH;
if (pll->flags & SUPPORTS_16BIT_ALPHA) {
a = low & ALPHA_16BIT_MASK;
} else {
regmap_read(pll->clkr.regmap, off + PLL_ALPHA_VAL_U,
&high);
a = (u64)high << 32 | low;
a >>= ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH;
}
}
return alpha_pll_calc_rate(prate, l, a);
@ -257,11 +391,15 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
return -EINVAL;
}
a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);
regmap_write(pll->clkr.regmap, off + PLL_L_VAL, l);
regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL, a);
regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, a >> 32);
if (pll->flags & SUPPORTS_16BIT_ALPHA) {
regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL,
a & ALPHA_16BIT_MASK);
} else {
a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);
regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, a >> 32);
}
regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL,
PLL_VCO_MASK << PLL_VCO_SHIFT,
@ -294,12 +432,23 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
const struct clk_ops clk_alpha_pll_ops = {
.enable = clk_alpha_pll_enable,
.disable = clk_alpha_pll_disable,
.is_enabled = clk_alpha_pll_is_enabled,
.recalc_rate = clk_alpha_pll_recalc_rate,
.round_rate = clk_alpha_pll_round_rate,
.set_rate = clk_alpha_pll_set_rate,
};
EXPORT_SYMBOL_GPL(clk_alpha_pll_ops);
const struct clk_ops clk_alpha_pll_hwfsm_ops = {
.enable = clk_alpha_pll_hwfsm_enable,
.disable = clk_alpha_pll_hwfsm_disable,
.is_enabled = clk_alpha_pll_hwfsm_is_enabled,
.recalc_rate = clk_alpha_pll_recalc_rate,
.round_rate = clk_alpha_pll_round_rate,
.set_rate = clk_alpha_pll_set_rate,
};
EXPORT_SYMBOL_GPL(clk_alpha_pll_hwfsm_ops);
static unsigned long
clk_alpha_pll_postdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{

View File

@ -34,6 +34,10 @@ struct clk_alpha_pll {
const struct pll_vco *vco_table;
size_t num_vco;
#define SUPPORTS_OFFLINE_REQ BIT(0)
#define SUPPORTS_16BIT_ALPHA BIT(1)
#define SUPPORTS_FSM_MODE BIT(2)
u8 flags;
struct clk_regmap clkr;
};
@ -51,7 +55,28 @@ struct clk_alpha_pll_postdiv {
struct clk_regmap clkr;
};
struct alpha_pll_config {
u32 l;
u32 alpha;
u32 config_ctl_val;
u32 config_ctl_hi_val;
u32 main_output_mask;
u32 aux_output_mask;
u32 aux2_output_mask;
u32 early_output_mask;
u32 pre_div_val;
u32 pre_div_mask;
u32 post_div_val;
u32 post_div_mask;
u32 vco_val;
u32 vco_mask;
};
extern const struct clk_ops clk_alpha_pll_ops;
extern const struct clk_ops clk_alpha_pll_hwfsm_ops;
extern const struct clk_ops clk_alpha_pll_postdiv_ops;
void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
const struct alpha_pll_config *config);
#endif

View File

@ -23,16 +23,11 @@
#include <asm/div64.h>
#include "clk-pll.h"
#include "common.h"
#define PLL_OUTCTRL BIT(0)
#define PLL_BYPASSNL BIT(1)
#define PLL_RESET_N BIT(2)
#define PLL_LOCK_COUNT_SHIFT 8
#define PLL_LOCK_COUNT_MASK 0x3f
#define PLL_BIAS_COUNT_SHIFT 14
#define PLL_BIAS_COUNT_MASK 0x3f
#define PLL_VOTE_FSM_ENA BIT(20)
#define PLL_VOTE_FSM_RESET BIT(21)
static int clk_pll_enable(struct clk_hw *hw)
{
@ -228,26 +223,6 @@ const struct clk_ops clk_pll_vote_ops = {
};
EXPORT_SYMBOL_GPL(clk_pll_vote_ops);
static void
clk_pll_set_fsm_mode(struct clk_pll *pll, struct regmap *regmap, u8 lock_count)
{
u32 val;
u32 mask;
/* De-assert reset to FSM */
regmap_update_bits(regmap, pll->mode_reg, PLL_VOTE_FSM_RESET, 0);
/* Program bias count and lock count */
val = 1 << PLL_BIAS_COUNT_SHIFT | lock_count << PLL_LOCK_COUNT_SHIFT;
mask = PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT;
mask |= PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT;
regmap_update_bits(regmap, pll->mode_reg, mask, val);
/* Enable PLL FSM voting */
regmap_update_bits(regmap, pll->mode_reg, PLL_VOTE_FSM_ENA,
PLL_VOTE_FSM_ENA);
}
static void clk_pll_configure(struct clk_pll *pll, struct regmap *regmap,
const struct pll_config *config)
{
@ -280,7 +255,7 @@ void clk_pll_configure_sr(struct clk_pll *pll, struct regmap *regmap,
{
clk_pll_configure(pll, regmap, config);
if (fsm_mode)
clk_pll_set_fsm_mode(pll, regmap, 8);
qcom_pll_set_fsm_mode(regmap, pll->mode_reg, 1, 8);
}
EXPORT_SYMBOL_GPL(clk_pll_configure_sr);
@ -289,7 +264,7 @@ void clk_pll_configure_sr_hpm_lp(struct clk_pll *pll, struct regmap *regmap,
{
clk_pll_configure(pll, regmap, config);
if (fsm_mode)
clk_pll_set_fsm_mode(pll, regmap, 0);
qcom_pll_set_fsm_mode(regmap, pll->mode_reg, 1, 0);
}
EXPORT_SYMBOL_GPL(clk_pll_configure_sr_hpm_lp);

View File

@ -173,6 +173,7 @@ struct clk_rcg2 {
#define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr)
extern const struct clk_ops clk_rcg2_ops;
extern const struct clk_ops clk_rcg2_floor_ops;
extern const struct clk_ops clk_rcg2_shared_ops;
extern const struct clk_ops clk_edp_pixel_ops;
extern const struct clk_ops clk_byte_ops;

View File

@ -47,6 +47,11 @@
#define N_REG 0xc
#define D_REG 0x10
enum freq_policy {
FLOOR,
CEIL,
};
static int clk_rcg2_is_enabled(struct clk_hw *hw)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
@ -176,15 +181,26 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
return calc_rate(parent_rate, m, n, mode, hid_div);
}
static int _freq_tbl_determine_rate(struct clk_hw *hw,
const struct freq_tbl *f, struct clk_rate_request *req)
static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
struct clk_rate_request *req,
enum freq_policy policy)
{
unsigned long clk_flags, rate = req->rate;
struct clk_hw *p;
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
int index;
f = qcom_find_freq(f, rate);
switch (policy) {
case FLOOR:
f = qcom_find_freq_floor(f, rate);
break;
case CEIL:
f = qcom_find_freq(f, rate);
break;
default:
return -EINVAL;
};
if (!f)
return -EINVAL;
@ -221,7 +237,15 @@ static int clk_rcg2_determine_rate(struct clk_hw *hw,
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req);
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, CEIL);
}
static int clk_rcg2_determine_floor_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR);
}
static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
@ -265,12 +289,23 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
return update_config(rcg);
}
static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
enum freq_policy policy)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
const struct freq_tbl *f;
f = qcom_find_freq(rcg->freq_tbl, rate);
switch (policy) {
case FLOOR:
f = qcom_find_freq_floor(rcg->freq_tbl, rate);
break;
case CEIL:
f = qcom_find_freq(rcg->freq_tbl, rate);
break;
default:
return -EINVAL;
};
if (!f)
return -EINVAL;
@ -280,13 +315,25 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
return __clk_rcg2_set_rate(hw, rate);
return __clk_rcg2_set_rate(hw, rate, CEIL);
}
static int clk_rcg2_set_floor_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
return __clk_rcg2_set_rate(hw, rate, FLOOR);
}
static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
unsigned long rate, unsigned long parent_rate, u8 index)
{
return __clk_rcg2_set_rate(hw, rate);
return __clk_rcg2_set_rate(hw, rate, CEIL);
}
static int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw,
unsigned long rate, unsigned long parent_rate, u8 index)
{
return __clk_rcg2_set_rate(hw, rate, FLOOR);
}
const struct clk_ops clk_rcg2_ops = {
@ -300,6 +347,17 @@ const struct clk_ops clk_rcg2_ops = {
};
EXPORT_SYMBOL_GPL(clk_rcg2_ops);
const struct clk_ops clk_rcg2_floor_ops = {
.is_enabled = clk_rcg2_is_enabled,
.get_parent = clk_rcg2_get_parent,
.set_parent = clk_rcg2_set_parent,
.recalc_rate = clk_rcg2_recalc_rate,
.determine_rate = clk_rcg2_determine_floor_rate,
.set_rate = clk_rcg2_set_floor_rate,
.set_rate_and_parent = clk_rcg2_set_floor_rate_and_parent,
};
EXPORT_SYMBOL_GPL(clk_rcg2_floor_ops);
static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
@ -323,7 +381,7 @@ static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate)
pr_err("%s: RCG did not turn on\n", name);
/* set clock rate */
ret = __clk_rcg2_set_rate(hw, rate);
ret = __clk_rcg2_set_rate(hw, rate, CEIL);
if (ret)
return ret;

497
drivers/clk/qcom/clk-rpm.c Normal file
View File

@ -0,0 +1,497 @@
/*
* Copyright (c) 2016, Linaro Limited
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/mfd/qcom_rpm.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <dt-bindings/mfd/qcom-rpm.h>
#include <dt-bindings/clock/qcom,rpmcc.h>
#define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63
#define QCOM_RPM_SCALING_ENABLE_ID 0x2
#define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \
static struct clk_rpm _platform##_##_active; \
static struct clk_rpm _platform##_##_name = { \
.rpm_clk_id = (r_id), \
.peer = &_platform##_##_active, \
.rate = INT_MAX, \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_rpm_ops, \
.name = #_name, \
.parent_names = (const char *[]){ "pxo_board" }, \
.num_parents = 1, \
}, \
}; \
static struct clk_rpm _platform##_##_active = { \
.rpm_clk_id = (r_id), \
.peer = &_platform##_##_name, \
.active_only = true, \
.rate = INT_MAX, \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_rpm_ops, \
.name = #_active, \
.parent_names = (const char *[]){ "pxo_board" }, \
.num_parents = 1, \
}, \
}
#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \
static struct clk_rpm _platform##_##_active; \
static struct clk_rpm _platform##_##_name = { \
.rpm_clk_id = (r_id), \
.active_only = true, \
.peer = &_platform##_##_active, \
.rate = (r), \
.branch = true, \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_rpm_branch_ops, \
.name = #_name, \
.parent_names = (const char *[]){ "pxo_board" }, \
.num_parents = 1, \
}, \
}; \
static struct clk_rpm _platform##_##_active = { \
.rpm_clk_id = (r_id), \
.peer = &_platform##_##_name, \
.rate = (r), \
.branch = true, \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_rpm_branch_ops, \
.name = #_active, \
.parent_names = (const char *[]){ "pxo_board" }, \
.num_parents = 1, \
}, \
}
#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r) \
static struct clk_rpm _platform##_##_active; \
static struct clk_rpm _platform##_##_name = { \
.rpm_clk_id = (r_id), \
.peer = &_platform##_##_active, \
.rate = (r), \
.branch = true, \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_rpm_branch_ops, \
.name = #_name, \
.parent_names = (const char *[]){ "cxo_board" }, \
.num_parents = 1, \
}, \
}; \
static struct clk_rpm _platform##_##_active = { \
.rpm_clk_id = (r_id), \
.active_only = true, \
.peer = &_platform##_##_name, \
.rate = (r), \
.branch = true, \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_rpm_branch_ops, \
.name = #_active, \
.parent_names = (const char *[]){ "cxo_board" }, \
.num_parents = 1, \
}, \
}
#define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw)
struct clk_rpm {
const int rpm_clk_id;
const bool active_only;
unsigned long rate;
bool enabled;
bool branch;
struct clk_rpm *peer;
struct clk_hw hw;
struct qcom_rpm *rpm;
};
struct rpm_cc {
struct qcom_rpm *rpm;
struct clk_rpm **clks;
size_t num_clks;
};
struct rpm_clk_desc {
struct clk_rpm **clks;
size_t num_clks;
};
static DEFINE_MUTEX(rpm_clk_lock);
static int clk_rpm_handoff(struct clk_rpm *r)
{
int ret;
u32 value = INT_MAX;
ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE,
r->rpm_clk_id, &value, 1);
if (ret)
return ret;
ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE,
r->rpm_clk_id, &value, 1);
if (ret)
return ret;
return 0;
}
static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate)
{
u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */
return qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE,
r->rpm_clk_id, &value, 1);
}
static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate)
{
u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */
return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE,
r->rpm_clk_id, &value, 1);
}
static void to_active_sleep(struct clk_rpm *r, unsigned long rate,
unsigned long *active, unsigned long *sleep)
{
*active = rate;
/*
* Active-only clocks don't care what the rate is during sleep. So,
* they vote for zero.
*/
if (r->active_only)
*sleep = 0;
else
*sleep = *active;
}
static int clk_rpm_prepare(struct clk_hw *hw)
{
struct clk_rpm *r = to_clk_rpm(hw);
struct clk_rpm *peer = r->peer;
unsigned long this_rate = 0, this_sleep_rate = 0;
unsigned long peer_rate = 0, peer_sleep_rate = 0;
unsigned long active_rate, sleep_rate;
int ret = 0;
mutex_lock(&rpm_clk_lock);
/* Don't send requests to the RPM if the rate has not been set. */
if (!r->rate)
goto out;
to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate);
/* Take peer clock's rate into account only if it's enabled. */
if (peer->enabled)
to_active_sleep(peer, peer->rate,
&peer_rate, &peer_sleep_rate);
active_rate = max(this_rate, peer_rate);
if (r->branch)
active_rate = !!active_rate;
ret = clk_rpm_set_rate_active(r, active_rate);
if (ret)
goto out;
sleep_rate = max(this_sleep_rate, peer_sleep_rate);
if (r->branch)
sleep_rate = !!sleep_rate;
ret = clk_rpm_set_rate_sleep(r, sleep_rate);
if (ret)
/* Undo the active set vote and restore it */
ret = clk_rpm_set_rate_active(r, peer_rate);
out:
if (!ret)
r->enabled = true;
mutex_unlock(&rpm_clk_lock);
return ret;
}
static void clk_rpm_unprepare(struct clk_hw *hw)
{
struct clk_rpm *r = to_clk_rpm(hw);
struct clk_rpm *peer = r->peer;
unsigned long peer_rate = 0, peer_sleep_rate = 0;
unsigned long active_rate, sleep_rate;
int ret;
mutex_lock(&rpm_clk_lock);
if (!r->rate)
goto out;
/* Take peer clock's rate into account only if it's enabled. */
if (peer->enabled)
to_active_sleep(peer, peer->rate, &peer_rate,
&peer_sleep_rate);
active_rate = r->branch ? !!peer_rate : peer_rate;
ret = clk_rpm_set_rate_active(r, active_rate);
if (ret)
goto out;
sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate;
ret = clk_rpm_set_rate_sleep(r, sleep_rate);
if (ret)
goto out;
r->enabled = false;
out:
mutex_unlock(&rpm_clk_lock);
}
static int clk_rpm_set_rate(struct clk_hw *hw,
unsigned long rate, unsigned long parent_rate)
{
struct clk_rpm *r = to_clk_rpm(hw);
struct clk_rpm *peer = r->peer;
unsigned long active_rate, sleep_rate;
unsigned long this_rate = 0, this_sleep_rate = 0;
unsigned long peer_rate = 0, peer_sleep_rate = 0;
int ret = 0;
mutex_lock(&rpm_clk_lock);
if (!r->enabled)
goto out;
to_active_sleep(r, rate, &this_rate, &this_sleep_rate);
/* Take peer clock's rate into account only if it's enabled. */
if (peer->enabled)
to_active_sleep(peer, peer->rate,
&peer_rate, &peer_sleep_rate);
active_rate = max(this_rate, peer_rate);
ret = clk_rpm_set_rate_active(r, active_rate);
if (ret)
goto out;
sleep_rate = max(this_sleep_rate, peer_sleep_rate);
ret = clk_rpm_set_rate_sleep(r, sleep_rate);
if (ret)
goto out;
r->rate = rate;
out:
mutex_unlock(&rpm_clk_lock);
return ret;
}
static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
/*
* RPM handles rate rounding and we don't have a way to
* know what the rate will be, so just return whatever
* rate is requested.
*/
return rate;
}
static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_rpm *r = to_clk_rpm(hw);
/*
* RPM handles rate rounding and we don't have a way to
* know what the rate will be, so just return whatever
* rate was set.
*/
return r->rate;
}
static const struct clk_ops clk_rpm_ops = {
.prepare = clk_rpm_prepare,
.unprepare = clk_rpm_unprepare,
.set_rate = clk_rpm_set_rate,
.round_rate = clk_rpm_round_rate,
.recalc_rate = clk_rpm_recalc_rate,
};
static const struct clk_ops clk_rpm_branch_ops = {
.prepare = clk_rpm_prepare,
.unprepare = clk_rpm_unprepare,
.round_rate = clk_rpm_round_rate,
.recalc_rate = clk_rpm_recalc_rate,
};
/* apq8064 */
DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK);
DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK);
DEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK);
DEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK);
DEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK);
DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK);
DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK);
DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK);
DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK);
static struct clk_rpm *apq8064_clks[] = {
[RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk,
[RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk,
[RPM_CFPB_CLK] = &apq8064_cfpb_clk,
[RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk,
[RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk,
[RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk,
[RPM_EBI1_CLK] = &apq8064_ebi1_clk,
[RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk,
[RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk,
[RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk,
[RPM_MMFPB_CLK] = &apq8064_mmfpb_clk,
[RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk,
[RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk,
[RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk,
[RPM_SFPB_CLK] = &apq8064_sfpb_clk,
[RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk,
[RPM_QDSS_CLK] = &apq8064_qdss_clk,
[RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk,
};
static const struct rpm_clk_desc rpm_clk_apq8064 = {
.clks = apq8064_clks,
.num_clks = ARRAY_SIZE(apq8064_clks),
};
static const struct of_device_id rpm_clk_match_table[] = {
{ .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 },
{ }
};
MODULE_DEVICE_TABLE(of, rpm_clk_match_table);
static struct clk_hw *qcom_rpm_clk_hw_get(struct of_phandle_args *clkspec,
void *data)
{
struct rpm_cc *rcc = data;
unsigned int idx = clkspec->args[0];
if (idx >= rcc->num_clks) {
pr_err("%s: invalid index %u\n", __func__, idx);
return ERR_PTR(-EINVAL);
}
return rcc->clks[idx] ? &rcc->clks[idx]->hw : ERR_PTR(-ENOENT);
}
static int rpm_clk_probe(struct platform_device *pdev)
{
struct rpm_cc *rcc;
int ret;
size_t num_clks, i;
struct qcom_rpm *rpm;
struct clk_rpm **rpm_clks;
const struct rpm_clk_desc *desc;
rpm = dev_get_drvdata(pdev->dev.parent);
if (!rpm) {
dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n");
return -ENODEV;
}
desc = of_device_get_match_data(&pdev->dev);
if (!desc)
return -EINVAL;
rpm_clks = desc->clks;
num_clks = desc->num_clks;
rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc), GFP_KERNEL);
if (!rcc)
return -ENOMEM;
rcc->clks = rpm_clks;
rcc->num_clks = num_clks;
for (i = 0; i < num_clks; i++) {
if (!rpm_clks[i])
continue;
rpm_clks[i]->rpm = rpm;
ret = clk_rpm_handoff(rpm_clks[i]);
if (ret)
goto err;
}
for (i = 0; i < num_clks; i++) {
if (!rpm_clks[i])
continue;
ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw);
if (ret)
goto err;
}
ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_rpm_clk_hw_get,
rcc);
if (ret)
goto err;
return 0;
err:
dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret);
return ret;
}
static int rpm_clk_remove(struct platform_device *pdev)
{
of_clk_del_provider(pdev->dev.of_node);
return 0;
}
static struct platform_driver rpm_clk_driver = {
.driver = {
.name = "qcom-clk-rpm",
.of_match_table = rpm_clk_match_table,
},
.probe = rpm_clk_probe,
.remove = rpm_clk_remove,
};
static int __init rpm_clk_init(void)
{
return platform_driver_register(&rpm_clk_driver);
}
core_initcall(rpm_clk_init);
static void __exit rpm_clk_exit(void)
{
platform_driver_unregister(&rpm_clk_driver);
}
module_exit(rpm_clk_exit);
MODULE_DESCRIPTION("Qualcomm RPM Clock Controller Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:qcom-clk-rpm");

View File

@ -0,0 +1,578 @@
/*
* Copyright (c) 2016, Linaro Limited
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/soc/qcom/smd-rpm.h>
#include <dt-bindings/clock/qcom,rpmcc.h>
#include <dt-bindings/mfd/qcom-rpm.h>
#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773
#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370
#define QCOM_RPM_SMD_KEY_RATE 0x007a484b
#define QCOM_RPM_SMD_KEY_ENABLE 0x62616e45
#define QCOM_RPM_SMD_KEY_STATE 0x54415453
#define QCOM_RPM_SCALING_ENABLE_ID 0x2
#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \
key) \
static struct clk_smd_rpm _platform##_##_active; \
static struct clk_smd_rpm _platform##_##_name = { \
.rpm_res_type = (type), \
.rpm_clk_id = (r_id), \
.rpm_status_id = (stat_id), \
.rpm_key = (key), \
.peer = &_platform##_##_active, \
.rate = INT_MAX, \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_smd_rpm_ops, \
.name = #_name, \
.parent_names = (const char *[]){ "xo_board" }, \
.num_parents = 1, \
}, \
}; \
static struct clk_smd_rpm _platform##_##_active = { \
.rpm_res_type = (type), \
.rpm_clk_id = (r_id), \
.rpm_status_id = (stat_id), \
.active_only = true, \
.rpm_key = (key), \
.peer = &_platform##_##_name, \
.rate = INT_MAX, \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_smd_rpm_ops, \
.name = #_active, \
.parent_names = (const char *[]){ "xo_board" }, \
.num_parents = 1, \
}, \
}
#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \
stat_id, r, key) \
static struct clk_smd_rpm _platform##_##_active; \
static struct clk_smd_rpm _platform##_##_name = { \
.rpm_res_type = (type), \
.rpm_clk_id = (r_id), \
.rpm_status_id = (stat_id), \
.rpm_key = (key), \
.branch = true, \
.peer = &_platform##_##_active, \
.rate = (r), \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_smd_rpm_branch_ops, \
.name = #_name, \
.parent_names = (const char *[]){ "xo_board" }, \
.num_parents = 1, \
}, \
}; \
static struct clk_smd_rpm _platform##_##_active = { \
.rpm_res_type = (type), \
.rpm_clk_id = (r_id), \
.rpm_status_id = (stat_id), \
.active_only = true, \
.rpm_key = (key), \
.branch = true, \
.peer = &_platform##_##_name, \
.rate = (r), \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_smd_rpm_branch_ops, \
.name = #_active, \
.parent_names = (const char *[]){ "xo_board" }, \
.num_parents = 1, \
}, \
}
#define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id) \
__DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \
0, QCOM_RPM_SMD_KEY_RATE)
#define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r) \
__DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, \
r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE)
#define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id) \
__DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \
0, QCOM_RPM_SMD_KEY_STATE)
#define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id) \
__DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \
QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \
QCOM_RPM_KEY_SOFTWARE_ENABLE)
#define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \
__DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \
QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \
QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY)
#define to_clk_smd_rpm(_hw) container_of(_hw, struct clk_smd_rpm, hw)
struct clk_smd_rpm {
const int rpm_res_type;
const int rpm_key;
const int rpm_clk_id;
const int rpm_status_id;
const bool active_only;
bool enabled;
bool branch;
struct clk_smd_rpm *peer;
struct clk_hw hw;
unsigned long rate;
struct qcom_smd_rpm *rpm;
};
struct clk_smd_rpm_req {
__le32 key;
__le32 nbytes;
__le32 value;
};
struct rpm_cc {
struct qcom_rpm *rpm;
struct clk_smd_rpm **clks;
size_t num_clks;
};
struct rpm_smd_clk_desc {
struct clk_smd_rpm **clks;
size_t num_clks;
};
static DEFINE_MUTEX(rpm_smd_clk_lock);
static int clk_smd_rpm_handoff(struct clk_smd_rpm *r)
{
int ret;
struct clk_smd_rpm_req req = {
.key = cpu_to_le32(r->rpm_key),
.nbytes = cpu_to_le32(sizeof(u32)),
.value = cpu_to_le32(INT_MAX),
};
ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
r->rpm_res_type, r->rpm_clk_id, &req,
sizeof(req));
if (ret)
return ret;
ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE,
r->rpm_res_type, r->rpm_clk_id, &req,
sizeof(req));
if (ret)
return ret;
return 0;
}
static int clk_smd_rpm_set_rate_active(struct clk_smd_rpm *r,
unsigned long rate)
{
struct clk_smd_rpm_req req = {
.key = cpu_to_le32(r->rpm_key),
.nbytes = cpu_to_le32(sizeof(u32)),
.value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */
};
return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
r->rpm_res_type, r->rpm_clk_id, &req,
sizeof(req));
}
static int clk_smd_rpm_set_rate_sleep(struct clk_smd_rpm *r,
unsigned long rate)
{
struct clk_smd_rpm_req req = {
.key = cpu_to_le32(r->rpm_key),
.nbytes = cpu_to_le32(sizeof(u32)),
.value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */
};
return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE,
r->rpm_res_type, r->rpm_clk_id, &req,
sizeof(req));
}
static void to_active_sleep(struct clk_smd_rpm *r, unsigned long rate,
unsigned long *active, unsigned long *sleep)
{
*active = rate;
/*
* Active-only clocks don't care what the rate is during sleep. So,
* they vote for zero.
*/
if (r->active_only)
*sleep = 0;
else
*sleep = *active;
}
static int clk_smd_rpm_prepare(struct clk_hw *hw)
{
struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
struct clk_smd_rpm *peer = r->peer;
unsigned long this_rate = 0, this_sleep_rate = 0;
unsigned long peer_rate = 0, peer_sleep_rate = 0;
unsigned long active_rate, sleep_rate;
int ret = 0;
mutex_lock(&rpm_smd_clk_lock);
/* Don't send requests to the RPM if the rate has not been set. */
if (!r->rate)
goto out;
to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate);
/* Take peer clock's rate into account only if it's enabled. */
if (peer->enabled)
to_active_sleep(peer, peer->rate,
&peer_rate, &peer_sleep_rate);
active_rate = max(this_rate, peer_rate);
if (r->branch)
active_rate = !!active_rate;
ret = clk_smd_rpm_set_rate_active(r, active_rate);
if (ret)
goto out;
sleep_rate = max(this_sleep_rate, peer_sleep_rate);
if (r->branch)
sleep_rate = !!sleep_rate;
ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate);
if (ret)
/* Undo the active set vote and restore it */
ret = clk_smd_rpm_set_rate_active(r, peer_rate);
out:
if (!ret)
r->enabled = true;
mutex_unlock(&rpm_smd_clk_lock);
return ret;
}
static void clk_smd_rpm_unprepare(struct clk_hw *hw)
{
struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
struct clk_smd_rpm *peer = r->peer;
unsigned long peer_rate = 0, peer_sleep_rate = 0;
unsigned long active_rate, sleep_rate;
int ret;
mutex_lock(&rpm_smd_clk_lock);
if (!r->rate)
goto out;
/* Take peer clock's rate into account only if it's enabled. */
if (peer->enabled)
to_active_sleep(peer, peer->rate, &peer_rate,
&peer_sleep_rate);
active_rate = r->branch ? !!peer_rate : peer_rate;
ret = clk_smd_rpm_set_rate_active(r, active_rate);
if (ret)
goto out;
sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate;
ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate);
if (ret)
goto out;
r->enabled = false;
out:
mutex_unlock(&rpm_smd_clk_lock);
}
static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
struct clk_smd_rpm *peer = r->peer;
unsigned long active_rate, sleep_rate;
unsigned long this_rate = 0, this_sleep_rate = 0;
unsigned long peer_rate = 0, peer_sleep_rate = 0;
int ret = 0;
mutex_lock(&rpm_smd_clk_lock);
if (!r->enabled)
goto out;
to_active_sleep(r, rate, &this_rate, &this_sleep_rate);
/* Take peer clock's rate into account only if it's enabled. */
if (peer->enabled)
to_active_sleep(peer, peer->rate,
&peer_rate, &peer_sleep_rate);
active_rate = max(this_rate, peer_rate);
ret = clk_smd_rpm_set_rate_active(r, active_rate);
if (ret)
goto out;
sleep_rate = max(this_sleep_rate, peer_sleep_rate);
ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate);
if (ret)
goto out;
r->rate = rate;
out:
mutex_unlock(&rpm_smd_clk_lock);
return ret;
}
static long clk_smd_rpm_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
/*
* RPM handles rate rounding and we don't have a way to
* know what the rate will be, so just return whatever
* rate is requested.
*/
return rate;
}
static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
/*
* RPM handles rate rounding and we don't have a way to
* know what the rate will be, so just return whatever
* rate was set.
*/
return r->rate;
}
static int clk_smd_rpm_enable_scaling(struct qcom_smd_rpm *rpm)
{
int ret;
struct clk_smd_rpm_req req = {
.key = cpu_to_le32(QCOM_RPM_SMD_KEY_ENABLE),
.nbytes = cpu_to_le32(sizeof(u32)),
.value = cpu_to_le32(1),
};
ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_SLEEP_STATE,
QCOM_SMD_RPM_MISC_CLK,
QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req));
if (ret) {
pr_err("RPM clock scaling (sleep set) not enabled!\n");
return ret;
}
ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_ACTIVE_STATE,
QCOM_SMD_RPM_MISC_CLK,
QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req));
if (ret) {
pr_err("RPM clock scaling (active set) not enabled!\n");
return ret;
}
pr_debug("%s: RPM clock scaling is enabled\n", __func__);
return 0;
}
static const struct clk_ops clk_smd_rpm_ops = {
.prepare = clk_smd_rpm_prepare,
.unprepare = clk_smd_rpm_unprepare,
.set_rate = clk_smd_rpm_set_rate,
.round_rate = clk_smd_rpm_round_rate,
.recalc_rate = clk_smd_rpm_recalc_rate,
};
static const struct clk_ops clk_smd_rpm_branch_ops = {
.prepare = clk_smd_rpm_prepare,
.unprepare = clk_smd_rpm_unprepare,
.round_rate = clk_smd_rpm_round_rate,
.recalc_rate = clk_smd_rpm_recalc_rate,
};
/* msm8916 */
DEFINE_CLK_SMD_RPM(msm8916, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0);
DEFINE_CLK_SMD_RPM(msm8916, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1);
DEFINE_CLK_SMD_RPM(msm8916, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0);
DEFINE_CLK_SMD_RPM_QDSS(msm8916, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1);
DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk1, bb_clk1_a, 1);
DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk2, bb_clk2_a, 2);
DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk1, rf_clk1_a, 4);
DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk2, rf_clk2_a, 5);
DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk1_pin, bb_clk1_a_pin, 1);
DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk2_pin, bb_clk2_a_pin, 2);
DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk1_pin, rf_clk1_a_pin, 4);
DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk2_pin, rf_clk2_a_pin, 5);
static struct clk_smd_rpm *msm8916_clks[] = {
[RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk,
[RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk,
[RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk,
[RPM_SMD_SNOC_A_CLK] = &msm8916_snoc_a_clk,
[RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk,
[RPM_SMD_BIMC_A_CLK] = &msm8916_bimc_a_clk,
[RPM_SMD_QDSS_CLK] = &msm8916_qdss_clk,
[RPM_SMD_QDSS_A_CLK] = &msm8916_qdss_a_clk,
[RPM_SMD_BB_CLK1] = &msm8916_bb_clk1,
[RPM_SMD_BB_CLK1_A] = &msm8916_bb_clk1_a,
[RPM_SMD_BB_CLK2] = &msm8916_bb_clk2,
[RPM_SMD_BB_CLK2_A] = &msm8916_bb_clk2_a,
[RPM_SMD_RF_CLK1] = &msm8916_rf_clk1,
[RPM_SMD_RF_CLK1_A] = &msm8916_rf_clk1_a,
[RPM_SMD_RF_CLK2] = &msm8916_rf_clk2,
[RPM_SMD_RF_CLK2_A] = &msm8916_rf_clk2_a,
[RPM_SMD_BB_CLK1_PIN] = &msm8916_bb_clk1_pin,
[RPM_SMD_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin,
[RPM_SMD_BB_CLK2_PIN] = &msm8916_bb_clk2_pin,
[RPM_SMD_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin,
[RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin,
[RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin,
[RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin,
[RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin,
};
static const struct rpm_smd_clk_desc rpm_clk_msm8916 = {
.clks = msm8916_clks,
.num_clks = ARRAY_SIZE(msm8916_clks),
};
static const struct of_device_id rpm_smd_clk_match_table[] = {
{ .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 },
{ }
};
MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table);
static struct clk_hw *qcom_smdrpm_clk_hw_get(struct of_phandle_args *clkspec,
void *data)
{
struct rpm_cc *rcc = data;
unsigned int idx = clkspec->args[0];
if (idx >= rcc->num_clks) {
pr_err("%s: invalid index %u\n", __func__, idx);
return ERR_PTR(-EINVAL);
}
return rcc->clks[idx] ? &rcc->clks[idx]->hw : ERR_PTR(-ENOENT);
}
static int rpm_smd_clk_probe(struct platform_device *pdev)
{
struct rpm_cc *rcc;
int ret;
size_t num_clks, i;
struct qcom_smd_rpm *rpm;
struct clk_smd_rpm **rpm_smd_clks;
const struct rpm_smd_clk_desc *desc;
rpm = dev_get_drvdata(pdev->dev.parent);
if (!rpm) {
dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n");
return -ENODEV;
}
desc = of_device_get_match_data(&pdev->dev);
if (!desc)
return -EINVAL;
rpm_smd_clks = desc->clks;
num_clks = desc->num_clks;
rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc), GFP_KERNEL);
if (!rcc)
return -ENOMEM;
rcc->clks = rpm_smd_clks;
rcc->num_clks = num_clks;
for (i = 0; i < num_clks; i++) {
if (!rpm_smd_clks[i])
continue;
rpm_smd_clks[i]->rpm = rpm;
ret = clk_smd_rpm_handoff(rpm_smd_clks[i]);
if (ret)
goto err;
}
ret = clk_smd_rpm_enable_scaling(rpm);
if (ret)
goto err;
for (i = 0; i < num_clks; i++) {
if (!rpm_smd_clks[i])
continue;
ret = devm_clk_hw_register(&pdev->dev, &rpm_smd_clks[i]->hw);
if (ret)
goto err;
}
ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_smdrpm_clk_hw_get,
rcc);
if (ret)
goto err;
return 0;
err:
dev_err(&pdev->dev, "Error registering SMD clock driver (%d)\n", ret);
return ret;
}
static int rpm_smd_clk_remove(struct platform_device *pdev)
{
of_clk_del_provider(pdev->dev.of_node);
return 0;
}
static struct platform_driver rpm_smd_clk_driver = {
.driver = {
.name = "qcom-clk-smd-rpm",
.of_match_table = rpm_smd_clk_match_table,
},
.probe = rpm_smd_clk_probe,
.remove = rpm_smd_clk_remove,
};
static int __init rpm_smd_clk_init(void)
{
return platform_driver_register(&rpm_smd_clk_driver);
}
core_initcall(rpm_smd_clk_init);
static void __exit rpm_smd_clk_exit(void)
{
platform_driver_unregister(&rpm_smd_clk_driver);
}
module_exit(rpm_smd_clk_exit);
MODULE_DESCRIPTION("Qualcomm RPM over SMD Clock Controller Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:qcom-clk-smd-rpm");

View File

@ -46,6 +46,22 @@ struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
}
EXPORT_SYMBOL_GPL(qcom_find_freq);
const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
unsigned long rate)
{
const struct freq_tbl *best = NULL;
for ( ; f->freq; f++) {
if (rate >= f->freq)
best = f;
else
break;
}
return best;
}
EXPORT_SYMBOL_GPL(qcom_find_freq_floor);
int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src)
{
int i, num_parents = clk_hw_get_num_parents(hw);
@ -74,6 +90,27 @@ qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc)
}
EXPORT_SYMBOL_GPL(qcom_cc_map);
void
qcom_pll_set_fsm_mode(struct regmap *map, u32 reg, u8 bias_count, u8 lock_count)
{
u32 val;
u32 mask;
/* De-assert reset to FSM */
regmap_update_bits(map, reg, PLL_VOTE_FSM_RESET, 0);
/* Program bias count and lock count */
val = bias_count << PLL_BIAS_COUNT_SHIFT |
lock_count << PLL_LOCK_COUNT_SHIFT;
mask = PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT;
mask |= PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT;
regmap_update_bits(map, reg, mask, val);
/* Enable PLL FSM voting */
regmap_update_bits(map, reg, PLL_VOTE_FSM_ENA, PLL_VOTE_FSM_ENA);
}
EXPORT_SYMBOL_GPL(qcom_pll_set_fsm_mode);
static void qcom_cc_del_clk_provider(void *data)
{
of_clk_del_provider(data);
@ -153,15 +190,12 @@ int qcom_cc_register_board_clk(struct device *dev, const char *path,
const char *name, unsigned long rate)
{
bool add_factor = true;
struct device_node *node;
/* The RPM clock driver will add the factor clock if present */
if (IS_ENABLED(CONFIG_QCOM_RPMCC)) {
node = of_find_compatible_node(NULL, NULL, "qcom,rpmcc");
if (of_device_is_available(node))
add_factor = false;
of_node_put(node);
}
/*
* TODO: The RPM clock driver currently does not support the xo clock.
* When xo is added to the RPM clock driver, we should change this
* function to skip registration of xo factor clocks.
*/
return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor);
}

View File

@ -22,6 +22,13 @@ struct freq_tbl;
struct clk_hw;
struct parent_map;
#define PLL_LOCK_COUNT_SHIFT 8
#define PLL_LOCK_COUNT_MASK 0x3f
#define PLL_BIAS_COUNT_SHIFT 14
#define PLL_BIAS_COUNT_MASK 0x3f
#define PLL_VOTE_FSM_ENA BIT(20)
#define PLL_VOTE_FSM_RESET BIT(21)
struct qcom_cc_desc {
const struct regmap_config *config;
struct clk_regmap **clks;
@ -34,6 +41,10 @@ struct qcom_cc_desc {
extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f,
unsigned long rate);
extern const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
unsigned long rate);
extern void
qcom_pll_set_fsm_mode(struct regmap *m, u32 reg, u8 bias_count, u8 lock_count);
extern int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map,
u8 src);

View File

@ -1142,7 +1142,7 @@ static struct clk_rcg2 sdcc1_apps_clk_src = {
.name = "sdcc1_apps_clk_src",
.parent_names = gcc_xo_gpll0_gpll4,
.num_parents = 3,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
},
};
@ -1156,7 +1156,7 @@ static struct clk_rcg2 sdcc2_apps_clk_src = {
.name = "sdcc2_apps_clk_src",
.parent_names = gcc_xo_gpll0,
.num_parents = 2,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
},
};
@ -1170,7 +1170,7 @@ static struct clk_rcg2 sdcc3_apps_clk_src = {
.name = "sdcc3_apps_clk_src",
.parent_names = gcc_xo_gpll0,
.num_parents = 2,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
},
};
@ -1184,7 +1184,7 @@ static struct clk_rcg2 sdcc4_apps_clk_src = {
.name = "sdcc4_apps_clk_src",
.parent_names = gcc_xo_gpll0,
.num_parents = 2,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
},
};

View File

@ -185,8 +185,7 @@ static struct clk_branch gcc_audio_pwm_clk = {
};
static const struct freq_tbl ftbl_gcc_blsp1_qup1_2_i2c_apps_clk[] = {
F(19200000, P_XO, 1, 2, 5),
F(24000000, P_XO, 1, 1, 2),
F(19050000, P_FEPLL200, 10.5, 1, 1),
{ }
};

View File

@ -2990,11 +2990,11 @@ static int gcc_ipq806x_probe(struct platform_device *pdev)
struct regmap *regmap;
int ret;
ret = qcom_cc_register_board_clk(dev, "cxo_board", "cxo", 19200000);
ret = qcom_cc_register_board_clk(dev, "cxo_board", "cxo", 25000000);
if (ret)
return ret;
ret = qcom_cc_register_board_clk(dev, "pxo_board", "pxo", 27000000);
ret = qcom_cc_register_board_clk(dev, "pxo_board", "pxo", 25000000);
if (ret)
return ret;

View File

@ -1107,7 +1107,7 @@ static struct clk_rcg2 sdcc1_apps_clk_src = {
.name = "sdcc1_apps_clk_src",
.parent_names = gcc_xo_gpll0,
.num_parents = 2,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
},
};
@ -1132,7 +1132,7 @@ static struct clk_rcg2 sdcc2_apps_clk_src = {
.name = "sdcc2_apps_clk_src",
.parent_names = gcc_xo_gpll0,
.num_parents = 2,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
},
};

View File

@ -872,7 +872,7 @@ static struct clk_init_data sdcc1_apps_clk_src_init = {
.name = "sdcc1_apps_clk_src",
.parent_names = gcc_xo_gpll0,
.num_parents = 2,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
};
static struct clk_rcg2 sdcc1_apps_clk_src = {
@ -894,7 +894,7 @@ static struct clk_rcg2 sdcc2_apps_clk_src = {
.name = "sdcc2_apps_clk_src",
.parent_names = gcc_xo_gpll0,
.num_parents = 2,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
},
};
@ -908,7 +908,7 @@ static struct clk_rcg2 sdcc3_apps_clk_src = {
.name = "sdcc3_apps_clk_src",
.parent_names = gcc_xo_gpll0,
.num_parents = 2,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
},
};
@ -922,7 +922,7 @@ static struct clk_rcg2 sdcc4_apps_clk_src = {
.name = "sdcc4_apps_clk_src",
.parent_names = gcc_xo_gpll0,
.num_parents = 2,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
},
};

File diff suppressed because it is too large Load Diff

View File

@ -460,14 +460,22 @@ static struct clk_rcg2 sdcc1_apps_clk_src = {
.name = "sdcc1_apps_clk_src",
.parent_names = gcc_xo_gpll0_gpll4_gpll0_early_div,
.num_parents = 4,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
},
};
static struct freq_tbl ftbl_sdcc1_ice_core_clk_src[] = {
F(19200000, P_XO, 1, 0, 0),
F(150000000, P_GPLL0, 4, 0, 0),
F(300000000, P_GPLL0, 2, 0, 0),
{ }
};
static struct clk_rcg2 sdcc1_ice_core_clk_src = {
.cmd_rcgr = 0x13024,
.hid_width = 5,
.parent_map = gcc_xo_gpll0_gpll4_gpll0_early_div_map,
.freq_tbl = ftbl_sdcc1_ice_core_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "sdcc1_ice_core_clk_src",
.parent_names = gcc_xo_gpll0_gpll4_gpll0_early_div,
@ -497,7 +505,7 @@ static struct clk_rcg2 sdcc2_apps_clk_src = {
.name = "sdcc2_apps_clk_src",
.parent_names = gcc_xo_gpll0_gpll4,
.num_parents = 3,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
},
};
@ -511,7 +519,7 @@ static struct clk_rcg2 sdcc3_apps_clk_src = {
.name = "sdcc3_apps_clk_src",
.parent_names = gcc_xo_gpll0_gpll4,
.num_parents = 3,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
},
};
@ -535,7 +543,7 @@ static struct clk_rcg2 sdcc4_apps_clk_src = {
.name = "sdcc4_apps_clk_src",
.parent_names = gcc_xo_gpll0,
.num_parents = 2,
.ops = &clk_rcg2_ops,
.ops = &clk_rcg2_floor_ops,
},
};
@ -1230,10 +1238,18 @@ static struct clk_rcg2 ufs_axi_clk_src = {
},
};
static const struct freq_tbl ftbl_ufs_ice_core_clk_src[] = {
F(19200000, P_XO, 1, 0, 0),
F(150000000, P_GPLL0, 4, 0, 0),
F(300000000, P_GPLL0, 2, 0, 0),
{ }
};
static struct clk_rcg2 ufs_ice_core_clk_src = {
.cmd_rcgr = 0x76014,
.hid_width = 5,
.parent_map = gcc_xo_gpll0_map,
.freq_tbl = ftbl_ufs_ice_core_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "ufs_ice_core_clk_src",
.parent_names = gcc_xo_gpll0,
@ -1242,10 +1258,19 @@ static struct clk_rcg2 ufs_ice_core_clk_src = {
},
};
static const struct freq_tbl ftbl_qspi_ser_clk_src[] = {
F(75000000, P_GPLL0, 8, 0, 0),
F(150000000, P_GPLL0, 4, 0, 0),
F(256000000, P_GPLL4, 1.5, 0, 0),
F(300000000, P_GPLL0, 2, 0, 0),
{ }
};
static struct clk_rcg2 qspi_ser_clk_src = {
.cmd_rcgr = 0x8b00c,
.hid_width = 5,
.parent_map = gcc_xo_gpll0_gpll1_early_div_gpll1_gpll4_gpll0_early_div_map,
.freq_tbl = ftbl_qspi_ser_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "qspi_ser_clk_src",
.parent_names = gcc_xo_gpll0_gpll1_early_div_gpll1_gpll4_gpll0_early_div,

View File

@ -30,6 +30,7 @@
#define SW_OVERRIDE_MASK BIT(2)
#define HW_CONTROL_MASK BIT(1)
#define SW_COLLAPSE_MASK BIT(0)
#define GMEM_CLAMP_IO_MASK BIT(0)
/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */
#define EN_REST_WAIT_VAL (0x2 << 20)
@ -55,6 +56,13 @@ static int gdsc_is_enabled(struct gdsc *sc, unsigned int reg)
return !!(val & PWR_ON_MASK);
}
static int gdsc_hwctrl(struct gdsc *sc, bool en)
{
u32 val = en ? HW_CONTROL_MASK : 0;
return regmap_update_bits(sc->regmap, sc->gdscr, HW_CONTROL_MASK, val);
}
static int gdsc_toggle_logic(struct gdsc *sc, bool en)
{
int ret;
@ -140,6 +148,18 @@ static inline void gdsc_clear_mem_on(struct gdsc *sc)
regmap_update_bits(sc->regmap, sc->cxcs[i], mask, 0);
}
static inline void gdsc_deassert_clamp_io(struct gdsc *sc)
{
regmap_update_bits(sc->regmap, sc->clamp_io_ctrl,
GMEM_CLAMP_IO_MASK, 0);
}
static inline void gdsc_assert_clamp_io(struct gdsc *sc)
{
regmap_update_bits(sc->regmap, sc->clamp_io_ctrl,
GMEM_CLAMP_IO_MASK, 1);
}
static int gdsc_enable(struct generic_pm_domain *domain)
{
struct gdsc *sc = domain_to_gdsc(domain);
@ -148,6 +168,9 @@ static int gdsc_enable(struct generic_pm_domain *domain)
if (sc->pwrsts == PWRSTS_ON)
return gdsc_deassert_reset(sc);
if (sc->flags & CLAMP_IO)
gdsc_deassert_clamp_io(sc);
ret = gdsc_toggle_logic(sc, true);
if (ret)
return ret;
@ -164,20 +187,39 @@ static int gdsc_enable(struct generic_pm_domain *domain)
*/
udelay(1);
/* Turn on HW trigger mode if supported */
if (sc->flags & HW_CTRL)
return gdsc_hwctrl(sc, true);
return 0;
}
static int gdsc_disable(struct generic_pm_domain *domain)
{
struct gdsc *sc = domain_to_gdsc(domain);
int ret;
if (sc->pwrsts == PWRSTS_ON)
return gdsc_assert_reset(sc);
/* Turn off HW trigger mode if supported */
if (sc->flags & HW_CTRL) {
ret = gdsc_hwctrl(sc, false);
if (ret < 0)
return ret;
}
if (sc->pwrsts & PWRSTS_OFF)
gdsc_clear_mem_on(sc);
return gdsc_toggle_logic(sc, false);
ret = gdsc_toggle_logic(sc, false);
if (ret)
return ret;
if (sc->flags & CLAMP_IO)
gdsc_assert_clamp_io(sc);
return 0;
}
static int gdsc_init(struct gdsc *sc)

View File

@ -39,6 +39,7 @@ struct gdsc {
struct regmap *regmap;
unsigned int gdscr;
unsigned int gds_hw_ctrl;
unsigned int clamp_io_ctrl;
unsigned int *cxcs;
unsigned int cxc_count;
const u8 pwrsts;
@ -50,6 +51,8 @@ struct gdsc {
#define PWRSTS_RET_ON (PWRSTS_RET | PWRSTS_ON)
const u8 flags;
#define VOTABLE BIT(0)
#define CLAMP_IO BIT(1)
#define HW_CTRL BIT(2)
struct reset_controller_dev *rcdev;
unsigned int *resets;
unsigned int reset_count;

View File

@ -443,7 +443,7 @@ static int lcc_ipq806x_probe(struct platform_device *pdev)
return PTR_ERR(regmap);
/* Configure the rate of PLL4 if the bootloader hasn't already */
val = regmap_read(regmap, 0x0, &val);
regmap_read(regmap, 0x0, &val);
if (!val)
clk_pll_configure_sr(&pll4, regmap, &pll4_config, true);
/* Enable PLL4 source on the LPASS Primary PLL Mux */

View File

@ -2945,6 +2945,7 @@ static struct gdsc venus_core0_gdsc = {
.name = "venus_core0",
},
.pwrsts = PWRSTS_OFF_ON,
.flags = HW_CTRL,
};
static struct gdsc venus_core1_gdsc = {
@ -2955,6 +2956,7 @@ static struct gdsc venus_core1_gdsc = {
.name = "venus_core1",
},
.pwrsts = PWRSTS_OFF_ON,
.flags = HW_CTRL,
};
static struct gdsc camss_gdsc = {
@ -3034,6 +3036,28 @@ static struct gdsc mdss_gdsc = {
.pwrsts = PWRSTS_OFF_ON,
};
static struct gdsc gpu_gdsc = {
.gdscr = 0x4034,
.gds_hw_ctrl = 0x4038,
.pd = {
.name = "gpu",
},
.pwrsts = PWRSTS_OFF_ON,
.flags = VOTABLE,
};
static struct gdsc gpu_gx_gdsc = {
.gdscr = 0x4024,
.clamp_io_ctrl = 0x4300,
.cxcs = (unsigned int []){ 0x4028 },
.cxc_count = 1,
.pd = {
.name = "gpu_gx",
},
.pwrsts = PWRSTS_OFF_ON,
.flags = CLAMP_IO,
};
static struct clk_regmap *mmcc_msm8996_clocks[] = {
[MMPLL0_EARLY] = &mmpll0_early.clkr,
[MMPLL0_PLL] = &mmpll0.clkr,
@ -3223,6 +3247,8 @@ static struct gdsc *mmcc_msm8996_gdscs[] = {
[CPP_GDSC] = &cpp_gdsc,
[FD_GDSC] = &fd_gdsc,
[MDSS_GDSC] = &mdss_gdsc,
[GPU_GDSC] = &gpu_gdsc,
[GPU_GX_GDSC] = &gpu_gx_gdsc,
};
static const struct qcom_reset_map mmcc_msm8996_resets[] = {

View File

@ -1,5 +1,7 @@
config CLK_RENESAS_CPG_MSSR
bool
default y if ARCH_R8A7743
default y if ARCH_R8A7745
default y if ARCH_R8A7795
default y if ARCH_R8A7796

View File

@ -2,6 +2,8 @@ obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o
obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o
obj-$(CONFIG_ARCH_R8A73A4) += clk-r8a73a4.o clk-div6.o
obj-$(CONFIG_ARCH_R8A7740) += clk-r8a7740.o clk-div6.o
obj-$(CONFIG_ARCH_R8A7743) += r8a7743-cpg-mssr.o rcar-gen2-cpg.o
obj-$(CONFIG_ARCH_R8A7745) += r8a7745-cpg-mssr.o rcar-gen2-cpg.o
obj-$(CONFIG_ARCH_R8A7778) += clk-r8a7778.o
obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o
obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o clk-div6.o

View File

@ -12,6 +12,7 @@
#include <linux/clk/renesas.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/soc/renesas/rcar-rst.h>
struct r8a7778_cpg {
struct clk_onecell_data data;
@ -83,6 +84,18 @@ static void __init r8a7778_cpg_clocks_init(struct device_node *np)
struct clk **clks;
unsigned int i;
int num_clks;
u32 mode;
if (rcar_rst_read_mode_pins(&mode))
return;
BUG_ON(!(mode & BIT(19)));
cpg_mode_rates = (!!(mode & BIT(18)) << 2) |
(!!(mode & BIT(12)) << 1) |
(!!(mode & BIT(11)));
cpg_mode_divs = (!!(mode & BIT(2)) << 1) |
(!!(mode & BIT(1)));
num_clks = of_property_count_strings(np, "clock-output-names");
if (num_clks < 0) {
@ -130,16 +143,3 @@ static void __init r8a7778_cpg_clocks_init(struct device_node *np)
CLK_OF_DECLARE(r8a7778_cpg_clks, "renesas,r8a7778-cpg-clocks",
r8a7778_cpg_clocks_init);
void __init r8a7778_clocks_init(u32 mode)
{
BUG_ON(!(mode & BIT(19)));
cpg_mode_rates = (!!(mode & BIT(18)) << 2) |
(!!(mode & BIT(12)) << 1) |
(!!(mode & BIT(11)));
cpg_mode_divs = (!!(mode & BIT(2)) << 1) |
(!!(mode & BIT(1)));
of_clk_init(NULL);
}

View File

@ -18,6 +18,7 @@
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/soc/renesas/rcar-rst.h>
#include <dt-bindings/clock/r8a7779-clock.h>
@ -88,8 +89,6 @@ static const unsigned int cpg_plla_mult[4] __initconst = { 42, 48, 56, 64 };
* Initialization
*/
static u32 cpg_mode __initdata;
static struct clk * __init
r8a7779_cpg_register_clock(struct device_node *np, struct r8a7779_cpg *cpg,
const struct cpg_clk_config *config,
@ -127,6 +126,10 @@ static void __init r8a7779_cpg_clocks_init(struct device_node *np)
struct clk **clks;
unsigned int i, plla_mult;
int num_clks;
u32 mode;
if (rcar_rst_read_mode_pins(&mode))
return;
num_clks = of_property_count_strings(np, "clock-output-names");
if (num_clks < 0) {
@ -148,8 +151,8 @@ static void __init r8a7779_cpg_clocks_init(struct device_node *np)
cpg->data.clks = clks;
cpg->data.clk_num = num_clks;
config = &cpg_clk_configs[CPG_CLK_CONFIG_INDEX(cpg_mode)];
plla_mult = cpg_plla_mult[CPG_PLLA_MULT_INDEX(cpg_mode)];
config = &cpg_clk_configs[CPG_CLK_CONFIG_INDEX(mode)];
plla_mult = cpg_plla_mult[CPG_PLLA_MULT_INDEX(mode)];
for (i = 0; i < num_clks; ++i) {
const char *name;
@ -173,10 +176,3 @@ static void __init r8a7779_cpg_clocks_init(struct device_node *np)
}
CLK_OF_DECLARE(r8a7779_cpg_clks, "renesas,r8a7779-cpg-clocks",
r8a7779_cpg_clocks_init);
void __init r8a7779_clocks_init(u32 mode)
{
cpg_mode = mode;
of_clk_init(NULL);
}

View File

@ -19,6 +19,7 @@
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/soc/renesas/rcar-rst.h>
struct rcar_gen2_cpg {
struct clk_onecell_data data;
@ -364,6 +365,23 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg,
4, 0, table, &cpg->lock);
}
/*
* Reset register definitions.
*/
#define MODEMR 0xe6160060
static u32 __init rcar_gen2_read_mode_pins(void)
{
void __iomem *modemr = ioremap_nocache(MODEMR, 4);
u32 mode;
BUG_ON(!modemr);
mode = ioread32(modemr);
iounmap(modemr);
return mode;
}
static void __init rcar_gen2_cpg_clocks_init(struct device_node *np)
{
const struct cpg_pll_config *config;
@ -372,6 +390,13 @@ static void __init rcar_gen2_cpg_clocks_init(struct device_node *np)
unsigned int i;
int num_clks;
if (rcar_rst_read_mode_pins(&cpg_mode)) {
/* Backward-compatibility with old DT */
pr_warn("%s: failed to obtain mode pins from RST\n",
np->full_name);
cpg_mode = rcar_gen2_read_mode_pins();
}
num_clks = of_property_count_strings(np, "clock-output-names");
if (num_clks < 0) {
pr_err("%s: failed to count clocks\n", __func__);
@ -420,10 +445,3 @@ static void __init rcar_gen2_cpg_clocks_init(struct device_node *np)
}
CLK_OF_DECLARE(rcar_gen2_cpg_clks, "renesas,rcar-gen2-cpg-clocks",
rcar_gen2_cpg_clocks_init);
void __init rcar_gen2_clocks_init(u32 mode)
{
cpg_mode = mode;
of_clk_init(NULL);
}

View File

@ -0,0 +1,270 @@
/*
* r8a7743 Clock Pulse Generator / Module Standby and Software Reset
*
* Copyright (C) 2016 Cogent Embedded Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation; of the License.
*/
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/soc/renesas/rcar-rst.h>
#include <dt-bindings/clock/r8a7743-cpg-mssr.h>
#include "renesas-cpg-mssr.h"
#include "rcar-gen2-cpg.h"
enum clk_ids {
/* Core Clock Outputs exported to DT */
LAST_DT_CORE_CLK = R8A7743_CLK_OSC,
/* External Input Clocks */
CLK_EXTAL,
CLK_USB_EXTAL,
/* Internal Core Clocks */
CLK_MAIN,
CLK_PLL0,
CLK_PLL1,
CLK_PLL3,
CLK_PLL1_DIV2,
/* Module Clocks */
MOD_CLK_BASE
};
static const struct cpg_core_clk r8a7743_core_clks[] __initconst = {
/* External Clock Inputs */
DEF_INPUT("extal", CLK_EXTAL),
DEF_INPUT("usb_extal", CLK_USB_EXTAL),
/* Internal Core Clocks */
DEF_BASE(".main", CLK_MAIN, CLK_TYPE_GEN2_MAIN, CLK_EXTAL),
DEF_BASE(".pll0", CLK_PLL0, CLK_TYPE_GEN2_PLL0, CLK_MAIN),
DEF_BASE(".pll1", CLK_PLL1, CLK_TYPE_GEN2_PLL1, CLK_MAIN),
DEF_BASE(".pll3", CLK_PLL3, CLK_TYPE_GEN2_PLL3, CLK_MAIN),
DEF_FIXED(".pll1_div2", CLK_PLL1_DIV2, CLK_PLL1, 2, 1),
/* Core Clock Outputs */
DEF_BASE("z", R8A7743_CLK_Z, CLK_TYPE_GEN2_Z, CLK_PLL0),
DEF_BASE("lb", R8A7743_CLK_LB, CLK_TYPE_GEN2_LB, CLK_PLL1),
DEF_BASE("sdh", R8A7743_CLK_SDH, CLK_TYPE_GEN2_SDH, CLK_PLL1),
DEF_BASE("sd0", R8A7743_CLK_SD0, CLK_TYPE_GEN2_SD0, CLK_PLL1),
DEF_BASE("qspi", R8A7743_CLK_QSPI, CLK_TYPE_GEN2_QSPI, CLK_PLL1_DIV2),
DEF_BASE("rcan", R8A7743_CLK_RCAN, CLK_TYPE_GEN2_RCAN, CLK_USB_EXTAL),
DEF_FIXED("zg", R8A7743_CLK_ZG, CLK_PLL1, 3, 1),
DEF_FIXED("zx", R8A7743_CLK_ZX, CLK_PLL1, 3, 1),
DEF_FIXED("zs", R8A7743_CLK_ZS, CLK_PLL1, 6, 1),
DEF_FIXED("hp", R8A7743_CLK_HP, CLK_PLL1, 12, 1),
DEF_FIXED("b", R8A7743_CLK_B, CLK_PLL1, 12, 1),
DEF_FIXED("p", R8A7743_CLK_P, CLK_PLL1, 24, 1),
DEF_FIXED("cl", R8A7743_CLK_CL, CLK_PLL1, 48, 1),
DEF_FIXED("m2", R8A7743_CLK_M2, CLK_PLL1, 8, 1),
DEF_FIXED("zb3", R8A7743_CLK_ZB3, CLK_PLL3, 4, 1),
DEF_FIXED("zb3d2", R8A7743_CLK_ZB3D2, CLK_PLL3, 8, 1),
DEF_FIXED("ddr", R8A7743_CLK_DDR, CLK_PLL3, 8, 1),
DEF_FIXED("mp", R8A7743_CLK_MP, CLK_PLL1_DIV2, 15, 1),
DEF_FIXED("cp", R8A7743_CLK_CP, CLK_EXTAL, 2, 1),
DEF_FIXED("r", R8A7743_CLK_R, CLK_PLL1, 49152, 1),
DEF_FIXED("osc", R8A7743_CLK_OSC, CLK_PLL1, 12288, 1),
DEF_DIV6P1("sd2", R8A7743_CLK_SD2, CLK_PLL1_DIV2, 0x078),
DEF_DIV6P1("sd3", R8A7743_CLK_SD3, CLK_PLL1_DIV2, 0x26c),
DEF_DIV6P1("mmc0", R8A7743_CLK_MMC0, CLK_PLL1_DIV2, 0x240),
};
static const struct mssr_mod_clk r8a7743_mod_clks[] __initconst = {
DEF_MOD("msiof0", 0, R8A7743_CLK_MP),
DEF_MOD("vcp0", 101, R8A7743_CLK_ZS),
DEF_MOD("vpc0", 103, R8A7743_CLK_ZS),
DEF_MOD("tmu1", 111, R8A7743_CLK_P),
DEF_MOD("3dg", 112, R8A7743_CLK_ZG),
DEF_MOD("2d-dmac", 115, R8A7743_CLK_ZS),
DEF_MOD("fdp1-1", 118, R8A7743_CLK_ZS),
DEF_MOD("fdp1-0", 119, R8A7743_CLK_ZS),
DEF_MOD("tmu3", 121, R8A7743_CLK_P),
DEF_MOD("tmu2", 122, R8A7743_CLK_P),
DEF_MOD("cmt0", 124, R8A7743_CLK_R),
DEF_MOD("tmu0", 125, R8A7743_CLK_CP),
DEF_MOD("vsp1du1", 127, R8A7743_CLK_ZS),
DEF_MOD("vsp1du0", 128, R8A7743_CLK_ZS),
DEF_MOD("vsp1-sy", 131, R8A7743_CLK_ZS),
DEF_MOD("scifa2", 202, R8A7743_CLK_MP),
DEF_MOD("scifa1", 203, R8A7743_CLK_MP),
DEF_MOD("scifa0", 204, R8A7743_CLK_MP),
DEF_MOD("msiof2", 205, R8A7743_CLK_MP),
DEF_MOD("scifb0", 206, R8A7743_CLK_MP),
DEF_MOD("scifb1", 207, R8A7743_CLK_MP),
DEF_MOD("msiof1", 208, R8A7743_CLK_MP),
DEF_MOD("scifb2", 216, R8A7743_CLK_MP),
DEF_MOD("sys-dmac1", 218, R8A7743_CLK_ZS),
DEF_MOD("sys-dmac0", 219, R8A7743_CLK_ZS),
DEF_MOD("tpu0", 304, R8A7743_CLK_CP),
DEF_MOD("sdhi3", 311, R8A7743_CLK_SD3),
DEF_MOD("sdhi2", 312, R8A7743_CLK_SD2),
DEF_MOD("sdhi0", 314, R8A7743_CLK_SD0),
DEF_MOD("mmcif0", 315, R8A7743_CLK_MMC0),
DEF_MOD("iic0", 318, R8A7743_CLK_HP),
DEF_MOD("pciec", 319, R8A7743_CLK_MP),
DEF_MOD("iic1", 323, R8A7743_CLK_HP),
DEF_MOD("usb3.0", 328, R8A7743_CLK_MP),
DEF_MOD("cmt1", 329, R8A7743_CLK_R),
DEF_MOD("usbhs-dmac0", 330, R8A7743_CLK_HP),
DEF_MOD("usbhs-dmac1", 331, R8A7743_CLK_HP),
DEF_MOD("irqc", 407, R8A7743_CLK_CP),
DEF_MOD("intc-sys", 408, R8A7743_CLK_ZS),
DEF_MOD("audio-dmac1", 501, R8A7743_CLK_HP),
DEF_MOD("audio-dmac0", 502, R8A7743_CLK_HP),
DEF_MOD("thermal", 522, CLK_EXTAL),
DEF_MOD("pwm", 523, R8A7743_CLK_P),
DEF_MOD("usb-ehci", 703, R8A7743_CLK_MP),
DEF_MOD("usbhs", 704, R8A7743_CLK_HP),
DEF_MOD("hscif2", 713, R8A7743_CLK_ZS),
DEF_MOD("scif5", 714, R8A7743_CLK_P),
DEF_MOD("scif4", 715, R8A7743_CLK_P),
DEF_MOD("hscif1", 716, R8A7743_CLK_ZS),
DEF_MOD("hscif0", 717, R8A7743_CLK_ZS),
DEF_MOD("scif3", 718, R8A7743_CLK_P),
DEF_MOD("scif2", 719, R8A7743_CLK_P),
DEF_MOD("scif1", 720, R8A7743_CLK_P),
DEF_MOD("scif0", 721, R8A7743_CLK_P),
DEF_MOD("du1", 723, R8A7743_CLK_ZX),
DEF_MOD("du0", 724, R8A7743_CLK_ZX),
DEF_MOD("lvds0", 726, R8A7743_CLK_ZX),
DEF_MOD("ipmmu-sgx", 800, R8A7743_CLK_ZX),
DEF_MOD("vin2", 809, R8A7743_CLK_ZG),
DEF_MOD("vin1", 810, R8A7743_CLK_ZG),
DEF_MOD("vin0", 811, R8A7743_CLK_ZG),
DEF_MOD("etheravb", 812, R8A7743_CLK_HP),
DEF_MOD("ether", 813, R8A7743_CLK_P),
DEF_MOD("sata1", 814, R8A7743_CLK_ZS),
DEF_MOD("sata0", 815, R8A7743_CLK_ZS),
DEF_MOD("gpio7", 904, R8A7743_CLK_CP),
DEF_MOD("gpio6", 905, R8A7743_CLK_CP),
DEF_MOD("gpio5", 907, R8A7743_CLK_CP),
DEF_MOD("gpio4", 908, R8A7743_CLK_CP),
DEF_MOD("gpio3", 909, R8A7743_CLK_CP),
DEF_MOD("gpio2", 910, R8A7743_CLK_CP),
DEF_MOD("gpio1", 911, R8A7743_CLK_CP),
DEF_MOD("gpio0", 912, R8A7743_CLK_CP),
DEF_MOD("can1", 915, R8A7743_CLK_P),
DEF_MOD("can0", 916, R8A7743_CLK_P),
DEF_MOD("qspi_mod", 917, R8A7743_CLK_QSPI),
DEF_MOD("i2c5", 925, R8A7743_CLK_HP),
DEF_MOD("iicdvfs", 926, R8A7743_CLK_CP),
DEF_MOD("i2c4", 927, R8A7743_CLK_HP),
DEF_MOD("i2c3", 928, R8A7743_CLK_HP),
DEF_MOD("i2c2", 929, R8A7743_CLK_HP),
DEF_MOD("i2c1", 930, R8A7743_CLK_HP),
DEF_MOD("i2c0", 931, R8A7743_CLK_HP),
DEF_MOD("ssi-all", 1005, R8A7743_CLK_P),
DEF_MOD("ssi9", 1006, MOD_CLK_ID(1005)),
DEF_MOD("ssi8", 1007, MOD_CLK_ID(1005)),
DEF_MOD("ssi7", 1008, MOD_CLK_ID(1005)),
DEF_MOD("ssi6", 1009, MOD_CLK_ID(1005)),
DEF_MOD("ssi5", 1010, MOD_CLK_ID(1005)),
DEF_MOD("ssi4", 1011, MOD_CLK_ID(1005)),
DEF_MOD("ssi3", 1012, MOD_CLK_ID(1005)),
DEF_MOD("ssi2", 1013, MOD_CLK_ID(1005)),
DEF_MOD("ssi1", 1014, MOD_CLK_ID(1005)),
DEF_MOD("ssi0", 1015, MOD_CLK_ID(1005)),
DEF_MOD("scu-all", 1017, R8A7743_CLK_P),
DEF_MOD("scu-dvc1", 1018, MOD_CLK_ID(1017)),
DEF_MOD("scu-dvc0", 1019, MOD_CLK_ID(1017)),
DEF_MOD("scu-ctu1-mix1", 1020, MOD_CLK_ID(1017)),
DEF_MOD("scu-ctu0-mix0", 1021, MOD_CLK_ID(1017)),
DEF_MOD("scu-src9", 1022, MOD_CLK_ID(1017)),
DEF_MOD("scu-src8", 1023, MOD_CLK_ID(1017)),
DEF_MOD("scu-src7", 1024, MOD_CLK_ID(1017)),
DEF_MOD("scu-src6", 1025, MOD_CLK_ID(1017)),
DEF_MOD("scu-src5", 1026, MOD_CLK_ID(1017)),
DEF_MOD("scu-src4", 1027, MOD_CLK_ID(1017)),
DEF_MOD("scu-src3", 1028, MOD_CLK_ID(1017)),
DEF_MOD("scu-src2", 1029, MOD_CLK_ID(1017)),
DEF_MOD("scu-src1", 1030, MOD_CLK_ID(1017)),
DEF_MOD("scu-src0", 1031, MOD_CLK_ID(1017)),
DEF_MOD("scifa3", 1106, R8A7743_CLK_MP),
DEF_MOD("scifa4", 1107, R8A7743_CLK_MP),
DEF_MOD("scifa5", 1108, R8A7743_CLK_MP),
};
static const unsigned int r8a7743_crit_mod_clks[] __initconst = {
MOD_CLK_ID(408), /* INTC-SYS (GIC) */
};
/*
* CPG Clock Data
*/
/*
* MD EXTAL PLL0 PLL1 PLL3
* 14 13 19 (MHz) *1 *1
*---------------------------------------------------
* 0 0 0 15 x172/2 x208/2 x106
* 0 0 1 15 x172/2 x208/2 x88
* 0 1 0 20 x130/2 x156/2 x80
* 0 1 1 20 x130/2 x156/2 x66
* 1 0 0 26 / 2 x200/2 x240/2 x122
* 1 0 1 26 / 2 x200/2 x240/2 x102
* 1 1 0 30 / 2 x172/2 x208/2 x106
* 1 1 1 30 / 2 x172/2 x208/2 x88
*
* *1 : Table 7.5a indicates VCO output (PLLx = VCO/2)
*/
#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 12) | \
(((md) & BIT(13)) >> 12) | \
(((md) & BIT(19)) >> 19))
static const struct rcar_gen2_cpg_pll_config cpg_pll_configs[8] __initconst = {
/* EXTAL div PLL1 mult PLL3 mult */
{ 1, 208, 106, },
{ 1, 208, 88, },
{ 1, 156, 80, },
{ 1, 156, 66, },
{ 2, 240, 122, },
{ 2, 240, 102, },
{ 2, 208, 106, },
{ 2, 208, 88, },
};
static int __init r8a7743_cpg_mssr_init(struct device *dev)
{
const struct rcar_gen2_cpg_pll_config *cpg_pll_config;
u32 cpg_mode;
int error;
error = rcar_rst_read_mode_pins(&cpg_mode);
if (error)
return error;
cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)];
return rcar_gen2_cpg_init(cpg_pll_config, 2, cpg_mode);
}
const struct cpg_mssr_info r8a7743_cpg_mssr_info __initconst = {
/* Core Clocks */
.core_clks = r8a7743_core_clks,
.num_core_clks = ARRAY_SIZE(r8a7743_core_clks),
.last_dt_core_clk = LAST_DT_CORE_CLK,
.num_total_core_clks = MOD_CLK_BASE,
/* Module Clocks */
.mod_clks = r8a7743_mod_clks,
.num_mod_clks = ARRAY_SIZE(r8a7743_mod_clks),
.num_hw_mod_clks = 12 * 32,
/* Critical Module Clocks */
.crit_mod_clks = r8a7743_crit_mod_clks,
.num_crit_mod_clks = ARRAY_SIZE(r8a7743_crit_mod_clks),
/* Callbacks */
.init = r8a7743_cpg_mssr_init,
.cpg_clk_register = rcar_gen2_cpg_clk_register,
};

View File

@ -0,0 +1,259 @@
/*
* r8a7745 Clock Pulse Generator / Module Standby and Software Reset
*
* Copyright (C) 2016 Cogent Embedded Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation; of the License.
*/
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/soc/renesas/rcar-rst.h>
#include <dt-bindings/clock/r8a7745-cpg-mssr.h>
#include "renesas-cpg-mssr.h"
#include "rcar-gen2-cpg.h"
enum clk_ids {
/* Core Clock Outputs exported to DT */
LAST_DT_CORE_CLK = R8A7745_CLK_OSC,
/* External Input Clocks */
CLK_EXTAL,
CLK_USB_EXTAL,
/* Internal Core Clocks */
CLK_MAIN,
CLK_PLL0,
CLK_PLL1,
CLK_PLL3,
CLK_PLL1_DIV2,
/* Module Clocks */
MOD_CLK_BASE
};
static const struct cpg_core_clk r8a7745_core_clks[] __initconst = {
/* External Clock Inputs */
DEF_INPUT("extal", CLK_EXTAL),
DEF_INPUT("usb_extal", CLK_USB_EXTAL),
/* Internal Core Clocks */
DEF_BASE(".main", CLK_MAIN, CLK_TYPE_GEN2_MAIN, CLK_EXTAL),
DEF_BASE(".pll0", CLK_PLL0, CLK_TYPE_GEN2_PLL0, CLK_MAIN),
DEF_BASE(".pll1", CLK_PLL1, CLK_TYPE_GEN2_PLL1, CLK_MAIN),
DEF_BASE(".pll3", CLK_PLL3, CLK_TYPE_GEN2_PLL3, CLK_MAIN),
DEF_FIXED(".pll1_div2", CLK_PLL1_DIV2, CLK_PLL1, 2, 1),
/* Core Clock Outputs */
DEF_BASE("lb", R8A7745_CLK_LB, CLK_TYPE_GEN2_LB, CLK_PLL1),
DEF_BASE("sdh", R8A7745_CLK_SDH, CLK_TYPE_GEN2_SDH, CLK_PLL1),
DEF_BASE("sd0", R8A7745_CLK_SD0, CLK_TYPE_GEN2_SD0, CLK_PLL1),
DEF_BASE("qspi", R8A7745_CLK_QSPI, CLK_TYPE_GEN2_QSPI, CLK_PLL1_DIV2),
DEF_BASE("rcan", R8A7745_CLK_RCAN, CLK_TYPE_GEN2_RCAN, CLK_USB_EXTAL),
DEF_FIXED("z2", R8A7745_CLK_Z2, CLK_PLL0, 1, 1),
DEF_FIXED("zg", R8A7745_CLK_ZG, CLK_PLL1, 6, 1),
DEF_FIXED("zx", R8A7745_CLK_ZX, CLK_PLL1, 3, 1),
DEF_FIXED("zs", R8A7745_CLK_ZS, CLK_PLL1, 6, 1),
DEF_FIXED("hp", R8A7745_CLK_HP, CLK_PLL1, 12, 1),
DEF_FIXED("b", R8A7745_CLK_B, CLK_PLL1, 12, 1),
DEF_FIXED("p", R8A7745_CLK_P, CLK_PLL1, 24, 1),
DEF_FIXED("cl", R8A7745_CLK_CL, CLK_PLL1, 48, 1),
DEF_FIXED("cp", R8A7745_CLK_CP, CLK_PLL1, 48, 1),
DEF_FIXED("m2", R8A7745_CLK_M2, CLK_PLL1, 8, 1),
DEF_FIXED("zb3", R8A7745_CLK_ZB3, CLK_PLL3, 4, 1),
DEF_FIXED("zb3d2", R8A7745_CLK_ZB3D2, CLK_PLL3, 8, 1),
DEF_FIXED("ddr", R8A7745_CLK_DDR, CLK_PLL3, 8, 1),
DEF_FIXED("mp", R8A7745_CLK_MP, CLK_PLL1_DIV2, 15, 1),
DEF_FIXED("cpex", R8A7745_CLK_CPEX, CLK_EXTAL, 2, 1),
DEF_FIXED("r", R8A7745_CLK_R, CLK_PLL1, 49152, 1),
DEF_FIXED("osc", R8A7745_CLK_OSC, CLK_PLL1, 12288, 1),
DEF_DIV6P1("sd2", R8A7745_CLK_SD2, CLK_PLL1_DIV2, 0x078),
DEF_DIV6P1("sd3", R8A7745_CLK_SD3, CLK_PLL1_DIV2, 0x26c),
DEF_DIV6P1("mmc0", R8A7745_CLK_MMC0, CLK_PLL1_DIV2, 0x240),
};
static const struct mssr_mod_clk r8a7745_mod_clks[] __initconst = {
DEF_MOD("msiof0", 0, R8A7745_CLK_MP),
DEF_MOD("vcp0", 101, R8A7745_CLK_ZS),
DEF_MOD("vpc0", 103, R8A7745_CLK_ZS),
DEF_MOD("tmu1", 111, R8A7745_CLK_P),
DEF_MOD("3dg", 112, R8A7745_CLK_ZG),
DEF_MOD("2d-dmac", 115, R8A7745_CLK_ZS),
DEF_MOD("fdp1-0", 119, R8A7745_CLK_ZS),
DEF_MOD("tmu3", 121, R8A7745_CLK_P),
DEF_MOD("tmu2", 122, R8A7745_CLK_P),
DEF_MOD("cmt0", 124, R8A7745_CLK_R),
DEF_MOD("tmu0", 125, R8A7745_CLK_CP),
DEF_MOD("vsp1du0", 128, R8A7745_CLK_ZS),
DEF_MOD("vsp1-sy", 131, R8A7745_CLK_ZS),
DEF_MOD("scifa2", 202, R8A7745_CLK_MP),
DEF_MOD("scifa1", 203, R8A7745_CLK_MP),
DEF_MOD("scifa0", 204, R8A7745_CLK_MP),
DEF_MOD("msiof2", 205, R8A7745_CLK_MP),
DEF_MOD("scifb0", 206, R8A7745_CLK_MP),
DEF_MOD("scifb1", 207, R8A7745_CLK_MP),
DEF_MOD("msiof1", 208, R8A7745_CLK_MP),
DEF_MOD("scifb2", 216, R8A7745_CLK_MP),
DEF_MOD("sys-dmac1", 218, R8A7745_CLK_ZS),
DEF_MOD("sys-dmac0", 219, R8A7745_CLK_ZS),
DEF_MOD("tpu0", 304, R8A7745_CLK_CP),
DEF_MOD("sdhi3", 311, R8A7745_CLK_SD3),
DEF_MOD("sdhi2", 312, R8A7745_CLK_SD2),
DEF_MOD("sdhi0", 314, R8A7745_CLK_SD0),
DEF_MOD("mmcif0", 315, R8A7745_CLK_MMC0),
DEF_MOD("iic0", 318, R8A7745_CLK_HP),
DEF_MOD("iic1", 323, R8A7745_CLK_HP),
DEF_MOD("cmt1", 329, R8A7745_CLK_R),
DEF_MOD("usbhs-dmac0", 330, R8A7745_CLK_HP),
DEF_MOD("usbhs-dmac1", 331, R8A7745_CLK_HP),
DEF_MOD("irqc", 407, R8A7745_CLK_CP),
DEF_MOD("intc-sys", 408, R8A7745_CLK_ZS),
DEF_MOD("audio-dmac0", 502, R8A7745_CLK_HP),
DEF_MOD("pwm", 523, R8A7745_CLK_P),
DEF_MOD("usb-ehci", 703, R8A7745_CLK_MP),
DEF_MOD("usbhs", 704, R8A7745_CLK_HP),
DEF_MOD("hscif2", 713, R8A7745_CLK_ZS),
DEF_MOD("scif5", 714, R8A7745_CLK_P),
DEF_MOD("scif4", 715, R8A7745_CLK_P),
DEF_MOD("hscif1", 716, R8A7745_CLK_ZS),
DEF_MOD("hscif0", 717, R8A7745_CLK_ZS),
DEF_MOD("scif3", 718, R8A7745_CLK_P),
DEF_MOD("scif2", 719, R8A7745_CLK_P),
DEF_MOD("scif1", 720, R8A7745_CLK_P),
DEF_MOD("scif0", 721, R8A7745_CLK_P),
DEF_MOD("du0", 724, R8A7745_CLK_ZX),
DEF_MOD("ipmmu-sgx", 800, R8A7745_CLK_ZX),
DEF_MOD("vin1", 810, R8A7745_CLK_ZG),
DEF_MOD("vin0", 811, R8A7745_CLK_ZG),
DEF_MOD("etheravb", 812, R8A7745_CLK_HP),
DEF_MOD("ether", 813, R8A7745_CLK_P),
DEF_MOD("gpio6", 905, R8A7745_CLK_CP),
DEF_MOD("gpio5", 907, R8A7745_CLK_CP),
DEF_MOD("gpio4", 908, R8A7745_CLK_CP),
DEF_MOD("gpio3", 909, R8A7745_CLK_CP),
DEF_MOD("gpio2", 910, R8A7745_CLK_CP),
DEF_MOD("gpio1", 911, R8A7745_CLK_CP),
DEF_MOD("gpio0", 912, R8A7745_CLK_CP),
DEF_MOD("can1", 915, R8A7745_CLK_P),
DEF_MOD("can0", 916, R8A7745_CLK_P),
DEF_MOD("qspi_mod", 917, R8A7745_CLK_QSPI),
DEF_MOD("i2c5", 925, R8A7745_CLK_HP),
DEF_MOD("i2c4", 927, R8A7745_CLK_HP),
DEF_MOD("i2c3", 928, R8A7745_CLK_HP),
DEF_MOD("i2c2", 929, R8A7745_CLK_HP),
DEF_MOD("i2c1", 930, R8A7745_CLK_HP),
DEF_MOD("i2c0", 931, R8A7745_CLK_HP),
DEF_MOD("ssi-all", 1005, R8A7745_CLK_P),
DEF_MOD("ssi9", 1006, MOD_CLK_ID(1005)),
DEF_MOD("ssi8", 1007, MOD_CLK_ID(1005)),
DEF_MOD("ssi7", 1008, MOD_CLK_ID(1005)),
DEF_MOD("ssi6", 1009, MOD_CLK_ID(1005)),
DEF_MOD("ssi5", 1010, MOD_CLK_ID(1005)),
DEF_MOD("ssi4", 1011, MOD_CLK_ID(1005)),
DEF_MOD("ssi3", 1012, MOD_CLK_ID(1005)),
DEF_MOD("ssi2", 1013, MOD_CLK_ID(1005)),
DEF_MOD("ssi1", 1014, MOD_CLK_ID(1005)),
DEF_MOD("ssi0", 1015, MOD_CLK_ID(1005)),
DEF_MOD("scu-all", 1017, R8A7745_CLK_P),
DEF_MOD("scu-dvc1", 1018, MOD_CLK_ID(1017)),
DEF_MOD("scu-dvc0", 1019, MOD_CLK_ID(1017)),
DEF_MOD("scu-ctu1-mix1", 1020, MOD_CLK_ID(1017)),
DEF_MOD("scu-ctu0-mix0", 1021, MOD_CLK_ID(1017)),
DEF_MOD("scu-src9", 1022, MOD_CLK_ID(1017)),
DEF_MOD("scu-src8", 1023, MOD_CLK_ID(1017)),
DEF_MOD("scu-src7", 1024, MOD_CLK_ID(1017)),
DEF_MOD("scu-src6", 1025, MOD_CLK_ID(1017)),
DEF_MOD("scu-src5", 1026, MOD_CLK_ID(1017)),
DEF_MOD("scu-src4", 1027, MOD_CLK_ID(1017)),
DEF_MOD("scu-src3", 1028, MOD_CLK_ID(1017)),
DEF_MOD("scu-src2", 1029, MOD_CLK_ID(1017)),
DEF_MOD("scu-src1", 1030, MOD_CLK_ID(1017)),
DEF_MOD("scu-src0", 1031, MOD_CLK_ID(1017)),
DEF_MOD("scifa3", 1106, R8A7745_CLK_MP),
DEF_MOD("scifa4", 1107, R8A7745_CLK_MP),
DEF_MOD("scifa5", 1108, R8A7745_CLK_MP),
};
static const unsigned int r8a7745_crit_mod_clks[] __initconst = {
MOD_CLK_ID(408), /* INTC-SYS (GIC) */
};
/*
* CPG Clock Data
*/
/*
* MD EXTAL PLL0 PLL1 PLL3
* 14 13 19 (MHz) *1 *2
*---------------------------------------------------
* 0 0 0 15 x200/3 x208/2 x106
* 0 0 1 15 x200/3 x208/2 x88
* 0 1 0 20 x150/3 x156/2 x80
* 0 1 1 20 x150/3 x156/2 x66
* 1 0 0 26 / 2 x230/3 x240/2 x122
* 1 0 1 26 / 2 x230/3 x240/2 x102
* 1 1 0 30 / 2 x200/3 x208/2 x106
* 1 1 1 30 / 2 x200/3 x208/2 x88
*
* *1 : Table 7.5b indicates VCO output (PLL0 = VCO/3)
* *2 : Table 7.5b indicates VCO output (PLL1 = VCO/2)
*/
#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 12) | \
(((md) & BIT(13)) >> 12) | \
(((md) & BIT(19)) >> 19))
static const struct rcar_gen2_cpg_pll_config cpg_pll_configs[8] __initconst = {
/* EXTAL div PLL1 mult PLL3 mult PLL0 mult */
{ 1, 208, 106, 200 },
{ 1, 208, 88, 200 },
{ 1, 156, 80, 150 },
{ 1, 156, 66, 150 },
{ 2, 240, 122, 230 },
{ 2, 240, 102, 230 },
{ 2, 208, 106, 200 },
{ 2, 208, 88, 200 },
};
static int __init r8a7745_cpg_mssr_init(struct device *dev)
{
const struct rcar_gen2_cpg_pll_config *cpg_pll_config;
u32 cpg_mode;
int error;
error = rcar_rst_read_mode_pins(&cpg_mode);
if (error)
return error;
cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)];
return rcar_gen2_cpg_init(cpg_pll_config, 3, cpg_mode);
}
const struct cpg_mssr_info r8a7745_cpg_mssr_info __initconst = {
/* Core Clocks */
.core_clks = r8a7745_core_clks,
.num_core_clks = ARRAY_SIZE(r8a7745_core_clks),
.last_dt_core_clk = LAST_DT_CORE_CLK,
.num_total_core_clks = MOD_CLK_BASE,
/* Module Clocks */
.mod_clks = r8a7745_mod_clks,
.num_mod_clks = ARRAY_SIZE(r8a7745_mod_clks),
.num_hw_mod_clks = 12 * 32,
/* Critical Module Clocks */
.crit_mod_clks = r8a7745_crit_mod_clks,
.num_crit_mod_clks = ARRAY_SIZE(r8a7745_crit_mod_clks),
/* Callbacks */
.init = r8a7745_cpg_mssr_init,
.cpg_clk_register = rcar_gen2_cpg_clk_register,
};

View File

@ -15,6 +15,7 @@
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/soc/renesas/rcar-rst.h>
#include <dt-bindings/clock/r8a7795-cpg-mssr.h>
@ -97,7 +98,7 @@ static const struct cpg_core_clk r8a7795_core_clks[] __initconst = {
DEF_FIXED("cp", R8A7795_CLK_CP, CLK_EXTAL, 2, 1),
DEF_DIV6P1("mso", R8A7795_CLK_MSO, CLK_PLL1_DIV4, 0x014),
DEF_DIV6P1("hdmi", R8A7795_CLK_HDMI, CLK_PLL1_DIV2, 0x250),
DEF_DIV6P1("hdmi", R8A7795_CLK_HDMI, CLK_PLL1_DIV4, 0x250),
DEF_DIV6P1("canfd", R8A7795_CLK_CANFD, CLK_PLL1_DIV4, 0x244),
DEF_DIV6P1("csi0", R8A7795_CLK_CSI0, CLK_PLL1_DIV4, 0x00c),
@ -311,7 +312,12 @@ static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[16] __initconst = {
static int __init r8a7795_cpg_mssr_init(struct device *dev)
{
const struct rcar_gen3_cpg_pll_config *cpg_pll_config;
u32 cpg_mode = rcar_gen3_read_mode_pins();
u32 cpg_mode;
int error;
error = rcar_rst_read_mode_pins(&cpg_mode);
if (error)
return error;
cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)];
if (!cpg_pll_config->extal_div) {

View File

@ -16,6 +16,7 @@
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/soc/renesas/rcar-rst.h>
#include <dt-bindings/clock/r8a7796-cpg-mssr.h>
@ -102,6 +103,8 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = {
DEF_FIXED("cl", R8A7796_CLK_CL, CLK_PLL1_DIV2, 48, 1),
DEF_FIXED("cp", R8A7796_CLK_CP, CLK_EXTAL, 2, 1),
DEF_DIV6P1("csi0", R8A7796_CLK_CSI0, CLK_PLL1_DIV4, 0x00c),
DEF_DIV6_RO("osc", R8A7796_CLK_OSC, CLK_EXTAL, CPG_RCKCR, 8),
DEF_DIV6_RO("r_int", CLK_RINT, CLK_EXTAL, CPG_RCKCR, 32),
@ -109,6 +112,14 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = {
};
static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = {
DEF_MOD("scif5", 202, R8A7796_CLK_S3D4),
DEF_MOD("scif4", 203, R8A7796_CLK_S3D4),
DEF_MOD("scif3", 204, R8A7796_CLK_S3D4),
DEF_MOD("scif1", 206, R8A7796_CLK_S3D4),
DEF_MOD("scif0", 207, R8A7796_CLK_S3D4),
DEF_MOD("sys-dmac2", 217, R8A7796_CLK_S0D3),
DEF_MOD("sys-dmac1", 218, R8A7796_CLK_S0D3),
DEF_MOD("sys-dmac0", 219, R8A7796_CLK_S0D3),
DEF_MOD("cmt3", 300, R8A7796_CLK_R),
DEF_MOD("cmt2", 301, R8A7796_CLK_R),
DEF_MOD("cmt1", 302, R8A7796_CLK_R),
@ -120,7 +131,47 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = {
DEF_MOD("sdif0", 314, R8A7796_CLK_SD0),
DEF_MOD("rwdt0", 402, R8A7796_CLK_R),
DEF_MOD("intc-ap", 408, R8A7796_CLK_S3D1),
DEF_MOD("drif7", 508, R8A7796_CLK_S3D2),
DEF_MOD("drif6", 509, R8A7796_CLK_S3D2),
DEF_MOD("drif5", 510, R8A7796_CLK_S3D2),
DEF_MOD("drif4", 511, R8A7796_CLK_S3D2),
DEF_MOD("drif3", 512, R8A7796_CLK_S3D2),
DEF_MOD("drif2", 513, R8A7796_CLK_S3D2),
DEF_MOD("drif1", 514, R8A7796_CLK_S3D2),
DEF_MOD("drif0", 515, R8A7796_CLK_S3D2),
DEF_MOD("hscif4", 516, R8A7796_CLK_S3D1),
DEF_MOD("hscif3", 517, R8A7796_CLK_S3D1),
DEF_MOD("hscif2", 518, R8A7796_CLK_S3D1),
DEF_MOD("hscif1", 519, R8A7796_CLK_S3D1),
DEF_MOD("hscif0", 520, R8A7796_CLK_S3D1),
DEF_MOD("thermal", 522, R8A7796_CLK_CP),
DEF_MOD("fcpvd2", 601, R8A7796_CLK_S0D2),
DEF_MOD("fcpvd1", 602, R8A7796_CLK_S0D2),
DEF_MOD("fcpvd0", 603, R8A7796_CLK_S0D2),
DEF_MOD("fcpvb0", 607, R8A7796_CLK_S0D1),
DEF_MOD("fcpvi0", 611, R8A7796_CLK_S0D1),
DEF_MOD("fcpf0", 615, R8A7796_CLK_S0D1),
DEF_MOD("fcpci0", 617, R8A7796_CLK_S0D2),
DEF_MOD("fcpcs", 619, R8A7796_CLK_S0D2),
DEF_MOD("vspd2", 621, R8A7796_CLK_S0D2),
DEF_MOD("vspd1", 622, R8A7796_CLK_S0D2),
DEF_MOD("vspd0", 623, R8A7796_CLK_S0D2),
DEF_MOD("vspb", 626, R8A7796_CLK_S0D1),
DEF_MOD("vspi0", 631, R8A7796_CLK_S0D1),
DEF_MOD("csi20", 714, R8A7796_CLK_CSI0),
DEF_MOD("csi40", 716, R8A7796_CLK_CSI0),
DEF_MOD("du2", 722, R8A7796_CLK_S2D1),
DEF_MOD("du1", 723, R8A7796_CLK_S2D1),
DEF_MOD("du0", 724, R8A7796_CLK_S2D1),
DEF_MOD("lvds", 727, R8A7796_CLK_S2D1),
DEF_MOD("vin7", 804, R8A7796_CLK_S0D2),
DEF_MOD("vin6", 805, R8A7796_CLK_S0D2),
DEF_MOD("vin5", 806, R8A7796_CLK_S0D2),
DEF_MOD("vin4", 807, R8A7796_CLK_S0D2),
DEF_MOD("vin3", 808, R8A7796_CLK_S0D2),
DEF_MOD("vin2", 809, R8A7796_CLK_S0D2),
DEF_MOD("vin1", 810, R8A7796_CLK_S0D2),
DEF_MOD("vin0", 811, R8A7796_CLK_S0D2),
DEF_MOD("etheravb", 812, R8A7796_CLK_S0D6),
DEF_MOD("gpio7", 905, R8A7796_CLK_S3D4),
DEF_MOD("gpio6", 906, R8A7796_CLK_S3D4),
@ -130,6 +181,13 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = {
DEF_MOD("gpio2", 910, R8A7796_CLK_S3D4),
DEF_MOD("gpio1", 911, R8A7796_CLK_S3D4),
DEF_MOD("gpio0", 912, R8A7796_CLK_S3D4),
DEF_MOD("i2c6", 918, R8A7796_CLK_S0D6),
DEF_MOD("i2c5", 919, R8A7796_CLK_S0D6),
DEF_MOD("i2c4", 927, R8A7796_CLK_S0D6),
DEF_MOD("i2c3", 928, R8A7796_CLK_S0D6),
DEF_MOD("i2c2", 929, R8A7796_CLK_S3D2),
DEF_MOD("i2c1", 930, R8A7796_CLK_S3D2),
DEF_MOD("i2c0", 931, R8A7796_CLK_S3D2),
};
static const unsigned int r8a7796_crit_mod_clks[] __initconst = {
@ -190,7 +248,12 @@ static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[16] __initconst = {
static int __init r8a7796_cpg_mssr_init(struct device *dev)
{
const struct rcar_gen3_cpg_pll_config *cpg_pll_config;
u32 cpg_mode = rcar_gen3_read_mode_pins();
u32 cpg_mode;
int error;
error = rcar_rst_read_mode_pins(&cpg_mode);
if (error)
return error;
cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)];
if (!cpg_pll_config->extal_div) {

View File

@ -0,0 +1,371 @@
/*
* R-Car Gen2 Clock Pulse Generator
*
* Copyright (C) 2016 Cogent Embedded Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/bug.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/slab.h>
#include "renesas-cpg-mssr.h"
#include "rcar-gen2-cpg.h"
#define CPG_FRQCRB 0x0004
#define CPG_FRQCRB_KICK BIT(31)
#define CPG_SDCKCR 0x0074
#define CPG_PLL0CR 0x00d8
#define CPG_PLL0CR_STC_SHIFT 24
#define CPG_PLL0CR_STC_MASK (0x7f << CPG_PLL0CR_STC_SHIFT)
#define CPG_FRQCRC 0x00e0
#define CPG_FRQCRC_ZFC_SHIFT 8
#define CPG_FRQCRC_ZFC_MASK (0x1f << CPG_FRQCRC_ZFC_SHIFT)
#define CPG_ADSPCKCR 0x025c
#define CPG_RCANCKCR 0x0270
static spinlock_t cpg_lock;
/*
* Z Clock
*
* Traits of this clock:
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
* rate - rate is adjustable. clk->rate = parent->rate * mult / 32
* parent - fixed parent. No clk_set_parent support
*/
struct cpg_z_clk {
struct clk_hw hw;
void __iomem *reg;
void __iomem *kick_reg;
};
#define to_z_clk(_hw) container_of(_hw, struct cpg_z_clk, hw)
static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct cpg_z_clk *zclk = to_z_clk(hw);
unsigned int mult;
unsigned int val;
val = (readl(zclk->reg) & CPG_FRQCRC_ZFC_MASK) >> CPG_FRQCRC_ZFC_SHIFT;
mult = 32 - val;
return div_u64((u64)parent_rate * mult, 32);
}
static long cpg_z_clk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long prate = *parent_rate;
unsigned int mult;
if (!prate)
prate = 1;
mult = div_u64((u64)rate * 32, prate);
mult = clamp(mult, 1U, 32U);
return *parent_rate / 32 * mult;
}
static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct cpg_z_clk *zclk = to_z_clk(hw);
unsigned int mult;
u32 val, kick;
unsigned int i;
mult = div_u64((u64)rate * 32, parent_rate);
mult = clamp(mult, 1U, 32U);
if (readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
return -EBUSY;
val = readl(zclk->reg);
val &= ~CPG_FRQCRC_ZFC_MASK;
val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT;
writel(val, zclk->reg);
/*
* Set KICK bit in FRQCRB to update hardware setting and wait for
* clock change completion.
*/
kick = readl(zclk->kick_reg);
kick |= CPG_FRQCRB_KICK;
writel(kick, zclk->kick_reg);
/*
* Note: There is no HW information about the worst case latency.
*
* Using experimental measurements, it seems that no more than
* ~10 iterations are needed, independently of the CPU rate.
* Since this value might be dependent on external xtal rate, pll1
* rate or even the other emulation clocks rate, use 1000 as a
* "super" safe value.
*/
for (i = 1000; i; i--) {
if (!(readl(zclk->kick_reg) & CPG_FRQCRB_KICK))
return 0;
cpu_relax();
}
return -ETIMEDOUT;
}
static const struct clk_ops cpg_z_clk_ops = {
.recalc_rate = cpg_z_clk_recalc_rate,
.round_rate = cpg_z_clk_round_rate,
.set_rate = cpg_z_clk_set_rate,
};
static struct clk * __init cpg_z_clk_register(const char *name,
const char *parent_name,
void __iomem *base)
{
struct clk_init_data init;
struct cpg_z_clk *zclk;
struct clk *clk;
zclk = kzalloc(sizeof(*zclk), GFP_KERNEL);
if (!zclk)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &cpg_z_clk_ops;
init.flags = 0;
init.parent_names = &parent_name;
init.num_parents = 1;
zclk->reg = base + CPG_FRQCRC;
zclk->kick_reg = base + CPG_FRQCRB;
zclk->hw.init = &init;
clk = clk_register(NULL, &zclk->hw);
if (IS_ERR(clk))
kfree(zclk);
return clk;
}
static struct clk * __init cpg_rcan_clk_register(const char *name,
const char *parent_name,
void __iomem *base)
{
struct clk_fixed_factor *fixed;
struct clk_gate *gate;
struct clk *clk;
fixed = kzalloc(sizeof(*fixed), GFP_KERNEL);
if (!fixed)
return ERR_PTR(-ENOMEM);
fixed->mult = 1;
fixed->div = 6;
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate) {
kfree(fixed);
return ERR_PTR(-ENOMEM);
}
gate->reg = base + CPG_RCANCKCR;
gate->bit_idx = 8;
gate->flags = CLK_GATE_SET_TO_DISABLE;
gate->lock = &cpg_lock;
clk = clk_register_composite(NULL, name, &parent_name, 1, NULL, NULL,
&fixed->hw, &clk_fixed_factor_ops,
&gate->hw, &clk_gate_ops, 0);
if (IS_ERR(clk)) {
kfree(gate);
kfree(fixed);
}
return clk;
}
/* ADSP divisors */
static const struct clk_div_table cpg_adsp_div_table[] = {
{ 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 },
{ 5, 12 }, { 6, 16 }, { 7, 18 }, { 8, 24 },
{ 10, 36 }, { 11, 48 }, { 0, 0 },
};
static struct clk * __init cpg_adsp_clk_register(const char *name,
const char *parent_name,
void __iomem *base)
{
struct clk_divider *div;
struct clk_gate *gate;
struct clk *clk;
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
return ERR_PTR(-ENOMEM);
div->reg = base + CPG_ADSPCKCR;
div->width = 4;
div->table = cpg_adsp_div_table;
div->lock = &cpg_lock;
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate) {
kfree(div);
return ERR_PTR(-ENOMEM);
}
gate->reg = base + CPG_ADSPCKCR;
gate->bit_idx = 8;
gate->flags = CLK_GATE_SET_TO_DISABLE;
gate->lock = &cpg_lock;
clk = clk_register_composite(NULL, name, &parent_name, 1, NULL, NULL,
&div->hw, &clk_divider_ops,
&gate->hw, &clk_gate_ops, 0);
if (IS_ERR(clk)) {
kfree(gate);
kfree(div);
}
return clk;
}
/* SDHI divisors */
static const struct clk_div_table cpg_sdh_div_table[] = {
{ 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 },
{ 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 },
{ 8, 24 }, { 10, 36 }, { 11, 48 }, { 0, 0 },
};
static const struct clk_div_table cpg_sd01_div_table[] = {
{ 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 },
{ 8, 24 }, { 10, 36 }, { 11, 48 }, { 12, 10 },
{ 0, 0 },
};
static const struct rcar_gen2_cpg_pll_config *cpg_pll_config __initdata;
static unsigned int cpg_pll0_div __initdata;
static u32 cpg_mode __initdata;
struct clk * __init rcar_gen2_cpg_clk_register(struct device *dev,
const struct cpg_core_clk *core,
const struct cpg_mssr_info *info,
struct clk **clks,
void __iomem *base)
{
const struct clk_div_table *table = NULL;
const struct clk *parent;
const char *parent_name;
unsigned int mult = 1;
unsigned int div = 1;
unsigned int shift;
parent = clks[core->parent];
if (IS_ERR(parent))
return ERR_CAST(parent);
parent_name = __clk_get_name(parent);
switch (core->type) {
/* R-Car Gen2 */
case CLK_TYPE_GEN2_MAIN:
div = cpg_pll_config->extal_div;
break;
case CLK_TYPE_GEN2_PLL0:
/*
* PLL0 is a configurable multiplier clock except on R-Car
* V2H/E2. Register the PLL0 clock as a fixed factor clock for
* now as there's no generic multiplier clock implementation and
* we currently have no need to change the multiplier value.
*/
mult = cpg_pll_config->pll0_mult;
div = cpg_pll0_div;
if (!mult) {
u32 pll0cr = readl(base + CPG_PLL0CR);
mult = (((pll0cr & CPG_PLL0CR_STC_MASK) >>
CPG_PLL0CR_STC_SHIFT) + 1) * 2;
}
break;
case CLK_TYPE_GEN2_PLL1:
mult = cpg_pll_config->pll1_mult / 2;
break;
case CLK_TYPE_GEN2_PLL3:
mult = cpg_pll_config->pll3_mult;
break;
case CLK_TYPE_GEN2_Z:
return cpg_z_clk_register(core->name, parent_name, base);
case CLK_TYPE_GEN2_LB:
div = cpg_mode & BIT(18) ? 36 : 24;
break;
case CLK_TYPE_GEN2_ADSP:
return cpg_adsp_clk_register(core->name, parent_name, base);
case CLK_TYPE_GEN2_SDH:
table = cpg_sdh_div_table;
shift = 8;
break;
case CLK_TYPE_GEN2_SD0:
table = cpg_sd01_div_table;
shift = 4;
break;
case CLK_TYPE_GEN2_SD1:
table = cpg_sd01_div_table;
shift = 0;
break;
case CLK_TYPE_GEN2_QSPI:
div = (cpg_mode & (BIT(3) | BIT(2) | BIT(1))) == BIT(2) ?
8 : 10;
break;
case CLK_TYPE_GEN2_RCAN:
return cpg_rcan_clk_register(core->name, parent_name, base);
default:
return ERR_PTR(-EINVAL);
}
if (!table)
return clk_register_fixed_factor(NULL, core->name, parent_name,
0, mult, div);
else
return clk_register_divider_table(NULL, core->name,
parent_name, 0,
base + CPG_SDCKCR, shift, 4,
0, table, &cpg_lock);
}
int __init rcar_gen2_cpg_init(const struct rcar_gen2_cpg_pll_config *config,
unsigned int pll0_div, u32 mode)
{
cpg_pll_config = config;
cpg_pll0_div = pll0_div;
cpg_mode = mode;
spin_lock_init(&cpg_lock);
return 0;
}

View File

@ -0,0 +1,43 @@
/*
* R-Car Gen2 Clock Pulse Generator
*
* Copyright (C) 2016 Cogent Embedded Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation; version 2 of the License.
*/
#ifndef __CLK_RENESAS_RCAR_GEN2_CPG_H__
#define __CLK_RENESAS_RCAR_GEN2_CPG_H__
enum rcar_gen2_clk_types {
CLK_TYPE_GEN2_MAIN = CLK_TYPE_CUSTOM,
CLK_TYPE_GEN2_PLL0,
CLK_TYPE_GEN2_PLL1,
CLK_TYPE_GEN2_PLL3,
CLK_TYPE_GEN2_Z,
CLK_TYPE_GEN2_LB,
CLK_TYPE_GEN2_ADSP,
CLK_TYPE_GEN2_SDH,
CLK_TYPE_GEN2_SD0,
CLK_TYPE_GEN2_SD1,
CLK_TYPE_GEN2_QSPI,
CLK_TYPE_GEN2_RCAN,
};
struct rcar_gen2_cpg_pll_config {
unsigned int extal_div;
unsigned int pll1_mult;
unsigned int pll3_mult;
unsigned int pll0_mult; /* leave as zero if PLL0CR exists */
};
struct clk *rcar_gen2_cpg_clk_register(struct device *dev,
const struct cpg_core_clk *core,
const struct cpg_mssr_info *info,
struct clk **clks, void __iomem *base);
int rcar_gen2_cpg_init(const struct rcar_gen2_cpg_pll_config *config,
unsigned int pll0_div, u32 mode);
#endif

Some files were not shown because too many files have changed in this diff Show More