clk: uniphier: rework UniPhier clk driver
The initial design of the UniPhier clk driver for U-Boot was not very nice. Here is a re-work to sync it with Linux's clk and reset drivers, maximizing the code reuse from Linux's clk data. Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
This commit is contained in:
parent
f666a65824
commit
805dc44cc8
@ -1,13 +1,10 @@
|
||||
config CLK_UNIPHIER
|
||||
bool
|
||||
bool "Clock driver for UniPhier SoCs"
|
||||
depends on ARCH_UNIPHIER
|
||||
select CLK
|
||||
select SPL_CLK
|
||||
|
||||
menu "Clock drivers for UniPhier SoCs"
|
||||
depends on CLK_UNIPHIER
|
||||
|
||||
config CLK_UNIPHIER_MIO
|
||||
bool "Clock driver for UniPhier Media I/O block"
|
||||
default y
|
||||
|
||||
endmenu
|
||||
help
|
||||
Support for clock controllers on UniPhier SoCs.
|
||||
Say Y if you want to control clocks provided by System Control
|
||||
block, Media I/O block, Peripheral Block.
|
||||
|
@ -1,3 +1,2 @@
|
||||
obj-y += clk-uniphier-core.o
|
||||
|
||||
obj-$(CONFIG_CLK_UNIPHIER_MIO) += clk-uniphier-mio.o
|
||||
obj-y += clk-uniphier-core.o
|
||||
obj-y += clk-uniphier-mio.o
|
||||
|
@ -6,11 +6,11 @@
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm/device.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm/device.h>
|
||||
|
||||
#include "clk-uniphier.h"
|
||||
|
||||
@ -18,14 +18,116 @@
|
||||
* struct uniphier_clk_priv - private data for UniPhier clock driver
|
||||
*
|
||||
* @base: base address of the clock provider
|
||||
* @socdata: SoC specific data
|
||||
* @data: SoC specific data
|
||||
*/
|
||||
struct uniphier_clk_priv {
|
||||
void __iomem *base;
|
||||
const struct uniphier_clk_soc_data *socdata;
|
||||
const struct uniphier_clk_data *data;
|
||||
};
|
||||
|
||||
int uniphier_clk_probe(struct udevice *dev)
|
||||
static int uniphier_clk_enable(struct clk *clk)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
unsigned long id = clk->id;
|
||||
const struct uniphier_clk_gate_data *p;
|
||||
|
||||
for (p = priv->data->gate; p->id != UNIPHIER_CLK_ID_END; p++) {
|
||||
u32 val;
|
||||
|
||||
if (p->id != id)
|
||||
continue;
|
||||
|
||||
val = readl(priv->base + p->reg);
|
||||
val |= BIT(p->bit);
|
||||
writel(val, priv->base + p->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_err(priv->dev, "clk_id=%lu was not handled\n", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct uniphier_clk_mux_data *
|
||||
uniphier_clk_get_mux_data(struct uniphier_clk_priv *priv, unsigned long id)
|
||||
{
|
||||
const struct uniphier_clk_mux_data *p;
|
||||
|
||||
for (p = priv->data->mux; p->id != UNIPHIER_CLK_ID_END; p++) {
|
||||
if (p->id == id)
|
||||
return p;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ulong uniphier_clk_get_rate(struct clk *clk)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
const struct uniphier_clk_mux_data *mux;
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
mux = uniphier_clk_get_mux_data(priv, clk->id);
|
||||
if (!mux)
|
||||
return 0;
|
||||
|
||||
if (!mux->nr_muxs) /* fixed-rate */
|
||||
return mux->rates[0];
|
||||
|
||||
val = readl(priv->base + mux->reg);
|
||||
|
||||
for (i = 0; i < mux->nr_muxs; i++)
|
||||
if ((mux->masks[i] & val) == mux->vals[i])
|
||||
return mux->rates[i];
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ulong uniphier_clk_set_rate(struct clk *clk, ulong rate)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
const struct uniphier_clk_mux_data *mux;
|
||||
u32 val;
|
||||
int i, best_rate_id = -1;
|
||||
ulong best_rate = 0;
|
||||
|
||||
mux = uniphier_clk_get_mux_data(priv, clk->id);
|
||||
if (!mux)
|
||||
return 0;
|
||||
|
||||
if (!mux->nr_muxs) /* fixed-rate */
|
||||
return mux->rates[0];
|
||||
|
||||
/* first, decide the best match rate */
|
||||
for (i = 0; i < mux->nr_muxs; i++) {
|
||||
if (mux->rates[i] > best_rate && mux->rates[i] <= rate) {
|
||||
best_rate = mux->rates[i];
|
||||
best_rate_id = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_rate_id < 0)
|
||||
return -EINVAL;
|
||||
|
||||
val = readl(priv->base + mux->reg);
|
||||
val &= ~mux->masks[best_rate_id];
|
||||
val |= mux->vals[best_rate_id];
|
||||
writel(val, priv->base + mux->reg);
|
||||
|
||||
debug("%s: requested rate = %lu, set rate = %lu\n", __func__,
|
||||
rate, best_rate);
|
||||
|
||||
return best_rate;
|
||||
}
|
||||
|
||||
const struct clk_ops uniphier_clk_ops = {
|
||||
.enable = uniphier_clk_enable,
|
||||
.get_rate = uniphier_clk_get_rate,
|
||||
.set_rate = uniphier_clk_set_rate,
|
||||
};
|
||||
|
||||
static int uniphier_clk_probe(struct udevice *dev)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(dev);
|
||||
fdt_addr_t addr;
|
||||
@ -38,125 +140,11 @@ int uniphier_clk_probe(struct udevice *dev)
|
||||
if (!priv->base)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->socdata = (void *)dev_get_driver_data(dev);
|
||||
priv->data = (void *)dev_get_driver_data(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_clk_enable(struct clk *clk)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
const struct uniphier_clk_gate_data *gate = priv->socdata->gate;
|
||||
unsigned int nr_gate = priv->socdata->nr_gate;
|
||||
void __iomem *reg;
|
||||
u32 mask, data, tmp;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_gate; i++) {
|
||||
if (gate[i].index != clk->id)
|
||||
continue;
|
||||
|
||||
reg = priv->base + gate[i].reg;
|
||||
mask = gate[i].mask;
|
||||
data = gate[i].data & mask;
|
||||
|
||||
tmp = readl(reg);
|
||||
tmp &= ~mask;
|
||||
tmp |= data & mask;
|
||||
debug("%s: %p: %08x\n", __func__, reg, tmp);
|
||||
writel(tmp, reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ulong uniphier_clk_get_rate(struct clk *clk)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
const struct uniphier_clk_rate_data *rdata = priv->socdata->rate;
|
||||
unsigned int nr_rdata = priv->socdata->nr_rate;
|
||||
void __iomem *reg;
|
||||
u32 mask, data;
|
||||
ulong matched_rate = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_rdata; i++) {
|
||||
if (rdata[i].index != clk->id)
|
||||
continue;
|
||||
|
||||
if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED)
|
||||
return rdata[i].rate;
|
||||
|
||||
reg = priv->base + rdata[i].reg;
|
||||
mask = rdata[i].mask;
|
||||
data = rdata[i].data & mask;
|
||||
if ((readl(reg) & mask) == data) {
|
||||
if (matched_rate && rdata[i].rate != matched_rate) {
|
||||
printf("failed to get clk rate for insane register values\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
matched_rate = rdata[i].rate;
|
||||
}
|
||||
}
|
||||
|
||||
debug("%s: rate = %lu\n", __func__, matched_rate);
|
||||
|
||||
return matched_rate;
|
||||
}
|
||||
|
||||
static ulong uniphier_clk_set_rate(struct clk *clk, ulong rate)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
const struct uniphier_clk_rate_data *rdata = priv->socdata->rate;
|
||||
unsigned int nr_rdata = priv->socdata->nr_rate;
|
||||
void __iomem *reg;
|
||||
u32 mask, data, tmp;
|
||||
ulong best_rate = 0;
|
||||
int i;
|
||||
|
||||
/* first, decide the best match rate */
|
||||
for (i = 0; i < nr_rdata; i++) {
|
||||
if (rdata[i].index != clk->id)
|
||||
continue;
|
||||
|
||||
if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED)
|
||||
return 0;
|
||||
|
||||
if (rdata[i].rate > best_rate && rdata[i].rate <= rate)
|
||||
best_rate = rdata[i].rate;
|
||||
}
|
||||
|
||||
if (!best_rate)
|
||||
return -ENODEV;
|
||||
|
||||
debug("%s: requested rate = %lu, set rate = %lu\n", __func__,
|
||||
rate, best_rate);
|
||||
|
||||
/* second, really set registers */
|
||||
for (i = 0; i < nr_rdata; i++) {
|
||||
if (rdata[i].index != clk->id || rdata[i].rate != best_rate)
|
||||
continue;
|
||||
|
||||
reg = priv->base + rdata[i].reg;
|
||||
mask = rdata[i].mask;
|
||||
data = rdata[i].data & mask;
|
||||
|
||||
tmp = readl(reg);
|
||||
tmp &= ~mask;
|
||||
tmp |= data;
|
||||
debug("%s: %p: %08x\n", __func__, reg, tmp);
|
||||
writel(tmp, reg);
|
||||
}
|
||||
|
||||
return best_rate;
|
||||
}
|
||||
|
||||
const struct clk_ops uniphier_clk_ops = {
|
||||
.enable = uniphier_clk_enable,
|
||||
.get_rate = uniphier_clk_get_rate,
|
||||
.set_rate = uniphier_clk_set_rate,
|
||||
};
|
||||
|
||||
static const struct udevice_id uniphier_clk_match[] = {
|
||||
{
|
||||
.compatible = "socionext,uniphier-sld3-mio-clock",
|
||||
|
@ -5,136 +5,81 @@
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <dm/device.h>
|
||||
|
||||
#include "clk-uniphier.h"
|
||||
|
||||
#define UNIPHIER_MIO_CLK_GATE_SD(ch, idx) \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x20 + 0x200 * (ch), \
|
||||
.mask = 0x00000100, \
|
||||
.data = 0x00000100, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x110 + 0x200 * (ch), \
|
||||
.mask = 0x00000001, \
|
||||
.data = 0x00000001, \
|
||||
}
|
||||
#define UNIPHIER_MIO_CLK_SD_GATE(id, ch) \
|
||||
UNIPHIER_CLK_GATE((id), 0x20 + 0x200 * (ch), 8)
|
||||
|
||||
#define UNIPHIER_MIO_CLK_RATE_SD(ch, idx) \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00000000, \
|
||||
.rate = 44444444, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00010000, \
|
||||
.rate = 33333333, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00020000, \
|
||||
.rate = 50000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00020000, \
|
||||
.rate = 66666666, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00001000, \
|
||||
.rate = 100000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00001100, \
|
||||
.rate = 40000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00001200, \
|
||||
.rate = 25000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.mask = 0x00031300, \
|
||||
.data = 0x00001300, \
|
||||
.rate = 22222222, \
|
||||
}
|
||||
#define UNIPHIER_MIO_CLK_USB2(id, ch) \
|
||||
UNIPHIER_CLK_GATE((id), 0x20 + 0x200 * (ch), 28)
|
||||
|
||||
#define UNIPHIER_MIO_CLK_GATE_USB(ch, idx) \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x20 + 0x200 * (ch), \
|
||||
.mask = 0x30000000, \
|
||||
.data = 0x30000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x110 + 0x200 * (ch), \
|
||||
.mask = 0x01000000, \
|
||||
.data = 0x01000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x114 + 0x200 * (ch), \
|
||||
.mask = 0x00000001, \
|
||||
.data = 0x00000001, \
|
||||
}
|
||||
#define UNIPHIER_MIO_CLK_USB2_PHY(id, ch) \
|
||||
UNIPHIER_CLK_GATE((id), 0x20 + 0x200 * (ch), 29)
|
||||
|
||||
#define UNIPHIER_MIO_CLK_GATE_DMAC(idx) \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x20, \
|
||||
.mask = 0x02000000, \
|
||||
.data = 0x02000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x110, \
|
||||
.mask = 0x00020000, \
|
||||
.data = 0x00020000, \
|
||||
#define UNIPHIER_MIO_CLK_DMAC(id) \
|
||||
UNIPHIER_CLK_GATE((id), 0x20, 25)
|
||||
|
||||
#define UNIPHIER_MIO_CLK_SD_MUX(_id, ch) \
|
||||
{ \
|
||||
.id = (_id), \
|
||||
.nr_muxs = 8, \
|
||||
.reg = 0x30 + 0x200 * (ch), \
|
||||
.masks = { \
|
||||
0x00031000, \
|
||||
0x00031000, \
|
||||
0x00031000, \
|
||||
0x00031000, \
|
||||
0x00001300, \
|
||||
0x00001300, \
|
||||
0x00001300, \
|
||||
0x00001300, \
|
||||
}, \
|
||||
.vals = { \
|
||||
0x00000000, \
|
||||
0x00010000, \
|
||||
0x00020000, \
|
||||
0x00030000, \
|
||||
0x00001000, \
|
||||
0x00001100, \
|
||||
0x00001200, \
|
||||
0x00001300, \
|
||||
}, \
|
||||
.rates = { \
|
||||
44444444, \
|
||||
33333333, \
|
||||
50000000, \
|
||||
66666666, \
|
||||
100000000, \
|
||||
40000000, \
|
||||
25000000, \
|
||||
22222222, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct uniphier_clk_gate_data uniphier_mio_clk_gate[] = {
|
||||
UNIPHIER_MIO_CLK_GATE_SD(0, 0),
|
||||
UNIPHIER_MIO_CLK_GATE_SD(1, 1),
|
||||
UNIPHIER_MIO_CLK_GATE_SD(2, 2), /* for PH1-Pro4 only */
|
||||
UNIPHIER_MIO_CLK_GATE_USB(0, 3),
|
||||
UNIPHIER_MIO_CLK_GATE_USB(1, 4),
|
||||
UNIPHIER_MIO_CLK_GATE_USB(2, 5),
|
||||
UNIPHIER_MIO_CLK_GATE_DMAC(6),
|
||||
UNIPHIER_MIO_CLK_GATE_USB(3, 7), /* for PH1-sLD3 only */
|
||||
UNIPHIER_MIO_CLK_SD_GATE(0, 0),
|
||||
UNIPHIER_MIO_CLK_SD_GATE(1, 1),
|
||||
UNIPHIER_MIO_CLK_SD_GATE(2, 2), /* for PH1-Pro4 only */
|
||||
UNIPHIER_MIO_CLK_DMAC(7),
|
||||
UNIPHIER_MIO_CLK_USB2(8, 0),
|
||||
UNIPHIER_MIO_CLK_USB2(9, 1),
|
||||
UNIPHIER_MIO_CLK_USB2(10, 2),
|
||||
UNIPHIER_MIO_CLK_USB2(11, 3), /* for PH1-sLD3 only */
|
||||
UNIPHIER_MIO_CLK_USB2_PHY(12, 0),
|
||||
UNIPHIER_MIO_CLK_USB2_PHY(13, 1),
|
||||
UNIPHIER_MIO_CLK_USB2_PHY(14, 2),
|
||||
UNIPHIER_MIO_CLK_USB2_PHY(15, 3), /* for PH1-sLD3 only */
|
||||
UNIPHIER_CLK_END
|
||||
};
|
||||
|
||||
static const struct uniphier_clk_rate_data uniphier_mio_clk_rate[] = {
|
||||
UNIPHIER_MIO_CLK_RATE_SD(0, 0),
|
||||
UNIPHIER_MIO_CLK_RATE_SD(1, 1),
|
||||
UNIPHIER_MIO_CLK_RATE_SD(2, 2), /* for PH1-Pro4 only */
|
||||
static const struct uniphier_clk_mux_data uniphier_mio_clk_mux[] = {
|
||||
UNIPHIER_MIO_CLK_SD_MUX(0, 0),
|
||||
UNIPHIER_MIO_CLK_SD_MUX(1, 1),
|
||||
UNIPHIER_MIO_CLK_SD_MUX(2, 2), /* for PH1-Pro4 only */
|
||||
UNIPHIER_CLK_END
|
||||
};
|
||||
|
||||
const struct uniphier_clk_soc_data uniphier_mio_clk_data = {
|
||||
const struct uniphier_clk_data uniphier_mio_clk_data = {
|
||||
.gate = uniphier_mio_clk_gate,
|
||||
.nr_gate = ARRAY_SIZE(uniphier_mio_clk_gate),
|
||||
.rate = uniphier_mio_clk_rate,
|
||||
.nr_rate = ARRAY_SIZE(uniphier_mio_clk_rate),
|
||||
.mux = uniphier_mio_clk_mux,
|
||||
};
|
||||
|
@ -10,36 +10,46 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define UNIPHIER_CLK_MAX_NR_MUXS 8
|
||||
|
||||
struct uniphier_clk_gate_data {
|
||||
int index;
|
||||
unsigned int id;
|
||||
unsigned int reg;
|
||||
u32 mask;
|
||||
u32 data;
|
||||
unsigned int bit;
|
||||
};
|
||||
|
||||
struct uniphier_clk_rate_data {
|
||||
int index;
|
||||
struct uniphier_clk_mux_data {
|
||||
unsigned int id;
|
||||
unsigned int nr_muxs;
|
||||
unsigned int reg;
|
||||
#define UNIPHIER_CLK_RATE_IS_FIXED UINT_MAX
|
||||
u32 mask;
|
||||
u32 data;
|
||||
unsigned long rate;
|
||||
unsigned int masks[UNIPHIER_CLK_MAX_NR_MUXS];
|
||||
unsigned int vals[UNIPHIER_CLK_MAX_NR_MUXS];
|
||||
unsigned long rates[UNIPHIER_CLK_MAX_NR_MUXS];
|
||||
};
|
||||
|
||||
struct uniphier_clk_soc_data {
|
||||
struct uniphier_clk_data {
|
||||
const struct uniphier_clk_gate_data *gate;
|
||||
unsigned int nr_gate;
|
||||
const struct uniphier_clk_rate_data *rate;
|
||||
unsigned int nr_rate;
|
||||
const struct uniphier_clk_mux_data *mux;
|
||||
};
|
||||
|
||||
#define UNIPHIER_CLK_FIXED_RATE(i, f) \
|
||||
#define UNIPHIER_CLK_ID_END (unsigned int)(-1)
|
||||
|
||||
#define UNIPHIER_CLK_END \
|
||||
{ .id = UNIPHIER_CLK_ID_END }
|
||||
|
||||
#define UNIPHIER_CLK_GATE(_id, _reg, _bit) \
|
||||
{ \
|
||||
.index = i, \
|
||||
.reg = UNIPHIER_CLK_RATE_IS_FIXED, \
|
||||
.rate = f, \
|
||||
.id = (_id), \
|
||||
.reg = (_reg), \
|
||||
.bit = (_bit), \
|
||||
}
|
||||
|
||||
extern const struct uniphier_clk_soc_data uniphier_mio_clk_data;
|
||||
#define UNIPHIER_CLK_FIXED_RATE(_id, _rate) \
|
||||
{ \
|
||||
.id = (_id), \
|
||||
.rates = {(_reg),}, \
|
||||
}
|
||||
|
||||
extern const struct uniphier_clk_data uniphier_mio_clk_data;
|
||||
|
||||
#endif /* __CLK_UNIPHIER_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user