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.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJUxSZeAAoJEBx+YmzsjxAgXeAP/0aIFOuvCQ00JrXoIiKogery
 S66HZpVclU9nvhFh99fhVBB0AP0TcH5qZPYCeughCO07SdtnbKVEb2A+2aMP4uzN
 dRlL6smk8F9/bUq0yhUzX4e1NbDGVeWaXw2JWxvZ7WGwavjQ9ejS1Pb1BMAwUnVY
 ZUebVkbw7XlVkYr3/GROVWTtNswYu8L/8aY438OoK+VnaVWo5N/+8kX8i+I85bxW
 4G8WvNO36neEh3Oc4aBbetW29ZQbXlt6IJ89tkEaxtC4Z29VNSeXJfpadYLZRrP+
 8+IUA5YVau/9VJePdF5a4BM55Uee7M4aMqVENGiUHMFLMGSJNyR2G9+qu658Twyn
 vP//imP34mTVI5D8oo9cOdKgQh2Prf9K8MmYuAvQxVaTEpt4wx2v7jVi1G/m9etX
 mxn95h0G7wIFMwPQiZfbvCgw8QOSXWYa59A4d1209SDB0vGYWgG2HJQvJnBPJmhq
 9Ifczv9Ia7M6CuTZfdhf0TrABML56IC8JCtCJ1Zk6mUKc+lE+m4IdM68drfM0WC9
 +KOC07QJiB0tEHyauppbVvaY6Jon2bYhUyEzGl6gpfYg4VuoBavzS2vVSc8E0n9Z
 iYPXtXE8soygRZVgvQ58YN8yKWZI+Ylpz9EAJ1a82fCAG8r3iJ8FNixpfB93nRxU
 6GcGqhSQayZY74mRnBqb
 =m8K6
 -----END PGP SIGNATURE-----

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.
This commit is contained in:
Michael Turquette 2015-01-27 16:33:45 -08:00
commit 8f101aa027
18 changed files with 1087 additions and 326 deletions

View file

@ -26,7 +26,7 @@ Required properties:
"allwinner,sun5i-a10s-ahb-gates-clk" - for the AHB gates on A10s "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,sun7i-a20-ahb-gates-clk" - for the AHB gates on A20
"allwinner,sun6i-a31-ar100-clk" - for the AR100 on A31 "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,sun6i-a31-ahb1-gates-clk" - for the AHB1 gates on A31
"allwinner,sun8i-a23-ahb1-gates-clk" - for the AHB1 gates on A23 "allwinner,sun8i-a23-ahb1-gates-clk" - for the AHB1 gates on A23
"allwinner,sun9i-a80-ahb0-gates-clk" - for the AHB0 gates on A80 "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,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31
"allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23 "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23
"allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 "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-clk" - for the MMC clock
"allwinner,sun4i-a10-mmc-sample-clk" - for the MMC sample clock on A10 "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,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,sun8i-a23-mbus-clk" - for the MBUS clock on A23
"allwinner,sun7i-a20-out-clk" - for the external output clocks "allwinner,sun7i-a20-out-clk" - for the external output clocks
"allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31 "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 - #clock-cells : from common clock binding; shall be set to 0 except for
the following compatibles where it shall be set to 1: the following compatibles where it shall be set to 1:
"allwinner,*-gates-clk", "allwinner,sun4i-pll5-clk", "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. - clock-output-names : shall be the corresponding names of the outputs.
If the clock module only has one output, the name shall be the If the clock module only has one output, the name shall be the
module name. module name.
@ -81,6 +85,10 @@ Required properties for all clocks:
And "allwinner,*-usb-clk" clocks also require: And "allwinner,*-usb-clk" clocks also require:
- reset-cells : shall be set to 1 - 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 For "allwinner,sun7i-a20-gmac-clk", the parent clocks shall be fixed rate
dummy clocks at 25 MHz and 125 MHz, respectively. See example. 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 is the normal PLL6 output, or "pll6". The second output is rate doubled
PLL6, or "pll6x2". 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: For example:
osc24M: clk@01c20050 { osc24M: clk@01c20050 {
@ -138,11 +154,11 @@ cpu: cpu@01c20054 {
}; };
mmc0_clk: clk@01c20088 { mmc0_clk: clk@01c20088 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>; reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0"; clock-output-names = "mmc0", "mmc0_output", "mmc0_sample";
}; };
mii_phy_tx_clk: clk@2 { mii_phy_tx_clk: clk@2 {
@ -170,3 +186,16 @@ gmac_clk: clk@01c20164 {
clocks = <&mii_phy_tx_clk>, <&gmac_int_tx_clk>; clocks = <&mii_phy_tx_clk>, <&gmac_int_tx_clk>;
clock-output-names = "gmac"; 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";
};

View file

@ -10,8 +10,8 @@ Absolute maximum transfer rate is 200MB/s
Required properties: Required properties:
- compatible : "allwinner,sun4i-a10-mmc" or "allwinner,sun5i-a13-mmc" - compatible : "allwinner,sun4i-a10-mmc" or "allwinner,sun5i-a13-mmc"
- reg : mmc controller base registers - reg : mmc controller base registers
- clocks : a list with 2 phandle + clock specifier pairs - clocks : a list with 4 phandle + clock specifier pairs
- clock-names : must contain "ahb" and "mmc" - clock-names : must contain "ahb", "mmc", "output" and "sample"
- interrupts : mmc controller interrupt - interrupts : mmc controller interrupt
Optional properties: Optional properties:
@ -25,8 +25,8 @@ Examples:
mmc0: mmc@01c0f000 { mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>; reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>; clocks = <&ahb_gates 8>, <&mmc0_clk>, <&mmc0_output_clk>, <&mmc0_sample_clk>;
clock-names = "ahb", "mod"; clock-names = "ahb", "mod", "output", "sample";
interrupts = <0 32 4>; interrupts = <0 32 4>;
status = "disabled"; status = "disabled";
}; };

View file

@ -226,35 +226,43 @@
}; };
mmc0_clk: clk@01c20088 { mmc0_clk: clk@01c20088 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>; reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0"; clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
}; };
mmc1_clk: clk@01c2008c { mmc1_clk: clk@01c2008c {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>; reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1"; clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
}; };
mmc2_clk: clk@01c20090 { mmc2_clk: clk@01c20090 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>; reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2"; clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
}; };
mmc3_clk: clk@01c20094 { mmc3_clk: clk@01c20094 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20094 0x4>; reg = <0x01c20094 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc3"; clock-output-names = "mmc3",
"mmc3_output",
"mmc3_sample";
}; };
ts_clk: clk@01c20098 { ts_clk: clk@01c20098 {
@ -398,8 +406,14 @@
mmc0: mmc@01c0f000 { mmc0: mmc@01c0f000 {
compatible = "allwinner,sun4i-a10-mmc"; compatible = "allwinner,sun4i-a10-mmc";
reg = <0x01c0f000 0x1000>; reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>; clocks = <&ahb_gates 8>,
clock-names = "ahb", "mmc"; <&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <32>; interrupts = <32>;
status = "disabled"; status = "disabled";
}; };
@ -407,8 +421,14 @@
mmc1: mmc@01c10000 { mmc1: mmc@01c10000 {
compatible = "allwinner,sun4i-a10-mmc"; compatible = "allwinner,sun4i-a10-mmc";
reg = <0x01c10000 0x1000>; reg = <0x01c10000 0x1000>;
clocks = <&ahb_gates 9>, <&mmc1_clk>; clocks = <&ahb_gates 9>,
clock-names = "ahb", "mmc"; <&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <33>; interrupts = <33>;
status = "disabled"; status = "disabled";
}; };
@ -416,8 +436,14 @@
mmc2: mmc@01c11000 { mmc2: mmc@01c11000 {
compatible = "allwinner,sun4i-a10-mmc"; compatible = "allwinner,sun4i-a10-mmc";
reg = <0x01c11000 0x1000>; reg = <0x01c11000 0x1000>;
clocks = <&ahb_gates 10>, <&mmc2_clk>; clocks = <&ahb_gates 10>,
clock-names = "ahb", "mmc"; <&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <34>; interrupts = <34>;
status = "disabled"; status = "disabled";
}; };
@ -425,8 +451,14 @@
mmc3: mmc@01c12000 { mmc3: mmc@01c12000 {
compatible = "allwinner,sun4i-a10-mmc"; compatible = "allwinner,sun4i-a10-mmc";
reg = <0x01c12000 0x1000>; reg = <0x01c12000 0x1000>;
clocks = <&ahb_gates 11>, <&mmc3_clk>; clocks = <&ahb_gates 11>,
clock-names = "ahb", "mmc"; <&mmc3_clk 0>,
<&mmc3_clk 1>,
<&mmc3_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <35>; interrupts = <35>;
status = "disabled"; status = "disabled";
}; };

View file

@ -211,27 +211,33 @@
}; };
mmc0_clk: clk@01c20088 { mmc0_clk: clk@01c20088 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>; reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0"; clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
}; };
mmc1_clk: clk@01c2008c { mmc1_clk: clk@01c2008c {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>; reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1"; clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
}; };
mmc2_clk: clk@01c20090 { mmc2_clk: clk@01c20090 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>; reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2"; clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
}; };
ts_clk: clk@01c20098 { ts_clk: clk@01c20098 {
@ -359,8 +365,14 @@
mmc0: mmc@01c0f000 { mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>; reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>; clocks = <&ahb_gates 8>,
clock-names = "ahb", "mmc"; <&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <32>; interrupts = <32>;
status = "disabled"; status = "disabled";
}; };
@ -368,8 +380,14 @@
mmc1: mmc@01c10000 { mmc1: mmc@01c10000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c10000 0x1000>; reg = <0x01c10000 0x1000>;
clocks = <&ahb_gates 9>, <&mmc1_clk>; clocks = <&ahb_gates 9>,
clock-names = "ahb", "mmc"; <&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <33>; interrupts = <33>;
status = "disabled"; status = "disabled";
}; };
@ -377,8 +395,14 @@
mmc2: mmc@01c11000 { mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>; reg = <0x01c11000 0x1000>;
clocks = <&ahb_gates 10>, <&mmc2_clk>; clocks = <&ahb_gates 10>,
clock-names = "ahb", "mmc"; <&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <34>; interrupts = <34>;
status = "disabled"; status = "disabled";
}; };

View file

@ -195,27 +195,33 @@
}; };
mmc0_clk: clk@01c20088 { mmc0_clk: clk@01c20088 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>; reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0"; clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
}; };
mmc1_clk: clk@01c2008c { mmc1_clk: clk@01c2008c {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>; reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1"; clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
}; };
mmc2_clk: clk@01c20090 { mmc2_clk: clk@01c20090 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>; reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2"; clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
}; };
ts_clk: clk@01c20098 { ts_clk: clk@01c20098 {
@ -327,8 +333,14 @@
mmc0: mmc@01c0f000 { mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>; reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>; clocks = <&ahb_gates 8>,
clock-names = "ahb", "mmc"; <&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <32>; interrupts = <32>;
status = "disabled"; status = "disabled";
}; };
@ -336,8 +348,14 @@
mmc2: mmc@01c11000 { mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>; reg = <0x01c11000 0x1000>;
clocks = <&ahb_gates 10>, <&mmc2_clk>; clocks = <&ahb_gates 10>,
clock-names = "ahb", "mmc"; <&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <34>; interrupts = <34>;
status = "disabled"; status = "disabled";
}; };

View file

@ -174,19 +174,11 @@
clock-output-names = "axi"; 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 { ahb1: ahb1@01c20054 {
#clock-cells = <0>; #clock-cells = <0>;
compatible = "allwinner,sun4i-a10-ahb-clk"; compatible = "allwinner,sun6i-a31-ahb1-clk";
reg = <0x01c20054 0x4>; reg = <0x01c20054 0x4>;
clocks = <&ahb1_mux>; clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>;
clock-output-names = "ahb1"; clock-output-names = "ahb1";
}; };
@ -249,35 +241,43 @@
}; };
mmc0_clk: clk@01c20088 { mmc0_clk: clk@01c20088 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>; reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 0>; clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc0"; clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
}; };
mmc1_clk: clk@01c2008c { mmc1_clk: clk@01c2008c {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>; reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 0>; clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc1"; clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
}; };
mmc2_clk: clk@01c20090 { mmc2_clk: clk@01c20090 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>; reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 0>; clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc2"; clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
}; };
mmc3_clk: clk@01c20094 { mmc3_clk: clk@01c20094 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20094 0x4>; reg = <0x01c20094 0x4>;
clocks = <&osc24M>, <&pll6 0>; clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc3"; clock-output-names = "mmc3",
"mmc3_output",
"mmc3_sample";
}; };
spi0_clk: clk@01c200a0 { spi0_clk: clk@01c200a0 {
@ -367,15 +367,21 @@
#dma-cells = <1>; #dma-cells = <1>;
/* DMA controller requires AHB1 clocked from PLL6 */ /* DMA controller requires AHB1 clocked from PLL6 */
assigned-clocks = <&ahb1_mux>; assigned-clocks = <&ahb1>;
assigned-clock-parents = <&pll6 0>; assigned-clock-parents = <&pll6 0>;
}; };
mmc0: mmc@01c0f000 { mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>; reg = <0x01c0f000 0x1000>;
clocks = <&ahb1_gates 8>, <&mmc0_clk>; clocks = <&ahb1_gates 8>,
clock-names = "ahb", "mmc"; <&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 8>; resets = <&ahb1_rst 8>;
reset-names = "ahb"; reset-names = "ahb";
interrupts = <0 60 4>; interrupts = <0 60 4>;
@ -385,8 +391,14 @@
mmc1: mmc@01c10000 { mmc1: mmc@01c10000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c10000 0x1000>; reg = <0x01c10000 0x1000>;
clocks = <&ahb1_gates 9>, <&mmc1_clk>; clocks = <&ahb1_gates 9>,
clock-names = "ahb", "mmc"; <&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 9>; resets = <&ahb1_rst 9>;
reset-names = "ahb"; reset-names = "ahb";
interrupts = <0 61 4>; interrupts = <0 61 4>;
@ -396,8 +408,14 @@
mmc2: mmc@01c11000 { mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>; reg = <0x01c11000 0x1000>;
clocks = <&ahb1_gates 10>, <&mmc2_clk>; clocks = <&ahb1_gates 10>,
clock-names = "ahb", "mmc"; <&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 10>; resets = <&ahb1_rst 10>;
reset-names = "ahb"; reset-names = "ahb";
interrupts = <0 62 4>; interrupts = <0 62 4>;
@ -407,8 +425,14 @@
mmc3: mmc@01c12000 { mmc3: mmc@01c12000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c12000 0x1000>; reg = <0x01c12000 0x1000>;
clocks = <&ahb1_gates 11>, <&mmc3_clk>; clocks = <&ahb1_gates 11>,
clock-names = "ahb", "mmc"; <&mmc3_clk 0>,
<&mmc3_clk 1>,
<&mmc3_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 11>; resets = <&ahb1_rst 11>;
reset-names = "ahb"; reset-names = "ahb";
interrupts = <0 63 4>; interrupts = <0 63 4>;

View file

@ -274,35 +274,43 @@
}; };
mmc0_clk: clk@01c20088 { mmc0_clk: clk@01c20088 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>; reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc0"; clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
}; };
mmc1_clk: clk@01c2008c { mmc1_clk: clk@01c2008c {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>; reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc1"; clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
}; };
mmc2_clk: clk@01c20090 { mmc2_clk: clk@01c20090 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>; reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc2"; clock-output-names = "mmc2",
"mmc2_output",
"mmc2_sample";
}; };
mmc3_clk: clk@01c20094 { mmc3_clk: clk@01c20094 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20094 0x4>; reg = <0x01c20094 0x4>;
clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
clock-output-names = "mmc3"; clock-output-names = "mmc3",
"mmc3_output",
"mmc3_sample";
}; };
ts_clk: clk@01c20098 { ts_clk: clk@01c20098 {
@ -518,8 +526,14 @@
mmc0: mmc@01c0f000 { mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>; reg = <0x01c0f000 0x1000>;
clocks = <&ahb_gates 8>, <&mmc0_clk>; clocks = <&ahb_gates 8>,
clock-names = "ahb", "mmc"; <&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <0 32 4>; interrupts = <0 32 4>;
status = "disabled"; status = "disabled";
}; };
@ -527,8 +541,14 @@
mmc1: mmc@01c10000 { mmc1: mmc@01c10000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c10000 0x1000>; reg = <0x01c10000 0x1000>;
clocks = <&ahb_gates 9>, <&mmc1_clk>; clocks = <&ahb_gates 9>,
clock-names = "ahb", "mmc"; <&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <0 33 4>; interrupts = <0 33 4>;
status = "disabled"; status = "disabled";
}; };
@ -536,8 +556,14 @@
mmc2: mmc@01c11000 { mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>; reg = <0x01c11000 0x1000>;
clocks = <&ahb_gates 10>, <&mmc2_clk>; clocks = <&ahb_gates 10>,
clock-names = "ahb", "mmc"; <&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <0 34 4>; interrupts = <0 34 4>;
status = "disabled"; status = "disabled";
}; };
@ -545,8 +571,14 @@
mmc3: mmc@01c12000 { mmc3: mmc@01c12000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c12000 0x1000>; reg = <0x01c12000 0x1000>;
clocks = <&ahb_gates 11>, <&mmc3_clk>; clocks = <&ahb_gates 11>,
clock-names = "ahb", "mmc"; <&mmc3_clk 0>,
<&mmc3_clk 1>,
<&mmc3_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
interrupts = <0 35 4>; interrupts = <0 35 4>;
status = "disabled"; status = "disabled";
}; };

View file

@ -110,11 +110,19 @@
}; };
/* dummy clock until actually implemented */ /* dummy clock until actually implemented */
pll6: pll6_clk { pll5: pll5_clk {
#clock-cells = <0>; #clock-cells = <0>;
compatible = "fixed-clock"; compatible = "fixed-clock";
clock-frequency = <600000000>; clock-frequency = <0>;
clock-output-names = "pll6"; 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 { cpu: cpu_clk@01c20050 {
@ -140,19 +148,11 @@
clock-output-names = "axi"; 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 { ahb1: ahb1_clk@01c20054 {
#clock-cells = <0>; #clock-cells = <0>;
compatible = "allwinner,sun4i-a10-ahb-clk"; compatible = "allwinner,sun6i-a31-ahb1-clk";
reg = <0x01c20054 0x4>; reg = <0x01c20054 0x4>;
clocks = <&ahb1_mux>; clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>;
clock-output-names = "ahb1"; clock-output-names = "ahb1";
}; };
@ -193,7 +193,7 @@
#clock-cells = <0>; #clock-cells = <0>;
compatible = "allwinner,sun4i-a10-apb1-clk"; compatible = "allwinner,sun4i-a10-apb1-clk";
reg = <0x01c20058 0x4>; reg = <0x01c20058 0x4>;
clocks = <&osc32k>, <&osc24M>, <&pll6>, <&pll6>; clocks = <&osc32k>, <&osc24M>, <&pll6 0>, <&pll6 0>;
clock-output-names = "apb2"; clock-output-names = "apb2";
}; };
@ -209,27 +209,41 @@
}; };
mmc0_clk: clk@01c20088 { mmc0_clk: clk@01c20088 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20088 0x4>; reg = <0x01c20088 0x4>;
clocks = <&osc24M>, <&pll6>; clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc0"; clock-output-names = "mmc0",
"mmc0_output",
"mmc0_sample";
}; };
mmc1_clk: clk@01c2008c { mmc1_clk: clk@01c2008c {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c2008c 0x4>; reg = <0x01c2008c 0x4>;
clocks = <&osc24M>, <&pll6>; clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc1"; clock-output-names = "mmc1",
"mmc1_output",
"mmc1_sample";
}; };
mmc2_clk: clk@01c20090 { mmc2_clk: clk@01c20090 {
#clock-cells = <0>; #clock-cells = <1>;
compatible = "allwinner,sun4i-a10-mod0-clk"; compatible = "allwinner,sun4i-a10-mmc-clk";
reg = <0x01c20090 0x4>; reg = <0x01c20090 0x4>;
clocks = <&osc24M>, <&pll6>; clocks = <&osc24M>, <&pll6 0>;
clock-output-names = "mmc2"; 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 @@
mmc0: mmc@01c0f000 { mmc0: mmc@01c0f000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c0f000 0x1000>; reg = <0x01c0f000 0x1000>;
clocks = <&ahb1_gates 8>, <&mmc0_clk>; clocks = <&ahb1_gates 8>,
clock-names = "ahb", "mmc"; <&mmc0_clk 0>,
<&mmc0_clk 1>,
<&mmc0_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 8>; resets = <&ahb1_rst 8>;
reset-names = "ahb"; reset-names = "ahb";
interrupts = <0 60 4>; interrupts = <0 60 4>;
@ -262,8 +282,14 @@
mmc1: mmc@01c10000 { mmc1: mmc@01c10000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c10000 0x1000>; reg = <0x01c10000 0x1000>;
clocks = <&ahb1_gates 9>, <&mmc1_clk>; clocks = <&ahb1_gates 9>,
clock-names = "ahb", "mmc"; <&mmc1_clk 0>,
<&mmc1_clk 1>,
<&mmc1_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 9>; resets = <&ahb1_rst 9>;
reset-names = "ahb"; reset-names = "ahb";
interrupts = <0 61 4>; interrupts = <0 61 4>;
@ -273,8 +299,14 @@
mmc2: mmc@01c11000 { mmc2: mmc@01c11000 {
compatible = "allwinner,sun5i-a13-mmc"; compatible = "allwinner,sun5i-a13-mmc";
reg = <0x01c11000 0x1000>; reg = <0x01c11000 0x1000>;
clocks = <&ahb1_gates 10>, <&mmc2_clk>; clocks = <&ahb1_gates 10>,
clock-names = "ahb", "mmc"; <&mmc2_clk 0>,
<&mmc2_clk 1>,
<&mmc2_clk 2>;
clock-names = "ahb",
"mmc",
"output",
"sample";
resets = <&ahb1_rst 10>; resets = <&ahb1_rst 10>;
reset-names = "ahb"; reset-names = "ahb";
interrupts = <0 62 4>; interrupts = <0 62 4>;

View file

@ -8,6 +8,7 @@ obj-y += clk-a20-gmac.o
obj-y += clk-mod0.o obj-y += clk-mod0.o
obj-y += clk-sun8i-mbus.o obj-y += clk-sun8i-mbus.o
obj-y += clk-sun9i-core.o obj-y += clk-sun9i-core.o
obj-y += clk-sun9i-mmc.o
obj-$(CONFIG_MFD_SUN6I_PRCM) += \ obj-$(CONFIG_MFD_SUN6I_PRCM) += \
clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \ clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \

View file

@ -156,9 +156,10 @@ static const struct clk_ops clk_factors_ops = {
.set_rate = clk_factors_set_rate, .set_rate = clk_factors_set_rate,
}; };
struct clk * __init sunxi_factors_register(struct device_node *node, struct clk *sunxi_factors_register(struct device_node *node,
const struct factors_data *data, const struct factors_data *data,
spinlock_t *lock) spinlock_t *lock,
void __iomem *reg)
{ {
struct clk *clk; struct clk *clk;
struct clk_factors *factors; struct clk_factors *factors;
@ -168,11 +169,8 @@ struct clk * __init sunxi_factors_register(struct device_node *node,
struct clk_hw *mux_hw = NULL; struct clk_hw *mux_hw = NULL;
const char *clk_name = node->name; const char *clk_name = node->name;
const char *parents[FACTORS_MAX_PARENTS]; const char *parents[FACTORS_MAX_PARENTS];
void __iomem *reg;
int i = 0; int i = 0;
reg = of_iomap(node, 0);
/* if we have a mux, we will have >1 parents */ /* if we have a mux, we will have >1 parents */
while (i < FACTORS_MAX_PARENTS && while (i < FACTORS_MAX_PARENTS &&
(parents[i] = of_clk_get_parent_name(node, i)) != NULL) (parents[i] = of_clk_get_parent_name(node, i)) != NULL)

View file

@ -36,8 +36,9 @@ struct clk_factors {
spinlock_t *lock; spinlock_t *lock;
}; };
struct clk * __init sunxi_factors_register(struct device_node *node, struct clk *sunxi_factors_register(struct device_node *node,
const struct factors_data *data, const struct factors_data *data,
spinlock_t *lock); spinlock_t *lock,
void __iomem *reg);
#endif #endif

View file

@ -17,6 +17,7 @@
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/clkdev.h> #include <linux/clkdev.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/platform_device.h>
#include "clk-factors.h" #include "clk-factors.h"
@ -67,7 +68,7 @@ static struct clk_factors_config sun4i_a10_mod0_config = {
.pwidth = 2, .pwidth = 2,
}; };
static const struct factors_data sun4i_a10_mod0_data __initconst = { static const struct factors_data sun4i_a10_mod0_data = {
.enable = 31, .enable = 31,
.mux = 24, .mux = 24,
.muxmask = BIT(1) | BIT(0), .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) 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); 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 DEFINE_SPINLOCK(sun5i_a13_mbus_lock);
static void __init sun5i_a13_mbus_setup(struct device_node *node) 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 */ /* The MBUS clocks needs to be always enabled */
__clk_get(mbus); __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); 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 mmc_phase {
struct clk_hw hw; struct clk_hw hw;
u8 offset;
void __iomem *reg; void __iomem *reg;
struct mmc_phase_data *data;
spinlock_t *lock; spinlock_t *lock;
}; };
@ -118,7 +195,7 @@ static int mmc_get_phase(struct clk_hw *hw)
u8 delay; u8 delay;
value = readl(phase->reg); value = readl(phase->reg);
delay = (value >> phase->data->offset) & 0x3; delay = (value >> phase->offset) & 0x3;
if (!delay) if (!delay)
return 180; return 180;
@ -206,8 +283,8 @@ static int mmc_set_phase(struct clk_hw *hw, int degrees)
spin_lock_irqsave(phase->lock, flags); spin_lock_irqsave(phase->lock, flags);
value = readl(phase->reg); value = readl(phase->reg);
value &= ~GENMASK(phase->data->offset + 3, phase->data->offset); value &= ~GENMASK(phase->offset + 3, phase->offset);
value |= delay << phase->data->offset; value |= delay << phase->offset;
writel(value, phase->reg); writel(value, phase->reg);
spin_unlock_irqrestore(phase->lock, flags); spin_unlock_irqrestore(phase->lock, flags);
@ -219,66 +296,97 @@ static const struct clk_ops mmc_clk_ops = {
.set_phase = mmc_set_phase, .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_onecell_data *clk_data;
struct clk_init_data init = { const char *parent;
.num_parents = 1, void __iomem *reg;
.parent_names = parent_names, int i;
.ops = &mmc_clk_ops,
};
struct mmc_phase *phase; reg = of_io_request_and_map(node, 0, of_node_full_name(node));
struct clk *clk; if (IS_ERR(reg)) {
pr_err("Couldn't map the %s clock registers\n", node->name);
return;
}
phase = kmalloc(sizeof(*phase), GFP_KERNEL); clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
if (!phase) if (!clk_data)
return; return;
phase->hw.init = &init; clk_data->clks = kcalloc(3, sizeof(*clk_data->clks), GFP_KERNEL);
if (!clk_data->clks)
goto err_free_data;
phase->reg = of_iomap(node, 0); clk_data->clk_num = 3;
if (!phase->reg) clk_data->clks[0] = sunxi_factors_register(node, data, lock, reg);
goto err_free; if (!clk_data->clks[0])
goto err_free_clks;
phase->data = data; parent = __clk_get_name(clk_data->clks[0]);
phase->lock = &sun4i_a10_mod0_lock;
if (of_property_read_string(node, "clock-output-names", &init.name)) for (i = 1; i < 3; i++) {
init.name = node->name; struct clk_init_data init = {
.num_parents = 1,
.parent_names = &parent,
.ops = &mmc_clk_ops,
};
struct mmc_phase *phase;
clk = clk_register(NULL, &phase->hw); phase = kmalloc(sizeof(*phase), GFP_KERNEL);
if (IS_ERR(clk)) if (!phase)
goto err_unmap; continue;
of_clk_add_provider(node, of_clk_src_simple_get, clk); 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_onecell_get, clk_data);
return; return;
err_unmap: err_free_clks:
iounmap(phase->reg); kfree(clk_data->clks);
err_free: err_free_data:
kfree(phase); kfree(clk_data);
} }
static DEFINE_SPINLOCK(sun4i_a10_mmc_lock);
static struct mmc_phase_data mmc_output_clk = { static void __init sun4i_a10_mmc_setup(struct device_node *node)
.offset = 8,
};
static struct mmc_phase_data mmc_sample_clk = {
.offset = 20,
};
static void __init sun4i_a10_mmc_output_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 void __init sun4i_a10_mmc_sample_setup(struct device_node *node) static DEFINE_SPINLOCK(sun9i_a80_mmc_lock);
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);

View file

@ -69,8 +69,17 @@ static DEFINE_SPINLOCK(sun8i_a23_mbus_lock);
static void __init sun8i_a23_mbus_setup(struct device_node *node) static void __init sun8i_a23_mbus_setup(struct device_node *node)
{ {
struct clk *mbus = sunxi_factors_register(node, &sun8i_a23_mbus_data, struct clk *mbus;
&sun8i_a23_mbus_lock); 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 */ /* The MBUS clocks needs to be always enabled */
__clk_get(mbus); __clk_get(mbus);

View file

@ -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 * PLL4 rate is calculated as follows
* rate = (parent_rate * n >> p) / (m + 1); * 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 * p and m are named div1 and div2 in Allwinner's SDK
*/ */
static void sun9i_a80_get_pll4_factors(u32 *freq, u32 parent_rate, 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 */ /* Normalize value to a 6 MHz multiple (24 MHz / 4) */
div = DIV_ROUND_UP(*freq, 6000000); n = DIV_ROUND_UP(*freq, 6000000);
/* divs above 256 cannot be odd */ /* If n is too large switch to steps of 12 MHz */
if (div > 256) if (n > 255) {
div = round_up(div, 2); m = 0;
n = (n + 1) / 2;
}
/* divs above 512 must be a multiple of 4 */ /* If n is still too large switch to steps of 24 MHz */
if (div > 512) if (n > 255) {
div = round_up(div, 4); p = 0;
n = (n + 1) / 2;
}
*freq = 6000000 * div; /* n must be between 12 and 255 */
if (n > 255)
n = 255;
else if (n < 12)
n = 12;
*freq = ((24000000 * n) >> p) / (m + 1);
/* we were called to round the frequency, we can now return */ /* we were called to round the frequency, we can now return */
if (n == NULL) if (n_ret == NULL)
return; return;
/* p will be 1 for divs under 512 */ *n_ret = n;
if (div < 512) *m_ret = m;
*p = 1; *p_ret = p;
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);
} }
static struct clk_factors_config sun9i_a80_pll4_config = { 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) 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); 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) static void __init sun9i_a80_gt_setup(struct device_node *node)
{ {
struct clk *gt = sunxi_factors_register(node, &sun9i_a80_gt_data, void __iomem *reg;
&sun9i_a80_gt_lock); 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 */ /* The GT bus clock needs to be always enabled */
__clk_get(gt); __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) 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); 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) 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); 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) 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); CLK_OF_DECLARE(sun9i_a80_apb1, "allwinner,sun9i-a80-apb1-clk", sun9i_a80_apb1_setup);

View file

@ -0,0 +1,219 @@
/*
* 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");

View file

@ -20,11 +20,219 @@
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/reset-controller.h> #include <linux/reset-controller.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/log2.h>
#include "clk-factors.h" #include "clk-factors.h"
static DEFINE_SPINLOCK(clk_lock); 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 */ /* Maximum number of parents our clocks have */
#define SUNXI_MAX_PARENTS 5 #define SUNXI_MAX_PARENTS 5
@ -354,43 +562,6 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
*p = calcp; *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 * sunxi_factors_clk_setup() - Setup function for factor clocks
*/ */
@ -413,6 +584,7 @@ static struct clk_factors_config sun6i_a31_pll1_config = {
.kwidth = 2, .kwidth = 2,
.mshift = 0, .mshift = 0,
.mwidth = 2, .mwidth = 2,
.n_start = 1,
}; };
static struct clk_factors_config sun8i_a23_pll1_config = { 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, static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
const struct factors_data *data) 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); of_property_read_string(node, "clock-output-names", &clk_name);
clk = clk_register_mux(NULL, clk_name, parents, i, 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, data->shift, SUNXI_MUX_GATE_WIDTH,
0, &clk_lock); 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 = { static const char *sun6i_critical_clocks[] __initdata = {
"cpu", "cpu",
"ahb1_sdram",
}; };
static void __init sun6i_init_clocks(struct device_node *node) static void __init sun6i_init_clocks(struct device_node *node)

View file

@ -21,8 +21,6 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clk/sunxi.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
@ -229,6 +227,8 @@ struct sunxi_mmc_host {
/* clock management */ /* clock management */
struct clk *clk_ahb; struct clk *clk_ahb;
struct clk *clk_mmc; struct clk *clk_mmc;
struct clk *clk_sample;
struct clk *clk_output;
/* irq */ /* irq */
spinlock_t lock; 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, static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
struct mmc_ios *ios) struct mmc_ios *ios)
{ {
u32 rate, oclk_dly, rval, sclk_dly, src_clk; u32 rate, oclk_dly, rval, sclk_dly;
int ret; int ret;
rate = clk_round_rate(host->clk_mmc, ios->clock); 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 */ /* determine delays */
if (rate <= 400000) { if (rate <= 400000) {
oclk_dly = 0; oclk_dly = 180;
sclk_dly = 7; sclk_dly = 42;
} else if (rate <= 25000000) { } else if (rate <= 25000000) {
oclk_dly = 0; oclk_dly = 180;
sclk_dly = 5; sclk_dly = 75;
} else if (rate <= 50000000) { } else if (rate <= 50000000) {
if (ios->timing == MMC_TIMING_UHS_DDR50) { if (ios->timing == MMC_TIMING_UHS_DDR50) {
oclk_dly = 2; oclk_dly = 60;
sclk_dly = 4; sclk_dly = 120;
} else { } else {
oclk_dly = 3; oclk_dly = 90;
sclk_dly = 5; sclk_dly = 150;
} }
} else if (rate <= 100000000) {
oclk_dly = 6;
sclk_dly = 24;
} else if (rate <= 200000000) {
oclk_dly = 3;
sclk_dly = 12;
} else { } else {
/* rate > 50000000 */ return -EINVAL;
oclk_dly = 2;
sclk_dly = 4;
} }
src_clk = clk_get_rate(clk_get_parent(host->clk_mmc)); clk_set_phase(host->clk_sample, sclk_dly);
if (src_clk >= 300000000 && src_clk <= 400000000) { clk_set_phase(host->clk_output, oclk_dly);
if (oclk_dly)
oclk_dly--;
if (sclk_dly)
sclk_dly--;
}
clk_sunxi_mmc_phase_control(host->clk_mmc, sclk_dly, oclk_dly);
return sunxi_mmc_oclk_onoff(host, 1); 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); 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"); host->reset = devm_reset_control_get(&pdev->dev, "ahb");
ret = clk_prepare_enable(host->clk_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; 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)) { if (!IS_ERR(host->reset)) {
ret = reset_control_deassert(host->reset); ret = reset_control_deassert(host->reset);
if (ret) { if (ret) {
dev_err(&pdev->dev, "reset err %d\n", 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: error_assert_reset:
if (!IS_ERR(host->reset)) if (!IS_ERR(host->reset))
reset_control_assert(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: error_disable_clk_mmc:
clk_disable_unprepare(host->clk_mmc); clk_disable_unprepare(host->clk_mmc);
error_disable_clk_ahb: error_disable_clk_ahb:

View file

@ -1,22 +0,0 @@
/*
* 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