diff options
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/Kconfig | 11 | ||||
-rw-r--r-- | drivers/clk/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/at91/clk-pll.c | 160 | ||||
-rw-r--r-- | drivers/clk/at91/clk-usb.c | 20 | ||||
-rw-r--r-- | drivers/clk/clk-fractional-divider.c | 2 | ||||
-rw-r--r-- | drivers/clk/clk-max-gen.c | 192 | ||||
-rw-r--r-- | drivers/clk/clk-max-gen.h | 32 | ||||
-rw-r--r-- | drivers/clk/clk-max77686.c | 183 | ||||
-rw-r--r-- | drivers/clk/clk-max77802.c | 98 | ||||
-rw-r--r-- | drivers/clk/mvebu/common.c | 9 | ||||
-rw-r--r-- | drivers/clk/mvebu/common.h | 2 | ||||
-rw-r--r-- | drivers/clk/mvebu/kirkwood.c | 102 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-rk3188.c | 8 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-rk3288.c | 8 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk.c | 73 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk.h | 1 | ||||
-rw-r--r-- | drivers/clk/shmobile/clk-rcar-gen2.c | 1 | ||||
-rw-r--r-- | drivers/clk/zynq/clkc.c | 30 | ||||
-rw-r--r-- | drivers/clk/zynq/pll.c | 4 |
19 files changed, 652 insertions, 286 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index cfd3af7..85131ae 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -32,12 +32,23 @@ config COMMON_CLK_WM831X source "drivers/clk/versatile/Kconfig" +config COMMON_CLK_MAX_GEN + bool + config COMMON_CLK_MAX77686 tristate "Clock driver for Maxim 77686 MFD" depends on MFD_MAX77686 + select COMMON_CLK_MAX_GEN ---help--- This driver supports Maxim 77686 crystal oscillator clock. +config COMMON_CLK_MAX77802 + tristate "Clock driver for Maxim 77802 PMIC" + depends on MFD_MAX77686 + select COMMON_CLK_MAX_GEN + ---help--- + This driver supports Maxim 77802 crystal oscillator clock. + config COMMON_CLK_SI5351 tristate "Clock driver for SiLabs 5351A/B/C" depends on I2C diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index f537a0b..27c542b 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -22,7 +22,9 @@ obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o +obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o +obj-$(CONFIG_COMMON_CLK_MAX77802) += clk-max77802.o obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c index cf6ed02..6ec79db 100644 --- a/drivers/clk/at91/clk-pll.c +++ b/drivers/clk/at91/clk-pll.c @@ -15,6 +15,7 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/io.h> +#include <linux/kernel.h> #include <linux/wait.h> #include <linux/sched.h> #include <linux/interrupt.h> @@ -29,9 +30,12 @@ #define PLL_DIV(reg) ((reg) & PLL_DIV_MASK) #define PLL_MUL(reg, layout) (((reg) >> (layout)->mul_shift) & \ (layout)->mul_mask) +#define PLL_MUL_MIN 2 +#define PLL_MUL_MASK(layout) ((layout)->mul_mask) +#define PLL_MUL_MAX(layout) (PLL_MUL_MASK(layout) + 1) #define PLL_ICPR_SHIFT(id) ((id) * 16) #define PLL_ICPR_MASK(id) (0xffff << PLL_ICPR_SHIFT(id)) -#define PLL_MAX_COUNT 0x3ff +#define PLL_MAX_COUNT 0x3f #define PLL_COUNT_SHIFT 8 #define PLL_OUT_SHIFT 14 #define PLL_MAX_ID 1 @@ -147,115 +151,113 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_pll *pll = to_clk_pll(hw); - const struct clk_pll_layout *layout = pll->layout; - struct at91_pmc *pmc = pll->pmc; - int offset = PLL_REG(pll->id); - u32 tmp = pmc_read(pmc, offset) & layout->pllr_mask; - u8 div = PLL_DIV(tmp); - u16 mul = PLL_MUL(tmp, layout); - if (!div || !mul) + + if (!pll->div || !pll->mul) return 0; - return (parent_rate * (mul + 1)) / div; + return (parent_rate / pll->div) * (pll->mul + 1); } static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate, unsigned long parent_rate, u32 *div, u32 *mul, u32 *index) { - unsigned long maxrate; - unsigned long minrate; - unsigned long divrate; - unsigned long bestdiv = 1; - unsigned long bestmul; - unsigned long tmpdiv; - unsigned long roundup; - unsigned long rounddown; - unsigned long remainder; - unsigned long bestremainder; - unsigned long maxmul; - unsigned long maxdiv; - unsigned long mindiv; - int i = 0; const struct clk_pll_layout *layout = pll->layout; const struct clk_pll_characteristics *characteristics = pll->characteristics; + unsigned long bestremainder = ULONG_MAX; + unsigned long maxdiv, mindiv, tmpdiv; + long bestrate = -ERANGE; + unsigned long bestdiv; + unsigned long bestmul; + int i = 0; - /* Minimum divider = 1 */ - /* Maximum multiplier = max_mul */ - maxmul = layout->mul_mask + 1; - maxrate = (parent_rate * maxmul) / 1; - - /* Maximum divider = max_div */ - /* Minimum multiplier = 2 */ - maxdiv = PLL_DIV_MAX; - minrate = (parent_rate * 2) / maxdiv; - + /* Check if parent_rate is a valid input rate */ if (parent_rate < characteristics->input.min || - parent_rate < characteristics->input.max) - return -ERANGE; - - if (parent_rate < minrate || parent_rate > maxrate) + parent_rate > characteristics->input.max) return -ERANGE; - for (i = 0; i < characteristics->num_output; i++) { - if (parent_rate >= characteristics->output[i].min && - parent_rate <= characteristics->output[i].max) - break; - } - - if (i >= characteristics->num_output) - return -ERANGE; - - bestmul = rate / parent_rate; - rounddown = parent_rate % rate; - roundup = rate - rounddown; - bestremainder = roundup < rounddown ? roundup : rounddown; - - if (!bestremainder) { - if (div) - *div = bestdiv; - if (mul) - *mul = bestmul; - if (index) - *index = i; - return rate; - } - - maxdiv = 255 / (bestmul + 1); - if (parent_rate / maxdiv < characteristics->input.min) - maxdiv = parent_rate / characteristics->input.min; - mindiv = parent_rate / characteristics->input.max; - if (parent_rate % characteristics->input.max) - mindiv++; - - for (tmpdiv = mindiv; tmpdiv < maxdiv; tmpdiv++) { - divrate = parent_rate / tmpdiv; - - rounddown = rate % divrate; - roundup = divrate - rounddown; - remainder = roundup < rounddown ? roundup : rounddown; - + /* + * Calculate minimum divider based on the minimum multiplier, the + * parent_rate and the requested rate. + * Should always be 2 according to the input and output characteristics + * of the PLL blocks. + */ + mindiv = (parent_rate * PLL_MUL_MIN) / rate; + if (!mindiv) + mindiv = 1; + + /* + * Calculate the maximum divider which is limited by PLL register + * layout (limited by the MUL or DIV field size). + */ + maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX(layout), rate); + if (maxdiv > PLL_DIV_MAX) + maxdiv = PLL_DIV_MAX; + + /* + * Iterate over the acceptable divider values to find the best + * divider/multiplier pair (the one that generates the closest + * rate to the requested one). + */ + for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) { + unsigned long remainder; + unsigned long tmprate; + unsigned long tmpmul; + + /* + * Calculate the multiplier associated with the current + * divider that provide the closest rate to the requested one. + */ + tmpmul = DIV_ROUND_CLOSEST(rate, parent_rate / tmpdiv); + tmprate = (parent_rate / tmpdiv) * tmpmul; + if (tmprate > rate) + remainder = tmprate - rate; + else + remainder = rate - tmprate; + + /* + * Compare the remainder with the best remainder found until + * now and elect a new best multiplier/divider pair if the + * current remainder is smaller than the best one. + */ if (remainder < bestremainder) { bestremainder = remainder; - bestmul = rate / divrate; bestdiv = tmpdiv; + bestmul = tmpmul; + bestrate = tmprate; } + /* + * We've found a perfect match! + * Stop searching now and use this multiplier/divider pair. + */ if (!remainder) break; } - rate = (parent_rate / bestdiv) * bestmul; + /* We haven't found any multiplier/divider pair => return -ERANGE */ + if (bestrate < 0) + return bestrate; + + /* Check if bestrate is a valid output rate */ + for (i = 0; i < characteristics->num_output; i++) { + if (bestrate >= characteristics->output[i].min && + bestrate <= characteristics->output[i].max) + break; + } + + if (i >= characteristics->num_output) + return -ERANGE; if (div) *div = bestdiv; if (mul) - *mul = bestmul; + *mul = bestmul - 1; if (index) *index = i; - return rate; + return bestrate; } static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index 7d1d26a..24b5b02 100644 --- a/drivers/clk/at91/clk-usb.c +++ b/drivers/clk/at91/clk-usb.c @@ -238,16 +238,22 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); + struct clk *parent = __clk_get_parent(hw->clk); unsigned long bestrate = 0; int bestdiff = -1; unsigned long tmprate; int tmpdiff; int i = 0; - for (i = 0; i < 4; i++) { + for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { + unsigned long tmp_parent_rate; + if (!usb->divisors[i]) continue; - tmprate = *parent_rate / usb->divisors[i]; + + tmp_parent_rate = rate * usb->divisors[i]; + tmp_parent_rate = __clk_round_rate(parent, tmp_parent_rate); + tmprate = tmp_parent_rate / usb->divisors[i]; if (tmprate < rate) tmpdiff = rate - tmprate; else @@ -256,6 +262,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, if (bestdiff < 0 || bestdiff > tmpdiff) { bestrate = tmprate; bestdiff = tmpdiff; + *parent_rate = tmp_parent_rate; } if (!bestdiff) @@ -272,10 +279,13 @@ static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, int i; struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); struct at91_pmc *pmc = usb->pmc; - unsigned long div = parent_rate / rate; + unsigned long div; - if (parent_rate % rate) + if (!rate || parent_rate % rate) return -EINVAL; + + div = parent_rate / rate; + for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { if (usb->divisors[i] == div) { tmp = pmc_read(pmc, AT91_CKGR_PLLBR) & @@ -311,7 +321,7 @@ at91rm9200_clk_register_usb(struct at91_pmc *pmc, const char *name, init.ops = &at91rm9200_usb_ops; init.parent_names = &parent_name; init.num_parents = 1; - init.flags = 0; + init.flags = CLK_SET_RATE_PARENT; usb->hw.init = &init; usb->pmc = pmc; diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index ede685c..82a59d0 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -36,7 +36,7 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, m = (val & fd->mmask) >> fd->mshift; n = (val & fd->nmask) >> fd->nshift; - ret = parent_rate * m; + ret = (u64)parent_rate * m; do_div(ret, n); return ret; diff --git a/drivers/clk/clk-max-gen.c b/drivers/clk/clk-max-gen.c new file mode 100644 index 0000000..6505049 --- /dev/null +++ b/drivers/clk/clk-max-gen.c @@ -0,0 +1,192 @@ +/* + * clk-max-gen.c - Generic clock driver for Maxim PMICs clocks + * + * Copyright (C) 2014 Google, Inc + * + * Copyright (C) 2012 Samsung Electornics + * Jonghwa Lee <jonghwa3.lee@samsung.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. + * + * This driver is based on clk-max77686.c + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/clk-provider.h> +#include <linux/mutex.h> +#include <linux/clkdev.h> +#include <linux/of.h> +#include <linux/export.h> + +struct max_gen_clk { + struct regmap *regmap; + u32 mask; + u32 reg; + struct clk_hw hw; +}; + +static struct max_gen_clk *to_max_gen_clk(struct clk_hw *hw) +{ + return container_of(hw, struct max_gen_clk, hw); +} + +static int max_gen_clk_prepare(struct clk_hw *hw) +{ + struct max_gen_clk *max_gen = to_max_gen_clk(hw); + + return regmap_update_bits(max_gen->regmap, max_gen->reg, + max_gen->mask, max_gen->mask); +} + +static void max_gen_clk_unprepare(struct clk_hw *hw) +{ + struct max_gen_clk *max_gen = to_max_gen_clk(hw); + + regmap_update_bits(max_gen->regmap, max_gen->reg, + max_gen->mask, ~max_gen->mask); +} + +static int max_gen_clk_is_prepared(struct clk_hw *hw) +{ + struct max_gen_clk *max_gen = to_max_gen_clk(hw); + int ret; + u32 val; + + ret = regmap_read(max_gen->regmap, max_gen->reg, &val); + + if (ret < 0) + return -EINVAL; + + return val & max_gen->mask; +} + +static unsigned long max_gen_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 32768; +} + +struct clk_ops max_gen_clk_ops = { + .prepare = max_gen_clk_prepare, + .unprepare = max_gen_clk_unprepare, + .is_prepared = max_gen_clk_is_prepared, + .recalc_rate = max_gen_recalc_rate, +}; +EXPORT_SYMBOL_GPL(max_gen_clk_ops); + +static struct clk *max_gen_clk_register(struct device *dev, + struct max_gen_clk *max_gen) +{ + struct clk *clk; + struct clk_hw *hw = &max_gen->hw; + int ret; + + clk = devm_clk_register(dev, hw); + if (IS_ERR(clk)) + return clk; + + ret = clk_register_clkdev(clk, hw->init->name, NULL); + + if (ret) + return ERR_PTR(ret); + + return clk; +} + +int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap, + u32 reg, struct clk_init_data *clks_init, int num_init) +{ + int i, ret; + struct max_gen_clk *max_gen_clks; + struct clk **clocks; + struct device *dev = pdev->dev.parent; + const char *clk_name; + struct clk_init_data *init; + + clocks = devm_kzalloc(dev, sizeof(struct clk *) * num_init, GFP_KERNEL); + if (!clocks) + return -ENOMEM; + + max_gen_clks = devm_kzalloc(dev, sizeof(struct max_gen_clk) + * num_init, GFP_KERNEL); + if (!max_gen_clks) + return -ENOMEM; + + for (i = 0; i < num_init; i++) { + max_gen_clks[i].regmap = regmap; + max_gen_clks[i].mask = 1 << i; + max_gen_clks[i].reg = reg; + + init = devm_kzalloc(dev, sizeof(*init), GFP_KERNEL); + if (!init) + return -ENOMEM; + + if (dev->of_node && + !of_property_read_string_index(dev->of_node, + "clock-output-names", + i, &clk_name)) + init->name = clk_name; + else + init->name = clks_init[i].name; + + init->ops = clks_init[i].ops; + init->flags = clks_init[i].flags; + + max_gen_clks[i].hw.init = init; + + clocks[i] = max_gen_clk_register(dev, &max_gen_clks[i]); + if (IS_ERR(clocks[i])) { + ret = PTR_ERR(clocks[i]); + dev_err(dev, "failed to register %s\n", + max_gen_clks[i].hw.init->name); + return ret; + } + } + + platform_set_drvdata(pdev, clocks); + + if (dev->of_node) { + struct clk_onecell_data *of_data; + + of_data = devm_kzalloc(dev, sizeof(*of_data), GFP_KERNEL); + if (!of_data) + return -ENOMEM; + + of_data->clks = clocks; + of_data->clk_num = num_init; + ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, + of_data); + + if (ret) { + dev_err(dev, "failed to register OF clock provider\n"); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(max_gen_clk_probe); + +int max_gen_clk_remove(struct platform_device *pdev, int num_init) +{ + struct device *dev = pdev->dev.parent; + + if (dev->of_node) + of_clk_del_provider(dev->of_node); + + return 0; +} +EXPORT_SYMBOL_GPL(max_gen_clk_remove); diff --git a/drivers/clk/clk-max-gen.h b/drivers/clk/clk-max-gen.h new file mode 100644 index 0000000..997e86f --- /dev/null +++ b/drivers/clk/clk-max-gen.h @@ -0,0 +1,32 @@ +/* + * clk-max-gen.h - Generic clock driver for Maxim PMICs clocks + * + * Copyright (C) 2014 Google, Inc + * + * 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 __CLK_MAX_GEN_H__ +#define __CLK_MAX_GEN_H__ + +#include <linux/types.h> +#include <linux/device.h> +#include <linux/clkdev.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> + +int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap, + u32 reg, struct clk_init_data *clks_init, int num_init); +int max_gen_clk_remove(struct platform_device *pdev, int num_init); +extern struct clk_ops max_gen_clk_ops; + +#endif /* __CLK_MAX_GEN_H__ */ diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c index 3d7e8dd..ed0beb4 100644 --- a/drivers/clk/clk-max77686.c +++ b/drivers/clk/clk-max77686.c @@ -30,193 +30,38 @@ #include <linux/mutex.h> #include <linux/clkdev.h> -enum { - MAX77686_CLK_AP = 0, - MAX77686_CLK_CP, - MAX77686_CLK_PMIC, - MAX77686_CLKS_NUM, -}; - -struct max77686_clk { - struct max77686_dev *iodev; - u32 mask; - struct clk_hw hw; - struct clk_lookup *lookup; -}; - -static struct max77686_clk *to_max77686_clk(struct clk_hw *hw) -{ - return container_of(hw, struct max77686_clk, hw); -} - -static int max77686_clk_prepare(struct clk_hw *hw) -{ - struct max77686_clk *max77686 = to_max77686_clk(hw); - - return regmap_update_bits(max77686->iodev->regmap, - MAX77686_REG_32KHZ, max77686->mask, - max77686->mask); -} - -static void max77686_clk_unprepare(struct clk_hw *hw) -{ - struct max77686_clk *max77686 = to_max77686_clk(hw); - - regmap_update_bits(max77686->iodev->regmap, - MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask); -} - -static int max77686_clk_is_prepared(struct clk_hw *hw) -{ - struct max77686_clk *max77686 = to_max77686_clk(hw); - int ret; - u32 val; - - ret = regmap_read(max77686->iodev->regmap, - MAX77686_REG_32KHZ, &val); - - if (ret < 0) - return -EINVAL; - - return val & max77686->mask; -} - -static unsigned long max77686_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - return 32768; -} - -static struct clk_ops max77686_clk_ops = { - .prepare = max77686_clk_prepare, - .unprepare = max77686_clk_unprepare, - .is_prepared = max77686_clk_is_prepared, - .recalc_rate = max77686_recalc_rate, -}; +#include <dt-bindings/clock/maxim,max77686.h> +#include "clk-max-gen.h" static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = { [MAX77686_CLK_AP] = { .name = "32khz_ap", - .ops = &max77686_clk_ops, + .ops = &max_gen_clk_ops, .flags = CLK_IS_ROOT, }, [MAX77686_CLK_CP] = { .name = "32khz_cp", - .ops = &max77686_clk_ops, + .ops = &max_gen_clk_ops, .flags = CLK_IS_ROOT, }, [MAX77686_CLK_PMIC] = { .name = "32khz_pmic", - .ops = &max77686_clk_ops, + .ops = &max_gen_clk_ops, .flags = CLK_IS_ROOT, }, }; -static struct clk *max77686_clk_register(struct device *dev, - struct max77686_clk *max77686) -{ - struct clk *clk; - struct clk_hw *hw = &max77686->hw; - - clk = clk_register(dev, hw); - if (IS_ERR(clk)) - return clk; - - max77686->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL); - if (!max77686->lookup) - return ERR_PTR(-ENOMEM); - - max77686->lookup->con_id = hw->init->name; - max77686->lookup->clk = clk; - - clkdev_add(max77686->lookup); - - return clk; -} - static int max77686_clk_probe(struct platform_device *pdev) { struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct max77686_clk *max77686_clks[MAX77686_CLKS_NUM]; - struct clk **clocks; - int i, ret; - - clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *) - * MAX77686_CLKS_NUM, GFP_KERNEL); - if (!clocks) - return -ENOMEM; - - for (i = 0; i < MAX77686_CLKS_NUM; i++) { - max77686_clks[i] = devm_kzalloc(&pdev->dev, - sizeof(struct max77686_clk), GFP_KERNEL); - if (!max77686_clks[i]) - return -ENOMEM; - } - - for (i = 0; i < MAX77686_CLKS_NUM; i++) { - max77686_clks[i]->iodev = iodev; - max77686_clks[i]->mask = 1 << i; - max77686_clks[i]->hw.init = &max77686_clks_init[i]; - - clocks[i] = max77686_clk_register(&pdev->dev, max77686_clks[i]); - if (IS_ERR(clocks[i])) { - ret = PTR_ERR(clocks[i]); - dev_err(&pdev->dev, "failed to register %s\n", - max77686_clks[i]->hw.init->name); - goto err_clocks; - } - } - - platform_set_drvdata(pdev, clocks); - - if (iodev->dev->of_node) { - struct clk_onecell_data *of_data; - of_data = devm_kzalloc(&pdev->dev, - sizeof(*of_data), GFP_KERNEL); - if (!of_data) { - ret = -ENOMEM; - goto err_clocks; - } - - of_data->clks = clocks; - of_data->clk_num = MAX77686_CLKS_NUM; - ret = of_clk_add_provider(iodev->dev->of_node, - of_clk_src_onecell_get, of_data); - if (ret) { - dev_err(&pdev->dev, "failed to register OF clock provider\n"); - goto err_clocks; - } - } - - return 0; - -err_clocks: - for (--i; i >= 0; --i) { - clkdev_drop(max77686_clks[i]->lookup); - clk_unregister(max77686_clks[i]->hw.clk); - } - - return ret; + return max_gen_clk_probe(pdev, iodev->regmap, MAX77686_REG_32KHZ, + max77686_clks_init, MAX77686_CLKS_NUM); } static int max77686_clk_remove(struct platform_device *pdev) { - struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct clk **clocks = platform_get_drvdata(pdev); - int i; - - if (iodev->dev->of_node) - of_clk_del_provider(iodev->dev->of_node); - - for (i = 0; i < MAX77686_CLKS_NUM; i++) { - struct clk_hw *hw = __clk_get_hw(clocks[i]); - struct max77686_clk *max77686 = to_max77686_clk(hw); - - clkdev_drop(max77686->lookup); - clk_unregister(clocks[i]); - } - return 0; + return max_gen_clk_remove(pdev, MAX77686_CLKS_NUM); } static const struct platform_device_id max77686_clk_id[] = { @@ -235,17 +80,7 @@ static struct platform_driver max77686_clk_driver = { .id_table = max77686_clk_id, }; -static int __init max77686_clk_init(void) -{ - return platform_driver_register(&max77686_clk_driver); -} -subsys_initcall(max77686_clk_init); - -static void __init max77686_clk_cleanup(void) -{ - platform_driver_unregister(&max77686_clk_driver); -} -module_exit(max77686_clk_cleanup); +module_platform_driver(max77686_clk_driver); MODULE_DESCRIPTION("MAXIM 77686 Clock Driver"); MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>"); diff --git a/drivers/clk/clk-max77802.c b/drivers/clk/clk-max77802.c new file mode 100644 index 0000000..8e480c5 --- /dev/null +++ b/drivers/clk/clk-max77802.c @@ -0,0 +1,98 @@ +/* + * clk-max77802.c - Clock driver for Maxim 77802 + * + * Copyright (C) 2014 Google, Inc + * + * Copyright (C) 2012 Samsung Electornics + * Jonghwa Lee <jonghwa3.lee@samsung.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. + * + * This driver is based on clk-max77686.c + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/mfd/max77686-private.h> +#include <linux/clk-provider.h> +#include <linux/mutex.h> +#include <linux/clkdev.h> + +#include <dt-bindings/clock/maxim,max77802.h> +#include "clk-max-gen.h" + +#define MAX77802_CLOCK_OPMODE_MASK 0x1 +#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3 + +static struct clk_init_data max77802_clks_init[MAX77802_CLKS_NUM] = { + [MAX77802_CLK_32K_AP] = { + .name = "32khz_ap", + .ops = &max_gen_clk_ops, + .flags = CLK_IS_ROOT, + }, + [MAX77802_CLK_32K_CP] = { + .name = "32khz_cp", + .ops = &max_gen_clk_ops, + .flags = CLK_IS_ROOT, + }, +}; + +static int max77802_clk_probe(struct platform_device *pdev) +{ + struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); + int ret; + + ret = max_gen_clk_probe(pdev, iodev->regmap, MAX77802_REG_32KHZ, + max77802_clks_init, MAX77802_CLKS_NUM); + + if (ret) { + dev_err(&pdev->dev, "generic probe failed %d\n", ret); + return ret; + } + + /* Enable low-jitter mode on the 32khz clocks. */ + ret = regmap_update_bits(iodev->regmap, MAX77802_REG_32KHZ, + 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT, + 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT); + if (ret < 0) + dev_err(&pdev->dev, "failed to enable low-jitter mode\n"); + + return ret; +} + +static int max77802_clk_remove(struct platform_device *pdev) +{ + return max_gen_clk_remove(pdev, MAX77802_CLKS_NUM); +} + +static const struct platform_device_id max77802_clk_id[] = { + { "max77802-clk", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, max77802_clk_id); + +static struct platform_driver max77802_clk_driver = { + .driver = { + .name = "max77802-clk", + .owner = THIS_MODULE, + }, + .probe = max77802_clk_probe, + .remove = max77802_clk_remove, + .id_table = max77802_clk_id, +}; + +module_platform_driver(max77802_clk_driver); + +MODULE_DESCRIPTION("MAXIM 77802 Clock Driver"); +MODULE_AUTHOR("Javier Martinez Canillas <javier.martinez@collabora.co.uk>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c index 25ceccf..8145c4e 100644 --- a/drivers/clk/mvebu/common.c +++ b/drivers/clk/mvebu/common.c @@ -89,8 +89,10 @@ void __init mvebu_coreclk_setup(struct device_node *np, * Clock Gating Control */ +DEFINE_SPINLOCK(ctrl_gating_lock); + struct clk_gating_ctrl { - spinlock_t lock; + spinlock_t *lock; struct clk **gates; int num_gates; }; @@ -138,7 +140,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np, if (WARN_ON(!ctrl)) goto ctrl_out; - spin_lock_init(&ctrl->lock); + /* lock must already be initialized */ + ctrl->lock = &ctrl_gating_lock; /* Count, allocate, and register clock gates */ for (n = 0; desc[n].name;) @@ -155,7 +158,7 @@ void __init mvebu_clk_gating_setup(struct device_node *np, (desc[n].parent) ? desc[n].parent : default_parent; ctrl->gates[n] = clk_register_gate(NULL, desc[n].name, parent, desc[n].flags, base, desc[n].bit_idx, - 0, &ctrl->lock); + 0, ctrl->lock); WARN_ON(IS_ERR(ctrl->gates[n])); } diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h index f968b4d..8cd28e4 100644 --- a/drivers/clk/mvebu/common.h +++ b/drivers/clk/mvebu/common.h @@ -17,6 +17,8 @@ #include <linux/kernel.h> +extern spinlock_t ctrl_gating_lock; + struct device_node; struct coreclk_ratio { diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c index ddb666a..99550f2 100644 --- a/drivers/clk/mvebu/kirkwood.c +++ b/drivers/clk/mvebu/kirkwood.c @@ -13,9 +13,11 @@ */ #include <linux/kernel.h> +#include <linux/slab.h> #include <linux/clk-provider.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_address.h> #include "common.h" /* @@ -214,7 +216,6 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = { { "runit", NULL, 7, 0 }, { "xor0", NULL, 8, 0 }, { "audio", NULL, 9, 0 }, - { "powersave", "cpuclk", 11, 0 }, { "sata0", NULL, 14, 0 }, { "sata1", NULL, 15, 0 }, { "xor1", NULL, 16, 0 }, @@ -225,6 +226,101 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = { { } }; + +/* + * Clock Muxing Control + */ + +struct clk_muxing_soc_desc { + const char *name; + const char **parents; + int num_parents; + int shift; + int width; + unsigned long flags; +}; + +struct clk_muxing_ctrl { + spinlock_t *lock; + struct clk **muxes; + int num_muxes; +}; + +static const char *powersave_parents[] = { + "cpuclk", + "ddrclk", +}; + +static const struct clk_muxing_soc_desc kirkwood_mux_desc[] __initconst = { + { "powersave", powersave_parents, ARRAY_SIZE(powersave_parents), + 11, 1, 0 }, +}; + +#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) + +static struct clk *clk_muxing_get_src( + struct of_phandle_args *clkspec, void *data) +{ + struct clk_muxing_ctrl *ctrl = (struct clk_muxing_ctrl *)data; + int n; + + if (clkspec->args_count < 1) + return ERR_PTR(-EINVAL); + + for (n = 0; n < ctrl->num_muxes; n++) { + struct clk_mux *mux = + to_clk_mux(__clk_get_hw(ctrl->muxes[n])); + if (clkspec->args[0] == mux->shift) + return ctrl->muxes[n]; + } + return ERR_PTR(-ENODEV); +} + +static void __init kirkwood_clk_muxing_setup(struct device_node *np, + const struct clk_muxing_soc_desc *desc) +{ + struct clk_muxing_ctrl *ctrl; + void __iomem *base; + int n; + + base = of_iomap(np, 0); + if (WARN_ON(!base)) + return; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (WARN_ON(!ctrl)) + goto ctrl_out; + + /* lock must already be initialized */ + ctrl->lock = &ctrl_gating_lock; + + /* Count, allocate, and register clock muxes */ + for (n = 0; desc[n].name;) + n++; + + ctrl->num_muxes = n; + ctrl->muxes = kcalloc(ctrl->num_muxes, sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!ctrl->muxes)) + goto muxes_out; + + for (n = 0; n < ctrl->num_muxes; n++) { + ctrl->muxes[n] = clk_register_mux(NULL, desc[n].name, + desc[n].parents, desc[n].num_parents, + desc[n].flags, base, desc[n].shift, + desc[n].width, desc[n].flags, ctrl->lock); + WARN_ON(IS_ERR(ctrl->muxes[n])); + } + + of_clk_add_provider(np, clk_muxing_get_src, ctrl); + + return; +muxes_out: + kfree(ctrl); +ctrl_out: + iounmap(base); +} + static void __init kirkwood_clk_init(struct device_node *np) { struct device_node *cgnp = @@ -236,8 +332,10 @@ static void __init kirkwood_clk_init(struct device_node *np) else mvebu_coreclk_setup(np, &kirkwood_coreclks); - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, kirkwood_gating_desc); + kirkwood_clk_muxing_setup(cgnp, kirkwood_mux_desc); + } } CLK_OF_DECLARE(kirkwood_clk, "marvell,kirkwood-core-clock", kirkwood_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index a83a6d8..0147614 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -599,6 +599,12 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = { GATE(ACLK_GPS, "aclk_gps", "aclk_peri", 0, RK2928_CLKGATE_CON(8), 13, GFLAGS), }; +static const char *rk3188_critical_clocks[] __initconst = { + "aclk_cpu", + "aclk_peri", + "hclk_peri", +}; + static void __init rk3188_common_clk_init(struct device_node *np) { void __iomem *reg_base; @@ -628,6 +634,8 @@ static void __init rk3188_common_clk_init(struct device_node *np) RK3188_GRF_SOC_STATUS); rockchip_clk_register_branches(common_clk_branches, ARRAY_SIZE(common_clk_branches)); + rockchip_clk_protect_critical(rk3188_critical_clocks, + ARRAY_SIZE(rk3188_critical_clocks)); rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index 0d8c6c5..08c24c6 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -680,6 +680,12 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { GATE(0, "pclk_isp_in", "ext_isp", 0, RK3288_CLKGATE_CON(16), 3, GFLAGS), }; +static const char *rk3288_critical_clocks[] __initconst = { + "aclk_cpu", + "aclk_peri", + "hclk_peri", +}; + static void __init rk3288_clk_init(struct device_node *np) { void __iomem *reg_base; @@ -710,6 +716,8 @@ static void __init rk3288_clk_init(struct device_node *np) RK3288_GRF_SOC_STATUS); rockchip_clk_register_branches(rk3288_clk_branches, ARRAY_SIZE(rk3288_clk_branches)); + rockchip_clk_protect_critical(rk3288_critical_clocks, + ARRAY_SIZE(rk3288_critical_clocks)); rockchip_register_softrst(np, 9, reg_base + RK3288_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index 278cf9d..d9c6db2 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -37,7 +37,7 @@ * * sometimes without one of those components. */ -struct clk *rockchip_clk_register_branch(const char *name, +static struct clk *rockchip_clk_register_branch(const char *name, const char **parent_names, u8 num_parents, void __iomem *base, int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags, u8 div_shift, u8 div_width, u8 div_flags, @@ -103,6 +103,54 @@ struct clk *rockchip_clk_register_branch(const char *name, return clk; } +static struct clk *rockchip_clk_register_frac_branch(const char *name, + const char **parent_names, u8 num_parents, void __iomem *base, + int muxdiv_offset, u8 div_flags, + int gate_offset, u8 gate_shift, u8 gate_flags, + unsigned long flags, spinlock_t *lock) +{ + struct clk *clk; + struct clk_gate *gate = NULL; + struct clk_fractional_divider *div = NULL; + const struct clk_ops *div_ops = NULL, *gate_ops = NULL; + + if (gate_offset >= 0) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->flags = gate_flags; + gate->reg = base + gate_offset; + gate->bit_idx = gate_shift; + gate->lock = lock; + gate_ops = &clk_gate_ops; + } + + if (muxdiv_offset < 0) + return ERR_PTR(-EINVAL); + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + div->flags = div_flags; + div->reg = base + muxdiv_offset; + div->mshift = 16; + div->mmask = 0xffff0000; + div->nshift = 0; + div->nmask = 0xffff; + div->lock = lock; + div_ops = &clk_fractional_divider_ops; + + clk = clk_register_composite(NULL, name, parent_names, num_parents, + NULL, NULL, + &div->hw, div_ops, + gate ? &gate->hw : NULL, gate_ops, + flags); + + return clk; +} + static DEFINE_SPINLOCK(clk_lock); static struct clk **clk_table; static void __iomem *reg_base; @@ -197,8 +245,14 @@ void __init rockchip_clk_register_branches( list->div_flags, &clk_lock); break; case branch_fraction_divider: - /* unimplemented */ - continue; + /* keep all gates untouched for now */ + flags |= CLK_IGNORE_UNUSED; + + clk = rockchip_clk_register_frac_branch(list->name, + list->parent_names, list->num_parents, + reg_base, list->muxdiv_offset, list->div_flags, + list->gate_offset, list->gate_shift, + list->gate_flags, flags, &clk_lock); break; case branch_gate: flags |= CLK_SET_RATE_PARENT; @@ -242,3 +296,16 @@ void __init rockchip_clk_register_branches( rockchip_clk_add_lookup(clk, list->id); } } + +void __init rockchip_clk_protect_critical(const char *clocks[], int nclocks) +{ + int i; + + /* Protect the clocks that needs to stay on */ + for (i = 0; i < nclocks; i++) { + struct clk *clk = __clk_lookup(clocks[i]); + + if (clk) + clk_prepare_enable(clk); + } +} diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 887cbde..2b0bca1 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -329,6 +329,7 @@ void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list, unsigned int nr_clk); void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list, unsigned int nr_pll, int grf_lock_offset); +void rockchip_clk_protect_critical(const char *clocks[], int nclocks); #define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0) diff --git a/drivers/clk/shmobile/clk-rcar-gen2.c b/drivers/clk/shmobile/clk-rcar-gen2.c index dff7f79..e996425 100644 --- a/drivers/clk/shmobile/clk-rcar-gen2.c +++ b/drivers/clk/shmobile/clk-rcar-gen2.c @@ -202,6 +202,7 @@ static const struct clk_div_table cpg_sdh_div_table[] = { }; 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 }, }; diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 246cf12..9037beb 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -85,24 +85,22 @@ static DEFINE_SPINLOCK(canmioclk_lock); static DEFINE_SPINLOCK(dbgclk_lock); static DEFINE_SPINLOCK(aperclk_lock); -static const char dummy_nm[] __initconst = "dummy_name"; - -static const char *armpll_parents[] __initdata = {"armpll_int", "ps_clk"}; -static const char *ddrpll_parents[] __initdata = {"ddrpll_int", "ps_clk"}; -static const char *iopll_parents[] __initdata = {"iopll_int", "ps_clk"}; -static const char *gem0_mux_parents[] __initdata = {"gem0_div1", dummy_nm}; -static const char *gem1_mux_parents[] __initdata = {"gem1_div1", dummy_nm}; -static const char *can0_mio_mux2_parents[] __initdata = {"can0_gate", +static const char *armpll_parents[] __initconst = {"armpll_int", "ps_clk"}; +static const char *ddrpll_parents[] __initconst = {"ddrpll_int", "ps_clk"}; +static const char *iopll_parents[] __initconst = {"iopll_int", "ps_clk"}; +static const char *gem0_mux_parents[] __initconst = {"gem0_div1", "dummy_name"}; +static const char *gem1_mux_parents[] __initconst = {"gem1_div1", "dummy_name"}; +static const char *can0_mio_mux2_parents[] __initconst = {"can0_gate", "can0_mio_mux"}; -static const char *can1_mio_mux2_parents[] __initdata = {"can1_gate", +static const char *can1_mio_mux2_parents[] __initconst = {"can1_gate", "can1_mio_mux"}; -static const char *dbg_emio_mux_parents[] __initdata = {"dbg_div", - dummy_nm}; +static const char *dbg_emio_mux_parents[] __initconst = {"dbg_div", + "dummy_name"}; -static const char *dbgtrc_emio_input_names[] __initdata = {"trace_emio_clk"}; -static const char *gem0_emio_input_names[] __initdata = {"gem0_emio_clk"}; -static const char *gem1_emio_input_names[] __initdata = {"gem1_emio_clk"}; -static const char *swdt_ext_clk_input_names[] __initdata = {"swdt_ext_clk"}; +static const char *dbgtrc_emio_input_names[] __initconst = {"trace_emio_clk"}; +static const char *gem0_emio_input_names[] __initconst = {"gem0_emio_clk"}; +static const char *gem1_emio_input_names[] __initconst = {"gem1_emio_clk"}; +static const char *swdt_ext_clk_input_names[] __initconst = {"swdt_ext_clk"}; static void __init zynq_clk_register_fclk(enum zynq_clk fclk, const char *clk_name, void __iomem *fclk_ctrl_reg, @@ -230,6 +228,7 @@ static void __init zynq_clk_setup(struct device_node *np) const char *periph_parents[4]; const char *swdt_ext_clk_mux_parents[2]; const char *can_mio_mux_parents[NUM_MIO_PINS]; + const char *dummy_nm = "dummy_name"; pr_info("Zynq clock init\n"); @@ -619,5 +618,4 @@ void __init zynq_clock_init(void) np_err: of_node_put(np); BUG(); - return; } diff --git a/drivers/clk/zynq/pll.c b/drivers/clk/zynq/pll.c index cec9759..00d72fb 100644 --- a/drivers/clk/zynq/pll.c +++ b/drivers/clk/zynq/pll.c @@ -211,10 +211,8 @@ struct clk *clk_register_zynq_pll(const char *name, const char *parent, }; pll = kmalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) { - pr_err("%s: Could not allocate Zynq PLL clk.\n", __func__); + if (!pll) return ERR_PTR(-ENOMEM); - } /* Populate the struct */ pll->hw.init = &initd; |