1
linux/drivers/clk/clk-ep93xx.c
Dan Carpenter 53cf1dc480 clk: ep93xx: Fix off by one in ep93xx_div_recalc_rate()
The psc->div[] array has psc->num_div elements.  These values come from
when we call clk_hw_register_div().  It's adc_divisors and
ARRAY_SIZE(adc_divisors)) and so on.  So this condition needs to be >=
instead of > to prevent an out of bounds read.

Fixes: 9645ccc7bd ("ep93xx: clock: convert in-place to COMMON_CLK")
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Acked-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>
Reviewed-by: Nikita Shubin <nikita.shubin@maquefel.me>
Signed-off-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>
Link: https://lore.kernel.org/r/1caf01ad4c0a8069535813c26c7f0b8ea011155e.camel@linaro.org
[arnd: the original patch was for arch/arm/mach-ep93xx/clock.c,
but the same bug ended up in arch/arm/mach-ep93xx/clock.c.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
2024-09-12 14:33:12 +00:00

851 lines
23 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Clock control for Cirrus EP93xx chips.
* Copyright (C) 2021 Nikita Shubin <nikita.shubin@maquefel.me>
*
* Based on a rewrite of arch/arm/mach-ep93xx/clock.c:
* Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
*/
#define pr_fmt(fmt) "ep93xx " KBUILD_MODNAME ": " fmt
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/clk-provider.h>
#include <linux/math.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <linux/soc/cirrus/ep93xx.h>
#include <dt-bindings/clock/cirrus,ep9301-syscon.h>
#include <asm/div64.h>
#define EP93XX_EXT_CLK_RATE 14745600
#define EP93XX_EXT_RTC_RATE 32768
#define EP93XX_SYSCON_POWER_STATE 0x00
#define EP93XX_SYSCON_PWRCNT 0x04
#define EP93XX_SYSCON_PWRCNT_UARTBAUD BIT(29)
#define EP93XX_SYSCON_PWRCNT_USH_EN 28
#define EP93XX_SYSCON_PWRCNT_DMA_M2M1 27
#define EP93XX_SYSCON_PWRCNT_DMA_M2M0 26
#define EP93XX_SYSCON_PWRCNT_DMA_M2P8 25
#define EP93XX_SYSCON_PWRCNT_DMA_M2P9 24
#define EP93XX_SYSCON_PWRCNT_DMA_M2P6 23
#define EP93XX_SYSCON_PWRCNT_DMA_M2P7 22
#define EP93XX_SYSCON_PWRCNT_DMA_M2P4 21
#define EP93XX_SYSCON_PWRCNT_DMA_M2P5 20
#define EP93XX_SYSCON_PWRCNT_DMA_M2P2 19
#define EP93XX_SYSCON_PWRCNT_DMA_M2P3 18
#define EP93XX_SYSCON_PWRCNT_DMA_M2P0 17
#define EP93XX_SYSCON_PWRCNT_DMA_M2P1 16
#define EP93XX_SYSCON_CLKSET1 0x20
#define EP93XX_SYSCON_CLKSET1_NBYP1 BIT(23)
#define EP93XX_SYSCON_CLKSET2 0x24
#define EP93XX_SYSCON_CLKSET2_NBYP2 BIT(19)
#define EP93XX_SYSCON_CLKSET2_PLL2_EN BIT(18)
#define EP93XX_SYSCON_DEVCFG 0x80
#define EP93XX_SYSCON_DEVCFG_U3EN 24
#define EP93XX_SYSCON_DEVCFG_U2EN 20
#define EP93XX_SYSCON_DEVCFG_U1EN 18
#define EP93XX_SYSCON_VIDCLKDIV 0x84
#define EP93XX_SYSCON_CLKDIV_ENABLE 15
#define EP93XX_SYSCON_CLKDIV_ESEL BIT(14)
#define EP93XX_SYSCON_CLKDIV_PSEL BIT(13)
#define EP93XX_SYSCON_CLKDIV_MASK GENMASK(14, 13)
#define EP93XX_SYSCON_CLKDIV_PDIV_SHIFT 8
#define EP93XX_SYSCON_I2SCLKDIV 0x8c
#define EP93XX_SYSCON_I2SCLKDIV_SENA 31
#define EP93XX_SYSCON_I2SCLKDIV_ORIDE BIT(29)
#define EP93XX_SYSCON_I2SCLKDIV_SPOL BIT(19)
#define EP93XX_SYSCON_KEYTCHCLKDIV 0x90
#define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN 31
#define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV 16
#define EP93XX_SYSCON_KEYTCHCLKDIV_KEN 15
#define EP93XX_SYSCON_KEYTCHCLKDIV_KDIV 0
#define EP93XX_SYSCON_CHIPID 0x94
#define EP93XX_SYSCON_CHIPID_ID 0x9213
#define EP93XX_FIXED_CLK_COUNT 21
static const char ep93xx_adc_divisors[] = { 16, 4 };
static const char ep93xx_sclk_divisors[] = { 2, 4 };
static const char ep93xx_lrclk_divisors[] = { 32, 64, 128 };
struct ep93xx_clk {
struct clk_hw hw;
u16 idx;
u16 reg;
u32 mask;
u8 bit_idx;
u8 shift;
u8 width;
u8 num_div;
const char *div;
};
struct ep93xx_clk_priv {
spinlock_t lock;
struct ep93xx_regmap_adev *aux_dev;
struct device *dev;
void __iomem *base;
struct regmap *map;
struct clk_hw *fixed[EP93XX_FIXED_CLK_COUNT];
struct ep93xx_clk reg[];
};
static struct ep93xx_clk *ep93xx_clk_from(struct clk_hw *hw)
{
return container_of(hw, struct ep93xx_clk, hw);
}
static struct ep93xx_clk_priv *ep93xx_priv_from(struct ep93xx_clk *clk)
{
return container_of(clk, struct ep93xx_clk_priv, reg[clk->idx]);
}
static void ep93xx_clk_write(struct ep93xx_clk_priv *priv, unsigned int reg, unsigned int val)
{
struct ep93xx_regmap_adev *aux = priv->aux_dev;
aux->write(aux->map, aux->lock, reg, val);
}
static int ep93xx_clk_is_enabled(struct clk_hw *hw)
{
struct ep93xx_clk *clk = ep93xx_clk_from(hw);
struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
u32 val;
regmap_read(priv->map, clk->reg, &val);
return !!(val & BIT(clk->bit_idx));
}
static int ep93xx_clk_enable(struct clk_hw *hw)
{
struct ep93xx_clk *clk = ep93xx_clk_from(hw);
struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
u32 val;
guard(spinlock_irqsave)(&priv->lock);
regmap_read(priv->map, clk->reg, &val);
val |= BIT(clk->bit_idx);
ep93xx_clk_write(priv, clk->reg, val);
return 0;
}
static void ep93xx_clk_disable(struct clk_hw *hw)
{
struct ep93xx_clk *clk = ep93xx_clk_from(hw);
struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
u32 val;
guard(spinlock_irqsave)(&priv->lock);
regmap_read(priv->map, clk->reg, &val);
val &= ~BIT(clk->bit_idx);
ep93xx_clk_write(priv, clk->reg, val);
}
static const struct clk_ops clk_ep93xx_gate_ops = {
.enable = ep93xx_clk_enable,
.disable = ep93xx_clk_disable,
.is_enabled = ep93xx_clk_is_enabled,
};
static int ep93xx_clk_register_gate(struct ep93xx_clk *clk,
const char *name,
struct clk_parent_data *parent_data,
unsigned long flags,
unsigned int reg,
u8 bit_idx)
{
struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
struct clk_init_data init = { };
init.name = name;
init.ops = &clk_ep93xx_gate_ops;
init.flags = flags;
init.parent_data = parent_data;
init.num_parents = 1;
clk->reg = reg;
clk->bit_idx = bit_idx;
clk->hw.init = &init;
return devm_clk_hw_register(priv->dev, &clk->hw);
}
static u8 ep93xx_mux_get_parent(struct clk_hw *hw)
{
struct ep93xx_clk *clk = ep93xx_clk_from(hw);
struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
u32 val;
regmap_read(priv->map, clk->reg, &val);
val &= EP93XX_SYSCON_CLKDIV_MASK;
switch (val) {
case EP93XX_SYSCON_CLKDIV_ESEL:
return 1; /* PLL1 */
case EP93XX_SYSCON_CLKDIV_MASK:
return 2; /* PLL2 */
default:
return 0; /* XTALI */
};
}
static int ep93xx_mux_set_parent_lock(struct clk_hw *hw, u8 index)
{
struct ep93xx_clk *clk = ep93xx_clk_from(hw);
struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
u32 val;
if (index >= 3)
return -EINVAL;
guard(spinlock_irqsave)(&priv->lock);
regmap_read(priv->map, clk->reg, &val);
val &= ~(EP93XX_SYSCON_CLKDIV_MASK);
val |= index > 0 ? EP93XX_SYSCON_CLKDIV_ESEL : 0;
val |= index > 1 ? EP93XX_SYSCON_CLKDIV_PSEL : 0;
ep93xx_clk_write(priv, clk->reg, val);
return 0;
}
static bool is_best(unsigned long rate, unsigned long now,
unsigned long best)
{
return abs_diff(rate, now) < abs_diff(rate, best);
}
static int ep93xx_mux_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
unsigned long best_rate = 0, actual_rate, mclk_rate;
unsigned long rate = req->rate;
struct clk_hw *parent_best = NULL;
unsigned long parent_rate_best;
unsigned long parent_rate;
int div, pdiv;
unsigned int i;
/*
* Try the two pll's and the external clock,
* because the valid predividers are 2, 2.5 and 3, we multiply
* all the clocks by 2 to avoid floating point math.
*
* This is based on the algorithm in the ep93xx raster guide:
* http://be-a-maverick.com/en/pubs/appNote/AN269REV1.pdf
*
*/
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
parent_rate = clk_hw_get_rate(parent);
mclk_rate = parent_rate * 2;
/* Try each predivider value */
for (pdiv = 4; pdiv <= 6; pdiv++) {
div = DIV_ROUND_CLOSEST(mclk_rate, rate * pdiv);
if (!in_range(div, 1, 127))
continue;
actual_rate = DIV_ROUND_CLOSEST(mclk_rate, pdiv * div);
if (is_best(rate, actual_rate, best_rate)) {
best_rate = actual_rate;
parent_rate_best = parent_rate;
parent_best = parent;
}
}
}
if (!parent_best)
return -EINVAL;
req->best_parent_rate = parent_rate_best;
req->best_parent_hw = parent_best;
req->rate = best_rate;
return 0;
}
static unsigned long ep93xx_ddiv_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct ep93xx_clk *clk = ep93xx_clk_from(hw);
struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
unsigned int pdiv, div;
u32 val;
regmap_read(priv->map, clk->reg, &val);
pdiv = (val >> EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) & GENMASK(1, 0);
div = val & GENMASK(6, 0);
if (!div)
return 0;
return DIV_ROUND_CLOSEST(parent_rate * 2, (pdiv + 3) * div);
}
static int ep93xx_ddiv_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct ep93xx_clk *clk = ep93xx_clk_from(hw);
struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
int pdiv, div, npdiv, ndiv;
unsigned long actual_rate, mclk_rate, rate_err = ULONG_MAX;
u32 val;
regmap_read(priv->map, clk->reg, &val);
mclk_rate = parent_rate * 2;
for (pdiv = 4; pdiv <= 6; pdiv++) {
div = DIV_ROUND_CLOSEST(mclk_rate, rate * pdiv);
if (!in_range(div, 1, 127))
continue;
actual_rate = DIV_ROUND_CLOSEST(mclk_rate, pdiv * div);
if (abs(actual_rate - rate) < rate_err) {
npdiv = pdiv - 3;
ndiv = div;
rate_err = abs(actual_rate - rate);
}
}
if (rate_err == ULONG_MAX)
return -EINVAL;
/*
* Clear old dividers.
* Bit 7 is reserved bit in all ClkDiv registers.
*/
val &= ~(GENMASK(9, 0) & ~BIT(7));
/* Set the new pdiv and div bits for the new clock rate */
val |= (npdiv << EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | ndiv;
ep93xx_clk_write(priv, clk->reg, val);
return 0;
}
static const struct clk_ops clk_ddiv_ops = {
.enable = ep93xx_clk_enable,
.disable = ep93xx_clk_disable,
.is_enabled = ep93xx_clk_is_enabled,
.get_parent = ep93xx_mux_get_parent,
.set_parent = ep93xx_mux_set_parent_lock,
.determine_rate = ep93xx_mux_determine_rate,
.recalc_rate = ep93xx_ddiv_recalc_rate,
.set_rate = ep93xx_ddiv_set_rate,
};
static int ep93xx_clk_register_ddiv(struct ep93xx_clk *clk,
const char *name,
struct clk_parent_data *parent_data,
u8 num_parents,
unsigned int reg,
u8 bit_idx)
{
struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
struct clk_init_data init = { };
init.name = name;
init.ops = &clk_ddiv_ops;
init.flags = 0;
init.parent_data = parent_data;
init.num_parents = num_parents;
clk->reg = reg;
clk->bit_idx = bit_idx;
clk->hw.init = &init;
return devm_clk_hw_register(priv->dev, &clk->hw);
}
static unsigned long ep93xx_div_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct ep93xx_clk *clk = ep93xx_clk_from(hw);
struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
u32 val;
u8 index;
regmap_read(priv->map, clk->reg, &val);
index = (val & clk->mask) >> clk->shift;
if (index >= clk->num_div)
return 0;
return DIV_ROUND_CLOSEST(parent_rate, clk->div[index]);
}
static long ep93xx_div_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct ep93xx_clk *clk = ep93xx_clk_from(hw);
unsigned long best = 0, now;
unsigned int i;
for (i = 0; i < clk->num_div; i++) {
if ((rate * clk->div[i]) == *parent_rate)
return rate;
now = DIV_ROUND_CLOSEST(*parent_rate, clk->div[i]);
if (!best || is_best(rate, now, best))
best = now;
}
return best;
}
static int ep93xx_div_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct ep93xx_clk *clk = ep93xx_clk_from(hw);
struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
unsigned int i;
u32 val;
regmap_read(priv->map, clk->reg, &val);
val &= ~clk->mask;
for (i = 0; i < clk->num_div; i++)
if (rate == DIV_ROUND_CLOSEST(parent_rate, clk->div[i]))
break;
if (i == clk->num_div)
return -EINVAL;
val |= i << clk->shift;
ep93xx_clk_write(priv, clk->reg, val);
return 0;
}
static const struct clk_ops ep93xx_div_ops = {
.enable = ep93xx_clk_enable,
.disable = ep93xx_clk_disable,
.is_enabled = ep93xx_clk_is_enabled,
.recalc_rate = ep93xx_div_recalc_rate,
.round_rate = ep93xx_div_round_rate,
.set_rate = ep93xx_div_set_rate,
};
static int ep93xx_register_div(struct ep93xx_clk *clk,
const char *name,
const struct clk_parent_data *parent_data,
unsigned int reg,
u8 enable_bit,
u8 shift,
u8 width,
const char *clk_divisors,
u8 num_div)
{
struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk);
struct clk_init_data init = { };
init.name = name;
init.ops = &ep93xx_div_ops;
init.flags = 0;
init.parent_data = parent_data;
init.num_parents = 1;
clk->reg = reg;
clk->bit_idx = enable_bit;
clk->mask = GENMASK(shift + width - 1, shift);
clk->shift = shift;
clk->div = clk_divisors;
clk->num_div = num_div;
clk->hw.init = &init;
return devm_clk_hw_register(priv->dev, &clk->hw);
}
struct ep93xx_gate {
unsigned int idx;
unsigned int bit;
const char *name;
};
static const struct ep93xx_gate ep93xx_uarts[] = {
{ EP93XX_CLK_UART1, EP93XX_SYSCON_DEVCFG_U1EN, "uart1" },
{ EP93XX_CLK_UART2, EP93XX_SYSCON_DEVCFG_U2EN, "uart2" },
{ EP93XX_CLK_UART3, EP93XX_SYSCON_DEVCFG_U3EN, "uart3" },
};
static int ep93xx_uart_clock_init(struct ep93xx_clk_priv *priv)
{
struct clk_parent_data parent_data = { };
unsigned int i, idx, ret, clk_uart_div;
struct ep93xx_clk *clk;
u32 val;
regmap_read(priv->map, EP93XX_SYSCON_PWRCNT, &val);
if (val & EP93XX_SYSCON_PWRCNT_UARTBAUD)
clk_uart_div = 1;
else
clk_uart_div = 2;
priv->fixed[EP93XX_CLK_UART] =
devm_clk_hw_register_fixed_factor_index(priv->dev, "uart",
0, /* XTALI external clock */
0, 1, clk_uart_div);
parent_data.hw = priv->fixed[EP93XX_CLK_UART];
/* parenting uart gate clocks to uart clock */
for (i = 0; i < ARRAY_SIZE(ep93xx_uarts); i++) {
idx = ep93xx_uarts[i].idx - EP93XX_CLK_UART1;
clk = &priv->reg[idx];
clk->idx = idx;
ret = ep93xx_clk_register_gate(clk,
ep93xx_uarts[i].name,
&parent_data, CLK_SET_RATE_PARENT,
EP93XX_SYSCON_DEVCFG,
ep93xx_uarts[i].bit);
if (ret)
return dev_err_probe(priv->dev, ret,
"failed to register uart[%d] clock\n", i);
}
return 0;
}
static const struct ep93xx_gate ep93xx_dmas[] = {
{ EP93XX_CLK_M2M0, EP93XX_SYSCON_PWRCNT_DMA_M2M0, "m2m0" },
{ EP93XX_CLK_M2M1, EP93XX_SYSCON_PWRCNT_DMA_M2M1, "m2m1" },
{ EP93XX_CLK_M2P0, EP93XX_SYSCON_PWRCNT_DMA_M2P0, "m2p0" },
{ EP93XX_CLK_M2P1, EP93XX_SYSCON_PWRCNT_DMA_M2P1, "m2p1" },
{ EP93XX_CLK_M2P2, EP93XX_SYSCON_PWRCNT_DMA_M2P2, "m2p2" },
{ EP93XX_CLK_M2P3, EP93XX_SYSCON_PWRCNT_DMA_M2P3, "m2p3" },
{ EP93XX_CLK_M2P4, EP93XX_SYSCON_PWRCNT_DMA_M2P4, "m2p4" },
{ EP93XX_CLK_M2P5, EP93XX_SYSCON_PWRCNT_DMA_M2P5, "m2p5" },
{ EP93XX_CLK_M2P6, EP93XX_SYSCON_PWRCNT_DMA_M2P6, "m2p6" },
{ EP93XX_CLK_M2P7, EP93XX_SYSCON_PWRCNT_DMA_M2P7, "m2p7" },
{ EP93XX_CLK_M2P8, EP93XX_SYSCON_PWRCNT_DMA_M2P8, "m2p8" },
{ EP93XX_CLK_M2P9, EP93XX_SYSCON_PWRCNT_DMA_M2P9, "m2p9" },
};
static int ep93xx_dma_clock_init(struct ep93xx_clk_priv *priv)
{
struct clk_parent_data parent_data = { };
unsigned int i, idx;
parent_data.hw = priv->fixed[EP93XX_CLK_HCLK];
for (i = 0; i < ARRAY_SIZE(ep93xx_dmas); i++) {
idx = ep93xx_dmas[i].idx;
priv->fixed[idx] = devm_clk_hw_register_gate_parent_data(priv->dev,
ep93xx_dmas[i].name,
&parent_data, 0,
priv->base + EP93XX_SYSCON_PWRCNT,
ep93xx_dmas[i].bit,
0,
&priv->lock);
if (IS_ERR(priv->fixed[idx]))
return PTR_ERR(priv->fixed[idx]);
}
return 0;
}
static struct clk_hw *of_clk_ep93xx_get(struct of_phandle_args *clkspec, void *data)
{
struct ep93xx_clk_priv *priv = data;
unsigned int idx = clkspec->args[0];
if (idx < EP93XX_CLK_UART1)
return priv->fixed[idx];
if (idx <= EP93XX_CLK_I2S_LRCLK)
return &priv->reg[idx - EP93XX_CLK_UART1].hw;
return ERR_PTR(-EINVAL);
}
/*
* PLL rate = 14.7456 MHz * (X1FBD + 1) * (X2FBD + 1) / (X2IPD + 1) / 2^PS
*/
static unsigned long calc_pll_rate(u64 rate, u32 config_word)
{
rate *= ((config_word >> 11) & GENMASK(4, 0)) + 1; /* X1FBD */
rate *= ((config_word >> 5) & GENMASK(5, 0)) + 1; /* X2FBD */
do_div(rate, (config_word & GENMASK(4, 0)) + 1); /* X2IPD */
rate >>= (config_word >> 16) & GENMASK(1, 0); /* PS */
return rate;
}
static int ep93xx_plls_init(struct ep93xx_clk_priv *priv)
{
const char fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 };
const char hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 };
const char pclk_divisors[] = { 1, 2, 4, 8 };
struct clk_parent_data xtali = { .index = 0 };
unsigned int clk_f_div, clk_h_div, clk_p_div;
unsigned long clk_pll1_rate, clk_pll2_rate;
struct device *dev = priv->dev;
struct clk_hw *hw, *pll1;
u32 value;
/* Determine the bootloader configured pll1 rate */
regmap_read(priv->map, EP93XX_SYSCON_CLKSET1, &value);
if (value & EP93XX_SYSCON_CLKSET1_NBYP1)
clk_pll1_rate = calc_pll_rate(EP93XX_EXT_CLK_RATE, value);
else
clk_pll1_rate = EP93XX_EXT_CLK_RATE;
pll1 = devm_clk_hw_register_fixed_rate_parent_data(dev, "pll1", &xtali,
0, clk_pll1_rate);
if (IS_ERR(pll1))
return PTR_ERR(pll1);
priv->fixed[EP93XX_CLK_PLL1] = pll1;
/* Initialize the pll1 derived clocks */
clk_f_div = fclk_divisors[(value >> 25) & GENMASK(2, 0)];
clk_h_div = hclk_divisors[(value >> 20) & GENMASK(2, 0)];
clk_p_div = pclk_divisors[(value >> 18) & GENMASK(1, 0)];
hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, "fclk", pll1, 0, 1, clk_f_div);
if (IS_ERR(hw))
return PTR_ERR(hw);
priv->fixed[EP93XX_CLK_FCLK] = hw;
hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, "hclk", pll1, 0, 1, clk_h_div);
if (IS_ERR(hw))
return PTR_ERR(hw);
priv->fixed[EP93XX_CLK_HCLK] = hw;
hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, "pclk", hw, 0, 1, clk_p_div);
if (IS_ERR(hw))
return PTR_ERR(hw);
priv->fixed[EP93XX_CLK_PCLK] = hw;
/* Determine the bootloader configured pll2 rate */
regmap_read(priv->map, EP93XX_SYSCON_CLKSET2, &value);
if (!(value & EP93XX_SYSCON_CLKSET2_NBYP2))
clk_pll2_rate = EP93XX_EXT_CLK_RATE;
else if (value & EP93XX_SYSCON_CLKSET2_PLL2_EN)
clk_pll2_rate = calc_pll_rate(EP93XX_EXT_CLK_RATE, value);
else
clk_pll2_rate = 0;
hw = devm_clk_hw_register_fixed_rate_parent_data(dev, "pll2", &xtali,
0, clk_pll2_rate);
if (IS_ERR(hw))
return PTR_ERR(hw);
priv->fixed[EP93XX_CLK_PLL2] = hw;
return 0;
}
static int ep93xx_clk_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct ep93xx_regmap_adev *rdev = to_ep93xx_regmap_adev(adev);
struct clk_parent_data xtali = { .index = 0 };
struct clk_parent_data ddiv_pdata[3] = { };
unsigned int clk_spi_div, clk_usb_div;
struct clk_parent_data pdata = {};
struct device *dev = &adev->dev;
struct ep93xx_clk_priv *priv;
struct ep93xx_clk *clk;
struct clk_hw *hw;
unsigned int idx;
int ret;
u32 value;
priv = devm_kzalloc(dev, struct_size(priv, reg, 10), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->lock);
priv->dev = dev;
priv->aux_dev = rdev;
priv->map = rdev->map;
priv->base = rdev->base;
ret = ep93xx_plls_init(priv);
if (ret)
return ret;
regmap_read(priv->map, EP93XX_SYSCON_CLKSET2, &value);
clk_usb_div = (value >> 28 & GENMASK(3, 0)) + 1;
hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, "usb_clk",
priv->fixed[EP93XX_CLK_PLL2], 0, 1,
clk_usb_div);
if (IS_ERR(hw))
return PTR_ERR(hw);
priv->fixed[EP93XX_CLK_USB] = hw;
ret = ep93xx_uart_clock_init(priv);
if (ret)
return ret;
ret = ep93xx_dma_clock_init(priv);
if (ret)
return ret;
clk_spi_div = id->driver_data;
hw = devm_clk_hw_register_fixed_factor_index(dev, "ep93xx-spi.0",
0, /* XTALI external clock */
0, 1, clk_spi_div);
if (IS_ERR(hw))
return PTR_ERR(hw);
priv->fixed[EP93XX_CLK_SPI] = hw;
/* PWM clock */
hw = devm_clk_hw_register_fixed_factor_index(dev, "pwm_clk", 0, /* XTALI external clock */
0, 1, 1);
if (IS_ERR(hw))
return PTR_ERR(hw);
priv->fixed[EP93XX_CLK_PWM] = hw;
/* USB clock */
pdata.hw = priv->fixed[EP93XX_CLK_USB];
hw = devm_clk_hw_register_gate_parent_data(priv->dev, "ohci-platform", &pdata,
0, priv->base + EP93XX_SYSCON_PWRCNT,
EP93XX_SYSCON_PWRCNT_USH_EN, 0,
&priv->lock);
if (IS_ERR(hw))
return PTR_ERR(hw);
priv->fixed[EP93XX_CLK_USB] = hw;
ddiv_pdata[0].index = 0; /* XTALI external clock */
ddiv_pdata[1].hw = priv->fixed[EP93XX_CLK_PLL1];
ddiv_pdata[2].hw = priv->fixed[EP93XX_CLK_PLL2];
/* touchscreen/ADC clock */
idx = EP93XX_CLK_ADC - EP93XX_CLK_UART1;
clk = &priv->reg[idx];
clk->idx = idx;
ret = ep93xx_register_div(clk, "ep93xx-adc", &xtali,
EP93XX_SYSCON_KEYTCHCLKDIV,
EP93XX_SYSCON_KEYTCHCLKDIV_TSEN,
EP93XX_SYSCON_KEYTCHCLKDIV_ADIV,
1,
ep93xx_adc_divisors,
ARRAY_SIZE(ep93xx_adc_divisors));
/* keypad clock */
idx = EP93XX_CLK_KEYPAD - EP93XX_CLK_UART1;
clk = &priv->reg[idx];
clk->idx = idx;
ret = ep93xx_register_div(clk, "ep93xx-keypad", &xtali,
EP93XX_SYSCON_KEYTCHCLKDIV,
EP93XX_SYSCON_KEYTCHCLKDIV_KEN,
EP93XX_SYSCON_KEYTCHCLKDIV_KDIV,
1,
ep93xx_adc_divisors,
ARRAY_SIZE(ep93xx_adc_divisors));
/*
* On reset PDIV and VDIV is set to zero, while PDIV zero
* means clock disable, VDIV shouldn't be zero.
* So we set both video and i2s dividers to minimum.
* ENA - Enable CLK divider.
* PDIV - 00 - Disable clock
* VDIV - at least 2
*/
/* Check and enable video clk registers */
regmap_read(priv->map, EP93XX_SYSCON_VIDCLKDIV, &value);
value |= BIT(EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | 2;
ep93xx_clk_write(priv, EP93XX_SYSCON_VIDCLKDIV, value);
/* Check and enable i2s clk registers */
regmap_read(priv->map, EP93XX_SYSCON_I2SCLKDIV, &value);
value |= BIT(EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | 2;
/*
* Override the SAI_MSTR_CLK_CFG from the I2S block and use the
* I2SClkDiv Register settings. LRCLK transitions on the falling SCLK
* edge.
*/
value |= EP93XX_SYSCON_I2SCLKDIV_ORIDE | EP93XX_SYSCON_I2SCLKDIV_SPOL;
ep93xx_clk_write(priv, EP93XX_SYSCON_I2SCLKDIV, value);
/* video clk */
idx = EP93XX_CLK_VIDEO - EP93XX_CLK_UART1;
clk = &priv->reg[idx];
clk->idx = idx;
ret = ep93xx_clk_register_ddiv(clk, "ep93xx-fb",
ddiv_pdata, ARRAY_SIZE(ddiv_pdata),
EP93XX_SYSCON_VIDCLKDIV,
EP93XX_SYSCON_CLKDIV_ENABLE);
/* i2s clk */
idx = EP93XX_CLK_I2S_MCLK - EP93XX_CLK_UART1;
clk = &priv->reg[idx];
clk->idx = idx;
ret = ep93xx_clk_register_ddiv(clk, "mclk",
ddiv_pdata, ARRAY_SIZE(ddiv_pdata),
EP93XX_SYSCON_I2SCLKDIV,
EP93XX_SYSCON_CLKDIV_ENABLE);
/* i2s sclk */
idx = EP93XX_CLK_I2S_SCLK - EP93XX_CLK_UART1;
clk = &priv->reg[idx];
clk->idx = idx;
pdata.hw = &priv->reg[EP93XX_CLK_I2S_MCLK - EP93XX_CLK_UART1].hw;
ret = ep93xx_register_div(clk, "sclk", &pdata,
EP93XX_SYSCON_I2SCLKDIV,
EP93XX_SYSCON_I2SCLKDIV_SENA,
16, /* EP93XX_I2SCLKDIV_SDIV_SHIFT */
1, /* EP93XX_I2SCLKDIV_SDIV_WIDTH */
ep93xx_sclk_divisors,
ARRAY_SIZE(ep93xx_sclk_divisors));
/* i2s lrclk */
idx = EP93XX_CLK_I2S_LRCLK - EP93XX_CLK_UART1;
clk = &priv->reg[idx];
clk->idx = idx;
pdata.hw = &priv->reg[EP93XX_CLK_I2S_SCLK - EP93XX_CLK_UART1].hw;
ret = ep93xx_register_div(clk, "lrclk", &pdata,
EP93XX_SYSCON_I2SCLKDIV,
EP93XX_SYSCON_I2SCLKDIV_SENA,
17, /* EP93XX_I2SCLKDIV_LRDIV32_SHIFT */
2, /* EP93XX_I2SCLKDIV_LRDIV32_WIDTH */
ep93xx_lrclk_divisors,
ARRAY_SIZE(ep93xx_lrclk_divisors));
/* IrDa clk uses same pattern but no init code presents in original clock driver */
return devm_of_clk_add_hw_provider(priv->dev, of_clk_ep93xx_get, priv);
}
static const struct auxiliary_device_id ep93xx_clk_ids[] = {
{ .name = "soc_ep93xx.clk-ep93xx", .driver_data = 2, },
{ .name = "soc_ep93xx.clk-ep93xx.e2", .driver_data = 1, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(auxiliary, ep93xx_clk_ids);
static struct auxiliary_driver ep93xx_clk_driver = {
.probe = ep93xx_clk_probe,
.id_table = ep93xx_clk_ids,
};
module_auxiliary_driver(ep93xx_clk_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nikita Shubin <nikita.shubin@maquefel.me>");
MODULE_DESCRIPTION("Clock control for Cirrus EP93xx chips");