Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
# 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_DA850) += pll-da850.o
obj-y += psc.o
obj-$(CONFIG_ARCH_DAVINCI_DA850) += psc-da850.o
endif
@@ -0,0 +1,787 @@
// 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.h>
#include <linux/platform_data/clk-da8xx-cfgchip.h>
#include <linux/platform_device.h>
#include <linux/property.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);
return PTR_ERR_OR_ZERO(gate);
}
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 = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.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 int da8xx_usb0_clk48_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
req->rate = 48000000;
return 0;
}
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,
.determine_rate = da8xx_usb0_clk48_determine_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)) {
return dev_err_cast_probe(dev, fck_clk, "Missing fck clock\n");
}
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 = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.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
* @dev: The device
* @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, struct_size(clk_data, hws, 2),
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(usb1) == -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;
da8xx_cfgchip_init clk_init = NULL;
struct regmap *regmap = NULL;
clk_init = device_get_match_data(dev);
if (clk_init) {
struct device_node *parent __free(device_node) = of_get_parent(dev->of_node);
regmap = syscon_node_to_regmap(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);
+228
View File
@@ -0,0 +1,228 @@
// 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/clk/davinci.h>
#include <linux/clkdev.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mfd/da8xx-cfgchip.h>
#include <linux/mfd/syscon.h>
#include <linux/of_address.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 regmap *cfgchip)
{
struct clk *clk;
davinci_pll_clk_register(dev, &da850_pll0_info, "ref_clk", base, cfgchip);
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
};
void of_da850_pll0_init(struct device_node *node)
{
void __iomem *base;
struct regmap *cfgchip;
base = of_iomap(node, 0);
if (!base) {
pr_err("%s: ioremap failed\n", __func__);
return;
}
cfgchip = syscon_regmap_lookup_by_compatible("ti,da830-cfgchip");
of_davinci_pll_init(NULL, node, &da850_pll0_info,
&da850_pll0_obsclk_info,
da850_pll0_sysclk_info, 7, base, cfgchip);
}
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 regmap *cfgchip)
{
struct clk *clk;
davinci_pll_clk_register(dev, &da850_pll1_info, "oscin", base, cfgchip);
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, struct regmap *cfgchip)
{
return of_davinci_pll_init(dev, dev->of_node, &da850_pll1_info,
&da850_pll1_obsclk_info,
da850_pll1_sysclk_info, 3, base, cfgchip);
}
+955
View File
@@ -0,0 +1,955 @@
// 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/clk/davinci.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.h>
#include <linux/platform_device.h>
#include <linux/property.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 maximum 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 void 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
* @dev: The PLL platform device or NULL
* @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;
struct clk *clk;
int ret;
gate = kzalloc_obj(*gate);
if (!gate)
return ERR_PTR(-ENOMEM);
gate->reg = reg;
gate->bit_idx = DIV_ENABLE_SHIFT;
divider = kzalloc_obj(*divider);
if (!divider) {
ret = -ENOMEM;
goto err_free_gate;
}
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;
}
clk = clk_register_composite(dev, name, parent_names, num_parents,
NULL, NULL, &divider->hw, divider_ops,
&gate->hw, &clk_gate_ops, flags);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto err_free_divider;
}
return clk;
err_free_divider:
kfree(divider);
err_free_gate:
kfree(gate);
return ERR_PTR(ret);
}
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 notifier_block davinci_pllen_notifier = {
.notifier_call = davinci_pllen_rate_change,
};
/**
* davinci_pll_clk_register - Register a PLL clock
* @dev: The PLL platform device or NULL
* @info: The device-specific clock info
* @parent_name: The parent clock name
* @base: The PLL's memory region
* @cfgchip: CFGCHIP syscon regmap for info->unlock_reg or NULL
*
* 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 regmap *cfgchip)
{
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 *oscin_clk = NULL;
struct clk *prediv_clk = NULL;
struct clk *pllout_clk;
struct clk *postdiv_clk = NULL;
struct clk *pllen_clk;
int ret;
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.
*/
oscin_clk = clk_register_fixed_factor(dev, OSCIN_CLK_NAME,
parent_name, 0, 1, 1);
if (IS_ERR(oscin_clk))
return oscin_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)
prediv_clk = clk_register_fixed_factor(dev, prediv_name,
parent_name, flags, 1, 8);
else
prediv_clk = davinci_pll_div_register(dev, prediv_name,
parent_name, base + PREDIV, fixed, flags);
if (IS_ERR(prediv_clk)) {
ret = PTR_ERR(prediv_clk);
goto err_unregister_oscin;
}
parent_name = prediv_name;
}
/* Unlock writing to PLL registers */
if (info->unlock_reg) {
if (IS_ERR_OR_NULL(cfgchip))
dev_warn(dev, "Failed to get CFGCHIP (%ld)\n",
PTR_ERR(cfgchip));
else
regmap_write_bits(cfgchip, info->unlock_reg,
info->unlock_mask, 0);
}
pllout = kzalloc_obj(*pllout);
if (!pllout) {
ret = -ENOMEM;
goto err_unregister_prediv;
}
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 = clk_register(dev, &pllout->hw);
if (IS_ERR(pllout_clk)) {
ret = PTR_ERR(pllout_clk);
goto err_free_pllout;
}
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;
postdiv_clk = davinci_pll_div_register(dev, postdiv_name,
parent_name, base + POSTDIV, fixed, flags);
if (IS_ERR(postdiv_clk)) {
ret = PTR_ERR(postdiv_clk);
goto err_unregister_pllout;
}
parent_name = postdiv_name;
}
pllen = kzalloc_obj(*pllen);
if (!pllen) {
ret = -ENOMEM;
goto err_unregister_postdiv;
}
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;
pllen_clk = clk_register(dev, &pllen->hw);
if (IS_ERR(pllen_clk)) {
ret = PTR_ERR(pllen_clk);
goto err_free_pllen;
}
clk_notifier_register(pllen_clk, &davinci_pllen_notifier);
return pllout_clk;
err_free_pllen:
kfree(pllen);
err_unregister_postdiv:
clk_unregister(postdiv_clk);
err_unregister_pllout:
clk_unregister(pllout_clk);
err_free_pllout:
kfree(pllout);
err_unregister_prediv:
clk_unregister(prediv_clk);
err_unregister_oscin:
clk_unregister(oscin_clk);
return ERR_PTR(ret);
}
/**
* davinci_pll_auxclk_register - Register bypass clock (AUXCLK)
* @dev: The PLL platform device or NULL
* @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)
* @dev: The PLL platform device or NULL
* @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)
* @dev: The PLL platform device or NULL
* @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;
struct clk *clk;
u32 oscdiv;
int ret;
mux = kzalloc_obj(*mux);
if (!mux)
return ERR_PTR(-ENOMEM);
mux->reg = base + OCSEL;
mux->table = info->table;
mux->mask = info->ocsrc_mask;
gate = kzalloc_obj(*gate);
if (!gate) {
ret = -ENOMEM;
goto err_free_mux;
}
gate->reg = base + CKEN;
gate->bit_idx = CKEN_OBSCLK_SHIFT;
divider = kzalloc_obj(*divider);
if (!divider) {
ret = -ENOMEM;
goto err_free_gate;
}
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);
clk = clk_register_composite(dev, info->name, info->parent_names,
info->num_parents,
&mux->hw, &clk_mux_ops,
&divider->hw, &clk_divider_ops,
&gate->hw, &clk_gate_ops, 0);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto err_free_divider;
}
return clk;
err_free_divider:
kfree(divider);
err_free_gate:
kfree(gate);
err_free_mux:
kfree(mux);
return ERR_PTR(ret);
}
/* 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)
* @dev: The PLL platform device or NULL
* @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;
int ret;
/* PLLDIVn registers are not entirely consecutive */
if (info->id < 4)
reg = PLLDIV1 + 4 * (info->id - 1);
else
reg = PLLDIV4 + 4 * (info->id - 4);
gate = kzalloc_obj(*gate);
if (!gate)
return ERR_PTR(-ENOMEM);
gate->reg = base + reg;
gate->bit_idx = DIV_ENABLE_SHIFT;
divider = kzalloc_obj(*divider);
if (!divider) {
ret = -ENOMEM;
goto err_free_gate;
}
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, &divider->hw, divider_ops,
&gate->hw, &clk_gate_ops, flags);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto err_free_divider;
}
clk_notifier_register(clk, &davinci_pll_sysclk_notifier);
return clk;
err_free_divider:
kfree(divider);
err_free_gate:
kfree(gate);
return ERR_PTR(ret);
}
int of_davinci_pll_init(struct device *dev, struct device_node *node,
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 regmap *cfgchip)
{
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, cfgchip);
if (IS_ERR(clk)) {
dev_err(dev, "failed to register %s\n", info->name);
return PTR_ERR(clk);
}
child = of_get_available_child_by_name(node, "pllout");
if (child) {
of_clk_add_provider(child, of_clk_src_simple_get, clk);
of_node_put(child);
}
child = of_get_available_child_by_name(node, "sysclk");
if (child) {
struct clk_onecell_data *clk_data;
struct clk **clks;
int n_clks = max_sysclk_id + 1;
int i;
clk_data = kzalloc_obj(*clk_data);
if (!clk_data) {
of_node_put(child);
return -ENOMEM;
}
clks = kmalloc_objs(*clks, n_clks);
if (!clks) {
kfree(clk_data);
of_node_put(child);
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_available_child_by_name(node, "auxclk");
if (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_available_child_by_name(node, "obsclk");
if (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;
}
/* needed in early boot for clocksource/clockevent */
CLK_OF_DECLARE(da850_pll0, "ti,da850-pll0", of_da850_pll0_init);
static const struct of_device_id davinci_pll_of_match[] = {
{ .compatible = "ti,da850-pll1", .data = of_da850_pll1_init },
{ }
};
static const struct platform_device_id davinci_pll_id_table[] = {
{ .name = "da850-pll0", .driver_data = (kernel_ulong_t)da850_pll0_init },
{ .name = "da850-pll1", .driver_data = (kernel_ulong_t)da850_pll1_init },
{ }
};
typedef int (*davinci_pll_init)(struct device *dev, void __iomem *base,
struct regmap *cfgchip);
static int davinci_pll_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
davinci_pll_init pll_init = NULL;
struct regmap *cfgchip;
void __iomem *base;
pll_init = device_get_match_data(dev);
if (!pll_init && 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;
}
cfgchip = syscon_regmap_lookup_by_compatible("ti,da830-cfgchip");
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
return pll_init(dev, base, cfgchip);
}
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 void 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;
regset = kzalloc_obj(*regset);
if (!regset)
return;
regset->regs = davinci_pll_regs;
regset->nregs = ARRAY_SIZE(davinci_pll_regs);
regset->base = pll->base;
debugfs_create_regset32("registers", 0400, dentry, regset);
}
#endif
+129
View File
@@ -0,0 +1,129 @@
/* 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/regmap.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] corresponding 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 regmap *cfgchip);
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, struct device_node *node,
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 regmap *cfgchip);
/* Platform-specific callbacks */
int da850_pll1_init(struct device *dev, void __iomem *base, struct regmap *cfgchip);
void of_da850_pll0_init(struct device_node *node);
int of_da850_pll1_init(struct device *dev, void __iomem *base, struct regmap *cfgchip);
#endif /* __CLK_DAVINCI_PLL_H___ */
+148
View File
@@ -0,0 +1,148 @@
// 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_CLKDEV1(emifa_clkdev, NULL, "ti-aemif");
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,
};
+556
View File
@@ -0,0 +1,556 @@
// 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/clk/davinci.h>
#include <linux/clkdev.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.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 or NULL
* @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
* @dev: the clocks's device or NULL
* @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 = kzalloc_obj(*lpsc);
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 = clk_hw_register(dev, &lpsc->hw);
if (ret < 0) {
kfree(lpsc);
return ERR_PTR(ret);
}
/* for now, genpd is only registered when using device-tree */
if (!dev || !dev->of_node)
return lpsc;
/* 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);
if (!lpsc->pm_domain.name) {
clk_hw_unregister(&lpsc->hw);
kfree(lpsc);
return ERR_PTR(-ENOMEM);
}
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;
}
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 = kzalloc_obj(*psc);
if (!psc)
return ERR_PTR(-ENOMEM);
clks = kmalloc_objs(*clks, num_clks);
if (!clks) {
ret = -ENOMEM;
goto err_free_psc;
}
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 = kzalloc_objs(*pm_domains, num_clks);
if (!pm_domains) {
ret = -ENOMEM;
goto err_free_clks;
}
psc->pm_data.domains = pm_domains;
psc->pm_data.num_domains = num_clks;
regmap = regmap_init_mmio(dev, base, &davinci_psc_regmap_config);
if (IS_ERR(regmap)) {
ret = PTR_ERR(regmap);
goto err_free_pm_domains;
}
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;
}
/*
* for now, a reset controller is only registered when there is a device
* to associate it with.
*/
if (!dev)
return psc;
psc->rcdev.ops = &davinci_psc_reset_ops;
psc->rcdev.owner = THIS_MODULE;
psc->rcdev.dev = dev;
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;
err_free_pm_domains:
kfree(pm_domains);
err_free_clks:
kfree(clks);
err_free_psc:
kfree(psc);
return ERR_PTR(ret);
}
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 = "da850-psc0", .driver_data = (kernel_ulong_t)&da850_psc0_init_data },
{ .name = "da850-psc1", .driver_data = (kernel_ulong_t)&da850_psc1_init_data },
{ }
};
static int davinci_psc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct davinci_psc_init_data *init_data = NULL;
void __iomem *base;
int ret;
init_data = device_get_match_data(dev);
if (!init_data && 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;
}
base = devm_platform_ioremap_resource(pdev, 0);
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);
+102
View File
@@ -0,0 +1,102 @@
/* 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 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;
#endif /* __CLK_DAVINCI_PSC_H__ */