diff options
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/at91/pmc.c | 20 | ||||
-rw-r--r-- | drivers/clk/at91/pmc.h | 1 | ||||
-rw-r--r-- | drivers/clk/bcm/clk-kona.c | 28 | ||||
-rw-r--r-- | drivers/clk/bcm/clk-kona.h | 1 | ||||
-rw-r--r-- | drivers/clk/clk-si5351.c | 63 | ||||
-rw-r--r-- | drivers/clk/clk.c | 8 | ||||
-rw-r--r-- | drivers/clk/pistachio/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/pistachio/clk-pistachio.c | 329 | ||||
-rw-r--r-- | drivers/clk/pistachio/clk-pll.c | 401 | ||||
-rw-r--r-- | drivers/clk/pistachio/clk.c | 140 | ||||
-rw-r--r-- | drivers/clk/pistachio/clk.h | 174 | ||||
-rw-r--r-- | drivers/clk/qcom/gcc-msm8916.c | 4 | ||||
-rw-r--r-- | drivers/clk/samsung/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/samsung/clk-exynos5420.c | 1 | ||||
-rw-r--r-- | drivers/clk/samsung/clk-exynos5433.c | 12 | ||||
-rw-r--r-- | drivers/clk/shmobile/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/shmobile/clk-r8a7778.c | 143 |
18 files changed, 1282 insertions, 50 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 492bcabe7930..3d00c25382c5 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_PLAT_ORION) += mvebu/ obj-$(CONFIG_ARCH_MXS) += mxs/ +obj-$(CONFIG_MACH_PISTACHIO) += pistachio/ obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index f07c8152e5cc..3f27d21fb729 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -89,12 +89,29 @@ static int pmc_irq_set_type(struct irq_data *d, unsigned type) return 0; } +static void pmc_irq_suspend(struct irq_data *d) +{ + struct at91_pmc *pmc = irq_data_get_irq_chip_data(d); + + pmc->imr = pmc_read(pmc, AT91_PMC_IMR); + pmc_write(pmc, AT91_PMC_IDR, pmc->imr); +} + +static void pmc_irq_resume(struct irq_data *d) +{ + struct at91_pmc *pmc = irq_data_get_irq_chip_data(d); + + pmc_write(pmc, AT91_PMC_IER, pmc->imr); +} + static struct irq_chip pmc_irq = { .name = "PMC", .irq_disable = pmc_irq_mask, .irq_mask = pmc_irq_mask, .irq_unmask = pmc_irq_unmask, .irq_set_type = pmc_irq_set_type, + .irq_suspend = pmc_irq_suspend, + .irq_resume = pmc_irq_resume, }; static struct lock_class_key pmc_lock_class; @@ -224,7 +241,8 @@ static struct at91_pmc *__init at91_pmc_init(struct device_node *np, goto out_free_pmc; pmc_write(pmc, AT91_PMC_IDR, 0xffffffff); - if (request_irq(pmc->virq, pmc_irq_handler, IRQF_SHARED, "pmc", pmc)) + if (request_irq(pmc->virq, pmc_irq_handler, + IRQF_SHARED | IRQF_COND_SUSPEND, "pmc", pmc)) goto out_remove_irqdomain; return pmc; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index af2c99195541..eb8e5dc9076d 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -33,6 +33,7 @@ struct at91_pmc { spinlock_t lock; const struct at91_pmc_caps *caps; struct irq_domain *irqdomain; + u32 imr; }; static inline void pmc_lock(struct at91_pmc *pmc) diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index 05abae89262e..a0ef4f75d457 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -15,6 +15,7 @@ #include "clk-kona.h" #include <linux/delay.h> +#include <linux/kernel.h> /* * "Policies" affect the frequencies of bus clocks provided by a @@ -51,21 +52,6 @@ static inline u32 bitfield_replace(u32 reg_val, u32 shift, u32 width, u32 val) /* Divider and scaling helpers */ -/* - * Implement DIV_ROUND_CLOSEST() for 64-bit dividend and both values - * unsigned. Note that unlike do_div(), the remainder is discarded - * and the return value is the quotient (not the remainder). - */ -u64 do_div_round_closest(u64 dividend, unsigned long divisor) -{ - u64 result; - - result = dividend + ((u64)divisor >> 1); - (void)do_div(result, divisor); - - return result; -} - /* Convert a divider into the scaled divisor value it represents. */ static inline u64 scaled_div_value(struct bcm_clk_div *div, u32 reg_div) { @@ -87,7 +73,7 @@ u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, u32 billionths) combined = (u64)div_value * BILLION + billionths; combined <<= div->u.s.frac_width; - return do_div_round_closest(combined, BILLION); + return DIV_ROUND_CLOSEST_ULL(combined, BILLION); } /* The scaled minimum divisor representable by a divider */ @@ -731,7 +717,7 @@ static unsigned long clk_recalc_rate(struct ccu_data *ccu, scaled_rate = scale_rate(pre_div, parent_rate); scaled_rate = scale_rate(div, scaled_rate); scaled_div = divider_read_scaled(ccu, pre_div); - scaled_parent_rate = do_div_round_closest(scaled_rate, + scaled_parent_rate = DIV_ROUND_CLOSEST_ULL(scaled_rate, scaled_div); } else { scaled_parent_rate = scale_rate(div, parent_rate); @@ -743,7 +729,7 @@ static unsigned long clk_recalc_rate(struct ccu_data *ccu, * rate. */ scaled_div = divider_read_scaled(ccu, div); - result = do_div_round_closest(scaled_parent_rate, scaled_div); + result = DIV_ROUND_CLOSEST_ULL(scaled_parent_rate, scaled_div); return (unsigned long)result; } @@ -790,7 +776,7 @@ static long round_rate(struct ccu_data *ccu, struct bcm_clk_div *div, scaled_rate = scale_rate(pre_div, parent_rate); scaled_rate = scale_rate(div, scaled_rate); scaled_pre_div = divider_read_scaled(ccu, pre_div); - scaled_parent_rate = do_div_round_closest(scaled_rate, + scaled_parent_rate = DIV_ROUND_CLOSEST_ULL(scaled_rate, scaled_pre_div); } else { scaled_parent_rate = scale_rate(div, parent_rate); @@ -802,7 +788,7 @@ static long round_rate(struct ccu_data *ccu, struct bcm_clk_div *div, * the best we can do. */ if (!divider_is_fixed(div)) { - best_scaled_div = do_div_round_closest(scaled_parent_rate, + best_scaled_div = DIV_ROUND_CLOSEST_ULL(scaled_parent_rate, rate); min_scaled_div = scaled_div_min(div); max_scaled_div = scaled_div_max(div); @@ -815,7 +801,7 @@ static long round_rate(struct ccu_data *ccu, struct bcm_clk_div *div, } /* OK, figure out the resulting rate */ - result = do_div_round_closest(scaled_parent_rate, best_scaled_div); + result = DIV_ROUND_CLOSEST_ULL(scaled_parent_rate, best_scaled_div); if (scaled_div) *scaled_div = best_scaled_div; diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index 2537b3072910..6849a64baf6d 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -503,7 +503,6 @@ extern struct clk_ops kona_peri_clk_ops; /* Externally visible functions */ -extern u64 do_div_round_closest(u64 dividend, unsigned long divisor); extern u64 scaled_div_max(struct bcm_clk_div *div); extern u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, u32 billionths); diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 44ea107cfc67..30335d3b99af 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -1128,13 +1128,6 @@ static int si5351_dt_parse(struct i2c_client *client, if (!pdata) return -ENOMEM; - pdata->clk_xtal = of_clk_get(np, 0); - if (!IS_ERR(pdata->clk_xtal)) - clk_put(pdata->clk_xtal); - pdata->clk_clkin = of_clk_get(np, 1); - if (!IS_ERR(pdata->clk_clkin)) - clk_put(pdata->clk_clkin); - /* * property silabs,pll-source : <num src>, [<..>] * allow to selectively set pll source @@ -1328,8 +1321,22 @@ static int si5351_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, drvdata); drvdata->client = client; drvdata->variant = variant; - drvdata->pxtal = pdata->clk_xtal; - drvdata->pclkin = pdata->clk_clkin; + drvdata->pxtal = devm_clk_get(&client->dev, "xtal"); + drvdata->pclkin = devm_clk_get(&client->dev, "clkin"); + + if (PTR_ERR(drvdata->pxtal) == -EPROBE_DEFER || + PTR_ERR(drvdata->pclkin) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + /* + * Check for valid parent clock: VARIANT_A and VARIANT_B need XTAL, + * VARIANT_C can have CLKIN instead. + */ + if (IS_ERR(drvdata->pxtal) && + (drvdata->variant != SI5351_VARIANT_C || IS_ERR(drvdata->pclkin))) { + dev_err(&client->dev, "missing parent clock\n"); + return -EINVAL; + } drvdata->regmap = devm_regmap_init_i2c(client, &si5351_regmap_config); if (IS_ERR(drvdata->regmap)) { @@ -1393,6 +1400,11 @@ static int si5351_i2c_probe(struct i2c_client *client, } } + if (!IS_ERR(drvdata->pxtal)) + clk_prepare_enable(drvdata->pxtal); + if (!IS_ERR(drvdata->pclkin)) + clk_prepare_enable(drvdata->pclkin); + /* register xtal input clock gate */ memset(&init, 0, sizeof(init)); init.name = si5351_input_names[0]; @@ -1407,7 +1419,8 @@ static int si5351_i2c_probe(struct i2c_client *client, clk = devm_clk_register(&client->dev, &drvdata->xtal); if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); - return PTR_ERR(clk); + ret = PTR_ERR(clk); + goto err_clk; } /* register clkin input clock gate */ @@ -1425,7 +1438,8 @@ static int si5351_i2c_probe(struct i2c_client *client, if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); - return PTR_ERR(clk); + ret = PTR_ERR(clk); + goto err_clk; } } @@ -1447,7 +1461,8 @@ static int si5351_i2c_probe(struct i2c_client *client, clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw); if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); - return -EINVAL; + ret = PTR_ERR(clk); + goto err_clk; } /* register PLLB or VXCO (Si5351B) */ @@ -1471,7 +1486,8 @@ static int si5351_i2c_probe(struct i2c_client *client, clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw); if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); - return -EINVAL; + ret = PTR_ERR(clk); + goto err_clk; } /* register clk multisync and clk out divider */ @@ -1492,8 +1508,10 @@ static int si5351_i2c_probe(struct i2c_client *client, num_clocks * sizeof(*drvdata->onecell.clks), GFP_KERNEL); if (WARN_ON(!drvdata->msynth || !drvdata->clkout || - !drvdata->onecell.clks)) - return -ENOMEM; + !drvdata->onecell.clks)) { + ret = -ENOMEM; + goto err_clk; + } for (n = 0; n < num_clocks; n++) { drvdata->msynth[n].num = n; @@ -1511,7 +1529,8 @@ static int si5351_i2c_probe(struct i2c_client *client, if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); - return -EINVAL; + ret = PTR_ERR(clk); + goto err_clk; } } @@ -1538,7 +1557,8 @@ static int si5351_i2c_probe(struct i2c_client *client, if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); - return -EINVAL; + ret = PTR_ERR(clk); + goto err_clk; } drvdata->onecell.clks[n] = clk; @@ -1557,10 +1577,17 @@ static int si5351_i2c_probe(struct i2c_client *client, &drvdata->onecell); if (ret) { dev_err(&client->dev, "unable to add clk provider\n"); - return ret; + goto err_clk; } return 0; + +err_clk: + if (!IS_ERR(drvdata->pxtal)) + clk_disable_unprepare(drvdata->pxtal); + if (!IS_ERR(drvdata->pclkin)) + clk_disable_unprepare(drvdata->pclkin); + return ret; } static const struct i2c_device_id si5351_i2c_ids[] = { diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 459ce9da13e0..5b0f41868b42 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1475,8 +1475,10 @@ static struct clk_core *__clk_set_parent_before(struct clk_core *clk, */ if (clk->prepare_count) { clk_core_prepare(parent); + flags = clk_enable_lock(); clk_core_enable(parent); clk_core_enable(clk); + clk_enable_unlock(flags); } /* update the clk tree topology */ @@ -1491,13 +1493,17 @@ static void __clk_set_parent_after(struct clk_core *core, struct clk_core *parent, struct clk_core *old_parent) { + unsigned long flags; + /* * Finish the migration of prepare state and undo the changes done * for preventing a race with clk_enable(). */ if (core->prepare_count) { + flags = clk_enable_lock(); clk_core_disable(core); clk_core_disable(old_parent); + clk_enable_unlock(flags); clk_core_unprepare(old_parent); } } @@ -1525,8 +1531,10 @@ static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent, clk_enable_unlock(flags); if (clk->prepare_count) { + flags = clk_enable_lock(); clk_core_disable(clk); clk_core_disable(parent); + clk_enable_unlock(flags); clk_core_unprepare(parent); } return ret; diff --git a/drivers/clk/pistachio/Makefile b/drivers/clk/pistachio/Makefile new file mode 100644 index 000000000000..f1e151fbef65 --- /dev/null +++ b/drivers/clk/pistachio/Makefile @@ -0,0 +1,3 @@ +obj-y += clk.o +obj-y += clk-pll.o +obj-y += clk-pistachio.o diff --git a/drivers/clk/pistachio/clk-pistachio.c b/drivers/clk/pistachio/clk-pistachio.c new file mode 100644 index 000000000000..8c0fe8828f99 --- /dev/null +++ b/drivers/clk/pistachio/clk-pistachio.c @@ -0,0 +1,329 @@ +/* + * Pistachio SoC clock controllers + * + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/clk-provider.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/of.h> + +#include <dt-bindings/clock/pistachio-clk.h> + +#include "clk.h" + +static struct pistachio_gate pistachio_gates[] __initdata = { + GATE(CLK_MIPS, "mips", "mips_div", 0x104, 0), + GATE(CLK_AUDIO_IN, "audio_in", "audio_clk_in_gate", 0x104, 1), + GATE(CLK_AUDIO, "audio", "audio_div", 0x104, 2), + GATE(CLK_I2S, "i2s", "i2s_div", 0x104, 3), + GATE(CLK_SPDIF, "spdif", "spdif_div", 0x104, 4), + GATE(CLK_AUDIO_DAC, "audio_dac", "audio_dac_div", 0x104, 5), + GATE(CLK_RPU_V, "rpu_v", "rpu_v_div", 0x104, 6), + GATE(CLK_RPU_L, "rpu_l", "rpu_l_div", 0x104, 7), + GATE(CLK_RPU_SLEEP, "rpu_sleep", "rpu_sleep_div", 0x104, 8), + GATE(CLK_WIFI_PLL_GATE, "wifi_pll_gate", "wifi_pll_mux", 0x104, 9), + GATE(CLK_RPU_CORE, "rpu_core", "rpu_core_div", 0x104, 10), + GATE(CLK_WIFI_ADC, "wifi_adc", "wifi_div8_mux", 0x104, 11), + GATE(CLK_WIFI_DAC, "wifi_dac", "wifi_div4_mux", 0x104, 12), + GATE(CLK_USB_PHY, "usb_phy", "usb_phy_div", 0x104, 13), + GATE(CLK_ENET_IN, "enet_in", "enet_clk_in_gate", 0x104, 14), + GATE(CLK_ENET, "enet", "enet_div", 0x104, 15), + GATE(CLK_UART0, "uart0", "uart0_div", 0x104, 16), + GATE(CLK_UART1, "uart1", "uart1_div", 0x104, 17), + GATE(CLK_PERIPH_SYS, "periph_sys", "sys_internal_div", 0x104, 18), + GATE(CLK_SPI0, "spi0", "spi0_div", 0x104, 19), + GATE(CLK_SPI1, "spi1", "spi1_div", 0x104, 20), + GATE(CLK_EVENT_TIMER, "event_timer", "event_timer_div", 0x104, 21), + GATE(CLK_AUX_ADC_INTERNAL, "aux_adc_internal", "sys_internal_div", + 0x104, 22), + GATE(CLK_AUX_ADC, "aux_adc", "aux_adc_div", 0x104, 23), + GATE(CLK_SD_HOST, "sd_host", "sd_host_div", 0x104, 24), + GATE(CLK_BT, "bt", "bt_div", 0x104, 25), + GATE(CLK_BT_DIV4, "bt_div4", "bt_div4_div", 0x104, 26), + GATE(CLK_BT_DIV8, "bt_div8", "bt_div8_div", 0x104, 27), + GATE(CLK_BT_1MHZ, "bt_1mhz", "bt_1mhz_div", 0x104, 28), +}; + +static struct pistachio_fixed_factor pistachio_ffs[] __initdata = { + FIXED_FACTOR(CLK_WIFI_DIV4, "wifi_div4", "wifi_pll", 4), + FIXED_FACTOR(CLK_WIFI_DIV8, "wifi_div8", "wifi_pll", 8), +}; + +static struct pistachio_div pistachio_divs[] __initdata = { + DIV(CLK_MIPS_INTERNAL_DIV, "mips_internal_div", "mips_pll_mux", + 0x204, 2), + DIV(CLK_MIPS_DIV, "mips_div", "mips_internal_div", 0x208, 8), + DIV_F(CLK_AUDIO_DIV, "audio_div", "audio_mux", + 0x20c, 8, CLK_DIVIDER_ROUND_CLOSEST), + DIV_F(CLK_I2S_DIV, "i2s_div", "audio_pll_mux", + 0x210, 8, CLK_DIVIDER_ROUND_CLOSEST), + DIV_F(CLK_SPDIF_DIV, "spdif_div", "audio_pll_mux", + 0x214, 8, CLK_DIVIDER_ROUND_CLOSEST), + DIV_F(CLK_AUDIO_DAC_DIV, "audio_dac_div", "audio_pll_mux", + 0x218, 8, CLK_DIVIDER_ROUND_CLOSEST), + DIV(CLK_RPU_V_DIV, "rpu_v_div", "rpu_v_pll_mux", 0x21c, 2), + DIV(CLK_RPU_L_DIV, "rpu_l_div", "rpu_l_mux", 0x220, 2), + DIV(CLK_RPU_SLEEP_DIV, "rpu_sleep_div", "xtal", 0x224, 10), + DIV(CLK_RPU_CORE_DIV, "rpu_core_div", "rpu_core_mux", 0x228, 3), + DIV(CLK_USB_PHY_DIV, "usb_phy_div", "sys_internal_div", 0x22c, 6), + DIV(CLK_ENET_DIV, "enet_div", "enet_mux", 0x230, 6), + DIV_F(CLK_UART0_INTERNAL_DIV, "uart0_internal_div", "sys_pll_mux", + 0x234, 3, CLK_DIVIDER_ROUND_CLOSEST), + DIV_F(CLK_UART0_DIV, "uart0_div", "uart0_internal_div", 0x238, 10, + CLK_DIVIDER_ROUND_CLOSEST), + DIV_F(CLK_UART1_INTERNAL_DIV, "uart1_internal_div", "sys_pll_mux", + 0x23c, 3, CLK_DIVIDER_ROUND_CLOSEST), + DIV_F(CLK_UART1_DIV, "uart1_div", "uart1_internal_div", 0x240, 10, + CLK_DIVIDER_ROUND_CLOSEST), + DIV(CLK_SYS_INTERNAL_DIV, "sys_internal_div", "sys_pll_mux", 0x244, 3), + DIV(CLK_SPI0_INTERNAL_DIV, "spi0_internal_div", "sys_pll_mux", + 0x248, 3), + DIV(CLK_SPI0_DIV, "spi0_div", "spi0_internal_div", 0x24c, 7), + DIV(CLK_SPI1_INTERNAL_DIV, "spi1_internal_div", "sys_pll_mux", + 0x250, 3), + DIV(CLK_SPI1_DIV, "spi1_div", "spi1_internal_div", 0x254, 7), + DIV(CLK_EVENT_TIMER_INTERNAL_DIV, "event_timer_internal_div", + "event_timer_mux", 0x258, 3), + DIV(CLK_EVENT_TIMER_DIV, "event_timer_div", "event_timer_internal_div", + 0x25c, 12), + DIV(CLK_AUX_ADC_INTERNAL_DIV, "aux_adc_internal_div", + "aux_adc_internal", 0x260, 3), + DIV(CLK_AUX_ADC_DIV, "aux_adc_div", "aux_adc_internal_div", 0x264, 10), + DIV(CLK_SD_HOST_DIV, "sd_host_div", "sd_host_mux", 0x268, 6), + DIV(CLK_BT_DIV, "bt_div", "bt_pll_mux", 0x26c, 6), + DIV(CLK_BT_DIV4_DIV, "bt_div4_div", "bt_pll_mux", 0x270, 6), + DIV(CLK_BT_DIV8_DIV, "bt_div8_div", "bt_pll_mux", 0x274, 6), + DIV(CLK_BT_1MHZ_INTERNAL_DIV, "bt_1mhz_internal_div", "bt_pll_mux", + 0x278, 3), + DIV(CLK_BT_1MHZ_DIV, "bt_1mhz_div", "bt_1mhz_internal_div", 0x27c, 10), +}; + +PNAME(mux_xtal_audio_refclk) = { "xtal", "audio_clk_in_gate" }; +PNAME(mux_xtal_mips) = { "xtal", "mips_pll" }; +PNAME(mux_xtal_audio) = { "xtal", "audio_pll", "audio_in" }; +PNAME(mux_audio_debug) = { "audio_pll_mux", "debug_mux" }; +PNAME(mux_xtal_rpu_v) = { "xtal", "rpu_v_pll" }; +PNAME(mux_xtal_rpu_l) = { "xtal", "rpu_l_pll" }; +PNAME(mux_rpu_l_mips) = { "rpu_l_pll_mux", "mips_pll_mux" }; +PNAME(mux_xtal_wifi) = { "xtal", "wifi_pll" }; +PNAME(mux_xtal_wifi_div4) = { "xtal", "wifi_div4" }; +PNAME(mux_xtal_wifi_div8) = { "xtal", "wifi_div8" }; +PNAME(mux_wifi_div4_rpu_l) = { "wifi_pll_gate", "wifi_div4_mux", + "rpu_l_pll_mux" }; +PNAME(mux_xtal_sys) = { "xtal", "sys_pll" }; +PNAME(mux_sys_enet) = { "sys_internal_div", "enet_in" }; +PNAME(mux_audio_sys) = { "audio_pll_mux", "sys_internal_div" }; +PNAME(mux_sys_bt) = { "sys_internal_div", "bt_pll_mux" }; +PNAME(mux_xtal_bt) = { "xtal", "bt_pll" }; + +static struct pistachio_mux pistachio_muxes[] __initdata = { + MUX(CLK_AUDIO_REF_MUX, "audio_refclk_mux", mux_xtal_audio_refclk, + 0x200, 0), + MUX(CLK_MIPS_PLL_MUX, "mips_pll_mux", mux_xtal_mips, 0x200, 1), + MUX(CLK_AUDIO_PLL_MUX, "audio_pll_mux", mux_xtal_audio, 0x200, 2), + MUX(CLK_AUDIO_MUX, "audio_mux", mux_audio_debug, 0x200, 4), + MUX(CLK_RPU_V_PLL_MUX, "rpu_v_pll_mux", mux_xtal_rpu_v, 0x200, 5), + MUX(CLK_RPU_L_PLL_MUX, "rpu_l_pll_mux", mux_xtal_rpu_l, 0x200, 6), + MUX(CLK_RPU_L_MUX, "rpu_l_mux", mux_rpu_l_mips, 0x200, 7), + MUX(CLK_WIFI_PLL_MUX, "wifi_pll_mux", mux_xtal_wifi, 0x200, 8), + MUX(CLK_WIFI_DIV4_MUX, "wifi_div4_mux", mux_xtal_wifi_div4, 0x200, 9), + MUX(CLK_WIFI_DIV8_MUX, "wifi_div8_mux", mux_xtal_wifi_div8, 0x200, 10), + MUX(CLK_RPU_CORE_MUX, "rpu_core_mux", mux_wifi_div4_rpu_l, 0x200, 11), + MUX(CLK_SYS_PLL_MUX, "sys_pll_mux", mux_xtal_sys, 0x200, 13), + MUX(CLK_ENET_MUX, "enet_mux", mux_sys_enet, 0x200, 14), + MUX(CLK_EVENT_TIMER_MUX, "event_timer_mux", mux_audio_sys, 0x200, 15), + MUX(CLK_SD_HOST_MUX, "sd_host_mux", mux_sys_bt, 0x200, 16), + MUX(CLK_BT_PLL_MUX, "bt_pll_mux", mux_xtal_bt, 0x200, 17), +}; + +static struct pistachio_pll pistachio_plls[] __initdata = { + PLL_FIXED(CLK_MIPS_PLL, "mips_pll", "xtal", PLL_GF40LP_LAINT, 0x0), + PLL_FIXED(CLK_AUDIO_PLL, "audio_pll", "audio_refclk_mux", + PLL_GF40LP_FRAC, 0xc), + PLL_FIXED(CLK_RPU_V_PLL, "rpu_v_pll", "xtal", PLL_GF40LP_LAINT, 0x20), + PLL_FIXED(CLK_RPU_L_PLL, "rpu_l_pll", "xtal", PLL_GF40LP_LAINT, 0x2c), + PLL_FIXED(CLK_SYS_PLL, "sys_pll", "xtal", PLL_GF40LP_FRAC, 0x38), + PLL_FIXED(CLK_WIFI_PLL, "wifi_pll", "xtal", PLL_GF40LP_FRAC, 0x4c), + PLL_FIXED(CLK_BT_PLL, "bt_pll", "xtal", PLL_GF40LP_LAINT, 0x60), +}; + +PNAME(mux_debug) = { "mips_pll_mux", "rpu_v_pll_mux", + "rpu_l_pll_mux", "sys_pll_mux", + "wifi_pll_mux", "bt_pll_mux" }; +static u32 mux_debug_idx[] = { 0x0, 0x1, 0x2, 0x4, 0x8, 0x10 }; + +static unsigned int pistachio_critical_clks[] __initdata = { + CLK_MIPS, + CLK_PERIPH_SYS, +}; + +static void __init pistachio_clk_init(struct device_node *np) +{ + struct pistachio_clk_provider *p; + struct clk *debug_clk; + + p = pistachio_clk_alloc_provider(np, CLK_NR_CLKS); + if (!p) + return; + + pistachio_clk_register_pll(p, pistachio_plls, + ARRAY_SIZE(pistachio_plls)); + pistachio_clk_register_mux(p, pistachio_muxes, + ARRAY_SIZE(pistachio_muxes)); + pistachio_clk_register_div(p, pistachio_divs, + ARRAY_SIZE(pistachio_divs)); + pistachio_clk_register_fixed_factor(p, pistachio_ffs, + ARRAY_SIZE(pistachio_ffs)); + pistachio_clk_register_gate(p, pistachio_gates, + ARRAY_SIZE(pistachio_gates)); + + debug_clk = clk_register_mux_table(NULL, "debug_mux", mux_debug, + ARRAY_SIZE(mux_debug), + CLK_SET_RATE_NO_REPARENT, + p->base + 0x200, 18, 0x1f, 0, + mux_debug_idx, NULL); + p->clk_data.clks[CLK_DEBUG_MUX] = debug_clk; + + pistachio_clk_register_provider(p); + + pistachio_clk_force_enable(p, pistachio_critical_clks, + ARRAY_SIZE(pistachio_critical_clks)); +} +CLK_OF_DECLARE(pistachio_clk, "img,pistachio-clk", pistachio_clk_init); + +static struct pistachio_gate pistachio_periph_gates[] __initdata = { + GATE(PERIPH_CLK_SYS, "sys", "periph_sys", 0x100, 0), + GATE(PERIPH_CLK_SYS_BUS, "bus_sys", "periph_sys", 0x100, 1), + GATE(PERIPH_CLK_DDR, "ddr", "periph_sys", 0x100, 2), + GATE(PERIPH_CLK_ROM, "rom", "rom_div", 0x100, 3), + GATE(PERIPH_CLK_COUNTER_FAST, "counter_fast", "counter_fast_div", + 0x100, 4), + GATE(PERIPH_CLK_COUNTER_SLOW, "counter_slow", "counter_slow_div", + 0x100, 5), + GATE(PERIPH_CLK_IR, "ir", "ir_div", 0x100, 6), + GATE(PERIPH_CLK_WD, "wd", "wd_div", 0x100, 7), + GATE(PERIPH_CLK_PDM, "pdm", "pdm_div", 0x100, 8), + GATE(PERIPH_CLK_PWM, "pwm", "pwm_div", 0x100, 9), + GATE(PERIPH_CLK_I2C0, "i2c0", "i2c0_div", 0x100, 10), + GATE(PERIPH_CLK_I2C1, "i2c1", "i2c1_div", 0x100, 11), + GATE(PERIPH_CLK_I2C2, "i2c2", "i2c2_div", 0x100, 12), + GATE(PERIPH_CLK_I2C3, "i2c3", "i2c3_div", 0x100, 13), +}; + +static struct pistachio_div pistachio_periph_divs[] __initdata = { + DIV(PERIPH_CLK_ROM_DIV, "rom_div", "periph_sys", 0x10c, 7), + DIV(PERIPH_CLK_COUNTER_FAST_DIV, "counter_fast_div", "periph_sys", + 0x110, 7), + DIV(PERIPH_CLK_COUNTER_SLOW_PRE_DIV, "counter_slow_pre_div", + "periph_sys", 0x114, 7), + DIV(PERIPH_CLK_COUNTER_SLOW_DIV, "counter_slow_div", + "counter_slow_pre_div", 0x118, 7), + DIV_F(PERIPH_CLK_IR_PRE_DIV, "ir_pre_div", "periph_sys", 0x11c, 7, + CLK_DIVIDER_ROUND_CLOSEST), + DIV_F(PERIPH_CLK_IR_DIV, "ir_div", "ir_pre_div", 0x120, 7, + CLK_DIVIDER_ROUND_CLOSEST), + DIV_F(PERIPH_CLK_WD_PRE_DIV, "wd_pre_div", "periph_sys", 0x124, 7, + CLK_DIVIDER_ROUND_CLOSEST), + DIV_F(PERIPH_CLK_WD_DIV, "wd_div", "wd_pre_div", 0x128, 7, + CLK_DIVIDER_ROUND_CLOSEST), + DIV(PERIPH_CLK_PDM_PRE_DIV, "pdm_pre_div", "periph_sys", 0x12c, 7), + DIV(PERIPH_CLK_PDM_DIV, "pdm_div", "pdm_pre_div", 0x130, 7), + DIV(PERIPH_CLK_PWM_PRE_DIV, "pwm_pre_div", "periph_sys", 0x134, 7), + DIV(PERIPH_CLK_PWM_DIV, "pwm_div", "pwm_pre_div", 0x138, 7), + DIV(PERIPH_CLK_I2C0_PRE_DIV, "i2c0_pre_div", "periph_sys", 0x13c, 7), + DIV(PERIPH_CLK_I2C0_DIV, "i2c0_div", "i2c0_pre_div", 0x140, 7), + DIV(PERIPH_CLK_I2C1_PRE_DIV, "i2c1_pre_div", "periph_sys", 0x144, 7), + DIV(PERIPH_CLK_I2C1_DIV, "i2c1_div", "i2c1_pre_div", 0x148, 7), + DIV(PERIPH_CLK_I2C2_PRE_DIV, "i2c2_pre_div", "periph_sys", 0x14c, 7), + DIV(PERIPH_CLK_I2C2_DIV, "i2c2_div", "i2c2_pre_div", 0x150, 7), + DIV(PERIPH_CLK_I2C3_PRE_DIV, "i2c3_pre_div", "periph_sys", 0x154, 7), + DIV(PERIPH_CLK_I2C3_DIV, "i2c3_div", "i2c3_pre_div", 0x158, 7), +}; + +static void __init pistachio_clk_periph_init(struct device_node *np) +{ + struct pistachio_clk_provider *p; + + p = pistachio_clk_alloc_provider(np, PERIPH_CLK_NR_CLKS); + if (!p) + return; + + pistachio_clk_register_div(p, pistachio_periph_divs, + ARRAY_SIZE(pistachio_periph_divs)); + pistachio_clk_register_gate(p, pistachio_periph_gates, + ARRAY_SIZE(pistachio_periph_gates)); + + pistachio_clk_register_provider(p); +} +CLK_OF_DECLARE(pistachio_clk_periph, "img,pistachio-clk-periph", + pistachio_clk_periph_init); + +static struct pistachio_gate pistachio_sys_gates[] __initdata = { + GATE(SYS_CLK_I2C0, "i2c0_sys", "sys", 0x8, 0), + GATE(SYS_CLK_I2C1, "i2c1_sys", "sys", 0x8, 1), + GATE(SYS_CLK_I2C2, "i2c2_sys", "sys", 0x8, 2), + GATE(SYS_CLK_I2C3, "i2c3_sys", "sys", 0x8, 3), + GATE(SYS_CLK_I2S_IN, "i2s_in_sys", "sys", 0x8, 4), + GATE(SYS_CLK_PAUD_OUT, "paud_out_sys", "sys", 0x8, 5), + GATE(SYS_CLK_SPDIF_OUT, "spdif_out_sys", "sys", 0x8, 6), + GATE(SYS_CLK_SPI0_MASTER, "spi0_master_sys", "sys", 0x8, 7), + GATE(SYS_CLK_SPI0_SLAVE, "spi0_slave_sys", "sys", 0x8, 8), + GATE(SYS_CLK_PWM, "pwm_sys", "sys", 0x8, 9), + GATE(SYS_CLK_UART0, "uart0_sys", "sys", 0x8, 10), + GATE(SYS_CLK_UART1, "uart1_sys", "sys", 0x8, 11), + GATE(SYS_CLK_SPI1, "spi1_sys", "sys", 0x8, 12), + GATE(SYS_CLK_MDC, "mdc_sys", "sys", 0x8, 13), + GATE(SYS_CLK_SD_HOST, "sd_host_sys", "sys", 0x8, 14), + GATE(SYS_CLK_ENET, "enet_sys", "sys", 0x8, 15), + GATE(SYS_CLK_IR, "ir_sys", "sys", 0x8, 16), + GATE(SYS_CLK_WD, "wd_sys", "sys", 0x8, 17), + GATE(SYS_CLK_TIMER, "timer_sys", "sys", 0x8, 18), + GATE(SYS_CLK_I2S_OUT, "i2s_out_sys", "sys", 0x8, 24), + GATE(SYS_CLK_SPDIF_IN, "spdif_in_sys", "sys", 0x8, 25), + GATE(SYS_CLK_EVENT_TIMER, "event_timer_sys", "sys", 0x8, 26), + GATE(SYS_CLK_HASH, "hash_sys", "sys", 0x8, 27), +}; + +static void __init pistachio_cr_periph_init(struct device_node *np) +{ + struct pistachio_clk_provider *p; + + p = pistachio_clk_alloc_provider(np, SYS_CLK_NR_CLKS); + if (!p) + return; + + pistachio_clk_register_gate(p, pistachio_sys_gates, + ARRAY_SIZE(pistachio_sys_gates)); + + pistachio_clk_register_provider(p); +} +CLK_OF_DECLARE(pistachio_cr_periph, "img,pistachio-cr-periph", + pistachio_cr_periph_init); + +static struct pistachio_gate pistachio_ext_gates[] __initdata = { + GATE(EXT_CLK_ENET_IN, "enet_clk_in_gate", "enet_clk_in", 0x58, 5), + GATE(EXT_CLK_AUDIO_IN, "audio_clk_in_gate", "audio_clk_in", 0x58, 8) +}; + +static void __init pistachio_cr_top_init(struct device_node *np) +{ + struct pistachio_clk_provider *p; + + p = pistachio_clk_alloc_provider(np, EXT_CLK_NR_CLKS); + if (!p) + return; + + pistachio_clk_register_gate(p, pistachio_ext_gates, + ARRAY_SIZE(pistachio_ext_gates)); + + pistachio_clk_register_provider(p); +} +CLK_OF_DECLARE(pistachio_cr_top, "img,pistachio-cr-top", + pistachio_cr_top_init); diff --git a/drivers/clk/pistachio/clk-pll.c b/drivers/clk/pistachio/clk-pll.c new file mode 100644 index 000000000000..de537560bf70 --- /dev/null +++ b/drivers/clk/pistachio/clk-pll.c @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#include "clk.h" + +#define PLL_STATUS 0x0 +#define PLL_STATUS_LOCK BIT(0) + +#define PLL_CTRL1 0x4 +#define PLL_CTRL1_REFDIV_SHIFT 0 +#define PLL_CTRL1_REFDIV_MASK 0x3f +#define PLL_CTRL1_FBDIV_SHIFT 6 +#define PLL_CTRL1_FBDIV_MASK 0xfff +#define PLL_INT_CTRL1_POSTDIV1_SHIFT 18 +#define PLL_INT_CTRL1_POSTDIV1_MASK 0x7 +#define PLL_INT_CTRL1_POSTDIV2_SHIFT 21 +#define PLL_INT_CTRL1_POSTDIV2_MASK 0x7 +#define PLL_INT_CTRL1_PD BIT(24) +#define PLL_INT_CTRL1_DSMPD BIT(25) +#define PLL_INT_CTRL1_FOUTPOSTDIVPD BIT(26) +#define PLL_INT_CTRL1_FOUTVCOPD BIT(27) + +#define PLL_CTRL2 0x8 +#define PLL_FRAC_CTRL2_FRAC_SHIFT 0 +#define PLL_FRAC_CTRL2_FRAC_MASK 0xffffff +#define PLL_FRAC_CTRL2_POSTDIV1_SHIFT 24 +#define PLL_FRAC_CTRL2_POSTDIV1_MASK 0x7 +#define PLL_FRAC_CTRL2_POSTDIV2_SHIFT 27 +#define PLL_FRAC_CTRL2_POSTDIV2_MASK 0x7 +#define PLL_INT_CTRL2_BYPASS BIT(28) + +#define PLL_CTRL3 0xc +#define PLL_FRAC_CTRL3_PD BIT(0) +#define PLL_FRAC_CTRL3_DACPD BIT(1) +#define PLL_FRAC_CTRL3_DSMPD BIT(2) +#define PLL_FRAC_CTRL3_FOUTPOSTDIVPD BIT(3) +#define PLL_FRAC_CTRL3_FOUT4PHASEPD BIT(4) +#define PLL_FRAC_CTRL3_FOUTVCOPD BIT(5) + +#define PLL_CTRL4 0x10 +#define PLL_FRAC_CTRL4_BYPASS BIT(28) + +struct pistachio_clk_pll { + struct clk_hw hw; + void __iomem *base; + struct pistachio_pll_rate_table *rates; + unsigned int nr_rates; +}; + +static inline u32 pll_readl(struct pistachio_clk_pll *pll, u32 reg) +{ + return readl(pll->base + reg); +} + +static inline void pll_writel(struct pistachio_clk_pll *pll, u32 val, u32 reg) +{ + writel(val, pll->base + reg); +} + +static inline u32 do_div_round_closest(u64 dividend, u32 divisor) +{ + dividend += divisor / 2; + do_div(dividend, divisor); + + return dividend; +} + +static inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw) +{ + return container_of(hw, struct pistachio_clk_pll, hw); +} + +static struct pistachio_pll_rate_table * +pll_get_params(struct pistachio_clk_pll *pll, unsigned long fref, + unsigned long fout) +{ + unsigned int i; + + for (i = 0; i < pll->nr_rates; i++) { + if (pll->rates[i].fref == fref && pll->rates[i].fout == fout) + return &pll->rates[i]; + } + + return NULL; +} + +static long pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + unsigned int i; + + for (i = 0; i < pll->nr_rates; i++) { + if (i > 0 && pll->rates[i].fref == *parent_rate && + pll->rates[i].fout <= rate) + return pll->rates[i - 1].fout; + } + + return pll->rates[0].fout; +} + +static int pll_gf40lp_frac_enable(struct clk_hw *hw) +{ + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + u32 val; + + val = pll_readl(pll, PLL_CTRL3); + val &= ~(PLL_FRAC_CTRL3_PD | PLL_FRAC_CTRL3_DACPD | + PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_FOUTPOSTDIVPD | + PLL_FRAC_CTRL3_FOUT4PHASEPD | PLL_FRAC_CTRL3_FOUTVCOPD); + pll_writel(pll, val, PLL_CTRL3); + + val = pll_readl(pll, PLL_CTRL4); + val &= ~PLL_FRAC_CTRL4_BYPASS; + pll_writel(pll, val, PLL_CTRL4); + + return 0; +} + +static void pll_gf40lp_frac_disable(struct clk_hw *hw) +{ + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + u32 val; + + val = pll_readl(pll, PLL_CTRL3); + val |= PLL_FRAC_CTRL3_PD; + pll_writel(pll, val, PLL_CTRL3); +} + +static int pll_gf40lp_frac_is_enabled(struct clk_hw *hw) +{ + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + + return !(pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_PD); +} + +static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + struct pistachio_pll_rate_table *params; + bool was_enabled; + u32 val; + + params = pll_get_params(pll, parent_rate, rate); + if (!params) + return -EINVAL; + + was_enabled = pll_gf40lp_frac_is_enabled(hw); + if (!was_enabled) + pll_gf40lp_frac_enable(hw); + + val = pll_readl(pll, PLL_CTRL1); + val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | + (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT)); + val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) | + (params->fbdiv << PLL_CTRL1_FBDIV_SHIFT); + pll_writel(pll, val, PLL_CTRL1); + + val = pll_readl(pll, PLL_CTRL2); + val &= ~((PLL_FRAC_CTRL2_FRAC_MASK << PLL_FRAC_CTRL2_FRAC_SHIFT) | + (PLL_FRAC_CTRL2_POSTDIV1_MASK << + PLL_FRAC_CTRL2_POSTDIV1_SHIFT) | + (PLL_FRAC_CTRL2_POSTDIV2_MASK << + PLL_FRAC_CTRL2_POSTDIV2_SHIFT)); + val |= (params->frac << PLL_FRAC_CTRL2_FRAC_SHIFT) | + (params->postdiv1 << PLL_FRAC_CTRL2_POSTDIV1_SHIFT) | + (params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT); + pll_writel(pll, val, PLL_CTRL2); + + while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK)) + cpu_relax(); + + if (!was_enabled) + pll_gf40lp_frac_disable(hw); + + return 0; +} + +static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + u32 val, prediv, fbdiv, frac, postdiv1, postdiv2; + u64 rate = parent_rate; + + val = pll_readl(pll, PLL_CTRL1); + prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK; + fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK; + + val = pll_readl(pll, PLL_CTRL2); + postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) & + PLL_FRAC_CTRL2_POSTDIV1_MASK; + postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) & + PLL_FRAC_CTRL2_POSTDIV2_MASK; + frac = (val >> PLL_FRAC_CTRL2_FRAC_SHIFT) & PLL_FRAC_CTRL2_FRAC_MASK; + + rate *= (fbdiv << 24) + frac; + rate = do_div_round_closest(rate, (prediv * postdiv1 * postdiv2) << 24); + + return rate; +} + +static struct clk_ops pll_gf40lp_frac_ops = { + .enable = pll_gf40lp_frac_enable, + .disable = pll_gf40lp_frac_disable, + .is_enabled = pll_gf40lp_frac_is_enabled, + .recalc_rate = pll_gf40lp_frac_recalc_rate, + .round_rate = pll_round_rate, + .set_rate = pll_gf40lp_frac_set_rate, +}; + +static struct clk_ops pll_gf40lp_frac_fixed_ops = { + .enable = pll_gf40lp_frac_enable, + .disable = pll_gf40lp_frac_disable, + .is_enabled = pll_gf40lp_frac_is_enabled, + .recalc_rate = pll_gf40lp_frac_recalc_rate, +}; + +static int pll_gf40lp_laint_enable(struct clk_hw *hw) +{ + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + u32 val; + + val = pll_readl(pll, PLL_CTRL1); + val &= ~(PLL_INT_CTRL1_PD | PLL_INT_CTRL1_DSMPD | + PLL_INT_CTRL1_FOUTPOSTDIVPD | PLL_INT_CTRL1_FOUTVCOPD); + pll_writel(pll, val, PLL_CTRL1); + + val = pll_readl(pll, PLL_CTRL2); + val &= ~PLL_INT_CTRL2_BYPASS; + pll_writel(pll, val, PLL_CTRL2); + + return 0; +} + +static void pll_gf40lp_laint_disable(struct clk_hw *hw) +{ + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + u32 val; + + val = pll_readl(pll, PLL_CTRL1); + val |= PLL_INT_CTRL1_PD; + pll_writel(pll, val, PLL_CTRL1); +} + +static int pll_gf40lp_laint_is_enabled(struct clk_hw *hw) +{ + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + + return !(pll_readl(pll, PLL_CTRL1) & PLL_INT_CTRL1_PD); +} + +static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + struct pistachio_pll_rate_table *params; + bool was_enabled; + u32 val; + + params = pll_get_params(pll, parent_rate, rate); + if (!params) + return -EINVAL; + + was_enabled = pll_gf40lp_laint_is_enabled(hw); + if (!was_enabled) + pll_gf40lp_laint_enable(hw); + + val = pll_readl(pll, PLL_CTRL1); + val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | + (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) | + (PLL_INT_CTRL1_POSTDIV1_MASK << PLL_INT_CTRL1_POSTDIV1_SHIFT) | + (PLL_INT_CTRL1_POSTDIV2_MASK << PLL_INT_CTRL1_POSTDIV2_SHIFT)); + val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) | + (params->fbdiv << PLL_CTRL1_FBDIV_SHIFT) | + (params->postdiv1 << PLL_INT_CTRL1_POSTDIV1_SHIFT) | + (params->postdiv2 << PLL_INT_CTRL1_POSTDIV2_SHIFT); + pll_writel(pll, val, PLL_CTRL1); + + while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK)) + cpu_relax(); + + if (!was_enabled) + pll_gf40lp_laint_disable(hw); + + return 0; +} + +static unsigned long pll_gf40lp_laint_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + u32 val, prediv, fbdiv, postdiv1, postdiv2; + u64 rate = parent_rate; + + val = pll_readl(pll, PLL_CTRL1); + prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK; + fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK; + postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) & + PLL_INT_CTRL1_POSTDIV1_MASK; + postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) & + PLL_INT_CTRL1_POSTDIV2_MASK; + + rate *= fbdiv; + rate = do_div_round_closest(rate, prediv * postdiv1 * postdiv2); + + return rate; +} + +static struct clk_ops pll_gf40lp_laint_ops = { + .enable = pll_gf40lp_laint_enable, + .disable = pll_gf40lp_laint_disable, + .is_enabled = pll_gf40lp_laint_is_enabled, + .recalc_rate = pll_gf40lp_laint_recalc_rate, + .round_rate = pll_round_rate, + .set_rate = pll_gf40lp_laint_set_rate, +}; + +static struct clk_ops pll_gf40lp_laint_fixed_ops = { + .enable = pll_gf40lp_laint_enable, + .disable = pll_gf40lp_laint_disable, + .is_enabled = pll_gf40lp_laint_is_enabled, + .recalc_rate = pll_gf40lp_laint_recalc_rate, +}; + +static struct clk *pll_register(const char *name, const char *parent_name, + unsigned long flags, void __iomem *base, + enum pistachio_pll_type type, + struct pistachio_pll_rate_table *rates, + unsigned int nr_rates) +{ + struct pistachio_clk_pll *pll; + struct clk_init_data init; + struct clk *clk; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = flags | CLK_GET_RATE_NOCACHE; + init.parent_names = &parent_name; + init.num_parents = 1; + + switch (type) { + case PLL_GF40LP_FRAC: + if (rates) + init.ops = &pll_gf40lp_frac_ops; + else + init.ops = &pll_gf40lp_frac_fixed_ops; + break; + case PLL_GF40LP_LAINT: + if (rates) + init.ops = &pll_gf40lp_laint_ops; + else + init.ops = &pll_gf40lp_laint_fixed_ops; + break; + default: + pr_err("Unrecognized PLL type %u\n", type); + kfree(pll); + return ERR_PTR(-EINVAL); + } + + pll->hw.init = &init; + pll->base = base; + pll->rates = rates; + pll->nr_rates = nr_rates; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +void pistachio_clk_register_pll(struct pistachio_clk_provider *p, + struct pistachio_pll *pll, + unsigned int num) +{ + struct clk *clk; + unsigned int i; + + for (i = 0; i < num; i++) { + clk = pll_register(pll[i].name, pll[i].parent, + 0, p->base + pll[i].reg_base, + pll[i].type, pll[i].rates, + pll[i].nr_rates); + p->clk_data.clks[pll[i].id] = clk; + } +} diff --git a/drivers/clk/pistachio/clk.c b/drivers/clk/pistachio/clk.c new file mode 100644 index 000000000000..85faa83e1bd7 --- /dev/null +++ b/drivers/clk/pistachio/clk.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/clk-provider.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> + +#include "clk.h" + +struct pistachio_clk_provider * +pistachio_clk_alloc_provider(struct device_node *node, unsigned int num_clks) +{ + struct pistachio_clk_provider *p; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return p; + + p->clk_data.clks = kcalloc(num_clks, sizeof(struct clk *), GFP_KERNEL); + if (!p->clk_data.clks) + goto free_provider; + p->clk_data.clk_num = num_clks; + p->node = node; + p->base = of_iomap(node, 0); + if (!p->base) { + pr_err("Failed to map clock provider registers\n"); + goto free_clks; + } + + return p; + +free_clks: + kfree(p->clk_data.clks); +free_provider: + kfree(p); + return NULL; +} + +void pistachio_clk_register_provider(struct pistachio_clk_provider *p) +{ + unsigned int i; + + for (i = 0; i < p->clk_data.clk_num; i++) { + if (IS_ERR(p->clk_data.clks[i])) + pr_warn("Failed to register clock %d: %ld\n", i, + PTR_ERR(p->clk_data.clks[i])); + } + + of_clk_add_provider(p->node, of_clk_src_onecell_get, &p->clk_data); +} + +void pistachio_clk_register_gate(struct pistachio_clk_provider *p, + struct pistachio_gate *gate, + unsigned int num) +{ + struct clk *clk; + unsigned int i; + + for (i = 0; i < num; i++) { + clk = clk_register_gate(NULL, gate[i].name, gate[i].parent, + CLK_SET_RATE_PARENT, + p->base + gate[i].reg, gate[i].shift, + 0, NULL); + p->clk_data.clks[gate[i].id] = clk; + } +} + +void pistachio_clk_register_mux(struct pistachio_clk_provider *p, + struct pistachio_mux *mux, + unsigned int num) +{ + struct clk *clk; + unsigned int i; + + for (i = 0; i < num; i++) { + clk = clk_register_mux(NULL, mux[i].name, mux[i].parents, + mux[i].num_parents, + CLK_SET_RATE_NO_REPARENT, + p->base + mux[i].reg, mux[i].shift, + get_count_order(mux[i].num_parents), + 0, NULL); + p->clk_data.clks[mux[i].id] = clk; + } +} + +void pistachio_clk_register_div(struct pistachio_clk_provider *p, + struct pistachio_div *div, + unsigned int num) +{ + struct clk *clk; + unsigned int i; + + for (i = 0; i < num; i++) { + clk = clk_register_divider(NULL, div[i].name, div[i].parent, + 0, p->base + div[i].reg, 0, + div[i].width, div[i].div_flags, + NULL); + p->clk_data.clks[div[i].id] = clk; + } +} + +void pistachio_clk_register_fixed_factor(struct pistachio_clk_provider *p, + struct pistachio_fixed_factor *ff, + unsigned int num) +{ + struct clk *clk; + unsigned int i; + + for (i = 0; i < num; i++) { + clk = clk_register_fixed_factor(NULL, ff[i].name, ff[i].parent, + 0, 1, ff[i].div); + p->clk_data.clks[ff[i].id] = clk; + } +} + +void pistachio_clk_force_enable(struct pistachio_clk_provider *p, + unsigned int *clk_ids, unsigned int num) +{ + unsigned int i; + int err; + + for (i = 0; i < num; i++) { + struct clk *clk = p->clk_data.clks[clk_ids[i]]; + + if (IS_ERR(clk)) + continue; + + err = clk_prepare_enable(clk); + if (err) + pr_err("Failed to enable clock %s: %d\n", + __clk_get_name(clk), err); + } +} diff --git a/drivers/clk/pistachio/clk.h b/drivers/clk/pistachio/clk.h new file mode 100644 index 000000000000..52fabbc24624 --- /dev/null +++ b/drivers/clk/pistachio/clk.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef __PISTACHIO_CLK_H +#define __PISTACHIO_CLK_H + +#include <linux/clk-provider.h> + +struct pistachio_gate { + unsigned int id; + unsigned long reg; + unsigned int shift; + const char *name; + const char *parent; +}; + +#define GATE(_id, _name, _pname, _reg, _shift) \ + { \ + .id = _id, \ + .reg = _reg, \ + .shift = _shift, \ + .name = _name, \ + .parent = _pname, \ + } + +struct pistachio_mux { + unsigned int id; + unsigned long reg; + unsigned int shift; + unsigned int num_parents; + const char *name; + const char **parents; +}; + +#define PNAME(x) static const char *x[] __initconst + +#define MUX(_id, _name, _pnames, _reg, _shift) \ + { \ + .id = _id, \ + .reg = _reg, \ + .shift = _shift, \ + .name = _name, \ + .parents = _pnames, \ + .num_parents = ARRAY_SIZE(_pnames) \ + } + + +struct pistachio_div { + unsigned int id; + unsigned long reg; + unsigned int width; + unsigned int div_flags; + const char *name; + const char *parent; +}; + +#define DIV(_id, _name, _pname, _reg, _width) \ + { \ + .id = _id, \ + .reg = _reg, \ + .width = _width, \ + .div_flags = 0, \ + .name = _name, \ + .parent = _pname, \ + } + +#define DIV_F(_id, _name, _pname, _reg, _width, _div_flags) \ + { \ + .id = _id, \ + .reg = _reg, \ + .width = _width, \ + .div_flags = _div_flags, \ + .name = _name, \ + .parent = _pname, \ + } + +struct pistachio_fixed_factor { + unsigned int id; + unsigned int div; + const char *name; + const char *parent; +}; + +#define FIXED_FACTOR(_id, _name, _pname, _div) \ + { \ + .id = _id, \ + .div = _div, \ + .name = _name, \ + .parent = _pname, \ + } + +struct pistachio_pll_rate_table { + unsigned long fref; + unsigned long fout; + unsigned int refdiv; + unsigned int fbdiv; + unsigned int postdiv1; + unsigned int postdiv2; + unsigned int frac; +}; + +enum pistachio_pll_type { + PLL_GF40LP_LAINT, + PLL_GF40LP_FRAC, +}; + +struct pistachio_pll { + unsigned int id; + unsigned long reg_base; + enum pistachio_pll_type type; + struct pistachio_pll_rate_table *rates; + unsigned int nr_rates; + const char *name; + const char *parent; +}; + +#define PLL(_id, _name, _pname, _type, _reg, _rates) \ + { \ + .id = _id, \ + .reg_base = _reg, \ + .type = _type, \ + .rates = _rates, \ + .nr_rates = ARRAY_SIZE(_rates), \ + .name = _name, \ + .parent = _pname, \ + } + +#define PLL_FIXED(_id, _name, _pname, _type, _reg) \ + { \ + .id = _id, \ + .reg_base = _reg, \ + .type = _type, \ + .rates = NULL, \ + .nr_rates = 0, \ + .name = _name, \ + .parent = _pname, \ + } + +struct pistachio_clk_provider { + struct device_node *node; + void __iomem *base; + struct clk_onecell_data clk_data; +}; + +extern struct pistachio_clk_provider * +pistachio_clk_alloc_provider(struct device_node *node, unsigned int num_clks); +extern void pistachio_clk_register_provider(struct pistachio_clk_provider *p); + +extern void pistachio_clk_register_gate(struct pistachio_clk_provider *p, + struct pistachio_gate *gate, + unsigned int num); +extern void pistachio_clk_register_mux(struct pistachio_clk_provider *p, + struct pistachio_mux *mux, + unsigned int num); +extern void pistachio_clk_register_div(struct pistachio_clk_provider *p, + struct pistachio_div *div, + unsigned int num); +extern void +pistachio_clk_register_fixed_factor(struct pistachio_clk_provider *p, + struct pistachio_fixed_factor *ff, + unsigned int num); +extern void pistachio_clk_register_pll(struct pistachio_clk_provider *p, + struct pistachio_pll *pll, + unsigned int num); + +extern void pistachio_clk_force_enable(struct pistachio_clk_provider *p, + unsigned int *clk_ids, unsigned int num); + +#endif diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c index d3458474eb3a..c66f7bc2ae87 100644 --- a/drivers/clk/qcom/gcc-msm8916.c +++ b/drivers/clk/qcom/gcc-msm8916.c @@ -71,8 +71,8 @@ static const char *gcc_xo_gpll0_bimc[] = { static const struct parent_map gcc_xo_gpll0a_gpll1_gpll2a_map[] = { { P_XO, 0 }, { P_GPLL0_AUX, 3 }, - { P_GPLL2_AUX, 2 }, { P_GPLL1, 1 }, + { P_GPLL2_AUX, 2 }, }; static const char *gcc_xo_gpll0a_gpll1_gpll2a[] = { @@ -1115,7 +1115,7 @@ static struct clk_rcg2 usb_hs_system_clk_src = { static const struct freq_tbl ftbl_gcc_venus0_vcodec0_clk[] = { F(100000000, P_GPLL0, 8, 0, 0), F(160000000, P_GPLL0, 5, 0, 0), - F(228570000, P_GPLL0, 5, 0, 0), + F(228570000, P_GPLL0, 3.5, 0, 0), { } }; diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 17e9af7fe81f..a17683b2cf27 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5260) += clk-exynos5260.o obj-$(CONFIG_SOC_EXYNOS5410) += clk-exynos5410.o obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o -obj-$(CONFIG_ARCH_EXYNOS5433) += clk-exynos5433.o +obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos5433.o obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-clkout.o diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 07d666cc6a29..bea4a173eef5 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -271,6 +271,7 @@ static const struct samsung_clk_reg_dump exynos5420_set_clksrc[] = { { .offset = SRC_MASK_PERIC0, .value = 0x11111110, }, { .offset = SRC_MASK_PERIC1, .value = 0x11111100, }, { .offset = SRC_MASK_ISP, .value = 0x11111000, }, + { .offset = GATE_BUS_TOP, .value = 0xffffffff, }, { .offset = GATE_BUS_DISP1, .value = 0xffffffff, }, { .offset = GATE_IP_PERIC, .value = 0xffffffff, }, }; diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 387e3e39e635..9e04ae2bb4d7 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -748,7 +748,7 @@ static struct samsung_pll_rate_table exynos5443_pll_rates[] = { PLL_35XX_RATE(825000000U, 275, 4, 1), PLL_35XX_RATE(800000000U, 400, 6, 1), PLL_35XX_RATE(733000000U, 733, 12, 1), - PLL_35XX_RATE(700000000U, 360, 6, 1), + PLL_35XX_RATE(700000000U, 175, 3, 1), PLL_35XX_RATE(667000000U, 222, 4, 1), PLL_35XX_RATE(633000000U, 211, 4, 1), PLL_35XX_RATE(600000000U, 500, 5, 2), @@ -760,14 +760,14 @@ static struct samsung_pll_rate_table exynos5443_pll_rates[] = { PLL_35XX_RATE(444000000U, 370, 5, 2), PLL_35XX_RATE(420000000U, 350, 5, 2), PLL_35XX_RATE(400000000U, 400, 6, 2), - PLL_35XX_RATE(350000000U, 360, 6, 2), + PLL_35XX_RATE(350000000U, 350, 6, 2), PLL_35XX_RATE(333000000U, 222, 4, 2), PLL_35XX_RATE(300000000U, 500, 5, 3), PLL_35XX_RATE(266000000U, 532, 6, 3), PLL_35XX_RATE(200000000U, 400, 6, 3), PLL_35XX_RATE(166000000U, 332, 6, 3), PLL_35XX_RATE(160000000U, 320, 6, 3), - PLL_35XX_RATE(133000000U, 552, 6, 4), + PLL_35XX_RATE(133000000U, 532, 6, 4), PLL_35XX_RATE(100000000U, 400, 6, 4), { /* sentinel */ } }; @@ -1490,7 +1490,7 @@ static struct samsung_gate_clock mif_gate_clks[] __initdata = { /* ENABLE_PCLK_MIF_SECURE_MONOTONIC_CNT */ GATE(CLK_PCLK_MONOTONIC_CNT, "pclk_monotonic_cnt", "div_aclk_mif_133", - ENABLE_PCLK_MIF_SECURE_RTC, 0, 0, 0), + ENABLE_PCLK_MIF_SECURE_MONOTONIC_CNT, 0, 0, 0), /* ENABLE_PCLK_MIF_SECURE_RTC */ GATE(CLK_PCLK_RTC, "pclk_rtc", "div_aclk_mif_133", @@ -3665,7 +3665,7 @@ static struct samsung_gate_clock apollo_gate_clks[] __initdata = { ENABLE_SCLK_APOLLO, 3, CLK_IGNORE_UNUSED, 0), GATE(CLK_SCLK_HPM_APOLLO, "sclk_hpm_apollo", "div_sclk_hpm_apollo", ENABLE_SCLK_APOLLO, 1, CLK_IGNORE_UNUSED, 0), - GATE(CLK_SCLK_APOLLO, "sclk_apollo", "div_apollo_pll", + GATE(CLK_SCLK_APOLLO, "sclk_apollo", "div_apollo2", ENABLE_SCLK_APOLLO, 0, CLK_IGNORE_UNUSED, 0), }; @@ -3927,7 +3927,7 @@ CLK_OF_DECLARE(exynos5433_cmu_atlas, "samsung,exynos5433-cmu-atlas", #define ENABLE_PCLK_MSCL 0x0900 #define ENABLE_PCLK_MSCL_SECURE_SMMU_M2MSCALER0 0x0904 #define ENABLE_PCLK_MSCL_SECURE_SMMU_M2MSCALER1 0x0908 -#define ENABLE_PCLK_MSCL_SECURE_SMMU_JPEG 0x000c +#define ENABLE_PCLK_MSCL_SECURE_SMMU_JPEG 0x090c #define ENABLE_SCLK_MSCL 0x0a00 #define ENABLE_IP_MSCL0 0x0b00 #define ENABLE_IP_MSCL1 0x0b04 diff --git a/drivers/clk/shmobile/Makefile b/drivers/clk/shmobile/Makefile index 0689d7fb2666..97c71c885e4f 100644 --- a/drivers/clk/shmobile/Makefile +++ b/drivers/clk/shmobile/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o obj-$(CONFIG_ARCH_R8A73A4) += clk-r8a73a4.o obj-$(CONFIG_ARCH_R8A7740) += clk-r8a7740.o +obj-$(CONFIG_ARCH_R8A7778) += clk-r8a7778.o obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o diff --git a/drivers/clk/shmobile/clk-r8a7778.c b/drivers/clk/shmobile/clk-r8a7778.c new file mode 100644 index 000000000000..cb33b57274bf --- /dev/null +++ b/drivers/clk/shmobile/clk-r8a7778.c @@ -0,0 +1,143 @@ +/* + * r8a7778 Core CPG Clocks + * + * Copyright (C) 2014 Ulrich Hecht + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/clk/shmobile.h> +#include <linux/of_address.h> + +struct r8a7778_cpg { + struct clk_onecell_data data; + spinlock_t lock; + void __iomem *reg; +}; + +/* PLL multipliers per bits 11, 12, and 18 of MODEMR */ +struct { + unsigned long plla_mult; + unsigned long pllb_mult; +} r8a7778_rates[] __initdata = { + [0] = { 21, 21 }, + [1] = { 24, 24 }, + [2] = { 28, 28 }, + [3] = { 32, 32 }, + [5] = { 24, 21 }, + [6] = { 28, 21 }, + [7] = { 32, 24 }, +}; + +/* Clock dividers per bits 1 and 2 of MODEMR */ +struct { + const char *name; + unsigned int div[4]; +} r8a7778_divs[6] __initdata = { + { "b", { 12, 12, 16, 18 } }, + { "out", { 12, 12, 16, 18 } }, + { "p", { 16, 12, 16, 12 } }, + { "s", { 4, 3, 4, 3 } }, + { "s1", { 8, 6, 8, 6 } }, +}; + +static u32 cpg_mode_rates __initdata; +static u32 cpg_mode_divs __initdata; + +static struct clk * __init +r8a7778_cpg_register_clock(struct device_node *np, struct r8a7778_cpg *cpg, + const char *name) +{ + if (!strcmp(name, "plla")) { + return clk_register_fixed_factor(NULL, "plla", + of_clk_get_parent_name(np, 0), 0, + r8a7778_rates[cpg_mode_rates].plla_mult, 1); + } else if (!strcmp(name, "pllb")) { + return clk_register_fixed_factor(NULL, "pllb", + of_clk_get_parent_name(np, 0), 0, + r8a7778_rates[cpg_mode_rates].pllb_mult, 1); + } else { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(r8a7778_divs); i++) { + if (!strcmp(name, r8a7778_divs[i].name)) { + return clk_register_fixed_factor(NULL, + r8a7778_divs[i].name, + "plla", 0, 1, + r8a7778_divs[i].div[cpg_mode_divs]); + } + } + } + + return ERR_PTR(-EINVAL); +} + + +static void __init r8a7778_cpg_clocks_init(struct device_node *np) +{ + struct r8a7778_cpg *cpg; + struct clk **clks; + unsigned int i; + int num_clks; + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (num_clks < 0) { + pr_err("%s: failed to count clocks\n", __func__); + return; + } + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); + if (cpg == NULL || clks == NULL) { + /* We're leaking memory on purpose, there's no point in cleaning + * up as the system won't boot anyway. + */ + return; + } + + spin_lock_init(&cpg->lock); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + cpg->reg = of_iomap(np, 0); + if (WARN_ON(cpg->reg == NULL)) + return; + + for (i = 0; i < num_clks; ++i) { + const char *name; + struct clk *clk; + + of_property_read_string_index(np, "clock-output-names", i, + &name); + + clk = r8a7778_cpg_register_clock(np, cpg, name); + if (IS_ERR(clk)) + pr_err("%s: failed to register %s %s clock (%ld)\n", + __func__, np->name, name, PTR_ERR(clk)); + else + cpg->data.clks[i] = clk; + } + + of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); +} + +CLK_OF_DECLARE(r8a7778_cpg_clks, "renesas,r8a7778-cpg-clocks", + r8a7778_cpg_clocks_init); + +void __init r8a7778_clocks_init(u32 mode) +{ + BUG_ON(!(mode & BIT(19))); + + cpg_mode_rates = (!!(mode & BIT(18)) << 2) | + (!!(mode & BIT(12)) << 1) | + (!!(mode & BIT(11))); + cpg_mode_divs = (!!(mode & BIT(2)) << 1) | + (!!(mode & BIT(1))); + + of_clk_init(NULL); +} |