diff options
Diffstat (limited to 'drivers/clk/at91')
32 files changed, 4657 insertions, 2328 deletions
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 423605f452..083555b4d2 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -15,8 +15,11 @@ obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o obj-$(CONFIG_HAVE_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o -obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o dt-compat.o +obj-$(CONFIG_SOC_AT91RM9200) += at91rm9200.o +obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o +obj-$(CONFIG_SOC_AT91SAM9) += at91sam9g45.o +obj-$(CONFIG_SOC_AT91SAM9) += at91sam9n12.o at91sam9x5.o obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o +obj-$(CONFIG_SOC_SAMA5D3) += sama5d3.o obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o -obj-$(CONFIG_SOC_SAMA5D3) += dt-compat.o obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o diff --git a/drivers/clk/at91/at91rm9200.c b/drivers/clk/at91/at91rm9200.c new file mode 100644 index 0000000000..df75a93edb --- /dev/null +++ b/drivers/clk/at91/at91rm9200.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/clk-provider.h> +#include <mfd/syscon.h> +#include <linux/slab.h> +#include <stdio.h> + +#include <dt-bindings/clock/at91.h> + +#include "pmc.h" + +static DEFINE_SPINLOCK(rm9200_mck_lock); + +struct sck { + char *n; + char *p; + u8 id; +}; + +struct pck { + char *n; + u8 id; +}; + +static const struct clk_master_characteristics rm9200_mck_characteristics = { + .output = { .min = 0, .max = 80000000 }, + .divisors = { 1, 2, 3, 4 }, +}; + +static u8 rm9200_pll_out[] = { 0, 2 }; + +static const struct clk_range rm9200_pll_outputs[] = { + { .min = 80000000, .max = 160000000 }, + { .min = 150000000, .max = 180000000 }, +}; + +static const struct clk_pll_characteristics rm9200_pll_characteristics = { + .input = { .min = 1000000, .max = 32000000 }, + .num_output = ARRAY_SIZE(rm9200_pll_outputs), + .output = rm9200_pll_outputs, + .out = rm9200_pll_out, +}; + +static const struct sck at91rm9200_systemck[] = { + { .n = "udpck", .p = "usbck", .id = 1 }, + { .n = "uhpck", .p = "usbck", .id = 4 }, + { .n = "pck0", .p = "prog0", .id = 8 }, + { .n = "pck1", .p = "prog1", .id = 9 }, + { .n = "pck2", .p = "prog2", .id = 10 }, + { .n = "pck3", .p = "prog3", .id = 11 }, +}; + +static const struct pck at91rm9200_periphck[] = { + { .n = "pioA_clk", .id = 2 }, + { .n = "pioB_clk", .id = 3 }, + { .n = "pioC_clk", .id = 4 }, + { .n = "pioD_clk", .id = 5 }, + { .n = "usart0_clk", .id = 6 }, + { .n = "usart1_clk", .id = 7 }, + { .n = "usart2_clk", .id = 8 }, + { .n = "usart3_clk", .id = 9 }, + { .n = "mci0_clk", .id = 10 }, + { .n = "udc_clk", .id = 11 }, + { .n = "twi0_clk", .id = 12 }, + { .n = "spi0_clk", .id = 13 }, + { .n = "ssc0_clk", .id = 14 }, + { .n = "ssc1_clk", .id = 15 }, + { .n = "ssc2_clk", .id = 16 }, + { .n = "tc0_clk", .id = 17 }, + { .n = "tc1_clk", .id = 18 }, + { .n = "tc2_clk", .id = 19 }, + { .n = "tc3_clk", .id = 20 }, + { .n = "tc4_clk", .id = 21 }, + { .n = "tc5_clk", .id = 22 }, + { .n = "ohci_clk", .id = 23 }, + { .n = "macb0_clk", .id = 24 }, +}; + +static void __init at91rm9200_pmc_setup(struct device_node *np) +{ + const char *slowxtal_name, *mainxtal_name; + struct pmc_data *at91rm9200_pmc; + u32 usb_div[] = { 1, 2, 0, 0 }; + const char *parent_names[6]; + struct regmap *regmap; + struct clk_hw *hw; + int i; + bool bypass; + + i = of_property_match_string(np, "clock-names", "slow_xtal"); + if (i < 0) + return; + + slowxtal_name = of_clk_get_parent_name(np, i); + + i = of_property_match_string(np, "clock-names", "main_xtal"); + if (i < 0) + return; + mainxtal_name = of_clk_get_parent_name(np, i); + + regmap = device_node_to_regmap(np); + if (IS_ERR(regmap)) + return; + + at91rm9200_pmc = pmc_data_allocate(PMC_PLLBCK + 1, + nck(at91rm9200_systemck), + nck(at91rm9200_periphck), 0, 4); + if (!at91rm9200_pmc) + return; + + bypass = of_property_read_bool(np, "atmel,osc-bypass"); + + hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, + bypass); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc"); + if (IS_ERR(hw)) + goto err_free; + + at91rm9200_pmc->chws[PMC_MAIN] = hw; + + hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0, + &at91rm9200_pll_layout, + &rm9200_pll_characteristics); + if (IS_ERR(hw)) + goto err_free; + + at91rm9200_pmc->chws[PMC_PLLACK] = hw; + + hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1, + &at91rm9200_pll_layout, + &rm9200_pll_characteristics); + if (IS_ERR(hw)) + goto err_free; + + at91rm9200_pmc->chws[PMC_PLLBCK] = hw; + + parent_names[0] = slowxtal_name; + parent_names[1] = "mainck"; + parent_names[2] = "pllack"; + parent_names[3] = "pllbck"; + hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4, + parent_names, + &at91rm9200_master_layout, + &rm9200_mck_characteristics, + &rm9200_mck_lock); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_master_div(regmap, "masterck_div", + "masterck_pres", + &at91rm9200_master_layout, + &rm9200_mck_characteristics, + &rm9200_mck_lock, CLK_SET_RATE_GATE); + if (IS_ERR(hw)) + goto err_free; + + at91rm9200_pmc->chws[PMC_MCK] = hw; + + hw = at91rm9200_clk_register_usb(regmap, "usbck", "pllbck", usb_div); + if (IS_ERR(hw)) + goto err_free; + + parent_names[0] = slowxtal_name; + parent_names[1] = "mainck"; + parent_names[2] = "pllack"; + parent_names[3] = "pllbck"; + for (i = 0; i < 4; i++) { + char name[6]; + + snprintf(name, sizeof(name), "prog%d", i); + + hw = at91_clk_register_programmable(regmap, name, + parent_names, 4, i, + &at91rm9200_programmable_layout, + NULL); + if (IS_ERR(hw)) + goto err_free; + + at91rm9200_pmc->pchws[i] = hw; + } + + for (i = 0; i < ARRAY_SIZE(at91rm9200_systemck); i++) { + hw = at91_clk_register_system(regmap, at91rm9200_systemck[i].n, + at91rm9200_systemck[i].p, + at91rm9200_systemck[i].id, 0); + if (IS_ERR(hw)) + goto err_free; + + at91rm9200_pmc->shws[at91rm9200_systemck[i].id] = hw; + } + + for (i = 0; i < ARRAY_SIZE(at91rm9200_periphck); i++) { + hw = at91_clk_register_peripheral(regmap, + at91rm9200_periphck[i].n, + "masterck_div", + at91rm9200_periphck[i].id); + if (IS_ERR(hw)) + goto err_free; + + at91rm9200_pmc->phws[at91rm9200_periphck[i].id] = hw; + } + + of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91rm9200_pmc); + + return; + +err_free: + kfree(at91rm9200_pmc); +} +/* + * While the TCB can be used as the clocksource, the system timer is most likely + * to be used instead. However, the pinctrl driver doesn't support probe + * deferring properly. Once this is fixed, this can be switched to a platform + * driver. + */ +CLK_OF_DECLARE(at91rm9200_pmc, "atmel,at91rm9200-pmc", at91rm9200_pmc_setup); diff --git a/drivers/clk/at91/at91sam9260.c b/drivers/clk/at91/at91sam9260.c index 066dedf2a1..c94cd95566 100644 --- a/drivers/clk/at91/at91sam9260.c +++ b/drivers/clk/at91/at91sam9260.c @@ -1,13 +1,8 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include <driver.h> -#include <regmap.h> -#include <stdio.h> +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/clk-provider.h> #include <mfd/syscon.h> - -#include <linux/clk.h> #include <linux/slab.h> -#include <linux/types.h> +#include <stdio.h> #include <dt-bindings/clock/at91.h> @@ -38,6 +33,8 @@ struct at91sam926x_data { bool has_slck; }; +static DEFINE_SPINLOCK(at91sam9260_mck_lock); + static const struct clk_master_characteristics sam9260_mck_characteristics = { .output = { .min = 0, .max = 105000000 }, .divisors = { 1, 2, 4, 0 }, @@ -224,8 +221,8 @@ static const struct sck at91sam9261_systemck[] = { { .n = "pck1", .p = "prog1", .id = 9 }, { .n = "pck2", .p = "prog2", .id = 10 }, { .n = "pck3", .p = "prog3", .id = 11 }, - { .n = "hclk0", .p = "masterck", .id = 16 }, - { .n = "hclk1", .p = "masterck", .id = 17 }, + { .n = "hclk0", .p = "masterck_div", .id = 16 }, + { .n = "hclk1", .p = "masterck_div", .id = 17 }, }; static const struct pck at91sam9261_periphck[] = { @@ -339,7 +336,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np, const char *parent_names[6]; const char *slck_name; struct regmap *regmap; - struct clk *hw; + struct clk_hw *hw; int i; bool bypass; @@ -358,9 +355,10 @@ static void __init at91sam926x_pmc_setup(struct device_node *np, if (IS_ERR(regmap)) return; - at91sam9260_pmc = pmc_data_allocate(PMC_MAIN + 1, + at91sam9260_pmc = pmc_data_allocate(PMC_PLLBCK + 1, ndck(data->sck, data->num_sck), - ndck(data->pck, data->num_pck), 0); + ndck(data->pck, data->num_pck), + 0, data->num_progck); if (!at91sam9260_pmc) return; @@ -378,7 +376,10 @@ static void __init at91sam926x_pmc_setup(struct device_node *np, at91sam9260_pmc->chws[PMC_MAIN] = hw; if (data->has_slck) { - hw = clk_fixed("slow_rc_osc", 32768); + hw = clk_hw_register_fixed_rate_with_accuracy(NULL, + "slow_rc_osc", + NULL, 0, 32768, + 50000000); if (IS_ERR(hw)) goto err_free; @@ -401,19 +402,34 @@ static void __init at91sam926x_pmc_setup(struct device_node *np, if (IS_ERR(hw)) goto err_free; + at91sam9260_pmc->chws[PMC_PLLACK] = hw; + hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1, data->pllb_layout, data->pllb_characteristics); if (IS_ERR(hw)) goto err_free; + at91sam9260_pmc->chws[PMC_PLLBCK] = hw; + parent_names[0] = slck_name; parent_names[1] = "mainck"; parent_names[2] = "pllack"; parent_names[3] = "pllbck"; - hw = at91_clk_register_master(regmap, "masterck", 4, parent_names, - &at91rm9200_master_layout, - data->mck_characteristics); + hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4, + parent_names, + &at91rm9200_master_layout, + data->mck_characteristics, + &at91sam9260_mck_lock); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_master_div(regmap, "masterck_div", + "masterck_pres", + &at91rm9200_master_layout, + data->mck_characteristics, + &at91sam9260_mck_lock, + CLK_SET_RATE_GATE); if (IS_ERR(hw)) goto err_free; @@ -428,21 +444,24 @@ static void __init at91sam926x_pmc_setup(struct device_node *np, parent_names[2] = "pllack"; parent_names[3] = "pllbck"; for (i = 0; i < data->num_progck; i++) { - char *name; + char name[6]; - name = xasprintf("prog%d", i); + snprintf(name, sizeof(name), "prog%d", i); hw = at91_clk_register_programmable(regmap, name, parent_names, 4, i, - &at91rm9200_programmable_layout); + &at91rm9200_programmable_layout, + NULL); if (IS_ERR(hw)) goto err_free; + + at91sam9260_pmc->pchws[i] = hw; } for (i = 0; i < data->num_sck; i++) { hw = at91_clk_register_system(regmap, data->sck[i].n, data->sck[i].p, - data->sck[i].id); + data->sck[i].id, 0); if (IS_ERR(hw)) goto err_free; @@ -452,7 +471,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np, for (i = 0; i < data->num_pck; i++) { hw = at91_clk_register_peripheral(regmap, data->pck[i].n, - "masterck", + "masterck_div", data->pck[i].id); if (IS_ERR(hw)) goto err_free; @@ -460,38 +479,38 @@ static void __init at91sam926x_pmc_setup(struct device_node *np, at91sam9260_pmc->phws[data->pck[i].id] = hw; } - of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9260_pmc); + of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9260_pmc); return; err_free: - pmc_data_free(at91sam9260_pmc); + kfree(at91sam9260_pmc); } static void __init at91sam9260_pmc_setup(struct device_node *np) { at91sam926x_pmc_setup(np, &at91sam9260_data); } -CLK_OF_DECLARE_DRIVER(at91sam9260_pmc, "atmel,at91sam9260-pmc", - at91sam9260_pmc_setup); + +CLK_OF_DECLARE(at91sam9260_pmc, "atmel,at91sam9260-pmc", at91sam9260_pmc_setup); static void __init at91sam9261_pmc_setup(struct device_node *np) { at91sam926x_pmc_setup(np, &at91sam9261_data); } -CLK_OF_DECLARE_DRIVER(at91sam9261_pmc, "atmel,at91sam9261-pmc", - at91sam9261_pmc_setup); + +CLK_OF_DECLARE(at91sam9261_pmc, "atmel,at91sam9261-pmc", at91sam9261_pmc_setup); static void __init at91sam9263_pmc_setup(struct device_node *np) { at91sam926x_pmc_setup(np, &at91sam9263_data); } -CLK_OF_DECLARE_DRIVER(at91sam9263_pmc, "atmel,at91sam9263-pmc", - at91sam9263_pmc_setup); + +CLK_OF_DECLARE(at91sam9263_pmc, "atmel,at91sam9263-pmc", at91sam9263_pmc_setup); static void __init at91sam9g20_pmc_setup(struct device_node *np) { at91sam926x_pmc_setup(np, &at91sam9g20_data); } -CLK_OF_DECLARE_DRIVER(at91sam9g20_pmc, "atmel,at91sam9g20-pmc", - at91sam9g20_pmc_setup); + +CLK_OF_DECLARE(at91sam9g20_pmc, "atmel,at91sam9g20-pmc", at91sam9g20_pmc_setup); diff --git a/drivers/clk/at91/at91sam9g45.c b/drivers/clk/at91/at91sam9g45.c new file mode 100644 index 0000000000..fedf961393 --- /dev/null +++ b/drivers/clk/at91/at91sam9g45.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/clk-provider.h> +#include <mfd/syscon.h> +#include <linux/slab.h> +#include <stdio.h> + +#include <dt-bindings/clock/at91.h> + +#include "pmc.h" + +static DEFINE_SPINLOCK(at91sam9g45_mck_lock); + +static const struct clk_master_characteristics mck_characteristics = { + .output = { .min = 0, .max = 133333333 }, + .divisors = { 1, 2, 4, 3 }, +}; + +static u8 plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 }; + +static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 }; + +static const struct clk_range plla_outputs[] = { + { .min = 745000000, .max = 800000000 }, + { .min = 695000000, .max = 750000000 }, + { .min = 645000000, .max = 700000000 }, + { .min = 595000000, .max = 650000000 }, + { .min = 545000000, .max = 600000000 }, + { .min = 495000000, .max = 555000000 }, + { .min = 445000000, .max = 500000000 }, + { .min = 400000000, .max = 450000000 }, +}; + +static const struct clk_pll_characteristics plla_characteristics = { + .input = { .min = 2000000, .max = 32000000 }, + .num_output = ARRAY_SIZE(plla_outputs), + .output = plla_outputs, + .icpll = plla_icpll, + .out = plla_out, +}; + +static const struct { + char *n; + char *p; + unsigned long flags; + u8 id; +} at91sam9g45_systemck[] = { + /* + * ddrck feeds DDR controller and is enabled by bootloader thus we need + * to keep it enabled in case there is no Linux consumer for it. + */ + { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL }, + { .n = "uhpck", .p = "usbck", .id = 6 }, + { .n = "pck0", .p = "prog0", .id = 8 }, + { .n = "pck1", .p = "prog1", .id = 9 }, +}; + +struct pck { + char *n; + u8 id; +}; + +static const struct pck at91sam9g45_periphck[] = { + { .n = "pioA_clk", .id = 2, }, + { .n = "pioB_clk", .id = 3, }, + { .n = "pioC_clk", .id = 4, }, + { .n = "pioDE_clk", .id = 5, }, + { .n = "trng_clk", .id = 6, }, + { .n = "usart0_clk", .id = 7, }, + { .n = "usart1_clk", .id = 8, }, + { .n = "usart2_clk", .id = 9, }, + { .n = "usart3_clk", .id = 10, }, + { .n = "mci0_clk", .id = 11, }, + { .n = "twi0_clk", .id = 12, }, + { .n = "twi1_clk", .id = 13, }, + { .n = "spi0_clk", .id = 14, }, + { .n = "spi1_clk", .id = 15, }, + { .n = "ssc0_clk", .id = 16, }, + { .n = "ssc1_clk", .id = 17, }, + { .n = "tcb0_clk", .id = 18, }, + { .n = "pwm_clk", .id = 19, }, + { .n = "adc_clk", .id = 20, }, + { .n = "dma0_clk", .id = 21, }, + { .n = "uhphs_clk", .id = 22, }, + { .n = "lcd_clk", .id = 23, }, + { .n = "ac97_clk", .id = 24, }, + { .n = "macb0_clk", .id = 25, }, + { .n = "isi_clk", .id = 26, }, + { .n = "udphs_clk", .id = 27, }, + { .n = "aestdessha_clk", .id = 28, }, + { .n = "mci1_clk", .id = 29, }, + { .n = "vdec_clk", .id = 30, }, +}; + +static void __init at91sam9g45_pmc_setup(struct device_node *np) +{ + const char *slck_name, *mainxtal_name; + struct pmc_data *at91sam9g45_pmc; + const char *parent_names[6]; + struct regmap *regmap; + struct clk_hw *hw; + int i; + bool bypass; + + i = of_property_match_string(np, "clock-names", "slow_clk"); + if (i < 0) + return; + + slck_name = of_clk_get_parent_name(np, i); + + i = of_property_match_string(np, "clock-names", "main_xtal"); + if (i < 0) + return; + mainxtal_name = of_clk_get_parent_name(np, i); + + regmap = device_node_to_regmap(np); + if (IS_ERR(regmap)) + return; + + at91sam9g45_pmc = pmc_data_allocate(PMC_PLLACK + 1, + nck(at91sam9g45_systemck), + nck(at91sam9g45_periphck), 0, 2); + if (!at91sam9g45_pmc) + return; + + bypass = of_property_read_bool(np, "atmel,osc-bypass"); + + hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, + bypass); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc"); + if (IS_ERR(hw)) + goto err_free; + + at91sam9g45_pmc->chws[PMC_MAIN] = hw; + + hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0, + &at91rm9200_pll_layout, &plla_characteristics); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack"); + if (IS_ERR(hw)) + goto err_free; + + at91sam9g45_pmc->chws[PMC_PLLACK] = hw; + + hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck"); + if (IS_ERR(hw)) + goto err_free; + + at91sam9g45_pmc->chws[PMC_UTMI] = hw; + + parent_names[0] = slck_name; + parent_names[1] = "mainck"; + parent_names[2] = "plladivck"; + parent_names[3] = "utmick"; + hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4, + parent_names, + &at91rm9200_master_layout, + &mck_characteristics, + &at91sam9g45_mck_lock); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_master_div(regmap, "masterck_div", + "masterck_pres", + &at91rm9200_master_layout, + &mck_characteristics, + &at91sam9g45_mck_lock, + CLK_SET_RATE_GATE); + if (IS_ERR(hw)) + goto err_free; + + at91sam9g45_pmc->chws[PMC_MCK] = hw; + + parent_names[0] = "plladivck"; + parent_names[1] = "utmick"; + hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2); + if (IS_ERR(hw)) + goto err_free; + + parent_names[0] = slck_name; + parent_names[1] = "mainck"; + parent_names[2] = "plladivck"; + parent_names[3] = "utmick"; + parent_names[4] = "masterck_div"; + for (i = 0; i < 2; i++) { + char name[6]; + + snprintf(name, sizeof(name), "prog%d", i); + + hw = at91_clk_register_programmable(regmap, name, + parent_names, 5, i, + &at91sam9g45_programmable_layout, + NULL); + if (IS_ERR(hw)) + goto err_free; + + at91sam9g45_pmc->pchws[i] = hw; + } + + for (i = 0; i < ARRAY_SIZE(at91sam9g45_systemck); i++) { + hw = at91_clk_register_system(regmap, at91sam9g45_systemck[i].n, + at91sam9g45_systemck[i].p, + at91sam9g45_systemck[i].id, + at91sam9g45_systemck[i].flags); + if (IS_ERR(hw)) + goto err_free; + + at91sam9g45_pmc->shws[at91sam9g45_systemck[i].id] = hw; + } + + for (i = 0; i < ARRAY_SIZE(at91sam9g45_periphck); i++) { + hw = at91_clk_register_peripheral(regmap, + at91sam9g45_periphck[i].n, + "masterck_div", + at91sam9g45_periphck[i].id); + if (IS_ERR(hw)) + goto err_free; + + at91sam9g45_pmc->phws[at91sam9g45_periphck[i].id] = hw; + } + + of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9g45_pmc); + + return; + +err_free: + kfree(at91sam9g45_pmc); +} +/* + * The TCB is used as the clocksource so its clock is needed early. This means + * this can't be a platform driver. + */ +CLK_OF_DECLARE(at91sam9g45_pmc, "atmel,at91sam9g45-pmc", at91sam9g45_pmc_setup); diff --git a/drivers/clk/at91/at91sam9n12.c b/drivers/clk/at91/at91sam9n12.c new file mode 100644 index 0000000000..bb075de9fd --- /dev/null +++ b/drivers/clk/at91/at91sam9n12.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/clk-provider.h> +#include <mfd/syscon.h> +#include <linux/slab.h> +#include <stdio.h> + +#include <dt-bindings/clock/at91.h> + +#include "pmc.h" + +static DEFINE_SPINLOCK(at91sam9n12_mck_lock); + +static const struct clk_master_characteristics mck_characteristics = { + .output = { .min = 0, .max = 133333333 }, + .divisors = { 1, 2, 4, 3 }, + .have_div3_pres = 1, +}; + +static u8 plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 }; + +static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 }; + +static const struct clk_range plla_outputs[] = { + { .min = 745000000, .max = 800000000 }, + { .min = 695000000, .max = 750000000 }, + { .min = 645000000, .max = 700000000 }, + { .min = 595000000, .max = 650000000 }, + { .min = 545000000, .max = 600000000 }, + { .min = 495000000, .max = 555000000 }, + { .min = 445000000, .max = 500000000 }, + { .min = 400000000, .max = 450000000 }, +}; + +static const struct clk_pll_characteristics plla_characteristics = { + .input = { .min = 2000000, .max = 32000000 }, + .num_output = ARRAY_SIZE(plla_outputs), + .output = plla_outputs, + .icpll = plla_icpll, + .out = plla_out, +}; + +static u8 pllb_out[] = { 0 }; + +static const struct clk_range pllb_outputs[] = { + { .min = 30000000, .max = 100000000 }, +}; + +static const struct clk_pll_characteristics pllb_characteristics = { + .input = { .min = 2000000, .max = 32000000 }, + .num_output = ARRAY_SIZE(pllb_outputs), + .output = pllb_outputs, + .out = pllb_out, +}; + +static const struct { + char *n; + char *p; + unsigned long flags; + u8 id; +} at91sam9n12_systemck[] = { + /* + * ddrck feeds DDR controller and is enabled by bootloader thus we need + * to keep it enabled in case there is no Linux consumer for it. + */ + { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL }, + { .n = "lcdck", .p = "masterck_div", .id = 3 }, + { .n = "uhpck", .p = "usbck", .id = 6 }, + { .n = "udpck", .p = "usbck", .id = 7 }, + { .n = "pck0", .p = "prog0", .id = 8 }, + { .n = "pck1", .p = "prog1", .id = 9 }, +}; + +static const struct clk_pcr_layout at91sam9n12_pcr_layout = { + .offset = 0x10c, + .cmd = BIT(12), + .pid_mask = GENMASK(5, 0), + .div_mask = GENMASK(17, 16), +}; + +struct pck { + char *n; + u8 id; +}; + +static const struct pck at91sam9n12_periphck[] = { + { .n = "pioAB_clk", .id = 2, }, + { .n = "pioCD_clk", .id = 3, }, + { .n = "fuse_clk", .id = 4, }, + { .n = "usart0_clk", .id = 5, }, + { .n = "usart1_clk", .id = 6, }, + { .n = "usart2_clk", .id = 7, }, + { .n = "usart3_clk", .id = 8, }, + { .n = "twi0_clk", .id = 9, }, + { .n = "twi1_clk", .id = 10, }, + { .n = "mci0_clk", .id = 12, }, + { .n = "spi0_clk", .id = 13, }, + { .n = "spi1_clk", .id = 14, }, + { .n = "uart0_clk", .id = 15, }, + { .n = "uart1_clk", .id = 16, }, + { .n = "tcb_clk", .id = 17, }, + { .n = "pwm_clk", .id = 18, }, + { .n = "adc_clk", .id = 19, }, + { .n = "dma0_clk", .id = 20, }, + { .n = "uhphs_clk", .id = 22, }, + { .n = "udphs_clk", .id = 23, }, + { .n = "lcdc_clk", .id = 25, }, + { .n = "sha_clk", .id = 27, }, + { .n = "ssc0_clk", .id = 28, }, + { .n = "aes_clk", .id = 29, }, + { .n = "trng_clk", .id = 30, }, +}; + +static void __init at91sam9n12_pmc_setup(struct device_node *np) +{ + struct clk_range range = CLK_RANGE(0, 0); + const char *slck_name, *mainxtal_name; + struct pmc_data *at91sam9n12_pmc; + const char *parent_names[6]; + struct regmap *regmap; + struct clk_hw *hw; + int i; + bool bypass; + + i = of_property_match_string(np, "clock-names", "slow_clk"); + if (i < 0) + return; + + slck_name = of_clk_get_parent_name(np, i); + + i = of_property_match_string(np, "clock-names", "main_xtal"); + if (i < 0) + return; + mainxtal_name = of_clk_get_parent_name(np, i); + + regmap = device_node_to_regmap(np); + if (IS_ERR(regmap)) + return; + + at91sam9n12_pmc = pmc_data_allocate(PMC_PLLBCK + 1, + nck(at91sam9n12_systemck), 31, 0, 2); + if (!at91sam9n12_pmc) + return; + + hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000, + 50000000); + if (IS_ERR(hw)) + goto err_free; + + bypass = of_property_read_bool(np, "atmel,osc-bypass"); + + hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, + bypass); + if (IS_ERR(hw)) + goto err_free; + + parent_names[0] = "main_rc_osc"; + parent_names[1] = "main_osc"; + hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2); + if (IS_ERR(hw)) + goto err_free; + + at91sam9n12_pmc->chws[PMC_MAIN] = hw; + + hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0, + &at91rm9200_pll_layout, &plla_characteristics); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack"); + if (IS_ERR(hw)) + goto err_free; + + at91sam9n12_pmc->chws[PMC_PLLACK] = hw; + + hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1, + &at91rm9200_pll_layout, &pllb_characteristics); + if (IS_ERR(hw)) + goto err_free; + + at91sam9n12_pmc->chws[PMC_PLLBCK] = hw; + + parent_names[0] = slck_name; + parent_names[1] = "mainck"; + parent_names[2] = "plladivck"; + parent_names[3] = "pllbck"; + hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4, + parent_names, + &at91sam9x5_master_layout, + &mck_characteristics, + &at91sam9n12_mck_lock); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_master_div(regmap, "masterck_div", + "masterck_pres", + &at91sam9x5_master_layout, + &mck_characteristics, + &at91sam9n12_mck_lock, + CLK_SET_RATE_GATE); + if (IS_ERR(hw)) + goto err_free; + + at91sam9n12_pmc->chws[PMC_MCK] = hw; + + hw = at91sam9n12_clk_register_usb(regmap, "usbck", "pllbck"); + if (IS_ERR(hw)) + goto err_free; + + parent_names[0] = slck_name; + parent_names[1] = "mainck"; + parent_names[2] = "plladivck"; + parent_names[3] = "pllbck"; + parent_names[4] = "masterck_div"; + for (i = 0; i < 2; i++) { + char name[6]; + + snprintf(name, sizeof(name), "prog%d", i); + + hw = at91_clk_register_programmable(regmap, name, + parent_names, 5, i, + &at91sam9x5_programmable_layout, + NULL); + if (IS_ERR(hw)) + goto err_free; + + at91sam9n12_pmc->pchws[i] = hw; + } + + for (i = 0; i < ARRAY_SIZE(at91sam9n12_systemck); i++) { + hw = at91_clk_register_system(regmap, at91sam9n12_systemck[i].n, + at91sam9n12_systemck[i].p, + at91sam9n12_systemck[i].id, + at91sam9n12_systemck[i].flags); + if (IS_ERR(hw)) + goto err_free; + + at91sam9n12_pmc->shws[at91sam9n12_systemck[i].id] = hw; + } + + for (i = 0; i < ARRAY_SIZE(at91sam9n12_periphck); i++) { + hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, + &at91sam9n12_pcr_layout, + at91sam9n12_periphck[i].n, + "masterck_div", + at91sam9n12_periphck[i].id, + &range, INT_MIN, 0); + if (IS_ERR(hw)) + goto err_free; + + at91sam9n12_pmc->phws[at91sam9n12_periphck[i].id] = hw; + } + + of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9n12_pmc); + + return; + +err_free: + kfree(at91sam9n12_pmc); +} +/* + * The TCB is used as the clocksource so its clock is needed early. This means + * this can't be a platform driver. + */ +CLK_OF_DECLARE(at91sam9n12_pmc, "atmel,at91sam9n12-pmc", at91sam9n12_pmc_setup); diff --git a/drivers/clk/at91/at91sam9rl.c b/drivers/clk/at91/at91sam9rl.c index ff47f94a8d..95b02d86d5 100644 --- a/drivers/clk/at91/at91sam9rl.c +++ b/drivers/clk/at91/at91sam9rl.c @@ -1,18 +1,15 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include <driver.h> -#include <regmap.h> -#include <stdio.h> +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/clk-provider.h> #include <mfd/syscon.h> - -#include <linux/clk.h> #include <linux/slab.h> -#include <linux/types.h> +#include <stdio.h> #include <dt-bindings/clock/at91.h> #include "pmc.h" +static DEFINE_SPINLOCK(sam9rl_mck_lock); + static const struct clk_master_characteristics sam9rl_mck_characteristics = { .output = { .min = 0, .max = 94000000 }, .divisors = { 1, 2, 4, 0 }, @@ -75,7 +72,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np) struct pmc_data *at91sam9rl_pmc; const char *parent_names[6]; struct regmap *regmap; - struct clk *hw; + struct clk_hw *hw; int i; i = of_property_match_string(np, "clock-names", "slow_clk"); @@ -93,9 +90,9 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np) if (IS_ERR(regmap)) return; - at91sam9rl_pmc = pmc_data_allocate(PMC_MAIN + 1, + at91sam9rl_pmc = pmc_data_allocate(PMC_PLLACK + 1, nck(at91sam9rl_systemck), - nck(at91sam9rl_periphck), 0); + nck(at91sam9rl_periphck), 0, 2); if (!at91sam9rl_pmc) return; @@ -111,6 +108,8 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np) if (IS_ERR(hw)) goto err_free; + at91sam9rl_pmc->chws[PMC_PLLACK] = hw; + hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck"); if (IS_ERR(hw)) goto err_free; @@ -121,9 +120,19 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np) parent_names[1] = "mainck"; parent_names[2] = "pllack"; parent_names[3] = "utmick"; - hw = at91_clk_register_master(regmap, "masterck", 4, parent_names, - &at91rm9200_master_layout, - &sam9rl_mck_characteristics); + hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4, + parent_names, + &at91rm9200_master_layout, + &sam9rl_mck_characteristics, + &sam9rl_mck_lock); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_master_div(regmap, "masterck_div", + "masterck_pres", + &at91rm9200_master_layout, + &sam9rl_mck_characteristics, + &sam9rl_mck_lock, CLK_SET_RATE_GATE); if (IS_ERR(hw)) goto err_free; @@ -133,23 +142,26 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np) parent_names[1] = "mainck"; parent_names[2] = "pllack"; parent_names[3] = "utmick"; - parent_names[4] = "masterck"; + parent_names[4] = "masterck_div"; for (i = 0; i < 2; i++) { - char *name; + char name[6]; - name = xasprintf("prog%d", i); + snprintf(name, sizeof(name), "prog%d", i); hw = at91_clk_register_programmable(regmap, name, parent_names, 5, i, - &at91rm9200_programmable_layout); + &at91rm9200_programmable_layout, + NULL); if (IS_ERR(hw)) goto err_free; + + at91sam9rl_pmc->pchws[i] = hw; } for (i = 0; i < ARRAY_SIZE(at91sam9rl_systemck); i++) { hw = at91_clk_register_system(regmap, at91sam9rl_systemck[i].n, at91sam9rl_systemck[i].p, - at91sam9rl_systemck[i].id); + at91sam9rl_systemck[i].id, 0); if (IS_ERR(hw)) goto err_free; @@ -159,7 +171,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np) for (i = 0; i < ARRAY_SIZE(at91sam9rl_periphck); i++) { hw = at91_clk_register_peripheral(regmap, at91sam9rl_periphck[i].n, - "masterck", + "masterck_div", at91sam9rl_periphck[i].id); if (IS_ERR(hw)) goto err_free; @@ -167,11 +179,12 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np) at91sam9rl_pmc->phws[at91sam9rl_periphck[i].id] = hw; } - of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9rl_pmc); + of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9rl_pmc); return; err_free: - pmc_data_free(at91sam9rl_pmc); + kfree(at91sam9rl_pmc); } -CLK_OF_DECLARE_DRIVER(at91sam9rl_pmc, "atmel,at91sam9rl-pmc", at91sam9rl_pmc_setup); + +CLK_OF_DECLARE(at91sam9rl_pmc, "atmel,at91sam9rl-pmc", at91sam9rl_pmc_setup); diff --git a/drivers/clk/at91/at91sam9x5.c b/drivers/clk/at91/at91sam9x5.c index baa71aa105..f4dc7ceeea 100644 --- a/drivers/clk/at91/at91sam9x5.c +++ b/drivers/clk/at91/at91sam9x5.c @@ -1,18 +1,15 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include <driver.h> -#include <regmap.h> -#include <stdio.h> +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/clk-provider.h> #include <mfd/syscon.h> - -#include <linux/clk.h> #include <linux/slab.h> -#include <linux/types.h> +#include <stdio.h> #include <dt-bindings/clock/at91.h> #include "pmc.h" +static DEFINE_SPINLOCK(mck_lock); + static const struct clk_master_characteristics mck_characteristics = { .output = { .min = 0, .max = 133333333 }, .divisors = { 1, 2, 4, 3 }, @@ -45,9 +42,14 @@ static const struct clk_pll_characteristics plla_characteristics = { static const struct { char *n; char *p; + unsigned long flags; u8 id; } at91sam9x5_systemck[] = { - { .n = "ddrck", .p = "masterck", .id = 2 }, + /* + * ddrck feeds DDR controller and is enabled by bootloader thus we need + * to keep it enabled in case there is no Linux consumer for it. + */ + { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL }, { .n = "smdck", .p = "smdclk", .id = 4 }, { .n = "uhpck", .p = "usbck", .id = 6 }, { .n = "udpck", .p = "usbck", .id = 7 }, @@ -137,7 +139,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, struct pmc_data *at91sam9x5_pmc; const char *parent_names[6]; struct regmap *regmap; - struct clk *hw; + struct clk_hw *hw; int i; bool bypass; @@ -156,8 +158,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, if (IS_ERR(regmap)) return; - at91sam9x5_pmc = pmc_data_allocate(PMC_MAIN + 1, - nck(at91sam9x5_systemck), 31, 0); + at91sam9x5_pmc = pmc_data_allocate(PMC_PLLACK + 1, + nck(at91sam9x5_systemck), 31, 0, 2); if (!at91sam9x5_pmc) return; @@ -190,6 +192,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, if (IS_ERR(hw)) goto err_free; + at91sam9x5_pmc->chws[PMC_PLLACK] = hw; + hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck"); if (IS_ERR(hw)) goto err_free; @@ -200,9 +204,18 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, parent_names[1] = "mainck"; parent_names[2] = "plladivck"; parent_names[3] = "utmick"; - hw = at91_clk_register_master(regmap, "masterck", 4, parent_names, - &at91sam9x5_master_layout, - &mck_characteristics); + hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4, + parent_names, + &at91sam9x5_master_layout, + &mck_characteristics, &mck_lock); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_master_div(regmap, "masterck_div", + "masterck_pres", + &at91sam9x5_master_layout, + &mck_characteristics, &mck_lock, + CLK_SET_RATE_GATE); if (IS_ERR(hw)) goto err_free; @@ -222,23 +235,27 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, parent_names[1] = "mainck"; parent_names[2] = "plladivck"; parent_names[3] = "utmick"; - parent_names[4] = "masterck"; + parent_names[4] = "masterck_div"; for (i = 0; i < 2; i++) { - char *name; + char name[6]; - name = xasprintf("prog%d", i); + snprintf(name, sizeof(name), "prog%d", i); hw = at91_clk_register_programmable(regmap, name, parent_names, 5, i, - &at91sam9x5_programmable_layout); + &at91sam9x5_programmable_layout, + NULL); if (IS_ERR(hw)) goto err_free; + + at91sam9x5_pmc->pchws[i] = hw; } for (i = 0; i < ARRAY_SIZE(at91sam9x5_systemck); i++) { hw = at91_clk_register_system(regmap, at91sam9x5_systemck[i].n, at91sam9x5_systemck[i].p, - at91sam9x5_systemck[i].id); + at91sam9x5_systemck[i].id, + at91sam9x5_systemck[i].flags); if (IS_ERR(hw)) goto err_free; @@ -246,7 +263,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, } if (has_lcdck) { - hw = at91_clk_register_system(regmap, "lcdck", "masterck", 3); + hw = at91_clk_register_system(regmap, "lcdck", "masterck_div", + 3, 0); if (IS_ERR(hw)) goto err_free; @@ -254,12 +272,12 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, } for (i = 0; i < ARRAY_SIZE(at91sam9x5_periphck); i++) { - hw = at91_clk_register_sam9x5_peripheral(regmap, + hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, &at91sam9x5_pcr_layout, at91sam9x5_periphck[i].n, - "masterck", + "masterck_div", at91sam9x5_periphck[i].id, - &range); + &range, INT_MIN, 0); if (IS_ERR(hw)) goto err_free; @@ -267,57 +285,57 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, } for (i = 0; extra_pcks[i].id; i++) { - hw = at91_clk_register_sam9x5_peripheral(regmap, + hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, &at91sam9x5_pcr_layout, extra_pcks[i].n, - "masterck", + "masterck_div", extra_pcks[i].id, - &range); + &range, INT_MIN, 0); if (IS_ERR(hw)) goto err_free; at91sam9x5_pmc->phws[extra_pcks[i].id] = hw; } - of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9x5_pmc); + of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9x5_pmc); return; err_free: - pmc_data_free(at91sam9x5_pmc); + kfree(at91sam9x5_pmc); } static void __init at91sam9g15_pmc_setup(struct device_node *np) { at91sam9x5_pmc_setup(np, at91sam9g15_periphck, true); } -CLK_OF_DECLARE_DRIVER(at91sam9g15_pmc, "atmel,at91sam9g15-pmc", - at91sam9g15_pmc_setup); + +CLK_OF_DECLARE(at91sam9g15_pmc, "atmel,at91sam9g15-pmc", at91sam9g15_pmc_setup); static void __init at91sam9g25_pmc_setup(struct device_node *np) { at91sam9x5_pmc_setup(np, at91sam9g25_periphck, false); } -CLK_OF_DECLARE_DRIVER(at91sam9g25_pmc, "atmel,at91sam9g25-pmc", - at91sam9g25_pmc_setup); + +CLK_OF_DECLARE(at91sam9g25_pmc, "atmel,at91sam9g25-pmc", at91sam9g25_pmc_setup); static void __init at91sam9g35_pmc_setup(struct device_node *np) { at91sam9x5_pmc_setup(np, at91sam9g35_periphck, true); } -CLK_OF_DECLARE_DRIVER(at91sam9g35_pmc, "atmel,at91sam9g35-pmc", - at91sam9g35_pmc_setup); + +CLK_OF_DECLARE(at91sam9g35_pmc, "atmel,at91sam9g35-pmc", at91sam9g35_pmc_setup); static void __init at91sam9x25_pmc_setup(struct device_node *np) { at91sam9x5_pmc_setup(np, at91sam9x25_periphck, false); } -CLK_OF_DECLARE_DRIVER(at91sam9x25_pmc, "atmel,at91sam9x25-pmc", - at91sam9x25_pmc_setup); + +CLK_OF_DECLARE(at91sam9x25_pmc, "atmel,at91sam9x25-pmc", at91sam9x25_pmc_setup); static void __init at91sam9x35_pmc_setup(struct device_node *np) { at91sam9x5_pmc_setup(np, at91sam9x35_periphck, true); } -CLK_OF_DECLARE_DRIVER(at91sam9x35_pmc, "atmel,at91sam9x35-pmc", - at91sam9x35_pmc_setup); + +CLK_OF_DECLARE(at91sam9x35_pmc, "atmel,at91sam9x35-pmc", at91sam9x35_pmc_setup); diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c index 47bff32fe8..71976567ea 100644 --- a/drivers/clk/at91/clk-audio-pll.c +++ b/drivers/clk/at91/clk-audio-pll.c @@ -30,14 +30,14 @@ * parent - fixed parent. No clk_set_parent support */ -#include <common.h> -#include <clock.h> -#include <of.h> -#include <linux/list.h> #include <linux/clk.h> +#include <linux/printk.h> +#include <linux/clk-provider.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> +#include <linux/slab.h> #include "pmc.h" @@ -57,35 +57,32 @@ #define AUDIO_PLL_FOUT_MAX 700000000UL struct clk_audio_frac { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; u32 fracr; u8 nd; - const char *parent_name; }; struct clk_audio_pad { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; u8 qdaudio; u8 div; - const char *parent_name; }; struct clk_audio_pmc { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; u8 qdpmc; - const char *parent_name; }; -#define to_clk_audio_frac(clk) container_of(clk, struct clk_audio_frac, clk) -#define to_clk_audio_pad(clk) container_of(clk, struct clk_audio_pad, clk) -#define to_clk_audio_pmc(clk) container_of(clk, struct clk_audio_pmc, clk) +#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw) +#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw) +#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw) -static int clk_audio_pll_frac_enable(struct clk *clk) +static int clk_audio_pll_frac_enable(struct clk_hw *hw) { - struct clk_audio_frac *frac = to_clk_audio_frac(clk); + struct clk_audio_frac *frac = to_clk_audio_frac(hw); regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0, AT91_PMC_AUDIO_PLL_RESETN, 0); @@ -108,9 +105,9 @@ static int clk_audio_pll_frac_enable(struct clk *clk) return 0; } -static int clk_audio_pll_pad_enable(struct clk *clk) +static int clk_audio_pll_pad_enable(struct clk_hw *hw) { - struct clk_audio_pad *apad_ck = to_clk_audio_pad(clk); + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1, AT91_PMC_AUDIO_PLL_QDPAD_MASK, @@ -121,9 +118,9 @@ static int clk_audio_pll_pad_enable(struct clk *clk) return 0; } -static int clk_audio_pll_pmc_enable(struct clk *clk) +static int clk_audio_pll_pmc_enable(struct clk_hw *hw) { - struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(clk); + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0, AT91_PMC_AUDIO_PLL_PMCEN | @@ -133,9 +130,9 @@ static int clk_audio_pll_pmc_enable(struct clk *clk) return 0; } -static void clk_audio_pll_frac_disable(struct clk *clk) +static void clk_audio_pll_frac_disable(struct clk_hw *hw) { - struct clk_audio_frac *frac = to_clk_audio_frac(clk); + struct clk_audio_frac *frac = to_clk_audio_frac(hw); regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0, AT91_PMC_AUDIO_PLL_PLLEN, 0); @@ -144,17 +141,17 @@ static void clk_audio_pll_frac_disable(struct clk *clk) AT91_PMC_AUDIO_PLL_RESETN, 0); } -static void clk_audio_pll_pad_disable(struct clk *clk) +static void clk_audio_pll_pad_disable(struct clk_hw *hw) { - struct clk_audio_pad *apad_ck = to_clk_audio_pad(clk); + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0, AT91_PMC_AUDIO_PLL_PADEN, 0); } -static void clk_audio_pll_pmc_disable(struct clk *clk) +static void clk_audio_pll_pmc_disable(struct clk_hw *hw) { - struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(clk); + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0, AT91_PMC_AUDIO_PLL_PMCEN, 0); @@ -174,10 +171,10 @@ static unsigned long clk_audio_pll_fout(unsigned long parent_rate, return parent_rate * (nd + 1) + fr; } -static unsigned long clk_audio_pll_frac_recalc_rate(struct clk *clk, +static unsigned long clk_audio_pll_frac_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_audio_frac *frac = to_clk_audio_frac(clk); + struct clk_audio_frac *frac = to_clk_audio_frac(hw); unsigned long fout; fout = clk_audio_pll_fout(parent_rate, frac->nd, frac->fracr); @@ -188,10 +185,10 @@ static unsigned long clk_audio_pll_frac_recalc_rate(struct clk *clk, return fout; } -static unsigned long clk_audio_pll_pad_recalc_rate(struct clk *clk, +static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_audio_pad *apad_ck = to_clk_audio_pad(clk); + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); unsigned long apad_rate = 0; if (apad_ck->qdaudio && apad_ck->div) @@ -203,10 +200,10 @@ static unsigned long clk_audio_pll_pad_recalc_rate(struct clk *clk, return apad_rate; } -static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk *clk, +static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(clk); + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); unsigned long apmc_rate = 0; apmc_rate = parent_rate / (apmc_ck->qdpmc + 1); @@ -245,10 +242,10 @@ static int clk_audio_pll_frac_compute_frac(unsigned long rate, return 0; } -static long clk_audio_pll_pad_round_rate(struct clk *clk, unsigned long rate, +static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { - struct clk *pclk = clk_get_parent(clk); + struct clk_hw *pclk = clk_hw_get_parent(hw); long best_rate = -EINVAL; unsigned long best_parent_rate; unsigned long tmp_qd; @@ -278,7 +275,7 @@ static long clk_audio_pll_pad_round_rate(struct clk *clk, unsigned long rate, if (div == 2 && tmp_qd % 3 == 0) continue; - best_parent_rate = clk_round_rate(pclk, + best_parent_rate = clk_hw_round_rate(pclk, rate * tmp_qd * div); tmp_rate = best_parent_rate / (div * tmp_qd); tmp_diff = abs(rate - tmp_rate); @@ -296,10 +293,10 @@ static long clk_audio_pll_pad_round_rate(struct clk *clk, unsigned long rate, return best_rate; } -static long clk_audio_pll_pmc_round_rate(struct clk *clk, unsigned long rate, +static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { - struct clk *pclk = clk_get_parent(clk); + struct clk_hw *pclk = clk_hw_get_parent(hw); long best_rate = -EINVAL; unsigned long best_parent_rate = 0; u32 tmp_qd = 0, div; @@ -313,10 +310,10 @@ static long clk_audio_pll_pmc_round_rate(struct clk *clk, unsigned long rate, if (!rate) return 0; - best_parent_rate = clk_round_rate(pclk, 1); + best_parent_rate = clk_round_rate(&pclk->clk, 1); div = max(best_parent_rate / rate, 1UL); for (; div <= AUDIO_PLL_QDPMC_MAX; div++) { - best_parent_rate = clk_round_rate(pclk, rate * div); + best_parent_rate = clk_round_rate(&pclk->clk, rate * div); tmp_rate = best_parent_rate / div; tmp_diff = abs(rate - tmp_rate); @@ -336,10 +333,10 @@ static long clk_audio_pll_pmc_round_rate(struct clk *clk, unsigned long rate, return best_rate; } -static int clk_audio_pll_frac_set_rate(struct clk *clk, unsigned long rate, +static int clk_audio_pll_frac_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct clk_audio_frac *frac = to_clk_audio_frac(clk); + struct clk_audio_frac *frac = to_clk_audio_frac(hw); unsigned long fracr, nd; int ret; @@ -359,10 +356,10 @@ static int clk_audio_pll_frac_set_rate(struct clk *clk, unsigned long rate, return 0; } -static int clk_audio_pll_pad_set_rate(struct clk *clk, unsigned long rate, +static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct clk_audio_pad *apad_ck = to_clk_audio_pad(clk); + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); u8 tmp_div; pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__, @@ -383,10 +380,10 @@ static int clk_audio_pll_pad_set_rate(struct clk *clk, unsigned long rate, return 0; } -static int clk_audio_pll_pmc_set_rate(struct clk *clk, unsigned long rate, +static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(clk); + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); if (!rate) return -EINVAL; @@ -422,91 +419,94 @@ static const struct clk_ops audio_pll_pmc_ops = { .set_rate = clk_audio_pll_pmc_set_rate, }; -struct clk * __init +struct clk_hw * __init at91_clk_register_audio_pll_frac(struct regmap *regmap, const char *name, const char *parent_name) { struct clk_audio_frac *frac_ck; + struct clk_init_data init = {}; int ret; frac_ck = kzalloc(sizeof(*frac_ck), GFP_KERNEL); if (!frac_ck) return ERR_PTR(-ENOMEM); - frac_ck->clk.name = name; - frac_ck->clk.ops = &audio_pll_frac_ops; - frac_ck->parent_name = parent_name; - frac_ck->clk.parent_names = &frac_ck->parent_name; - frac_ck->clk.num_parents = 1; - /* frac_ck->clk.flags = CLK_SET_RATE_GATE; */ + init.name = name; + init.ops = &audio_pll_frac_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE; + frac_ck->hw.init = &init; frac_ck->regmap = regmap; - ret = clk_register(&frac_ck->clk); + ret = clk_hw_register(NULL, &frac_ck->hw); if (ret) { kfree(frac_ck); return ERR_PTR(ret); } - return &frac_ck->clk; + return &frac_ck->hw; } -struct clk * __init +struct clk_hw * __init at91_clk_register_audio_pll_pad(struct regmap *regmap, const char *name, const char *parent_name) { struct clk_audio_pad *apad_ck; + struct clk_init_data init; int ret; apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL); if (!apad_ck) return ERR_PTR(-ENOMEM); - apad_ck->clk.name = name; - apad_ck->clk.ops = &audio_pll_pad_ops; - apad_ck->parent_name = parent_name; - apad_ck->clk.parent_names = &apad_ck->parent_name; - apad_ck->clk.num_parents = 1; - /* apad_ck->clk.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | - CLK_SET_RATE_PARENT; */ + init.name = name; + init.ops = &audio_pll_pad_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; + apad_ck->hw.init = &init; apad_ck->regmap = regmap; - ret = clk_register(&apad_ck->clk); + ret = clk_hw_register(NULL, &apad_ck->hw); if (ret) { kfree(apad_ck); return ERR_PTR(ret); } - return &apad_ck->clk; + return &apad_ck->hw; } -struct clk * __init +struct clk_hw * __init at91_clk_register_audio_pll_pmc(struct regmap *regmap, const char *name, const char *parent_name) { struct clk_audio_pmc *apmc_ck; + struct clk_init_data init; int ret; apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL); if (!apmc_ck) return ERR_PTR(-ENOMEM); - apmc_ck->clk.name = name; - apmc_ck->clk.ops = &audio_pll_pmc_ops; - apmc_ck->parent_name = parent_name; - apmc_ck->clk.parent_names = &apmc_ck->parent_name; - apmc_ck->clk.num_parents = 1; - /* apmc_ck.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | - CLK_SET_RATE_PARENT; */ + init.name = name; + init.ops = &audio_pll_pmc_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; + apmc_ck->hw.init = &init; apmc_ck->regmap = regmap; - ret = clk_register(&apmc_ck->clk); + ret = clk_hw_register(NULL, &apmc_ck->hw); if (ret) { kfree(apmc_ck); return ERR_PTR(ret); } - return &apmc_ck->clk; + return &apmc_ck->hw; } diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c index 56b800facb..e59cff2bdf 100644 --- a/drivers/clk/at91/clk-generated.c +++ b/drivers/clk/at91/clk-generated.c @@ -6,80 +6,99 @@ * Based on clk-programmable & clk-peripheral drivers by Boris BREZILLON. */ -#include <common.h> -#include <clock.h> -#include <io.h> -#include <linux/list.h> -#include <linux/clk.h> +#include <linux/bitfield.h> +#include <linux/printk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> -#include <linux/bitfield.h> +#include <linux/regmap.h> #include "pmc.h" #define GENERATED_MAX_DIV 255 -#define GCK_INDEX_DT_AUDIO_PLL 5 - struct clk_generated { - struct clk hw; + struct clk_hw hw; struct regmap *regmap; struct clk_range range; + spinlock_t *lock; + u32 *mux_table; u32 id; u32 gckdiv; const struct clk_pcr_layout *layout; + struct at91_clk_pms pms; u8 parent_id; - bool audio_pll_allowed; + int chg_pid; }; #define to_clk_generated(hw) \ container_of(hw, struct clk_generated, hw) -static int clk_generated_enable(struct clk *hw) +static int clk_generated_set(struct clk_generated *gck, int status) { - struct clk_generated *gck = to_clk_generated(hw); - - pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n", - __func__, gck->gckdiv, gck->parent_id); + unsigned long flags; + unsigned int enable = status ? AT91_PMC_PCR_GCKEN : 0; + spin_lock_irqsave(gck->lock, flags); regmap_write(gck->regmap, gck->layout->offset, (gck->id & gck->layout->pid_mask)); regmap_update_bits(gck->regmap, gck->layout->offset, AT91_PMC_PCR_GCKDIV_MASK | gck->layout->gckcss_mask | - gck->layout->cmd | AT91_PMC_PCR_GCKEN, + gck->layout->cmd | enable, field_prep(gck->layout->gckcss_mask, gck->parent_id) | gck->layout->cmd | FIELD_PREP(AT91_PMC_PCR_GCKDIV_MASK, gck->gckdiv) | - AT91_PMC_PCR_GCKEN); + enable); + spin_unlock_irqrestore(gck->lock, flags); + return 0; } -static void clk_generated_disable(struct clk *hw) +static int clk_generated_enable(struct clk_hw *hw) { struct clk_generated *gck = to_clk_generated(hw); + pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n", + __func__, gck->gckdiv, gck->parent_id); + + clk_generated_set(gck, 1); + + return 0; +} + +static void clk_generated_disable(struct clk_hw *hw) +{ + struct clk_generated *gck = to_clk_generated(hw); + unsigned long flags; + + spin_lock_irqsave(gck->lock, flags); regmap_write(gck->regmap, gck->layout->offset, (gck->id & gck->layout->pid_mask)); regmap_update_bits(gck->regmap, gck->layout->offset, gck->layout->cmd | AT91_PMC_PCR_GCKEN, gck->layout->cmd); + spin_unlock_irqrestore(gck->lock, flags); } -static int clk_generated_is_enabled(struct clk *hw) +static int clk_generated_is_enabled(struct clk_hw *hw) { struct clk_generated *gck = to_clk_generated(hw); + unsigned long flags; unsigned int status; + spin_lock_irqsave(gck->lock, flags); regmap_write(gck->regmap, gck->layout->offset, (gck->id & gck->layout->pid_mask)); regmap_read(gck->regmap, gck->layout->offset, &status); + spin_unlock_irqrestore(gck->lock, flags); - return status & AT91_PMC_PCR_GCKEN ? 1 : 0; + return !!(status & AT91_PMC_PCR_GCKEN); } static unsigned long -clk_generated_recalc_rate(struct clk *hw, +clk_generated_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_generated *gck = to_clk_generated(hw); @@ -88,18 +107,22 @@ clk_generated_recalc_rate(struct clk *hw, } /* No modification of hardware as we have the flag CLK_SET_PARENT_GATE set */ -static int clk_generated_set_parent(struct clk *hw, u8 index) +static int clk_generated_set_parent(struct clk_hw *hw, u8 index) { struct clk_generated *gck = to_clk_generated(hw); - if (index >= clk_get_num_parents(hw)) + if (index >= clk_hw_get_num_parents(hw)) return -EINVAL; - gck->parent_id = index; + if (gck->mux_table) + gck->parent_id = clk_mux_index_to_val(gck->mux_table, 0, index); + else + gck->parent_id = index; + return 0; } -static int clk_generated_get_parent(struct clk *hw) +static int clk_generated_get_parent(struct clk_hw *hw) { struct clk_generated *gck = to_clk_generated(hw); @@ -107,7 +130,7 @@ static int clk_generated_get_parent(struct clk *hw) } /* No modification of hardware as we have the flag CLK_SET_RATE_GATE set */ -static int clk_generated_set_rate(struct clk *hw, +static int clk_generated_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -150,53 +173,58 @@ static const struct clk_ops generated_ops = { static void clk_generated_startup(struct clk_generated *gck) { u32 tmp; + unsigned long flags; + spin_lock_irqsave(gck->lock, flags); regmap_write(gck->regmap, gck->layout->offset, (gck->id & gck->layout->pid_mask)); regmap_read(gck->regmap, gck->layout->offset, &tmp); + spin_unlock_irqrestore(gck->lock, flags); gck->parent_id = field_get(gck->layout->gckcss_mask, tmp); gck->gckdiv = FIELD_GET(AT91_PMC_PCR_GCKDIV_MASK, tmp); } -struct clk * __init -at91_clk_register_generated(struct regmap *regmap, +struct clk_hw * __init +at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, const struct clk_pcr_layout *layout, const char *name, const char **parent_names, - u8 num_parents, u8 id, bool pll_audio, - const struct clk_range *range) + u32 *mux_table, u8 num_parents, u8 id, + const struct clk_range *range, + int chg_pid) { - size_t parents_array_size; struct clk_generated *gck; - struct clk *hw; + struct clk_init_data init; + struct clk_hw *hw; int ret; gck = kzalloc(sizeof(*gck), GFP_KERNEL); if (!gck) return ERR_PTR(-ENOMEM); - gck->id = id; - gck->hw.name = name; - gck->hw.ops = &generated_ops; + init.name = name; + init.ops = &generated_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + if (chg_pid >= 0) + init.flags |= CLK_SET_RATE_PARENT; - parents_array_size = num_parents * sizeof(gck->hw.parent_names[0]); - gck->hw.parent_names = xmemdup(parent_names, parents_array_size); - gck->hw.num_parents = num_parents; - - /* gck->hw.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | CLK_SET_PARENT; */ + gck->id = id; + gck->hw.init = &init; gck->regmap = regmap; + gck->lock = lock; gck->range = *range; - gck->audio_pll_allowed = pll_audio; + gck->chg_pid = chg_pid; gck->layout = layout; + gck->mux_table = mux_table; clk_generated_startup(gck); hw = &gck->hw; - ret = clk_register(&gck->hw); + ret = clk_hw_register(NULL, &gck->hw); if (ret) { kfree(gck); hw = ERR_PTR(ret); - } else { - pmc_register_id(id); } return hw; diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c index 6052886cca..e5b98692a9 100644 --- a/drivers/clk/at91/clk-h32mx.c +++ b/drivers/clk/at91/clk-h32mx.c @@ -7,27 +7,26 @@ * Alexandre Belloni <alexandre.belloni@free-electrons.com> */ -#include <common.h> -#include <clock.h> -#include <linux/list.h> -#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> -#include <regmap.h> - +#include <of.h> +#include <linux/regmap.h> +#include <mfd/syscon.h> +#include <linux/printk.h> #include "pmc.h" #define H32MX_MAX_FREQ 90000000 struct clk_sama5d4_h32mx { - struct clk hw; + struct clk_hw hw; struct regmap *regmap; - const char *parent; }; #define to_clk_sama5d4_h32mx(hw) container_of(hw, struct clk_sama5d4_h32mx, hw) -static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk *hw, +static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw); @@ -42,7 +41,7 @@ static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk *hw, return parent_rate; } -static long clk_sama5d4_h32mx_round_rate(struct clk *hw, unsigned long rate, +static long clk_sama5d4_h32mx_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { unsigned long div; @@ -59,7 +58,7 @@ static long clk_sama5d4_h32mx_round_rate(struct clk *hw, unsigned long rate, return *parent_rate; } -static int clk_sama5d4_h32mx_set_rate(struct clk *hw, unsigned long rate, +static int clk_sama5d4_h32mx_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw); @@ -83,27 +82,28 @@ static const struct clk_ops h32mx_ops = { .set_rate = clk_sama5d4_h32mx_set_rate, }; -struct clk * __init +struct clk_hw * __init at91_clk_register_h32mx(struct regmap *regmap, const char *name, const char *parent_name) { struct clk_sama5d4_h32mx *h32mxclk; + struct clk_init_data init; int ret; h32mxclk = kzalloc(sizeof(*h32mxclk), GFP_KERNEL); if (!h32mxclk) return ERR_PTR(-ENOMEM); - h32mxclk->parent = parent_name; - h32mxclk->hw.name = name; - h32mxclk->hw.ops = &h32mx_ops; - h32mxclk->hw.parent_names = &h32mxclk->parent; - h32mxclk->hw.num_parents = 1; - /* h32mxclk.hw.flags = CLK_SET_RATE_GATE; */ + init.name = name; + init.ops = &h32mx_ops; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.flags = CLK_SET_RATE_GATE; + h32mxclk->hw.init = &init; h32mxclk->regmap = regmap; - ret = clk_register(&h32mxclk->hw); + ret = clk_hw_register(NULL, &h32mxclk->hw); if (ret) { kfree(h32mxclk); return ERR_PTR(ret); diff --git a/drivers/clk/at91/clk-i2s-mux.c b/drivers/clk/at91/clk-i2s-mux.c index 1418ec8662..71ef2e6386 100644 --- a/drivers/clk/at91/clk-i2s-mux.c +++ b/drivers/clk/at91/clk-i2s-mux.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2018 Microchip Technology Inc, * Codrin Ciubotariu <codrin.ciubotariu@microchip.com> @@ -6,33 +6,27 @@ * */ -#include <common.h> -#include <clock.h> +#include <linux/clk-provider.h> #include <of.h> -#include <linux/list.h> -#include <linux/clk.h> -#include <linux/clk/at91_pmc.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> +#include <linux/slab.h> #include <soc/at91/atmel-sfr.h> #include "pmc.h" -#define I2S_MUX_SOURCE_MAX 2 - struct clk_i2s_mux { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; u8 bus_id; - const char *parent_names[I2S_MUX_SOURCE_MAX]; }; -#define to_clk_i2s_mux(clk) container_of(clk, struct clk_i2s_mux, clk) +#define to_clk_i2s_mux(hw) container_of(hw, struct clk_i2s_mux, hw) -static int clk_i2s_mux_get_parent(struct clk *clk) +static int clk_i2s_mux_get_parent(struct clk_hw *hw) { - struct clk_i2s_mux *mux = to_clk_i2s_mux(clk); + struct clk_i2s_mux *mux = to_clk_i2s_mux(hw); u32 val; regmap_read(mux->regmap, AT91_SFR_I2SCLKSEL, &val); @@ -40,26 +34,25 @@ static int clk_i2s_mux_get_parent(struct clk *clk) return (val & BIT(mux->bus_id)) >> mux->bus_id; } -static int clk_i2s_mux_set_parent(struct clk *clk, u8 index) +static int clk_i2s_mux_set_parent(struct clk_hw *hw, u8 index) { - struct clk_i2s_mux *mux = to_clk_i2s_mux(clk); + struct clk_i2s_mux *mux = to_clk_i2s_mux(hw); return regmap_update_bits(mux->regmap, AT91_SFR_I2SCLKSEL, BIT(mux->bus_id), index << mux->bus_id); } static const struct clk_ops clk_i2s_mux_ops = { - .set_rate = clk_parent_set_rate, - .round_rate = clk_parent_round_rate, .get_parent = clk_i2s_mux_get_parent, .set_parent = clk_i2s_mux_set_parent, }; -struct clk * __init +struct clk_hw * __init at91_clk_i2s_mux_register(struct regmap *regmap, const char *name, const char * const *parent_names, unsigned int num_parents, u8 bus_id) { + struct clk_init_data init = {}; struct clk_i2s_mux *i2s_ck; int ret; @@ -67,21 +60,20 @@ at91_clk_i2s_mux_register(struct regmap *regmap, const char *name, if (!i2s_ck) return ERR_PTR(-ENOMEM); - i2s_ck->clk.name = name; - i2s_ck->clk.ops = &clk_i2s_mux_ops; - memcpy(i2s_ck->parent_names, parent_names, - num_parents * sizeof(i2s_ck->parent_names[0])); - i2s_ck->clk.parent_names = &i2s_ck->parent_names[0]; - i2s_ck->clk.num_parents = num_parents; + init.name = name; + init.ops = &clk_i2s_mux_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + i2s_ck->hw.init = &init; i2s_ck->bus_id = bus_id; i2s_ck->regmap = regmap; - ret = clk_register(&i2s_ck->clk); + ret = clk_hw_register(NULL, &i2s_ck->hw); if (ret) { kfree(i2s_ck); return ERR_PTR(ret); } - return &i2s_ck->clk; + return &i2s_ck->hw; } diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c index 08abb1673b..a1dd327b56 100644 --- a/drivers/clk/at91/clk-main.c +++ b/drivers/clk/at91/clk-main.c @@ -2,21 +2,22 @@ /* * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> */ -#include <common.h> -#include <clock.h> -#include <linux/list.h> -#include <linux/clk.h> + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <clock.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> +#include <linux/printk.h> #include "pmc.h" #define SLOW_CLOCK_FREQ 32768 #define MAINF_DIV 16 -#define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * SECOND) / \ +#define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * USEC_PER_SEC) / \ SLOW_CLOCK_FREQ) -#define MAINF_LOOP_MIN_WAIT (SECOND / SLOW_CLOCK_FREQ) +#define MAINF_LOOP_MIN_WAIT (USEC_PER_SEC / SLOW_CLOCK_FREQ) #define MAINF_LOOP_MAX_WAIT MAINFRDY_TIMEOUT #define MOR_KEY_MASK (0xff << 16) @@ -26,36 +27,38 @@ AT91_PMC_OSCBYPASS)) ? 1 : 0) struct clk_main_osc { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; - const char *parent; + struct at91_clk_pms pms; }; -#define to_clk_main_osc(clk) container_of(clk, struct clk_main_osc, clk) +#define to_clk_main_osc(hw) container_of(hw, struct clk_main_osc, hw) struct clk_main_rc_osc { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; unsigned long frequency; + unsigned long accuracy; + struct at91_clk_pms pms; }; -#define to_clk_main_rc_osc(clk) container_of(clk, struct clk_main_rc_osc, clk) +#define to_clk_main_rc_osc(hw) container_of(hw, struct clk_main_rc_osc, hw) struct clk_rm9200_main { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; - const char *parent; }; -#define to_clk_rm9200_main(clk) container_of(clk, struct clk_rm9200_main, clk) +#define to_clk_rm9200_main(hw) container_of(hw, struct clk_rm9200_main, hw) struct clk_sam9x5_main { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; + struct at91_clk_pms pms; u8 parent; }; -#define to_clk_sam9x5_main(clk) container_of(clk, struct clk_sam9x5_main, clk) +#define to_clk_sam9x5_main(hw) container_of(hw, struct clk_sam9x5_main, hw) static inline bool clk_main_osc_ready(struct regmap *regmap) { @@ -66,9 +69,9 @@ static inline bool clk_main_osc_ready(struct regmap *regmap) return status & AT91_PMC_MOSCS; } -static int clk_main_osc_enable(struct clk *clk) +static int clk_main_osc_prepare(struct clk_hw *hw) { - struct clk_main_osc *osc = to_clk_main_osc(clk); + struct clk_main_osc *osc = to_clk_main_osc(hw); struct regmap *regmap = osc->regmap; u32 tmp; @@ -84,14 +87,14 @@ static int clk_main_osc_enable(struct clk *clk) } while (!clk_main_osc_ready(regmap)) - barrier(); + cpu_relax(); return 0; } -static void clk_main_osc_disable(struct clk *clk) +static void clk_main_osc_unprepare(struct clk_hw *hw) { - struct clk_main_osc *osc = to_clk_main_osc(clk); + struct clk_main_osc *osc = to_clk_main_osc(hw); struct regmap *regmap = osc->regmap; u32 tmp; @@ -106,9 +109,9 @@ static void clk_main_osc_disable(struct clk *clk) regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_KEY); } -static int clk_main_osc_is_enabled(struct clk *clk) +static int clk_main_osc_is_prepared(struct clk_hw *hw) { - struct clk_main_osc *osc = to_clk_main_osc(clk); + struct clk_main_osc *osc = to_clk_main_osc(hw); struct regmap *regmap = osc->regmap; u32 tmp, status; @@ -122,45 +125,52 @@ static int clk_main_osc_is_enabled(struct clk *clk) } static const struct clk_ops main_osc_ops = { - .enable = clk_main_osc_enable, - .disable = clk_main_osc_disable, - .is_enabled = clk_main_osc_is_enabled, + .enable = clk_main_osc_prepare, + .disable = clk_main_osc_unprepare, + .is_enabled = clk_main_osc_is_prepared, }; -struct clk * __init +struct clk_hw * __init at91_clk_register_main_osc(struct regmap *regmap, const char *name, const char *parent_name, bool bypass) { struct clk_main_osc *osc; + struct clk_init_data init; + struct clk_hw *hw; int ret; if (!name || !parent_name) return ERR_PTR(-EINVAL); - osc = xzalloc(sizeof(*osc)); + osc = kzalloc(sizeof(*osc), GFP_KERNEL); + if (!osc) + return ERR_PTR(-ENOMEM); - osc->parent = parent_name; - osc->clk.name = name; - osc->clk.ops = &main_osc_ops; - osc->clk.parent_names = &osc->parent; - osc->clk.num_parents = 1; + init.name = name; + init.ops = &main_osc_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_IGNORE_UNUSED; + + osc->hw.init = &init; osc->regmap = regmap; if (bypass) - regmap_write_bits(regmap, - AT91_CKGR_MOR, MOR_KEY_MASK | - AT91_PMC_MOSCEN, - AT91_PMC_OSCBYPASS | AT91_PMC_KEY); + regmap_update_bits(regmap, + AT91_CKGR_MOR, MOR_KEY_MASK | + AT91_PMC_OSCBYPASS, + AT91_PMC_OSCBYPASS | AT91_PMC_KEY); - ret = clk_register(&osc->clk); + hw = &osc->hw; + ret = clk_hw_register(NULL, &osc->hw); if (ret) { - free(osc); - return ERR_PTR(ret); + kfree(osc); + hw = ERR_PTR(ret); } - return &osc->clk; + return hw; } static bool clk_main_rc_osc_ready(struct regmap *regmap) @@ -169,31 +179,31 @@ static bool clk_main_rc_osc_ready(struct regmap *regmap) regmap_read(regmap, AT91_PMC_SR, &status); - return status & AT91_PMC_MOSCRCS; + return !!(status & AT91_PMC_MOSCRCS); } -static int clk_main_rc_osc_enable(struct clk *clk) +static int clk_main_rc_osc_prepare(struct clk_hw *hw) { - struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk); + struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); struct regmap *regmap = osc->regmap; unsigned int mor; regmap_read(regmap, AT91_CKGR_MOR, &mor); if (!(mor & AT91_PMC_MOSCRCEN)) - regmap_write_bits(regmap, AT91_CKGR_MOR, - MOR_KEY_MASK | AT91_PMC_MOSCRCEN, - AT91_PMC_MOSCRCEN | AT91_PMC_KEY); + regmap_update_bits(regmap, AT91_CKGR_MOR, + MOR_KEY_MASK | AT91_PMC_MOSCRCEN, + AT91_PMC_MOSCRCEN | AT91_PMC_KEY); while (!clk_main_rc_osc_ready(regmap)) - barrier(); + cpu_relax(); return 0; } -static void clk_main_rc_osc_disable(struct clk *clk) +static void clk_main_rc_osc_unprepare(struct clk_hw *hw) { - struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk); + struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); struct regmap *regmap = osc->regmap; unsigned int mor; @@ -202,13 +212,13 @@ static void clk_main_rc_osc_disable(struct clk *clk) if (!(mor & AT91_PMC_MOSCRCEN)) return; - regmap_write_bits(regmap, AT91_CKGR_MOR, - MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY); + regmap_update_bits(regmap, AT91_CKGR_MOR, + MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY); } -static int clk_main_rc_osc_is_enabled(struct clk *clk) +static int clk_main_rc_osc_is_prepared(struct clk_hw *hw) { - struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk); + struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); struct regmap *regmap = osc->regmap; unsigned int mor, status; @@ -218,61 +228,71 @@ static int clk_main_rc_osc_is_enabled(struct clk *clk) return (mor & AT91_PMC_MOSCRCEN) && (status & AT91_PMC_MOSCRCS); } -static unsigned long clk_main_rc_osc_recalc_rate(struct clk *clk, +static unsigned long clk_main_rc_osc_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk); + struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); return osc->frequency; } static const struct clk_ops main_rc_osc_ops = { - .enable = clk_main_rc_osc_enable, - .disable = clk_main_rc_osc_disable, - .is_enabled = clk_main_rc_osc_is_enabled, + .enable = clk_main_rc_osc_prepare, + .disable = clk_main_rc_osc_unprepare, + .is_enabled = clk_main_rc_osc_is_prepared, .recalc_rate = clk_main_rc_osc_recalc_rate, }; -struct clk * __init +struct clk_hw * __init at91_clk_register_main_rc_osc(struct regmap *regmap, const char *name, u32 frequency, u32 accuracy) { - int ret; struct clk_main_rc_osc *osc; + struct clk_init_data init; + struct clk_hw *hw; + int ret; if (!name || !frequency) return ERR_PTR(-EINVAL); - osc = xzalloc(sizeof(*osc)); + osc = kzalloc(sizeof(*osc), GFP_KERNEL); + if (!osc) + return ERR_PTR(-ENOMEM); - osc->clk.name = name; - osc->clk.ops = &main_rc_osc_ops; - osc->clk.parent_names = NULL; - osc->clk.num_parents = 0; + init.name = name; + init.ops = &main_rc_osc_ops; + init.parent_names = NULL; + init.num_parents = 0; + init.flags = CLK_IGNORE_UNUSED; + osc->hw.init = &init; osc->regmap = regmap; osc->frequency = frequency; + osc->accuracy = accuracy; - ret = clk_register(&osc->clk); + hw = &osc->hw; + ret = clk_hw_register(NULL, hw); if (ret) { kfree(osc); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &osc->clk; + return hw; } static int clk_main_probe_frequency(struct regmap *regmap) { + u64 start_time; unsigned int mcfr; - uint64_t start = get_time_ns(); + start_time = get_time_ns(); do { regmap_read(regmap, AT91_CKGR_MCFR, &mcfr); if (mcfr & AT91_PMC_MAINRDY) return 0; - } while (!is_timeout(start, MAINFRDY_TIMEOUT * USECOND)); + udelay(MAINF_LOOP_MIN_WAIT); + } while (!is_timeout(start_time, MAINFRDY_TIMEOUT * NSEC_PER_USEC)); return -ETIMEDOUT; } @@ -293,44 +313,46 @@ static unsigned long clk_main_recalc_rate(struct regmap *regmap, return ((mcfr & AT91_PMC_MAINF) * SLOW_CLOCK_FREQ) / MAINF_DIV; } -static int clk_rm9200_main_enable(struct clk *clk) +static int clk_rm9200_main_prepare(struct clk_hw *hw) { - struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk); + struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw); return clk_main_probe_frequency(clkmain->regmap); } -static int clk_rm9200_main_is_enabled(struct clk *clk) +static int clk_rm9200_main_is_prepared(struct clk_hw *hw) { - struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk); + struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw); unsigned int status; regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status); - return status & AT91_PMC_MAINRDY ? 1 : 0; + return !!(status & AT91_PMC_MAINRDY); } -static unsigned long clk_rm9200_main_recalc_rate(struct clk *clk, +static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk); + struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw); return clk_main_recalc_rate(clkmain->regmap, parent_rate); } static const struct clk_ops rm9200_main_ops = { - .enable = clk_rm9200_main_enable, - .is_enabled = clk_rm9200_main_is_enabled, + .enable = clk_rm9200_main_prepare, + .is_enabled = clk_rm9200_main_is_prepared, .recalc_rate = clk_rm9200_main_recalc_rate, }; -struct clk * __init +struct clk_hw * __init at91_clk_register_rm9200_main(struct regmap *regmap, const char *name, const char *parent_name) { - int ret; struct clk_rm9200_main *clkmain; + struct clk_init_data init; + struct clk_hw *hw; + int ret; if (!name) return ERR_PTR(-EINVAL); @@ -338,22 +360,27 @@ at91_clk_register_rm9200_main(struct regmap *regmap, if (!parent_name) return ERR_PTR(-EINVAL); - clkmain = xzalloc(sizeof(*clkmain)); + clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL); + if (!clkmain) + return ERR_PTR(-ENOMEM); - clkmain->parent = parent_name; - clkmain->clk.name = name; - clkmain->clk.ops = &rm9200_main_ops; - clkmain->clk.parent_names = &clkmain->parent; - clkmain->clk.num_parents = 1; + init.name = name; + init.ops = &rm9200_main_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = 0; + + clkmain->hw.init = &init; clkmain->regmap = regmap; - ret = clk_register(&clkmain->clk); + hw = &clkmain->hw; + ret = clk_hw_register(NULL, &clkmain->hw); if (ret) { kfree(clkmain); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &clkmain->clk; + return hw; } static inline bool clk_sam9x5_main_ready(struct regmap *regmap) @@ -362,38 +389,38 @@ static inline bool clk_sam9x5_main_ready(struct regmap *regmap) regmap_read(regmap, AT91_PMC_SR, &status); - return status & AT91_PMC_MOSCSELS ? 1 : 0; + return !!(status & AT91_PMC_MOSCSELS); } -static int clk_sam9x5_main_enable(struct clk *clk) +static int clk_sam9x5_main_prepare(struct clk_hw *hw) { - struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); struct regmap *regmap = clkmain->regmap; while (!clk_sam9x5_main_ready(regmap)) - barrier(); + cpu_relax(); return clk_main_probe_frequency(regmap); } -static int clk_sam9x5_main_is_enabled(struct clk *clk) +static int clk_sam9x5_main_is_prepared(struct clk_hw *hw) { - struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); return clk_sam9x5_main_ready(clkmain->regmap); } -static unsigned long clk_sam9x5_main_recalc_rate(struct clk *clk, +static unsigned long clk_sam9x5_main_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); return clk_main_recalc_rate(clkmain->regmap, parent_rate); } -static int clk_sam9x5_main_set_parent(struct clk *clk, u8 index) +static int clk_sam9x5_main_set_parent(struct clk_hw *hw, u8 index) { - struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); struct regmap *regmap = clkmain->regmap; unsigned int tmp; @@ -401,22 +428,27 @@ static int clk_sam9x5_main_set_parent(struct clk *clk, u8 index) return -EINVAL; regmap_read(regmap, AT91_CKGR_MOR, &tmp); - tmp &= ~MOR_KEY_MASK; if (index && !(tmp & AT91_PMC_MOSCSEL)) - regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL); + tmp = AT91_PMC_MOSCSEL; else if (!index && (tmp & AT91_PMC_MOSCSEL)) - regmap_write(regmap, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL); + tmp = 0; + else + return 0; + + regmap_update_bits(regmap, AT91_CKGR_MOR, + AT91_PMC_MOSCSEL | MOR_KEY_MASK, + tmp | AT91_PMC_KEY); while (!clk_sam9x5_main_ready(regmap)) - barrier(); + cpu_relax(); return 0; } -static int clk_sam9x5_main_get_parent(struct clk *clk) +static int clk_sam9x5_main_get_parent(struct clk_hw *hw) { - struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); unsigned int status; regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status); @@ -425,23 +457,24 @@ static int clk_sam9x5_main_get_parent(struct clk *clk) } static const struct clk_ops sam9x5_main_ops = { - .enable = clk_sam9x5_main_enable, - .is_enabled = clk_sam9x5_main_is_enabled, + .enable = clk_sam9x5_main_prepare, + .is_enabled = clk_sam9x5_main_is_prepared, .recalc_rate = clk_sam9x5_main_recalc_rate, .set_parent = clk_sam9x5_main_set_parent, .get_parent = clk_sam9x5_main_get_parent, }; -struct clk * __init +struct clk_hw * __init at91_clk_register_sam9x5_main(struct regmap *regmap, const char *name, const char **parent_names, int num_parents) { - int ret; - unsigned int status; - size_t parents_array_size; struct clk_sam9x5_main *clkmain; + struct clk_init_data init; + unsigned int status; + struct clk_hw *hw; + int ret; if (!name) return ERR_PTR(-EINVAL); @@ -449,25 +482,27 @@ at91_clk_register_sam9x5_main(struct regmap *regmap, if (!parent_names || !num_parents) return ERR_PTR(-EINVAL); - clkmain = xzalloc(sizeof(*clkmain)); - - clkmain->clk.name = name; - clkmain->clk.ops = &sam9x5_main_ops; - parents_array_size = num_parents * sizeof (clkmain->clk.parent_names[0]); - clkmain->clk.parent_names = xmemdup(parent_names, parents_array_size); - clkmain->clk.num_parents = num_parents; + clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL); + if (!clkmain) + return ERR_PTR(-ENOMEM); - /* init.flags = CLK_SET_PARENT_GATE; */ + init.name = name; + init.ops = &sam9x5_main_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_PARENT_GATE; + clkmain->hw.init = &init; clkmain->regmap = regmap; regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status); clkmain->parent = clk_main_parent_select(status); - ret = clk_register(&clkmain->clk); + hw = &clkmain->hw; + ret = clk_hw_register(NULL, &clkmain->hw); if (ret) { kfree(clkmain); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &clkmain->clk; + return hw; } diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c index 4e3b512aaa..db5e235b6b 100644 --- a/drivers/clk/at91/clk-master.c +++ b/drivers/clk/at91/clk-master.c @@ -2,145 +2,462 @@ /* * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> */ -#include <common.h> -#include <clock.h> -#include <linux/list.h> + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> +#include <linux/printk.h> #include "pmc.h" -#define MASTER_SOURCE_MAX 4 - #define MASTER_PRES_MASK 0x7 #define MASTER_PRES_MAX MASTER_PRES_MASK #define MASTER_DIV_SHIFT 8 -#define MASTER_DIV_MASK 0x3 +#define MASTER_DIV_MASK 0x7 + +#define PMC_MCR_CSS_SHIFT (16) + +#define MASTER_MAX_ID 4 -#define to_clk_master(clk) container_of(clk, struct clk_master, clk) +#define to_clk_master(hw) container_of(hw, struct clk_master, hw) struct clk_master { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; + spinlock_t *lock; const struct clk_master_layout *layout; const struct clk_master_characteristics *characteristics; - const char *parents[MASTER_SOURCE_MAX]; + struct at91_clk_pms pms; + u32 *mux_table; u32 mckr; + int chg_pid; + u8 id; + u8 parent; + u8 div; }; -static inline bool clk_master_ready(struct regmap *regmap) +static inline bool clk_master_ready(struct clk_master *master) { + unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY; unsigned int status; - regmap_read(regmap, AT91_PMC_SR, &status); + regmap_read(master->regmap, AT91_PMC_SR, &status); - return status & AT91_PMC_MCKRDY ? 1 : 0; + return !!(status & bit); } -static int clk_master_enable(struct clk *clk) +static int clk_master_prepare(struct clk_hw *hw) { - struct clk_master *master = to_clk_master(clk); + struct clk_master *master = to_clk_master(hw); + unsigned long flags; + + spin_lock_irqsave(master->lock, flags); + + while (!clk_master_ready(master)) + cpu_relax(); - while (!clk_master_ready(master->regmap)) - barrier(); + spin_unlock_irqrestore(master->lock, flags); return 0; } -static int clk_master_is_enabled(struct clk *clk) +static int clk_master_is_prepared(struct clk_hw *hw) { - struct clk_master *master = to_clk_master(clk); + struct clk_master *master = to_clk_master(hw); + unsigned long flags; + bool status; - return clk_master_ready(master->regmap); + spin_lock_irqsave(master->lock, flags); + status = clk_master_ready(master); + spin_unlock_irqrestore(master->lock, flags); + + return status; } -static unsigned long clk_master_recalc_rate(struct clk *clk, - unsigned long parent_rate) +static unsigned long clk_master_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) { - u8 pres; u8 div; - unsigned long rate = parent_rate; - struct clk_master *master = to_clk_master(clk); + unsigned long flags, rate = parent_rate; + struct clk_master *master = to_clk_master(hw); const struct clk_master_layout *layout = master->layout; const struct clk_master_characteristics *characteristics = master->characteristics; unsigned int mckr; + spin_lock_irqsave(master->lock, flags); regmap_read(master->regmap, master->layout->offset, &mckr); + spin_unlock_irqrestore(master->lock, flags); + mckr &= layout->mask; - pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK; div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; - if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX) - rate /= 3; - else - rate >>= pres; - rate /= characteristics->divisors[div]; if (rate < characteristics->output.min) - pr_warn("master clk is underclocked"); + pr_warn("master clk div is underclocked"); else if (rate > characteristics->output.max) - pr_warn("master clk is overclocked"); + pr_warn("master clk div is overclocked"); return rate; } -static int clk_master_get_parent(struct clk *clk) +static const struct clk_ops master_div_ops = { + .enable = clk_master_prepare, + .is_enabled = clk_master_is_prepared, + .recalc_rate = clk_master_div_recalc_rate, +}; + +static unsigned long clk_master_div_recalc_rate_chg(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_master *master = to_clk_master(hw); + + return DIV_ROUND_CLOSEST_ULL(parent_rate, master->div); +} + +static const struct clk_ops master_div_ops_chg = { + .enable = clk_master_prepare, + .is_enabled = clk_master_is_prepared, + .recalc_rate = clk_master_div_recalc_rate_chg, +}; + +static unsigned long clk_master_pres_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_master *master = to_clk_master(hw); + const struct clk_master_characteristics *characteristics = + master->characteristics; + unsigned long flags; + unsigned int val, pres; + + spin_lock_irqsave(master->lock, flags); + regmap_read(master->regmap, master->layout->offset, &val); + spin_unlock_irqrestore(master->lock, flags); + + val &= master->layout->mask; + pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK; + if (pres == MASTER_PRES_MAX && characteristics->have_div3_pres) + pres = 3; + else + pres = (1 << pres); + + return DIV_ROUND_CLOSEST_ULL(parent_rate, pres); +} + +static int clk_master_pres_get_parent(struct clk_hw *hw) { - struct clk_master *master = to_clk_master(clk); + struct clk_master *master = to_clk_master(hw); + unsigned long flags; unsigned int mckr; + spin_lock_irqsave(master->lock, flags); regmap_read(master->regmap, master->layout->offset, &mckr); + spin_unlock_irqrestore(master->lock, flags); + + mckr &= master->layout->mask; return mckr & AT91_PMC_CSS; } -static const struct clk_ops master_ops = { - .enable = clk_master_enable, - .is_enabled = clk_master_is_enabled, - .recalc_rate = clk_master_recalc_rate, - .get_parent = clk_master_get_parent, +static const struct clk_ops master_pres_ops = { + .enable = clk_master_prepare, + .is_enabled = clk_master_is_prepared, + .recalc_rate = clk_master_pres_recalc_rate, + .get_parent = clk_master_pres_get_parent, }; -struct clk * __init -at91_clk_register_master(struct regmap *regmap, - const char *name, int num_parents, - const char **parent_names, - const struct clk_master_layout *layout, - const struct clk_master_characteristics *characteristics) +static struct clk_hw * __init +at91_clk_register_master_internal(struct regmap *regmap, + const char *name, int num_parents, + const char **parent_names, + const struct clk_master_layout *layout, + const struct clk_master_characteristics *characteristics, + const struct clk_ops *ops, spinlock_t *lock, u32 flags) { - int ret; - const size_t parent_names_size = num_parents * sizeof(parent_names[0]); struct clk_master *master; + struct clk_init_data init; + struct clk_hw *hw; + unsigned int mckr; + unsigned long irqflags; + int ret; - if (!name || !num_parents || !parent_names) + if (!name || !num_parents || !parent_names || !lock) return ERR_PTR(-EINVAL); - master = xzalloc(sizeof(*master)); + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) + return ERR_PTR(-ENOMEM); - master->clk.name = name; - master->clk.ops = &master_ops; - memcpy(master->parents, parent_names, parent_names_size); - master->clk.parent_names = master->parents; - master->clk.num_parents = num_parents; + init.name = name; + init.ops = ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = flags; + master->hw.init = &init; master->layout = layout; master->characteristics = characteristics; master->regmap = regmap; + master->lock = lock; + + if (ops == &master_div_ops_chg) { + spin_lock_irqsave(master->lock, irqflags); + regmap_read(master->regmap, master->layout->offset, &mckr); + spin_unlock_irqrestore(master->lock, irqflags); - ret = clk_register(&master->clk); + mckr &= layout->mask; + mckr = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; + master->div = characteristics->divisors[mckr]; + } + + hw = &master->hw; + ret = clk_hw_register(NULL, &master->hw); if (ret) { kfree(master); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &master->clk; + return hw; +} + +struct clk_hw * __init +at91_clk_register_master_pres(struct regmap *regmap, + const char *name, int num_parents, + const char **parent_names, + const struct clk_master_layout *layout, + const struct clk_master_characteristics *characteristics, + spinlock_t *lock) +{ + return at91_clk_register_master_internal(regmap, name, num_parents, + parent_names, layout, + characteristics, + &master_pres_ops, + lock, CLK_SET_RATE_GATE); +} + +struct clk_hw * __init +at91_clk_register_master_div(struct regmap *regmap, + const char *name, const char *parent_name, + const struct clk_master_layout *layout, + const struct clk_master_characteristics *characteristics, + spinlock_t *lock, u32 flags) +{ + const struct clk_ops *ops; + + if (flags & CLK_SET_RATE_GATE) + ops = &master_div_ops; + else + ops = &master_div_ops_chg; + + return at91_clk_register_master_internal(regmap, name, 1, + &parent_name, layout, + characteristics, ops, + lock, flags); +} + +static unsigned long +clk_sama7g5_master_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_master *master = to_clk_master(hw); + + return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div)); +} + +static int clk_sama7g5_master_get_parent(struct clk_hw *hw) +{ + struct clk_master *master = to_clk_master(hw); + unsigned long flags; + u8 index; + + spin_lock_irqsave(master->lock, flags); + index = clk_mux_val_to_index(&master->hw, master->mux_table, 0, + master->parent); + spin_unlock_irqrestore(master->lock, flags); + + return index; +} + +static int clk_sama7g5_master_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_master *master = to_clk_master(hw); + unsigned long flags; + + if (index >= clk_hw_get_num_parents(hw)) + return -EINVAL; + + spin_lock_irqsave(master->lock, flags); + master->parent = clk_mux_index_to_val(master->mux_table, 0, index); + spin_unlock_irqrestore(master->lock, flags); + + return 0; +} + +static void clk_sama7g5_master_set(struct clk_master *master, + unsigned int status) +{ + unsigned long flags; + unsigned int val, cparent; + unsigned int enable = status ? AT91_PMC_MCR_V2_EN : 0; + unsigned int parent = master->parent << PMC_MCR_CSS_SHIFT; + unsigned int div = master->div << MASTER_DIV_SHIFT; + + spin_lock_irqsave(master->lock, flags); + + regmap_write(master->regmap, AT91_PMC_MCR_V2, + AT91_PMC_MCR_V2_ID(master->id)); + regmap_read(master->regmap, AT91_PMC_MCR_V2, &val); + regmap_update_bits(master->regmap, AT91_PMC_MCR_V2, + enable | AT91_PMC_MCR_V2_CSS | AT91_PMC_MCR_V2_DIV | + AT91_PMC_MCR_V2_CMD | AT91_PMC_MCR_V2_ID_MSK, + enable | parent | div | AT91_PMC_MCR_V2_CMD | + AT91_PMC_MCR_V2_ID(master->id)); + + cparent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT; + + /* Wait here only if parent is being changed. */ + while ((cparent != master->parent) && !clk_master_ready(master)) + cpu_relax(); + + spin_unlock_irqrestore(master->lock, flags); +} + +static int clk_sama7g5_master_enable(struct clk_hw *hw) +{ + struct clk_master *master = to_clk_master(hw); + + clk_sama7g5_master_set(master, 1); + + return 0; +} + +static void clk_sama7g5_master_disable(struct clk_hw *hw) +{ + struct clk_master *master = to_clk_master(hw); + unsigned long flags; + + spin_lock_irqsave(master->lock, flags); + + regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id); + regmap_update_bits(master->regmap, AT91_PMC_MCR_V2, + AT91_PMC_MCR_V2_EN | AT91_PMC_MCR_V2_CMD | + AT91_PMC_MCR_V2_ID_MSK, + AT91_PMC_MCR_V2_CMD | + AT91_PMC_MCR_V2_ID(master->id)); + + spin_unlock_irqrestore(master->lock, flags); +} + +static int clk_sama7g5_master_is_enabled(struct clk_hw *hw) +{ + struct clk_master *master = to_clk_master(hw); + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(master->lock, flags); + + regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id); + regmap_read(master->regmap, AT91_PMC_MCR_V2, &val); + + spin_unlock_irqrestore(master->lock, flags); + + return !!(val & AT91_PMC_MCR_V2_EN); } +static int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_master *master = to_clk_master(hw); + unsigned long div, flags; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1))) + return -EINVAL; + + if (div == 3) + div = MASTER_PRES_MAX; + else if (div) + div = ffs(div) - 1; + + spin_lock_irqsave(master->lock, flags); + master->div = div; + spin_unlock_irqrestore(master->lock, flags); + + return 0; +} + +static const struct clk_ops sama7g5_master_ops = { + .enable = clk_sama7g5_master_enable, + .disable = clk_sama7g5_master_disable, + .is_enabled = clk_sama7g5_master_is_enabled, + .recalc_rate = clk_sama7g5_master_recalc_rate, + .set_rate = clk_sama7g5_master_set_rate, + .get_parent = clk_sama7g5_master_get_parent, + .set_parent = clk_sama7g5_master_set_parent, +}; + +struct clk_hw * __init +at91_clk_sama7g5_register_master(struct regmap *regmap, + const char *name, int num_parents, + const char **parent_names, + u32 *mux_table, + spinlock_t *lock, u8 id, + bool critical, int chg_pid) +{ + struct clk_master *master; + struct clk_hw *hw; + struct clk_init_data init; + unsigned long flags; + unsigned int val; + int ret; + + if (!name || !num_parents || !parent_names || !mux_table || + !lock || id > MASTER_MAX_ID) + return ERR_PTR(-EINVAL); + + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &sama7g5_master_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + if (chg_pid >= 0) + init.flags |= CLK_SET_RATE_PARENT; + if (critical) + init.flags |= CLK_IS_CRITICAL; + + master->hw.init = &init; + master->regmap = regmap; + master->id = id; + master->chg_pid = chg_pid; + master->lock = lock; + master->mux_table = mux_table; + + spin_lock_irqsave(master->lock, flags); + regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id); + regmap_read(master->regmap, AT91_PMC_MCR_V2, &val); + master->parent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT; + master->div = (val & AT91_PMC_MCR_V2_DIV) >> MASTER_DIV_SHIFT; + spin_unlock_irqrestore(master->lock, flags); + + hw = &master->hw; + ret = clk_hw_register(NULL, &master->hw); + if (ret) { + kfree(master); + hw = ERR_PTR(ret); + } + + return hw; +} const struct clk_master_layout at91rm9200_master_layout = { .mask = 0x31F, diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c index 2b9008eb2c..bd4b50b142 100644 --- a/drivers/clk/at91/clk-peripheral.c +++ b/drivers/clk/at91/clk-peripheral.c @@ -3,51 +3,51 @@ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> */ -#include <common.h> -#include <clock.h> -#include <linux/list.h> -#include <linux/clk.h> +#include <linux/bitops.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> #include "pmc.h" +DEFINE_SPINLOCK(pmc_pcr_lock); + #define PERIPHERAL_ID_MIN 2 #define PERIPHERAL_ID_MAX 31 #define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) -#define PERIPHERAL_RSHIFT_MASK 0x3 -#define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK) - #define PERIPHERAL_MAX_SHIFT 3 struct clk_peripheral { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; u32 id; - const char *parent; }; -#define to_clk_peripheral(clk) container_of(clk, struct clk_peripheral, clk) +#define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw) struct clk_sam9x5_peripheral { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; struct clk_range range; + spinlock_t *lock; u32 id; u32 div; const struct clk_pcr_layout *layout; + struct at91_clk_pms pms; bool auto_div; - const char *parent; + int chg_pid; }; -#define to_clk_sam9x5_peripheral(clk) \ - container_of(clk, struct clk_sam9x5_peripheral, clk) +#define to_clk_sam9x5_peripheral(hw) \ + container_of(hw, struct clk_sam9x5_peripheral, hw) -static int clk_peripheral_enable(struct clk *clk) +static int clk_peripheral_enable(struct clk_hw *hw) { - struct clk_peripheral *periph = to_clk_peripheral(clk); + struct clk_peripheral *periph = to_clk_peripheral(hw); int offset = AT91_PMC_PCER; u32 id = periph->id; @@ -60,9 +60,9 @@ static int clk_peripheral_enable(struct clk *clk) return 0; } -static void clk_peripheral_disable(struct clk *clk) +static void clk_peripheral_disable(struct clk_hw *hw) { - struct clk_peripheral *periph = to_clk_peripheral(clk); + struct clk_peripheral *periph = to_clk_peripheral(hw); int offset = AT91_PMC_PCDR; u32 id = periph->id; @@ -73,9 +73,9 @@ static void clk_peripheral_disable(struct clk *clk) regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id)); } -static int clk_peripheral_is_enabled(struct clk *clk) +static int clk_peripheral_is_enabled(struct clk_hw *hw) { - struct clk_peripheral *periph = to_clk_peripheral(clk); + struct clk_peripheral *periph = to_clk_peripheral(hw); int offset = AT91_PMC_PCSR; unsigned int status; u32 id = periph->id; @@ -95,42 +95,45 @@ static const struct clk_ops peripheral_ops = { .is_enabled = clk_peripheral_is_enabled, }; -struct clk * __init +struct clk_hw * __init at91_clk_register_peripheral(struct regmap *regmap, const char *name, const char *parent_name, u32 id) { - int ret; struct clk_peripheral *periph; + struct clk_init_data init; + struct clk_hw *hw; + int ret; if (!name || !parent_name || id > PERIPHERAL_ID_MAX) return ERR_PTR(-EINVAL); - periph = xzalloc(sizeof(*periph)); - - periph->clk.name = name; - periph->clk.ops = &peripheral_ops; + periph = kzalloc(sizeof(*periph), GFP_KERNEL); + if (!periph) + return ERR_PTR(-ENOMEM); - if (parent_name) { - periph->parent = parent_name; - periph->clk.parent_names = &periph->parent; - periph->clk.num_parents = 1; - } + init.name = name; + init.ops = &peripheral_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = 0; periph->id = id; + periph->hw.init = &init; periph->regmap = regmap; - ret = clk_register(&periph->clk); + hw = &periph->hw; + ret = clk_hw_register(NULL, &periph->hw); if (ret) { kfree(periph); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &periph->clk; + return hw; } static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph) { - struct clk *parent; + struct clk_hw *parent; unsigned long parent_rate; int shift = 0; @@ -138,8 +141,8 @@ static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph) return; if (periph->range.max) { - parent = clk_get_parent(&periph->clk); - parent_rate = clk_get_rate(parent); + parent = clk_hw_get_parent_by_index(&periph->hw, 0); + parent_rate = clk_hw_get_rate(parent); if (!parent_rate) return; @@ -153,67 +156,86 @@ static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph) periph->div = shift; } -static int clk_sam9x5_peripheral_enable(struct clk *clk) +static int clk_sam9x5_peripheral_set(struct clk_sam9x5_peripheral *periph, + unsigned int status) { - struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + unsigned long flags; + unsigned int enable = status ? AT91_PMC_PCR_EN : 0; if (periph->id < PERIPHERAL_ID_MIN) return 0; + spin_lock_irqsave(periph->lock, flags); regmap_write(periph->regmap, periph->layout->offset, (periph->id & periph->layout->pid_mask)); - regmap_write_bits(periph->regmap, periph->layout->offset, - periph->layout->div_mask | periph->layout->cmd | - AT91_PMC_PCR_EN, - field_prep(periph->layout->div_mask, periph->div) | - periph->layout->cmd | - AT91_PMC_PCR_EN); + regmap_update_bits(periph->regmap, periph->layout->offset, + periph->layout->div_mask | periph->layout->cmd | + enable, + field_prep(periph->layout->div_mask, periph->div) | + periph->layout->cmd | enable); + spin_unlock_irqrestore(periph->lock, flags); return 0; } -static void clk_sam9x5_peripheral_disable(struct clk *clk) +static int clk_sam9x5_peripheral_enable(struct clk_hw *hw) { - struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); + + return clk_sam9x5_peripheral_set(periph, 1); +} + +static void clk_sam9x5_peripheral_disable(struct clk_hw *hw) +{ + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); + unsigned long flags; if (periph->id < PERIPHERAL_ID_MIN) return; + spin_lock_irqsave(periph->lock, flags); regmap_write(periph->regmap, periph->layout->offset, (periph->id & periph->layout->pid_mask)); - regmap_write_bits(periph->regmap, periph->layout->offset, - AT91_PMC_PCR_EN | periph->layout->cmd, - periph->layout->cmd); + regmap_update_bits(periph->regmap, periph->layout->offset, + AT91_PMC_PCR_EN | periph->layout->cmd, + periph->layout->cmd); + spin_unlock_irqrestore(periph->lock, flags); } -static int clk_sam9x5_peripheral_is_enabled(struct clk *clk) +static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw) { - struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); + unsigned long flags; unsigned int status; if (periph->id < PERIPHERAL_ID_MIN) return 1; + spin_lock_irqsave(periph->lock, flags); regmap_write(periph->regmap, periph->layout->offset, (periph->id & periph->layout->pid_mask)); regmap_read(periph->regmap, periph->layout->offset, &status); + spin_unlock_irqrestore(periph->lock, flags); - return status & AT91_PMC_PCR_EN ? 1 : 0; + return !!(status & AT91_PMC_PCR_EN); } static unsigned long -clk_sam9x5_peripheral_recalc_rate(struct clk *clk, +clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); + unsigned long flags; unsigned int status; if (periph->id < PERIPHERAL_ID_MIN) return parent_rate; + spin_lock_irqsave(periph->lock, flags); regmap_write(periph->regmap, periph->layout->offset, (periph->id & periph->layout->pid_mask)); regmap_read(periph->regmap, periph->layout->offset, &status); + spin_unlock_irqrestore(periph->lock, flags); if (status & AT91_PMC_PCR_EN) { periph->div = field_get(periph->layout->div_mask, status); @@ -225,7 +247,7 @@ clk_sam9x5_peripheral_recalc_rate(struct clk *clk, return parent_rate >> periph->div; } -static long clk_sam9x5_peripheral_round_rate(struct clk *clk, +static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { @@ -234,7 +256,7 @@ static long clk_sam9x5_peripheral_round_rate(struct clk *clk, unsigned long best_diff; unsigned long cur_rate = *parent_rate; unsigned long cur_diff; - struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) return *parent_rate; @@ -271,12 +293,12 @@ static long clk_sam9x5_peripheral_round_rate(struct clk *clk, return best_rate; } -static int clk_sam9x5_peripheral_set_rate(struct clk *clk, +static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { int shift; - struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) { if (parent_rate == rate) return 0; @@ -307,45 +329,64 @@ static const struct clk_ops sam9x5_peripheral_ops = { .set_rate = clk_sam9x5_peripheral_set_rate, }; -struct clk * __init -at91_clk_register_sam9x5_peripheral(struct regmap *regmap, +static const struct clk_ops sam9x5_peripheral_chg_ops = { + .enable = clk_sam9x5_peripheral_enable, + .disable = clk_sam9x5_peripheral_disable, + .is_enabled = clk_sam9x5_peripheral_is_enabled, + .recalc_rate = clk_sam9x5_peripheral_recalc_rate, + .set_rate = clk_sam9x5_peripheral_set_rate, +}; + +struct clk_hw * __init +at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, const struct clk_pcr_layout *layout, const char *name, const char *parent_name, - u32 id, const struct clk_range *range) + u32 id, const struct clk_range *range, + int chg_pid, unsigned long flags) { - int ret; struct clk_sam9x5_peripheral *periph; + struct clk_init_data init; + struct clk_hw *hw; + int ret; if (!name || !parent_name) return ERR_PTR(-EINVAL); - periph = xzalloc(sizeof(*periph)); - - periph->clk.name = name; - periph->clk.ops = &sam9x5_peripheral_ops; + periph = kzalloc(sizeof(*periph), GFP_KERNEL); + if (!periph) + return ERR_PTR(-ENOMEM); - if (parent_name) { - periph->parent = parent_name; - periph->clk.parent_names = &periph->parent; - periph->clk.num_parents = 1; + init.name = name; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = flags; + if (chg_pid < 0) { + init.ops = &sam9x5_peripheral_ops; + } else { + init.flags |= CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; + init.ops = &sam9x5_peripheral_chg_ops; } periph->id = id; + periph->hw.init = &init; periph->div = 0; periph->regmap = regmap; + periph->lock = lock; if (layout->div_mask) periph->auto_div = true; periph->layout = layout; periph->range = *range; + periph->chg_pid = chg_pid; - ret = clk_register(&periph->clk); + hw = &periph->hw; + ret = clk_hw_register(NULL, &periph->hw); if (ret) { kfree(periph); - return ERR_PTR(ret); + hw = ERR_PTR(ret); + } else { + clk_sam9x5_peripheral_autodiv(periph); } - clk_sam9x5_peripheral_autodiv(periph); - pmc_register_id(id); - - return &periph->clk; + return hw; } diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c index 5cb156e784..027e1fc773 100644 --- a/drivers/clk/at91/clk-pll.c +++ b/drivers/clk/at91/clk-pll.c @@ -3,14 +3,12 @@ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> */ -#include <common.h> -#include <clock.h> -#include <of.h> -#include <linux/list.h> -#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> #include "pmc.h" @@ -31,10 +29,10 @@ #define PLL_OUT_SHIFT 14 #define PLL_MAX_ID 1 -#define to_clk_pll(clk) container_of(clk, struct clk_pll, clk) +#define to_clk_pll(hw) container_of(hw, struct clk_pll, hw) struct clk_pll { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; u8 id; u8 div; @@ -42,7 +40,7 @@ struct clk_pll { u16 mul; const struct clk_pll_layout *layout; const struct clk_pll_characteristics *characteristics; - const char *parent; + struct at91_clk_pms pms; }; static inline bool clk_pll_ready(struct regmap *regmap, int id) @@ -54,9 +52,9 @@ static inline bool clk_pll_ready(struct regmap *regmap, int id) return status & PLL_STATUS_MASK(id) ? 1 : 0; } -static int clk_pll_enable(struct clk *clk) +static int clk_pll_prepare(struct clk_hw *hw) { - struct clk_pll *pll = to_clk_pll(clk); + struct clk_pll *pll = to_clk_pll(hw); struct regmap *regmap = pll->regmap; const struct clk_pll_layout *layout = pll->layout; const struct clk_pll_characteristics *characteristics = @@ -83,39 +81,39 @@ static int clk_pll_enable(struct clk *clk) out = characteristics->out[pll->range]; if (characteristics->icpll) - regmap_write_bits(regmap, AT91_PMC_PLLICPR, PLL_ICPR_MASK(id), + regmap_update_bits(regmap, AT91_PMC_PLLICPR, PLL_ICPR_MASK(id), characteristics->icpll[pll->range] << PLL_ICPR_SHIFT(id)); - regmap_write_bits(regmap, offset, layout->pllr_mask, - pll->div | (PLL_MAX_COUNT << PLL_COUNT_SHIFT) | - (out << PLL_OUT_SHIFT) | - ((pll->mul & layout->mul_mask) << layout->mul_shift)); + regmap_update_bits(regmap, offset, layout->pllr_mask, + pll->div | (PLL_MAX_COUNT << PLL_COUNT_SHIFT) | + (out << PLL_OUT_SHIFT) | + ((pll->mul & layout->mul_mask) << layout->mul_shift)); while (!clk_pll_ready(regmap, pll->id)) - barrier(); + cpu_relax(); return 0; } -static int clk_pll_is_enabled(struct clk *clk) +static int clk_pll_is_prepared(struct clk_hw *hw) { - struct clk_pll *pll = to_clk_pll(clk); + struct clk_pll *pll = to_clk_pll(hw); return clk_pll_ready(pll->regmap, pll->id); } -static void clk_pll_disable(struct clk *clk) +static void clk_pll_unprepare(struct clk_hw *hw) { - struct clk_pll *pll = to_clk_pll(clk); + struct clk_pll *pll = to_clk_pll(hw); unsigned int mask = pll->layout->pllr_mask; - regmap_write_bits(pll->regmap, PLL_REG(pll->id), mask, ~mask); + regmap_update_bits(pll->regmap, PLL_REG(pll->id), mask, ~mask); } -static unsigned long clk_pll_recalc_rate(struct clk *clk, +static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_pll *pll = to_clk_pll(clk); + struct clk_pll *pll = to_clk_pll(hw); if (!pll->div || !pll->mul) return 0; @@ -233,19 +231,19 @@ static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate, return bestrate; } -static long clk_pll_round_rate(struct clk *clk, unsigned long rate, - unsigned long *parent_rate) +static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) { - struct clk_pll *pll = to_clk_pll(clk); + struct clk_pll *pll = to_clk_pll(hw); return clk_pll_get_best_div_mul(pll, rate, *parent_rate, NULL, NULL, NULL); } -static int clk_pll_set_rate(struct clk *clk, unsigned long rate, +static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct clk_pll *pll = to_clk_pll(clk); + struct clk_pll *pll = to_clk_pll(hw); long ret; u32 div; u32 mul; @@ -264,21 +262,23 @@ static int clk_pll_set_rate(struct clk *clk, unsigned long rate, } static const struct clk_ops pll_ops = { - .enable = clk_pll_enable, - .disable = clk_pll_disable, - .is_enabled = clk_pll_is_enabled, + .enable = clk_pll_prepare, + .disable = clk_pll_unprepare, + .is_enabled = clk_pll_is_prepared, .recalc_rate = clk_pll_recalc_rate, .round_rate = clk_pll_round_rate, .set_rate = clk_pll_set_rate, }; -struct clk * __init +struct clk_hw * __init at91_clk_register_pll(struct regmap *regmap, const char *name, const char *parent_name, u8 id, const struct clk_pll_layout *layout, const struct clk_pll_characteristics *characteristics) { struct clk_pll *pll; + struct clk_hw *hw; + struct clk_init_data init; int offset = PLL_REG(id); unsigned int pllr; int ret; @@ -286,17 +286,18 @@ at91_clk_register_pll(struct regmap *regmap, const char *name, if (id > PLL_MAX_ID) return ERR_PTR(-EINVAL); - pll = xzalloc(sizeof(*pll)); - - pll->parent = parent_name; - pll->clk.name = name; - pll->clk.ops = &pll_ops; - pll->clk.parent_names = &pll->parent; - pll->clk.num_parents = 1; + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); - /* init.flags = CLK_SET_RATE_GATE; */ + init.name = name; + init.ops = &pll_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE; pll->id = id; + pll->hw.init = &init; pll->layout = layout; pll->characteristics = characteristics; pll->regmap = regmap; @@ -304,13 +305,14 @@ at91_clk_register_pll(struct regmap *regmap, const char *name, pll->div = PLL_DIV(pllr); pll->mul = PLL_MUL(pllr, layout); - ret = clk_register(&pll->clk); + hw = &pll->hw; + ret = clk_hw_register(NULL, &pll->hw); if (ret) { kfree(pll); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &pll->clk; + return hw; } diff --git a/drivers/clk/at91/clk-plldiv.c b/drivers/clk/at91/clk-plldiv.c index 1cbb61bb2c..7fe4411149 100644 --- a/drivers/clk/at91/clk-plldiv.c +++ b/drivers/clk/at91/clk-plldiv.c @@ -3,29 +3,26 @@ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> */ -#include <common.h> -#include <clock.h> -#include <of.h> -#include <linux/list.h> -#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> #include "pmc.h" -#define to_clk_plldiv(hw) container_of(clk, struct clk_plldiv, clk) +#define to_clk_plldiv(hw) container_of(hw, struct clk_plldiv, hw) struct clk_plldiv { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; - const char *parent; }; -static unsigned long clk_plldiv_recalc_rate(struct clk *clk, +static unsigned long clk_plldiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_plldiv *plldiv = to_clk_plldiv(clk); + struct clk_plldiv *plldiv = to_clk_plldiv(hw); unsigned int mckr; regmap_read(plldiv->regmap, AT91_PMC_MCKR, &mckr); @@ -36,8 +33,8 @@ static unsigned long clk_plldiv_recalc_rate(struct clk *clk, return parent_rate; } -static long clk_plldiv_round_rate(struct clk *clk, unsigned long rate, - unsigned long *parent_rate) +static long clk_plldiv_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) { unsigned long div; @@ -53,16 +50,16 @@ static long clk_plldiv_round_rate(struct clk *clk, unsigned long rate, return *parent_rate; } -static int clk_plldiv_set_rate(struct clk *clk, unsigned long rate, +static int clk_plldiv_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct clk_plldiv *plldiv = to_clk_plldiv(clk); + struct clk_plldiv *plldiv = to_clk_plldiv(hw); if ((parent_rate != rate) && (parent_rate / 2 != rate)) return -EINVAL; - regmap_write_bits(plldiv->regmap, AT91_PMC_MCKR, AT91_PMC_PLLADIV2, - parent_rate != rate ? AT91_PMC_PLLADIV2 : 0); + regmap_update_bits(plldiv->regmap, AT91_PMC_MCKR, AT91_PMC_PLLADIV2, + parent_rate != rate ? AT91_PMC_PLLADIV2 : 0); return 0; } @@ -73,33 +70,34 @@ static const struct clk_ops plldiv_ops = { .set_rate = clk_plldiv_set_rate, }; -struct clk * __init +struct clk_hw * __init at91_clk_register_plldiv(struct regmap *regmap, const char *name, const char *parent_name) { - int ret; struct clk_plldiv *plldiv; + struct clk_hw *hw; + struct clk_init_data init; + int ret; - plldiv = xzalloc(sizeof(*plldiv)); - - plldiv->clk.name = name; - plldiv->clk.ops = &plldiv_ops; - - if (parent_name) { - plldiv->parent = parent_name; - plldiv->clk.parent_names = &plldiv->parent; - plldiv->clk.num_parents = 1; - } + plldiv = kzalloc(sizeof(*plldiv), GFP_KERNEL); + if (!plldiv) + return ERR_PTR(-ENOMEM); - /* init.flags = CLK_SET_RATE_GATE; */ + init.name = name; + init.ops = &plldiv_ops; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.flags = CLK_SET_RATE_GATE; + plldiv->hw.init = &init; plldiv->regmap = regmap; - ret = clk_register(&plldiv->clk); + hw = &plldiv->hw; + ret = clk_hw_register(NULL, &plldiv->hw); if (ret) { kfree(plldiv); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &plldiv->clk; + return hw; } diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c index 26c36a882d..3bf13568f5 100644 --- a/drivers/clk/at91/clk-programmable.c +++ b/drivers/clk/at91/clk-programmable.c @@ -3,18 +3,15 @@ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> */ -#include <common.h> -#include <clock.h> -#include <io.h> -#include <linux/list.h> -#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> #include "pmc.h" -#define PROG_SOURCE_MAX 5 #define PROG_ID_MAX 7 #define PROG_STATUS_MASK(id) (1 << ((id) + 8)) @@ -22,19 +19,20 @@ #define PROG_MAX_RM9200_CSS 3 struct clk_programmable { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; + u32 *mux_table; u8 id; const struct clk_programmable_layout *layout; - const char *parent_names[PROG_SOURCE_MAX]; + struct at91_clk_pms pms; }; -#define to_clk_programmable(clk) container_of(clk, struct clk_programmable, clk) +#define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw) -static unsigned long clk_programmable_recalc_rate(struct clk *clk, +static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_programmable *prog = to_clk_programmable(clk); + struct clk_programmable *prog = to_clk_programmable(hw); const struct clk_programmable_layout *layout = prog->layout; unsigned int pckr; unsigned long rate; @@ -49,9 +47,9 @@ static unsigned long clk_programmable_recalc_rate(struct clk *clk, return rate; } -static int clk_programmable_set_parent(struct clk *clk, u8 index) +static int clk_programmable_set_parent(struct clk_hw *hw, u8 index) { - struct clk_programmable *prog = to_clk_programmable(clk); + struct clk_programmable *prog = to_clk_programmable(hw); const struct clk_programmable_layout *layout = prog->layout; unsigned int mask = layout->css_mask; unsigned int pckr = index; @@ -59,6 +57,9 @@ static int clk_programmable_set_parent(struct clk *clk, u8 index) if (layout->have_slck_mck) mask |= AT91_PMC_CSSMCK_MCK; + if (prog->mux_table) + pckr = clk_mux_index_to_val(prog->mux_table, 0, index); + if (index > layout->css_mask) { if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck) return -EINVAL; @@ -66,14 +67,14 @@ static int clk_programmable_set_parent(struct clk *clk, u8 index) pckr |= AT91_PMC_CSSMCK_MCK; } - regmap_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id), mask, pckr); + regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id), mask, pckr); return 0; } -static int clk_programmable_get_parent(struct clk *clk) +static int clk_programmable_get_parent(struct clk_hw *hw) { - struct clk_programmable *prog = to_clk_programmable(clk); + struct clk_programmable *prog = to_clk_programmable(hw); const struct clk_programmable_layout *layout = prog->layout; unsigned int pckr; u8 ret; @@ -85,13 +86,16 @@ static int clk_programmable_get_parent(struct clk *clk) if (layout->have_slck_mck && (pckr & AT91_PMC_CSSMCK_MCK) && !ret) ret = PROG_MAX_RM9200_CSS + 1; + if (prog->mux_table) + ret = clk_mux_val_to_index(&prog->hw, prog->mux_table, 0, ret); + return ret; } -static int clk_programmable_set_rate(struct clk *clk, unsigned long rate, +static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct clk_programmable *prog = to_clk_programmable(clk); + struct clk_programmable *prog = to_clk_programmable(hw); const struct clk_programmable_layout *layout = prog->layout; unsigned long div = parent_rate / rate; int shift = 0; @@ -114,9 +118,9 @@ static int clk_programmable_set_rate(struct clk *clk, unsigned long rate, return -EINVAL; } - regmap_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id), - layout->pres_mask << layout->pres_shift, - shift << layout->pres_shift); + regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id), + layout->pres_mask << layout->pres_shift, + shift << layout->pres_shift); return 0; } @@ -128,13 +132,16 @@ static const struct clk_ops programmable_ops = { .set_rate = clk_programmable_set_rate, }; -struct clk * __init +struct clk_hw * __init at91_clk_register_programmable(struct regmap *regmap, const char *name, const char **parent_names, u8 num_parents, u8 id, - const struct clk_programmable_layout *layout) + const struct clk_programmable_layout *layout, + u32 *mux_table) { struct clk_programmable *prog; + struct clk_hw *hw; + struct clk_init_data init; int ret; if (id > PROG_ID_MAX) @@ -144,27 +151,26 @@ at91_clk_register_programmable(struct regmap *regmap, if (!prog) return ERR_PTR(-ENOMEM); - prog->clk.name = name; - prog->clk.ops = &programmable_ops; - memcpy(prog->parent_names, parent_names, - num_parents * sizeof(prog->parent_names[0])); - prog->clk.parent_names = &prog->parent_names[0]; - prog->clk.num_parents = num_parents; - /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */ + init.name = name; + init.ops = &programmable_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; prog->id = id; prog->layout = layout; + prog->hw.init = &init; prog->regmap = regmap; + prog->mux_table = mux_table; - ret = clk_register(&prog->clk); + hw = &prog->hw; + ret = clk_hw_register(NULL, &prog->hw); if (ret) { kfree(prog); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - pmc_register_pck(id); - - return &prog->clk; + return hw; } const struct clk_programmable_layout at91rm9200_programmable_layout = { diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c index 9ca77f8722..c4f1606128 100644 --- a/drivers/clk/at91/clk-sam9x60-pll.c +++ b/drivers/clk/at91/clk-sam9x60-pll.c @@ -4,319 +4,654 @@ * */ -#include <common.h> -#include <clock.h> -#include <of.h> -#include <linux/list.h> +#include <linux/bitfield.h> #include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> -#include <linux/bitfield.h> +#include <linux/regmap.h> #include "pmc.h" -#define PMC_PLL_CTRL0 0xc -#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0) -#define PMC_PLL_CTRL0_ENPLL BIT(28) -#define PMC_PLL_CTRL0_ENPLLCK BIT(29) -#define PMC_PLL_CTRL0_ENLOCK BIT(31) - -#define PMC_PLL_CTRL1 0x10 -#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0) -#define PMC_PLL_CTRL1_MUL_MSK GENMASK(30, 24) - -#define PMC_PLL_ACR 0x18 -#define PMC_PLL_ACR_DEFAULT_UPLL 0x12020010UL -#define PMC_PLL_ACR_DEFAULT_PLLA 0x00020010UL -#define PMC_PLL_ACR_UTMIVR BIT(12) -#define PMC_PLL_ACR_UTMIBG BIT(13) -#define PMC_PLL_ACR_LOOP_FILTER_MSK GENMASK(31, 24) - -#define PMC_PLL_UPDT 0x1c -#define PMC_PLL_UPDT_UPDATE BIT(8) - -#define PMC_PLL_ISR0 0xec +#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0) +#define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24) +#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0) #define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1) #define UPLL_DIV 2 #define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1) -#define PLL_MAX_ID 1 +#define FCORE_MIN (600000000) +#define FCORE_MAX (1200000000) -struct sam9x60_pll { - struct clk clk; +#define PLL_MAX_ID 7 + +struct sam9x60_pll_core { struct regmap *regmap; + spinlock_t *lock; const struct clk_pll_characteristics *characteristics; - u32 frac; + const struct clk_pll_layout *layout; + struct clk_hw hw; u8 id; - u8 div; +}; + +struct sam9x60_frac { + struct sam9x60_pll_core core; + struct at91_clk_pms pms; + u32 frac; u16 mul; - const char *parent_name; }; -#define to_sam9x60_pll(clk) container_of(clk, struct sam9x60_pll, clk) +struct sam9x60_div { + struct sam9x60_pll_core core; + struct at91_clk_pms pms; + u8 div; +}; + +#define to_sam9x60_pll_core(hw) container_of(hw, struct sam9x60_pll_core, hw) +#define to_sam9x60_frac(core) container_of(core, struct sam9x60_frac, core) +#define to_sam9x60_div(core) container_of(core, struct sam9x60_div, core) static inline bool sam9x60_pll_ready(struct regmap *regmap, int id) { unsigned int status; - regmap_read(regmap, PMC_PLL_ISR0, &status); + regmap_read(regmap, AT91_PMC_PLL_ISR0, &status); return !!(status & BIT(id)); } -static int sam9x60_pll_enable(struct clk *clk) +static bool sam9x60_frac_pll_ready(struct regmap *regmap, u8 id) { - struct sam9x60_pll *pll = to_sam9x60_pll(clk); - struct regmap *regmap = pll->regmap; - u8 div; - u16 mul; - u32 val; + return sam9x60_pll_ready(regmap, id); +} - regmap_write(regmap, PMC_PLL_UPDT, pll->id); +static unsigned long sam9x60_frac_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); + struct sam9x60_frac *frac = to_sam9x60_frac(core); - regmap_read(regmap, PMC_PLL_CTRL0, &val); - div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val); + return parent_rate * (frac->mul + 1) + + DIV_ROUND_CLOSEST_ULL((u64)parent_rate * frac->frac, (1 << 22)); +} - regmap_read(regmap, PMC_PLL_CTRL1, &val); - mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val); +static int sam9x60_frac_pll_set(struct sam9x60_pll_core *core) +{ + struct sam9x60_frac *frac = to_sam9x60_frac(core); + struct regmap *regmap = core->regmap; + unsigned int val, cfrac, cmul; + unsigned long flags; - if (sam9x60_pll_ready(regmap, pll->id) && - (div == pll->div && mul == pll->mul)) { - return 0; - } + spin_lock_irqsave(core->lock, flags); + + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_ID_MSK, core->id); + regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val); + cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift; + cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift; + + if (sam9x60_frac_pll_ready(regmap, core->id) && + (cmul == frac->mul && cfrac == frac->frac)) + goto unlock; /* Recommended value for PMC_PLL_ACR */ - if (pll->characteristics->upll) - val = PMC_PLL_ACR_DEFAULT_UPLL; + if (core->characteristics->upll) + val = AT91_PMC_PLL_ACR_DEFAULT_UPLL; else - val = PMC_PLL_ACR_DEFAULT_PLLA; - regmap_write(regmap, PMC_PLL_ACR, val); + val = AT91_PMC_PLL_ACR_DEFAULT_PLLA; + regmap_write(regmap, AT91_PMC_PLL_ACR, val); - regmap_write(regmap, PMC_PLL_CTRL1, - FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul)); + regmap_write(regmap, AT91_PMC_PLL_CTRL1, + (frac->mul << core->layout->mul_shift) | + (frac->frac << core->layout->frac_shift)); - if (pll->characteristics->upll) { + if (core->characteristics->upll) { /* Enable the UTMI internal bandgap */ - val |= PMC_PLL_ACR_UTMIBG; - regmap_write(regmap, PMC_PLL_ACR, val); + val |= AT91_PMC_PLL_ACR_UTMIBG; + regmap_write(regmap, AT91_PMC_PLL_ACR, val); udelay(10); /* Enable the UTMI internal regulator */ - val |= PMC_PLL_ACR_UTMIVR; - regmap_write(regmap, PMC_PLL_ACR, val); + val |= AT91_PMC_PLL_ACR_UTMIVR; + regmap_write(regmap, AT91_PMC_PLL_ACR, val); udelay(10); } - regmap_update_bits(regmap, PMC_PLL_UPDT, - PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE); + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | core->id); - regmap_write(regmap, PMC_PLL_CTRL0, - PMC_PLL_CTRL0_ENLOCK | PMC_PLL_CTRL0_ENPLL | - PMC_PLL_CTRL0_ENPLLCK | pll->div); + regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, + AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL, + AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL); - regmap_update_bits(regmap, PMC_PLL_UPDT, - PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE); + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | core->id); - while (!sam9x60_pll_ready(regmap, pll->id)) + while (!sam9x60_pll_ready(regmap, core->id)) cpu_relax(); +unlock: + spin_unlock_irqrestore(core->lock, flags); + return 0; } -static int sam9x60_pll_is_enabled(struct clk *clk) +static int sam9x60_frac_pll_prepare(struct clk_hw *hw) { - struct sam9x60_pll *pll = to_sam9x60_pll(clk); + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); - return sam9x60_pll_ready(pll->regmap, pll->id); + return sam9x60_frac_pll_set(core); } -static void sam9x60_pll_disable(struct clk *clk) +static void sam9x60_frac_pll_unprepare(struct clk_hw *hw) { - struct sam9x60_pll *pll = to_sam9x60_pll(clk); + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); + struct regmap *regmap = core->regmap; + unsigned long flags; - regmap_write(pll->regmap, PMC_PLL_UPDT, pll->id); + spin_lock_irqsave(core->lock, flags); - regmap_update_bits(pll->regmap, PMC_PLL_CTRL0, - PMC_PLL_CTRL0_ENPLLCK, 0); + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_ID_MSK, core->id); - regmap_update_bits(pll->regmap, PMC_PLL_UPDT, - PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE); + regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, AT91_PMC_PLL_CTRL0_ENPLL, 0); - regmap_update_bits(pll->regmap, PMC_PLL_CTRL0, PMC_PLL_CTRL0_ENPLL, 0); + if (core->characteristics->upll) + regmap_update_bits(regmap, AT91_PMC_PLL_ACR, + AT91_PMC_PLL_ACR_UTMIBG | AT91_PMC_PLL_ACR_UTMIVR, 0); - if (pll->characteristics->upll) - regmap_update_bits(pll->regmap, PMC_PLL_ACR, - PMC_PLL_ACR_UTMIBG | PMC_PLL_ACR_UTMIVR, 0); + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | core->id); - regmap_update_bits(pll->regmap, PMC_PLL_UPDT, - PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE); + spin_unlock_irqrestore(core->lock, flags); } -static unsigned long sam9x60_pll_recalc_rate(struct clk *clk, - unsigned long parent_rate) +static int sam9x60_frac_pll_is_prepared(struct clk_hw *hw) { - struct sam9x60_pll *pll = to_sam9x60_pll(clk); + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); - return (parent_rate * (pll->mul + 1)) / (pll->div + 1); + return sam9x60_pll_ready(core->regmap, core->id); } -static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll, - unsigned long rate, - unsigned long parent_rate, - bool update) +static long sam9x60_frac_pll_compute_mul_frac(struct sam9x60_pll_core *core, + unsigned long rate, + unsigned long parent_rate, + bool update) { - const struct clk_pll_characteristics *characteristics = - pll->characteristics; - unsigned long bestremainder = ULONG_MAX; - unsigned long maxdiv, mindiv, tmpdiv; - long bestrate = -ERANGE; - unsigned long bestdiv = 0; - unsigned long bestmul = 0; - unsigned long bestfrac = 0; + struct sam9x60_frac *frac = to_sam9x60_frac(core); + unsigned long tmprate, remainder; + unsigned long nmul = 0; + unsigned long nfrac = 0; - if (rate < characteristics->output[0].min || - rate > characteristics->output[0].max) + if (rate < FCORE_MIN || rate > FCORE_MAX) return -ERANGE; - if (!pll->characteristics->upll) { - mindiv = parent_rate / rate; - if (mindiv < 2) - mindiv = 2; + /* + * Calculate the multiplier associated with the current + * divider that provide the closest rate to the requested one. + */ + nmul = mult_frac(rate, 1, parent_rate); + tmprate = mult_frac(parent_rate, nmul, 1); + remainder = rate - tmprate; - maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX, rate); - if (maxdiv > PLL_DIV_MAX) - maxdiv = PLL_DIV_MAX; - } else { - mindiv = maxdiv = UPLL_DIV; + if (remainder) { + nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22), + parent_rate); + + tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate, + (1 << 22)); } - for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) { - unsigned long remainder; - unsigned long tmprate; - unsigned long tmpmul; - unsigned long tmpfrac = 0; + /* Check if resulted rate is a valid. */ + if (tmprate < FCORE_MIN || tmprate > FCORE_MAX) + return -ERANGE; - /* - * Calculate the multiplier associated with the current - * divider that provide the closest rate to the requested one. - */ - tmpmul = mult_frac(rate, tmpdiv, parent_rate); - tmprate = mult_frac(parent_rate, tmpmul, tmpdiv); - remainder = rate - tmprate; + if (update) { + frac->mul = nmul - 1; + frac->frac = nfrac; + } - if (remainder) { - tmpfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * tmpdiv * (1 << 22), - parent_rate); + return tmprate; +} - tmprate += DIV_ROUND_CLOSEST_ULL((u64)tmpfrac * parent_rate, - tmpdiv * (1 << 22)); +static long sam9x60_frac_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); - if (tmprate > rate) - remainder = tmprate - rate; - else - remainder = rate - tmprate; - } + return sam9x60_frac_pll_compute_mul_frac(core, rate, *parent_rate, false); +} - /* - * Compare the remainder with the best remainder found until - * now and elect a new best multiplier/divider pair if the - * current remainder is smaller than the best one. - */ - if (remainder < bestremainder) { - bestremainder = remainder; - bestdiv = tmpdiv; - bestmul = tmpmul; - bestrate = tmprate; - bestfrac = tmpfrac; +static int sam9x60_frac_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); + + return sam9x60_frac_pll_compute_mul_frac(core, rate, parent_rate, true); +} + +static int sam9x60_frac_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); + struct sam9x60_frac *frac = to_sam9x60_frac(core); + struct regmap *regmap = core->regmap; + unsigned long irqflags; + unsigned int val, cfrac, cmul; + long ret; + + ret = sam9x60_frac_pll_compute_mul_frac(core, rate, parent_rate, true); + if (ret <= 0) + return ret; + + spin_lock_irqsave(core->lock, irqflags); + + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, + core->id); + regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val); + cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift; + cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift; + + if (cmul == frac->mul && cfrac == frac->frac) + goto unlock; + + regmap_write(regmap, AT91_PMC_PLL_CTRL1, + (frac->mul << core->layout->mul_shift) | + (frac->frac << core->layout->frac_shift)); + + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | core->id); + + regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, + AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL, + AT91_PMC_PLL_CTRL0_ENLOCK | + AT91_PMC_PLL_CTRL0_ENPLL); + + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | core->id); + + while (!sam9x60_pll_ready(regmap, core->id)) + cpu_relax(); + +unlock: + spin_unlock_irqrestore(core->lock, irqflags); + + return ret; +} + +static const struct clk_ops sam9x60_frac_pll_ops = { + .enable = sam9x60_frac_pll_prepare, + .disable = sam9x60_frac_pll_unprepare, + .is_enabled = sam9x60_frac_pll_is_prepared, + .recalc_rate = sam9x60_frac_pll_recalc_rate, + .round_rate = sam9x60_frac_pll_round_rate, + .set_rate = sam9x60_frac_pll_set_rate, +}; + +static const struct clk_ops sam9x60_frac_pll_ops_chg = { + .enable = sam9x60_frac_pll_prepare, + .disable = sam9x60_frac_pll_unprepare, + .is_enabled = sam9x60_frac_pll_is_prepared, + .recalc_rate = sam9x60_frac_pll_recalc_rate, + .round_rate = sam9x60_frac_pll_round_rate, + .set_rate = sam9x60_frac_pll_set_rate_chg, +}; + +/* This function should be called with spinlock acquired. */ +static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, u32 div, + bool enable) +{ + struct regmap *regmap = core->regmap; + u32 ena_msk = enable ? core->layout->endiv_mask : 0; + u32 ena_val = enable ? (1 << core->layout->endiv_shift) : 0; + + regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, + core->layout->div_mask | ena_msk, + (div << core->layout->div_shift) | ena_val); + + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | core->id); + + while (!sam9x60_pll_ready(regmap, core->id)) + cpu_relax(); +} + +static int sam9x60_div_pll_set(struct sam9x60_pll_core *core) +{ + struct sam9x60_div *div = to_sam9x60_div(core); + struct regmap *regmap = core->regmap; + unsigned long flags; + unsigned int val, cdiv; + + spin_lock_irqsave(core->lock, flags); + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_ID_MSK, core->id); + regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val); + cdiv = (val & core->layout->div_mask) >> core->layout->div_shift; + + /* Stop if enabled an nothing changed. */ + if (!!(val & core->layout->endiv_mask) && cdiv == div->div) + goto unlock; + + sam9x60_div_pll_set_div(core, div->div, 1); + +unlock: + spin_unlock_irqrestore(core->lock, flags); + + return 0; +} + +static int sam9x60_div_pll_prepare(struct clk_hw *hw) +{ + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); + + return sam9x60_div_pll_set(core); +} + +static void sam9x60_div_pll_unprepare(struct clk_hw *hw) +{ + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); + struct regmap *regmap = core->regmap; + unsigned long flags; + + spin_lock_irqsave(core->lock, flags); + + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_ID_MSK, core->id); + + regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, + core->layout->endiv_mask, 0); + + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK, + AT91_PMC_PLL_UPDT_UPDATE | core->id); + + spin_unlock_irqrestore(core->lock, flags); +} + +static int sam9x60_div_pll_is_prepared(struct clk_hw *hw) +{ + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); + struct regmap *regmap = core->regmap; + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(core->lock, flags); + + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_ID_MSK, core->id); + regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val); + + spin_unlock_irqrestore(core->lock, flags); + + return !!(val & core->layout->endiv_mask); +} + +static unsigned long sam9x60_div_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); + struct sam9x60_div *div = to_sam9x60_div(core); + + return DIV_ROUND_CLOSEST_ULL(parent_rate, (div->div + 1)); +} + +static long sam9x60_div_pll_compute_div(struct sam9x60_pll_core *core, + unsigned long *parent_rate, + unsigned long rate) +{ + const struct clk_pll_characteristics *characteristics = + core->characteristics; + struct clk_hw *parent = clk_hw_get_parent(&core->hw); + unsigned long tmp_rate, tmp_parent_rate, tmp_diff; + long best_diff = -1, best_rate = -EINVAL; + u32 divid; + + if (!rate) + return 0; + + if (rate < characteristics->output[0].min || + rate > characteristics->output[0].max) + return -ERANGE; + + for (divid = 1; divid < core->layout->div_mask; divid++) { + tmp_parent_rate = clk_hw_round_rate(parent, rate * divid); + if (!tmp_parent_rate) + continue; + + tmp_rate = DIV_ROUND_CLOSEST_ULL(tmp_parent_rate, divid); + tmp_diff = abs(rate - tmp_rate); + + if (best_diff < 0 || best_diff > tmp_diff) { + *parent_rate = tmp_parent_rate; + best_rate = tmp_rate; + best_diff = tmp_diff; } - /* We've found a perfect match! */ - if (!remainder) + if (!best_diff) break; } - /* Check if bestrate is a valid output rate */ - if (bestrate < characteristics->output[0].min && - bestrate > characteristics->output[0].max) + if (best_rate < characteristics->output[0].min || + best_rate > characteristics->output[0].max) return -ERANGE; - if (update) { - pll->div = bestdiv - 1; - pll->mul = bestmul - 1; - pll->frac = bestfrac; - } + return best_rate; +} - return bestrate; +static long sam9x60_div_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); + + return sam9x60_div_pll_compute_div(core, parent_rate, rate); } -static long sam9x60_pll_round_rate(struct clk *clk, unsigned long rate, - unsigned long *parent_rate) +static int sam9x60_div_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) { - struct sam9x60_pll *pll = to_sam9x60_pll(clk); + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); + struct sam9x60_div *div = to_sam9x60_div(core); + + div->div = DIV_ROUND_CLOSEST(parent_rate, rate) - 1; - return sam9x60_pll_get_best_div_mul(pll, rate, *parent_rate, false); + return 0; } -static int sam9x60_pll_set_rate(struct clk *clk, unsigned long rate, - unsigned long parent_rate) +static int sam9x60_div_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) { - struct sam9x60_pll *pll = to_sam9x60_pll(clk); + struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); + struct sam9x60_div *div = to_sam9x60_div(core); + struct regmap *regmap = core->regmap; + unsigned long irqflags; + unsigned int val, cdiv; + + div->div = DIV_ROUND_CLOSEST(parent_rate, rate) - 1; - return sam9x60_pll_get_best_div_mul(pll, rate, parent_rate, true); + spin_lock_irqsave(core->lock, irqflags); + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK, + core->id); + regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val); + cdiv = (val & core->layout->div_mask) >> core->layout->div_shift; + + /* Stop if nothing changed. */ + if (cdiv == div->div) + goto unlock; + + sam9x60_div_pll_set_div(core, div->div, 0); + +unlock: + spin_unlock_irqrestore(core->lock, irqflags); + + return 0; } -static const struct clk_ops pll_ops = { - .enable = sam9x60_pll_enable, - .disable = sam9x60_pll_disable, - .is_enabled = sam9x60_pll_is_enabled, - .recalc_rate = sam9x60_pll_recalc_rate, - .round_rate = sam9x60_pll_round_rate, - .set_rate = sam9x60_pll_set_rate, +static const struct clk_ops sam9x60_div_pll_ops = { + .enable = sam9x60_div_pll_prepare, + .disable = sam9x60_div_pll_unprepare, + .is_enabled = sam9x60_div_pll_is_prepared, + .recalc_rate = sam9x60_div_pll_recalc_rate, + .round_rate = sam9x60_div_pll_round_rate, + .set_rate = sam9x60_div_pll_set_rate, }; -struct clk * __init -sam9x60_clk_register_pll(struct regmap *regmap, - const char *name, const char *parent_name, u8 id, - const struct clk_pll_characteristics *characteristics) +static const struct clk_ops sam9x60_div_pll_ops_chg = { + .enable = sam9x60_div_pll_prepare, + .disable = sam9x60_div_pll_unprepare, + .is_enabled = sam9x60_div_pll_is_prepared, + .recalc_rate = sam9x60_div_pll_recalc_rate, + .round_rate = sam9x60_div_pll_round_rate, + .set_rate = sam9x60_div_pll_set_rate_chg, +}; + +struct clk_hw * __init +sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock, + const char *name, const char *parent_name, + struct clk_hw *parent_hw, u8 id, + const struct clk_pll_characteristics *characteristics, + const struct clk_pll_layout *layout, u32 flags) { - struct sam9x60_pll *pll; - unsigned int pllr; + struct sam9x60_frac *frac; + struct clk_hw *hw; + struct clk_init_data init; + unsigned long parent_rate, irqflags; + unsigned int val; int ret; - if (id > PLL_MAX_ID) + if (id > PLL_MAX_ID || !lock || !parent_hw) return ERR_PTR(-EINVAL); - pll = kzalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) + frac = kzalloc(sizeof(*frac), GFP_KERNEL); + if (!frac) return ERR_PTR(-ENOMEM); - pll->clk.name = name; - pll->clk.ops = &pll_ops; - pll->parent_name = parent_name; - pll->clk.parent_names = &pll->parent_name; - pll->clk.num_parents = 1; - /* pll->clk.flags = CLK_SET_RATE_GATE; */ + init.name = name; + init.parent_names = &parent_name; + init.num_parents = 1; + if (flags & CLK_SET_RATE_GATE) + init.ops = &sam9x60_frac_pll_ops; + else + init.ops = &sam9x60_frac_pll_ops_chg; + + init.flags = flags; + + frac->core.id = id; + frac->core.hw.init = &init; + frac->core.characteristics = characteristics; + frac->core.layout = layout; + frac->core.regmap = regmap; + frac->core.lock = lock; + + spin_lock_irqsave(frac->core.lock, irqflags); + if (sam9x60_pll_ready(regmap, id)) { + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_ID_MSK, id); + regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val); + frac->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val); + frac->frac = FIELD_GET(PMC_PLL_CTRL1_FRACR_MSK, val); + } else { + /* + * This means the PLL is not setup by bootloaders. In this + * case we need to set the minimum rate for it. Otherwise + * a clock child of this PLL may be enabled before setting + * its rate leading to enabling this PLL with unsupported + * rate. This will lead to PLL not being locked at all. + */ + parent_rate = clk_hw_get_rate(parent_hw); + if (!parent_rate) { + hw = ERR_PTR(-EINVAL); + goto free; + } + + ret = sam9x60_frac_pll_compute_mul_frac(&frac->core, FCORE_MIN, + parent_rate, true); + if (ret < 0) { + hw = ERR_PTR(ret); + goto free; + } + } + spin_unlock_irqrestore(frac->core.lock, irqflags); + + hw = &frac->core.hw; + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(frac); + hw = ERR_PTR(ret); + } + + return hw; + +free: + spin_unlock_irqrestore(frac->core.lock, irqflags); + kfree(frac); + return hw; +} + +struct clk_hw * __init +sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock, + const char *name, const char *parent_name, u8 id, + const struct clk_pll_characteristics *characteristics, + const struct clk_pll_layout *layout, u32 flags) +{ + struct sam9x60_div *div; + struct clk_hw *hw; + struct clk_init_data init; + unsigned long irqflags; + unsigned int val; + int ret; + + /* We only support one changeable PLL. */ + if (id > PLL_MAX_ID || !lock) + return ERR_PTR(-EINVAL); + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.parent_names = &parent_name; + init.num_parents = 1; + if (flags & CLK_SET_RATE_GATE) + init.ops = &sam9x60_div_pll_ops; + else + init.ops = &sam9x60_div_pll_ops_chg; + init.flags = flags; + + div->core.id = id; + div->core.hw.init = &init; + div->core.characteristics = characteristics; + div->core.layout = layout; + div->core.regmap = regmap; + div->core.lock = lock; + + spin_lock_irqsave(div->core.lock, irqflags); - pll->id = id; - pll->characteristics = characteristics; - pll->regmap = regmap; + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_ID_MSK, id); + regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val); + div->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val); - regmap_write(regmap, PMC_PLL_UPDT, id); - regmap_read(regmap, PMC_PLL_CTRL0, &pllr); - pll->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, pllr); - regmap_read(regmap, PMC_PLL_CTRL1, &pllr); - pll->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, pllr); + spin_unlock_irqrestore(div->core.lock, irqflags); - ret = clk_register(&pll->clk); + hw = &div->core.hw; + ret = clk_hw_register(NULL, hw); if (ret) { - kfree(pll); - return ERR_PTR(ret); + kfree(div); + hw = ERR_PTR(ret); } - return &pll->clk; + return hw; } diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c index 960678db1c..3a070d0d34 100644 --- a/drivers/clk/at91/clk-slow.c +++ b/drivers/clk/at91/clk-slow.c @@ -5,28 +5,25 @@ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> */ -#include <common.h> -#include <clock.h> -#include <io.h> -#include <linux/list.h> -#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> #include "pmc.h" struct clk_sam9260_slow { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; - const char *parent_names[2]; }; -#define to_clk_sam9260_slow(clk) container_of(clk, struct clk_sam9260_slow, clk) +#define to_clk_sam9260_slow(hw) container_of(hw, struct clk_sam9260_slow, hw) -static int clk_sam9260_slow_get_parent(struct clk *clk) +static int clk_sam9260_slow_get_parent(struct clk_hw *hw) { - struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(clk); + struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(hw); unsigned int status; regmap_read(slowck->regmap, AT91_PMC_SR, &status); @@ -38,13 +35,15 @@ static const struct clk_ops sam9260_slow_ops = { .get_parent = clk_sam9260_slow_get_parent, }; -struct clk * __init +struct clk_hw * __init at91_clk_register_sam9260_slow(struct regmap *regmap, const char *name, const char **parent_names, int num_parents) { struct clk_sam9260_slow *slowck; + struct clk_hw *hw; + struct clk_init_data init; int ret; if (!name) @@ -53,20 +52,25 @@ at91_clk_register_sam9260_slow(struct regmap *regmap, if (!parent_names || !num_parents) return ERR_PTR(-EINVAL); - slowck = xzalloc(sizeof(*slowck)); - slowck->clk.name = name; - slowck->clk.ops = &sam9260_slow_ops; - memcpy(slowck->parent_names, parent_names, - num_parents * sizeof(slowck->parent_names[0])); - slowck->clk.parent_names = slowck->parent_names; - slowck->clk.num_parents = num_parents; + slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); + if (!slowck) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &sam9260_slow_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = 0; + + slowck->hw.init = &init; slowck->regmap = regmap; - ret = clk_register(&slowck->clk); + hw = &slowck->hw; + ret = clk_hw_register(NULL, &slowck->hw); if (ret) { kfree(slowck); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &slowck->clk; + return hw; } diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c index 0027ebc8bb..dc1b150750 100644 --- a/drivers/clk/at91/clk-smd.c +++ b/drivers/clk/at91/clk-smd.c @@ -3,35 +3,30 @@ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> */ -#include <common.h> -#include <clock.h> -#include <io.h> -#include <linux/list.h> -#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> #include "pmc.h" -#define SMD_SOURCE_MAX 2 - #define SMD_DIV_SHIFT 8 #define SMD_MAX_DIV 0xf struct at91sam9x5_clk_smd { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; - const char *parent_names[SMD_SOURCE_MAX]; }; -#define to_at91sam9x5_clk_smd(clk) \ - container_of(clk, struct at91sam9x5_clk_smd, clk) +#define to_at91sam9x5_clk_smd(hw) \ + container_of(hw, struct at91sam9x5_clk_smd, hw) -static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk *clk, +static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk); + struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw); unsigned int smdr; u8 smddiv; @@ -41,7 +36,7 @@ static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk *clk, return parent_rate / (smddiv + 1); } -static long at91sam9x5_clk_smd_round_rate(struct clk *clk, unsigned long rate, +static long at91sam9x5_clk_smd_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { unsigned long div; @@ -63,22 +58,22 @@ static long at91sam9x5_clk_smd_round_rate(struct clk *clk, unsigned long rate, return bestrate; } -static int at91sam9x5_clk_smd_set_parent(struct clk *clk, u8 index) +static int at91sam9x5_clk_smd_set_parent(struct clk_hw *hw, u8 index) { - struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk); + struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw); if (index > 1) return -EINVAL; - regmap_write_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMDS, - index ? AT91_PMC_SMDS : 0); + regmap_update_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMDS, + index ? AT91_PMC_SMDS : 0); return 0; } -static int at91sam9x5_clk_smd_get_parent(struct clk *clk) +static int at91sam9x5_clk_smd_get_parent(struct clk_hw *hw) { - struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk); + struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw); unsigned int smdr; regmap_read(smd->regmap, AT91_PMC_SMD, &smdr); @@ -86,17 +81,17 @@ static int at91sam9x5_clk_smd_get_parent(struct clk *clk) return smdr & AT91_PMC_SMDS; } -static int at91sam9x5_clk_smd_set_rate(struct clk *clk, unsigned long rate, +static int at91sam9x5_clk_smd_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk); + struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw); unsigned long div = parent_rate / rate; if (parent_rate % rate || div < 1 || div > (SMD_MAX_DIV + 1)) return -EINVAL; - regmap_write_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMD_DIV, - (div - 1) << SMD_DIV_SHIFT); + regmap_update_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMD_DIV, + (div - 1) << SMD_DIV_SHIFT); return 0; } @@ -109,28 +104,34 @@ static const struct clk_ops at91sam9x5_smd_ops = { .set_rate = at91sam9x5_clk_smd_set_rate, }; -struct clk * __init +struct clk_hw * __init at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name, const char **parent_names, u8 num_parents) { struct at91sam9x5_clk_smd *smd; + struct clk_hw *hw; + struct clk_init_data init; int ret; - smd = xzalloc(sizeof(*smd)); - smd->clk.name = name; - smd->clk.ops = &at91sam9x5_smd_ops; - memcpy(smd->parent_names, parent_names, - num_parents * sizeof(smd->parent_names[0])); - smd->clk.parent_names = smd->parent_names; - smd->clk.num_parents = num_parents; - /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */ + smd = kzalloc(sizeof(*smd), GFP_KERNEL); + if (!smd) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &at91sam9x5_smd_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + + smd->hw.init = &init; smd->regmap = regmap; - ret = clk_register(&smd->clk); + hw = &smd->hw; + ret = clk_hw_register(NULL, &smd->hw); if (ret) { kfree(smd); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &smd->clk; + return hw; } diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c index 77f0dff98b..5f367e292a 100644 --- a/drivers/clk/at91/clk-system.c +++ b/drivers/clk/at91/clk-system.c @@ -2,14 +2,13 @@ /* * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> */ -#include <common.h> -#include <clock.h> -#include <io.h> -#include <linux/list.h> -#include <linux/clk.h> + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> #include "pmc.h" @@ -17,12 +16,12 @@ #define SYSTEM_MAX_NAME_SZ 32 -#define to_clk_system(clk) container_of(clk, struct clk_system, clk) +#define to_clk_system(hw) container_of(hw, struct clk_system, hw) struct clk_system { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; + struct at91_clk_pms pms; u8 id; - const char *parent_name; }; static inline int is_pck(int id) @@ -36,12 +35,12 @@ static inline bool clk_system_ready(struct regmap *regmap, int id) regmap_read(regmap, AT91_PMC_SR, &status); - return status & (1 << id) ? 1 : 0; + return !!(status & (1 << id)); } -static int clk_system_enable(struct clk *clk) +static int clk_system_prepare(struct clk_hw *hw) { - struct clk_system *sys = to_clk_system(clk); + struct clk_system *sys = to_clk_system(hw); regmap_write(sys->regmap, AT91_PMC_SCER, 1 << sys->id); @@ -49,21 +48,21 @@ static int clk_system_enable(struct clk *clk) return 0; while (!clk_system_ready(sys->regmap, sys->id)) - barrier(); + cpu_relax(); return 0; } -static void clk_system_disable(struct clk *clk) +static void clk_system_unprepare(struct clk_hw *hw) { - struct clk_system *sys = to_clk_system(clk); + struct clk_system *sys = to_clk_system(hw); regmap_write(sys->regmap, AT91_PMC_SCDR, 1 << sys->id); } -static int clk_system_is_enabled(struct clk *clk) +static int clk_system_is_prepared(struct clk_hw *hw) { - struct clk_system *sys = to_clk_system(clk); + struct clk_system *sys = to_clk_system(hw); unsigned int status; regmap_read(sys->regmap, AT91_PMC_SCSR, &status); @@ -76,40 +75,47 @@ static int clk_system_is_enabled(struct clk *clk) regmap_read(sys->regmap, AT91_PMC_SR, &status); - return status & (1 << sys->id) ? 1 : 0; + return !!(status & (1 << sys->id)); } static const struct clk_ops system_ops = { - .enable = clk_system_enable, - .disable = clk_system_disable, - .is_enabled = clk_system_is_enabled, + .enable = clk_system_prepare, + .disable = clk_system_unprepare, + .is_enabled = clk_system_is_prepared, }; -struct clk * __init +struct clk_hw * __init at91_clk_register_system(struct regmap *regmap, const char *name, - const char *parent_name, u8 id) + const char *parent_name, u8 id, unsigned long flags) { struct clk_system *sys; + struct clk_hw *hw; + struct clk_init_data init; int ret; if (!parent_name || id > SYSTEM_MAX_ID) return ERR_PTR(-EINVAL); - sys = xzalloc(sizeof(*sys)); - sys->clk.name = name; - sys->clk.ops = &system_ops; - sys->parent_name = parent_name; - sys->clk.parent_names = &sys->parent_name; - sys->clk.num_parents = 1; - /* init.flags = CLK_SET_RATE_PARENT; */ + sys = kzalloc(sizeof(*sys), GFP_KERNEL); + if (!sys) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &system_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_PARENT | flags; + sys->id = id; + sys->hw.init = &init; sys->regmap = regmap; - ret = clk_register(&sys->clk); + hw = &sys->hw; + ret = clk_hw_register(NULL, &sys->hw); if (ret) { kfree(sys); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &sys->clk; + return hw; } diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index 2cf68593c0..4473dc7c34 100644 --- a/drivers/clk/at91/clk-usb.c +++ b/drivers/clk/at91/clk-usb.c @@ -3,19 +3,15 @@ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> */ -#include <common.h> -#include <clock.h> -#include <io.h> -#include <linux/list.h> -#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> #include "pmc.h" -#define USB_SOURCE_MAX 2 - #define SAM9X5_USB_DIV_SHIFT 8 #define SAM9X5_USB_MAX_DIV 0xf @@ -26,29 +22,29 @@ #define SAM9X60_USBS_MASK GENMASK(1, 0) struct at91sam9x5_clk_usb { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; - const char *parent_names[USB_SOURCE_MAX]; + struct at91_clk_pms pms; u32 usbs_mask; + u8 num_parents; }; -#define to_at91sam9x5_clk_usb(clk) \ - container_of(clk, struct at91sam9x5_clk_usb, clk) +#define to_at91sam9x5_clk_usb(hw) \ + container_of(hw, struct at91sam9x5_clk_usb, hw) struct at91rm9200_clk_usb { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; u32 divisors[4]; - const char *parent_name; }; -#define to_at91rm9200_clk_usb(clk) \ - container_of(clk, struct at91rm9200_clk_usb, clk) +#define to_at91rm9200_clk_usb(hw) \ + container_of(hw, struct at91rm9200_clk_usb, hw) -static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk *clk, +static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); unsigned int usbr; u8 usbdiv; @@ -58,21 +54,21 @@ static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk *clk, return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1)); } -static int at91sam9x5_clk_usb_set_parent(struct clk *clk, u8 index) +static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); - if (index > 1) + if (index >= usb->num_parents) return -EINVAL; - regmap_write_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index); + regmap_update_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index); return 0; } -static int at91sam9x5_clk_usb_get_parent(struct clk *clk) +static int at91sam9x5_clk_usb_get_parent(struct clk_hw *hw) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); unsigned int usbr; regmap_read(usb->regmap, AT91_PMC_USB, &usbr); @@ -80,10 +76,10 @@ static int at91sam9x5_clk_usb_get_parent(struct clk *clk) return usbr & usb->usbs_mask; } -static int at91sam9x5_clk_usb_set_rate(struct clk *clk, unsigned long rate, +static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); unsigned long div; if (!rate) @@ -93,8 +89,8 @@ static int at91sam9x5_clk_usb_set_rate(struct clk *clk, unsigned long rate, if (div > SAM9X5_USB_MAX_DIV + 1 || !div) return -EINVAL; - regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV, - (div - 1) << SAM9X5_USB_DIV_SHIFT); + regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV, + (div - 1) << SAM9X5_USB_DIV_SHIFT); return 0; } @@ -106,26 +102,26 @@ static const struct clk_ops at91sam9x5_usb_ops = { .set_rate = at91sam9x5_clk_usb_set_rate, }; -static int at91sam9n12_clk_usb_enable(struct clk *clk) +static int at91sam9n12_clk_usb_enable(struct clk_hw *hw) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); - regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, - AT91_PMC_USBS); + regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, + AT91_PMC_USBS); return 0; } -static void at91sam9n12_clk_usb_disable(struct clk *clk) +static void at91sam9n12_clk_usb_disable(struct clk_hw *hw) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); - regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0); + regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0); } -static int at91sam9n12_clk_usb_is_enabled(struct clk *clk) +static int at91sam9n12_clk_usb_is_enabled(struct clk_hw *hw) { - struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); unsigned int usbr; regmap_read(usb->regmap, AT91_PMC_USB, &usbr); @@ -141,37 +137,43 @@ static const struct clk_ops at91sam9n12_usb_ops = { .set_rate = at91sam9x5_clk_usb_set_rate, }; -static struct clk * __init +static struct clk_hw * __init _at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, const char **parent_names, u8 num_parents, u32 usbs_mask) { struct at91sam9x5_clk_usb *usb; + struct clk_hw *hw; + struct clk_init_data init; int ret; usb = kzalloc(sizeof(*usb), GFP_KERNEL); - usb->clk.name = name; - usb->clk.ops = &at91sam9x5_usb_ops; - memcpy(usb->parent_names, parent_names, - num_parents * sizeof(usb->parent_names[0])); - usb->clk.parent_names = usb->parent_names; - usb->clk.num_parents = num_parents; - usb->clk.flags = CLK_SET_RATE_PARENT; - /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | */ - /* CLK_SET_RATE_PARENT; */ + if (!usb) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &at91sam9x5_usb_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; + + usb->hw.init = &init; usb->regmap = regmap; - usb->usbs_mask = SAM9X5_USBS_MASK; + usb->usbs_mask = usbs_mask; + usb->num_parents = num_parents; - ret = clk_register(&usb->clk); + hw = &usb->hw; + ret = clk_hw_register(NULL, &usb->hw); if (ret) { kfree(usb); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &usb->clk; + return hw; } -struct clk * __init +struct clk_hw * __init at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, const char **parent_names, u8 num_parents) { @@ -179,7 +181,7 @@ at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, num_parents, SAM9X5_USBS_MASK); } -struct clk * __init +struct clk_hw * __init sam9x60_clk_register_usb(struct regmap *regmap, const char *name, const char **parent_names, u8 num_parents) { @@ -187,35 +189,42 @@ sam9x60_clk_register_usb(struct regmap *regmap, const char *name, num_parents, SAM9X60_USBS_MASK); } -struct clk * __init +struct clk_hw * __init at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name, const char *parent_name) { struct at91sam9x5_clk_usb *usb; + struct clk_hw *hw; + struct clk_init_data init; int ret; - usb = xzalloc(sizeof(*usb)); - usb->clk.name = name; - usb->clk.ops = &at91sam9n12_usb_ops; - usb->parent_names[0] = parent_name; - usb->clk.parent_names = &usb->parent_names[0]; - usb->clk.num_parents = 1; - /* init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; */ + usb = kzalloc(sizeof(*usb), GFP_KERNEL); + if (!usb) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &at91sam9n12_usb_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; + + usb->hw.init = &init; usb->regmap = regmap; - ret = clk_register(&usb->clk); + hw = &usb->hw; + ret = clk_hw_register(NULL, &usb->hw); if (ret) { kfree(usb); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &usb->clk; + return hw; } -static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk *clk, +static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk); + struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); unsigned int pllbr; u8 usbdiv; @@ -228,11 +237,11 @@ static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk *clk, return 0; } -static long at91rm9200_clk_usb_round_rate(struct clk *clk, unsigned long rate, +static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { - struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk); - struct clk *parent = clk_get_parent(clk); + struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); + struct clk_hw *parent = clk_hw_get_parent(hw); unsigned long bestrate = 0; int bestdiff = -1; unsigned long tmprate; @@ -246,7 +255,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk *clk, unsigned long rate, continue; tmp_parent_rate = rate * usb->divisors[i]; - tmp_parent_rate = clk_round_rate(parent, tmp_parent_rate); + tmp_parent_rate = clk_hw_round_rate(parent, tmp_parent_rate); tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]); if (tmprate < rate) tmpdiff = rate - tmprate; @@ -266,11 +275,11 @@ static long at91rm9200_clk_usb_round_rate(struct clk *clk, unsigned long rate, return bestrate; } -static int at91rm9200_clk_usb_set_rate(struct clk *clk, unsigned long rate, +static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { int i; - struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk); + struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); unsigned long div; if (!rate) @@ -280,9 +289,9 @@ static int at91rm9200_clk_usb_set_rate(struct clk *clk, unsigned long rate, for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { if (usb->divisors[i] == div) { - regmap_write_bits(usb->regmap, AT91_CKGR_PLLBR, - AT91_PMC_USBDIV, - i << RM9200_USB_DIV_SHIFT); + regmap_update_bits(usb->regmap, AT91_CKGR_PLLBR, + AT91_PMC_USBDIV, + i << RM9200_USB_DIV_SHIFT); return 0; } @@ -297,29 +306,35 @@ static const struct clk_ops at91rm9200_usb_ops = { .set_rate = at91rm9200_clk_usb_set_rate, }; -struct clk * __init +struct clk_hw * __init at91rm9200_clk_register_usb(struct regmap *regmap, const char *name, const char *parent_name, const u32 *divisors) { struct at91rm9200_clk_usb *usb; + struct clk_hw *hw; + struct clk_init_data init; int ret; - usb = xzalloc(sizeof(*usb)); - usb->clk.name = name; - usb->clk.ops = &at91rm9200_usb_ops; - usb->parent_name = parent_name; - usb->clk.parent_names = &usb->parent_name; - usb->clk.num_parents = 1; - /* init.flags = CLK_SET_RATE_PARENT; */ + usb = kzalloc(sizeof(*usb), GFP_KERNEL); + if (!usb) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &at91rm9200_usb_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_PARENT; + usb->hw.init = &init; usb->regmap = regmap; memcpy(usb->divisors, divisors, sizeof(usb->divisors)); - ret = clk_register(&usb->clk); + hw = &usb->hw; + ret = clk_hw_register(NULL, &usb->hw); if (ret) { kfree(usb); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &usb->clk; + return hw; } diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c index 3d71cd615f..7d85e43024 100644 --- a/drivers/clk/at91/clk-utmi.c +++ b/drivers/clk/at91/clk-utmi.c @@ -3,15 +3,14 @@ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> */ -#include <common.h> -#include <clock.h> -#include <linux/list.h> -#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> - +#include <linux/regmap.h> #include <soc/at91/atmel-sfr.h> +#include <linux/printk.h> #include "pmc.h" @@ -19,16 +18,16 @@ * The purpose of this clock is to generate a 480 MHz signal. A different * rate can't be configured. */ -#define UTMI_RATE 480000000 +#define UTMI_RATE 480000000 struct clk_utmi { - struct clk clk; - const char *parent; + struct clk_hw hw; struct regmap *regmap_pmc; struct regmap *regmap_sfr; + struct at91_clk_pms pms; }; -#define to_clk_utmi(clk) container_of(clk, struct clk_utmi, clk) +#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw) static inline bool clk_utmi_ready(struct regmap *regmap) { @@ -39,10 +38,10 @@ static inline bool clk_utmi_ready(struct regmap *regmap) return status & AT91_PMC_LOCKU; } -static int clk_utmi_enable(struct clk *clk) +static int clk_utmi_prepare(struct clk_hw *hw) { - struct clk *hw_parent; - struct clk_utmi *utmi = to_clk_utmi(clk); + struct clk_hw *hw_parent; + struct clk_utmi *utmi = to_clk_utmi(hw); unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | AT91_PMC_BIASEN; unsigned int utmi_ref_clk_freq; @@ -53,8 +52,8 @@ static int clk_utmi_enable(struct clk *clk) * FREQ field of the SFR_UTMICKTRIM register to generate properly * the utmi clock. */ - hw_parent = clk_get_parent(clk); - parent_rate = clk_get_rate(hw_parent); + hw_parent = clk_hw_get_parent(hw); + parent_rate = clk_hw_get_rate(hw_parent); switch (parent_rate) { case 12000000: @@ -78,80 +77,173 @@ static int clk_utmi_enable(struct clk *clk) return -EINVAL; } - if (utmi->regmap_sfr) { - regmap_write_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM, - AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq); + regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM, + AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq); } else if (utmi_ref_clk_freq) { pr_err("UTMICK: sfr node required\n"); return -EINVAL; } - regmap_write_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr); + regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr); while (!clk_utmi_ready(utmi->regmap_pmc)) - barrier(); + cpu_relax(); return 0; } -static int clk_utmi_is_enabled(struct clk *clk) +static int clk_utmi_is_prepared(struct clk_hw *hw) { - struct clk_utmi *utmi = to_clk_utmi(clk); + struct clk_utmi *utmi = to_clk_utmi(hw); return clk_utmi_ready(utmi->regmap_pmc); } -static void clk_utmi_disable(struct clk *clk) +static void clk_utmi_unprepare(struct clk_hw *hw) { - struct clk_utmi *utmi = to_clk_utmi(clk); + struct clk_utmi *utmi = to_clk_utmi(hw); - regmap_write_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, - AT91_PMC_UPLLEN, 0); + regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, + AT91_PMC_UPLLEN, 0); } -static unsigned long clk_utmi_recalc_rate(struct clk *clk, +static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - /* UTMI clk rate is fixed */ + /* UTMI clk rate is fixed. */ return UTMI_RATE; } static const struct clk_ops utmi_ops = { - .enable = clk_utmi_enable, - .disable = clk_utmi_disable, - .is_enabled = clk_utmi_is_enabled, + .enable = clk_utmi_prepare, + .disable = clk_utmi_unprepare, + .is_enabled = clk_utmi_is_prepared, .recalc_rate = clk_utmi_recalc_rate, }; -struct clk * __init -at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, - const char *name, const char *parent_name) +static struct clk_hw * __init +at91_clk_register_utmi_internal(struct regmap *regmap_pmc, + struct regmap *regmap_sfr, + const char *name, const char *parent_name, + const struct clk_ops *ops, unsigned long flags) { - int ret; struct clk_utmi *utmi; + struct clk_hw *hw; + struct clk_init_data init; + int ret; - utmi = xzalloc(sizeof(*utmi)); - - utmi->clk.name = name; - utmi->clk.ops = &utmi_ops; - - if (parent_name) { - utmi->parent = parent_name; - utmi->clk.parent_names = &utmi->parent; - utmi->clk.num_parents = 1; - } + utmi = kzalloc(sizeof(*utmi), GFP_KERNEL); + if (!utmi) + return ERR_PTR(-ENOMEM); - /* utmi->clk.flags = CLK_SET_RATE_GATE; */ + init.name = name; + init.ops = ops; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.flags = flags; + utmi->hw.init = &init; utmi->regmap_pmc = regmap_pmc; utmi->regmap_sfr = regmap_sfr; - ret = clk_register(&utmi->clk); + hw = &utmi->hw; + ret = clk_hw_register(NULL, &utmi->hw); if (ret) { kfree(utmi); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &utmi->clk; + return hw; +} + +struct clk_hw * __init +at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, + const char *name, const char *parent_name) +{ + return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name, + parent_name, &utmi_ops, CLK_SET_RATE_GATE); +} + +static int clk_utmi_sama7g5_prepare(struct clk_hw *hw) +{ + struct clk_utmi *utmi = to_clk_utmi(hw); + struct clk_hw *hw_parent; + unsigned long parent_rate; + unsigned int val; + + hw_parent = clk_hw_get_parent(hw); + parent_rate = clk_hw_get_rate(hw_parent); + + switch (parent_rate) { + case 16000000: + val = 0; + break; + case 20000000: + val = 2; + break; + case 24000000: + val = 3; + break; + case 32000000: + val = 5; + break; + default: + pr_err("UTMICK: unsupported main_xtal rate\n"); + return -EINVAL; + } + + regmap_write(utmi->regmap_pmc, AT91_PMC_XTALF, val); + + return 0; + +} + +static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw) +{ + struct clk_utmi *utmi = to_clk_utmi(hw); + struct clk_hw *hw_parent; + unsigned long parent_rate; + unsigned int val; + + hw_parent = clk_hw_get_parent(hw); + parent_rate = clk_hw_get_rate(hw_parent); + + regmap_read(utmi->regmap_pmc, AT91_PMC_XTALF, &val); + switch (val & 0x7) { + case 0: + if (parent_rate == 16000000) + return 1; + break; + case 2: + if (parent_rate == 20000000) + return 1; + break; + case 3: + if (parent_rate == 24000000) + return 1; + break; + case 5: + if (parent_rate == 32000000) + return 1; + break; + default: + break; + } + + return 0; +} + +static const struct clk_ops sama7g5_utmi_ops = { + .enable = clk_utmi_sama7g5_prepare, + .is_enabled = clk_utmi_sama7g5_is_prepared, + .recalc_rate = clk_utmi_recalc_rate, +}; + +struct clk_hw * __init +at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name, + const char *parent_name) +{ + return at91_clk_register_utmi_internal(regmap_pmc, NULL, name, + parent_name, &sama7g5_utmi_ops, 0); } diff --git a/drivers/clk/at91/dt-compat.c b/drivers/clk/at91/dt-compat.c deleted file mode 100644 index b888249199..0000000000 --- a/drivers/clk/at91/dt-compat.c +++ /dev/null @@ -1,727 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <linux/kernel.h> -#include <linux/clk.h> -#include <of.h> -#include <driver.h> -#include <regmap.h> -#include <mfd/syscon.h> - - -#include "pmc.h" - -#define MASTER_SOURCE_MAX 4 - -#define PERIPHERAL_AT91RM9200 0 -#define PERIPHERAL_AT91SAM9X5 1 - -#define PERIPHERAL_MAX 64 - -#define PERIPHERAL_ID_MIN 2 - -#define PROG_SOURCE_MAX 5 -#define PROG_ID_MAX 7 - -#define SYSTEM_MAX_ID 31 - -static const struct clk_pcr_layout dt_pcr_layout = { - .offset = 0x10c, - .cmd = BIT(12), - .pid_mask = GENMASK(5, 0), - .div_mask = GENMASK(17, 16), - .gckcss_mask = GENMASK(10, 8), -}; - -static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np) -{ - struct clk *hw; - const char *name = np->name; - const char *parent_name; - struct regmap *regmap; - bool bypass; - - of_property_read_string(np, "clock-output-names", &name); - bypass = of_property_read_bool(np, "atmel,osc-bypass"); - parent_name = of_clk_get_parent_name(np, 0); - - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - - hw = at91_clk_register_main_osc(regmap, name, parent_name, bypass); - if (IS_ERR(hw)) - return; - - of_clk_add_provider(np, of_clk_src_simple_get, hw); -} -CLK_OF_DECLARE(at91rm9200_clk_main_osc, "atmel,at91rm9200-clk-main-osc", - of_at91rm9200_clk_main_osc_setup); - -static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np) -{ - struct clk *hw; - u32 frequency = 0; - u32 accuracy = 0; - const char *name = np->name; - struct regmap *regmap; - - of_property_read_string(np, "clock-output-names", &name); - of_property_read_u32(np, "clock-frequency", &frequency); - of_property_read_u32(np, "clock-accuracy", &accuracy); - - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - - hw = at91_clk_register_main_rc_osc(regmap, name, frequency, accuracy); - if (IS_ERR(hw)) - return; - - of_clk_add_provider(np, of_clk_src_simple_get, hw); -} -CLK_OF_DECLARE(at91sam9x5_clk_main_rc_osc, "atmel,at91sam9x5-clk-main-rc-osc", - of_at91sam9x5_clk_main_rc_osc_setup); - -static void __init of_at91rm9200_clk_main_setup(struct device_node *np) -{ - struct clk *hw; - const char *parent_name; - const char *name = np->name; - struct regmap *regmap; - - parent_name = of_clk_get_parent_name(np, 0); - of_property_read_string(np, "clock-output-names", &name); - - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - - hw = at91_clk_register_rm9200_main(regmap, name, parent_name); - if (IS_ERR(hw)) - return; - - of_clk_add_provider(np, of_clk_src_simple_get, hw); -} -CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-clk-main", - of_at91rm9200_clk_main_setup); - -static void __init of_at91sam9x5_clk_main_setup(struct device_node *np) -{ - struct clk *hw; - const char *parent_names[2]; - unsigned int num_parents; - const char *name = np->name; - struct regmap *regmap; - - num_parents = of_clk_get_parent_count(np); - if (num_parents == 0 || num_parents > 2) - return; - - of_clk_parent_fill(np, parent_names, num_parents); - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - - of_property_read_string(np, "clock-output-names", &name); - - hw = at91_clk_register_sam9x5_main(regmap, name, parent_names, - num_parents); - if (IS_ERR(hw)) - return; - - of_clk_add_provider(np, of_clk_src_simple_get, hw); -} -CLK_OF_DECLARE(at91sam9x5_clk_main, "atmel,at91sam9x5-clk-main", - of_at91sam9x5_clk_main_setup); - -static struct clk_master_characteristics * __init -of_at91_clk_master_get_characteristics(struct device_node *np) -{ - struct clk_master_characteristics *characteristics; - - characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL); - if (!characteristics) - return NULL; - - if (of_at91_get_clk_range(np, "atmel,clk-output-range", &characteristics->output)) - goto out_free_characteristics; - - of_property_read_u32_array(np, "atmel,clk-divisors", - characteristics->divisors, 4); - - characteristics->have_div3_pres = - of_property_read_bool(np, "atmel,master-clk-have-div3-pres"); - - return characteristics; - -out_free_characteristics: - kfree(characteristics); - return NULL; -} - -static void __init -of_at91_clk_master_setup(struct device_node *np, - const struct clk_master_layout *layout) -{ - struct clk *hw; - unsigned int num_parents; - const char *parent_names[MASTER_SOURCE_MAX]; - const char *name = np->name; - struct clk_master_characteristics *characteristics; - struct regmap *regmap; - - num_parents = of_clk_get_parent_count(np); - if (num_parents == 0 || num_parents > MASTER_SOURCE_MAX) - return; - - of_clk_parent_fill(np, parent_names, num_parents); - - of_property_read_string(np, "clock-output-names", &name); - - characteristics = of_at91_clk_master_get_characteristics(np); - if (!characteristics) - return; - - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - - hw = at91_clk_register_master(regmap, name, num_parents, - parent_names, layout, - characteristics); - if (IS_ERR(hw)) - goto out_free_characteristics; - - of_clk_add_provider(np, of_clk_src_simple_get, hw); - return; - -out_free_characteristics: - kfree(characteristics); -} - -static void __init of_at91rm9200_clk_master_setup(struct device_node *np) -{ - of_at91_clk_master_setup(np, &at91rm9200_master_layout); -} -CLK_OF_DECLARE(at91rm9200_clk_master, "atmel,at91rm9200-clk-master", - of_at91rm9200_clk_master_setup); - -static void __init of_at91sam9x5_clk_master_setup(struct device_node *np) -{ - of_at91_clk_master_setup(np, &at91sam9x5_master_layout); -} -CLK_OF_DECLARE(at91sam9x5_clk_master, "atmel,at91sam9x5-clk-master", - of_at91sam9x5_clk_master_setup); - -static void __init -of_at91_clk_periph_setup(struct device_node *np, u8 type) -{ - int num; - u32 id; - struct clk *hw; - const char *parent_name; - const char *name; - struct device_node *periphclknp; - struct regmap *regmap; - - parent_name = of_clk_get_parent_name(np, 0); - if (!parent_name) - return; - - num = of_get_child_count(np); - if (!num || num > PERIPHERAL_MAX) - return; - - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - - for_each_child_of_node(np, periphclknp) { - if (of_property_read_u32(periphclknp, "reg", &id)) - continue; - - if (id >= PERIPHERAL_MAX) - continue; - - if (of_property_read_string(np, "clock-output-names", &name)) - name = periphclknp->name; - - if (type == PERIPHERAL_AT91RM9200) { - hw = at91_clk_register_peripheral(regmap, name, - parent_name, id); - } else { - struct clk_range range = CLK_RANGE(0, 0); - - of_at91_get_clk_range(periphclknp, - "atmel,clk-output-range", - &range); - - hw = at91_clk_register_sam9x5_peripheral(regmap, - &dt_pcr_layout, - name, - parent_name, - id, &range); - } - - if (IS_ERR(hw)) - continue; - - of_clk_add_provider(periphclknp, of_clk_src_simple_get, hw); - } -} - -static void __init of_at91rm9200_clk_periph_setup(struct device_node *np) -{ - of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200); -} -CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral", - of_at91rm9200_clk_periph_setup); - -static void __init of_at91sam9x5_clk_periph_setup(struct device_node *np) -{ - of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5); -} -CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral", - of_at91sam9x5_clk_periph_setup); - -static struct clk_pll_characteristics * __init -of_at91_clk_pll_get_characteristics(struct device_node *np) -{ - int i; - int offset; - u32 tmp; - int num_output; - u32 num_cells; - struct clk_range input; - struct clk_range *output; - u8 *out = NULL; - u16 *icpll = NULL; - struct clk_pll_characteristics *characteristics; - - if (of_at91_get_clk_range(np, "atmel,clk-input-range", &input)) - return NULL; - - if (of_property_read_u32(np, "#atmel,pll-clk-output-range-cells", - &num_cells)) - return NULL; - - if (num_cells < 2 || num_cells > 4) - return NULL; - - if (!of_get_property(np, "atmel,pll-clk-output-ranges", &tmp)) - return NULL; - num_output = tmp / (sizeof(u32) * num_cells); - - characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL); - if (!characteristics) - return NULL; - - output = kcalloc(num_output, sizeof(*output), GFP_KERNEL); - if (!output) - goto out_free_characteristics; - - if (num_cells > 2) { - out = kcalloc(num_output, sizeof(*out), GFP_KERNEL); - if (!out) - goto out_free_output; - } - - if (num_cells > 3) { - icpll = kcalloc(num_output, sizeof(*icpll), GFP_KERNEL); - if (!icpll) - goto out_free_output; - } - - for (i = 0; i < num_output; i++) { - offset = i * num_cells; - if (of_property_read_u32_index(np, - "atmel,pll-clk-output-ranges", - offset, &tmp)) - goto out_free_output; - output[i].min = tmp; - if (of_property_read_u32_index(np, - "atmel,pll-clk-output-ranges", - offset + 1, &tmp)) - goto out_free_output; - output[i].max = tmp; - - if (num_cells == 2) - continue; - - if (of_property_read_u32_index(np, - "atmel,pll-clk-output-ranges", - offset + 2, &tmp)) - goto out_free_output; - out[i] = tmp; - - if (num_cells == 3) - continue; - - if (of_property_read_u32_index(np, - "atmel,pll-clk-output-ranges", - offset + 3, &tmp)) - goto out_free_output; - icpll[i] = tmp; - } - - characteristics->input = input; - characteristics->num_output = num_output; - characteristics->output = output; - characteristics->out = out; - characteristics->icpll = icpll; - return characteristics; - -out_free_output: - kfree(icpll); - kfree(out); - kfree(output); -out_free_characteristics: - kfree(characteristics); - return NULL; -} - -static void __init -of_at91_clk_pll_setup(struct device_node *np, - const struct clk_pll_layout *layout) -{ - u32 id; - struct clk *hw; - struct regmap *regmap; - const char *parent_name; - const char *name = np->name; - struct clk_pll_characteristics *characteristics; - - if (of_property_read_u32(np, "reg", &id)) - return; - - parent_name = of_clk_get_parent_name(np, 0); - - of_property_read_string(np, "clock-output-names", &name); - - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - - characteristics = of_at91_clk_pll_get_characteristics(np); - if (!characteristics) - return; - - hw = at91_clk_register_pll(regmap, name, parent_name, id, layout, - characteristics); - if (IS_ERR(hw)) - goto out_free_characteristics; - - of_clk_add_provider(np, of_clk_src_simple_get, hw); - return; - -out_free_characteristics: - kfree(characteristics); -} - -static void __init of_at91rm9200_clk_pll_setup(struct device_node *np) -{ - of_at91_clk_pll_setup(np, &at91rm9200_pll_layout); -} -CLK_OF_DECLARE(at91rm9200_clk_pll, "atmel,at91rm9200-clk-pll", - of_at91rm9200_clk_pll_setup); - -static void __init of_sama5d3_clk_pll_setup(struct device_node *np) -{ - of_at91_clk_pll_setup(np, &sama5d3_pll_layout); -} -CLK_OF_DECLARE(sama5d3_clk_pll, "atmel,sama5d3-clk-pll", - of_sama5d3_clk_pll_setup); - -static void __init -of_at91sam9x5_clk_plldiv_setup(struct device_node *np) -{ - struct clk *hw; - const char *parent_name; - const char *name = np->name; - struct regmap *regmap; - - parent_name = of_clk_get_parent_name(np, 0); - - of_property_read_string(np, "clock-output-names", &name); - - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - - hw = at91_clk_register_plldiv(regmap, name, parent_name); - if (IS_ERR(hw)) - return; - - of_clk_add_provider(np, of_clk_src_simple_get, hw); -} -CLK_OF_DECLARE(at91sam9x5_clk_plldiv, "atmel,at91sam9x5-clk-plldiv", - of_at91sam9x5_clk_plldiv_setup); - -static void __init -of_at91_clk_prog_setup(struct device_node *np, - const struct clk_programmable_layout *layout) -{ - int num; - u32 id; - struct clk *hw; - unsigned int num_parents; - const char *parent_names[PROG_SOURCE_MAX]; - const char *name; - struct device_node *progclknp; - struct regmap *regmap; - - num_parents = of_clk_get_parent_count(np); - if (num_parents == 0 || num_parents > PROG_SOURCE_MAX) - return; - - of_clk_parent_fill(np, parent_names, num_parents); - - num = of_get_child_count(np); - if (!num || num > (PROG_ID_MAX + 1)) - return; - - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - - for_each_child_of_node(np, progclknp) { - if (of_property_read_u32(progclknp, "reg", &id)) - continue; - - if (of_property_read_string(np, "clock-output-names", &name)) - name = progclknp->name; - - hw = at91_clk_register_programmable(regmap, name, - parent_names, num_parents, - id, layout); - if (IS_ERR(hw)) - continue; - - of_clk_add_provider(progclknp, of_clk_src_simple_get, hw); - } -} - -static void __init of_at91rm9200_clk_prog_setup(struct device_node *np) -{ - of_at91_clk_prog_setup(np, &at91rm9200_programmable_layout); -} -CLK_OF_DECLARE(at91rm9200_clk_prog, "atmel,at91rm9200-clk-programmable", - of_at91rm9200_clk_prog_setup); - -static void __init of_at91sam9g45_clk_prog_setup(struct device_node *np) -{ - of_at91_clk_prog_setup(np, &at91sam9g45_programmable_layout); -} -CLK_OF_DECLARE(at91sam9g45_clk_prog, "atmel,at91sam9g45-clk-programmable", - of_at91sam9g45_clk_prog_setup); - -static void __init of_at91sam9x5_clk_prog_setup(struct device_node *np) -{ - of_at91_clk_prog_setup(np, &at91sam9x5_programmable_layout); -} -CLK_OF_DECLARE(at91sam9x5_clk_prog, "atmel,at91sam9x5-clk-programmable", - of_at91sam9x5_clk_prog_setup); - -#ifdef CONFIG_HAVE_AT91_SMD -#define SMD_SOURCE_MAX 2 - -static void __init of_at91sam9x5_clk_smd_setup(struct device_node *np) -{ - struct clk *hw; - unsigned int num_parents; - const char *parent_names[SMD_SOURCE_MAX]; - const char *name = np->name; - struct regmap *regmap; - - num_parents = of_clk_get_parent_count(np); - if (num_parents == 0 || num_parents > SMD_SOURCE_MAX) - return; - - of_clk_parent_fill(np, parent_names, num_parents); - - of_property_read_string(np, "clock-output-names", &name); - - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - - hw = at91sam9x5_clk_register_smd(regmap, name, parent_names, - num_parents); - if (IS_ERR(hw)) - return; - - of_clk_add_provider(np, of_clk_src_simple_get, hw); -} -CLK_OF_DECLARE(at91sam9x5_clk_smd, "atmel,at91sam9x5-clk-smd", - of_at91sam9x5_clk_smd_setup); -#endif /* CONFIG_HAVE_AT91_SMD */ - -static void __init of_at91rm9200_clk_sys_setup(struct device_node *np) -{ - int num; - u32 id; - struct clk *hw; - const char *name; - struct device_node *sysclknp; - const char *parent_name; - struct regmap *regmap; - - num = of_get_child_count(np); - if (num > (SYSTEM_MAX_ID + 1)) - return; - - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - - for_each_child_of_node(np, sysclknp) { - if (of_property_read_u32(sysclknp, "reg", &id)) - continue; - - if (of_property_read_string(np, "clock-output-names", &name)) - name = sysclknp->name; - - parent_name = of_clk_get_parent_name(sysclknp, 0); - - hw = at91_clk_register_system(regmap, name, parent_name, id); - if (IS_ERR(hw)) - continue; - - of_clk_add_provider(sysclknp, of_clk_src_simple_get, hw); - } -} -CLK_OF_DECLARE(at91rm9200_clk_sys, "atmel,at91rm9200-clk-system", - of_at91rm9200_clk_sys_setup); - -#ifdef CONFIG_HAVE_AT91_USB_CLK -#define USB_SOURCE_MAX 2 - -static void __init of_at91sam9x5_clk_usb_setup(struct device_node *np) -{ - struct clk *hw; - unsigned int num_parents; - const char *parent_names[USB_SOURCE_MAX]; - const char *name = np->name; - struct regmap *regmap; - - num_parents = of_clk_get_parent_count(np); - if (num_parents == 0 || num_parents > USB_SOURCE_MAX) - return; - - of_clk_parent_fill(np, parent_names, num_parents); - - of_property_read_string(np, "clock-output-names", &name); - - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - - hw = at91sam9x5_clk_register_usb(regmap, name, parent_names, - num_parents); - if (IS_ERR(hw)) - return; - - of_clk_add_provider(np, of_clk_src_simple_get, hw); -} -CLK_OF_DECLARE(at91sam9x5_clk_usb, "atmel,at91sam9x5-clk-usb", - of_at91sam9x5_clk_usb_setup); - -static void __init of_at91sam9n12_clk_usb_setup(struct device_node *np) -{ - struct clk *hw; - const char *parent_name; - const char *name = np->name; - struct regmap *regmap; - - parent_name = of_clk_get_parent_name(np, 0); - if (!parent_name) - return; - - of_property_read_string(np, "clock-output-names", &name); - - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - - hw = at91sam9n12_clk_register_usb(regmap, name, parent_name); - if (IS_ERR(hw)) - return; - - of_clk_add_provider(np, of_clk_src_simple_get, hw); -} -CLK_OF_DECLARE(at91sam9n12_clk_usb, "atmel,at91sam9n12-clk-usb", - of_at91sam9n12_clk_usb_setup); - -static void __init of_at91rm9200_clk_usb_setup(struct device_node *np) -{ - struct clk *hw; - const char *parent_name; - const char *name = np->name; - u32 divisors[4] = {0, 0, 0, 0}; - struct regmap *regmap; - - parent_name = of_clk_get_parent_name(np, 0); - if (!parent_name) - return; - - of_property_read_u32_array(np, "atmel,clk-divisors", divisors, 4); - if (!divisors[0]) - return; - - of_property_read_string(np, "clock-output-names", &name); - - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) - return; - hw = at91rm9200_clk_register_usb(regmap, name, parent_name, divisors); - if (IS_ERR(hw)) - return; - - of_clk_add_provider(np, of_clk_src_simple_get, hw); -} -CLK_OF_DECLARE(at91rm9200_clk_usb, "atmel,at91rm9200-clk-usb", - of_at91rm9200_clk_usb_setup); -#endif /* CONFIG_HAVE_AT91_USB_CLK */ - -#ifdef CONFIG_HAVE_AT91_UTMI -static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np) -{ - struct clk *hw; - const char *parent_name; - const char *name = np->name; - struct regmap *regmap_pmc, *regmap_sfr; - - parent_name = of_clk_get_parent_name(np, 0); - - of_property_read_string(np, "clock-output-names", &name); - - regmap_pmc = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap_pmc)) - return; - - /* - * If the device supports different mainck rates, this value has to be - * set in the UTMI Clock Trimming register. - * - 9x5: mainck supports several rates but it is indicated that a - * 12 MHz is needed in case of USB. - * - sama5d3 and sama5d2: mainck supports several rates. Configuring - * the FREQ field of the UTMI Clock Trimming register is mandatory. - * - sama5d4: mainck is at 12 MHz. - * - * We only need to retrieve sama5d3 or sama5d2 sfr regmap. - */ - regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d3-sfr"); - if (IS_ERR(regmap_sfr)) { - regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr"); - if (IS_ERR(regmap_sfr)) - regmap_sfr = NULL; - } - - hw = at91_clk_register_utmi(regmap_pmc, regmap_sfr, name, parent_name); - if (IS_ERR(hw)) - return; - - of_clk_add_provider(np, of_clk_src_simple_get, hw); -} -CLK_OF_DECLARE(at91sam9x5_clk_utmi, "atmel,at91sam9x5-clk-utmi", - of_at91sam9x5_clk_utmi_setup); -#endif /* CONFIG_HAVE_AT91_UTMI */ diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 171b62cbfd..4780b5790d 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -3,14 +3,14 @@ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> */ -#include <module.h> -#include <linux/list.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/clkdev.h> +#include <linux/clk/at91_pmc.h> #include <of.h> +#include <of_address.h> #include <mfd/syscon.h> -#include <regmap.h> - -#include <dt-bindings/clock/at91.h> +#include <linux/regmap.h> #include "pmc.h" @@ -40,7 +40,7 @@ int of_at91_get_clk_range(struct device_node *np, const char *propname, } EXPORT_SYMBOL_GPL(of_at91_get_clk_range); -struct clk *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data) +struct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data) { unsigned int type = clkspec->args[0]; unsigned int idx = clkspec->args[1]; @@ -63,6 +63,10 @@ struct clk *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data) if (idx < pmc_data->ngck) return pmc_data->ghws[idx]; break; + case PMC_TYPE_PROGRAMMABLE: + if (idx < pmc_data->npck) + return pmc_data->pchws[idx]; + break; default: break; } @@ -72,213 +76,32 @@ struct clk *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data) return ERR_PTR(-EINVAL); } -void pmc_data_free(struct pmc_data *pmc_data) -{ - kfree(pmc_data->chws); - kfree(pmc_data->shws); - kfree(pmc_data->phws); - kfree(pmc_data->ghws); -} - struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem, - unsigned int nperiph, unsigned int ngck) + unsigned int nperiph, unsigned int ngck, + unsigned int npck) { - struct pmc_data *pmc_data = kzalloc(sizeof(*pmc_data), GFP_KERNEL); + unsigned int num_clks = ncore + nsystem + nperiph + ngck + npck; + struct pmc_data *pmc_data; + pmc_data = kzalloc(struct_size(pmc_data, hwtable, num_clks), + GFP_KERNEL); if (!pmc_data) return NULL; pmc_data->ncore = ncore; - pmc_data->chws = kcalloc(ncore, sizeof(struct clk *), GFP_KERNEL); - if (!pmc_data->chws) - goto err; + pmc_data->chws = pmc_data->hwtable; pmc_data->nsystem = nsystem; - pmc_data->shws = kcalloc(nsystem, sizeof(struct clk *), GFP_KERNEL); - if (!pmc_data->shws) - goto err; + pmc_data->shws = pmc_data->chws + ncore; pmc_data->nperiph = nperiph; - pmc_data->phws = kcalloc(nperiph, sizeof(struct clk *), GFP_KERNEL); - if (!pmc_data->phws) - goto err; + pmc_data->phws = pmc_data->shws + nsystem; pmc_data->ngck = ngck; - pmc_data->ghws = kcalloc(ngck, sizeof(struct clk *), GFP_KERNEL); - if (!pmc_data->ghws) - goto err; - - return pmc_data; - -err: - pmc_data_free(pmc_data); - - return NULL; -} - -#ifdef CONFIG_PM -static struct regmap *pmcreg; - -static u8 registered_ids[PMC_MAX_IDS]; -static u8 registered_pcks[PMC_MAX_PCKS]; - -static struct -{ - u32 scsr; - u32 pcsr0; - u32 uckr; - u32 mor; - u32 mcfr; - u32 pllar; - u32 mckr; - u32 usb; - u32 imr; - u32 pcsr1; - u32 pcr[PMC_MAX_IDS]; - u32 audio_pll0; - u32 audio_pll1; - u32 pckr[PMC_MAX_PCKS]; -} pmc_cache; - -/* - * As Peripheral ID 0 is invalid on AT91 chips, the identifier is stored - * without alteration in the table, and 0 is for unused clocks. - */ -void pmc_register_id(u8 id) -{ - int i; - - for (i = 0; i < PMC_MAX_IDS; i++) { - if (registered_ids[i] == 0) { - registered_ids[i] = id; - break; - } - if (registered_ids[i] == id) - break; - } -} - -/* - * As Programmable Clock 0 is valid on AT91 chips, there is an offset - * of 1 between the stored value and the real clock ID. - */ -void pmc_register_pck(u8 pck) -{ - int i; - - for (i = 0; i < PMC_MAX_PCKS; i++) { - if (registered_pcks[i] == 0) { - registered_pcks[i] = pck + 1; - break; - } - if (registered_pcks[i] == (pck + 1)) - break; - } -} - -static int pmc_suspend(void) -{ - int i; - u8 num; + pmc_data->ghws = pmc_data->phws + nperiph; - regmap_read(pmcreg, AT91_PMC_SCSR, &pmc_cache.scsr); - regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0); - regmap_read(pmcreg, AT91_CKGR_UCKR, &pmc_cache.uckr); - regmap_read(pmcreg, AT91_CKGR_MOR, &pmc_cache.mor); - regmap_read(pmcreg, AT91_CKGR_MCFR, &pmc_cache.mcfr); - regmap_read(pmcreg, AT91_CKGR_PLLAR, &pmc_cache.pllar); - regmap_read(pmcreg, AT91_PMC_MCKR, &pmc_cache.mckr); - regmap_read(pmcreg, AT91_PMC_USB, &pmc_cache.usb); - regmap_read(pmcreg, AT91_PMC_IMR, &pmc_cache.imr); - regmap_read(pmcreg, AT91_PMC_PCSR1, &pmc_cache.pcsr1); + pmc_data->npck = npck; + pmc_data->pchws = pmc_data->ghws + ngck; - for (i = 0; registered_ids[i]; i++) { - regmap_write(pmcreg, AT91_PMC_PCR, - (registered_ids[i] & AT91_PMC_PCR_PID_MASK)); - regmap_read(pmcreg, AT91_PMC_PCR, - &pmc_cache.pcr[registered_ids[i]]); - } - for (i = 0; registered_pcks[i]; i++) { - num = registered_pcks[i] - 1; - regmap_read(pmcreg, AT91_PMC_PCKR(num), &pmc_cache.pckr[num]); - } - - return 0; -} - -static bool pmc_ready(unsigned int mask) -{ - unsigned int status; - - regmap_read(pmcreg, AT91_PMC_SR, &status); - - return ((status & mask) == mask) ? 1 : 0; -} - -static void pmc_resume(void) -{ - int i; - u8 num; - u32 tmp; - u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA; - - regmap_read(pmcreg, AT91_PMC_MCKR, &tmp); - if (pmc_cache.mckr != tmp) - pr_warn("MCKR was not configured properly by the firmware\n"); - regmap_read(pmcreg, AT91_CKGR_PLLAR, &tmp); - if (pmc_cache.pllar != tmp) - pr_warn("PLLAR was not configured properly by the firmware\n"); - - regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr); - regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0); - regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr); - regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor); - regmap_write(pmcreg, AT91_CKGR_MCFR, pmc_cache.mcfr); - regmap_write(pmcreg, AT91_PMC_USB, pmc_cache.usb); - regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.imr); - regmap_write(pmcreg, AT91_PMC_PCER1, pmc_cache.pcsr1); - - for (i = 0; registered_ids[i]; i++) { - regmap_write(pmcreg, AT91_PMC_PCR, - pmc_cache.pcr[registered_ids[i]] | - AT91_PMC_PCR_CMD); - } - for (i = 0; registered_pcks[i]; i++) { - num = registered_pcks[i] - 1; - regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]); - } - - if (pmc_cache.uckr & AT91_PMC_UPLLEN) - mask |= AT91_PMC_LOCKU; - - while (!pmc_ready(mask)) - cpu_relax(); -} - -static struct syscore_ops pmc_syscore_ops = { - .suspend = pmc_suspend, - .resume = pmc_resume, -}; - -static const struct of_device_id sama5d2_pmc_dt_ids[] = { - { .compatible = "atmel,sama5d2-pmc" }, - { /* sentinel */ } -}; - -static int __init pmc_register_ops(void) -{ - struct device_node *np; - - np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids); - - pmcreg = device_node_to_regmap(np); - if (IS_ERR(pmcreg)) - return PTR_ERR(pmcreg); - - register_syscore_ops(&pmc_syscore_ops); - - return 0; + return pmc_data; } -/* This has to happen before arch_initcall because of the tcb_clksrc driver */ -postcore_initcall(pmc_register_ops); -#endif diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index d96a94e6e5..6c8801a0f9 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -8,19 +8,29 @@ #ifndef __PMC_H_ #define __PMC_H_ -#include <io.h> -#include <linux/bitops.h> -#include <printk.h> +#include <linux/io.h> +#include <linux/regmap.h> +#include <of.h> +#include <linux/barebox-wrapper.h> +#include <linux/spinlock.h> + +#include <dt-bindings/clock/at91.h> + +extern spinlock_t pmc_pcr_lock; struct pmc_data { unsigned int ncore; - struct clk **chws; + struct clk_hw **chws; unsigned int nsystem; - struct clk **shws; + struct clk_hw **shws; unsigned int nperiph; - struct clk **phws; + struct clk_hw **phws; unsigned int ngck; - struct clk **ghws; + struct clk_hw **ghws; + unsigned int npck; + struct clk_hw **pchws; + + struct clk_hw *hwtable[]; }; struct clk_range { @@ -41,14 +51,20 @@ extern const struct clk_master_layout at91sam9x5_master_layout; struct clk_master_characteristics { struct clk_range output; - u32 divisors[4]; + u32 divisors[5]; u8 have_div3_pres; }; struct clk_pll_layout { u32 pllr_mask; - u16 mul_mask; + u32 mul_mask; + u32 frac_mask; + u32 div_mask; + u32 endiv_mask; u8 mul_shift; + u8 frac_shift; + u8 div_shift; + u8 endiv_shift; }; extern const struct clk_pll_layout at91rm9200_pll_layout; @@ -85,134 +101,168 @@ struct clk_pcr_layout { u32 pid_mask; }; +/** + * struct at91_clk_pms - Power management state for AT91 clock + * @rate: clock rate + * @parent_rate: clock parent rate + * @status: clock status (enabled or disabled) + * @parent: clock parent index + */ +struct at91_clk_pms { + unsigned long rate; + unsigned long parent_rate; + unsigned int status; + unsigned int parent; +}; + #define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) #define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) #define ndck(a, s) (a[s - 1].id + 1) #define nck(a) (a[ARRAY_SIZE(a) - 1].id + 1) struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem, - unsigned int nperiph, unsigned int ngck); -void pmc_data_free(struct pmc_data *pmc_data); + unsigned int nperiph, unsigned int ngck, + unsigned int npck); int of_at91_get_clk_range(struct device_node *np, const char *propname, struct clk_range *range); -struct clk *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data); +struct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data); -struct clk * __init +struct clk_hw * __init at91_clk_register_audio_pll_frac(struct regmap *regmap, const char *name, const char *parent_name); -struct clk * __init +struct clk_hw * __init at91_clk_register_audio_pll_pad(struct regmap *regmap, const char *name, const char *parent_name); -struct clk * __init +struct clk_hw * __init at91_clk_register_audio_pll_pmc(struct regmap *regmap, const char *name, const char *parent_name); -struct clk * __init -at91_clk_register_generated(struct regmap *regmap, +struct clk_hw * __init +at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, const struct clk_pcr_layout *layout, const char *name, const char **parent_names, - u8 num_parents, u8 id, bool pll_audio, - const struct clk_range *range); + u32 *mux_table, u8 num_parents, u8 id, + const struct clk_range *range, int chg_pid); -struct clk * __init +struct clk_hw * __init at91_clk_register_h32mx(struct regmap *regmap, const char *name, const char *parent_name); -struct clk * __init +struct clk_hw * __init at91_clk_i2s_mux_register(struct regmap *regmap, const char *name, const char * const *parent_names, unsigned int num_parents, u8 bus_id); -struct clk * __init +struct clk_hw * __init at91_clk_register_main_rc_osc(struct regmap *regmap, const char *name, u32 frequency, u32 accuracy); -struct clk * __init +struct clk_hw * __init at91_clk_register_main_osc(struct regmap *regmap, const char *name, const char *parent_name, bool bypass); -struct clk * __init +struct clk_hw * __init at91_clk_register_rm9200_main(struct regmap *regmap, const char *name, const char *parent_name); -struct clk * __init +struct clk_hw * __init at91_clk_register_sam9x5_main(struct regmap *regmap, const char *name, const char **parent_names, int num_parents); -struct clk * __init -at91_clk_register_master(struct regmap *regmap, const char *name, - int num_parents, const char **parent_names, - const struct clk_master_layout *layout, - const struct clk_master_characteristics *characteristics); - -struct clk * __init +struct clk_hw * __init +at91_clk_register_master_pres(struct regmap *regmap, const char *name, + int num_parents, const char **parent_names, + const struct clk_master_layout *layout, + const struct clk_master_characteristics *characteristics, + spinlock_t *lock); + +struct clk_hw * __init +at91_clk_register_master_div(struct regmap *regmap, const char *name, + const char *parent_names, + const struct clk_master_layout *layout, + const struct clk_master_characteristics *characteristics, + spinlock_t *lock, u32 flags); + +struct clk_hw * __init +at91_clk_sama7g5_register_master(struct regmap *regmap, + const char *name, int num_parents, + const char **parent_names, u32 *mux_table, + spinlock_t *lock, u8 id, bool critical, + int chg_pid); + +struct clk_hw * __init at91_clk_register_peripheral(struct regmap *regmap, const char *name, const char *parent_name, u32 id); -struct clk * __init -at91_clk_register_sam9x5_peripheral(struct regmap *regmap, +struct clk_hw * __init +at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, const struct clk_pcr_layout *layout, const char *name, const char *parent_name, - u32 id, const struct clk_range *range); + u32 id, const struct clk_range *range, + int chg_pid, unsigned long flags); -struct clk * __init +struct clk_hw * __init at91_clk_register_pll(struct regmap *regmap, const char *name, const char *parent_name, u8 id, const struct clk_pll_layout *layout, const struct clk_pll_characteristics *characteristics); -struct clk * __init +struct clk_hw * __init at91_clk_register_plldiv(struct regmap *regmap, const char *name, const char *parent_name); -struct clk * __init -sam9x60_clk_register_pll(struct regmap *regmap, - const char *name, const char *parent_name, u8 id, - const struct clk_pll_characteristics *characteristics); +struct clk_hw * __init +sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock, + const char *name, const char *parent_name, u8 id, + const struct clk_pll_characteristics *characteristics, + const struct clk_pll_layout *layout, u32 flags); -struct clk * __init +struct clk_hw * __init +sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock, + const char *name, const char *parent_name, + struct clk_hw *parent_hw, u8 id, + const struct clk_pll_characteristics *characteristics, + const struct clk_pll_layout *layout, u32 flags); + +struct clk_hw * __init at91_clk_register_programmable(struct regmap *regmap, const char *name, const char **parent_names, u8 num_parents, u8 id, - const struct clk_programmable_layout *layout); + const struct clk_programmable_layout *layout, + u32 *mux_table); -struct clk * __init +struct clk_hw * __init at91_clk_register_sam9260_slow(struct regmap *regmap, const char *name, const char **parent_names, int num_parents); -struct clk * __init +struct clk_hw * __init at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name, const char **parent_names, u8 num_parents); -struct clk * __init +struct clk_hw * __init at91_clk_register_system(struct regmap *regmap, const char *name, - const char *parent_name, u8 id); + const char *parent_name, u8 id, unsigned long flags); -struct clk * __init +struct clk_hw * __init at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, const char **parent_names, u8 num_parents); -struct clk * __init +struct clk_hw * __init at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name, const char *parent_name); -struct clk * __init +struct clk_hw * __init sam9x60_clk_register_usb(struct regmap *regmap, const char *name, const char **parent_names, u8 num_parents); - -struct clk * __init +struct clk_hw * __init at91rm9200_clk_register_usb(struct regmap *regmap, const char *name, const char *parent_name, const u32 *divisors); -struct clk * __init +struct clk_hw * __init at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, const char *name, const char *parent_name); -#ifdef CONFIG_PM -void pmc_register_id(u8 id); -void pmc_register_pck(u8 pck); -#else -static inline void pmc_register_id(u8 id) {} -static inline void pmc_register_pck(u8 pck) {} -#endif +struct clk_hw * __init +at91_clk_sama7g5_register_utmi(struct regmap *regmap, const char *name, + const char *parent_name); #endif /* __PMC_H_ */ diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c index 36a7a846ef..3a477ffc95 100644 --- a/drivers/clk/at91/sam9x60.c +++ b/drivers/clk/at91/sam9x60.c @@ -1,18 +1,16 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include <driver.h> -#include <regmap.h> -#include <stdio.h> +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/clk-provider.h> #include <mfd/syscon.h> - -#include <linux/clk.h> #include <linux/slab.h> -#include <linux/types.h> +#include <stdio.h> #include <dt-bindings/clock/at91.h> #include "pmc.h" +static DEFINE_SPINLOCK(pmc_pll_lock); +static DEFINE_SPINLOCK(mck_lock); + static const struct clk_master_characteristics mck_characteristics = { .output = { .min = 140000000, .max = 200000000 }, .divisors = { 1, 2, 4, 3 }, @@ -26,7 +24,7 @@ static const struct clk_master_layout sam9x60_master_layout = { }; static const struct clk_range plla_outputs[] = { - { .min = 300000000, .max = 600000000 }, + { .min = 2343750, .max = 1200000000 }, }; static const struct clk_pll_characteristics plla_characteristics = { @@ -46,6 +44,20 @@ static const struct clk_pll_characteristics upll_characteristics = { .upll = true, }; +static const struct clk_pll_layout pll_frac_layout = { + .mul_mask = GENMASK(31, 24), + .frac_mask = GENMASK(21, 0), + .mul_shift = 24, + .frac_shift = 0, +}; + +static const struct clk_pll_layout pll_div_layout = { + .div_mask = GENMASK(7, 0), + .endiv_mask = BIT(29), + .div_shift = 0, + .endiv_shift = 29, +}; + static const struct clk_programmable_layout sam9x60_programmable_layout = { .pres_mask = 0xff, .pres_shift = 8, @@ -64,17 +76,23 @@ static const struct clk_pcr_layout sam9x60_pcr_layout = { static const struct { char *n; char *p; + unsigned long flags; u8 id; } sam9x60_systemck[] = { - { .n = "ddrck", .p = "masterck", .id = 2 }, + /* + * ddrck feeds DDR controller and is enabled by bootloader thus we need + * to keep it enabled in case there is no Linux consumer for it. + */ + { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL }, { .n = "uhpck", .p = "usbck", .id = 6 }, { .n = "pck0", .p = "prog0", .id = 8 }, { .n = "pck1", .p = "prog1", .id = 9 }, - { .n = "qspick", .p = "masterck", .id = 19 }, + { .n = "qspick", .p = "masterck_div", .id = 19 }, }; static const struct { char *n; + unsigned long flags; u8 id; } sam9x60_periphck[] = { { .n = "pioA_clk", .id = 2, }, @@ -121,14 +139,17 @@ static const struct { { .n = "pioD_clk", .id = 44, }, { .n = "tcb1_clk", .id = 45, }, { .n = "dbgu_clk", .id = 47, }, - { .n = "mpddr_clk", .id = 49, }, + /* + * mpddr_clk feeds DDR controller and is enabled by bootloader thus we + * need to keep it enabled in case there is no Linux consumer for it. + */ + { .n = "mpddr_clk", .id = 49, .flags = CLK_IS_CRITICAL }, }; static const struct { char *n; u8 id; struct clk_range r; - bool pll; } sam9x60_gck[] = { { .n = "flex0_gclk", .id = 5, }, { .n = "flex1_gclk", .id = 6, }, @@ -148,11 +169,9 @@ static const struct { { .n = "sdmmc1_gclk", .id = 26, .r = { .min = 0, .max = 105000000 }, }, { .n = "flex11_gclk", .id = 32, }, { .n = "flex12_gclk", .id = 33, }, - { .n = "i2s_gclk", .id = 34, .r = { .min = 0, .max = 105000000 }, - .pll = true, }, + { .n = "i2s_gclk", .id = 34, .r = { .min = 0, .max = 105000000 }, }, { .n = "pit64b_gclk", .id = 37, }, - { .n = "classd_gclk", .id = 42, .r = { .min = 0, .max = 100000000 }, - .pll = true, }, + { .n = "classd_gclk", .id = 42, .r = { .min = 0, .max = 100000000 }, }, { .n = "tcb1_gclk", .id = 45, }, { .n = "dbgu_gclk", .id = 47, }, }; @@ -163,10 +182,10 @@ static void __init sam9x60_pmc_setup(struct device_node *np) const char *td_slck_name, *md_slck_name, *mainxtal_name; struct pmc_data *sam9x60_pmc; const char *parent_names[6]; + struct clk_hw *main_osc_hw; struct regmap *regmap; - struct clk *hw; + struct clk_hw *hw; int i; - bool bypass; i = of_property_match_string(np, "clock-names", "td_slck"); if (i < 0) @@ -185,28 +204,26 @@ static void __init sam9x60_pmc_setup(struct device_node *np) return; mainxtal_name = of_clk_get_parent_name(np, i); - regmap = syscon_node_to_regmap(np); + regmap = device_node_to_regmap(np); if (IS_ERR(regmap)) return; - sam9x60_pmc = pmc_data_allocate(PMC_MAIN + 1, + sam9x60_pmc = pmc_data_allocate(PMC_PLLACK + 1, nck(sam9x60_systemck), nck(sam9x60_periphck), - nck(sam9x60_gck)); + nck(sam9x60_gck), 8); if (!sam9x60_pmc) return; - hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 24000000, + hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000, 50000000); if (IS_ERR(hw)) goto err_free; - bypass = of_property_read_bool(np, "atmel,osc-bypass"); - - hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, - bypass); + hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, 0); if (IS_ERR(hw)) goto err_free; + main_osc_hw = hw; parent_names[0] = "main_rc_osc"; parent_names[1] = "main_osc"; @@ -216,13 +233,45 @@ static void __init sam9x60_pmc_setup(struct device_node *np) sam9x60_pmc->chws[PMC_MAIN] = hw; - hw = sam9x60_clk_register_pll(regmap, "pllack", - "mainck", 0, &plla_characteristics); + hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "pllack_fracck", + "mainck", sam9x60_pmc->chws[PMC_MAIN], + 0, &plla_characteristics, + &pll_frac_layout, + /* + * This feeds pllack_divck which + * feeds CPU. It should not be + * disabled. + */ + CLK_IS_CRITICAL | CLK_SET_RATE_GATE); + if (IS_ERR(hw)) + goto err_free; + + hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "pllack_divck", + "pllack_fracck", 0, &plla_characteristics, + &pll_div_layout, + /* + * This feeds CPU. It should not + * be disabled. + */ + CLK_IS_CRITICAL | CLK_SET_RATE_GATE); + if (IS_ERR(hw)) + goto err_free; + + sam9x60_pmc->chws[PMC_PLLACK] = hw; + + hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "upllck_fracck", + "main_osc", main_osc_hw, 1, + &upll_characteristics, + &pll_frac_layout, CLK_SET_RATE_GATE); if (IS_ERR(hw)) goto err_free; - hw = sam9x60_clk_register_pll(regmap, "upllck", - "main_osc", 1, &upll_characteristics); + hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "upllck_divck", + "upllck_fracck", 1, &upll_characteristics, + &pll_div_layout, + CLK_SET_RATE_GATE | + CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT); if (IS_ERR(hw)) goto err_free; @@ -230,45 +279,55 @@ static void __init sam9x60_pmc_setup(struct device_node *np) parent_names[0] = md_slck_name; parent_names[1] = "mainck"; - parent_names[2] = "pllack"; - hw = at91_clk_register_master(regmap, "masterck", 3, parent_names, - &sam9x60_master_layout, - &mck_characteristics); + parent_names[2] = "pllack_divck"; + hw = at91_clk_register_master_pres(regmap, "masterck_pres", 3, + parent_names, &sam9x60_master_layout, + &mck_characteristics, &mck_lock); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_master_div(regmap, "masterck_div", + "masterck_pres", &sam9x60_master_layout, + &mck_characteristics, &mck_lock, + CLK_SET_RATE_GATE); if (IS_ERR(hw)) goto err_free; sam9x60_pmc->chws[PMC_MCK] = hw; - parent_names[0] = "pllack"; - parent_names[1] = "upllck"; - parent_names[2] = "mainck"; - parent_names[3] = "mainck"; - hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 4); + parent_names[0] = "pllack_divck"; + parent_names[1] = "upllck_divck"; + parent_names[2] = "main_osc"; + hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3); if (IS_ERR(hw)) goto err_free; parent_names[0] = md_slck_name; parent_names[1] = td_slck_name; parent_names[2] = "mainck"; - parent_names[3] = "masterck"; - parent_names[4] = "pllack"; - parent_names[5] = "upllck"; - for (i = 0; i < 8; i++) { + parent_names[3] = "masterck_div"; + parent_names[4] = "pllack_divck"; + parent_names[5] = "upllck_divck"; + for (i = 0; i < 2; i++) { char name[6]; snprintf(name, sizeof(name), "prog%d", i); hw = at91_clk_register_programmable(regmap, name, parent_names, 6, i, - &sam9x60_programmable_layout); + &sam9x60_programmable_layout, + NULL); if (IS_ERR(hw)) goto err_free; + + sam9x60_pmc->pchws[i] = hw; } for (i = 0; i < ARRAY_SIZE(sam9x60_systemck); i++) { hw = at91_clk_register_system(regmap, sam9x60_systemck[i].n, sam9x60_systemck[i].p, - sam9x60_systemck[i].id); + sam9x60_systemck[i].id, + sam9x60_systemck[i].flags); if (IS_ERR(hw)) goto err_free; @@ -276,12 +335,13 @@ static void __init sam9x60_pmc_setup(struct device_node *np) } for (i = 0; i < ARRAY_SIZE(sam9x60_periphck); i++) { - hw = at91_clk_register_sam9x5_peripheral(regmap, + hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, &sam9x60_pcr_layout, sam9x60_periphck[i].n, - "masterck", + "masterck_div", sam9x60_periphck[i].id, - &range); + &range, INT_MIN, + sam9x60_periphck[i].flags); if (IS_ERR(hw)) goto err_free; @@ -289,25 +349,24 @@ static void __init sam9x60_pmc_setup(struct device_node *np) } for (i = 0; i < ARRAY_SIZE(sam9x60_gck); i++) { - hw = at91_clk_register_generated(regmap, + hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, &sam9x60_pcr_layout, sam9x60_gck[i].n, - parent_names, 6, + parent_names, NULL, 6, sam9x60_gck[i].id, - sam9x60_gck[i].pll, - &sam9x60_gck[i].r); + &sam9x60_gck[i].r, INT_MIN); if (IS_ERR(hw)) goto err_free; sam9x60_pmc->ghws[sam9x60_gck[i].id] = hw; } - of_clk_add_provider(np, of_clk_hw_pmc_get, sam9x60_pmc); + of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sam9x60_pmc); return; err_free: - pmc_data_free(sam9x60_pmc); + kfree(sam9x60_pmc); } /* Some clks are used for a clocksource */ CLK_OF_DECLARE(sam9x60_pmc, "microchip,sam9x60-pmc", sam9x60_pmc_setup); diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c index 731637e4ab..96c0d1f6a4 100644 --- a/drivers/clk/at91/sama5d2.c +++ b/drivers/clk/at91/sama5d2.c @@ -1,18 +1,15 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include <driver.h> -#include <regmap.h> -#include <stdio.h> +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/clk-provider.h> #include <mfd/syscon.h> - -#include <linux/clk.h> #include <linux/slab.h> -#include <linux/types.h> +#include <stdio.h> #include <dt-bindings/clock/at91.h> #include "pmc.h" +static DEFINE_SPINLOCK(mck_lock); + static const struct clk_master_characteristics mck_characteristics = { .output = { .min = 124000000, .max = 166000000 }, .divisors = { 1, 2, 4, 3 }, @@ -44,16 +41,21 @@ static const struct clk_pcr_layout sama5d2_pcr_layout = { static const struct { char *n; char *p; + unsigned long flags; u8 id; } sama5d2_systemck[] = { - { .n = "ddrck", .p = "masterck", .id = 2 }, - { .n = "lcdck", .p = "masterck", .id = 3 }, - { .n = "uhpck", .p = "usbck", .id = 6 }, - { .n = "udpck", .p = "usbck", .id = 7 }, - { .n = "pck0", .p = "prog0", .id = 8 }, - { .n = "pck1", .p = "prog1", .id = 9 }, - { .n = "pck2", .p = "prog2", .id = 10 }, - { .n = "iscck", .p = "masterck", .id = 18 }, + /* + * ddrck feeds DDR controller and is enabled by bootloader thus we need + * to keep it enabled in case there is no Linux consumer for it. + */ + { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL }, + { .n = "lcdck", .p = "masterck_div", .id = 3 }, + { .n = "uhpck", .p = "usbck", .id = 6 }, + { .n = "udpck", .p = "usbck", .id = 7 }, + { .n = "pck0", .p = "prog0", .id = 8 }, + { .n = "pck1", .p = "prog1", .id = 9 }, + { .n = "pck2", .p = "prog2", .id = 10 }, + { .n = "iscck", .p = "masterck_div", .id = 18 }, }; static const struct { @@ -95,11 +97,13 @@ static const struct { { .n = "i2s1_clk", .id = 55, .r = { .min = 0, .max = 83000000 }, }, { .n = "can0_clk", .id = 56, .r = { .min = 0, .max = 83000000 }, }, { .n = "can1_clk", .id = 57, .r = { .min = 0, .max = 83000000 }, }, + { .n = "ptc_clk", .id = 58, .r = { .min = 0, .max = 83000000 }, }, { .n = "classd_clk", .id = 59, .r = { .min = 0, .max = 83000000 }, }, }; static const struct { char *n; + unsigned long flags; u8 id; } sama5d2_periphck[] = { { .n = "dma0_clk", .id = 6, }, @@ -107,7 +111,11 @@ static const struct { { .n = "aes_clk", .id = 9, }, { .n = "aesb_clk", .id = 10, }, { .n = "sha_clk", .id = 12, }, - { .n = "mpddr_clk", .id = 13, }, + /* + * mpddr_clk feeds DDR controller and is enabled by bootloader thus we + * need to keep it enabled in case there is no Linux consumer for it. + */ + { .n = "mpddr_clk", .id = 13, .flags = CLK_IS_CRITICAL }, { .n = "matrix0_clk", .id = 15, }, { .n = "sdmmc0_hclk", .id = 31, }, { .n = "sdmmc1_hclk", .id = 32, }, @@ -121,21 +129,30 @@ static const struct { char *n; u8 id; struct clk_range r; - bool pll; + int chg_pid; } sama5d2_gck[] = { - { .n = "sdmmc0_gclk", .id = 31, }, - { .n = "sdmmc1_gclk", .id = 32, }, - { .n = "tcb0_gclk", .id = 35, .r = { .min = 0, .max = 83000000 }, }, - { .n = "tcb1_gclk", .id = 36, .r = { .min = 0, .max = 83000000 }, }, - { .n = "pwm_gclk", .id = 38, .r = { .min = 0, .max = 83000000 }, }, - { .n = "isc_gclk", .id = 46, }, - { .n = "pdmic_gclk", .id = 48, }, - { .n = "i2s0_gclk", .id = 54, .pll = true }, - { .n = "i2s1_gclk", .id = 55, .pll = true }, - { .n = "can0_gclk", .id = 56, .r = { .min = 0, .max = 80000000 }, }, - { .n = "can1_gclk", .id = 57, .r = { .min = 0, .max = 80000000 }, }, - { .n = "classd_gclk", .id = 59, .r = { .min = 0, .max = 100000000 }, - .pll = true }, + { .n = "flx0_gclk", .id = 19, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, + { .n = "flx1_gclk", .id = 20, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, + { .n = "flx2_gclk", .id = 21, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, + { .n = "flx3_gclk", .id = 22, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, + { .n = "flx4_gclk", .id = 23, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, + { .n = "uart0_gclk", .id = 24, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, + { .n = "uart1_gclk", .id = 25, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, + { .n = "uart2_gclk", .id = 26, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, + { .n = "uart3_gclk", .id = 27, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, + { .n = "uart4_gclk", .id = 28, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, }, + { .n = "sdmmc0_gclk", .id = 31, .chg_pid = INT_MIN, }, + { .n = "sdmmc1_gclk", .id = 32, .chg_pid = INT_MIN, }, + { .n = "tcb0_gclk", .id = 35, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, }, + { .n = "tcb1_gclk", .id = 36, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, }, + { .n = "pwm_gclk", .id = 38, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, }, + { .n = "isc_gclk", .id = 46, .chg_pid = INT_MIN, }, + { .n = "pdmic_gclk", .id = 48, .chg_pid = INT_MIN, }, + { .n = "i2s0_gclk", .id = 54, .chg_pid = 5, }, + { .n = "i2s1_gclk", .id = 55, .chg_pid = 5, }, + { .n = "can0_gclk", .id = 56, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, }, + { .n = "can1_gclk", .id = 57, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, }, + { .n = "classd_gclk", .id = 59, .chg_pid = 5, .r = { .min = 0, .max = 100000000 }, }, }; static const struct clk_programmable_layout sama5d2_programmable_layout = { @@ -153,7 +170,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np) struct pmc_data *sama5d2_pmc; const char *parent_names[6]; struct regmap *regmap, *regmap_sfr; - struct clk *hw; + struct clk_hw *hw; int i; bool bypass; @@ -172,10 +189,10 @@ static void __init sama5d2_pmc_setup(struct device_node *np) if (IS_ERR(regmap)) return; - sama5d2_pmc = pmc_data_allocate(PMC_I2S1_MUX + 1, + sama5d2_pmc = pmc_data_allocate(PMC_AUDIOPINCK + 1, nck(sama5d2_systemck), nck(sama5d2_periph32ck), - nck(sama5d2_gck)); + nck(sama5d2_gck), 3); if (!sama5d2_pmc) return; @@ -208,6 +225,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np) if (IS_ERR(hw)) goto err_free; + sama5d2_pmc->chws[PMC_PLLACK] = hw; + hw = at91_clk_register_audio_pll_frac(regmap, "audiopll_fracck", "mainck"); if (IS_ERR(hw)) @@ -218,11 +237,15 @@ static void __init sama5d2_pmc_setup(struct device_node *np) if (IS_ERR(hw)) goto err_free; + sama5d2_pmc->chws[PMC_AUDIOPINCK] = hw; + hw = at91_clk_register_audio_pll_pmc(regmap, "audiopll_pmcck", "audiopll_fracck"); if (IS_ERR(hw)) goto err_free; + sama5d2_pmc->chws[PMC_AUDIOPLLCK] = hw; + regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr"); if (IS_ERR(regmap_sfr)) regmap_sfr = NULL; @@ -237,15 +260,24 @@ static void __init sama5d2_pmc_setup(struct device_node *np) parent_names[1] = "mainck"; parent_names[2] = "plladivck"; parent_names[3] = "utmick"; - hw = at91_clk_register_master(regmap, "masterck", 4, parent_names, - &at91sam9x5_master_layout, - &mck_characteristics); + hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4, + parent_names, + &at91sam9x5_master_layout, + &mck_characteristics, &mck_lock); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_master_div(regmap, "masterck_div", + "masterck_pres", + &at91sam9x5_master_layout, + &mck_characteristics, &mck_lock, + CLK_SET_RATE_GATE); if (IS_ERR(hw)) goto err_free; sama5d2_pmc->chws[PMC_MCK] = hw; - hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck"); + hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck_div"); if (IS_ERR(hw)) goto err_free; @@ -261,24 +293,28 @@ static void __init sama5d2_pmc_setup(struct device_node *np) parent_names[1] = "mainck"; parent_names[2] = "plladivck"; parent_names[3] = "utmick"; - parent_names[4] = "masterck"; + parent_names[4] = "masterck_div"; parent_names[5] = "audiopll_pmcck"; for (i = 0; i < 3; i++) { - char *name; + char name[6]; - name = xasprintf("prog%d", i); + snprintf(name, sizeof(name), "prog%d", i); hw = at91_clk_register_programmable(regmap, name, parent_names, 6, i, - &sama5d2_programmable_layout); + &sama5d2_programmable_layout, + NULL); if (IS_ERR(hw)) goto err_free; + + sama5d2_pmc->pchws[i] = hw; } for (i = 0; i < ARRAY_SIZE(sama5d2_systemck); i++) { hw = at91_clk_register_system(regmap, sama5d2_systemck[i].n, sama5d2_systemck[i].p, - sama5d2_systemck[i].id); + sama5d2_systemck[i].id, + sama5d2_systemck[i].flags); if (IS_ERR(hw)) goto err_free; @@ -286,12 +322,13 @@ static void __init sama5d2_pmc_setup(struct device_node *np) } for (i = 0; i < ARRAY_SIZE(sama5d2_periphck); i++) { - hw = at91_clk_register_sam9x5_peripheral(regmap, + hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, &sama5d2_pcr_layout, sama5d2_periphck[i].n, - "masterck", + "masterck_div", sama5d2_periphck[i].id, - &range); + &range, INT_MIN, + sama5d2_periphck[i].flags); if (IS_ERR(hw)) goto err_free; @@ -299,12 +336,13 @@ static void __init sama5d2_pmc_setup(struct device_node *np) } for (i = 0; i < ARRAY_SIZE(sama5d2_periph32ck); i++) { - hw = at91_clk_register_sam9x5_peripheral(regmap, + hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, &sama5d2_pcr_layout, sama5d2_periph32ck[i].n, "h32mxck", sama5d2_periph32ck[i].id, - &sama5d2_periph32ck[i].r); + &sama5d2_periph32ck[i].r, + INT_MIN, 0); if (IS_ERR(hw)) goto err_free; @@ -315,16 +353,16 @@ static void __init sama5d2_pmc_setup(struct device_node *np) parent_names[1] = "mainck"; parent_names[2] = "plladivck"; parent_names[3] = "utmick"; - parent_names[4] = "masterck"; + parent_names[4] = "masterck_div"; parent_names[5] = "audiopll_pmcck"; for (i = 0; i < ARRAY_SIZE(sama5d2_gck); i++) { - hw = at91_clk_register_generated(regmap, + hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, &sama5d2_pcr_layout, sama5d2_gck[i].n, - parent_names, 6, + parent_names, NULL, 6, sama5d2_gck[i].id, - sama5d2_gck[i].pll, - &sama5d2_gck[i].r); + &sama5d2_gck[i].r, + sama5d2_gck[i].chg_pid); if (IS_ERR(hw)) goto err_free; @@ -351,11 +389,12 @@ static void __init sama5d2_pmc_setup(struct device_node *np) sama5d2_pmc->chws[PMC_I2S1_MUX] = hw; } - of_clk_add_provider(np, of_clk_hw_pmc_get, sama5d2_pmc); + of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama5d2_pmc); return; err_free: - pmc_data_free(sama5d2_pmc); + kfree(sama5d2_pmc); } -CLK_OF_DECLARE_DRIVER(sama5d2_pmc, "atmel,sama5d2-pmc", sama5d2_pmc_setup); + +CLK_OF_DECLARE(sama5d2_pmc, "atmel,sama5d2-pmc", sama5d2_pmc_setup); diff --git a/drivers/clk/at91/sama5d3.c b/drivers/clk/at91/sama5d3.c new file mode 100644 index 0000000000..53a1a7413a --- /dev/null +++ b/drivers/clk/at91/sama5d3.c @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/clk-provider.h> +#include <mfd/syscon.h> +#include <linux/slab.h> +#include <stdio.h> + +#include <dt-bindings/clock/at91.h> + +#include "pmc.h" + +static DEFINE_SPINLOCK(mck_lock); + +static const struct clk_master_characteristics mck_characteristics = { + .output = { .min = 0, .max = 166000000 }, + .divisors = { 1, 2, 4, 3 }, +}; + +static u8 plla_out[] = { 0 }; + +static u16 plla_icpll[] = { 0 }; + +static const struct clk_range plla_outputs[] = { + { .min = 400000000, .max = 1000000000 }, +}; + +static const struct clk_pll_characteristics plla_characteristics = { + .input = { .min = 8000000, .max = 50000000 }, + .num_output = ARRAY_SIZE(plla_outputs), + .output = plla_outputs, + .icpll = plla_icpll, + .out = plla_out, +}; + +static const struct clk_pcr_layout sama5d3_pcr_layout = { + .offset = 0x10c, + .cmd = BIT(12), + .pid_mask = GENMASK(6, 0), + .div_mask = GENMASK(17, 16), +}; + +static const struct { + char *n; + char *p; + unsigned long flags; + u8 id; +} sama5d3_systemck[] = { + /* + * ddrck feeds DDR controller and is enabled by bootloader thus we need + * to keep it enabled in case there is no Linux consumer for it. + */ + { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL }, + { .n = "lcdck", .p = "masterck_div", .id = 3 }, + { .n = "smdck", .p = "smdclk", .id = 4 }, + { .n = "uhpck", .p = "usbck", .id = 6 }, + { .n = "udpck", .p = "usbck", .id = 7 }, + { .n = "pck0", .p = "prog0", .id = 8 }, + { .n = "pck1", .p = "prog1", .id = 9 }, + { .n = "pck2", .p = "prog2", .id = 10 }, +}; + +static const struct { + char *n; + u8 id; + struct clk_range r; + unsigned long flags; +} sama5d3_periphck[] = { + { .n = "dbgu_clk", .id = 2, }, + { .n = "hsmc_clk", .id = 5, }, + { .n = "pioA_clk", .id = 6, }, + { .n = "pioB_clk", .id = 7, }, + { .n = "pioC_clk", .id = 8, }, + { .n = "pioD_clk", .id = 9, }, + { .n = "pioE_clk", .id = 10, }, + { .n = "usart0_clk", .id = 12, .r = { .min = 0, .max = 83000000 }, }, + { .n = "usart1_clk", .id = 13, .r = { .min = 0, .max = 83000000 }, }, + { .n = "usart2_clk", .id = 14, .r = { .min = 0, .max = 83000000 }, }, + { .n = "usart3_clk", .id = 15, .r = { .min = 0, .max = 83000000 }, }, + { .n = "uart0_clk", .id = 16, .r = { .min = 0, .max = 83000000 }, }, + { .n = "uart1_clk", .id = 17, .r = { .min = 0, .max = 83000000 }, }, + { .n = "twi0_clk", .id = 18, .r = { .min = 0, .max = 41500000 }, }, + { .n = "twi1_clk", .id = 19, .r = { .min = 0, .max = 41500000 }, }, + { .n = "twi2_clk", .id = 20, .r = { .min = 0, .max = 41500000 }, }, + { .n = "mci0_clk", .id = 21, }, + { .n = "mci1_clk", .id = 22, }, + { .n = "mci2_clk", .id = 23, }, + { .n = "spi0_clk", .id = 24, .r = { .min = 0, .max = 166000000 }, }, + { .n = "spi1_clk", .id = 25, .r = { .min = 0, .max = 166000000 }, }, + { .n = "tcb0_clk", .id = 26, .r = { .min = 0, .max = 166000000 }, }, + { .n = "tcb1_clk", .id = 27, .r = { .min = 0, .max = 166000000 }, }, + { .n = "pwm_clk", .id = 28, }, + { .n = "adc_clk", .id = 29, .r = { .min = 0, .max = 83000000 }, }, + { .n = "dma0_clk", .id = 30, }, + { .n = "dma1_clk", .id = 31, }, + { .n = "uhphs_clk", .id = 32, }, + { .n = "udphs_clk", .id = 33, }, + { .n = "macb0_clk", .id = 34, }, + { .n = "macb1_clk", .id = 35, }, + { .n = "lcdc_clk", .id = 36, }, + { .n = "isi_clk", .id = 37, }, + { .n = "ssc0_clk", .id = 38, .r = { .min = 0, .max = 83000000 }, }, + { .n = "ssc1_clk", .id = 39, .r = { .min = 0, .max = 83000000 }, }, + { .n = "can0_clk", .id = 40, .r = { .min = 0, .max = 83000000 }, }, + { .n = "can1_clk", .id = 41, .r = { .min = 0, .max = 83000000 }, }, + { .n = "sha_clk", .id = 42, }, + { .n = "aes_clk", .id = 43, }, + { .n = "tdes_clk", .id = 44, }, + { .n = "trng_clk", .id = 45, }, + { .n = "fuse_clk", .id = 48, }, + /* + * mpddr_clk feeds DDR controller and is enabled by bootloader thus we + * need to keep it enabled in case there is no Linux consumer for it. + */ + { .n = "mpddr_clk", .id = 49, .flags = CLK_IS_CRITICAL }, +}; + +static void __init sama5d3_pmc_setup(struct device_node *np) +{ + const char *slck_name, *mainxtal_name; + struct pmc_data *sama5d3_pmc; + const char *parent_names[5]; + struct regmap *regmap; + struct clk_hw *hw; + int i; + bool bypass; + + i = of_property_match_string(np, "clock-names", "slow_clk"); + if (i < 0) + return; + + slck_name = of_clk_get_parent_name(np, i); + + i = of_property_match_string(np, "clock-names", "main_xtal"); + if (i < 0) + return; + mainxtal_name = of_clk_get_parent_name(np, i); + + regmap = device_node_to_regmap(np); + if (IS_ERR(regmap)) + return; + + sama5d3_pmc = pmc_data_allocate(PMC_PLLACK + 1, + nck(sama5d3_systemck), + nck(sama5d3_periphck), 0, 3); + if (!sama5d3_pmc) + return; + + hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000, + 50000000); + if (IS_ERR(hw)) + goto err_free; + + bypass = of_property_read_bool(np, "atmel,osc-bypass"); + + hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, + bypass); + if (IS_ERR(hw)) + goto err_free; + + parent_names[0] = "main_rc_osc"; + parent_names[1] = "main_osc"; + hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0, + &sama5d3_pll_layout, &plla_characteristics); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack"); + if (IS_ERR(hw)) + goto err_free; + + sama5d3_pmc->chws[PMC_PLLACK] = hw; + + hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck"); + if (IS_ERR(hw)) + goto err_free; + + sama5d3_pmc->chws[PMC_UTMI] = hw; + + parent_names[0] = slck_name; + parent_names[1] = "mainck"; + parent_names[2] = "plladivck"; + parent_names[3] = "utmick"; + hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4, + parent_names, + &at91sam9x5_master_layout, + &mck_characteristics, &mck_lock); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_master_div(regmap, "masterck_div", + "masterck_pres", + &at91sam9x5_master_layout, + &mck_characteristics, &mck_lock, + CLK_SET_RATE_GATE); + if (IS_ERR(hw)) + goto err_free; + + sama5d3_pmc->chws[PMC_MCK] = hw; + + parent_names[0] = "plladivck"; + parent_names[1] = "utmick"; + hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2); + if (IS_ERR(hw)) + goto err_free; + + hw = at91sam9x5_clk_register_smd(regmap, "smdclk", parent_names, 2); + if (IS_ERR(hw)) + goto err_free; + + parent_names[0] = slck_name; + parent_names[1] = "mainck"; + parent_names[2] = "plladivck"; + parent_names[3] = "utmick"; + parent_names[4] = "masterck_div"; + for (i = 0; i < 3; i++) { + char name[6]; + + snprintf(name, sizeof(name), "prog%d", i); + + hw = at91_clk_register_programmable(regmap, name, + parent_names, 5, i, + &at91sam9x5_programmable_layout, + NULL); + if (IS_ERR(hw)) + goto err_free; + + sama5d3_pmc->pchws[i] = hw; + } + + for (i = 0; i < ARRAY_SIZE(sama5d3_systemck); i++) { + hw = at91_clk_register_system(regmap, sama5d3_systemck[i].n, + sama5d3_systemck[i].p, + sama5d3_systemck[i].id, + sama5d3_systemck[i].flags); + if (IS_ERR(hw)) + goto err_free; + + sama5d3_pmc->shws[sama5d3_systemck[i].id] = hw; + } + + for (i = 0; i < ARRAY_SIZE(sama5d3_periphck); i++) { + hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, + &sama5d3_pcr_layout, + sama5d3_periphck[i].n, + "masterck_div", + sama5d3_periphck[i].id, + &sama5d3_periphck[i].r, + INT_MIN, + sama5d3_periphck[i].flags); + if (IS_ERR(hw)) + goto err_free; + + sama5d3_pmc->phws[sama5d3_periphck[i].id] = hw; + } + + of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama5d3_pmc); + + return; + +err_free: + kfree(sama5d3_pmc); +} +/* + * The TCB is used as the clocksource so its clock is needed early. This means + * this can't be a platform driver. + */ +CLK_OF_DECLARE(sama5d3_pmc, "atmel,sama5d3-pmc", sama5d3_pmc_setup); diff --git a/drivers/clk/at91/sama5d4.c b/drivers/clk/at91/sama5d4.c index 77ccd77404..8fbd810883 100644 --- a/drivers/clk/at91/sama5d4.c +++ b/drivers/clk/at91/sama5d4.c @@ -1,18 +1,15 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include <driver.h> -#include <regmap.h> -#include <stdio.h> +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/clk-provider.h> #include <mfd/syscon.h> - -#include <linux/clk.h> #include <linux/slab.h> -#include <linux/types.h> +#include <stdio.h> #include <dt-bindings/clock/at91.h> #include "pmc.h" +static DEFINE_SPINLOCK(mck_lock); + static const struct clk_master_characteristics mck_characteristics = { .output = { .min = 125000000, .max = 200000000 }, .divisors = { 1, 2, 4, 3 }, @@ -43,16 +40,21 @@ static const struct clk_pcr_layout sama5d4_pcr_layout = { static const struct { char *n; char *p; + unsigned long flags; u8 id; } sama5d4_systemck[] = { - { .n = "ddrck", .p = "masterck", .id = 2 }, - { .n = "lcdck", .p = "masterck", .id = 3 }, - { .n = "smdck", .p = "smdclk", .id = 4 }, - { .n = "uhpck", .p = "usbck", .id = 6 }, - { .n = "udpck", .p = "usbck", .id = 7 }, - { .n = "pck0", .p = "prog0", .id = 8 }, - { .n = "pck1", .p = "prog1", .id = 9 }, - { .n = "pck2", .p = "prog2", .id = 10 }, + /* + * ddrck feeds DDR controller and is enabled by bootloader thus we need + * to keep it enabled in case there is no Linux consumer for it. + */ + { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL }, + { .n = "lcdck", .p = "masterck_div", .id = 3 }, + { .n = "smdck", .p = "smdclk", .id = 4 }, + { .n = "uhpck", .p = "usbck", .id = 6 }, + { .n = "udpck", .p = "usbck", .id = 7 }, + { .n = "pck0", .p = "prog0", .id = 8 }, + { .n = "pck1", .p = "prog1", .id = 9 }, + { .n = "pck2", .p = "prog2", .id = 10 }, }; static const struct { @@ -107,12 +109,17 @@ static const struct { static const struct { char *n; + unsigned long flags; u8 id; } sama5d4_periphck[] = { { .n = "dma0_clk", .id = 8 }, { .n = "cpkcc_clk", .id = 10 }, { .n = "aesb_clk", .id = 13 }, - { .n = "mpddr_clk", .id = 16 }, + /* + * mpddr_clk feeds DDR controller and is enabled by bootloader thus we + * need to keep it enabled in case there is no Linux consumer for it. + */ + { .n = "mpddr_clk", .id = 16, .flags = CLK_IS_CRITICAL }, { .n = "matrix0_clk", .id = 18 }, { .n = "vdec_clk", .id = 19 }, { .n = "dma1_clk", .id = 50 }, @@ -127,7 +134,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np) struct pmc_data *sama5d4_pmc; const char *parent_names[5]; struct regmap *regmap; - struct clk *hw; + struct clk_hw *hw; int i; bool bypass; @@ -146,9 +153,9 @@ static void __init sama5d4_pmc_setup(struct device_node *np) if (IS_ERR(regmap)) return; - sama5d4_pmc = pmc_data_allocate(PMC_MCK2 + 1, + sama5d4_pmc = pmc_data_allocate(PMC_PLLACK + 1, nck(sama5d4_systemck), - nck(sama5d4_periph32ck), 0); + nck(sama5d4_periph32ck), 0, 3); if (!sama5d4_pmc) return; @@ -179,6 +186,8 @@ static void __init sama5d4_pmc_setup(struct device_node *np) if (IS_ERR(hw)) goto err_free; + sama5d4_pmc->chws[PMC_PLLACK] = hw; + hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck"); if (IS_ERR(hw)) goto err_free; @@ -189,15 +198,24 @@ static void __init sama5d4_pmc_setup(struct device_node *np) parent_names[1] = "mainck"; parent_names[2] = "plladivck"; parent_names[3] = "utmick"; - hw = at91_clk_register_master(regmap, "masterck", 4, parent_names, - &at91sam9x5_master_layout, - &mck_characteristics); + hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4, + parent_names, + &at91sam9x5_master_layout, + &mck_characteristics, &mck_lock); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_master_div(regmap, "masterck_div", + "masterck_pres", + &at91sam9x5_master_layout, + &mck_characteristics, &mck_lock, + CLK_SET_RATE_GATE); if (IS_ERR(hw)) goto err_free; sama5d4_pmc->chws[PMC_MCK] = hw; - hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck"); + hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck_div"); if (IS_ERR(hw)) goto err_free; @@ -219,23 +237,27 @@ static void __init sama5d4_pmc_setup(struct device_node *np) parent_names[1] = "mainck"; parent_names[2] = "plladivck"; parent_names[3] = "utmick"; - parent_names[4] = "masterck"; + parent_names[4] = "masterck_div"; for (i = 0; i < 3; i++) { - char *name; + char name[6]; - name = xasprintf("prog%d", i); + snprintf(name, sizeof(name), "prog%d", i); hw = at91_clk_register_programmable(regmap, name, parent_names, 5, i, - &at91sam9x5_programmable_layout); + &at91sam9x5_programmable_layout, + NULL); if (IS_ERR(hw)) goto err_free; + + sama5d4_pmc->pchws[i] = hw; } for (i = 0; i < ARRAY_SIZE(sama5d4_systemck); i++) { hw = at91_clk_register_system(regmap, sama5d4_systemck[i].n, sama5d4_systemck[i].p, - sama5d4_systemck[i].id); + sama5d4_systemck[i].id, + sama5d4_systemck[i].flags); if (IS_ERR(hw)) goto err_free; @@ -243,12 +265,13 @@ static void __init sama5d4_pmc_setup(struct device_node *np) } for (i = 0; i < ARRAY_SIZE(sama5d4_periphck); i++) { - hw = at91_clk_register_sam9x5_peripheral(regmap, + hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, &sama5d4_pcr_layout, sama5d4_periphck[i].n, - "masterck", + "masterck_div", sama5d4_periphck[i].id, - &range); + &range, INT_MIN, + sama5d4_periphck[i].flags); if (IS_ERR(hw)) goto err_free; @@ -256,23 +279,24 @@ static void __init sama5d4_pmc_setup(struct device_node *np) } for (i = 0; i < ARRAY_SIZE(sama5d4_periph32ck); i++) { - hw = at91_clk_register_sam9x5_peripheral(regmap, + hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, &sama5d4_pcr_layout, sama5d4_periph32ck[i].n, "h32mxck", sama5d4_periph32ck[i].id, - &range); + &range, INT_MIN, 0); if (IS_ERR(hw)) goto err_free; sama5d4_pmc->phws[sama5d4_periph32ck[i].id] = hw; } - of_clk_add_provider(np, of_clk_hw_pmc_get, sama5d4_pmc); + of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama5d4_pmc); return; err_free: - pmc_data_free(sama5d4_pmc); + kfree(sama5d4_pmc); } -CLK_OF_DECLARE_DRIVER(sama5d4_pmc, "atmel,sama5d4-pmc", sama5d4_pmc_setup); + +CLK_OF_DECLARE(sama5d4_pmc, "atmel,sama5d4-pmc", sama5d4_pmc_setup); diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c new file mode 100644 index 0000000000..7d33367176 --- /dev/null +++ b/drivers/clk/at91/sama7g5.c @@ -0,0 +1,1133 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SAMA7G5 PMC code. + * + * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries + * + * Author: Claudiu Beznea <claudiu.beznea@microchip.com> + * + */ +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <mfd/syscon.h> +#include <linux/slab.h> +#include <stdio.h> + +#include <dt-bindings/clock/at91.h> + +#include "pmc.h" + +#define SAMA7G5_INIT_TABLE(_table, _count) \ + do { \ + u8 _i; \ + for (_i = 0; _i < (_count); _i++) \ + (_table)[_i] = _i; \ + } while (0) + +#define SAMA7G5_FILL_TABLE(_to, _from, _count) \ + do { \ + u8 _i; \ + for (_i = 0; _i < (_count); _i++) { \ + (_to)[_i] = (_from)[_i]; \ + } \ + } while (0) + +static DEFINE_SPINLOCK(pmc_pll_lock); +static DEFINE_SPINLOCK(pmc_mck0_lock); +static DEFINE_SPINLOCK(pmc_mckX_lock); + +/* + * PLL clocks identifiers + * @PLL_ID_CPU: CPU PLL identifier + * @PLL_ID_SYS: System PLL identifier + * @PLL_ID_DDR: DDR PLL identifier + * @PLL_ID_IMG: Image subsystem PLL identifier + * @PLL_ID_BAUD: Baud PLL identifier + * @PLL_ID_AUDIO: Audio PLL identifier + * @PLL_ID_ETH: Ethernet PLL identifier + */ +enum pll_ids { + PLL_ID_CPU, + PLL_ID_SYS, + PLL_ID_DDR, + PLL_ID_IMG, + PLL_ID_BAUD, + PLL_ID_AUDIO, + PLL_ID_ETH, + PLL_ID_MAX, +}; + +/* + * PLL type identifiers + * @PLL_TYPE_FRAC: fractional PLL identifier + * @PLL_TYPE_DIV: divider PLL identifier + */ +enum pll_type { + PLL_TYPE_FRAC, + PLL_TYPE_DIV, +}; + +/* Layout for fractional PLLs. */ +static const struct clk_pll_layout pll_layout_frac = { + .mul_mask = GENMASK(31, 24), + .frac_mask = GENMASK(21, 0), + .mul_shift = 24, + .frac_shift = 0, +}; + +/* Layout for DIVPMC dividers. */ +static const struct clk_pll_layout pll_layout_divpmc = { + .div_mask = GENMASK(7, 0), + .endiv_mask = BIT(29), + .div_shift = 0, + .endiv_shift = 29, +}; + +/* Layout for DIVIO dividers. */ +static const struct clk_pll_layout pll_layout_divio = { + .div_mask = GENMASK(19, 12), + .endiv_mask = BIT(30), + .div_shift = 12, + .endiv_shift = 30, +}; + +/* + * CPU PLL output range. + * Notice: The upper limit has been setup to 1000000002 due to hardware + * block which cannot output exactly 1GHz. + */ +static const struct clk_range cpu_pll_outputs[] = { + { .min = 2343750, .max = 1000000002 }, +}; + +/* PLL output range. */ +static const struct clk_range pll_outputs[] = { + { .min = 2343750, .max = 1200000000 }, +}; + +/* CPU PLL characteristics. */ +static const struct clk_pll_characteristics cpu_pll_characteristics = { + .input = { .min = 12000000, .max = 50000000 }, + .num_output = ARRAY_SIZE(cpu_pll_outputs), + .output = cpu_pll_outputs, +}; + +/* PLL characteristics. */ +static const struct clk_pll_characteristics pll_characteristics = { + .input = { .min = 12000000, .max = 50000000 }, + .num_output = ARRAY_SIZE(pll_outputs), + .output = pll_outputs, +}; + +/* + * PLL clocks description + * @n: clock name + * @p: clock parent + * @l: clock layout + * @c: clock characteristics + * @t: clock type + * @f: clock flags + * @eid: export index in sama7g5->chws[] array + */ +static const struct { + const char *n; + const char *p; + const struct clk_pll_layout *l; + const struct clk_pll_characteristics *c; + unsigned long f; + u8 t; + u8 eid; +} sama7g5_plls[][PLL_ID_MAX] = { + [PLL_ID_CPU] = { + { .n = "cpupll_fracck", + .p = "mainck", + .l = &pll_layout_frac, + .c = &cpu_pll_characteristics, + .t = PLL_TYPE_FRAC, + /* + * This feeds cpupll_divpmcck which feeds CPU. It should + * not be disabled. + */ + .f = CLK_IS_CRITICAL, }, + + { .n = "cpupll_divpmcck", + .p = "cpupll_fracck", + .l = &pll_layout_divpmc, + .c = &cpu_pll_characteristics, + .t = PLL_TYPE_DIV, + /* This feeds CPU. It should not be disabled. */ + .f = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, + .eid = PMC_CPUPLL, }, + }, + + [PLL_ID_SYS] = { + { .n = "syspll_fracck", + .p = "mainck", + .l = &pll_layout_frac, + .c = &pll_characteristics, + .t = PLL_TYPE_FRAC, + /* + * This feeds syspll_divpmcck which may feed critical parts + * of the systems like timers. Therefore it should not be + * disabled. + */ + .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, }, + + { .n = "syspll_divpmcck", + .p = "syspll_fracck", + .l = &pll_layout_divpmc, + .c = &pll_characteristics, + .t = PLL_TYPE_DIV, + /* + * This may feed critical parts of the systems like timers. + * Therefore it should not be disabled. + */ + .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, + .eid = PMC_SYSPLL, }, + }, + + [PLL_ID_DDR] = { + { .n = "ddrpll_fracck", + .p = "mainck", + .l = &pll_layout_frac, + .c = &pll_characteristics, + .t = PLL_TYPE_FRAC, + /* + * This feeds ddrpll_divpmcck which feeds DDR. It should not + * be disabled. + */ + .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, }, + + { .n = "ddrpll_divpmcck", + .p = "ddrpll_fracck", + .l = &pll_layout_divpmc, + .c = &pll_characteristics, + .t = PLL_TYPE_DIV, + /* This feeds DDR. It should not be disabled. */ + .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, }, + }, + + [PLL_ID_IMG] = { + { .n = "imgpll_fracck", + .p = "mainck", + .l = &pll_layout_frac, + .c = &pll_characteristics, + .t = PLL_TYPE_FRAC, + .f = CLK_SET_RATE_GATE, }, + + { .n = "imgpll_divpmcck", + .p = "imgpll_fracck", + .l = &pll_layout_divpmc, + .c = &pll_characteristics, + .t = PLL_TYPE_DIV, + .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT, }, + }, + + [PLL_ID_BAUD] = { + { .n = "baudpll_fracck", + .p = "mainck", + .l = &pll_layout_frac, + .c = &pll_characteristics, + .t = PLL_TYPE_FRAC, + .f = CLK_SET_RATE_GATE, }, + + { .n = "baudpll_divpmcck", + .p = "baudpll_fracck", + .l = &pll_layout_divpmc, + .c = &pll_characteristics, + .t = PLL_TYPE_DIV, + .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT, }, + }, + + [PLL_ID_AUDIO] = { + { .n = "audiopll_fracck", + .p = "main_xtal", + .l = &pll_layout_frac, + .c = &pll_characteristics, + .t = PLL_TYPE_FRAC, + .f = CLK_SET_RATE_GATE, }, + + { .n = "audiopll_divpmcck", + .p = "audiopll_fracck", + .l = &pll_layout_divpmc, + .c = &pll_characteristics, + .t = PLL_TYPE_DIV, + .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT, + .eid = PMC_AUDIOPMCPLL, }, + + { .n = "audiopll_diviock", + .p = "audiopll_fracck", + .l = &pll_layout_divio, + .c = &pll_characteristics, + .t = PLL_TYPE_DIV, + .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT, + .eid = PMC_AUDIOIOPLL, }, + }, + + [PLL_ID_ETH] = { + { .n = "ethpll_fracck", + .p = "main_xtal", + .l = &pll_layout_frac, + .c = &pll_characteristics, + .t = PLL_TYPE_FRAC, + .f = CLK_SET_RATE_GATE, }, + + { .n = "ethpll_divpmcck", + .p = "ethpll_fracck", + .l = &pll_layout_divpmc, + .c = &pll_characteristics, + .t = PLL_TYPE_DIV, + .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT, }, + }, +}; + +/* + * Master clock (MCK[1..4]) description + * @n: clock name + * @ep: extra parents names array + * @ep_chg_chg_id: index in parents array that specifies the changeable + * parent + * @ep_count: extra parents count + * @ep_mux_table: mux table for extra parents + * @id: clock id + * @eid: export index in sama7g5->chws[] array + * @c: true if clock is critical and cannot be disabled + */ +static const struct { + const char *n; + const char *ep[4]; + int ep_chg_id; + u8 ep_count; + u8 ep_mux_table[4]; + u8 id; + u8 eid; + u8 c; +} sama7g5_mckx[] = { + { .n = "mck1", + .id = 1, + .ep = { "syspll_divpmcck", }, + .ep_mux_table = { 5, }, + .ep_count = 1, + .ep_chg_id = INT_MIN, + .eid = PMC_MCK1, + .c = 1, }, + + { .n = "mck2", + .id = 2, + .ep = { "ddrpll_divpmcck", }, + .ep_mux_table = { 6, }, + .ep_count = 1, + .ep_chg_id = INT_MIN, + .c = 1, }, + + { .n = "mck3", + .id = 3, + .ep = { "syspll_divpmcck", "ddrpll_divpmcck", "imgpll_divpmcck", }, + .ep_mux_table = { 5, 6, 7, }, + .ep_count = 3, + .ep_chg_id = 5, }, + + { .n = "mck4", + .id = 4, + .ep = { "syspll_divpmcck", }, + .ep_mux_table = { 5, }, + .ep_count = 1, + .ep_chg_id = INT_MIN, + .c = 1, }, +}; + +/* + * System clock description + * @n: clock name + * @p: clock parent name + * @id: clock id + */ +static const struct { + const char *n; + const char *p; + u8 id; +} sama7g5_systemck[] = { + { .n = "pck0", .p = "prog0", .id = 8, }, + { .n = "pck1", .p = "prog1", .id = 9, }, + { .n = "pck2", .p = "prog2", .id = 10, }, + { .n = "pck3", .p = "prog3", .id = 11, }, + { .n = "pck4", .p = "prog4", .id = 12, }, + { .n = "pck5", .p = "prog5", .id = 13, }, + { .n = "pck6", .p = "prog6", .id = 14, }, + { .n = "pck7", .p = "prog7", .id = 15, }, +}; + +/* Mux table for programmable clocks. */ +static u32 sama7g5_prog_mux_table[] = { 0, 1, 2, 5, 6, 7, 8, 9, 10, }; + +/* + * Peripheral clock description + * @n: clock name + * @p: clock parent name + * @r: clock range values + * @id: clock id + * @chgp: index in parent array of the changeable parent + */ +static const struct { + const char *n; + const char *p; + struct clk_range r; + u8 chgp; + u8 id; +} sama7g5_periphck[] = { + { .n = "pioA_clk", .p = "mck0", .id = 11, }, + { .n = "securam_clk", .p = "mck0", .id = 18, }, + { .n = "sfr_clk", .p = "mck1", .id = 19, }, + { .n = "hsmc_clk", .p = "mck1", .id = 21, }, + { .n = "xdmac0_clk", .p = "mck1", .id = 22, }, + { .n = "xdmac1_clk", .p = "mck1", .id = 23, }, + { .n = "xdmac2_clk", .p = "mck1", .id = 24, }, + { .n = "acc_clk", .p = "mck1", .id = 25, }, + { .n = "aes_clk", .p = "mck1", .id = 27, }, + { .n = "tzaesbasc_clk", .p = "mck1", .id = 28, }, + { .n = "asrc_clk", .p = "mck1", .id = 30, .r = { .max = 200000000, }, }, + { .n = "cpkcc_clk", .p = "mck0", .id = 32, }, + { .n = "csi_clk", .p = "mck3", .id = 33, .r = { .max = 266000000, }, .chgp = 1, }, + { .n = "csi2dc_clk", .p = "mck3", .id = 34, .r = { .max = 266000000, }, .chgp = 1, }, + { .n = "eic_clk", .p = "mck1", .id = 37, }, + { .n = "flex0_clk", .p = "mck1", .id = 38, }, + { .n = "flex1_clk", .p = "mck1", .id = 39, }, + { .n = "flex2_clk", .p = "mck1", .id = 40, }, + { .n = "flex3_clk", .p = "mck1", .id = 41, }, + { .n = "flex4_clk", .p = "mck1", .id = 42, }, + { .n = "flex5_clk", .p = "mck1", .id = 43, }, + { .n = "flex6_clk", .p = "mck1", .id = 44, }, + { .n = "flex7_clk", .p = "mck1", .id = 45, }, + { .n = "flex8_clk", .p = "mck1", .id = 46, }, + { .n = "flex9_clk", .p = "mck1", .id = 47, }, + { .n = "flex10_clk", .p = "mck1", .id = 48, }, + { .n = "flex11_clk", .p = "mck1", .id = 49, }, + { .n = "gmac0_clk", .p = "mck1", .id = 51, }, + { .n = "gmac1_clk", .p = "mck1", .id = 52, }, + { .n = "icm_clk", .p = "mck1", .id = 55, }, + { .n = "isc_clk", .p = "mck3", .id = 56, .r = { .max = 266000000, }, .chgp = 1, }, + { .n = "i2smcc0_clk", .p = "mck1", .id = 57, .r = { .max = 200000000, }, }, + { .n = "i2smcc1_clk", .p = "mck1", .id = 58, .r = { .max = 200000000, }, }, + { .n = "matrix_clk", .p = "mck1", .id = 60, }, + { .n = "mcan0_clk", .p = "mck1", .id = 61, .r = { .max = 200000000, }, }, + { .n = "mcan1_clk", .p = "mck1", .id = 62, .r = { .max = 200000000, }, }, + { .n = "mcan2_clk", .p = "mck1", .id = 63, .r = { .max = 200000000, }, }, + { .n = "mcan3_clk", .p = "mck1", .id = 64, .r = { .max = 200000000, }, }, + { .n = "mcan4_clk", .p = "mck1", .id = 65, .r = { .max = 200000000, }, }, + { .n = "mcan5_clk", .p = "mck1", .id = 66, .r = { .max = 200000000, }, }, + { .n = "pdmc0_clk", .p = "mck1", .id = 68, .r = { .max = 200000000, }, }, + { .n = "pdmc1_clk", .p = "mck1", .id = 69, .r = { .max = 200000000, }, }, + { .n = "pit64b0_clk", .p = "mck1", .id = 70, }, + { .n = "pit64b1_clk", .p = "mck1", .id = 71, }, + { .n = "pit64b2_clk", .p = "mck1", .id = 72, }, + { .n = "pit64b3_clk", .p = "mck1", .id = 73, }, + { .n = "pit64b4_clk", .p = "mck1", .id = 74, }, + { .n = "pit64b5_clk", .p = "mck1", .id = 75, }, + { .n = "pwm_clk", .p = "mck1", .id = 77, }, + { .n = "qspi0_clk", .p = "mck1", .id = 78, }, + { .n = "qspi1_clk", .p = "mck1", .id = 79, }, + { .n = "sdmmc0_clk", .p = "mck1", .id = 80, }, + { .n = "sdmmc1_clk", .p = "mck1", .id = 81, }, + { .n = "sdmmc2_clk", .p = "mck1", .id = 82, }, + { .n = "sha_clk", .p = "mck1", .id = 83, }, + { .n = "spdifrx_clk", .p = "mck1", .id = 84, .r = { .max = 200000000, }, }, + { .n = "spdiftx_clk", .p = "mck1", .id = 85, .r = { .max = 200000000, }, }, + { .n = "ssc0_clk", .p = "mck1", .id = 86, .r = { .max = 200000000, }, }, + { .n = "ssc1_clk", .p = "mck1", .id = 87, .r = { .max = 200000000, }, }, + { .n = "tcb0_ch0_clk", .p = "mck1", .id = 88, .r = { .max = 200000000, }, }, + { .n = "tcb0_ch1_clk", .p = "mck1", .id = 89, .r = { .max = 200000000, }, }, + { .n = "tcb0_ch2_clk", .p = "mck1", .id = 90, .r = { .max = 200000000, }, }, + { .n = "tcb1_ch0_clk", .p = "mck1", .id = 91, .r = { .max = 200000000, }, }, + { .n = "tcb1_ch1_clk", .p = "mck1", .id = 92, .r = { .max = 200000000, }, }, + { .n = "tcb1_ch2_clk", .p = "mck1", .id = 93, .r = { .max = 200000000, }, }, + { .n = "tcpca_clk", .p = "mck1", .id = 94, }, + { .n = "tcpcb_clk", .p = "mck1", .id = 95, }, + { .n = "tdes_clk", .p = "mck1", .id = 96, }, + { .n = "trng_clk", .p = "mck1", .id = 97, }, + { .n = "udphsa_clk", .p = "mck1", .id = 104, }, + { .n = "udphsb_clk", .p = "mck1", .id = 105, }, + { .n = "uhphs_clk", .p = "mck1", .id = 106, }, +}; + +/* + * Generic clock description + * @n: clock name + * @pp: PLL parents + * @pp_mux_table: PLL parents mux table + * @r: clock output range + * @pp_chg_id: id in parent array of changeable PLL parent + * @pp_count: PLL parents count + * @id: clock id + */ +static const struct { + const char *n; + const char *pp[8]; + const char pp_mux_table[8]; + struct clk_range r; + int pp_chg_id; + u8 pp_count; + u8 id; +} sama7g5_gck[] = { + { .n = "adc_gclk", + .id = 26, + .r = { .max = 100000000, }, + .pp = { "syspll_divpmcck", "imgpll_divpmcck", "audiopll_divpmcck", }, + .pp_mux_table = { 5, 7, 9, }, + .pp_count = 3, + .pp_chg_id = INT_MIN, }, + + { .n = "asrc_gclk", + .id = 30, + .r = { .max = 200000000 }, + .pp = { "audiopll_divpmcck", }, + .pp_mux_table = { 9, }, + .pp_count = 1, + .pp_chg_id = 3, }, + + { .n = "csi_gclk", + .id = 33, + .r = { .max = 27000000 }, + .pp = { "ddrpll_divpmcck", "imgpll_divpmcck", }, + .pp_mux_table = { 6, 7, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "flex0_gclk", + .id = 38, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "flex1_gclk", + .id = 39, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "flex2_gclk", + .id = 40, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "flex3_gclk", + .id = 41, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "flex4_gclk", + .id = 42, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "flex5_gclk", + .id = 43, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "flex6_gclk", + .id = 44, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "flex7_gclk", + .id = 45, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "flex8_gclk", + .id = 46, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "flex9_gclk", + .id = 47, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "flex10_gclk", + .id = 48, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "flex11_gclk", + .id = 49, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "gmac0_gclk", + .id = 51, + .r = { .max = 125000000 }, + .pp = { "ethpll_divpmcck", }, + .pp_mux_table = { 10, }, + .pp_count = 1, + .pp_chg_id = 3, }, + + { .n = "gmac1_gclk", + .id = 52, + .r = { .max = 50000000 }, + .pp = { "ethpll_divpmcck", }, + .pp_mux_table = { 10, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, }, + + { .n = "gmac0_tsu_gclk", + .id = 53, + .r = { .max = 300000000 }, + .pp = { "audiopll_divpmcck", "ethpll_divpmcck", }, + .pp_mux_table = { 9, 10, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "gmac1_tsu_gclk", + .id = 54, + .r = { .max = 300000000 }, + .pp = { "audiopll_divpmcck", "ethpll_divpmcck", }, + .pp_mux_table = { 9, 10, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "i2smcc0_gclk", + .id = 57, + .r = { .max = 100000000 }, + .pp = { "syspll_divpmcck", "audiopll_divpmcck", }, + .pp_mux_table = { 5, 9, }, + .pp_count = 2, + .pp_chg_id = 4, }, + + { .n = "i2smcc1_gclk", + .id = 58, + .r = { .max = 100000000 }, + .pp = { "syspll_divpmcck", "audiopll_divpmcck", }, + .pp_mux_table = { 5, 9, }, + .pp_count = 2, + .pp_chg_id = 4, }, + + { .n = "mcan0_gclk", + .id = 61, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "mcan1_gclk", + .id = 62, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "mcan2_gclk", + .id = 63, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "mcan3_gclk", + .id = 64, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "mcan4_gclk", + .id = 65, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "mcan5_gclk", + .id = 66, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "pdmc0_gclk", + .id = 68, + .r = { .max = 50000000 }, + .pp = { "syspll_divpmcck", "audiopll_divpmcck", }, + .pp_mux_table = { 5, 9, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "pdmc1_gclk", + .id = 69, + .r = { .max = 50000000, }, + .pp = { "syspll_divpmcck", "audiopll_divpmcck", }, + .pp_mux_table = { 5, 9, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "pit64b0_gclk", + .id = 70, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .pp_mux_table = { 5, 7, 8, 9, 10, }, + .pp_count = 5, + .pp_chg_id = INT_MIN, }, + + { .n = "pit64b1_gclk", + .id = 71, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .pp_mux_table = { 5, 7, 8, 9, 10, }, + .pp_count = 5, + .pp_chg_id = INT_MIN, }, + + { .n = "pit64b2_gclk", + .id = 72, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .pp_mux_table = { 5, 7, 8, 9, 10, }, + .pp_count = 5, + .pp_chg_id = INT_MIN, }, + + { .n = "pit64b3_gclk", + .id = 73, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .pp_mux_table = { 5, 7, 8, 9, 10, }, + .pp_count = 5, + .pp_chg_id = INT_MIN, }, + + { .n = "pit64b4_gclk", + .id = 74, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .pp_mux_table = { 5, 7, 8, 9, 10, }, + .pp_count = 5, + .pp_chg_id = INT_MIN, }, + + { .n = "pit64b5_gclk", + .id = 75, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .pp_mux_table = { 5, 7, 8, 9, 10, }, + .pp_count = 5, + .pp_chg_id = INT_MIN, }, + + { .n = "qspi0_gclk", + .id = 78, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "qspi1_gclk", + .id = 79, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, }, + + { .n = "sdmmc0_gclk", + .id = 80, + .r = { .max = 208000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = 4, }, + + { .n = "sdmmc1_gclk", + .id = 81, + .r = { .max = 208000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = 4, }, + + { .n = "sdmmc2_gclk", + .id = 82, + .r = { .max = 208000000 }, + .pp = { "syspll_divpmcck", "baudpll_divpmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = 4, }, + + { .n = "spdifrx_gclk", + .id = 84, + .r = { .max = 150000000 }, + .pp = { "syspll_divpmcck", "audiopll_divpmcck", }, + .pp_mux_table = { 5, 9, }, + .pp_count = 2, + .pp_chg_id = 4, }, + + { .n = "spdiftx_gclk", + .id = 85, + .r = { .max = 25000000 }, + .pp = { "syspll_divpmcck", "audiopll_divpmcck", }, + .pp_mux_table = { 5, 9, }, + .pp_count = 2, + .pp_chg_id = 4, }, + + { .n = "tcb0_ch0_gclk", + .id = 88, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .pp_mux_table = { 5, 7, 8, 9, 10, }, + .pp_count = 5, + .pp_chg_id = INT_MIN, }, + + { .n = "tcb1_ch0_gclk", + .id = 91, + .r = { .max = 200000000 }, + .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck", + "audiopll_divpmcck", "ethpll_divpmcck", }, + .pp_mux_table = { 5, 7, 8, 9, 10, }, + .pp_count = 5, + .pp_chg_id = INT_MIN, }, + + { .n = "tcpca_gclk", + .id = 94, + .r = { .max = 32768, }, + .pp_chg_id = INT_MIN, }, + + { .n = "tcpcb_gclk", + .id = 95, + .r = { .max = 32768, }, + .pp_chg_id = INT_MIN, }, +}; + +/* MCK0 characteristics. */ +static const struct clk_master_characteristics mck0_characteristics = { + .output = { .min = 32768, .max = 200000000 }, + .divisors = { 1, 2, 4, 3, 5 }, + .have_div3_pres = 1, +}; + +/* MCK0 layout. */ +static const struct clk_master_layout mck0_layout = { + .mask = 0x773, + .pres_shift = 4, + .offset = 0x28, +}; + +/* Programmable clock layout. */ +static const struct clk_programmable_layout programmable_layout = { + .pres_mask = 0xff, + .pres_shift = 8, + .css_mask = 0x1f, + .have_slck_mck = 0, + .is_pres_direct = 1, +}; + +/* Peripheral clock layout. */ +static const struct clk_pcr_layout sama7g5_pcr_layout = { + .offset = 0x88, + .cmd = BIT(31), + .gckcss_mask = GENMASK(12, 8), + .pid_mask = GENMASK(6, 0), +}; + +static void __init sama7g5_pmc_setup(struct device_node *np) +{ + const char *td_slck_name, *md_slck_name, *mainxtal_name; + struct pmc_data *sama7g5_pmc; + const char *parent_names[10]; + void **alloc_mem = NULL; + int alloc_mem_size = 0; + struct regmap *regmap; + struct clk_hw *hw; + bool bypass; + int i, j; + + i = of_property_match_string(np, "clock-names", "td_slck"); + if (i < 0) + return; + + td_slck_name = of_clk_get_parent_name(np, i); + + i = of_property_match_string(np, "clock-names", "md_slck"); + if (i < 0) + return; + + md_slck_name = of_clk_get_parent_name(np, i); + + i = of_property_match_string(np, "clock-names", "main_xtal"); + if (i < 0) + return; + + mainxtal_name = of_clk_get_parent_name(np, i); + + regmap = device_node_to_regmap(np); + if (IS_ERR(regmap)) + return; + + sama7g5_pmc = pmc_data_allocate(PMC_MCK1 + 1, + nck(sama7g5_systemck), + nck(sama7g5_periphck), + nck(sama7g5_gck), 8); + if (!sama7g5_pmc) + return; + + alloc_mem = kmalloc(sizeof(void *) * + (ARRAY_SIZE(sama7g5_mckx) + ARRAY_SIZE(sama7g5_gck)), + GFP_KERNEL); + if (!alloc_mem) + goto err_free; + + hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000, + 50000000); + if (IS_ERR(hw)) + goto err_free; + + bypass = of_property_read_bool(np, "atmel,osc-bypass"); + + hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, + bypass); + if (IS_ERR(hw)) + goto err_free; + + parent_names[0] = "main_rc_osc"; + parent_names[1] = "main_osc"; + hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2); + if (IS_ERR(hw)) + goto err_free; + + sama7g5_pmc->chws[PMC_MAIN] = hw; + + for (i = 0; i < PLL_ID_MAX; i++) { + for (j = 0; j < 3; j++) { + struct clk_hw *parent_hw; + + if (!sama7g5_plls[i][j].n) + continue; + + switch (sama7g5_plls[i][j].t) { + case PLL_TYPE_FRAC: + if (!strcmp(sama7g5_plls[i][j].p, "mainck")) + parent_hw = sama7g5_pmc->chws[PMC_MAIN]; + else + parent_hw = __clk_get_hw(of_clk_get_by_name(np, + sama7g5_plls[i][j].p)); + + hw = sam9x60_clk_register_frac_pll(regmap, + &pmc_pll_lock, sama7g5_plls[i][j].n, + sama7g5_plls[i][j].p, parent_hw, i, + sama7g5_plls[i][j].c, + sama7g5_plls[i][j].l, + sama7g5_plls[i][j].f); + break; + + case PLL_TYPE_DIV: + hw = sam9x60_clk_register_div_pll(regmap, + &pmc_pll_lock, sama7g5_plls[i][j].n, + sama7g5_plls[i][j].p, i, + sama7g5_plls[i][j].c, + sama7g5_plls[i][j].l, + sama7g5_plls[i][j].f); + break; + + default: + continue; + } + + if (IS_ERR(hw)) + goto err_free; + + if (sama7g5_plls[i][j].eid) + sama7g5_pmc->chws[sama7g5_plls[i][j].eid] = hw; + } + } + + parent_names[0] = "cpupll_divpmcck"; + hw = at91_clk_register_master_div(regmap, "mck0", "cpupll_divpmcck", + &mck0_layout, &mck0_characteristics, + &pmc_mck0_lock, CLK_GET_RATE_NOCACHE); + if (IS_ERR(hw)) + goto err_free; + + sama7g5_pmc->chws[PMC_MCK] = hw; + + parent_names[0] = md_slck_name; + parent_names[1] = td_slck_name; + parent_names[2] = "mainck"; + for (i = 0; i < ARRAY_SIZE(sama7g5_mckx); i++) { + u8 num_parents = 3 + sama7g5_mckx[i].ep_count; + u32 *mux_table; + + mux_table = kmalloc_array(num_parents, sizeof(*mux_table), + GFP_KERNEL); + if (!mux_table) + goto err_free; + + SAMA7G5_INIT_TABLE(mux_table, 3); + SAMA7G5_FILL_TABLE(&mux_table[3], sama7g5_mckx[i].ep_mux_table, + sama7g5_mckx[i].ep_count); + SAMA7G5_FILL_TABLE(&parent_names[3], sama7g5_mckx[i].ep, + sama7g5_mckx[i].ep_count); + + hw = at91_clk_sama7g5_register_master(regmap, sama7g5_mckx[i].n, + num_parents, parent_names, mux_table, + &pmc_mckX_lock, sama7g5_mckx[i].id, + sama7g5_mckx[i].c, + sama7g5_mckx[i].ep_chg_id); + if (IS_ERR(hw)) + goto err_free; + + alloc_mem[alloc_mem_size++] = mux_table; + + if (sama7g5_mckx[i].eid) + sama7g5_pmc->chws[sama7g5_mckx[i].eid] = hw; + } + + hw = at91_clk_sama7g5_register_utmi(regmap, "utmick", "main_xtal"); + if (IS_ERR(hw)) + goto err_free; + + sama7g5_pmc->chws[PMC_UTMI] = hw; + + parent_names[0] = md_slck_name; + parent_names[1] = td_slck_name; + parent_names[2] = "mainck"; + parent_names[3] = "syspll_divpmcck"; + parent_names[4] = "ddrpll_divpmcck"; + parent_names[5] = "imgpll_divpmcck"; + parent_names[6] = "baudpll_divpmcck"; + parent_names[7] = "audiopll_divpmcck"; + parent_names[8] = "ethpll_divpmcck"; + for (i = 0; i < 8; i++) { + char name[6]; + + snprintf(name, sizeof(name), "prog%d", i); + + hw = at91_clk_register_programmable(regmap, name, parent_names, + 9, i, + &programmable_layout, + sama7g5_prog_mux_table); + if (IS_ERR(hw)) + goto err_free; + + sama7g5_pmc->pchws[i] = hw; + } + + for (i = 0; i < ARRAY_SIZE(sama7g5_systemck); i++) { + hw = at91_clk_register_system(regmap, sama7g5_systemck[i].n, + sama7g5_systemck[i].p, + sama7g5_systemck[i].id, 0); + if (IS_ERR(hw)) + goto err_free; + + sama7g5_pmc->shws[sama7g5_systemck[i].id] = hw; + } + + for (i = 0; i < ARRAY_SIZE(sama7g5_periphck); i++) { + hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, + &sama7g5_pcr_layout, + sama7g5_periphck[i].n, + sama7g5_periphck[i].p, + sama7g5_periphck[i].id, + &sama7g5_periphck[i].r, + sama7g5_periphck[i].chgp ? 0 : + INT_MIN, 0); + if (IS_ERR(hw)) + goto err_free; + + sama7g5_pmc->phws[sama7g5_periphck[i].id] = hw; + } + + parent_names[0] = md_slck_name; + parent_names[1] = td_slck_name; + parent_names[2] = "mainck"; + for (i = 0; i < ARRAY_SIZE(sama7g5_gck); i++) { + u8 num_parents = 3 + sama7g5_gck[i].pp_count; + u32 *mux_table; + + mux_table = kmalloc_array(num_parents, sizeof(*mux_table), + GFP_KERNEL); + if (!mux_table) + goto err_free; + + SAMA7G5_INIT_TABLE(mux_table, 3); + SAMA7G5_FILL_TABLE(&mux_table[3], sama7g5_gck[i].pp_mux_table, + sama7g5_gck[i].pp_count); + SAMA7G5_FILL_TABLE(&parent_names[3], sama7g5_gck[i].pp, + sama7g5_gck[i].pp_count); + + hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, + &sama7g5_pcr_layout, + sama7g5_gck[i].n, + parent_names, mux_table, + num_parents, + sama7g5_gck[i].id, + &sama7g5_gck[i].r, + sama7g5_gck[i].pp_chg_id); + if (IS_ERR(hw)) + goto err_free; + + sama7g5_pmc->ghws[sama7g5_gck[i].id] = hw; + alloc_mem[alloc_mem_size++] = mux_table; + } + + of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama7g5_pmc); + + return; + +err_free: + if (alloc_mem) { + for (i = 0; i < alloc_mem_size; i++) + kfree(alloc_mem[i]); + kfree(alloc_mem); + } + + kfree(sama7g5_pmc); +} + +/* Some clks are used for a clocksource */ +CLK_OF_DECLARE(sama7g5_pmc, "microchip,sama7g5-pmc", sama7g5_pmc_setup); diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c index 1a33a64421..1e03537cf1 100644 --- a/drivers/clk/at91/sckc.c +++ b/drivers/clk/at91/sckc.c @@ -5,18 +5,12 @@ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> */ -#include <common.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <clock.h> #include <of.h> #include <of_address.h> -#include <io.h> -#include <linux/list.h> -#include <linux/clk.h> -#include <linux/clk/at91_pmc.h> -#include <mfd/syscon.h> -#include <regmap.h> - - +#include <linux/io.h> #define SLOW_CLOCK_FREQ 32768 #define SLOWCK_SW_CYCLES 5 @@ -33,50 +27,47 @@ struct clk_slow_bits { }; struct clk_slow_osc { - struct clk clk; + struct clk_hw hw; void __iomem *sckcr; const struct clk_slow_bits *bits; unsigned long startup_usec; - const char *parent_name; }; -#define to_clk_slow_osc(clk) container_of(clk, struct clk_slow_osc, clk) +#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw) struct clk_sama5d4_slow_osc { - struct clk clk; + struct clk_hw hw; void __iomem *sckcr; const struct clk_slow_bits *bits; unsigned long startup_usec; bool prepared; - const char *parent_name; }; -#define to_clk_sama5d4_slow_osc(clk) container_of(clk, struct clk_sama5d4_slow_osc, clk) +#define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw) struct clk_slow_rc_osc { - struct clk clk; + struct clk_hw hw; void __iomem *sckcr; const struct clk_slow_bits *bits; unsigned long frequency; + unsigned long accuracy; unsigned long startup_usec; - const char *parent_name; }; -#define to_clk_slow_rc_osc(clk) container_of(clk, struct clk_slow_rc_osc, clk) +#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw) struct clk_sam9x5_slow { - struct clk clk; + struct clk_hw hw; void __iomem *sckcr; const struct clk_slow_bits *bits; u8 parent; - const char *parent_names[2]; }; -#define to_clk_sam9x5_slow(clk) container_of(clk, struct clk_sam9x5_slow, clk) +#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw) -static int clk_slow_osc_enable(struct clk *clk) +static int clk_slow_osc_prepare(struct clk_hw *hw) { - struct clk_slow_osc *osc = to_clk_slow_osc(clk); + struct clk_slow_osc *osc = to_clk_slow_osc(hw); void __iomem *sckcr = osc->sckcr; u32 tmp = readl(sckcr); @@ -90,9 +81,9 @@ static int clk_slow_osc_enable(struct clk *clk) return 0; } -static void clk_slow_osc_disable(struct clk *clk) +static void clk_slow_osc_unprepare(struct clk_hw *hw) { - struct clk_slow_osc *osc = to_clk_slow_osc(clk); + struct clk_slow_osc *osc = to_clk_slow_osc(hw); void __iomem *sckcr = osc->sckcr; u32 tmp = readl(sckcr); @@ -102,9 +93,9 @@ static void clk_slow_osc_disable(struct clk *clk) writel(tmp & ~osc->bits->cr_osc32en, sckcr); } -static int clk_slow_osc_is_enabled(struct clk *clk) +static int clk_slow_osc_is_prepared(struct clk_hw *hw) { - struct clk_slow_osc *osc = to_clk_slow_osc(clk); + struct clk_slow_osc *osc = to_clk_slow_osc(hw); void __iomem *sckcr = osc->sckcr; u32 tmp = readl(sckcr); @@ -115,12 +106,12 @@ static int clk_slow_osc_is_enabled(struct clk *clk) } static const struct clk_ops slow_osc_ops = { - .enable = clk_slow_osc_enable, - .disable = clk_slow_osc_disable, - .is_enabled = clk_slow_osc_is_enabled, + .enable = clk_slow_osc_prepare, + .disable = clk_slow_osc_unprepare, + .is_enabled = clk_slow_osc_is_prepared, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_slow_osc(void __iomem *sckcr, const char *name, const char *parent_name, @@ -128,21 +119,25 @@ at91_clk_register_slow_osc(void __iomem *sckcr, bool bypass, const struct clk_slow_bits *bits) { - int ret; struct clk_slow_osc *osc; + struct clk_hw *hw; + struct clk_init_data init; + int ret; if (!sckcr || !name || !parent_name) return ERR_PTR(-EINVAL); - osc = xzalloc(sizeof(*osc)); + osc = kzalloc(sizeof(*osc), GFP_KERNEL); + if (!osc) + return ERR_PTR(-ENOMEM); - osc->clk.name = name; - osc->clk.ops = &slow_osc_ops; - osc->parent_name = parent_name; - osc->clk.parent_names = &osc->parent_name; - osc->clk.num_parents = 1; - /* osc->clk.flags = CLK_IGNORE_UNUSED; */ + init.name = name; + init.ops = &slow_osc_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_IGNORE_UNUSED; + osc->hw.init = &init; osc->sckcr = sckcr; osc->startup_usec = startup; osc->bits = bits; @@ -151,34 +146,35 @@ at91_clk_register_slow_osc(void __iomem *sckcr, writel((readl(sckcr) & ~osc->bits->cr_osc32en) | osc->bits->cr_osc32byp, sckcr); - ret = clk_register(&osc->clk); + hw = &osc->hw; + ret = clk_hw_register(NULL, &osc->hw); if (ret) { kfree(osc); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &osc->clk; + return hw; } -static void at91_clk_unregister_slow_osc(struct clk *clk) +static void at91_clk_unregister_slow_osc(struct clk_hw *hw) { - struct clk_slow_osc *osc = to_clk_slow_osc(clk); + struct clk_slow_osc *osc = to_clk_slow_osc(hw); - clk_unregister(clk); + clk_hw_unregister(hw); kfree(osc); } -static unsigned long clk_slow_rc_osc_recalc_rate(struct clk *clk, +static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk); + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); return osc->frequency; } -static int clk_slow_rc_osc_enable(struct clk *clk) +static int clk_slow_rc_osc_prepare(struct clk_hw *hw) { - struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk); + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); void __iomem *sckcr = osc->sckcr; writel(readl(sckcr) | osc->bits->cr_rcen, sckcr); @@ -188,29 +184,29 @@ static int clk_slow_rc_osc_enable(struct clk *clk) return 0; } -static void clk_slow_rc_osc_disable(struct clk *clk) +static void clk_slow_rc_osc_unprepare(struct clk_hw *hw) { - struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk); + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); void __iomem *sckcr = osc->sckcr; writel(readl(sckcr) & ~osc->bits->cr_rcen, sckcr); } -static int clk_slow_rc_osc_is_enabled(struct clk *clk) +static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw) { - struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk); + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); return !!(readl(osc->sckcr) & osc->bits->cr_rcen); } static const struct clk_ops slow_rc_osc_ops = { - .enable = clk_slow_rc_osc_enable, - .disable = clk_slow_rc_osc_disable, - .is_enabled = clk_slow_rc_osc_is_enabled, + .enable = clk_slow_rc_osc_prepare, + .disable = clk_slow_rc_osc_unprepare, + .is_enabled = clk_slow_rc_osc_is_prepared, .recalc_rate = clk_slow_rc_osc_recalc_rate, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_slow_rc_osc(void __iomem *sckcr, const char *name, unsigned long frequency, @@ -219,43 +215,51 @@ at91_clk_register_slow_rc_osc(void __iomem *sckcr, const struct clk_slow_bits *bits) { struct clk_slow_rc_osc *osc; + struct clk_hw *hw; + struct clk_init_data init; int ret; if (!sckcr || !name) return ERR_PTR(-EINVAL); - osc = xzalloc(sizeof(*osc)); - osc->clk.name = name; - osc->clk.ops = &slow_rc_osc_ops; - osc->clk.parent_names = NULL; - osc->clk.num_parents = 0; - /* init.flags = CLK_IGNORE_UNUSED; */ + osc = kzalloc(sizeof(*osc), GFP_KERNEL); + if (!osc) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &slow_rc_osc_ops; + init.parent_names = NULL; + init.num_parents = 0; + init.flags = CLK_IGNORE_UNUSED; + osc->hw.init = &init; osc->sckcr = sckcr; osc->bits = bits; osc->frequency = frequency; + osc->accuracy = accuracy; osc->startup_usec = startup; - ret = clk_register(&osc->clk); + hw = &osc->hw; + ret = clk_hw_register(NULL, &osc->hw); if (ret) { kfree(osc); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &osc->clk; + return hw; } -static void at91_clk_unregister_slow_rc_osc(struct clk *clk) +static void at91_clk_unregister_slow_rc_osc(struct clk_hw *hw) { - struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk); + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); - clk_unregister(clk); + clk_hw_unregister(hw); kfree(osc); } -static int clk_sam9x5_slow_set_parent(struct clk *clk, u8 index) +static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) { - struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk); + struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); void __iomem *sckcr = slowck->sckcr; u32 tmp; @@ -280,9 +284,9 @@ static int clk_sam9x5_slow_set_parent(struct clk *clk, u8 index) return 0; } -static int clk_sam9x5_slow_get_parent(struct clk *clk) +static int clk_sam9x5_slow_get_parent(struct clk_hw *hw) { - struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk); + struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); return !!(readl(slowck->sckcr) & slowck->bits->cr_oscsel); } @@ -292,7 +296,7 @@ static const struct clk_ops sam9x5_slow_ops = { .get_parent = clk_sam9x5_slow_get_parent, }; -static struct clk * __init +static struct clk_hw * __init at91_clk_register_sam9x5_slow(void __iomem *sckcr, const char *name, const char **parent_names, @@ -300,37 +304,43 @@ at91_clk_register_sam9x5_slow(void __iomem *sckcr, const struct clk_slow_bits *bits) { struct clk_sam9x5_slow *slowck; + struct clk_hw *hw; + struct clk_init_data init; int ret; if (!sckcr || !name || !parent_names || !num_parents) return ERR_PTR(-EINVAL); - slowck = xzalloc(sizeof(*slowck)); - slowck->clk.name = name; - slowck->clk.ops = &sam9x5_slow_ops; + slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); + if (!slowck) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &sam9x5_slow_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = 0; - memcpy(slowck->parent_names, parent_names, - num_parents * sizeof(slowck->parent_names[0])); - slowck->clk.parent_names = slowck->parent_names; - slowck->clk.num_parents = num_parents; + slowck->hw.init = &init; slowck->sckcr = sckcr; slowck->bits = bits; slowck->parent = !!(readl(sckcr) & slowck->bits->cr_oscsel); - ret = clk_register(&slowck->clk); + hw = &slowck->hw; + ret = clk_hw_register(NULL, &slowck->hw); if (ret) { kfree(slowck); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &slowck->clk; + return hw; } -static void at91_clk_unregister_sam9x5_slow(struct clk *clk) +static void at91_clk_unregister_sam9x5_slow(struct clk_hw *hw) { - struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk); + struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); - clk_unregister(clk); + clk_hw_unregister(hw); kfree(slowck); } @@ -342,7 +352,7 @@ static void __init at91sam9x5_sckc_register(struct device_node *np, void __iomem *regbase = of_iomap(np, 0); struct device_node *child = NULL; const char *xtal_name; - struct clk *slow_rc, *slow_osc, *slowck; + struct clk_hw *slow_rc, *slow_osc, *slowck; bool bypass; int ret; @@ -385,10 +395,10 @@ static void __init at91sam9x5_sckc_register(struct device_node *np, /* DT backward compatibility */ if (child) - ret = of_clk_add_provider(child, of_clk_src_simple_get, + ret = of_clk_add_hw_provider(child, of_clk_hw_simple_get, slowck); else - ret = of_clk_add_provider(np, of_clk_src_simple_get, slowck); + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck); if (WARN_ON(ret)) goto unregister_slowck; @@ -433,8 +443,8 @@ static const struct clk_slow_bits at91sam9x60_bits = { static void __init of_sam9x60_sckc_setup(struct device_node *np) { void __iomem *regbase = of_iomap(np, 0); - struct clk_onecell_data *clk_data; - struct clk *slow_rc, *slow_osc; + struct clk_hw_onecell_data *clk_data; + struct clk_hw *slow_rc, *slow_osc; const char *xtal_name; const char *parent_names[2] = { "slow_rc_osc", "slow_osc" }; bool bypass; @@ -443,8 +453,9 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np) if (!regbase) return; - slow_rc = clk_register_fixed_rate(parent_names[0], NULL, 0, - 32768); + slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0], + NULL, 0, 32768, + 93750000); if (IS_ERR(slow_rc)) return; @@ -459,61 +470,54 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np) if (IS_ERR(slow_osc)) goto unregister_slow_rc; - clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + clk_data = kzalloc(struct_size(clk_data, hws, 2), GFP_KERNEL); if (!clk_data) goto unregister_slow_osc; /* MD_SLCK and TD_SLCK. */ - clk_data->clk_num = 2; - clk_data->clks = kcalloc(clk_data->clk_num, - sizeof(*clk_data->clks), GFP_KERNEL); - if (!clk_data->clks) + clk_data->num = 2; + clk_data->hws[0] = clk_hw_register_fixed_rate(NULL, "md_slck", + parent_names[0], + 0, 32768); + if (IS_ERR(clk_data->hws[0])) goto clk_data_free; - clk_data->clks[0] = clk_register_fixed_rate("md_slck", - parent_names[0], - 0, 32768); - if (IS_ERR(clk_data->clks[0])) - goto clks_free; - - clk_data->clks[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck", + clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck", parent_names, 2, &at91sam9x60_bits); - if (IS_ERR(clk_data->clks[1])) + if (IS_ERR(clk_data->hws[1])) goto unregister_md_slck; - ret = of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); if (WARN_ON(ret)) goto unregister_td_slck; return; unregister_td_slck: - at91_clk_unregister_sam9x5_slow(clk_data->clks[1]); + at91_clk_unregister_sam9x5_slow(clk_data->hws[1]); unregister_md_slck: - clk_unregister(clk_data->clks[0]); -clks_free: - kfree(clk_data->clks); + clk_hw_unregister(clk_data->hws[0]); clk_data_free: kfree(clk_data); unregister_slow_osc: at91_clk_unregister_slow_osc(slow_osc); unregister_slow_rc: - clk_unregister(slow_rc); + clk_hw_unregister(slow_rc); } CLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc", of_sam9x60_sckc_setup); -static int clk_sama5d4_slow_osc_enable(struct clk *clk) +static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw) { - struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(clk); + struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); if (osc->prepared) return 0; /* * Assume that if it has already been selected (for example by the - * bootloader), enough time has aready passed. + * bootloader), enough time has already passed. */ if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) { osc->prepared = true; @@ -526,16 +530,16 @@ static int clk_sama5d4_slow_osc_enable(struct clk *clk) return 0; } -static int clk_sama5d4_slow_osc_is_enabled(struct clk *clk) +static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw) { - struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(clk); + struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); return osc->prepared; } static const struct clk_ops sama5d4_slow_osc_ops = { - .enable = clk_sama5d4_slow_osc_enable, - .is_enabled = clk_sama5d4_slow_osc_is_enabled, + .enable = clk_sama5d4_slow_osc_prepare, + .is_enabled = clk_sama5d4_slow_osc_is_prepared, }; static const struct clk_slow_bits at91sama5d4_bits = { @@ -545,32 +549,41 @@ static const struct clk_slow_bits at91sama5d4_bits = { static void __init of_sama5d4_sckc_setup(struct device_node *np) { void __iomem *regbase = of_iomap(np, 0); - struct clk *slow_rc, *slowck; + struct clk_hw *slow_rc, *slowck; struct clk_sama5d4_slow_osc *osc; + struct clk_init_data init; + const char *xtal_name; const char *parent_names[2] = { "slow_rc_osc", "slow_osc" }; int ret; if (!regbase) return; - slow_rc = clk_fixed(parent_names[0], 32768); + slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, + parent_names[0], + NULL, 0, 32768, + 250000000); if (IS_ERR(slow_rc)) return; - osc = xzalloc(sizeof(*osc)); - osc->parent_name = of_clk_get_parent_name(np, 0); - osc->clk.name = parent_names[1]; - osc->clk.ops = &sama5d4_slow_osc_ops; - osc->clk.parent_names = &osc->parent_name; - osc->clk.num_parents = 1; + xtal_name = of_clk_get_parent_name(np, 0); + + osc = kzalloc(sizeof(*osc), GFP_KERNEL); + if (!osc) + goto unregister_slow_rc; - /* osc->clk.flags = CLK_IGNORE_UNUSED; */ + init.name = parent_names[1]; + init.ops = &sama5d4_slow_osc_ops; + init.parent_names = &xtal_name; + init.num_parents = 1; + init.flags = CLK_IGNORE_UNUSED; + osc->hw.init = &init; osc->sckcr = regbase; osc->startup_usec = 1200000; osc->bits = &at91sama5d4_bits; - ret = clk_register(&osc->clk); + ret = clk_hw_register(NULL, &osc->hw); if (ret) goto free_slow_osc_data; @@ -580,7 +593,7 @@ static void __init of_sama5d4_sckc_setup(struct device_node *np) if (IS_ERR(slowck)) goto unregister_slow_osc; - ret = of_clk_add_provider(np, of_clk_src_simple_get, slowck); + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck); if (WARN_ON(ret)) goto unregister_slowck; @@ -589,10 +602,11 @@ static void __init of_sama5d4_sckc_setup(struct device_node *np) unregister_slowck: at91_clk_unregister_sam9x5_slow(slowck); unregister_slow_osc: - clk_unregister(&osc->clk); + clk_hw_unregister(&osc->hw); free_slow_osc_data: kfree(osc); - clk_unregister(slow_rc); +unregister_slow_rc: + clk_hw_unregister(slow_rc); } CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc", of_sama5d4_sckc_setup); |