diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index b24de10039471fc64536441204e2a9ee6fd5b4a4..c383d1259e5c1c89d2a89bea911a62356a8b57fb 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -8,6 +8,7 @@ Required properties: - compatible : shall be one of the following: "allwinner,sun4i-osc-clk" - for a gatable oscillator "allwinner,sun4i-pll1-clk" - for the main PLL clock + "allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31 "allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock "allwinner,sun4i-axi-clk" - for the AXI clock "allwinner,sun4i-axi-gates-clk" - for the AXI gates @@ -15,6 +16,8 @@ Required properties: "allwinner,sun4i-ahb-gates-clk" - for the AHB gates on A10 "allwinner,sun5i-a13-ahb-gates-clk" - for the AHB gates on A13 "allwinner,sun5i-a10s-ahb-gates-clk" - for the AHB gates on A10s + "allwinner,sun6i-a31-ahb1-mux-clk" - for the AHB1 multiplexer on A31 + "allwinner,sun6i-a31-ahb1-gates-clk" - for the AHB1 gates on A31 "allwinner,sun4i-apb0-clk" - for the APB0 clock "allwinner,sun4i-apb0-gates-clk" - for the APB0 gates on A10 "allwinner,sun5i-a13-apb0-gates-clk" - for the APB0 gates on A13 @@ -24,6 +27,9 @@ Required properties: "allwinner,sun4i-apb1-gates-clk" - for the APB1 gates on A10 "allwinner,sun5i-a13-apb1-gates-clk" - for the APB1 gates on A13 "allwinner,sun5i-a10s-apb1-gates-clk" - for the APB1 gates on A10s + "allwinner,sun6i-a31-apb1-gates-clk" - for the APB1 gates on A31 + "allwinner,sun6i-a31-apb2-div-clk" - for the APB2 gates on A31 + "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 Required properties for all clocks: - reg : shall be the control register address for the clock. diff --git a/Documentation/devicetree/bindings/clock/sunxi/sun6i-a31-gates.txt b/Documentation/devicetree/bindings/clock/sunxi/sun6i-a31-gates.txt new file mode 100644 index 0000000000000000000000000000000000000000..fe44932b5c6b3843a94195c72e28db39aa880976 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/sunxi/sun6i-a31-gates.txt @@ -0,0 +1,83 @@ +Gate clock outputs +------------------ + + * AHB1 gates ("allwinner,sun6i-a31-ahb1-gates-clk") + + MIPI DSI 1 + + SS 5 + DMA 6 + + MMC0 8 + MMC1 9 + MMC2 10 + MMC3 11 + + NAND1 12 + NAND0 13 + SDRAM 14 + + GMAC 17 + TS 18 + HSTIMER 19 + SPI0 20 + SPI1 21 + SPI2 22 + SPI3 23 + USB_OTG 24 + + EHCI0 26 + EHCI1 27 + + OHCI0 29 + OHCI1 30 + OHCI2 31 + VE 32 + + LCD0 36 + LCD1 37 + + CSI 40 + + HDMI 43 + DE_BE0 44 + DE_BE1 45 + DE_FE1 46 + DE_FE1 47 + + MP 50 + + GPU 52 + + DEU0 55 + DEU1 56 + DRC0 57 + DRC1 58 + + * APB1 gates ("allwinner,sun6i-a31-apb1-gates-clk") + + CODEC 0 + + DIGITAL MIC 4 + PIO 5 + + DAUDIO0 12 + DAUDIO1 13 + + * APB2 gates ("allwinner,sun6i-a31-apb2-gates-clk") + + I2C0 0 + I2C1 1 + I2C2 2 + I2C3 3 + + UART0 16 + UART1 17 + UART2 18 + UART3 19 + UART4 20 + UART5 21 + +Notation: + [*]: The datasheet didn't mention these, but they are present on AW code + [**]: The datasheet had this marked as "NC" but they are used on AW code diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 2cafda88b7f66747672b1bddaec279a234460969..6fd02042c13bb9119782ca7a52b886308f548514 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -125,7 +125,89 @@ static void sun4i_get_pll1_factors(u32 *freq, u32 parent_rate, *n = div / 4; } +/** + * sun6i_a31_get_pll1_factors() - calculates n, k and m factors for PLL1 + * PLL1 rate is calculated as follows + * rate = parent_rate * (n + 1) * (k + 1) / (m + 1); + * parent_rate should always be 24MHz + */ +static void sun6i_a31_get_pll1_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + /* + * We can operate only on MHz, this will make our life easier + * later. + */ + u32 freq_mhz = *freq / 1000000; + u32 parent_freq_mhz = parent_rate / 1000000; + + /* + * Round down the frequency to the closest multiple of either + * 6 or 16 + */ + u32 round_freq_6 = round_down(freq_mhz, 6); + u32 round_freq_16 = round_down(freq_mhz, 16); + + if (round_freq_6 > round_freq_16) + freq_mhz = round_freq_6; + else + freq_mhz = round_freq_16; + *freq = freq_mhz * 1000000; + + /* + * If the factors pointer are null, we were just called to + * round down the frequency. + * Exit. + */ + if (n == NULL) + return; + + /* If the frequency is a multiple of 32 MHz, k is always 3 */ + if (!(freq_mhz % 32)) + *k = 3; + /* If the frequency is a multiple of 9 MHz, k is always 2 */ + else if (!(freq_mhz % 9)) + *k = 2; + /* If the frequency is a multiple of 8 MHz, k is always 1 */ + else if (!(freq_mhz % 8)) + *k = 1; + /* Otherwise, we don't use the k factor */ + else + *k = 0; + + /* + * If the frequency is a multiple of 2 but not a multiple of + * 3, m is 3. This is the first time we use 6 here, yet we + * will use it on several other places. + * We use this number because it's the lowest frequency we can + * generate (with n = 0, k = 0, m = 3), so every other frequency + * somehow relates to this frequency. + */ + if ((freq_mhz % 6) == 2 || (freq_mhz % 6) == 4) + *m = 2; + /* + * If the frequency is a multiple of 6MHz, but the factor is + * odd, m will be 3 + */ + else if ((freq_mhz / 6) & 1) + *m = 3; + /* Otherwise, we end up with m = 1 */ + else + *m = 1; + + /* Calculate n thanks to the above factors we already got */ + *n = freq_mhz * (*m + 1) / ((*k + 1) * parent_freq_mhz) - 1; + + /* + * If n end up being outbound, and that we can still decrease + * m, do it. + */ + if ((*n + 1) > 31 && (*m + 1) > 1) { + *n = (*n + 1) / 2 - 1; + *m = (*m + 1) / 2 - 1; + } +} /** * sun4i_get_apb1_factors() - calculates m, p factors for APB1 @@ -190,6 +272,15 @@ static struct clk_factors_config sun4i_pll1_config = { .pwidth = 2, }; +static struct clk_factors_config sun6i_a31_pll1_config = { + .nshift = 8, + .nwidth = 5, + .kshift = 4, + .kwidth = 2, + .mshift = 0, + .mwidth = 2, +}; + static struct clk_factors_config sun4i_apb1_config = { .mshift = 0, .mwidth = 5, @@ -202,6 +293,11 @@ static const __initconst struct factors_data sun4i_pll1_data = { .getter = sun4i_get_pll1_factors, }; +static const __initconst struct factors_data sun6i_a31_pll1_data = { + .table = &sun6i_a31_pll1_config, + .getter = sun6i_a31_get_pll1_factors, +}; + static const __initconst struct factors_data sun4i_apb1_data = { .table = &sun4i_apb1_config, .getter = sun4i_get_apb1_factors, @@ -244,6 +340,10 @@ static const __initconst struct mux_data sun4i_cpu_mux_data = { .shift = 16, }; +static const __initconst struct mux_data sun6i_a31_ahb1_mux_data = { + .shift = 12, +}; + static const __initconst struct mux_data sun4i_apb1_mux_data = { .shift = 24, }; @@ -303,6 +403,12 @@ static const __initconst struct div_data sun4i_apb0_data = { .width = 2, }; +static const __initconst struct div_data sun6i_a31_apb2_div_data = { + .shift = 0, + .pow = 0, + .width = 4, +}; + static void __init sunxi_divider_clk_setup(struct device_node *node, struct div_data *data) { @@ -353,6 +459,10 @@ static const __initconst struct gates_data sun5i_a13_ahb_gates_data = { .mask = {0x107067e7, 0x185111}, }; +static const __initconst struct gates_data sun6i_a31_ahb1_gates_data = { + .mask = {0xEDFE7F62, 0x794F931}, +}; + static const __initconst struct gates_data sun4i_apb0_gates_data = { .mask = {0x4EF}, }; @@ -377,6 +487,14 @@ static const __initconst struct gates_data sun5i_a13_apb1_gates_data = { .mask = {0xa0007}, }; +static const __initconst struct gates_data sun6i_a31_apb1_gates_data = { + .mask = {0x3031}, +}; + +static const __initconst struct gates_data sun6i_a31_apb2_gates_data = { + .mask = {0x3F000F}, +}; + static void __init sunxi_gates_clk_setup(struct device_node *node, struct gates_data *data) { @@ -429,6 +547,7 @@ static void __init sunxi_gates_clk_setup(struct device_node *node, /* Matches for factors clocks */ static const __initconst struct of_device_id clk_factors_match[] = { {.compatible = "allwinner,sun4i-pll1-clk", .data = &sun4i_pll1_data,}, + {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,}, {.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_apb1_data,}, {} }; @@ -438,6 +557,7 @@ static const __initconst struct of_device_id clk_div_match[] = { {.compatible = "allwinner,sun4i-axi-clk", .data = &sun4i_axi_data,}, {.compatible = "allwinner,sun4i-ahb-clk", .data = &sun4i_ahb_data,}, {.compatible = "allwinner,sun4i-apb0-clk", .data = &sun4i_apb0_data,}, + {.compatible = "allwinner,sun6i-a31-apb2-div-clk", .data = &sun6i_a31_apb2_div_data,}, {} }; @@ -445,6 +565,7 @@ static const __initconst struct of_device_id clk_div_match[] = { static const __initconst struct of_device_id clk_mux_match[] = { {.compatible = "allwinner,sun4i-cpu-clk", .data = &sun4i_cpu_mux_data,}, {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &sun4i_apb1_mux_data,}, + {.compatible = "allwinner,sun6i-a31-ahb1-mux-clk", .data = &sun6i_a31_ahb1_mux_data,}, {} }; @@ -454,12 +575,15 @@ static const __initconst struct of_device_id clk_gates_match[] = { {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &sun4i_ahb_gates_data,}, {.compatible = "allwinner,sun5i-a10s-ahb-gates-clk", .data = &sun5i_a10s_ahb_gates_data,}, {.compatible = "allwinner,sun5i-a13-ahb-gates-clk", .data = &sun5i_a13_ahb_gates_data,}, + {.compatible = "allwinner,sun6i-a31-ahb1-gates-clk", .data = &sun6i_a31_ahb1_gates_data,}, {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &sun4i_apb0_gates_data,}, {.compatible = "allwinner,sun5i-a10s-apb0-gates-clk", .data = &sun5i_a10s_apb0_gates_data,}, {.compatible = "allwinner,sun5i-a13-apb0-gates-clk", .data = &sun5i_a13_apb0_gates_data,}, {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &sun4i_apb1_gates_data,}, {.compatible = "allwinner,sun5i-a10s-apb1-gates-clk", .data = &sun5i_a10s_apb1_gates_data,}, {.compatible = "allwinner,sun5i-a13-apb1-gates-clk", .data = &sun5i_a13_apb1_gates_data,}, + {.compatible = "allwinner,sun6i-a31-apb1-gates-clk", .data = &sun6i_a31_apb1_gates_data,}, + {.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,}, {} };