Commit 8f101aa0 authored by Michael Turquette's avatar Michael Turquette

Merge tag 'sunxi-clocks-for-3.20' of...

Merge tag 'sunxi-clocks-for-3.20' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux into clk-next

Allwinner clock changes for 3.20

The set of clock changes for the 3.20 merge window, with mostly:
  - Some PLL fixes for the A80 and A31
  - The MMC custom phase functions are removed, and moved over to the generic
    phase API.
  - Add the A80 MMC clocks

Some DT changes slipped here as well, to preserve bisectability.
parents b80418f3 76820fcf
......@@ -26,7 +26,7 @@ Required properties:
"allwinner,sun5i-a10s-ahb-gates-clk" - for the AHB gates on A10s
"allwinner,sun7i-a20-ahb-gates-clk" - for the AHB gates on A20
"allwinner,sun6i-a31-ar100-clk" - for the AR100 on A31
"allwinner,sun6i-a31-ahb1-mux-clk" - for the AHB1 multiplexer on A31
"allwinner,sun6i-a31-ahb1-clk" - for the AHB1 clock on A31
"allwinner,sun6i-a31-ahb1-gates-clk" - for the AHB1 gates on A31
"allwinner,sun8i-a23-ahb1-gates-clk" - for the AHB1 gates on A23
"allwinner,sun9i-a80-ahb0-gates-clk" - for the AHB0 gates on A80
......@@ -55,9 +55,11 @@ Required properties:
"allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31
"allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23
"allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13
"allwinner,sun4i-a10-mmc-output-clk" - for the MMC output clock on A10
"allwinner,sun4i-a10-mmc-sample-clk" - for the MMC sample clock on A10
"allwinner,sun4i-a10-mmc-clk" - for the MMC clock
"allwinner,sun9i-a80-mmc-clk" - for mmc module clocks on A80
"allwinner,sun9i-a80-mmc-config-clk" - for mmc gates + resets on A80
"allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks
"allwinner,sun9i-a80-mod0-clk" - for module 0 (storage) clocks on A80
"allwinner,sun8i-a23-mbus-clk" - for the MBUS clock on A23
"allwinner,sun7i-a20-out-clk" - for the external output clocks
"allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
......@@ -73,7 +75,9 @@ Required properties for all clocks:
- #clock-cells : from common clock binding; shall be set to 0 except for
the following compatibles where it shall be set to 1:
"allwinner,*-gates-clk", "allwinner,sun4i-pll5-clk",
"allwinner,sun4i-pll6-clk", "allwinner,sun6i-a31-pll6-clk"
"allwinner,sun4i-pll6-clk", "allwinner,sun6i-a31-pll6-clk",
"allwinner,*-usb-clk", "allwinner,*-mmc-clk",
"allwinner,*-mmc-config-clk"
- clock-output-names : shall be the corresponding names of the outputs.
If the clock module only has one output, the name shall be the
module name.
......@@ -81,6 +85,10 @@ Required properties for all clocks:
And "allwinner,*-usb-clk" clocks also require:
- reset-cells : shall be set to 1
The "allwinner,sun9i-a80-mmc-config-clk" clock also requires:
- #reset-cells : shall be set to 1
- resets : shall be the reset control phandle for the mmc block.
For "allwinner,sun7i-a20-gmac-clk", the parent clocks shall be fixed rate
dummy clocks at 25 MHz and 125 MHz, respectively. See example.
......@@ -95,6 +103,14 @@ For "allwinner,sun6i-a31-pll6-clk", there are 2 outputs. The first output
is the normal PLL6 output, or "pll6". The second output is rate doubled
PLL6, or "pll6x2".
The "allwinner,*-mmc-clk" clocks have three different outputs: the
main clock, with the ID 0, and the output and sample clocks, with the
IDs 1 and 2, respectively.
The "allwinner,sun9i-a80-mmc-config-clk" clock has one clock/reset output
per mmc controller. The number of outputs is determined by the size of
the address block, which is related to the overall mmc block.
For example:
osc24M: clk@01c20050 {
......@@ -138,11 +154,11 @@ cpu: cpu@01c20054 {
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
clock-output-names = "mmc0", "mmc0_output", "mmc0_sample";
};
mii_phy_tx_clk: clk@2 {
......@@ -170,3 +186,16 @@ gmac_clk: clk@01c20164 {
clocks = <&mii_phy_tx_clk>, <&gmac_int_tx_clk>;
clock-output-names = "gmac";
};
mmc_config_clk: clk@01c13000 {
compatible = "allwinner,sun9i-a80-mmc-config-clk";
reg = <0x01c13000 0x10>;
clocks = <&ahb0_gates 8>;
clock-names = "ahb";
resets = <&ahb0_resets 8>;
reset-names = "ahb";
#clock-cells = <1>;
#reset-cells = <1>;
clock-output-names = "mmc0_config", "mmc1_config",
"mmc2_config", "mmc3_config";
};
......@@ -10,8 +10,8 @@ Absolute maximum transfer rate is 200MB/s
Required properties:
- compatible : "allwinner,sun4i-a10-mmc" or "allwinner,sun5i-a13-mmc"
- reg : mmc controller base registers
- clocks : a list with 2 phandle + clock specifier pairs
- clock-names : must contain "ahb" and "mmc"
- clocks : a list with 4 phandle + clock specifier pairs
- clock-names : must contain "ahb", "mmc", "output" and "sample"
- interrupts : mmc controller interrupt
Optional properties:
......@@ -25,8 +25,8 @@ Examples:
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mod";
clocks = <&ahb_gates 8>, <&mmc0_clk>, <&mmc0_output_clk>, <&mmc0_sample_clk>;
clock-names = "ahb", "mod", "output", "sample";
interrupts = <0 32 4>;
status = "disabled";
};
......
......@@ -226,35 +226,43 @@ ms_clk: clk@01c20084 {
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
};
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1";
clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
};
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2";
clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
};
mmc3_clk: clk@01c20094 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20094 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc3";
clock-output-names = "mmc3",
"mmc3_output",
"mmc3_sample";
};
ts_clk: clk@01c20098 {
......@@ -398,8 +406,14 @@ mdio@01c0b080 {
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun4i-a10-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 8>,
<&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <32>;
status = "disabled";
};
......@@ -407,8 +421,14 @@ mmc0: mmc@01c0f000 {
mmc1: mmc@01c10000 {
compatible = "allwinner,sun4i-a10-mmc";
reg = <0x01c10000 0x1000>;
clocks = <&ahb_gates 9>, <&mmc1_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 9>,
<&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <33>;
status = "disabled";
};
......@@ -416,8 +436,14 @@ mmc1: mmc@01c10000 {
mmc2: mmc@01c11000 {
compatible = "allwinner,sun4i-a10-mmc";
reg = <0x01c11000 0x1000>;
clocks = <&ahb_gates 10>, <&mmc2_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 10>,
<&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <34>;
status = "disabled";
};
......@@ -425,8 +451,14 @@ mmc2: mmc@01c11000 {
mmc3: mmc@01c12000 {
compatible = "allwinner,sun4i-a10-mmc";
reg = <0x01c12000 0x1000>;
clocks = <&ahb_gates 11>, <&mmc3_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 11>,
<&mmc3_clk 0>,
<&mmc3_clk 1>,
<&mmc3_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <35>;
status = "disabled";
};
......
......@@ -211,27 +211,33 @@ ms_clk: clk@01c20084 {
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
};
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1";
clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
};
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2";
clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
};
ts_clk: clk@01c20098 {
......@@ -359,8 +365,14 @@ mdio@01c0b080 {
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 8>,
<&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <32>;
status = "disabled";
};
......@@ -368,8 +380,14 @@ mmc0: mmc@01c0f000 {
mmc1: mmc@01c10000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c10000 0x1000>;
clocks = <&ahb_gates 9>, <&mmc1_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 9>,
<&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <33>;
status = "disabled";
};
......@@ -377,8 +395,14 @@ mmc1: mmc@01c10000 {
mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>;
clocks = <&ahb_gates 10>, <&mmc2_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 10>,
<&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <34>;
status = "disabled";
};
......
......@@ -195,27 +195,33 @@ ms_clk: clk@01c20084 {
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
};
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1";
clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
};
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2";
clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
};
ts_clk: clk@01c20098 {
......@@ -327,8 +333,14 @@ spi1: spi@01c06000 {
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 8>,
<&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <32>;
status = "disabled";
};
......@@ -336,8 +348,14 @@ mmc0: mmc@01c0f000 {
mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>;
clocks = <&ahb_gates 10>, <&mmc2_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 10>,
<&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <34>;
status = "disabled";
};
......
......@@ -174,19 +174,11 @@ axi: axi@01c20050 {
clock-output-names = "axi";
};
ahb1_mux: ahb1_mux@01c20054 {
#clock-cells = <0>;
compatible = "allwinner,sun6i-a31-ahb1-mux-clk";
reg = <0x01c20054 0x4>;
clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>;
clock-output-names = "ahb1_mux";
};
ahb1: ahb1@01c20054 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-ahb-clk";
compatible = "allwinner,sun6i-a31-ahb1-clk";
reg = <0x01c20054 0x4>;
clocks = <&ahb1_mux>;
clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>;
clock-output-names = "ahb1";
};
......@@ -249,35 +241,43 @@ apb2_gates: clk@01c2006c {
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc0";
clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
};
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc1";
clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
};
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc2";
clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
};
mmc3_clk: clk@01c20094 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20094 0x4>;
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc3";
clock-output-names = "mmc3",
"mmc3_output",
"mmc3_sample";
};
spi0_clk: clk@01c200a0 {
......@@ -367,15 +367,21 @@ dma: dma-controller@01c02000 {
#dma-cells = <1>;
/* DMA controller requires AHB1 clocked from PLL6 */
assigned-clocks = <&ahb1_mux>;
assigned-clocks = <&ahb1>;
assigned-clock-parents = <&pll6 0>;
};
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb1_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 8>,
<&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 8>;
reset-names = "ahb";
interrupts = <0 60 4>;
......@@ -385,8 +391,14 @@ mmc0: mmc@01c0f000 {
mmc1: mmc@01c10000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c10000 0x1000>;
clocks = <&ahb1_gates 9>, <&mmc1_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 9>,
<&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 9>;
reset-names = "ahb";
interrupts = <0 61 4>;
......@@ -396,8 +408,14 @@ mmc1: mmc@01c10000 {
mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>;
clocks = <&ahb1_gates 10>, <&mmc2_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 10>,
<&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 10>;
reset-names = "ahb";
interrupts = <0 62 4>;
......@@ -407,8 +425,14 @@ mmc2: mmc@01c11000 {
mmc3: mmc@01c12000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c12000 0x1000>;
clocks = <&ahb1_gates 11>, <&mmc3_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 11>,
<&mmc3_clk 0>,
<&mmc3_clk 1>,
<&mmc3_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 11>;
reset-names = "ahb";
interrupts = <0 63 4>;
......
......@@ -274,35 +274,43 @@ ms_clk: clk@01c20084 {
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0";
clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
};
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1";
clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
};
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2";
clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
};
mmc3_clk: clk@01c20094 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20094 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc3";
clock-output-names = "mmc3",
"mmc3_output",
"mmc3_sample";
};
ts_clk: clk@01c20098 {
......@@ -518,8 +526,14 @@ mdio@01c0b080 {
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 8>,
<&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <0 32 4>;
status = "disabled";
};
......@@ -527,8 +541,14 @@ mmc0: mmc@01c0f000 {
mmc1: mmc@01c10000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c10000 0x1000>;
clocks = <&ahb_gates 9>, <&mmc1_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 9>,
<&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <0 33 4>;
status = "disabled";
};
......@@ -536,8 +556,14 @@ mmc1: mmc@01c10000 {
mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>;
clocks = <&ahb_gates 10>, <&mmc2_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 10>,
<&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <0 34 4>;
status = "disabled";
};
......@@ -545,8 +571,14 @@ mmc2: mmc@01c11000 {
mmc3: mmc@01c12000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c12000 0x1000>;
clocks = <&ahb_gates 11>, <&mmc3_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb_gates 11>,
<&mmc3_clk 0>,
<&mmc3_clk 1>,
<&mmc3_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <0 35 4>;
status = "disabled";
};
......
......@@ -110,11 +110,19 @@ pll1: clk@01c20000 {
};
/* dummy clock until actually implemented */
pll6: pll6_clk {
pll5: pll5_clk {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <600000000>;
clock-output-names = "pll6";
clock-frequency = <0>;
clock-output-names = "pll5";
};
pll6: clk@01c20028 {
#clock-cells = <1>;
compatible = "allwinner,sun6i-a31-pll6-clk";
reg = <0x01c20028 0x4>;
clocks = <&osc24M>;
clock-output-names = "pll6", "pll6x2";
};
cpu: cpu_clk@01c20050 {
......@@ -140,19 +148,11 @@ axi: axi_clk@01c20050 {
clock-output-names = "axi";
};
ahb1_mux: ahb1_mux_clk@01c20054 {
#clock-cells = <0>;
compatible = "allwinner,sun6i-a31-ahb1-mux-clk";
reg = <0x01c20054 0x4>;
clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6>;
clock-output-names = "ahb1_mux";
};
ahb1: ahb1_clk@01c20054 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-ahb-clk";
compatible = "allwinner,sun6i-a31-ahb1-clk";
reg = <0x01c20054 0x4>;
clocks = <&ahb1_mux>;
clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>;
clock-output-names = "ahb1";
};
......@@ -193,7 +193,7 @@ apb2: clk@01c20058 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-apb1-clk";
reg = <0x01c20058 0x4>;
clocks = <&osc32k>, <&osc24M>, <&pll6>, <&pll6>;
clocks = <&osc32k>, <&osc24M>, <&pll6 0>, <&pll6 0>;
clock-output-names = "apb2";
};
......@@ -209,27 +209,41 @@ apb2_gates: clk@01c2006c {
};
mmc0_clk: clk@01c20088 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6>;
clock-output-names = "mmc0";
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
};
mmc1_clk: clk@01c2008c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6>;
clock-output-names = "mmc1";
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
};
mmc2_clk: clk@01c20090 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-mod0-clk";
#clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6>;
clock-output-names = "mmc2";
clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
};
mbus_clk: clk@01c2015c {
#clock-cells = <0>;
compatible = "allwinner,sun8i-a23-mbus-clk";
reg = <0x01c2015c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5>;
clock-output-names = "mbus";
};
};
......@@ -251,8 +265,14 @@ dma: dma-controller@01c02000 {
mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>;
clocks = <&ahb1_gates 8>, <&mmc0_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 8>,
<&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 8>;
reset-names = "ahb";
interrupts = <0 60 4>;
......@@ -262,8 +282,14 @@ mmc0: mmc@01c0f000 {
mmc1: mmc@01c10000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c10000 0x1000>;
clocks = <&ahb1_gates 9>, <&mmc1_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 9>,
<&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 9>;
reset-names = "ahb";
interrupts = <0 61 4>;
......@@ -273,8 +299,14 @@ mmc1: mmc@01c10000 {
mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>;
clocks = <&ahb1_gates 10>, <&mmc2_clk>;
clock-names = "ahb", "mmc";
clocks = <&ahb1_gates 10>,
<&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 10>;
reset-names = "ahb";
interrupts = <0 62 4>;
......
......@@ -8,6 +8,7 @@ obj-y += clk-a20-gmac.o
obj-y += clk-mod0.o
obj-y += clk-sun8i-mbus.o
obj-y += clk-sun9i-core.o
obj-y += clk-sun9i-mmc.o
obj-$(CONFIG_MFD_SUN6I_PRCM) += \
clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \
......
......@@ -156,9 +156,10 @@ static const struct clk_ops clk_factors_ops = {
.set_rate = clk_factors_set_rate,
};
struct clk * __init sunxi_factors_register(struct device_node *node,
const struct factors_data *data,
spinlock_t *lock)
struct clk *sunxi_factors_register(struct device_node *node,
const struct factors_data *data,
spinlock_t *lock,
void __iomem *reg)
{
struct clk *clk;
struct clk_factors *factors;
......@@ -168,11 +169,8 @@ struct clk * __init sunxi_factors_register(struct device_node *node,
struct clk_hw *mux_hw = NULL;
const char *clk_name = node->name;
const char *parents[FACTORS_MAX_PARENTS];
void __iomem *reg;
int i = 0;
reg = of_iomap(node, 0);
/* if we have a mux, we will have >1 parents */
while (i < FACTORS_MAX_PARENTS &&
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
......
......@@ -36,8 +36,9 @@ struct clk_factors {
spinlock_t *lock;
};
struct clk * __init sunxi_factors_register(struct device_node *node,
const struct factors_data *data,
spinlock_t *lock);
struct clk *sunxi_factors_register(struct device_node *node,
const struct factors_data *data,
spinlock_t *lock,
void __iomem *reg);
#endif
......@@ -17,6 +17,7 @@
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include "clk-factors.h"
......@@ -67,7 +68,7 @@ static struct clk_factors_config sun4i_a10_mod0_config = {
.pwidth = 2,
};
static const struct factors_data sun4i_a10_mod0_data __initconst = {
static const struct factors_data sun4i_a10_mod0_data = {
.enable = 31,
.mux = 24,
.muxmask = BIT(1) | BIT(0),
......@@ -79,15 +80,95 @@ static DEFINE_SPINLOCK(sun4i_a10_mod0_lock);
static void __init sun4i_a10_mod0_setup(struct device_node *node)
{
sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun4i_a10_mod0_lock);
void __iomem *reg;
reg = of_iomap(node, 0);
if (!reg) {
/*
* This happens with mod0 clk nodes instantiated through
* mfd, as those do not have their resources assigned at
* CLK_OF_DECLARE time yet, so do not print an error.
*/
return;
}
sunxi_factors_register(node, &sun4i_a10_mod0_data,
&sun4i_a10_mod0_lock, reg);
}
CLK_OF_DECLARE(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", sun4i_a10_mod0_setup);
static int sun4i_a10_mod0_clk_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct resource *r;
void __iomem *reg;
if (!np)
return -ENODEV;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(reg))
return PTR_ERR(reg);
sunxi_factors_register(np, &sun4i_a10_mod0_data,
&sun4i_a10_mod0_lock, reg);
return 0;
}
static const struct of_device_id sun4i_a10_mod0_clk_dt_ids[] = {
{ .compatible = "allwinner,sun4i-a10-mod0-clk" },
{ /* sentinel */ }
};
static struct platform_driver sun4i_a10_mod0_clk_driver = {
.driver = {
.name = "sun4i-a10-mod0-clk",
.of_match_table = sun4i_a10_mod0_clk_dt_ids,
},
.probe = sun4i_a10_mod0_clk_probe,
};
module_platform_driver(sun4i_a10_mod0_clk_driver);
static const struct factors_data sun9i_a80_mod0_data __initconst = {
.enable = 31,
.mux = 24,
.muxmask = BIT(3) | BIT(2) | BIT(1) | BIT(0),
.table = &sun4i_a10_mod0_config,
.getter = sun4i_a10_get_mod0_factors,
};
static void __init sun9i_a80_mod0_setup(struct device_node *node)
{
void __iomem *reg;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (IS_ERR(reg)) {
pr_err("Could not get registers for mod0-clk: %s\n",
node->name);
return;
}
sunxi_factors_register(node, &sun9i_a80_mod0_data,
&sun4i_a10_mod0_lock, reg);
}
CLK_OF_DECLARE(sun9i_a80_mod0, "allwinner,sun9i-a80-mod0-clk", sun9i_a80_mod0_setup);
static DEFINE_SPINLOCK(sun5i_a13_mbus_lock);
static void __init sun5i_a13_mbus_setup(struct device_node *node)
{
struct clk *mbus = sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun5i_a13_mbus_lock);
struct clk *mbus;
void __iomem *reg;
reg = of_iomap(node, 0);
if (!reg) {
pr_err("Could not get registers for a13-mbus-clk\n");
return;
}
mbus = sunxi_factors_register(node, &sun4i_a10_mod0_data,
&sun5i_a13_mbus_lock, reg);
/* The MBUS clocks needs to be always enabled */
__clk_get(mbus);
......@@ -95,14 +176,10 @@ static void __init sun5i_a13_mbus_setup(struct device_node *node)
}
CLK_OF_DECLARE(sun5i_a13_mbus, "allwinner,sun5i-a13-mbus-clk", sun5i_a13_mbus_setup);
struct mmc_phase_data {
u8 offset;
};
struct mmc_phase {
struct clk_hw hw;
u8 offset;
void __iomem *reg;
struct mmc_phase_data *data;
spinlock_t *lock;
};
......@@ -118,7 +195,7 @@ static int mmc_get_phase(struct clk_hw *hw)
u8 delay;
value = readl(phase->reg);
delay = (value >> phase->data->offset) & 0x3;
delay = (value >> phase->offset) & 0x3;
if (!delay)
return 180;
......@@ -206,8 +283,8 @@ static int mmc_set_phase(struct clk_hw *hw, int degrees)
spin_lock_irqsave(phase->lock, flags);
value = readl(phase->reg);
value &= ~GENMASK(phase->data->offset + 3, phase->data->offset);
value |= delay << phase->data->offset;
value &= ~GENMASK(phase->offset + 3, phase->offset);
value |= delay << phase->offset;
writel(value, phase->reg);
spin_unlock_irqrestore(phase->lock, flags);
......@@ -219,66 +296,97 @@ static const struct clk_ops mmc_clk_ops = {
.set_phase = mmc_set_phase,
};
static void __init sun4i_a10_mmc_phase_setup(struct device_node *node,
struct mmc_phase_data *data)
/*
* sunxi_mmc_setup - Common setup function for mmc module clocks
*
* The only difference between module clocks on different platforms is the
* width of the mux register bits and the valid values, which are passed in
* through struct factors_data. The phase clocks parts are identical.
*/
static void __init sunxi_mmc_setup(struct device_node *node,
const struct factors_data *data,
spinlock_t *lock)
{
const char *parent_names[1] = { of_clk_get_parent_name(node, 0) };
struct clk_init_data init = {
.num_parents = 1,
.parent_names = parent_names,
.ops = &mmc_clk_ops,
};
struct mmc_phase *phase;
struct clk *clk;
phase = kmalloc(sizeof(*phase), GFP_KERNEL);
if (!phase)
struct clk_onecell_data *clk_data;
const char *parent;
void __iomem *reg;
int i;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (IS_ERR(reg)) {
pr_err("Couldn't map the %s clock registers\n", node->name);
return;
}
phase->hw.init = &init;
phase->reg = of_iomap(node, 0);
if (!phase->reg)
goto err_free;
phase->data = data;
phase->lock = &sun4i_a10_mod0_lock;
if (of_property_read_string(node, "clock-output-names", &init.name))
init.name = node->name;
clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
return;
clk = clk_register(NULL, &phase->hw);
if (IS_ERR(clk))
goto err_unmap;
clk_data->clks = kcalloc(3, sizeof(*clk_data->clks), GFP_KERNEL);
if (!clk_data->clks)
goto err_free_data;
clk_data->clk_num = 3;
clk_data->clks[0] = sunxi_factors_register(node, data, lock, reg);
if (!clk_data->clks[0])
goto err_free_clks;
parent = __clk_get_name(clk_data->clks[0]);
for (i = 1; i < 3; i++) {
struct clk_init_data init = {
.num_parents = 1,
.parent_names = &parent,
.ops = &mmc_clk_ops,
};
struct mmc_phase *phase;
phase = kmalloc(sizeof(*phase), GFP_KERNEL);
if (!phase)
continue;
phase->hw.init = &init;
phase->reg = reg;
phase->lock = lock;
if (i == 1)
phase->offset = 8;
else
phase->offset = 20;
if (of_property_read_string_index(node, "clock-output-names",
i, &init.name))
init.name = node->name;
clk_data->clks[i] = clk_register(NULL, &phase->hw);
if (IS_ERR(clk_data->clks[i])) {
kfree(phase);
continue;
}
}
of_clk_add_provider(node, of_clk_src_simple_get, clk);
of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
return;
err_unmap:
iounmap(phase->reg);
err_free:
kfree(phase);
err_free_clks:
kfree(clk_data->clks);
err_free_data:
kfree(clk_data);
}
static DEFINE_SPINLOCK(sun4i_a10_mmc_lock);
static struct mmc_phase_data mmc_output_clk = {
.offset = 8,
};
static struct mmc_phase_data mmc_sample_clk = {
.offset = 20,
};
static void __init sun4i_a10_mmc_output_setup(struct device_node *node)
static void __init sun4i_a10_mmc_setup(struct device_node *node)
{
sun4i_a10_mmc_phase_setup(node, &mmc_output_clk);
sunxi_mmc_setup(node, &sun4i_a10_mod0_data, &sun4i_a10_mmc_lock);
}
CLK_OF_DECLARE(sun4i_a10_mmc_output, "allwinner,sun4i-a10-mmc-output-clk", sun4i_a10_mmc_output_setup);
CLK_OF_DECLARE(sun4i_a10_mmc, "allwinner,sun4i-a10-mmc-clk", sun4i_a10_mmc_setup);
static DEFINE_SPINLOCK(sun9i_a80_mmc_lock);
static void __init sun4i_a10_mmc_sample_setup(struct device_node *node)
static void __init sun9i_a80_mmc_setup(struct device_node *node)
{
sun4i_a10_mmc_phase_setup(node, &mmc_sample_clk);
sunxi_mmc_setup(node, &sun9i_a80_mod0_data, &sun9i_a80_mmc_lock);
}
CLK_OF_DECLARE(sun4i_a10_mmc_sample, "allwinner,sun4i-a10-mmc-sample-clk", sun4i_a10_mmc_sample_setup);
CLK_OF_DECLARE(sun9i_a80_mmc, "allwinner,sun9i-a80-mmc-clk", sun9i_a80_mmc_setup);
......@@ -69,8 +69,17 @@ static DEFINE_SPINLOCK(sun8i_a23_mbus_lock);
static void __init sun8i_a23_mbus_setup(struct device_node *node)
{
struct clk *mbus = sunxi_factors_register(node, &sun8i_a23_mbus_data,
&sun8i_a23_mbus_lock);
struct clk *mbus;
void __iomem *reg;
reg = of_iomap(node, 0);
if (!reg) {
pr_err("Could not get registers for a23-mbus-clk\n");
return;
}
mbus = sunxi_factors_register(node, &sun8i_a23_mbus_data,
&sun8i_a23_mbus_lock, reg);
/* The MBUS clocks needs to be always enabled */
__clk_get(mbus);
......
......@@ -24,50 +24,51 @@
/**
* sun9i_a80_get_pll4_factors() - calculates n, p, m factors for PLL1
* sun9i_a80_get_pll4_factors() - calculates n, p, m factors for PLL4
* PLL4 rate is calculated as follows
* rate = (parent_rate * n >> p) / (m + 1);
* parent_rate is always 24Mhz
* parent_rate is always 24MHz
*
* p and m are named div1 and div2 in Allwinner's SDK
*/
static void sun9i_a80_get_pll4_factors(u32 *freq, u32 parent_rate,
u8 *n, u8 *k, u8 *m, u8 *p)
u8 *n_ret, u8 *k, u8 *m_ret, u8 *p_ret)
{
int div;
int n;
int m = 1;
int p = 1;
/* Normalize value to a 6M multiple */
div = DIV_ROUND_UP(*freq, 6000000);
/* Normalize value to a 6 MHz multiple (24 MHz / 4) */
n = DIV_ROUND_UP(*freq, 6000000);
/* divs above 256 cannot be odd */
if (div > 256)
div = round_up(div, 2);
/* If n is too large switch to steps of 12 MHz */
if (n > 255) {
m = 0;
n = (n + 1) / 2;
}
/* If n is still too large switch to steps of 24 MHz */
if (n > 255) {
p = 0;
n = (n + 1) / 2;
}
/* divs above 512 must be a multiple of 4 */
if (div > 512)
div = round_up(div, 4);
/* n must be between 12 and 255 */
if (n > 255)
n = 255;
else if (n < 12)
n = 12;
*freq = 6000000 * div;
*freq = ((24000000 * n) >> p) / (m + 1);
/* we were called to round the frequency, we can now return */
if (n == NULL)
if (n_ret == NULL)
return;
/* p will be 1 for divs under 512 */
if (div < 512)
*p = 1;
else
*p = 0;
/* m will be 1 if div is odd */
if (div & 1)
*m = 1;
else
*m = 0;
/* calculate a suitable n based on m and p */
*n = div / (*p + 1) / (*m + 1);
*n_ret = n;
*m_ret = m;
*p_ret = p;
}
static struct clk_factors_config sun9i_a80_pll4_config = {
......@@ -89,7 +90,17 @@ static DEFINE_SPINLOCK(sun9i_a80_pll4_lock);
static void __init sun9i_a80_pll4_setup(struct device_node *node)
{
sunxi_factors_register(node, &sun9i_a80_pll4_data, &sun9i_a80_pll4_lock);
void __iomem *reg;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (!reg) {
pr_err("Could not get registers for a80-pll4-clk: %s\n",
node->name);
return;
}
sunxi_factors_register(node, &sun9i_a80_pll4_data,
&sun9i_a80_pll4_lock, reg);
}
CLK_OF_DECLARE(sun9i_a80_pll4, "allwinner,sun9i-a80-pll4-clk", sun9i_a80_pll4_setup);
......@@ -139,8 +150,18 @@ static DEFINE_SPINLOCK(sun9i_a80_gt_lock);
static void __init sun9i_a80_gt_setup(struct device_node *node)
{
struct clk *gt = sunxi_factors_register(node, &sun9i_a80_gt_data,
&sun9i_a80_gt_lock);
void __iomem *reg;
struct clk *gt;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (!reg) {
pr_err("Could not get registers for a80-gt-clk: %s\n",
node->name);
return;
}
gt = sunxi_factors_register(node, &sun9i_a80_gt_data,
&sun9i_a80_gt_lock, reg);
/* The GT bus clock needs to be always enabled */
__clk_get(gt);
......@@ -194,7 +215,17 @@ static DEFINE_SPINLOCK(sun9i_a80_ahb_lock);
static void __init sun9i_a80_ahb_setup(struct device_node *node)
{
sunxi_factors_register(node, &sun9i_a80_ahb_data, &sun9i_a80_ahb_lock);
void __iomem *reg;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (!reg) {
pr_err("Could not get registers for a80-ahb-clk: %s\n",
node->name);
return;
}
sunxi_factors_register(node, &sun9i_a80_ahb_data,
&sun9i_a80_ahb_lock, reg);
}
CLK_OF_DECLARE(sun9i_a80_ahb, "allwinner,sun9i-a80-ahb-clk", sun9i_a80_ahb_setup);
......@@ -210,7 +241,17 @@ static DEFINE_SPINLOCK(sun9i_a80_apb0_lock);
static void __init sun9i_a80_apb0_setup(struct device_node *node)
{
sunxi_factors_register(node, &sun9i_a80_apb0_data, &sun9i_a80_apb0_lock);
void __iomem *reg;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (!reg) {
pr_err("Could not get registers for a80-apb0-clk: %s\n",
node->name);
return;
}
sunxi_factors_register(node, &sun9i_a80_apb0_data,
&sun9i_a80_apb0_lock, reg);
}
CLK_OF_DECLARE(sun9i_a80_apb0, "allwinner,sun9i-a80-apb0-clk", sun9i_a80_apb0_setup);
......@@ -266,6 +307,16 @@ static DEFINE_SPINLOCK(sun9i_a80_apb1_lock);
static void __init sun9i_a80_apb1_setup(struct device_node *node)
{
sunxi_factors_register(node, &sun9i_a80_apb1_data, &sun9i_a80_apb1_lock);
void __iomem *reg;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
if (!reg) {
pr_err("Could not get registers for a80-apb1-clk: %s\n",
node->name);
return;
}
sunxi_factors_register(node, &sun9i_a80_apb1_data,
&sun9i_a80_apb1_lock, reg);
}
CLK_OF_DECLARE(sun9i_a80_apb1, "allwinner,sun9i-a80-apb1-clk", sun9i_a80_apb1_setup);
/*
* Copyright 2015 Chen-Yu Tsai
*
* Chen-Yu Tsai <wens@csie.org>
*
* 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.
*/
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/reset.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/spinlock.h>
#define SUN9I_MMC_WIDTH 4
#define SUN9I_MMC_GATE_BIT 16
#define SUN9I_MMC_RESET_BIT 18
struct sun9i_mmc_clk_data {
spinlock_t lock;
void __iomem *membase;
struct clk *clk;
struct reset_control *reset;
struct clk_onecell_data clk_data;
struct reset_controller_dev rcdev;
};
static int sun9i_mmc_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct sun9i_mmc_clk_data *data = container_of(rcdev,
struct sun9i_mmc_clk_data,
rcdev);
unsigned long flags;
void __iomem *reg = data->membase + SUN9I_MMC_WIDTH * id;
u32 val;
clk_prepare_enable(data->clk);
spin_lock_irqsave(&data->lock, flags);
val = readl(reg);
writel(val & ~BIT(SUN9I_MMC_RESET_BIT), reg);
spin_unlock_irqrestore(&data->lock, flags);
clk_disable_unprepare(data->clk);
return 0;
}
static int sun9i_mmc_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct sun9i_mmc_clk_data *data = container_of(rcdev,
struct sun9i_mmc_clk_data,
rcdev);
unsigned long flags;
void __iomem *reg = data->membase + SUN9I_MMC_WIDTH * id;
u32 val;
clk_prepare_enable(data->clk);
spin_lock_irqsave(&data->lock, flags);
val = readl(reg);
writel(val | BIT(SUN9I_MMC_RESET_BIT), reg);
spin_unlock_irqrestore(&data->lock, flags);
clk_disable_unprepare(data->clk);
return 0;
}
static struct reset_control_ops sun9i_mmc_reset_ops = {
.assert = sun9i_mmc_reset_assert,
.deassert = sun9i_mmc_reset_deassert,
};
static int sun9i_a80_mmc_config_clk_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct sun9i_mmc_clk_data *data;
struct clk_onecell_data *clk_data;
const char *clk_name = np->name;
const char *clk_parent;
struct resource *r;
int count, i, ret;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
spin_lock_init(&data->lock);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/* one clock/reset pair per word */
count = DIV_ROUND_UP((r->end - r->start + 1), SUN9I_MMC_WIDTH);
data->membase = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(data->membase))
return PTR_ERR(data->membase);
clk_data = &data->clk_data;
clk_data->clk_num = count;
clk_data->clks = devm_kcalloc(&pdev->dev, count, sizeof(struct clk *),
GFP_KERNEL);
if (!clk_data->clks)
return -ENOMEM;
data->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(data->clk)) {
dev_err(&pdev->dev, "Could not get clock\n");
return PTR_ERR(data->clk);
}
data->reset = devm_reset_control_get(&pdev->dev, NULL);
if (IS_ERR(data->reset)) {
dev_err(&pdev->dev, "Could not get reset control\n");
return PTR_ERR(data->reset);
}
ret = reset_control_deassert(data->reset);
if (ret) {
dev_err(&pdev->dev, "Reset deassert err %d\n", ret);
return ret;
}
clk_parent = __clk_get_name(data->clk);
for (i = 0; i < count; i++) {
of_property_read_string_index(np, "clock-output-names",
i, &clk_name);
clk_data->clks[i] = clk_register_gate(&pdev->dev, clk_name,
clk_parent, 0,
data->membase + SUN9I_MMC_WIDTH * i,
SUN9I_MMC_GATE_BIT, 0,
&data->lock);
if (IS_ERR(clk_data->clks[i])) {
ret = PTR_ERR(clk_data->clks[i]);
goto err_clk_register;
}
}
ret = of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
if (ret)
goto err_clk_provider;
data->rcdev.owner = THIS_MODULE;
data->rcdev.nr_resets = count;
data->rcdev.ops = &sun9i_mmc_reset_ops;
data->rcdev.of_node = pdev->dev.of_node;
ret = reset_controller_register(&data->rcdev);
if (ret)
goto err_rc_reg;
platform_set_drvdata(pdev, data);
return 0;
err_rc_reg:
of_clk_del_provider(np);
err_clk_provider:
for (i = 0; i < count; i++)
clk_unregister(clk_data->clks[i]);
err_clk_register:
reset_control_assert(data->reset);
return ret;
}
static int sun9i_a80_mmc_config_clk_remove(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct sun9i_mmc_clk_data *data = platform_get_drvdata(pdev);
struct clk_onecell_data *clk_data = &data->clk_data;
int i;
reset_controller_unregister(&data->rcdev);
of_clk_del_provider(np);
for (i = 0; i < clk_data->clk_num; i++)
clk_unregister(clk_data->clks[i]);
reset_control_assert(data->reset);
return 0;
}
static const struct of_device_id sun9i_a80_mmc_config_clk_dt_ids[] = {
{ .compatible = "allwinner,sun9i-a80-mmc-config-clk" },
{ /* sentinel */ }
};
static struct platform_driver sun9i_a80_mmc_config_clk_driver = {
.driver = {
.name = "sun9i-a80-mmc-config-clk",
.of_match_table = sun9i_a80_mmc_config_clk_dt_ids,
},
.probe = sun9i_a80_mmc_config_clk_probe,
.remove = sun9i_a80_mmc_config_clk_remove,
};
module_platform_driver(sun9i_a80_mmc_config_clk_driver);
MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
MODULE_DESCRIPTION("Allwinner A80 MMC clock/reset Driver");
MODULE_LICENSE("GPL v2");
......@@ -20,11 +20,219 @@
#include <linux/of_address.h>
#include <linux/reset-controller.h>
#include <linux/spinlock.h>
#include <linux/log2.h>
#include "clk-factors.h"
static DEFINE_SPINLOCK(clk_lock);
/**
* sun6i_a31_ahb1_clk_setup() - Setup function for a31 ahb1 composite clk
*/
#define SUN6I_AHB1_MAX_PARENTS 4
#define SUN6I_AHB1_MUX_PARENT_PLL6 3
#define SUN6I_AHB1_MUX_SHIFT 12
/* un-shifted mask is what mux_clk expects */
#define SUN6I_AHB1_MUX_MASK 0x3
#define SUN6I_AHB1_MUX_GET_PARENT(reg) ((reg >> SUN6I_AHB1_MUX_SHIFT) & \
SUN6I_AHB1_MUX_MASK)
#define SUN6I_AHB1_DIV_SHIFT 4
#define SUN6I_AHB1_DIV_MASK (0x3 << SUN6I_AHB1_DIV_SHIFT)
#define SUN6I_AHB1_DIV_GET(reg) ((reg & SUN6I_AHB1_DIV_MASK) >> \
SUN6I_AHB1_DIV_SHIFT)
#define SUN6I_AHB1_DIV_SET(reg, div) ((reg & ~SUN6I_AHB1_DIV_MASK) | \
(div << SUN6I_AHB1_DIV_SHIFT))
#define SUN6I_AHB1_PLL6_DIV_SHIFT 6
#define SUN6I_AHB1_PLL6_DIV_MASK (0x3 << SUN6I_AHB1_PLL6_DIV_SHIFT)
#define SUN6I_AHB1_PLL6_DIV_GET(reg) ((reg & SUN6I_AHB1_PLL6_DIV_MASK) >> \
SUN6I_AHB1_PLL6_DIV_SHIFT)
#define SUN6I_AHB1_PLL6_DIV_SET(reg, div) ((reg & ~SUN6I_AHB1_PLL6_DIV_MASK) | \
(div << SUN6I_AHB1_PLL6_DIV_SHIFT))
struct sun6i_ahb1_clk {
struct clk_hw hw;
void __iomem *reg;
};
#define to_sun6i_ahb1_clk(_hw) container_of(_hw, struct sun6i_ahb1_clk, hw)
static unsigned long sun6i_ahb1_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct sun6i_ahb1_clk *ahb1 = to_sun6i_ahb1_clk(hw);
unsigned long rate;
u32 reg;
/* Fetch the register value */
reg = readl(ahb1->reg);
/* apply pre-divider first if parent is pll6 */
if (SUN6I_AHB1_MUX_GET_PARENT(reg) == SUN6I_AHB1_MUX_PARENT_PLL6)
parent_rate /= SUN6I_AHB1_PLL6_DIV_GET(reg) + 1;
/* clk divider */
rate = parent_rate >> SUN6I_AHB1_DIV_GET(reg);
return rate;
}
static long sun6i_ahb1_clk_round(unsigned long rate, u8 *divp, u8 *pre_divp,
u8 parent, unsigned long parent_rate)
{
u8 div, calcp, calcm = 1;
/*
* clock can only divide, so we will never be able to achieve
* frequencies higher than the parent frequency
*/
if (parent_rate && rate > parent_rate)
rate = parent_rate;
div = DIV_ROUND_UP(parent_rate, rate);
/* calculate pre-divider if parent is pll6 */
if (parent == SUN6I_AHB1_MUX_PARENT_PLL6) {
if (div < 4)
calcp = 0;
else if (div / 2 < 4)
calcp = 1;
else if (div / 4 < 4)
calcp = 2;
else
calcp = 3;
calcm = DIV_ROUND_UP(div, 1 << calcp);
} else {
calcp = __roundup_pow_of_two(div);
calcp = calcp > 3 ? 3 : calcp;
}
/* we were asked to pass back divider values */
if (divp) {
*divp = calcp;
*pre_divp = calcm - 1;
}
return (parent_rate / calcm) >> calcp;
}
static long sun6i_ahb1_clk_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *best_parent_rate,
struct clk_hw **best_parent_clk)
{
struct clk *clk = hw->clk, *parent, *best_parent = NULL;
int i, num_parents;
unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0;
/* find the parent that can help provide the fastest rate <= rate */
num_parents = __clk_get_num_parents(clk);
for (i = 0; i < num_parents; i++) {
parent = clk_get_parent_by_index(clk, i);
if (!parent)
continue;
if (__clk_get_flags(clk) & CLK_SET_RATE_PARENT)
parent_rate = __clk_round_rate(parent, rate);
else
parent_rate = __clk_get_rate(parent);
child_rate = sun6i_ahb1_clk_round(rate, NULL, NULL, i,
parent_rate);
if (child_rate <= rate && child_rate > best_child_rate) {
best_parent = parent;
best = parent_rate;
best_child_rate = child_rate;
}
}
if (best_parent)
*best_parent_clk = __clk_get_hw(best_parent);
*best_parent_rate = best;
return best_child_rate;
}
static int sun6i_ahb1_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct sun6i_ahb1_clk *ahb1 = to_sun6i_ahb1_clk(hw);
unsigned long flags;
u8 div, pre_div, parent;
u32 reg;
spin_lock_irqsave(&clk_lock, flags);
reg = readl(ahb1->reg);
/* need to know which parent is used to apply pre-divider */
parent = SUN6I_AHB1_MUX_GET_PARENT(reg);
sun6i_ahb1_clk_round(rate, &div, &pre_div, parent, parent_rate);
reg = SUN6I_AHB1_DIV_SET(reg, div);
reg = SUN6I_AHB1_PLL6_DIV_SET(reg, pre_div);
writel(reg, ahb1->reg);
spin_unlock_irqrestore(&clk_lock, flags);
return 0;
}
static const struct clk_ops sun6i_ahb1_clk_ops = {
.determine_rate = sun6i_ahb1_clk_determine_rate,
.recalc_rate = sun6i_ahb1_clk_recalc_rate,
.set_rate = sun6i_ahb1_clk_set_rate,
};
static void __init sun6i_ahb1_clk_setup(struct device_node *node)
{
struct clk *clk;
struct sun6i_ahb1_clk *ahb1;
struct clk_mux *mux;
const char *clk_name = node->name;
const char *parents[SUN6I_AHB1_MAX_PARENTS];
void __iomem *reg;
int i = 0;
reg = of_io_request_and_map(node, 0, of_node_full_name(node));
/* we have a mux, we will have >1 parents */
while (i < SUN6I_AHB1_MAX_PARENTS &&
(parents[i] = of_clk_get_parent_name(node, i)) != NULL)
i++;
of_property_read_string(node, "clock-output-names", &clk_name);
ahb1 = kzalloc(sizeof(struct sun6i_ahb1_clk), GFP_KERNEL);
if (!ahb1)
return;
mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
if (!mux) {
kfree(ahb1);
return;
}
/* set up clock properties */
mux->reg = reg;
mux->shift = SUN6I_AHB1_MUX_SHIFT;
mux->mask = SUN6I_AHB1_MUX_MASK;
mux->lock = &clk_lock;
ahb1->reg = reg;
clk = clk_register_composite(NULL, clk_name, parents, i,
&mux->hw, &clk_mux_ops,
&ahb1->hw, &sun6i_ahb1_clk_ops,
NULL, NULL, 0);
if (!IS_ERR(clk)) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
CLK_OF_DECLARE(sun6i_a31_ahb1, "allwinner,sun6i-a31-ahb1-clk", sun6i_ahb1_clk_setup);
/* Maximum number of parents our clocks have */
#define SUNXI_MAX_PARENTS 5
......@@ -354,43 +562,6 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
*p = calcp;
}
/**
* clk_sunxi_mmc_phase_control() - configures MMC clock phase control
*/
void clk_sunxi_mmc_phase_control(struct clk *clk, u8 sample, u8 output)
{
#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw)
#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
struct clk_hw *hw = __clk_get_hw(clk);
struct clk_composite *composite = to_clk_composite(hw);
struct clk_hw *rate_hw = composite->rate_hw;
struct clk_factors *factors = to_clk_factors(rate_hw);
unsigned long flags = 0;
u32 reg;
if (factors->lock)
spin_lock_irqsave(factors->lock, flags);
reg = readl(factors->reg);
/* set sample clock phase control */
reg &= ~(0x7 << 20);
reg |= ((sample & 0x7) << 20);
/* set output clock phase control */
reg &= ~(0x7 << 8);
reg |= ((output & 0x7) << 8);
writel(reg, factors->reg);
if (factors->lock)
spin_unlock_irqrestore(factors->lock, flags);
}
EXPORT_SYMBOL(clk_sunxi_mmc_phase_control);
/**
* sunxi_factors_clk_setup() - Setup function for factor clocks
*/
......@@ -413,6 +584,7 @@ static struct clk_factors_config sun6i_a31_pll1_config = {
.kwidth = 2,
.mshift = 0,
.mwidth = 2,
.n_start = 1,
};
static struct clk_factors_config sun8i_a23_pll1_config = {
......@@ -520,7 +692,16 @@ static const struct factors_data sun7i_a20_out_data __initconst = {
static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
const struct factors_data *data)
{
return sunxi_factors_register(node, data, &clk_lock);
void __iomem *reg;
reg = of_iomap(node, 0);
if (!reg) {
pr_err("Could not get registers for factors-clk: %s\n",
node->name);
return NULL;
}
return sunxi_factors_register(node, data, &clk_lock, reg);
}
......@@ -561,7 +742,7 @@ static void __init sunxi_mux_clk_setup(struct device_node *node,
of_property_read_string(node, "clock-output-names", &clk_name);
clk = clk_register_mux(NULL, clk_name, parents, i,
CLK_SET_RATE_NO_REPARENT, reg,
CLK_SET_RATE_PARENT, reg,
data->shift, SUNXI_MUX_GATE_WIDTH,
0, &clk_lock);
......@@ -1217,7 +1398,6 @@ CLK_OF_DECLARE(sun7i_a20_clk_init, "allwinner,sun7i-a20", sun5i_init_clocks);
static const char *sun6i_critical_clocks[] __initdata = {
"cpu",
"ahb1_sdram",
};
static void __init sun6i_init_clocks(struct device_node *node)
......
......@@ -21,8 +21,6 @@
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/clk/sunxi.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
......@@ -229,6 +227,8 @@ struct sunxi_mmc_host {
/* clock management */
struct clk *clk_ahb;
struct clk *clk_mmc;
struct clk *clk_sample;
struct clk *clk_output;
/* irq */
spinlock_t lock;
......@@ -616,7 +616,7 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
struct mmc_ios *ios)
{
u32 rate, oclk_dly, rval, sclk_dly, src_clk;
u32 rate, oclk_dly, rval, sclk_dly;
int ret;
rate = clk_round_rate(host->clk_mmc, ios->clock);
......@@ -642,34 +642,31 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
/* determine delays */
if (rate <= 400000) {
oclk_dly = 0;
sclk_dly = 7;
oclk_dly = 180;
sclk_dly = 42;
} else if (rate <= 25000000) {
oclk_dly = 0;
sclk_dly = 5;
oclk_dly = 180;
sclk_dly = 75;
} else if (rate <= 50000000) {
if (ios->timing == MMC_TIMING_UHS_DDR50) {
oclk_dly = 2;
sclk_dly = 4;
oclk_dly = 60;
sclk_dly = 120;
} else {
oclk_dly = 3;
sclk_dly = 5;
oclk_dly = 90;
sclk_dly = 150;
}
} else if (rate <= 100000000) {
oclk_dly = 6;
sclk_dly = 24;
} else if (rate <= 200000000) {
oclk_dly = 3;
sclk_dly = 12;
} else {
/* rate > 50000000 */
oclk_dly = 2;
sclk_dly = 4;
}
src_clk = clk_get_rate(clk_get_parent(host->clk_mmc));
if (src_clk >= 300000000 && src_clk <= 400000000) {
if (oclk_dly)
oclk_dly--;
if (sclk_dly)
sclk_dly--;
return -EINVAL;
}
clk_sunxi_mmc_phase_control(host->clk_mmc, sclk_dly, oclk_dly);
clk_set_phase(host->clk_sample, sclk_dly);
clk_set_phase(host->clk_output, oclk_dly);
return sunxi_mmc_oclk_onoff(host, 1);
}
......@@ -908,6 +905,18 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
return PTR_ERR(host->clk_mmc);
}
host->clk_output = devm_clk_get(&pdev->dev, "output");
if (IS_ERR(host->clk_output)) {
dev_err(&pdev->dev, "Could not get output clock\n");
return PTR_ERR(host->clk_output);
}
host->clk_sample = devm_clk_get(&pdev->dev, "sample");
if (IS_ERR(host->clk_sample)) {
dev_err(&pdev->dev, "Could not get sample clock\n");
return PTR_ERR(host->clk_sample);
}
host->reset = devm_reset_control_get(&pdev->dev, "ahb");
ret = clk_prepare_enable(host->clk_ahb);
......@@ -922,11 +931,23 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
goto error_disable_clk_ahb;
}
ret = clk_prepare_enable(host->clk_output);
if (ret) {
dev_err(&pdev->dev, "Enable output clk err %d\n", ret);
goto error_disable_clk_mmc;
}
ret = clk_prepare_enable(host->clk_sample);
if (ret) {
dev_err(&pdev->dev, "Enable sample clk err %d\n", ret);
goto error_disable_clk_output;
}
if (!IS_ERR(host->reset)) {
ret = reset_control_deassert(host->reset);
if (ret) {
dev_err(&pdev->dev, "reset err %d\n", ret);
goto error_disable_clk_mmc;
goto error_disable_clk_sample;
}
}
......@@ -945,6 +966,10 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
error_assert_reset:
if (!IS_ERR(host->reset))
reset_control_assert(host->reset);
error_disable_clk_sample:
clk_disable_unprepare(host->clk_sample);
error_disable_clk_output:
clk_disable_unprepare(host->clk_output);
error_disable_clk_mmc:
clk_disable_unprepare(host->clk_mmc);
error_disable_clk_ahb:
......
/*
* Copyright 2013 - Hans de Goede <hdegoede@redhat.com>
*
* 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 __LINUX_CLK_SUNXI_H_
#define __LINUX_CLK_SUNXI_H_
#include <linux/clk.h>
void clk_sunxi_mmc_phase_control(struct clk *clk, u8 sample, u8 output);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment