@@ -254,7 +254,15 @@ static unsigned long clk_sysclk_recalc(struct clk *clk)
u32 v, plldiv;
struct pll_data *pll;
unsigned long rate = clk->rate;
+ struct clk *parent = clk;
+ if (clk == NULL || IS_ERR(clk))
+ return -EINVAL;
+ while (parent->parent->parent)
+ parent = parent->parent;
+
+ if (parent == clk)
+ return -EPERM;
/* If this is the PLL base clock, no more calculations needed */
if (clk->pll_data)
return rate;
@@ -262,13 +270,13 @@ static unsigned long clk_sysclk_recalc(struct clk *clk)
if (WARN_ON(!clk->parent))
return rate;
- rate = clk->parent->rate;
+ rate = parent->rate;
+
/* Otherwise, the parent must be a PLL */
- if (WARN_ON(!clk->parent->pll_data))
+ if (WARN_ON(!parent->pll_data))
return rate;
-
- pll = clk->parent->pll_data;
+ pll = parent->pll_data;
/* If pre-PLL, source clock is before the multiplier and divider(s) */
if (clk->flags & PRE_PLL)
@@ -293,26 +301,33 @@ int davinci_set_sysclk_rate(struct clk *clk, unsigned long rate)
struct pll_data *pll;
unsigned long input;
unsigned ratio = 0;
+ struct clk *parent = clk;
+
+ /* searching the right ancestor (pll1_clk or pll2_clk) */
+ while (parent->parent->parent)
+ parent = parent->parent;
+ if (parent == clk)
+ return -EPERM;
/* If this is the PLL base clock, wrong function to call */
if (clk->pll_data)
return 0;
/* There must be a parent... */
- if (WARN_ON(!clk->parent))
+ if (WARN_ON(!parent))
return 0;
/* ... the parent must be a PLL... */
- if (WARN_ON(!clk->parent->pll_data))
+ if (WARN_ON(!parent->pll_data))
return 0;
/* ... and this clock must have a divider. */
if (WARN_ON(!clk->div_reg))
return 0;
- pll = clk->parent->pll_data;
+ pll = parent->pll_data;
- input = clk->parent->rate;
+ input = parent->rate;
/* If pre-PLL, source clock is before the multiplier and divider(s) */
if (clk->flags & PRE_PLL)
@@ -343,6 +358,7 @@ int davinci_set_sysclk_rate(struct clk *clk, unsigned long rate)
return 0;
}
+EXPORT_SYMBOL(davinci_set_sysclk_rate);
static unsigned long clk_leafclk_recalc(struct clk *clk)
{
@@ -50,6 +50,11 @@
#define PLLDIV_EN BIT(15)
#define PLLDIV_RATIO_MASK 0x1f
+#define PERI_CLKCTL 0x48
+#define CLOCKOUT2EN 2
+#define CLOCKOUT1EN 1
+#define CLOCKOUT0EN 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
@@ -40,6 +40,11 @@
#include "mux.h"
#define DM365_REF_FREQ 24000000 /* 24 MHz on the DM365 EVM */
+#define PINMUX0 0x00
+#define PINMUX1 0x04
+#define PINMUX2 0x08
+#define PINMUX3 0x0c
+#define PINMUX4 0x10
static struct pll_data pll1_data = {
.num = 1,
@@ -124,6 +129,7 @@ static struct clk pll1_sysclk6 = {
.parent = &pll1_clk,
.flags = CLK_PLL,
.div_reg = PLLDIV6,
+ .set_rate = davinci_set_sysclk_rate,
};
static struct clk pll1_sysclk7 = {
@@ -145,6 +151,14 @@ static struct clk pll1_sysclk9 = {
.parent = &pll1_clk,
.flags = CLK_PLL,
.div_reg = PLLDIV9,
+ .set_rate = davinci_set_sysclk_rate,
+};
+
+static struct clk clkout2_clk = {
+ .name = "clkout2",
+ .parent = &pll1_sysclk9,
+ .flags = CLK_PLL,
+ .set_rate = dm365_clkout2_set_rate,
};
static struct clk pll2_clk = {
@@ -421,6 +435,7 @@ static struct clk_lookup dm365_clks[] = {
CLK(NULL, "pll1_sysclk7", &pll1_sysclk7),
CLK(NULL, "pll1_sysclk8", &pll1_sysclk8),
CLK(NULL, "pll1_sysclk9", &pll1_sysclk9),
+ CLK(NULL, "clkout2", &clkout2_clk),
CLK(NULL, "pll2", &pll2_clk),
CLK(NULL, "pll2_aux", &pll2_aux_clk),
CLK(NULL, "clkout1", &clkout1_clk),
@@ -657,6 +672,48 @@ static struct resource dm365_spi0_resources[] = {
},
};
+int dm365_clkout2_set_rate(unsigned long rate)
+{
+ int ret = -EINVAL;
+ int i, err, min_err, i_min_err;
+ u32 regval;
+ struct clk *clk;
+ static void __iomem *system_module_base;
+
+ clk = &clkout2_clk;
+ system_module_base = ioremap(DAVINCI_SYSTEM_MODULE_BASE, SZ_4K);
+ regval = __raw_readl(system_module_base + PERI_CLKCTL);
+
+ /* check all possibilities to get best fitting for the required freq */
+ i_min_err = min_err = INT_MAX;
+ for (i = 0x0F; i > 0; i--) {
+ if (clk->parent->set_rate) {
+ ret = clk_set_rate(clk->parent, rate * i) ;
+ err = clk_get_rate(clk->parent) - rate * i;
+ if (min_err > abs(err)) {
+ min_err = abs(err);
+ i_min_err = i;
+ }
+ }
+ }
+ ret = clk_set_rate(clk->parent, rate * i_min_err) ;
+ if (ret)
+ return ret;
+
+ /* setup DIV1 value */
+ regval &= ~(0x0F << 3);
+ regval |= (i_min_err - 1) << 3;
+
+ /* to make changes work stop CLOCKOUT & start it again */
+ regval |= 1 << CLOCKOUT2EN;
+ __raw_writel(regval, system_module_base + PERI_CLKCTL);
+ regval &= ~(1 << CLOCKOUT2EN);
+ __raw_writel(regval, system_module_base + PERI_CLKCTL);
+
+ return ret;
+}
+EXPORT_SYMBOL(dm365_clkout2_set_rate);
+
static struct platform_device dm365_spi0_device = {
.name = "spi_davinci",
.id = 0,
@@ -49,4 +49,5 @@ void dm365_init_spi0(unsigned chipselect_mask,
struct spi_board_info *info, unsigned len);
void dm365_set_vpfe_config(struct vpfe_config *cfg);
+int dm365_clkout2_set_rate(unsigned long rate);
#endif /* __ASM_ARCH_DM365_H */