summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/clocksource/sh_mtu2.c184
1 files changed, 130 insertions, 54 deletions
diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c
index 14cc7b6f703b..7cc6d9429f81 100644
--- a/drivers/clocksource/sh_mtu2.c
+++ b/drivers/clocksource/sh_mtu2.c
@@ -54,6 +54,9 @@ struct sh_mtu2_device {
struct sh_mtu2_channel *channels;
unsigned int num_channels;
+
+ bool legacy;
+ bool has_clockevent;
};
static DEFINE_RAW_SPINLOCK(sh_mtu2_lock);
@@ -163,8 +166,12 @@ static inline unsigned long sh_mtu2_read(struct sh_mtu2_channel *ch, int reg_nr)
{
unsigned long offs;
- if (reg_nr == TSTR)
- return ioread8(ch->mtu->mapbase);
+ if (reg_nr == TSTR) {
+ if (ch->mtu->legacy)
+ return ioread8(ch->mtu->mapbase);
+ else
+ return ioread8(ch->mtu->mapbase + 0x280);
+ }
offs = mtu2_reg_offs[reg_nr];
@@ -180,8 +187,10 @@ static inline void sh_mtu2_write(struct sh_mtu2_channel *ch, int reg_nr,
unsigned long offs;
if (reg_nr == TSTR) {
- iowrite8(value, ch->mtu->mapbase);
- return;
+ if (ch->mtu->legacy)
+ return iowrite8(value, ch->mtu->mapbase);
+ else
+ return iowrite8(value, ch->mtu->mapbase + 0x280);
}
offs = mtu2_reg_offs[reg_nr];
@@ -353,109 +362,168 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch,
static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name,
bool clockevent)
{
- if (clockevent)
+ if (clockevent) {
+ ch->mtu->has_clockevent = true;
sh_mtu2_register_clockevent(ch, name);
+ }
return 0;
}
-static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch,
+static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, unsigned int index,
struct sh_mtu2_device *mtu)
{
- struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
+ static const unsigned int channel_offsets[] = {
+ 0x300, 0x380, 0x000,
+ };
+ bool clockevent;
ch->mtu = mtu;
- ch->index = cfg->timer_bit;
- ch->irq = platform_get_irq(mtu->pdev, 0);
+ if (mtu->legacy) {
+ struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
+
+ clockevent = cfg->clockevent_rating != 0;
+
+ ch->irq = platform_get_irq(mtu->pdev, 0);
+ ch->base = mtu->mapbase - cfg->channel_offset;
+ ch->index = cfg->timer_bit;
+ } else {
+ char name[6];
+
+ clockevent = true;
+
+ sprintf(name, "tgi%ua", index);
+ ch->irq = platform_get_irq_byname(mtu->pdev, name);
+ ch->base = mtu->mapbase + channel_offsets[index];
+ ch->index = index;
+ }
+
if (ch->irq < 0) {
+ /* Skip channels with no declared interrupt. */
+ if (!mtu->legacy)
+ return 0;
+
dev_err(&mtu->pdev->dev, "ch%u: failed to get irq\n",
ch->index);
return ch->irq;
}
- return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev),
- cfg->clockevent_rating != 0);
+ return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev), clockevent);
}
-static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
- struct platform_device *pdev)
+static int sh_mtu2_map_memory(struct sh_mtu2_device *mtu)
{
- struct sh_timer_config *cfg = pdev->dev.platform_data;
struct resource *res;
- void __iomem *base;
- int ret;
- ret = -ENXIO;
-
- mtu->pdev = pdev;
-
- if (!cfg) {
- dev_err(&mtu->pdev->dev, "missing platform data\n");
- goto err0;
- }
-
- platform_set_drvdata(pdev, mtu);
res = platform_get_resource(mtu->pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&mtu->pdev->dev, "failed to get I/O memory\n");
- goto err0;
+ return -ENXIO;
}
+ mtu->mapbase = ioremap_nocache(res->start, resource_size(res));
+ if (mtu->mapbase == NULL)
+ return -ENXIO;
+
/*
- * Map memory, let base point to our channel and mapbase to the
- * start/stop shared register.
+ * In legacy platform device configuration (with one device per channel)
+ * the resource points to the channel base address.
*/
- base = ioremap_nocache(res->start, resource_size(res));
- if (base == NULL) {
- dev_err(&mtu->pdev->dev, "failed to remap I/O memory\n");
- goto err0;
+ if (mtu->legacy) {
+ struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
+ mtu->mapbase += cfg->channel_offset;
+ }
+
+ return 0;
+}
+
+static void sh_mtu2_unmap_memory(struct sh_mtu2_device *mtu)
+{
+ if (mtu->legacy) {
+ struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
+ mtu->mapbase -= cfg->channel_offset;
}
- mtu->mapbase = base + cfg->channel_offset;
+ iounmap(mtu->mapbase);
+}
+
+static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
+ struct platform_device *pdev)
+{
+ struct sh_timer_config *cfg = pdev->dev.platform_data;
+ const struct platform_device_id *id = pdev->id_entry;
+ unsigned int i;
+ int ret;
+
+ mtu->pdev = pdev;
+ mtu->legacy = id->driver_data;
+
+ if (mtu->legacy && !cfg) {
+ dev_err(&mtu->pdev->dev, "missing platform data\n");
+ return -ENXIO;
+ }
- /* get hold of clock */
+ /* Get hold of clock. */
mtu->clk = clk_get(&mtu->pdev->dev, "mtu2_fck");
if (IS_ERR(mtu->clk)) {
dev_err(&mtu->pdev->dev, "cannot get clock\n");
- ret = PTR_ERR(mtu->clk);
- goto err1;
+ return PTR_ERR(mtu->clk);
}
ret = clk_prepare(mtu->clk);
if (ret < 0)
- goto err2;
+ goto err_clk_put;
- mtu->channels = kzalloc(sizeof(*mtu->channels), GFP_KERNEL);
+ /* Map the memory resource. */
+ ret = sh_mtu2_map_memory(mtu);
+ if (ret < 0) {
+ dev_err(&mtu->pdev->dev, "failed to remap I/O memory\n");
+ goto err_clk_unprepare;
+ }
+
+ /* Allocate and setup the channels. */
+ if (mtu->legacy)
+ mtu->num_channels = 1;
+ else
+ mtu->num_channels = 3;
+
+ mtu->channels = kzalloc(sizeof(*mtu->channels) * mtu->num_channels,
+ GFP_KERNEL);
if (mtu->channels == NULL) {
ret = -ENOMEM;
- goto err3;
+ goto err_unmap;
}
- mtu->num_channels = 1;
-
- mtu->channels[0].base = base;
+ if (mtu->legacy) {
+ ret = sh_mtu2_setup_channel(&mtu->channels[0], 0, mtu);
+ if (ret < 0)
+ goto err_unmap;
+ } else {
+ for (i = 0; i < mtu->num_channels; ++i) {
+ ret = sh_mtu2_setup_channel(&mtu->channels[i], i, mtu);
+ if (ret < 0)
+ goto err_unmap;
+ }
+ }
- ret = sh_mtu2_setup_channel(&mtu->channels[0], mtu);
- if (ret < 0)
- goto err3;
+ platform_set_drvdata(pdev, mtu);
return 0;
- err3:
+
+err_unmap:
kfree(mtu->channels);
+ sh_mtu2_unmap_memory(mtu);
+err_clk_unprepare:
clk_unprepare(mtu->clk);
- err2:
+err_clk_put:
clk_put(mtu->clk);
- err1:
- iounmap(base);
- err0:
return ret;
}
static int sh_mtu2_probe(struct platform_device *pdev)
{
struct sh_mtu2_device *mtu = platform_get_drvdata(pdev);
- struct sh_timer_config *cfg = pdev->dev.platform_data;
int ret;
if (!is_early_platform_device(pdev)) {
@@ -484,7 +552,7 @@ static int sh_mtu2_probe(struct platform_device *pdev)
return 0;
out:
- if (cfg->clockevent_rating)
+ if (mtu->has_clockevent)
pm_runtime_irq_safe(&pdev->dev);
else
pm_runtime_idle(&pdev->dev);
@@ -497,12 +565,20 @@ static int sh_mtu2_remove(struct platform_device *pdev)
return -EBUSY; /* cannot unregister clockevent */
}
+static const struct platform_device_id sh_mtu2_id_table[] = {
+ { "sh_mtu2", 1 },
+ { "sh-mtu2", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, sh_mtu2_id_table);
+
static struct platform_driver sh_mtu2_device_driver = {
.probe = sh_mtu2_probe,
.remove = sh_mtu2_remove,
.driver = {
.name = "sh_mtu2",
- }
+ },
+ .id_table = sh_mtu2_id_table,
};
static int __init sh_mtu2_init(void)