clk: uniphier: add Media I/O clock driver for UniPhier SoCs
This is the initial commit for the UniPhier clock drivers. Currently, only the Media I/O clock is supported. Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
This commit is contained in:
parent
fec4816387
commit
48264d9beb
@ -689,6 +689,7 @@ config TARGET_COLIBRI_PXA270
|
||||
|
||||
config ARCH_UNIPHIER
|
||||
bool "Socionext UniPhier SoCs"
|
||||
select CLK_UNIPHIER
|
||||
select CPU_V7
|
||||
select SUPPORT_SPL
|
||||
select SPL
|
||||
|
@ -20,4 +20,6 @@ config SPL_CLK
|
||||
setting up clocks within SPL, and allows the same drivers to be
|
||||
used as U-Boot proper.
|
||||
|
||||
source "drivers/clk/uniphier/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -10,3 +10,4 @@ obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o
|
||||
obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o
|
||||
obj-$(CONFIG_SANDBOX) += clk_sandbox.o
|
||||
obj-$(CONFIG_MACH_PIC32) += clk_pic32.o
|
||||
obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
|
||||
|
13
drivers/clk/uniphier/Kconfig
Normal file
13
drivers/clk/uniphier/Kconfig
Normal file
@ -0,0 +1,13 @@
|
||||
config CLK_UNIPHIER
|
||||
bool
|
||||
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
|
3
drivers/clk/uniphier/Makefile
Normal file
3
drivers/clk/uniphier/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
obj-y += clk-uniphier-core.o
|
||||
|
||||
obj-$(CONFIG_CLK_UNIPHIER_MIO) += clk-uniphier-mio.o
|
159
drivers/clk/uniphier/clk-uniphier-core.c
Normal file
159
drivers/clk/uniphier/clk-uniphier-core.c
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <mapmem.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/io.h>
|
||||
#include <clk.h>
|
||||
#include <dm/device.h>
|
||||
|
||||
#include "clk-uniphier.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static int uniphier_clk_enable(struct udevice *dev, int index)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(dev);
|
||||
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 != index)
|
||||
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 udevice *dev, int index)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(dev);
|
||||
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 != index)
|
||||
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 udevice *dev, int index, ulong rate)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(dev);
|
||||
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 != index)
|
||||
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 != index || 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_periph_rate = uniphier_clk_get_rate,
|
||||
.set_periph_rate = uniphier_clk_set_rate,
|
||||
};
|
||||
|
||||
int uniphier_clk_probe(struct udevice *dev)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(dev);
|
||||
fdt_addr_t addr;
|
||||
fdt_size_t size;
|
||||
|
||||
addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg",
|
||||
&size);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
priv->base = map_sysmem(addr, size);
|
||||
if (!priv->base)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->socdata = (void *)dev_get_driver_data(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uniphier_clk_remove(struct udevice *dev)
|
||||
{
|
||||
struct uniphier_clk_priv *priv = dev_get_priv(dev);
|
||||
|
||||
unmap_sysmem(priv->base);
|
||||
|
||||
return 0;
|
||||
}
|
178
drivers/clk/uniphier/clk-uniphier-mio.c
Normal file
178
drivers/clk/uniphier/clk-uniphier-mio.c
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <clk.h>
|
||||
#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_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_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_GATE_DMAC(idx) \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x20, \
|
||||
.mask = 0x02000000, \
|
||||
.data = 0x02000000, \
|
||||
}, \
|
||||
{ \
|
||||
.index = (idx), \
|
||||
.reg = 0x110, \
|
||||
.mask = 0x00020000, \
|
||||
.data = 0x00020000, \
|
||||
}
|
||||
|
||||
static 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 */
|
||||
};
|
||||
|
||||
static 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 struct uniphier_clk_soc_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),
|
||||
};
|
||||
|
||||
static const struct udevice_id uniphier_mio_clk_match[] = {
|
||||
{
|
||||
.compatible = "socionext,ph1-sld3-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,ph1-ld4-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,ph1-pro4-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,ph1-sld8-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,ph1-pro5-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{
|
||||
.compatible = "socionext,proxstream2-mioctrl",
|
||||
.data = (ulong)&uniphier_mio_clk_data,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(uniphier_mio_clk) = {
|
||||
.name = "uniphier-mio-clk",
|
||||
.id = UCLASS_CLK,
|
||||
.of_match = uniphier_mio_clk_match,
|
||||
.probe = uniphier_clk_probe,
|
||||
.remove = uniphier_clk_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct uniphier_clk_priv),
|
||||
.ops = &uniphier_clk_ops,
|
||||
};
|
57
drivers/clk/uniphier/clk-uniphier.h
Normal file
57
drivers/clk/uniphier/clk-uniphier.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef __CLK_UNIPHIER_H__
|
||||
#define __CLK_UNIPHIER_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
struct uniphier_clk_gate_data {
|
||||
int index;
|
||||
unsigned int reg;
|
||||
u32 mask;
|
||||
u32 data;
|
||||
};
|
||||
|
||||
struct uniphier_clk_rate_data {
|
||||
int index;
|
||||
unsigned int reg;
|
||||
#define UNIPHIER_CLK_RATE_IS_FIXED UINT_MAX
|
||||
u32 mask;
|
||||
u32 data;
|
||||
unsigned long rate;
|
||||
};
|
||||
|
||||
struct uniphier_clk_soc_data {
|
||||
struct uniphier_clk_gate_data *gate;
|
||||
unsigned int nr_gate;
|
||||
struct uniphier_clk_rate_data *rate;
|
||||
unsigned int nr_rate;
|
||||
};
|
||||
|
||||
#define UNIPHIER_CLK_FIXED_RATE(i, f) \
|
||||
{ \
|
||||
.index = i, \
|
||||
.reg = UNIPHIER_CLK_RATE_IS_FIXED, \
|
||||
.rate = f, \
|
||||
}
|
||||
|
||||
/**
|
||||
* struct uniphier_clk_priv - private data for UniPhier clock driver
|
||||
*
|
||||
* @base: base address of the clock provider
|
||||
* @socdata: SoC specific data
|
||||
*/
|
||||
struct uniphier_clk_priv {
|
||||
void __iomem *base;
|
||||
struct uniphier_clk_soc_data *socdata;
|
||||
};
|
||||
|
||||
extern const struct clk_ops uniphier_clk_ops;
|
||||
int uniphier_clk_probe(struct udevice *dev);
|
||||
int uniphier_clk_remove(struct udevice *dev);
|
||||
|
||||
#endif /* __CLK_UNIPHIER_H__ */
|
Loading…
Reference in New Issue
Block a user