diff options
Diffstat (limited to 'drivers/clk')
127 files changed, 11280 insertions, 737 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 6a8ac04bedeb..56c1998ced3e 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -33,7 +33,7 @@ source "drivers/clk/versatile/Kconfig" config COMMON_CLK_MAX77686 tristate "Clock driver for Maxim 77620/77686/77802 MFD" - depends on MFD_MAX77686 || MFD_MAX77620 + depends on MFD_MAX77686 || MFD_MAX77620 || COMPILE_TEST ---help--- This driver supports Maxim 77620/77686/77802 crystal oscillator clock. @@ -119,7 +119,7 @@ config COMMON_CLK_CS2000_CP config COMMON_CLK_S2MPS11 tristate "Clock driver for S2MPS1X/S5M8767 MFD" - depends on MFD_SEC_CORE + depends on MFD_SEC_CORE || COMPILE_TEST ---help--- This driver supports S2MPS11/S2MPS14/S5M8767 crystal oscillator clock. These multi-function devices have two (S2MPS14) or three diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig index e3eed5a78404..b5ae5311b0a2 100644 --- a/drivers/clk/bcm/Kconfig +++ b/drivers/clk/bcm/Kconfig @@ -1,7 +1,6 @@ config CLK_BCM_63XX bool "Broadcom BCM63xx clock support" depends on ARCH_BCM_63XX || COMPILE_TEST - depends on COMMON_CLK select COMMON_CLK_IPROC default ARCH_BCM_63XX help @@ -11,27 +10,22 @@ config CLK_BCM_63XX config CLK_BCM_KONA bool "Broadcom Kona CCU clock support" depends on ARCH_BCM_MOBILE || COMPILE_TEST - depends on COMMON_CLK - default y + default ARCH_BCM_MOBILE help Enable common clock framework support for Broadcom SoCs using "Kona" style clock control units, including those in the BCM281xx and BCM21664 families. config COMMON_CLK_IPROC - bool "Broadcom iProc clock support" - depends on ARCH_BCM_IPROC || ARCH_BCM_63XX || COMPILE_TEST - depends on COMMON_CLK - default ARCH_BCM_IPROC + bool help Enable common clock framework support for Broadcom SoCs based on the iProc architecture -if COMMON_CLK_IPROC - config CLK_BCM_CYGNUS bool "Broadcom Cygnus clock support" depends on ARCH_BCM_CYGNUS || COMPILE_TEST + select COMMON_CLK_IPROC default ARCH_BCM_CYGNUS help Enable common clock framework support for the Broadcom Cygnus SoC @@ -39,6 +33,7 @@ config CLK_BCM_CYGNUS config CLK_BCM_NSP bool "Broadcom Northstar/Northstar Plus clock support" depends on ARCH_BCM_5301X || ARCH_BCM_NSP || COMPILE_TEST + select COMMON_CLK_IPROC default ARCH_BCM_5301X || ARCH_BCM_NSP help Enable common clock framework support for the Broadcom Northstar and @@ -47,8 +42,7 @@ config CLK_BCM_NSP config CLK_BCM_NS2 bool "Broadcom Northstar 2 clock support" depends on ARCH_BCM_IPROC || COMPILE_TEST + select COMMON_CLK_IPROC default ARCH_BCM_IPROC help Enable common clock framework support for the Broadcom Northstar 2 SoC - -endif diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 8c7763fd9efc..0d14409097e7 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -436,6 +436,9 @@ struct bcm2835_clock_data { const char *const *parents; int num_mux_parents; + /* Bitmap encoding which parents accept rate change propagation. */ + unsigned int set_rate_parent; + u32 ctl_reg; u32 div_reg; @@ -751,7 +754,9 @@ static void bcm2835_pll_divider_off(struct clk_hw *hw) cprman_write(cprman, data->cm_reg, (cprman_read(cprman, data->cm_reg) & ~data->load_mask) | data->hold_mask); - cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE); + cprman_write(cprman, data->a2w_reg, + cprman_read(cprman, data->a2w_reg) | + A2W_PLL_CHANNEL_DISABLE); spin_unlock(&cprman->regs_lock); } @@ -1015,10 +1020,60 @@ bcm2835_clk_is_pllc(struct clk_hw *hw) return strncmp(clk_hw_get_name(hw), "pllc", 4) == 0; } +static unsigned long bcm2835_clock_choose_div_and_prate(struct clk_hw *hw, + int parent_idx, + unsigned long rate, + u32 *div, + unsigned long *prate) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + unsigned long best_rate = 0; + u32 curdiv, mindiv, maxdiv; + struct clk_hw *parent; + + parent = clk_hw_get_parent_by_index(hw, parent_idx); + + if (!(BIT(parent_idx) & data->set_rate_parent)) { + *prate = clk_hw_get_rate(parent); + *div = bcm2835_clock_choose_div(hw, rate, *prate, true); + + return bcm2835_clock_rate_from_divisor(clock, *prate, + *div); + } + + if (data->frac_bits) + dev_warn(cprman->dev, + "frac bits are not used when propagating rate change"); + + /* clamp to min divider of 2 if we're dealing with a mash clock */ + mindiv = data->is_mash_clock ? 2 : 1; + maxdiv = BIT(data->int_bits) - 1; + + /* TODO: Be smart, and only test a subset of the available divisors. */ + for (curdiv = mindiv; curdiv <= maxdiv; curdiv++) { + unsigned long tmp_rate; + + tmp_rate = clk_hw_round_rate(parent, rate * curdiv); + tmp_rate /= curdiv; + if (curdiv == mindiv || + (tmp_rate > best_rate && tmp_rate <= rate)) + best_rate = tmp_rate; + + if (best_rate == rate) + break; + } + + *div = curdiv << CM_DIV_FRAC_BITS; + *prate = curdiv * best_rate; + + return best_rate; +} + static int bcm2835_clock_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { - struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); struct clk_hw *parent, *best_parent = NULL; bool current_parent_is_pllc; unsigned long rate, best_rate = 0; @@ -1046,9 +1101,8 @@ static int bcm2835_clock_determine_rate(struct clk_hw *hw, if (bcm2835_clk_is_pllc(parent) && !current_parent_is_pllc) continue; - prate = clk_hw_get_rate(parent); - div = bcm2835_clock_choose_div(hw, req->rate, prate, true); - rate = bcm2835_clock_rate_from_divisor(clock, prate, div); + rate = bcm2835_clock_choose_div_and_prate(hw, i, req->rate, + &div, &prate); if (rate > best_rate && rate <= req->rate) { best_parent = parent; best_prate = prate; @@ -1260,6 +1314,13 @@ static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman, init.name = data->name; init.flags = data->flags | CLK_IGNORE_UNUSED; + /* + * Pass the CLK_SET_RATE_PARENT flag if we are allowed to propagate + * rate changes on at least of the parents. + */ + if (data->set_rate_parent) + init.flags |= CLK_SET_RATE_PARENT; + if (data->is_vpu_clock) { init.ops = &bcm2835_vpu_clock_clk_ops; } else { @@ -1596,7 +1657,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLH_AUX, .load_mask = CM_PLLH_LOADAUX, .hold_mask = 0, - .fixed_divider = 10), + .fixed_divider = 1), [BCM2835_PLLH_PIX] = REGISTER_PLL_DIV( .name = "pllh_pix", .source_pll = "pllh", @@ -1800,7 +1861,12 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .ctl_reg = CM_VECCTL, .div_reg = CM_VECDIV, .int_bits = 4, - .frac_bits = 0), + .frac_bits = 0, + /* + * Allow rate change propagation only on PLLH_AUX which is + * assigned index 7 in the parent array. + */ + .set_rate_parent = BIT(7)), /* dsi clocks */ [BCM2835_CLOCK_DSI0E] = REGISTER_PER_CLK( diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c index b8459c14a1b7..f793b2d9238c 100644 --- a/drivers/clk/clk-cdce925.c +++ b/drivers/clk/clk-cdce925.c @@ -216,7 +216,7 @@ static int cdce925_pll_prepare(struct clk_hw *hw) nn = n * BIT(p); /* q = int(nn/m) */ q = nn / m; - if ((q < 16) || (1 > 64)) { + if ((q < 16) || (q > 63)) { pr_debug("%s invalid q=%d\n", __func__, q); return -EINVAL; } diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c index 8f571548870f..3a218c3a06ae 100644 --- a/drivers/clk/clk-devres.c +++ b/drivers/clk/clk-devres.c @@ -53,3 +53,24 @@ void devm_clk_put(struct device *dev, struct clk *clk) WARN_ON(ret); } EXPORT_SYMBOL(devm_clk_put); + +struct clk *devm_get_clk_from_child(struct device *dev, + struct device_node *np, const char *con_id) +{ + struct clk **ptr, *clk; + + ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + clk = of_clk_get_by_name(np, con_id); + if (!IS_ERR(clk)) { + *ptr = clk; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return clk; +} +EXPORT_SYMBOL(devm_get_clk_from_child); diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 4e691e35483a..4e0c054a787c 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -145,8 +145,8 @@ struct clk_hw *clk_hw_register_gate(struct device *dev, const char *name, init.name = name; init.ops = &clk_gate_ops; init.flags = flags | CLK_IS_BASIC; - init.parent_names = (parent_name ? &parent_name: NULL); - init.num_parents = (parent_name ? 1 : 0); + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; /* struct clk_gate assignments */ gate->reg = reg; diff --git a/drivers/clk/clk-oxnas.c b/drivers/clk/clk-oxnas.c index 47649ac5d399..e51e0023fc6e 100644 --- a/drivers/clk/clk-oxnas.c +++ b/drivers/clk/clk-oxnas.c @@ -20,31 +20,43 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/stringify.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> +#include <dt-bindings/clock/oxsemi,ox810se.h> +#include <dt-bindings/clock/oxsemi,ox820.h> + /* Standard regmap gate clocks */ -struct clk_oxnas { +struct clk_oxnas_gate { struct clk_hw hw; - signed char bit; + unsigned int bit; struct regmap *regmap; }; +struct oxnas_stdclk_data { + struct clk_hw_onecell_data *onecell_data; + struct clk_oxnas_gate **gates; + unsigned int ngates; + struct clk_oxnas_pll **plls; + unsigned int nplls; +}; + /* Regmap offsets */ #define CLK_STAT_REGOFFSET 0x24 #define CLK_SET_REGOFFSET 0x2c #define CLK_CLR_REGOFFSET 0x30 -static inline struct clk_oxnas *to_clk_oxnas(struct clk_hw *hw) +static inline struct clk_oxnas_gate *to_clk_oxnas_gate(struct clk_hw *hw) { - return container_of(hw, struct clk_oxnas, hw); + return container_of(hw, struct clk_oxnas_gate, hw); } -static int oxnas_clk_is_enabled(struct clk_hw *hw) +static int oxnas_clk_gate_is_enabled(struct clk_hw *hw) { - struct clk_oxnas *std = to_clk_oxnas(hw); + struct clk_oxnas_gate *std = to_clk_oxnas_gate(hw); int ret; unsigned int val; @@ -55,29 +67,29 @@ static int oxnas_clk_is_enabled(struct clk_hw *hw) return val & BIT(std->bit); } -static int oxnas_clk_enable(struct clk_hw *hw) +static int oxnas_clk_gate_enable(struct clk_hw *hw) { - struct clk_oxnas *std = to_clk_oxnas(hw); + struct clk_oxnas_gate *std = to_clk_oxnas_gate(hw); regmap_write(std->regmap, CLK_SET_REGOFFSET, BIT(std->bit)); return 0; } -static void oxnas_clk_disable(struct clk_hw *hw) +static void oxnas_clk_gate_disable(struct clk_hw *hw) { - struct clk_oxnas *std = to_clk_oxnas(hw); + struct clk_oxnas_gate *std = to_clk_oxnas_gate(hw); regmap_write(std->regmap, CLK_CLR_REGOFFSET, BIT(std->bit)); } -static const struct clk_ops oxnas_clk_ops = { - .enable = oxnas_clk_enable, - .disable = oxnas_clk_disable, - .is_enabled = oxnas_clk_is_enabled, +static const struct clk_ops oxnas_clk_gate_ops = { + .enable = oxnas_clk_gate_enable, + .disable = oxnas_clk_gate_disable, + .is_enabled = oxnas_clk_gate_is_enabled, }; -static const char *const oxnas_clk_parents[] = { +static const char *const osc_parents[] = { "oscillator", }; @@ -85,63 +97,138 @@ static const char *const eth_parents[] = { "gmacclk", }; -#define DECLARE_STD_CLKP(__clk, __parent) \ -static const struct clk_init_data clk_##__clk##_init = { \ - .name = __stringify(__clk), \ - .ops = &oxnas_clk_ops, \ - .parent_names = __parent, \ - .num_parents = ARRAY_SIZE(__parent), \ +#define OXNAS_GATE(_name, _bit, _parents) \ +struct clk_oxnas_gate _name = { \ + .bit = (_bit), \ + .hw.init = &(struct clk_init_data) { \ + .name = #_name, \ + .ops = &oxnas_clk_gate_ops, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \ + }, \ } -#define DECLARE_STD_CLK(__clk) DECLARE_STD_CLKP(__clk, oxnas_clk_parents) +static OXNAS_GATE(ox810se_leon, 0, osc_parents); +static OXNAS_GATE(ox810se_dma_sgdma, 1, osc_parents); +static OXNAS_GATE(ox810se_cipher, 2, osc_parents); +static OXNAS_GATE(ox810se_sata, 4, osc_parents); +static OXNAS_GATE(ox810se_audio, 5, osc_parents); +static OXNAS_GATE(ox810se_usbmph, 6, osc_parents); +static OXNAS_GATE(ox810se_etha, 7, eth_parents); +static OXNAS_GATE(ox810se_pciea, 8, osc_parents); +static OXNAS_GATE(ox810se_nand, 9, osc_parents); + +static struct clk_oxnas_gate *ox810se_gates[] = { + &ox810se_leon, + &ox810se_dma_sgdma, + &ox810se_cipher, + &ox810se_sata, + &ox810se_audio, + &ox810se_usbmph, + &ox810se_etha, + &ox810se_pciea, + &ox810se_nand, +}; + +static OXNAS_GATE(ox820_leon, 0, osc_parents); +static OXNAS_GATE(ox820_dma_sgdma, 1, osc_parents); +static OXNAS_GATE(ox820_cipher, 2, osc_parents); +static OXNAS_GATE(ox820_sd, 3, osc_parents); +static OXNAS_GATE(ox820_sata, 4, osc_parents); +static OXNAS_GATE(ox820_audio, 5, osc_parents); +static OXNAS_GATE(ox820_usbmph, 6, osc_parents); +static OXNAS_GATE(ox820_etha, 7, eth_parents); +static OXNAS_GATE(ox820_pciea, 8, osc_parents); +static OXNAS_GATE(ox820_nand, 9, osc_parents); +static OXNAS_GATE(ox820_ethb, 10, eth_parents); +static OXNAS_GATE(ox820_pcieb, 11, osc_parents); +static OXNAS_GATE(ox820_ref600, 12, osc_parents); +static OXNAS_GATE(ox820_usbdev, 13, osc_parents); + +static struct clk_oxnas_gate *ox820_gates[] = { + &ox820_leon, + &ox820_dma_sgdma, + &ox820_cipher, + &ox820_sd, + &ox820_sata, + &ox820_audio, + &ox820_usbmph, + &ox820_etha, + &ox820_pciea, + &ox820_nand, + &ox820_etha, + &ox820_pciea, + &ox820_ref600, + &ox820_usbdev, +}; + +static struct clk_hw_onecell_data ox810se_hw_onecell_data = { + .hws = { + [CLK_810_LEON] = &ox810se_leon.hw, + [CLK_810_DMA_SGDMA] = &ox810se_dma_sgdma.hw, + [CLK_810_CIPHER] = &ox810se_cipher.hw, + [CLK_810_SATA] = &ox810se_sata.hw, + [CLK_810_AUDIO] = &ox810se_audio.hw, + [CLK_810_USBMPH] = &ox810se_usbmph.hw, + [CLK_810_ETHA] = &ox810se_etha.hw, + [CLK_810_PCIEA] = &ox810se_pciea.hw, + [CLK_810_NAND] = &ox810se_nand.hw, + }, + .num = ARRAY_SIZE(ox810se_gates), +}; + +static struct clk_hw_onecell_data ox820_hw_onecell_data = { + .hws = { + [CLK_820_LEON] = &ox820_leon.hw, + [CLK_820_DMA_SGDMA] = &ox820_dma_sgdma.hw, + [CLK_820_CIPHER] = &ox820_cipher.hw, + [CLK_820_SD] = &ox820_sd.hw, + [CLK_820_SATA] = &ox820_sata.hw, + [CLK_820_AUDIO] = &ox820_audio.hw, + [CLK_820_USBMPH] = &ox820_usbmph.hw, + [CLK_820_ETHA] = &ox820_etha.hw, + [CLK_820_PCIEA] = &ox820_pciea.hw, + [CLK_820_NAND] = &ox820_nand.hw, + [CLK_820_ETHB] = &ox820_ethb.hw, + [CLK_820_PCIEB] = &ox820_pcieb.hw, + [CLK_820_REF600] = &ox820_ref600.hw, + [CLK_820_USBDEV] = &ox820_usbdev.hw, + }, + .num = ARRAY_SIZE(ox820_gates), +}; -/* Hardware Bit - Clock association */ -struct clk_oxnas_init_data { - unsigned long bit; - const struct clk_init_data *clk_init; +static struct oxnas_stdclk_data ox810se_stdclk_data = { + .onecell_data = &ox810se_hw_onecell_data, + .gates = ox810se_gates, + .ngates = ARRAY_SIZE(ox810se_gates), }; -/* Clk init data declaration */ -DECLARE_STD_CLK(leon); -DECLARE_STD_CLK(dma_sgdma); -DECLARE_STD_CLK(cipher); -DECLARE_STD_CLK(sata); -DECLARE_STD_CLK(audio); -DECLARE_STD_CLK(usbmph); -DECLARE_STD_CLKP(etha, eth_parents); -DECLARE_STD_CLK(pciea); -DECLARE_STD_CLK(nand); - -/* Table index is clock indice */ -static const struct clk_oxnas_init_data clk_oxnas_init[] = { - [0] = {0, &clk_leon_init}, - [1] = {1, &clk_dma_sgdma_init}, - [2] = {2, &clk_cipher_init}, - /* Skip & Do not touch to DDR clock */ - [3] = {4, &clk_sata_init}, - [4] = {5, &clk_audio_init}, - [5] = {6, &clk_usbmph_init}, - [6] = {7, &clk_etha_init}, - [7] = {8, &clk_pciea_init}, - [8] = {9, &clk_nand_init}, +static struct oxnas_stdclk_data ox820_stdclk_data = { + .onecell_data = &ox820_hw_onecell_data, + .gates = ox820_gates, + .ngates = ARRAY_SIZE(ox820_gates), }; -struct clk_oxnas_data { - struct clk_oxnas clk_oxnas[ARRAY_SIZE(clk_oxnas_init)]; - struct clk_onecell_data onecell_data[ARRAY_SIZE(clk_oxnas_init)]; - struct clk *clks[ARRAY_SIZE(clk_oxnas_init)]; +static const struct of_device_id oxnas_stdclk_dt_ids[] = { + { .compatible = "oxsemi,ox810se-stdclk", &ox810se_stdclk_data }, + { .compatible = "oxsemi,ox820-stdclk", &ox820_stdclk_data }, + { } }; static int oxnas_stdclk_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct clk_oxnas_data *clk_oxnas; + const struct oxnas_stdclk_data *data; + const struct of_device_id *id; struct regmap *regmap; + int ret; int i; - clk_oxnas = devm_kzalloc(&pdev->dev, sizeof(*clk_oxnas), GFP_KERNEL); - if (!clk_oxnas) - return -ENOMEM; + id = of_match_device(oxnas_stdclk_dt_ids, &pdev->dev); + if (!id) + return -ENODEV; + data = id->data; regmap = syscon_node_to_regmap(of_get_parent(np)); if (IS_ERR(regmap)) { @@ -149,32 +236,23 @@ static int oxnas_stdclk_probe(struct platform_device *pdev) return PTR_ERR(regmap); } - for (i = 0; i < ARRAY_SIZE(clk_oxnas_init); i++) { - struct clk_oxnas *_clk; + for (i = 0 ; i < data->ngates ; ++i) + data->gates[i]->regmap = regmap; - _clk = &clk_oxnas->clk_oxnas[i]; - _clk->bit = clk_oxnas_init[i].bit; - _clk->hw.init = clk_oxnas_init[i].clk_init; - _clk->regmap = regmap; + for (i = 0; i < data->onecell_data->num; i++) { + if (!data->onecell_data->hws[i]) + continue; - clk_oxnas->clks[i] = - devm_clk_register(&pdev->dev, &_clk->hw); - if (WARN_ON(IS_ERR(clk_oxnas->clks[i]))) - return PTR_ERR(clk_oxnas->clks[i]); + ret = devm_clk_hw_register(&pdev->dev, + data->onecell_data->hws[i]); + if (ret) + return ret; } - clk_oxnas->onecell_data->clks = clk_oxnas->clks; - clk_oxnas->onecell_data->clk_num = ARRAY_SIZE(clk_oxnas_init); - - return of_clk_add_provider(np, of_clk_src_onecell_get, - clk_oxnas->onecell_data); + return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, + data->onecell_data); } -static const struct of_device_id oxnas_stdclk_dt_ids[] = { - { .compatible = "oxsemi,ox810se-stdclk" }, - { } -}; - static struct platform_driver oxnas_stdclk_driver = { .probe = oxnas_stdclk_probe, .driver = { diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index 80ae2a51452d..d0bf8b1c67de 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -266,6 +266,39 @@ static const struct clockgen_muxinfo ls1043a_hwa2 = { }, }; +static const struct clockgen_muxinfo ls1046a_hwa1 = { + { + {}, + {}, + { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, + { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 }, + { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 }, + { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 }, + { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, + { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 }, + }, +}; + +static const struct clockgen_muxinfo ls1046a_hwa2 = { + { + {}, + { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 }, + { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 }, + { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 }, + {}, + {}, + { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, + }, +}; + +static const struct clockgen_muxinfo ls1012a_cmux = { + { + [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 }, + {}, + [2] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 }, + } +}; + static const struct clockgen_muxinfo t1023_hwa1 = { { {}, @@ -489,6 +522,31 @@ static const struct clockgen_chipinfo chipinfo[] = { .flags = CG_PLL_8BIT, }, { + .compat = "fsl,ls1046a-clockgen", + .init_periph = t2080_init_periph, + .cmux_groups = { + &t1040_cmux + }, + .hwaccel = { + &ls1046a_hwa1, &ls1046a_hwa2 + }, + .cmux_to_group = { + 0, -1 + }, + .pll_mask = 0x07, + .flags = CG_PLL_8BIT, + }, + { + .compat = "fsl,ls1012a-clockgen", + .cmux_groups = { + &ls1012a_cmux + }, + .cmux_to_group = { + 0, -1 + }, + .pll_mask = 0x03, + }, + { .compat = "fsl,ls2080a-clockgen", .cmux_groups = { &clockgen2_cmux_cga12, &clockgen2_cmux_cgb @@ -1273,8 +1331,10 @@ err: CLK_OF_DECLARE(qoriq_clockgen_1, "fsl,qoriq-clockgen-1.0", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_2, "fsl,qoriq-clockgen-2.0", clockgen_init); +CLK_OF_DECLARE(qoriq_clockgen_ls1012a, "fsl,ls1012a-clockgen", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_ls1021a, "fsl,ls1021a-clockgen", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_ls1043a, "fsl,ls1043a-clockgen", clockgen_init); +CLK_OF_DECLARE(qoriq_clockgen_ls1046a, "fsl,ls1046a-clockgen", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_ls2080a, "fsl,ls2080a-clockgen", clockgen_init); /* Legacy nodes */ diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c index 02d681008401..5eb05dbf59b8 100644 --- a/drivers/clk/clk-stm32f4.c +++ b/drivers/clk/clk-stm32f4.c @@ -19,10 +19,14 @@ #include <linux/clk-provider.h> #include <linux/err.h> #include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/ioport.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> #define STM32F4_RCC_PLLCFGR 0x04 #define STM32F4_RCC_CFGR 0x08 @@ -31,6 +35,8 @@ #define STM32F4_RCC_AHB3ENR 0x38 #define STM32F4_RCC_APB1ENR 0x40 #define STM32F4_RCC_APB2ENR 0x44 +#define STM32F4_RCC_BDCR 0x70 +#define STM32F4_RCC_CSR 0x74 struct stm32f4_gate_data { u8 offset; @@ -40,7 +46,7 @@ struct stm32f4_gate_data { unsigned long flags; }; -static const struct stm32f4_gate_data stm32f4_gates[] __initconst = { +static const struct stm32f4_gate_data stm32f429_gates[] __initconst = { { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" }, { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" }, { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" }, @@ -120,26 +126,113 @@ static const struct stm32f4_gate_data stm32f4_gates[] __initconst = { { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" }, }; -/* - * MAX_CLKS is the maximum value in the enumeration below plus the combined - * hweight of stm32f42xx_gate_map (plus one). - */ -#define MAX_CLKS 74 +static const struct stm32f4_gate_data stm32f469_gates[] __initconst = { + { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 20, "ccmdatam", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" }, + + { STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" }, + { STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" }, + + { STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div", + CLK_IGNORE_UNUSED }, + { STM32F4_RCC_AHB3ENR, 1, "qspi", "ahb_div", + CLK_IGNORE_UNUSED }, + + { STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 17, "uart2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 18, "uart3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 19, "uart4", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 20, "uart5", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 21, "i2c1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 22, "i2c2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 23, "i2c3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 30, "uart7", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 31, "uart8", "apb1_div" }, + + { STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 4, "usart1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 5, "usart6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 11, "sdio", "pll48" }, + { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" }, +}; -enum { SYSTICK, FCLK }; +enum { SYSTICK, FCLK, CLK_LSI, CLK_LSE, CLK_HSE_RTC, CLK_RTC, END_PRIMARY_CLK }; /* * This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx * have gate bits associated with them. Its combined hweight is 71. */ -static const u64 stm32f42xx_gate_map[] = { 0x000000f17ef417ffull, - 0x0000000000000001ull, - 0x04777f33f6fec9ffull }; +#define MAX_GATE_MAP 3 + +static const u64 stm32f42xx_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull, + 0x0000000000000001ull, + 0x04777f33f6fec9ffull }; + +static const u64 stm32f46xx_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull, + 0x0000000000000003ull, + 0x0c777f33f6fec9ffull }; + +static const u64 *stm32f4_gate_map; + +static struct clk_hw **clks; -static struct clk_hw *clks[MAX_CLKS]; static DEFINE_SPINLOCK(stm32f4_clk_lock); static void __iomem *base; +static struct regmap *pdrm; + /* * "Multiplier" device for APBx clocks. * @@ -256,15 +349,15 @@ static void stm32f4_rcc_register_pll(const char *hse_clk, const char *hsi_clk) */ static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary) { - u64 table[ARRAY_SIZE(stm32f42xx_gate_map)]; + u64 table[MAX_GATE_MAP]; if (primary == 1) { - if (WARN_ON(secondary > FCLK)) + if (WARN_ON(secondary >= END_PRIMARY_CLK)) return -EINVAL; return secondary; } - memcpy(table, stm32f42xx_gate_map, sizeof(table)); + memcpy(table, stm32f4_gate_map, sizeof(table)); /* only bits set in table can be used as indices */ if (WARN_ON(secondary >= BITS_PER_BYTE * sizeof(table) || @@ -276,7 +369,7 @@ static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary) table[BIT_ULL_WORD(secondary)] &= GENMASK_ULL(secondary % BITS_PER_LONG_LONG, 0); - return FCLK + hweight64(table[0]) + + return END_PRIMARY_CLK - 1 + hweight64(table[0]) + (BIT_ULL_WORD(secondary) >= 1 ? hweight64(table[1]) : 0) + (BIT_ULL_WORD(secondary) >= 2 ? hweight64(table[2]) : 0); } @@ -292,6 +385,212 @@ stm32f4_rcc_lookup_clk(struct of_phandle_args *clkspec, void *data) return clks[i]; } +#define to_rgclk(_rgate) container_of(_rgate, struct stm32_rgate, gate) + +static inline void disable_power_domain_write_protection(void) +{ + if (pdrm) + regmap_update_bits(pdrm, 0x00, (1 << 8), (1 << 8)); +} + +static inline void enable_power_domain_write_protection(void) +{ + if (pdrm) + regmap_update_bits(pdrm, 0x00, (1 << 8), (0 << 8)); +} + +static inline void sofware_reset_backup_domain(void) +{ + unsigned long val; + + val = readl(base + STM32F4_RCC_BDCR); + writel(val | BIT(16), base + STM32F4_RCC_BDCR); + writel(val & ~BIT(16), base + STM32F4_RCC_BDCR); +} + +struct stm32_rgate { + struct clk_gate gate; + u8 bit_rdy_idx; +}; + +#define RTC_TIMEOUT 1000000 + +static int rgclk_enable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32_rgate *rgate = to_rgclk(gate); + u32 reg; + int ret; + + disable_power_domain_write_protection(); + + clk_gate_ops.enable(hw); + + ret = readl_relaxed_poll_timeout_atomic(gate->reg, reg, + reg & rgate->bit_rdy_idx, 1000, RTC_TIMEOUT); + + enable_power_domain_write_protection(); + return ret; +} + +static void rgclk_disable(struct clk_hw *hw) +{ + clk_gate_ops.disable(hw); +} + +static int rgclk_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +static const struct clk_ops rgclk_ops = { + .enable = rgclk_enable, + .disable = rgclk_disable, + .is_enabled = rgclk_is_enabled, +}; + +static struct clk_hw *clk_register_rgate(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, u8 bit_rdy_idx, + u8 clk_gate_flags, spinlock_t *lock) +{ + struct stm32_rgate *rgate; + struct clk_init_data init = { NULL }; + struct clk_hw *hw; + int ret; + + rgate = kzalloc(sizeof(*rgate), GFP_KERNEL); + if (!rgate) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &rgclk_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + rgate->bit_rdy_idx = bit_rdy_idx; + + rgate->gate.lock = lock; + rgate->gate.reg = reg; + rgate->gate.bit_idx = bit_idx; + rgate->gate.hw.init = &init; + + hw = &rgate->gate.hw; + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(rgate); + hw = ERR_PTR(ret); + } + + return hw; +} + +static int cclk_gate_enable(struct clk_hw *hw) +{ + int ret; + + disable_power_domain_write_protection(); + + ret = clk_gate_ops.enable(hw); + + enable_power_domain_write_protection(); + + return ret; +} + +static void cclk_gate_disable(struct clk_hw *hw) +{ + disable_power_domain_write_protection(); + + clk_gate_ops.disable(hw); + + enable_power_domain_write_protection(); +} + +static int cclk_gate_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +static const struct clk_ops cclk_gate_ops = { + .enable = cclk_gate_enable, + .disable = cclk_gate_disable, + .is_enabled = cclk_gate_is_enabled, +}; + +static u8 cclk_mux_get_parent(struct clk_hw *hw) +{ + return clk_mux_ops.get_parent(hw); +} + +static int cclk_mux_set_parent(struct clk_hw *hw, u8 index) +{ + int ret; + + disable_power_domain_write_protection(); + + sofware_reset_backup_domain(); + + ret = clk_mux_ops.set_parent(hw, index); + + enable_power_domain_write_protection(); + + return ret; +} + +static const struct clk_ops cclk_mux_ops = { + .get_parent = cclk_mux_get_parent, + .set_parent = cclk_mux_set_parent, +}; + +static struct clk_hw *stm32_register_cclk(struct device *dev, const char *name, + const char * const *parent_names, int num_parents, + void __iomem *reg, u8 bit_idx, u8 shift, unsigned long flags, + spinlock_t *lock) +{ + struct clk_hw *hw; + struct clk_gate *gate; + struct clk_mux *mux; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + hw = ERR_PTR(-EINVAL); + goto fail; + } + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) { + kfree(gate); + hw = ERR_PTR(-EINVAL); + goto fail; + } + + gate->reg = reg; + gate->bit_idx = bit_idx; + gate->flags = 0; + gate->lock = lock; + + mux->reg = reg; + mux->shift = shift; + mux->mask = 3; + mux->flags = 0; + + hw = clk_hw_register_composite(dev, name, parent_names, num_parents, + &mux->hw, &cclk_mux_ops, + NULL, NULL, + &gate->hw, &cclk_gate_ops, + flags); + + if (IS_ERR(hw)) { + kfree(gate); + kfree(mux); + } + +fail: + return hw; +} + static const char *sys_parents[] __initdata = { "hsi", NULL, "pll" }; static const struct clk_div_table ahb_div_table[] = { @@ -308,10 +607,46 @@ static const struct clk_div_table apb_div_table[] = { { 0 }, }; +static const char *rtc_parents[4] = { + "no-clock", "lse", "lsi", "hse-rtc" +}; + +struct stm32f4_clk_data { + const struct stm32f4_gate_data *gates_data; + const u64 *gates_map; + int gates_num; +}; + +static const struct stm32f4_clk_data stm32f429_clk_data = { + .gates_data = stm32f429_gates, + .gates_map = stm32f42xx_gate_map, + .gates_num = ARRAY_SIZE(stm32f429_gates), +}; + +static const struct stm32f4_clk_data stm32f469_clk_data = { + .gates_data = stm32f469_gates, + .gates_map = stm32f46xx_gate_map, + .gates_num = ARRAY_SIZE(stm32f469_gates), +}; + +static const struct of_device_id stm32f4_of_match[] = { + { + .compatible = "st,stm32f42xx-rcc", + .data = &stm32f429_clk_data + }, + { + .compatible = "st,stm32f469-rcc", + .data = &stm32f469_clk_data + }, + {} +}; + static void __init stm32f4_rcc_init(struct device_node *np) { const char *hse_clk; int n; + const struct of_device_id *match; + const struct stm32f4_clk_data *data; base = of_iomap(np, 0); if (!base) { @@ -319,6 +654,25 @@ static void __init stm32f4_rcc_init(struct device_node *np) return; } + pdrm = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(pdrm)) { + pdrm = NULL; + pr_warn("%s: Unable to get syscfg\n", __func__); + } + + match = of_match_node(stm32f4_of_match, np); + if (WARN_ON(!match)) + return; + + data = match->data; + + clks = kmalloc_array(data->gates_num + END_PRIMARY_CLK, + sizeof(*clks), GFP_KERNEL); + if (!clks) + goto fail; + + stm32f4_gate_map = data->gates_map; + hse_clk = of_clk_get_parent_name(np, 0); clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0, @@ -351,11 +705,15 @@ static void __init stm32f4_rcc_init(struct device_node *np) clks[FCLK] = clk_hw_register_fixed_factor(NULL, "fclk", "ahb_div", 0, 1, 1); - for (n = 0; n < ARRAY_SIZE(stm32f4_gates); n++) { - const struct stm32f4_gate_data *gd = &stm32f4_gates[n]; - unsigned int secondary = - 8 * (gd->offset - STM32F4_RCC_AHB1ENR) + gd->bit_idx; - int idx = stm32f4_rcc_lookup_clk_idx(0, secondary); + for (n = 0; n < data->gates_num; n++) { + const struct stm32f4_gate_data *gd; + unsigned int secondary; + int idx; + + gd = &data->gates_data[n]; + secondary = 8 * (gd->offset - STM32F4_RCC_AHB1ENR) + + gd->bit_idx; + idx = stm32f4_rcc_lookup_clk_idx(0, secondary); if (idx < 0) goto fail; @@ -371,9 +729,44 @@ static void __init stm32f4_rcc_init(struct device_node *np) } } + clks[CLK_LSI] = clk_register_rgate(NULL, "lsi", "clk-lsi", 0, + base + STM32F4_RCC_CSR, 0, 2, 0, &stm32f4_clk_lock); + + if (IS_ERR(clks[CLK_LSI])) { + pr_err("Unable to register lsi clock\n"); + goto fail; + } + + clks[CLK_LSE] = clk_register_rgate(NULL, "lse", "clk-lse", 0, + base + STM32F4_RCC_BDCR, 0, 2, 0, &stm32f4_clk_lock); + + if (IS_ERR(clks[CLK_LSE])) { + pr_err("Unable to register lse clock\n"); + goto fail; + } + + clks[CLK_HSE_RTC] = clk_hw_register_divider(NULL, "hse-rtc", "clk-hse", + 0, base + STM32F4_RCC_CFGR, 16, 5, 0, + &stm32f4_clk_lock); + + if (IS_ERR(clks[CLK_HSE_RTC])) { + pr_err("Unable to register hse-rtc clock\n"); + goto fail; + } + + clks[CLK_RTC] = stm32_register_cclk(NULL, "rtc", rtc_parents, 4, + base + STM32F4_RCC_BDCR, 15, 8, 0, &stm32f4_clk_lock); + + if (IS_ERR(clks[CLK_RTC])) { + pr_err("Unable to register rtc clock\n"); + goto fail; + } + of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL); return; fail: + kfree(clks); iounmap(base); } -CLK_OF_DECLARE(stm32f4_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init); +CLK_OF_DECLARE(stm32f42xx_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init); +CLK_OF_DECLARE(stm32f46xx_rcc, "st,stm32f469-rcc", stm32f4_rcc_init); diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index f4fdac55727c..0621fbfb4beb 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -243,7 +243,7 @@ static int wm831x_clkout_is_prepared(struct clk_hw *hw) if (ret < 0) { dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n", ret); - return true; + return false; } return (ret & WM831X_CLKOUT_ENA) != 0; diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 97ae60fa1584..bb8a77a5985f 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -448,12 +448,20 @@ EXPORT_SYMBOL(clk_register_clkdev); * * con_id or dev_id may be NULL as a wildcard, just as in the rest of * clkdev. + * + * To make things easier for mass registration, we detect error clk_hws + * from a previous clk_hw_register_*() call, and return the error code for + * those. This is to permit this function to be called immediately + * after clk_hw_register_*(). */ int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id, const char *dev_id) { struct clk_lookup *cl; + if (IS_ERR(hw)) + return PTR_ERR(hw); + /* * Since dev_id can be NULL, and NULL is handled specially, we must * pass it as either a NULL format string, or with "%s". diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig index 3f537a04c6a6..cbed6602172b 100644 --- a/drivers/clk/hisilicon/Kconfig +++ b/drivers/clk/hisilicon/Kconfig @@ -1,3 +1,11 @@ +config COMMON_CLK_HI3516CV300 + tristate "HI3516CV300 Clock Driver" + depends on ARCH_HISI || COMPILE_TEST + select RESET_HISI + default ARCH_HISI + help + Build the clock driver for hi3516cv300. + config COMMON_CLK_HI3519 tristate "Hi3519 Clock Driver" depends on ARCH_HISI || COMPILE_TEST @@ -6,6 +14,14 @@ config COMMON_CLK_HI3519 help Build the clock driver for hi3519. +config COMMON_CLK_HI3798CV200 + tristate "Hi3798CV200 Clock Driver" + depends on ARCH_HISI || COMPILE_TEST + select RESET_HISI + default ARCH_HISI + help + Build the clock driver for hi3798cv200. + config COMMON_CLK_HI6220 bool "Hi6220 Clock Driver" depends on ARCH_HISI || COMPILE_TEST @@ -23,5 +39,6 @@ config RESET_HISI config STUB_CLK_HI6220 bool "Hi6220 Stub Clock Driver" depends on COMMON_CLK_HI6220 && MAILBOX + default ARCH_HISI help Build the Hisilicon Hi6220 stub clock driver. diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile index e169ec7da023..4eec5e511e4c 100644 --- a/drivers/clk/hisilicon/Makefile +++ b/drivers/clk/hisilicon/Makefile @@ -7,7 +7,9 @@ obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o +obj-$(CONFIG_COMMON_CLK_HI3516CV300) += crg-hi3516cv300.o obj-$(CONFIG_COMMON_CLK_HI3519) += clk-hi3519.o +obj-$(CONFIG_COMMON_CLK_HI3798CV200) += crg-hi3798cv200.o obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o obj-$(CONFIG_RESET_HISI) += reset.o obj-$(CONFIG_STUB_CLK_HI6220) += clk-hi6220-stub.o diff --git a/drivers/clk/hisilicon/crg-hi3516cv300.c b/drivers/clk/hisilicon/crg-hi3516cv300.c new file mode 100644 index 000000000000..2007123832bb --- /dev/null +++ b/drivers/clk/hisilicon/crg-hi3516cv300.c @@ -0,0 +1,330 @@ +/* + * Hi3516CV300 Clock and Reset Generator Driver + * + * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <dt-bindings/clock/hi3516cv300-clock.h> +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include "clk.h" +#include "crg.h" +#include "reset.h" + +/* hi3516CV300 core CRG */ +#define HI3516CV300_INNER_CLK_OFFSET 64 +#define HI3516CV300_FIXED_3M 65 +#define HI3516CV300_FIXED_6M 66 +#define HI3516CV300_FIXED_24M 67 +#define HI3516CV300_FIXED_49P5 68 +#define HI3516CV300_FIXED_50M 69 +#define HI3516CV300_FIXED_83P3M 70 +#define HI3516CV300_FIXED_99M 71 +#define HI3516CV300_FIXED_100M 72 +#define HI3516CV300_FIXED_148P5M 73 +#define HI3516CV300_FIXED_198M 74 +#define HI3516CV300_FIXED_297M 75 +#define HI3516CV300_UART_MUX 76 +#define HI3516CV300_FMC_MUX 77 +#define HI3516CV300_MMC0_MUX 78 +#define HI3516CV300_MMC1_MUX 79 +#define HI3516CV300_MMC2_MUX 80 +#define HI3516CV300_MMC3_MUX 81 +#define HI3516CV300_PWM_MUX 82 +#define HI3516CV300_CRG_NR_CLKS 128 + +static const struct hisi_fixed_rate_clock hi3516cv300_fixed_rate_clks[] = { + { HI3516CV300_FIXED_3M, "3m", NULL, 0, 3000000, }, + { HI3516CV300_FIXED_6M, "6m", NULL, 0, 6000000, }, + { HI3516CV300_FIXED_24M, "24m", NULL, 0, 24000000, }, + { HI3516CV300_FIXED_49P5, "49.5m", NULL, 0, 49500000, }, + { HI3516CV300_FIXED_50M, "50m", NULL, 0, 50000000, }, + { HI3516CV300_FIXED_83P3M, "83.3m", NULL, 0, 83300000, }, + { HI3516CV300_FIXED_99M, "99m", NULL, 0, 99000000, }, + { HI3516CV300_FIXED_100M, "100m", NULL, 0, 100000000, }, + { HI3516CV300_FIXED_148P5M, "148.5m", NULL, 0, 148500000, }, + { HI3516CV300_FIXED_198M, "198m", NULL, 0, 198000000, }, + { HI3516CV300_FIXED_297M, "297m", NULL, 0, 297000000, }, + { HI3516CV300_APB_CLK, "apb", NULL, 0, 50000000, }, +}; + +static const char *const uart_mux_p[] = {"24m", "6m"}; +static const char *const fmc_mux_p[] = { + "24m", "83.3m", "148.5m", "198m", "297m" +}; +static const char *const mmc_mux_p[] = {"49.5m"}; +static const char *const mmc2_mux_p[] = {"99m", "49.5m"}; +static const char *const pwm_mux_p[] = {"3m", "50m", "24m", "24m"}; + +static u32 uart_mux_table[] = {0, 1}; +static u32 fmc_mux_table[] = {0, 1, 2, 3, 4}; +static u32 mmc_mux_table[] = {0}; +static u32 mmc2_mux_table[] = {0, 2}; +static u32 pwm_mux_table[] = {0, 1, 2, 3}; + +static const struct hisi_mux_clock hi3516cv300_mux_clks[] = { + { HI3516CV300_UART_MUX, "uart_mux", uart_mux_p, ARRAY_SIZE(uart_mux_p), + CLK_SET_RATE_PARENT, 0xe4, 19, 1, 0, uart_mux_table, }, + { HI3516CV300_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), + CLK_SET_RATE_PARENT, 0xc0, 2, 3, 0, fmc_mux_table, }, + { HI3516CV300_MMC0_MUX, "mmc0_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), + CLK_SET_RATE_PARENT, 0xc4, 4, 2, 0, mmc_mux_table, }, + { HI3516CV300_MMC1_MUX, "mmc1_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), + CLK_SET_RATE_PARENT, 0xc4, 12, 2, 0, mmc_mux_table, }, + { HI3516CV300_MMC2_MUX, "mmc2_mux", mmc2_mux_p, ARRAY_SIZE(mmc2_mux_p), + CLK_SET_RATE_PARENT, 0xc4, 20, 2, 0, mmc2_mux_table, }, + { HI3516CV300_MMC3_MUX, "mmc3_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), + CLK_SET_RATE_PARENT, 0xc8, 4, 2, 0, mmc_mux_table, }, + { HI3516CV300_PWM_MUX, "pwm_mux", pwm_mux_p, ARRAY_SIZE(pwm_mux_p), + CLK_SET_RATE_PARENT, 0x38, 2, 2, 0, pwm_mux_table, }, +}; + +static const struct hisi_gate_clock hi3516cv300_gate_clks[] = { + + { HI3516CV300_UART0_CLK, "clk_uart0", "uart_mux", CLK_SET_RATE_PARENT, + 0xe4, 15, 0, }, + { HI3516CV300_UART1_CLK, "clk_uart1", "uart_mux", CLK_SET_RATE_PARENT, + 0xe4, 16, 0, }, + { HI3516CV300_UART2_CLK, "clk_uart2", "uart_mux", CLK_SET_RATE_PARENT, + 0xe4, 17, 0, }, + + { HI3516CV300_SPI0_CLK, "clk_spi0", "100m", CLK_SET_RATE_PARENT, + 0xe4, 13, 0, }, + { HI3516CV300_SPI1_CLK, "clk_spi1", "100m", CLK_SET_RATE_PARENT, + 0xe4, 14, 0, }, + + { HI3516CV300_FMC_CLK, "clk_fmc", "fmc_mux", CLK_SET_RATE_PARENT, + 0xc0, 1, 0, }, + { HI3516CV300_MMC0_CLK, "clk_mmc0", "mmc0_mux", CLK_SET_RATE_PARENT, + 0xc4, 1, 0, }, + { HI3516CV300_MMC1_CLK, "clk_mmc1", "mmc1_mux", CLK_SET_RATE_PARENT, + 0xc4, 9, 0, }, + { HI3516CV300_MMC2_CLK, "clk_mmc2", "mmc2_mux", CLK_SET_RATE_PARENT, + 0xc4, 17, 0, }, + { HI3516CV300_MMC3_CLK, "clk_mmc3", "mmc3_mux", CLK_SET_RATE_PARENT, + 0xc8, 1, 0, }, + + { HI3516CV300_ETH_CLK, "clk_eth", NULL, 0, 0xec, 1, 0, }, + + { HI3516CV300_DMAC_CLK, "clk_dmac", NULL, 0, 0xd8, 5, 0, }, + { HI3516CV300_PWM_CLK, "clk_pwm", "pwm_mux", CLK_SET_RATE_PARENT, + 0x38, 1, 0, }, + + { HI3516CV300_USB2_BUS_CLK, "clk_usb2_bus", NULL, 0, 0xb8, 0, 0, }, + { HI3516CV300_USB2_OHCI48M_CLK, "clk_usb2_ohci48m", NULL, 0, + 0xb8, 1, 0, }, + { HI3516CV300_USB2_OHCI12M_CLK, "clk_usb2_ohci12m", NULL, 0, + 0xb8, 2, 0, }, + { HI3516CV300_USB2_OTG_UTMI_CLK, "clk_usb2_otg_utmi", NULL, 0, + 0xb8, 3, 0, }, + { HI3516CV300_USB2_HST_PHY_CLK, "clk_usb2_hst_phy", NULL, 0, + 0xb8, 4, 0, }, + { HI3516CV300_USB2_UTMI0_CLK, "clk_usb2_utmi0", NULL, 0, 0xb8, 5, 0, }, + { HI3516CV300_USB2_PHY_CLK, "clk_usb2_phy", NULL, 0, 0xb8, 7, 0, }, +}; + +static struct hisi_clock_data *hi3516cv300_clk_register( + struct platform_device *pdev) +{ + struct hisi_clock_data *clk_data; + int ret; + + clk_data = hisi_clk_alloc(pdev, HI3516CV300_CRG_NR_CLKS); + if (!clk_data) + return ERR_PTR(-ENOMEM); + + ret = hisi_clk_register_fixed_rate(hi3516cv300_fixed_rate_clks, + ARRAY_SIZE(hi3516cv300_fixed_rate_clks), clk_data); + if (ret) + return ERR_PTR(ret); + + ret = hisi_clk_register_mux(hi3516cv300_mux_clks, + ARRAY_SIZE(hi3516cv300_mux_clks), clk_data); + if (ret) + goto unregister_fixed_rate; + + ret = hisi_clk_register_gate(hi3516cv300_gate_clks, + ARRAY_SIZE(hi3516cv300_gate_clks), clk_data); + if (ret) + goto unregister_mux; + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, &clk_data->clk_data); + if (ret) + goto unregister_gate; + + return clk_data; + +unregister_gate: + hisi_clk_unregister_gate(hi3516cv300_gate_clks, + ARRAY_SIZE(hi3516cv300_gate_clks), clk_data); +unregister_mux: + hisi_clk_unregister_mux(hi3516cv300_mux_clks, + ARRAY_SIZE(hi3516cv300_mux_clks), clk_data); +unregister_fixed_rate: + hisi_clk_unregister_fixed_rate(hi3516cv300_fixed_rate_clks, + ARRAY_SIZE(hi3516cv300_fixed_rate_clks), clk_data); + return ERR_PTR(ret); +} + +static void hi3516cv300_clk_unregister(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + + hisi_clk_unregister_gate(hi3516cv300_gate_clks, + ARRAY_SIZE(hi3516cv300_gate_clks), crg->clk_data); + hisi_clk_unregister_mux(hi3516cv300_mux_clks, + ARRAY_SIZE(hi3516cv300_mux_clks), crg->clk_data); + hisi_clk_unregister_fixed_rate(hi3516cv300_fixed_rate_clks, + ARRAY_SIZE(hi3516cv300_fixed_rate_clks), crg->clk_data); +} + +static const struct hisi_crg_funcs hi3516cv300_crg_funcs = { + .register_clks = hi3516cv300_clk_register, + .unregister_clks = hi3516cv300_clk_unregister, +}; + +/* hi3516CV300 sysctrl CRG */ +#define HI3516CV300_SYSCTRL_NR_CLKS 16 + +static const char *wdt_mux_p[] __initconst = { "3m", "apb" }; +static u32 wdt_mux_table[] = {0, 1}; + +static const struct hisi_mux_clock hi3516cv300_sysctrl_mux_clks[] = { + { HI3516CV300_WDT_CLK, "wdt", wdt_mux_p, ARRAY_SIZE(wdt_mux_p), + CLK_SET_RATE_PARENT, 0x0, 23, 1, 0, wdt_mux_table, }, +}; + +static struct hisi_clock_data *hi3516cv300_sysctrl_clk_register( + struct platform_device *pdev) +{ + struct hisi_clock_data *clk_data; + int ret; + + clk_data = hisi_clk_alloc(pdev, HI3516CV300_SYSCTRL_NR_CLKS); + if (!clk_data) + return ERR_PTR(-ENOMEM); + + ret = hisi_clk_register_mux(hi3516cv300_sysctrl_mux_clks, + ARRAY_SIZE(hi3516cv300_sysctrl_mux_clks), clk_data); + if (ret) + return ERR_PTR(ret); + + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, &clk_data->clk_data); + if (ret) + goto unregister_mux; + + return clk_data; + +unregister_mux: + hisi_clk_unregister_mux(hi3516cv300_sysctrl_mux_clks, + ARRAY_SIZE(hi3516cv300_sysctrl_mux_clks), clk_data); + return ERR_PTR(ret); +} + +static void hi3516cv300_sysctrl_clk_unregister(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + + hisi_clk_unregister_mux(hi3516cv300_sysctrl_mux_clks, + ARRAY_SIZE(hi3516cv300_sysctrl_mux_clks), + crg->clk_data); +} + +static const struct hisi_crg_funcs hi3516cv300_sysctrl_funcs = { + .register_clks = hi3516cv300_sysctrl_clk_register, + .unregister_clks = hi3516cv300_sysctrl_clk_unregister, +}; + +static const struct of_device_id hi3516cv300_crg_match_table[] = { + { + .compatible = "hisilicon,hi3516cv300-crg", + .data = &hi3516cv300_crg_funcs + }, + { + .compatible = "hisilicon,hi3516cv300-sysctrl", + .data = &hi3516cv300_sysctrl_funcs + }, + { } +}; +MODULE_DEVICE_TABLE(of, hi3516cv300_crg_match_table); + +static int hi3516cv300_crg_probe(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg; + + crg = devm_kmalloc(&pdev->dev, sizeof(*crg), GFP_KERNEL); + if (!crg) + return -ENOMEM; + + crg->funcs = of_device_get_match_data(&pdev->dev); + if (!crg->funcs) + return -ENOENT; + + crg->rstc = hisi_reset_init(pdev); + if (!crg->rstc) + return -ENOMEM; + + crg->clk_data = crg->funcs->register_clks(pdev); + if (IS_ERR(crg->clk_data)) { + hisi_reset_exit(crg->rstc); + return PTR_ERR(crg->clk_data); + } + + platform_set_drvdata(pdev, crg); + return 0; +} + +static int hi3516cv300_crg_remove(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + hisi_reset_exit(crg->rstc); + crg->funcs->unregister_clks(pdev); + return 0; +} + +static struct platform_driver hi3516cv300_crg_driver = { + .probe = hi3516cv300_crg_probe, + .remove = hi3516cv300_crg_remove, + .driver = { + .name = "hi3516cv300-crg", + .of_match_table = hi3516cv300_crg_match_table, + }, +}; + +static int __init hi3516cv300_crg_init(void) +{ + return platform_driver_register(&hi3516cv300_crg_driver); +} +core_initcall(hi3516cv300_crg_init); + +static void __exit hi3516cv300_crg_exit(void) +{ + platform_driver_unregister(&hi3516cv300_crg_driver); +} +module_exit(hi3516cv300_crg_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("HiSilicon Hi3516CV300 CRG Driver"); diff --git a/drivers/clk/hisilicon/crg-hi3798cv200.c b/drivers/clk/hisilicon/crg-hi3798cv200.c new file mode 100644 index 000000000000..fc8b5bc2d50d --- /dev/null +++ b/drivers/clk/hisilicon/crg-hi3798cv200.c @@ -0,0 +1,337 @@ +/* + * Hi3798CV200 Clock and Reset Generator Driver + * + * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <dt-bindings/clock/histb-clock.h> +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include "clk.h" +#include "crg.h" +#include "reset.h" + +/* hi3798CV200 core CRG */ +#define HI3798CV200_INNER_CLK_OFFSET 64 +#define HI3798CV200_FIXED_24M 65 +#define HI3798CV200_FIXED_25M 66 +#define HI3798CV200_FIXED_50M 67 +#define HI3798CV200_FIXED_75M 68 +#define HI3798CV200_FIXED_100M 69 +#define HI3798CV200_FIXED_150M 70 +#define HI3798CV200_FIXED_200M 71 +#define HI3798CV200_FIXED_250M 72 +#define HI3798CV200_FIXED_300M 73 +#define HI3798CV200_FIXED_400M 74 +#define HI3798CV200_MMC_MUX 75 +#define HI3798CV200_ETH_PUB_CLK 76 +#define HI3798CV200_ETH_BUS_CLK 77 +#define HI3798CV200_ETH_BUS0_CLK 78 +#define HI3798CV200_ETH_BUS1_CLK 79 +#define HI3798CV200_COMBPHY1_MUX 80 + +#define HI3798CV200_CRG_NR_CLKS 128 + +static const struct hisi_fixed_rate_clock hi3798cv200_fixed_rate_clks[] = { + { HISTB_OSC_CLK, "clk_osc", NULL, 0, 24000000, }, + { HISTB_APB_CLK, "clk_apb", NULL, 0, 100000000, }, + { HISTB_AHB_CLK, "clk_ahb", NULL, 0, 200000000, }, + { HI3798CV200_FIXED_24M, "24m", NULL, 0, 24000000, }, + { HI3798CV200_FIXED_25M, "25m", NULL, 0, 25000000, }, + { HI3798CV200_FIXED_50M, "50m", NULL, 0, 50000000, }, + { HI3798CV200_FIXED_75M, "75m", NULL, 0, 75000000, }, + { HI3798CV200_FIXED_100M, "100m", NULL, 0, 100000000, }, + { HI3798CV200_FIXED_150M, "150m", NULL, 0, 150000000, }, + { HI3798CV200_FIXED_200M, "200m", NULL, 0, 200000000, }, + { HI3798CV200_FIXED_250M, "250m", NULL, 0, 250000000, }, +}; + +static const char *const mmc_mux_p[] = { + "100m", "50m", "25m", "200m", "150m" }; +static u32 mmc_mux_table[] = {0, 1, 2, 3, 6}; + +static const char *const comphy1_mux_p[] = { + "100m", "25m"}; +static u32 comphy1_mux_table[] = {2, 3}; + +static struct hisi_mux_clock hi3798cv200_mux_clks[] = { + { HI3798CV200_MMC_MUX, "mmc_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), + CLK_SET_RATE_PARENT, 0xa0, 8, 3, 0, mmc_mux_table, }, + { HI3798CV200_COMBPHY1_MUX, "combphy1_mux", + comphy1_mux_p, ARRAY_SIZE(comphy1_mux_p), + CLK_SET_RATE_PARENT, 0x188, 10, 2, 0, comphy1_mux_table, }, +}; + +static const struct hisi_gate_clock hi3798cv200_gate_clks[] = { + /* UART */ + { HISTB_UART2_CLK, "clk_uart2", "75m", + CLK_SET_RATE_PARENT, 0x68, 4, 0, }, + /* I2C */ + { HISTB_I2C0_CLK, "clk_i2c0", "clk_apb", + CLK_SET_RATE_PARENT, 0x6C, 4, 0, }, + { HISTB_I2C1_CLK, "clk_i2c1", "clk_apb", + CLK_SET_RATE_PARENT, 0x6C, 8, 0, }, + { HISTB_I2C2_CLK, "clk_i2c2", "clk_apb", + CLK_SET_RATE_PARENT, 0x6C, 12, 0, }, + { HISTB_I2C3_CLK, "clk_i2c3", "clk_apb", + CLK_SET_RATE_PARENT, 0x6C, 16, 0, }, + { HISTB_I2C4_CLK, "clk_i2c4", "clk_apb", + CLK_SET_RATE_PARENT, 0x6C, 20, 0, }, + /* SPI */ + { HISTB_SPI0_CLK, "clk_spi0", "clk_apb", + CLK_SET_RATE_PARENT, 0x70, 0, 0, }, + /* SDIO */ + { HISTB_SDIO0_BIU_CLK, "clk_sdio0_biu", "200m", + CLK_SET_RATE_PARENT, 0x9c, 0, 0, }, + { HISTB_SDIO0_CIU_CLK, "clk_sdio0_ciu", "mmc_mux", + CLK_SET_RATE_PARENT, 0x9c, 1, 0, }, + /* EMMC */ + { HISTB_MMC_BIU_CLK, "clk_mmc_biu", "200m", + CLK_SET_RATE_PARENT, 0xa0, 0, 0, }, + { HISTB_MMC_CIU_CLK, "clk_mmc_ciu", "mmc_mux", + CLK_SET_RATE_PARENT, 0xa0, 1, 0, }, + /* PCIE*/ + { HISTB_PCIE_BUS_CLK, "clk_pcie_bus", "200m", + CLK_SET_RATE_PARENT, 0x18c, 0, 0, }, + { HISTB_PCIE_SYS_CLK, "clk_pcie_sys", "100m", + CLK_SET_RATE_PARENT, 0x18c, 1, 0, }, + { HISTB_PCIE_PIPE_CLK, "clk_pcie_pipe", "250m", + CLK_SET_RATE_PARENT, 0x18c, 2, 0, }, + { HISTB_PCIE_AUX_CLK, "clk_pcie_aux", "24m", + CLK_SET_RATE_PARENT, 0x18c, 3, 0, }, + /* Ethernet */ + { HI3798CV200_ETH_PUB_CLK, "clk_pub", NULL, + CLK_SET_RATE_PARENT, 0xcc, 5, 0, }, + { HI3798CV200_ETH_BUS_CLK, "clk_bus", "clk_pub", + CLK_SET_RATE_PARENT, 0xcc, 0, 0, }, + { HI3798CV200_ETH_BUS0_CLK, "clk_bus_m0", "clk_bus", + CLK_SET_RATE_PARENT, 0xcc, 1, 0, }, + { HI3798CV200_ETH_BUS1_CLK, "clk_bus_m1", "clk_bus", + CLK_SET_RATE_PARENT, 0xcc, 2, 0, }, + { HISTB_ETH0_MAC_CLK, "clk_mac0", "clk_bus_m0", + CLK_SET_RATE_PARENT, 0xcc, 3, 0, }, + { HISTB_ETH0_MACIF_CLK, "clk_macif0", "clk_bus_m0", + CLK_SET_RATE_PARENT, 0xcc, 24, 0, }, + { HISTB_ETH1_MAC_CLK, "clk_mac1", "clk_bus_m1", + CLK_SET_RATE_PARENT, 0xcc, 4, 0, }, + { HISTB_ETH1_MACIF_CLK, "clk_macif1", "clk_bus_m1", + CLK_SET_RATE_PARENT, 0xcc, 25, 0, }, + /* COMBPHY1 */ + { HISTB_COMBPHY1_CLK, "clk_combphy1", "combphy1_mux", + CLK_SET_RATE_PARENT, 0x188, 8, 0, }, +}; + +static struct hisi_clock_data *hi3798cv200_clk_register( + struct platform_device *pdev) +{ + struct hisi_clock_data *clk_data; + int ret; + + clk_data = hisi_clk_alloc(pdev, HI3798CV200_CRG_NR_CLKS); + if (!clk_data) + return ERR_PTR(-ENOMEM); + + ret = hisi_clk_register_fixed_rate(hi3798cv200_fixed_rate_clks, + ARRAY_SIZE(hi3798cv200_fixed_rate_clks), + clk_data); + if (ret) + return ERR_PTR(ret); + + ret = hisi_clk_register_mux(hi3798cv200_mux_clks, + ARRAY_SIZE(hi3798cv200_mux_clks), + clk_data); + if (ret) + goto unregister_fixed_rate; + + ret = hisi_clk_register_gate(hi3798cv200_gate_clks, + ARRAY_SIZE(hi3798cv200_gate_clks), + clk_data); + if (ret) + goto unregister_mux; + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, &clk_data->clk_data); + if (ret) + goto unregister_gate; + + return clk_data; + +unregister_fixed_rate: + hisi_clk_unregister_fixed_rate(hi3798cv200_fixed_rate_clks, + ARRAY_SIZE(hi3798cv200_fixed_rate_clks), + clk_data); + +unregister_mux: + hisi_clk_unregister_mux(hi3798cv200_mux_clks, + ARRAY_SIZE(hi3798cv200_mux_clks), + clk_data); +unregister_gate: + hisi_clk_unregister_gate(hi3798cv200_gate_clks, + ARRAY_SIZE(hi3798cv200_gate_clks), + clk_data); + return ERR_PTR(ret); +} + +static void hi3798cv200_clk_unregister(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + + hisi_clk_unregister_gate(hi3798cv200_gate_clks, + ARRAY_SIZE(hi3798cv200_gate_clks), + crg->clk_data); + hisi_clk_unregister_mux(hi3798cv200_mux_clks, + ARRAY_SIZE(hi3798cv200_mux_clks), + crg->clk_data); + hisi_clk_unregister_fixed_rate(hi3798cv200_fixed_rate_clks, + ARRAY_SIZE(hi3798cv200_fixed_rate_clks), + crg->clk_data); +} + +static const struct hisi_crg_funcs hi3798cv200_crg_funcs = { + .register_clks = hi3798cv200_clk_register, + .unregister_clks = hi3798cv200_clk_unregister, +}; + +/* hi3798CV200 sysctrl CRG */ + +#define HI3798CV200_SYSCTRL_NR_CLKS 16 + +static const struct hisi_gate_clock hi3798cv200_sysctrl_gate_clks[] = { + { HISTB_IR_CLK, "clk_ir", "100m", + CLK_SET_RATE_PARENT, 0x48, 4, 0, }, + { HISTB_TIMER01_CLK, "clk_timer01", "24m", + CLK_SET_RATE_PARENT, 0x48, 6, 0, }, + { HISTB_UART0_CLK, "clk_uart0", "75m", + CLK_SET_RATE_PARENT, 0x48, 10, 0, }, +}; + +static struct hisi_clock_data *hi3798cv200_sysctrl_clk_register( + struct platform_device *pdev) +{ + struct hisi_clock_data *clk_data; + int ret; + + clk_data = hisi_clk_alloc(pdev, HI3798CV200_SYSCTRL_NR_CLKS); + if (!clk_data) + return ERR_PTR(-ENOMEM); + + ret = hisi_clk_register_gate(hi3798cv200_sysctrl_gate_clks, + ARRAY_SIZE(hi3798cv200_sysctrl_gate_clks), + clk_data); + if (ret) + return ERR_PTR(ret); + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, &clk_data->clk_data); + if (ret) + goto unregister_gate; + + return clk_data; + +unregister_gate: + hisi_clk_unregister_gate(hi3798cv200_sysctrl_gate_clks, + ARRAY_SIZE(hi3798cv200_sysctrl_gate_clks), + clk_data); + return ERR_PTR(ret); +} + +static void hi3798cv200_sysctrl_clk_unregister(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + of_clk_del_provider(pdev->dev.of_node); + + hisi_clk_unregister_gate(hi3798cv200_sysctrl_gate_clks, + ARRAY_SIZE(hi3798cv200_sysctrl_gate_clks), + crg->clk_data); +} + +static const struct hisi_crg_funcs hi3798cv200_sysctrl_funcs = { + .register_clks = hi3798cv200_sysctrl_clk_register, + .unregister_clks = hi3798cv200_sysctrl_clk_unregister, +}; + +static const struct of_device_id hi3798cv200_crg_match_table[] = { + { .compatible = "hisilicon,hi3798cv200-crg", + .data = &hi3798cv200_crg_funcs }, + { .compatible = "hisilicon,hi3798cv200-sysctrl", + .data = &hi3798cv200_sysctrl_funcs }, + { } +}; +MODULE_DEVICE_TABLE(of, hi3798cv200_crg_match_table); + +static int hi3798cv200_crg_probe(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg; + + crg = devm_kmalloc(&pdev->dev, sizeof(*crg), GFP_KERNEL); + if (!crg) + return -ENOMEM; + + crg->funcs = of_device_get_match_data(&pdev->dev); + if (!crg->funcs) + return -ENOENT; + + crg->rstc = hisi_reset_init(pdev); + if (!crg->rstc) + return -ENOMEM; + + crg->clk_data = crg->funcs->register_clks(pdev); + if (IS_ERR(crg->clk_data)) { + hisi_reset_exit(crg->rstc); + return PTR_ERR(crg->clk_data); + } + + platform_set_drvdata(pdev, crg); + return 0; +} + +static int hi3798cv200_crg_remove(struct platform_device *pdev) +{ + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + hisi_reset_exit(crg->rstc); + crg->funcs->unregister_clks(pdev); + return 0; +} + +static struct platform_driver hi3798cv200_crg_driver = { + .probe = hi3798cv200_crg_probe, + .remove = hi3798cv200_crg_remove, + .driver = { + .name = "hi3798cv200-crg", + .of_match_table = hi3798cv200_crg_match_table, + }, +}; + +static int __init hi3798cv200_crg_init(void) +{ + return platform_driver_register(&hi3798cv200_crg_driver); +} +core_initcall(hi3798cv200_crg_init); + +static void __exit hi3798cv200_crg_exit(void) +{ + platform_driver_unregister(&hi3798cv200_crg_driver); +} +module_exit(hi3798cv200_crg_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("HiSilicon Hi3798CV200 CRG Driver"); diff --git a/drivers/clk/hisilicon/crg.h b/drivers/clk/hisilicon/crg.h new file mode 100644 index 000000000000..e0739717de9a --- /dev/null +++ b/drivers/clk/hisilicon/crg.h @@ -0,0 +1,34 @@ +/* + * HiSilicon Clock and Reset Driver Header + * + * Copyright (c) 2016 HiSilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __HISI_CRG_H +#define __HISI_CRG_H + +struct hisi_clock_data; +struct hisi_reset_controller; + +struct hisi_crg_funcs { + struct hisi_clock_data* (*register_clks)(struct platform_device *pdev); + void (*unregister_clks)(struct platform_device *pdev); +}; + +struct hisi_crg_dev { + struct hisi_clock_data *clk_data; + struct hisi_reset_controller *rstc; + const struct hisi_crg_funcs *funcs; +}; + +#endif /* __HISI_CRG_H */ diff --git a/drivers/clk/imx/clk-imx31.c b/drivers/clk/imx/clk-imx31.c index 6a964144a5b5..cbce308aad04 100644 --- a/drivers/clk/imx/clk-imx31.c +++ b/drivers/clk/imx/clk-imx31.c @@ -21,6 +21,7 @@ #include <linux/io.h> #include <linux/err.h> #include <linux/of.h> +#include <linux/of_address.h> #include <soc/imx/revision.h> #include <soc/imx/timer.h> #include <asm/irq.h> @@ -72,14 +73,8 @@ static struct clk ** const uart_clks[] __initconst = { NULL }; -static void __init _mx31_clocks_init(unsigned long fref) +static void __init _mx31_clocks_init(void __iomem *base, unsigned long fref) { - void __iomem *base; - struct device_node *np; - - base = ioremap(MX31_CCM_BASE_ADDR, SZ_4K); - BUG_ON(!base); - clk[dummy] = imx_clk_fixed("dummy", 0); clk[ckih] = imx_clk_fixed("ckih", fref); clk[ckil] = imx_clk_fixed("ckil", 32768); @@ -147,21 +142,17 @@ static void __init _mx31_clocks_init(unsigned long fref) clk_prepare_enable(clk[iim_gate]); mx31_revision(); clk_disable_unprepare(clk[iim_gate]); - - np = of_find_compatible_node(NULL, NULL, "fsl,imx31-ccm"); - - if (np) { - clk_data.clks = clk; - clk_data.clk_num = ARRAY_SIZE(clk); - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - } } -int __init mx31_clocks_init(void) +int __init mx31_clocks_init(unsigned long fref) { - u32 fref = 26000000; /* default */ + void __iomem *base; + + base = ioremap(MX31_CCM_BASE_ADDR, SZ_4K); + if (!base) + panic("%s: failed to map registers\n", __func__); - _mx31_clocks_init(fref); + _mx31_clocks_init(base, fref); clk_register_clkdev(clk[gpt_gate], "per", "imx-gpt.0"); clk_register_clkdev(clk[ipg], "ipg", "imx-gpt.0"); @@ -224,22 +215,31 @@ int __init mx31_clocks_init(void) return 0; } -int __init mx31_clocks_init_dt(void) +static void __init mx31_clocks_init_dt(struct device_node *np) { - struct device_node *np; + struct device_node *osc_np; u32 fref = 26000000; /* default */ + void __iomem *ccm; - for_each_compatible_node(np, NULL, "fixed-clock") { - if (!of_device_is_compatible(np, "fsl,imx-osc26m")) + for_each_compatible_node(osc_np, NULL, "fixed-clock") { + if (!of_device_is_compatible(osc_np, "fsl,imx-osc26m")) continue; - if (!of_property_read_u32(np, "clock-frequency", &fref)) { - of_node_put(np); + if (!of_property_read_u32(osc_np, "clock-frequency", &fref)) { + of_node_put(osc_np); break; } } - _mx31_clocks_init(fref); + ccm = of_iomap(np, 0); + if (!ccm) + panic("%s: failed to map registers\n", __func__); - return 0; + _mx31_clocks_init(ccm, fref); + + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); } + +CLK_OF_DECLARE(imx31_ccm, "fsl,imx31-ccm", mx31_clocks_init_dt); diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index ce8ea10407e4..42ffc1c92bab 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -156,10 +156,267 @@ static struct clk ** const uart_clks[] __initconst = { NULL }; +static int ldb_di_sel_by_clock_id(int clock_id) +{ + switch (clock_id) { + case IMX6QDL_CLK_PLL5_VIDEO_DIV: + if (clk_on_imx6q() && + imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) + return -ENOENT; + return 0; + case IMX6QDL_CLK_PLL2_PFD0_352M: + return 1; + case IMX6QDL_CLK_PLL2_PFD2_396M: + return 2; + case IMX6QDL_CLK_MMDC_CH1_AXI: + return 3; + case IMX6QDL_CLK_PLL3_USB_OTG: + return 4; + default: + return -ENOENT; + } +} + +static void of_assigned_ldb_sels(struct device_node *node, + unsigned int *ldb_di0_sel, + unsigned int *ldb_di1_sel) +{ + struct of_phandle_args clkspec; + int index, rc, num_parents; + int parent, child, sel; + + num_parents = of_count_phandle_with_args(node, "assigned-clock-parents", + "#clock-cells"); + for (index = 0; index < num_parents; index++) { + rc = of_parse_phandle_with_args(node, "assigned-clock-parents", + "#clock-cells", index, &clkspec); + if (rc < 0) { + /* skip empty (null) phandles */ + if (rc == -ENOENT) + continue; + else + return; + } + if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) { + pr_err("ccm: parent clock %d not in ccm\n", index); + return; + } + parent = clkspec.args[0]; + + rc = of_parse_phandle_with_args(node, "assigned-clocks", + "#clock-cells", index, &clkspec); + if (rc < 0) + return; + if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) { + pr_err("ccm: child clock %d not in ccm\n", index); + return; + } + child = clkspec.args[0]; + + if (child != IMX6QDL_CLK_LDB_DI0_SEL && + child != IMX6QDL_CLK_LDB_DI1_SEL) + continue; + + sel = ldb_di_sel_by_clock_id(parent); + if (sel < 0) { + pr_err("ccm: invalid ldb_di%d parent clock: %d\n", + child == IMX6QDL_CLK_LDB_DI1_SEL, parent); + continue; + } + + if (child == IMX6QDL_CLK_LDB_DI0_SEL) + *ldb_di0_sel = sel; + if (child == IMX6QDL_CLK_LDB_DI1_SEL) + *ldb_di1_sel = sel; + } +} + +#define CCM_CCDR 0x04 +#define CCM_CCSR 0x0c +#define CCM_CS2CDR 0x2c + +#define CCDR_MMDC_CH1_MASK BIT(16) +#define CCSR_PLL3_SW_CLK_SEL BIT(0) + +#define CS2CDR_LDB_DI0_CLK_SEL_SHIFT 9 +#define CS2CDR_LDB_DI1_CLK_SEL_SHIFT 12 + +static void __init imx6q_mmdc_ch1_mask_handshake(void __iomem *ccm_base) +{ + unsigned int reg; + + reg = readl_relaxed(ccm_base + CCM_CCDR); + reg |= CCDR_MMDC_CH1_MASK; + writel_relaxed(reg, ccm_base + CCM_CCDR); +} + +/* + * The only way to disable the MMDC_CH1 clock is to move it to pll3_sw_clk + * via periph2_clk2_sel and then to disable pll3_sw_clk by selecting the + * bypass clock source, since there is no CG bit for mmdc_ch1. + */ +static void mmdc_ch1_disable(void __iomem *ccm_base) +{ + unsigned int reg; + + clk_set_parent(clk[IMX6QDL_CLK_PERIPH2_CLK2_SEL], + clk[IMX6QDL_CLK_PLL3_USB_OTG]); + + /* + * Handshake with mmdc_ch1 module must be masked when changing + * periph2_clk_sel. + */ + clk_set_parent(clk[IMX6QDL_CLK_PERIPH2], clk[IMX6QDL_CLK_PERIPH2_CLK2]); + + /* Disable pll3_sw_clk by selecting the bypass clock source */ + reg = readl_relaxed(ccm_base + CCM_CCSR); + reg |= CCSR_PLL3_SW_CLK_SEL; + writel_relaxed(reg, ccm_base + CCM_CCSR); +} + +static void mmdc_ch1_reenable(void __iomem *ccm_base) +{ + unsigned int reg; + + /* Enable pll3_sw_clk by disabling the bypass */ + reg = readl_relaxed(ccm_base + CCM_CCSR); + reg &= ~CCSR_PLL3_SW_CLK_SEL; + writel_relaxed(reg, ccm_base + CCM_CCSR); + + clk_set_parent(clk[IMX6QDL_CLK_PERIPH2], clk[IMX6QDL_CLK_PERIPH2_PRE]); +} + +/* + * We have to follow a strict procedure when changing the LDB clock source, + * otherwise we risk introducing a glitch that can lock up the LDB divider. + * Things to keep in mind: + * + * 1. The current and new parent clock inputs to the mux must be disabled. + * 2. The default clock input for ldb_di0/1_clk_sel is mmdc_ch1_axi, which + * has no CG bit. + * 3. pll2_pfd2_396m can not be gated if it is used as memory clock. + * 4. In the RTL implementation of the LDB_DI_CLK_SEL muxes the top four + * options are in one mux and the PLL3 option along with three unused + * inputs is in a second mux. There is a third mux with two inputs used + * to decide between the first and second 4-port mux: + * + * pll5_video_div 0 --|\ + * pll2_pfd0_352m 1 --| |_ + * pll2_pfd2_396m 2 --| | `-|\ + * mmdc_ch1_axi 3 --|/ | | + * | |-- + * pll3_usb_otg 4 --|\ | | + * 5 --| |_,-|/ + * 6 --| | + * 7 --|/ + * + * The ldb_di0/1_clk_sel[1:0] bits control both 4-port muxes at the same time. + * The ldb_di0/1_clk_sel[2] bit controls the 2-port mux. The code below + * switches the parent to the bottom mux first and then manipulates the top + * mux to ensure that no glitch will enter the divider. + */ +static void init_ldb_clks(struct device_node *np, void __iomem *ccm_base) +{ + unsigned int reg; + unsigned int sel[2][4]; + int i; + + reg = readl_relaxed(ccm_base + CCM_CS2CDR); + sel[0][0] = (reg >> CS2CDR_LDB_DI0_CLK_SEL_SHIFT) & 7; + sel[1][0] = (reg >> CS2CDR_LDB_DI1_CLK_SEL_SHIFT) & 7; + + sel[0][3] = sel[0][2] = sel[0][1] = sel[0][0]; + sel[1][3] = sel[1][2] = sel[1][1] = sel[1][0]; + + of_assigned_ldb_sels(np, &sel[0][3], &sel[1][3]); + + for (i = 0; i < 2; i++) { + /* Warn if a glitch might have been introduced already */ + if (sel[i][0] != 3) { + pr_warn("ccm: ldb_di%d_sel already changed from reset value: %d\n", + i, sel[i][0]); + } + + if (sel[i][0] == sel[i][3]) + continue; + + /* Only switch to or from pll2_pfd2_396m if it is disabled */ + if ((sel[i][0] == 2 || sel[i][3] == 2) && + (clk_get_parent(clk[IMX6QDL_CLK_PERIPH_PRE]) == + clk[IMX6QDL_CLK_PLL2_PFD2_396M])) { + pr_err("ccm: ldb_di%d_sel: couldn't disable pll2_pfd2_396m\n", + i); + sel[i][3] = sel[i][2] = sel[i][1] = sel[i][0]; + continue; + } + + /* First switch to the bottom mux */ + sel[i][1] = sel[i][0] | 4; + + /* Then configure the top mux before switching back to it */ + sel[i][2] = sel[i][3] | 4; + + pr_debug("ccm: switching ldb_di%d_sel: %d->%d->%d->%d\n", i, + sel[i][0], sel[i][1], sel[i][2], sel[i][3]); + } + + if (sel[0][0] == sel[0][3] && sel[1][0] == sel[1][3]) + return; + + mmdc_ch1_disable(ccm_base); + + for (i = 1; i < 4; i++) { + reg = readl_relaxed(ccm_base + CCM_CS2CDR); + reg &= ~((7 << CS2CDR_LDB_DI0_CLK_SEL_SHIFT) | + (7 << CS2CDR_LDB_DI1_CLK_SEL_SHIFT)); + reg |= ((sel[0][i] << CS2CDR_LDB_DI0_CLK_SEL_SHIFT) | + (sel[1][i] << CS2CDR_LDB_DI1_CLK_SEL_SHIFT)); + writel_relaxed(reg, ccm_base + CCM_CS2CDR); + } + + mmdc_ch1_reenable(ccm_base); +} + +#define CCM_ANALOG_PLL_VIDEO 0xa0 +#define CCM_ANALOG_PFD_480 0xf0 +#define CCM_ANALOG_PFD_528 0x100 + +#define PLL_ENABLE BIT(13) + +#define PFD0_CLKGATE BIT(7) +#define PFD1_CLKGATE BIT(15) +#define PFD2_CLKGATE BIT(23) +#define PFD3_CLKGATE BIT(31) + +static void disable_anatop_clocks(void __iomem *anatop_base) +{ + unsigned int reg; + + /* Make sure PLL2 PFDs 0-2 are gated */ + reg = readl_relaxed(anatop_base + CCM_ANALOG_PFD_528); + /* Cannot gate PFD2 if pll2_pfd2_396m is the parent of MMDC clock */ + if (clk_get_parent(clk[IMX6QDL_CLK_PERIPH_PRE]) == + clk[IMX6QDL_CLK_PLL2_PFD2_396M]) + reg |= PFD0_CLKGATE | PFD1_CLKGATE; + else + reg |= PFD0_CLKGATE | PFD1_CLKGATE | PFD2_CLKGATE; + writel_relaxed(reg, anatop_base + CCM_ANALOG_PFD_528); + + /* Make sure PLL3 PFDs 0-3 are gated */ + reg = readl_relaxed(anatop_base + CCM_ANALOG_PFD_480); + reg |= PFD0_CLKGATE | PFD1_CLKGATE | PFD2_CLKGATE | PFD3_CLKGATE; + writel_relaxed(reg, anatop_base + CCM_ANALOG_PFD_480); + + /* Make sure PLL5 is disabled */ + reg = readl_relaxed(anatop_base + CCM_ANALOG_PLL_VIDEO); + reg &= ~PLL_ENABLE; + writel_relaxed(reg, anatop_base + CCM_ANALOG_PLL_VIDEO); +} + static void __init imx6q_clocks_init(struct device_node *ccm_node) { struct device_node *np; - void __iomem *base; + void __iomem *anatop_base, *base; int i; int ret; @@ -172,7 +429,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_ANACLK2] = imx_obtain_fixed_clock("anaclk2", 0); np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); - base = of_iomap(np, 0); + anatop_base = base = of_iomap(np, 0); WARN_ON(!base); /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */ @@ -330,8 +587,20 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_GPU3D_SHADER_SEL] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels)); clk[IMX6QDL_CLK_IPU1_SEL] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); clk[IMX6QDL_CLK_IPU2_SEL] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); - clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); - clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); + + disable_anatop_clocks(anatop_base); + + imx6q_mmdc_ch1_mask_handshake(base); + + /* + * The LDB_DI0/1_SEL muxes are registered read-only due to a hardware + * bug. Set the muxes to the requested values before registering the + * ldb_di_sel clocks. + */ + init_ldb_clks(np, base); + + clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_ldb("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels)); + clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_ldb("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels)); clk[IMX6QDL_CLK_IPU1_DI0_PRE_SEL] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); clk[IMX6QDL_CLK_IPU1_DI1_PRE_SEL] = imx_clk_mux_flags("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); clk[IMX6QDL_CLK_IPU2_DI0_PRE_SEL] = imx_clk_mux_flags("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); @@ -582,12 +851,6 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk_register_clkdev(clk[IMX6QDL_CLK_ENET_REF], "enet_ref", NULL); - if ((imx_get_soc_revision() != IMX_CHIP_REVISION_1_0) || - clk_on_imx6dl()) { - clk_set_parent(clk[IMX6QDL_CLK_LDB_DI0_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]); - clk_set_parent(clk[IMX6QDL_CLK_LDB_DI1_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]); - } - clk_set_rate(clk[IMX6QDL_CLK_PLL3_PFD1_540M], 540000000); if (clk_on_imx6dl()) clk_set_parent(clk[IMX6QDL_CLK_IPU1_SEL], clk[IMX6QDL_CLK_PLL3_PFD1_540M]); diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c index d1d7787ce211..75c35fb12b60 100644 --- a/drivers/clk/imx/clk-imx6ul.c +++ b/drivers/clk/imx/clk-imx6ul.c @@ -64,6 +64,10 @@ static const char *perclk_sels[] = { "ipg", "osc", }; static const char *lcdif_sels[] = { "lcdif_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; static const char *csi_sels[] = { "osc", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", }; static const char *sim_sels[] = { "sim_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; +/* epdc_pre_sels, epdc_sels, esai_sels only exists on i.MX6ULL */ +static const char *epdc_pre_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd2_508m", }; +static const char *esai_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll5_video_div", "pll3_usb_otg", }; +static const char *epdc_sels[] = { "epdc_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; static struct clk *clks[IMX6UL_CLK_END]; static struct clk_onecell_data clk_data; @@ -102,6 +106,17 @@ static u32 share_count_audio; static u32 share_count_sai1; static u32 share_count_sai2; static u32 share_count_sai3; +static u32 share_count_esai; + +static inline int clk_on_imx6ul(void) +{ + return of_machine_is_compatible("fsl,imx6ul"); +} + +static inline int clk_on_imx6ull(void) +{ + return of_machine_is_compatible("fsl,imx6ull"); +} static void __init imx6ul_clocks_init(struct device_node *ccm_node) { @@ -238,12 +253,19 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6UL_CLK_QSPI1_SEL] = imx_clk_mux("qspi1_sel", base + 0x1c, 7, 3, qspi1_sels, ARRAY_SIZE(qspi1_sels)); clks[IMX6UL_CLK_PERCLK_SEL] = imx_clk_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels)); clks[IMX6UL_CLK_CAN_SEL] = imx_clk_mux("can_sel", base + 0x20, 8, 2, can_sels, ARRAY_SIZE(can_sels)); + if (clk_on_imx6ull()) + clks[IMX6ULL_CLK_ESAI_SEL] = imx_clk_mux("esai_sel", base + 0x20, 19, 2, esai_sels, ARRAY_SIZE(esai_sels)); clks[IMX6UL_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); clks[IMX6UL_CLK_ENFC_SEL] = imx_clk_mux("enfc_sel", base + 0x2c, 15, 3, enfc_sels, ARRAY_SIZE(enfc_sels)); clks[IMX6UL_CLK_LDB_DI0_SEL] = imx_clk_mux("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di0_sels, ARRAY_SIZE(ldb_di0_sels)); clks[IMX6UL_CLK_SPDIF_SEL] = imx_clk_mux("spdif_sel", base + 0x30, 20, 2, spdif_sels, ARRAY_SIZE(spdif_sels)); - clks[IMX6UL_CLK_SIM_PRE_SEL] = imx_clk_mux("sim_pre_sel", base + 0x34, 15, 3, sim_pre_sels, ARRAY_SIZE(sim_pre_sels)); - clks[IMX6UL_CLK_SIM_SEL] = imx_clk_mux("sim_sel", base + 0x34, 9, 3, sim_sels, ARRAY_SIZE(sim_sels)); + if (clk_on_imx6ul()) { + clks[IMX6UL_CLK_SIM_PRE_SEL] = imx_clk_mux("sim_pre_sel", base + 0x34, 15, 3, sim_pre_sels, ARRAY_SIZE(sim_pre_sels)); + clks[IMX6UL_CLK_SIM_SEL] = imx_clk_mux("sim_sel", base + 0x34, 9, 3, sim_sels, ARRAY_SIZE(sim_sels)); + } else if (clk_on_imx6ull()) { + clks[IMX6ULL_CLK_EPDC_PRE_SEL] = imx_clk_mux("epdc_pre_sel", base + 0x34, 15, 3, epdc_pre_sels, ARRAY_SIZE(epdc_pre_sels)); + clks[IMX6ULL_CLK_EPDC_SEL] = imx_clk_mux("epdc_sel", base + 0x34, 9, 3, epdc_sels, ARRAY_SIZE(epdc_sels)); + } clks[IMX6UL_CLK_ECSPI_SEL] = imx_clk_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); clks[IMX6UL_CLK_LCDIF_PRE_SEL] = imx_clk_mux("lcdif_pre_sel", base + 0x38, 15, 3, lcdif_pre_sels, ARRAY_SIZE(lcdif_pre_sels)); clks[IMX6UL_CLK_LCDIF_SEL] = imx_clk_mux("lcdif_sel", base + 0x38, 9, 3, lcdif_sels, ARRAY_SIZE(lcdif_sels)); @@ -276,6 +298,10 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6UL_CLK_SAI3_PODF] = imx_clk_divider("sai3_podf", "sai3_pred", base + 0x28, 16, 6); clks[IMX6UL_CLK_SAI1_PRED] = imx_clk_divider("sai1_pred", "sai1_sel", base + 0x28, 6, 3); clks[IMX6UL_CLK_SAI1_PODF] = imx_clk_divider("sai1_podf", "sai1_pred", base + 0x28, 0, 6); + if (clk_on_imx6ull()) { + clks[IMX6ULL_CLK_ESAI_PRED] = imx_clk_divider("esai_pred", "esai_sel", base + 0x28, 9, 3); + clks[IMX6ULL_CLK_ESAI_PODF] = imx_clk_divider("esai_podf", "esai_pred", base + 0x28, 25, 3); + } clks[IMX6UL_CLK_ENFC_PRED] = imx_clk_divider("enfc_pred", "enfc_sel", base + 0x2c, 18, 3); clks[IMX6UL_CLK_ENFC_PODF] = imx_clk_divider("enfc_podf", "enfc_pred", base + 0x2c, 21, 6); clks[IMX6UL_CLK_SAI2_PRED] = imx_clk_divider("sai2_pred", "sai2_sel", base + 0x2c, 6, 3); @@ -298,9 +324,15 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6UL_CLK_APBHDMA] = imx_clk_gate2("apbh_dma", "bch_podf", base + 0x68, 4); clks[IMX6UL_CLK_ASRC_IPG] = imx_clk_gate2_shared("asrc_ipg", "ahb", base + 0x68, 6, &share_count_asrc); clks[IMX6UL_CLK_ASRC_MEM] = imx_clk_gate2_shared("asrc_mem", "ahb", base + 0x68, 6, &share_count_asrc); - clks[IMX6UL_CLK_CAAM_MEM] = imx_clk_gate2("caam_mem", "ahb", base + 0x68, 8); - clks[IMX6UL_CLK_CAAM_ACLK] = imx_clk_gate2("caam_aclk", "ahb", base + 0x68, 10); - clks[IMX6UL_CLK_CAAM_IPG] = imx_clk_gate2("caam_ipg", "ipg", base + 0x68, 12); + if (clk_on_imx6ul()) { + clks[IMX6UL_CLK_CAAM_MEM] = imx_clk_gate2("caam_mem", "ahb", base + 0x68, 8); + clks[IMX6UL_CLK_CAAM_ACLK] = imx_clk_gate2("caam_aclk", "ahb", base + 0x68, 10); + clks[IMX6UL_CLK_CAAM_IPG] = imx_clk_gate2("caam_ipg", "ipg", base + 0x68, 12); + } else if (clk_on_imx6ull()) { + clks[IMX6ULL_CLK_DCP_CLK] = imx_clk_gate2("dcp", "ahb", base + 0x68, 10); + clks[IMX6UL_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x68, 12); + clks[IMX6UL_CLK_ENET_AHB] = imx_clk_gate2("enet_ahb", "ahb", base + 0x68, 12); + } clks[IMX6UL_CLK_CAN1_IPG] = imx_clk_gate2("can1_ipg", "ipg", base + 0x68, 14); clks[IMX6UL_CLK_CAN1_SERIAL] = imx_clk_gate2("can1_serial", "can_podf", base + 0x68, 16); clks[IMX6UL_CLK_CAN2_IPG] = imx_clk_gate2("can2_ipg", "ipg", base + 0x68, 18); @@ -309,7 +341,10 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6UL_CLK_GPT2_SERIAL] = imx_clk_gate2("gpt2_serial", "perclk", base + 0x68, 26); clks[IMX6UL_CLK_UART2_IPG] = imx_clk_gate2("uart2_ipg", "ipg", base + 0x68, 28); clks[IMX6UL_CLK_UART2_SERIAL] = imx_clk_gate2("uart2_serial", "uart_podf", base + 0x68, 28); - clks[IMX6UL_CLK_AIPSTZ3] = imx_clk_gate2("aips_tz3", "ahb", base + 0x68, 30); + if (clk_on_imx6ul()) + clks[IMX6UL_CLK_AIPSTZ3] = imx_clk_gate2("aips_tz3", "ahb", base + 0x68, 30); + else if (clk_on_imx6ull()) + clks[IMX6UL_CLK_AIPSTZ3] = imx_clk_gate2("aips_tz3", "ahb", base + 0x80, 18); /* CCGR1 */ clks[IMX6UL_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_podf", base + 0x6c, 0); @@ -328,6 +363,11 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6UL_CLK_UART4_SERIAL] = imx_clk_gate2("uart4_serail", "uart_podf", base + 0x6c, 24); /* CCGR2 */ + if (clk_on_imx6ull()) { + clks[IMX6ULL_CLK_ESAI_EXTAL] = imx_clk_gate2_shared("esai_extal", "esai_podf", base + 0x70, 0, &share_count_esai); + clks[IMX6ULL_CLK_ESAI_IPG] = imx_clk_gate2_shared("esai_ipg", "ahb", base + 0x70, 0, &share_count_esai); + clks[IMX6ULL_CLK_ESAI_MEM] = imx_clk_gate2_shared("esai_mem", "ahb", base + 0x70, 0, &share_count_esai); + } clks[IMX6UL_CLK_CSI] = imx_clk_gate2("csi", "csi_podf", base + 0x70, 2); clks[IMX6UL_CLK_I2C1] = imx_clk_gate2("i2c1", "perclk", base + 0x70, 6); clks[IMX6UL_CLK_I2C2] = imx_clk_gate2("i2c2", "perclk", base + 0x70, 8); @@ -340,8 +380,13 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) /* CCGR3 */ clks[IMX6UL_CLK_UART5_IPG] = imx_clk_gate2("uart5_ipg", "ipg", base + 0x74, 2); clks[IMX6UL_CLK_UART5_SERIAL] = imx_clk_gate2("uart5_serial", "uart_podf", base + 0x74, 2); - clks[IMX6UL_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x74, 4); - clks[IMX6UL_CLK_ENET_AHB] = imx_clk_gate2("enet_ahb", "ahb", base + 0x74, 4); + if (clk_on_imx6ul()) { + clks[IMX6UL_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x74, 4); + clks[IMX6UL_CLK_ENET_AHB] = imx_clk_gate2("enet_ahb", "ahb", base + 0x74, 4); + } else if (clk_on_imx6ull()) { + clks[IMX6ULL_CLK_EPDC_ACLK] = imx_clk_gate2("epdc_aclk", "axi", base + 0x74, 4); + clks[IMX6ULL_CLK_EPDC_PIX] = imx_clk_gate2("epdc_pix", "epdc_podf", base + 0x74, 4); + } clks[IMX6UL_CLK_UART6_IPG] = imx_clk_gate2("uart6_ipg", "ipg", base + 0x74, 6); clks[IMX6UL_CLK_UART6_SERIAL] = imx_clk_gate2("uart6_serial", "uart_podf", base + 0x74, 6); clks[IMX6UL_CLK_LCDIF_PIX] = imx_clk_gate2("lcdif_pix", "lcdif_podf", base + 0x74, 10); @@ -385,8 +430,10 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6UL_CLK_USBOH3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0); clks[IMX6UL_CLK_USDHC1] = imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2); clks[IMX6UL_CLK_USDHC2] = imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); - clks[IMX6UL_CLK_SIM1] = imx_clk_gate2("sim1", "sim_sel", base + 0x80, 6); - clks[IMX6UL_CLK_SIM2] = imx_clk_gate2("sim2", "sim_sel", base + 0x80, 8); + if (clk_on_imx6ul()) { + clks[IMX6UL_CLK_SIM1] = imx_clk_gate2("sim1", "sim_sel", base + 0x80, 6); + clks[IMX6UL_CLK_SIM2] = imx_clk_gate2("sim2", "sim_sel", base + 0x80, 8); + } clks[IMX6UL_CLK_EIM] = imx_clk_gate2("eim", "eim_slow_podf", base + 0x80, 10); clks[IMX6UL_CLK_PWM8] = imx_clk_gate2("pwm8", "perclk", base + 0x80, 16); clks[IMX6UL_CLK_UART8_IPG] = imx_clk_gate2("uart8_ipg", "ipg", base + 0x80, 14); @@ -441,7 +488,10 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) } clk_set_parent(clks[IMX6UL_CLK_CAN_SEL], clks[IMX6UL_CLK_PLL3_60M]); - clk_set_parent(clks[IMX6UL_CLK_SIM_PRE_SEL], clks[IMX6UL_CLK_PLL3_USB_OTG]); + if (clk_on_imx6ul()) + clk_set_parent(clks[IMX6UL_CLK_SIM_PRE_SEL], clks[IMX6UL_CLK_PLL3_USB_OTG]); + else if (clk_on_imx6ull()) + clk_set_parent(clks[IMX6ULL_CLK_EPDC_PRE_SEL], clks[IMX6UL_CLK_PLL3_PFD2]); clk_set_parent(clks[IMX6UL_CLK_ENFC_SEL], clks[IMX6UL_CLK_PLL2_PFD2]); } diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c index 7a6acc3e4a92..ed3a2df536ea 100644 --- a/drivers/clk/imx/clk-pllv3.c +++ b/drivers/clk/imx/clk-pllv3.c @@ -234,6 +234,7 @@ static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long max_rate = parent_rate * 54; u32 div; u32 mfn, mfd = 1000000; + u32 max_mfd = 0x3FFFFFFF; u64 temp64; if (rate > max_rate) @@ -241,6 +242,9 @@ static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate, else if (rate < min_rate) rate = min_rate; + if (parent_rate <= max_mfd) + mfd = parent_rate; + div = rate / parent_rate; temp64 = (u64) (rate - div * parent_rate); temp64 *= mfd; @@ -262,11 +266,15 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long max_rate = parent_rate * 54; u32 val, div; u32 mfn, mfd = 1000000; + u32 max_mfd = 0x3FFFFFFF; u64 temp64; if (rate < min_rate || rate > max_rate) return -EINVAL; + if (parent_rate <= max_mfd) + mfd = parent_rate; + div = rate / parent_rate; temp64 = (u64) (rate - div * parent_rate); temp64 *= mfd; diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 3799ff82a9b4..4afad3b96a61 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -75,6 +75,14 @@ static inline struct clk *imx_clk_fixed(const char *name, int rate) return clk_register_fixed_rate(NULL, name, NULL, 0, rate); } +static inline struct clk *imx_clk_mux_ldb(const char *name, void __iomem *reg, + u8 shift, u8 width, const char **parents, int num_parents) +{ + return clk_register_mux(NULL, name, parents, num_parents, + CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, reg, + shift, width, CLK_MUX_READ_ONLY, &imx_ccm_lock); +} + static inline struct clk *imx_clk_fixed_factor(const char *name, const char *parent, unsigned int mult, unsigned int div) { diff --git a/drivers/clk/keystone/pll.c b/drivers/clk/keystone/pll.c index a26ba2184454..e7e840fb74ea 100644 --- a/drivers/clk/keystone/pll.c +++ b/drivers/clk/keystone/pll.c @@ -154,7 +154,7 @@ out: } /** - * _of_clk_init - PLL initialisation via DT + * _of_pll_clk_init - PLL initialisation via DT * @node: device tree node for this clock * @pllctrl: If true, lower 6 bits of multiplier is in pllm register of * pll controller, else it is in the control register0(bit 11-6) @@ -235,7 +235,7 @@ CLK_OF_DECLARE(keystone_pll_clock, "ti,keystone,pll-clock", of_keystone_pll_clk_init); /** - * of_keystone_pll_main_clk_init - Main PLL initialisation DT wrapper + * of_keystone_main_pll_clk_init - Main PLL initialisation DT wrapper * @node: device tree node for this clock */ static void __init of_keystone_main_pll_clk_init(struct device_node *node) @@ -267,25 +267,30 @@ static void __init of_pll_div_clk_init(struct device_node *node) parent_name = of_clk_get_parent_name(node, 0); if (!parent_name) { pr_err("%s: missing parent clock\n", __func__); + iounmap(reg); return; } if (of_property_read_u32(node, "bit-shift", &shift)) { pr_err("%s: missing 'shift' property\n", __func__); + iounmap(reg); return; } if (of_property_read_u32(node, "bit-mask", &mask)) { pr_err("%s: missing 'bit-mask' property\n", __func__); + iounmap(reg); return; } clk = clk_register_divider(NULL, clk_name, parent_name, 0, reg, shift, mask, 0, NULL); - if (clk) + if (clk) { of_clk_add_provider(node, of_clk_src_simple_get, clk); - else + } else { pr_err("%s: error registering divider %s\n", __func__, clk_name); + iounmap(reg); + } } CLK_OF_DECLARE(pll_divider_clock, "ti,keystone,pll-divider-clock", of_pll_div_clk_init); diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig index f042bd2a6a99..0bd631a41f6a 100644 --- a/drivers/clk/mediatek/Kconfig +++ b/drivers/clk/mediatek/Kconfig @@ -6,6 +6,49 @@ config COMMON_CLK_MEDIATEK ---help--- Mediatek SoCs' clock support. +config COMMON_CLK_MT2701 + bool "Clock driver for Mediatek MT2701" + select COMMON_CLK_MEDIATEK + default ARCH_MEDIATEK + ---help--- + This driver supports Mediatek MT2701 basic clocks. + +config COMMON_CLK_MT2701_MMSYS + bool "Clock driver for Mediatek MT2701 mmsys" + select COMMON_CLK_MT2701 + ---help--- + This driver supports Mediatek MT2701 mmsys clocks. + +config COMMON_CLK_MT2701_IMGSYS + bool "Clock driver for Mediatek MT2701 imgsys" + select COMMON_CLK_MT2701 + ---help--- + This driver supports Mediatek MT2701 imgsys clocks. + +config COMMON_CLK_MT2701_VDECSYS + bool "Clock driver for Mediatek MT2701 vdecsys" + select COMMON_CLK_MT2701 + ---help--- + This driver supports Mediatek MT2701 vdecsys clocks. + +config COMMON_CLK_MT2701_HIFSYS + bool "Clock driver for Mediatek MT2701 hifsys" + select COMMON_CLK_MT2701 + ---help--- + This driver supports Mediatek MT2701 hifsys clocks. + +config COMMON_CLK_MT2701_ETHSYS + bool "Clock driver for Mediatek MT2701 ethsys" + select COMMON_CLK_MT2701 + ---help--- + This driver supports Mediatek MT2701 ethsys clocks. + +config COMMON_CLK_MT2701_BDPSYS + bool "Clock driver for Mediatek MT2701 bdpsys" + select COMMON_CLK_MT2701 + ---help--- + This driver supports Mediatek MT2701 bdpsys clocks. + config COMMON_CLK_MT8135 bool "Clock driver for Mediatek MT8135" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index 32e7222e7305..19ae7ef79b57 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -1,4 +1,11 @@ obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o obj-$(CONFIG_RESET_CONTROLLER) += reset.o +obj-$(CONFIG_COMMON_CLK_MT2701) += clk-mt2701.o +obj-$(CONFIG_COMMON_CLK_MT2701_BDPSYS) += clk-mt2701-bdp.o +obj-$(CONFIG_COMMON_CLK_MT2701_ETHSYS) += clk-mt2701-eth.o +obj-$(CONFIG_COMMON_CLK_MT2701_HIFSYS) += clk-mt2701-hif.o +obj-$(CONFIG_COMMON_CLK_MT2701_IMGSYS) += clk-mt2701-img.o +obj-$(CONFIG_COMMON_CLK_MT2701_MMSYS) += clk-mt2701-mm.o +obj-$(CONFIG_COMMON_CLK_MT2701_VDECSYS) += clk-mt2701-vdec.o obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c index d8787bf444eb..934bf0e45e26 100644 --- a/drivers/clk/mediatek/clk-gate.c +++ b/drivers/clk/mediatek/clk-gate.c @@ -61,6 +61,22 @@ static void mtk_cg_clr_bit(struct clk_hw *hw) regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit)); } +static void mtk_cg_set_bit_no_setclr(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_mtk_clk_gate(hw); + u32 cgbit = BIT(cg->bit); + + regmap_update_bits(cg->regmap, cg->sta_ofs, cgbit, cgbit); +} + +static void mtk_cg_clr_bit_no_setclr(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_mtk_clk_gate(hw); + u32 cgbit = BIT(cg->bit); + + regmap_update_bits(cg->regmap, cg->sta_ofs, cgbit, 0); +} + static int mtk_cg_enable(struct clk_hw *hw) { mtk_cg_clr_bit(hw); @@ -85,6 +101,30 @@ static void mtk_cg_disable_inv(struct clk_hw *hw) mtk_cg_clr_bit(hw); } +static int mtk_cg_enable_no_setclr(struct clk_hw *hw) +{ + mtk_cg_clr_bit_no_setclr(hw); + + return 0; +} + +static void mtk_cg_disable_no_setclr(struct clk_hw *hw) +{ + mtk_cg_set_bit_no_setclr(hw); +} + +static int mtk_cg_enable_inv_no_setclr(struct clk_hw *hw) +{ + mtk_cg_set_bit_no_setclr(hw); + + return 0; +} + +static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw) +{ + mtk_cg_clr_bit_no_setclr(hw); +} + const struct clk_ops mtk_clk_gate_ops_setclr = { .is_enabled = mtk_cg_bit_is_cleared, .enable = mtk_cg_enable, @@ -97,6 +137,18 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = { .disable = mtk_cg_disable_inv, }; +const struct clk_ops mtk_clk_gate_ops_no_setclr = { + .is_enabled = mtk_cg_bit_is_cleared, + .enable = mtk_cg_enable_no_setclr, + .disable = mtk_cg_disable_no_setclr, +}; + +const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = { + .is_enabled = mtk_cg_bit_is_set, + .enable = mtk_cg_enable_inv_no_setclr, + .disable = mtk_cg_disable_inv_no_setclr, +}; + struct clk *mtk_clk_register_gate( const char *name, const char *parent_name, diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h index b1821603b887..72ef89b3ad7b 100644 --- a/drivers/clk/mediatek/clk-gate.h +++ b/drivers/clk/mediatek/clk-gate.h @@ -36,6 +36,8 @@ static inline struct mtk_clk_gate *to_mtk_clk_gate(struct clk_hw *hw) extern const struct clk_ops mtk_clk_gate_ops_setclr; extern const struct clk_ops mtk_clk_gate_ops_setclr_inv; +extern const struct clk_ops mtk_clk_gate_ops_no_setclr; +extern const struct clk_ops mtk_clk_gate_ops_no_setclr_inv; struct clk *mtk_clk_register_gate( const char *name, diff --git a/drivers/clk/mediatek/clk-mt2701-bdp.c b/drivers/clk/mediatek/clk-mt2701-bdp.c new file mode 100644 index 000000000000..fe4964d05b5f --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2701-bdp.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Shunli Wang <shunli.wang@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include <dt-bindings/clock/mt2701-clk.h> + +static const struct mtk_gate_regs bdp0_cg_regs = { + .set_ofs = 0x0104, + .clr_ofs = 0x0108, + .sta_ofs = 0x0100, +}; + +static const struct mtk_gate_regs bdp1_cg_regs = { + .set_ofs = 0x0114, + .clr_ofs = 0x0118, + .sta_ofs = 0x0110, +}; + +#define GATE_BDP0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &bdp0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr_inv, \ + } + +#define GATE_BDP1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &bdp1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr_inv, \ + } + +static const struct mtk_gate bdp_clks[] = { + GATE_BDP0(CLK_BDP_BRG_BA, "brg_baclk", "mm_sel", 0), + GATE_BDP0(CLK_BDP_BRG_DRAM, "brg_dram", "mm_sel", 1), + GATE_BDP0(CLK_BDP_LARB_DRAM, "larb_dram", "mm_sel", 2), + GATE_BDP0(CLK_BDP_WR_VDI_PXL, "wr_vdi_pxl", "hdmi_0_deep340m", 3), + GATE_BDP0(CLK_BDP_WR_VDI_DRAM, "wr_vdi_dram", "mm_sel", 4), + GATE_BDP0(CLK_BDP_WR_B, "wr_bclk", "mm_sel", 5), + GATE_BDP0(CLK_BDP_DGI_IN, "dgi_in", "dpi1_sel", 6), + GATE_BDP0(CLK_BDP_DGI_OUT, "dgi_out", "dpi1_sel", 7), + GATE_BDP0(CLK_BDP_FMT_MAST_27, "fmt_mast_27", "dpi1_sel", 8), + GATE_BDP0(CLK_BDP_FMT_B, "fmt_bclk", "mm_sel", 9), + GATE_BDP0(CLK_BDP_OSD_B, "osd_bclk", "mm_sel", 10), + GATE_BDP0(CLK_BDP_OSD_DRAM, "osd_dram", "mm_sel", 11), + GATE_BDP0(CLK_BDP_OSD_AGENT, "osd_agent", "osd_sel", 12), + GATE_BDP0(CLK_BDP_OSD_PXL, "osd_pxl", "dpi1_sel", 13), + GATE_BDP0(CLK_BDP_RLE_B, "rle_bclk", "mm_sel", 14), + GATE_BDP0(CLK_BDP_RLE_AGENT, "rle_agent", "mm_sel", 15), + GATE_BDP0(CLK_BDP_RLE_DRAM, "rle_dram", "mm_sel", 16), + GATE_BDP0(CLK_BDP_F27M, "f27m", "di_sel", 17), + GATE_BDP0(CLK_BDP_F27M_VDOUT, "f27m_vdout", "di_sel", 18), + GATE_BDP0(CLK_BDP_F27_74_74, "f27_74_74", "di_sel", 19), + GATE_BDP0(CLK_BDP_F2FS, "f2fs", "di_sel", 20), + GATE_BDP0(CLK_BDP_F2FS74_148, "f2fs74_148", "di_sel", 21), + GATE_BDP0(CLK_BDP_FB, "fbclk", "mm_sel", 22), + GATE_BDP0(CLK_BDP_VDO_DRAM, "vdo_dram", "mm_sel", 23), + GATE_BDP0(CLK_BDP_VDO_2FS, "vdo_2fs", "di_sel", 24), + GATE_BDP0(CLK_BDP_VDO_B, "vdo_bclk", "mm_sel", 25), + GATE_BDP0(CLK_BDP_WR_DI_PXL, "wr_di_pxl", "di_sel", 26), + GATE_BDP0(CLK_BDP_WR_DI_DRAM, "wr_di_dram", "mm_sel", 27), + GATE_BDP0(CLK_BDP_WR_DI_B, "wr_di_bclk", "mm_sel", 28), + GATE_BDP0(CLK_BDP_NR_PXL, "nr_pxl", "nr_sel", 29), + GATE_BDP0(CLK_BDP_NR_DRAM, "nr_dram", "mm_sel", 30), + GATE_BDP0(CLK_BDP_NR_B, "nr_bclk", "mm_sel", 31), + GATE_BDP1(CLK_BDP_RX_F, "rx_fclk", "hadds2_fbclk", 0), + GATE_BDP1(CLK_BDP_RX_X, "rx_xclk", "clk26m", 1), + GATE_BDP1(CLK_BDP_RXPDT, "rxpdtclk", "hdmi_0_pix340m", 2), + GATE_BDP1(CLK_BDP_RX_CSCL_N, "rx_cscl_n", "clk26m", 3), + GATE_BDP1(CLK_BDP_RX_CSCL, "rx_cscl", "clk26m", 4), + GATE_BDP1(CLK_BDP_RX_DDCSCL_N, "rx_ddcscl_n", "hdmi_scl_rx", 5), + GATE_BDP1(CLK_BDP_RX_DDCSCL, "rx_ddcscl", "hdmi_scl_rx", 6), + GATE_BDP1(CLK_BDP_RX_VCO, "rx_vcoclk", "hadds2pll_294m", 7), + GATE_BDP1(CLK_BDP_RX_DP, "rx_dpclk", "hdmi_0_pll340m", 8), + GATE_BDP1(CLK_BDP_RX_P, "rx_pclk", "hdmi_0_pll340m", 9), + GATE_BDP1(CLK_BDP_RX_M, "rx_mclk", "hadds2pll_294m", 10), + GATE_BDP1(CLK_BDP_RX_PLL, "rx_pllclk", "hdmi_0_pix340m", 11), + GATE_BDP1(CLK_BDP_BRG_RT_B, "brg_rt_bclk", "mm_sel", 12), + GATE_BDP1(CLK_BDP_BRG_RT_DRAM, "brg_rt_dram", "mm_sel", 13), + GATE_BDP1(CLK_BDP_LARBRT_DRAM, "larbrt_dram", "mm_sel", 14), + GATE_BDP1(CLK_BDP_TMDS_SYN, "tmds_syn", "hdmi_0_pll340m", 15), + GATE_BDP1(CLK_BDP_HDMI_MON, "hdmi_mon", "hdmi_0_pll340m", 16), +}; + +static const struct of_device_id of_match_clk_mt2701_bdp[] = { + { .compatible = "mediatek,mt2701-bdpsys", }, + {} +}; + +static int clk_mt2701_bdp_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_BDP_NR); + + mtk_clk_register_gates(node, bdp_clks, ARRAY_SIZE(bdp_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + return r; +} + +static struct platform_driver clk_mt2701_bdp_drv = { + .probe = clk_mt2701_bdp_probe, + .driver = { + .name = "clk-mt2701-bdp", + .of_match_table = of_match_clk_mt2701_bdp, + }, +}; + +builtin_platform_driver(clk_mt2701_bdp_drv); diff --git a/drivers/clk/mediatek/clk-mt2701-eth.c b/drivers/clk/mediatek/clk-mt2701-eth.c new file mode 100644 index 000000000000..877be8715afa --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2701-eth.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Shunli Wang <shunli.wang@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include <dt-bindings/clock/mt2701-clk.h> + +static const struct mtk_gate_regs eth_cg_regs = { + .sta_ofs = 0x0030, +}; + +#define GATE_ETH(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = ð_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate eth_clks[] = { + GATE_ETH(CLK_ETHSYS_HSDMA, "hsdma_clk", "ethif_sel", 5), + GATE_ETH(CLK_ETHSYS_ESW, "esw_clk", "ethpll_500m_ck", 6), + GATE_ETH(CLK_ETHSYS_GP2, "gp2_clk", "trgpll", 7), + GATE_ETH(CLK_ETHSYS_GP1, "gp1_clk", "ethpll_500m_ck", 8), + GATE_ETH(CLK_ETHSYS_PCM, "pcm_clk", "ethif_sel", 11), + GATE_ETH(CLK_ETHSYS_GDMA, "gdma_clk", "ethif_sel", 14), + GATE_ETH(CLK_ETHSYS_I2S, "i2s_clk", "ethif_sel", 17), + GATE_ETH(CLK_ETHSYS_CRYPTO, "crypto_clk", "ethif_sel", 29), +}; + +static const struct of_device_id of_match_clk_mt2701_eth[] = { + { .compatible = "mediatek,mt2701-ethsys", }, + {} +}; + +static int clk_mt2701_eth_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_ETHSYS_NR); + + mtk_clk_register_gates(node, eth_clks, ARRAY_SIZE(eth_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + return r; +} + +static struct platform_driver clk_mt2701_eth_drv = { + .probe = clk_mt2701_eth_probe, + .driver = { + .name = "clk-mt2701-eth", + .of_match_table = of_match_clk_mt2701_eth, + }, +}; + +builtin_platform_driver(clk_mt2701_eth_drv); diff --git a/drivers/clk/mediatek/clk-mt2701-hif.c b/drivers/clk/mediatek/clk-mt2701-hif.c new file mode 100644 index 000000000000..18f3723be3e8 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2701-hif.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Shunli Wang <shunli.wang@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include <dt-bindings/clock/mt2701-clk.h> + +static const struct mtk_gate_regs hif_cg_regs = { + .sta_ofs = 0x0030, +}; + +#define GATE_HIF(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &hif_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate hif_clks[] = { + GATE_HIF(CLK_HIFSYS_USB0PHY, "usb0_phy_clk", "ethpll_500m_ck", 21), + GATE_HIF(CLK_HIFSYS_USB1PHY, "usb1_phy_clk", "ethpll_500m_ck", 22), + GATE_HIF(CLK_HIFSYS_PCIE0, "pcie0_clk", "ethpll_500m_ck", 24), + GATE_HIF(CLK_HIFSYS_PCIE1, "pcie1_clk", "ethpll_500m_ck", 25), + GATE_HIF(CLK_HIFSYS_PCIE2, "pcie2_clk", "ethpll_500m_ck", 26), +}; + +static const struct of_device_id of_match_clk_mt2701_hif[] = { + { .compatible = "mediatek,mt2701-hifsys", }, + {} +}; + +static int clk_mt2701_hif_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_HIFSYS_NR); + + mtk_clk_register_gates(node, hif_clks, ARRAY_SIZE(hif_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) { + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + return r; + } + + mtk_register_reset_controller(node, 1, 0x34); + + return 0; +} + +static struct platform_driver clk_mt2701_hif_drv = { + .probe = clk_mt2701_hif_probe, + .driver = { + .name = "clk-mt2701-hif", + .of_match_table = of_match_clk_mt2701_hif, + }, +}; + +builtin_platform_driver(clk_mt2701_hif_drv); diff --git a/drivers/clk/mediatek/clk-mt2701-img.c b/drivers/clk/mediatek/clk-mt2701-img.c new file mode 100644 index 000000000000..b7441c98bda8 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2701-img.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Shunli Wang <shunli.wang@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include <dt-bindings/clock/mt2701-clk.h> + +static const struct mtk_gate_regs img_cg_regs = { + .set_ofs = 0x0004, + .clr_ofs = 0x0008, + .sta_ofs = 0x0000, +}; + +#define GATE_IMG(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &img_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate img_clks[] = { + GATE_IMG(CLK_IMG_SMI_COMM, "img_smi_comm", "mm_sel", 0), + GATE_IMG(CLK_IMG_RESZ, "img_resz", "mm_sel", 1), + GATE_IMG(CLK_IMG_JPGDEC_SMI, "img_jpgdec_smi", "mm_sel", 5), + GATE_IMG(CLK_IMG_JPGDEC, "img_jpgdec", "mm_sel", 6), + GATE_IMG(CLK_IMG_VENC_LT, "img_venc_lt", "mm_sel", 8), + GATE_IMG(CLK_IMG_VENC, "img_venc", "mm_sel", 9), +}; + +static const struct of_device_id of_match_clk_mt2701_img[] = { + { .compatible = "mediatek,mt2701-imgsys", }, + {} +}; + +static int clk_mt2701_img_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_IMG_NR); + + mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + return r; +} + +static struct platform_driver clk_mt2701_img_drv = { + .probe = clk_mt2701_img_probe, + .driver = { + .name = "clk-mt2701-img", + .of_match_table = of_match_clk_mt2701_img, + }, +}; + +builtin_platform_driver(clk_mt2701_img_drv); diff --git a/drivers/clk/mediatek/clk-mt2701-mm.c b/drivers/clk/mediatek/clk-mt2701-mm.c new file mode 100644 index 000000000000..fe1f85072fc5 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2701-mm.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Shunli Wang <shunli.wang@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include <dt-bindings/clock/mt2701-clk.h> + +static const struct mtk_gate_regs disp0_cg_regs = { + .set_ofs = 0x0104, + .clr_ofs = 0x0108, + .sta_ofs = 0x0100, +}; + +static const struct mtk_gate_regs disp1_cg_regs = { + .set_ofs = 0x0114, + .clr_ofs = 0x0118, + .sta_ofs = 0x0110, +}; + +#define GATE_DISP0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &disp0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_DISP1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &disp1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate mm_clks[] = { + GATE_DISP0(CLK_MM_SMI_COMMON, "mm_smi_comm", "mm_sel", 0), + GATE_DISP0(CLK_MM_SMI_LARB0, "mm_smi_larb0", "mm_sel", 1), + GATE_DISP0(CLK_MM_CMDQ, "mm_cmdq", "mm_sel", 2), + GATE_DISP0(CLK_MM_MUTEX, "mm_mutex", "mm_sel", 3), + GATE_DISP0(CLK_MM_DISP_COLOR, "mm_disp_color", "mm_sel", 4), + GATE_DISP0(CLK_MM_DISP_BLS, "mm_disp_bls", "mm_sel", 5), + GATE_DISP0(CLK_MM_DISP_WDMA, "mm_disp_wdma", "mm_sel", 6), + GATE_DISP0(CLK_MM_DISP_RDMA, "mm_disp_rdma", "mm_sel", 7), + GATE_DISP0(CLK_MM_DISP_OVL, "mm_disp_ovl", "mm_sel", 8), + GATE_DISP0(CLK_MM_MDP_TDSHP, "mm_mdp_tdshp", "mm_sel", 9), + GATE_DISP0(CLK_MM_MDP_WROT, "mm_mdp_wrot", "mm_sel", 10), + GATE_DISP0(CLK_MM_MDP_WDMA, "mm_mdp_wdma", "mm_sel", 11), + GATE_DISP0(CLK_MM_MDP_RSZ1, "mm_mdp_rsz1", "mm_sel", 12), + GATE_DISP0(CLK_MM_MDP_RSZ0, "mm_mdp_rsz0", "mm_sel", 13), + GATE_DISP0(CLK_MM_MDP_RDMA, "mm_mdp_rdma", "mm_sel", 14), + GATE_DISP0(CLK_MM_MDP_BLS_26M, "mm_mdp_bls_26m", "pwm_sel", 15), + GATE_DISP0(CLK_MM_CAM_MDP, "mm_cam_mdp", "mm_sel", 16), + GATE_DISP0(CLK_MM_FAKE_ENG, "mm_fake_eng", "mm_sel", 17), + GATE_DISP0(CLK_MM_MUTEX_32K, "mm_mutex_32k", "rtc_sel", 18), + GATE_DISP0(CLK_MM_DISP_RDMA1, "mm_disp_rdma1", "mm_sel", 19), + GATE_DISP0(CLK_MM_DISP_UFOE, "mm_disp_ufoe", "mm_sel", 20), + GATE_DISP1(CLK_MM_DSI_ENGINE, "mm_dsi_eng", "mm_sel", 0), + GATE_DISP1(CLK_MM_DSI_DIG, "mm_dsi_dig", "dsi0_lntc_dsi", 1), + GATE_DISP1(CLK_MM_DPI_DIGL, "mm_dpi_digl", "dpi0_sel", 2), + GATE_DISP1(CLK_MM_DPI_ENGINE, "mm_dpi_eng", "mm_sel", 3), + GATE_DISP1(CLK_MM_DPI1_DIGL, "mm_dpi1_digl", "dpi1_sel", 4), + GATE_DISP1(CLK_MM_DPI1_ENGINE, "mm_dpi1_eng", "mm_sel", 5), + GATE_DISP1(CLK_MM_TVE_OUTPUT, "mm_tve_output", "tve_sel", 6), + GATE_DISP1(CLK_MM_TVE_INPUT, "mm_tve_input", "dpi0_sel", 7), + GATE_DISP1(CLK_MM_HDMI_PIXEL, "mm_hdmi_pixel", "dpi1_sel", 8), + GATE_DISP1(CLK_MM_HDMI_PLL, "mm_hdmi_pll", "hdmi_sel", 9), + GATE_DISP1(CLK_MM_HDMI_AUDIO, "mm_hdmi_audio", "apll_sel", 10), + GATE_DISP1(CLK_MM_HDMI_SPDIF, "mm_hdmi_spdif", "apll_sel", 11), + GATE_DISP1(CLK_MM_TVE_FMM, "mm_tve_fmm", "mm_sel", 14), +}; + +static const struct of_device_id of_match_clk_mt2701_mm[] = { + { .compatible = "mediatek,mt2701-mmsys", }, + {} +}; + +static int clk_mt2701_mm_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_MM_NR); + + mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + return r; +} + +static struct platform_driver clk_mt2701_mm_drv = { + .probe = clk_mt2701_mm_probe, + .driver = { + .name = "clk-mt2701-mm", + .of_match_table = of_match_clk_mt2701_mm, + }, +}; + +builtin_platform_driver(clk_mt2701_mm_drv); diff --git a/drivers/clk/mediatek/clk-mt2701-vdec.c b/drivers/clk/mediatek/clk-mt2701-vdec.c new file mode 100644 index 000000000000..d3c0fc9d6f02 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2701-vdec.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Shunli Wang <shunli.wang@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include <dt-bindings/clock/mt2701-clk.h> + +static const struct mtk_gate_regs vdec0_cg_regs = { + .set_ofs = 0x0000, + .clr_ofs = 0x0004, + .sta_ofs = 0x0000, +}; + +static const struct mtk_gate_regs vdec1_cg_regs = { + .set_ofs = 0x0008, + .clr_ofs = 0x000c, + .sta_ofs = 0x0008, +}; + +#define GATE_VDEC0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &vdec0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr_inv, \ + } + +#define GATE_VDEC1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &vdec1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr_inv, \ + } + +static const struct mtk_gate vdec_clks[] = { + GATE_VDEC0(CLK_VDEC_CKGEN, "vdec_cken", "vdec_sel", 0), + GATE_VDEC1(CLK_VDEC_LARB, "vdec_larb_cken", "mm_sel", 0), +}; + +static const struct of_device_id of_match_clk_mt2701_vdec[] = { + { .compatible = "mediatek,mt2701-vdecsys", }, + {} +}; + +static int clk_mt2701_vdec_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + int r; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_VDEC_NR); + + mtk_clk_register_gates(node, vdec_clks, ARRAY_SIZE(vdec_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + return r; +} + +static struct platform_driver clk_mt2701_vdec_drv = { + .probe = clk_mt2701_vdec_probe, + .driver = { + .name = "clk-mt2701-vdec", + .of_match_table = of_match_clk_mt2701_vdec, + }, +}; + +builtin_platform_driver(clk_mt2701_vdec_drv); diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c new file mode 100644 index 000000000000..6f26e6a37a6b --- /dev/null +++ b/drivers/clk/mediatek/clk-mt2701.c @@ -0,0 +1,1035 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Shunli Wang <shunli.wang@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include <dt-bindings/clock/mt2701-clk.h> + +/* + * For some clocks, we don't care what their actual rates are. And these + * clocks may change their rate on different products or different scenarios. + * So we model these clocks' rate as 0, to denote it's not an actual rate. + */ +#define DUMMY_RATE 0 + +static DEFINE_SPINLOCK(mt2701_clk_lock); + +static const struct mtk_fixed_clk top_fixed_clks[] = { + FIXED_CLK(CLK_TOP_DPI, "dpi_ck", "clk26m", + 108 * MHZ), + FIXED_CLK(CLK_TOP_DMPLL, "dmpll_ck", "clk26m", + 400 * MHZ), + FIXED_CLK(CLK_TOP_VENCPLL, "vencpll_ck", "clk26m", + 295750000), + FIXED_CLK(CLK_TOP_HDMI_0_PIX340M, "hdmi_0_pix340m", "clk26m", + 340 * MHZ), + FIXED_CLK(CLK_TOP_HDMI_0_DEEP340M, "hdmi_0_deep340m", "clk26m", + 340 * MHZ), + FIXED_CLK(CLK_TOP_HDMI_0_PLL340M, "hdmi_0_pll340m", "clk26m", + 340 * MHZ), + FIXED_CLK(CLK_TOP_HDMITX_CLKDIG_CTS, "hdmitx_dig_cts", "clk26m", + 300 * MHZ), + FIXED_CLK(CLK_TOP_HADDS2_FB, "hadds2_fbclk", "clk26m", + 27 * MHZ), + FIXED_CLK(CLK_TOP_WBG_DIG_416M, "wbg_dig_ck_416m", "clk26m", + 416 * MHZ), + FIXED_CLK(CLK_TOP_DSI0_LNTC_DSI, "dsi0_lntc_dsi", "clk26m", + 143 * MHZ), + FIXED_CLK(CLK_TOP_HDMI_SCL_RX, "hdmi_scl_rx", "clk26m", + 27 * MHZ), + FIXED_CLK(CLK_TOP_AUD_EXT1, "aud_ext1", "clk26m", + DUMMY_RATE), + FIXED_CLK(CLK_TOP_AUD_EXT2, "aud_ext2", "clk26m", + DUMMY_RATE), + FIXED_CLK(CLK_TOP_NFI1X_PAD, "nfi1x_pad", "clk26m", + DUMMY_RATE), +}; + +static const struct mtk_fixed_factor top_fixed_divs[] = { + FACTOR(CLK_TOP_SYSPLL, "syspll_ck", "mainpll", 1, 1), + FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "mainpll", 1, 2), + FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "mainpll", 1, 3), + FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll", 1, 5), + FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "mainpll", 1, 7), + FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "syspll_d2", 1, 2), + FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "syspll_d2", 1, 4), + FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "syspll_d2", 1, 8), + FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "syspll_d2", 1, 16), + FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "syspll_d3", 1, 2), + FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "syspll_d3", 1, 4), + FACTOR(CLK_TOP_SYSPLL2_D8, "syspll2_d8", "syspll_d3", 1, 8), + FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "syspll_d5", 1, 2), + FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "syspll_d5", 1, 4), + FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "syspll_d7", 1, 2), + FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "syspll_d7", 1, 4), + + FACTOR(CLK_TOP_UNIVPLL, "univpll_ck", "univpll", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2), + FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3), + FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5), + FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7), + FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univpll", 1, 26), + FACTOR(CLK_TOP_UNIVPLL_D52, "univpll_d52", "univpll", 1, 52), + FACTOR(CLK_TOP_UNIVPLL_D108, "univpll_d108", "univpll", 1, 108), + FACTOR(CLK_TOP_USB_PHY48M, "usb_phy48m_ck", "univpll", 1, 26), + FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll_d2", 1, 2), + FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll_d2", 1, 4), + FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll_d2", 1, 8), + FACTOR(CLK_TOP_8BDAC, "8bdac_ck", "univpll_d2", 1, 1), + FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll_d3", 1, 2), + FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll_d3", 1, 4), + FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll_d3", 1, 8), + FACTOR(CLK_TOP_UNIVPLL2_D16, "univpll2_d16", "univpll_d3", 1, 16), + FACTOR(CLK_TOP_UNIVPLL2_D32, "univpll2_d32", "univpll_d3", 1, 32), + FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univpll_d5", 1, 2), + FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univpll_d5", 1, 4), + FACTOR(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univpll_d5", 1, 8), + + FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, 1), + FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2), + FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1, 4), + FACTOR(CLK_TOP_MSDCPLL_D8, "msdcpll_d8", "msdcpll", 1, 8), + + FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, 1), + FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2), + + FACTOR(CLK_TOP_DMPLL_D2, "dmpll_d2", "dmpll_ck", 1, 2), + FACTOR(CLK_TOP_DMPLL_D4, "dmpll_d4", "dmpll_ck", 1, 4), + FACTOR(CLK_TOP_DMPLL_X2, "dmpll_x2", "dmpll_ck", 1, 1), + + FACTOR(CLK_TOP_TVDPLL, "tvdpll_ck", "tvdpll", 1, 1), + FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll", 1, 2), + FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll", 1, 4), + + FACTOR(CLK_TOP_VDECPLL, "vdecpll_ck", "vdecpll", 1, 1), + FACTOR(CLK_TOP_TVD2PLL, "tvd2pll_ck", "tvd2pll", 1, 1), + FACTOR(CLK_TOP_TVD2PLL_D2, "tvd2pll_d2", "tvd2pll", 1, 2), + + FACTOR(CLK_TOP_MIPIPLL, "mipipll", "dpi_ck", 1, 1), + FACTOR(CLK_TOP_MIPIPLL_D2, "mipipll_d2", "dpi_ck", 1, 2), + FACTOR(CLK_TOP_MIPIPLL_D4, "mipipll_d4", "dpi_ck", 1, 4), + + FACTOR(CLK_TOP_HDMIPLL, "hdmipll_ck", "hdmitx_dig_cts", 1, 1), + FACTOR(CLK_TOP_HDMIPLL_D2, "hdmipll_d2", "hdmitx_dig_cts", 1, 2), + FACTOR(CLK_TOP_HDMIPLL_D3, "hdmipll_d3", "hdmitx_dig_cts", 1, 3), + + FACTOR(CLK_TOP_ARMPLL_1P3G, "armpll_1p3g_ck", "armpll", 1, 1), + + FACTOR(CLK_TOP_AUDPLL, "audpll", "audpll_sel", 1, 1), + FACTOR(CLK_TOP_AUDPLL_D4, "audpll_d4", "audpll_sel", 1, 4), + FACTOR(CLK_TOP_AUDPLL_D8, "audpll_d8", "audpll_sel", 1, 8), + FACTOR(CLK_TOP_AUDPLL_D16, "audpll_d16", "audpll_sel", 1, 16), + FACTOR(CLK_TOP_AUDPLL_D24, "audpll_d24", "audpll_sel", 1, 24), + + FACTOR(CLK_TOP_AUD1PLL_98M, "aud1pll_98m_ck", "aud1pll", 1, 3), + FACTOR(CLK_TOP_AUD2PLL_90M, "aud2pll_90m_ck", "aud2pll", 1, 3), + FACTOR(CLK_TOP_HADDS2PLL_98M, "hadds2pll_98m", "hadds2pll", 1, 3), + FACTOR(CLK_TOP_HADDS2PLL_294M, "hadds2pll_294m", "hadds2pll", 1, 1), + FACTOR(CLK_TOP_ETHPLL_500M, "ethpll_500m_ck", "ethpll", 1, 1), + FACTOR(CLK_TOP_CLK26M_D8, "clk26m_d8", "clk26m", 1, 8), + FACTOR(CLK_TOP_32K_INTERNAL, "32k_internal", "clk26m", 1, 793), + FACTOR(CLK_TOP_32K_EXTERNAL, "32k_external", "rtc32k", 1, 1), +}; + +static const char * const axi_parents[] = { + "clk26m", + "syspll1_d2", + "syspll_d5", + "syspll1_d4", + "univpll_d5", + "univpll2_d2", + "mmpll_d2", + "dmpll_d2" +}; + +static const char * const mem_parents[] = { + "clk26m", + "dmpll_ck" +}; + +static const char * const ddrphycfg_parents[] = { + "clk26m", + "syspll1_d8" +}; + +static const char * const mm_parents[] = { + "clk26m", + "vencpll_ck", + "syspll1_d2", + "syspll1_d4", + "univpll_d5", + "univpll1_d2", + "univpll2_d2", + "dmpll_ck" +}; + +static const char * const pwm_parents[] = { + "clk26m", + "univpll2_d4", + "univpll3_d2", + "univpll1_d4", +}; + +static const char * const vdec_parents[] = { + "clk26m", + "vdecpll_ck", + "syspll_d5", + "syspll1_d4", + "univpll_d5", + "univpll2_d2", + "vencpll_ck", + "msdcpll_d2", + "mmpll_d2" +}; + +static const char * const mfg_parents[] = { + "clk26m", + "mmpll_ck", + "dmpll_x2_ck", + "msdcpll_ck", + "clk26m", + "syspll_d3", + "univpll_d3", + "univpll1_d2" +}; + +static const char * const camtg_parents[] = { + "clk26m", + "univpll_d26", + "univpll2_d2", + "syspll3_d2", + "syspll3_d4", + "msdcpll_d2", + "mmpll_d2" +}; + +static const char * const uart_parents[] = { + "clk26m", + "univpll2_d8" +}; + +static const char * const spi_parents[] = { + "clk26m", + "syspll3_d2", + "syspll4_d2", + "univpll2_d4", + "univpll1_d8" +}; + +static const char * const usb20_parents[] = { + "clk26m", + "univpll1_d8", + "univpll3_d4" +}; + +static const char * const msdc30_parents[] = { + "clk26m", + "msdcpll_d2", + "syspll2_d2", + "syspll1_d4", + "univpll1_d4", + "univpll2_d4" +}; + +static const char * const audio_parents[] = { + "clk26m", + "syspll1_d16" +}; + +static const char * const aud_intbus_parents[] = { + "clk26m", + "syspll1_d4", + "syspll3_d2", + "syspll4_d2", + "univpll3_d2", + "univpll2_d4" +}; + +static const char * const pmicspi_parents[] = { + "clk26m", + "syspll1_d8", + "syspll2_d4", + "syspll4_d2", + "syspll3_d4", + "syspll2_d8", + "syspll1_d16", + "univpll3_d4", + "univpll_d26", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const scp_parents[] = { + "clk26m", + "syspll1_d8", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const dpi0_parents[] = { + "clk26m", + "mipipll", + "mipipll_d2", + "mipipll_d4", + "clk26m", + "tvdpll_ck", + "tvdpll_d2", + "tvdpll_d4" +}; + +static const char * const dpi1_parents[] = { + "clk26m", + "tvdpll_ck", + "tvdpll_d2", + "tvdpll_d4" +}; + +static const char * const tve_parents[] = { + "clk26m", + "mipipll", + "mipipll_d2", + "mipipll_d4", + "clk26m", + "tvdpll_ck", + "tvdpll_d2", + "tvdpll_d4" +}; + +static const char * const hdmi_parents[] = { + "clk26m", + "hdmipll_ck", + "hdmipll_d2", + "hdmipll_d3" +}; + +static const char * const apll_parents[] = { + "clk26m", + "audpll", + "audpll_d4", + "audpll_d8", + "audpll_d16", + "audpll_d24", + "clk26m", + "clk26m" +}; + +static const char * const rtc_parents[] = { + "32k_internal", + "32k_external", + "clk26m", + "univpll3_d8" +}; + +static const char * const nfi2x_parents[] = { + "clk26m", + "syspll2_d2", + "syspll_d7", + "univpll3_d2", + "syspll2_d4", + "univpll3_d4", + "syspll4_d4", + "clk26m" +}; + +static const char * const emmc_hclk_parents[] = { + "clk26m", + "syspll1_d2", + "syspll1_d4", + "syspll2_d2" +}; + +static const char * const flash_parents[] = { + "clk26m_d8", + "clk26m", + "syspll2_d8", + "syspll3_d4", + "univpll3_d4", + "syspll4_d2", + "syspll2_d4", + "univpll2_d4" +}; + +static const char * const di_parents[] = { + "clk26m", + "tvd2pll_ck", + "tvd2pll_d2", + "clk26m" +}; + +static const char * const nr_osd_parents[] = { + "clk26m", + "vencpll_ck", + "syspll1_d2", + "syspll1_d4", + "univpll_d5", + "univpll1_d2", + "univpll2_d2", + "dmpll_ck" +}; + +static const char * const hdmirx_bist_parents[] = { + "clk26m", + "syspll_d3", + "clk26m", + "syspll1_d16", + "syspll4_d2", + "syspll1_d4", + "vencpll_ck", + "clk26m" +}; + +static const char * const intdir_parents[] = { + "clk26m", + "mmpll_ck", + "syspll_d2", + "univpll_d2" +}; + +static const char * const asm_parents[] = { + "clk26m", + "univpll2_d4", + "univpll2_d2", + "syspll_d5" +}; + +static const char * const ms_card_parents[] = { + "clk26m", + "univpll3_d8", + "syspll4_d4" +}; + +static const char * const ethif_parents[] = { + "clk26m", + "syspll1_d2", + "syspll_d5", + "syspll1_d4", + "univpll_d5", + "univpll1_d2", + "dmpll_ck", + "dmpll_d2" +}; + +static const char * const hdmirx_parents[] = { + "clk26m", + "univpll_d52" +}; + +static const char * const cmsys_parents[] = { + "clk26m", + "syspll1_d2", + "univpll1_d2", + "univpll_d5", + "syspll_d5", + "syspll2_d2", + "syspll1_d4", + "syspll3_d2", + "syspll2_d4", + "syspll1_d8", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "clk26m" +}; + +static const char * const clk_8bdac_parents[] = { + "32k_internal", + "8bdac_ck", + "clk26m", + "clk26m" +}; + +static const char * const aud2dvd_parents[] = { + "a1sys_hp_ck", + "a2sys_hp_ck" +}; + +static const char * const padmclk_parents[] = { + "clk26m", + "univpll_d26", + "univpll_d52", + "univpll_d108", + "univpll2_d8", + "univpll2_d16", + "univpll2_d32" +}; + +static const char * const aud_mux_parents[] = { + "clk26m", + "aud1pll_98m_ck", + "aud2pll_90m_ck", + "hadds2pll_98m", + "audio_ext1_ck", + "audio_ext2_ck" +}; + +static const char * const aud_src_parents[] = { + "aud_mux1_sel", + "aud_mux2_sel" +}; + +static const char * const cpu_parents[] = { + "clk26m", + "armpll", + "mainpll", + "mmpll" +}; + +static const struct mtk_composite top_muxes[] = { + MUX_GATE_FLAGS(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, + 0x0040, 0, 3, 7, CLK_IS_CRITICAL), + MUX_GATE_FLAGS(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, + 0x0040, 8, 1, 15, CLK_IS_CRITICAL), + MUX_GATE_FLAGS(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", + ddrphycfg_parents, 0x0040, 16, 1, 23, CLK_IS_CRITICAL), + MUX_GATE(CLK_TOP_MM_SEL, "mm_sel", mm_parents, + 0x0040, 24, 3, 31), + + MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, + 0x0050, 0, 2, 7), + MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, + 0x0050, 8, 4, 15), + MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, + 0x0050, 16, 3, 23), + MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, + 0x0050, 24, 3, 31), + MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, + 0x0060, 0, 1, 7), + + MUX_GATE(CLK_TOP_SPI0_SEL, "spi0_sel", spi_parents, + 0x0060, 8, 3, 15), + MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, + 0x0060, 16, 2, 23), + MUX_GATE(CLK_TOP_MSDC30_0_SEL, "msdc30_0_sel", msdc30_parents, + 0x0060, 24, 3, 31), + + MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_parents, + 0x0070, 0, 3, 7), + MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_parents, + 0x0070, 8, 3, 15), + MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", msdc30_parents, + 0x0070, 16, 1, 23), + MUX_GATE(CLK_TOP_AUDINTBUS_SEL, "aud_intbus_sel", aud_intbus_parents, + 0x0070, 24, 3, 31), + + MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, + 0x0080, 0, 4, 7), + MUX_GATE(CLK_TOP_SCP_SEL, "scp_sel", scp_parents, + 0x0080, 8, 2, 15), + MUX_GATE(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, + 0x0080, 16, 3, 23), + MUX_GATE(CLK_TOP_DPI1_SEL, "dpi1_sel", dpi1_parents, + 0x0080, 24, 2, 31), + + MUX_GATE(CLK_TOP_TVE_SEL, "tve_sel", tve_parents, + 0x0090, 0, 3, 7), + MUX_GATE(CLK_TOP_HDMI_SEL, "hdmi_sel", hdmi_parents, + 0x0090, 8, 2, 15), + MUX_GATE(CLK_TOP_APLL_SEL, "apll_sel", apll_parents, + 0x0090, 16, 3, 23), + + MUX_GATE_FLAGS(CLK_TOP_RTC_SEL, "rtc_sel", rtc_parents, + 0x00A0, 0, 2, 7, CLK_IS_CRITICAL), + MUX_GATE(CLK_TOP_NFI2X_SEL, "nfi2x_sel", nfi2x_parents, + 0x00A0, 8, 3, 15), + MUX_GATE(CLK_TOP_EMMC_HCLK_SEL, "emmc_hclk_sel", emmc_hclk_parents, + 0x00A0, 24, 2, 31), + + MUX_GATE(CLK_TOP_FLASH_SEL, "flash_sel", flash_parents, + 0x00B0, 0, 3, 7), + MUX_GATE(CLK_TOP_DI_SEL, "di_sel", di_parents, + 0x00B0, 8, 2, 15), + MUX_GATE(CLK_TOP_NR_SEL, "nr_sel", nr_osd_parents, + 0x00B0, 16, 3, 23), + MUX_GATE(CLK_TOP_OSD_SEL, "osd_sel", nr_osd_parents, + 0x00B0, 24, 3, 31), + + MUX_GATE(CLK_TOP_HDMIRX_BIST_SEL, "hdmirx_bist_sel", + hdmirx_bist_parents, 0x00C0, 0, 3, 7), + MUX_GATE(CLK_TOP_INTDIR_SEL, "intdir_sel", intdir_parents, + 0x00C0, 8, 2, 15), + MUX_GATE(CLK_TOP_ASM_I_SEL, "asm_i_sel", asm_parents, + 0x00C0, 16, 2, 23), + MUX_GATE(CLK_TOP_ASM_M_SEL, "asm_m_sel", asm_parents, + 0x00C0, 24, 3, 31), + + MUX_GATE(CLK_TOP_ASM_H_SEL, "asm_h_sel", asm_parents, + 0x00D0, 0, 2, 7), + MUX_GATE(CLK_TOP_MS_CARD_SEL, "ms_card_sel", ms_card_parents, + 0x00D0, 16, 2, 23), + MUX_GATE(CLK_TOP_ETHIF_SEL, "ethif_sel", ethif_parents, + 0x00D0, 24, 3, 31), + + MUX_GATE(CLK_TOP_HDMIRX26_24_SEL, "hdmirx26_24_sel", hdmirx_parents, + 0x00E0, 0, 1, 7), + MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_parents, + 0x00E0, 8, 3, 15), + MUX_GATE(CLK_TOP_CMSYS_SEL, "cmsys_sel", cmsys_parents, + 0x00E0, 16, 4, 23), + + MUX_GATE(CLK_TOP_SPI1_SEL, "spi2_sel", spi_parents, + 0x00E0, 24, 3, 31), + MUX_GATE(CLK_TOP_SPI2_SEL, "spi1_sel", spi_parents, + 0x00F0, 0, 3, 7), + MUX_GATE(CLK_TOP_8BDAC_SEL, "8bdac_sel", clk_8bdac_parents, + 0x00F0, 8, 2, 15), + MUX_GATE(CLK_TOP_AUD2DVD_SEL, "aud2dvd_sel", aud2dvd_parents, + 0x00F0, 16, 1, 23), + + MUX(CLK_TOP_PADMCLK_SEL, "padmclk_sel", padmclk_parents, + 0x0100, 0, 3), + + MUX(CLK_TOP_AUD_MUX1_SEL, "aud_mux1_sel", aud_mux_parents, + 0x012c, 0, 3), + MUX(CLK_TOP_AUD_MUX2_SEL, "aud_mux2_sel", aud_mux_parents, + 0x012c, 3, 3), + MUX(CLK_TOP_AUDPLL_MUX_SEL, "audpll_sel", aud_mux_parents, + 0x012c, 6, 3), + MUX_GATE(CLK_TOP_AUD_K1_SRC_SEL, "aud_k1_src_sel", aud_src_parents, + 0x012c, 15, 1, 23), + MUX_GATE(CLK_TOP_AUD_K2_SRC_SEL, "aud_k2_src_sel", aud_src_parents, + 0x012c, 16, 1, 24), + MUX_GATE(CLK_TOP_AUD_K3_SRC_SEL, "aud_k3_src_sel", aud_src_parents, + 0x012c, 17, 1, 25), + MUX_GATE(CLK_TOP_AUD_K4_SRC_SEL, "aud_k4_src_sel", aud_src_parents, + 0x012c, 18, 1, 26), + MUX_GATE(CLK_TOP_AUD_K5_SRC_SEL, "aud_k5_src_sel", aud_src_parents, + 0x012c, 19, 1, 27), + MUX_GATE(CLK_TOP_AUD_K6_SRC_SEL, "aud_k6_src_sel", aud_src_parents, + 0x012c, 20, 1, 28), +}; + +static const struct mtk_clk_divider top_adj_divs[] = { + DIV_ADJ(CLK_TOP_AUD_EXTCK1_DIV, "audio_ext1_ck", "aud_ext1", + 0x0120, 0, 8), + DIV_ADJ(CLK_TOP_AUD_EXTCK2_DIV, "audio_ext2_ck", "aud_ext2", + 0x0120, 8, 8), + DIV_ADJ(CLK_TOP_AUD_MUX1_DIV, "aud_mux1_div", "aud_mux1_sel", + 0x0120, 16, 8), + DIV_ADJ(CLK_TOP_AUD_MUX2_DIV, "aud_mux2_div", "aud_mux2_sel", + 0x0120, 24, 8), + DIV_ADJ(CLK_TOP_AUD_K1_SRC_DIV, "aud_k1_src_div", "aud_k1_src_sel", + 0x0124, 0, 8), + DIV_ADJ(CLK_TOP_AUD_K2_SRC_DIV, "aud_k2_src_div", "aud_k2_src_sel", + 0x0124, 8, 8), + DIV_ADJ(CLK_TOP_AUD_K3_SRC_DIV, "aud_k3_src_div", "aud_k3_src_sel", + 0x0124, 16, 8), + DIV_ADJ(CLK_TOP_AUD_K4_SRC_DIV, "aud_k4_src_div", "aud_k4_src_sel", + 0x0124, 24, 8), + DIV_ADJ(CLK_TOP_AUD_K5_SRC_DIV, "aud_k5_src_div", "aud_k5_src_sel", + 0x0128, 0, 8), + DIV_ADJ(CLK_TOP_AUD_K6_SRC_DIV, "aud_k6_src_div", "aud_k6_src_sel", + 0x0128, 8, 8), +}; + +static const struct mtk_gate_regs top_aud_cg_regs = { + .sta_ofs = 0x012C, +}; + +#define GATE_TOP_AUD(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &top_aud_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr, \ + } + +static const struct mtk_gate top_clks[] = { + GATE_TOP_AUD(CLK_TOP_AUD_48K_TIMING, "a1sys_hp_ck", "aud_mux1_div", + 21), + GATE_TOP_AUD(CLK_TOP_AUD_44K_TIMING, "a2sys_hp_ck", "aud_mux2_div", + 22), + GATE_TOP_AUD(CLK_TOP_AUD_I2S1_MCLK, "aud_i2s1_mclk", "aud_k1_src_div", + 23), + GATE_TOP_AUD(CLK_TOP_AUD_I2S2_MCLK, "aud_i2s2_mclk", "aud_k2_src_div", + 24), + GATE_TOP_AUD(CLK_TOP_AUD_I2S3_MCLK, "aud_i2s3_mclk", "aud_k3_src_div", + 25), + GATE_TOP_AUD(CLK_TOP_AUD_I2S4_MCLK, "aud_i2s4_mclk", "aud_k4_src_div", + 26), + GATE_TOP_AUD(CLK_TOP_AUD_I2S5_MCLK, "aud_i2s5_mclk", "aud_k5_src_div", + 27), + GATE_TOP_AUD(CLK_TOP_AUD_I2S6_MCLK, "aud_i2s6_mclk", "aud_k6_src_div", + 28), +}; + +static int mtk_topckgen_init(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + struct device_node *node = pdev->dev.of_node; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + clk_data = mtk_alloc_clk_data(CLK_TOP_NR); + + mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks), + clk_data); + + mtk_clk_register_factors(top_fixed_divs, ARRAY_SIZE(top_fixed_divs), + clk_data); + + mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), + base, &mt2701_clk_lock, clk_data); + + mtk_clk_register_dividers(top_adj_divs, ARRAY_SIZE(top_adj_divs), + base, &mt2701_clk_lock, clk_data); + + mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks), + clk_data); + + return of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} + +static const struct mtk_gate_regs infra_cg_regs = { + .set_ofs = 0x0040, + .clr_ofs = 0x0044, + .sta_ofs = 0x0048, +}; + +#define GATE_ICG(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] = { + GATE_ICG(CLK_INFRA_DBG, "dbgclk", "axi_sel", 0), + GATE_ICG(CLK_INFRA_SMI, "smi_ck", "mm_sel", 1), + GATE_ICG(CLK_INFRA_QAXI_CM4, "cm4_ck", "axi_sel", 2), + GATE_ICG(CLK_INFRA_AUD_SPLIN_B, "audio_splin_bck", "hadds2pll_294m", 4), + GATE_ICG(CLK_INFRA_AUDIO, "audio_ck", "clk26m", 5), + GATE_ICG(CLK_INFRA_EFUSE, "efuse_ck", "clk26m", 6), + GATE_ICG(CLK_INFRA_L2C_SRAM, "l2c_sram_ck", "mm_sel", 7), + GATE_ICG(CLK_INFRA_M4U, "m4u_ck", "mem_sel", 8), + GATE_ICG(CLK_INFRA_CONNMCU, "connsys_bus", "wbg_dig_ck_416m", 12), + GATE_ICG(CLK_INFRA_TRNG, "trng_ck", "axi_sel", 13), + GATE_ICG(CLK_INFRA_RAMBUFIF, "rambufif_ck", "mem_sel", 14), + GATE_ICG(CLK_INFRA_CPUM, "cpum_ck", "mem_sel", 15), + GATE_ICG(CLK_INFRA_KP, "kp_ck", "axi_sel", 16), + GATE_ICG(CLK_INFRA_CEC, "cec_ck", "rtc_sel", 18), + GATE_ICG(CLK_INFRA_IRRX, "irrx_ck", "axi_sel", 19), + GATE_ICG(CLK_INFRA_PMICSPI, "pmicspi_ck", "pmicspi_sel", 22), + GATE_ICG(CLK_INFRA_PMICWRAP, "pmicwrap_ck", "axi_sel", 23), + GATE_ICG(CLK_INFRA_DDCCI, "ddcci_ck", "axi_sel", 24), +}; + +static const struct mtk_fixed_factor infra_fixed_divs[] = { + FACTOR(CLK_INFRA_CLK_13M, "clk13m", "clk26m", 1, 2), +}; + +static struct clk_onecell_data *infra_clk_data; + +static void mtk_infrasys_init_early(struct device_node *node) +{ + int r, i; + + if (!infra_clk_data) { + infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR); + + for (i = 0; i < CLK_INFRA_NR; i++) + infra_clk_data->clks[i] = ERR_PTR(-EPROBE_DEFER); + } + + mtk_clk_register_factors(infra_fixed_divs, ARRAY_SIZE(infra_fixed_divs), + infra_clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, infra_clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE_DRIVER(mtk_infra, "mediatek,mt2701-infracfg", + mtk_infrasys_init_early); + +static int mtk_infrasys_init(struct platform_device *pdev) +{ + int r, i; + struct device_node *node = pdev->dev.of_node; + + if (!infra_clk_data) { + infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR); + } else { + for (i = 0; i < CLK_INFRA_NR; i++) { + if (infra_clk_data->clks[i] == ERR_PTR(-EPROBE_DEFER)) + infra_clk_data->clks[i] = ERR_PTR(-ENOENT); + } + } + + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), + infra_clk_data); + mtk_clk_register_factors(infra_fixed_divs, ARRAY_SIZE(infra_fixed_divs), + infra_clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, infra_clk_data); + if (r) + return r; + + mtk_register_reset_controller(node, 2, 0x30); + + return 0; +} + +static const struct mtk_gate_regs peri0_cg_regs = { + .set_ofs = 0x0008, + .clr_ofs = 0x0010, + .sta_ofs = 0x0018, +}; + +static const struct mtk_gate_regs peri1_cg_regs = { + .set_ofs = 0x000c, + .clr_ofs = 0x0014, + .sta_ofs = 0x001c, +}; + +#define GATE_PERI0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_PERI1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate peri_clks[] = { + GATE_PERI0(CLK_PERI_USB0_MCU, "usb0_mcu_ck", "axi_sel", 31), + GATE_PERI0(CLK_PERI_ETH, "eth_ck", "clk26m", 30), + GATE_PERI0(CLK_PERI_SPI0, "spi0_ck", "spi0_sel", 29), + GATE_PERI0(CLK_PERI_AUXADC, "auxadc_ck", "clk26m", 28), + GATE_PERI0(CLK_PERI_I2C3, "i2c3_ck", "clk26m", 27), + GATE_PERI0(CLK_PERI_I2C2, "i2c2_ck", "axi_sel", 26), + GATE_PERI0(CLK_PERI_I2C1, "i2c1_ck", "axi_sel", 25), + GATE_PERI0(CLK_PERI_I2C0, "i2c0_ck", "axi_sel", 24), + GATE_PERI0(CLK_PERI_BTIF, "bitif_ck", "axi_sel", 23), + GATE_PERI0(CLK_PERI_UART3, "uart3_ck", "axi_sel", 22), + GATE_PERI0(CLK_PERI_UART2, "uart2_ck", "axi_sel", 21), + GATE_PERI0(CLK_PERI_UART1, "uart1_ck", "axi_sel", 20), + GATE_PERI0(CLK_PERI_UART0, "uart0_ck", "axi_sel", 19), + GATE_PERI0(CLK_PERI_NLI, "nli_ck", "axi_sel", 18), + GATE_PERI0(CLK_PERI_MSDC50_3, "msdc50_3_ck", "emmc_hclk_sel", 17), + GATE_PERI0(CLK_PERI_MSDC30_3, "msdc30_3_ck", "msdc30_3_sel", 16), + GATE_PERI0(CLK_PERI_MSDC30_2, "msdc30_2_ck", "msdc30_2_sel", 15), + GATE_PERI0(CLK_PERI_MSDC30_1, "msdc30_1_ck", "msdc30_1_sel", 14), + GATE_PERI0(CLK_PERI_MSDC30_0, "msdc30_0_ck", "msdc30_0_sel", 13), + GATE_PERI0(CLK_PERI_AP_DMA, "ap_dma_ck", "axi_sel", 12), + GATE_PERI0(CLK_PERI_USB1, "usb1_ck", "usb20_sel", 11), + GATE_PERI0(CLK_PERI_USB0, "usb0_ck", "usb20_sel", 10), + GATE_PERI0(CLK_PERI_PWM, "pwm_ck", "axi_sel", 9), + GATE_PERI0(CLK_PERI_PWM7, "pwm7_ck", "axi_sel", 8), + GATE_PERI0(CLK_PERI_PWM6, "pwm6_ck", "axi_sel", 7), + GATE_PERI0(CLK_PERI_PWM5, "pwm5_ck", "axi_sel", 6), + GATE_PERI0(CLK_PERI_PWM4, "pwm4_ck", "axi_sel", 5), + GATE_PERI0(CLK_PERI_PWM3, "pwm3_ck", "axi_sel", 4), + GATE_PERI0(CLK_PERI_PWM2, "pwm2_ck", "axi_sel", 3), + GATE_PERI0(CLK_PERI_PWM1, "pwm1_ck", "axi_sel", 2), + GATE_PERI0(CLK_PERI_THERM, "therm_ck", "axi_sel", 1), + GATE_PERI0(CLK_PERI_NFI, "nfi_ck", "nfi2x_sel", 0), + + GATE_PERI1(CLK_PERI_FCI, "fci_ck", "ms_card_sel", 11), + GATE_PERI1(CLK_PERI_SPI2, "spi2_ck", "spi2_sel", 10), + GATE_PERI1(CLK_PERI_SPI1, "spi1_ck", "spi1_sel", 9), + GATE_PERI1(CLK_PERI_HOST89_DVD, "host89_dvd_ck", "aud2dvd_sel", 8), + GATE_PERI1(CLK_PERI_HOST89_SPI, "host89_spi_ck", "spi0_sel", 7), + GATE_PERI1(CLK_PERI_HOST89_INT, "host89_int_ck", "axi_sel", 6), + GATE_PERI1(CLK_PERI_FLASH, "flash_ck", "nfi2x_sel", 5), + GATE_PERI1(CLK_PERI_NFI_PAD, "nfi_pad_ck", "nfi1x_pad", 4), + GATE_PERI1(CLK_PERI_NFI_ECC, "nfi_ecc_ck", "nfi1x_pad", 3), + GATE_PERI1(CLK_PERI_GCPU, "gcpu_ck", "axi_sel", 2), + GATE_PERI1(CLK_PERI_USB_SLV, "usbslv_ck", "axi_sel", 1), + GATE_PERI1(CLK_PERI_USB1_MCU, "usb1_mcu_ck", "axi_sel", 0), +}; + +static const char * const uart_ck_sel_parents[] = { + "clk26m", + "uart_sel", +}; + +static const struct mtk_composite peri_muxs[] = { + MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, + 0x40c, 0, 1), + MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, + 0x40c, 1, 1), + MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, + 0x40c, 2, 1), + MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, + 0x40c, 3, 1), +}; + +static int mtk_pericfg_init(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + struct device_node *node = pdev->dev.of_node; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + clk_data = mtk_alloc_clk_data(CLK_PERI_NR); + + mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks), + clk_data); + + mtk_clk_register_composites(peri_muxs, ARRAY_SIZE(peri_muxs), base, + &mt2701_clk_lock, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + return r; + + mtk_register_reset_controller(node, 2, 0x0); + + return 0; +} + +#define MT8590_PLL_FMAX (2000 * MHZ) +#define CON0_MT8590_RST_BAR BIT(27) + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, \ + _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .rst_bar_mask = CON0_MT8590_RST_BAR, \ + .fmax = MT8590_PLL_FMAX, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + } + +static const struct mtk_pll_data apmixed_plls[] = { + PLL(CLK_APMIXED_ARMPLL, "armpll", 0x200, 0x20c, 0x80000001, + PLL_AO, 21, 0x204, 24, 0x0, 0x204, 0), + PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x210, 0x21c, 0xf0000001, + HAVE_RST_BAR, 21, 0x210, 4, 0x0, 0x214, 0), + PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x220, 0x22c, 0xf3000001, + HAVE_RST_BAR, 7, 0x220, 4, 0x0, 0x224, 14), + PLL(CLK_APMIXED_MMPLL, "mmpll", 0x230, 0x23c, 0x00000001, 0, + 21, 0x230, 4, 0x0, 0x234, 0), + PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x240, 0x24c, 0x00000001, 0, + 21, 0x240, 4, 0x0, 0x244, 0), + PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x250, 0x25c, 0x00000001, 0, + 21, 0x250, 4, 0x0, 0x254, 0), + PLL(CLK_APMIXED_AUD1PLL, "aud1pll", 0x270, 0x27c, 0x00000001, 0, + 31, 0x270, 4, 0x0, 0x274, 0), + PLL(CLK_APMIXED_TRGPLL, "trgpll", 0x280, 0x28c, 0x00000001, 0, + 31, 0x280, 4, 0x0, 0x284, 0), + PLL(CLK_APMIXED_ETHPLL, "ethpll", 0x290, 0x29c, 0x00000001, 0, + 31, 0x290, 4, 0x0, 0x294, 0), + PLL(CLK_APMIXED_VDECPLL, "vdecpll", 0x2a0, 0x2ac, 0x00000001, 0, + 31, 0x2a0, 4, 0x0, 0x2a4, 0), + PLL(CLK_APMIXED_HADDS2PLL, "hadds2pll", 0x2b0, 0x2bc, 0x00000001, 0, + 31, 0x2b0, 4, 0x0, 0x2b4, 0), + PLL(CLK_APMIXED_AUD2PLL, "aud2pll", 0x2c0, 0x2cc, 0x00000001, 0, + 31, 0x2c0, 4, 0x0, 0x2c4, 0), + PLL(CLK_APMIXED_TVD2PLL, "tvd2pll", 0x2d0, 0x2dc, 0x00000001, 0, + 21, 0x2d0, 4, 0x0, 0x2d4, 0), +}; + +static int mtk_apmixedsys_init(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + struct device_node *node = pdev->dev.of_node; + + clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR); + if (!clk_data) + return -ENOMEM; + + mtk_clk_register_plls(node, apmixed_plls, ARRAY_SIZE(apmixed_plls), + clk_data); + + return of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} + +static const struct of_device_id of_match_clk_mt2701[] = { + { + .compatible = "mediatek,mt2701-topckgen", + .data = mtk_topckgen_init, + }, { + .compatible = "mediatek,mt2701-infracfg", + .data = mtk_infrasys_init, + }, { + .compatible = "mediatek,mt2701-pericfg", + .data = mtk_pericfg_init, + }, { + .compatible = "mediatek,mt2701-apmixedsys", + .data = mtk_apmixedsys_init, + }, { + /* sentinel */ + } +}; + +static int clk_mt2701_probe(struct platform_device *pdev) +{ + int (*clk_init)(struct platform_device *); + int r; + + clk_init = of_device_get_match_data(&pdev->dev); + if (!clk_init) + return -EINVAL; + + r = clk_init(pdev); + if (r) + dev_err(&pdev->dev, + "could not register clock provider: %s: %d\n", + pdev->name, r); + + return r; +} + +static struct platform_driver clk_mt2701_drv = { + .probe = clk_mt2701_probe, + .driver = { + .name = "clk-mt2701", + .of_match_table = of_match_clk_mt2701, + }, +}; + +static int __init clk_mt2701_init(void) +{ + return platform_driver_register(&clk_mt2701_drv); +} + +arch_initcall(clk_mt2701_init); diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c index bb30f7063569..0541df78141c 100644 --- a/drivers/clk/mediatek/clk-mtk.c +++ b/drivers/clk/mediatek/clk-mtk.c @@ -58,6 +58,9 @@ void mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks, for (i = 0; i < num; i++) { const struct mtk_fixed_clk *rc = &clks[i]; + if (clk_data && !IS_ERR_OR_NULL(clk_data->clks[rc->id])) + continue; + clk = clk_register_fixed_rate(NULL, rc->name, rc->parent, 0, rc->rate); @@ -81,6 +84,9 @@ void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, for (i = 0; i < num; i++) { const struct mtk_fixed_factor *ff = &clks[i]; + if (clk_data && !IS_ERR_OR_NULL(clk_data->clks[ff->id])) + continue; + clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name, CLK_SET_RATE_PARENT, ff->mult, ff->div); @@ -116,6 +122,9 @@ int mtk_clk_register_gates(struct device_node *node, for (i = 0; i < num; i++) { const struct mtk_gate *gate = &clks[i]; + if (!IS_ERR_OR_NULL(clk_data->clks[gate->id])) + continue; + clk = mtk_clk_register_gate(gate->name, gate->parent_name, regmap, gate->regs->set_ofs, @@ -232,6 +241,9 @@ void mtk_clk_register_composites(const struct mtk_composite *mcs, for (i = 0; i < num; i++) { const struct mtk_composite *mc = &mcs[i]; + if (clk_data && !IS_ERR_OR_NULL(clk_data->clks[mc->id])) + continue; + clk = mtk_clk_register_composite(mc, base, lock); if (IS_ERR(clk)) { @@ -244,3 +256,31 @@ void mtk_clk_register_composites(const struct mtk_composite *mcs, clk_data->clks[mc->id] = clk; } } + +void mtk_clk_register_dividers(const struct mtk_clk_divider *mcds, + int num, void __iomem *base, spinlock_t *lock, + struct clk_onecell_data *clk_data) +{ + struct clk *clk; + int i; + + for (i = 0; i < num; i++) { + const struct mtk_clk_divider *mcd = &mcds[i]; + + if (clk_data && !IS_ERR_OR_NULL(clk_data->clks[mcd->id])) + continue; + + clk = clk_register_divider(NULL, mcd->name, mcd->parent_name, + mcd->flags, base + mcd->div_reg, mcd->div_shift, + mcd->div_width, mcd->clk_divider_flags, lock); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + mcd->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[mcd->id] = clk; + } +} diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h index 9f24fcfa304f..f5d6b70ce189 100644 --- a/drivers/clk/mediatek/clk-mtk.h +++ b/drivers/clk/mediatek/clk-mtk.h @@ -87,7 +87,8 @@ struct mtk_composite { * In case the rate change propagation to parent clocks is undesirable, * this macro allows to specify the clock flags manually. */ -#define MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, _gate, _flags) { \ +#define MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, \ + _gate, _flags) { \ .id = _id, \ .name = _name, \ .mux_reg = _reg, \ @@ -106,7 +107,8 @@ struct mtk_composite { * parent clock by default. */ #define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) \ - MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, _gate, CLK_SET_RATE_PARENT) + MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, \ + _gate, CLK_SET_RATE_PARENT) #define MUX(_id, _name, _parents, _reg, _shift, _width) { \ .id = _id, \ @@ -121,7 +123,8 @@ struct mtk_composite { .flags = CLK_SET_RATE_PARENT, \ } -#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) { \ +#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, \ + _div_width, _div_shift) { \ .id = _id, \ .parent = _parent, \ .name = _name, \ @@ -156,12 +159,40 @@ struct mtk_gate { const struct clk_ops *ops; }; -int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, - int num, struct clk_onecell_data *clk_data); +int mtk_clk_register_gates(struct device_node *node, + const struct mtk_gate *clks, int num, + struct clk_onecell_data *clk_data); + +struct mtk_clk_divider { + int id; + const char *name; + const char *parent_name; + unsigned long flags; + + u32 div_reg; + unsigned char div_shift; + unsigned char div_width; + unsigned char clk_divider_flags; + const struct clk_div_table *clk_div_table; +}; + +#define DIV_ADJ(_id, _name, _parent, _reg, _shift, _width) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .div_reg = _reg, \ + .div_shift = _shift, \ + .div_width = _width, \ +} + +void mtk_clk_register_dividers(const struct mtk_clk_divider *mcds, + int num, void __iomem *base, spinlock_t *lock, + struct clk_onecell_data *clk_data); struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num); #define HAVE_RST_BAR BIT(0) +#define PLL_AO BIT(1) struct mtk_pll_div_table { u32 div; diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c index 0c2deac17ce9..a409142e9346 100644 --- a/drivers/clk/mediatek/clk-pll.c +++ b/drivers/clk/mediatek/clk-pll.c @@ -301,6 +301,7 @@ static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data, pll->data = data; init.name = data->name; + init.flags = (data->flags & PLL_AO) ? CLK_IS_CRITICAL : 0; init.ops = &mtk_pll_ops; init.parent_names = &parent_name; init.num_parents = 1; diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index 9adaf48aea23..0fc75c395957 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -309,19 +309,19 @@ static void __init mmp2_clk_init(struct device_node *np) pxa_unit->mpmu_base = of_iomap(np, 0); if (!pxa_unit->mpmu_base) { pr_err("failed to map mpmu registers\n"); - return; + goto free_memory; } pxa_unit->apmu_base = of_iomap(np, 1); if (!pxa_unit->apmu_base) { pr_err("failed to map apmu registers\n"); - return; + goto unmap_mpmu_region; } pxa_unit->apbc_base = of_iomap(np, 2); if (!pxa_unit->apbc_base) { pr_err("failed to map apbc registers\n"); - return; + goto unmap_apmu_region; } mmp_clk_init(np, &pxa_unit->unit, MMP2_NR_CLKS); @@ -333,6 +333,15 @@ static void __init mmp2_clk_init(struct device_node *np) mmp2_axi_periph_clk_init(pxa_unit); mmp2_clk_reset_init(np, pxa_unit); + + return; + +unmap_apmu_region: + iounmap(pxa_unit->apmu_base); +unmap_mpmu_region: + iounmap(pxa_unit->mpmu_base); +free_memory: + kfree(pxa_unit); } CLK_OF_DECLARE(mmp2_clk, "marvell,mmp2-clock", mmp2_clk_init); diff --git a/drivers/clk/mmp/clk-of-pxa1928.c b/drivers/clk/mmp/clk-of-pxa1928.c index e478ff44e170..cede7b4ca3b9 100644 --- a/drivers/clk/mmp/clk-of-pxa1928.c +++ b/drivers/clk/mmp/clk-of-pxa1928.c @@ -216,6 +216,7 @@ static void __init pxa1928_mpmu_clk_init(struct device_node *np) pxa_unit->mpmu_base = of_iomap(np, 0); if (!pxa_unit->mpmu_base) { pr_err("failed to map mpmu registers\n"); + kfree(pxa_unit); return; } @@ -234,6 +235,7 @@ static void __init pxa1928_apmu_clk_init(struct device_node *np) pxa_unit->apmu_base = of_iomap(np, 0); if (!pxa_unit->apmu_base) { pr_err("failed to map apmu registers\n"); + kfree(pxa_unit); return; } @@ -254,6 +256,7 @@ static void __init pxa1928_apbc_clk_init(struct device_node *np) pxa_unit->apbc_base = of_iomap(np, 0); if (!pxa_unit->apbc_base) { pr_err("failed to map apbc registers\n"); + kfree(pxa_unit); return; } diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c index 64d1ef49caeb..1dcabe95cb67 100644 --- a/drivers/clk/mmp/clk-of-pxa910.c +++ b/drivers/clk/mmp/clk-of-pxa910.c @@ -278,25 +278,25 @@ static void __init pxa910_clk_init(struct device_node *np) pxa_unit->mpmu_base = of_iomap(np, 0); if (!pxa_unit->mpmu_base) { pr_err("failed to map mpmu registers\n"); - return; + goto free_memory; } pxa_unit->apmu_base = of_iomap(np, 1); if (!pxa_unit->apmu_base) { pr_err("failed to map apmu registers\n"); - return; + goto unmap_mpmu_region; } pxa_unit->apbc_base = of_iomap(np, 2); if (!pxa_unit->apbc_base) { pr_err("failed to map apbc registers\n"); - return; + goto unmap_apmu_region; } pxa_unit->apbcp_base = of_iomap(np, 3); if (!pxa_unit->apbcp_base) { pr_err("failed to map apbcp registers\n"); - return; + goto unmap_apbc_region; } mmp_clk_init(np, &pxa_unit->unit, PXA910_NR_CLKS); @@ -308,6 +308,17 @@ static void __init pxa910_clk_init(struct device_node *np) pxa910_axi_periph_clk_init(pxa_unit); pxa910_clk_reset_init(np, pxa_unit); + + return; + +unmap_apbc_region: + iounmap(pxa_unit->apbc_base); +unmap_apmu_region: + iounmap(pxa_unit->apmu_base); +unmap_mpmu_region: + iounmap(pxa_unit->mpmu_base); +free_memory: + kfree(pxa_unit); } CLK_OF_DECLARE(pxa910_clk, "marvell,pxa910-clock", pxa910_clk_init); diff --git a/drivers/clk/mvebu/ap806-system-controller.c b/drivers/clk/mvebu/ap806-system-controller.c index 02023baf86c9..8181b919f062 100644 --- a/drivers/clk/mvebu/ap806-system-controller.c +++ b/drivers/clk/mvebu/ap806-system-controller.c @@ -14,7 +14,7 @@ #include <linux/clk-provider.h> #include <linux/mfd/syscon.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/platform_device.h> @@ -135,34 +135,17 @@ fail0: return ret; } -static int ap806_syscon_clk_remove(struct platform_device *pdev) -{ - of_clk_del_provider(pdev->dev.of_node); - clk_unregister_fixed_factor(ap806_clks[3]); - clk_unregister_fixed_rate(ap806_clks[2]); - clk_unregister_fixed_rate(ap806_clks[1]); - clk_unregister_fixed_rate(ap806_clks[0]); - - return 0; -} - static const struct of_device_id ap806_syscon_of_match[] = { { .compatible = "marvell,ap806-system-controller", }, { } }; -MODULE_DEVICE_TABLE(of, armada8k_pcie_of_match); static struct platform_driver ap806_syscon_driver = { .probe = ap806_syscon_clk_probe, - .remove = ap806_syscon_clk_remove, .driver = { .name = "marvell-ap806-system-controller", .of_match_table = ap806_syscon_of_match, + .suppress_bind_attrs = true, }, }; - -module_platform_driver(ap806_syscon_driver); - -MODULE_DESCRIPTION("Marvell AP806 System Controller driver"); -MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>"); -MODULE_LICENSE("GPL"); +builtin_platform_driver(ap806_syscon_driver); diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c index f2303da7fda7..32e5b43c086f 100644 --- a/drivers/clk/mvebu/cp110-system-controller.c +++ b/drivers/clk/mvebu/cp110-system-controller.c @@ -30,7 +30,7 @@ #include <linux/clk-provider.h> #include <linux/mfd/syscon.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/platform_device.h> @@ -87,7 +87,7 @@ struct cp110_gate_clk { u8 bit_idx; }; -#define to_cp110_gate_clk(clk) container_of(clk, struct cp110_gate_clk, hw) +#define to_cp110_gate_clk(hw) container_of(hw, struct cp110_gate_clk, hw) static int cp110_gate_enable(struct clk_hw *hw) { @@ -123,13 +123,14 @@ static const struct clk_ops cp110_gate_ops = { .is_enabled = cp110_gate_is_enabled, }; -static struct clk *cp110_register_gate(const char *name, - const char *parent_name, - struct regmap *regmap, u8 bit_idx) +static struct clk_hw *cp110_register_gate(const char *name, + const char *parent_name, + struct regmap *regmap, u8 bit_idx) { struct cp110_gate_clk *gate; - struct clk *clk; + struct clk_hw *hw; struct clk_init_data init; + int ret; gate = kzalloc(sizeof(*gate), GFP_KERNEL); if (!gate) @@ -146,39 +147,37 @@ static struct clk *cp110_register_gate(const char *name, gate->bit_idx = bit_idx; gate->hw.init = &init; - clk = clk_register(NULL, &gate->hw); - if (IS_ERR(clk)) + hw = &gate->hw; + ret = clk_hw_register(NULL, hw); + if (ret) { kfree(gate); + hw = ERR_PTR(ret); + } - return clk; + return hw; } -static void cp110_unregister_gate(struct clk *clk) +static void cp110_unregister_gate(struct clk_hw *hw) { - struct clk_hw *hw; - - hw = __clk_get_hw(clk); - if (!hw) - return; - - clk_unregister(clk); + clk_hw_unregister(hw); kfree(to_cp110_gate_clk(hw)); } -static struct clk *cp110_of_clk_get(struct of_phandle_args *clkspec, void *data) +static struct clk_hw *cp110_of_clk_get(struct of_phandle_args *clkspec, + void *data) { - struct clk_onecell_data *clk_data = data; + struct clk_hw_onecell_data *clk_data = data; unsigned int type = clkspec->args[0]; unsigned int idx = clkspec->args[1]; if (type == CP110_CLK_TYPE_CORE) { if (idx > CP110_MAX_CORE_CLOCKS) return ERR_PTR(-EINVAL); - return clk_data->clks[idx]; + return clk_data->hws[idx]; } else if (type == CP110_CLK_TYPE_GATABLE) { if (idx > CP110_MAX_GATABLE_CLOCKS) return ERR_PTR(-EINVAL); - return clk_data->clks[CP110_MAX_CORE_CLOCKS + idx]; + return clk_data->hws[CP110_MAX_CORE_CLOCKS + idx]; } return ERR_PTR(-EINVAL); @@ -189,8 +188,8 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) struct regmap *regmap; struct device_node *np = pdev->dev.of_node; const char *ppv2_name, *apll_name, *core_name, *eip_name, *nand_name; - struct clk_onecell_data *cp110_clk_data; - struct clk *clk, **cp110_clks; + struct clk_hw_onecell_data *cp110_clk_data; + struct clk_hw *hw, **cp110_clks; u32 nand_clk_ctrl; int i, ret; @@ -203,80 +202,75 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) if (ret) return ret; - cp110_clks = devm_kcalloc(&pdev->dev, sizeof(struct clk *), - CP110_CLK_NUM, GFP_KERNEL); - if (!cp110_clks) - return -ENOMEM; - - cp110_clk_data = devm_kzalloc(&pdev->dev, - sizeof(*cp110_clk_data), + cp110_clk_data = devm_kzalloc(&pdev->dev, sizeof(*cp110_clk_data) + + sizeof(struct clk_hw *) * CP110_CLK_NUM, GFP_KERNEL); if (!cp110_clk_data) return -ENOMEM; - cp110_clk_data->clks = cp110_clks; - cp110_clk_data->clk_num = CP110_CLK_NUM; + cp110_clks = cp110_clk_data->hws; + cp110_clk_data->num = CP110_CLK_NUM; - /* Register the APLL which is the root of the clk tree */ + /* Register the APLL which is the root of the hw tree */ of_property_read_string_index(np, "core-clock-output-names", CP110_CORE_APLL, &apll_name); - clk = clk_register_fixed_rate(NULL, apll_name, NULL, 0, - 1000 * 1000 * 1000); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); + hw = clk_hw_register_fixed_rate(NULL, apll_name, NULL, 0, + 1000 * 1000 * 1000); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); goto fail0; } - cp110_clks[CP110_CORE_APLL] = clk; + cp110_clks[CP110_CORE_APLL] = hw; /* PPv2 is APLL/3 */ of_property_read_string_index(np, "core-clock-output-names", CP110_CORE_PPV2, &ppv2_name); - clk = clk_register_fixed_factor(NULL, ppv2_name, apll_name, 0, 1, 3); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); + hw = clk_hw_register_fixed_factor(NULL, ppv2_name, apll_name, 0, 1, 3); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); goto fail1; } - cp110_clks[CP110_CORE_PPV2] = clk; + cp110_clks[CP110_CORE_PPV2] = hw; /* EIP clock is APLL/2 */ of_property_read_string_index(np, "core-clock-output-names", CP110_CORE_EIP, &eip_name); - clk = clk_register_fixed_factor(NULL, eip_name, apll_name, 0, 1, 2); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); + hw = clk_hw_register_fixed_factor(NULL, eip_name, apll_name, 0, 1, 2); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); goto fail2; } - cp110_clks[CP110_CORE_EIP] = clk; + cp110_clks[CP110_CORE_EIP] = hw; /* Core clock is EIP/2 */ of_property_read_string_index(np, "core-clock-output-names", CP110_CORE_CORE, &core_name); - clk = clk_register_fixed_factor(NULL, core_name, eip_name, 0, 1, 2); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); + hw = clk_hw_register_fixed_factor(NULL, core_name, eip_name, 0, 1, 2); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); goto fail3; } - cp110_clks[CP110_CORE_CORE] = clk; + cp110_clks[CP110_CORE_CORE] = hw; /* NAND can be either APLL/2.5 or core clock */ of_property_read_string_index(np, "core-clock-output-names", CP110_CORE_NAND, &nand_name); if (nand_clk_ctrl & NF_CLOCK_SEL_400_MASK) - clk = clk_register_fixed_factor(NULL, nand_name, - apll_name, 0, 2, 5); + hw = clk_hw_register_fixed_factor(NULL, nand_name, + apll_name, 0, 2, 5); else - clk = clk_register_fixed_factor(NULL, nand_name, - core_name, 0, 1, 1); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); + hw = clk_hw_register_fixed_factor(NULL, nand_name, + core_name, 0, 1, 1); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); goto fail4; } - cp110_clks[CP110_CORE_NAND] = clk; + cp110_clks[CP110_CORE_NAND] = hw; for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) { const char *parent, *name; @@ -335,16 +329,16 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) break; } - clk = cp110_register_gate(name, parent, regmap, i); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); + hw = cp110_register_gate(name, parent, regmap, i); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); goto fail_gate; } - cp110_clks[CP110_MAX_CORE_CLOCKS + i] = clk; + cp110_clks[CP110_MAX_CORE_CLOCKS + i] = hw; } - ret = of_clk_add_provider(np, cp110_of_clk_get, cp110_clk_data); + ret = of_clk_add_hw_provider(np, cp110_of_clk_get, cp110_clk_data); if (ret) goto fail_clk_add; @@ -355,65 +349,36 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) fail_clk_add: fail_gate: for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) { - clk = cp110_clks[CP110_MAX_CORE_CLOCKS + i]; + hw = cp110_clks[CP110_MAX_CORE_CLOCKS + i]; - if (clk) - cp110_unregister_gate(clk); + if (hw) + cp110_unregister_gate(hw); } - clk_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]); + clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]); fail4: - clk_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]); + clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]); fail3: - clk_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]); + clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]); fail2: - clk_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]); + clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]); fail1: - clk_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]); + clk_hw_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]); fail0: return ret; } -static int cp110_syscon_clk_remove(struct platform_device *pdev) -{ - struct clk **cp110_clks = platform_get_drvdata(pdev); - int i; - - of_clk_del_provider(pdev->dev.of_node); - - for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) { - struct clk *clk = cp110_clks[CP110_MAX_CORE_CLOCKS + i]; - - if (clk) - cp110_unregister_gate(clk); - } - - clk_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]); - clk_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]); - clk_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]); - clk_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]); - clk_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]); - - return 0; -} - static const struct of_device_id cp110_syscon_of_match[] = { { .compatible = "marvell,cp110-system-controller0", }, { } }; -MODULE_DEVICE_TABLE(of, armada8k_pcie_of_match); static struct platform_driver cp110_syscon_driver = { .probe = cp110_syscon_clk_probe, - .remove = cp110_syscon_clk_remove, .driver = { .name = "marvell-cp110-system-controller0", .of_match_table = cp110_syscon_of_match, + .suppress_bind_attrs = true, }, }; - -module_platform_driver(cp110_syscon_driver); - -MODULE_DESCRIPTION("Marvell CP110 System Controller 0 driver"); -MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>"); -MODULE_LICENSE("GPL"); +builtin_platform_driver(cp110_syscon_driver); diff --git a/drivers/clk/nxp/clk-lpc18xx-ccu.c b/drivers/clk/nxp/clk-lpc18xx-ccu.c index f7136b94fd0e..27781b49eb82 100644 --- a/drivers/clk/nxp/clk-lpc18xx-ccu.c +++ b/drivers/clk/nxp/clk-lpc18xx-ccu.c @@ -277,12 +277,15 @@ static void __init lpc18xx_ccu_init(struct device_node *np) } clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); - if (!clk_data) + if (!clk_data) { + iounmap(reg_base); return; + } clk_data->num = of_property_count_strings(np, "clock-names"); clk_data->name = kcalloc(clk_data->num, sizeof(char *), GFP_KERNEL); if (!clk_data->name) { + iounmap(reg_base); kfree(clk_data); return; } diff --git a/drivers/clk/nxp/clk-lpc32xx.c b/drivers/clk/nxp/clk-lpc32xx.c index 34c97353cdeb..5b98ff9076f3 100644 --- a/drivers/clk/nxp/clk-lpc32xx.c +++ b/drivers/clk/nxp/clk-lpc32xx.c @@ -1282,13 +1282,13 @@ static struct clk_hw_proto clk_hw_proto[LPC32XX_CLK_HW_MAX] = { LPC32XX_DEFINE_MUX(PWM1_MUX, PWMCLK_CTRL, 1, 0x1, NULL, 0), LPC32XX_DEFINE_DIV(PWM1_DIV, PWMCLK_CTRL, 4, 4, NULL, - CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO), + CLK_DIVIDER_ONE_BASED), LPC32XX_DEFINE_GATE(PWM1_GATE, PWMCLK_CTRL, 0, 0), LPC32XX_DEFINE_COMPOSITE(PWM1, PWM1_MUX, PWM1_DIV, PWM1_GATE), LPC32XX_DEFINE_MUX(PWM2_MUX, PWMCLK_CTRL, 3, 0x1, NULL, 0), LPC32XX_DEFINE_DIV(PWM2_DIV, PWMCLK_CTRL, 8, 4, NULL, - CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO), + CLK_DIVIDER_ONE_BASED), LPC32XX_DEFINE_GATE(PWM2_GATE, PWMCLK_CTRL, 2, 0), LPC32XX_DEFINE_COMPOSITE(PWM2, PWM2_MUX, PWM2_DIV, PWM2_GATE), @@ -1335,8 +1335,7 @@ static struct clk_hw_proto clk_hw_proto[LPC32XX_CLK_HW_MAX] = { LPC32XX_DEFINE_GATE(USB_DIV_GATE, USB_CTRL, 17, 0), LPC32XX_DEFINE_COMPOSITE(USB_DIV, _NULL, USB_DIV_DIV, USB_DIV_GATE), - LPC32XX_DEFINE_DIV(SD_DIV, MS_CTRL, 0, 4, NULL, - CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO), + LPC32XX_DEFINE_DIV(SD_DIV, MS_CTRL, 0, 4, NULL, CLK_DIVIDER_ONE_BASED), LPC32XX_DEFINE_CLK(SD_GATE, MS_CTRL, BIT(5) | BIT(9), BIT(5) | BIT(9), 0x0, BIT(5) | BIT(9), 0x0, 0x0, clk_mask_ops), LPC32XX_DEFINE_COMPOSITE(SD, _NULL, SD_DIV, SD_GATE), @@ -1478,6 +1477,20 @@ static struct clk * __init lpc32xx_clk_register(u32 id) return clk; } +static void __init lpc32xx_clk_div_quirk(u32 reg, u32 div_mask, u32 gate) +{ + u32 val; + + regmap_read(clk_regmap, reg, &val); + + if (!(val & div_mask)) { + val &= ~gate; + val |= BIT(__ffs(div_mask)); + } + + regmap_update_bits(clk_regmap, reg, gate | div_mask, val); +} + static void __init lpc32xx_clk_init(struct device_node *np) { unsigned int i; @@ -1517,6 +1530,17 @@ static void __init lpc32xx_clk_init(struct device_node *np) return; } + /* + * Divider part of PWM and MS clocks requires a quirk to avoid + * a misinterpretation of formally valid zero value in register + * bitfield, which indicates another clock gate. Instead of + * adding complexity to a gate clock ensure that zero value in + * divider clock is never met in runtime. + */ + lpc32xx_clk_div_quirk(LPC32XX_CLKPWR_PWMCLK_CTRL, 0xf0, BIT(0)); + lpc32xx_clk_div_quirk(LPC32XX_CLKPWR_PWMCLK_CTRL, 0xf00, BIT(2)); + lpc32xx_clk_div_quirk(LPC32XX_CLKPWR_MS_CTRL, 0xf, BIT(5) | BIT(9)); + for (i = 1; i < LPC32XX_CLK_MAX; i++) { clk[i] = lpc32xx_clk_register(i); if (IS_ERR(clk[i])) { diff --git a/drivers/clk/pxa/clk-pxa.c b/drivers/clk/pxa/clk-pxa.c index 29cee9e8d4d9..74f64c3c4290 100644 --- a/drivers/clk/pxa/clk-pxa.c +++ b/drivers/clk/pxa/clk-pxa.c @@ -18,7 +18,27 @@ #include <dt-bindings/clock/pxa-clock.h> #include "clk-pxa.h" -DEFINE_SPINLOCK(lock); +#define KHz 1000 +#define MHz (1000 * 1000) + +#define MDREFR_K0DB4 (1 << 29) /* SDCLK0 Divide by 4 Control/Status */ +#define MDREFR_K2FREE (1 << 25) /* SDRAM Free-Running Control */ +#define MDREFR_K1FREE (1 << 24) /* SDRAM Free-Running Control */ +#define MDREFR_K0FREE (1 << 23) /* SDRAM Free-Running Control */ +#define MDREFR_SLFRSH (1 << 22) /* SDRAM Self-Refresh Control/Status */ +#define MDREFR_APD (1 << 20) /* SDRAM/SSRAM Auto-Power-Down Enable */ +#define MDREFR_K2DB2 (1 << 19) /* SDCLK2 Divide by 2 Control/Status */ +#define MDREFR_K2RUN (1 << 18) /* SDCLK2 Run Control/Status */ +#define MDREFR_K1DB2 (1 << 17) /* SDCLK1 Divide by 2 Control/Status */ +#define MDREFR_K1RUN (1 << 16) /* SDCLK1 Run Control/Status */ +#define MDREFR_E1PIN (1 << 15) /* SDCKE1 Level Control/Status */ +#define MDREFR_K0DB2 (1 << 14) /* SDCLK0 Divide by 2 Control/Status */ +#define MDREFR_K0RUN (1 << 13) /* SDCLK0 Run Control/Status */ +#define MDREFR_E0PIN (1 << 12) /* SDCKE0 Level Control/Status */ +#define MDREFR_DB2_MASK (MDREFR_K2DB2 | MDREFR_K1DB2) +#define MDREFR_DRI_MASK 0xFFF + +static DEFINE_SPINLOCK(pxa_clk_lock); static struct clk *pxa_clocks[CLK_MAX]; static struct clk_onecell_data onecell_data = { @@ -89,7 +109,7 @@ int __init clk_pxa_cken_init(const struct desc_clk_cken *clks, int nb_clks) pxa_clk->lp = clks[i].lp; pxa_clk->hp = clks[i].hp; pxa_clk->gate = clks[i].gate; - pxa_clk->gate.lock = &lock; + pxa_clk->gate.lock = &pxa_clk_lock; clk = clk_register_composite(NULL, clks[i].name, clks[i].parent_names, 2, &pxa_clk->hw, &cken_mux_ops, @@ -106,3 +126,124 @@ void __init clk_pxa_dt_common_init(struct device_node *np) { of_clk_add_provider(np, of_clk_src_onecell_get, &onecell_data); } + +void pxa2xx_core_turbo_switch(bool on) +{ + unsigned long flags; + unsigned int unused, clkcfg; + + local_irq_save(flags); + + asm("mrc p14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); + clkcfg &= ~CLKCFG_TURBO & ~CLKCFG_HALFTURBO; + if (on) + clkcfg |= CLKCFG_TURBO; + clkcfg |= CLKCFG_FCS; + + asm volatile( + " b 2f\n" + " .align 5\n" + "1: mcr p14, 0, %1, c6, c0, 0\n" + " b 3f\n" + "2: b 1b\n" + "3: nop\n" + : "=&r" (unused) + : "r" (clkcfg) + : ); + + local_irq_restore(flags); +} + +void pxa2xx_cpll_change(struct pxa2xx_freq *freq, + u32 (*mdrefr_dri)(unsigned int), void __iomem *mdrefr, + void __iomem *cccr) +{ + unsigned int clkcfg = freq->clkcfg; + unsigned int unused, preset_mdrefr, postset_mdrefr; + unsigned long flags; + + local_irq_save(flags); + + /* Calculate the next MDREFR. If we're slowing down the SDRAM clock + * we need to preset the smaller DRI before the change. If we're + * speeding up we need to set the larger DRI value after the change. + */ + preset_mdrefr = postset_mdrefr = readl(mdrefr); + if ((preset_mdrefr & MDREFR_DRI_MASK) > mdrefr_dri(freq->membus_khz)) { + preset_mdrefr = (preset_mdrefr & ~MDREFR_DRI_MASK); + preset_mdrefr |= mdrefr_dri(freq->membus_khz); + } + postset_mdrefr = + (postset_mdrefr & ~MDREFR_DRI_MASK) | + mdrefr_dri(freq->membus_khz); + + /* If we're dividing the memory clock by two for the SDRAM clock, this + * must be set prior to the change. Clearing the divide must be done + * after the change. + */ + if (freq->div2) { + preset_mdrefr |= MDREFR_DB2_MASK; + postset_mdrefr |= MDREFR_DB2_MASK; + } else { + postset_mdrefr &= ~MDREFR_DB2_MASK; + } + + /* Set new the CCCR and prepare CLKCFG */ + writel(freq->cccr, cccr); + + asm volatile( + " ldr r4, [%1]\n" + " b 2f\n" + " .align 5\n" + "1: str %3, [%1] /* preset the MDREFR */\n" + " mcr p14, 0, %2, c6, c0, 0 /* set CLKCFG[FCS] */\n" + " str %4, [%1] /* postset the MDREFR */\n" + " b 3f\n" + "2: b 1b\n" + "3: nop\n" + : "=&r" (unused) + : "r" (mdrefr), "r" (clkcfg), "r" (preset_mdrefr), + "r" (postset_mdrefr) + : "r4", "r5"); + + local_irq_restore(flags); +} + +int pxa2xx_determine_rate(struct clk_rate_request *req, + struct pxa2xx_freq *freqs, int nb_freqs) +{ + int i, closest_below = -1, closest_above = -1; + unsigned long rate; + + for (i = 0; i < nb_freqs; i++) { + rate = freqs[i].cpll; + if (rate == req->rate) + break; + if (rate < req->min_rate) + continue; + if (rate > req->max_rate) + continue; + if (rate <= req->rate) + closest_below = i; + if ((rate >= req->rate) && (closest_above == -1)) + closest_above = i; + } + + req->best_parent_hw = NULL; + + if (i < nb_freqs) { + rate = req->rate; + } else if (closest_below >= 0) { + rate = freqs[closest_below].cpll; + } else if (closest_above >= 0) { + rate = freqs[closest_above].cpll; + } else { + pr_debug("%s(rate=%lu) no match\n", __func__, req->rate); + return -EINVAL; + } + + pr_debug("%s(rate=%lu) rate=%lu\n", __func__, req->rate, rate); + req->rate = rate; + + return 0; +} diff --git a/drivers/clk/pxa/clk-pxa.h b/drivers/clk/pxa/clk-pxa.h index d1de805df867..2b90c5917b32 100644 --- a/drivers/clk/pxa/clk-pxa.h +++ b/drivers/clk/pxa/clk-pxa.h @@ -13,6 +13,11 @@ #ifndef _CLK_PXA_ #define _CLK_PXA_ +#define CLKCFG_TURBO 0x1 +#define CLKCFG_FCS 0x2 +#define CLKCFG_HALFTURBO 0x4 +#define CLKCFG_FASTBUS 0x8 + #define PARENTS(name) \ static const char *const name ## _parents[] __initconst #define MUX_RO_RATE_RO_OPS(name, clk_name) \ @@ -35,10 +40,27 @@ NULL, NULL, CLK_GET_RATE_NOCACHE); \ } -#define RATE_RO_OPS(name, clk_name) \ +#define RATE_RO_OPS(name, clk_name) \ + static struct clk_hw name ## _rate_hw; \ + static const struct clk_ops name ## _rate_ops = { \ + .recalc_rate = name ## _get_rate, \ + }; \ + static struct clk * __init clk_register_ ## name(void) \ + { \ + return clk_register_composite(NULL, clk_name, \ + name ## _parents, \ + ARRAY_SIZE(name ## _parents), \ + NULL, NULL, \ + &name ## _rate_hw, &name ## _rate_ops, \ + NULL, NULL, CLK_GET_RATE_NOCACHE); \ + } + +#define RATE_OPS(name, clk_name) \ static struct clk_hw name ## _rate_hw; \ static struct clk_ops name ## _rate_ops = { \ .recalc_rate = name ## _get_rate, \ + .set_rate = name ## _set_rate, \ + .determine_rate = name ## _determine_rate, \ }; \ static struct clk * __init clk_register_ ## name(void) \ { \ @@ -50,6 +72,24 @@ NULL, NULL, CLK_GET_RATE_NOCACHE); \ } +#define MUX_OPS(name, clk_name, flags) \ + static struct clk_hw name ## _mux_hw; \ + static const struct clk_ops name ## _mux_ops = { \ + .get_parent = name ## _get_parent, \ + .set_parent = name ## _set_parent, \ + .determine_rate = name ## _determine_rate, \ + }; \ + static struct clk * __init clk_register_ ## name(void) \ + { \ + return clk_register_composite(NULL, clk_name, \ + name ## _parents, \ + ARRAY_SIZE(name ## _parents), \ + &name ## _mux_hw, &name ## _mux_ops, \ + NULL, NULL, \ + NULL, NULL, \ + CLK_GET_RATE_NOCACHE | flags); \ + } + /* * CKEN clock type * This clock takes it source from 2 possible parents : @@ -95,7 +135,15 @@ struct desc_clk_cken { PXA_CKEN(dev_id, con_id, name, parents, 1, 1, 1, 1, \ NULL, cken_reg, cken_bit, flag) -static int dummy_clk_set_parent(struct clk_hw *hw, u8 index) +struct pxa2xx_freq { + unsigned long cpll; + unsigned int membus_khz; + unsigned int cccr; + unsigned int div2; + unsigned int clkcfg; +}; + +static inline int dummy_clk_set_parent(struct clk_hw *hw, u8 index) { return 0; } @@ -105,4 +153,11 @@ extern void clkdev_pxa_register(int ckid, const char *con_id, extern int clk_pxa_cken_init(const struct desc_clk_cken *clks, int nb_clks); void clk_pxa_dt_common_init(struct device_node *np); +void pxa2xx_core_turbo_switch(bool on); +void pxa2xx_cpll_change(struct pxa2xx_freq *freq, + u32 (*mdrefr_dri)(unsigned int), void __iomem *mdrefr, + void __iomem *cccr); +int pxa2xx_determine_rate(struct clk_rate_request *req, + struct pxa2xx_freq *freqs, int nb_freqs); + #endif diff --git a/drivers/clk/pxa/clk-pxa25x.c b/drivers/clk/pxa/clk-pxa25x.c index a98b98e2a9e4..6416c1f8e632 100644 --- a/drivers/clk/pxa/clk-pxa25x.c +++ b/drivers/clk/pxa/clk-pxa25x.c @@ -18,6 +18,7 @@ #include <linux/io.h> #include <linux/of.h> #include <mach/pxa2xx-regs.h> +#include <mach/smemc.h> #include <dt-bindings/clock/pxa-clock.h> #include "clk-pxa.h" @@ -30,6 +31,17 @@ enum { PXA_CORE_TURBO, }; +#define PXA25x_CLKCFG(T) \ + (CLKCFG_FCS | \ + ((T) ? CLKCFG_TURBO : 0)) +#define PXA25x_CCCR(N2, M, L) (N2 << 7 | M << 5 | L) + +#define MDCNFG_DRAC2(mdcnfg) (((mdcnfg) >> 21) & 0x3) +#define MDCNFG_DRAC0(mdcnfg) (((mdcnfg) >> 5) & 0x3) + +/* Define the refresh period in mSec for the SDRAM and the number of rows */ +#define SDRAM_TREF 64 /* standard 64ms SDRAM */ + /* * Various clock factors driven by the CCCR register. */ @@ -48,6 +60,34 @@ static const char * const get_freq_khz[] = { "core", "run", "cpll", "memory" }; +static int get_sdram_rows(void) +{ + static int sdram_rows; + unsigned int drac2 = 0, drac0 = 0; + u32 mdcnfg; + + if (sdram_rows) + return sdram_rows; + + mdcnfg = readl_relaxed(MDCNFG); + + if (mdcnfg & (MDCNFG_DE2 | MDCNFG_DE3)) + drac2 = MDCNFG_DRAC2(mdcnfg); + + if (mdcnfg & (MDCNFG_DE0 | MDCNFG_DE1)) + drac0 = MDCNFG_DRAC0(mdcnfg); + + sdram_rows = 1 << (11 + max(drac0, drac2)); + return sdram_rows; +} + +static u32 mdrefr_dri(unsigned int freq_khz) +{ + u32 interval = freq_khz * SDRAM_TREF / get_sdram_rows(); + + return interval / 32; +} + /* * Get the clock frequency as reflected by CCCR and the turbo flag. * We assume these values have been applied via a fcs. @@ -139,6 +179,21 @@ static struct desc_clk_cken pxa25x_clocks[] __initdata = { clk_pxa25x_memory_parents, 0), }; +/* + * In this table, PXA25x_CCCR(N2, M, L) has the following meaning, where : + * - freq_cpll = n * m * L * 3.6864 MHz + * - n = N2 / 2 + * - m = 2^(M - 1), where 1 <= M <= 3 + * - l = L_clk_mult[L], ie. { 0, 27, 32, 36, 40, 45, 0, }[L] + */ +static struct pxa2xx_freq pxa25x_freqs[] = { + /* CPU MEMBUS CCCR DIV2 CCLKCFG */ + { 99532800, 99500, PXA25x_CCCR(2, 1, 1), 1, PXA25x_CLKCFG(1)}, + {199065600, 99500, PXA25x_CCCR(4, 1, 1), 0, PXA25x_CLKCFG(1)}, + {298598400, 99500, PXA25x_CCCR(3, 2, 1), 0, PXA25x_CLKCFG(1)}, + {398131200, 99500, PXA25x_CCCR(4, 2, 1), 0, PXA25x_CLKCFG(1)}, +}; + static u8 clk_pxa25x_core_get_parent(struct clk_hw *hw) { unsigned long clkcfg; @@ -151,13 +206,24 @@ static u8 clk_pxa25x_core_get_parent(struct clk_hw *hw) return PXA_CORE_RUN; } -static unsigned long clk_pxa25x_core_get_rate(struct clk_hw *hw, - unsigned long parent_rate) +static int clk_pxa25x_core_set_parent(struct clk_hw *hw, u8 index) { - return parent_rate; + if (index > PXA_CORE_TURBO) + return -EINVAL; + + pxa2xx_core_turbo_switch(index == PXA_CORE_TURBO); + + return 0; +} + +static int clk_pxa25x_core_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return __clk_mux_determine_rate(hw, req); } + PARENTS(clk_pxa25x_core) = { "run", "cpll" }; -MUX_RO_RATE_RO_OPS(clk_pxa25x_core, "core"); +MUX_OPS(clk_pxa25x_core, "core", CLK_SET_RATE_PARENT); static unsigned long clk_pxa25x_run_get_rate(struct clk_hw *hw, unsigned long parent_rate) @@ -182,17 +248,42 @@ static unsigned long clk_pxa25x_cpll_get_rate(struct clk_hw *hw, m = M_clk_mult[(cccr >> 5) & 0x03]; n2 = N2_clk_mult[(cccr >> 7) & 0x07]; - if (t) - return m * l * n2 * parent_rate / 2; - return m * l * parent_rate; + return m * l * n2 * parent_rate / 2; +} + +static int clk_pxa25x_cpll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return pxa2xx_determine_rate(req, pxa25x_freqs, + ARRAY_SIZE(pxa25x_freqs)); +} + +static int clk_pxa25x_cpll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int i; + + pr_debug("%s(rate=%lu parent_rate=%lu)\n", __func__, rate, parent_rate); + for (i = 0; i < ARRAY_SIZE(pxa25x_freqs); i++) + if (pxa25x_freqs[i].cpll == rate) + break; + + if (i >= ARRAY_SIZE(pxa25x_freqs)) + return -EINVAL; + + pxa2xx_cpll_change(&pxa25x_freqs[i], mdrefr_dri, MDREFR, CCCR); + + return 0; } PARENTS(clk_pxa25x_cpll) = { "osc_3_6864mhz" }; -RATE_RO_OPS(clk_pxa25x_cpll, "cpll"); +RATE_OPS(clk_pxa25x_cpll, "cpll"); static void __init pxa25x_register_core(void) { - clk_register_clk_pxa25x_cpll(); - clk_register_clk_pxa25x_run(); + clkdev_pxa_register(CLK_NONE, "cpll", NULL, + clk_register_clk_pxa25x_cpll()); + clkdev_pxa_register(CLK_NONE, "run", NULL, + clk_register_clk_pxa25x_run()); clkdev_pxa_register(CLK_CORE, "core", NULL, clk_register_clk_pxa25x_core()); } @@ -214,7 +305,8 @@ static void __init pxa25x_base_clocks_init(void) { pxa25x_register_plls(); pxa25x_register_core(); - clk_register_clk_pxa25x_memory(); + clkdev_pxa_register(CLK_NONE, "system_bus", NULL, + clk_register_clk_pxa25x_memory()); } #define DUMMY_CLK(_con_id, _dev_id, _parent) \ @@ -230,7 +322,7 @@ static struct dummy_clk dummy_clks[] __initdata = { DUMMY_CLK("GPIO11_CLK", NULL, "osc_3_6864mhz"), DUMMY_CLK("GPIO12_CLK", NULL, "osc_32_768khz"), DUMMY_CLK(NULL, "sa1100-rtc", "osc_32_768khz"), - DUMMY_CLK("OSTIMER0", NULL, "osc_32_768khz"), + DUMMY_CLK("OSTIMER0", NULL, "osc_3_6864mhz"), DUMMY_CLK("UARTCLK", "pxa2xx-ir", "STUART"), }; diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c index c40b1804f58c..25a30194d27a 100644 --- a/drivers/clk/pxa/clk-pxa27x.c +++ b/drivers/clk/pxa/clk-pxa27x.c @@ -17,6 +17,8 @@ #include <linux/clkdev.h> #include <linux/of.h> +#include <mach/smemc.h> + #include <dt-bindings/clock/pxa-clock.h> #include "clk-pxa.h" @@ -45,11 +47,52 @@ enum { PXA_MEM_RUN, }; +#define PXA27x_CLKCFG(B, HT, T) \ + (CLKCFG_FCS | \ + ((B) ? CLKCFG_FASTBUS : 0) | \ + ((HT) ? CLKCFG_HALFTURBO : 0) | \ + ((T) ? CLKCFG_TURBO : 0)) +#define PXA27x_CCCR(A, L, N2) (A << 25 | N2 << 7 | L) + +#define MDCNFG_DRAC2(mdcnfg) (((mdcnfg) >> 21) & 0x3) +#define MDCNFG_DRAC0(mdcnfg) (((mdcnfg) >> 5) & 0x3) + +/* Define the refresh period in mSec for the SDRAM and the number of rows */ +#define SDRAM_TREF 64 /* standard 64ms SDRAM */ + static const char * const get_freq_khz[] = { "core", "run", "cpll", "memory", "system_bus" }; +static int get_sdram_rows(void) +{ + static int sdram_rows; + unsigned int drac2 = 0, drac0 = 0; + u32 mdcnfg; + + if (sdram_rows) + return sdram_rows; + + mdcnfg = readl_relaxed(MDCNFG); + + if (mdcnfg & (MDCNFG_DE2 | MDCNFG_DE3)) + drac2 = MDCNFG_DRAC2(mdcnfg); + + if (mdcnfg & (MDCNFG_DE0 | MDCNFG_DE1)) + drac0 = MDCNFG_DRAC0(mdcnfg); + + sdram_rows = 1 << (11 + max(drac0, drac2)); + return sdram_rows; +} + +static u32 mdrefr_dri(unsigned int freq_khz) +{ + u32 interval = freq_khz * SDRAM_TREF / get_sdram_rows(); + + return (interval - 31) / 32; +} + /* * Get the clock frequency as reflected by CCSR and the turbo flag. * We assume these values have been applied via a fcs. @@ -145,6 +188,42 @@ static struct desc_clk_cken pxa27x_clocks[] __initdata = { }; +/* + * PXA270 definitions + * + * For the PXA27x: + * Control variables are A, L, 2N for CCCR; B, HT, T for CLKCFG. + * + * A = 0 => memory controller clock from table 3-7, + * A = 1 => memory controller clock = system bus clock + * Run mode frequency = 13 MHz * L + * Turbo mode frequency = 13 MHz * L * N + * System bus frequency = 13 MHz * L / (B + 1) + * + * In CCCR: + * A = 1 + * L = 16 oscillator to run mode ratio + * 2N = 6 2 * (turbo mode to run mode ratio) + * + * In CCLKCFG: + * B = 1 Fast bus mode + * HT = 0 Half-Turbo mode + * T = 1 Turbo mode + * + * For now, just support some of the combinations in table 3-7 of + * PXA27x Processor Family Developer's Manual to simplify frequency + * change sequences. + */ +static struct pxa2xx_freq pxa27x_freqs[] = { + {104000000, 104000, PXA27x_CCCR(1, 8, 2), 0, PXA27x_CLKCFG(1, 0, 1) }, + {156000000, 104000, PXA27x_CCCR(1, 8, 3), 0, PXA27x_CLKCFG(1, 0, 1) }, + {208000000, 208000, PXA27x_CCCR(0, 16, 2), 1, PXA27x_CLKCFG(0, 0, 1) }, + {312000000, 208000, PXA27x_CCCR(1, 16, 3), 1, PXA27x_CLKCFG(1, 0, 1) }, + {416000000, 208000, PXA27x_CCCR(1, 16, 4), 1, PXA27x_CLKCFG(1, 0, 1) }, + {520000000, 208000, PXA27x_CCCR(1, 16, 5), 1, PXA27x_CLKCFG(1, 0, 1) }, + {624000000, 208000, PXA27x_CCCR(1, 16, 6), 1, PXA27x_CLKCFG(1, 0, 1) }, +}; + static unsigned long clk_pxa27x_cpll_get_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -162,10 +241,35 @@ static unsigned long clk_pxa27x_cpll_get_rate(struct clk_hw *hw, L = l * parent_rate; N = (L * n2) / 2; - return t ? N : L; + return N; +} + +static int clk_pxa27x_cpll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return pxa2xx_determine_rate(req, pxa27x_freqs, + ARRAY_SIZE(pxa27x_freqs)); +} + +static int clk_pxa27x_cpll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int i; + + pr_debug("%s(rate=%lu parent_rate=%lu)\n", __func__, rate, parent_rate); + for (i = 0; i < ARRAY_SIZE(pxa27x_freqs); i++) + if (pxa27x_freqs[i].cpll == rate) + break; + + if (i >= ARRAY_SIZE(pxa27x_freqs)) + return -EINVAL; + + pxa2xx_cpll_change(&pxa27x_freqs[i], mdrefr_dri, MDREFR, CCCR); + return 0; } + PARENTS(clk_pxa27x_cpll) = { "osc_13mhz" }; -RATE_RO_OPS(clk_pxa27x_cpll, "cpll"); +RATE_OPS(clk_pxa27x_cpll, "cpll"); static unsigned long clk_pxa27x_lcd_base_get_rate(struct clk_hw *hw, unsigned long parent_rate) @@ -217,31 +321,10 @@ static void __init pxa27x_register_plls(void) clk_register_fixed_factor(NULL, "ppll_312mhz", "osc_13mhz", 0, 24, 1); } -static unsigned long clk_pxa27x_core_get_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - unsigned long clkcfg; - unsigned int t, ht, b, osc_forced; - unsigned long ccsr = readl(CCSR); - - osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); - asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); - t = clkcfg & (1 << 0); - ht = clkcfg & (1 << 2); - b = clkcfg & (1 << 3); - - if (osc_forced) - return parent_rate; - if (ht) - return parent_rate / 2; - else - return parent_rate; -} - static u8 clk_pxa27x_core_get_parent(struct clk_hw *hw) { unsigned long clkcfg; - unsigned int t, ht, b, osc_forced; + unsigned int t, ht, osc_forced; unsigned long ccsr = readl(CCSR); osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); @@ -251,14 +334,30 @@ static u8 clk_pxa27x_core_get_parent(struct clk_hw *hw) asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); t = clkcfg & (1 << 0); ht = clkcfg & (1 << 2); - b = clkcfg & (1 << 3); if (ht || t) return PXA_CORE_TURBO; return PXA_CORE_RUN; } + +static int clk_pxa27x_core_set_parent(struct clk_hw *hw, u8 index) +{ + if (index > PXA_CORE_TURBO) + return -EINVAL; + + pxa2xx_core_turbo_switch(index == PXA_CORE_TURBO); + + return 0; +} + +static int clk_pxa27x_core_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return __clk_mux_determine_rate(hw, req); +} + PARENTS(clk_pxa27x_core) = { "osc_13mhz", "run", "cpll" }; -MUX_RO_RATE_RO_OPS(clk_pxa27x_core, "core"); +MUX_OPS(clk_pxa27x_core, "core", CLK_SET_RATE_PARENT); static unsigned long clk_pxa27x_run_get_rate(struct clk_hw *hw, unsigned long parent_rate) @@ -273,9 +372,10 @@ RATE_RO_OPS(clk_pxa27x_run, "run"); static void __init pxa27x_register_core(void) { - clk_register_clk_pxa27x_cpll(); - clk_register_clk_pxa27x_run(); - + clkdev_pxa_register(CLK_NONE, "cpll", NULL, + clk_register_clk_pxa27x_cpll()); + clkdev_pxa_register(CLK_NONE, "run", NULL, + clk_register_clk_pxa27x_run()); clkdev_pxa_register(CLK_CORE, "core", NULL, clk_register_clk_pxa27x_core()); } @@ -294,9 +394,9 @@ static unsigned long clk_pxa27x_system_bus_get_rate(struct clk_hw *hw, if (osc_forced) return parent_rate; if (b) - return parent_rate / 2; - else return parent_rate; + else + return parent_rate / 2; } static u8 clk_pxa27x_system_bus_get_parent(struct clk_hw *hw) @@ -385,8 +485,10 @@ static void __init pxa27x_base_clocks_init(void) { pxa27x_register_plls(); pxa27x_register_core(); - clk_register_clk_pxa27x_system_bus(); - clk_register_clk_pxa27x_memory(); + clkdev_pxa_register(CLK_NONE, "system_bus", NULL, + clk_register_clk_pxa27x_system_bus()); + clkdev_pxa_register(CLK_NONE, "memory", NULL, + clk_register_clk_pxa27x_memory()); clk_register_clk_pxa27x_lcd_base(); } diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 0146d3c2547f..5fb8d7430908 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -2,6 +2,9 @@ config QCOM_GDSC bool select PM_GENERIC_DOMAINS if PM +config QCOM_RPMCC + bool + config COMMON_CLK_QCOM tristate "Support for Qualcomm's clock controllers" depends on OF @@ -9,6 +12,32 @@ config COMMON_CLK_QCOM select REGMAP_MMIO select RESET_CONTROLLER +config QCOM_CLK_RPM + tristate "RPM based Clock Controller" + depends on COMMON_CLK_QCOM && MFD_QCOM_RPM + select QCOM_RPMCC + help + The RPM (Resource Power Manager) is a dedicated hardware engine for + managing the shared SoC resources in order to keep the lowest power + profile. It communicates with other hardware subsystems via shared + memory and accepts clock requests, aggregates the requests and turns + the clocks on/off or scales them on demand. + Say Y if you want to support the clocks exposed by the RPM on + platforms such as apq8064, msm8660, msm8960 etc. + +config QCOM_CLK_SMD_RPM + tristate "RPM over SMD based Clock Controller" + depends on COMMON_CLK_QCOM && QCOM_SMD_RPM + select QCOM_RPMCC + help + The RPM (Resource Power Manager) is a dedicated hardware engine for + managing the shared SoC resources in order to keep the lowest power + profile. It communicates with other hardware subsystems via shared + memory and accepts clock requests, aggregates the requests and turns + the clocks on/off or scales them on demand. + Say Y if you want to support the clocks exposed by the RPM on + platforms such as apq8016, apq8084, msm8974 etc. + config APQ_GCC_8084 tristate "APQ8084 Global Clock Controller" select QCOM_GDSC @@ -132,6 +161,14 @@ config MSM_MMCC_8974 Say Y if you want to support multimedia devices such as display, graphics, video encode/decode, camera, etc. +config MSM_GCC_8994 + tristate "MSM8994 Global Clock Controller" + depends on COMMON_CLK_QCOM + help + Support for the global clock controller on msm8994 devices. + Say Y if you want to use peripheral devices such as UART, SPI, + i2c, USB, UFS, SD/eMMC, PCIe, etc. + config MSM_GCC_8996 tristate "MSM8996 Global Clock Controller" select QCOM_GDSC diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 1fb1f5476cb0..1c3e222b917b 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -24,8 +24,11 @@ obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o obj-$(CONFIG_MSM_GCC_8916) += gcc-msm8916.o obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o +obj-$(CONFIG_MSM_GCC_8994) += gcc-msm8994.o obj-$(CONFIG_MSM_GCC_8996) += gcc-msm8996.o obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o +obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o +obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index e6a03eaf7a93..47a1da3739ce 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -18,17 +18,21 @@ #include <linux/delay.h> #include "clk-alpha-pll.h" +#include "common.h" #define PLL_MODE 0x00 # define PLL_OUTCTRL BIT(0) # define PLL_BYPASSNL BIT(1) # define PLL_RESET_N BIT(2) +# define PLL_OFFLINE_REQ BIT(7) # define PLL_LOCK_COUNT_SHIFT 8 # define PLL_LOCK_COUNT_MASK 0x3f # define PLL_BIAS_COUNT_SHIFT 14 # define PLL_BIAS_COUNT_MASK 0x3f # define PLL_VOTE_FSM_ENA BIT(20) +# define PLL_FSM_ENA BIT(20) # define PLL_VOTE_FSM_RESET BIT(21) +# define PLL_OFFLINE_ACK BIT(28) # define PLL_ACTIVE_FLAG BIT(30) # define PLL_LOCK_DET BIT(31) @@ -46,6 +50,7 @@ #define PLL_USER_CTL_U 0x14 #define PLL_CONFIG_CTL 0x18 +#define PLL_CONFIG_CTL_U 0x20 #define PLL_TEST_CTL 0x1c #define PLL_TEST_CTL_U 0x20 #define PLL_STATUS 0x24 @@ -55,6 +60,7 @@ */ #define ALPHA_REG_BITWIDTH 40 #define ALPHA_BITWIDTH 32 +#define ALPHA_16BIT_MASK 0xffff #define to_clk_alpha_pll(_hw) container_of(to_clk_regmap(_hw), \ struct clk_alpha_pll, clkr) @@ -62,9 +68,10 @@ #define to_clk_alpha_pll_postdiv(_hw) container_of(to_clk_regmap(_hw), \ struct clk_alpha_pll_postdiv, clkr) -static int wait_for_pll(struct clk_alpha_pll *pll) +static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse, + const char *action) { - u32 val, mask, off; + u32 val, off; int count; int ret; const char *name = clk_hw_get_name(&pll->clkr.hw); @@ -74,26 +81,148 @@ static int wait_for_pll(struct clk_alpha_pll *pll) if (ret) return ret; - if (val & PLL_VOTE_FSM_ENA) - mask = PLL_ACTIVE_FLAG; - else - mask = PLL_LOCK_DET; - - /* Wait for pll to enable. */ for (count = 100; count > 0; count--) { ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); if (ret) return ret; - if ((val & mask) == mask) + if (inverse && !(val & mask)) + return 0; + else if ((val & mask) == mask) return 0; udelay(1); } - WARN(1, "%s didn't enable after voting for it!\n", name); + WARN(1, "%s failed to %s!\n", name, action); return -ETIMEDOUT; } +#define wait_for_pll_enable_active(pll) \ + wait_for_pll(pll, PLL_ACTIVE_FLAG, 0, "enable") + +#define wait_for_pll_enable_lock(pll) \ + wait_for_pll(pll, PLL_LOCK_DET, 0, "enable") + +#define wait_for_pll_disable(pll) \ + wait_for_pll(pll, PLL_ACTIVE_FLAG, 1, "disable") + +#define wait_for_pll_offline(pll) \ + wait_for_pll(pll, PLL_OFFLINE_ACK, 0, "offline") + +void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, + const struct alpha_pll_config *config) +{ + u32 val, mask; + u32 off = pll->offset; + + regmap_write(regmap, off + PLL_L_VAL, config->l); + regmap_write(regmap, off + PLL_ALPHA_VAL, config->alpha); + regmap_write(regmap, off + PLL_CONFIG_CTL, config->config_ctl_val); + regmap_write(regmap, off + PLL_CONFIG_CTL_U, config->config_ctl_hi_val); + + val = config->main_output_mask; + val |= config->aux_output_mask; + val |= config->aux2_output_mask; + val |= config->early_output_mask; + val |= config->pre_div_val; + val |= config->post_div_val; + val |= config->vco_val; + + mask = config->main_output_mask; + mask |= config->aux_output_mask; + mask |= config->aux2_output_mask; + mask |= config->early_output_mask; + mask |= config->pre_div_mask; + mask |= config->post_div_mask; + mask |= config->vco_mask; + + regmap_update_bits(regmap, off + PLL_USER_CTL, mask, val); + + if (pll->flags & SUPPORTS_FSM_MODE) + qcom_pll_set_fsm_mode(regmap, off + PLL_MODE, 6, 0); +} + +static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw) +{ + int ret; + u32 val, off; + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + + off = pll->offset; + ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); + if (ret) + return ret; + + val |= PLL_FSM_ENA; + + if (pll->flags & SUPPORTS_OFFLINE_REQ) + val &= ~PLL_OFFLINE_REQ; + + ret = regmap_write(pll->clkr.regmap, off + PLL_MODE, val); + if (ret) + return ret; + + /* Make sure enable request goes through before waiting for update */ + mb(); + + return wait_for_pll_enable_active(pll); +} + +static void clk_alpha_pll_hwfsm_disable(struct clk_hw *hw) +{ + int ret; + u32 val, off; + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + + off = pll->offset; + ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); + if (ret) + return; + + if (pll->flags & SUPPORTS_OFFLINE_REQ) { + ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE, + PLL_OFFLINE_REQ, PLL_OFFLINE_REQ); + if (ret) + return; + + ret = wait_for_pll_offline(pll); + if (ret) + return; + } + + /* Disable hwfsm */ + ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE, + PLL_FSM_ENA, 0); + if (ret) + return; + + wait_for_pll_disable(pll); +} + +static int pll_is_enabled(struct clk_hw *hw, u32 mask) +{ + int ret; + u32 val, off; + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + + off = pll->offset; + ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); + if (ret) + return ret; + + return !!(val & mask); +} + +static int clk_alpha_pll_hwfsm_is_enabled(struct clk_hw *hw) +{ + return pll_is_enabled(hw, PLL_ACTIVE_FLAG); +} + +static int clk_alpha_pll_is_enabled(struct clk_hw *hw) +{ + return pll_is_enabled(hw, PLL_LOCK_DET); +} + static int clk_alpha_pll_enable(struct clk_hw *hw) { int ret; @@ -112,7 +241,7 @@ static int clk_alpha_pll_enable(struct clk_hw *hw) ret = clk_enable_regmap(hw); if (ret) return ret; - return wait_for_pll(pll); + return wait_for_pll_enable_active(pll); } /* Skip if already enabled */ @@ -136,7 +265,7 @@ static int clk_alpha_pll_enable(struct clk_hw *hw) if (ret) return ret; - ret = wait_for_pll(pll); + ret = wait_for_pll_enable_lock(pll); if (ret) return ret; @@ -234,9 +363,14 @@ clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) regmap_read(pll->clkr.regmap, off + PLL_USER_CTL, &ctl); if (ctl & PLL_ALPHA_EN) { regmap_read(pll->clkr.regmap, off + PLL_ALPHA_VAL, &low); - regmap_read(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, &high); - a = (u64)high << 32 | low; - a >>= ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH; + if (pll->flags & SUPPORTS_16BIT_ALPHA) { + a = low & ALPHA_16BIT_MASK; + } else { + regmap_read(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, + &high); + a = (u64)high << 32 | low; + a >>= ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH; + } } return alpha_pll_calc_rate(prate, l, a); @@ -257,11 +391,15 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, return -EINVAL; } - a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH); - regmap_write(pll->clkr.regmap, off + PLL_L_VAL, l); - regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL, a); - regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, a >> 32); + + if (pll->flags & SUPPORTS_16BIT_ALPHA) { + regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL, + a & ALPHA_16BIT_MASK); + } else { + a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH); + regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, a >> 32); + } regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL, PLL_VCO_MASK << PLL_VCO_SHIFT, @@ -294,12 +432,23 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate, const struct clk_ops clk_alpha_pll_ops = { .enable = clk_alpha_pll_enable, .disable = clk_alpha_pll_disable, + .is_enabled = clk_alpha_pll_is_enabled, .recalc_rate = clk_alpha_pll_recalc_rate, .round_rate = clk_alpha_pll_round_rate, .set_rate = clk_alpha_pll_set_rate, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_ops); +const struct clk_ops clk_alpha_pll_hwfsm_ops = { + .enable = clk_alpha_pll_hwfsm_enable, + .disable = clk_alpha_pll_hwfsm_disable, + .is_enabled = clk_alpha_pll_hwfsm_is_enabled, + .recalc_rate = clk_alpha_pll_recalc_rate, + .round_rate = clk_alpha_pll_round_rate, + .set_rate = clk_alpha_pll_set_rate, +}; +EXPORT_SYMBOL_GPL(clk_alpha_pll_hwfsm_ops); + static unsigned long clk_alpha_pll_postdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index 90ce2016e1a0..d6e1ee2c7348 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -34,6 +34,10 @@ struct clk_alpha_pll { const struct pll_vco *vco_table; size_t num_vco; +#define SUPPORTS_OFFLINE_REQ BIT(0) +#define SUPPORTS_16BIT_ALPHA BIT(1) +#define SUPPORTS_FSM_MODE BIT(2) + u8 flags; struct clk_regmap clkr; }; @@ -51,7 +55,28 @@ struct clk_alpha_pll_postdiv { struct clk_regmap clkr; }; +struct alpha_pll_config { + u32 l; + u32 alpha; + u32 config_ctl_val; + u32 config_ctl_hi_val; + u32 main_output_mask; + u32 aux_output_mask; + u32 aux2_output_mask; + u32 early_output_mask; + u32 pre_div_val; + u32 pre_div_mask; + u32 post_div_val; + u32 post_div_mask; + u32 vco_val; + u32 vco_mask; +}; + extern const struct clk_ops clk_alpha_pll_ops; +extern const struct clk_ops clk_alpha_pll_hwfsm_ops; extern const struct clk_ops clk_alpha_pll_postdiv_ops; +void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, + const struct alpha_pll_config *config); + #endif diff --git a/drivers/clk/qcom/clk-pll.c b/drivers/clk/qcom/clk-pll.c index 5b940d629045..cb6cb8710daf 100644 --- a/drivers/clk/qcom/clk-pll.c +++ b/drivers/clk/qcom/clk-pll.c @@ -23,16 +23,11 @@ #include <asm/div64.h> #include "clk-pll.h" +#include "common.h" #define PLL_OUTCTRL BIT(0) #define PLL_BYPASSNL BIT(1) #define PLL_RESET_N BIT(2) -#define PLL_LOCK_COUNT_SHIFT 8 -#define PLL_LOCK_COUNT_MASK 0x3f -#define PLL_BIAS_COUNT_SHIFT 14 -#define PLL_BIAS_COUNT_MASK 0x3f -#define PLL_VOTE_FSM_ENA BIT(20) -#define PLL_VOTE_FSM_RESET BIT(21) static int clk_pll_enable(struct clk_hw *hw) { @@ -228,26 +223,6 @@ const struct clk_ops clk_pll_vote_ops = { }; EXPORT_SYMBOL_GPL(clk_pll_vote_ops); -static void -clk_pll_set_fsm_mode(struct clk_pll *pll, struct regmap *regmap, u8 lock_count) -{ - u32 val; - u32 mask; - - /* De-assert reset to FSM */ - regmap_update_bits(regmap, pll->mode_reg, PLL_VOTE_FSM_RESET, 0); - - /* Program bias count and lock count */ - val = 1 << PLL_BIAS_COUNT_SHIFT | lock_count << PLL_LOCK_COUNT_SHIFT; - mask = PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT; - mask |= PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT; - regmap_update_bits(regmap, pll->mode_reg, mask, val); - - /* Enable PLL FSM voting */ - regmap_update_bits(regmap, pll->mode_reg, PLL_VOTE_FSM_ENA, - PLL_VOTE_FSM_ENA); -} - static void clk_pll_configure(struct clk_pll *pll, struct regmap *regmap, const struct pll_config *config) { @@ -280,7 +255,7 @@ void clk_pll_configure_sr(struct clk_pll *pll, struct regmap *regmap, { clk_pll_configure(pll, regmap, config); if (fsm_mode) - clk_pll_set_fsm_mode(pll, regmap, 8); + qcom_pll_set_fsm_mode(regmap, pll->mode_reg, 1, 8); } EXPORT_SYMBOL_GPL(clk_pll_configure_sr); @@ -289,7 +264,7 @@ void clk_pll_configure_sr_hpm_lp(struct clk_pll *pll, struct regmap *regmap, { clk_pll_configure(pll, regmap, config); if (fsm_mode) - clk_pll_set_fsm_mode(pll, regmap, 0); + qcom_pll_set_fsm_mode(regmap, pll->mode_reg, 1, 0); } EXPORT_SYMBOL_GPL(clk_pll_configure_sr_hpm_lp); diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index b904c335cda4..1b3e8d265bdb 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -173,6 +173,7 @@ struct clk_rcg2 { #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr) extern const struct clk_ops clk_rcg2_ops; +extern const struct clk_ops clk_rcg2_floor_ops; extern const struct clk_ops clk_rcg2_shared_ops; extern const struct clk_ops clk_edp_pixel_ops; extern const struct clk_ops clk_byte_ops; diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index a071bba8018c..1a0985ae20d2 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -47,6 +47,11 @@ #define N_REG 0xc #define D_REG 0x10 +enum freq_policy { + FLOOR, + CEIL, +}; + static int clk_rcg2_is_enabled(struct clk_hw *hw) { struct clk_rcg2 *rcg = to_clk_rcg2(hw); @@ -176,15 +181,26 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) return calc_rate(parent_rate, m, n, mode, hid_div); } -static int _freq_tbl_determine_rate(struct clk_hw *hw, - const struct freq_tbl *f, struct clk_rate_request *req) +static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, + struct clk_rate_request *req, + enum freq_policy policy) { unsigned long clk_flags, rate = req->rate; struct clk_hw *p; struct clk_rcg2 *rcg = to_clk_rcg2(hw); int index; - f = qcom_find_freq(f, rate); + switch (policy) { + case FLOOR: + f = qcom_find_freq_floor(f, rate); + break; + case CEIL: + f = qcom_find_freq(f, rate); + break; + default: + return -EINVAL; + }; + if (!f) return -EINVAL; @@ -221,7 +237,15 @@ static int clk_rcg2_determine_rate(struct clk_hw *hw, { struct clk_rcg2 *rcg = to_clk_rcg2(hw); - return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req); + return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, CEIL); +} + +static int clk_rcg2_determine_floor_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + + return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR); } static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) @@ -265,12 +289,23 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) return update_config(rcg); } -static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) +static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, + enum freq_policy policy) { struct clk_rcg2 *rcg = to_clk_rcg2(hw); const struct freq_tbl *f; - f = qcom_find_freq(rcg->freq_tbl, rate); + switch (policy) { + case FLOOR: + f = qcom_find_freq_floor(rcg->freq_tbl, rate); + break; + case CEIL: + f = qcom_find_freq(rcg->freq_tbl, rate); + break; + default: + return -EINVAL; + }; + if (!f) return -EINVAL; @@ -280,13 +315,25 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - return __clk_rcg2_set_rate(hw, rate); + return __clk_rcg2_set_rate(hw, rate, CEIL); +} + +static int clk_rcg2_set_floor_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + return __clk_rcg2_set_rate(hw, rate, FLOOR); } static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index) { - return __clk_rcg2_set_rate(hw, rate); + return __clk_rcg2_set_rate(hw, rate, CEIL); +} + +static int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate, u8 index) +{ + return __clk_rcg2_set_rate(hw, rate, FLOOR); } const struct clk_ops clk_rcg2_ops = { @@ -300,6 +347,17 @@ const struct clk_ops clk_rcg2_ops = { }; EXPORT_SYMBOL_GPL(clk_rcg2_ops); +const struct clk_ops clk_rcg2_floor_ops = { + .is_enabled = clk_rcg2_is_enabled, + .get_parent = clk_rcg2_get_parent, + .set_parent = clk_rcg2_set_parent, + .recalc_rate = clk_rcg2_recalc_rate, + .determine_rate = clk_rcg2_determine_floor_rate, + .set_rate = clk_rcg2_set_floor_rate, + .set_rate_and_parent = clk_rcg2_set_floor_rate_and_parent, +}; +EXPORT_SYMBOL_GPL(clk_rcg2_floor_ops); + static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate) { struct clk_rcg2 *rcg = to_clk_rcg2(hw); @@ -323,7 +381,7 @@ static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate) pr_err("%s: RCG did not turn on\n", name); /* set clock rate */ - ret = __clk_rcg2_set_rate(hw, rate); + ret = __clk_rcg2_set_rate(hw, rate, CEIL); if (ret) return ret; diff --git a/drivers/clk/qcom/clk-rpm.c b/drivers/clk/qcom/clk-rpm.c new file mode 100644 index 000000000000..df3e5fe8442a --- /dev/null +++ b/drivers/clk/qcom/clk-rpm.c @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2016, Linaro Limited + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/mfd/qcom_rpm.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#include <dt-bindings/mfd/qcom-rpm.h> +#include <dt-bindings/clock/qcom,rpmcc.h> + +#define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63 +#define QCOM_RPM_SCALING_ENABLE_ID 0x2 + +#define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \ + static struct clk_rpm _platform##_##_active; \ + static struct clk_rpm _platform##_##_name = { \ + .rpm_clk_id = (r_id), \ + .peer = &_platform##_##_active, \ + .rate = INT_MAX, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpm_ops, \ + .name = #_name, \ + .parent_names = (const char *[]){ "pxo_board" }, \ + .num_parents = 1, \ + }, \ + }; \ + static struct clk_rpm _platform##_##_active = { \ + .rpm_clk_id = (r_id), \ + .peer = &_platform##_##_name, \ + .active_only = true, \ + .rate = INT_MAX, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpm_ops, \ + .name = #_active, \ + .parent_names = (const char *[]){ "pxo_board" }, \ + .num_parents = 1, \ + }, \ + } + +#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \ + static struct clk_rpm _platform##_##_active; \ + static struct clk_rpm _platform##_##_name = { \ + .rpm_clk_id = (r_id), \ + .active_only = true, \ + .peer = &_platform##_##_active, \ + .rate = (r), \ + .branch = true, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpm_branch_ops, \ + .name = #_name, \ + .parent_names = (const char *[]){ "pxo_board" }, \ + .num_parents = 1, \ + }, \ + }; \ + static struct clk_rpm _platform##_##_active = { \ + .rpm_clk_id = (r_id), \ + .peer = &_platform##_##_name, \ + .rate = (r), \ + .branch = true, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpm_branch_ops, \ + .name = #_active, \ + .parent_names = (const char *[]){ "pxo_board" }, \ + .num_parents = 1, \ + }, \ + } + +#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r) \ + static struct clk_rpm _platform##_##_active; \ + static struct clk_rpm _platform##_##_name = { \ + .rpm_clk_id = (r_id), \ + .peer = &_platform##_##_active, \ + .rate = (r), \ + .branch = true, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpm_branch_ops, \ + .name = #_name, \ + .parent_names = (const char *[]){ "cxo_board" }, \ + .num_parents = 1, \ + }, \ + }; \ + static struct clk_rpm _platform##_##_active = { \ + .rpm_clk_id = (r_id), \ + .active_only = true, \ + .peer = &_platform##_##_name, \ + .rate = (r), \ + .branch = true, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpm_branch_ops, \ + .name = #_active, \ + .parent_names = (const char *[]){ "cxo_board" }, \ + .num_parents = 1, \ + }, \ + } + +#define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw) + +struct clk_rpm { + const int rpm_clk_id; + const bool active_only; + unsigned long rate; + bool enabled; + bool branch; + struct clk_rpm *peer; + struct clk_hw hw; + struct qcom_rpm *rpm; +}; + +struct rpm_cc { + struct qcom_rpm *rpm; + struct clk_rpm **clks; + size_t num_clks; +}; + +struct rpm_clk_desc { + struct clk_rpm **clks; + size_t num_clks; +}; + +static DEFINE_MUTEX(rpm_clk_lock); + +static int clk_rpm_handoff(struct clk_rpm *r) +{ + int ret; + u32 value = INT_MAX; + + ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, + r->rpm_clk_id, &value, 1); + if (ret) + return ret; + ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, + r->rpm_clk_id, &value, 1); + if (ret) + return ret; + + return 0; +} + +static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate) +{ + u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ + + return qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, + r->rpm_clk_id, &value, 1); +} + +static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate) +{ + u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ + + return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, + r->rpm_clk_id, &value, 1); +} + +static void to_active_sleep(struct clk_rpm *r, unsigned long rate, + unsigned long *active, unsigned long *sleep) +{ + *active = rate; + + /* + * Active-only clocks don't care what the rate is during sleep. So, + * they vote for zero. + */ + if (r->active_only) + *sleep = 0; + else + *sleep = *active; +} + +static int clk_rpm_prepare(struct clk_hw *hw) +{ + struct clk_rpm *r = to_clk_rpm(hw); + struct clk_rpm *peer = r->peer; + unsigned long this_rate = 0, this_sleep_rate = 0; + unsigned long peer_rate = 0, peer_sleep_rate = 0; + unsigned long active_rate, sleep_rate; + int ret = 0; + + mutex_lock(&rpm_clk_lock); + + /* Don't send requests to the RPM if the rate has not been set. */ + if (!r->rate) + goto out; + + to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); + + /* Take peer clock's rate into account only if it's enabled. */ + if (peer->enabled) + to_active_sleep(peer, peer->rate, + &peer_rate, &peer_sleep_rate); + + active_rate = max(this_rate, peer_rate); + + if (r->branch) + active_rate = !!active_rate; + + ret = clk_rpm_set_rate_active(r, active_rate); + if (ret) + goto out; + + sleep_rate = max(this_sleep_rate, peer_sleep_rate); + if (r->branch) + sleep_rate = !!sleep_rate; + + ret = clk_rpm_set_rate_sleep(r, sleep_rate); + if (ret) + /* Undo the active set vote and restore it */ + ret = clk_rpm_set_rate_active(r, peer_rate); + +out: + if (!ret) + r->enabled = true; + + mutex_unlock(&rpm_clk_lock); + + return ret; +} + +static void clk_rpm_unprepare(struct clk_hw *hw) +{ + struct clk_rpm *r = to_clk_rpm(hw); + struct clk_rpm *peer = r->peer; + unsigned long peer_rate = 0, peer_sleep_rate = 0; + unsigned long active_rate, sleep_rate; + int ret; + + mutex_lock(&rpm_clk_lock); + + if (!r->rate) + goto out; + + /* Take peer clock's rate into account only if it's enabled. */ + if (peer->enabled) + to_active_sleep(peer, peer->rate, &peer_rate, + &peer_sleep_rate); + + active_rate = r->branch ? !!peer_rate : peer_rate; + ret = clk_rpm_set_rate_active(r, active_rate); + if (ret) + goto out; + + sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; + ret = clk_rpm_set_rate_sleep(r, sleep_rate); + if (ret) + goto out; + + r->enabled = false; + +out: + mutex_unlock(&rpm_clk_lock); +} + +static int clk_rpm_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct clk_rpm *r = to_clk_rpm(hw); + struct clk_rpm *peer = r->peer; + unsigned long active_rate, sleep_rate; + unsigned long this_rate = 0, this_sleep_rate = 0; + unsigned long peer_rate = 0, peer_sleep_rate = 0; + int ret = 0; + + mutex_lock(&rpm_clk_lock); + + if (!r->enabled) + goto out; + + to_active_sleep(r, rate, &this_rate, &this_sleep_rate); + + /* Take peer clock's rate into account only if it's enabled. */ + if (peer->enabled) + to_active_sleep(peer, peer->rate, + &peer_rate, &peer_sleep_rate); + + active_rate = max(this_rate, peer_rate); + ret = clk_rpm_set_rate_active(r, active_rate); + if (ret) + goto out; + + sleep_rate = max(this_sleep_rate, peer_sleep_rate); + ret = clk_rpm_set_rate_sleep(r, sleep_rate); + if (ret) + goto out; + + r->rate = rate; + +out: + mutex_unlock(&rpm_clk_lock); + + return ret; +} + +static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + /* + * RPM handles rate rounding and we don't have a way to + * know what the rate will be, so just return whatever + * rate is requested. + */ + return rate; +} + +static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_rpm *r = to_clk_rpm(hw); + + /* + * RPM handles rate rounding and we don't have a way to + * know what the rate will be, so just return whatever + * rate was set. + */ + return r->rate; +} + +static const struct clk_ops clk_rpm_ops = { + .prepare = clk_rpm_prepare, + .unprepare = clk_rpm_unprepare, + .set_rate = clk_rpm_set_rate, + .round_rate = clk_rpm_round_rate, + .recalc_rate = clk_rpm_recalc_rate, +}; + +static const struct clk_ops clk_rpm_branch_ops = { + .prepare = clk_rpm_prepare, + .unprepare = clk_rpm_unprepare, + .round_rate = clk_rpm_round_rate, + .recalc_rate = clk_rpm_recalc_rate, +}; + +/* apq8064 */ +DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); +DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); +DEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); +DEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); +DEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK); +DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK); +DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); +DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); +DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); + +static struct clk_rpm *apq8064_clks[] = { + [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, + [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, + [RPM_CFPB_CLK] = &apq8064_cfpb_clk, + [RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk, + [RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk, + [RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk, + [RPM_EBI1_CLK] = &apq8064_ebi1_clk, + [RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk, + [RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk, + [RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk, + [RPM_MMFPB_CLK] = &apq8064_mmfpb_clk, + [RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk, + [RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk, + [RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk, + [RPM_SFPB_CLK] = &apq8064_sfpb_clk, + [RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk, + [RPM_QDSS_CLK] = &apq8064_qdss_clk, + [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, +}; + +static const struct rpm_clk_desc rpm_clk_apq8064 = { + .clks = apq8064_clks, + .num_clks = ARRAY_SIZE(apq8064_clks), +}; + +static const struct of_device_id rpm_clk_match_table[] = { + { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, + { } +}; +MODULE_DEVICE_TABLE(of, rpm_clk_match_table); + +static struct clk_hw *qcom_rpm_clk_hw_get(struct of_phandle_args *clkspec, + void *data) +{ + struct rpm_cc *rcc = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= rcc->num_clks) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return rcc->clks[idx] ? &rcc->clks[idx]->hw : ERR_PTR(-ENOENT); +} + +static int rpm_clk_probe(struct platform_device *pdev) +{ + struct rpm_cc *rcc; + int ret; + size_t num_clks, i; + struct qcom_rpm *rpm; + struct clk_rpm **rpm_clks; + const struct rpm_clk_desc *desc; + + rpm = dev_get_drvdata(pdev->dev.parent); + if (!rpm) { + dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); + return -ENODEV; + } + + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + + rpm_clks = desc->clks; + num_clks = desc->num_clks; + + rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc), GFP_KERNEL); + if (!rcc) + return -ENOMEM; + + rcc->clks = rpm_clks; + rcc->num_clks = num_clks; + + for (i = 0; i < num_clks; i++) { + if (!rpm_clks[i]) + continue; + + rpm_clks[i]->rpm = rpm; + + ret = clk_rpm_handoff(rpm_clks[i]); + if (ret) + goto err; + } + + for (i = 0; i < num_clks; i++) { + if (!rpm_clks[i]) + continue; + + ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw); + if (ret) + goto err; + } + + ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_rpm_clk_hw_get, + rcc); + if (ret) + goto err; + + return 0; +err: + dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret); + return ret; +} + +static int rpm_clk_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + return 0; +} + +static struct platform_driver rpm_clk_driver = { + .driver = { + .name = "qcom-clk-rpm", + .of_match_table = rpm_clk_match_table, + }, + .probe = rpm_clk_probe, + .remove = rpm_clk_remove, +}; + +static int __init rpm_clk_init(void) +{ + return platform_driver_register(&rpm_clk_driver); +} +core_initcall(rpm_clk_init); + +static void __exit rpm_clk_exit(void) +{ + platform_driver_unregister(&rpm_clk_driver); +} +module_exit(rpm_clk_exit); + +MODULE_DESCRIPTION("Qualcomm RPM Clock Controller Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:qcom-clk-rpm"); diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c new file mode 100644 index 000000000000..07e2cc6ed781 --- /dev/null +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -0,0 +1,578 @@ +/* + * Copyright (c) 2016, Linaro Limited + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/soc/qcom/smd-rpm.h> + +#include <dt-bindings/clock/qcom,rpmcc.h> +#include <dt-bindings/mfd/qcom-rpm.h> + +#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773 +#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370 +#define QCOM_RPM_SMD_KEY_RATE 0x007a484b +#define QCOM_RPM_SMD_KEY_ENABLE 0x62616e45 +#define QCOM_RPM_SMD_KEY_STATE 0x54415453 +#define QCOM_RPM_SCALING_ENABLE_ID 0x2 + +#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \ + key) \ + static struct clk_smd_rpm _platform##_##_active; \ + static struct clk_smd_rpm _platform##_##_name = { \ + .rpm_res_type = (type), \ + .rpm_clk_id = (r_id), \ + .rpm_status_id = (stat_id), \ + .rpm_key = (key), \ + .peer = &_platform##_##_active, \ + .rate = INT_MAX, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_smd_rpm_ops, \ + .name = #_name, \ + .parent_names = (const char *[]){ "xo_board" }, \ + .num_parents = 1, \ + }, \ + }; \ + static struct clk_smd_rpm _platform##_##_active = { \ + .rpm_res_type = (type), \ + .rpm_clk_id = (r_id), \ + .rpm_status_id = (stat_id), \ + .active_only = true, \ + .rpm_key = (key), \ + .peer = &_platform##_##_name, \ + .rate = INT_MAX, \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_smd_rpm_ops, \ + .name = #_active, \ + .parent_names = (const char *[]){ "xo_board" }, \ + .num_parents = 1, \ + }, \ + } + +#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \ + stat_id, r, key) \ + static struct clk_smd_rpm _platform##_##_active; \ + static struct clk_smd_rpm _platform##_##_name = { \ + .rpm_res_type = (type), \ + .rpm_clk_id = (r_id), \ + .rpm_status_id = (stat_id), \ + .rpm_key = (key), \ + .branch = true, \ + .peer = &_platform##_##_active, \ + .rate = (r), \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_smd_rpm_branch_ops, \ + .name = #_name, \ + .parent_names = (const char *[]){ "xo_board" }, \ + .num_parents = 1, \ + }, \ + }; \ + static struct clk_smd_rpm _platform##_##_active = { \ + .rpm_res_type = (type), \ + .rpm_clk_id = (r_id), \ + .rpm_status_id = (stat_id), \ + .active_only = true, \ + .rpm_key = (key), \ + .branch = true, \ + .peer = &_platform##_##_name, \ + .rate = (r), \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_smd_rpm_branch_ops, \ + .name = #_active, \ + .parent_names = (const char *[]){ "xo_board" }, \ + .num_parents = 1, \ + }, \ + } + +#define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id) \ + __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ + 0, QCOM_RPM_SMD_KEY_RATE) + +#define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r) \ + __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, \ + r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE) + +#define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id) \ + __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ + 0, QCOM_RPM_SMD_KEY_STATE) + +#define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id) \ + __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ + QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ + QCOM_RPM_KEY_SOFTWARE_ENABLE) + +#define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \ + __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ + QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ + QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY) + +#define to_clk_smd_rpm(_hw) container_of(_hw, struct clk_smd_rpm, hw) + +struct clk_smd_rpm { + const int rpm_res_type; + const int rpm_key; + const int rpm_clk_id; + const int rpm_status_id; + const bool active_only; + bool enabled; + bool branch; + struct clk_smd_rpm *peer; + struct clk_hw hw; + unsigned long rate; + struct qcom_smd_rpm *rpm; +}; + +struct clk_smd_rpm_req { + __le32 key; + __le32 nbytes; + __le32 value; +}; + +struct rpm_cc { + struct qcom_rpm *rpm; + struct clk_smd_rpm **clks; + size_t num_clks; +}; + +struct rpm_smd_clk_desc { + struct clk_smd_rpm **clks; + size_t num_clks; +}; + +static DEFINE_MUTEX(rpm_smd_clk_lock); + +static int clk_smd_rpm_handoff(struct clk_smd_rpm *r) +{ + int ret; + struct clk_smd_rpm_req req = { + .key = cpu_to_le32(r->rpm_key), + .nbytes = cpu_to_le32(sizeof(u32)), + .value = cpu_to_le32(INT_MAX), + }; + + ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE, + r->rpm_res_type, r->rpm_clk_id, &req, + sizeof(req)); + if (ret) + return ret; + ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE, + r->rpm_res_type, r->rpm_clk_id, &req, + sizeof(req)); + if (ret) + return ret; + + return 0; +} + +static int clk_smd_rpm_set_rate_active(struct clk_smd_rpm *r, + unsigned long rate) +{ + struct clk_smd_rpm_req req = { + .key = cpu_to_le32(r->rpm_key), + .nbytes = cpu_to_le32(sizeof(u32)), + .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */ + }; + + return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE, + r->rpm_res_type, r->rpm_clk_id, &req, + sizeof(req)); +} + +static int clk_smd_rpm_set_rate_sleep(struct clk_smd_rpm *r, + unsigned long rate) +{ + struct clk_smd_rpm_req req = { + .key = cpu_to_le32(r->rpm_key), + .nbytes = cpu_to_le32(sizeof(u32)), + .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */ + }; + + return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE, + r->rpm_res_type, r->rpm_clk_id, &req, + sizeof(req)); +} + +static void to_active_sleep(struct clk_smd_rpm *r, unsigned long rate, + unsigned long *active, unsigned long *sleep) +{ + *active = rate; + + /* + * Active-only clocks don't care what the rate is during sleep. So, + * they vote for zero. + */ + if (r->active_only) + *sleep = 0; + else + *sleep = *active; +} + +static int clk_smd_rpm_prepare(struct clk_hw *hw) +{ + struct clk_smd_rpm *r = to_clk_smd_rpm(hw); + struct clk_smd_rpm *peer = r->peer; + unsigned long this_rate = 0, this_sleep_rate = 0; + unsigned long peer_rate = 0, peer_sleep_rate = 0; + unsigned long active_rate, sleep_rate; + int ret = 0; + + mutex_lock(&rpm_smd_clk_lock); + + /* Don't send requests to the RPM if the rate has not been set. */ + if (!r->rate) + goto out; + + to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); + + /* Take peer clock's rate into account only if it's enabled. */ + if (peer->enabled) + to_active_sleep(peer, peer->rate, + &peer_rate, &peer_sleep_rate); + + active_rate = max(this_rate, peer_rate); + + if (r->branch) + active_rate = !!active_rate; + + ret = clk_smd_rpm_set_rate_active(r, active_rate); + if (ret) + goto out; + + sleep_rate = max(this_sleep_rate, peer_sleep_rate); + if (r->branch) + sleep_rate = !!sleep_rate; + + ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); + if (ret) + /* Undo the active set vote and restore it */ + ret = clk_smd_rpm_set_rate_active(r, peer_rate); + +out: + if (!ret) + r->enabled = true; + + mutex_unlock(&rpm_smd_clk_lock); + + return ret; +} + +static void clk_smd_rpm_unprepare(struct clk_hw *hw) +{ + struct clk_smd_rpm *r = to_clk_smd_rpm(hw); + struct clk_smd_rpm *peer = r->peer; + unsigned long peer_rate = 0, peer_sleep_rate = 0; + unsigned long active_rate, sleep_rate; + int ret; + + mutex_lock(&rpm_smd_clk_lock); + + if (!r->rate) + goto out; + + /* Take peer clock's rate into account only if it's enabled. */ + if (peer->enabled) + to_active_sleep(peer, peer->rate, &peer_rate, + &peer_sleep_rate); + + active_rate = r->branch ? !!peer_rate : peer_rate; + ret = clk_smd_rpm_set_rate_active(r, active_rate); + if (ret) + goto out; + + sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; + ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); + if (ret) + goto out; + + r->enabled = false; + +out: + mutex_unlock(&rpm_smd_clk_lock); +} + +static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_smd_rpm *r = to_clk_smd_rpm(hw); + struct clk_smd_rpm *peer = r->peer; + unsigned long active_rate, sleep_rate; + unsigned long this_rate = 0, this_sleep_rate = 0; + unsigned long peer_rate = 0, peer_sleep_rate = 0; + int ret = 0; + + mutex_lock(&rpm_smd_clk_lock); + + if (!r->enabled) + goto out; + + to_active_sleep(r, rate, &this_rate, &this_sleep_rate); + + /* Take peer clock's rate into account only if it's enabled. */ + if (peer->enabled) + to_active_sleep(peer, peer->rate, + &peer_rate, &peer_sleep_rate); + + active_rate = max(this_rate, peer_rate); + ret = clk_smd_rpm_set_rate_active(r, active_rate); + if (ret) + goto out; + + sleep_rate = max(this_sleep_rate, peer_sleep_rate); + ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); + if (ret) + goto out; + + r->rate = rate; + +out: + mutex_unlock(&rpm_smd_clk_lock); + + return ret; +} + +static long clk_smd_rpm_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + /* + * RPM handles rate rounding and we don't have a way to + * know what the rate will be, so just return whatever + * rate is requested. + */ + return rate; +} + +static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_smd_rpm *r = to_clk_smd_rpm(hw); + + /* + * RPM handles rate rounding and we don't have a way to + * know what the rate will be, so just return whatever + * rate was set. + */ + return r->rate; +} + +static int clk_smd_rpm_enable_scaling(struct qcom_smd_rpm *rpm) +{ + int ret; + struct clk_smd_rpm_req req = { + .key = cpu_to_le32(QCOM_RPM_SMD_KEY_ENABLE), + .nbytes = cpu_to_le32(sizeof(u32)), + .value = cpu_to_le32(1), + }; + + ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_SLEEP_STATE, + QCOM_SMD_RPM_MISC_CLK, + QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req)); + if (ret) { + pr_err("RPM clock scaling (sleep set) not enabled!\n"); + return ret; + } + + ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_ACTIVE_STATE, + QCOM_SMD_RPM_MISC_CLK, + QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req)); + if (ret) { + pr_err("RPM clock scaling (active set) not enabled!\n"); + return ret; + } + + pr_debug("%s: RPM clock scaling is enabled\n", __func__); + return 0; +} + +static const struct clk_ops clk_smd_rpm_ops = { + .prepare = clk_smd_rpm_prepare, + .unprepare = clk_smd_rpm_unprepare, + .set_rate = clk_smd_rpm_set_rate, + .round_rate = clk_smd_rpm_round_rate, + .recalc_rate = clk_smd_rpm_recalc_rate, +}; + +static const struct clk_ops clk_smd_rpm_branch_ops = { + .prepare = clk_smd_rpm_prepare, + .unprepare = clk_smd_rpm_unprepare, + .round_rate = clk_smd_rpm_round_rate, + .recalc_rate = clk_smd_rpm_recalc_rate, +}; + +/* msm8916 */ +DEFINE_CLK_SMD_RPM(msm8916, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8916, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8916, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); +DEFINE_CLK_SMD_RPM_QDSS(msm8916, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk1, bb_clk1_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk2, bb_clk2_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk1, rf_clk1_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk2, rf_clk2_a, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk1_pin, bb_clk1_a_pin, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk2_pin, bb_clk2_a_pin, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk1_pin, rf_clk1_a_pin, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk2_pin, rf_clk2_a_pin, 5); + +static struct clk_smd_rpm *msm8916_clks[] = { + [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk, + [RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk, + [RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8916_snoc_a_clk, + [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &msm8916_bimc_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8916_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8916_qdss_a_clk, + [RPM_SMD_BB_CLK1] = &msm8916_bb_clk1, + [RPM_SMD_BB_CLK1_A] = &msm8916_bb_clk1_a, + [RPM_SMD_BB_CLK2] = &msm8916_bb_clk2, + [RPM_SMD_BB_CLK2_A] = &msm8916_bb_clk2_a, + [RPM_SMD_RF_CLK1] = &msm8916_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &msm8916_rf_clk1_a, + [RPM_SMD_RF_CLK2] = &msm8916_rf_clk2, + [RPM_SMD_RF_CLK2_A] = &msm8916_rf_clk2_a, + [RPM_SMD_BB_CLK1_PIN] = &msm8916_bb_clk1_pin, + [RPM_SMD_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin, + [RPM_SMD_BB_CLK2_PIN] = &msm8916_bb_clk2_pin, + [RPM_SMD_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin, + [RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin, + [RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin, + [RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin, + [RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msm8916 = { + .clks = msm8916_clks, + .num_clks = ARRAY_SIZE(msm8916_clks), +}; + +static const struct of_device_id rpm_smd_clk_match_table[] = { + { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, + { } +}; +MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); + +static struct clk_hw *qcom_smdrpm_clk_hw_get(struct of_phandle_args *clkspec, + void *data) +{ + struct rpm_cc *rcc = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= rcc->num_clks) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return rcc->clks[idx] ? &rcc->clks[idx]->hw : ERR_PTR(-ENOENT); +} + +static int rpm_smd_clk_probe(struct platform_device *pdev) +{ + struct rpm_cc *rcc; + int ret; + size_t num_clks, i; + struct qcom_smd_rpm *rpm; + struct clk_smd_rpm **rpm_smd_clks; + const struct rpm_smd_clk_desc *desc; + + rpm = dev_get_drvdata(pdev->dev.parent); + if (!rpm) { + dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); + return -ENODEV; + } + + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + + rpm_smd_clks = desc->clks; + num_clks = desc->num_clks; + + rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc), GFP_KERNEL); + if (!rcc) + return -ENOMEM; + + rcc->clks = rpm_smd_clks; + rcc->num_clks = num_clks; + + for (i = 0; i < num_clks; i++) { + if (!rpm_smd_clks[i]) + continue; + + rpm_smd_clks[i]->rpm = rpm; + + ret = clk_smd_rpm_handoff(rpm_smd_clks[i]); + if (ret) + goto err; + } + + ret = clk_smd_rpm_enable_scaling(rpm); + if (ret) + goto err; + + for (i = 0; i < num_clks; i++) { + if (!rpm_smd_clks[i]) + continue; + + ret = devm_clk_hw_register(&pdev->dev, &rpm_smd_clks[i]->hw); + if (ret) + goto err; + } + + ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_smdrpm_clk_hw_get, + rcc); + if (ret) + goto err; + + return 0; +err: + dev_err(&pdev->dev, "Error registering SMD clock driver (%d)\n", ret); + return ret; +} + +static int rpm_smd_clk_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + return 0; +} + +static struct platform_driver rpm_smd_clk_driver = { + .driver = { + .name = "qcom-clk-smd-rpm", + .of_match_table = rpm_smd_clk_match_table, + }, + .probe = rpm_smd_clk_probe, + .remove = rpm_smd_clk_remove, +}; + +static int __init rpm_smd_clk_init(void) +{ + return platform_driver_register(&rpm_smd_clk_driver); +} +core_initcall(rpm_smd_clk_init); + +static void __exit rpm_smd_clk_exit(void) +{ + platform_driver_unregister(&rpm_smd_clk_driver); +} +module_exit(rpm_smd_clk_exit); + +MODULE_DESCRIPTION("Qualcomm RPM over SMD Clock Controller Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:qcom-clk-smd-rpm"); diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index fffcbaf0fba7..cfab7b400381 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -46,6 +46,22 @@ struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate) } EXPORT_SYMBOL_GPL(qcom_find_freq); +const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f, + unsigned long rate) +{ + const struct freq_tbl *best = NULL; + + for ( ; f->freq; f++) { + if (rate >= f->freq) + best = f; + else + break; + } + + return best; +} +EXPORT_SYMBOL_GPL(qcom_find_freq_floor); + int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src) { int i, num_parents = clk_hw_get_num_parents(hw); @@ -74,6 +90,27 @@ qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc) } EXPORT_SYMBOL_GPL(qcom_cc_map); +void +qcom_pll_set_fsm_mode(struct regmap *map, u32 reg, u8 bias_count, u8 lock_count) +{ + u32 val; + u32 mask; + + /* De-assert reset to FSM */ + regmap_update_bits(map, reg, PLL_VOTE_FSM_RESET, 0); + + /* Program bias count and lock count */ + val = bias_count << PLL_BIAS_COUNT_SHIFT | + lock_count << PLL_LOCK_COUNT_SHIFT; + mask = PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT; + mask |= PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT; + regmap_update_bits(map, reg, mask, val); + + /* Enable PLL FSM voting */ + regmap_update_bits(map, reg, PLL_VOTE_FSM_ENA, PLL_VOTE_FSM_ENA); +} +EXPORT_SYMBOL_GPL(qcom_pll_set_fsm_mode); + static void qcom_cc_del_clk_provider(void *data) { of_clk_del_provider(data); @@ -153,15 +190,12 @@ int qcom_cc_register_board_clk(struct device *dev, const char *path, const char *name, unsigned long rate) { bool add_factor = true; - struct device_node *node; - - /* The RPM clock driver will add the factor clock if present */ - if (IS_ENABLED(CONFIG_QCOM_RPMCC)) { - node = of_find_compatible_node(NULL, NULL, "qcom,rpmcc"); - if (of_device_is_available(node)) - add_factor = false; - of_node_put(node); - } + + /* + * TODO: The RPM clock driver currently does not support the xo clock. + * When xo is added to the RPM clock driver, we should change this + * function to skip registration of xo factor clocks. + */ return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor); } diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index ae9bdeb21f29..23c1927669ba 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -22,6 +22,13 @@ struct freq_tbl; struct clk_hw; struct parent_map; +#define PLL_LOCK_COUNT_SHIFT 8 +#define PLL_LOCK_COUNT_MASK 0x3f +#define PLL_BIAS_COUNT_SHIFT 14 +#define PLL_BIAS_COUNT_MASK 0x3f +#define PLL_VOTE_FSM_ENA BIT(20) +#define PLL_VOTE_FSM_RESET BIT(21) + struct qcom_cc_desc { const struct regmap_config *config; struct clk_regmap **clks; @@ -34,6 +41,10 @@ struct qcom_cc_desc { extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate); +extern const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f, + unsigned long rate); +extern void +qcom_pll_set_fsm_mode(struct regmap *m, u32 reg, u8 bias_count, u8 lock_count); extern int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src); diff --git a/drivers/clk/qcom/gcc-apq8084.c b/drivers/clk/qcom/gcc-apq8084.c index 070037a29ea5..486d9610355c 100644 --- a/drivers/clk/qcom/gcc-apq8084.c +++ b/drivers/clk/qcom/gcc-apq8084.c @@ -1142,7 +1142,7 @@ static struct clk_rcg2 sdcc1_apps_clk_src = { .name = "sdcc1_apps_clk_src", .parent_names = gcc_xo_gpll0_gpll4, .num_parents = 3, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; @@ -1156,7 +1156,7 @@ static struct clk_rcg2 sdcc2_apps_clk_src = { .name = "sdcc2_apps_clk_src", .parent_names = gcc_xo_gpll0, .num_parents = 2, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; @@ -1170,7 +1170,7 @@ static struct clk_rcg2 sdcc3_apps_clk_src = { .name = "sdcc3_apps_clk_src", .parent_names = gcc_xo_gpll0, .num_parents = 2, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; @@ -1184,7 +1184,7 @@ static struct clk_rcg2 sdcc4_apps_clk_src = { .name = "sdcc4_apps_clk_src", .parent_names = gcc_xo_gpll0, .num_parents = 2, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; diff --git a/drivers/clk/qcom/gcc-ipq4019.c b/drivers/clk/qcom/gcc-ipq4019.c index b593065de8db..33d09138f5e5 100644 --- a/drivers/clk/qcom/gcc-ipq4019.c +++ b/drivers/clk/qcom/gcc-ipq4019.c @@ -185,8 +185,7 @@ static struct clk_branch gcc_audio_pwm_clk = { }; static const struct freq_tbl ftbl_gcc_blsp1_qup1_2_i2c_apps_clk[] = { - F(19200000, P_XO, 1, 2, 5), - F(24000000, P_XO, 1, 1, 2), + F(19050000, P_FEPLL200, 10.5, 1, 1), { } }; diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c index 52a7d3959875..28eb200d0f1e 100644 --- a/drivers/clk/qcom/gcc-ipq806x.c +++ b/drivers/clk/qcom/gcc-ipq806x.c @@ -2990,11 +2990,11 @@ static int gcc_ipq806x_probe(struct platform_device *pdev) struct regmap *regmap; int ret; - ret = qcom_cc_register_board_clk(dev, "cxo_board", "cxo", 19200000); + ret = qcom_cc_register_board_clk(dev, "cxo_board", "cxo", 25000000); if (ret) return ret; - ret = qcom_cc_register_board_clk(dev, "pxo_board", "pxo", 27000000); + ret = qcom_cc_register_board_clk(dev, "pxo_board", "pxo", 25000000); if (ret) return ret; diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c index 5c4e193164d4..628e6ca276ec 100644 --- a/drivers/clk/qcom/gcc-msm8916.c +++ b/drivers/clk/qcom/gcc-msm8916.c @@ -1107,7 +1107,7 @@ static struct clk_rcg2 sdcc1_apps_clk_src = { .name = "sdcc1_apps_clk_src", .parent_names = gcc_xo_gpll0, .num_parents = 2, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; @@ -1132,7 +1132,7 @@ static struct clk_rcg2 sdcc2_apps_clk_src = { .name = "sdcc2_apps_clk_src", .parent_names = gcc_xo_gpll0, .num_parents = 2, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c index 00915209e7c5..348e30da4f18 100644 --- a/drivers/clk/qcom/gcc-msm8974.c +++ b/drivers/clk/qcom/gcc-msm8974.c @@ -872,7 +872,7 @@ static struct clk_init_data sdcc1_apps_clk_src_init = { .name = "sdcc1_apps_clk_src", .parent_names = gcc_xo_gpll0, .num_parents = 2, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }; static struct clk_rcg2 sdcc1_apps_clk_src = { @@ -894,7 +894,7 @@ static struct clk_rcg2 sdcc2_apps_clk_src = { .name = "sdcc2_apps_clk_src", .parent_names = gcc_xo_gpll0, .num_parents = 2, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; @@ -908,7 +908,7 @@ static struct clk_rcg2 sdcc3_apps_clk_src = { .name = "sdcc3_apps_clk_src", .parent_names = gcc_xo_gpll0, .num_parents = 2, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; @@ -922,7 +922,7 @@ static struct clk_rcg2 sdcc4_apps_clk_src = { .name = "sdcc4_apps_clk_src", .parent_names = gcc_xo_gpll0, .num_parents = 2, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; diff --git a/drivers/clk/qcom/gcc-msm8994.c b/drivers/clk/qcom/gcc-msm8994.c new file mode 100644 index 000000000000..8afd8304a070 --- /dev/null +++ b/drivers/clk/qcom/gcc-msm8994.c @@ -0,0 +1,2300 @@ +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/ctype.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,gcc-msm8994.h> + +#include "common.h" +#include "clk-regmap.h" +#include "clk-alpha-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "reset.h" + +enum { + P_XO, + P_GPLL0, + P_GPLL4, +}; + +static const struct parent_map gcc_xo_gpll0_map[] = { + { P_XO, 0 }, + { P_GPLL0, 1 }, +}; + +static const char * const gcc_xo_gpll0[] = { + "xo", + "gpll0", +}; + +static const struct parent_map gcc_xo_gpll0_gpll4_map[] = { + { P_XO, 0 }, + { P_GPLL0, 1 }, + { P_GPLL4, 5 }, +}; + +static const char * const gcc_xo_gpll0_gpll4[] = { + "xo", + "gpll0", + "gpll4", +}; + +#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } + +static struct clk_fixed_factor xo = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data) + { + .name = "xo", + .parent_names = (const char *[]) { "xo_board" }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + }, +}; + +static struct clk_alpha_pll gpll0_early = { + .offset = 0x00000, + .clkr = { + .enable_reg = 0x1480, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gpll0_early", + .parent_names = (const char *[]) { "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +static struct clk_alpha_pll_postdiv gpll0 = { + .offset = 0x00000, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "gpll0", + .parent_names = (const char *[]) { "gpll0_early" }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ops, + }, +}; + +static struct clk_alpha_pll gpll4_early = { + .offset = 0x1dc0, + .clkr = { + .enable_reg = 0x1480, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data) + { + .name = "gpll4_early", + .parent_names = (const char *[]) { "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +static struct clk_alpha_pll_postdiv gpll4 = { + .offset = 0x1dc0, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "gpll4", + .parent_names = (const char *[]) { "gpll4_early" }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ops, + }, +}; + +static struct freq_tbl ftbl_ufs_axi_clk_src[] = { + F(50000000, P_GPLL0, 12, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(150000000, P_GPLL0, 4, 0, 0), + F(171430000, P_GPLL0, 3.5, 0, 0), + F(200000000, P_GPLL0, 3, 0, 0), + F(240000000, P_GPLL0, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 ufs_axi_clk_src = { + .cmd_rcgr = 0x1d68, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_ufs_axi_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "ufs_axi_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_usb30_master_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(125000000, P_GPLL0, 1, 5, 24), + { } +}; + +static struct clk_rcg2 usb30_master_clk_src = { + .cmd_rcgr = 0x03d4, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_usb30_master_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "usb30_master_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_blsp_i2c_apps_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(50000000, P_GPLL0, 12, 0, 0), + { } +}; + +static struct clk_rcg2 blsp1_qup1_i2c_apps_clk_src = { + .cmd_rcgr = 0x0660, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_qup1_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_blspqup_spi_apps_clk_src[] = { + F(960000, P_XO, 10, 1, 2), + F(4800000, P_XO, 4, 0, 0), + F(9600000, P_XO, 2, 0, 0), + F(15000000, P_GPLL0, 10, 1, 4), + F(19200000, P_XO, 1, 0, 0), + F(24000000, P_GPLL0, 12.5, 1, 2), + F(25000000, P_GPLL0, 12, 1, 2), + F(48000000, P_GPLL0, 12.5, 0, 0), + F(50000000, P_GPLL0, 12, 0, 0), + { } +}; + +static struct clk_rcg2 blsp1_qup1_spi_apps_clk_src = { + .cmd_rcgr = 0x064c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blspqup_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_qup1_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup2_i2c_apps_clk_src = { + .cmd_rcgr = 0x06e0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_qup2_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup2_spi_apps_clk_src = { + .cmd_rcgr = 0x06cc, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blspqup_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_qup2_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup3_i2c_apps_clk_src = { + .cmd_rcgr = 0x0760, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_qup3_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup3_spi_apps_clk_src = { + .cmd_rcgr = 0x074c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blspqup_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_qup3_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup4_i2c_apps_clk_src = { + .cmd_rcgr = 0x07e0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_qup4_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup4_spi_apps_clk_src = { + .cmd_rcgr = 0x07cc, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blspqup_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_qup4_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup5_i2c_apps_clk_src = { + .cmd_rcgr = 0x0860, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_qup5_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup5_spi_apps_clk_src = { + .cmd_rcgr = 0x084c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blspqup_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_qup5_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup6_i2c_apps_clk_src = { + .cmd_rcgr = 0x08e0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_qup6_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup6_spi_apps_clk_src = { + .cmd_rcgr = 0x08cc, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blspqup_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_qup6_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_blsp_uart_apps_clk_src[] = { + F(3686400, P_GPLL0, 1, 96, 15625), + F(7372800, P_GPLL0, 1, 192, 15625), + F(14745600, P_GPLL0, 1, 384, 15625), + F(16000000, P_GPLL0, 5, 2, 15), + F(19200000, P_XO, 1, 0, 0), + F(24000000, P_GPLL0, 5, 1, 5), + F(32000000, P_GPLL0, 1, 4, 75), + F(40000000, P_GPLL0, 15, 0, 0), + F(46400000, P_GPLL0, 1, 29, 375), + F(48000000, P_GPLL0, 12.5, 0, 0), + F(51200000, P_GPLL0, 1, 32, 375), + F(56000000, P_GPLL0, 1, 7, 75), + F(58982400, P_GPLL0, 1, 1536, 15625), + F(60000000, P_GPLL0, 10, 0, 0), + F(63160000, P_GPLL0, 9.5, 0, 0), + { } +}; + +static struct clk_rcg2 blsp1_uart1_apps_clk_src = { + .cmd_rcgr = 0x068c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_uart_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_uart1_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_uart2_apps_clk_src = { + .cmd_rcgr = 0x070c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_uart_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_uart2_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_uart3_apps_clk_src = { + .cmd_rcgr = 0x078c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_uart_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_uart3_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_uart4_apps_clk_src = { + .cmd_rcgr = 0x080c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_uart_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_uart4_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_uart5_apps_clk_src = { + .cmd_rcgr = 0x088c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_uart_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_uart5_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_uart6_apps_clk_src = { + .cmd_rcgr = 0x090c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_uart_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp1_uart6_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup1_i2c_apps_clk_src = { + .cmd_rcgr = 0x09a0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_qup1_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup1_spi_apps_clk_src = { + .cmd_rcgr = 0x098c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blspqup_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_qup1_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup2_i2c_apps_clk_src = { + .cmd_rcgr = 0x0a20, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_qup2_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup2_spi_apps_clk_src = { + .cmd_rcgr = 0x0a0c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blspqup_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_qup2_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup3_i2c_apps_clk_src = { + .cmd_rcgr = 0x0aa0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_qup3_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup3_spi_apps_clk_src = { + .cmd_rcgr = 0x0a8c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blspqup_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_qup3_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup4_i2c_apps_clk_src = { + .cmd_rcgr = 0x0b20, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_qup4_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup4_spi_apps_clk_src = { + .cmd_rcgr = 0x0b0c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blspqup_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_qup4_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup5_i2c_apps_clk_src = { + .cmd_rcgr = 0x0ba0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_qup5_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup5_spi_apps_clk_src = { + .cmd_rcgr = 0x0b8c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blspqup_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_qup5_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup6_i2c_apps_clk_src = { + .cmd_rcgr = 0x0c20, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_i2c_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_qup6_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup6_spi_apps_clk_src = { + .cmd_rcgr = 0x0c0c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blspqup_spi_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_qup6_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_uart1_apps_clk_src = { + .cmd_rcgr = 0x09cc, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_uart_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_uart1_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_uart2_apps_clk_src = { + .cmd_rcgr = 0x0a4c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_uart_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_uart2_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_uart3_apps_clk_src = { + .cmd_rcgr = 0x0acc, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_uart_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_uart3_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_uart4_apps_clk_src = { + .cmd_rcgr = 0x0b4c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_uart_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_uart4_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_uart5_apps_clk_src = { + .cmd_rcgr = 0x0bcc, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_uart_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_uart5_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_uart6_apps_clk_src = { + .cmd_rcgr = 0x0c4c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_blsp_uart_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "blsp2_uart6_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_gp1_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(200000000, P_GPLL0, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gp1_clk_src = { + .cmd_rcgr = 0x1904, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gp1_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "gp1_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_gp2_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(200000000, P_GPLL0, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gp2_clk_src = { + .cmd_rcgr = 0x1944, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gp2_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "gp2_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_gp3_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(200000000, P_GPLL0, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gp3_clk_src = { + .cmd_rcgr = 0x1984, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gp3_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "gp3_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_pcie_0_aux_clk_src[] = { + F(1011000, P_XO, 1, 1, 19), + { } +}; + +static struct clk_rcg2 pcie_0_aux_clk_src = { + .cmd_rcgr = 0x1b00, + .mnd_width = 8, + .hid_width = 5, + .freq_tbl = ftbl_pcie_0_aux_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "pcie_0_aux_clk_src", + .parent_names = (const char *[]) { "xo" }, + .num_parents = 1, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_pcie_pipe_clk_src[] = { + F(125000000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 pcie_0_pipe_clk_src = { + .cmd_rcgr = 0x1adc, + .hid_width = 5, + .freq_tbl = ftbl_pcie_pipe_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "pcie_0_pipe_clk_src", + .parent_names = (const char *[]) { "xo" }, + .num_parents = 1, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_pcie_1_aux_clk_src[] = { + F(1011000, P_XO, 1, 1, 19), + { } +}; + +static struct clk_rcg2 pcie_1_aux_clk_src = { + .cmd_rcgr = 0x1b80, + .mnd_width = 8, + .hid_width = 5, + .freq_tbl = ftbl_pcie_1_aux_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "pcie_1_aux_clk_src", + .parent_names = (const char *[]) { "xo" }, + .num_parents = 1, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 pcie_1_pipe_clk_src = { + .cmd_rcgr = 0x1b5c, + .hid_width = 5, + .freq_tbl = ftbl_pcie_pipe_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "pcie_1_pipe_clk_src", + .parent_names = (const char *[]) { "xo" }, + .num_parents = 1, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_pdm2_clk_src[] = { + F(60000000, P_GPLL0, 10, 0, 0), + { } +}; + +static struct clk_rcg2 pdm2_clk_src = { + .cmd_rcgr = 0x0cd0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_pdm2_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "pdm2_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_sdcc1_apps_clk_src[] = { + F(144000, P_XO, 16, 3, 25), + F(400000, P_XO, 12, 1, 4), + F(20000000, P_GPLL0, 15, 1, 2), + F(25000000, P_GPLL0, 12, 1, 2), + F(50000000, P_GPLL0, 12, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(192000000, P_GPLL4, 2, 0, 0), + F(384000000, P_GPLL4, 1, 0, 0), + { } +}; + +static struct clk_rcg2 sdcc1_apps_clk_src = { + .cmd_rcgr = 0x04d0, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_gpll4_map, + .freq_tbl = ftbl_sdcc1_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "sdcc1_apps_clk_src", + .parent_names = gcc_xo_gpll0_gpll4, + .num_parents = 3, + .ops = &clk_rcg2_floor_ops, + }, +}; + +static struct freq_tbl ftbl_sdcc2_4_apps_clk_src[] = { + F(144000, P_XO, 16, 3, 25), + F(400000, P_XO, 12, 1, 4), + F(20000000, P_GPLL0, 15, 1, 2), + F(25000000, P_GPLL0, 12, 1, 2), + F(50000000, P_GPLL0, 12, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(200000000, P_GPLL0, 3, 0, 0), + { } +}; + +static struct clk_rcg2 sdcc2_apps_clk_src = { + .cmd_rcgr = 0x0510, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_sdcc2_4_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "sdcc2_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_floor_ops, + }, +}; + +static struct clk_rcg2 sdcc3_apps_clk_src = { + .cmd_rcgr = 0x0550, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_sdcc2_4_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "sdcc3_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_floor_ops, + }, +}; + +static struct clk_rcg2 sdcc4_apps_clk_src = { + .cmd_rcgr = 0x0590, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_sdcc2_4_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "sdcc4_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_floor_ops, + }, +}; + +static struct freq_tbl ftbl_tsif_ref_clk_src[] = { + F(105500, P_XO, 1, 1, 182), + { } +}; + +static struct clk_rcg2 tsif_ref_clk_src = { + .cmd_rcgr = 0x0d90, + .mnd_width = 8, + .hid_width = 5, + .freq_tbl = ftbl_tsif_ref_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "tsif_ref_clk_src", + .parent_names = (const char *[]) { "xo" }, + .num_parents = 1, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_usb30_mock_utmi_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(60000000, P_GPLL0, 10, 0, 0), + { } +}; + +static struct clk_rcg2 usb30_mock_utmi_clk_src = { + .cmd_rcgr = 0x03e8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_usb30_mock_utmi_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "usb30_mock_utmi_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_usb3_phy_aux_clk_src[] = { + F(1200000, P_XO, 16, 0, 0), + { } +}; + +static struct clk_rcg2 usb3_phy_aux_clk_src = { + .cmd_rcgr = 0x1414, + .hid_width = 5, + .freq_tbl = ftbl_usb3_phy_aux_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "usb3_phy_aux_clk_src", + .parent_names = (const char *[]) { "xo" }, + .num_parents = 1, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_usb_hs_system_clk_src[] = { + F(75000000, P_GPLL0, 8, 0, 0), + { } +}; + +static struct clk_rcg2 usb_hs_system_clk_src = { + .cmd_rcgr = 0x0490, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_usb_hs_system_clk_src, + .clkr.hw.init = &(struct clk_init_data) + { + .name = "usb_hs_system_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gcc_blsp1_ahb_clk = { + .halt_reg = 0x05c4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, + .enable_mask = BIT(17), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup1_i2c_apps_clk = { + .halt_reg = 0x0648, + .clkr = { + .enable_reg = 0x0648, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_qup1_i2c_apps_clk", + .parent_names = (const char *[]) { + "blsp1_qup1_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup1_spi_apps_clk = { + .halt_reg = 0x0644, + .clkr = { + .enable_reg = 0x0644, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_qup1_spi_apps_clk", + .parent_names = (const char *[]) { + "blsp1_qup1_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup2_i2c_apps_clk = { + .halt_reg = 0x06c8, + .clkr = { + .enable_reg = 0x06c8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_qup2_i2c_apps_clk", + .parent_names = (const char *[]) { + "blsp1_qup2_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup2_spi_apps_clk = { + .halt_reg = 0x06c4, + .clkr = { + .enable_reg = 0x06c4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_qup2_spi_apps_clk", + .parent_names = (const char *[]) { + "blsp1_qup2_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup3_i2c_apps_clk = { + .halt_reg = 0x0748, + .clkr = { + .enable_reg = 0x0748, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_qup3_i2c_apps_clk", + .parent_names = (const char *[]) { + "blsp1_qup3_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup3_spi_apps_clk = { + .halt_reg = 0x0744, + .clkr = { + .enable_reg = 0x0744, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_qup3_spi_apps_clk", + .parent_names = (const char *[]) { + "blsp1_qup3_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup4_i2c_apps_clk = { + .halt_reg = 0x07c8, + .clkr = { + .enable_reg = 0x07c8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_qup4_i2c_apps_clk", + .parent_names = (const char *[]) { + "blsp1_qup4_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup4_spi_apps_clk = { + .halt_reg = 0x07c4, + .clkr = { + .enable_reg = 0x07c4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_qup4_spi_apps_clk", + .parent_names = (const char *[]) { + "blsp1_qup4_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup5_i2c_apps_clk = { + .halt_reg = 0x0848, + .clkr = { + .enable_reg = 0x0848, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_qup5_i2c_apps_clk", + .parent_names = (const char *[]) { + "blsp1_qup5_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup5_spi_apps_clk = { + .halt_reg = 0x0844, + .clkr = { + .enable_reg = 0x0844, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_qup5_spi_apps_clk", + .parent_names = (const char *[]) { + "blsp1_qup5_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup6_i2c_apps_clk = { + .halt_reg = 0x08c8, + .clkr = { + .enable_reg = 0x08c8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_qup6_i2c_apps_clk", + .parent_names = (const char *[]) { + "blsp1_qup6_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup6_spi_apps_clk = { + .halt_reg = 0x08c4, + .clkr = { + .enable_reg = 0x08c4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_qup6_spi_apps_clk", + .parent_names = (const char *[]) { + "blsp1_qup6_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart1_apps_clk = { + .halt_reg = 0x0684, + .clkr = { + .enable_reg = 0x0684, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_uart1_apps_clk", + .parent_names = (const char *[]) { + "blsp1_uart1_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart2_apps_clk = { + .halt_reg = 0x0704, + .clkr = { + .enable_reg = 0x0704, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_uart2_apps_clk", + .parent_names = (const char *[]) { + "blsp1_uart2_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart3_apps_clk = { + .halt_reg = 0x0784, + .clkr = { + .enable_reg = 0x0784, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_uart3_apps_clk", + .parent_names = (const char *[]) { + "blsp1_uart3_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart4_apps_clk = { + .halt_reg = 0x0804, + .clkr = { + .enable_reg = 0x0804, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_uart4_apps_clk", + .parent_names = (const char *[]) { + "blsp1_uart4_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart5_apps_clk = { + .halt_reg = 0x0884, + .clkr = { + .enable_reg = 0x0884, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_uart5_apps_clk", + .parent_names = (const char *[]) { + "blsp1_uart5_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart6_apps_clk = { + .halt_reg = 0x0904, + .clkr = { + .enable_reg = 0x0904, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp1_uart6_apps_clk", + .parent_names = (const char *[]) { + "blsp1_uart6_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_ahb_clk = { + .halt_reg = 0x0944, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, + .enable_mask = BIT(15), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup1_i2c_apps_clk = { + .halt_reg = 0x0988, + .clkr = { + .enable_reg = 0x0988, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_qup1_i2c_apps_clk", + .parent_names = (const char *[]) { + "blsp2_qup1_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup1_spi_apps_clk = { + .halt_reg = 0x0984, + .clkr = { + .enable_reg = 0x0984, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_qup1_spi_apps_clk", + .parent_names = (const char *[]) { + "blsp2_qup1_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup2_i2c_apps_clk = { + .halt_reg = 0x0a08, + .clkr = { + .enable_reg = 0x0a08, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_qup2_i2c_apps_clk", + .parent_names = (const char *[]) { + "blsp2_qup2_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup2_spi_apps_clk = { + .halt_reg = 0x0a04, + .clkr = { + .enable_reg = 0x0a04, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_qup2_spi_apps_clk", + .parent_names = (const char *[]) { + "blsp2_qup2_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup3_i2c_apps_clk = { + .halt_reg = 0x0a88, + .clkr = { + .enable_reg = 0x0a88, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_qup3_i2c_apps_clk", + .parent_names = (const char *[]) { + "blsp2_qup3_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup3_spi_apps_clk = { + .halt_reg = 0x0a84, + .clkr = { + .enable_reg = 0x0a84, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_qup3_spi_apps_clk", + .parent_names = (const char *[]) { + "blsp2_qup3_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup4_i2c_apps_clk = { + .halt_reg = 0x0b08, + .clkr = { + .enable_reg = 0x0b08, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_qup4_i2c_apps_clk", + .parent_names = (const char *[]) { + "blsp2_qup4_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup4_spi_apps_clk = { + .halt_reg = 0x0b04, + .clkr = { + .enable_reg = 0x0b04, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_qup4_spi_apps_clk", + .parent_names = (const char *[]) { + "blsp2_qup4_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup5_i2c_apps_clk = { + .halt_reg = 0x0b88, + .clkr = { + .enable_reg = 0x0b88, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_qup5_i2c_apps_clk", + .parent_names = (const char *[]) { + "blsp2_qup5_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup5_spi_apps_clk = { + .halt_reg = 0x0b84, + .clkr = { + .enable_reg = 0x0b84, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_qup5_spi_apps_clk", + .parent_names = (const char *[]) { + "blsp2_qup5_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup6_i2c_apps_clk = { + .halt_reg = 0x0c08, + .clkr = { + .enable_reg = 0x0c08, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_qup6_i2c_apps_clk", + .parent_names = (const char *[]) { + "blsp2_qup6_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup6_spi_apps_clk = { + .halt_reg = 0x0c04, + .clkr = { + .enable_reg = 0x0c04, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_qup6_spi_apps_clk", + .parent_names = (const char *[]) { + "blsp2_qup6_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_uart1_apps_clk = { + .halt_reg = 0x09c4, + .clkr = { + .enable_reg = 0x09c4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_uart1_apps_clk", + .parent_names = (const char *[]) { + "blsp2_uart1_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_uart2_apps_clk = { + .halt_reg = 0x0a44, + .clkr = { + .enable_reg = 0x0a44, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_uart2_apps_clk", + .parent_names = (const char *[]) { + "blsp2_uart2_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_uart3_apps_clk = { + .halt_reg = 0x0ac4, + .clkr = { + .enable_reg = 0x0ac4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_uart3_apps_clk", + .parent_names = (const char *[]) { + "blsp2_uart3_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_uart4_apps_clk = { + .halt_reg = 0x0b44, + .clkr = { + .enable_reg = 0x0b44, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_uart4_apps_clk", + .parent_names = (const char *[]) { + "blsp2_uart4_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_uart5_apps_clk = { + .halt_reg = 0x0bc4, + .clkr = { + .enable_reg = 0x0bc4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_uart5_apps_clk", + .parent_names = (const char *[]) { + "blsp2_uart5_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_uart6_apps_clk = { + .halt_reg = 0x0c44, + .clkr = { + .enable_reg = 0x0c44, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_blsp2_uart6_apps_clk", + .parent_names = (const char *[]) { + "blsp2_uart6_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp1_clk = { + .halt_reg = 0x1900, + .clkr = { + .enable_reg = 0x1900, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_gp1_clk", + .parent_names = (const char *[]) { + "gp1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp2_clk = { + .halt_reg = 0x1940, + .clkr = { + .enable_reg = 0x1940, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_gp2_clk", + .parent_names = (const char *[]) { + "gp2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp3_clk = { + .halt_reg = 0x1980, + .clkr = { + .enable_reg = 0x1980, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_gp3_clk", + .parent_names = (const char *[]) { + "gp3_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_aux_clk = { + .halt_reg = 0x1ad4, + .clkr = { + .enable_reg = 0x1ad4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_pcie_0_aux_clk", + .parent_names = (const char *[]) { + "pcie_0_aux_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_pipe_clk = { + .halt_reg = 0x1ad8, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x1ad8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_pcie_0_pipe_clk", + .parent_names = (const char *[]) { + "pcie_0_pipe_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_aux_clk = { + .halt_reg = 0x1b54, + .clkr = { + .enable_reg = 0x1b54, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_pcie_1_aux_clk", + .parent_names = (const char *[]) { + "pcie_1_aux_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_pipe_clk = { + .halt_reg = 0x1b58, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x1b58, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_pcie_1_pipe_clk", + .parent_names = (const char *[]) { + "pcie_1_pipe_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm2_clk = { + .halt_reg = 0x0ccc, + .clkr = { + .enable_reg = 0x0ccc, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_pdm2_clk", + .parent_names = (const char *[]) { + "pdm2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_apps_clk = { + .halt_reg = 0x04c4, + .clkr = { + .enable_reg = 0x04c4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_sdcc1_apps_clk", + .parent_names = (const char *[]) { + "sdcc1_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_apps_clk = { + .halt_reg = 0x0504, + .clkr = { + .enable_reg = 0x0504, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_sdcc2_apps_clk", + .parent_names = (const char *[]) { + "sdcc2_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc3_apps_clk = { + .halt_reg = 0x0544, + .clkr = { + .enable_reg = 0x0544, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_sdcc3_apps_clk", + .parent_names = (const char *[]) { + "sdcc3_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc4_apps_clk = { + .halt_reg = 0x0584, + .clkr = { + .enable_reg = 0x0584, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_sdcc4_apps_clk", + .parent_names = (const char *[]) { + "sdcc4_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sys_noc_ufs_axi_clk = { + .halt_reg = 0x1d7c, + .clkr = { + .enable_reg = 0x1d7c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_sys_noc_ufs_axi_clk", + .parent_names = (const char *[]) { + "ufs_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sys_noc_usb3_axi_clk = { + .halt_reg = 0x03fc, + .clkr = { + .enable_reg = 0x03fc, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_sys_noc_usb3_axi_clk", + .parent_names = (const char *[]) { + "usb30_master_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_tsif_ref_clk = { + .halt_reg = 0x0d88, + .clkr = { + .enable_reg = 0x0d88, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_tsif_ref_clk", + .parent_names = (const char *[]) { + "tsif_ref_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_axi_clk = { + .halt_reg = 0x1d48, + .clkr = { + .enable_reg = 0x1d48, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_ufs_axi_clk", + .parent_names = (const char *[]) { + "ufs_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_rx_cfg_clk = { + .halt_reg = 0x1d54, + .clkr = { + .enable_reg = 0x1d54, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_ufs_rx_cfg_clk", + .parent_names = (const char *[]) { + "ufs_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_tx_cfg_clk = { + .halt_reg = 0x1d50, + .clkr = { + .enable_reg = 0x1d50, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_ufs_tx_cfg_clk", + .parent_names = (const char *[]) { + "ufs_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_master_clk = { + .halt_reg = 0x03c8, + .clkr = { + .enable_reg = 0x03c8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_usb30_master_clk", + .parent_names = (const char *[]) { + "usb30_master_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_mock_utmi_clk = { + .halt_reg = 0x03d0, + .clkr = { + .enable_reg = 0x03d0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_usb30_mock_utmi_clk", + .parent_names = (const char *[]) { + "usb30_mock_utmi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_phy_aux_clk = { + .halt_reg = 0x1408, + .clkr = { + .enable_reg = 0x1408, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_usb3_phy_aux_clk", + .parent_names = (const char *[]) { + "usb3_phy_aux_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb_hs_system_clk = { + .halt_reg = 0x0484, + .clkr = { + .enable_reg = 0x0484, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_usb_hs_system_clk", + .parent_names = (const char *[]) { + "usb_hs_system_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *gcc_msm8994_clocks[] = { + [GPLL0_EARLY] = &gpll0_early.clkr, + [GPLL0] = &gpll0.clkr, + [GPLL4_EARLY] = &gpll4_early.clkr, + [GPLL4] = &gpll4.clkr, + [UFS_AXI_CLK_SRC] = &ufs_axi_clk_src.clkr, + [USB30_MASTER_CLK_SRC] = &usb30_master_clk_src.clkr, + [BLSP1_QUP1_I2C_APPS_CLK_SRC] = &blsp1_qup1_i2c_apps_clk_src.clkr, + [BLSP1_QUP1_SPI_APPS_CLK_SRC] = &blsp1_qup1_spi_apps_clk_src.clkr, + [BLSP1_QUP2_I2C_APPS_CLK_SRC] = &blsp1_qup2_i2c_apps_clk_src.clkr, + [BLSP1_QUP2_SPI_APPS_CLK_SRC] = &blsp1_qup2_spi_apps_clk_src.clkr, + [BLSP1_QUP3_I2C_APPS_CLK_SRC] = &blsp1_qup3_i2c_apps_clk_src.clkr, + [BLSP1_QUP3_SPI_APPS_CLK_SRC] = &blsp1_qup3_spi_apps_clk_src.clkr, + [BLSP1_QUP4_I2C_APPS_CLK_SRC] = &blsp1_qup4_i2c_apps_clk_src.clkr, + [BLSP1_QUP4_SPI_APPS_CLK_SRC] = &blsp1_qup4_spi_apps_clk_src.clkr, + [BLSP1_QUP5_I2C_APPS_CLK_SRC] = &blsp1_qup5_i2c_apps_clk_src.clkr, + [BLSP1_QUP5_SPI_APPS_CLK_SRC] = &blsp1_qup5_spi_apps_clk_src.clkr, + [BLSP1_QUP6_I2C_APPS_CLK_SRC] = &blsp1_qup6_i2c_apps_clk_src.clkr, + [BLSP1_QUP6_SPI_APPS_CLK_SRC] = &blsp1_qup6_spi_apps_clk_src.clkr, + [BLSP1_UART1_APPS_CLK_SRC] = &blsp1_uart1_apps_clk_src.clkr, + [BLSP1_UART2_APPS_CLK_SRC] = &blsp1_uart2_apps_clk_src.clkr, + [BLSP1_UART3_APPS_CLK_SRC] = &blsp1_uart3_apps_clk_src.clkr, + [BLSP1_UART4_APPS_CLK_SRC] = &blsp1_uart4_apps_clk_src.clkr, + [BLSP1_UART5_APPS_CLK_SRC] = &blsp1_uart5_apps_clk_src.clkr, + [BLSP1_UART6_APPS_CLK_SRC] = &blsp1_uart6_apps_clk_src.clkr, + [BLSP2_QUP1_I2C_APPS_CLK_SRC] = &blsp2_qup1_i2c_apps_clk_src.clkr, + [BLSP2_QUP1_SPI_APPS_CLK_SRC] = &blsp2_qup1_spi_apps_clk_src.clkr, + [BLSP2_QUP2_I2C_APPS_CLK_SRC] = &blsp2_qup2_i2c_apps_clk_src.clkr, + [BLSP2_QUP2_SPI_APPS_CLK_SRC] = &blsp2_qup2_spi_apps_clk_src.clkr, + [BLSP2_QUP3_I2C_APPS_CLK_SRC] = &blsp2_qup3_i2c_apps_clk_src.clkr, + [BLSP2_QUP3_SPI_APPS_CLK_SRC] = &blsp2_qup3_spi_apps_clk_src.clkr, + [BLSP2_QUP4_I2C_APPS_CLK_SRC] = &blsp2_qup4_i2c_apps_clk_src.clkr, + [BLSP2_QUP4_SPI_APPS_CLK_SRC] = &blsp2_qup4_spi_apps_clk_src.clkr, + [BLSP2_QUP5_I2C_APPS_CLK_SRC] = &blsp2_qup5_i2c_apps_clk_src.clkr, + [BLSP2_QUP5_SPI_APPS_CLK_SRC] = &blsp2_qup5_spi_apps_clk_src.clkr, + [BLSP2_QUP6_I2C_APPS_CLK_SRC] = &blsp2_qup6_i2c_apps_clk_src.clkr, + [BLSP2_QUP6_SPI_APPS_CLK_SRC] = &blsp2_qup6_spi_apps_clk_src.clkr, + [BLSP2_UART1_APPS_CLK_SRC] = &blsp2_uart1_apps_clk_src.clkr, + [BLSP2_UART2_APPS_CLK_SRC] = &blsp2_uart2_apps_clk_src.clkr, + [BLSP2_UART3_APPS_CLK_SRC] = &blsp2_uart3_apps_clk_src.clkr, + [BLSP2_UART4_APPS_CLK_SRC] = &blsp2_uart4_apps_clk_src.clkr, + [BLSP2_UART5_APPS_CLK_SRC] = &blsp2_uart5_apps_clk_src.clkr, + [BLSP2_UART6_APPS_CLK_SRC] = &blsp2_uart6_apps_clk_src.clkr, + [GP1_CLK_SRC] = &gp1_clk_src.clkr, + [GP2_CLK_SRC] = &gp2_clk_src.clkr, + [GP3_CLK_SRC] = &gp3_clk_src.clkr, + [PCIE_0_AUX_CLK_SRC] = &pcie_0_aux_clk_src.clkr, + [PCIE_0_PIPE_CLK_SRC] = &pcie_0_pipe_clk_src.clkr, + [PCIE_1_AUX_CLK_SRC] = &pcie_1_aux_clk_src.clkr, + [PCIE_1_PIPE_CLK_SRC] = &pcie_1_pipe_clk_src.clkr, + [PDM2_CLK_SRC] = &pdm2_clk_src.clkr, + [SDCC1_APPS_CLK_SRC] = &sdcc1_apps_clk_src.clkr, + [SDCC2_APPS_CLK_SRC] = &sdcc2_apps_clk_src.clkr, + [SDCC3_APPS_CLK_SRC] = &sdcc3_apps_clk_src.clkr, + [SDCC4_APPS_CLK_SRC] = &sdcc4_apps_clk_src.clkr, + [TSIF_REF_CLK_SRC] = &tsif_ref_clk_src.clkr, + [USB30_MOCK_UTMI_CLK_SRC] = &usb30_mock_utmi_clk_src.clkr, + [USB3_PHY_AUX_CLK_SRC] = &usb3_phy_aux_clk_src.clkr, + [USB_HS_SYSTEM_CLK_SRC] = &usb_hs_system_clk_src.clkr, + [GCC_BLSP1_AHB_CLK] = &gcc_blsp1_ahb_clk.clkr, + [GCC_BLSP1_QUP1_I2C_APPS_CLK] = &gcc_blsp1_qup1_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP1_SPI_APPS_CLK] = &gcc_blsp1_qup1_spi_apps_clk.clkr, + [GCC_BLSP1_QUP2_I2C_APPS_CLK] = &gcc_blsp1_qup2_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP2_SPI_APPS_CLK] = &gcc_blsp1_qup2_spi_apps_clk.clkr, + [GCC_BLSP1_QUP3_I2C_APPS_CLK] = &gcc_blsp1_qup3_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP3_SPI_APPS_CLK] = &gcc_blsp1_qup3_spi_apps_clk.clkr, + [GCC_BLSP1_QUP4_I2C_APPS_CLK] = &gcc_blsp1_qup4_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP4_SPI_APPS_CLK] = &gcc_blsp1_qup4_spi_apps_clk.clkr, + [GCC_BLSP1_QUP5_I2C_APPS_CLK] = &gcc_blsp1_qup5_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP5_SPI_APPS_CLK] = &gcc_blsp1_qup5_spi_apps_clk.clkr, + [GCC_BLSP1_QUP6_I2C_APPS_CLK] = &gcc_blsp1_qup6_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP6_SPI_APPS_CLK] = &gcc_blsp1_qup6_spi_apps_clk.clkr, + [GCC_BLSP1_UART1_APPS_CLK] = &gcc_blsp1_uart1_apps_clk.clkr, + [GCC_BLSP1_UART2_APPS_CLK] = &gcc_blsp1_uart2_apps_clk.clkr, + [GCC_BLSP1_UART3_APPS_CLK] = &gcc_blsp1_uart3_apps_clk.clkr, + [GCC_BLSP1_UART4_APPS_CLK] = &gcc_blsp1_uart4_apps_clk.clkr, + [GCC_BLSP1_UART5_APPS_CLK] = &gcc_blsp1_uart5_apps_clk.clkr, + [GCC_BLSP1_UART6_APPS_CLK] = &gcc_blsp1_uart6_apps_clk.clkr, + [GCC_BLSP2_AHB_CLK] = &gcc_blsp2_ahb_clk.clkr, + [GCC_BLSP2_QUP1_I2C_APPS_CLK] = &gcc_blsp2_qup1_i2c_apps_clk.clkr, + [GCC_BLSP2_QUP1_SPI_APPS_CLK] = &gcc_blsp2_qup1_spi_apps_clk.clkr, + [GCC_BLSP2_QUP2_I2C_APPS_CLK] = &gcc_blsp2_qup2_i2c_apps_clk.clkr, + [GCC_BLSP2_QUP2_SPI_APPS_CLK] = &gcc_blsp2_qup2_spi_apps_clk.clkr, + [GCC_BLSP2_QUP3_I2C_APPS_CLK] = &gcc_blsp2_qup3_i2c_apps_clk.clkr, + [GCC_BLSP2_QUP3_SPI_APPS_CLK] = &gcc_blsp2_qup3_spi_apps_clk.clkr, + [GCC_BLSP2_QUP4_I2C_APPS_CLK] = &gcc_blsp2_qup4_i2c_apps_clk.clkr, + [GCC_BLSP2_QUP4_SPI_APPS_CLK] = &gcc_blsp2_qup4_spi_apps_clk.clkr, + [GCC_BLSP2_QUP5_I2C_APPS_CLK] = &gcc_blsp2_qup5_i2c_apps_clk.clkr, + [GCC_BLSP2_QUP5_SPI_APPS_CLK] = &gcc_blsp2_qup5_spi_apps_clk.clkr, + [GCC_BLSP2_QUP6_I2C_APPS_CLK] = &gcc_blsp2_qup6_i2c_apps_clk.clkr, + [GCC_BLSP2_QUP6_SPI_APPS_CLK] = &gcc_blsp2_qup6_spi_apps_clk.clkr, + [GCC_BLSP2_UART1_APPS_CLK] = &gcc_blsp2_uart1_apps_clk.clkr, + [GCC_BLSP2_UART2_APPS_CLK] = &gcc_blsp2_uart2_apps_clk.clkr, + [GCC_BLSP2_UART3_APPS_CLK] = &gcc_blsp2_uart3_apps_clk.clkr, + [GCC_BLSP2_UART4_APPS_CLK] = &gcc_blsp2_uart4_apps_clk.clkr, + [GCC_BLSP2_UART5_APPS_CLK] = &gcc_blsp2_uart5_apps_clk.clkr, + [GCC_BLSP2_UART6_APPS_CLK] = &gcc_blsp2_uart6_apps_clk.clkr, + [GCC_GP1_CLK] = &gcc_gp1_clk.clkr, + [GCC_GP2_CLK] = &gcc_gp2_clk.clkr, + [GCC_GP3_CLK] = &gcc_gp3_clk.clkr, + [GCC_PCIE_0_AUX_CLK] = &gcc_pcie_0_aux_clk.clkr, + [GCC_PCIE_0_PIPE_CLK] = &gcc_pcie_0_pipe_clk.clkr, + [GCC_PCIE_1_AUX_CLK] = &gcc_pcie_1_aux_clk.clkr, + [GCC_PCIE_1_PIPE_CLK] = &gcc_pcie_1_pipe_clk.clkr, + [GCC_PDM2_CLK] = &gcc_pdm2_clk.clkr, + [GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr, + [GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr, + [GCC_SDCC3_APPS_CLK] = &gcc_sdcc3_apps_clk.clkr, + [GCC_SDCC4_APPS_CLK] = &gcc_sdcc4_apps_clk.clkr, + [GCC_SYS_NOC_UFS_AXI_CLK] = &gcc_sys_noc_ufs_axi_clk.clkr, + [GCC_SYS_NOC_USB3_AXI_CLK] = &gcc_sys_noc_usb3_axi_clk.clkr, + [GCC_TSIF_REF_CLK] = &gcc_tsif_ref_clk.clkr, + [GCC_UFS_AXI_CLK] = &gcc_ufs_axi_clk.clkr, + [GCC_UFS_RX_CFG_CLK] = &gcc_ufs_rx_cfg_clk.clkr, + [GCC_UFS_TX_CFG_CLK] = &gcc_ufs_tx_cfg_clk.clkr, + [GCC_USB30_MASTER_CLK] = &gcc_usb30_master_clk.clkr, + [GCC_USB30_MOCK_UTMI_CLK] = &gcc_usb30_mock_utmi_clk.clkr, + [GCC_USB3_PHY_AUX_CLK] = &gcc_usb3_phy_aux_clk.clkr, + [GCC_USB_HS_SYSTEM_CLK] = &gcc_usb_hs_system_clk.clkr, +}; + +static const struct regmap_config gcc_msm8994_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x2000, + .fast_io = true, +}; + +static const struct qcom_cc_desc gcc_msm8994_desc = { + .config = &gcc_msm8994_regmap_config, + .clks = gcc_msm8994_clocks, + .num_clks = ARRAY_SIZE(gcc_msm8994_clocks), +}; + +static const struct of_device_id gcc_msm8994_match_table[] = { + { .compatible = "qcom,gcc-msm8994" }, + {} +}; +MODULE_DEVICE_TABLE(of, gcc_msm8994_match_table); + +static int gcc_msm8994_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk *clk; + + clk = devm_clk_register(dev, &xo.hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return qcom_cc_probe(pdev, &gcc_msm8994_desc); +} + +static struct platform_driver gcc_msm8994_driver = { + .probe = gcc_msm8994_probe, + .driver = { + .name = "gcc-msm8994", + .of_match_table = gcc_msm8994_match_table, + }, +}; + +static int __init gcc_msm8994_init(void) +{ + return platform_driver_register(&gcc_msm8994_driver); +} +core_initcall(gcc_msm8994_init); + +static void __exit gcc_msm8994_exit(void) +{ + platform_driver_unregister(&gcc_msm8994_driver); +} +module_exit(gcc_msm8994_exit); + +MODULE_DESCRIPTION("Qualcomm GCC MSM8994 Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gcc-msm8994"); diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c index fe03e6fbc7df..4b1fc1730d29 100644 --- a/drivers/clk/qcom/gcc-msm8996.c +++ b/drivers/clk/qcom/gcc-msm8996.c @@ -460,14 +460,22 @@ static struct clk_rcg2 sdcc1_apps_clk_src = { .name = "sdcc1_apps_clk_src", .parent_names = gcc_xo_gpll0_gpll4_gpll0_early_div, .num_parents = 4, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; +static struct freq_tbl ftbl_sdcc1_ice_core_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(150000000, P_GPLL0, 4, 0, 0), + F(300000000, P_GPLL0, 2, 0, 0), + { } +}; + static struct clk_rcg2 sdcc1_ice_core_clk_src = { .cmd_rcgr = 0x13024, .hid_width = 5, .parent_map = gcc_xo_gpll0_gpll4_gpll0_early_div_map, + .freq_tbl = ftbl_sdcc1_ice_core_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "sdcc1_ice_core_clk_src", .parent_names = gcc_xo_gpll0_gpll4_gpll0_early_div, @@ -497,7 +505,7 @@ static struct clk_rcg2 sdcc2_apps_clk_src = { .name = "sdcc2_apps_clk_src", .parent_names = gcc_xo_gpll0_gpll4, .num_parents = 3, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; @@ -511,7 +519,7 @@ static struct clk_rcg2 sdcc3_apps_clk_src = { .name = "sdcc3_apps_clk_src", .parent_names = gcc_xo_gpll0_gpll4, .num_parents = 3, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; @@ -535,7 +543,7 @@ static struct clk_rcg2 sdcc4_apps_clk_src = { .name = "sdcc4_apps_clk_src", .parent_names = gcc_xo_gpll0, .num_parents = 2, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_floor_ops, }, }; @@ -1230,10 +1238,18 @@ static struct clk_rcg2 ufs_axi_clk_src = { }, }; +static const struct freq_tbl ftbl_ufs_ice_core_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(150000000, P_GPLL0, 4, 0, 0), + F(300000000, P_GPLL0, 2, 0, 0), + { } +}; + static struct clk_rcg2 ufs_ice_core_clk_src = { .cmd_rcgr = 0x76014, .hid_width = 5, .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_ufs_ice_core_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "ufs_ice_core_clk_src", .parent_names = gcc_xo_gpll0, @@ -1242,10 +1258,19 @@ static struct clk_rcg2 ufs_ice_core_clk_src = { }, }; +static const struct freq_tbl ftbl_qspi_ser_clk_src[] = { + F(75000000, P_GPLL0, 8, 0, 0), + F(150000000, P_GPLL0, 4, 0, 0), + F(256000000, P_GPLL4, 1.5, 0, 0), + F(300000000, P_GPLL0, 2, 0, 0), + { } +}; + static struct clk_rcg2 qspi_ser_clk_src = { .cmd_rcgr = 0x8b00c, .hid_width = 5, .parent_map = gcc_xo_gpll0_gpll1_early_div_gpll1_gpll4_gpll0_early_div_map, + .freq_tbl = ftbl_qspi_ser_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "qspi_ser_clk_src", .parent_names = gcc_xo_gpll0_gpll1_early_div_gpll1_gpll4_gpll0_early_div, diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c index f12d7b2bddd7..288186cce0ae 100644 --- a/drivers/clk/qcom/gdsc.c +++ b/drivers/clk/qcom/gdsc.c @@ -30,6 +30,7 @@ #define SW_OVERRIDE_MASK BIT(2) #define HW_CONTROL_MASK BIT(1) #define SW_COLLAPSE_MASK BIT(0) +#define GMEM_CLAMP_IO_MASK BIT(0) /* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */ #define EN_REST_WAIT_VAL (0x2 << 20) @@ -55,6 +56,13 @@ static int gdsc_is_enabled(struct gdsc *sc, unsigned int reg) return !!(val & PWR_ON_MASK); } +static int gdsc_hwctrl(struct gdsc *sc, bool en) +{ + u32 val = en ? HW_CONTROL_MASK : 0; + + return regmap_update_bits(sc->regmap, sc->gdscr, HW_CONTROL_MASK, val); +} + static int gdsc_toggle_logic(struct gdsc *sc, bool en) { int ret; @@ -140,6 +148,18 @@ static inline void gdsc_clear_mem_on(struct gdsc *sc) regmap_update_bits(sc->regmap, sc->cxcs[i], mask, 0); } +static inline void gdsc_deassert_clamp_io(struct gdsc *sc) +{ + regmap_update_bits(sc->regmap, sc->clamp_io_ctrl, + GMEM_CLAMP_IO_MASK, 0); +} + +static inline void gdsc_assert_clamp_io(struct gdsc *sc) +{ + regmap_update_bits(sc->regmap, sc->clamp_io_ctrl, + GMEM_CLAMP_IO_MASK, 1); +} + static int gdsc_enable(struct generic_pm_domain *domain) { struct gdsc *sc = domain_to_gdsc(domain); @@ -148,6 +168,9 @@ static int gdsc_enable(struct generic_pm_domain *domain) if (sc->pwrsts == PWRSTS_ON) return gdsc_deassert_reset(sc); + if (sc->flags & CLAMP_IO) + gdsc_deassert_clamp_io(sc); + ret = gdsc_toggle_logic(sc, true); if (ret) return ret; @@ -164,20 +187,39 @@ static int gdsc_enable(struct generic_pm_domain *domain) */ udelay(1); + /* Turn on HW trigger mode if supported */ + if (sc->flags & HW_CTRL) + return gdsc_hwctrl(sc, true); + return 0; } static int gdsc_disable(struct generic_pm_domain *domain) { struct gdsc *sc = domain_to_gdsc(domain); + int ret; if (sc->pwrsts == PWRSTS_ON) return gdsc_assert_reset(sc); + /* Turn off HW trigger mode if supported */ + if (sc->flags & HW_CTRL) { + ret = gdsc_hwctrl(sc, false); + if (ret < 0) + return ret; + } + if (sc->pwrsts & PWRSTS_OFF) gdsc_clear_mem_on(sc); - return gdsc_toggle_logic(sc, false); + ret = gdsc_toggle_logic(sc, false); + if (ret) + return ret; + + if (sc->flags & CLAMP_IO) + gdsc_assert_clamp_io(sc); + + return 0; } static int gdsc_init(struct gdsc *sc) diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h index 3bf497c36bdf..39648348e5ec 100644 --- a/drivers/clk/qcom/gdsc.h +++ b/drivers/clk/qcom/gdsc.h @@ -39,6 +39,7 @@ struct gdsc { struct regmap *regmap; unsigned int gdscr; unsigned int gds_hw_ctrl; + unsigned int clamp_io_ctrl; unsigned int *cxcs; unsigned int cxc_count; const u8 pwrsts; @@ -50,6 +51,8 @@ struct gdsc { #define PWRSTS_RET_ON (PWRSTS_RET | PWRSTS_ON) const u8 flags; #define VOTABLE BIT(0) +#define CLAMP_IO BIT(1) +#define HW_CTRL BIT(2) struct reset_controller_dev *rcdev; unsigned int *resets; unsigned int reset_count; diff --git a/drivers/clk/qcom/lcc-ipq806x.c b/drivers/clk/qcom/lcc-ipq806x.c index db3998e5e2d8..977e98eadbeb 100644 --- a/drivers/clk/qcom/lcc-ipq806x.c +++ b/drivers/clk/qcom/lcc-ipq806x.c @@ -443,7 +443,7 @@ static int lcc_ipq806x_probe(struct platform_device *pdev) return PTR_ERR(regmap); /* Configure the rate of PLL4 if the bootloader hasn't already */ - val = regmap_read(regmap, 0x0, &val); + regmap_read(regmap, 0x0, &val); if (!val) clk_pll_configure_sr(&pll4, regmap, &pll4_config, true); /* Enable PLL4 source on the LPASS Primary PLL Mux */ diff --git a/drivers/clk/qcom/mmcc-msm8996.c b/drivers/clk/qcom/mmcc-msm8996.c index ca97e1151797..9b97246287a7 100644 --- a/drivers/clk/qcom/mmcc-msm8996.c +++ b/drivers/clk/qcom/mmcc-msm8996.c @@ -2945,6 +2945,7 @@ static struct gdsc venus_core0_gdsc = { .name = "venus_core0", }, .pwrsts = PWRSTS_OFF_ON, + .flags = HW_CTRL, }; static struct gdsc venus_core1_gdsc = { @@ -2955,6 +2956,7 @@ static struct gdsc venus_core1_gdsc = { .name = "venus_core1", }, .pwrsts = PWRSTS_OFF_ON, + .flags = HW_CTRL, }; static struct gdsc camss_gdsc = { @@ -3034,6 +3036,28 @@ static struct gdsc mdss_gdsc = { .pwrsts = PWRSTS_OFF_ON, }; +static struct gdsc gpu_gdsc = { + .gdscr = 0x4034, + .gds_hw_ctrl = 0x4038, + .pd = { + .name = "gpu", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, +}; + +static struct gdsc gpu_gx_gdsc = { + .gdscr = 0x4024, + .clamp_io_ctrl = 0x4300, + .cxcs = (unsigned int []){ 0x4028 }, + .cxc_count = 1, + .pd = { + .name = "gpu_gx", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = CLAMP_IO, +}; + static struct clk_regmap *mmcc_msm8996_clocks[] = { [MMPLL0_EARLY] = &mmpll0_early.clkr, [MMPLL0_PLL] = &mmpll0.clkr, @@ -3223,6 +3247,8 @@ static struct gdsc *mmcc_msm8996_gdscs[] = { [CPP_GDSC] = &cpp_gdsc, [FD_GDSC] = &fd_gdsc, [MDSS_GDSC] = &mdss_gdsc, + [GPU_GDSC] = &gpu_gdsc, + [GPU_GX_GDSC] = &gpu_gx_gdsc, }; static const struct qcom_reset_map mmcc_msm8996_resets[] = { diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index 41a12d376799..2586dfa0026b 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -1,5 +1,7 @@ config CLK_RENESAS_CPG_MSSR bool + default y if ARCH_R8A7743 + default y if ARCH_R8A7745 default y if ARCH_R8A7795 default y if ARCH_R8A7796 diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index 90dd0db7d9c6..1072f7653c0c 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -2,6 +2,8 @@ obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o obj-$(CONFIG_ARCH_R8A73A4) += clk-r8a73a4.o clk-div6.o obj-$(CONFIG_ARCH_R8A7740) += clk-r8a7740.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7743) += r8a7743-cpg-mssr.o rcar-gen2-cpg.o +obj-$(CONFIG_ARCH_R8A7745) += r8a7745-cpg-mssr.o rcar-gen2-cpg.o obj-$(CONFIG_ARCH_R8A7778) += clk-r8a7778.o obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o clk-div6.o diff --git a/drivers/clk/renesas/clk-r8a7778.c b/drivers/clk/renesas/clk-r8a7778.c index 40e3a501a50e..886a8380e912 100644 --- a/drivers/clk/renesas/clk-r8a7778.c +++ b/drivers/clk/renesas/clk-r8a7778.c @@ -12,6 +12,7 @@ #include <linux/clk/renesas.h> #include <linux/of_address.h> #include <linux/slab.h> +#include <linux/soc/renesas/rcar-rst.h> struct r8a7778_cpg { struct clk_onecell_data data; @@ -83,6 +84,18 @@ static void __init r8a7778_cpg_clocks_init(struct device_node *np) struct clk **clks; unsigned int i; int num_clks; + u32 mode; + + if (rcar_rst_read_mode_pins(&mode)) + return; + + 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))); num_clks = of_property_count_strings(np, "clock-output-names"); if (num_clks < 0) { @@ -130,16 +143,3 @@ static void __init r8a7778_cpg_clocks_init(struct device_node *np) 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); -} diff --git a/drivers/clk/renesas/clk-r8a7779.c b/drivers/clk/renesas/clk-r8a7779.c index cf2a37df03b1..27fbfafaf2cd 100644 --- a/drivers/clk/renesas/clk-r8a7779.c +++ b/drivers/clk/renesas/clk-r8a7779.c @@ -18,6 +18,7 @@ #include <linux/of_address.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/soc/renesas/rcar-rst.h> #include <dt-bindings/clock/r8a7779-clock.h> @@ -88,8 +89,6 @@ static const unsigned int cpg_plla_mult[4] __initconst = { 42, 48, 56, 64 }; * Initialization */ -static u32 cpg_mode __initdata; - static struct clk * __init r8a7779_cpg_register_clock(struct device_node *np, struct r8a7779_cpg *cpg, const struct cpg_clk_config *config, @@ -127,6 +126,10 @@ static void __init r8a7779_cpg_clocks_init(struct device_node *np) struct clk **clks; unsigned int i, plla_mult; int num_clks; + u32 mode; + + if (rcar_rst_read_mode_pins(&mode)) + return; num_clks = of_property_count_strings(np, "clock-output-names"); if (num_clks < 0) { @@ -148,8 +151,8 @@ static void __init r8a7779_cpg_clocks_init(struct device_node *np) cpg->data.clks = clks; cpg->data.clk_num = num_clks; - config = &cpg_clk_configs[CPG_CLK_CONFIG_INDEX(cpg_mode)]; - plla_mult = cpg_plla_mult[CPG_PLLA_MULT_INDEX(cpg_mode)]; + config = &cpg_clk_configs[CPG_CLK_CONFIG_INDEX(mode)]; + plla_mult = cpg_plla_mult[CPG_PLLA_MULT_INDEX(mode)]; for (i = 0; i < num_clks; ++i) { const char *name; @@ -173,10 +176,3 @@ static void __init r8a7779_cpg_clocks_init(struct device_node *np) } CLK_OF_DECLARE(r8a7779_cpg_clks, "renesas,r8a7779-cpg-clocks", r8a7779_cpg_clocks_init); - -void __init r8a7779_clocks_init(u32 mode) -{ - cpg_mode = mode; - - of_clk_init(NULL); -} diff --git a/drivers/clk/renesas/clk-rcar-gen2.c b/drivers/clk/renesas/clk-rcar-gen2.c index 00e6aba4b9c0..f39519edc645 100644 --- a/drivers/clk/renesas/clk-rcar-gen2.c +++ b/drivers/clk/renesas/clk-rcar-gen2.c @@ -19,6 +19,7 @@ #include <linux/of_address.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/soc/renesas/rcar-rst.h> struct rcar_gen2_cpg { struct clk_onecell_data data; @@ -364,6 +365,23 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg, 4, 0, table, &cpg->lock); } +/* + * Reset register definitions. + */ +#define MODEMR 0xe6160060 + +static u32 __init rcar_gen2_read_mode_pins(void) +{ + void __iomem *modemr = ioremap_nocache(MODEMR, 4); + u32 mode; + + BUG_ON(!modemr); + mode = ioread32(modemr); + iounmap(modemr); + + return mode; +} + static void __init rcar_gen2_cpg_clocks_init(struct device_node *np) { const struct cpg_pll_config *config; @@ -372,6 +390,13 @@ static void __init rcar_gen2_cpg_clocks_init(struct device_node *np) unsigned int i; int num_clks; + if (rcar_rst_read_mode_pins(&cpg_mode)) { + /* Backward-compatibility with old DT */ + pr_warn("%s: failed to obtain mode pins from RST\n", + np->full_name); + cpg_mode = rcar_gen2_read_mode_pins(); + } + num_clks = of_property_count_strings(np, "clock-output-names"); if (num_clks < 0) { pr_err("%s: failed to count clocks\n", __func__); @@ -420,10 +445,3 @@ static void __init rcar_gen2_cpg_clocks_init(struct device_node *np) } CLK_OF_DECLARE(rcar_gen2_cpg_clks, "renesas,rcar-gen2-cpg-clocks", rcar_gen2_cpg_clocks_init); - -void __init rcar_gen2_clocks_init(u32 mode) -{ - cpg_mode = mode; - - of_clk_init(NULL); -} diff --git a/drivers/clk/renesas/r8a7743-cpg-mssr.c b/drivers/clk/renesas/r8a7743-cpg-mssr.c new file mode 100644 index 000000000000..6dc0b3082aa6 --- /dev/null +++ b/drivers/clk/renesas/r8a7743-cpg-mssr.c @@ -0,0 +1,270 @@ +/* + * r8a7743 Clock Pulse Generator / Module Standby and Software Reset + * + * Copyright (C) 2016 Cogent Embedded Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation; of the License. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/soc/renesas/rcar-rst.h> + +#include <dt-bindings/clock/r8a7743-cpg-mssr.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen2-cpg.h" + +enum clk_ids { + /* Core Clock Outputs exported to DT */ + LAST_DT_CORE_CLK = R8A7743_CLK_OSC, + + /* External Input Clocks */ + CLK_EXTAL, + CLK_USB_EXTAL, + + /* Internal Core Clocks */ + CLK_MAIN, + CLK_PLL0, + CLK_PLL1, + CLK_PLL3, + CLK_PLL1_DIV2, + + /* Module Clocks */ + MOD_CLK_BASE +}; + +static const struct cpg_core_clk r8a7743_core_clks[] __initconst = { + /* External Clock Inputs */ + DEF_INPUT("extal", CLK_EXTAL), + DEF_INPUT("usb_extal", CLK_USB_EXTAL), + + /* Internal Core Clocks */ + DEF_BASE(".main", CLK_MAIN, CLK_TYPE_GEN2_MAIN, CLK_EXTAL), + DEF_BASE(".pll0", CLK_PLL0, CLK_TYPE_GEN2_PLL0, CLK_MAIN), + DEF_BASE(".pll1", CLK_PLL1, CLK_TYPE_GEN2_PLL1, CLK_MAIN), + DEF_BASE(".pll3", CLK_PLL3, CLK_TYPE_GEN2_PLL3, CLK_MAIN), + + DEF_FIXED(".pll1_div2", CLK_PLL1_DIV2, CLK_PLL1, 2, 1), + + /* Core Clock Outputs */ + DEF_BASE("z", R8A7743_CLK_Z, CLK_TYPE_GEN2_Z, CLK_PLL0), + DEF_BASE("lb", R8A7743_CLK_LB, CLK_TYPE_GEN2_LB, CLK_PLL1), + DEF_BASE("sdh", R8A7743_CLK_SDH, CLK_TYPE_GEN2_SDH, CLK_PLL1), + DEF_BASE("sd0", R8A7743_CLK_SD0, CLK_TYPE_GEN2_SD0, CLK_PLL1), + DEF_BASE("qspi", R8A7743_CLK_QSPI, CLK_TYPE_GEN2_QSPI, CLK_PLL1_DIV2), + DEF_BASE("rcan", R8A7743_CLK_RCAN, CLK_TYPE_GEN2_RCAN, CLK_USB_EXTAL), + + DEF_FIXED("zg", R8A7743_CLK_ZG, CLK_PLL1, 3, 1), + DEF_FIXED("zx", R8A7743_CLK_ZX, CLK_PLL1, 3, 1), + DEF_FIXED("zs", R8A7743_CLK_ZS, CLK_PLL1, 6, 1), + DEF_FIXED("hp", R8A7743_CLK_HP, CLK_PLL1, 12, 1), + DEF_FIXED("b", R8A7743_CLK_B, CLK_PLL1, 12, 1), + DEF_FIXED("p", R8A7743_CLK_P, CLK_PLL1, 24, 1), + DEF_FIXED("cl", R8A7743_CLK_CL, CLK_PLL1, 48, 1), + DEF_FIXED("m2", R8A7743_CLK_M2, CLK_PLL1, 8, 1), + DEF_FIXED("zb3", R8A7743_CLK_ZB3, CLK_PLL3, 4, 1), + DEF_FIXED("zb3d2", R8A7743_CLK_ZB3D2, CLK_PLL3, 8, 1), + DEF_FIXED("ddr", R8A7743_CLK_DDR, CLK_PLL3, 8, 1), + DEF_FIXED("mp", R8A7743_CLK_MP, CLK_PLL1_DIV2, 15, 1), + DEF_FIXED("cp", R8A7743_CLK_CP, CLK_EXTAL, 2, 1), + DEF_FIXED("r", R8A7743_CLK_R, CLK_PLL1, 49152, 1), + DEF_FIXED("osc", R8A7743_CLK_OSC, CLK_PLL1, 12288, 1), + + DEF_DIV6P1("sd2", R8A7743_CLK_SD2, CLK_PLL1_DIV2, 0x078), + DEF_DIV6P1("sd3", R8A7743_CLK_SD3, CLK_PLL1_DIV2, 0x26c), + DEF_DIV6P1("mmc0", R8A7743_CLK_MMC0, CLK_PLL1_DIV2, 0x240), +}; + +static const struct mssr_mod_clk r8a7743_mod_clks[] __initconst = { + DEF_MOD("msiof0", 0, R8A7743_CLK_MP), + DEF_MOD("vcp0", 101, R8A7743_CLK_ZS), + DEF_MOD("vpc0", 103, R8A7743_CLK_ZS), + DEF_MOD("tmu1", 111, R8A7743_CLK_P), + DEF_MOD("3dg", 112, R8A7743_CLK_ZG), + DEF_MOD("2d-dmac", 115, R8A7743_CLK_ZS), + DEF_MOD("fdp1-1", 118, R8A7743_CLK_ZS), + DEF_MOD("fdp1-0", 119, R8A7743_CLK_ZS), + DEF_MOD("tmu3", 121, R8A7743_CLK_P), + DEF_MOD("tmu2", 122, R8A7743_CLK_P), + DEF_MOD("cmt0", 124, R8A7743_CLK_R), + DEF_MOD("tmu0", 125, R8A7743_CLK_CP), + DEF_MOD("vsp1du1", 127, R8A7743_CLK_ZS), + DEF_MOD("vsp1du0", 128, R8A7743_CLK_ZS), + DEF_MOD("vsp1-sy", 131, R8A7743_CLK_ZS), + DEF_MOD("scifa2", 202, R8A7743_CLK_MP), + DEF_MOD("scifa1", 203, R8A7743_CLK_MP), + DEF_MOD("scifa0", 204, R8A7743_CLK_MP), + DEF_MOD("msiof2", 205, R8A7743_CLK_MP), + DEF_MOD("scifb0", 206, R8A7743_CLK_MP), + DEF_MOD("scifb1", 207, R8A7743_CLK_MP), + DEF_MOD("msiof1", 208, R8A7743_CLK_MP), + DEF_MOD("scifb2", 216, R8A7743_CLK_MP), + DEF_MOD("sys-dmac1", 218, R8A7743_CLK_ZS), + DEF_MOD("sys-dmac0", 219, R8A7743_CLK_ZS), + DEF_MOD("tpu0", 304, R8A7743_CLK_CP), + DEF_MOD("sdhi3", 311, R8A7743_CLK_SD3), + DEF_MOD("sdhi2", 312, R8A7743_CLK_SD2), + DEF_MOD("sdhi0", 314, R8A7743_CLK_SD0), + DEF_MOD("mmcif0", 315, R8A7743_CLK_MMC0), + DEF_MOD("iic0", 318, R8A7743_CLK_HP), + DEF_MOD("pciec", 319, R8A7743_CLK_MP), + DEF_MOD("iic1", 323, R8A7743_CLK_HP), + DEF_MOD("usb3.0", 328, R8A7743_CLK_MP), + DEF_MOD("cmt1", 329, R8A7743_CLK_R), + DEF_MOD("usbhs-dmac0", 330, R8A7743_CLK_HP), + DEF_MOD("usbhs-dmac1", 331, R8A7743_CLK_HP), + DEF_MOD("irqc", 407, R8A7743_CLK_CP), + DEF_MOD("intc-sys", 408, R8A7743_CLK_ZS), + DEF_MOD("audio-dmac1", 501, R8A7743_CLK_HP), + DEF_MOD("audio-dmac0", 502, R8A7743_CLK_HP), + DEF_MOD("thermal", 522, CLK_EXTAL), + DEF_MOD("pwm", 523, R8A7743_CLK_P), + DEF_MOD("usb-ehci", 703, R8A7743_CLK_MP), + DEF_MOD("usbhs", 704, R8A7743_CLK_HP), + DEF_MOD("hscif2", 713, R8A7743_CLK_ZS), + DEF_MOD("scif5", 714, R8A7743_CLK_P), + DEF_MOD("scif4", 715, R8A7743_CLK_P), + DEF_MOD("hscif1", 716, R8A7743_CLK_ZS), + DEF_MOD("hscif0", 717, R8A7743_CLK_ZS), + DEF_MOD("scif3", 718, R8A7743_CLK_P), + DEF_MOD("scif2", 719, R8A7743_CLK_P), + DEF_MOD("scif1", 720, R8A7743_CLK_P), + DEF_MOD("scif0", 721, R8A7743_CLK_P), + DEF_MOD("du1", 723, R8A7743_CLK_ZX), + DEF_MOD("du0", 724, R8A7743_CLK_ZX), + DEF_MOD("lvds0", 726, R8A7743_CLK_ZX), + DEF_MOD("ipmmu-sgx", 800, R8A7743_CLK_ZX), + DEF_MOD("vin2", 809, R8A7743_CLK_ZG), + DEF_MOD("vin1", 810, R8A7743_CLK_ZG), + DEF_MOD("vin0", 811, R8A7743_CLK_ZG), + DEF_MOD("etheravb", 812, R8A7743_CLK_HP), + DEF_MOD("ether", 813, R8A7743_CLK_P), + DEF_MOD("sata1", 814, R8A7743_CLK_ZS), + DEF_MOD("sata0", 815, R8A7743_CLK_ZS), + DEF_MOD("gpio7", 904, R8A7743_CLK_CP), + DEF_MOD("gpio6", 905, R8A7743_CLK_CP), + DEF_MOD("gpio5", 907, R8A7743_CLK_CP), + DEF_MOD("gpio4", 908, R8A7743_CLK_CP), + DEF_MOD("gpio3", 909, R8A7743_CLK_CP), + DEF_MOD("gpio2", 910, R8A7743_CLK_CP), + DEF_MOD("gpio1", 911, R8A7743_CLK_CP), + DEF_MOD("gpio0", 912, R8A7743_CLK_CP), + DEF_MOD("can1", 915, R8A7743_CLK_P), + DEF_MOD("can0", 916, R8A7743_CLK_P), + DEF_MOD("qspi_mod", 917, R8A7743_CLK_QSPI), + DEF_MOD("i2c5", 925, R8A7743_CLK_HP), + DEF_MOD("iicdvfs", 926, R8A7743_CLK_CP), + DEF_MOD("i2c4", 927, R8A7743_CLK_HP), + DEF_MOD("i2c3", 928, R8A7743_CLK_HP), + DEF_MOD("i2c2", 929, R8A7743_CLK_HP), + DEF_MOD("i2c1", 930, R8A7743_CLK_HP), + DEF_MOD("i2c0", 931, R8A7743_CLK_HP), + DEF_MOD("ssi-all", 1005, R8A7743_CLK_P), + DEF_MOD("ssi9", 1006, MOD_CLK_ID(1005)), + DEF_MOD("ssi8", 1007, MOD_CLK_ID(1005)), + DEF_MOD("ssi7", 1008, MOD_CLK_ID(1005)), + DEF_MOD("ssi6", 1009, MOD_CLK_ID(1005)), + DEF_MOD("ssi5", 1010, MOD_CLK_ID(1005)), + DEF_MOD("ssi4", 1011, MOD_CLK_ID(1005)), + DEF_MOD("ssi3", 1012, MOD_CLK_ID(1005)), + DEF_MOD("ssi2", 1013, MOD_CLK_ID(1005)), + DEF_MOD("ssi1", 1014, MOD_CLK_ID(1005)), + DEF_MOD("ssi0", 1015, MOD_CLK_ID(1005)), + DEF_MOD("scu-all", 1017, R8A7743_CLK_P), + DEF_MOD("scu-dvc1", 1018, MOD_CLK_ID(1017)), + DEF_MOD("scu-dvc0", 1019, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu1-mix1", 1020, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu0-mix0", 1021, MOD_CLK_ID(1017)), + DEF_MOD("scu-src9", 1022, MOD_CLK_ID(1017)), + DEF_MOD("scu-src8", 1023, MOD_CLK_ID(1017)), + DEF_MOD("scu-src7", 1024, MOD_CLK_ID(1017)), + DEF_MOD("scu-src6", 1025, MOD_CLK_ID(1017)), + DEF_MOD("scu-src5", 1026, MOD_CLK_ID(1017)), + DEF_MOD("scu-src4", 1027, MOD_CLK_ID(1017)), + DEF_MOD("scu-src3", 1028, MOD_CLK_ID(1017)), + DEF_MOD("scu-src2", 1029, MOD_CLK_ID(1017)), + DEF_MOD("scu-src1", 1030, MOD_CLK_ID(1017)), + DEF_MOD("scu-src0", 1031, MOD_CLK_ID(1017)), + DEF_MOD("scifa3", 1106, R8A7743_CLK_MP), + DEF_MOD("scifa4", 1107, R8A7743_CLK_MP), + DEF_MOD("scifa5", 1108, R8A7743_CLK_MP), +}; + +static const unsigned int r8a7743_crit_mod_clks[] __initconst = { + MOD_CLK_ID(408), /* INTC-SYS (GIC) */ +}; + +/* + * CPG Clock Data + */ + +/* + * MD EXTAL PLL0 PLL1 PLL3 + * 14 13 19 (MHz) *1 *1 + *--------------------------------------------------- + * 0 0 0 15 x172/2 x208/2 x106 + * 0 0 1 15 x172/2 x208/2 x88 + * 0 1 0 20 x130/2 x156/2 x80 + * 0 1 1 20 x130/2 x156/2 x66 + * 1 0 0 26 / 2 x200/2 x240/2 x122 + * 1 0 1 26 / 2 x200/2 x240/2 x102 + * 1 1 0 30 / 2 x172/2 x208/2 x106 + * 1 1 1 30 / 2 x172/2 x208/2 x88 + * + * *1 : Table 7.5a indicates VCO output (PLLx = VCO/2) + */ +#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 12) | \ + (((md) & BIT(13)) >> 12) | \ + (((md) & BIT(19)) >> 19)) + +static const struct rcar_gen2_cpg_pll_config cpg_pll_configs[8] __initconst = { + /* EXTAL div PLL1 mult PLL3 mult */ + { 1, 208, 106, }, + { 1, 208, 88, }, + { 1, 156, 80, }, + { 1, 156, 66, }, + { 2, 240, 122, }, + { 2, 240, 102, }, + { 2, 208, 106, }, + { 2, 208, 88, }, +}; + +static int __init r8a7743_cpg_mssr_init(struct device *dev) +{ + const struct rcar_gen2_cpg_pll_config *cpg_pll_config; + u32 cpg_mode; + int error; + + error = rcar_rst_read_mode_pins(&cpg_mode); + if (error) + return error; + + cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)]; + + return rcar_gen2_cpg_init(cpg_pll_config, 2, cpg_mode); +} + +const struct cpg_mssr_info r8a7743_cpg_mssr_info __initconst = { + /* Core Clocks */ + .core_clks = r8a7743_core_clks, + .num_core_clks = ARRAY_SIZE(r8a7743_core_clks), + .last_dt_core_clk = LAST_DT_CORE_CLK, + .num_total_core_clks = MOD_CLK_BASE, + + /* Module Clocks */ + .mod_clks = r8a7743_mod_clks, + .num_mod_clks = ARRAY_SIZE(r8a7743_mod_clks), + .num_hw_mod_clks = 12 * 32, + + /* Critical Module Clocks */ + .crit_mod_clks = r8a7743_crit_mod_clks, + .num_crit_mod_clks = ARRAY_SIZE(r8a7743_crit_mod_clks), + + /* Callbacks */ + .init = r8a7743_cpg_mssr_init, + .cpg_clk_register = rcar_gen2_cpg_clk_register, +}; diff --git a/drivers/clk/renesas/r8a7745-cpg-mssr.c b/drivers/clk/renesas/r8a7745-cpg-mssr.c new file mode 100644 index 000000000000..2f15ba786c3b --- /dev/null +++ b/drivers/clk/renesas/r8a7745-cpg-mssr.c @@ -0,0 +1,259 @@ +/* + * r8a7745 Clock Pulse Generator / Module Standby and Software Reset + * + * Copyright (C) 2016 Cogent Embedded Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation; of the License. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/soc/renesas/rcar-rst.h> + +#include <dt-bindings/clock/r8a7745-cpg-mssr.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen2-cpg.h" + +enum clk_ids { + /* Core Clock Outputs exported to DT */ + LAST_DT_CORE_CLK = R8A7745_CLK_OSC, + + /* External Input Clocks */ + CLK_EXTAL, + CLK_USB_EXTAL, + + /* Internal Core Clocks */ + CLK_MAIN, + CLK_PLL0, + CLK_PLL1, + CLK_PLL3, + CLK_PLL1_DIV2, + + /* Module Clocks */ + MOD_CLK_BASE +}; + +static const struct cpg_core_clk r8a7745_core_clks[] __initconst = { + /* External Clock Inputs */ + DEF_INPUT("extal", CLK_EXTAL), + DEF_INPUT("usb_extal", CLK_USB_EXTAL), + + /* Internal Core Clocks */ + DEF_BASE(".main", CLK_MAIN, CLK_TYPE_GEN2_MAIN, CLK_EXTAL), + DEF_BASE(".pll0", CLK_PLL0, CLK_TYPE_GEN2_PLL0, CLK_MAIN), + DEF_BASE(".pll1", CLK_PLL1, CLK_TYPE_GEN2_PLL1, CLK_MAIN), + DEF_BASE(".pll3", CLK_PLL3, CLK_TYPE_GEN2_PLL3, CLK_MAIN), + + DEF_FIXED(".pll1_div2", CLK_PLL1_DIV2, CLK_PLL1, 2, 1), + + /* Core Clock Outputs */ + DEF_BASE("lb", R8A7745_CLK_LB, CLK_TYPE_GEN2_LB, CLK_PLL1), + DEF_BASE("sdh", R8A7745_CLK_SDH, CLK_TYPE_GEN2_SDH, CLK_PLL1), + DEF_BASE("sd0", R8A7745_CLK_SD0, CLK_TYPE_GEN2_SD0, CLK_PLL1), + DEF_BASE("qspi", R8A7745_CLK_QSPI, CLK_TYPE_GEN2_QSPI, CLK_PLL1_DIV2), + DEF_BASE("rcan", R8A7745_CLK_RCAN, CLK_TYPE_GEN2_RCAN, CLK_USB_EXTAL), + + DEF_FIXED("z2", R8A7745_CLK_Z2, CLK_PLL0, 1, 1), + DEF_FIXED("zg", R8A7745_CLK_ZG, CLK_PLL1, 6, 1), + DEF_FIXED("zx", R8A7745_CLK_ZX, CLK_PLL1, 3, 1), + DEF_FIXED("zs", R8A7745_CLK_ZS, CLK_PLL1, 6, 1), + DEF_FIXED("hp", R8A7745_CLK_HP, CLK_PLL1, 12, 1), + DEF_FIXED("b", R8A7745_CLK_B, CLK_PLL1, 12, 1), + DEF_FIXED("p", R8A7745_CLK_P, CLK_PLL1, 24, 1), + DEF_FIXED("cl", R8A7745_CLK_CL, CLK_PLL1, 48, 1), + DEF_FIXED("cp", R8A7745_CLK_CP, CLK_PLL1, 48, 1), + DEF_FIXED("m2", R8A7745_CLK_M2, CLK_PLL1, 8, 1), + DEF_FIXED("zb3", R8A7745_CLK_ZB3, CLK_PLL3, 4, 1), + DEF_FIXED("zb3d2", R8A7745_CLK_ZB3D2, CLK_PLL3, 8, 1), + DEF_FIXED("ddr", R8A7745_CLK_DDR, CLK_PLL3, 8, 1), + DEF_FIXED("mp", R8A7745_CLK_MP, CLK_PLL1_DIV2, 15, 1), + DEF_FIXED("cpex", R8A7745_CLK_CPEX, CLK_EXTAL, 2, 1), + DEF_FIXED("r", R8A7745_CLK_R, CLK_PLL1, 49152, 1), + DEF_FIXED("osc", R8A7745_CLK_OSC, CLK_PLL1, 12288, 1), + + DEF_DIV6P1("sd2", R8A7745_CLK_SD2, CLK_PLL1_DIV2, 0x078), + DEF_DIV6P1("sd3", R8A7745_CLK_SD3, CLK_PLL1_DIV2, 0x26c), + DEF_DIV6P1("mmc0", R8A7745_CLK_MMC0, CLK_PLL1_DIV2, 0x240), +}; + +static const struct mssr_mod_clk r8a7745_mod_clks[] __initconst = { + DEF_MOD("msiof0", 0, R8A7745_CLK_MP), + DEF_MOD("vcp0", 101, R8A7745_CLK_ZS), + DEF_MOD("vpc0", 103, R8A7745_CLK_ZS), + DEF_MOD("tmu1", 111, R8A7745_CLK_P), + DEF_MOD("3dg", 112, R8A7745_CLK_ZG), + DEF_MOD("2d-dmac", 115, R8A7745_CLK_ZS), + DEF_MOD("fdp1-0", 119, R8A7745_CLK_ZS), + DEF_MOD("tmu3", 121, R8A7745_CLK_P), + DEF_MOD("tmu2", 122, R8A7745_CLK_P), + DEF_MOD("cmt0", 124, R8A7745_CLK_R), + DEF_MOD("tmu0", 125, R8A7745_CLK_CP), + DEF_MOD("vsp1du0", 128, R8A7745_CLK_ZS), + DEF_MOD("vsp1-sy", 131, R8A7745_CLK_ZS), + DEF_MOD("scifa2", 202, R8A7745_CLK_MP), + DEF_MOD("scifa1", 203, R8A7745_CLK_MP), + DEF_MOD("scifa0", 204, R8A7745_CLK_MP), + DEF_MOD("msiof2", 205, R8A7745_CLK_MP), + DEF_MOD("scifb0", 206, R8A7745_CLK_MP), + DEF_MOD("scifb1", 207, R8A7745_CLK_MP), + DEF_MOD("msiof1", 208, R8A7745_CLK_MP), + DEF_MOD("scifb2", 216, R8A7745_CLK_MP), + DEF_MOD("sys-dmac1", 218, R8A7745_CLK_ZS), + DEF_MOD("sys-dmac0", 219, R8A7745_CLK_ZS), + DEF_MOD("tpu0", 304, R8A7745_CLK_CP), + DEF_MOD("sdhi3", 311, R8A7745_CLK_SD3), + DEF_MOD("sdhi2", 312, R8A7745_CLK_SD2), + DEF_MOD("sdhi0", 314, R8A7745_CLK_SD0), + DEF_MOD("mmcif0", 315, R8A7745_CLK_MMC0), + DEF_MOD("iic0", 318, R8A7745_CLK_HP), + DEF_MOD("iic1", 323, R8A7745_CLK_HP), + DEF_MOD("cmt1", 329, R8A7745_CLK_R), + DEF_MOD("usbhs-dmac0", 330, R8A7745_CLK_HP), + DEF_MOD("usbhs-dmac1", 331, R8A7745_CLK_HP), + DEF_MOD("irqc", 407, R8A7745_CLK_CP), + DEF_MOD("intc-sys", 408, R8A7745_CLK_ZS), + DEF_MOD("audio-dmac0", 502, R8A7745_CLK_HP), + DEF_MOD("pwm", 523, R8A7745_CLK_P), + DEF_MOD("usb-ehci", 703, R8A7745_CLK_MP), + DEF_MOD("usbhs", 704, R8A7745_CLK_HP), + DEF_MOD("hscif2", 713, R8A7745_CLK_ZS), + DEF_MOD("scif5", 714, R8A7745_CLK_P), + DEF_MOD("scif4", 715, R8A7745_CLK_P), + DEF_MOD("hscif1", 716, R8A7745_CLK_ZS), + DEF_MOD("hscif0", 717, R8A7745_CLK_ZS), + DEF_MOD("scif3", 718, R8A7745_CLK_P), + DEF_MOD("scif2", 719, R8A7745_CLK_P), + DEF_MOD("scif1", 720, R8A7745_CLK_P), + DEF_MOD("scif0", 721, R8A7745_CLK_P), + DEF_MOD("du0", 724, R8A7745_CLK_ZX), + DEF_MOD("ipmmu-sgx", 800, R8A7745_CLK_ZX), + DEF_MOD("vin1", 810, R8A7745_CLK_ZG), + DEF_MOD("vin0", 811, R8A7745_CLK_ZG), + DEF_MOD("etheravb", 812, R8A7745_CLK_HP), + DEF_MOD("ether", 813, R8A7745_CLK_P), + DEF_MOD("gpio6", 905, R8A7745_CLK_CP), + DEF_MOD("gpio5", 907, R8A7745_CLK_CP), + DEF_MOD("gpio4", 908, R8A7745_CLK_CP), + DEF_MOD("gpio3", 909, R8A7745_CLK_CP), + DEF_MOD("gpio2", 910, R8A7745_CLK_CP), + DEF_MOD("gpio1", 911, R8A7745_CLK_CP), + DEF_MOD("gpio0", 912, R8A7745_CLK_CP), + DEF_MOD("can1", 915, R8A7745_CLK_P), + DEF_MOD("can0", 916, R8A7745_CLK_P), + DEF_MOD("qspi_mod", 917, R8A7745_CLK_QSPI), + DEF_MOD("i2c5", 925, R8A7745_CLK_HP), + DEF_MOD("i2c4", 927, R8A7745_CLK_HP), + DEF_MOD("i2c3", 928, R8A7745_CLK_HP), + DEF_MOD("i2c2", 929, R8A7745_CLK_HP), + DEF_MOD("i2c1", 930, R8A7745_CLK_HP), + DEF_MOD("i2c0", 931, R8A7745_CLK_HP), + DEF_MOD("ssi-all", 1005, R8A7745_CLK_P), + DEF_MOD("ssi9", 1006, MOD_CLK_ID(1005)), + DEF_MOD("ssi8", 1007, MOD_CLK_ID(1005)), + DEF_MOD("ssi7", 1008, MOD_CLK_ID(1005)), + DEF_MOD("ssi6", 1009, MOD_CLK_ID(1005)), + DEF_MOD("ssi5", 1010, MOD_CLK_ID(1005)), + DEF_MOD("ssi4", 1011, MOD_CLK_ID(1005)), + DEF_MOD("ssi3", 1012, MOD_CLK_ID(1005)), + DEF_MOD("ssi2", 1013, MOD_CLK_ID(1005)), + DEF_MOD("ssi1", 1014, MOD_CLK_ID(1005)), + DEF_MOD("ssi0", 1015, MOD_CLK_ID(1005)), + DEF_MOD("scu-all", 1017, R8A7745_CLK_P), + DEF_MOD("scu-dvc1", 1018, MOD_CLK_ID(1017)), + DEF_MOD("scu-dvc0", 1019, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu1-mix1", 1020, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu0-mix0", 1021, MOD_CLK_ID(1017)), + DEF_MOD("scu-src9", 1022, MOD_CLK_ID(1017)), + DEF_MOD("scu-src8", 1023, MOD_CLK_ID(1017)), + DEF_MOD("scu-src7", 1024, MOD_CLK_ID(1017)), + DEF_MOD("scu-src6", 1025, MOD_CLK_ID(1017)), + DEF_MOD("scu-src5", 1026, MOD_CLK_ID(1017)), + DEF_MOD("scu-src4", 1027, MOD_CLK_ID(1017)), + DEF_MOD("scu-src3", 1028, MOD_CLK_ID(1017)), + DEF_MOD("scu-src2", 1029, MOD_CLK_ID(1017)), + DEF_MOD("scu-src1", 1030, MOD_CLK_ID(1017)), + DEF_MOD("scu-src0", 1031, MOD_CLK_ID(1017)), + DEF_MOD("scifa3", 1106, R8A7745_CLK_MP), + DEF_MOD("scifa4", 1107, R8A7745_CLK_MP), + DEF_MOD("scifa5", 1108, R8A7745_CLK_MP), +}; + +static const unsigned int r8a7745_crit_mod_clks[] __initconst = { + MOD_CLK_ID(408), /* INTC-SYS (GIC) */ +}; + +/* + * CPG Clock Data + */ + +/* + * MD EXTAL PLL0 PLL1 PLL3 + * 14 13 19 (MHz) *1 *2 + *--------------------------------------------------- + * 0 0 0 15 x200/3 x208/2 x106 + * 0 0 1 15 x200/3 x208/2 x88 + * 0 1 0 20 x150/3 x156/2 x80 + * 0 1 1 20 x150/3 x156/2 x66 + * 1 0 0 26 / 2 x230/3 x240/2 x122 + * 1 0 1 26 / 2 x230/3 x240/2 x102 + * 1 1 0 30 / 2 x200/3 x208/2 x106 + * 1 1 1 30 / 2 x200/3 x208/2 x88 + * + * *1 : Table 7.5b indicates VCO output (PLL0 = VCO/3) + * *2 : Table 7.5b indicates VCO output (PLL1 = VCO/2) + */ +#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 12) | \ + (((md) & BIT(13)) >> 12) | \ + (((md) & BIT(19)) >> 19)) + +static const struct rcar_gen2_cpg_pll_config cpg_pll_configs[8] __initconst = { + /* EXTAL div PLL1 mult PLL3 mult PLL0 mult */ + { 1, 208, 106, 200 }, + { 1, 208, 88, 200 }, + { 1, 156, 80, 150 }, + { 1, 156, 66, 150 }, + { 2, 240, 122, 230 }, + { 2, 240, 102, 230 }, + { 2, 208, 106, 200 }, + { 2, 208, 88, 200 }, +}; + +static int __init r8a7745_cpg_mssr_init(struct device *dev) +{ + const struct rcar_gen2_cpg_pll_config *cpg_pll_config; + u32 cpg_mode; + int error; + + error = rcar_rst_read_mode_pins(&cpg_mode); + if (error) + return error; + + cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)]; + + return rcar_gen2_cpg_init(cpg_pll_config, 3, cpg_mode); +} + +const struct cpg_mssr_info r8a7745_cpg_mssr_info __initconst = { + /* Core Clocks */ + .core_clks = r8a7745_core_clks, + .num_core_clks = ARRAY_SIZE(r8a7745_core_clks), + .last_dt_core_clk = LAST_DT_CORE_CLK, + .num_total_core_clks = MOD_CLK_BASE, + + /* Module Clocks */ + .mod_clks = r8a7745_mod_clks, + .num_mod_clks = ARRAY_SIZE(r8a7745_mod_clks), + .num_hw_mod_clks = 12 * 32, + + /* Critical Module Clocks */ + .crit_mod_clks = r8a7745_crit_mod_clks, + .num_crit_mod_clks = ARRAY_SIZE(r8a7745_crit_mod_clks), + + /* Callbacks */ + .init = r8a7745_cpg_mssr_init, + .cpg_clk_register = rcar_gen2_cpg_clk_register, +}; diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c index f255e451e8ca..50698a7d9074 100644 --- a/drivers/clk/renesas/r8a7795-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c @@ -15,6 +15,7 @@ #include <linux/device.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/soc/renesas/rcar-rst.h> #include <dt-bindings/clock/r8a7795-cpg-mssr.h> @@ -97,7 +98,7 @@ static const struct cpg_core_clk r8a7795_core_clks[] __initconst = { DEF_FIXED("cp", R8A7795_CLK_CP, CLK_EXTAL, 2, 1), DEF_DIV6P1("mso", R8A7795_CLK_MSO, CLK_PLL1_DIV4, 0x014), - DEF_DIV6P1("hdmi", R8A7795_CLK_HDMI, CLK_PLL1_DIV2, 0x250), + DEF_DIV6P1("hdmi", R8A7795_CLK_HDMI, CLK_PLL1_DIV4, 0x250), DEF_DIV6P1("canfd", R8A7795_CLK_CANFD, CLK_PLL1_DIV4, 0x244), DEF_DIV6P1("csi0", R8A7795_CLK_CSI0, CLK_PLL1_DIV4, 0x00c), @@ -311,7 +312,12 @@ static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[16] __initconst = { static int __init r8a7795_cpg_mssr_init(struct device *dev) { const struct rcar_gen3_cpg_pll_config *cpg_pll_config; - u32 cpg_mode = rcar_gen3_read_mode_pins(); + u32 cpg_mode; + int error; + + error = rcar_rst_read_mode_pins(&cpg_mode); + if (error) + return error; cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)]; if (!cpg_pll_config->extal_div) { diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c index eb347ed265f2..7d298c57a3e0 100644 --- a/drivers/clk/renesas/r8a7796-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c @@ -16,6 +16,7 @@ #include <linux/device.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/soc/renesas/rcar-rst.h> #include <dt-bindings/clock/r8a7796-cpg-mssr.h> @@ -102,6 +103,8 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = { DEF_FIXED("cl", R8A7796_CLK_CL, CLK_PLL1_DIV2, 48, 1), DEF_FIXED("cp", R8A7796_CLK_CP, CLK_EXTAL, 2, 1), + DEF_DIV6P1("csi0", R8A7796_CLK_CSI0, CLK_PLL1_DIV4, 0x00c), + DEF_DIV6_RO("osc", R8A7796_CLK_OSC, CLK_EXTAL, CPG_RCKCR, 8), DEF_DIV6_RO("r_int", CLK_RINT, CLK_EXTAL, CPG_RCKCR, 32), @@ -109,6 +112,14 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = { }; static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = { + DEF_MOD("scif5", 202, R8A7796_CLK_S3D4), + DEF_MOD("scif4", 203, R8A7796_CLK_S3D4), + DEF_MOD("scif3", 204, R8A7796_CLK_S3D4), + DEF_MOD("scif1", 206, R8A7796_CLK_S3D4), + DEF_MOD("scif0", 207, R8A7796_CLK_S3D4), + DEF_MOD("sys-dmac2", 217, R8A7796_CLK_S0D3), + DEF_MOD("sys-dmac1", 218, R8A7796_CLK_S0D3), + DEF_MOD("sys-dmac0", 219, R8A7796_CLK_S0D3), DEF_MOD("cmt3", 300, R8A7796_CLK_R), DEF_MOD("cmt2", 301, R8A7796_CLK_R), DEF_MOD("cmt1", 302, R8A7796_CLK_R), @@ -120,7 +131,47 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = { DEF_MOD("sdif0", 314, R8A7796_CLK_SD0), DEF_MOD("rwdt0", 402, R8A7796_CLK_R), DEF_MOD("intc-ap", 408, R8A7796_CLK_S3D1), + DEF_MOD("drif7", 508, R8A7796_CLK_S3D2), + DEF_MOD("drif6", 509, R8A7796_CLK_S3D2), + DEF_MOD("drif5", 510, R8A7796_CLK_S3D2), + DEF_MOD("drif4", 511, R8A7796_CLK_S3D2), + DEF_MOD("drif3", 512, R8A7796_CLK_S3D2), + DEF_MOD("drif2", 513, R8A7796_CLK_S3D2), + DEF_MOD("drif1", 514, R8A7796_CLK_S3D2), + DEF_MOD("drif0", 515, R8A7796_CLK_S3D2), + DEF_MOD("hscif4", 516, R8A7796_CLK_S3D1), + DEF_MOD("hscif3", 517, R8A7796_CLK_S3D1), + DEF_MOD("hscif2", 518, R8A7796_CLK_S3D1), + DEF_MOD("hscif1", 519, R8A7796_CLK_S3D1), + DEF_MOD("hscif0", 520, R8A7796_CLK_S3D1), DEF_MOD("thermal", 522, R8A7796_CLK_CP), + DEF_MOD("fcpvd2", 601, R8A7796_CLK_S0D2), + DEF_MOD("fcpvd1", 602, R8A7796_CLK_S0D2), + DEF_MOD("fcpvd0", 603, R8A7796_CLK_S0D2), + DEF_MOD("fcpvb0", 607, R8A7796_CLK_S0D1), + DEF_MOD("fcpvi0", 611, R8A7796_CLK_S0D1), + DEF_MOD("fcpf0", 615, R8A7796_CLK_S0D1), + DEF_MOD("fcpci0", 617, R8A7796_CLK_S0D2), + DEF_MOD("fcpcs", 619, R8A7796_CLK_S0D2), + DEF_MOD("vspd2", 621, R8A7796_CLK_S0D2), + DEF_MOD("vspd1", 622, R8A7796_CLK_S0D2), + DEF_MOD("vspd0", 623, R8A7796_CLK_S0D2), + DEF_MOD("vspb", 626, R8A7796_CLK_S0D1), + DEF_MOD("vspi0", 631, R8A7796_CLK_S0D1), + DEF_MOD("csi20", 714, R8A7796_CLK_CSI0), + DEF_MOD("csi40", 716, R8A7796_CLK_CSI0), + DEF_MOD("du2", 722, R8A7796_CLK_S2D1), + DEF_MOD("du1", 723, R8A7796_CLK_S2D1), + DEF_MOD("du0", 724, R8A7796_CLK_S2D1), + DEF_MOD("lvds", 727, R8A7796_CLK_S2D1), + DEF_MOD("vin7", 804, R8A7796_CLK_S0D2), + DEF_MOD("vin6", 805, R8A7796_CLK_S0D2), + DEF_MOD("vin5", 806, R8A7796_CLK_S0D2), + DEF_MOD("vin4", 807, R8A7796_CLK_S0D2), + DEF_MOD("vin3", 808, R8A7796_CLK_S0D2), + DEF_MOD("vin2", 809, R8A7796_CLK_S0D2), + DEF_MOD("vin1", 810, R8A7796_CLK_S0D2), + DEF_MOD("vin0", 811, R8A7796_CLK_S0D2), DEF_MOD("etheravb", 812, R8A7796_CLK_S0D6), DEF_MOD("gpio7", 905, R8A7796_CLK_S3D4), DEF_MOD("gpio6", 906, R8A7796_CLK_S3D4), @@ -130,6 +181,13 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = { DEF_MOD("gpio2", 910, R8A7796_CLK_S3D4), DEF_MOD("gpio1", 911, R8A7796_CLK_S3D4), DEF_MOD("gpio0", 912, R8A7796_CLK_S3D4), + DEF_MOD("i2c6", 918, R8A7796_CLK_S0D6), + DEF_MOD("i2c5", 919, R8A7796_CLK_S0D6), + DEF_MOD("i2c4", 927, R8A7796_CLK_S0D6), + DEF_MOD("i2c3", 928, R8A7796_CLK_S0D6), + DEF_MOD("i2c2", 929, R8A7796_CLK_S3D2), + DEF_MOD("i2c1", 930, R8A7796_CLK_S3D2), + DEF_MOD("i2c0", 931, R8A7796_CLK_S3D2), }; static const unsigned int r8a7796_crit_mod_clks[] __initconst = { @@ -190,7 +248,12 @@ static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[16] __initconst = { static int __init r8a7796_cpg_mssr_init(struct device *dev) { const struct rcar_gen3_cpg_pll_config *cpg_pll_config; - u32 cpg_mode = rcar_gen3_read_mode_pins(); + u32 cpg_mode; + int error; + + error = rcar_rst_read_mode_pins(&cpg_mode); + if (error) + return error; cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)]; if (!cpg_pll_config->extal_div) { diff --git a/drivers/clk/renesas/rcar-gen2-cpg.c b/drivers/clk/renesas/rcar-gen2-cpg.c new file mode 100644 index 000000000000..123b1e622179 --- /dev/null +++ b/drivers/clk/renesas/rcar-gen2-cpg.c @@ -0,0 +1,371 @@ +/* + * R-Car Gen2 Clock Pulse Generator + * + * Copyright (C) 2016 Cogent Embedded Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include <linux/bug.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen2-cpg.h" + +#define CPG_FRQCRB 0x0004 +#define CPG_FRQCRB_KICK BIT(31) +#define CPG_SDCKCR 0x0074 +#define CPG_PLL0CR 0x00d8 +#define CPG_PLL0CR_STC_SHIFT 24 +#define CPG_PLL0CR_STC_MASK (0x7f << CPG_PLL0CR_STC_SHIFT) +#define CPG_FRQCRC 0x00e0 +#define CPG_FRQCRC_ZFC_SHIFT 8 +#define CPG_FRQCRC_ZFC_MASK (0x1f << CPG_FRQCRC_ZFC_SHIFT) +#define CPG_ADSPCKCR 0x025c +#define CPG_RCANCKCR 0x0270 + +static spinlock_t cpg_lock; + +/* + * Z Clock + * + * Traits of this clock: + * prepare - clk_prepare only ensures that parents are prepared + * enable - clk_enable only ensures that parents are enabled + * rate - rate is adjustable. clk->rate = parent->rate * mult / 32 + * parent - fixed parent. No clk_set_parent support + */ + +struct cpg_z_clk { + struct clk_hw hw; + void __iomem *reg; + void __iomem *kick_reg; +}; + +#define to_z_clk(_hw) container_of(_hw, struct cpg_z_clk, hw) + +static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct cpg_z_clk *zclk = to_z_clk(hw); + unsigned int mult; + unsigned int val; + + val = (readl(zclk->reg) & CPG_FRQCRC_ZFC_MASK) >> CPG_FRQCRC_ZFC_SHIFT; + mult = 32 - val; + + return div_u64((u64)parent_rate * mult, 32); +} + +static long cpg_z_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long prate = *parent_rate; + unsigned int mult; + + if (!prate) + prate = 1; + + mult = div_u64((u64)rate * 32, prate); + mult = clamp(mult, 1U, 32U); + + return *parent_rate / 32 * mult; +} + +static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct cpg_z_clk *zclk = to_z_clk(hw); + unsigned int mult; + u32 val, kick; + unsigned int i; + + mult = div_u64((u64)rate * 32, parent_rate); + mult = clamp(mult, 1U, 32U); + + if (readl(zclk->kick_reg) & CPG_FRQCRB_KICK) + return -EBUSY; + + val = readl(zclk->reg); + val &= ~CPG_FRQCRC_ZFC_MASK; + val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT; + writel(val, zclk->reg); + + /* + * Set KICK bit in FRQCRB to update hardware setting and wait for + * clock change completion. + */ + kick = readl(zclk->kick_reg); + kick |= CPG_FRQCRB_KICK; + writel(kick, zclk->kick_reg); + + /* + * Note: There is no HW information about the worst case latency. + * + * Using experimental measurements, it seems that no more than + * ~10 iterations are needed, independently of the CPU rate. + * Since this value might be dependent on external xtal rate, pll1 + * rate or even the other emulation clocks rate, use 1000 as a + * "super" safe value. + */ + for (i = 1000; i; i--) { + if (!(readl(zclk->kick_reg) & CPG_FRQCRB_KICK)) + return 0; + + cpu_relax(); + } + + return -ETIMEDOUT; +} + +static const struct clk_ops cpg_z_clk_ops = { + .recalc_rate = cpg_z_clk_recalc_rate, + .round_rate = cpg_z_clk_round_rate, + .set_rate = cpg_z_clk_set_rate, +}; + +static struct clk * __init cpg_z_clk_register(const char *name, + const char *parent_name, + void __iomem *base) +{ + struct clk_init_data init; + struct cpg_z_clk *zclk; + struct clk *clk; + + zclk = kzalloc(sizeof(*zclk), GFP_KERNEL); + if (!zclk) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &cpg_z_clk_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = 1; + + zclk->reg = base + CPG_FRQCRC; + zclk->kick_reg = base + CPG_FRQCRB; + zclk->hw.init = &init; + + clk = clk_register(NULL, &zclk->hw); + if (IS_ERR(clk)) + kfree(zclk); + + return clk; +} + +static struct clk * __init cpg_rcan_clk_register(const char *name, + const char *parent_name, + void __iomem *base) +{ + struct clk_fixed_factor *fixed; + struct clk_gate *gate; + struct clk *clk; + + fixed = kzalloc(sizeof(*fixed), GFP_KERNEL); + if (!fixed) + return ERR_PTR(-ENOMEM); + + fixed->mult = 1; + fixed->div = 6; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + kfree(fixed); + return ERR_PTR(-ENOMEM); + } + + gate->reg = base + CPG_RCANCKCR; + gate->bit_idx = 8; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = &cpg_lock; + + clk = clk_register_composite(NULL, name, &parent_name, 1, NULL, NULL, + &fixed->hw, &clk_fixed_factor_ops, + &gate->hw, &clk_gate_ops, 0); + if (IS_ERR(clk)) { + kfree(gate); + kfree(fixed); + } + + return clk; +} + +/* ADSP divisors */ +static const struct clk_div_table cpg_adsp_div_table[] = { + { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, + { 5, 12 }, { 6, 16 }, { 7, 18 }, { 8, 24 }, + { 10, 36 }, { 11, 48 }, { 0, 0 }, +}; + +static struct clk * __init cpg_adsp_clk_register(const char *name, + const char *parent_name, + void __iomem *base) +{ + struct clk_divider *div; + struct clk_gate *gate; + struct clk *clk; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + div->reg = base + CPG_ADSPCKCR; + div->width = 4; + div->table = cpg_adsp_div_table; + div->lock = &cpg_lock; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + kfree(div); + return ERR_PTR(-ENOMEM); + } + + gate->reg = base + CPG_ADSPCKCR; + gate->bit_idx = 8; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = &cpg_lock; + + clk = clk_register_composite(NULL, name, &parent_name, 1, NULL, NULL, + &div->hw, &clk_divider_ops, + &gate->hw, &clk_gate_ops, 0); + if (IS_ERR(clk)) { + kfree(gate); + kfree(div); + } + + return clk; +} + +/* SDHI divisors */ +static const struct clk_div_table cpg_sdh_div_table[] = { + { 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, + { 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 }, + { 8, 24 }, { 10, 36 }, { 11, 48 }, { 0, 0 }, +}; + +static const struct clk_div_table cpg_sd01_div_table[] = { + { 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 }, + { 8, 24 }, { 10, 36 }, { 11, 48 }, { 12, 10 }, + { 0, 0 }, +}; + +static const struct rcar_gen2_cpg_pll_config *cpg_pll_config __initdata; +static unsigned int cpg_pll0_div __initdata; +static u32 cpg_mode __initdata; + +struct clk * __init rcar_gen2_cpg_clk_register(struct device *dev, + const struct cpg_core_clk *core, + const struct cpg_mssr_info *info, + struct clk **clks, + void __iomem *base) +{ + const struct clk_div_table *table = NULL; + const struct clk *parent; + const char *parent_name; + unsigned int mult = 1; + unsigned int div = 1; + unsigned int shift; + + parent = clks[core->parent]; + if (IS_ERR(parent)) + return ERR_CAST(parent); + + parent_name = __clk_get_name(parent); + + switch (core->type) { + /* R-Car Gen2 */ + case CLK_TYPE_GEN2_MAIN: + div = cpg_pll_config->extal_div; + break; + + case CLK_TYPE_GEN2_PLL0: + /* + * PLL0 is a configurable multiplier clock except on R-Car + * V2H/E2. Register the PLL0 clock as a fixed factor clock for + * now as there's no generic multiplier clock implementation and + * we currently have no need to change the multiplier value. + */ + mult = cpg_pll_config->pll0_mult; + div = cpg_pll0_div; + if (!mult) { + u32 pll0cr = readl(base + CPG_PLL0CR); + + mult = (((pll0cr & CPG_PLL0CR_STC_MASK) >> + CPG_PLL0CR_STC_SHIFT) + 1) * 2; + } + break; + + case CLK_TYPE_GEN2_PLL1: + mult = cpg_pll_config->pll1_mult / 2; + break; + + case CLK_TYPE_GEN2_PLL3: + mult = cpg_pll_config->pll3_mult; + break; + + case CLK_TYPE_GEN2_Z: + return cpg_z_clk_register(core->name, parent_name, base); + + case CLK_TYPE_GEN2_LB: + div = cpg_mode & BIT(18) ? 36 : 24; + break; + + case CLK_TYPE_GEN2_ADSP: + return cpg_adsp_clk_register(core->name, parent_name, base); + + case CLK_TYPE_GEN2_SDH: + table = cpg_sdh_div_table; + shift = 8; + break; + + case CLK_TYPE_GEN2_SD0: + table = cpg_sd01_div_table; + shift = 4; + break; + + case CLK_TYPE_GEN2_SD1: + table = cpg_sd01_div_table; + shift = 0; + break; + + case CLK_TYPE_GEN2_QSPI: + div = (cpg_mode & (BIT(3) | BIT(2) | BIT(1))) == BIT(2) ? + 8 : 10; + break; + + case CLK_TYPE_GEN2_RCAN: + return cpg_rcan_clk_register(core->name, parent_name, base); + + default: + return ERR_PTR(-EINVAL); + } + + if (!table) + return clk_register_fixed_factor(NULL, core->name, parent_name, + 0, mult, div); + else + return clk_register_divider_table(NULL, core->name, + parent_name, 0, + base + CPG_SDCKCR, shift, 4, + 0, table, &cpg_lock); +} + +int __init rcar_gen2_cpg_init(const struct rcar_gen2_cpg_pll_config *config, + unsigned int pll0_div, u32 mode) +{ + cpg_pll_config = config; + cpg_pll0_div = pll0_div; + cpg_mode = mode; + + spin_lock_init(&cpg_lock); + + return 0; +} diff --git a/drivers/clk/renesas/rcar-gen2-cpg.h b/drivers/clk/renesas/rcar-gen2-cpg.h new file mode 100644 index 000000000000..9eba07ff8b11 --- /dev/null +++ b/drivers/clk/renesas/rcar-gen2-cpg.h @@ -0,0 +1,43 @@ +/* + * R-Car Gen2 Clock Pulse Generator + * + * Copyright (C) 2016 Cogent Embedded Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation; version 2 of the License. + */ + +#ifndef __CLK_RENESAS_RCAR_GEN2_CPG_H__ +#define __CLK_RENESAS_RCAR_GEN2_CPG_H__ + +enum rcar_gen2_clk_types { + CLK_TYPE_GEN2_MAIN = CLK_TYPE_CUSTOM, + CLK_TYPE_GEN2_PLL0, + CLK_TYPE_GEN2_PLL1, + CLK_TYPE_GEN2_PLL3, + CLK_TYPE_GEN2_Z, + CLK_TYPE_GEN2_LB, + CLK_TYPE_GEN2_ADSP, + CLK_TYPE_GEN2_SDH, + CLK_TYPE_GEN2_SD0, + CLK_TYPE_GEN2_SD1, + CLK_TYPE_GEN2_QSPI, + CLK_TYPE_GEN2_RCAN, +}; + +struct rcar_gen2_cpg_pll_config { + unsigned int extal_div; + unsigned int pll1_mult; + unsigned int pll3_mult; + unsigned int pll0_mult; /* leave as zero if PLL0CR exists */ +}; + +struct clk *rcar_gen2_cpg_clk_register(struct device *dev, + const struct cpg_core_clk *core, + const struct cpg_mssr_info *info, + struct clk **clks, void __iomem *base); +int rcar_gen2_cpg_init(const struct rcar_gen2_cpg_pll_config *config, + unsigned int pll0_div, u32 mode); + +#endif diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index bb4f2f9a8c2f..742f6dc7c156 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -98,7 +98,7 @@ static int cpg_sd_clock_enable(struct clk_hw *hw) u32 val, sd_fc; unsigned int i; - val = clk_readl(clock->reg); + val = readl(clock->reg); sd_fc = val & CPG_SD_FC_MASK; for (i = 0; i < clock->div_num; i++) @@ -111,7 +111,7 @@ static int cpg_sd_clock_enable(struct clk_hw *hw) val &= ~(CPG_SD_STP_MASK); val |= clock->div_table[i].val & CPG_SD_STP_MASK; - clk_writel(val, clock->reg); + writel(val, clock->reg); return 0; } @@ -120,14 +120,14 @@ static void cpg_sd_clock_disable(struct clk_hw *hw) { struct sd_clock *clock = to_sd_clock(hw); - clk_writel(clk_readl(clock->reg) | CPG_SD_STP_MASK, clock->reg); + writel(readl(clock->reg) | CPG_SD_STP_MASK, clock->reg); } static int cpg_sd_clock_is_enabled(struct clk_hw *hw) { struct sd_clock *clock = to_sd_clock(hw); - return !(clk_readl(clock->reg) & CPG_SD_STP_MASK); + return !(readl(clock->reg) & CPG_SD_STP_MASK); } static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw *hw, @@ -138,7 +138,7 @@ static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw *hw, u32 val, sd_fc; unsigned int i; - val = clk_readl(clock->reg); + val = readl(clock->reg); sd_fc = val & CPG_SD_FC_MASK; for (i = 0; i < clock->div_num; i++) @@ -189,10 +189,10 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate, if (i >= clock->div_num) return -EINVAL; - val = clk_readl(clock->reg); + val = readl(clock->reg); val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK); val |= clock->div_table[i].val & (CPG_SD_STP_MASK | CPG_SD_FC_MASK); - clk_writel(val, clock->reg); + writel(val, clock->reg); return 0; } @@ -333,23 +333,6 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, __clk_get_name(parent), 0, mult, div); } -/* - * Reset register definitions. - */ -#define MODEMR 0xe6160060 - -u32 __init rcar_gen3_read_mode_pins(void) -{ - void __iomem *modemr = ioremap_nocache(MODEMR, 4); - u32 mode; - - BUG_ON(!modemr); - mode = ioread32(modemr); - iounmap(modemr); - - return mode; -} - int __init rcar_gen3_cpg_init(const struct rcar_gen3_cpg_pll_config *config, unsigned int clk_extalr) { diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h index f699085147d1..f788f481dd42 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.h +++ b/drivers/clk/renesas/rcar-gen3-cpg.h @@ -33,7 +33,6 @@ struct rcar_gen3_cpg_pll_config { #define CPG_RCKCR 0x240 -u32 rcar_gen3_read_mode_pins(void); struct clk *rcar_gen3_cpg_clk_register(struct device *dev, const struct cpg_core_clk *core, const struct cpg_mssr_info *info, struct clk **clks, void __iomem *base); diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index e1365e7491ae..8359ce75db7a 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -33,9 +33,9 @@ #include "clk-div6.h" #ifdef DEBUG -#define WARN_DEBUG(x) do { } while (0) -#else #define WARN_DEBUG(x) WARN_ON(x) +#else +#define WARN_DEBUG(x) do { } while (0) #endif @@ -146,12 +146,12 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) enable ? "ON" : "OFF"); spin_lock_irqsave(&priv->mstp_lock, flags); - value = clk_readl(priv->base + SMSTPCR(reg)); + value = readl(priv->base + SMSTPCR(reg)); if (enable) value &= ~bitmask; else value |= bitmask; - clk_writel(value, priv->base + SMSTPCR(reg)); + writel(value, priv->base + SMSTPCR(reg)); spin_unlock_irqrestore(&priv->mstp_lock, flags); @@ -159,8 +159,7 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) return 0; for (i = 1000; i > 0; --i) { - if (!(clk_readl(priv->base + MSTPSR(reg)) & - bitmask)) + if (!(readl(priv->base + MSTPSR(reg)) & bitmask)) break; cpu_relax(); } @@ -190,7 +189,7 @@ static int cpg_mstp_clock_is_enabled(struct clk_hw *hw) struct cpg_mssr_priv *priv = clock->priv; u32 value; - value = clk_readl(priv->base + MSTPSR(clock->index / 32)); + value = readl(priv->base + MSTPSR(clock->index / 32)); return !(value & BIT(clock->index % 32)); } @@ -309,7 +308,7 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core, return; fail: - dev_err(dev, "Failed to register %s clock %s: %ld\n", "core,", + dev_err(dev, "Failed to register %s clock %s: %ld\n", "core", core->name, PTR_ERR(clk)); } @@ -377,7 +376,7 @@ static void __init cpg_mssr_register_mod_clk(const struct mssr_mod_clk *mod, return; fail: - dev_err(dev, "Failed to register %s clock %s: %ld\n", "module,", + dev_err(dev, "Failed to register %s clock %s: %ld\n", "module", mod->name, PTR_ERR(clk)); kfree(clock); } @@ -503,6 +502,18 @@ static int __init cpg_mssr_add_clk_domain(struct device *dev, } static const struct of_device_id cpg_mssr_match[] = { +#ifdef CONFIG_ARCH_R8A7743 + { + .compatible = "renesas,r8a7743-cpg-mssr", + .data = &r8a7743_cpg_mssr_info, + }, +#endif +#ifdef CONFIG_ARCH_R8A7745 + { + .compatible = "renesas,r8a7745-cpg-mssr", + .data = &r8a7745_cpg_mssr_info, + }, +#endif #ifdef CONFIG_ARCH_R8A7795 { .compatible = "renesas,r8a7795-cpg-mssr", diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h index ee7edfaf1408..4bb7a80c6469 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.h +++ b/drivers/clk/renesas/renesas-cpg-mssr.h @@ -130,6 +130,8 @@ struct cpg_mssr_info { struct clk **clks, void __iomem *base); }; +extern const struct cpg_mssr_info r8a7743_cpg_mssr_info; +extern const struct cpg_mssr_info r8a7745_cpg_mssr_info; extern const struct cpg_mssr_info r8a7795_cpg_mssr_info; extern const struct cpg_mssr_info r8a7796_cpg_mssr_info; #endif diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index b5f2c8ed12e1..16e098c36f90 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -11,6 +11,7 @@ obj-y += clk-mmc-phase.o obj-y += clk-ddr.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o +obj-y += clk-rk1108.o obj-y += clk-rk3036.o obj-y += clk-rk3188.o obj-y += clk-rk3228.o diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c index 05b3d73bfefa..0e09684d43a5 100644 --- a/drivers/clk/rockchip/clk-cpu.c +++ b/drivers/clk/rockchip/clk-cpu.c @@ -124,9 +124,18 @@ static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk, struct clk_notifier_data *ndata) { const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data; + const struct rockchip_cpuclk_rate_table *rate; unsigned long alt_prate, alt_div; unsigned long flags; + /* check validity of the new rate */ + rate = rockchip_get_cpuclk_settings(cpuclk, ndata->new_rate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for cpuclk\n", + __func__, ndata->new_rate); + return -EINVAL; + } + alt_prate = clk_get_rate(cpuclk->alt_parent); spin_lock_irqsave(cpuclk->lock, flags); diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index 9c1373e81683..6ed605776abd 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -319,7 +319,8 @@ static void rockchip_rk3036_pll_init(struct clk_hw *hw) if (rate->fbdiv != cur.fbdiv || rate->postdiv1 != cur.postdiv1 || rate->refdiv != cur.refdiv || rate->postdiv2 != cur.postdiv2 || - rate->dsmpd != cur.dsmpd || rate->frac != cur.frac) { + rate->dsmpd != cur.dsmpd || + (!cur.dsmpd && (rate->frac != cur.frac))) { struct clk *parent = clk_get_parent(hw->clk); if (!parent) { @@ -795,7 +796,8 @@ static void rockchip_rk3399_pll_init(struct clk_hw *hw) if (rate->fbdiv != cur.fbdiv || rate->postdiv1 != cur.postdiv1 || rate->refdiv != cur.refdiv || rate->postdiv2 != cur.postdiv2 || - rate->dsmpd != cur.dsmpd || rate->frac != cur.frac) { + rate->dsmpd != cur.dsmpd || + (!cur.dsmpd && (rate->frac != cur.frac))) { struct clk *parent = clk_get_parent(hw->clk); if (!parent) { diff --git a/drivers/clk/rockchip/clk-rk1108.c b/drivers/clk/rockchip/clk-rk1108.c new file mode 100644 index 000000000000..92750d798e5d --- /dev/null +++ b/drivers/clk/rockchip/clk-rk1108.c @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2016 Rockchip Electronics Co. Ltd. + * Author: Shawn Lin <shawn.lin@rock-chips.com> + * Andy Yan <andy.yan@rock-chips.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/syscore_ops.h> +#include <dt-bindings/clock/rk1108-cru.h> +#include "clk.h" + +#define RK1108_GRF_SOC_STATUS0 0x480 + +enum rk1108_plls { + apll, dpll, gpll, +}; + +static struct rockchip_pll_rate_table rk1108_pll_rates[] = { + /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ + RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0), + RK3036_PLL_RATE(1584000000, 1, 66, 1, 1, 1, 0), + RK3036_PLL_RATE(1560000000, 1, 65, 1, 1, 1, 0), + RK3036_PLL_RATE(1536000000, 1, 64, 1, 1, 1, 0), + RK3036_PLL_RATE(1512000000, 1, 63, 1, 1, 1, 0), + RK3036_PLL_RATE(1488000000, 1, 62, 1, 1, 1, 0), + RK3036_PLL_RATE(1464000000, 1, 61, 1, 1, 1, 0), + RK3036_PLL_RATE(1440000000, 1, 60, 1, 1, 1, 0), + RK3036_PLL_RATE(1416000000, 1, 59, 1, 1, 1, 0), + RK3036_PLL_RATE(1392000000, 1, 58, 1, 1, 1, 0), + RK3036_PLL_RATE(1368000000, 1, 57, 1, 1, 1, 0), + RK3036_PLL_RATE(1344000000, 1, 56, 1, 1, 1, 0), + RK3036_PLL_RATE(1320000000, 1, 55, 1, 1, 1, 0), + RK3036_PLL_RATE(1296000000, 1, 54, 1, 1, 1, 0), + RK3036_PLL_RATE(1272000000, 1, 53, 1, 1, 1, 0), + RK3036_PLL_RATE(1248000000, 1, 52, 1, 1, 1, 0), + RK3036_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0), + RK3036_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0), + RK3036_PLL_RATE(1104000000, 1, 46, 1, 1, 1, 0), + RK3036_PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0), + RK3036_PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0), + RK3036_PLL_RATE(1000000000, 6, 500, 2, 1, 1, 0), + RK3036_PLL_RATE( 984000000, 1, 82, 2, 1, 1, 0), + RK3036_PLL_RATE( 960000000, 1, 80, 2, 1, 1, 0), + RK3036_PLL_RATE( 936000000, 1, 78, 2, 1, 1, 0), + RK3036_PLL_RATE( 912000000, 1, 76, 2, 1, 1, 0), + RK3036_PLL_RATE( 900000000, 4, 300, 2, 1, 1, 0), + RK3036_PLL_RATE( 888000000, 1, 74, 2, 1, 1, 0), + RK3036_PLL_RATE( 864000000, 1, 72, 2, 1, 1, 0), + RK3036_PLL_RATE( 840000000, 1, 70, 2, 1, 1, 0), + RK3036_PLL_RATE( 816000000, 1, 68, 2, 1, 1, 0), + RK3036_PLL_RATE( 800000000, 6, 400, 2, 1, 1, 0), + RK3036_PLL_RATE( 700000000, 6, 350, 2, 1, 1, 0), + RK3036_PLL_RATE( 696000000, 1, 58, 2, 1, 1, 0), + RK3036_PLL_RATE( 600000000, 1, 75, 3, 1, 1, 0), + RK3036_PLL_RATE( 594000000, 2, 99, 2, 1, 1, 0), + RK3036_PLL_RATE( 504000000, 1, 63, 3, 1, 1, 0), + RK3036_PLL_RATE( 500000000, 6, 250, 2, 1, 1, 0), + RK3036_PLL_RATE( 408000000, 1, 68, 2, 2, 1, 0), + RK3036_PLL_RATE( 312000000, 1, 52, 2, 2, 1, 0), + RK3036_PLL_RATE( 216000000, 1, 72, 4, 2, 1, 0), + RK3036_PLL_RATE( 96000000, 1, 64, 4, 4, 1, 0), + { /* sentinel */ }, +}; + +#define RK1108_DIV_CORE_MASK 0xf +#define RK1108_DIV_CORE_SHIFT 4 + +#define RK1108_CLKSEL0(_core_peri_div) \ + { \ + .reg = RK1108_CLKSEL_CON(1), \ + .val = HIWORD_UPDATE(_core_peri_div, RK1108_DIV_CORE_MASK,\ + RK1108_DIV_CORE_SHIFT) \ + } + +#define RK1108_CPUCLK_RATE(_prate, _core_peri_div) \ + { \ + .prate = _prate, \ + .divs = { \ + RK1108_CLKSEL0(_core_peri_div), \ + }, \ + } + +static struct rockchip_cpuclk_rate_table rk1108_cpuclk_rates[] __initdata = { + RK1108_CPUCLK_RATE(816000000, 4), + RK1108_CPUCLK_RATE(600000000, 4), + RK1108_CPUCLK_RATE(312000000, 4), +}; + +static const struct rockchip_cpuclk_reg_data rk1108_cpuclk_data = { + .core_reg = RK1108_CLKSEL_CON(0), + .div_core_shift = 0, + .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 0, + .mux_core_shift = 8, + .mux_core_mask = 0x1, +}; + +PNAME(mux_pll_p) = { "xin24m", "xin24m"}; +PNAME(mux_ddrphy_p) = { "dpll_ddr", "gpll_ddr", "apll_ddr" }; +PNAME(mux_armclk_p) = { "apll_core", "gpll_core", "dpll_core" }; +PNAME(mux_usb480m_pre_p) = { "usbphy", "xin24m" }; +PNAME(mux_hdmiphy_phy_p) = { "hdmiphy", "xin24m" }; +PNAME(mux_dclk_hdmiphy_pre_p) = { "dclk_hdmiphy_src_gpll", "dclk_hdmiphy_src_dpll" }; +PNAME(mux_pll_src_4plls_p) = { "dpll", "hdmiphy", "gpll", "usb480m" }; +PNAME(mux_pll_src_3plls_p) = { "apll", "gpll", "dpll" }; +PNAME(mux_pll_src_2plls_p) = { "dpll", "gpll" }; +PNAME(mux_pll_src_apll_gpll_p) = { "apll", "gpll" }; +PNAME(mux_aclk_peri_src_p) = { "aclk_peri_src_dpll", "aclk_peri_src_gpll" }; +PNAME(mux_aclk_bus_src_p) = { "aclk_bus_src_gpll", "aclk_bus_src_apll", "aclk_bus_src_dpll" }; +PNAME(mux_mmc_src_p) = { "dpll", "gpll", "xin24m", "usb480m" }; +PNAME(mux_pll_src_dpll_gpll_usb480m_p) = { "dpll", "gpll", "usb480m" }; +PNAME(mux_uart0_p) = { "uart0_src", "uart0_frac", "xin24m" }; +PNAME(mux_uart1_p) = { "uart1_src", "uart1_frac", "xin24m" }; +PNAME(mux_uart2_p) = { "uart2_src", "uart2_frac", "xin24m" }; +PNAME(mux_sclk_macphy_p) = { "sclk_macphy_pre", "ext_gmac" }; +PNAME(mux_i2s0_pre_p) = { "i2s0_src", "i2s0_frac", "ext_i2s", "xin12m" }; +PNAME(mux_i2s_out_p) = { "i2s0_pre", "xin12m" }; +PNAME(mux_i2s1_p) = { "i2s1_src", "i2s1_frac", "xin12m" }; +PNAME(mux_i2s2_p) = { "i2s2_src", "i2s2_frac", "xin12m" }; + +static struct rockchip_pll_clock rk1108_pll_clks[] __initdata = { + [apll] = PLL(pll_rk3399, PLL_APLL, "apll", mux_pll_p, 0, RK1108_PLL_CON(0), + RK1108_PLL_CON(3), 8, 31, 0, rk1108_pll_rates), + [dpll] = PLL(pll_rk3399, PLL_DPLL, "dpll", mux_pll_p, 0, RK1108_PLL_CON(8), + RK1108_PLL_CON(11), 8, 31, 0, NULL), + [gpll] = PLL(pll_rk3399, PLL_GPLL, "gpll", mux_pll_p, 0, RK1108_PLL_CON(16), + RK1108_PLL_CON(19), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk1108_pll_rates), +}; + +#define MFLAGS CLK_MUX_HIWORD_MASK +#define DFLAGS CLK_DIVIDER_HIWORD_MASK +#define GFLAGS (CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE) +#define IFLAGS ROCKCHIP_INVERTER_HIWORD_MASK + +static struct rockchip_clk_branch rk1108_uart0_fracmux __initdata = + MUX(SCLK_UART0, "sclk_uart0", mux_uart0_p, CLK_SET_RATE_PARENT, + RK1108_CLKSEL_CON(13), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk1108_uart1_fracmux __initdata = + MUX(SCLK_UART1, "sclk_uart1", mux_uart1_p, CLK_SET_RATE_PARENT, + RK1108_CLKSEL_CON(14), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk1108_uart2_fracmux __initdata = + MUX(SCLK_UART2, "sclk_uart2", mux_uart2_p, CLK_SET_RATE_PARENT, + RK1108_CLKSEL_CON(15), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk1108_i2s0_fracmux __initdata = + MUX(0, "i2s0_pre", mux_i2s0_pre_p, CLK_SET_RATE_PARENT, + RK1108_CLKSEL_CON(5), 12, 2, MFLAGS); + +static struct rockchip_clk_branch rk1108_i2s1_fracmux __initdata = + MUX(0, "i2s1_pre", mux_i2s1_p, CLK_SET_RATE_PARENT, + RK1108_CLKSEL_CON(6), 12, 2, MFLAGS); + +static struct rockchip_clk_branch rk1108_i2s2_fracmux __initdata = + MUX(0, "i2s2_pre", mux_i2s2_p, CLK_SET_RATE_PARENT, + RK1108_CLKSEL_CON(7), 12, 2, MFLAGS); + +static struct rockchip_clk_branch rk1108_clk_branches[] __initdata = { + MUX(0, "hdmi_phy", mux_hdmiphy_phy_p, CLK_SET_RATE_PARENT, + RK1108_MISC_CON, 13, 2, MFLAGS), + MUX(0, "usb480m", mux_usb480m_pre_p, CLK_SET_RATE_PARENT, + RK1108_MISC_CON, 15, 2, MFLAGS), + /* + * Clock-Architecture Diagram 2 + */ + + /* PD_CORE */ + GATE(0, "dpll_core", "dpll", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(0), 1, GFLAGS), + GATE(0, "apll_core", "apll", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(0), 0, GFLAGS), + GATE(0, "gpll_core", "gpll", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(0), 2, GFLAGS), + COMPOSITE_NOMUX(0, "pclken_dbg", "armclk", CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(1), 4, 4, DFLAGS | CLK_DIVIDER_READ_ONLY, + RK1108_CLKGATE_CON(0), 5, GFLAGS), + COMPOSITE_NOMUX(ACLK_ENMCORE, "aclkenm_core", "armclk", CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(1), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, + RK1108_CLKGATE_CON(0), 4, GFLAGS), + GATE(ACLK_CORE, "aclk_core", "aclkenm_core", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(11), 0, GFLAGS), + GATE(0, "pclk_dbg", "pclken_dbg", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(11), 1, GFLAGS), + + /* PD_RKVENC */ + + /* PD_RKVDEC */ + + /* PD_PMU_wrapper */ + COMPOSITE_NOMUX(0, "pmu_24m_ena", "gpll", CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(38), 0, 5, DFLAGS, + RK1108_CLKGATE_CON(8), 12, GFLAGS), + GATE(0, "pmu", "pmu_24m_ena", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(10), 0, GFLAGS), + GATE(0, "intmem1", "pmu_24m_ena", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(10), 1, GFLAGS), + GATE(0, "gpio0_pmu", "pmu_24m_ena", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(10), 2, GFLAGS), + GATE(0, "pmugrf", "pmu_24m_ena", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(10), 3, GFLAGS), + GATE(0, "pmu_noc", "pmu_24m_ena", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(10), 4, GFLAGS), + GATE(0, "i2c0_pmu_pclk", "pmu_24m_ena", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(10), 5, GFLAGS), + GATE(0, "pwm0_pmu_pclk", "pmu_24m_ena", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(10), 6, GFLAGS), + COMPOSITE(0, "pwm0_pmu_clk", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(12), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK1108_CLKGATE_CON(8), 15, GFLAGS), + COMPOSITE(0, "i2c0_pmu_clk", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(19), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK1108_CLKGATE_CON(8), 14, GFLAGS), + GATE(0, "pvtm_pmu", "xin24m", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(8), 13, GFLAGS), + + /* + * Clock-Architecture Diagram 4 + */ + COMPOSITE(0, "aclk_vio0_2wrap_occ", mux_pll_src_4plls_p, CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(28), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK1108_CLKGATE_CON(6), 0, GFLAGS), + GATE(0, "aclk_vio0_pre", "aclk_vio0_2wrap_occ", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(17), 0, GFLAGS), + COMPOSITE_NOMUX(0, "hclk_vio_pre", "aclk_vio0_pre", 0, + RK1108_CLKSEL_CON(29), 0, 5, DFLAGS, + RK1108_CLKGATE_CON(7), 2, GFLAGS), + COMPOSITE_NOMUX(0, "pclk_vio_pre", "aclk_vio0_pre", 0, + RK1108_CLKSEL_CON(29), 8, 5, DFLAGS, + RK1108_CLKGATE_CON(7), 3, GFLAGS), + + INVERTER(0, "pclk_vip", "ext_vip", + RK1108_CLKSEL_CON(31), 8, IFLAGS), + GATE(0, "pclk_isp_pre", "pclk_vip", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(7), 6, GFLAGS), + GATE(0, "pclk_isp", "pclk_isp_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(18), 10, GFLAGS), + GATE(0, "dclk_hdmiphy_src_gpll", "gpll", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(6), 5, GFLAGS), + GATE(0, "dclk_hdmiphy_src_dpll", "dpll", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(6), 4, GFLAGS), + COMPOSITE_NOGATE(0, "dclk_hdmiphy", mux_dclk_hdmiphy_pre_p, 0, + RK1108_CLKSEL_CON(32), 6, 2, MFLAGS, 8, 6, DFLAGS), + + /* + * Clock-Architecture Diagram 5 + */ + + FACTOR(0, "xin12m", "xin24m", 0, 1, 2), + + COMPOSITE(0, "i2s0_src", mux_pll_src_2plls_p, 0, + RK1108_CLKSEL_CON(5), 8, 1, MFLAGS, 0, 7, DFLAGS, + RK1108_CLKGATE_CON(2), 0, GFLAGS), + COMPOSITE_FRACMUX(0, "i2s1_frac", "i2s1_src", CLK_SET_RATE_PARENT, + RK1108_CLKSEL_CON(8), 0, + RK1108_CLKGATE_CON(2), 1, GFLAGS, + &rk1108_i2s0_fracmux), + GATE(SCLK_I2S0, "sclk_i2s0", "i2s0_pre", CLK_SET_RATE_PARENT, + RK1108_CLKGATE_CON(2), 2, GFLAGS), + COMPOSITE_NODIV(0, "i2s_out", mux_i2s_out_p, 0, + RK1108_CLKSEL_CON(5), 15, 1, MFLAGS, + RK1108_CLKGATE_CON(2), 3, GFLAGS), + + COMPOSITE(0, "i2s1_src", mux_pll_src_2plls_p, 0, + RK1108_CLKSEL_CON(6), 8, 1, MFLAGS, 0, 7, DFLAGS, + RK1108_CLKGATE_CON(2), 4, GFLAGS), + COMPOSITE_FRACMUX(0, "i2s1_frac", "i2s1_src", CLK_SET_RATE_PARENT, + RK2928_CLKSEL_CON(9), 0, + RK2928_CLKGATE_CON(2), 5, GFLAGS, + &rk1108_i2s1_fracmux), + GATE(SCLK_I2S1, "sclk_i2s1", "i2s1_pre", CLK_SET_RATE_PARENT, + RK1108_CLKGATE_CON(2), 6, GFLAGS), + + COMPOSITE(0, "i2s2_src", mux_pll_src_2plls_p, 0, + RK1108_CLKSEL_CON(7), 8, 1, MFLAGS, 0, 7, DFLAGS, + RK1108_CLKGATE_CON(3), 8, GFLAGS), + COMPOSITE_FRACMUX(0, "i2s2_frac", "i2s2_src", CLK_SET_RATE_PARENT, + RK1108_CLKSEL_CON(10), 0, + RK1108_CLKGATE_CON(2), 9, GFLAGS, + &rk1108_i2s2_fracmux), + GATE(SCLK_I2S2, "sclk_i2s2", "i2s2_pre", CLK_SET_RATE_PARENT, + RK1108_CLKGATE_CON(2), 10, GFLAGS), + + /* PD_BUS */ + GATE(0, "aclk_bus_src_gpll", "gpll", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(1), 0, GFLAGS), + GATE(0, "aclk_bus_src_apll", "apll", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(1), 1, GFLAGS), + GATE(0, "aclk_bus_src_dpll", "dpll", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(1), 2, GFLAGS), + COMPOSITE_NOGATE(ACLK_PRE, "aclk_bus_pre", mux_aclk_bus_src_p, 0, + RK1108_CLKSEL_CON(2), 8, 2, MFLAGS, 0, 5, DFLAGS), + COMPOSITE_NOMUX(0, "hclk_bus_pre", "aclk_bus_2wrap_occ", 0, + RK1108_CLKSEL_CON(3), 0, 5, DFLAGS, + RK1108_CLKGATE_CON(1), 4, GFLAGS), + COMPOSITE_NOMUX(0, "pclken_bus", "aclk_bus_2wrap_occ", 0, + RK1108_CLKSEL_CON(3), 8, 5, DFLAGS, + RK1108_CLKGATE_CON(1), 5, GFLAGS), + GATE(0, "pclk_bus_pre", "pclken_bus", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(1), 6, GFLAGS), + GATE(0, "pclk_top_pre", "pclken_bus", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(1), 7, GFLAGS), + GATE(0, "pclk_ddr_pre", "pclken_bus", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(1), 8, GFLAGS), + GATE(0, "clk_timer0", "mux_pll_p", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(1), 9, GFLAGS), + GATE(0, "clk_timer1", "mux_pll_p", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(1), 10, GFLAGS), + GATE(0, "pclk_timer", "pclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(13), 4, GFLAGS), + + COMPOSITE(0, "uart0_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(13), 12, 2, MFLAGS, 0, 7, DFLAGS, + RK1108_CLKGATE_CON(3), 1, GFLAGS), + COMPOSITE(0, "uart1_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(14), 12, 2, MFLAGS, 0, 7, DFLAGS, + RK1108_CLKGATE_CON(3), 3, GFLAGS), + COMPOSITE(0, "uart21_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(15), 12, 2, MFLAGS, 0, 7, DFLAGS, + RK1108_CLKGATE_CON(3), 5, GFLAGS), + + COMPOSITE_FRACMUX(0, "uart0_frac", "uart0_src", CLK_SET_RATE_PARENT, + RK1108_CLKSEL_CON(16), 0, + RK1108_CLKGATE_CON(3), 2, GFLAGS, + &rk1108_uart0_fracmux), + COMPOSITE_FRACMUX(0, "uart1_frac", "uart1_src", CLK_SET_RATE_PARENT, + RK1108_CLKSEL_CON(17), 0, + RK1108_CLKGATE_CON(3), 4, GFLAGS, + &rk1108_uart1_fracmux), + COMPOSITE_FRACMUX(0, "uart2_frac", "uart2_src", CLK_SET_RATE_PARENT, + RK1108_CLKSEL_CON(18), 0, + RK1108_CLKGATE_CON(3), 6, GFLAGS, + &rk1108_uart2_fracmux), + GATE(PCLK_UART0, "pclk_uart0", "pclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(13), 10, GFLAGS), + GATE(PCLK_UART1, "pclk_uart1", "pclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(13), 11, GFLAGS), + GATE(PCLK_UART2, "pclk_uart2", "pclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(13), 12, GFLAGS), + + COMPOSITE(0, "clk_i2c1", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(19), 15, 2, MFLAGS, 8, 7, DFLAGS, + RK1108_CLKGATE_CON(3), 7, GFLAGS), + COMPOSITE(0, "clk_i2c2", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(20), 7, 2, MFLAGS, 0, 7, DFLAGS, + RK1108_CLKGATE_CON(3), 8, GFLAGS), + COMPOSITE(0, "clk_i2c3", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(20), 15, 2, MFLAGS, 8, 7, DFLAGS, + RK1108_CLKGATE_CON(3), 9, GFLAGS), + GATE(0, "pclk_i2c1", "pclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(13), 0, GFLAGS), + GATE(0, "pclk_i2c2", "pclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(13), 1, GFLAGS), + GATE(0, "pclk_i2c3", "pclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(13), 2, GFLAGS), + COMPOSITE(0, "clk_pwm1", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(12), 15, 2, MFLAGS, 8, 7, DFLAGS, + RK1108_CLKGATE_CON(3), 10, GFLAGS), + GATE(0, "pclk_pwm1", "pclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(13), 6, GFLAGS), + GATE(0, "pclk_wdt", "pclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(13), 3, GFLAGS), + GATE(0, "pclk_gpio1", "pclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(13), 7, GFLAGS), + GATE(0, "pclk_gpio2", "pclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(13), 8, GFLAGS), + GATE(0, "pclk_gpio3", "pclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(13), 9, GFLAGS), + + GATE(0, "pclk_grf", "pclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(14), 0, GFLAGS), + + GATE(ACLK_DMAC, "aclk_dmac", "aclk_bus_pre", 0, + RK1108_CLKGATE_CON(12), 2, GFLAGS), + GATE(0, "hclk_rom", "hclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(12), 3, GFLAGS), + GATE(0, "aclk_intmem", "aclk_bus_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(12), 1, GFLAGS), + + /* PD_DDR */ + GATE(0, "apll_ddr", "apll", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(0), 8, GFLAGS), + GATE(0, "dpll_ddr", "dpll", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(0), 9, GFLAGS), + GATE(0, "gpll_ddr", "gpll", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(0), 10, GFLAGS), + COMPOSITE(0, "ddrphy4x", mux_ddrphy_p, CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(4), 8, 2, MFLAGS, 0, 3, + DFLAGS | CLK_DIVIDER_POWER_OF_TWO, + RK1108_CLKGATE_CON(10), 9, GFLAGS), + GATE(0, "ddrupctl", "ddrphy_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(12), 4, GFLAGS), + GATE(0, "ddrc", "ddrphy", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(12), 5, GFLAGS), + GATE(0, "ddrmon", "ddrphy_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(12), 6, GFLAGS), + GATE(0, "timer_clk", "xin24m", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(0), 11, GFLAGS), + + /* + * Clock-Architecture Diagram 6 + */ + + /* PD_PERI */ + COMPOSITE_NOMUX(0, "pclk_periph_pre", "gpll", 0, + RK1108_CLKSEL_CON(23), 10, 5, DFLAGS, + RK1108_CLKGATE_CON(4), 5, GFLAGS), + GATE(0, "pclk_periph", "pclk_periph_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(15), 13, GFLAGS), + COMPOSITE_NOMUX(0, "hclk_periph_pre", "gpll", 0, + RK1108_CLKSEL_CON(23), 5, 5, DFLAGS, + RK1108_CLKGATE_CON(4), 4, GFLAGS), + GATE(0, "hclk_periph", "hclk_periph_pre", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(15), 12, GFLAGS), + + GATE(0, "aclk_peri_src_dpll", "dpll", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(4), 1, GFLAGS), + GATE(0, "aclk_peri_src_gpll", "gpll", CLK_IGNORE_UNUSED, + RK1108_CLKGATE_CON(4), 2, GFLAGS), + COMPOSITE(0, "aclk_periph", mux_aclk_peri_src_p, CLK_IGNORE_UNUSED, + RK1108_CLKSEL_CON(23), 15, 2, MFLAGS, 0, 5, DFLAGS, + RK1108_CLKGATE_CON(15), 11, GFLAGS), + + COMPOSITE(SCLK_SDMMC, "sclk_sdmmc0", mux_mmc_src_p, 0, + RK1108_CLKSEL_CON(25), 8, 2, MFLAGS, 0, 8, DFLAGS, + RK1108_CLKGATE_CON(5), 0, GFLAGS), + + COMPOSITE_NODIV(0, "sclk_sdio_src", mux_mmc_src_p, 0, + RK1108_CLKSEL_CON(25), 10, 2, MFLAGS, + RK1108_CLKGATE_CON(5), 2, GFLAGS), + DIV(SCLK_SDIO, "sclk_sdio", "sclk_sdio_src", 0, + RK1108_CLKSEL_CON(26), 0, 8, DFLAGS), + + COMPOSITE_NODIV(0, "sclk_emmc_src", mux_mmc_src_p, 0, + RK1108_CLKSEL_CON(25), 12, 2, MFLAGS, + RK1108_CLKGATE_CON(5), 1, GFLAGS), + DIV(SCLK_EMMC, "sclk_emmc", "sclk_emmc_src", 0, + RK2928_CLKSEL_CON(26), 8, 8, DFLAGS), + GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_periph", 0, RK1108_CLKGATE_CON(15), 0, GFLAGS), + GATE(HCLK_SDIO, "hclk_sdio", "hclk_periph", 0, RK1108_CLKGATE_CON(15), 1, GFLAGS), + GATE(HCLK_EMMC, "hclk_emmc", "hclk_periph", 0, RK1108_CLKGATE_CON(15), 2, GFLAGS), + + COMPOSITE(SCLK_NANDC, "sclk_nandc", mux_pll_src_2plls_p, 0, + RK1108_CLKSEL_CON(27), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK1108_CLKGATE_CON(5), 3, GFLAGS), + GATE(HCLK_NANDC, "hclk_nandc", "hclk_periph", 0, RK1108_CLKGATE_CON(15), 3, GFLAGS), + + COMPOSITE(SCLK_SFC, "sclk_sfc", mux_pll_src_2plls_p, 0, + RK1108_CLKSEL_CON(27), 7, 2, MFLAGS, 0, 7, DFLAGS, + RK1108_CLKGATE_CON(5), 4, GFLAGS), + GATE(HCLK_SFC, "hclk_sfc", "hclk_periph", 0, RK1108_CLKGATE_CON(15), 10, GFLAGS), + + COMPOSITE(0, "sclk_macphy_pre", mux_pll_src_apll_gpll_p, 0, + RK1108_CLKSEL_CON(24), 12, 2, MFLAGS, 0, 5, DFLAGS, + RK1108_CLKGATE_CON(4), 10, GFLAGS), + MUX(0, "sclk_macphy", mux_sclk_macphy_p, CLK_SET_RATE_PARENT, + RK1108_CLKSEL_CON(24), 8, 2, MFLAGS), + GATE(0, "sclk_macphy_rx", "sclk_macphy", 0, RK1108_CLKGATE_CON(4), 8, GFLAGS), + GATE(0, "sclk_mac_ref", "sclk_macphy", 0, RK1108_CLKGATE_CON(4), 6, GFLAGS), + GATE(0, "sclk_mac_refout", "sclk_macphy", 0, RK1108_CLKGATE_CON(4), 7, GFLAGS), + + MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc", RK1108_SDMMC_CON0, 1), + MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc", RK1108_SDMMC_CON1, 1), + + MMC(SCLK_SDIO_DRV, "sdio_drv", "sclk_sdio", RK1108_SDIO_CON0, 1), + MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "sclk_sdio", RK1108_SDIO_CON1, 1), + + MMC(SCLK_EMMC_DRV, "emmc_drv", "sclk_emmc", RK1108_EMMC_CON0, 1), + MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "sclk_emmc", RK1108_EMMC_CON1, 1), +}; + +static const char *const rk1108_critical_clocks[] __initconst = { + "aclk_core", + "aclk_bus_src_gpll", + "aclk_periph", + "hclk_periph", + "pclk_periph", +}; + +static void __init rk1108_clk_init(struct device_node *np) +{ + struct rockchip_clk_provider *ctx; + void __iomem *reg_base; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: could not map cru region\n", __func__); + return; + } + + ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(reg_base); + return; + } + + rockchip_clk_register_plls(ctx, rk1108_pll_clks, + ARRAY_SIZE(rk1108_pll_clks), + RK1108_GRF_SOC_STATUS0); + rockchip_clk_register_branches(ctx, rk1108_clk_branches, + ARRAY_SIZE(rk1108_clk_branches)); + rockchip_clk_protect_critical(rk1108_critical_clocks, + ARRAY_SIZE(rk1108_critical_clocks)); + + rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", + mux_armclk_p, ARRAY_SIZE(mux_armclk_p), + &rk1108_cpuclk_data, rk1108_cpuclk_rates, + ARRAY_SIZE(rk1108_cpuclk_rates)); + + rockchip_register_softrst(np, 13, reg_base + RK1108_SOFTRST_CON(0), + ROCKCHIP_SOFTRST_HIWORD_MASK); + + rockchip_register_restart_notifier(ctx, RK1108_GLB_SRST_FST, NULL); + + rockchip_clk_of_add_provider(np, ctx); +} +CLK_OF_DECLARE(rk1108_cru, "rockchip,rk1108-cru", rk1108_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index d0e722a0e8cf..062ef4960244 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -89,6 +89,7 @@ static struct rockchip_pll_rate_table rk3188_pll_rates[] = { RK3066_PLL_RATE( 504000000, 1, 84, 4), RK3066_PLL_RATE( 456000000, 1, 76, 4), RK3066_PLL_RATE( 408000000, 1, 68, 4), + RK3066_PLL_RATE( 400000000, 3, 100, 2), RK3066_PLL_RATE( 384000000, 2, 128, 4), RK3066_PLL_RATE( 360000000, 1, 60, 4), RK3066_PLL_RATE( 312000000, 1, 52, 4), @@ -306,14 +307,14 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = { RK2928_CLKSEL_CON(26), 8, 1, MFLAGS, 0, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, RK2928_CLKGATE_CON(0), 2, GFLAGS), - GATE(0, "aclk_cpu", "aclk_cpu_pre", 0, + GATE(ACLK_CPU, "aclk_cpu", "aclk_cpu_pre", 0, RK2928_CLKGATE_CON(0), 3, GFLAGS), GATE(0, "atclk_cpu", "pclk_cpu_pre", 0, RK2928_CLKGATE_CON(0), 6, GFLAGS), - GATE(0, "pclk_cpu", "pclk_cpu_pre", 0, + GATE(PCLK_CPU, "pclk_cpu", "pclk_cpu_pre", 0, RK2928_CLKGATE_CON(0), 5, GFLAGS), - GATE(0, "hclk_cpu", "hclk_cpu_pre", CLK_IGNORE_UNUSED, + GATE(HCLK_CPU, "hclk_cpu", "hclk_cpu_pre", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(0), 4, GFLAGS), COMPOSITE(0, "aclk_lcdc0_pre", mux_pll_src_cpll_gpll_p, CLK_IGNORE_UNUSED, @@ -323,12 +324,12 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = { RK2928_CLKSEL_CON(31), 15, 1, MFLAGS, 8, 5, DFLAGS, RK2928_CLKGATE_CON(1), 4, GFLAGS), - GATE(0, "aclk_peri", "aclk_peri_pre", 0, + GATE(ACLK_PERI, "aclk_peri", "aclk_peri_pre", 0, RK2928_CLKGATE_CON(2), 1, GFLAGS), - COMPOSITE_NOMUX(0, "hclk_peri", "aclk_peri_pre", 0, + COMPOSITE_NOMUX(HCLK_PERI, "hclk_peri", "aclk_peri_pre", 0, RK2928_CLKSEL_CON(10), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, RK2928_CLKGATE_CON(2), 2, GFLAGS), - COMPOSITE_NOMUX(0, "pclk_peri", "aclk_peri_pre", 0, + COMPOSITE_NOMUX(PCLK_PERI, "pclk_peri", "aclk_peri_pre", 0, RK2928_CLKSEL_CON(10), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, RK2928_CLKGATE_CON(2), 3, GFLAGS), diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c index 8387c7a40bda..3490887b0579 100644 --- a/drivers/clk/rockchip/clk-rk3399.c +++ b/drivers/clk/rockchip/clk-rk3399.c @@ -77,7 +77,7 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = { RK3036_PLL_RATE(1104000000, 1, 46, 1, 1, 1, 0), RK3036_PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0), RK3036_PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0), - RK3036_PLL_RATE(1000000000, 6, 500, 2, 1, 1, 0), + RK3036_PLL_RATE(1000000000, 1, 125, 3, 1, 1, 0), RK3036_PLL_RATE( 984000000, 1, 82, 2, 1, 1, 0), RK3036_PLL_RATE( 960000000, 1, 80, 2, 1, 1, 0), RK3036_PLL_RATE( 936000000, 1, 78, 2, 1, 1, 0), @@ -87,12 +87,13 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = { RK3036_PLL_RATE( 864000000, 1, 72, 2, 1, 1, 0), RK3036_PLL_RATE( 840000000, 1, 70, 2, 1, 1, 0), RK3036_PLL_RATE( 816000000, 1, 68, 2, 1, 1, 0), - RK3036_PLL_RATE( 800000000, 6, 400, 2, 1, 1, 0), + RK3036_PLL_RATE( 800000000, 1, 100, 3, 1, 1, 0), RK3036_PLL_RATE( 700000000, 6, 350, 2, 1, 1, 0), RK3036_PLL_RATE( 696000000, 1, 58, 2, 1, 1, 0), RK3036_PLL_RATE( 676000000, 3, 169, 2, 1, 1, 0), RK3036_PLL_RATE( 600000000, 1, 75, 3, 1, 1, 0), RK3036_PLL_RATE( 594000000, 1, 99, 4, 1, 1, 0), + RK3036_PLL_RATE( 533250000, 8, 711, 4, 1, 1, 0), RK3036_PLL_RATE( 504000000, 1, 63, 3, 1, 1, 0), RK3036_PLL_RATE( 500000000, 6, 250, 2, 1, 1, 0), RK3036_PLL_RATE( 408000000, 1, 68, 2, 2, 1, 0), @@ -410,11 +411,11 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { GATE(SCLK_USB2PHY1_REF, "clk_usb2phy1_ref", "xin24m", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(6), 6, GFLAGS), - GATE(0, "clk_usbphy0_480m_src", "clk_usbphy0_480m", CLK_IGNORE_UNUSED, + GATE(0, "clk_usbphy0_480m_src", "clk_usbphy0_480m", 0, RK3399_CLKGATE_CON(13), 12, GFLAGS), - GATE(0, "clk_usbphy1_480m_src", "clk_usbphy1_480m", CLK_IGNORE_UNUSED, + GATE(0, "clk_usbphy1_480m_src", "clk_usbphy1_480m", 0, RK3399_CLKGATE_CON(13), 12, GFLAGS), - MUX(0, "clk_usbphy_480m", mux_usbphy_480m_p, CLK_IGNORE_UNUSED, + MUX(0, "clk_usbphy_480m", mux_usbphy_480m_p, 0, RK3399_CLKSEL_CON(14), 6, 1, MFLAGS), MUX(0, "upll", mux_pll_src_24m_usbphy480m_p, 0, @@ -498,7 +499,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { RK3399_CLKGATE_CON(14), 10, GFLAGS), GATE(ACLK_GIC_ADB400_CORE_L_2_GIC, "aclk_core_adb400_core_l_2_gic", "armclkl", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(14), 11, GFLAGS), - GATE(SCLK_PVTM_CORE_L, "clk_pvtm_core_l", "xin24m", CLK_IGNORE_UNUSED, + GATE(SCLK_PVTM_CORE_L, "clk_pvtm_core_l", "xin24m", 0, RK3399_CLKGATE_CON(0), 7, GFLAGS), /* big core */ @@ -539,7 +540,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { GATE(0, "pclk_dbg_cxcs_pd_core_b", "pclk_dbg_core_b", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(14), 2, GFLAGS), - GATE(SCLK_PVTM_CORE_B, "clk_pvtm_core_b", "xin24m", CLK_IGNORE_UNUSED, + GATE(SCLK_PVTM_CORE_B, "clk_pvtm_core_b", "xin24m", 0, RK3399_CLKGATE_CON(1), 7, GFLAGS), /* gmac */ @@ -675,18 +676,18 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { GATE(PCLK_CENTER_MAIN_NOC, "pclk_center_main_noc", "pclk_ddr", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(18), 10, GFLAGS), - GATE(PCLK_DDR_MON, "pclk_ddr_mon", "pclk_ddr", CLK_IGNORE_UNUSED, + GATE(PCLK_DDR_MON, "pclk_ddr_mon", "pclk_ddr", 0, RK3399_CLKGATE_CON(18), 12, GFLAGS), GATE(PCLK_CIC, "pclk_cic", "pclk_ddr", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(18), 15, GFLAGS), GATE(PCLK_DDR_SGRF, "pclk_ddr_sgrf", "pclk_ddr", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(19), 2, GFLAGS), - GATE(SCLK_PVTM_DDR, "clk_pvtm_ddr", "xin24m", CLK_IGNORE_UNUSED, + GATE(SCLK_PVTM_DDR, "clk_pvtm_ddr", "xin24m", 0, RK3399_CLKGATE_CON(4), 11, GFLAGS), - GATE(SCLK_DFIMON0_TIMER, "clk_dfimon0_timer", "xin24m", CLK_IGNORE_UNUSED, + GATE(SCLK_DFIMON0_TIMER, "clk_dfimon0_timer", "xin24m", 0, RK3399_CLKGATE_CON(3), 5, GFLAGS), - GATE(SCLK_DFIMON1_TIMER, "clk_dfimon1_timer", "xin24m", CLK_IGNORE_UNUSED, + GATE(SCLK_DFIMON1_TIMER, "clk_dfimon1_timer", "xin24m", 0, RK3399_CLKGATE_CON(3), 6, GFLAGS), /* cci */ @@ -966,7 +967,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { GATE(SCLK_INTMEM3, "clk_intmem3", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 5, GFLAGS), GATE(SCLK_INTMEM4, "clk_intmem4", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 6, GFLAGS), GATE(SCLK_INTMEM5, "clk_intmem5", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 7, GFLAGS), - GATE(ACLK_DCF, "aclk_dcf", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 8, GFLAGS), + GATE(ACLK_DCF, "aclk_dcf", "aclk_perilp0", 0, RK3399_CLKGATE_CON(23), 8, GFLAGS), GATE(ACLK_DMAC0_PERILP, "aclk_dmac0_perilp", "aclk_perilp0", 0, RK3399_CLKGATE_CON(25), 5, GFLAGS), GATE(ACLK_DMAC1_PERILP, "aclk_dmac1_perilp", "aclk_perilp0", 0, RK3399_CLKGATE_CON(25), 6, GFLAGS), GATE(ACLK_PERILP0_NOC, "aclk_perilp0_noc", "aclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(25), 7, GFLAGS), @@ -980,7 +981,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { GATE(HCLK_PERILP0_NOC, "hclk_perilp0_noc", "hclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(25), 8, GFLAGS), /* pclk_perilp0 gates */ - GATE(PCLK_DCF, "pclk_dcf", "pclk_perilp0", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(23), 9, GFLAGS), + GATE(PCLK_DCF, "pclk_dcf", "pclk_perilp0", 0, RK3399_CLKGATE_CON(23), 9, GFLAGS), /* crypto */ COMPOSITE(SCLK_CRYPTO0, "clk_crypto0", mux_pll_src_cpll_gpll_ppll_p, 0, @@ -1382,8 +1383,8 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { /* clk_test */ /* clk_test_pre is controlled by CRU_MISC_CON[3] */ COMPOSITE_NOMUX(0, "clk_test", "clk_test_pre", CLK_IGNORE_UNUSED, - RK3368_CLKSEL_CON(58), 0, 5, DFLAGS, - RK3368_CLKGATE_CON(13), 11, GFLAGS), + RK3399_CLKSEL_CON(58), 0, 5, DFLAGS, + RK3399_CLKGATE_CON(13), 11, GFLAGS), /* ddrc */ GATE(0, "clk_ddrc_lpll_src", "lpll", 0, RK3399_CLKGATE_CON(3), diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 1653edd792a5..d67eecc4ade9 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -34,6 +34,21 @@ struct clk; #define HIWORD_UPDATE(val, mask, shift) \ ((val) << (shift) | (mask) << ((shift) + 16)) +/* register positions shared by RK1108, RK2928, RK3036, RK3066, RK3188 and RK3228 */ +#define RK1108_PLL_CON(x) ((x) * 0x4) +#define RK1108_CLKSEL_CON(x) ((x) * 0x4 + 0x60) +#define RK1108_CLKGATE_CON(x) ((x) * 0x4 + 0x120) +#define RK1108_SOFTRST_CON(x) ((x) * 0x4 + 0x180) +#define RK1108_GLB_SRST_FST 0x1c0 +#define RK1108_GLB_SRST_SND 0x1c4 +#define RK1108_MISC_CON 0x1cc +#define RK1108_SDMMC_CON0 0x1d8 +#define RK1108_SDMMC_CON1 0x1dc +#define RK1108_SDIO_CON0 0x1e0 +#define RK1108_SDIO_CON1 0x1e4 +#define RK1108_EMMC_CON0 0x1e8 +#define RK1108_EMMC_CON1 0x1ec + #define RK2928_PLL_CON(x) ((x) * 0x4) #define RK2928_MODE_CON 0x40 #define RK2928_CLKSEL_CON(x) ((x) * 0x4 + 0x44) diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index ea1608682d7f..f096bd7df40c 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -543,7 +543,7 @@ static const struct samsung_div_clock top_div_clks[] __initconst = { static const struct samsung_gate_clock top_gate_clks[] __initconst = { /* ENABLE_ACLK_TOP */ GATE(CLK_ACLK_G3D_400, "aclk_g3d_400", "div_aclk_g3d_400", - ENABLE_ACLK_TOP, 30, 0, 0), + ENABLE_ACLK_TOP, 30, CLK_IS_CRITICAL, 0), GATE(CLK_ACLK_IMEM_SSX_266, "aclk_imem_ssx_266", "div_aclk_imem_sssx_266", ENABLE_ACLK_TOP, 29, CLK_IGNORE_UNUSED, 0), @@ -555,25 +555,25 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = { CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), GATE(CLK_ACLK_IMEM_200, "aclk_imem_200", "div_aclk_imem_266", ENABLE_ACLK_TOP, 24, - CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), + CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0), GATE(CLK_ACLK_IMEM_266, "aclk_imem_266", "div_aclk_imem_200", ENABLE_ACLK_TOP, 23, CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), GATE(CLK_ACLK_PERIC_66, "aclk_peric_66", "div_aclk_peric_66_b", ENABLE_ACLK_TOP, 22, - CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), GATE(CLK_ACLK_PERIS_66, "aclk_peris_66", "div_aclk_peris_66_b", ENABLE_ACLK_TOP, 21, - CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), GATE(CLK_ACLK_MSCL_400, "aclk_mscl_400", "div_aclk_mscl_400", ENABLE_ACLK_TOP, 19, - CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), GATE(CLK_ACLK_FSYS_200, "aclk_fsys_200", "div_aclk_fsys_200", ENABLE_ACLK_TOP, 18, - CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), GATE(CLK_ACLK_GSCL_111, "aclk_gscl_111", "div_aclk_gscl_111", ENABLE_ACLK_TOP, 15, - CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), GATE(CLK_ACLK_GSCL_333, "aclk_gscl_333", "div_aclk_gscl_333", ENABLE_ACLK_TOP, 14, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), @@ -582,7 +582,7 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = { CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_CAM1_400, "aclk_cam1_400", "div_aclk_cam1_400", ENABLE_ACLK_TOP, 12, - CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), GATE(CLK_ACLK_CAM1_552, "aclk_cam1_552", "div_aclk_cam1_552", ENABLE_ACLK_TOP, 11, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), @@ -591,7 +591,7 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = { CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_CAM0_400, "aclk_cam0_400", "div_aclk_cam0_400", ENABLE_ACLK_TOP, 9, - CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), GATE(CLK_ACLK_CAM0_552, "aclk_cam0_552", "div_aclk_cam0_552", ENABLE_ACLK_TOP, 8, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), @@ -600,19 +600,19 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = { CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_ISP_400, "aclk_isp_400", "div_aclk_isp_400", ENABLE_ACLK_TOP, 6, - CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), GATE(CLK_ACLK_HEVC_400, "aclk_hevc_400", "div_aclk_hevc_400", ENABLE_ACLK_TOP, 5, - CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), GATE(CLK_ACLK_MFC_400, "aclk_mfc_400", "div_aclk_mfc_400", ENABLE_ACLK_TOP, 3, - CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), GATE(CLK_ACLK_G2D_266, "aclk_g2d_266", "div_aclk_g2d_266", ENABLE_ACLK_TOP, 2, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_G2D_400, "aclk_g2d_400", "div_aclk_g2d_400", ENABLE_ACLK_TOP, 0, - CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), /* ENABLE_SCLK_TOP_MSCL */ GATE(CLK_SCLK_JPEG_MSCL, "sclk_jpeg_mscl", "div_sclk_jpeg", @@ -1385,7 +1385,7 @@ static const struct samsung_gate_clock mif_gate_clks[] __initconst = { CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), GATE(CLK_ACLK_DISP_333, "aclk_disp_333", "div_aclk_disp_333", ENABLE_ACLK_MIF3, 1, - CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), + CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0), GATE(CLK_ACLK_CPIF_200, "aclk_cpif_200", "div_aclk_cpif_200", ENABLE_ACLK_MIF3, 0, CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), @@ -1929,7 +1929,7 @@ CLK_OF_DECLARE(exynos5433_cmu_peris, "samsung,exynos5433-cmu-peris", /* list of all parent clock list */ PNAME(mout_sclk_ufs_mphy_user_p) = { "oscclk", "sclk_ufs_mphy", }; -PNAME(mout_aclk_fsys_200_user_p) = { "oscclk", "div_aclk_fsys_200", }; +PNAME(mout_aclk_fsys_200_user_p) = { "oscclk", "aclk_fsys_200", }; PNAME(mout_sclk_pcie_100_user_p) = { "oscclk", "sclk_pcie_100_fsys",}; PNAME(mout_sclk_ufsunipro_user_p) = { "oscclk", "sclk_ufsunipro_fsys",}; PNAME(mout_sclk_mmc2_user_p) = { "oscclk", "sclk_mmc2_fsys", }; diff --git a/drivers/clk/st/clk-flexgen.c b/drivers/clk/st/clk-flexgen.c index a485f3b284b9..918ba3164da9 100644 --- a/drivers/clk/st/clk-flexgen.c +++ b/drivers/clk/st/clk-flexgen.c @@ -329,8 +329,10 @@ static void __init st_of_flexgen_setup(struct device_node *np) return; parents = flexgen_get_parents(np, &num_parents); - if (!parents) + if (!parents) { + iounmap(reg); return; + } match = of_match_node(flexgen_of_match, np); if (match) { @@ -394,6 +396,7 @@ static void __init st_of_flexgen_setup(struct device_node *np) return; err: + iounmap(reg); if (clk_data) kfree(clk_data->clks); kfree(clk_data); diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig index 254d9526c018..8454c6e3dd65 100644 --- a/drivers/clk/sunxi-ng/Kconfig +++ b/drivers/clk/sunxi-ng/Kconfig @@ -35,17 +35,14 @@ config SUNXI_CCU_NK config SUNXI_CCU_NKM bool - select RATIONAL select SUNXI_CCU_GATE config SUNXI_CCU_NKMP bool - select RATIONAL select SUNXI_CCU_GATE config SUNXI_CCU_NM bool - select RATIONAL select SUNXI_CCU_FRAC select SUNXI_CCU_GATE @@ -56,6 +53,17 @@ config SUNXI_CCU_MP # SoC Drivers +config SUN50I_A64_CCU + bool "Support for the Allwinner A64 CCU" + select SUNXI_CCU_DIV + select SUNXI_CCU_NK + select SUNXI_CCU_NKM + select SUNXI_CCU_NKMP + select SUNXI_CCU_NM + select SUNXI_CCU_MP + select SUNXI_CCU_PHASE + default ARM64 && ARCH_SUNXI + config SUN6I_A31_CCU bool "Support for the Allwinner A31/A31s CCU" select SUNXI_CCU_DIV diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile index 106cba27c331..24fbc6e5deb8 100644 --- a/drivers/clk/sunxi-ng/Makefile +++ b/drivers/clk/sunxi-ng/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_SUNXI_CCU_NM) += ccu_nm.o obj-$(CONFIG_SUNXI_CCU_MP) += ccu_mp.o # SoC support +obj-$(CONFIG_SUN50I_A64_CCU) += ccu-sun50i-a64.o obj-$(CONFIG_SUN6I_A31_CCU) += ccu-sun6i-a31.o obj-$(CONFIG_SUN8I_A23_CCU) += ccu-sun8i-a23.o obj-$(CONFIG_SUN8I_A33_CCU) += ccu-sun8i-a33.o diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c new file mode 100644 index 000000000000..e3c084cc6da5 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c @@ -0,0 +1,915 @@ +/* + * Copyright (c) 2016 Maxime Ripard. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mp.h" +#include "ccu_mult.h" +#include "ccu_nk.h" +#include "ccu_nkm.h" +#include "ccu_nkmp.h" +#include "ccu_nm.h" +#include "ccu_phase.h" + +#include "ccu-sun50i-a64.h" + +static struct ccu_nkmp pll_cpux_clk = { + .enable = BIT(31), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 5), + .k = _SUNXI_CCU_MULT(4, 2), + .m = _SUNXI_CCU_DIV(0, 2), + .p = _SUNXI_CCU_DIV_MAX(16, 2, 4), + .common = { + .reg = 0x000, + .hw.init = CLK_HW_INIT("pll-cpux", + "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +/* + * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from + * the base (2x, 4x and 8x), and one variable divider (the one true + * pll audio). + * + * We don't have any need for the variable divider for now, so we just + * hardcode it to match with the clock names + */ +#define SUN50I_A64_PLL_AUDIO_REG 0x008 + +static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", + "osc24M", 0x008, + 8, 7, /* N */ + 0, 5, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video0_clk, "pll-video0", + "osc24M", 0x010, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve", + "osc24M", 0x018, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr0_clk, "pll-ddr0", + "osc24M", 0x020, + 8, 5, /* N */ + 4, 2, /* K */ + 0, 2, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static struct ccu_nk pll_periph0_clk = { + .enable = BIT(31), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 5), + .k = _SUNXI_CCU_MULT_MIN(4, 2, 2), + .fixed_post_div = 2, + .common = { + .reg = 0x028, + .features = CCU_FEATURE_FIXED_POSTDIV, + .hw.init = CLK_HW_INIT("pll-periph0", "osc24M", + &ccu_nk_ops, CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nk pll_periph1_clk = { + .enable = BIT(31), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 5), + .k = _SUNXI_CCU_MULT_MIN(4, 2, 2), + .fixed_post_div = 2, + .common = { + .reg = 0x02c, + .features = CCU_FEATURE_FIXED_POSTDIV, + .hw.init = CLK_HW_INIT("pll-periph1", "osc24M", + &ccu_nk_ops, CLK_SET_RATE_UNGATE), + }, +}; + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video1_clk, "pll-video1", + "osc24M", 0x030, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu", + "osc24M", 0x038, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* + * The output function can be changed to something more complex that + * we do not handle yet. + * + * Hardcode the mode so that we don't fall in that case. + */ +#define SUN50I_A64_PLL_MIPI_REG 0x040 + +static struct ccu_nkm pll_mipi_clk = { + .enable = BIT(31), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 4), + .k = _SUNXI_CCU_MULT_MIN(4, 2, 2), + .m = _SUNXI_CCU_DIV(0, 4), + .common = { + .reg = 0x040, + .hw.init = CLK_HW_INIT("pll-mipi", "pll-video0", + &ccu_nkm_ops, CLK_SET_RATE_UNGATE), + }, +}; + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_hsic_clk, "pll-hsic", + "osc24M", 0x044, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de", + "osc24M", 0x048, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_ddr1_clk, "pll-ddr1", + "osc24M", 0x04c, + 8, 7, /* N */ + 0, 2, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static const char * const cpux_parents[] = { "osc32k", "osc24M", + "pll-cpux", "pll-cpux" }; +static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents, + 0x050, 16, 2, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + +static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0); + +static const char * const ahb1_parents[] = { "osc32k", "osc24M", + "axi", "pll-periph0" }; +static struct ccu_div ahb1_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + + .mux = { + .shift = 12, + .width = 2, + + .variable_prediv = { + .index = 3, + .shift = 6, + .width = 2, + }, + }, + + .common = { + .reg = 0x054, + .features = CCU_FEATURE_VARIABLE_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("ahb1", + ahb1_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct clk_div_table apb1_div_table[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { /* Sentinel */ }, +}; +static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1", + 0x054, 8, 2, apb1_div_table, 0); + +static const char * const apb2_parents[] = { "osc32k", "osc24M", + "pll-periph0-2x", + "pll-periph0-2x" }; +static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058, + 0, 5, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + 0); + +static const char * const ahb2_parents[] = { "ahb1", "pll-periph0" }; +static const struct ccu_mux_fixed_prediv ahb2_fixed_predivs[] = { + { .index = 1, .div = 2 }, +}; +static struct ccu_mux ahb2_clk = { + .mux = { + .shift = 0, + .width = 1, + .fixed_predivs = ahb2_fixed_predivs, + .n_predivs = ARRAY_SIZE(ahb2_fixed_predivs), + }, + + .common = { + .reg = 0x05c, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("ahb2", + ahb2_parents, + &ccu_mux_ops, + 0), + }, +}; + +static SUNXI_CCU_GATE(bus_mipi_dsi_clk, "bus-mipi-dsi", "ahb1", + 0x060, BIT(1), 0); +static SUNXI_CCU_GATE(bus_ce_clk, "bus-ce", "ahb1", + 0x060, BIT(5), 0); +static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "ahb1", + 0x060, BIT(6), 0); +static SUNXI_CCU_GATE(bus_mmc0_clk, "bus-mmc0", "ahb1", + 0x060, BIT(8), 0); +static SUNXI_CCU_GATE(bus_mmc1_clk, "bus-mmc1", "ahb1", + 0x060, BIT(9), 0); +static SUNXI_CCU_GATE(bus_mmc2_clk, "bus-mmc2", "ahb1", + 0x060, BIT(10), 0); +static SUNXI_CCU_GATE(bus_nand_clk, "bus-nand", "ahb1", + 0x060, BIT(13), 0); +static SUNXI_CCU_GATE(bus_dram_clk, "bus-dram", "ahb1", + 0x060, BIT(14), 0); +static SUNXI_CCU_GATE(bus_emac_clk, "bus-emac", "ahb2", + 0x060, BIT(17), 0); +static SUNXI_CCU_GATE(bus_ts_clk, "bus-ts", "ahb1", + 0x060, BIT(18), 0); +static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "ahb1", + 0x060, BIT(19), 0); +static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb1", + 0x060, BIT(20), 0); +static SUNXI_CCU_GATE(bus_spi1_clk, "bus-spi1", "ahb1", + 0x060, BIT(21), 0); +static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb1", + 0x060, BIT(23), 0); +static SUNXI_CCU_GATE(bus_ehci0_clk, "bus-ehci0", "ahb1", + 0x060, BIT(24), 0); +static SUNXI_CCU_GATE(bus_ehci1_clk, "bus-ehci1", "ahb2", + 0x060, BIT(25), 0); +static SUNXI_CCU_GATE(bus_ohci0_clk, "bus-ohci0", "ahb1", + 0x060, BIT(28), 0); +static SUNXI_CCU_GATE(bus_ohci1_clk, "bus-ohci1", "ahb2", + 0x060, BIT(29), 0); + +static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "ahb1", + 0x064, BIT(0), 0); +static SUNXI_CCU_GATE(bus_tcon0_clk, "bus-tcon0", "ahb1", + 0x064, BIT(3), 0); +static SUNXI_CCU_GATE(bus_tcon1_clk, "bus-tcon1", "ahb1", + 0x064, BIT(4), 0); +static SUNXI_CCU_GATE(bus_deinterlace_clk, "bus-deinterlace", "ahb1", + 0x064, BIT(5), 0); +static SUNXI_CCU_GATE(bus_csi_clk, "bus-csi", "ahb1", + 0x064, BIT(8), 0); +static SUNXI_CCU_GATE(bus_hdmi_clk, "bus-hdmi", "ahb1", + 0x064, BIT(11), 0); +static SUNXI_CCU_GATE(bus_de_clk, "bus-de", "ahb1", + 0x064, BIT(12), 0); +static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "ahb1", + 0x064, BIT(20), 0); +static SUNXI_CCU_GATE(bus_msgbox_clk, "bus-msgbox", "ahb1", + 0x064, BIT(21), 0); +static SUNXI_CCU_GATE(bus_spinlock_clk, "bus-spinlock", "ahb1", + 0x064, BIT(22), 0); + +static SUNXI_CCU_GATE(bus_codec_clk, "bus-codec", "apb1", + 0x068, BIT(0), 0); +static SUNXI_CCU_GATE(bus_spdif_clk, "bus-spdif", "apb1", + 0x068, BIT(1), 0); +static SUNXI_CCU_GATE(bus_pio_clk, "bus-pio", "apb1", + 0x068, BIT(5), 0); +static SUNXI_CCU_GATE(bus_ths_clk, "bus-ths", "apb1", + 0x068, BIT(8), 0); +static SUNXI_CCU_GATE(bus_i2s0_clk, "bus-i2s0", "apb1", + 0x068, BIT(12), 0); +static SUNXI_CCU_GATE(bus_i2s1_clk, "bus-i2s1", "apb1", + 0x068, BIT(13), 0); +static SUNXI_CCU_GATE(bus_i2s2_clk, "bus-i2s2", "apb1", + 0x068, BIT(14), 0); + +static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2", + 0x06c, BIT(0), 0); +static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb2", + 0x06c, BIT(1), 0); +static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb2", + 0x06c, BIT(2), 0); +static SUNXI_CCU_GATE(bus_scr_clk, "bus-scr", "apb2", + 0x06c, BIT(5), 0); +static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb2", + 0x06c, BIT(16), 0); +static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb2", + 0x06c, BIT(17), 0); +static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb2", + 0x06c, BIT(18), 0); +static SUNXI_CCU_GATE(bus_uart3_clk, "bus-uart3", "apb2", + 0x06c, BIT(19), 0); +static SUNXI_CCU_GATE(bus_uart4_clk, "bus-uart4", "apb2", + 0x06c, BIT(20), 0); + +static SUNXI_CCU_GATE(bus_dbg_clk, "bus-dbg", "ahb1", + 0x070, BIT(7), 0); + +static struct clk_div_table ths_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 6 }, +}; +static const char * const ths_parents[] = { "osc24M" }; +static struct ccu_div ths_clk = { + .enable = BIT(31), + .div = _SUNXI_CCU_DIV_TABLE(0, 2, ths_div_table), + .mux = _SUNXI_CCU_MUX(24, 2), + .common = { + .reg = 0x074, + .hw.init = CLK_HW_INIT_PARENTS("ths", + ths_parents, + &ccu_div_ops, + 0), + }, +}; + +static const char * const mod0_default_parents[] = { "osc24M", "pll-periph0", + "pll-periph1" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const mmc_default_parents[] = { "osc24M", "pll-periph0-2x", + "pll-periph1-2x" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mmc_default_parents, 0x088, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mmc_default_parents, 0x08c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mmc_default_parents, 0x090, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const ts_parents[] = { "osc24M", "pll-periph0", }; +static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", ts_parents, 0x098, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ce_clk, "ce", mmc_default_parents, 0x09c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x", + "pll-audio-2x", "pll-audio" }; +static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents, + 0x0b0, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents, + 0x0b4, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_MUX_WITH_GATE(i2s2_clk, "i2s2", i2s_parents, + 0x0b8, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_GATE(spdif_clk, "spdif", "pll-audio", + 0x0c0, 0, 4, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", + 0x0cc, BIT(8), 0); +static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "osc24M", + 0x0cc, BIT(9), 0); +static SUNXI_CCU_GATE(usb_hsic_clk, "usb-hsic", "pll-hsic", + 0x0cc, BIT(10), 0); +static SUNXI_CCU_GATE(usb_hsic_12m_clk, "usb-hsic-12M", "osc12M", + 0x0cc, BIT(11), 0); +static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc12M", + 0x0cc, BIT(16), 0); +static SUNXI_CCU_GATE(usb_ohci1_clk, "usb-ohci1", "usb-ohci0", + 0x0cc, BIT(17), 0); + +static const char * const dram_parents[] = { "pll-ddr0", "pll-ddr1" }; +static SUNXI_CCU_M_WITH_MUX(dram_clk, "dram", dram_parents, + 0x0f4, 0, 4, 20, 2, CLK_IS_CRITICAL); + +static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "dram", + 0x100, BIT(0), 0); +static SUNXI_CCU_GATE(dram_csi_clk, "dram-csi", "dram", + 0x100, BIT(1), 0); +static SUNXI_CCU_GATE(dram_deinterlace_clk, "dram-deinterlace", "dram", + 0x100, BIT(2), 0); +static SUNXI_CCU_GATE(dram_ts_clk, "dram-ts", "dram", + 0x100, BIT(3), 0); + +static const char * const de_parents[] = { "pll-periph0-2x", "pll-de" }; +static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents, + 0x104, 0, 4, 24, 3, BIT(31), 0); + +static const char * const tcon0_parents[] = { "pll-mipi", "pll-video0-2x" }; +static const u8 tcon0_table[] = { 0, 2, }; +static SUNXI_CCU_MUX_TABLE_WITH_GATE(tcon0_clk, "tcon0", tcon0_parents, + tcon0_table, 0x118, 24, 3, BIT(31), + CLK_SET_RATE_PARENT); + +static const char * const tcon1_parents[] = { "pll-video0", "pll-video1" }; +static const u8 tcon1_table[] = { 0, 2, }; +static struct ccu_div tcon1_clk = { + .enable = BIT(31), + .div = _SUNXI_CCU_DIV(0, 4), + .mux = _SUNXI_CCU_MUX_TABLE(24, 2, tcon1_table), + .common = { + .reg = 0x11c, + .hw.init = CLK_HW_INIT_PARENTS("tcon1", + tcon1_parents, + &ccu_div_ops, + CLK_SET_RATE_PARENT), + }, +}; + +static const char * const deinterlace_parents[] = { "pll-periph0", "pll-periph1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(deinterlace_clk, "deinterlace", deinterlace_parents, + 0x124, 0, 4, 24, 3, BIT(31), 0); + +static SUNXI_CCU_GATE(csi_misc_clk, "csi-misc", "osc24M", + 0x130, BIT(31), 0); + +static const char * const csi_sclk_parents[] = { "pll-periph0", "pll-periph1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(csi_sclk_clk, "csi-sclk", csi_sclk_parents, + 0x134, 16, 4, 24, 3, BIT(31), 0); + +static const char * const csi_mclk_parents[] = { "osc24M", "pll-video1", "pll-periph1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(csi_mclk_clk, "csi-mclk", csi_mclk_parents, + 0x134, 0, 5, 8, 3, BIT(15), 0); + +static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", + 0x13c, 16, 3, BIT(31), 0); + +static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio", + 0x140, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(ac_dig_4x_clk, "ac-dig-4x", "pll-audio-4x", + 0x140, BIT(30), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", + 0x144, BIT(31), 0); + +static const char * const hdmi_parents[] = { "pll-video0", "pll-video1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", hdmi_parents, + 0x150, 0, 4, 24, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(hdmi_ddc_clk, "hdmi-ddc", "osc24M", + 0x154, BIT(31), 0); + +static const char * const mbus_parents[] = { "osc24M", "pll-periph0-2x", + "pll-ddr0", "pll-ddr1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents, + 0x15c, 0, 3, 24, 2, BIT(31), CLK_IS_CRITICAL); + +static const char * const dsi_dphy_parents[] = { "pll-video0", "pll-periph0" }; +static const u8 dsi_dphy_table[] = { 0, 2, }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_dphy_clk, "dsi-dphy", + dsi_dphy_parents, dsi_dphy_table, + 0x168, 0, 4, 8, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu", + 0x1a0, 0, 3, BIT(31), CLK_SET_RATE_PARENT); + +/* Fixed Factor clocks */ +static CLK_FIXED_FACTOR(osc12M_clk, "osc12M", "osc24M", 1, 2, 0); + +/* We hardcode the divider to 4 for now */ +static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", + "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", + "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x", + "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_periph0_2x_clk, "pll-periph0-2x", + "pll-periph0", 1, 2, 0); +static CLK_FIXED_FACTOR(pll_periph1_2x_clk, "pll-periph1-2x", + "pll-periph1", 1, 2, 0); +static CLK_FIXED_FACTOR(pll_video0_2x_clk, "pll-video0-2x", + "pll-video0", 1, 2, CLK_SET_RATE_PARENT); + +static struct ccu_common *sun50i_a64_ccu_clks[] = { + &pll_cpux_clk.common, + &pll_audio_base_clk.common, + &pll_video0_clk.common, + &pll_ve_clk.common, + &pll_ddr0_clk.common, + &pll_periph0_clk.common, + &pll_periph1_clk.common, + &pll_video1_clk.common, + &pll_gpu_clk.common, + &pll_mipi_clk.common, + &pll_hsic_clk.common, + &pll_de_clk.common, + &pll_ddr1_clk.common, + &cpux_clk.common, + &axi_clk.common, + &ahb1_clk.common, + &apb1_clk.common, + &apb2_clk.common, + &ahb2_clk.common, + &bus_mipi_dsi_clk.common, + &bus_ce_clk.common, + &bus_dma_clk.common, + &bus_mmc0_clk.common, + &bus_mmc1_clk.common, + &bus_mmc2_clk.common, + &bus_nand_clk.common, + &bus_dram_clk.common, + &bus_emac_clk.common, + &bus_ts_clk.common, + &bus_hstimer_clk.common, + &bus_spi0_clk.common, + &bus_spi1_clk.common, + &bus_otg_clk.common, + &bus_ehci0_clk.common, + &bus_ehci1_clk.common, + &bus_ohci0_clk.common, + &bus_ohci1_clk.common, + &bus_ve_clk.common, + &bus_tcon0_clk.common, + &bus_tcon1_clk.common, + &bus_deinterlace_clk.common, + &bus_csi_clk.common, + &bus_hdmi_clk.common, + &bus_de_clk.common, + &bus_gpu_clk.common, + &bus_msgbox_clk.common, + &bus_spinlock_clk.common, + &bus_codec_clk.common, + &bus_spdif_clk.common, + &bus_pio_clk.common, + &bus_ths_clk.common, + &bus_i2s0_clk.common, + &bus_i2s1_clk.common, + &bus_i2s2_clk.common, + &bus_i2c0_clk.common, + &bus_i2c1_clk.common, + &bus_i2c2_clk.common, + &bus_scr_clk.common, + &bus_uart0_clk.common, + &bus_uart1_clk.common, + &bus_uart2_clk.common, + &bus_uart3_clk.common, + &bus_uart4_clk.common, + &bus_dbg_clk.common, + &ths_clk.common, + &nand_clk.common, + &mmc0_clk.common, + &mmc1_clk.common, + &mmc2_clk.common, + &ts_clk.common, + &ce_clk.common, + &spi0_clk.common, + &spi1_clk.common, + &i2s0_clk.common, + &i2s1_clk.common, + &i2s2_clk.common, + &spdif_clk.common, + &usb_phy0_clk.common, + &usb_phy1_clk.common, + &usb_hsic_clk.common, + &usb_hsic_12m_clk.common, + &usb_ohci0_clk.common, + &usb_ohci1_clk.common, + &dram_clk.common, + &dram_ve_clk.common, + &dram_csi_clk.common, + &dram_deinterlace_clk.common, + &dram_ts_clk.common, + &de_clk.common, + &tcon0_clk.common, + &tcon1_clk.common, + &deinterlace_clk.common, + &csi_misc_clk.common, + &csi_sclk_clk.common, + &csi_mclk_clk.common, + &ve_clk.common, + &ac_dig_clk.common, + &ac_dig_4x_clk.common, + &avs_clk.common, + &hdmi_clk.common, + &hdmi_ddc_clk.common, + &mbus_clk.common, + &dsi_dphy_clk.common, + &gpu_clk.common, +}; + +static struct clk_hw_onecell_data sun50i_a64_hw_clks = { + .hws = { + [CLK_OSC_12M] = &osc12M_clk.hw, + [CLK_PLL_CPUX] = &pll_cpux_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw, + [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw, + [CLK_PLL_VE] = &pll_ve_clk.common.hw, + [CLK_PLL_DDR0] = &pll_ddr0_clk.common.hw, + [CLK_PLL_PERIPH0] = &pll_periph0_clk.common.hw, + [CLK_PLL_PERIPH0_2X] = &pll_periph0_2x_clk.hw, + [CLK_PLL_PERIPH1] = &pll_periph1_clk.common.hw, + [CLK_PLL_PERIPH1_2X] = &pll_periph1_2x_clk.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw, + [CLK_PLL_GPU] = &pll_gpu_clk.common.hw, + [CLK_PLL_MIPI] = &pll_mipi_clk.common.hw, + [CLK_PLL_HSIC] = &pll_hsic_clk.common.hw, + [CLK_PLL_DE] = &pll_de_clk.common.hw, + [CLK_PLL_DDR1] = &pll_ddr1_clk.common.hw, + [CLK_CPUX] = &cpux_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AHB1] = &ahb1_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_APB2] = &apb2_clk.common.hw, + [CLK_AHB2] = &ahb2_clk.common.hw, + [CLK_BUS_MIPI_DSI] = &bus_mipi_dsi_clk.common.hw, + [CLK_BUS_CE] = &bus_ce_clk.common.hw, + [CLK_BUS_DMA] = &bus_dma_clk.common.hw, + [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw, + [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw, + [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw, + [CLK_BUS_NAND] = &bus_nand_clk.common.hw, + [CLK_BUS_DRAM] = &bus_dram_clk.common.hw, + [CLK_BUS_EMAC] = &bus_emac_clk.common.hw, + [CLK_BUS_TS] = &bus_ts_clk.common.hw, + [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw, + [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw, + [CLK_BUS_SPI1] = &bus_spi1_clk.common.hw, + [CLK_BUS_OTG] = &bus_otg_clk.common.hw, + [CLK_BUS_EHCI0] = &bus_ehci0_clk.common.hw, + [CLK_BUS_EHCI1] = &bus_ehci1_clk.common.hw, + [CLK_BUS_OHCI0] = &bus_ohci0_clk.common.hw, + [CLK_BUS_OHCI1] = &bus_ohci1_clk.common.hw, + [CLK_BUS_VE] = &bus_ve_clk.common.hw, + [CLK_BUS_TCON0] = &bus_tcon0_clk.common.hw, + [CLK_BUS_TCON1] = &bus_tcon1_clk.common.hw, + [CLK_BUS_DEINTERLACE] = &bus_deinterlace_clk.common.hw, + [CLK_BUS_CSI] = &bus_csi_clk.common.hw, + [CLK_BUS_HDMI] = &bus_hdmi_clk.common.hw, + [CLK_BUS_DE] = &bus_de_clk.common.hw, + [CLK_BUS_GPU] = &bus_gpu_clk.common.hw, + [CLK_BUS_MSGBOX] = &bus_msgbox_clk.common.hw, + [CLK_BUS_SPINLOCK] = &bus_spinlock_clk.common.hw, + [CLK_BUS_CODEC] = &bus_codec_clk.common.hw, + [CLK_BUS_SPDIF] = &bus_spdif_clk.common.hw, + [CLK_BUS_PIO] = &bus_pio_clk.common.hw, + [CLK_BUS_THS] = &bus_ths_clk.common.hw, + [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw, + [CLK_BUS_I2S1] = &bus_i2s1_clk.common.hw, + [CLK_BUS_I2S2] = &bus_i2s2_clk.common.hw, + [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw, + [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw, + [CLK_BUS_I2C2] = &bus_i2c2_clk.common.hw, + [CLK_BUS_UART0] = &bus_uart0_clk.common.hw, + [CLK_BUS_UART1] = &bus_uart1_clk.common.hw, + [CLK_BUS_UART2] = &bus_uart2_clk.common.hw, + [CLK_BUS_UART3] = &bus_uart3_clk.common.hw, + [CLK_BUS_UART4] = &bus_uart4_clk.common.hw, + [CLK_BUS_SCR] = &bus_scr_clk.common.hw, + [CLK_BUS_DBG] = &bus_dbg_clk.common.hw, + [CLK_THS] = &ths_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_TS] = &ts_clk.common.hw, + [CLK_CE] = &ce_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_I2S0] = &i2s0_clk.common.hw, + [CLK_I2S1] = &i2s1_clk.common.hw, + [CLK_I2S2] = &i2s2_clk.common.hw, + [CLK_SPDIF] = &spdif_clk.common.hw, + [CLK_USB_PHY0] = &usb_phy0_clk.common.hw, + [CLK_USB_PHY1] = &usb_phy1_clk.common.hw, + [CLK_USB_HSIC] = &usb_hsic_clk.common.hw, + [CLK_USB_HSIC_12M] = &usb_hsic_12m_clk.common.hw, + [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw, + [CLK_USB_OHCI1] = &usb_ohci1_clk.common.hw, + [CLK_DRAM] = &dram_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI] = &dram_csi_clk.common.hw, + [CLK_DRAM_DEINTERLACE] = &dram_deinterlace_clk.common.hw, + [CLK_DRAM_TS] = &dram_ts_clk.common.hw, + [CLK_DE] = &de_clk.common.hw, + [CLK_TCON0] = &tcon0_clk.common.hw, + [CLK_TCON1] = &tcon1_clk.common.hw, + [CLK_DEINTERLACE] = &deinterlace_clk.common.hw, + [CLK_CSI_MISC] = &csi_misc_clk.common.hw, + [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw, + [CLK_CSI_MCLK] = &csi_mclk_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_AC_DIG] = &ac_dig_clk.common.hw, + [CLK_AC_DIG_4X] = &ac_dig_4x_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_HDMI] = &hdmi_clk.common.hw, + [CLK_HDMI_DDC] = &hdmi_ddc_clk.common.hw, + [CLK_MBUS] = &mbus_clk.common.hw, + [CLK_DSI_DPHY] = &dsi_dphy_clk.common.hw, + [CLK_GPU] = &gpu_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct ccu_reset_map sun50i_a64_ccu_resets[] = { + [RST_USB_PHY0] = { 0x0cc, BIT(0) }, + [RST_USB_PHY1] = { 0x0cc, BIT(1) }, + [RST_USB_HSIC] = { 0x0cc, BIT(2) }, + + [RST_DRAM] = { 0x0f4, BIT(31) }, + [RST_MBUS] = { 0x0fc, BIT(31) }, + + [RST_BUS_MIPI_DSI] = { 0x2c0, BIT(1) }, + [RST_BUS_CE] = { 0x2c0, BIT(5) }, + [RST_BUS_DMA] = { 0x2c0, BIT(6) }, + [RST_BUS_MMC0] = { 0x2c0, BIT(8) }, + [RST_BUS_MMC1] = { 0x2c0, BIT(9) }, + [RST_BUS_MMC2] = { 0x2c0, BIT(10) }, + [RST_BUS_NAND] = { 0x2c0, BIT(13) }, + [RST_BUS_DRAM] = { 0x2c0, BIT(14) }, + [RST_BUS_EMAC] = { 0x2c0, BIT(17) }, + [RST_BUS_TS] = { 0x2c0, BIT(18) }, + [RST_BUS_HSTIMER] = { 0x2c0, BIT(19) }, + [RST_BUS_SPI0] = { 0x2c0, BIT(20) }, + [RST_BUS_SPI1] = { 0x2c0, BIT(21) }, + [RST_BUS_OTG] = { 0x2c0, BIT(23) }, + [RST_BUS_EHCI0] = { 0x2c0, BIT(24) }, + [RST_BUS_EHCI1] = { 0x2c0, BIT(25) }, + [RST_BUS_OHCI0] = { 0x2c0, BIT(28) }, + [RST_BUS_OHCI1] = { 0x2c0, BIT(29) }, + + [RST_BUS_VE] = { 0x2c4, BIT(0) }, + [RST_BUS_TCON0] = { 0x2c4, BIT(3) }, + [RST_BUS_TCON1] = { 0x2c4, BIT(4) }, + [RST_BUS_DEINTERLACE] = { 0x2c4, BIT(5) }, + [RST_BUS_CSI] = { 0x2c4, BIT(8) }, + [RST_BUS_HDMI0] = { 0x2c4, BIT(10) }, + [RST_BUS_HDMI1] = { 0x2c4, BIT(11) }, + [RST_BUS_DE] = { 0x2c4, BIT(12) }, + [RST_BUS_GPU] = { 0x2c4, BIT(20) }, + [RST_BUS_MSGBOX] = { 0x2c4, BIT(21) }, + [RST_BUS_SPINLOCK] = { 0x2c4, BIT(22) }, + [RST_BUS_DBG] = { 0x2c4, BIT(31) }, + + [RST_BUS_LVDS] = { 0x2c8, BIT(0) }, + + [RST_BUS_CODEC] = { 0x2d0, BIT(0) }, + [RST_BUS_SPDIF] = { 0x2d0, BIT(1) }, + [RST_BUS_THS] = { 0x2d0, BIT(8) }, + [RST_BUS_I2S0] = { 0x2d0, BIT(12) }, + [RST_BUS_I2S1] = { 0x2d0, BIT(13) }, + [RST_BUS_I2S2] = { 0x2d0, BIT(14) }, + + [RST_BUS_I2C0] = { 0x2d8, BIT(0) }, + [RST_BUS_I2C1] = { 0x2d8, BIT(1) }, + [RST_BUS_I2C2] = { 0x2d8, BIT(2) }, + [RST_BUS_SCR] = { 0x2d8, BIT(5) }, + [RST_BUS_UART0] = { 0x2d8, BIT(16) }, + [RST_BUS_UART1] = { 0x2d8, BIT(17) }, + [RST_BUS_UART2] = { 0x2d8, BIT(18) }, + [RST_BUS_UART3] = { 0x2d8, BIT(19) }, + [RST_BUS_UART4] = { 0x2d8, BIT(20) }, +}; + +static const struct sunxi_ccu_desc sun50i_a64_ccu_desc = { + .ccu_clks = sun50i_a64_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun50i_a64_ccu_clks), + + .hw_clks = &sun50i_a64_hw_clks, + + .resets = sun50i_a64_ccu_resets, + .num_resets = ARRAY_SIZE(sun50i_a64_ccu_resets), +}; + +static int sun50i_a64_ccu_probe(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *reg; + u32 val; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + /* Force the PLL-Audio-1x divider to 4 */ + val = readl(reg + SUN50I_A64_PLL_AUDIO_REG); + val &= ~GENMASK(19, 16); + writel(val | (3 << 16), reg + SUN50I_A64_PLL_AUDIO_REG); + + writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG); + + return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a64_ccu_desc); +} + +static const struct of_device_id sun50i_a64_ccu_ids[] = { + { .compatible = "allwinner,sun50i-a64-ccu" }, + { } +}; + +static struct platform_driver sun50i_a64_ccu_driver = { + .probe = sun50i_a64_ccu_probe, + .driver = { + .name = "sun50i-a64-ccu", + .of_match_table = sun50i_a64_ccu_ids, + }, +}; +builtin_platform_driver(sun50i_a64_ccu_driver); diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.h b/drivers/clk/sunxi-ng/ccu-sun50i-a64.h new file mode 100644 index 000000000000..9b3cd24b78d2 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.h @@ -0,0 +1,72 @@ +/* + * Copyright 2016 Maxime Ripard + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CCU_SUN50I_A64_H_ +#define _CCU_SUN50I_A64_H_ + +#include <dt-bindings/clock/sun50i-a64-ccu.h> +#include <dt-bindings/reset/sun50i-a64-ccu.h> + +#define CLK_OSC_12M 0 +#define CLK_PLL_CPUX 1 +#define CLK_PLL_AUDIO_BASE 2 +#define CLK_PLL_AUDIO 3 +#define CLK_PLL_AUDIO_2X 4 +#define CLK_PLL_AUDIO_4X 5 +#define CLK_PLL_AUDIO_8X 6 +#define CLK_PLL_VIDEO0 7 +#define CLK_PLL_VIDEO0_2X 8 +#define CLK_PLL_VE 9 +#define CLK_PLL_DDR0 10 +#define CLK_PLL_PERIPH0 11 +#define CLK_PLL_PERIPH0_2X 12 +#define CLK_PLL_PERIPH1 13 +#define CLK_PLL_PERIPH1_2X 14 +#define CLK_PLL_VIDEO1 15 +#define CLK_PLL_GPU 16 +#define CLK_PLL_MIPI 17 +#define CLK_PLL_HSIC 18 +#define CLK_PLL_DE 19 +#define CLK_PLL_DDR1 20 +#define CLK_CPUX 21 +#define CLK_AXI 22 +#define CLK_APB 23 +#define CLK_AHB1 24 +#define CLK_APB1 25 +#define CLK_APB2 26 +#define CLK_AHB2 27 + +/* All the bus gates are exported */ + +/* The first bunch of module clocks are exported */ + +#define CLK_USB_OHCI0_12M 90 + +#define CLK_USB_OHCI1_12M 92 + +#define CLK_DRAM 94 + +/* All the DRAM gates are exported */ + +/* Some more module clocks are exported */ + +#define CLK_MBUS 112 + +/* And the DSI and GPU module clock is exported */ + +#define CLK_NUMBER (CLK_GPU + 1) + +#endif /* _CCU_SUN50I_A64_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c index 2646d980087b..5c6d37bdf247 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c @@ -344,10 +344,10 @@ static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4, static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x", "pll-audio-2x", "pll-audio" }; static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents, - 0x0b0, 16, 2, BIT(31), 0); + 0x0b0, 16, 2, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents, - 0x0b4, 16, 2, BIT(31), 0); + 0x0b4, 16, 2, BIT(31), CLK_SET_RATE_PARENT); /* TODO: the parent for most of the USB clocks is not known */ static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", @@ -415,7 +415,7 @@ static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", 0x13c, 16, 3, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio", - 0x140, BIT(31), 0); + 0x140, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", 0x144, BIT(31), 0); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c index 4d70590f05e3..21c427d86f28 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c @@ -394,16 +394,16 @@ static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4, static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x", "pll-audio-2x", "pll-audio" }; static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents, - 0x0b0, 16, 2, BIT(31), 0); + 0x0b0, 16, 2, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents, - 0x0b4, 16, 2, BIT(31), 0); + 0x0b4, 16, 2, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_MUX_WITH_GATE(i2s2_clk, "i2s2", i2s_parents, - 0x0b8, 16, 2, BIT(31), 0); + 0x0b8, 16, 2, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_M_WITH_GATE(spdif_clk, "spdif", "pll-audio", - 0x0c0, 0, 4, BIT(31), 0); + 0x0c0, 0, 4, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", 0x0cc, BIT(8), 0); @@ -466,7 +466,7 @@ static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", 0x13c, 16, 3, BIT(31), 0); static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio", - 0x140, BIT(31), 0); + 0x140, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", 0x144, BIT(31), 0); diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h index 34c338832c0d..06540f7cf41c 100644 --- a/drivers/clk/sunxi-ng/ccu_div.h +++ b/drivers/clk/sunxi-ng/ccu_div.h @@ -20,7 +20,7 @@ #include "ccu_mux.h" /** - * struct _ccu_div - Internal divider description + * struct ccu_div_internal - Internal divider description * @shift: Bit offset of the divider in its register * @width: Width of the divider field in its register * @max: Maximum value allowed for that divider. This is the @@ -36,7 +36,7 @@ * It is basically a wrapper around the clk_divider functions * arguments. */ -struct _ccu_div { +struct ccu_div_internal { u8 shift; u8 width; @@ -78,7 +78,7 @@ struct _ccu_div { struct ccu_div { u32 enable; - struct _ccu_div div; + struct ccu_div_internal div; struct ccu_mux_internal mux; struct ccu_common common; }; diff --git a/drivers/clk/sunxi-ng/ccu_frac.c b/drivers/clk/sunxi-ng/ccu_frac.c index 5c4b10cd15b5..8b5eb7756bf7 100644 --- a/drivers/clk/sunxi-ng/ccu_frac.c +++ b/drivers/clk/sunxi-ng/ccu_frac.c @@ -14,7 +14,7 @@ #include "ccu_frac.h" bool ccu_frac_helper_is_enabled(struct ccu_common *common, - struct _ccu_frac *cf) + struct ccu_frac_internal *cf) { if (!(common->features & CCU_FEATURE_FRACTIONAL)) return false; @@ -23,7 +23,7 @@ bool ccu_frac_helper_is_enabled(struct ccu_common *common, } void ccu_frac_helper_enable(struct ccu_common *common, - struct _ccu_frac *cf) + struct ccu_frac_internal *cf) { unsigned long flags; u32 reg; @@ -38,7 +38,7 @@ void ccu_frac_helper_enable(struct ccu_common *common, } void ccu_frac_helper_disable(struct ccu_common *common, - struct _ccu_frac *cf) + struct ccu_frac_internal *cf) { unsigned long flags; u32 reg; @@ -53,7 +53,7 @@ void ccu_frac_helper_disable(struct ccu_common *common, } bool ccu_frac_helper_has_rate(struct ccu_common *common, - struct _ccu_frac *cf, + struct ccu_frac_internal *cf, unsigned long rate) { if (!(common->features & CCU_FEATURE_FRACTIONAL)) @@ -63,7 +63,7 @@ bool ccu_frac_helper_has_rate(struct ccu_common *common, } unsigned long ccu_frac_helper_read_rate(struct ccu_common *common, - struct _ccu_frac *cf) + struct ccu_frac_internal *cf) { u32 reg; @@ -84,7 +84,7 @@ unsigned long ccu_frac_helper_read_rate(struct ccu_common *common, } int ccu_frac_helper_set_rate(struct ccu_common *common, - struct _ccu_frac *cf, + struct ccu_frac_internal *cf, unsigned long rate) { unsigned long flags; diff --git a/drivers/clk/sunxi-ng/ccu_frac.h b/drivers/clk/sunxi-ng/ccu_frac.h index e4c670b1cdfe..7b1ee380156f 100644 --- a/drivers/clk/sunxi-ng/ccu_frac.h +++ b/drivers/clk/sunxi-ng/ccu_frac.h @@ -18,7 +18,7 @@ #include "ccu_common.h" -struct _ccu_frac { +struct ccu_frac_internal { u32 enable; u32 select; @@ -33,21 +33,21 @@ struct _ccu_frac { } bool ccu_frac_helper_is_enabled(struct ccu_common *common, - struct _ccu_frac *cf); + struct ccu_frac_internal *cf); void ccu_frac_helper_enable(struct ccu_common *common, - struct _ccu_frac *cf); + struct ccu_frac_internal *cf); void ccu_frac_helper_disable(struct ccu_common *common, - struct _ccu_frac *cf); + struct ccu_frac_internal *cf); bool ccu_frac_helper_has_rate(struct ccu_common *common, - struct _ccu_frac *cf, + struct ccu_frac_internal *cf, unsigned long rate); unsigned long ccu_frac_helper_read_rate(struct ccu_common *common, - struct _ccu_frac *cf); + struct ccu_frac_internal *cf); int ccu_frac_helper_set_rate(struct ccu_common *common, - struct _ccu_frac *cf, + struct ccu_frac_internal *cf, unsigned long rate); #endif /* _CCU_FRAC_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu_mp.h b/drivers/clk/sunxi-ng/ccu_mp.h index edf9215ea8cc..915625e97d98 100644 --- a/drivers/clk/sunxi-ng/ccu_mp.h +++ b/drivers/clk/sunxi-ng/ccu_mp.h @@ -29,8 +29,8 @@ struct ccu_mp { u32 enable; - struct _ccu_div m; - struct _ccu_div p; + struct ccu_div_internal m; + struct ccu_div_internal p; struct ccu_mux_internal mux; struct ccu_common common; }; diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c index 010e9424691d..678b6cb49f01 100644 --- a/drivers/clk/sunxi-ng/ccu_mult.c +++ b/drivers/clk/sunxi-ng/ccu_mult.c @@ -13,10 +13,23 @@ #include "ccu_gate.h" #include "ccu_mult.h" +struct _ccu_mult { + unsigned long mult, min, max; +}; + static void ccu_mult_find_best(unsigned long parent, unsigned long rate, - unsigned int max_n, unsigned int *n) + struct _ccu_mult *mult) { - *n = rate / parent; + int _mult; + + _mult = rate / parent; + if (_mult < mult->min) + _mult = mult->min; + + if (_mult > mult->max) + _mult = mult->max; + + mult->mult = _mult; } static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux, @@ -25,11 +38,13 @@ static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux, void *data) { struct ccu_mult *cm = data; - unsigned int n; + struct _ccu_mult _cm; - ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n); + _cm.min = 1; + _cm.max = 1 << cm->mult.width; + ccu_mult_find_best(parent_rate, rate, &_cm); - return parent_rate * n; + return parent_rate * _cm.mult; } static void ccu_mult_disable(struct clk_hw *hw) @@ -83,21 +98,23 @@ static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct ccu_mult *cm = hw_to_ccu_mult(hw); + struct _ccu_mult _cm; unsigned long flags; - unsigned int n; u32 reg; ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1, &parent_rate); - ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n); + _cm.min = cm->mult.min; + _cm.max = 1 << cm->mult.width; + ccu_mult_find_best(parent_rate, rate, &_cm); spin_lock_irqsave(cm->common.lock, flags); reg = readl(cm->common.base + cm->common.reg); reg &= ~GENMASK(cm->mult.width + cm->mult.shift - 1, cm->mult.shift); - writel(reg | ((n - 1) << cm->mult.shift), + writel(reg | ((_cm.mult - 1) << cm->mult.shift), cm->common.base + cm->common.reg); spin_unlock_irqrestore(cm->common.lock, flags); diff --git a/drivers/clk/sunxi-ng/ccu_mult.h b/drivers/clk/sunxi-ng/ccu_mult.h index 5d2c8dc14073..c1a2134bdc71 100644 --- a/drivers/clk/sunxi-ng/ccu_mult.h +++ b/drivers/clk/sunxi-ng/ccu_mult.h @@ -4,21 +4,26 @@ #include "ccu_common.h" #include "ccu_mux.h" -struct _ccu_mult { +struct ccu_mult_internal { u8 shift; u8 width; + u8 min; }; -#define _SUNXI_CCU_MULT(_shift, _width) \ - { \ - .shift = _shift, \ - .width = _width, \ +#define _SUNXI_CCU_MULT_MIN(_shift, _width, _min) \ + { \ + .shift = _shift, \ + .width = _width, \ + .min = _min, \ } +#define _SUNXI_CCU_MULT(_shift, _width) \ + _SUNXI_CCU_MULT_MIN(_shift, _width, 1) + struct ccu_mult { u32 enable; - struct _ccu_mult mult; + struct ccu_mult_internal mult; struct ccu_mux_internal mux; struct ccu_common common; }; diff --git a/drivers/clk/sunxi-ng/ccu_nk.c b/drivers/clk/sunxi-ng/ccu_nk.c index d6fafb397489..eaf0fdf78d2b 100644 --- a/drivers/clk/sunxi-ng/ccu_nk.c +++ b/drivers/clk/sunxi-ng/ccu_nk.c @@ -9,21 +9,24 @@ */ #include <linux/clk-provider.h> -#include <linux/rational.h> #include "ccu_gate.h" #include "ccu_nk.h" +struct _ccu_nk { + unsigned long n, min_n, max_n; + unsigned long k, min_k, max_k; +}; + static void ccu_nk_find_best(unsigned long parent, unsigned long rate, - unsigned int max_n, unsigned int max_k, - unsigned int *n, unsigned int *k) + struct _ccu_nk *nk) { unsigned long best_rate = 0; unsigned int best_k = 0, best_n = 0; unsigned int _k, _n; - for (_k = 1; _k <= max_k; _k++) { - for (_n = 1; _n <= max_n; _n++) { + for (_k = nk->min_k; _k <= nk->max_k; _k++) { + for (_n = nk->min_n; _n <= nk->max_n; _n++) { unsigned long tmp_rate = parent * _n * _k; if (tmp_rate > rate) @@ -37,8 +40,8 @@ static void ccu_nk_find_best(unsigned long parent, unsigned long rate, } } - *k = best_k; - *n = best_n; + nk->k = best_k; + nk->n = best_n; } static void ccu_nk_disable(struct clk_hw *hw) @@ -89,16 +92,19 @@ static long ccu_nk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct ccu_nk *nk = hw_to_ccu_nk(hw); - unsigned int n, k; + struct _ccu_nk _nk; if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) rate *= nk->fixed_post_div; - ccu_nk_find_best(*parent_rate, rate, - 1 << nk->n.width, 1 << nk->k.width, - &n, &k); + _nk.min_n = nk->n.min; + _nk.max_n = 1 << nk->n.width; + _nk.min_k = nk->k.min; + _nk.max_k = 1 << nk->k.width; + + ccu_nk_find_best(*parent_rate, rate, &_nk); + rate = *parent_rate * _nk.n * _nk.k; - rate = *parent_rate * n * k; if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) rate = rate / nk->fixed_post_div; @@ -110,15 +116,18 @@ static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate, { struct ccu_nk *nk = hw_to_ccu_nk(hw); unsigned long flags; - unsigned int n, k; + struct _ccu_nk _nk; u32 reg; if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) rate = rate * nk->fixed_post_div; - ccu_nk_find_best(parent_rate, rate, - 1 << nk->n.width, 1 << nk->k.width, - &n, &k); + _nk.min_n = nk->n.min; + _nk.max_n = 1 << nk->n.width; + _nk.min_k = nk->k.min; + _nk.max_k = 1 << nk->k.width; + + ccu_nk_find_best(parent_rate, rate, &_nk); spin_lock_irqsave(nk->common.lock, flags); @@ -126,7 +135,7 @@ static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate, reg &= ~GENMASK(nk->n.width + nk->n.shift - 1, nk->n.shift); reg &= ~GENMASK(nk->k.width + nk->k.shift - 1, nk->k.shift); - writel(reg | ((k - 1) << nk->k.shift) | ((n - 1) << nk->n.shift), + writel(reg | ((_nk.k - 1) << nk->k.shift) | ((_nk.n - 1) << nk->n.shift), nk->common.base + nk->common.reg); spin_unlock_irqrestore(nk->common.lock, flags); diff --git a/drivers/clk/sunxi-ng/ccu_nk.h b/drivers/clk/sunxi-ng/ccu_nk.h index 4b52da0c29fe..437836b80696 100644 --- a/drivers/clk/sunxi-ng/ccu_nk.h +++ b/drivers/clk/sunxi-ng/ccu_nk.h @@ -30,8 +30,8 @@ struct ccu_nk { u32 enable; u32 lock; - struct _ccu_mult n; - struct _ccu_mult k; + struct ccu_mult_internal n; + struct ccu_mult_internal k; unsigned int fixed_post_div; diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c index 059fdc3b4f96..9b840a47a94d 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.c +++ b/drivers/clk/sunxi-ng/ccu_nkm.c @@ -9,15 +9,14 @@ */ #include <linux/clk-provider.h> -#include <linux/rational.h> #include "ccu_gate.h" #include "ccu_nkm.h" struct _ccu_nkm { - unsigned long n, max_n; - unsigned long k, max_k; - unsigned long m, max_m; + unsigned long n, min_n, max_n; + unsigned long k, min_k, max_k; + unsigned long m, min_m, max_m; }; static void ccu_nkm_find_best(unsigned long parent, unsigned long rate, @@ -27,22 +26,22 @@ static void ccu_nkm_find_best(unsigned long parent, unsigned long rate, unsigned long best_n = 0, best_k = 0, best_m = 0; unsigned long _n, _k, _m; - for (_k = 1; _k <= nkm->max_k; _k++) { - unsigned long tmp_rate; - - rational_best_approximation(rate / _k, parent, - nkm->max_n, nkm->max_m, &_n, &_m); - - tmp_rate = parent * _n * _k / _m; - - if (tmp_rate > rate) - continue; - - if ((rate - tmp_rate) < (rate - best_rate)) { - best_rate = tmp_rate; - best_n = _n; - best_k = _k; - best_m = _m; + for (_k = nkm->min_k; _k <= nkm->max_k; _k++) { + for (_n = nkm->min_n; _n <= nkm->max_n; _n++) { + for (_m = nkm->min_m; _m <= nkm->max_m; _m++) { + unsigned long tmp_rate; + + tmp_rate = parent * _n * _k / _m; + + if (tmp_rate > rate) + continue; + if ((rate - tmp_rate) < (rate - best_rate)) { + best_rate = tmp_rate; + best_n = _n; + best_k = _k; + best_m = _m; + } + } } } @@ -101,8 +100,11 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, struct ccu_nkm *nkm = data; struct _ccu_nkm _nkm; + _nkm.min_n = nkm->n.min; _nkm.max_n = 1 << nkm->n.width; + _nkm.min_k = nkm->k.min; _nkm.max_k = 1 << nkm->k.width; + _nkm.min_m = 1; _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; ccu_nkm_find_best(parent_rate, rate, &_nkm); @@ -127,8 +129,11 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags; u32 reg; + _nkm.min_n = nkm->n.min; _nkm.max_n = 1 << nkm->n.width; + _nkm.min_k = nkm->k.min; _nkm.max_k = 1 << nkm->k.width; + _nkm.min_m = 1; _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; ccu_nkm_find_best(parent_rate, rate, &_nkm); diff --git a/drivers/clk/sunxi-ng/ccu_nkm.h b/drivers/clk/sunxi-ng/ccu_nkm.h index 35493fddd8ab..34580894f4d1 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.h +++ b/drivers/clk/sunxi-ng/ccu_nkm.h @@ -29,9 +29,9 @@ struct ccu_nkm { u32 enable; u32 lock; - struct _ccu_mult n; - struct _ccu_mult k; - struct _ccu_div m; + struct ccu_mult_internal n; + struct ccu_mult_internal k; + struct ccu_div_internal m; struct ccu_mux_internal mux; struct ccu_common common; diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c index 9769dee99511..684c42da3ebb 100644 --- a/drivers/clk/sunxi-ng/ccu_nkmp.c +++ b/drivers/clk/sunxi-ng/ccu_nkmp.c @@ -9,16 +9,15 @@ */ #include <linux/clk-provider.h> -#include <linux/rational.h> #include "ccu_gate.h" #include "ccu_nkmp.h" struct _ccu_nkmp { - unsigned long n, max_n; - unsigned long k, max_k; - unsigned long m, max_m; - unsigned long p, max_p; + unsigned long n, min_n, max_n; + unsigned long k, min_k, max_k; + unsigned long m, min_m, max_m; + unsigned long p, min_p, max_p; }; static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate, @@ -28,25 +27,25 @@ static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate, unsigned long best_n = 0, best_k = 0, best_m = 0, best_p = 0; unsigned long _n, _k, _m, _p; - for (_k = 1; _k <= nkmp->max_k; _k++) { - for (_p = 1; _p <= nkmp->max_p; _p <<= 1) { - unsigned long tmp_rate; - - rational_best_approximation(rate / _k, parent / _p, - nkmp->max_n, nkmp->max_m, - &_n, &_m); - - tmp_rate = parent * _n * _k / (_m * _p); - - if (tmp_rate > rate) - continue; - - if ((rate - tmp_rate) < (rate - best_rate)) { - best_rate = tmp_rate; - best_n = _n; - best_k = _k; - best_m = _m; - best_p = _p; + for (_k = nkmp->min_k; _k <= nkmp->max_k; _k++) { + for (_n = nkmp->min_n; _n <= nkmp->max_n; _n++) { + for (_m = nkmp->min_m; _m <= nkmp->max_m; _m++) { + for (_p = nkmp->min_p; _p <= nkmp->max_p; _p <<= 1) { + unsigned long tmp_rate; + + tmp_rate = parent * _n * _k / (_m * _p); + + if (tmp_rate > rate) + continue; + + if ((rate - tmp_rate) < (rate - best_rate)) { + best_rate = tmp_rate; + best_n = _n; + best_k = _k; + best_m = _m; + best_p = _p; + } + } } } } @@ -108,9 +107,13 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate, struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw); struct _ccu_nkmp _nkmp; + _nkmp.min_n = nkmp->n.min; _nkmp.max_n = 1 << nkmp->n.width; + _nkmp.min_k = nkmp->k.min; _nkmp.max_k = 1 << nkmp->k.width; + _nkmp.min_m = 1; _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width; + _nkmp.min_p = 1; _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1); ccu_nkmp_find_best(*parent_rate, rate, &_nkmp); @@ -126,9 +129,13 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags; u32 reg; + _nkmp.min_n = 1; _nkmp.max_n = 1 << nkmp->n.width; + _nkmp.min_k = 1; _nkmp.max_k = 1 << nkmp->k.width; + _nkmp.min_m = 1; _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width; + _nkmp.min_p = 1; _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1); ccu_nkmp_find_best(parent_rate, rate, &_nkmp); diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.h b/drivers/clk/sunxi-ng/ccu_nkmp.h index 5adb0c92a614..a82facbc6144 100644 --- a/drivers/clk/sunxi-ng/ccu_nkmp.h +++ b/drivers/clk/sunxi-ng/ccu_nkmp.h @@ -29,10 +29,10 @@ struct ccu_nkmp { u32 enable; u32 lock; - struct _ccu_mult n; - struct _ccu_mult k; - struct _ccu_div m; - struct _ccu_div p; + struct ccu_mult_internal n; + struct ccu_mult_internal k; + struct ccu_div_internal m; + struct ccu_div_internal p; struct ccu_common common; }; diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c index b61bdd8c7a7f..c9f3b6c982f0 100644 --- a/drivers/clk/sunxi-ng/ccu_nm.c +++ b/drivers/clk/sunxi-ng/ccu_nm.c @@ -9,12 +9,42 @@ */ #include <linux/clk-provider.h> -#include <linux/rational.h> #include "ccu_frac.h" #include "ccu_gate.h" #include "ccu_nm.h" +struct _ccu_nm { + unsigned long n, min_n, max_n; + unsigned long m, min_m, max_m; +}; + +static void ccu_nm_find_best(unsigned long parent, unsigned long rate, + struct _ccu_nm *nm) +{ + unsigned long best_rate = 0; + unsigned long best_n = 0, best_m = 0; + unsigned long _n, _m; + + for (_n = nm->min_n; _n <= nm->max_n; _n++) { + for (_m = nm->min_m; _m <= nm->max_m; _m++) { + unsigned long tmp_rate = parent * _n / _m; + + if (tmp_rate > rate) + continue; + + if ((rate - tmp_rate) < (rate - best_rate)) { + best_rate = tmp_rate; + best_n = _n; + best_m = _m; + } + } + } + + nm->n = best_n; + nm->m = best_m; +} + static void ccu_nm_disable(struct clk_hw *hw) { struct ccu_nm *nm = hw_to_ccu_nm(hw); @@ -61,24 +91,24 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct ccu_nm *nm = hw_to_ccu_nm(hw); - unsigned long max_n, max_m; - unsigned long n, m; + struct _ccu_nm _nm; - max_n = 1 << nm->n.width; - max_m = nm->m.max ?: 1 << nm->m.width; + _nm.min_n = nm->n.min; + _nm.max_n = 1 << nm->n.width; + _nm.min_m = 1; + _nm.max_m = nm->m.max ?: 1 << nm->m.width; - rational_best_approximation(rate, *parent_rate, max_n, max_m, &n, &m); + ccu_nm_find_best(*parent_rate, rate, &_nm); - return *parent_rate * n / m; + return *parent_rate * _nm.n / _nm.m; } static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct ccu_nm *nm = hw_to_ccu_nm(hw); + struct _ccu_nm _nm; unsigned long flags; - unsigned long max_n, max_m; - unsigned long n, m; u32 reg; if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) @@ -86,10 +116,12 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, else ccu_frac_helper_disable(&nm->common, &nm->frac); - max_n = 1 << nm->n.width; - max_m = nm->m.max ?: 1 << nm->m.width; + _nm.min_n = 1; + _nm.max_n = 1 << nm->n.width; + _nm.min_m = 1; + _nm.max_m = nm->m.max ?: 1 << nm->m.width; - rational_best_approximation(rate, parent_rate, max_n, max_m, &n, &m); + ccu_nm_find_best(parent_rate, rate, &_nm); spin_lock_irqsave(nm->common.lock, flags); @@ -97,7 +129,7 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, reg &= ~GENMASK(nm->n.width + nm->n.shift - 1, nm->n.shift); reg &= ~GENMASK(nm->m.width + nm->m.shift - 1, nm->m.shift); - writel(reg | ((m - 1) << nm->m.shift) | ((n - 1) << nm->n.shift), + writel(reg | ((_nm.m - 1) << nm->m.shift) | ((_nm.n - 1) << nm->n.shift), nm->common.base + nm->common.reg); spin_unlock_irqrestore(nm->common.lock, flags); diff --git a/drivers/clk/sunxi-ng/ccu_nm.h b/drivers/clk/sunxi-ng/ccu_nm.h index 0b7bcd33a2df..e87fd186da78 100644 --- a/drivers/clk/sunxi-ng/ccu_nm.h +++ b/drivers/clk/sunxi-ng/ccu_nm.h @@ -30,9 +30,9 @@ struct ccu_nm { u32 enable; u32 lock; - struct _ccu_mult n; - struct _ccu_div m; - struct _ccu_frac frac; + struct ccu_mult_internal n; + struct ccu_div_internal m; + struct ccu_frac_internal frac; struct ccu_common common; }; diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c index e54266cc1c51..4417ae129ac7 100644 --- a/drivers/clk/sunxi/clk-mod0.c +++ b/drivers/clk/sunxi/clk-mod0.c @@ -24,7 +24,7 @@ #include "clk-factors.h" /** - * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks + * sun4i_a10_get_mod0_factors() - calculates m, n factors for MOD0-style clocks * MOD0 rate is calculated as follows * rate = (parent_rate >> p) / (m + 1); */ diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c index c205809ba580..ad1c1cc829cb 100644 --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c @@ -20,7 +20,7 @@ #include <linux/cpu.h> #include <linux/err.h> #include <linux/kernel.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/platform_device.h> #include <soc/tegra/fuse.h> @@ -148,7 +148,6 @@ static const struct of_device_id tegra124_dfll_fcpu_of_match[] = { { .compatible = "nvidia,tegra124-dfll", }, { }, }; -MODULE_DEVICE_TABLE(of, tegra124_dfll_fcpu_of_match); static const struct dev_pm_ops tegra124_dfll_pm_ops = { SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend, @@ -164,20 +163,4 @@ static struct platform_driver tegra124_dfll_fcpu_driver = { .pm = &tegra124_dfll_pm_ops, }, }; - -static int __init tegra124_dfll_fcpu_init(void) -{ - return platform_driver_register(&tegra124_dfll_fcpu_driver); -} -module_init(tegra124_dfll_fcpu_init); - -static void __exit tegra124_dfll_fcpu_exit(void) -{ - platform_driver_unregister(&tegra124_dfll_fcpu_driver); -} -module_exit(tegra124_dfll_fcpu_exit); - -MODULE_DESCRIPTION("Tegra124 DFLL clock source driver"); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Aleksandr Frid <afrid@nvidia.com>"); -MODULE_AUTHOR("Paul Walmsley <pwalmsley@nvidia.com>"); +builtin_platform_driver(tegra124_dfll_fcpu_driver); diff --git a/drivers/clk/tegra/cvb.c b/drivers/clk/tegra/cvb.c index 624115e82ff9..da9e8e7b5ce5 100644 --- a/drivers/clk/tegra/cvb.c +++ b/drivers/clk/tegra/cvb.c @@ -92,19 +92,19 @@ static int build_opp_table(struct device *dev, const struct cvb_table *table, /** * tegra_cvb_add_opp_table - build OPP table from Tegra CVB tables - * @cvb_tables: array of CVB tables - * @sz: size of the previously mentioned array + * @dev: the struct device * for which the OPP table is built + * @tables: array of CVB tables + * @count: size of the previously mentioned array * @process_id: process id of the HW module * @speedo_id: speedo id of the HW module * @speedo_value: speedo value of the HW module - * @max_rate: highest safe clock rate - * @opp_dev: the struct device * for which the OPP table is built + * @max_freq: highest safe clock rate * * On Tegra, a CVB table encodes the relationship between operating voltage * and safe maximal frequency for a given module (e.g. GPU or CPU). This * function calculates the optimal voltage-frequency operating points * for the given arguments and exports them via the OPP library for the - * given @opp_dev. Returns a pointer to the struct cvb_table that matched + * given @dev. Returns a pointer to the struct cvb_table that matched * or an ERR_PTR on failure. */ const struct cvb_table * diff --git a/drivers/clk/ti/clk-3xxx.c b/drivers/clk/ti/clk-3xxx.c index 8831e1a05367..11d8aa3ec186 100644 --- a/drivers/clk/ti/clk-3xxx.c +++ b/drivers/clk/ti/clk-3xxx.c @@ -22,13 +22,6 @@ #include "clock.h" -/* - * DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks - * that are sourced by DPLL5, and both of these require this clock - * to be at 120 MHz for proper operation. - */ -#define DPLL5_FREQ_FOR_USBHOST 120000000 - #define OMAP3430ES2_ST_DSS_IDLE_SHIFT 1 #define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT 5 #define OMAP3430ES2_ST_SSI_IDLE_SHIFT 8 @@ -546,14 +539,21 @@ void __init omap3_clk_lock_dpll5(void) struct clk *dpll5_clk; struct clk *dpll5_m2_clk; + /* + * Errata sprz319f advisory 2.1 documents a USB host clock drift issue + * that can be worked around using specially crafted dpll5 settings + * with a dpll5_m2 divider set to 8. Set the dpll5 rate to 8x the USB + * host clock rate, its .set_rate handler() will detect that frequency + * and use the errata settings. + */ dpll5_clk = clk_get(NULL, "dpll5_ck"); - clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST); + clk_set_rate(dpll5_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST * 8); clk_prepare_enable(dpll5_clk); - /* Program dpll5_m2_clk divider for no division */ + /* Program dpll5_m2_clk divider */ dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck"); clk_prepare_enable(dpll5_m2_clk); - clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST); + clk_set_rate(dpll5_m2_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST); clk_disable_unprepare(dpll5_m2_clk); clk_disable_unprepare(dpll5_clk); diff --git a/drivers/clk/ti/clk-7xx.c b/drivers/clk/ti/clk-7xx.c index bfa17d33ef3b..9fd6043314eb 100644 --- a/drivers/clk/ti/clk-7xx.c +++ b/drivers/clk/ti/clk-7xx.c @@ -201,7 +201,6 @@ static struct ti_dt_clk dra7xx_clks[] = { DT_CLK(NULL, "atl_dpll_clk_mux", "atl_dpll_clk_mux"), DT_CLK(NULL, "atl_gfclk_mux", "atl_gfclk_mux"), DT_CLK(NULL, "dcan1_sys_clk_mux", "dcan1_sys_clk_mux"), - DT_CLK(NULL, "gmac_gmii_ref_clk_div", "gmac_gmii_ref_clk_div"), DT_CLK(NULL, "gmac_rft_clk_mux", "gmac_rft_clk_mux"), DT_CLK(NULL, "gpu_core_gclk_mux", "gpu_core_gclk_mux"), DT_CLK(NULL, "gpu_hyd_gclk_mux", "gpu_hyd_gclk_mux"), diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c index c77333230bdf..45d05339d583 100644 --- a/drivers/clk/ti/clk-dra7-atl.c +++ b/drivers/clk/ti/clk-dra7-atl.c @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -#include <linux/module.h> +#include <linux/init.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/slab.h> @@ -295,31 +295,17 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) return ret; } -static int of_dra7_atl_clk_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); - - return 0; -} - static const struct of_device_id of_dra7_atl_clk_match_tbl[] = { { .compatible = "ti,dra7-atl", }, {}, }; -MODULE_DEVICE_TABLE(of, of_dra7_atl_clk_match_tbl); static struct platform_driver dra7_atl_clk_driver = { .driver = { .name = "dra7-atl", + .suppress_bind_attrs = true, .of_match_table = of_dra7_atl_clk_match_tbl, }, .probe = of_dra7_atl_clk_probe, - .remove = of_dra7_atl_clk_remove, }; - -module_platform_driver(dra7_atl_clk_driver); - -MODULE_DESCRIPTION("Clock driver for DRA7 Audio Tracking Logic"); -MODULE_ALIAS("platform:dra7-atl-clock"); -MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); -MODULE_LICENSE("GPL v2"); +builtin_platform_driver(dra7_atl_clk_driver); diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h index 90f3f472ae1c..13c37f48d9d6 100644 --- a/drivers/clk/ti/clock.h +++ b/drivers/clk/ti/clock.h @@ -257,11 +257,20 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw, unsigned long parent_rate); +/* + * OMAP3_DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks + * that are sourced by DPLL5, and both of these require this clock + * to be at 120 MHz for proper operation. + */ +#define OMAP3_DPLL5_FREQ_FOR_USBHOST 120000000 + unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate); int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate, unsigned long parent_rate); int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index); +int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); void omap3_clk_lock_dpll5(void); unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw, diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 9fc8754a6e61..4b9a419d8e14 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -114,6 +114,18 @@ static const struct clk_ops omap3_dpll_ck_ops = { .round_rate = &omap2_dpll_round_rate, }; +static const struct clk_ops omap3_dpll5_ck_ops = { + .enable = &omap3_noncore_dpll_enable, + .disable = &omap3_noncore_dpll_disable, + .get_parent = &omap2_init_dpll_parent, + .recalc_rate = &omap3_dpll_recalc, + .set_rate = &omap3_dpll5_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent, + .determine_rate = &omap3_noncore_dpll_determine_rate, + .round_rate = &omap2_dpll_round_rate, +}; + static const struct clk_ops omap3_dpll_per_ck_ops = { .enable = &omap3_noncore_dpll_enable, .disable = &omap3_noncore_dpll_disable, @@ -474,7 +486,12 @@ static void __init of_ti_omap3_dpll_setup(struct device_node *node) .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED), }; - of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd); + if ((of_machine_is_compatible("ti,omap3630") || + of_machine_is_compatible("ti,omap36xx")) && + !strcmp(node->name, "dpll5_ck")) + of_ti_dpll_setup(node, &omap3_dpll5_ck_ops, &dd); + else + of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd); } CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock", of_ti_omap3_dpll_setup); diff --git a/drivers/clk/ti/dpll3xxx.c b/drivers/clk/ti/dpll3xxx.c index 88f2ce81ba55..4cdd28a25584 100644 --- a/drivers/clk/ti/dpll3xxx.c +++ b/drivers/clk/ti/dpll3xxx.c @@ -838,3 +838,70 @@ int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate, index); } + +/* Apply DM3730 errata sprz319 advisory 2.1. */ +static bool omap3_dpll5_apply_errata(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct omap3_dpll5_settings { + unsigned int rate, m, n; + }; + + static const struct omap3_dpll5_settings precomputed[] = { + /* + * From DM3730 errata advisory 2.1, table 35 and 36. + * The N value is increased by 1 compared to the tables as the + * errata lists register values while last_rounded_field is the + * real divider value. + */ + { 12000000, 80, 0 + 1 }, + { 13000000, 443, 5 + 1 }, + { 19200000, 50, 0 + 1 }, + { 26000000, 443, 11 + 1 }, + { 38400000, 25, 0 + 1 } + }; + + const struct omap3_dpll5_settings *d; + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + struct dpll_data *dd; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(precomputed); ++i) { + if (parent_rate == precomputed[i].rate) + break; + } + + if (i == ARRAY_SIZE(precomputed)) + return false; + + d = &precomputed[i]; + + /* Update the M, N and rounded rate values and program the DPLL. */ + dd = clk->dpll_data; + dd->last_rounded_m = d->m; + dd->last_rounded_n = d->n; + dd->last_rounded_rate = div_u64((u64)parent_rate * d->m, d->n); + omap3_noncore_dpll_program(clk, 0); + + return true; +} + +/** + * omap3_dpll5_set_rate - set rate for omap3 dpll5 + * @hw: clock to change + * @rate: target rate for clock + * @parent_rate: rate of the parent clock + * + * Set rate for the DPLL5 clock. Apply the sprz319 advisory 2.1 on OMAP36xx if + * the DPLL is used for USB host (detected through the requested rate). + */ +int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + if (rate == OMAP3_DPLL5_FREQ_FOR_USBHOST * 8) { + if (omap3_dpll5_apply_errata(hw, parent_rate)) + return 0; + } + + return omap3_noncore_dpll_set_rate(hw, rate, parent_rate); +} diff --git a/drivers/clk/uniphier/Makefile b/drivers/clk/uniphier/Makefile index f27b360329ca..665d1d65a90e 100644 --- a/drivers/clk/uniphier/Makefile +++ b/drivers/clk/uniphier/Makefile @@ -1,8 +1,11 @@ obj-y += clk-uniphier-core.o + +obj-y += clk-uniphier-cpugear.o obj-y += clk-uniphier-fixed-factor.o obj-y += clk-uniphier-fixed-rate.o obj-y += clk-uniphier-gate.o obj-y += clk-uniphier-mux.o + obj-y += clk-uniphier-sys.o obj-y += clk-uniphier-mio.o obj-y += clk-uniphier-peri.o diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c index 26c53f7963a4..0007218ce6a0 100644 --- a/drivers/clk/uniphier/clk-uniphier-core.c +++ b/drivers/clk/uniphier/clk-uniphier-core.c @@ -27,6 +27,9 @@ static struct clk_hw *uniphier_clk_register(struct device *dev, const struct uniphier_clk_data *data) { switch (data->type) { + case UNIPHIER_CLK_TYPE_CPUGEAR: + return uniphier_clk_register_cpugear(dev, regmap, data->name, + &data->data.cpugear); case UNIPHIER_CLK_TYPE_FIXED_FACTOR: return uniphier_clk_register_fixed_factor(dev, data->name, &data->data.factor); diff --git a/drivers/clk/uniphier/clk-uniphier-cpugear.c b/drivers/clk/uniphier/clk-uniphier-cpugear.c new file mode 100644 index 000000000000..9bff26e0cbb0 --- /dev/null +++ b/drivers/clk/uniphier/clk-uniphier-cpugear.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/regmap.h> + +#include "clk-uniphier.h" + +#define UNIPHIER_CLK_CPUGEAR_STAT 0 /* status */ +#define UNIPHIER_CLK_CPUGEAR_SET 4 /* set */ +#define UNIPHIER_CLK_CPUGEAR_UPD 8 /* update */ +#define UNIPHIER_CLK_CPUGEAR_UPD_BIT BIT(0) + +struct uniphier_clk_cpugear { + struct clk_hw hw; + struct regmap *regmap; + unsigned int regbase; + unsigned int mask; +}; + +#define to_uniphier_clk_cpugear(_hw) \ + container_of(_hw, struct uniphier_clk_cpugear, hw) + +static int uniphier_clk_cpugear_set_parent(struct clk_hw *hw, u8 index) +{ + struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw); + int ret; + unsigned int val; + + ret = regmap_write_bits(gear->regmap, + gear->regbase + UNIPHIER_CLK_CPUGEAR_SET, + gear->mask, index); + if (ret) + return ret; + + ret = regmap_write_bits(gear->regmap, + gear->regbase + UNIPHIER_CLK_CPUGEAR_SET, + UNIPHIER_CLK_CPUGEAR_UPD_BIT, + UNIPHIER_CLK_CPUGEAR_UPD_BIT); + if (ret) + return ret; + + return regmap_read_poll_timeout(gear->regmap, + gear->regbase + UNIPHIER_CLK_CPUGEAR_UPD, + val, !(val & UNIPHIER_CLK_CPUGEAR_UPD_BIT), + 0, 1); +} + +static u8 uniphier_clk_cpugear_get_parent(struct clk_hw *hw) +{ + struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw); + int num_parents = clk_hw_get_num_parents(hw); + int ret; + unsigned int val; + + ret = regmap_read(gear->regmap, + gear->regbase + UNIPHIER_CLK_CPUGEAR_STAT, &val); + if (ret) + return ret; + + val &= gear->mask; + + return val < num_parents ? val : -EINVAL; +} + +static const struct clk_ops uniphier_clk_cpugear_ops = { + .determine_rate = __clk_mux_determine_rate, + .set_parent = uniphier_clk_cpugear_set_parent, + .get_parent = uniphier_clk_cpugear_get_parent, +}; + +struct clk_hw *uniphier_clk_register_cpugear(struct device *dev, + struct regmap *regmap, + const char *name, + const struct uniphier_clk_cpugear_data *data) +{ + struct uniphier_clk_cpugear *gear; + struct clk_init_data init; + int ret; + + gear = devm_kzalloc(dev, sizeof(*gear), GFP_KERNEL); + if (!gear) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &uniphier_clk_cpugear_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = data->parent_names; + init.num_parents = data->num_parents, + + gear->regmap = regmap; + gear->regbase = data->regbase; + gear->mask = data->mask; + gear->hw.init = &init; + + ret = devm_clk_hw_register(dev, &gear->hw); + if (ret) + return ERR_PTR(ret); + + return &gear->hw; +} diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c index 5d029991047d..d049316c1c0f 100644 --- a/drivers/clk/uniphier/clk-uniphier-sys.c +++ b/drivers/clk/uniphier/clk-uniphier-sys.c @@ -125,16 +125,35 @@ const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = { }; const struct uniphier_clk_data uniphier_ld11_sys_clk_data[] = { + UNIPHIER_CLK_FACTOR("cpll", -1, "ref", 392, 5), /* 1960 MHz */ + UNIPHIER_CLK_FACTOR("mpll", -1, "ref", 64, 1), /* 1600 MHz */ UNIPHIER_CLK_FACTOR("spll", -1, "ref", 80, 1), /* 2000 MHz */ + UNIPHIER_CLK_FACTOR("vspll", -1, "ref", 80, 1), /* 2000 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40), UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC, MIO */ UNIPHIER_CLK_FACTOR("usb2", -1, "ref", 24, 25), + /* CPU gears */ + UNIPHIER_CLK_DIV4("cpll", 2, 3, 4, 8), + UNIPHIER_CLK_DIV4("mpll", 2, 3, 4, 8), + UNIPHIER_CLK_DIV3("spll", 3, 4, 8), + /* Note: both gear1 and gear4 are spll/4. This is not a bug. */ + UNIPHIER_CLK_CPUGEAR("cpu-ca53", 33, 0x8080, 0xf, 8, + "cpll/2", "spll/4", "cpll/3", "spll/3", + "spll/4", "spll/8", "cpll/4", "cpll/8"), + UNIPHIER_CLK_CPUGEAR("cpu-ipp", 34, 0x8100, 0xf, 8, + "mpll/2", "spll/4", "mpll/3", "spll/3", + "spll/4", "spll/8", "mpll/4", "mpll/8"), { /* sentinel */ } }; const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = { + UNIPHIER_CLK_FACTOR("cpll", -1, "ref", 88, 1), /* ARM: 2200 MHz */ + UNIPHIER_CLK_FACTOR("gppll", -1, "ref", 52, 1), /* Mali: 1300 MHz */ + UNIPHIER_CLK_FACTOR("mpll", -1, "ref", 64, 1), /* Codec: 1600 MHz */ UNIPHIER_CLK_FACTOR("spll", -1, "ref", 80, 1), /* 2000 MHz */ + UNIPHIER_CLK_FACTOR("s2pll", -1, "ref", 88, 1), /* IPP: 2200 MHz */ + UNIPHIER_CLK_FACTOR("vppll", -1, "ref", 504, 5), /* 2520 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40), UNIPHIER_LD20_SYS_CLK_SD, @@ -147,5 +166,18 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = { UNIPHIER_CLK_GATE("usb30", 14, NULL, 0x210c, 14), UNIPHIER_CLK_GATE("usb30-phy0", 16, NULL, 0x210c, 12), UNIPHIER_CLK_GATE("usb30-phy1", 17, NULL, 0x210c, 13), + /* CPU gears */ + UNIPHIER_CLK_DIV4("cpll", 2, 3, 4, 8), + UNIPHIER_CLK_DIV4("spll", 2, 3, 4, 8), + UNIPHIER_CLK_DIV4("s2pll", 2, 3, 4, 8), + UNIPHIER_CLK_CPUGEAR("cpu-ca72", 32, 0x8000, 0xf, 8, + "cpll/2", "spll/2", "cpll/3", "spll/3", + "spll/4", "spll/8", "cpll/4", "cpll/8"), + UNIPHIER_CLK_CPUGEAR("cpu-ca53", 33, 0x8080, 0xf, 8, + "cpll/2", "spll/2", "cpll/3", "spll/3", + "spll/4", "spll/8", "cpll/4", "cpll/8"), + UNIPHIER_CLK_CPUGEAR("cpu-ipp", 34, 0x8100, 0xf, 8, + "s2pll/2", "spll/2", "s2pll/3", "spll/3", + "spll/4", "spll/8", "s2pll/4", "s2pll/8"), { /* sentinel */ } }; diff --git a/drivers/clk/uniphier/clk-uniphier.h b/drivers/clk/uniphier/clk-uniphier.h index 0244dba1f4cf..01c16ecec48f 100644 --- a/drivers/clk/uniphier/clk-uniphier.h +++ b/drivers/clk/uniphier/clk-uniphier.h @@ -20,15 +20,24 @@ struct clk_hw; struct device; struct regmap; -#define UNIPHIER_CLK_MUX_MAX_PARENTS 8 +#define UNIPHIER_CLK_CPUGEAR_MAX_PARENTS 16 +#define UNIPHIER_CLK_MUX_MAX_PARENTS 8 enum uniphier_clk_type { + UNIPHIER_CLK_TYPE_CPUGEAR, UNIPHIER_CLK_TYPE_FIXED_FACTOR, UNIPHIER_CLK_TYPE_FIXED_RATE, UNIPHIER_CLK_TYPE_GATE, UNIPHIER_CLK_TYPE_MUX, }; +struct uniphier_clk_cpugear_data { + const char *parent_names[UNIPHIER_CLK_CPUGEAR_MAX_PARENTS]; + unsigned int num_parents; + unsigned int regbase; + unsigned int mask; +}; + struct uniphier_clk_fixed_factor_data { const char *parent_name; unsigned int mult; @@ -58,6 +67,7 @@ struct uniphier_clk_data { enum uniphier_clk_type type; int idx; union { + struct uniphier_clk_cpugear_data cpugear; struct uniphier_clk_fixed_factor_data factor; struct uniphier_clk_fixed_rate_data rate; struct uniphier_clk_gate_data gate; @@ -65,6 +75,20 @@ struct uniphier_clk_data { } data; }; +#define UNIPHIER_CLK_CPUGEAR(_name, _idx, _regbase, _mask, \ + _num_parents, ...) \ + { \ + .name = (_name), \ + .type = UNIPHIER_CLK_TYPE_CPUGEAR, \ + .idx = (_idx), \ + .data.cpugear = { \ + .parent_names = { __VA_ARGS__ }, \ + .num_parents = (_num_parents), \ + .regbase = (_regbase), \ + .mask = (_mask) \ + }, \ + } + #define UNIPHIER_CLK_FACTOR(_name, _idx, _parent, _mult, _div) \ { \ .name = (_name), \ @@ -77,7 +101,6 @@ struct uniphier_clk_data { }, \ } - #define UNIPHIER_CLK_GATE(_name, _idx, _parent, _reg, _bit) \ { \ .name = (_name), \ @@ -90,7 +113,25 @@ struct uniphier_clk_data { }, \ } +#define UNIPHIER_CLK_DIV(parent, div) \ + UNIPHIER_CLK_FACTOR(parent "/" #div, -1, parent, 1, div) + +#define UNIPHIER_CLK_DIV2(parent, div0, div1) \ + UNIPHIER_CLK_DIV(parent, div0), \ + UNIPHIER_CLK_DIV(parent, div1) + +#define UNIPHIER_CLK_DIV3(parent, div0, div1, div2) \ + UNIPHIER_CLK_DIV2(parent, div0, div1), \ + UNIPHIER_CLK_DIV(parent, div2) + +#define UNIPHIER_CLK_DIV4(parent, div0, div1, div2, div3) \ + UNIPHIER_CLK_DIV2(parent, div0, div1), \ + UNIPHIER_CLK_DIV2(parent, div2, div3) +struct clk_hw *uniphier_clk_register_cpugear(struct device *dev, + struct regmap *regmap, + const char *name, + const struct uniphier_clk_cpugear_data *data); struct clk_hw *uniphier_clk_register_fixed_factor(struct device *dev, const char *name, const struct uniphier_clk_fixed_factor_data *data); |