diff options
Diffstat (limited to 'drivers/clocksource')
-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 { | |||
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 | ||
59 | static DEFINE_RAW_SPINLOCK(sh_mtu2_lock); | 62 | 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) | |||
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, | |||
353 | static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name, | 362 | static 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 | ||
362 | static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, | 373 | static 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 | ||
381 | static int sh_mtu2_setup(struct sh_mtu2_device *mtu, | 415 | static 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 | |||
441 | static 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 | |||
451 | static 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 | |
514 | err_unmap: | ||
445 | kfree(mtu->channels); | 515 | kfree(mtu->channels); |
516 | sh_mtu2_unmap_memory(mtu); | ||
517 | err_clk_unprepare: | ||
446 | clk_unprepare(mtu->clk); | 518 | clk_unprepare(mtu->clk); |
447 | err2: | 519 | err_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 | ||
455 | static int sh_mtu2_probe(struct platform_device *pdev) | 524 | static 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 | ||
568 | static const struct platform_device_id sh_mtu2_id_table[] = { | ||
569 | { "sh_mtu2", 1 }, | ||
570 | { "sh-mtu2", 0 }, | ||
571 | { }, | ||
572 | }; | ||
573 | MODULE_DEVICE_TABLE(platform, sh_mtu2_id_table); | ||
574 | |||
500 | static struct platform_driver sh_mtu2_device_driver = { | 575 | static 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 | ||
508 | static int __init sh_mtu2_init(void) | 584 | static int __init sh_mtu2_init(void) |