aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2014-03-04 12:05:45 -0500
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2014-04-16 06:03:32 -0400
commitfaf3f4f8c805f5f8a786ba544c94bf3e01838388 (patch)
tree3d1c21b3e98c025fb5f996c384a99a31d9c32941 /drivers/clocksource
parent207e21a9732a27f58843ccae1c9644f3a1636b66 (diff)
clocksource: sh_mtu2: Add support for multiple channels per device
MTU2 hardware devices can support multiple channels, with global registers and per-channel registers. The sh_mtu2 driver currently models the hardware with one Linux device per channel. This model makes it difficult to handle global registers in a clean way. Add support for a new model that uses one Linux device per timer with multiple channels per device. This requires changes to platform data, add new channel configuration fields. Support for the legacy model is kept and will be removed after all platforms switch to the new model. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Tested-by: Wolfram Sang <wsa@sang-engineering.com>
Diffstat (limited to 'drivers/clocksource')
-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 {
54 54
55 struct sh_mtu2_channel *channels; 55 struct sh_mtu2_channel *channels;
56 unsigned int num_channels; 56 unsigned int num_channels;
57
58 bool legacy;
59 bool has_clockevent;
57}; 60};
58 61
59static DEFINE_RAW_SPINLOCK(sh_mtu2_lock); 62static 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)
163{ 166{
164 unsigned long offs; 167 unsigned long offs;
165 168
166 if (reg_nr == TSTR) 169 if (reg_nr == TSTR) {
167 return ioread8(ch->mtu->mapbase); 170 if (ch->mtu->legacy)
171 return ioread8(ch->mtu->mapbase);
172 else
173 return ioread8(ch->mtu->mapbase + 0x280);
174 }
168 175
169 offs = mtu2_reg_offs[reg_nr]; 176 offs = mtu2_reg_offs[reg_nr];
170 177
@@ -180,8 +187,10 @@ static inline void sh_mtu2_write(struct sh_mtu2_channel *ch, int reg_nr,
180 unsigned long offs; 187 unsigned long offs;
181 188
182 if (reg_nr == TSTR) { 189 if (reg_nr == TSTR) {
183 iowrite8(value, ch->mtu->mapbase); 190 if (ch->mtu->legacy)
184 return; 191 return iowrite8(value, ch->mtu->mapbase);
192 else
193 return iowrite8(value, ch->mtu->mapbase + 0x280);
185 } 194 }
186 195
187 offs = mtu2_reg_offs[reg_nr]; 196 offs = mtu2_reg_offs[reg_nr];
@@ -353,109 +362,168 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch,
353static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name, 362static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name,
354 bool clockevent) 363 bool clockevent)
355{ 364{
356 if (clockevent) 365 if (clockevent) {
366 ch->mtu->has_clockevent = true;
357 sh_mtu2_register_clockevent(ch, name); 367 sh_mtu2_register_clockevent(ch, name);
368 }
358 369
359 return 0; 370 return 0;
360} 371}
361 372
362static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, 373static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, unsigned int index,
363 struct sh_mtu2_device *mtu) 374 struct sh_mtu2_device *mtu)
364{ 375{
365 struct sh_timer_config *cfg = mtu->pdev->dev.platform_data; 376 static const unsigned int channel_offsets[] = {
377 0x300, 0x380, 0x000,
378 };
379 bool clockevent;
366 380
367 ch->mtu = mtu; 381 ch->mtu = mtu;
368 ch->index = cfg->timer_bit;
369 382
370 ch->irq = platform_get_irq(mtu->pdev, 0); 383 if (mtu->legacy) {
384 struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
385
386 clockevent = cfg->clockevent_rating != 0;
387
388 ch->irq = platform_get_irq(mtu->pdev, 0);
389 ch->base = mtu->mapbase - cfg->channel_offset;
390 ch->index = cfg->timer_bit;
391 } else {
392 char name[6];
393
394 clockevent = true;
395
396 sprintf(name, "tgi%ua", index);
397 ch->irq = platform_get_irq_byname(mtu->pdev, name);
398 ch->base = mtu->mapbase + channel_offsets[index];
399 ch->index = index;
400 }
401
371 if (ch->irq < 0) { 402 if (ch->irq < 0) {
403 /* Skip channels with no declared interrupt. */
404 if (!mtu->legacy)
405 return 0;
406
372 dev_err(&mtu->pdev->dev, "ch%u: failed to get irq\n", 407 dev_err(&mtu->pdev->dev, "ch%u: failed to get irq\n",
373 ch->index); 408 ch->index);
374 return ch->irq; 409 return ch->irq;
375 } 410 }
376 411
377 return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev), 412 return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev), clockevent);
378 cfg->clockevent_rating != 0);
379} 413}
380 414
381static int sh_mtu2_setup(struct sh_mtu2_device *mtu, 415static int sh_mtu2_map_memory(struct sh_mtu2_device *mtu)
382 struct platform_device *pdev)
383{ 416{
384 struct sh_timer_config *cfg = pdev->dev.platform_data;
385 struct resource *res; 417 struct resource *res;
386 void __iomem *base;
387 int ret;
388 ret = -ENXIO;
389
390 mtu->pdev = pdev;
391
392 if (!cfg) {
393 dev_err(&mtu->pdev->dev, "missing platform data\n");
394 goto err0;
395 }
396
397 platform_set_drvdata(pdev, mtu);
398 418
399 res = platform_get_resource(mtu->pdev, IORESOURCE_MEM, 0); 419 res = platform_get_resource(mtu->pdev, IORESOURCE_MEM, 0);
400 if (!res) { 420 if (!res) {
401 dev_err(&mtu->pdev->dev, "failed to get I/O memory\n"); 421 dev_err(&mtu->pdev->dev, "failed to get I/O memory\n");
402 goto err0; 422 return -ENXIO;
403 } 423 }
404 424
425 mtu->mapbase = ioremap_nocache(res->start, resource_size(res));
426 if (mtu->mapbase == NULL)
427 return -ENXIO;
428
405 /* 429 /*
406 * Map memory, let base point to our channel and mapbase to the 430 * In legacy platform device configuration (with one device per channel)
407 * start/stop shared register. 431 * the resource points to the channel base address.
408 */ 432 */
409 base = ioremap_nocache(res->start, resource_size(res)); 433 if (mtu->legacy) {
410 if (base == NULL) { 434 struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
411 dev_err(&mtu->pdev->dev, "failed to remap I/O memory\n"); 435 mtu->mapbase += cfg->channel_offset;
412 goto err0; 436 }
437
438 return 0;
439}
440
441static void sh_mtu2_unmap_memory(struct sh_mtu2_device *mtu)
442{
443 if (mtu->legacy) {
444 struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
445 mtu->mapbase -= cfg->channel_offset;
413 } 446 }
414 447
415 mtu->mapbase = base + cfg->channel_offset; 448 iounmap(mtu->mapbase);
449}
450
451static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
452 struct platform_device *pdev)
453{
454 struct sh_timer_config *cfg = pdev->dev.platform_data;
455 const struct platform_device_id *id = pdev->id_entry;
456 unsigned int i;
457 int ret;
458
459 mtu->pdev = pdev;
460 mtu->legacy = id->driver_data;
461
462 if (mtu->legacy && !cfg) {
463 dev_err(&mtu->pdev->dev, "missing platform data\n");
464 return -ENXIO;
465 }
416 466
417 /* get hold of clock */ 467 /* Get hold of clock. */
418 mtu->clk = clk_get(&mtu->pdev->dev, "mtu2_fck"); 468 mtu->clk = clk_get(&mtu->pdev->dev, "mtu2_fck");
419 if (IS_ERR(mtu->clk)) { 469 if (IS_ERR(mtu->clk)) {
420 dev_err(&mtu->pdev->dev, "cannot get clock\n"); 470 dev_err(&mtu->pdev->dev, "cannot get clock\n");
421 ret = PTR_ERR(mtu->clk); 471 return PTR_ERR(mtu->clk);
422 goto err1;
423 } 472 }
424 473
425 ret = clk_prepare(mtu->clk); 474 ret = clk_prepare(mtu->clk);
426 if (ret < 0) 475 if (ret < 0)
427 goto err2; 476 goto err_clk_put;
428 477
429 mtu->channels = kzalloc(sizeof(*mtu->channels), GFP_KERNEL); 478 /* Map the memory resource. */
479 ret = sh_mtu2_map_memory(mtu);
480 if (ret < 0) {
481 dev_err(&mtu->pdev->dev, "failed to remap I/O memory\n");
482 goto err_clk_unprepare;
483 }
484
485 /* Allocate and setup the channels. */
486 if (mtu->legacy)
487 mtu->num_channels = 1;
488 else
489 mtu->num_channels = 3;
490
491 mtu->channels = kzalloc(sizeof(*mtu->channels) * mtu->num_channels,
492 GFP_KERNEL);
430 if (mtu->channels == NULL) { 493 if (mtu->channels == NULL) {
431 ret = -ENOMEM; 494 ret = -ENOMEM;
432 goto err3; 495 goto err_unmap;
433 } 496 }
434 497
435 mtu->num_channels = 1; 498 if (mtu->legacy) {
436 499 ret = sh_mtu2_setup_channel(&mtu->channels[0], 0, mtu);
437 mtu->channels[0].base = base; 500 if (ret < 0)
501 goto err_unmap;
502 } else {
503 for (i = 0; i < mtu->num_channels; ++i) {
504 ret = sh_mtu2_setup_channel(&mtu->channels[i], i, mtu);
505 if (ret < 0)
506 goto err_unmap;
507 }
508 }
438 509
439 ret = sh_mtu2_setup_channel(&mtu->channels[0], mtu); 510 platform_set_drvdata(pdev, mtu);
440 if (ret < 0)
441 goto err3;
442 511
443 return 0; 512 return 0;
444 err3: 513
514err_unmap:
445 kfree(mtu->channels); 515 kfree(mtu->channels);
516 sh_mtu2_unmap_memory(mtu);
517err_clk_unprepare:
446 clk_unprepare(mtu->clk); 518 clk_unprepare(mtu->clk);
447 err2: 519err_clk_put:
448 clk_put(mtu->clk); 520 clk_put(mtu->clk);
449 err1:
450 iounmap(base);
451 err0:
452 return ret; 521 return ret;
453} 522}
454 523
455static int sh_mtu2_probe(struct platform_device *pdev) 524static int sh_mtu2_probe(struct platform_device *pdev)
456{ 525{
457 struct sh_mtu2_device *mtu = platform_get_drvdata(pdev); 526 struct sh_mtu2_device *mtu = platform_get_drvdata(pdev);
458 struct sh_timer_config *cfg = pdev->dev.platform_data;
459 int ret; 527 int ret;
460 528
461 if (!is_early_platform_device(pdev)) { 529 if (!is_early_platform_device(pdev)) {
@@ -484,7 +552,7 @@ static int sh_mtu2_probe(struct platform_device *pdev)
484 return 0; 552 return 0;
485 553
486 out: 554 out:
487 if (cfg->clockevent_rating) 555 if (mtu->has_clockevent)
488 pm_runtime_irq_safe(&pdev->dev); 556 pm_runtime_irq_safe(&pdev->dev);
489 else 557 else
490 pm_runtime_idle(&pdev->dev); 558 pm_runtime_idle(&pdev->dev);
@@ -497,12 +565,20 @@ static int sh_mtu2_remove(struct platform_device *pdev)
497 return -EBUSY; /* cannot unregister clockevent */ 565 return -EBUSY; /* cannot unregister clockevent */
498} 566}
499 567
568static const struct platform_device_id sh_mtu2_id_table[] = {
569 { "sh_mtu2", 1 },
570 { "sh-mtu2", 0 },
571 { },
572};
573MODULE_DEVICE_TABLE(platform, sh_mtu2_id_table);
574
500static struct platform_driver sh_mtu2_device_driver = { 575static struct platform_driver sh_mtu2_device_driver = {
501 .probe = sh_mtu2_probe, 576 .probe = sh_mtu2_probe,
502 .remove = sh_mtu2_remove, 577 .remove = sh_mtu2_remove,
503 .driver = { 578 .driver = {
504 .name = "sh_mtu2", 579 .name = "sh_mtu2",
505 } 580 },
581 .id_table = sh_mtu2_id_table,
506}; 582};
507 583
508static int __init sh_mtu2_init(void) 584static int __init sh_mtu2_init(void)