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>master
parent
fec4816387
commit
48264d9beb
@ -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 |
@ -0,0 +1,3 @@ |
||||
obj-y += clk-uniphier-core.o
|
||||
|
||||
obj-$(CONFIG_CLK_UNIPHIER_MIO) += clk-uniphier-mio.o
|
@ -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; |
||||
} |
@ -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, |
||||
}; |
@ -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