diff options
Diffstat (limited to 'drivers/clk')
29 files changed, 4394 insertions, 45 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index ede9cb0b79d6..7abe4885a47a 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -93,6 +93,15 @@ config COMMON_CLK_SI514 This driver supports the Silicon Labs 514 programmable clock generator. +config COMMON_CLK_SI544 + tristate "Clock driver for SiLabs 544 devices" + depends on I2C + select REGMAP_I2C + help + ---help--- + This driver supports the Silicon Labs 544 programmable clock + generator. + config COMMON_CLK_SI570 tristate "Clock driver for SiLabs 570 and compatible devices" depends on I2C diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index e0c106ed9407..fdbffc5249f3 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_COMMON_CLK_SCPI) += clk-scpi.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_COMMON_CLK_SI514) += clk-si514.o +obj-$(CONFIG_COMMON_CLK_SI544) += clk-si544.o obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o obj-$(CONFIG_COMMON_CLK_STM32F) += clk-stm32f4.o obj-$(CONFIG_COMMON_CLK_STM32H7) += clk-stm32h7.o @@ -62,6 +63,7 @@ obj-$(CONFIG_ARCH_ARTPEC) += axis/ obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/ obj-y += bcm/ obj-$(CONFIG_ARCH_BERLIN) += berlin/ +obj-$(CONFIG_ARCH_DAVINCI) += davinci/ obj-$(CONFIG_H8300) += h8300/ obj-$(CONFIG_ARCH_HISI) += hisilicon/ obj-y += imgtec/ diff --git a/drivers/clk/clk-si544.c b/drivers/clk/clk-si544.c new file mode 100644 index 000000000000..1c96a9f6c022 --- /dev/null +++ b/drivers/clk/clk-si544.c @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Silicon Labs Si544 Programmable Oscillator + * Copyright (C) 2018 Topic Embedded Products + * Author: Mike Looijmans <mike.looijmans@topic.nl> + */ + +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +/* I2C registers (decimal as in datasheet) */ +#define SI544_REG_CONTROL 7 +#define SI544_REG_OE_STATE 17 +#define SI544_REG_HS_DIV 23 +#define SI544_REG_LS_HS_DIV 24 +#define SI544_REG_FBDIV0 26 +#define SI544_REG_FBDIV8 27 +#define SI544_REG_FBDIV16 28 +#define SI544_REG_FBDIV24 29 +#define SI544_REG_FBDIV32 30 +#define SI544_REG_FBDIV40 31 +#define SI544_REG_FCAL_OVR 69 +#define SI544_REG_ADPLL_DELTA_M0 231 +#define SI544_REG_ADPLL_DELTA_M8 232 +#define SI544_REG_ADPLL_DELTA_M16 233 +#define SI544_REG_PAGE_SELECT 255 + +/* Register values */ +#define SI544_CONTROL_RESET BIT(7) +#define SI544_CONTROL_MS_ICAL2 BIT(3) + +#define SI544_OE_STATE_ODC_OE BIT(0) + +/* Max freq depends on speed grade */ +#define SI544_MIN_FREQ 200000U + +/* Si544 Internal oscilator runs at 55.05 MHz */ +#define FXO 55050000U + +/* VCO range is 10.8 .. 12.1 GHz, max depends on speed grade */ +#define FVCO_MIN 10800000000ULL + +#define HS_DIV_MAX 2046 +#define HS_DIV_MAX_ODD 33 + +/* Lowest frequency synthesizeable using only the HS divider */ +#define MIN_HSDIV_FREQ (FVCO_MIN / HS_DIV_MAX) + +enum si544_speed_grade { + si544a, + si544b, + si544c, +}; + +struct clk_si544 { + struct clk_hw hw; + struct regmap *regmap; + struct i2c_client *i2c_client; + enum si544_speed_grade speed_grade; +}; +#define to_clk_si544(_hw) container_of(_hw, struct clk_si544, hw) + +/** + * struct clk_si544_muldiv - Multiplier/divider settings + * @fb_div_frac: integer part of feedback divider (32 bits) + * @fb_div_int: fractional part of feedback divider (11 bits) + * @hs_div: 1st divider, 5..2046, must be even when >33 + * @ls_div_bits: 2nd divider, as 2^x, range 0..5 + * If ls_div_bits is non-zero, hs_div must be even + */ +struct clk_si544_muldiv { + u32 fb_div_frac; + u16 fb_div_int; + u16 hs_div; + u8 ls_div_bits; +}; + +/* Enables or disables the output driver */ +static int si544_enable_output(struct clk_si544 *data, bool enable) +{ + return regmap_update_bits(data->regmap, SI544_REG_OE_STATE, + SI544_OE_STATE_ODC_OE, enable ? SI544_OE_STATE_ODC_OE : 0); +} + +/* Retrieve clock multiplier and dividers from hardware */ +static int si544_get_muldiv(struct clk_si544 *data, + struct clk_si544_muldiv *settings) +{ + int err; + u8 reg[6]; + + err = regmap_bulk_read(data->regmap, SI544_REG_HS_DIV, reg, 2); + if (err) + return err; + + settings->ls_div_bits = (reg[1] >> 4) & 0x07; + settings->hs_div = (reg[1] & 0x07) << 8 | reg[0]; + + err = regmap_bulk_read(data->regmap, SI544_REG_FBDIV0, reg, 6); + if (err) + return err; + + settings->fb_div_int = reg[4] | (reg[5] & 0x07) << 8; + settings->fb_div_frac = reg[0] | reg[1] << 8 | reg[2] << 16 | + reg[3] << 24; + return 0; +} + +static int si544_set_muldiv(struct clk_si544 *data, + struct clk_si544_muldiv *settings) +{ + int err; + u8 reg[6]; + + reg[0] = settings->hs_div; + reg[1] = settings->hs_div >> 8 | settings->ls_div_bits << 4; + + err = regmap_bulk_write(data->regmap, SI544_REG_HS_DIV, reg, 2); + if (err < 0) + return err; + + reg[0] = settings->fb_div_frac; + reg[1] = settings->fb_div_frac >> 8; + reg[2] = settings->fb_div_frac >> 16; + reg[3] = settings->fb_div_frac >> 24; + reg[4] = settings->fb_div_int; + reg[5] = settings->fb_div_int >> 8; + + /* + * Writing to SI544_REG_FBDIV40 triggers the clock change, so that + * must be written last + */ + return regmap_bulk_write(data->regmap, SI544_REG_FBDIV0, reg, 6); +} + +static bool is_valid_frequency(const struct clk_si544 *data, + unsigned long frequency) +{ + unsigned long max_freq = 0; + + if (frequency < SI544_MIN_FREQ) + return false; + + switch (data->speed_grade) { + case si544a: + max_freq = 1500000000; + break; + case si544b: + max_freq = 800000000; + break; + case si544c: + max_freq = 350000000; + break; + } + + return frequency <= max_freq; +} + +/* Calculate divider settings for a given frequency */ +static int si544_calc_muldiv(struct clk_si544_muldiv *settings, + unsigned long frequency) +{ + u64 vco; + u32 ls_freq; + u32 tmp; + u8 res; + + /* Determine the minimum value of LS_DIV and resulting target freq. */ + ls_freq = frequency; + settings->ls_div_bits = 0; + + if (frequency >= MIN_HSDIV_FREQ) { + settings->ls_div_bits = 0; + } else { + res = 1; + tmp = 2 * HS_DIV_MAX; + while (tmp <= (HS_DIV_MAX * 32)) { + if (((u64)frequency * tmp) >= FVCO_MIN) + break; + ++res; + tmp <<= 1; + } + settings->ls_div_bits = res; + ls_freq = frequency << res; + } + + /* Determine minimum HS_DIV by rounding up */ + vco = FVCO_MIN + ls_freq - 1; + do_div(vco, ls_freq); + settings->hs_div = vco; + + /* round up to even number when required */ + if ((settings->hs_div & 1) && + (settings->hs_div > HS_DIV_MAX_ODD || settings->ls_div_bits)) + ++settings->hs_div; + + /* Calculate VCO frequency (in 10..12GHz range) */ + vco = (u64)ls_freq * settings->hs_div; + + /* Calculate the integer part of the feedback divider */ + tmp = do_div(vco, FXO); + settings->fb_div_int = vco; + + /* And the fractional bits using the remainder */ + vco = (u64)tmp << 32; + do_div(vco, FXO); + settings->fb_div_frac = vco; + + return 0; +} + +/* Calculate resulting frequency given the register settings */ +static unsigned long si544_calc_rate(struct clk_si544_muldiv *settings) +{ + u32 d = settings->hs_div * BIT(settings->ls_div_bits); + u64 vco; + + /* Calculate VCO from the fractional part */ + vco = (u64)settings->fb_div_frac * FXO; + vco += (FXO / 2); + vco >>= 32; + + /* Add the integer part of the VCO frequency */ + vco += (u64)settings->fb_div_int * FXO; + + /* Apply divider to obtain the generated frequency */ + do_div(vco, d); + + return vco; +} + +static unsigned long si544_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_si544 *data = to_clk_si544(hw); + struct clk_si544_muldiv settings; + int err; + + err = si544_get_muldiv(data, &settings); + if (err) + return 0; + + return si544_calc_rate(&settings); +} + +static long si544_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_si544 *data = to_clk_si544(hw); + struct clk_si544_muldiv settings; + int err; + + if (!is_valid_frequency(data, rate)) + return -EINVAL; + + err = si544_calc_muldiv(&settings, rate); + if (err) + return err; + + return si544_calc_rate(&settings); +} + +/* + * Update output frequency for "big" frequency changes + */ +static int si544_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_si544 *data = to_clk_si544(hw); + struct clk_si544_muldiv settings; + int err; + + if (!is_valid_frequency(data, rate)) + return -EINVAL; + + err = si544_calc_muldiv(&settings, rate); + if (err) + return err; + + si544_enable_output(data, false); + + /* Allow FCAL for this frequency update */ + err = regmap_write(data->regmap, SI544_REG_FCAL_OVR, 0); + if (err < 0) + return err; + + + err = si544_set_muldiv(data, &settings); + if (err < 0) + return err; /* Undefined state now, best to leave disabled */ + + /* Trigger calibration */ + err = regmap_write(data->regmap, SI544_REG_CONTROL, + SI544_CONTROL_MS_ICAL2); + if (err < 0) + return err; + + /* Applying a new frequency can take up to 10ms */ + usleep_range(10000, 12000); + + si544_enable_output(data, true); + + return err; +} + +static const struct clk_ops si544_clk_ops = { + .recalc_rate = si544_recalc_rate, + .round_rate = si544_round_rate, + .set_rate = si544_set_rate, +}; + +static bool si544_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SI544_REG_CONTROL: + case SI544_REG_FCAL_OVR: + return true; + default: + return false; + } +} + +static const struct regmap_config si544_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = SI544_REG_PAGE_SELECT, + .volatile_reg = si544_regmap_is_volatile, +}; + +static int si544_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct clk_si544 *data; + struct clk_init_data init; + int err; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + init.ops = &si544_clk_ops; + init.flags = 0; + init.num_parents = 0; + data->hw.init = &init; + data->i2c_client = client; + data->speed_grade = id->driver_data; + + if (of_property_read_string(client->dev.of_node, "clock-output-names", + &init.name)) + init.name = client->dev.of_node->name; + + data->regmap = devm_regmap_init_i2c(client, &si544_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + i2c_set_clientdata(client, data); + + /* Select page 0, just to be sure, there appear to be no more */ + err = regmap_write(data->regmap, SI544_REG_PAGE_SELECT, 0); + if (err < 0) + return err; + + err = devm_clk_hw_register(&client->dev, &data->hw); + if (err) { + dev_err(&client->dev, "clock registration failed\n"); + return err; + } + err = devm_of_clk_add_hw_provider(&client->dev, of_clk_hw_simple_get, + &data->hw); + if (err) { + dev_err(&client->dev, "unable to add clk provider\n"); + return err; + } + + return 0; +} + +static const struct i2c_device_id si544_id[] = { + { "si544a", si544a }, + { "si544b", si544b }, + { "si544c", si544c }, + { } +}; +MODULE_DEVICE_TABLE(i2c, si544_id); + +static const struct of_device_id clk_si544_of_match[] = { + { .compatible = "silabs,si544a" }, + { .compatible = "silabs,si544b" }, + { .compatible = "silabs,si544c" }, + { }, +}; +MODULE_DEVICE_TABLE(of, clk_si544_of_match); + +static struct i2c_driver si544_driver = { + .driver = { + .name = "si544", + .of_match_table = clk_si544_of_match, + }, + .probe = si544_probe, + .id_table = si544_id, +}; +module_i2c_driver(si544_driver); + +MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>"); +MODULE_DESCRIPTION("Si544 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/davinci/Makefile b/drivers/clk/davinci/Makefile new file mode 100644 index 000000000000..11178b79b483 --- /dev/null +++ b/drivers/clk/davinci/Makefile @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0 + +ifeq ($(CONFIG_COMMON_CLK), y) +obj-$(CONFIG_ARCH_DAVINCI_DA8XX) += da8xx-cfgchip.o + +obj-y += pll.o +obj-$(CONFIG_ARCH_DAVINCI_DA830) += pll-da830.o +obj-$(CONFIG_ARCH_DAVINCI_DA850) += pll-da850.o +obj-$(CONFIG_ARCH_DAVINCI_DM355) += pll-dm355.o +obj-$(CONFIG_ARCH_DAVINCI_DM365) += pll-dm365.o +obj-$(CONFIG_ARCH_DAVINCI_DM644x) += pll-dm644x.o +obj-$(CONFIG_ARCH_DAVINCI_DM646x) += pll-dm646x.o + +obj-y += psc.o +obj-$(CONFIG_ARCH_DAVINCI_DA830) += psc-da830.o +obj-$(CONFIG_ARCH_DAVINCI_DA850) += psc-da850.o +obj-$(CONFIG_ARCH_DAVINCI_DM355) += psc-dm355.o +obj-$(CONFIG_ARCH_DAVINCI_DM365) += psc-dm365.o +obj-$(CONFIG_ARCH_DAVINCI_DM644x) += psc-dm644x.o +obj-$(CONFIG_ARCH_DAVINCI_DM646x) += psc-dm646x.o +endif diff --git a/drivers/clk/davinci/da8xx-cfgchip.c b/drivers/clk/davinci/da8xx-cfgchip.c new file mode 100644 index 000000000000..c971111d2601 --- /dev/null +++ b/drivers/clk/davinci/da8xx-cfgchip.c @@ -0,0 +1,790 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Clock driver for DA8xx/AM17xx/AM18xx/OMAP-L13x CFGCHIP + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/init.h> +#include <linux/mfd/da8xx-cfgchip.h> +#include <linux/mfd/syscon.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_data/clk-da8xx-cfgchip.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +/* --- Gate clocks --- */ + +#define DA8XX_GATE_CLOCK_IS_DIV4P5 BIT(1) + +struct da8xx_cfgchip_gate_clk_info { + const char *name; + u32 cfgchip; + u32 bit; + u32 flags; +}; + +struct da8xx_cfgchip_gate_clk { + struct clk_hw hw; + struct regmap *regmap; + u32 reg; + u32 mask; +}; + +#define to_da8xx_cfgchip_gate_clk(_hw) \ + container_of((_hw), struct da8xx_cfgchip_gate_clk, hw) + +static int da8xx_cfgchip_gate_clk_enable(struct clk_hw *hw) +{ + struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw); + + return regmap_write_bits(clk->regmap, clk->reg, clk->mask, clk->mask); +} + +static void da8xx_cfgchip_gate_clk_disable(struct clk_hw *hw) +{ + struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw); + + regmap_write_bits(clk->regmap, clk->reg, clk->mask, 0); +} + +static int da8xx_cfgchip_gate_clk_is_enabled(struct clk_hw *hw) +{ + struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw); + unsigned int val; + + regmap_read(clk->regmap, clk->reg, &val); + + return !!(val & clk->mask); +} + +static unsigned long da8xx_cfgchip_div4p5_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + /* this clock divides by 4.5 */ + return parent_rate * 2 / 9; +} + +static const struct clk_ops da8xx_cfgchip_gate_clk_ops = { + .enable = da8xx_cfgchip_gate_clk_enable, + .disable = da8xx_cfgchip_gate_clk_disable, + .is_enabled = da8xx_cfgchip_gate_clk_is_enabled, +}; + +static const struct clk_ops da8xx_cfgchip_div4p5_clk_ops = { + .enable = da8xx_cfgchip_gate_clk_enable, + .disable = da8xx_cfgchip_gate_clk_disable, + .is_enabled = da8xx_cfgchip_gate_clk_is_enabled, + .recalc_rate = da8xx_cfgchip_div4p5_recalc_rate, +}; + +static struct da8xx_cfgchip_gate_clk * __init +da8xx_cfgchip_gate_clk_register(struct device *dev, + const struct da8xx_cfgchip_gate_clk_info *info, + struct regmap *regmap) +{ + struct clk *parent; + const char *parent_name; + struct da8xx_cfgchip_gate_clk *gate; + struct clk_init_data init; + int ret; + + parent = devm_clk_get(dev, NULL); + if (IS_ERR(parent)) + return ERR_CAST(parent); + + parent_name = __clk_get_name(parent); + + gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + init.name = info->name; + if (info->flags & DA8XX_GATE_CLOCK_IS_DIV4P5) + init.ops = &da8xx_cfgchip_div4p5_clk_ops; + else + init.ops = &da8xx_cfgchip_gate_clk_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = 0; + + gate->hw.init = &init; + gate->regmap = regmap; + gate->reg = info->cfgchip; + gate->mask = info->bit; + + ret = devm_clk_hw_register(dev, &gate->hw); + if (ret < 0) + return ERR_PTR(ret); + + return gate; +} + +static const struct da8xx_cfgchip_gate_clk_info da8xx_tbclksync_info __initconst = { + .name = "ehrpwm_tbclk", + .cfgchip = CFGCHIP(1), + .bit = CFGCHIP1_TBCLKSYNC, +}; + +static int __init da8xx_cfgchip_register_tbclk(struct device *dev, + struct regmap *regmap) +{ + struct da8xx_cfgchip_gate_clk *gate; + + gate = da8xx_cfgchip_gate_clk_register(dev, &da8xx_tbclksync_info, + regmap); + if (IS_ERR(gate)) + return PTR_ERR(gate); + + clk_hw_register_clkdev(&gate->hw, "tbclk", "ehrpwm.0"); + clk_hw_register_clkdev(&gate->hw, "tbclk", "ehrpwm.1"); + + return 0; +} + +static const struct da8xx_cfgchip_gate_clk_info da8xx_div4p5ena_info __initconst = { + .name = "div4.5", + .cfgchip = CFGCHIP(3), + .bit = CFGCHIP3_DIV45PENA, + .flags = DA8XX_GATE_CLOCK_IS_DIV4P5, +}; + +static int __init da8xx_cfgchip_register_div4p5(struct device *dev, + struct regmap *regmap) +{ + struct da8xx_cfgchip_gate_clk *gate; + + gate = da8xx_cfgchip_gate_clk_register(dev, &da8xx_div4p5ena_info, regmap); + if (IS_ERR(gate)) + return PTR_ERR(gate); + + return 0; +} + +static int __init +of_da8xx_cfgchip_gate_clk_init(struct device *dev, + const struct da8xx_cfgchip_gate_clk_info *info, + struct regmap *regmap) +{ + struct da8xx_cfgchip_gate_clk *gate; + + gate = da8xx_cfgchip_gate_clk_register(dev, info, regmap); + if (IS_ERR(gate)) + return PTR_ERR(gate); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, gate); +} + +static int __init of_da8xx_tbclksync_init(struct device *dev, + struct regmap *regmap) +{ + return of_da8xx_cfgchip_gate_clk_init(dev, &da8xx_tbclksync_info, regmap); +} + +static int __init of_da8xx_div4p5ena_init(struct device *dev, + struct regmap *regmap) +{ + return of_da8xx_cfgchip_gate_clk_init(dev, &da8xx_div4p5ena_info, regmap); +} + +/* --- MUX clocks --- */ + +struct da8xx_cfgchip_mux_clk_info { + const char *name; + const char *parent0; + const char *parent1; + u32 cfgchip; + u32 bit; +}; + +struct da8xx_cfgchip_mux_clk { + struct clk_hw hw; + struct regmap *regmap; + u32 reg; + u32 mask; +}; + +#define to_da8xx_cfgchip_mux_clk(_hw) \ + container_of((_hw), struct da8xx_cfgchip_mux_clk, hw) + +static int da8xx_cfgchip_mux_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct da8xx_cfgchip_mux_clk *clk = to_da8xx_cfgchip_mux_clk(hw); + unsigned int val = index ? clk->mask : 0; + + return regmap_write_bits(clk->regmap, clk->reg, clk->mask, val); +} + +static u8 da8xx_cfgchip_mux_clk_get_parent(struct clk_hw *hw) +{ + struct da8xx_cfgchip_mux_clk *clk = to_da8xx_cfgchip_mux_clk(hw); + unsigned int val; + + regmap_read(clk->regmap, clk->reg, &val); + + return (val & clk->mask) ? 1 : 0; +} + +static const struct clk_ops da8xx_cfgchip_mux_clk_ops = { + .set_parent = da8xx_cfgchip_mux_clk_set_parent, + .get_parent = da8xx_cfgchip_mux_clk_get_parent, +}; + +static struct da8xx_cfgchip_mux_clk * __init +da8xx_cfgchip_mux_clk_register(struct device *dev, + const struct da8xx_cfgchip_mux_clk_info *info, + struct regmap *regmap) +{ + const char * const parent_names[] = { info->parent0, info->parent1 }; + struct da8xx_cfgchip_mux_clk *mux; + struct clk_init_data init; + int ret; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + init.name = info->name; + init.ops = &da8xx_cfgchip_mux_clk_ops; + init.parent_names = parent_names; + init.num_parents = 2; + init.flags = 0; + + mux->hw.init = &init; + mux->regmap = regmap; + mux->reg = info->cfgchip; + mux->mask = info->bit; + + ret = devm_clk_hw_register(dev, &mux->hw); + if (ret < 0) + return ERR_PTR(ret); + + return mux; +} + +static const struct da8xx_cfgchip_mux_clk_info da850_async1_info __initconst = { + .name = "async1", + .parent0 = "pll0_sysclk3", + .parent1 = "div4.5", + .cfgchip = CFGCHIP(3), + .bit = CFGCHIP3_EMA_CLKSRC, +}; + +static int __init da8xx_cfgchip_register_async1(struct device *dev, + struct regmap *regmap) +{ + struct da8xx_cfgchip_mux_clk *mux; + + mux = da8xx_cfgchip_mux_clk_register(dev, &da850_async1_info, regmap); + if (IS_ERR(mux)) + return PTR_ERR(mux); + + clk_hw_register_clkdev(&mux->hw, "async1", "da850-psc0"); + + return 0; +} + +static const struct da8xx_cfgchip_mux_clk_info da850_async3_info __initconst = { + .name = "async3", + .parent0 = "pll0_sysclk2", + .parent1 = "pll1_sysclk2", + .cfgchip = CFGCHIP(3), + .bit = CFGCHIP3_ASYNC3_CLKSRC, +}; + +static int __init da850_cfgchip_register_async3(struct device *dev, + struct regmap *regmap) +{ + struct da8xx_cfgchip_mux_clk *mux; + struct clk_hw *parent; + + mux = da8xx_cfgchip_mux_clk_register(dev, &da850_async3_info, regmap); + if (IS_ERR(mux)) + return PTR_ERR(mux); + + clk_hw_register_clkdev(&mux->hw, "async3", "da850-psc1"); + + /* pll1_sysclk2 is not affected by CPU scaling, so use it for async3 */ + parent = clk_hw_get_parent_by_index(&mux->hw, 1); + if (parent) + clk_set_parent(mux->hw.clk, parent->clk); + else + dev_warn(dev, "Failed to find async3 parent clock\n"); + + return 0; +} + +static int __init +of_da8xx_cfgchip_init_mux_clock(struct device *dev, + const struct da8xx_cfgchip_mux_clk_info *info, + struct regmap *regmap) +{ + struct da8xx_cfgchip_mux_clk *mux; + + mux = da8xx_cfgchip_mux_clk_register(dev, info, regmap); + if (IS_ERR(mux)) + return PTR_ERR(mux); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &mux->hw); +} + +static int __init of_da850_async1_init(struct device *dev, struct regmap *regmap) +{ + return of_da8xx_cfgchip_init_mux_clock(dev, &da850_async1_info, regmap); +} + +static int __init of_da850_async3_init(struct device *dev, struct regmap *regmap) +{ + return of_da8xx_cfgchip_init_mux_clock(dev, &da850_async3_info, regmap); +} + +/* --- USB 2.0 PHY clock --- */ + +struct da8xx_usb0_clk48 { + struct clk_hw hw; + struct clk *fck; + struct regmap *regmap; +}; + +#define to_da8xx_usb0_clk48(_hw) \ + container_of((_hw), struct da8xx_usb0_clk48, hw) + +static int da8xx_usb0_clk48_prepare(struct clk_hw *hw) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + + /* The USB 2.0 PSC clock is only needed temporarily during the USB 2.0 + * PHY clock enable, but since clk_prepare() can't be called in an + * atomic context (i.e. in clk_enable()), we have to prepare it here. + */ + return clk_prepare(usb0->fck); +} + +static void da8xx_usb0_clk48_unprepare(struct clk_hw *hw) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + + clk_unprepare(usb0->fck); +} + +static int da8xx_usb0_clk48_enable(struct clk_hw *hw) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + unsigned int mask, val; + int ret; + + /* Locking the USB 2.O PLL requires that the USB 2.O PSC is enabled + * temporaily. It can be turned back off once the PLL is locked. + */ + clk_enable(usb0->fck); + + /* Turn on the USB 2.0 PHY, but just the PLL, and not OTG. The USB 1.1 + * PHY may use the USB 2.0 PLL clock without USB 2.0 OTG being used. + */ + mask = CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_PHY_PLLON; + val = CFGCHIP2_PHY_PLLON; + + regmap_write_bits(usb0->regmap, CFGCHIP(2), mask, val); + ret = regmap_read_poll_timeout(usb0->regmap, CFGCHIP(2), val, + val & CFGCHIP2_PHYCLKGD, 0, 500000); + + clk_disable(usb0->fck); + + return ret; +} + +static void da8xx_usb0_clk48_disable(struct clk_hw *hw) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + unsigned int val; + + val = CFGCHIP2_PHYPWRDN; + regmap_write_bits(usb0->regmap, CFGCHIP(2), val, val); +} + +static int da8xx_usb0_clk48_is_enabled(struct clk_hw *hw) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + unsigned int val; + + regmap_read(usb0->regmap, CFGCHIP(2), &val); + + return !!(val & CFGCHIP2_PHYCLKGD); +} + +static unsigned long da8xx_usb0_clk48_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + unsigned int mask, val; + + /* The parent clock rate must be one of the following */ + mask = CFGCHIP2_REFFREQ_MASK; + switch (parent_rate) { + case 12000000: + val = CFGCHIP2_REFFREQ_12MHZ; + break; + case 13000000: + val = CFGCHIP2_REFFREQ_13MHZ; + break; + case 19200000: + val = CFGCHIP2_REFFREQ_19_2MHZ; + break; + case 20000000: + val = CFGCHIP2_REFFREQ_20MHZ; + break; + case 24000000: + val = CFGCHIP2_REFFREQ_24MHZ; + break; + case 26000000: + val = CFGCHIP2_REFFREQ_26MHZ; + break; + case 38400000: + val = CFGCHIP2_REFFREQ_38_4MHZ; + break; + case 40000000: + val = CFGCHIP2_REFFREQ_40MHZ; + break; + case 48000000: + val = CFGCHIP2_REFFREQ_48MHZ; + break; + default: + return 0; + } + + regmap_write_bits(usb0->regmap, CFGCHIP(2), mask, val); + + /* USB 2.0 PLL always supplies 48MHz */ + return 48000000; +} + +static long da8xx_usb0_clk48_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return 48000000; +} + +static int da8xx_usb0_clk48_set_parent(struct clk_hw *hw, u8 index) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + + return regmap_write_bits(usb0->regmap, CFGCHIP(2), + CFGCHIP2_USB2PHYCLKMUX, + index ? CFGCHIP2_USB2PHYCLKMUX : 0); +} + +static u8 da8xx_usb0_clk48_get_parent(struct clk_hw *hw) +{ + struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw); + unsigned int val; + + regmap_read(usb0->regmap, CFGCHIP(2), &val); + + return (val & CFGCHIP2_USB2PHYCLKMUX) ? 1 : 0; +} + +static const struct clk_ops da8xx_usb0_clk48_ops = { + .prepare = da8xx_usb0_clk48_prepare, + .unprepare = da8xx_usb0_clk48_unprepare, + .enable = da8xx_usb0_clk48_enable, + .disable = da8xx_usb0_clk48_disable, + .is_enabled = da8xx_usb0_clk48_is_enabled, + .recalc_rate = da8xx_usb0_clk48_recalc_rate, + .round_rate = da8xx_usb0_clk48_round_rate, + .set_parent = da8xx_usb0_clk48_set_parent, + .get_parent = da8xx_usb0_clk48_get_parent, +}; + +static struct da8xx_usb0_clk48 * +da8xx_cfgchip_register_usb0_clk48(struct device *dev, + struct regmap *regmap) +{ + const char * const parent_names[] = { "usb_refclkin", "pll0_auxclk" }; + struct clk *fck_clk; + struct da8xx_usb0_clk48 *usb0; + struct clk_init_data init; + int ret; + + fck_clk = devm_clk_get(dev, "fck"); + if (IS_ERR(fck_clk)) { + if (PTR_ERR(fck_clk) != -EPROBE_DEFER) + dev_err(dev, "Missing fck clock\n"); + return ERR_CAST(fck_clk); + } + + usb0 = devm_kzalloc(dev, sizeof(*usb0), GFP_KERNEL); + if (!usb0) + return ERR_PTR(-ENOMEM); + + init.name = "usb0_clk48"; + init.ops = &da8xx_usb0_clk48_ops; + init.parent_names = parent_names; + init.num_parents = 2; + + usb0->hw.init = &init; + usb0->fck = fck_clk; + usb0->regmap = regmap; + + ret = devm_clk_hw_register(dev, &usb0->hw); + if (ret < 0) + return ERR_PTR(ret); + + return usb0; +} + +/* --- USB 1.1 PHY clock --- */ + +struct da8xx_usb1_clk48 { + struct clk_hw hw; + struct regmap *regmap; +}; + +#define to_da8xx_usb1_clk48(_hw) \ + container_of((_hw), struct da8xx_usb1_clk48, hw) + +static int da8xx_usb1_clk48_set_parent(struct clk_hw *hw, u8 index) +{ + struct da8xx_usb1_clk48 *usb1 = to_da8xx_usb1_clk48(hw); + + return regmap_write_bits(usb1->regmap, CFGCHIP(2), + CFGCHIP2_USB1PHYCLKMUX, + index ? CFGCHIP2_USB1PHYCLKMUX : 0); +} + +static u8 da8xx_usb1_clk48_get_parent(struct clk_hw *hw) +{ + struct da8xx_usb1_clk48 *usb1 = to_da8xx_usb1_clk48(hw); + unsigned int val; + + regmap_read(usb1->regmap, CFGCHIP(2), &val); + + return (val & CFGCHIP2_USB1PHYCLKMUX) ? 1 : 0; +} + +static const struct clk_ops da8xx_usb1_clk48_ops = { + .set_parent = da8xx_usb1_clk48_set_parent, + .get_parent = da8xx_usb1_clk48_get_parent, +}; + +/** + * da8xx_cfgchip_register_usb1_clk48 - Register a new USB 1.1 PHY clock + * @regmap: The CFGCHIP regmap + */ +static struct da8xx_usb1_clk48 * +da8xx_cfgchip_register_usb1_clk48(struct device *dev, + struct regmap *regmap) +{ + const char * const parent_names[] = { "usb0_clk48", "usb_refclkin" }; + struct da8xx_usb1_clk48 *usb1; + struct clk_init_data init; + int ret; + + usb1 = devm_kzalloc(dev, sizeof(*usb1), GFP_KERNEL); + if (!usb1) + return ERR_PTR(-ENOMEM); + + init.name = "usb1_clk48"; + init.ops = &da8xx_usb1_clk48_ops; + init.parent_names = parent_names; + init.num_parents = 2; + + usb1->hw.init = &init; + usb1->regmap = regmap; + + ret = devm_clk_hw_register(dev, &usb1->hw); + if (ret < 0) + return ERR_PTR(ret); + + return usb1; +} + +static int da8xx_cfgchip_register_usb_phy_clk(struct device *dev, + struct regmap *regmap) +{ + struct da8xx_usb0_clk48 *usb0; + struct da8xx_usb1_clk48 *usb1; + struct clk_hw *parent; + + usb0 = da8xx_cfgchip_register_usb0_clk48(dev, regmap); + if (IS_ERR(usb0)) + return PTR_ERR(usb0); + + /* + * All existing boards use pll0_auxclk as the parent and new boards + * should use device tree, so hard-coding the value (1) here. + */ + parent = clk_hw_get_parent_by_index(&usb0->hw, 1); + if (parent) + clk_set_parent(usb0->hw.clk, parent->clk); + else + dev_warn(dev, "Failed to find usb0 parent clock\n"); + + usb1 = da8xx_cfgchip_register_usb1_clk48(dev, regmap); + if (IS_ERR(usb1)) + return PTR_ERR(usb1); + + /* + * All existing boards use usb0_clk48 as the parent and new boards + * should use device tree, so hard-coding the value (0) here. + */ + parent = clk_hw_get_parent_by_index(&usb1->hw, 0); + if (parent) + clk_set_parent(usb1->hw.clk, parent->clk); + else + dev_warn(dev, "Failed to find usb1 parent clock\n"); + + clk_hw_register_clkdev(&usb0->hw, "usb0_clk48", "da8xx-usb-phy"); + clk_hw_register_clkdev(&usb1->hw, "usb1_clk48", "da8xx-usb-phy"); + + return 0; +} + +static int of_da8xx_usb_phy_clk_init(struct device *dev, struct regmap *regmap) +{ + struct clk_hw_onecell_data *clk_data; + struct da8xx_usb0_clk48 *usb0; + struct da8xx_usb1_clk48 *usb1; + + clk_data = devm_kzalloc(dev, sizeof(*clk_data) + 2 * + sizeof(*clk_data->hws), GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = 2; + + usb0 = da8xx_cfgchip_register_usb0_clk48(dev, regmap); + if (IS_ERR(usb0)) { + if (PTR_ERR(usb0) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_warn(dev, "Failed to register usb0_clk48 (%ld)\n", + PTR_ERR(usb0)); + + clk_data->hws[0] = ERR_PTR(-ENOENT); + } else { + clk_data->hws[0] = &usb0->hw; + } + + usb1 = da8xx_cfgchip_register_usb1_clk48(dev, regmap); + if (IS_ERR(usb1)) { + if (PTR_ERR(usb0) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_warn(dev, "Failed to register usb1_clk48 (%ld)\n", + PTR_ERR(usb1)); + + clk_data->hws[1] = ERR_PTR(-ENOENT); + } else { + clk_data->hws[1] = &usb1->hw; + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); +} + +/* --- platform device --- */ + +static const struct of_device_id da8xx_cfgchip_of_match[] = { + { + .compatible = "ti,da830-tbclksync", + .data = of_da8xx_tbclksync_init, + }, + { + .compatible = "ti,da830-div4p5ena", + .data = of_da8xx_div4p5ena_init, + }, + { + .compatible = "ti,da850-async1-clksrc", + .data = of_da850_async1_init, + }, + { + .compatible = "ti,da850-async3-clksrc", + .data = of_da850_async3_init, + }, + { + .compatible = "ti,da830-usb-phy-clocks", + .data = of_da8xx_usb_phy_clk_init, + }, + { } +}; + +static const struct platform_device_id da8xx_cfgchip_id_table[] = { + { + .name = "da830-tbclksync", + .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_tbclk, + }, + { + .name = "da830-div4p5ena", + .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_div4p5, + }, + { + .name = "da850-async1-clksrc", + .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_async1, + }, + { + .name = "da850-async3-clksrc", + .driver_data = (kernel_ulong_t)da850_cfgchip_register_async3, + }, + { + .name = "da830-usb-phy-clks", + .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_usb_phy_clk, + }, + { } +}; + +typedef int (*da8xx_cfgchip_init)(struct device *dev, struct regmap *regmap); + +static int da8xx_cfgchip_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct da8xx_cfgchip_clk_platform_data *pdata = dev->platform_data; + const struct of_device_id *of_id; + da8xx_cfgchip_init clk_init = NULL; + struct regmap *regmap = NULL; + + of_id = of_match_device(da8xx_cfgchip_of_match, dev); + if (of_id) { + struct device_node *parent; + + clk_init = of_id->data; + parent = of_get_parent(dev->of_node); + regmap = syscon_node_to_regmap(parent); + of_node_put(parent); + } else if (pdev->id_entry && pdata) { + clk_init = (void *)pdev->id_entry->driver_data; + regmap = pdata->cfgchip; + } + + if (!clk_init) { + dev_err(dev, "unable to find driver data\n"); + return -EINVAL; + } + + if (IS_ERR_OR_NULL(regmap)) { + dev_err(dev, "no regmap for CFGCHIP syscon\n"); + return regmap ? PTR_ERR(regmap) : -ENOENT; + } + + return clk_init(dev, regmap); +} + +static struct platform_driver da8xx_cfgchip_driver = { + .probe = da8xx_cfgchip_probe, + .driver = { + .name = "da8xx-cfgchip-clk", + .of_match_table = da8xx_cfgchip_of_match, + }, + .id_table = da8xx_cfgchip_id_table, +}; + +static int __init da8xx_cfgchip_driver_init(void) +{ + return platform_driver_register(&da8xx_cfgchip_driver); +} + +/* has to be postcore_initcall because PSC devices depend on the async3 clock */ +postcore_initcall(da8xx_cfgchip_driver_init); diff --git a/drivers/clk/davinci/pll-da830.c b/drivers/clk/davinci/pll-da830.c new file mode 100644 index 000000000000..929a3d3a9adb --- /dev/null +++ b/drivers/clk/davinci/pll-da830.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PLL clock descriptions for TI DA830/OMAP-L137/AM17XX + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#include <linux/clkdev.h> +#include <linux/bitops.h> +#include <linux/init.h> +#include <linux/types.h> + +#include "pll.h" + +static const struct davinci_pll_clk_info da830_pll_info = { + .name = "pll0", + .pllm_mask = GENMASK(4, 0), + .pllm_min = 4, + .pllm_max = 32, + .pllout_min_rate = 300000000, + .pllout_max_rate = 600000000, + .flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_HAS_POSTDIV, +}; + +/* + * NB: Technically, the clocks flagged as SYSCLK_FIXED_DIV are "fixed ratio", + * meaning that we could change the divider as long as we keep the correct + * ratio between all of the clocks, but we don't support that because there is + * currently not a need for it. + */ + +SYSCLK(2, pll0_sysclk2, pll0_pllen, 5, SYSCLK_FIXED_DIV); +SYSCLK(3, pll0_sysclk3, pll0_pllen, 5, 0); +SYSCLK(4, pll0_sysclk4, pll0_pllen, 5, SYSCLK_FIXED_DIV); +SYSCLK(5, pll0_sysclk5, pll0_pllen, 5, 0); +SYSCLK(6, pll0_sysclk6, pll0_pllen, 5, SYSCLK_FIXED_DIV); +SYSCLK(7, pll0_sysclk7, pll0_pllen, 5, 0); + +int da830_pll_init(struct device *dev, void __iomem *base) +{ + struct clk *clk; + + davinci_pll_clk_register(dev, &da830_pll_info, "ref_clk", base); + + clk = davinci_pll_sysclk_register(dev, &pll0_sysclk2, base); + clk_register_clkdev(clk, "pll0_sysclk2", "da830-psc0"); + clk_register_clkdev(clk, "pll0_sysclk2", "da830-psc1"); + + clk = davinci_pll_sysclk_register(dev, &pll0_sysclk3, base); + clk_register_clkdev(clk, "pll0_sysclk3", "da830-psc0"); + + clk = davinci_pll_sysclk_register(dev, &pll0_sysclk4, base); + clk_register_clkdev(clk, "pll0_sysclk4", "da830-psc0"); + clk_register_clkdev(clk, "pll0_sysclk4", "da830-psc1"); + + clk = davinci_pll_sysclk_register(dev, &pll0_sysclk5, base); + clk_register_clkdev(clk, "pll0_sysclk5", "da830-psc1"); + + clk = davinci_pll_sysclk_register(dev, &pll0_sysclk6, base); + clk_register_clkdev(clk, "pll0_sysclk6", "da830-psc0"); + + clk = davinci_pll_sysclk_register(dev, &pll0_sysclk7, base); + + clk = davinci_pll_auxclk_register(dev, "pll0_auxclk", base); + clk_register_clkdev(clk, NULL, "i2c_davinci.1"); + clk_register_clkdev(clk, "timer0", NULL); + clk_register_clkdev(clk, NULL, "davinci-wdt"); + + return 0; +} diff --git a/drivers/clk/davinci/pll-da850.c b/drivers/clk/davinci/pll-da850.c new file mode 100644 index 000000000000..2a038b7908cc --- /dev/null +++ b/drivers/clk/davinci/pll-da850.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PLL clock descriptions for TI DA850/OMAP-L138/AM18XX + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#include <linux/bitops.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mfd/da8xx-cfgchip.h> +#include <linux/of.h> +#include <linux/types.h> + +#include "pll.h" + +#define OCSEL_OCSRC_OSCIN 0x14 +#define OCSEL_OCSRC_PLL0_SYSCLK(n) (0x16 + (n)) +#define OCSEL_OCSRC_PLL1_OBSCLK 0x1e +#define OCSEL_OCSRC_PLL1_SYSCLK(n) (0x16 + (n)) + +static const struct davinci_pll_clk_info da850_pll0_info = { + .name = "pll0", + .unlock_reg = CFGCHIP(0), + .unlock_mask = CFGCHIP0_PLL_MASTER_LOCK, + .pllm_mask = GENMASK(4, 0), + .pllm_min = 4, + .pllm_max = 32, + .pllout_min_rate = 300000000, + .pllout_max_rate = 600000000, + .flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_HAS_POSTDIV | + PLL_HAS_EXTCLKSRC, +}; + +/* + * NB: Technically, the clocks flagged as SYSCLK_FIXED_DIV are "fixed ratio", + * meaning that we could change the divider as long as we keep the correct + * ratio between all of the clocks, but we don't support that because there is + * currently not a need for it. + */ + +SYSCLK(1, pll0_sysclk1, pll0_pllen, 5, SYSCLK_FIXED_DIV); +SYSCLK(2, pll0_sysclk2, pll0_pllen, 5, SYSCLK_FIXED_DIV); +SYSCLK(3, pll0_sysclk3, pll0_pllen, 5, 0); +SYSCLK(4, pll0_sysclk4, pll0_pllen, 5, SYSCLK_FIXED_DIV); +SYSCLK(5, pll0_sysclk5, pll0_pllen, 5, 0); +SYSCLK(6, pll0_sysclk6, pll0_pllen, 5, SYSCLK_ARM_RATE | SYSCLK_FIXED_DIV); +SYSCLK(7, pll0_sysclk7, pll0_pllen, 5, 0); + +static const char * const da850_pll0_obsclk_parent_names[] = { + "oscin", + "pll0_sysclk1", + "pll0_sysclk2", + "pll0_sysclk3", + "pll0_sysclk4", + "pll0_sysclk5", + "pll0_sysclk6", + "pll0_sysclk7", + "pll1_obsclk", +}; + +static u32 da850_pll0_obsclk_table[] = { + OCSEL_OCSRC_OSCIN, + OCSEL_OCSRC_PLL0_SYSCLK(1), + OCSEL_OCSRC_PLL0_SYSCLK(2), + OCSEL_OCSRC_PLL0_SYSCLK(3), + OCSEL_OCSRC_PLL0_SYSCLK(4), + OCSEL_OCSRC_PLL0_SYSCLK(5), + OCSEL_OCSRC_PLL0_SYSCLK(6), + OCSEL_OCSRC_PLL0_SYSCLK(7), + OCSEL_OCSRC_PLL1_OBSCLK, +}; + +static const struct davinci_pll_obsclk_info da850_pll0_obsclk_info = { + .name = "pll0_obsclk", + .parent_names = da850_pll0_obsclk_parent_names, + .num_parents = ARRAY_SIZE(da850_pll0_obsclk_parent_names), + .table = da850_pll0_obsclk_table, + .ocsrc_mask = GENMASK(4, 0), +}; + +int da850_pll0_init(struct device *dev, void __iomem *base) +{ + struct clk *clk; + + davinci_pll_clk_register(dev, &da850_pll0_info, "ref_clk", base); + + clk = davinci_pll_sysclk_register(dev, &pll0_sysclk1, base); + clk_register_clkdev(clk, "pll0_sysclk1", "da850-psc0"); + + clk = davinci_pll_sysclk_register(dev, &pll0_sysclk2, base); + clk_register_clkdev(clk, "pll0_sysclk2", "da850-psc0"); + clk_register_clkdev(clk, "pll0_sysclk2", "da850-psc1"); + clk_register_clkdev(clk, "pll0_sysclk2", "da850-async3-clksrc"); + + clk = davinci_pll_sysclk_register(dev, &pll0_sysclk3, base); + clk_register_clkdev(clk, "pll0_sysclk3", "da850-async1-clksrc"); + + clk = davinci_pll_sysclk_register(dev, &pll0_sysclk4, base); + clk_register_clkdev(clk, "pll0_sysclk4", "da850-psc0"); + clk_register_clkdev(clk, "pll0_sysclk4", "da850-psc1"); + + davinci_pll_sysclk_register(dev, &pll0_sysclk5, base); + + clk = davinci_pll_sysclk_register(dev, &pll0_sysclk6, base); + clk_register_clkdev(clk, "pll0_sysclk6", "da850-psc0"); + + davinci_pll_sysclk_register(dev, &pll0_sysclk7, base); + + davinci_pll_auxclk_register(dev, "pll0_auxclk", base); + + clk = clk_register_fixed_factor(dev, "async2", "pll0_auxclk", + CLK_IS_CRITICAL, 1, 1); + + clk_register_clkdev(clk, NULL, "i2c_davinci.1"); + clk_register_clkdev(clk, "timer0", NULL); + clk_register_clkdev(clk, NULL, "davinci-wdt"); + + davinci_pll_obsclk_register(dev, &da850_pll0_obsclk_info, base); + + return 0; +} + +static const struct davinci_pll_sysclk_info *da850_pll0_sysclk_info[] = { + &pll0_sysclk1, + &pll0_sysclk2, + &pll0_sysclk3, + &pll0_sysclk4, + &pll0_sysclk5, + &pll0_sysclk6, + &pll0_sysclk7, + NULL +}; + +int of_da850_pll0_init(struct device *dev, void __iomem *base) +{ + return of_davinci_pll_init(dev, &da850_pll0_info, + &da850_pll0_obsclk_info, + da850_pll0_sysclk_info, 7, base); +} + +static const struct davinci_pll_clk_info da850_pll1_info = { + .name = "pll1", + .unlock_reg = CFGCHIP(3), + .unlock_mask = CFGCHIP3_PLL1_MASTER_LOCK, + .pllm_mask = GENMASK(4, 0), + .pllm_min = 4, + .pllm_max = 32, + .pllout_min_rate = 300000000, + .pllout_max_rate = 600000000, + .flags = PLL_HAS_POSTDIV, +}; + +SYSCLK(1, pll1_sysclk1, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(2, pll1_sysclk2, pll1_pllen, 5, 0); +SYSCLK(3, pll1_sysclk3, pll1_pllen, 5, 0); + +static const char * const da850_pll1_obsclk_parent_names[] = { + "oscin", + "pll1_sysclk1", + "pll1_sysclk2", + "pll1_sysclk3", +}; + +static u32 da850_pll1_obsclk_table[] = { + OCSEL_OCSRC_OSCIN, + OCSEL_OCSRC_PLL1_SYSCLK(1), + OCSEL_OCSRC_PLL1_SYSCLK(2), + OCSEL_OCSRC_PLL1_SYSCLK(3), +}; + +static const struct davinci_pll_obsclk_info da850_pll1_obsclk_info = { + .name = "pll1_obsclk", + .parent_names = da850_pll1_obsclk_parent_names, + .num_parents = ARRAY_SIZE(da850_pll1_obsclk_parent_names), + .table = da850_pll1_obsclk_table, + .ocsrc_mask = GENMASK(4, 0), +}; + +int da850_pll1_init(struct device *dev, void __iomem *base) +{ + struct clk *clk; + + davinci_pll_clk_register(dev, &da850_pll1_info, "oscin", base); + + davinci_pll_sysclk_register(dev, &pll1_sysclk1, base); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base); + clk_register_clkdev(clk, "pll1_sysclk2", "da850-async3-clksrc"); + + davinci_pll_sysclk_register(dev, &pll1_sysclk3, base); + + davinci_pll_obsclk_register(dev, &da850_pll1_obsclk_info, base); + + return 0; +} + +static const struct davinci_pll_sysclk_info *da850_pll1_sysclk_info[] = { + &pll1_sysclk1, + &pll1_sysclk2, + &pll1_sysclk3, + NULL +}; + +int of_da850_pll1_init(struct device *dev, void __iomem *base) +{ + return of_davinci_pll_init(dev, &da850_pll1_info, + &da850_pll1_obsclk_info, + da850_pll1_sysclk_info, 3, base); +} diff --git a/drivers/clk/davinci/pll-dm355.c b/drivers/clk/davinci/pll-dm355.c new file mode 100644 index 000000000000..5345f8286c50 --- /dev/null +++ b/drivers/clk/davinci/pll-dm355.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PLL clock descriptions for TI DM355 + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#include <linux/bitops.h> +#include <linux/clkdev.h> +#include <linux/init.h> +#include <linux/types.h> + +#include "pll.h" + +static const struct davinci_pll_clk_info dm355_pll1_info = { + .name = "pll1", + .pllm_mask = GENMASK(7, 0), + .pllm_min = 92, + .pllm_max = 184, + .flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_PREDIV_ALWAYS_ENABLED | + PLL_PREDIV_FIXED8 | PLL_HAS_POSTDIV | + PLL_POSTDIV_ALWAYS_ENABLED | PLL_POSTDIV_FIXED_DIV, +}; + +SYSCLK(1, pll1_sysclk1, pll1, 5, SYSCLK_FIXED_DIV | SYSCLK_ALWAYS_ENABLED); +SYSCLK(2, pll1_sysclk2, pll1, 5, SYSCLK_FIXED_DIV | SYSCLK_ALWAYS_ENABLED); +SYSCLK(3, pll1_sysclk3, pll1, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(4, pll1_sysclk4, pll1, 5, SYSCLK_ALWAYS_ENABLED); + +int dm355_pll1_init(struct device *dev, void __iomem *base) +{ + struct clk *clk; + + davinci_pll_clk_register(dev, &dm355_pll1_info, "ref_clk", base); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base); + clk_register_clkdev(clk, "pll1_sysclk1", "dm355-psc"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base); + clk_register_clkdev(clk, "pll1_sysclk2", "dm355-psc"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base); + clk_register_clkdev(clk, "pll1_sysclk3", "dm355-psc"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk4, base); + clk_register_clkdev(clk, "pll1_sysclk4", "dm355-psc"); + + clk = davinci_pll_auxclk_register(dev, "pll1_auxclk", base); + clk_register_clkdev(clk, "pll1_auxclk", "dm355-psc"); + + davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base); + + return 0; +} + +static const struct davinci_pll_clk_info dm355_pll2_info = { + .name = "pll2", + .pllm_mask = GENMASK(7, 0), + .pllm_min = 92, + .pllm_max = 184, + .flags = PLL_HAS_PREDIV | PLL_PREDIV_ALWAYS_ENABLED | PLL_HAS_POSTDIV | + PLL_POSTDIV_ALWAYS_ENABLED | PLL_POSTDIV_FIXED_DIV, +}; + +SYSCLK(1, pll2_sysclk1, pll2, 5, SYSCLK_FIXED_DIV); +SYSCLK(2, pll2_sysclk2, pll2, 5, SYSCLK_FIXED_DIV | SYSCLK_ALWAYS_ENABLED); + +int dm355_pll2_init(struct device *dev, void __iomem *base) +{ + davinci_pll_clk_register(dev, &dm355_pll2_info, "oscin", base); + + davinci_pll_sysclk_register(dev, &pll2_sysclk1, base); + + davinci_pll_sysclk_register(dev, &pll2_sysclk2, base); + + davinci_pll_sysclkbp_clk_register(dev, "pll2_sysclkbp", base); + + return 0; +} diff --git a/drivers/clk/davinci/pll-dm365.c b/drivers/clk/davinci/pll-dm365.c new file mode 100644 index 000000000000..5f8d9f42d0f3 --- /dev/null +++ b/drivers/clk/davinci/pll-dm365.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PLL clock descriptions for TI DM365 + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#include <linux/bitops.h> +#include <linux/clkdev.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include "pll.h" + +#define OCSEL_OCSRC_ENABLE 0 + +static const struct davinci_pll_clk_info dm365_pll1_info = { + .name = "pll1", + .pllm_mask = GENMASK(9, 0), + .pllm_min = 1, + .pllm_max = 1023, + .flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_HAS_POSTDIV | + PLL_POSTDIV_ALWAYS_ENABLED | PLL_PLLM_2X, +}; + +SYSCLK(1, pll1_sysclk1, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(2, pll1_sysclk2, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(3, pll1_sysclk3, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(4, pll1_sysclk4, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(5, pll1_sysclk5, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(6, pll1_sysclk6, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(7, pll1_sysclk7, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(8, pll1_sysclk8, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(9, pll1_sysclk9, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED); + +/* + * This is a bit of a hack to make OCSEL[OCSRC] on DM365 look like OCSEL[OCSRC] + * on DA850. On DM365, OCSEL[OCSRC] is just an enable/disable bit instead of a + * multiplexer. By modeling it as a single parent mux clock, the clock code will + * still do the right thing in this case. + */ +static const char * const dm365_pll_obsclk_parent_names[] = { + "oscin", +}; + +static u32 dm365_pll_obsclk_table[] = { + OCSEL_OCSRC_ENABLE, +}; + +static const struct davinci_pll_obsclk_info dm365_pll1_obsclk_info = { + .name = "pll1_obsclk", + .parent_names = dm365_pll_obsclk_parent_names, + .num_parents = ARRAY_SIZE(dm365_pll_obsclk_parent_names), + .table = dm365_pll_obsclk_table, + .ocsrc_mask = BIT(4), +}; + +int dm365_pll1_init(struct device *dev, void __iomem *base) +{ + struct clk *clk; + + davinci_pll_clk_register(dev, &dm365_pll1_info, "ref_clk", base); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base); + clk_register_clkdev(clk, "pll1_sysclk1", "dm365-psc"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base); + clk_register_clkdev(clk, "pll1_sysclk2", "dm365-psc"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base); + clk_register_clkdev(clk, "pll1_sysclk3", "dm365-psc"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk4, base); + clk_register_clkdev(clk, "pll1_sysclk4", "dm365-psc"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk5, base); + clk_register_clkdev(clk, "pll1_sysclk5", "dm365-psc"); + + davinci_pll_sysclk_register(dev, &pll1_sysclk6, base); + + davinci_pll_sysclk_register(dev, &pll1_sysclk7, base); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk8, base); + clk_register_clkdev(clk, "pll1_sysclk8", "dm365-psc"); + + davinci_pll_sysclk_register(dev, &pll1_sysclk9, base); + + clk = davinci_pll_auxclk_register(dev, "pll1_auxclk", base); + clk_register_clkdev(clk, "pll1_auxclk", "dm355-psc"); + + davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base); + + davinci_pll_obsclk_register(dev, &dm365_pll1_obsclk_info, base); + + return 0; +} + +static const struct davinci_pll_clk_info dm365_pll2_info = { + .name = "pll2", + .pllm_mask = GENMASK(9, 0), + .pllm_min = 1, + .pllm_max = 1023, + .flags = PLL_HAS_PREDIV | PLL_HAS_POSTDIV | PLL_POSTDIV_ALWAYS_ENABLED | + PLL_PLLM_2X, +}; + +SYSCLK(1, pll2_sysclk1, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(2, pll2_sysclk2, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(3, pll2_sysclk3, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(4, pll2_sysclk4, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED); +SYSCLK(5, pll2_sysclk5, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED); + +static const struct davinci_pll_obsclk_info dm365_pll2_obsclk_info = { + .name = "pll2_obsclk", + .parent_names = dm365_pll_obsclk_parent_names, + .num_parents = ARRAY_SIZE(dm365_pll_obsclk_parent_names), + .table = dm365_pll_obsclk_table, + .ocsrc_mask = BIT(4), +}; + +int dm365_pll2_init(struct device *dev, void __iomem *base) +{ + struct clk *clk; + + davinci_pll_clk_register(dev, &dm365_pll2_info, "oscin", base); + + davinci_pll_sysclk_register(dev, &pll2_sysclk1, base); + + clk = davinci_pll_sysclk_register(dev, &pll2_sysclk2, base); + clk_register_clkdev(clk, "pll1_sysclk2", "dm365-psc"); + + davinci_pll_sysclk_register(dev, &pll2_sysclk3, base); + + clk = davinci_pll_sysclk_register(dev, &pll2_sysclk4, base); + clk_register_clkdev(clk, "pll1_sysclk4", "dm365-psc"); + + davinci_pll_sysclk_register(dev, &pll2_sysclk5, base); + + davinci_pll_auxclk_register(dev, "pll2_auxclk", base); + + davinci_pll_obsclk_register(dev, &dm365_pll2_obsclk_info, base); + + return 0; +} diff --git a/drivers/clk/davinci/pll-dm644x.c b/drivers/clk/davinci/pll-dm644x.c new file mode 100644 index 000000000000..69bf785377cf --- /dev/null +++ b/drivers/clk/davinci/pll-dm644x.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PLL clock descriptions for TI DM644X + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#include <linux/bitops.h> +#include <linux/clkdev.h> +#include <linux/init.h> +#include <linux/types.h> + +#include "pll.h" + +static const struct davinci_pll_clk_info dm644x_pll1_info = { + .name = "pll1", + .pllm_mask = GENMASK(4, 0), + .pllm_min = 1, + .pllm_max = 32, + .pllout_min_rate = 400000000, + .pllout_max_rate = 600000000, /* 810MHz @ 1.3V, -810 only */ + .flags = PLL_HAS_CLKMODE | PLL_HAS_POSTDIV, +}; + +SYSCLK(1, pll1_sysclk1, pll1_pllen, 4, SYSCLK_FIXED_DIV); +SYSCLK(2, pll1_sysclk2, pll1_pllen, 4, SYSCLK_FIXED_DIV); +SYSCLK(3, pll1_sysclk3, pll1_pllen, 4, SYSCLK_FIXED_DIV); +SYSCLK(5, pll1_sysclk5, pll1_pllen, 4, SYSCLK_FIXED_DIV); + +int dm644x_pll1_init(struct device *dev, void __iomem *base) +{ + struct clk *clk; + + davinci_pll_clk_register(dev, &dm644x_pll1_info, "ref_clk", base); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base); + clk_register_clkdev(clk, "pll1_sysclk1", "dm644x-psc"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base); + clk_register_clkdev(clk, "pll1_sysclk2", "dm644x-psc"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base); + clk_register_clkdev(clk, "pll1_sysclk3", "dm644x-psc"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk5, base); + clk_register_clkdev(clk, "pll1_sysclk5", "dm644x-psc"); + + clk = davinci_pll_auxclk_register(dev, "pll1_auxclk", base); + clk_register_clkdev(clk, "pll1_auxclk", "dm644x-psc"); + + davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base); + + return 0; +} + +static const struct davinci_pll_clk_info dm644x_pll2_info = { + .name = "pll2", + .pllm_mask = GENMASK(4, 0), + .pllm_min = 1, + .pllm_max = 32, + .pllout_min_rate = 400000000, + .pllout_max_rate = 900000000, + .flags = PLL_HAS_POSTDIV | PLL_POSTDIV_FIXED_DIV, +}; + +SYSCLK(1, pll2_sysclk1, pll2_pllen, 4, 0); +SYSCLK(2, pll2_sysclk2, pll2_pllen, 4, 0); + +int dm644x_pll2_init(struct device *dev, void __iomem *base) +{ + davinci_pll_clk_register(dev, &dm644x_pll2_info, "oscin", base); + + davinci_pll_sysclk_register(dev, &pll2_sysclk1, base); + + davinci_pll_sysclk_register(dev, &pll2_sysclk2, base); + + davinci_pll_sysclkbp_clk_register(dev, "pll2_sysclkbp", base); + + return 0; +} diff --git a/drivers/clk/davinci/pll-dm646x.c b/drivers/clk/davinci/pll-dm646x.c new file mode 100644 index 000000000000..a61cc3256418 --- /dev/null +++ b/drivers/clk/davinci/pll-dm646x.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PLL clock descriptions for TI DM646X + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/init.h> +#include <linux/types.h> + +#include "pll.h" + +static const struct davinci_pll_clk_info dm646x_pll1_info = { + .name = "pll1", + .pllm_mask = GENMASK(4, 0), + .pllm_min = 14, + .pllm_max = 32, + .flags = PLL_HAS_CLKMODE, +}; + +SYSCLK(1, pll1_sysclk1, pll1_pllen, 4, SYSCLK_FIXED_DIV); +SYSCLK(2, pll1_sysclk2, pll1_pllen, 4, SYSCLK_FIXED_DIV); +SYSCLK(3, pll1_sysclk3, pll1_pllen, 4, SYSCLK_FIXED_DIV); +SYSCLK(4, pll1_sysclk4, pll1_pllen, 4, 0); +SYSCLK(5, pll1_sysclk5, pll1_pllen, 4, 0); +SYSCLK(6, pll1_sysclk6, pll1_pllen, 4, 0); +SYSCLK(8, pll1_sysclk8, pll1_pllen, 4, 0); +SYSCLK(9, pll1_sysclk9, pll1_pllen, 4, 0); + +int dm646x_pll1_init(struct device *dev, void __iomem *base) +{ + struct clk *clk; + + davinci_pll_clk_register(dev, &dm646x_pll1_info, "ref_clk", base); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base); + clk_register_clkdev(clk, "pll1_sysclk1", "dm646x-psc"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base); + clk_register_clkdev(clk, "pll1_sysclk2", "dm646x-psc"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base); + clk_register_clkdev(clk, "pll1_sysclk3", "dm646x-psc"); + clk_register_clkdev(clk, NULL, "davinci-wdt"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk4, base); + clk_register_clkdev(clk, "pll1_sysclk4", "dm646x-psc"); + + clk = davinci_pll_sysclk_register(dev, &pll1_sysclk5, base); + clk_register_clkdev(clk, "pll1_sysclk5", "dm646x-psc"); + + davinci_pll_sysclk_register(dev, &pll1_sysclk6, base); + + davinci_pll_sysclk_register(dev, &pll1_sysclk8, base); + + davinci_pll_sysclk_register(dev, &pll1_sysclk9, base); + + davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base); + + davinci_pll_auxclk_register(dev, "pll1_auxclk", base); + + return 0; +} + +static const struct davinci_pll_clk_info dm646x_pll2_info = { + .name = "pll2", + .pllm_mask = GENMASK(4, 0), + .pllm_min = 14, + .pllm_max = 32, + .flags = 0, +}; + +SYSCLK(1, pll2_sysclk1, pll2_pllen, 4, 0); + +int dm646x_pll2_init(struct device *dev, void __iomem *base) +{ + davinci_pll_clk_register(dev, &dm646x_pll2_info, "oscin", base); + + davinci_pll_sysclk_register(dev, &pll2_sysclk1, base); + + return 0; +} diff --git a/drivers/clk/davinci/pll.c b/drivers/clk/davinci/pll.c new file mode 100644 index 000000000000..23a24c944f1d --- /dev/null +++ b/drivers/clk/davinci/pll.c @@ -0,0 +1,899 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PLL clock driver for TI Davinci SoCs + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + * + * Based on arch/arm/mach-davinci/clock.c + * Copyright (C) 2006-2007 Texas Instruments. + * Copyright (C) 2008-2009 Deep Root Systems, LLC + */ + +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/notifier.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_data/clk-davinci-pll.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "pll.h" + +#define MAX_NAME_SIZE 20 +#define OSCIN_CLK_NAME "oscin" + +#define REVID 0x000 +#define PLLCTL 0x100 +#define OCSEL 0x104 +#define PLLSECCTL 0x108 +#define PLLM 0x110 +#define PREDIV 0x114 +#define PLLDIV1 0x118 +#define PLLDIV2 0x11c +#define PLLDIV3 0x120 +#define OSCDIV 0x124 +#define POSTDIV 0x128 +#define BPDIV 0x12c +#define PLLCMD 0x138 +#define PLLSTAT 0x13c +#define ALNCTL 0x140 +#define DCHANGE 0x144 +#define CKEN 0x148 +#define CKSTAT 0x14c +#define SYSTAT 0x150 +#define PLLDIV4 0x160 +#define PLLDIV5 0x164 +#define PLLDIV6 0x168 +#define PLLDIV7 0x16c +#define PLLDIV8 0x170 +#define PLLDIV9 0x174 + +#define PLLCTL_PLLEN BIT(0) +#define PLLCTL_PLLPWRDN BIT(1) +#define PLLCTL_PLLRST BIT(3) +#define PLLCTL_PLLDIS BIT(4) +#define PLLCTL_PLLENSRC BIT(5) +#define PLLCTL_CLKMODE BIT(8) + +/* shared by most *DIV registers */ +#define DIV_RATIO_SHIFT 0 +#define DIV_RATIO_WIDTH 5 +#define DIV_ENABLE_SHIFT 15 + +#define PLLCMD_GOSET BIT(0) +#define PLLSTAT_GOSTAT BIT(0) + +#define CKEN_OBSCLK_SHIFT 1 +#define CKEN_AUXEN_SHIFT 0 + +/* + * OMAP-L138 system reference guide recommends a wait for 4 OSCIN/CLKIN + * cycles to ensure that the PLLC has switched to bypass mode. Delay of 1us + * ensures we are good for all > 4MHz OSCIN/CLKIN inputs. Typically the input + * is ~25MHz. Units are micro seconds. + */ +#define PLL_BYPASS_TIME 1 + +/* From OMAP-L138 datasheet table 6-4. Units are micro seconds */ +#define PLL_RESET_TIME 1 + +/* + * From OMAP-L138 datasheet table 6-4; assuming prediv = 1, sqrt(pllm) = 4 + * Units are micro seconds. + */ +#define PLL_LOCK_TIME 20 + +/** + * struct davinci_pll_clk - Main PLL clock (aka PLLOUT) + * @hw: clk_hw for the pll + * @base: Base memory address + * @pllm_min: The minimum allowable PLLM[PLLM] value + * @pllm_max: The maxiumum allowable PLLM[PLLM] value + * @pllm_mask: Bitmask for PLLM[PLLM] value + */ +struct davinci_pll_clk { + struct clk_hw hw; + void __iomem *base; + u32 pllm_min; + u32 pllm_max; + u32 pllm_mask; +}; + +#define to_davinci_pll_clk(_hw) \ + container_of((_hw), struct davinci_pll_clk, hw) + +static unsigned long davinci_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct davinci_pll_clk *pll = to_davinci_pll_clk(hw); + unsigned long rate = parent_rate; + u32 mult; + + mult = readl(pll->base + PLLM) & pll->pllm_mask; + rate *= mult + 1; + + return rate; +} + +static int davinci_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct davinci_pll_clk *pll = to_davinci_pll_clk(hw); + struct clk_hw *parent = req->best_parent_hw; + unsigned long parent_rate = req->best_parent_rate; + unsigned long rate = req->rate; + unsigned long best_rate, r; + u32 mult; + + /* there is a limited range of valid outputs (see datasheet) */ + if (rate < req->min_rate) + return -EINVAL; + + rate = min(rate, req->max_rate); + mult = rate / parent_rate; + best_rate = parent_rate * mult; + + /* easy case when there is no PREDIV */ + if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) { + if (best_rate < req->min_rate) + return -EINVAL; + + if (mult < pll->pllm_min || mult > pll->pllm_max) + return -EINVAL; + + req->rate = best_rate; + + return 0; + } + + /* see if the PREDIV clock can help us */ + best_rate = 0; + + for (mult = pll->pllm_min; mult <= pll->pllm_max; mult++) { + parent_rate = clk_hw_round_rate(parent, rate / mult); + r = parent_rate * mult; + if (r < req->min_rate) + continue; + if (r > rate || r > req->max_rate) + break; + if (r > best_rate) { + best_rate = r; + req->rate = best_rate; + req->best_parent_rate = parent_rate; + if (best_rate == rate) + break; + } + } + + return 0; +} + +static int davinci_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct davinci_pll_clk *pll = to_davinci_pll_clk(hw); + u32 mult; + + mult = rate / parent_rate; + writel(mult - 1, pll->base + PLLM); + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static int davinci_pll_debug_init(struct clk_hw *hw, struct dentry *dentry); +#else +#define davinci_pll_debug_init NULL +#endif + +static const struct clk_ops davinci_pll_ops = { + .recalc_rate = davinci_pll_recalc_rate, + .determine_rate = davinci_pll_determine_rate, + .set_rate = davinci_pll_set_rate, + .debug_init = davinci_pll_debug_init, +}; + +/* PLLM works differently on DM365 */ +static unsigned long dm365_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct davinci_pll_clk *pll = to_davinci_pll_clk(hw); + unsigned long rate = parent_rate; + u32 mult; + + mult = readl(pll->base + PLLM) & pll->pllm_mask; + rate *= mult * 2; + + return rate; +} + +static const struct clk_ops dm365_pll_ops = { + .recalc_rate = dm365_pll_recalc_rate, + .debug_init = davinci_pll_debug_init, +}; + +/** + * davinci_pll_div_register - common *DIV clock implementation + * @name: the clock name + * @parent_name: the parent clock name + * @reg: the *DIV register + * @fixed: if true, the divider is a fixed value + * @flags: bitmap of CLK_* flags from clock-provider.h + */ +static struct clk *davinci_pll_div_register(struct device *dev, + const char *name, + const char *parent_name, + void __iomem *reg, + bool fixed, u32 flags) +{ + const char * const *parent_names = parent_name ? &parent_name : NULL; + int num_parents = parent_name ? 1 : 0; + const struct clk_ops *divider_ops = &clk_divider_ops; + struct clk_gate *gate; + struct clk_divider *divider; + + gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->reg = reg; + gate->bit_idx = DIV_ENABLE_SHIFT; + + divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL); + if (!divider) + return ERR_PTR(-ENOMEM); + + divider->reg = reg; + divider->shift = DIV_RATIO_SHIFT; + divider->width = DIV_RATIO_WIDTH; + + if (fixed) { + divider->flags |= CLK_DIVIDER_READ_ONLY; + divider_ops = &clk_divider_ro_ops; + } + + return clk_register_composite(dev, name, parent_names, num_parents, + NULL, NULL, ÷r->hw, divider_ops, + &gate->hw, &clk_gate_ops, flags); +} + +struct davinci_pllen_clk { + struct clk_hw hw; + void __iomem *base; +}; + +#define to_davinci_pllen_clk(_hw) \ + container_of((_hw), struct davinci_pllen_clk, hw) + +static const struct clk_ops davinci_pllen_ops = { + /* this clocks just uses the clock notification feature */ +}; + +/* + * The PLL has to be switched into bypass mode while we are chaning the rate, + * so we do that on the PLLEN clock since it is the end of the line. This will + * switch to bypass before any of the parent clocks (PREDIV, PLL, POSTDIV) are + * changed and will switch back to the PLL after the changes have been made. + */ +static int davinci_pllen_rate_change(struct notifier_block *nb, + unsigned long flags, void *data) +{ + struct clk_notifier_data *cnd = data; + struct clk_hw *hw = __clk_get_hw(cnd->clk); + struct davinci_pllen_clk *pll = to_davinci_pllen_clk(hw); + u32 ctrl; + + ctrl = readl(pll->base + PLLCTL); + + if (flags == PRE_RATE_CHANGE) { + /* Switch the PLL to bypass mode */ + ctrl &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN); + writel(ctrl, pll->base + PLLCTL); + + udelay(PLL_BYPASS_TIME); + + /* Reset and enable PLL */ + ctrl &= ~(PLLCTL_PLLRST | PLLCTL_PLLDIS); + writel(ctrl, pll->base + PLLCTL); + } else { + udelay(PLL_RESET_TIME); + + /* Bring PLL out of reset */ + ctrl |= PLLCTL_PLLRST; + writel(ctrl, pll->base + PLLCTL); + + udelay(PLL_LOCK_TIME); + + /* Remove PLL from bypass mode */ + ctrl |= PLLCTL_PLLEN; + writel(ctrl, pll->base + PLLCTL); + } + + return NOTIFY_OK; +} + +static struct davinci_pll_platform_data *davinci_pll_get_pdata(struct device *dev) +{ + struct davinci_pll_platform_data *pdata = dev_get_platdata(dev); + + /* + * Platform data is optional, so allocate a new struct if one was not + * provided. For device tree, this will always be the case. + */ + if (!pdata) + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + /* for device tree, we need to fill in the struct */ + if (dev->of_node) + pdata->cfgchip = + syscon_regmap_lookup_by_compatible("ti,da830-cfgchip"); + + return pdata; +} + +static struct notifier_block davinci_pllen_notifier = { + .notifier_call = davinci_pllen_rate_change, +}; + +/** + * davinci_pll_clk_register - Register a PLL clock + * @info: The device-specific clock info + * @parent_name: The parent clock name + * @base: The PLL's memory region + * + * This creates a series of clocks that represent the PLL. + * + * OSCIN > [PREDIV >] PLL > [POSTDIV >] PLLEN + * + * - OSCIN is the parent clock (on secondary PLL, may come from primary PLL) + * - PREDIV and POSTDIV are optional (depends on the PLL controller) + * - PLL is the PLL output (aka PLLOUT) + * - PLLEN is the bypass multiplexer + * + * Returns: The PLLOUT clock or a negative error code. + */ +struct clk *davinci_pll_clk_register(struct device *dev, + const struct davinci_pll_clk_info *info, + const char *parent_name, + void __iomem *base) +{ + struct davinci_pll_platform_data *pdata; + char prediv_name[MAX_NAME_SIZE]; + char pllout_name[MAX_NAME_SIZE]; + char postdiv_name[MAX_NAME_SIZE]; + char pllen_name[MAX_NAME_SIZE]; + struct clk_init_data init; + struct davinci_pll_clk *pllout; + struct davinci_pllen_clk *pllen; + struct clk *pllout_clk, *clk; + + pdata = davinci_pll_get_pdata(dev); + if (!pdata) + return ERR_PTR(-ENOMEM); + + if (info->flags & PLL_HAS_CLKMODE) { + /* + * If a PLL has PLLCTL[CLKMODE], then it is the primary PLL. + * We register a clock named "oscin" that serves as the internal + * "input clock" domain shared by both PLLs (if there are 2) + * and will be the parent clock to the AUXCLK, SYSCLKBP and + * OBSCLK domains. NB: The various TRMs use "OSCIN" to mean + * a number of different things. In this driver we use it to + * mean the signal after the PLLCTL[CLKMODE] switch. + */ + clk = clk_register_fixed_factor(dev, OSCIN_CLK_NAME, + parent_name, 0, 1, 1); + if (IS_ERR(clk)) + return clk; + + parent_name = OSCIN_CLK_NAME; + } + + if (info->flags & PLL_HAS_PREDIV) { + bool fixed = info->flags & PLL_PREDIV_FIXED_DIV; + u32 flags = 0; + + snprintf(prediv_name, MAX_NAME_SIZE, "%s_prediv", info->name); + + if (info->flags & PLL_PREDIV_ALWAYS_ENABLED) + flags |= CLK_IS_CRITICAL; + + /* Some? DM355 chips don't correctly report the PREDIV value */ + if (info->flags & PLL_PREDIV_FIXED8) + clk = clk_register_fixed_factor(dev, prediv_name, + parent_name, flags, 1, 8); + else + clk = davinci_pll_div_register(dev, prediv_name, + parent_name, base + PREDIV, fixed, flags); + if (IS_ERR(clk)) + return clk; + + parent_name = prediv_name; + } + + /* Unlock writing to PLL registers */ + if (info->unlock_reg) { + if (IS_ERR_OR_NULL(pdata->cfgchip)) + dev_warn(dev, "Failed to get CFGCHIP (%ld)\n", + PTR_ERR(pdata->cfgchip)); + else + regmap_write_bits(pdata->cfgchip, info->unlock_reg, + info->unlock_mask, 0); + } + + pllout = devm_kzalloc(dev, sizeof(*pllout), GFP_KERNEL); + if (!pllout) + return ERR_PTR(-ENOMEM); + + snprintf(pllout_name, MAX_NAME_SIZE, "%s_pllout", info->name); + + init.name = pllout_name; + if (info->flags & PLL_PLLM_2X) + init.ops = &dm365_pll_ops; + else + init.ops = &davinci_pll_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = 0; + + if (info->flags & PLL_HAS_PREDIV) + init.flags |= CLK_SET_RATE_PARENT; + + pllout->hw.init = &init; + pllout->base = base; + pllout->pllm_mask = info->pllm_mask; + pllout->pllm_min = info->pllm_min; + pllout->pllm_max = info->pllm_max; + + pllout_clk = devm_clk_register(dev, &pllout->hw); + if (IS_ERR(pllout_clk)) + return pllout_clk; + + clk_hw_set_rate_range(&pllout->hw, info->pllout_min_rate, + info->pllout_max_rate); + + parent_name = pllout_name; + + if (info->flags & PLL_HAS_POSTDIV) { + bool fixed = info->flags & PLL_POSTDIV_FIXED_DIV; + u32 flags = CLK_SET_RATE_PARENT; + + snprintf(postdiv_name, MAX_NAME_SIZE, "%s_postdiv", info->name); + + if (info->flags & PLL_POSTDIV_ALWAYS_ENABLED) + flags |= CLK_IS_CRITICAL; + + clk = davinci_pll_div_register(dev, postdiv_name, parent_name, + base + POSTDIV, fixed, flags); + if (IS_ERR(clk)) + return clk; + + parent_name = postdiv_name; + } + + pllen = devm_kzalloc(dev, sizeof(*pllout), GFP_KERNEL); + if (!pllen) + return ERR_PTR(-ENOMEM); + + snprintf(pllen_name, MAX_NAME_SIZE, "%s_pllen", info->name); + + init.name = pllen_name; + init.ops = &davinci_pllen_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_PARENT; + + pllen->hw.init = &init; + pllen->base = base; + + clk = devm_clk_register(dev, &pllen->hw); + if (IS_ERR(clk)) + return clk; + + clk_notifier_register(clk, &davinci_pllen_notifier); + + return pllout_clk; +} + +/** + * davinci_pll_auxclk_register - Register bypass clock (AUXCLK) + * @name: The clock name + * @base: The PLL memory region + */ +struct clk *davinci_pll_auxclk_register(struct device *dev, + const char *name, + void __iomem *base) +{ + return clk_register_gate(dev, name, OSCIN_CLK_NAME, 0, base + CKEN, + CKEN_AUXEN_SHIFT, 0, NULL); +} + +/** + * davinci_pll_sysclkbp_clk_register - Register bypass divider clock (SYSCLKBP) + * @name: The clock name + * @base: The PLL memory region + */ +struct clk *davinci_pll_sysclkbp_clk_register(struct device *dev, + const char *name, + void __iomem *base) +{ + return clk_register_divider(dev, name, OSCIN_CLK_NAME, 0, base + BPDIV, + DIV_RATIO_SHIFT, DIV_RATIO_WIDTH, + CLK_DIVIDER_READ_ONLY, NULL); +} + +/** + * davinci_pll_obsclk_register - Register oscillator divider clock (OBSCLK) + * @info: The clock info + * @base: The PLL memory region + */ +struct clk * +davinci_pll_obsclk_register(struct device *dev, + const struct davinci_pll_obsclk_info *info, + void __iomem *base) +{ + struct clk_mux *mux; + struct clk_gate *gate; + struct clk_divider *divider; + u32 oscdiv; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = base + OCSEL; + mux->table = info->table; + mux->mask = info->ocsrc_mask; + + gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->reg = base + CKEN; + gate->bit_idx = CKEN_OBSCLK_SHIFT; + + divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL); + if (!divider) + return ERR_PTR(-ENOMEM); + + divider->reg = base + OSCDIV; + divider->shift = DIV_RATIO_SHIFT; + divider->width = DIV_RATIO_WIDTH; + + /* make sure divider is enabled just in case bootloader disabled it */ + oscdiv = readl(base + OSCDIV); + oscdiv |= BIT(DIV_ENABLE_SHIFT); + writel(oscdiv, base + OSCDIV); + + return clk_register_composite(dev, info->name, info->parent_names, + info->num_parents, + &mux->hw, &clk_mux_ops, + ÷r->hw, &clk_divider_ops, + &gate->hw, &clk_gate_ops, 0); +} + +/* The PLL SYSCLKn clocks have a mechanism for synchronizing rate changes. */ +static int davinci_pll_sysclk_rate_change(struct notifier_block *nb, + unsigned long flags, void *data) +{ + struct clk_notifier_data *cnd = data; + struct clk_hw *hw = __clk_get_hw(clk_get_parent(cnd->clk)); + struct davinci_pllen_clk *pll = to_davinci_pllen_clk(hw); + u32 pllcmd, pllstat; + + switch (flags) { + case POST_RATE_CHANGE: + /* apply the changes */ + pllcmd = readl(pll->base + PLLCMD); + pllcmd |= PLLCMD_GOSET; + writel(pllcmd, pll->base + PLLCMD); + /* fallthrough */ + case PRE_RATE_CHANGE: + /* Wait until for outstanding changes to take effect */ + do { + pllstat = readl(pll->base + PLLSTAT); + } while (pllstat & PLLSTAT_GOSTAT); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block davinci_pll_sysclk_notifier = { + .notifier_call = davinci_pll_sysclk_rate_change, +}; + +/** + * davinci_pll_sysclk_register - Register divider clocks (SYSCLKn) + * @info: The clock info + * @base: The PLL memory region + */ +struct clk * +davinci_pll_sysclk_register(struct device *dev, + const struct davinci_pll_sysclk_info *info, + void __iomem *base) +{ + const struct clk_ops *divider_ops = &clk_divider_ops; + struct clk_gate *gate; + struct clk_divider *divider; + struct clk *clk; + u32 reg; + u32 flags = 0; + + /* PLLDIVn registers are not entirely consecutive */ + if (info->id < 4) + reg = PLLDIV1 + 4 * (info->id - 1); + else + reg = PLLDIV4 + 4 * (info->id - 4); + + gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->reg = base + reg; + gate->bit_idx = DIV_ENABLE_SHIFT; + + divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL); + if (!divider) + return ERR_PTR(-ENOMEM); + + divider->reg = base + reg; + divider->shift = DIV_RATIO_SHIFT; + divider->width = info->ratio_width; + divider->flags = 0; + + if (info->flags & SYSCLK_FIXED_DIV) { + divider->flags |= CLK_DIVIDER_READ_ONLY; + divider_ops = &clk_divider_ro_ops; + } + + /* Only the ARM clock can change the parent PLL rate */ + if (info->flags & SYSCLK_ARM_RATE) + flags |= CLK_SET_RATE_PARENT; + + if (info->flags & SYSCLK_ALWAYS_ENABLED) + flags |= CLK_IS_CRITICAL; + + clk = clk_register_composite(dev, info->name, &info->parent_name, 1, + NULL, NULL, ÷r->hw, divider_ops, + &gate->hw, &clk_gate_ops, flags); + if (IS_ERR(clk)) + return clk; + + clk_notifier_register(clk, &davinci_pll_sysclk_notifier); + + return clk; +} + +int of_davinci_pll_init(struct device *dev, + const struct davinci_pll_clk_info *info, + const struct davinci_pll_obsclk_info *obsclk_info, + const struct davinci_pll_sysclk_info **div_info, + u8 max_sysclk_id, + void __iomem *base) +{ + struct device_node *node = dev->of_node; + struct device_node *child; + const char *parent_name; + struct clk *clk; + + if (info->flags & PLL_HAS_CLKMODE) + parent_name = of_clk_get_parent_name(node, 0); + else + parent_name = OSCIN_CLK_NAME; + + clk = davinci_pll_clk_register(dev, info, parent_name, base); + if (IS_ERR(clk)) { + dev_err(dev, "failed to register %s\n", info->name); + return PTR_ERR(clk); + } + + child = of_get_child_by_name(node, "pllout"); + if (of_device_is_available(child)) + of_clk_add_provider(child, of_clk_src_simple_get, clk); + of_node_put(child); + + child = of_get_child_by_name(node, "sysclk"); + if (of_device_is_available(child)) { + struct clk_onecell_data *clk_data; + struct clk **clks; + int n_clks = max_sysclk_id + 1; + int i; + + clk_data = devm_kzalloc(dev, sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clks = devm_kmalloc_array(dev, n_clks, sizeof(*clks), GFP_KERNEL); + if (!clks) + return -ENOMEM; + + clk_data->clks = clks; + clk_data->clk_num = n_clks; + + for (i = 0; i < n_clks; i++) + clks[i] = ERR_PTR(-ENOENT); + + for (; *div_info; div_info++) { + clk = davinci_pll_sysclk_register(dev, *div_info, base); + if (IS_ERR(clk)) + dev_warn(dev, "failed to register %s (%ld)\n", + (*div_info)->name, PTR_ERR(clk)); + else + clks[(*div_info)->id] = clk; + } + of_clk_add_provider(child, of_clk_src_onecell_get, clk_data); + } + of_node_put(child); + + child = of_get_child_by_name(node, "auxclk"); + if (of_device_is_available(child)) { + char child_name[MAX_NAME_SIZE]; + + snprintf(child_name, MAX_NAME_SIZE, "%s_auxclk", info->name); + + clk = davinci_pll_auxclk_register(dev, child_name, base); + if (IS_ERR(clk)) + dev_warn(dev, "failed to register %s (%ld)\n", + child_name, PTR_ERR(clk)); + else + of_clk_add_provider(child, of_clk_src_simple_get, clk); + } + of_node_put(child); + + child = of_get_child_by_name(node, "obsclk"); + if (of_device_is_available(child)) { + if (obsclk_info) + clk = davinci_pll_obsclk_register(dev, obsclk_info, base); + else + clk = ERR_PTR(-EINVAL); + + if (IS_ERR(clk)) + dev_warn(dev, "failed to register obsclk (%ld)\n", + PTR_ERR(clk)); + else + of_clk_add_provider(child, of_clk_src_simple_get, clk); + } + of_node_put(child); + + return 0; +} + +static const struct of_device_id davinci_pll_of_match[] = { + { .compatible = "ti,da850-pll0", .data = of_da850_pll0_init }, + { .compatible = "ti,da850-pll1", .data = of_da850_pll1_init }, + { } +}; + +static const struct platform_device_id davinci_pll_id_table[] = { + { .name = "da830-pll", .driver_data = (kernel_ulong_t)da830_pll_init }, + { .name = "da850-pll0", .driver_data = (kernel_ulong_t)da850_pll0_init }, + { .name = "da850-pll1", .driver_data = (kernel_ulong_t)da850_pll1_init }, + { .name = "dm355-pll1", .driver_data = (kernel_ulong_t)dm355_pll1_init }, + { .name = "dm355-pll2", .driver_data = (kernel_ulong_t)dm355_pll2_init }, + { .name = "dm365-pll1", .driver_data = (kernel_ulong_t)dm365_pll1_init }, + { .name = "dm365-pll2", .driver_data = (kernel_ulong_t)dm365_pll2_init }, + { .name = "dm644x-pll1", .driver_data = (kernel_ulong_t)dm644x_pll1_init }, + { .name = "dm644x-pll2", .driver_data = (kernel_ulong_t)dm644x_pll2_init }, + { .name = "dm646x-pll1", .driver_data = (kernel_ulong_t)dm646x_pll1_init }, + { .name = "dm646x-pll2", .driver_data = (kernel_ulong_t)dm646x_pll2_init }, + { } +}; + +typedef int (*davinci_pll_init)(struct device *dev, void __iomem *base); + +static int davinci_pll_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *of_id; + davinci_pll_init pll_init = NULL; + struct resource *res; + void __iomem *base; + + of_id = of_match_device(davinci_pll_of_match, dev); + if (of_id) + pll_init = of_id->data; + else if (pdev->id_entry) + pll_init = (void *)pdev->id_entry->driver_data; + + if (!pll_init) { + dev_err(dev, "unable to find driver data\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + return pll_init(dev, base); +} + +static struct platform_driver davinci_pll_driver = { + .probe = davinci_pll_probe, + .driver = { + .name = "davinci-pll-clk", + .of_match_table = davinci_pll_of_match, + }, + .id_table = davinci_pll_id_table, +}; + +static int __init davinci_pll_driver_init(void) +{ + return platform_driver_register(&davinci_pll_driver); +} + +/* has to be postcore_initcall because PSC devices depend on PLL parent clocks */ +postcore_initcall(davinci_pll_driver_init); + +#ifdef CONFIG_DEBUG_FS +#include <linux/debugfs.h> + +#define DEBUG_REG(n) \ +{ \ + .name = #n, \ + .offset = n, \ +} + +static const struct debugfs_reg32 davinci_pll_regs[] = { + DEBUG_REG(REVID), + DEBUG_REG(PLLCTL), + DEBUG_REG(OCSEL), + DEBUG_REG(PLLSECCTL), + DEBUG_REG(PLLM), + DEBUG_REG(PREDIV), + DEBUG_REG(PLLDIV1), + DEBUG_REG(PLLDIV2), + DEBUG_REG(PLLDIV3), + DEBUG_REG(OSCDIV), + DEBUG_REG(POSTDIV), + DEBUG_REG(BPDIV), + DEBUG_REG(PLLCMD), + DEBUG_REG(PLLSTAT), + DEBUG_REG(ALNCTL), + DEBUG_REG(DCHANGE), + DEBUG_REG(CKEN), + DEBUG_REG(CKSTAT), + DEBUG_REG(SYSTAT), + DEBUG_REG(PLLDIV4), + DEBUG_REG(PLLDIV5), + DEBUG_REG(PLLDIV6), + DEBUG_REG(PLLDIV7), + DEBUG_REG(PLLDIV8), + DEBUG_REG(PLLDIV9), +}; + +static int davinci_pll_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + struct davinci_pll_clk *pll = to_davinci_pll_clk(hw); + struct debugfs_regset32 *regset; + struct dentry *d; + + regset = kzalloc(sizeof(*regset), GFP_KERNEL); + if (!regset) + return -ENOMEM; + + regset->regs = davinci_pll_regs; + regset->nregs = ARRAY_SIZE(davinci_pll_regs); + regset->base = pll->base; + + d = debugfs_create_regset32("registers", 0400, dentry, regset); + if (IS_ERR(d)) { + kfree(regset); + return PTR_ERR(d); + } + + return 0; +} +#endif diff --git a/drivers/clk/davinci/pll.h b/drivers/clk/davinci/pll.h new file mode 100644 index 000000000000..b1b6fb23f972 --- /dev/null +++ b/drivers/clk/davinci/pll.h @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Clock driver for TI Davinci PSC controllers + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#ifndef __CLK_DAVINCI_PLL_H___ +#define __CLK_DAVINCI_PLL_H___ + +#include <linux/bitops.h> +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/types.h> + +#define PLL_HAS_CLKMODE BIT(0) /* PLL has PLLCTL[CLKMODE] */ +#define PLL_HAS_PREDIV BIT(1) /* has prediv before PLL */ +#define PLL_PREDIV_ALWAYS_ENABLED BIT(2) /* don't clear DEN bit */ +#define PLL_PREDIV_FIXED_DIV BIT(3) /* fixed divider value */ +#define PLL_HAS_POSTDIV BIT(4) /* has postdiv after PLL */ +#define PLL_POSTDIV_ALWAYS_ENABLED BIT(5) /* don't clear DEN bit */ +#define PLL_POSTDIV_FIXED_DIV BIT(6) /* fixed divider value */ +#define PLL_HAS_EXTCLKSRC BIT(7) /* has selectable bypass */ +#define PLL_PLLM_2X BIT(8) /* PLLM value is 2x (DM365) */ +#define PLL_PREDIV_FIXED8 BIT(9) /* DM355 quirk */ + +/** davinci_pll_clk_info - controller-specific PLL info + * @name: The name of the PLL + * @unlock_reg: Option CFGCHIP register for unlocking PLL + * @unlock_mask: Bitmask used with @unlock_reg + * @pllm_mask: Bitmask for PLLM[PLLM] value + * @pllm_min: Minimum allowable value for PLLM[PLLM] + * @pllm_max: Maximum allowable value for PLLM[PLLM] + * @pllout_min_rate: Minimum allowable rate for PLLOUT + * @pllout_max_rate: Maximum allowable rate for PLLOUT + * @flags: Bitmap of PLL_* flags. + */ +struct davinci_pll_clk_info { + const char *name; + u32 unlock_reg; + u32 unlock_mask; + u32 pllm_mask; + u32 pllm_min; + u32 pllm_max; + unsigned long pllout_min_rate; + unsigned long pllout_max_rate; + u32 flags; +}; + +#define SYSCLK_ARM_RATE BIT(0) /* Controls ARM rate */ +#define SYSCLK_ALWAYS_ENABLED BIT(1) /* Or bad things happen */ +#define SYSCLK_FIXED_DIV BIT(2) /* Fixed divider */ + +/** davinci_pll_sysclk_info - SYSCLKn-specific info + * @name: The name of the clock + * @parent_name: The name of the parent clock + * @id: "n" in "SYSCLKn" + * @ratio_width: Width (in bits) of RATIO in PLLDIVn register + * @flags: Bitmap of SYSCLK_* flags. + */ +struct davinci_pll_sysclk_info { + const char *name; + const char *parent_name; + u32 id; + u32 ratio_width; + u32 flags; +}; + +#define SYSCLK(i, n, p, w, f) \ +static const struct davinci_pll_sysclk_info n = { \ + .name = #n, \ + .parent_name = #p, \ + .id = (i), \ + .ratio_width = (w), \ + .flags = (f), \ +} + +/** davinci_pll_obsclk_info - OBSCLK-specific info + * @name: The name of the clock + * @parent_names: Array of names of the parent clocks + * @num_parents: Length of @parent_names + * @table: Array of values to write to OCSEL[OCSRC] cooresponding to + * @parent_names + * @ocsrc_mask: Bitmask for OCSEL[OCSRC] + */ +struct davinci_pll_obsclk_info { + const char *name; + const char * const *parent_names; + u8 num_parents; + u32 *table; + u32 ocsrc_mask; +}; + +struct clk *davinci_pll_clk_register(struct device *dev, + const struct davinci_pll_clk_info *info, + const char *parent_name, + void __iomem *base); +struct clk *davinci_pll_auxclk_register(struct device *dev, + const char *name, + void __iomem *base); +struct clk *davinci_pll_sysclkbp_clk_register(struct device *dev, + const char *name, + void __iomem *base); +struct clk * +davinci_pll_obsclk_register(struct device *dev, + const struct davinci_pll_obsclk_info *info, + void __iomem *base); +struct clk * +davinci_pll_sysclk_register(struct device *dev, + const struct davinci_pll_sysclk_info *info, + void __iomem *base); + +int of_davinci_pll_init(struct device *dev, + const struct davinci_pll_clk_info *info, + const struct davinci_pll_obsclk_info *obsclk_info, + const struct davinci_pll_sysclk_info **div_info, + u8 max_sysclk_id, + void __iomem *base); + +/* Platform-specific callbacks */ + +int da830_pll_init(struct device *dev, void __iomem *base); + +int da850_pll0_init(struct device *dev, void __iomem *base); +int da850_pll1_init(struct device *dev, void __iomem *base); +int of_da850_pll0_init(struct device *dev, void __iomem *base); +int of_da850_pll1_init(struct device *dev, void __iomem *base); + +int dm355_pll1_init(struct device *dev, void __iomem *base); +int dm355_pll2_init(struct device *dev, void __iomem *base); + +int dm365_pll1_init(struct device *dev, void __iomem *base); +int dm365_pll2_init(struct device *dev, void __iomem *base); + +int dm644x_pll1_init(struct device *dev, void __iomem *base); +int dm644x_pll2_init(struct device *dev, void __iomem *base); + +int dm646x_pll1_init(struct device *dev, void __iomem *base); +int dm646x_pll2_init(struct device *dev, void __iomem *base); + +#endif /* __CLK_DAVINCI_PLL_H___ */ diff --git a/drivers/clk/davinci/psc-da830.c b/drivers/clk/davinci/psc-da830.c new file mode 100644 index 000000000000..f61abf5632ff --- /dev/null +++ b/drivers/clk/davinci/psc-da830.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PSC clock descriptions for TI DA830/OMAP-L137/AM17XX + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include "psc.h" + +LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0"); +LPSC_CLKDEV1(mmcsd_clkdev, NULL, "da830-mmc.0"); +LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0"); + +static const struct davinci_lpsc_clk_info da830_psc0_info[] = { + LPSC(0, 0, tpcc, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(1, 0, tptc0, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(2, 0, tptc1, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(3, 0, aemif, pll0_sysclk3, NULL, LPSC_ALWAYS_ENABLED), + LPSC(4, 0, spi0, pll0_sysclk2, spi0_clkdev, 0), + LPSC(5, 0, mmcsd, pll0_sysclk2, mmcsd_clkdev, 0), + LPSC(6, 0, aintc, pll0_sysclk4, NULL, LPSC_ALWAYS_ENABLED), + LPSC(7, 0, arm_rom, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(8, 0, secu_mgr, pll0_sysclk4, NULL, LPSC_ALWAYS_ENABLED), + LPSC(9, 0, uart0, pll0_sysclk2, uart0_clkdev, 0), + LPSC(10, 0, scr0_ss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(11, 0, scr1_ss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(12, 0, scr2_ss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(13, 0, pruss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(14, 0, arm, pll0_sysclk6, NULL, LPSC_ALWAYS_ENABLED), + { } +}; + +static int da830_psc0_init(struct device *dev, void __iomem *base) +{ + return davinci_psc_register_clocks(dev, da830_psc0_info, 16, base); +} + +static struct clk_bulk_data da830_psc0_parent_clks[] = { + { .id = "pll0_sysclk2" }, + { .id = "pll0_sysclk3" }, + { .id = "pll0_sysclk4" }, + { .id = "pll0_sysclk6" }, +}; + +const struct davinci_psc_init_data da830_psc0_init_data = { + .parent_clks = da830_psc0_parent_clks, + .num_parent_clks = ARRAY_SIZE(da830_psc0_parent_clks), + .psc_init = &da830_psc0_init, +}; + +LPSC_CLKDEV2(usb0_clkdev, NULL, "musb-da8xx", + NULL, "cppi41-dmaengine"); +LPSC_CLKDEV1(usb1_clkdev, NULL, "ohci-da8xx"); +/* REVISIT: gpio-davinci.c should be modified to drop con_id */ +LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL); +LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1", + "fck", "davinci_mdio.0"); +LPSC_CLKDEV1(mcasp0_clkdev, NULL, "davinci-mcasp.0"); +LPSC_CLKDEV1(mcasp1_clkdev, NULL, "davinci-mcasp.1"); +LPSC_CLKDEV1(mcasp2_clkdev, NULL, "davinci-mcasp.2"); +LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1"); +LPSC_CLKDEV1(i2c1_clkdev, NULL, "i2c_davinci.2"); +LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1"); +LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2"); +LPSC_CLKDEV1(lcdc_clkdev, "fck", "da8xx_lcdc.0"); +LPSC_CLKDEV2(pwm_clkdev, "fck", "ehrpwm.0", + "fck", "ehrpwm.1"); +LPSC_CLKDEV3(ecap_clkdev, "fck", "ecap.0", + "fck", "ecap.1", + "fck", "ecap.2"); +LPSC_CLKDEV2(eqep_clkdev, NULL, "eqep.0", + NULL, "eqep.1"); + +static const struct davinci_lpsc_clk_info da830_psc1_info[] = { + LPSC(1, 0, usb0, pll0_sysclk2, usb0_clkdev, 0), + LPSC(2, 0, usb1, pll0_sysclk4, usb1_clkdev, 0), + LPSC(3, 0, gpio, pll0_sysclk4, gpio_clkdev, 0), + LPSC(5, 0, emac, pll0_sysclk4, emac_clkdev, 0), + LPSC(6, 0, emif3, pll0_sysclk5, NULL, LPSC_ALWAYS_ENABLED), + LPSC(7, 0, mcasp0, pll0_sysclk2, mcasp0_clkdev, 0), + LPSC(8, 0, mcasp1, pll0_sysclk2, mcasp1_clkdev, 0), + LPSC(9, 0, mcasp2, pll0_sysclk2, mcasp2_clkdev, 0), + LPSC(10, 0, spi1, pll0_sysclk2, spi1_clkdev, 0), + LPSC(11, 0, i2c1, pll0_sysclk4, i2c1_clkdev, 0), + LPSC(12, 0, uart1, pll0_sysclk2, uart1_clkdev, 0), + LPSC(13, 0, uart2, pll0_sysclk2, uart2_clkdev, 0), + LPSC(16, 0, lcdc, pll0_sysclk2, lcdc_clkdev, 0), + LPSC(17, 0, pwm, pll0_sysclk2, pwm_clkdev, 0), + LPSC(20, 0, ecap, pll0_sysclk2, ecap_clkdev, 0), + LPSC(21, 0, eqep, pll0_sysclk2, eqep_clkdev, 0), + { } +}; + +static int da830_psc1_init(struct device *dev, void __iomem *base) +{ + return davinci_psc_register_clocks(dev, da830_psc1_info, 32, base); +} + +static struct clk_bulk_data da830_psc1_parent_clks[] = { + { .id = "pll0_sysclk2" }, + { .id = "pll0_sysclk4" }, + { .id = "pll0_sysclk5" }, +}; + +const struct davinci_psc_init_data da830_psc1_init_data = { + .parent_clks = da830_psc1_parent_clks, + .num_parent_clks = ARRAY_SIZE(da830_psc1_parent_clks), + .psc_init = &da830_psc1_init, +}; diff --git a/drivers/clk/davinci/psc-da850.c b/drivers/clk/davinci/psc-da850.c new file mode 100644 index 000000000000..ccc7eb17bf3a --- /dev/null +++ b/drivers/clk/davinci/psc-da850.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PSC clock descriptions for TI DA850/OMAP-L138/AM18XX + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/types.h> + +#include "psc.h" + +LPSC_CLKDEV2(emifa_clkdev, NULL, "ti-aemif", + "aemif", "davinci_nand.0"); +LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0"); +LPSC_CLKDEV1(mmcsd0_clkdev, NULL, "da830-mmc.0"); +LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0"); +/* REVISIT: used dev_id instead of con_id */ +LPSC_CLKDEV1(arm_clkdev, "arm", NULL); +LPSC_CLKDEV1(dsp_clkdev, NULL, "davinci-rproc.0"); + +static const struct davinci_lpsc_clk_info da850_psc0_info[] = { + LPSC(0, 0, tpcc0, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(1, 0, tptc0, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(2, 0, tptc1, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(3, 0, emifa, async1, emifa_clkdev, 0), + LPSC(4, 0, spi0, pll0_sysclk2, spi0_clkdev, 0), + LPSC(5, 0, mmcsd0, pll0_sysclk2, mmcsd0_clkdev, 0), + LPSC(6, 0, aintc, pll0_sysclk4, NULL, LPSC_ALWAYS_ENABLED), + LPSC(7, 0, arm_rom, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(9, 0, uart0, pll0_sysclk2, uart0_clkdev, 0), + LPSC(13, 0, pruss, pll0_sysclk2, NULL, 0), + LPSC(14, 0, arm, pll0_sysclk6, arm_clkdev, LPSC_ALWAYS_ENABLED | LPSC_SET_RATE_PARENT), + LPSC(15, 1, dsp, pll0_sysclk1, dsp_clkdev, LPSC_FORCE | LPSC_LOCAL_RESET), + { } +}; + +LPSC_CLKDEV3(usb0_clkdev, "fck", "da830-usb-phy-clks", + NULL, "musb-da8xx", + NULL, "cppi41-dmaengine"); +LPSC_CLKDEV1(usb1_clkdev, NULL, "ohci-da8xx"); +/* REVISIT: gpio-davinci.c should be modified to drop con_id */ +LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL); +LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1", + "fck", "davinci_mdio.0"); +LPSC_CLKDEV1(mcasp0_clkdev, NULL, "davinci-mcasp.0"); +LPSC_CLKDEV1(sata_clkdev, "fck", "ahci_da850"); +LPSC_CLKDEV1(vpif_clkdev, NULL, "vpif"); +LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1"); +LPSC_CLKDEV1(i2c1_clkdev, NULL, "i2c_davinci.2"); +LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1"); +LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2"); +LPSC_CLKDEV1(mcbsp0_clkdev, NULL, "davinci-mcbsp.0"); +LPSC_CLKDEV1(mcbsp1_clkdev, NULL, "davinci-mcbsp.1"); +LPSC_CLKDEV1(lcdc_clkdev, "fck", "da8xx_lcdc.0"); +LPSC_CLKDEV3(ehrpwm_clkdev, "fck", "ehrpwm.0", + "fck", "ehrpwm.1", + NULL, "da830-tbclksync"); +LPSC_CLKDEV1(mmcsd1_clkdev, NULL, "da830-mmc.1"); +LPSC_CLKDEV3(ecap_clkdev, "fck", "ecap.0", + "fck", "ecap.1", + "fck", "ecap.2"); + +static int da850_psc0_init(struct device *dev, void __iomem *base) +{ + return davinci_psc_register_clocks(dev, da850_psc0_info, 16, base); +} + +static int of_da850_psc0_init(struct device *dev, void __iomem *base) +{ + return of_davinci_psc_clk_init(dev, da850_psc0_info, 16, base); +} + +static struct clk_bulk_data da850_psc0_parent_clks[] = { + { .id = "pll0_sysclk1" }, + { .id = "pll0_sysclk2" }, + { .id = "pll0_sysclk4" }, + { .id = "pll0_sysclk6" }, + { .id = "async1" }, +}; + +const struct davinci_psc_init_data da850_psc0_init_data = { + .parent_clks = da850_psc0_parent_clks, + .num_parent_clks = ARRAY_SIZE(da850_psc0_parent_clks), + .psc_init = &da850_psc0_init, +}; + +const struct davinci_psc_init_data of_da850_psc0_init_data = { + .parent_clks = da850_psc0_parent_clks, + .num_parent_clks = ARRAY_SIZE(da850_psc0_parent_clks), + .psc_init = &of_da850_psc0_init, +}; + +static const struct davinci_lpsc_clk_info da850_psc1_info[] = { + LPSC(0, 0, tpcc1, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(1, 0, usb0, pll0_sysclk2, usb0_clkdev, 0), + LPSC(2, 0, usb1, pll0_sysclk4, usb1_clkdev, 0), + LPSC(3, 0, gpio, pll0_sysclk4, gpio_clkdev, 0), + LPSC(5, 0, emac, pll0_sysclk4, emac_clkdev, 0), + LPSC(6, 0, ddr, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(7, 0, mcasp0, async3, mcasp0_clkdev, 0), + LPSC(8, 0, sata, pll0_sysclk2, sata_clkdev, LPSC_FORCE), + LPSC(9, 0, vpif, pll0_sysclk2, vpif_clkdev, 0), + LPSC(10, 0, spi1, async3, spi1_clkdev, 0), + LPSC(11, 0, i2c1, pll0_sysclk4, i2c1_clkdev, 0), + LPSC(12, 0, uart1, async3, uart1_clkdev, 0), + LPSC(13, 0, uart2, async3, uart2_clkdev, 0), + LPSC(14, 0, mcbsp0, async3, mcbsp0_clkdev, 0), + LPSC(15, 0, mcbsp1, async3, mcbsp1_clkdev, 0), + LPSC(16, 0, lcdc, pll0_sysclk2, lcdc_clkdev, 0), + LPSC(17, 0, ehrpwm, async3, ehrpwm_clkdev, 0), + LPSC(18, 0, mmcsd1, pll0_sysclk2, mmcsd1_clkdev, 0), + LPSC(20, 0, ecap, async3, ecap_clkdev, 0), + LPSC(21, 0, tptc2, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + { } +}; + +static int da850_psc1_init(struct device *dev, void __iomem *base) +{ + return davinci_psc_register_clocks(dev, da850_psc1_info, 32, base); +} + +static int of_da850_psc1_init(struct device *dev, void __iomem *base) +{ + return of_davinci_psc_clk_init(dev, da850_psc1_info, 32, base); +} + +static struct clk_bulk_data da850_psc1_parent_clks[] = { + { .id = "pll0_sysclk2" }, + { .id = "pll0_sysclk4" }, + { .id = "async3" }, +}; + +const struct davinci_psc_init_data da850_psc1_init_data = { + .parent_clks = da850_psc1_parent_clks, + .num_parent_clks = ARRAY_SIZE(da850_psc1_parent_clks), + .psc_init = &da850_psc1_init, +}; + +const struct davinci_psc_init_data of_da850_psc1_init_data = { + .parent_clks = da850_psc1_parent_clks, + .num_parent_clks = ARRAY_SIZE(da850_psc1_parent_clks), + .psc_init = &of_da850_psc1_init, +}; diff --git a/drivers/clk/davinci/psc-dm355.c b/drivers/clk/davinci/psc-dm355.c new file mode 100644 index 000000000000..6995ecea2677 --- /dev/null +++ b/drivers/clk/davinci/psc-dm355.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PSC clock descriptions for TI DaVinci DM355 + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include "psc.h" + +LPSC_CLKDEV1(vpss_master_clkdev, "master", "vpss"); +LPSC_CLKDEV1(vpss_slave_clkdev, "slave", "vpss"); +LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1"); +LPSC_CLKDEV1(mmcsd1_clkdev, NULL, "dm6441-mmc.1"); +LPSC_CLKDEV1(mcbsp1_clkdev, NULL, "davinci-mcbsp.1"); +LPSC_CLKDEV1(usb_clkdev, "usb", NULL); +LPSC_CLKDEV1(spi2_clkdev, NULL, "spi_davinci.2"); +LPSC_CLKDEV1(aemif_clkdev, "aemif", NULL); +LPSC_CLKDEV1(mmcsd0_clkdev, NULL, "dm6441-mmc.0"); +LPSC_CLKDEV1(mcbsp0_clkdev, NULL, "davinci-mcbsp.0"); +LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1"); +LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0"); +LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1"); +LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2"); +LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0"); +/* REVISIT: gpio-davinci.c should be modified to drop con_id */ +LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL); +LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL); +LPSC_CLKDEV1(timer2_clkdev, NULL, "davinci-wdt"); +LPSC_CLKDEV1(vpss_dac_clkdev, "vpss_dac", NULL); + +static const struct davinci_lpsc_clk_info dm355_psc_info[] = { + LPSC(0, 0, vpss_master, pll1_sysclk4, vpss_master_clkdev, 0), + LPSC(1, 0, vpss_slave, pll1_sysclk4, vpss_slave_clkdev, 0), + LPSC(5, 0, timer3, pll1_auxclk, NULL, 0), + LPSC(6, 0, spi1, pll1_sysclk2, spi1_clkdev, 0), + LPSC(7, 0, mmcsd1, pll1_sysclk2, mmcsd1_clkdev, 0), + LPSC(8, 0, asp1, pll1_sysclk2, NULL, 0), + LPSC(9, 0, usb, pll1_sysclk2, usb_clkdev, 0), + LPSC(10, 0, pwm3, pll1_auxclk, NULL, 0), + LPSC(11, 0, spi2, pll1_sysclk2, spi2_clkdev, 0), + LPSC(12, 0, rto, pll1_auxclk, NULL, 0), + LPSC(14, 0, aemif, pll1_sysclk2, aemif_clkdev, 0), + LPSC(15, 0, mmcsd0, pll1_sysclk2, mmcsd0_clkdev, 0), + LPSC(17, 0, asp0, pll1_sysclk2, NULL, 0), + LPSC(18, 0, i2c, pll1_auxclk, i2c_clkdev, 0), + LPSC(19, 0, uart0, pll1_auxclk, uart0_clkdev, 0), + LPSC(20, 0, uart1, pll1_auxclk, uart1_clkdev, 0), + LPSC(21, 0, uart2, pll1_sysclk2, uart2_clkdev, 0), + LPSC(22, 0, spi0, pll1_sysclk2, spi0_clkdev, 0), + LPSC(23, 0, pwm0, pll1_auxclk, NULL, 0), + LPSC(24, 0, pwm1, pll1_auxclk, NULL, 0), + LPSC(25, 0, pwm2, pll1_auxclk, NULL, 0), + LPSC(26, 0, gpio, pll1_sysclk2, gpio_clkdev, 0), + LPSC(27, 0, timer0, pll1_auxclk, timer0_clkdev, LPSC_ALWAYS_ENABLED), + LPSC(28, 0, timer1, pll1_auxclk, NULL, 0), + /* REVISIT: why can't this be disabled? */ + LPSC(29, 0, timer2, pll1_auxclk, timer2_clkdev, LPSC_ALWAYS_ENABLED), + LPSC(31, 0, arm, pll1_sysclk1, NULL, LPSC_ALWAYS_ENABLED), + LPSC(40, 0, mjcp, pll1_sysclk1, NULL, 0), + LPSC(41, 0, vpss_dac, pll1_sysclk3, vpss_dac_clkdev, 0), + { } +}; + +static int dm355_psc_init(struct device *dev, void __iomem *base) +{ + return davinci_psc_register_clocks(dev, dm355_psc_info, 42, base); +} + +static struct clk_bulk_data dm355_psc_parent_clks[] = { + { .id = "pll1_sysclk1" }, + { .id = "pll1_sysclk2" }, + { .id = "pll1_sysclk3" }, + { .id = "pll1_sysclk4" }, + { .id = "pll1_auxclk" }, +}; + +const struct davinci_psc_init_data dm355_psc_init_data = { + .parent_clks = dm355_psc_parent_clks, + .num_parent_clks = ARRAY_SIZE(dm355_psc_parent_clks), + .psc_init = &dm355_psc_init, +}; diff --git a/drivers/clk/davinci/psc-dm365.c b/drivers/clk/davinci/psc-dm365.c new file mode 100644 index 000000000000..3ad915f37376 --- /dev/null +++ b/drivers/clk/davinci/psc-dm365.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PSC clock descriptions for TI DaVinci DM365 + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include "psc.h" + +LPSC_CLKDEV1(vpss_slave_clkdev, "slave", "vpss"); +LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1"); +LPSC_CLKDEV1(mmcsd1_clkdev, NULL, "da830-mmc.1"); +LPSC_CLKDEV1(asp0_clkdev, NULL, "davinci-mcbsp"); +LPSC_CLKDEV1(usb_clkdev, "usb", NULL); +LPSC_CLKDEV1(spi2_clkdev, NULL, "spi_davinci.2"); +LPSC_CLKDEV1(aemif_clkdev, "aemif", NULL); +LPSC_CLKDEV1(mmcsd0_clkdev, NULL, "da830-mmc.0"); +LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1"); +LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0"); +LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1"); +LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0"); +/* REVISIT: gpio-davinci.c should be modified to drop con_id */ +LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL); +LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL); +LPSC_CLKDEV1(timer2_clkdev, NULL, "davinci-wdt"); +LPSC_CLKDEV1(spi3_clkdev, NULL, "spi_davinci.3"); +LPSC_CLKDEV1(spi4_clkdev, NULL, "spi_davinci.4"); +LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1", + "fck", "davinci_mdio.0"); +LPSC_CLKDEV1(voice_codec_clkdev, NULL, "davinci_voicecodec"); +LPSC_CLKDEV1(vpss_dac_clkdev, "vpss_dac", NULL); +LPSC_CLKDEV1(vpss_master_clkdev, "master", "vpss"); + +static const struct davinci_lpsc_clk_info dm365_psc_info[] = { + LPSC(1, 0, vpss_slave, pll1_sysclk5, vpss_slave_clkdev, 0), + LPSC(5, 0, timer3, pll1_auxclk, NULL, 0), + LPSC(6, 0, spi1, pll1_sysclk4, spi1_clkdev, 0), + LPSC(7, 0, mmcsd1, pll1_sysclk4, mmcsd1_clkdev, 0), + LPSC(8, 0, asp0, pll1_sysclk4, asp0_clkdev, 0), + LPSC(9, 0, usb, pll1_auxclk, usb_clkdev, 0), + LPSC(10, 0, pwm3, pll1_auxclk, NULL, 0), + LPSC(11, 0, spi2, pll1_sysclk4, spi2_clkdev, 0), + LPSC(12, 0, rto, pll1_sysclk4, NULL, 0), + LPSC(14, 0, aemif, pll1_sysclk4, aemif_clkdev, 0), + LPSC(15, 0, mmcsd0, pll1_sysclk8, mmcsd0_clkdev, 0), + LPSC(18, 0, i2c, pll1_auxclk, i2c_clkdev, 0), + LPSC(19, 0, uart0, pll1_auxclk, uart0_clkdev, 0), + LPSC(20, 0, uart1, pll1_sysclk4, uart1_clkdev, 0), + LPSC(22, 0, spi0, pll1_sysclk4, spi0_clkdev, 0), + LPSC(23, 0, pwm0, pll1_auxclk, NULL, 0), + LPSC(24, 0, pwm1, pll1_auxclk, NULL, 0), + LPSC(25, 0, pwm2, pll1_auxclk, NULL, 0), + LPSC(26, 0, gpio, pll1_sysclk4, gpio_clkdev, 0), + LPSC(27, 0, timer0, pll1_auxclk, timer0_clkdev, LPSC_ALWAYS_ENABLED), + LPSC(28, 0, timer1, pll1_auxclk, NULL, 0), + /* REVISIT: why can't this be disabled? */ + LPSC(29, 0, timer2, pll1_auxclk, timer2_clkdev, LPSC_ALWAYS_ENABLED), + LPSC(31, 0, arm, pll2_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(38, 0, spi3, pll1_sysclk4, spi3_clkdev, 0), + LPSC(39, 0, spi4, pll1_auxclk, spi4_clkdev, 0), + LPSC(40, 0, emac, pll2_sysclk4, emac_clkdev, 0), + LPSC(44, 1, voice_codec, pll1_sysclk3, voice_codec_clkdev, 0), + LPSC(46, 1, vpss_dac, pll1_sysclk3, vpss_dac_clkdev, 0), + LPSC(47, 0, vpss_master, pll1_sysclk5, vpss_master_clkdev, 0), + LPSC(50, 0, mjcp, pll1_sysclk3, NULL, 0), + { } +}; + +static int dm365_psc_init(struct device *dev, void __iomem *base) +{ + return davinci_psc_register_clocks(dev, dm365_psc_info, 52, base); +} + +static struct clk_bulk_data dm365_psc_parent_clks[] = { + { .id = "pll1_sysclk1" }, + { .id = "pll1_sysclk3" }, + { .id = "pll1_sysclk4" }, + { .id = "pll1_sysclk5" }, + { .id = "pll1_sysclk8" }, + { .id = "pll2_sysclk2" }, + { .id = "pll2_sysclk4" }, + { .id = "pll1_auxclk" }, +}; + +const struct davinci_psc_init_data dm365_psc_init_data = { + .parent_clks = dm365_psc_parent_clks, + .num_parent_clks = ARRAY_SIZE(dm365_psc_parent_clks), + .psc_init = &dm365_psc_init, +}; diff --git a/drivers/clk/davinci/psc-dm644x.c b/drivers/clk/davinci/psc-dm644x.c new file mode 100644 index 000000000000..c22367baa46f --- /dev/null +++ b/drivers/clk/davinci/psc-dm644x.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PSC clock descriptions for TI DaVinci DM644x + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include "psc.h" + +LPSC_CLKDEV1(vpss_master_clkdev, "master", "vpss"); +LPSC_CLKDEV1(vpss_slave_clkdev, "slave", "vpss"); +LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1", + "fck", "davinci_mdio.0"); +LPSC_CLKDEV1(usb_clkdev, "usb", NULL); +LPSC_CLKDEV1(ide_clkdev, NULL, "palm_bk3710"); +LPSC_CLKDEV1(aemif_clkdev, "aemif", NULL); +LPSC_CLKDEV1(mmcsd_clkdev, NULL, "dm6441-mmc.0"); +LPSC_CLKDEV1(asp0_clkdev, NULL, "davinci-mcbsp"); +LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1"); +LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0"); +LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1"); +LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2"); +/* REVISIT: gpio-davinci.c should be modified to drop con_id */ +LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL); +LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL); +LPSC_CLKDEV1(timer2_clkdev, NULL, "davinci-wdt"); + +static const struct davinci_lpsc_clk_info dm644x_psc_info[] = { + LPSC(0, 0, vpss_master, pll1_sysclk3, vpss_master_clkdev, 0), + LPSC(1, 0, vpss_slave, pll1_sysclk3, vpss_slave_clkdev, 0), + LPSC(6, 0, emac, pll1_sysclk5, emac_clkdev, 0), + LPSC(9, 0, usb, pll1_sysclk5, usb_clkdev, 0), + LPSC(10, 0, ide, pll1_sysclk5, ide_clkdev, 0), + LPSC(11, 0, vlynq, pll1_sysclk5, NULL, 0), + LPSC(14, 0, aemif, pll1_sysclk5, aemif_clkdev, 0), + LPSC(15, 0, mmcsd, pll1_sysclk5, mmcsd_clkdev, 0), + LPSC(17, 0, asp0, pll1_sysclk5, asp0_clkdev, 0), + LPSC(18, 0, i2c, pll1_auxclk, i2c_clkdev, 0), + LPSC(19, 0, uart0, pll1_auxclk, uart0_clkdev, 0), + LPSC(20, 0, uart1, pll1_auxclk, uart1_clkdev, 0), + LPSC(21, 0, uart2, pll1_auxclk, uart2_clkdev, 0), + LPSC(22, 0, spi, pll1_sysclk5, NULL, 0), + LPSC(23, 0, pwm0, pll1_auxclk, NULL, 0), + LPSC(24, 0, pwm1, pll1_auxclk, NULL, 0), + LPSC(25, 0, pwm2, pll1_auxclk, NULL, 0), + LPSC(26, 0, gpio, pll1_sysclk5, gpio_clkdev, 0), + LPSC(27, 0, timer0, pll1_auxclk, timer0_clkdev, LPSC_ALWAYS_ENABLED), + LPSC(28, 0, timer1, pll1_auxclk, NULL, 0), + /* REVISIT: why can't this be disabled? */ + LPSC(29, 0, timer2, pll1_auxclk, timer2_clkdev, LPSC_ALWAYS_ENABLED), + LPSC(31, 0, arm, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + /* REVISIT how to disable? */ + LPSC(39, 1, dsp, pll1_sysclk1, NULL, LPSC_ALWAYS_ENABLED), + /* REVISIT how to disable? */ + LPSC(40, 1, vicp, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + { } +}; + +static int dm644x_psc_init(struct device *dev, void __iomem *base) +{ + return davinci_psc_register_clocks(dev, dm644x_psc_info, 41, base); +} + +static struct clk_bulk_data dm644x_psc_parent_clks[] = { + { .id = "pll1_sysclk1" }, + { .id = "pll1_sysclk2" }, + { .id = "pll1_sysclk3" }, + { .id = "pll1_sysclk5" }, + { .id = "pll1_auxclk" }, +}; + +const struct davinci_psc_init_data dm644x_psc_init_data = { + .parent_clks = dm644x_psc_parent_clks, + .num_parent_clks = ARRAY_SIZE(dm644x_psc_parent_clks), + .psc_init = &dm644x_psc_init, +}; diff --git a/drivers/clk/davinci/psc-dm646x.c b/drivers/clk/davinci/psc-dm646x.c new file mode 100644 index 000000000000..468ef86ea40b --- /dev/null +++ b/drivers/clk/davinci/psc-dm646x.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PSC clock descriptions for TI DaVinci DM646x + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include "psc.h" + +LPSC_CLKDEV1(ide_clkdev, NULL, "palm_bk3710"); +LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1", + "fck", "davinci_mdio.0"); +LPSC_CLKDEV1(aemif_clkdev, "aemif", NULL); +LPSC_CLKDEV1(mcasp0_clkdev, NULL, "davinci-mcasp.0"); +LPSC_CLKDEV1(mcasp1_clkdev, NULL, "davinci-mcasp.1"); +LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0"); +LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1"); +LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2"); +LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1"); +/* REVISIT: gpio-davinci.c should be modified to drop con_id */ +LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL); +LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL); + +static const struct davinci_lpsc_clk_info dm646x_psc_info[] = { + LPSC(0, 0, arm, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + /* REVISIT how to disable? */ + LPSC(1, 0, dsp, pll1_sysclk1, NULL, LPSC_ALWAYS_ENABLED), + LPSC(4, 0, edma_cc, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(5, 0, edma_tc0, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(6, 0, edma_tc1, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(7, 0, edma_tc2, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(8, 0, edma_tc3, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED), + LPSC(10, 0, ide, pll1_sysclk4, ide_clkdev, 0), + LPSC(14, 0, emac, pll1_sysclk3, emac_clkdev, 0), + LPSC(16, 0, vpif0, ref_clk, NULL, LPSC_ALWAYS_ENABLED), + LPSC(17, 0, vpif1, ref_clk, NULL, LPSC_ALWAYS_ENABLED), + LPSC(21, 0, aemif, pll1_sysclk3, aemif_clkdev, LPSC_ALWAYS_ENABLED), + LPSC(22, 0, mcasp0, pll1_sysclk3, mcasp0_clkdev, 0), + LPSC(23, 0, mcasp1, pll1_sysclk3, mcasp1_clkdev, 0), + LPSC(26, 0, uart0, aux_clkin, uart0_clkdev, 0), + LPSC(27, 0, uart1, aux_clkin, uart1_clkdev, 0), + LPSC(28, 0, uart2, aux_clkin, uart2_clkdev, 0), + /* REVIST: disabling hangs system */ + LPSC(29, 0, pwm0, pll1_sysclk3, NULL, LPSC_ALWAYS_ENABLED), + /* REVIST: disabling hangs system */ + LPSC(30, 0, pwm1, pll1_sysclk3, NULL, LPSC_ALWAYS_ENABLED), + LPSC(31, 0, i2c, pll1_sysclk3, i2c_clkdev, 0), + LPSC(33, 0, gpio, pll1_sysclk3, gpio_clkdev, 0), + LPSC(34, 0, timer0, pll1_sysclk3, timer0_clkdev, LPSC_ALWAYS_ENABLED), + LPSC(35, 0, timer1, pll1_sysclk3, NULL, 0), + { } +}; + +static int dm646x_psc_init(struct device *dev, void __iomem *base) +{ + return davinci_psc_register_clocks(dev, dm646x_psc_info, 46, base); +} + +static struct clk_bulk_data dm646x_psc_parent_clks[] = { + { .id = "ref_clk" }, + { .id = "aux_clkin" }, + { .id = "pll1_sysclk1" }, + { .id = "pll1_sysclk2" }, + { .id = "pll1_sysclk3" }, + { .id = "pll1_sysclk4" }, + { .id = "pll1_sysclk5" }, +}; + +const struct davinci_psc_init_data dm646x_psc_init_data = { + .parent_clks = dm646x_psc_parent_clks, + .num_parent_clks = ARRAY_SIZE(dm646x_psc_parent_clks), + .psc_init = &dm646x_psc_init, +}; diff --git a/drivers/clk/davinci/psc.c b/drivers/clk/davinci/psc.c new file mode 100644 index 000000000000..ff6f4a038e04 --- /dev/null +++ b/drivers/clk/davinci/psc.c @@ -0,0 +1,550 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Clock driver for TI Davinci PSC controllers + * + * Copyright (C) 2017 David Lechner <david@lechnology.com> + * + * Based on: drivers/clk/keystone/gate.c + * Copyright (C) 2013 Texas Instruments. + * Murali Karicheri <m-karicheri2@ti.com> + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * And: arch/arm/mach-davinci/psc.c + * Copyright (C) 2006 Texas Instruments. + */ + +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_clock.h> +#include <linux/pm_domain.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "psc.h" + +/* PSC register offsets */ +#define EPCPR 0x070 +#define PTCMD 0x120 +#define PTSTAT 0x128 +#define PDSTAT(n) (0x200 + 4 * (n)) +#define PDCTL(n) (0x300 + 4 * (n)) +#define MDSTAT(n) (0x800 + 4 * (n)) +#define MDCTL(n) (0xa00 + 4 * (n)) + +/* PSC module states */ +enum davinci_lpsc_state { + LPSC_STATE_SWRSTDISABLE = 0, + LPSC_STATE_SYNCRST = 1, + LPSC_STATE_DISABLE = 2, + LPSC_STATE_ENABLE = 3, +}; + +#define MDSTAT_STATE_MASK GENMASK(5, 0) +#define MDSTAT_MCKOUT BIT(12) +#define PDSTAT_STATE_MASK GENMASK(4, 0) +#define MDCTL_FORCE BIT(31) +#define MDCTL_LRESET BIT(8) +#define PDCTL_EPCGOOD BIT(8) +#define PDCTL_NEXT BIT(0) + +struct davinci_psc_data { + struct clk_onecell_data clk_data; + struct genpd_onecell_data pm_data; + struct reset_controller_dev rcdev; +}; + +/** + * struct davinci_lpsc_clk - LPSC clock structure + * @dev: the device that provides this LPSC + * @hw: clk_hw for the LPSC + * @pm_domain: power domain for the LPSC + * @genpd_clk: clock reference owned by @pm_domain + * @regmap: PSC MMIO region + * @md: Module domain (LPSC module id) + * @pd: Power domain + * @flags: LPSC_* quirk flags + */ +struct davinci_lpsc_clk { + struct device *dev; + struct clk_hw hw; + struct generic_pm_domain pm_domain; + struct clk *genpd_clk; + struct regmap *regmap; + u32 md; + u32 pd; + u32 flags; +}; + +#define to_davinci_psc_data(x) container_of(x, struct davinci_psc_data, x) +#define to_davinci_lpsc_clk(x) container_of(x, struct davinci_lpsc_clk, x) + +/** + * best_dev_name - get the "best" device name. + * @dev: the device + * + * Returns the device tree compatible name if the device has a DT node, + * otherwise return the device name. This is mainly needed because clkdev + * lookups are limited to 20 chars for dev_id and when using device tree, + * dev_name(dev) is much longer than that. + */ +static inline const char *best_dev_name(struct device *dev) +{ + const char *compatible; + + if (!of_property_read_string(dev->of_node, "compatible", &compatible)) + return compatible; + + return dev_name(dev); +} + +static void davinci_lpsc_config(struct davinci_lpsc_clk *lpsc, + enum davinci_lpsc_state next_state) +{ + u32 epcpr, pdstat, mdstat, ptstat; + + regmap_write_bits(lpsc->regmap, MDCTL(lpsc->md), MDSTAT_STATE_MASK, + next_state); + + if (lpsc->flags & LPSC_FORCE) + regmap_write_bits(lpsc->regmap, MDCTL(lpsc->md), MDCTL_FORCE, + MDCTL_FORCE); + + regmap_read(lpsc->regmap, PDSTAT(lpsc->pd), &pdstat); + if ((pdstat & PDSTAT_STATE_MASK) == 0) { + regmap_write_bits(lpsc->regmap, PDCTL(lpsc->pd), PDCTL_NEXT, + PDCTL_NEXT); + + regmap_write(lpsc->regmap, PTCMD, BIT(lpsc->pd)); + + regmap_read_poll_timeout(lpsc->regmap, EPCPR, epcpr, + epcpr & BIT(lpsc->pd), 0, 0); + + regmap_write_bits(lpsc->regmap, PDCTL(lpsc->pd), PDCTL_EPCGOOD, + PDCTL_EPCGOOD); + } else { + regmap_write(lpsc->regmap, PTCMD, BIT(lpsc->pd)); + } + + regmap_read_poll_timeout(lpsc->regmap, PTSTAT, ptstat, + !(ptstat & BIT(lpsc->pd)), 0, 0); + + regmap_read_poll_timeout(lpsc->regmap, MDSTAT(lpsc->md), mdstat, + (mdstat & MDSTAT_STATE_MASK) == next_state, + 0, 0); +} + +static int davinci_lpsc_clk_enable(struct clk_hw *hw) +{ + struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw); + + davinci_lpsc_config(lpsc, LPSC_STATE_ENABLE); + + return 0; +} + +static void davinci_lpsc_clk_disable(struct clk_hw *hw) +{ + struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw); + + davinci_lpsc_config(lpsc, LPSC_STATE_DISABLE); +} + +static int davinci_lpsc_clk_is_enabled(struct clk_hw *hw) +{ + struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw); + u32 mdstat; + + regmap_read(lpsc->regmap, MDSTAT(lpsc->md), &mdstat); + + return (mdstat & MDSTAT_MCKOUT) ? 1 : 0; +} + +static const struct clk_ops davinci_lpsc_clk_ops = { + .enable = davinci_lpsc_clk_enable, + .disable = davinci_lpsc_clk_disable, + .is_enabled = davinci_lpsc_clk_is_enabled, +}; + +static int davinci_psc_genpd_attach_dev(struct generic_pm_domain *pm_domain, + struct device *dev) +{ + struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(pm_domain); + struct clk *clk; + int ret; + + /* + * pm_clk_remove_clk() will call clk_put(), so we have to use clk_get() + * to get the clock instead of using lpsc->hw.clk directly. + */ + clk = clk_get_sys(best_dev_name(lpsc->dev), clk_hw_get_name(&lpsc->hw)); + if (IS_ERR(clk)) + return (PTR_ERR(clk)); + + ret = pm_clk_create(dev); + if (ret < 0) + goto fail_clk_put; + + ret = pm_clk_add_clk(dev, clk); + if (ret < 0) + goto fail_pm_clk_destroy; + + lpsc->genpd_clk = clk; + + return 0; + +fail_pm_clk_destroy: + pm_clk_destroy(dev); +fail_clk_put: + clk_put(clk); + + return ret; +} + +static void davinci_psc_genpd_detach_dev(struct generic_pm_domain *pm_domain, + struct device *dev) +{ + struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(pm_domain); + + pm_clk_remove_clk(dev, lpsc->genpd_clk); + pm_clk_destroy(dev); + + lpsc->genpd_clk = NULL; +} + +/** + * davinci_lpsc_clk_register - register LPSC clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @regmap: PSC MMIO region + * @md: local PSC number + * @pd: power domain + * @flags: LPSC_* flags + */ +static struct davinci_lpsc_clk * +davinci_lpsc_clk_register(struct device *dev, const char *name, + const char *parent_name, struct regmap *regmap, + u32 md, u32 pd, u32 flags) +{ + struct clk_init_data init; + struct davinci_lpsc_clk *lpsc; + int ret; + bool is_on; + + lpsc = devm_kzalloc(dev, sizeof(*lpsc), GFP_KERNEL); + if (!lpsc) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &davinci_lpsc_clk_ops; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + init.flags = 0; + + if (flags & LPSC_ALWAYS_ENABLED) + init.flags |= CLK_IS_CRITICAL; + + if (flags & LPSC_SET_RATE_PARENT) + init.flags |= CLK_SET_RATE_PARENT; + + lpsc->dev = dev; + lpsc->regmap = regmap; + lpsc->hw.init = &init; + lpsc->md = md; + lpsc->pd = pd; + lpsc->flags = flags; + + ret = devm_clk_hw_register(dev, &lpsc->hw); + if (ret < 0) + return ERR_PTR(ret); + + /* genpd attach needs a way to look up this clock */ + ret = clk_hw_register_clkdev(&lpsc->hw, name, best_dev_name(dev)); + + lpsc->pm_domain.name = devm_kasprintf(dev, GFP_KERNEL, "%s: %s", + best_dev_name(dev), name); + lpsc->pm_domain.attach_dev = davinci_psc_genpd_attach_dev; + lpsc->pm_domain.detach_dev = davinci_psc_genpd_detach_dev; + lpsc->pm_domain.flags = GENPD_FLAG_PM_CLK; + + is_on = davinci_lpsc_clk_is_enabled(&lpsc->hw); + pm_genpd_init(&lpsc->pm_domain, NULL, is_on); + + return lpsc; +} + +static int davinci_lpsc_clk_reset(struct clk *clk, bool reset) +{ + struct clk_hw *hw = __clk_get_hw(clk); + struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw); + u32 mdctl; + + if (IS_ERR_OR_NULL(lpsc)) + return -EINVAL; + + mdctl = reset ? 0 : MDCTL_LRESET; + regmap_write_bits(lpsc->regmap, MDCTL(lpsc->md), MDCTL_LRESET, mdctl); + + return 0; +} + +/* + * REVISIT: These exported functions can be removed after a non-DT lookup is + * added to the reset controller framework and the davinci-rproc driver is + * updated to use the generic reset controller framework. + */ + +int davinci_clk_reset_assert(struct clk *clk) +{ + return davinci_lpsc_clk_reset(clk, true); +} +EXPORT_SYMBOL(davinci_clk_reset_assert); + +int davinci_clk_reset_deassert(struct clk *clk) +{ + return davinci_lpsc_clk_reset(clk, false); +} +EXPORT_SYMBOL(davinci_clk_reset_deassert); + +static int davinci_psc_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct davinci_psc_data *psc = to_davinci_psc_data(rcdev); + struct clk *clk = psc->clk_data.clks[id]; + + return davinci_lpsc_clk_reset(clk, true); +} + +static int davinci_psc_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct davinci_psc_data *psc = to_davinci_psc_data(rcdev); + struct clk *clk = psc->clk_data.clks[id]; + + return davinci_lpsc_clk_reset(clk, false); +} + +static const struct reset_control_ops davinci_psc_reset_ops = { + .assert = davinci_psc_reset_assert, + .deassert = davinci_psc_reset_deassert, +}; + +static int davinci_psc_reset_of_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + struct of_phandle_args clkspec = *reset_spec; /* discard const qualifier */ + struct clk *clk; + struct clk_hw *hw; + struct davinci_lpsc_clk *lpsc; + + /* the clock node is the same as the reset node */ + clk = of_clk_get_from_provider(&clkspec); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + hw = __clk_get_hw(clk); + lpsc = to_davinci_lpsc_clk(hw); + clk_put(clk); + + /* not all modules support local reset */ + if (!(lpsc->flags & LPSC_LOCAL_RESET)) + return -EINVAL; + + return lpsc->md; +} + +static const struct regmap_config davinci_psc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +}; + +static struct davinci_psc_data * +__davinci_psc_register_clocks(struct device *dev, + const struct davinci_lpsc_clk_info *info, + int num_clks, + void __iomem *base) +{ + struct davinci_psc_data *psc; + struct clk **clks; + struct generic_pm_domain **pm_domains; + struct regmap *regmap; + int i, ret; + + psc = devm_kzalloc(dev, sizeof(*psc), GFP_KERNEL); + if (!psc) + return ERR_PTR(-ENOMEM); + + clks = devm_kmalloc_array(dev, num_clks, sizeof(*clks), GFP_KERNEL); + if (!clks) + return ERR_PTR(-ENOMEM); + + psc->clk_data.clks = clks; + psc->clk_data.clk_num = num_clks; + + /* + * init array with error so that of_clk_src_onecell_get() doesn't + * return NULL for gaps in the sparse array + */ + for (i = 0; i < num_clks; i++) + clks[i] = ERR_PTR(-ENOENT); + + pm_domains = devm_kcalloc(dev, num_clks, sizeof(*pm_domains), GFP_KERNEL); + if (!pm_domains) + return ERR_PTR(-ENOMEM); + + psc->pm_data.domains = pm_domains; + psc->pm_data.num_domains = num_clks; + + regmap = devm_regmap_init_mmio(dev, base, &davinci_psc_regmap_config); + if (IS_ERR(regmap)) + return ERR_CAST(regmap); + + for (; info->name; info++) { + struct davinci_lpsc_clk *lpsc; + + lpsc = davinci_lpsc_clk_register(dev, info->name, info->parent, + regmap, info->md, info->pd, + info->flags); + if (IS_ERR(lpsc)) { + dev_warn(dev, "Failed to register %s (%ld)\n", + info->name, PTR_ERR(lpsc)); + continue; + } + + clks[info->md] = lpsc->hw.clk; + pm_domains[info->md] = &lpsc->pm_domain; + } + + psc->rcdev.ops = &davinci_psc_reset_ops; + psc->rcdev.owner = THIS_MODULE; + psc->rcdev.of_node = dev->of_node; + psc->rcdev.of_reset_n_cells = 1; + psc->rcdev.of_xlate = davinci_psc_reset_of_xlate; + psc->rcdev.nr_resets = num_clks; + + ret = devm_reset_controller_register(dev, &psc->rcdev); + if (ret < 0) + dev_warn(dev, "Failed to register reset controller (%d)\n", ret); + + return psc; +} + +int davinci_psc_register_clocks(struct device *dev, + const struct davinci_lpsc_clk_info *info, + u8 num_clks, + void __iomem *base) +{ + struct davinci_psc_data *psc; + + psc = __davinci_psc_register_clocks(dev, info, num_clks, base); + if (IS_ERR(psc)) + return PTR_ERR(psc); + + for (; info->name; info++) { + const struct davinci_lpsc_clkdev_info *cdevs = info->cdevs; + struct clk *clk = psc->clk_data.clks[info->md]; + + if (!cdevs || IS_ERR_OR_NULL(clk)) + continue; + + for (; cdevs->con_id || cdevs->dev_id; cdevs++) + clk_register_clkdev(clk, cdevs->con_id, cdevs->dev_id); + } + + return 0; +} + +int of_davinci_psc_clk_init(struct device *dev, + const struct davinci_lpsc_clk_info *info, + u8 num_clks, + void __iomem *base) +{ + struct device_node *node = dev->of_node; + struct davinci_psc_data *psc; + + psc = __davinci_psc_register_clocks(dev, info, num_clks, base); + if (IS_ERR(psc)) + return PTR_ERR(psc); + + of_genpd_add_provider_onecell(node, &psc->pm_data); + + of_clk_add_provider(node, of_clk_src_onecell_get, &psc->clk_data); + + return 0; +} + +static const struct of_device_id davinci_psc_of_match[] = { + { .compatible = "ti,da850-psc0", .data = &of_da850_psc0_init_data }, + { .compatible = "ti,da850-psc1", .data = &of_da850_psc1_init_data }, + { } +}; + +static const struct platform_device_id davinci_psc_id_table[] = { + { .name = "da830-psc0", .driver_data = (kernel_ulong_t)&da830_psc0_init_data }, + { .name = "da830-psc1", .driver_data = (kernel_ulong_t)&da830_psc1_init_data }, + { .name = "da850-psc0", .driver_data = (kernel_ulong_t)&da850_psc0_init_data }, + { .name = "da850-psc1", .driver_data = (kernel_ulong_t)&da850_psc1_init_data }, + { .name = "dm355-psc", .driver_data = (kernel_ulong_t)&dm355_psc_init_data }, + { .name = "dm365-psc", .driver_data = (kernel_ulong_t)&dm365_psc_init_data }, + { .name = "dm644x-psc", .driver_data = (kernel_ulong_t)&dm644x_psc_init_data }, + { .name = "dm646x-psc", .driver_data = (kernel_ulong_t)&dm646x_psc_init_data }, + { } +}; + +static int davinci_psc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *of_id; + const struct davinci_psc_init_data *init_data = NULL; + struct resource *res; + void __iomem *base; + int ret; + + of_id = of_match_device(davinci_psc_of_match, dev); + if (of_id) + init_data = of_id->data; + else if (pdev->id_entry) + init_data = (void *)pdev->id_entry->driver_data; + + if (!init_data) { + dev_err(dev, "unable to find driver init data\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + ret = devm_clk_bulk_get(dev, init_data->num_parent_clks, + init_data->parent_clks); + if (ret < 0) + return ret; + + return init_data->psc_init(dev, base); +} + +static struct platform_driver davinci_psc_driver = { + .probe = davinci_psc_probe, + .driver = { + .name = "davinci-psc-clk", + .of_match_table = davinci_psc_of_match, + }, + .id_table = davinci_psc_id_table, +}; + +static int __init davinci_psc_driver_init(void) +{ + return platform_driver_register(&davinci_psc_driver); +} + +/* has to be postcore_initcall because davinci_gpio depend on PSC clocks */ +postcore_initcall(davinci_psc_driver_init); diff --git a/drivers/clk/davinci/psc.h b/drivers/clk/davinci/psc.h new file mode 100644 index 000000000000..c2a7df6413fe --- /dev/null +++ b/drivers/clk/davinci/psc.h @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Clock driver for TI Davinci PSC controllers + * + * Copyright (C) 2018 David Lechner <david@lechnology.com> + */ + +#ifndef __CLK_DAVINCI_PSC_H__ +#define __CLK_DAVINCI_PSC_H__ + +#include <linux/clk-provider.h> +#include <linux/types.h> + +/* PSC quirk flags */ +#define LPSC_ALWAYS_ENABLED BIT(0) /* never disable this clock */ +#define LPSC_SET_RATE_PARENT BIT(1) /* propagate set_rate to parent clock */ +#define LPSC_FORCE BIT(2) /* requires MDCTL FORCE bit */ +#define LPSC_LOCAL_RESET BIT(3) /* acts as reset provider */ + +struct davinci_lpsc_clkdev_info { + const char *con_id; + const char *dev_id; +}; + +#define LPSC_CLKDEV(c, d) { \ + .con_id = (c), \ + .dev_id = (d) \ +} + +#define LPSC_CLKDEV1(n, c, d) \ +static const struct davinci_lpsc_clkdev_info n[] __initconst = { \ + LPSC_CLKDEV((c), (d)), \ + { } \ +} + +#define LPSC_CLKDEV2(n, c1, d1, c2, d2) \ +static const struct davinci_lpsc_clkdev_info n[] __initconst = { \ + LPSC_CLKDEV((c1), (d1)), \ + LPSC_CLKDEV((c2), (d2)), \ + { } \ +} + +#define LPSC_CLKDEV3(n, c1, d1, c2, d2, c3, d3) \ +static const struct davinci_lpsc_clkdev_info n[] __initconst = { \ + LPSC_CLKDEV((c1), (d1)), \ + LPSC_CLKDEV((c2), (d2)), \ + LPSC_CLKDEV((c3), (d3)), \ + { } \ +} + +/** + * davinci_lpsc_clk_info - LPSC module-specific clock information + * @name: the clock name + * @parent: the parent clock name + * @cdevs: optional array of clkdev lookup table info + * @md: the local module domain (LPSC id) + * @pd: the power domain id + * @flags: bitmask of LPSC_* flags + */ +struct davinci_lpsc_clk_info { + const char *name; + const char *parent; + const struct davinci_lpsc_clkdev_info *cdevs; + u32 md; + u32 pd; + unsigned long flags; +}; + +#define LPSC(m, d, n, p, c, f) \ +{ \ + .name = #n, \ + .parent = #p, \ + .cdevs = (c), \ + .md = (m), \ + .pd = (d), \ + .flags = (f), \ +} + +int davinci_psc_register_clocks(struct device *dev, + const struct davinci_lpsc_clk_info *info, + u8 num_clks, + void __iomem *base); + +int of_davinci_psc_clk_init(struct device *dev, + const struct davinci_lpsc_clk_info *info, + u8 num_clks, + void __iomem *base); + +/* Device-specific data */ + +struct davinci_psc_init_data { + struct clk_bulk_data *parent_clks; + int num_parent_clks; + int (*psc_init)(struct device *dev, void __iomem *base); +}; + +extern const struct davinci_psc_init_data da830_psc0_init_data; +extern const struct davinci_psc_init_data da830_psc1_init_data; +extern const struct davinci_psc_init_data da850_psc0_init_data; +extern const struct davinci_psc_init_data da850_psc1_init_data; +extern const struct davinci_psc_init_data of_da850_psc0_init_data; +extern const struct davinci_psc_init_data of_da850_psc1_init_data; +extern const struct davinci_psc_init_data dm355_psc_init_data; +extern const struct davinci_psc_init_data dm365_psc_init_data; +extern const struct davinci_psc_init_data dm644x_psc_init_data; +extern const struct davinci_psc_init_data dm646x_psc_init_data; + +#endif /* __CLK_DAVINCI_PSC_H__ */ diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c index 077fcdc7908b..026a26bb702d 100644 --- a/drivers/clk/rockchip/clk-mmc-phase.c +++ b/drivers/clk/rockchip/clk-mmc-phase.c @@ -25,6 +25,8 @@ struct rockchip_mmc_clock { void __iomem *reg; int id; int shift; + int cached_phase; + struct notifier_block clk_rate_change_nb; }; #define to_mmc_clock(_hw) container_of(_hw, struct rockchip_mmc_clock, hw) @@ -58,6 +60,12 @@ static int rockchip_mmc_get_phase(struct clk_hw *hw) u16 degrees; u32 delay_num = 0; + /* See the comment for rockchip_mmc_set_phase below */ + if (!rate) { + pr_err("%s: invalid clk rate\n", __func__); + return -EINVAL; + } + raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift); degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90; @@ -84,6 +92,23 @@ static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees) u32 raw_value; u32 delay; + /* + * The below calculation is based on the output clock from + * MMC host to the card, which expects the phase clock inherits + * the clock rate from its parent, namely the output clock + * provider of MMC host. However, things may go wrong if + * (1) It is orphan. + * (2) It is assigned to the wrong parent. + * + * This check help debug the case (1), which seems to be the + * most likely problem we often face and which makes it difficult + * for people to debug unstable mmc tuning results. + */ + if (!rate) { + pr_err("%s: invalid clk rate\n", __func__); + return -EINVAL; + } + nineties = degrees / 90; remainder = (degrees % 90); @@ -139,6 +164,41 @@ static const struct clk_ops rockchip_mmc_clk_ops = { .set_phase = rockchip_mmc_set_phase, }; +#define to_rockchip_mmc_clock(x) \ + container_of(x, struct rockchip_mmc_clock, clk_rate_change_nb) +static int rockchip_mmc_clk_rate_notify(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct rockchip_mmc_clock *mmc_clock = to_rockchip_mmc_clock(nb); + struct clk_notifier_data *ndata = data; + + /* + * rockchip_mmc_clk is mostly used by mmc controllers to sample + * the intput data, which expects the fixed phase after the tuning + * process. However if the clock rate is changed, the phase is stale + * and may break the data sampling. So here we try to restore the phase + * for that case, except that + * (1) cached_phase is invaild since we inevitably cached it when the + * clock provider be reparented from orphan to its real parent in the + * first place. Otherwise we may mess up the initialization of MMC cards + * since we only set the default sample phase and drive phase later on. + * (2) the new coming rate is higher than the older one since mmc driver + * set the max-frequency to match the boards' ability but we can't go + * over the heads of that, otherwise the tests smoke out the issue. + */ + if (ndata->old_rate <= ndata->new_rate) + return NOTIFY_DONE; + + if (event == PRE_RATE_CHANGE) + mmc_clock->cached_phase = + rockchip_mmc_get_phase(&mmc_clock->hw); + else if (mmc_clock->cached_phase != -EINVAL && + event == POST_RATE_CHANGE) + rockchip_mmc_set_phase(&mmc_clock->hw, mmc_clock->cached_phase); + + return NOTIFY_DONE; +} + struct clk *rockchip_clk_register_mmc(const char *name, const char *const *parent_names, u8 num_parents, void __iomem *reg, int shift) @@ -146,6 +206,7 @@ struct clk *rockchip_clk_register_mmc(const char *name, struct clk_init_data init; struct rockchip_mmc_clock *mmc_clock; struct clk *clk; + int ret; mmc_clock = kmalloc(sizeof(*mmc_clock), GFP_KERNEL); if (!mmc_clock) @@ -162,8 +223,21 @@ struct clk *rockchip_clk_register_mmc(const char *name, mmc_clock->shift = shift; clk = clk_register(NULL, &mmc_clock->hw); - if (IS_ERR(clk)) - kfree(mmc_clock); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto err_register; + } + + mmc_clock->clk_rate_change_nb.notifier_call = + &rockchip_mmc_clk_rate_notify; + ret = clk_notifier_register(clk, &mmc_clock->clk_rate_change_nb); + if (ret) + goto err_notifier; return clk; +err_notifier: + clk_unregister(clk); +err_register: + kfree(mmc_clock); + return ERR_PTR(ret); } diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c index 11e7f2d1c054..7af48184b022 100644 --- a/drivers/clk/rockchip/clk-rk3228.c +++ b/drivers/clk/rockchip/clk-rk3228.c @@ -387,7 +387,7 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = { RK2928_CLKSEL_CON(23), 5, 2, MFLAGS, 0, 6, DFLAGS, RK2928_CLKGATE_CON(2), 15, GFLAGS), - COMPOSITE(SCLK_SDMMC, "sclk_sdmmc0", mux_mmc_src_p, 0, + COMPOSITE(SCLK_SDMMC, "sclk_sdmmc", mux_mmc_src_p, 0, RK2928_CLKSEL_CON(11), 8, 2, MFLAGS, 0, 8, DFLAGS, RK2928_CLKGATE_CON(2), 11, GFLAGS), diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c index b04f29774ee7..252366a5231f 100644 --- a/drivers/clk/rockchip/clk-rk3328.c +++ b/drivers/clk/rockchip/clk-rk3328.c @@ -304,7 +304,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "aclk_core", "armclk", CLK_IGNORE_UNUSED, RK3328_CLKSEL_CON(1), 4, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3328_CLKGATE_CON(7), 1, GFLAGS), - GATE(0, "aclk_core_niu", "aclk_core", CLK_IGNORE_UNUSED, + GATE(0, "aclk_core_niu", "aclk_core", 0, RK3328_CLKGATE_CON(13), 0, GFLAGS), GATE(0, "aclk_gic400", "aclk_core", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(13), 1, GFLAGS), @@ -318,7 +318,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { RK3328_CLKGATE_CON(6), 6, GFLAGS), GATE(ACLK_GPU, "aclk_gpu", "aclk_gpu_pre", CLK_SET_RATE_PARENT, RK3328_CLKGATE_CON(14), 0, GFLAGS), - GATE(0, "aclk_gpu_niu", "aclk_gpu_pre", CLK_IGNORE_UNUSED, + GATE(0, "aclk_gpu_niu", "aclk_gpu_pre", 0, RK3328_CLKGATE_CON(14), 1, GFLAGS), /* PD_DDR */ @@ -513,9 +513,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { RK3328_CLKGATE_CON(24), 0, GFLAGS), GATE(HCLK_RKVDEC, "hclk_rkvdec", "hclk_rkvdec_pre", CLK_SET_RATE_PARENT, RK3328_CLKGATE_CON(24), 1, GFLAGS), - GATE(0, "aclk_rkvdec_niu", "aclk_rkvdec_pre", CLK_IGNORE_UNUSED, + GATE(0, "aclk_rkvdec_niu", "aclk_rkvdec_pre", 0, RK3328_CLKGATE_CON(24), 2, GFLAGS), - GATE(0, "hclk_rkvdec_niu", "hclk_rkvdec_pre", CLK_IGNORE_UNUSED, + GATE(0, "hclk_rkvdec_niu", "hclk_rkvdec_pre", 0, RK3328_CLKGATE_CON(24), 3, GFLAGS), COMPOSITE(SCLK_VDEC_CABAC, "sclk_vdec_cabac", mux_4plls_p, 0, @@ -535,9 +535,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { RK3328_CLKGATE_CON(23), 0, GFLAGS), GATE(HCLK_VPU, "hclk_vpu", "hclk_vpu_pre", CLK_SET_RATE_PARENT, RK3328_CLKGATE_CON(23), 1, GFLAGS), - GATE(0, "aclk_vpu_niu", "aclk_vpu_pre", CLK_IGNORE_UNUSED, + GATE(0, "aclk_vpu_niu", "aclk_vpu_pre", 0, RK3328_CLKGATE_CON(23), 2, GFLAGS), - GATE(0, "hclk_vpu_niu", "hclk_vpu_pre", CLK_IGNORE_UNUSED, + GATE(0, "hclk_vpu_niu", "hclk_vpu_pre", 0, RK3328_CLKGATE_CON(23), 3, GFLAGS), COMPOSITE(ACLK_RKVENC, "aclk_rkvenc", mux_4plls_p, 0, @@ -545,9 +545,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { RK3328_CLKGATE_CON(6), 3, GFLAGS), FACTOR_GATE(HCLK_RKVENC, "hclk_rkvenc", "aclk_rkvenc", 0, 1, 4, RK3328_CLKGATE_CON(11), 4, GFLAGS), - GATE(0, "aclk_rkvenc_niu", "aclk_rkvenc", CLK_IGNORE_UNUSED, + GATE(0, "aclk_rkvenc_niu", "aclk_rkvenc", 0, RK3328_CLKGATE_CON(25), 0, GFLAGS), - GATE(0, "hclk_rkvenc_niu", "hclk_rkvenc", CLK_IGNORE_UNUSED, + GATE(0, "hclk_rkvenc_niu", "hclk_rkvenc", 0, RK3328_CLKGATE_CON(25), 1, GFLAGS), GATE(ACLK_H265, "aclk_h265", "aclk_rkvenc", 0, RK3328_CLKGATE_CON(25), 0, GFLAGS), @@ -588,7 +588,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { COMPOSITE(ACLK_VOP_PRE, "aclk_vop_pre", mux_4plls_p, 0, RK3328_CLKSEL_CON(39), 6, 2, MFLAGS, 0, 5, DFLAGS, RK3328_CLKGATE_CON(5), 5, GFLAGS), - GATE(0, "clk_hdmi_sfc", "xin24m", 0, + GATE(SCLK_HDMI_SFC, "sclk_hdmi_sfc", "xin24m", 0, RK3328_CLKGATE_CON(5), 4, GFLAGS), COMPOSITE_NODIV(0, "clk_cif_src", mux_2plls_p, 0, @@ -602,7 +602,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { RK3328_CLKGATE_CON(5), 6, GFLAGS), DIV(DCLK_HDMIPHY, "dclk_hdmiphy", "dclk_lcdc_src", 0, RK3328_CLKSEL_CON(40), 3, 3, DFLAGS), - MUX(DCLK_LCDC, "dclk_lcdc", mux_dclk_lcdc_p, 0, + MUX(DCLK_LCDC, "dclk_lcdc", mux_dclk_lcdc_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, RK3328_CLKSEL_CON(40), 1, 1, MFLAGS), /* @@ -709,14 +709,14 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { /* PD_VOP */ GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 0, RK3328_CLKGATE_CON(21), 10, GFLAGS), - GATE(0, "aclk_rga_niu", "aclk_rga_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(22), 3, GFLAGS), + GATE(0, "aclk_rga_niu", "aclk_rga_pre", 0, RK3328_CLKGATE_CON(22), 3, GFLAGS), GATE(ACLK_VOP, "aclk_vop", "aclk_vop_pre", 0, RK3328_CLKGATE_CON(21), 2, GFLAGS), - GATE(0, "aclk_vop_niu", "aclk_vop_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 4, GFLAGS), + GATE(0, "aclk_vop_niu", "aclk_vop_pre", 0, RK3328_CLKGATE_CON(21), 4, GFLAGS), GATE(ACLK_IEP, "aclk_iep", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 6, GFLAGS), GATE(ACLK_CIF, "aclk_cif", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 8, GFLAGS), GATE(ACLK_HDCP, "aclk_hdcp", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 15, GFLAGS), - GATE(0, "aclk_vio_niu", "aclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(22), 2, GFLAGS), + GATE(0, "aclk_vio_niu", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 2, GFLAGS), GATE(HCLK_VOP, "hclk_vop", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 3, GFLAGS), GATE(0, "hclk_vop_niu", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 5, GFLAGS), @@ -724,10 +724,10 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { GATE(HCLK_CIF, "hclk_cif", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 9, GFLAGS), GATE(HCLK_RGA, "hclk_rga", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 11, GFLAGS), GATE(0, "hclk_ahb1tom", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 12, GFLAGS), - GATE(0, "pclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 13, GFLAGS), - GATE(0, "hclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 14, GFLAGS), + GATE(0, "pclk_vio_h2p", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 13, GFLAGS), + GATE(0, "hclk_vio_h2p", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 14, GFLAGS), GATE(HCLK_HDCP, "hclk_hdcp", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 0, GFLAGS), - GATE(HCLK_VIO, "hclk_vio", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 1, GFLAGS), + GATE(0, "hclk_vio_niu", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 1, GFLAGS), GATE(PCLK_HDMI, "pclk_hdmi", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 4, GFLAGS), GATE(PCLK_HDCP, "pclk_hdcp", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 5, GFLAGS), @@ -743,19 +743,19 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { GATE(HCLK_HOST0_ARB, "hclk_host0_arb", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 7, GFLAGS), GATE(HCLK_OTG, "hclk_otg", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 8, GFLAGS), GATE(HCLK_OTG_PMU, "hclk_otg_pmu", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 9, GFLAGS), - GATE(0, "hclk_peri_niu", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 12, GFLAGS), - GATE(0, "pclk_peri_niu", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 13, GFLAGS), + GATE(0, "hclk_peri_niu", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 12, GFLAGS), + GATE(0, "pclk_peri_niu", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 13, GFLAGS), /* PD_GMAC */ GATE(ACLK_MAC2PHY, "aclk_mac2phy", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 0, GFLAGS), GATE(ACLK_MAC2IO, "aclk_mac2io", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 2, GFLAGS), - GATE(0, "aclk_gmac_niu", "aclk_gmac", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(26), 4, GFLAGS), + GATE(0, "aclk_gmac_niu", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 4, GFLAGS), GATE(PCLK_MAC2PHY, "pclk_mac2phy", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 1, GFLAGS), GATE(PCLK_MAC2IO, "pclk_mac2io", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 3, GFLAGS), - GATE(0, "pclk_gmac_niu", "pclk_gmac", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(26), 5, GFLAGS), + GATE(0, "pclk_gmac_niu", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 5, GFLAGS), /* PD_BUS */ - GATE(0, "aclk_bus_niu", "aclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 12, GFLAGS), + GATE(0, "aclk_bus_niu", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 12, GFLAGS), GATE(ACLK_DCF, "aclk_dcf", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 11, GFLAGS), GATE(ACLK_TSP, "aclk_tsp", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(17), 12, GFLAGS), GATE(0, "aclk_intmem", "aclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 0, GFLAGS), @@ -769,10 +769,10 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { GATE(HCLK_TSP, "hclk_tsp", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(17), 11, GFLAGS), GATE(HCLK_CRYPTO_MST, "hclk_crypto_mst", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 7, GFLAGS), GATE(HCLK_CRYPTO_SLV, "hclk_crypto_slv", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 8, GFLAGS), - GATE(0, "hclk_bus_niu", "hclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 13, GFLAGS), + GATE(0, "hclk_bus_niu", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 13, GFLAGS), GATE(HCLK_PDM, "hclk_pdm", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(28), 0, GFLAGS), - GATE(0, "pclk_bus_niu", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 14, GFLAGS), + GATE(0, "pclk_bus_niu", "pclk_bus", 0, RK3328_CLKGATE_CON(15), 14, GFLAGS), GATE(0, "pclk_efuse", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 9, GFLAGS), GATE(0, "pclk_otp", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(28), 4, GFLAGS), GATE(PCLK_I2C0, "pclk_i2c0", "pclk_bus", 0, RK3328_CLKGATE_CON(15), 10, GFLAGS), @@ -807,37 +807,42 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { GATE(0, "pclk_acodecphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 5, GFLAGS), GATE(PCLK_HDMIPHY, "pclk_hdmiphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 7, GFLAGS), GATE(0, "pclk_vdacphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 8, GFLAGS), - GATE(0, "pclk_phy_niu", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 15, GFLAGS), + GATE(0, "pclk_phy_niu", "pclk_phy_pre", 0, RK3328_CLKGATE_CON(15), 15, GFLAGS), /* PD_MMC */ - MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc", + MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "clk_sdmmc", RK3328_SDMMC_CON0, 1), - MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc", + MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "clk_sdmmc", RK3328_SDMMC_CON1, 1), - MMC(SCLK_SDIO_DRV, "sdio_drv", "sclk_sdio", + MMC(SCLK_SDIO_DRV, "sdio_drv", "clk_sdio", RK3328_SDIO_CON0, 1), - MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "sclk_sdio", + MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "clk_sdio", RK3328_SDIO_CON1, 1), - MMC(SCLK_EMMC_DRV, "emmc_drv", "sclk_emmc", + MMC(SCLK_EMMC_DRV, "emmc_drv", "clk_emmc", RK3328_EMMC_CON0, 1), - MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "sclk_emmc", + MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "clk_emmc", RK3328_EMMC_CON1, 1), - MMC(SCLK_SDMMC_EXT_DRV, "sdmmc_ext_drv", "sclk_sdmmc_ext", + MMC(SCLK_SDMMC_EXT_DRV, "sdmmc_ext_drv", "clk_sdmmc_ext", RK3328_SDMMC_EXT_CON0, 1), - MMC(SCLK_SDMMC_EXT_SAMPLE, "sdmmc_ext_sample", "sclk_sdmmc_ext", + MMC(SCLK_SDMMC_EXT_SAMPLE, "sdmmc_ext_sample", "clk_sdmmc_ext", RK3328_SDMMC_EXT_CON1, 1), }; static const char *const rk3328_critical_clocks[] __initconst = { "aclk_bus", + "aclk_bus_niu", "pclk_bus", + "pclk_bus_niu", "hclk_bus", + "hclk_bus_niu", "aclk_peri", "hclk_peri", + "hclk_peri_niu", "pclk_peri", + "pclk_peri_niu", "pclk_dbg", "aclk_core_niu", "aclk_gic400", @@ -861,6 +866,20 @@ static const char *const rk3328_critical_clocks[] __initconst = { "aclk_rga_niu", "pclk_vio_h2p", "hclk_vio_h2p", + "aclk_vio_niu", + "hclk_vio_niu", + "aclk_vop_niu", + "hclk_vop_niu", + "aclk_gpu_niu", + "aclk_rkvdec_niu", + "hclk_rkvdec_niu", + "aclk_vpu_niu", + "hclk_vpu_niu", + "aclk_rkvenc_niu", + "hclk_rkvenc_niu", + "aclk_gmac_niu", + "pclk_gmac_niu", + "pclk_phy_niu", }; static void __init rk3328_clk_init(struct device_node *np) diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c index 6847120b61cd..bca10d618f0a 100644 --- a/drivers/clk/rockchip/clk-rk3399.c +++ b/drivers/clk/rockchip/clk-rk3399.c @@ -57,6 +57,7 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = { RK3036_PLL_RATE(1656000000, 1, 69, 1, 1, 1, 0), RK3036_PLL_RATE(1632000000, 1, 68, 1, 1, 1, 0), RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0), + RK3036_PLL_RATE(1600000000, 3, 200, 1, 1, 1, 0), RK3036_PLL_RATE(1584000000, 1, 66, 1, 1, 1, 0), RK3036_PLL_RATE(1560000000, 1, 65, 1, 1, 1, 0), RK3036_PLL_RATE(1536000000, 1, 64, 1, 1, 1, 0), @@ -670,7 +671,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { RK3399_CLKGATE_CON(9), 7, GFLAGS, &rk3399_uart3_fracmux), - COMPOSITE(0, "pclk_ddr", mux_pll_src_cpll_gpll_p, CLK_IGNORE_UNUSED, + COMPOSITE(PCLK_DDR, "pclk_ddr", mux_pll_src_cpll_gpll_p, CLK_IGNORE_UNUSED, RK3399_CLKSEL_CON(6), 15, 1, MFLAGS, 8, 5, DFLAGS, RK3399_CLKGATE_CON(3), 4, GFLAGS), @@ -886,7 +887,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { RK3399_CLKGATE_CON(31), 8, GFLAGS), /* sdio & sdmmc */ - COMPOSITE(0, "hclk_sd", mux_pll_src_cpll_gpll_p, 0, + COMPOSITE(HCLK_SD, "hclk_sd", mux_pll_src_cpll_gpll_p, 0, RK3399_CLKSEL_CON(13), 15, 1, MFLAGS, 8, 5, DFLAGS, RK3399_CLKGATE_CON(12), 13, GFLAGS), GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_sd", 0, diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index 35dbd63c2f49..3cd8ad59e0b7 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -57,6 +57,7 @@ static struct clk *rockchip_clk_register_branch(const char *name, struct clk_divider *div = NULL; const struct clk_ops *mux_ops = NULL, *div_ops = NULL, *gate_ops = NULL; + int ret; if (num_parents > 1) { mux = kzalloc(sizeof(*mux), GFP_KERNEL); @@ -74,8 +75,10 @@ static struct clk *rockchip_clk_register_branch(const char *name, if (gate_offset >= 0) { gate = kzalloc(sizeof(*gate), GFP_KERNEL); - if (!gate) + if (!gate) { + ret = -ENOMEM; goto err_gate; + } gate->flags = gate_flags; gate->reg = base + gate_offset; @@ -86,8 +89,10 @@ static struct clk *rockchip_clk_register_branch(const char *name, if (div_width > 0) { div = kzalloc(sizeof(*div), GFP_KERNEL); - if (!div) + if (!div) { + ret = -ENOMEM; goto err_div; + } div->flags = div_flags; div->reg = base + muxdiv_offset; @@ -106,12 +111,19 @@ static struct clk *rockchip_clk_register_branch(const char *name, gate ? &gate->hw : NULL, gate_ops, flags); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto err_composite; + } + return clk; +err_composite: + kfree(div); err_div: kfree(gate); err_gate: kfree(mux); - return ERR_PTR(-ENOMEM); + return ERR_PTR(ret); } struct rockchip_clk_frac { @@ -291,8 +303,10 @@ static struct clk *rockchip_clk_register_frac_branch( init.num_parents = child->num_parents; mux_clk = clk_register(NULL, &frac_mux->hw); - if (IS_ERR(mux_clk)) + if (IS_ERR(mux_clk)) { + kfree(frac); return clk; + } rockchip_clk_add_lookup(ctx, mux_clk, child->id); diff --git a/drivers/clk/ti/clkctrl.c b/drivers/clk/ti/clkctrl.c index afa0d6bfc5c1..421b05392220 100644 --- a/drivers/clk/ti/clkctrl.c +++ b/drivers/clk/ti/clkctrl.c @@ -537,6 +537,8 @@ static void __init _ti_omap4_clkctrl_setup(struct device_node *node) init.parent_names = ®_data->parent; init.num_parents = 1; init.flags = 0; + if (reg_data->flags & CLKF_SET_RATE_PARENT) + init.flags |= CLK_SET_RATE_PARENT; init.name = kasprintf(GFP_KERNEL, "%s:%s:%04x:%d", node->parent->name, node->name, reg_data->offset, 0); diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h index 90b86aadfda7..b58278077226 100644 --- a/drivers/clk/ti/clock.h +++ b/drivers/clk/ti/clock.h @@ -76,6 +76,11 @@ enum { #define CLKF_CORE (1 << 9) #define CLKF_J_TYPE (1 << 10) +/* CLKCTRL flags */ +#define CLKF_SW_SUP BIT(5) +#define CLKF_HW_SUP BIT(6) +#define CLKF_NO_IDLEST BIT(7) + #define CLK(dev, con, ck) \ { \ .lk = { \ @@ -185,10 +190,6 @@ extern const struct omap_clkctrl_data am438x_clkctrl_data[]; extern const struct omap_clkctrl_data dm814_clkctrl_data[]; extern const struct omap_clkctrl_data dm816_clkctrl_data[]; -#define CLKF_SW_SUP BIT(0) -#define CLKF_HW_SUP BIT(1) -#define CLKF_NO_IDLEST BIT(2) - typedef void (*ti_of_clk_init_cb_t)(void *, struct device_node *); struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw, diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c index d244e724e198..ebc78ab2df05 100644 --- a/drivers/clk/uniphier/clk-uniphier-sys.c +++ b/drivers/clk/uniphier/clk-uniphier-sys.c @@ -57,6 +57,14 @@ #define UNIPHIER_PRO4_SYS_CLK_USB3(idx, ch) \ UNIPHIER_CLK_GATE("usb3" #ch, (idx), NULL, 0x2104, 16 + (ch)) +#define UNIPHIER_PRO4_SYS_CLK_AIO(idx) \ + UNIPHIER_CLK_FACTOR("aio-io200m", -1, "spll", 1, 8), \ + UNIPHIER_CLK_GATE("aio", (idx), "aio-io200m", 0x2104, 13) + +#define UNIPHIER_PRO5_SYS_CLK_AIO(idx) \ + UNIPHIER_CLK_FACTOR("aio-io200m", -1, "spll", 1, 12), \ + UNIPHIER_CLK_GATE("aio", (idx), "aio-io200m", 0x2104, 13) + #define UNIPHIER_LD11_SYS_CLK_AIO(idx) \ UNIPHIER_CLK_FACTOR("aio-io200m", -1, "spll", 1, 10), \ UNIPHIER_CLK_GATE("aio", (idx), "aio-io200m", 0x2108, 0) @@ -94,16 +102,22 @@ const struct uniphier_clk_data uniphier_pro4_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("upll", -1, "ref", 288, 25), /* 288 MHz */ UNIPHIER_CLK_FACTOR("a2pll", -1, "upll", 256, 125), /* 589.824 MHz */ UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */ + UNIPHIER_CLK_FACTOR("gpll", -1, "ref", 10, 1), /* 250 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 8), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 32), UNIPHIER_LD4_SYS_CLK_NAND(2), UNIPHIER_LD4_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), UNIPHIER_PRO4_SYS_CLK_ETHER(6), + UNIPHIER_CLK_GATE("ether-gb", 7, "gpll", 0x2104, 5), UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* HSC, MIO, RLE */ + UNIPHIER_CLK_GATE("ether-phy", 10, "ref", 0x2260, 0), UNIPHIER_PRO4_SYS_CLK_GIO(12), /* Ether, SATA, USB3 */ UNIPHIER_PRO4_SYS_CLK_USB3(14, 0), UNIPHIER_PRO4_SYS_CLK_USB3(15, 1), + UNIPHIER_CLK_GATE("sata0", 28, NULL, 0x2104, 18), + UNIPHIER_CLK_GATE("sata1", 29, NULL, 0x2104, 19), + UNIPHIER_PRO4_SYS_CLK_AIO(40), { /* sentinel */ } }; @@ -132,6 +146,8 @@ const struct uniphier_clk_data uniphier_pro5_sys_clk_data[] = { UNIPHIER_PRO4_SYS_CLK_GIO(12), /* PCIe, USB3 */ UNIPHIER_PRO4_SYS_CLK_USB3(14, 0), UNIPHIER_PRO4_SYS_CLK_USB3(15, 1), + UNIPHIER_CLK_GATE("pcie", 24, NULL, 0x2108, 2), + UNIPHIER_PRO5_SYS_CLK_AIO(40), { /* sentinel */ } }; @@ -149,6 +165,8 @@ const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = { /* The document mentions 0x2104 bit 18, but not functional */ UNIPHIER_CLK_GATE("usb30-phy", 16, NULL, 0x2104, 19), UNIPHIER_CLK_GATE("usb31-phy", 20, NULL, 0x2104, 20), + UNIPHIER_CLK_GATE("sata0", 28, NULL, 0x2104, 22), + UNIPHIER_PRO5_SYS_CLK_AIO(40), { /* sentinel */ } }; @@ -205,6 +223,7 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = { UNIPHIER_CLK_GATE("usb30", 14, NULL, 0x210c, 14), UNIPHIER_CLK_GATE("usb30-phy0", 16, NULL, 0x210c, 12), UNIPHIER_CLK_GATE("usb30-phy1", 17, NULL, 0x210c, 13), + UNIPHIER_CLK_GATE("pcie", 24, NULL, 0x210c, 4), UNIPHIER_LD11_SYS_CLK_AIO(40), UNIPHIER_LD11_SYS_CLK_EVEA(41), UNIPHIER_LD11_SYS_CLK_EXIV(42), @@ -233,6 +252,8 @@ const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[] = { UNIPHIER_LD20_SYS_CLK_SD, UNIPHIER_LD11_SYS_CLK_NAND(2), UNIPHIER_LD11_SYS_CLK_EMMC(4), + UNIPHIER_CLK_GATE("ether0", 6, NULL, 0x210c, 9), + UNIPHIER_CLK_GATE("ether1", 7, NULL, 0x210c, 10), UNIPHIER_CLK_GATE("usb30", 12, NULL, 0x210c, 4), /* =GIO0 */ UNIPHIER_CLK_GATE("usb31-0", 13, NULL, 0x210c, 5), /* =GIO1 */ UNIPHIER_CLK_GATE("usb31-1", 14, NULL, 0x210c, 6), /* =GIO1-1 */ @@ -241,6 +262,10 @@ const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[] = { UNIPHIER_CLK_GATE("usb30-phy2", 18, NULL, 0x210c, 20), UNIPHIER_CLK_GATE("usb31-phy0", 20, NULL, 0x210c, 17), UNIPHIER_CLK_GATE("usb31-phy1", 21, NULL, 0x210c, 19), + UNIPHIER_CLK_GATE("pcie", 24, NULL, 0x210c, 3), + UNIPHIER_CLK_GATE("sata0", 28, NULL, 0x210c, 7), + UNIPHIER_CLK_GATE("sata1", 29, NULL, 0x210c, 8), + UNIPHIER_CLK_GATE("sata-phy", 30, NULL, 0x210c, 21), /* CPU gears */ UNIPHIER_CLK_DIV4("cpll", 2, 3, 4, 8), UNIPHIER_CLK_DIV4("spll", 2, 3, 4, 8), |