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-slow.c | 2 | ||||
-rw-r--r-- | drivers/clk/at91/clk-usb.c | 20 | ||||
-rw-r--r-- | drivers/clk/clk-efm32gg.c | 6 | ||||
-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/clk.c | 80 | ||||
-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/qcom/gcc-ipq806x.c | 2 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-rk3188.c | 8 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-rk3288.c | 12 | ||||
-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/ti/clk-dra7-atl.c | 6 | ||||
-rw-r--r-- | drivers/clk/ti/divider.c | 7 | ||||
-rw-r--r-- | drivers/clk/zynq/clkc.c | 30 | ||||
-rw-r--r-- | drivers/clk/zynq/pll.c | 4 |
25 files changed, 703 insertions, 342 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index cfd3af7b2cbd..85131ae0b288 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 f537a0b1f798..27c542ba9e73 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 cf6ed023504c..6ec79dbc0840 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-slow.c b/drivers/clk/at91/clk-slow.c index 0300c46ee247..32f7c1b36204 100644 --- a/drivers/clk/at91/clk-slow.c +++ b/drivers/clk/at91/clk-slow.c @@ -447,7 +447,7 @@ void __init of_at91sam9260_clk_slow_setup(struct device_node *np, int i; num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); - if (num_parents <= 0 || num_parents > 1) + if (num_parents != 2) return; for (i = 0; i < num_parents; ++i) { diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index 7d1d26a4bd04..24b5b020753a 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-efm32gg.c b/drivers/clk/clk-efm32gg.c index bac2ddf49d02..73a8d0ff530c 100644 --- a/drivers/clk/clk-efm32gg.c +++ b/drivers/clk/clk-efm32gg.c @@ -22,7 +22,7 @@ static struct clk_onecell_data clk_data = { .clk_num = ARRAY_SIZE(clk), }; -static int __init efm32gg_cmu_init(struct device_node *np) +static void __init efm32gg_cmu_init(struct device_node *np) { int i; void __iomem *base; @@ -33,7 +33,7 @@ static int __init efm32gg_cmu_init(struct device_node *np) base = of_iomap(np, 0); if (!base) { pr_warn("Failed to map address range for efm32gg,cmu node\n"); - return -EADDRNOTAVAIL; + return; } clk[clk_HFXO] = clk_register_fixed_rate(NULL, "HFXO", NULL, @@ -76,6 +76,6 @@ static int __init efm32gg_cmu_init(struct device_node *np) clk[clk_HFPERCLKDAC0] = clk_register_gate(NULL, "HFPERCLK.DAC0", "HFXO", 0, base + CMU_HFPERCLKEN0, 17, 0, NULL); - return of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); } CLK_OF_DECLARE(efm32ggcmu, "efm32gg,cmu", efm32gg_cmu_init); diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index ede685ca0d20..82a59d0086cc 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 000000000000..6505049d50f1 --- /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 000000000000..997e86fc3f4d --- /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 3d7e8dd8fd58..ed0beb4cb39b 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 000000000000..8e480c5c2945 --- /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", |