diff options
Diffstat (limited to 'drivers/clocksource/sh_mtu2.c')
-rw-r--r-- | drivers/clocksource/sh_mtu2.c | 184 |
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) |