diff options
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/Makefile | 11 | ||||
-rw-r--r-- | drivers/clk/clk-divider.c | 6 | ||||
-rw-r--r-- | drivers/clk/clk-fixed-factor.c | 5 | ||||
-rw-r--r-- | drivers/clk/clk-fixed-rate.c | 3 | ||||
-rw-r--r-- | drivers/clk/clk-highbank.c | 20 | ||||
-rw-r--r-- | drivers/clk/clk-max77686.c | 37 | ||||
-rw-r--r-- | drivers/clk/clk-prima2.c | 205 | ||||
-rw-r--r-- | drivers/clk/clk-sunxi.c | 30 | ||||
-rw-r--r-- | drivers/clk/clk-vt8500.c | 143 | ||||
-rw-r--r-- | drivers/clk/clk-zynq.c | 14 | ||||
-rw-r--r-- | drivers/clk/clk.c | 169 | ||||
-rw-r--r-- | drivers/clk/mvebu/clk-gating-ctrl.c | 1 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-imx23.c | 2 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-imx28.c | 2 | ||||
-rw-r--r-- | drivers/clk/versatile/clk-vexpress-osc.c | 1 | ||||
-rw-r--r-- | drivers/clk/versatile/clk-vexpress.c | 8 | ||||
-rw-r--r-- | drivers/clk/x86/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/x86/clk-lpss.c | 99 | ||||
-rw-r--r-- | drivers/clk/x86/clk-lpss.h | 36 | ||||
-rw-r--r-- | drivers/clk/x86/clk-lpt.c | 86 |
20 files changed, 631 insertions, 249 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index ee90e87e767..14fde73ea6f 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,8 +1,13 @@ # common clock types obj-$(CONFIG_HAVE_CLK) += clk-devres.o obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o -obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \ - clk-mux.o clk-divider.o clk-fixed-factor.o +obj-$(CONFIG_COMMON_CLK) += clk.o +obj-$(CONFIG_COMMON_CLK) += clk-divider.o +obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o +obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o +obj-$(CONFIG_COMMON_CLK) += clk-gate.o +obj-$(CONFIG_COMMON_CLK) += clk-mux.o + # SoCs specific obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o @@ -20,8 +25,8 @@ endif obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o -obj-$(CONFIG_ARCH_SUNXI) += clk-sunxi.o obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o +obj-$(CONFIG_X86) += x86/ # Chip specific obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index a9204c69148..68b40210117 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -16,6 +16,7 @@ #include <linux/io.h> #include <linux/err.h> #include <linux/string.h> +#include <linux/log2.h> /* * DOC: basic adjustable divider clock that cannot gate @@ -29,8 +30,7 @@ #define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw) -#define div_mask(d) ((1 << (d->width)) - 1) -#define is_power_of_two(i) !(i & ~i) +#define div_mask(d) ((1 << ((d)->width)) - 1) static unsigned int _get_table_maxdiv(const struct clk_div_table *table) { @@ -137,7 +137,7 @@ static bool _is_valid_table_div(const struct clk_div_table *table, static bool _is_valid_div(struct clk_divider *divider, unsigned int div) { if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) - return is_power_of_two(div); + return is_power_of_2(div); if (divider->table) return _is_valid_table_div(divider->table, div); return true; diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index a4899855c0f..1ef271e4759 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -28,8 +28,11 @@ static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_fixed_factor *fix = to_clk_fixed_factor(hw); + unsigned long long int rate; - return parent_rate * fix->mult / fix->div; + rate = (unsigned long long int)parent_rate * fix->mult; + do_div(rate, fix->div); + return (unsigned long)rate; } static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index af78ed6b67e..dc58fbd8516 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -85,7 +85,7 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name, /** * of_fixed_clk_setup() - Setup function for simple fixed rate clock */ -void __init of_fixed_clk_setup(struct device_node *node) +void of_fixed_clk_setup(struct device_node *node) { struct clk *clk; const char *clk_name = node->name; @@ -101,4 +101,5 @@ void __init of_fixed_clk_setup(struct device_node *node) of_clk_add_provider(node, of_clk_src_simple_get, clk); } EXPORT_SYMBOL_GPL(of_fixed_clk_setup); +CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup); #endif diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 52fecadf004..2e08cb00193 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, reg |= HB_PLL_EXT_ENA; reg &= ~HB_PLL_EXT_BYPASS; } else { + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); reg &= ~HB_PLL_DIVQ_MASK; reg |= divq << HB_PLL_DIVQ_SHIFT; + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); } writel(reg, hbclk->reg); @@ -314,33 +316,23 @@ static void __init hb_pll_init(struct device_node *node) { hb_clk_init(node, &clk_pll_ops); } +CLK_OF_DECLARE(hb_pll, "calxeda,hb-pll-clock", hb_pll_init); static void __init hb_a9periph_init(struct device_node *node) { hb_clk_init(node, &a9periphclk_ops); } +CLK_OF_DECLARE(hb_a9periph, "calxeda,hb-a9periph-clock", hb_a9periph_init); static void __init hb_a9bus_init(struct device_node *node) { struct clk *clk = hb_clk_init(node, &a9bclk_ops); clk_prepare_enable(clk); } +CLK_OF_DECLARE(hb_a9bus, "calxeda,hb-a9bus-clock", hb_a9bus_init); static void __init hb_emmc_init(struct device_node *node) { hb_clk_init(node, &periclk_ops); } - -static const __initconst struct of_device_id clk_match[] = { - { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, - { .compatible = "calxeda,hb-pll-clock", .data = hb_pll_init, }, - { .compatible = "calxeda,hb-a9periph-clock", .data = hb_a9periph_init, }, - { .compatible = "calxeda,hb-a9bus-clock", .data = hb_a9bus_init, }, - { .compatible = "calxeda,hb-emmc-clock", .data = hb_emmc_init, }, - {} -}; - -void __init highbank_clocks_init(void) -{ - of_clk_init(clk_match); -} +CLK_OF_DECLARE(hb_emmc, "calxeda,hb-emmc-clock", hb_emmc_init); diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c index d098f72e1d5..9f57bc37cd6 100644 --- a/drivers/clk/clk-max77686.c +++ b/drivers/clk/clk-max77686.c @@ -44,33 +44,23 @@ struct max77686_clk { struct clk_lookup *lookup; }; -static struct max77686_clk *get_max77686_clk(struct clk_hw *hw) +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; - int ret; - - max77686 = get_max77686_clk(hw); - if (!max77686) - return -ENOMEM; - - ret = regmap_update_bits(max77686->iodev->regmap, - MAX77686_REG_32KHZ, max77686->mask, max77686->mask); + struct max77686_clk *max77686 = to_max77686_clk(hw); - return ret; + 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; - - max77686 = get_max77686_clk(hw); - if (!max77686) - return; + struct max77686_clk *max77686 = to_max77686_clk(hw); regmap_update_bits(max77686->iodev->regmap, MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask); @@ -78,14 +68,10 @@ static void max77686_clk_unprepare(struct clk_hw *hw) static int max77686_clk_is_enabled(struct clk_hw *hw) { - struct max77686_clk *max77686; + struct max77686_clk *max77686 = to_max77686_clk(hw); int ret; u32 val; - max77686 = get_max77686_clk(hw); - if (!max77686) - return -ENOMEM; - ret = regmap_read(max77686->iodev->regmap, MAX77686_REG_32KHZ, &val); @@ -130,9 +116,8 @@ static int max77686_clk_register(struct device *dev, if (IS_ERR(clk)) return -ENOMEM; - max77686->lookup = devm_kzalloc(dev, sizeof(struct clk_lookup), - GFP_KERNEL); - if (IS_ERR(max77686->lookup)) + max77686->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL); + if (!max77686->lookup) return -ENOMEM; max77686->lookup->con_id = hw->init->name; @@ -151,13 +136,13 @@ static int max77686_clk_probe(struct platform_device *pdev) max77686_clks = devm_kzalloc(&pdev->dev, sizeof(struct max77686_clk *) * MAX77686_CLKS_NUM, GFP_KERNEL); - if (IS_ERR(max77686_clks)) + if (!max77686_clks) return -ENOMEM; for (i = 0; i < MAX77686_CLKS_NUM; i++) { max77686_clks[i] = devm_kzalloc(&pdev->dev, sizeof(struct max77686_clk), GFP_KERNEL); - if (IS_ERR(max77686_clks[i])) + if (!max77686_clks[i]) return -ENOMEM; } diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c index a203ecccdc4..f8e9d0c27be 100644 --- a/drivers/clk/clk-prima2.c +++ b/drivers/clk/clk-prima2.c @@ -1025,20 +1025,67 @@ static struct of_device_id rsc_ids[] = { {}, }; +enum prima2_clk_index { + /* 0 1 2 3 4 5 6 7 8 9 */ + rtc, osc, pll1, pll2, pll3, mem, sys, security, dsp, gps, + mf, io, cpu, uart0, uart1, uart2, tsc, i2c0, i2c1, spi0, + spi1, pwmc, efuse, pulse, dmac0, dmac1, nand, audio, usp0, usp1, + usp2, vip, gfx, mm, lcd, vpp, mmc01, mmc23, mmc45, usbpll, + usb0, usb1, maxclk, +}; + +static __initdata struct clk_hw* prima2_clk_hw_array[maxclk] = { + NULL, /* dummy */ + NULL, + &clk_pll1.hw, + &clk_pll2.hw, + &clk_pll3.hw, + &clk_mem.hw, + &clk_sys.hw, + &clk_security.hw, + &clk_dsp.hw, + &clk_gps.hw, + &clk_mf.hw, + &clk_io.hw, + &clk_cpu.hw, + &clk_uart0.hw, + &clk_uart1.hw, + &clk_uart2.hw, + &clk_tsc.hw, + &clk_i2c0.hw, + &clk_i2c1.hw, + &clk_spi0.hw, + &clk_spi1.hw, + &clk_pwmc.hw, + &clk_efuse.hw, + &clk_pulse.hw, + &clk_dmac0.hw, + &clk_dmac1.hw, + &clk_nand.hw, + &clk_audio.hw, + &clk_usp0.hw, + &clk_usp1.hw, + &clk_usp2.hw, + &clk_vip.hw, + &clk_gfx.hw, + &clk_mm.hw, + &clk_lcd.hw, + &clk_vpp.hw, + &clk_mmc01.hw, + &clk_mmc23.hw, + &clk_mmc45.hw, + &usb_pll_clk_hw, + &clk_usb0.hw, + &clk_usb1.hw, +}; + +static struct clk *prima2_clks[maxclk]; +static struct clk_onecell_data clk_data; + void __init sirfsoc_of_clk_init(void) { - struct clk *clk; struct device_node *np; - - np = of_find_matching_node(NULL, clkc_ids); - if (!np) - panic("unable to find compatible clkc node in dtb\n"); - - sirfsoc_clk_vbase = of_iomap(np, 0); - if (!sirfsoc_clk_vbase) - panic("unable to map clkc registers\n"); - - of_node_put(np); + int i; np = of_find_matching_node(NULL, rsc_ids); if (!np) @@ -1050,122 +1097,30 @@ void __init sirfsoc_of_clk_init(void) of_node_put(np); + np = of_find_matching_node(NULL, clkc_ids); + if (!np) + return; + + sirfsoc_clk_vbase = of_iomap(np, 0); + if (!sirfsoc_clk_vbase) + panic("unable to map clkc registers\n"); /* These are always available (RTC and 26MHz OSC)*/ - clk = clk_register_fixed_rate(NULL, "rtc", NULL, + prima2_clks[rtc] = clk_register_fixed_rate(NULL, "rtc", NULL, CLK_IS_ROOT, 32768); - BUG_ON(IS_ERR(clk)); - clk = clk_register_fixed_rate(NULL, "osc", NULL, + prima2_clks[osc]= clk_register_fixed_rate(NULL, "osc", NULL, CLK_IS_ROOT, 26000000); - BUG_ON(IS_ERR(clk)); - - clk = clk_register(NULL, &clk_pll1.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_pll2.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_pll3.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_mem.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_sys.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_security.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b8030000.security"); - clk = clk_register(NULL, &clk_dsp.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_gps.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "a8010000.gps"); - clk = clk_register(NULL, &clk_mf.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_io.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "io"); - clk = clk_register(NULL, &clk_cpu.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "cpu"); - clk = clk_register(NULL, &clk_uart0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0050000.uart"); - clk = clk_register(NULL, &clk_uart1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0060000.uart"); - clk = clk_register(NULL, &clk_uart2.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0070000.uart"); - clk = clk_register(NULL, &clk_tsc.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0110000.tsc"); - clk = clk_register(NULL, &clk_i2c0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00e0000.i2c"); - clk = clk_register(NULL, &clk_i2c1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00f0000.i2c"); - clk = clk_register(NULL, &clk_spi0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00d0000.spi"); - clk = clk_register(NULL, &clk_spi1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0170000.spi"); - clk = clk_register(NULL, &clk_pwmc.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0130000.pwm"); - clk = clk_register(NULL, &clk_efuse.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0140000.efusesys"); - clk = clk_register(NULL, &clk_pulse.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0150000.pulsec"); - clk = clk_register(NULL, &clk_dmac0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00b0000.dma-controller"); - clk = clk_register(NULL, &clk_dmac1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0160000.dma-controller"); - clk = clk_register(NULL, &clk_nand.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0030000.nand"); - clk = clk_register(NULL, &clk_audio.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0040000.audio"); - clk = clk_register(NULL, &clk_usp0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0080000.usp"); - clk = clk_register(NULL, &clk_usp1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0090000.usp"); - clk = clk_register(NULL, &clk_usp2.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00a0000.usp"); - clk = clk_register(NULL, &clk_vip.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00c0000.vip"); - clk = clk_register(NULL, &clk_gfx.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "98000000.graphics"); - clk = clk_register(NULL, &clk_mm.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "a0000000.multimedia"); - clk = clk_register(NULL, &clk_lcd.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "90010000.display"); - clk = clk_register(NULL, &clk_vpp.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "90020000.vpp"); - clk = clk_register(NULL, &clk_mmc01.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_mmc23.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_mmc45.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &usb_pll_clk_hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_usb0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00e0000.usb"); - clk = clk_register(NULL, &clk_usb1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00f0000.usb"); + + for (i = pll1; i < maxclk; i++) { + prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]); + BUG_ON(!prima2_clks[i]); + } + clk_register_clkdev(prima2_clks[cpu], NULL, "cpu"); + clk_register_clkdev(prima2_clks[io], NULL, "io"); + clk_register_clkdev(prima2_clks[mem], NULL, "mem"); + + clk_data.clks = prima2_clks; + clk_data.clk_num = maxclk; + + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); } diff --git a/drivers/clk/clk-sunxi.c b/drivers/clk/clk-sunxi.c deleted file mode 100644 index 0e831b584ba..00000000000 --- a/drivers/clk/clk-sunxi.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2012 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. - */ - -#include <linux/clk-provider.h> -#include <linux/clkdev.h> -#include <linux/clk/sunxi.h> -#include <linux/of.h> - -static const __initconst struct of_device_id clk_match[] = { - { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, - {} -}; - -void __init sunxi_init_clocks(void) -{ - of_clk_init(clk_match); -} diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c index fe25570874d..b5538bba7a1 100644 --- a/drivers/clk/clk-vt8500.c +++ b/drivers/clk/clk-vt8500.c @@ -41,6 +41,7 @@ struct clk_device { #define PLL_TYPE_VT8500 0 #define PLL_TYPE_WM8650 1 +#define PLL_TYPE_WM8750 2 struct clk_pll { struct clk_hw hw; @@ -121,7 +122,16 @@ static long vt8500_dclk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { struct clk_device *cdev = to_clk_device(hw); - u32 divisor = *prate / rate; + u32 divisor; + + if (rate == 0) + return 0; + + divisor = *prate / rate; + + /* If prate / rate would be decimal, incr the divisor */ + if (rate * divisor < *prate) + divisor++; /* * If this is a request for SDMMC we have to adjust the divisor @@ -138,9 +148,18 @@ static int vt8500_dclk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_device *cdev = to_clk_device(hw); - u32 divisor = parent_rate / rate; + u32 divisor; unsigned long flags = 0; + if (rate == 0) + return 0; + + divisor = parent_rate / rate; + + /* If prate / rate would be decimal, incr the divisor */ + if (rate * divisor < *prate) + divisor++; + if (divisor == cdev->div_mask + 1) divisor = 0; @@ -272,7 +291,7 @@ static __init void vtwm_device_clk_init(struct device_node *node) rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); clk_register_clkdev(clk, clk_name, NULL); } - +CLK_OF_DECLARE(vt8500_device, "via,vt8500-device-clock", vtwm_device_clk_init); /* PLL clock related functions */ @@ -298,6 +317,16 @@ static __init void vtwm_device_clk_init(struct device_node *node) #define WM8650_BITS_TO_VAL(m, d1, d2) \ ((d2 << 13) | (d1 << 10) | (m & 0x3FF)) +/* Helper macros for PLL_WM8750 */ +#define WM8750_PLL_MUL(x) (((x >> 16) & 0xFF) + 1) +#define WM8750_PLL_DIV(x) ((((x >> 8) & 1) + 1) * (1 << (x & 7))) + +#define WM8750_BITS_TO_FREQ(r, m, d1, d2) \ + (r * (m+1) / ((d1+1) * (1 << d2))) + +#define WM8750_BITS_TO_VAL(f, m, d1, d2) \ + ((f << 24) | ((m - 1) << 16) | ((d1 - 1) << 8) | d2) + static void vt8500_find_pll_bits(unsigned long rate, unsigned long parent_rate, u32 *multiplier, u32 *prediv) @@ -361,16 +390,87 @@ static void wm8650_find_pll_bits(unsigned long rate, unsigned long parent_rate, /* if we got here, it wasn't an exact match */ pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate, rate - best_err); - *multiplier = mul; - *divisor1 = div1; - *divisor2 = div2; + *multiplier = best_mul; + *divisor1 = best_div1; + *divisor2 = best_div2; +} + +static u32 wm8750_get_filter(u32 parent_rate, u32 divisor1) +{ + /* calculate frequency (MHz) after pre-divisor */ + u32 freq = (parent_rate / 1000000) / (divisor1 + 1); + + if ((freq < 10) || (freq > 200)) + pr_warn("%s: PLL recommended input frequency 10..200Mhz (requested %d Mhz)\n", + __func__, freq); + + if (freq >= 166) + return 7; + else if (freq >= 104) + return 6; + else if (freq >= 65) + return 5; + else if (freq >= 42) + return 4; + else if (freq >= 26) + return 3; + else if (freq >= 16) + return 2; + else if (freq >= 10) + return 1; + + return 0; +} + +static void wm8750_find_pll_bits(unsigned long rate, unsigned long parent_rate, + u32 *filter, u32 *multiplier, u32 *divisor1, u32 *divisor2) +{ + u32 mul, div1, div2; + u32 best_mul, best_div1, best_div2; + unsigned long tclk, rate_err, best_err; + + best_err = (unsigned long)-1; + + /* Find the closest match (lower or equal to requested) */ + for (div1 = 1; div1 >= 0; div1--) + for (div2 = 7; div2 >= 0; div2--) + for (mul = 0; mul <= 255; mul++) { + tclk = parent_rate * (mul + 1) / ((div1 + 1) * (1 << div2)); + if (tclk > rate) + continue; + /* error will always be +ve */ + rate_err = rate - tclk; + if (rate_err == 0) { + *filter = wm8750_get_filter(parent_rate, div1); + *multiplier = mul; + *divisor1 = div1; + *divisor2 = div2; + return; + } + + if (rate_err < best_err) { + best_err = rate_err; + best_mul = mul; + best_div1 = div1; + best_div2 = div2; + } + } + + /* if we got here, it wasn't an exact match */ + pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate, + rate - best_err); + + *filter = wm8750_get_filter(parent_rate, best_div1); + *multiplier = best_mul; + *divisor1 = best_div1; + *divisor2 = best_div2; } static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_pll *pll = to_clk_pll(hw); - u32 mul, div1, div2; + u32 filter, mul, div1, div2; u32 pll_val; unsigned long flags = 0; @@ -385,6 +485,9 @@ static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate, wm8650_find_pll_bits(rate, parent_rate, &mul, &div1, &div2); pll_val = WM8650_BITS_TO_VAL(mul, div1, div2); break; + case PLL_TYPE_WM8750: + wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2); + pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2); default: pr_err("%s: invalid pll type\n", __func__); return 0; @@ -405,7 +508,7 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { struct clk_pll *pll = to_clk_pll(hw); - u32 mul, div1, div2; + u32 filter, mul, div1, div2; long round_rate; switch (pll->type) { @@ -417,6 +520,9 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate, wm8650_find_pll_bits(rate, *prate, &mul, &div1, &div2); round_rate = WM8650_BITS_TO_FREQ(*prate, mul, div1, div2); break; + case PLL_TYPE_WM8750: + wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2); + round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2); default: round_rate = 0; } @@ -440,6 +546,10 @@ static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw, pll_freq = parent_rate * WM8650_PLL_MUL(pll_val); pll_freq /= WM8650_PLL_DIV(pll_val); break; + case PLL_TYPE_WM8750: + pll_freq = parent_rate * WM8750_PLL_MUL(pll_val); + pll_freq /= WM8750_PLL_DIV(pll_val); + break; default: pll_freq = 0; } @@ -502,20 +612,19 @@ static void __init vt8500_pll_init(struct device_node *node) { vtwm_pll_clk_init(node, PLL_TYPE_VT8500); } +CLK_OF_DECLARE(vt8500_pll, "via,vt8500-pll-clock", vt8500_pll_init); static void __init wm8650_pll_init(struct device_node *node) { vtwm_pll_clk_init(node, PLL_TYPE_WM8650); } +CLK_OF_DECLARE(wm8650_pll, "wm,wm8650-pll-clock", wm8650_pll_init); -static const __initconst struct of_device_id clk_match[] = { - { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, - { .compatible = "via,vt8500-pll-clock", .data = vt8500_pll_init, }, - { .compatible = "wm,wm8650-pll-clock", .data = wm8650_pll_init, }, - { .compatible = "via,vt8500-device-clock", - .data = vtwm_device_clk_init, }, - { /* sentinel */ } -}; +static void __init wm8750_pll_init(struct device_node *node) +{ + vtwm_pll_clk_init(node, PLL_TYPE_WM8750); +} +CLK_OF_DECLARE(wm8750_pll, "wm,wm8750-pll-clock", wm8750_pll_init); void __init vtwm_clk_init(void __iomem *base) { @@ -524,5 +633,5 @@ void __init vtwm_clk_init(void __iomem *base) pmc_base = base; - of_clk_init(clk_match); + of_clk_init(NULL); } diff --git a/drivers/clk/clk-zynq.c b/drivers/clk/clk-zynq.c index 37a30514fd6..b14a25f3925 100644 --- a/drivers/clk/clk-zynq.c +++ b/drivers/clk/clk-zynq.c @@ -81,6 +81,7 @@ static void __init zynq_pll_clk_setup(struct device_node *np) if (WARN_ON(ret)) return; } +CLK_OF_DECLARE(zynq_pll, "xlnx,zynq-pll", zynq_pll_clk_setup); struct zynq_periph_clk { struct clk_hw hw; @@ -187,6 +188,7 @@ static void __init zynq_periph_clk_setup(struct device_node *np) if (WARN_ON(err)) return; } +CLK_OF_DECLARE(zynq_periph, "xlnx,zynq-periph-clock", zynq_periph_clk_setup); /* CPU Clock domain is modelled as a mux with 4 children subclks, whose * derivative rates depend on CLK_621_TRUE @@ -366,18 +368,10 @@ static void __init zynq_cpu_clk_setup(struct device_node *np) if (WARN_ON(err)) return; } - -static const __initconst struct of_device_id zynq_clk_match[] = { - { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, - { .compatible = "xlnx,zynq-pll", .data = zynq_pll_clk_setup, }, - { .compatible = "xlnx,zynq-periph-clock", - .data = zynq_periph_clk_setup, }, - { .compatible = "xlnx,zynq-cpu-clock", .data = zynq_cpu_clk_setup, }, - {} -}; +CLK_OF_DECLARE(zynq_cpu, "xlnx,zynq-cpu-clock", zynq_cpu_clk_setup); void __init xilinx_zynq_clocks_init(void __iomem *slcr) { slcr_base = slcr; - of_clk_init(zynq_clk_match); + of_clk_init(NULL); } diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 251e45d6024..fabbfe1a925 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/of.h> #include <linux/device.h> +#include <linux/init.h> static DEFINE_SPINLOCK(enable_lock); static DEFINE_MUTEX(prepare_lock); @@ -35,6 +36,137 @@ static struct dentry *rootdir; static struct dentry *orphandir; static int inited = 0; +static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) +{ + if (!c) + return; + + seq_printf(s, "%*s%-*s %-11d %-12d %-10lu", + level * 3 + 1, "", + 30 - level * 3, c->name, + c->enable_count, c->prepare_count, c->rate); + seq_printf(s, "\n"); +} + +static void clk_summary_show_subtree(struct seq_file *s, struct clk *c, + int level) +{ + struct clk *child; + struct hlist_node *tmp; + + if (!c) + return; + + clk_summary_show_one(s, c, level); + + hlist_for_each_entry(child, tmp, &c->children, child_node) + clk_summary_show_subtree(s, child, level + 1); +} + +static int clk_summary_show(struct seq_file *s, void *data) +{ + struct clk *c; + struct hlist_node *tmp; + + seq_printf(s, " clock enable_cnt prepare_cnt rate\n"); + seq_printf(s, "---------------------------------------------------------------------\n"); + + mutex_lock(&prepare_lock); + + hlist_for_each_entry(c, tmp, &clk_root_list, child_node) + clk_summary_show_subtree(s, c, 0); + + hlist_for_each_entry(c, tmp, &clk_orphan_list, child_node) + clk_summary_show_subtree(s, c, 0); + + mutex_unlock(&prepare_lock); + + return 0; +} + + +static int clk_summary_open(struct inode *inode, struct file *file) +{ + return single_open(file, clk_summary_show, inode->i_private); +} + +static const struct file_operations clk_summary_fops = { + .open = clk_summary_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void clk_dump_one(struct seq_file *s, struct clk *c, int level) +{ + if (!c) + return; + + seq_printf(s, "\"%s\": { ", c->name); + seq_printf(s, "\"enable_count\": %d,", c->enable_count); + seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); + seq_printf(s, "\"rate\": %lu", c->rate); +} + +static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level) +{ + struct clk *child; + struct hlist_node *tmp; + + if (!c) + return; + + clk_dump_one(s, c, level); + + hlist_for_each_entry(child, tmp, &c->children, child_node) { + seq_printf(s, ","); + clk_dump_subtree(s, child, level + 1); + } + + seq_printf(s, "}"); +} + +static int clk_dump(struct seq_file *s, void *data) +{ + struct clk *c; + struct hlist_node *tmp; + bool first_node = true; + + seq_printf(s, "{"); + + mutex_lock(&prepare_lock); + + hlist_for_each_entry(c, tmp, &clk_root_list, child_node) { + if (!first_node) + seq_printf(s, ","); + first_node = false; + clk_dump_subtree(s, c, 0); + } + + hlist_for_each_entry(c, tmp, &clk_orphan_list, child_node) { + seq_printf(s, ","); + clk_dump_subtree(s, c, 0); + } + + mutex_unlock(&prepare_lock); + + seq_printf(s, "}"); + return 0; +} + + +static int clk_dump_open(struct inode *inode, struct file *file) +{ + return single_open(file, clk_dump, inode->i_private); +} + +static const struct file_operations clk_dump_fops = { + .open = clk_dump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /* caller must hold prepare_lock */ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry) { @@ -168,12 +300,23 @@ static int __init clk_debug_init(void) { struct clk *clk; struct hlist_node *tmp; + struct dentry *d; rootdir = debugfs_create_dir("clk", NULL); if (!rootdir) return -ENOMEM; + d = debugfs_create_file("clk_summary", S_IRUGO, rootdir, NULL, + &clk_summary_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file("clk_dump", S_IRUGO, rootdir, NULL, + &clk_dump_fops); + if (!d) + return -ENOMEM; + orphandir = debugfs_create_dir("orphans", rootdir); if (!orphandir) @@ -259,32 +402,33 @@ late_initcall(clk_disable_unused); /*** helper functions ***/ -inline const char *__clk_get_name(struct clk *clk) +const char *__clk_get_name(struct clk *clk) { return !clk ? NULL : clk->name; } +EXPORT_SYMBOL_GPL(__clk_get_name); -inline struct clk_hw *__clk_get_hw(struct clk *clk) +struct clk_hw *__clk_get_hw(struct clk *clk) { return !clk ? NULL : clk->hw; } -inline u8 __clk_get_num_parents(struct clk *clk) +u8 __clk_get_num_parents(struct clk *clk) { return !clk ? 0 : clk->num_parents; } -inline struct clk *__clk_get_parent(struct clk *clk) +struct clk *__clk_get_parent(struct clk *clk) { return !clk ? NULL : clk->parent; } -inline unsigned int __clk_get_enable_count(struct clk *clk) +unsigned int __clk_get_enable_count(struct clk *clk) { return !clk ? 0 : clk->enable_count; } -inline unsigned int __clk_get_prepare_count(struct clk *clk) +unsigned int __clk_get_prepare_count(struct clk *clk) { return !clk ? 0 : clk->prepare_count; } @@ -310,7 +454,7 @@ out: return ret; } -inline unsigned long __clk_get_flags(struct clk *clk) +unsigned long __clk_get_flags(struct clk *clk) { return !clk ? 0 : clk->flags; } @@ -950,9 +1094,6 @@ int clk_set_rate(struct clk *clk, unsigned long rate) /* change the rates */ clk_change_rate(top); - mutex_unlock(&prepare_lock); - - return 0; out: mutex_unlock(&prepare_lock); @@ -1663,6 +1804,11 @@ struct of_clk_provider { void *data; }; +extern struct of_device_id __clk_of_table[]; + +static const struct of_device_id __clk_of_table_sentinel + __used __section(__clk_of_table_end); + static LIST_HEAD(of_clk_providers); static DEFINE_MUTEX(of_clk_lock); @@ -1791,6 +1937,9 @@ void __init of_clk_init(const struct of_device_id *matches) { struct device_node *np; + if (!matches) + matches = __clk_of_table; + for_each_matching_node(np, matches) { const struct of_device_id *match = of_match_node(matches, np); of_clk_init_cb_t clk_init_cb = match->data; diff --git a/drivers/clk/mvebu/clk-gating-ctrl.c b/drivers/clk/mvebu/clk-gating-ctrl.c index 8fa5408b6c7..ebf141d4374 100644 --- a/drivers/clk/mvebu/clk-gating-ctrl.c +++ b/drivers/clk/mvebu/clk-gating-ctrl.c @@ -193,6 +193,7 @@ static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = { { "runit", NULL, 7 }, { "xor0", NULL, 8 }, { "audio", NULL, 9 }, + { "powersave", "cpuclk", 11 }, { "sata0", NULL, 14 }, { "sata1", NULL, 15 }, { "xor1", NULL, 16 }, diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index 8dd476e2a9c..b5c06f9766f 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -99,7 +99,7 @@ static enum imx23_clk clks_init_on[] __initdata = { int __init mx23_clocks_init(void) { struct device_node *np; - int i; + u32 i; clk_misc_init(); diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index db3af087412..126370a62ce 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -154,7 +154,7 @@ static enum imx28_clk clks_init_on[] __initdata = { int __init mx28_clocks_init(void) { struct device_node *np; - int i; + u32 i; clk_misc_init(); diff --git a/drivers/clk/versatile/clk-vexpress-osc.c b/drivers/clk/versatile/clk-vexpress-osc.c index dcb6ae0a042..256c8be74df 100644 --- a/drivers/clk/versatile/clk-vexpress-osc.c +++ b/drivers/clk/versatile/clk-vexpress-osc.c @@ -144,3 +144,4 @@ error: vexpress_config_func_put(osc->func); kfree(osc); } +CLK_OF_DECLARE(vexpress_soc, "arm,vexpress-osc", vexpress_osc_of_setup); diff --git a/drivers/clk/versatile/clk-vexpress.c b/drivers/clk/versatile/clk-vexpress.c index c742ac7c60b..f889f2f07b3 100644 --- a/drivers/clk/versatile/clk-vexpress.c +++ b/drivers/clk/versatile/clk-vexpress.c @@ -99,19 +99,13 @@ struct clk *vexpress_sp810_of_get(struct of_phandle_args *clkspec, void *data) return vexpress_sp810_timerclken[clkspec->args[0]]; } -static const __initconst struct of_device_id vexpress_fixed_clk_match[] = { - { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, - { .compatible = "arm,vexpress-osc", .data = vexpress_osc_of_setup, }, - {} -}; - void __init vexpress_clk_of_init(void) { struct device_node *node; struct clk *clk; struct clk *refclk, *timclk; - of_clk_init(vexpress_fixed_clk_match); + of_clk_init(NULL); node = of_find_compatible_node(NULL, NULL, "arm,sp810"); vexpress_sp810_init(of_iomap(node, 0)); diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile new file mode 100644 index 00000000000..f9ba4fab0dd --- /dev/null +++ b/drivers/clk/x86/Makefile @@ -0,0 +1,2 @@ +clk-x86-lpss-objs := clk-lpss.o clk-lpt.o +obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o diff --git a/drivers/clk/x86/clk-lpss.c b/drivers/clk/x86/clk-lpss.c new file mode 100644 index 00000000000..b5e229f3c3d --- /dev/null +++ b/drivers/clk/x86/clk-lpss.c @@ -0,0 +1,99 @@ +/* + * Intel Low Power Subsystem clocks. + * + * Copyright (C) 2013, Intel Corporation + * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> + * Heikki Krogerus <heikki.krogerus@linux.intel.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. + */ + +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> + +static int clk_lpss_is_mmio_resource(struct acpi_resource *res, void *data) +{ + struct resource r; + return !acpi_dev_resource_memory(res, &r); +} + +static acpi_status clk_lpss_find_mmio(acpi_handle handle, u32 level, + void *data, void **retval) +{ + struct resource_list_entry *rentry; + struct list_head resource_list; + struct acpi_device *adev; + const char *uid = data; + int ret; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + if (uid) { + if (!adev->pnp.unique_id) + return AE_OK; + if (strcmp(uid, adev->pnp.unique_id)) + return AE_OK; + } + + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, + clk_lpss_is_mmio_resource, NULL); + if (ret < 0) + return AE_NO_MEMORY; + + list_for_each_entry(rentry, &resource_list, node) + if (resource_type(&rentry->res) == IORESOURCE_MEM) { + *(struct resource *)retval = rentry->res; + break; + } + + acpi_dev_free_resource_list(&resource_list); + return AE_OK; +} + +/** + * clk_register_lpss_gate - register LPSS clock gate + * @name: name of this clock gate + * @parent_name: parent clock name + * @hid: ACPI _HID of the device + * @uid: ACPI _UID of the device (optional) + * @offset: LPSS PRV_CLOCK_PARAMS offset + * + * Creates and registers LPSS clock gate. + */ +struct clk *clk_register_lpss_gate(const char *name, const char *parent_name, + const char *hid, const char *uid, + unsigned offset) +{ + struct resource res = { }; + void __iomem *mmio_base; + acpi_status status; + struct clk *clk; + + /* + * First try to look the device and its mmio resource from the + * ACPI namespace. + */ + status = acpi_get_devices(hid, clk_lpss_find_mmio, (void *)uid, + (void **)&res); + if (ACPI_FAILURE(status) || !res.start) + return ERR_PTR(-ENODEV); + + mmio_base = ioremap(res.start, resource_size(&res)); + if (!mmio_base) + return ERR_PTR(-ENOMEM); + + clk = clk_register_gate(NULL, name, parent_name, 0, mmio_base + offset, + 0, 0, NULL); + if (IS_ERR(clk)) + iounmap(mmio_base); + + return clk; +} diff --git a/drivers/clk/x86/clk-lpss.h b/drivers/clk/x86/clk-lpss.h new file mode 100644 index 00000000000..e9460f44229 --- /dev/null +++ b/drivers/clk/x86/clk-lpss.h @@ -0,0 +1,36 @@ +/* + * Intel Low Power Subsystem clock. + * + * Copyright (C) 2013, Intel Corporation + * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> + * Heikki Krogerus <heikki.krogerus@linux.intel.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. + */ + +#ifndef __CLK_LPSS_H +#define __CLK_LPSS_H + +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/clk.h> + +#ifdef CONFIG_ACPI +extern struct clk *clk_register_lpss_gate(const char *name, + const char *parent_name, + const char *hid, const char *uid, + unsigned offset); +#else +static inline struct clk *clk_register_lpss_gate(const char *name, + const char *parent_name, + const char *hid, + const char *uid, + unsigned offset) +{ + return ERR_PTR(-ENODEV); +} +#endif + +#endif /* __CLK_LPSS_H */ diff --git a/drivers/clk/x86/clk-lpt.c b/drivers/clk/x86/clk-lpt.c new file mode 100644 index 00000000000..81298aeef7e --- /dev/null +++ b/drivers/clk/x86/clk-lpt.c @@ -0,0 +1,86 @@ +/* + * Intel Lynxpoint LPSS clocks. + * + * Copyright (C) 2013, Intel Corporation + * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> + * Heikki Krogerus <heikki.krogerus@linux.intel.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. + */ + +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "clk-lpss.h" + +#define PRV_CLOCK_PARAMS 0x800 + +static int lpt_clk_probe(struct platform_device *pdev) +{ + struct clk *clk; + + /* LPSS free running clock */ + clk = clk_register_fixed_rate(&pdev->dev, "lpss_clk", NULL, CLK_IS_ROOT, + 100000000); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + /* Shared DMA clock */ + clk_register_clkdev(clk, "hclk", "INTL9C60.0.auto"); + + /* SPI clocks */ + clk = clk_register_lpss_gate("spi0_clk", "lpss_clk", "INT33C0", NULL, + PRV_CLOCK_PARAMS); + if (!IS_ERR(clk)) + clk_register_clkdev(clk, NULL, "INT33C0:00"); + + clk = clk_register_lpss_gate("spi1_clk", "lpss_clk", "INT33C1", NULL, + PRV_CLOCK_PARAMS); + if (!IS_ERR(clk)) + clk_register_clkdev(clk, NULL, "INT33C1:00"); + + /* I2C clocks */ + clk = clk_register_lpss_gate("i2c0_clk", "lpss_clk", "INT33C2", NULL, + PRV_CLOCK_PARAMS); + if (!IS_ERR(clk)) + clk_register_clkdev(clk, NULL, "INT33C2:00"); + + clk = clk_register_lpss_gate("i2c1_clk", "lpss_clk", "INT33C3", NULL, + PRV_CLOCK_PARAMS); + if (!IS_ERR(clk)) + clk_register_clkdev(clk, NULL, "INT33C3:00"); + + /* UART clocks */ + clk = clk_register_lpss_gate("uart0_clk", "lpss_clk", "INT33C4", NULL, + PRV_CLOCK_PARAMS); + if (!IS_ERR(clk)) + clk_register_clkdev(clk, NULL, "INT33C4:00"); + + clk = clk_register_lpss_gate("uart1_clk", "lpss_clk", "INT33C5", NULL, + PRV_CLOCK_PARAMS); + if (!IS_ERR(clk)) + clk_register_clkdev(clk, NULL, "INT33C5:00"); + + return 0; +} + +static struct platform_driver lpt_clk_driver = { + .driver = { + .name = "clk-lpt", + .owner = THIS_MODULE, + }, + .probe = lpt_clk_probe, +}; + +static int __init lpt_clk_init(void) +{ + return platform_driver_register(&lpt_clk_driver); +} +arch_initcall(lpt_clk_init); |