diff options
| author | Magnus Damm <damm@opensource.se> | 2013-06-17 02:40:52 -0400 |
|---|---|---|
| committer | Daniel Lezcano <daniel.lezcano@linaro.org> | 2013-08-21 18:18:45 -0400 |
| commit | 8874c5e3b92fc23af4fd4da8830f7d4de41d03a0 (patch) | |
| tree | 167b35941bbf6c3ece56c99ec97a10620480fe14 | |
| parent | 1745e696e174b54e37c057882970e50af1e80a7f (diff) | |
clocksource: sh_cmt: 32-bit control register support
Add support for CMT hardware with 32-bit control and counter
registers, as found on r8a73a4 and r8a7790. To use the CMT
with 32-bit hardware a second I/O memory resource needs to
point out the CMSTR register and it needs to be 32 bit wide.
Signed-off-by: Magnus Damm <damm@opensource.se>
Reviewed-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
| -rw-r--r-- | drivers/clocksource/sh_cmt.c | 50 |
1 files changed, 36 insertions, 14 deletions
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 08d0c418c94a..0965e9848b3d 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c | |||
| @@ -37,6 +37,7 @@ | |||
| 37 | 37 | ||
| 38 | struct sh_cmt_priv { | 38 | struct sh_cmt_priv { |
| 39 | void __iomem *mapbase; | 39 | void __iomem *mapbase; |
| 40 | void __iomem *mapbase_str; | ||
| 40 | struct clk *clk; | 41 | struct clk *clk; |
| 41 | unsigned long width; /* 16 or 32 bit version of hardware block */ | 42 | unsigned long width; /* 16 or 32 bit version of hardware block */ |
| 42 | unsigned long overflow_bit; | 43 | unsigned long overflow_bit; |
| @@ -79,6 +80,12 @@ struct sh_cmt_priv { | |||
| 79 | * CMCSR 0xffca0060 16-bit | 80 | * CMCSR 0xffca0060 16-bit |
| 80 | * CMCNT 0xffca0064 32-bit | 81 | * CMCNT 0xffca0064 32-bit |
| 81 | * CMCOR 0xffca0068 32-bit | 82 | * CMCOR 0xffca0068 32-bit |
| 83 | * | ||
| 84 | * "32-bit counter and 32-bit control" as found on r8a73a4 and r8a7790: | ||
| 85 | * CMSTR 0xffca0500 32-bit | ||
| 86 | * CMCSR 0xffca0510 32-bit | ||
| 87 | * CMCNT 0xffca0514 32-bit | ||
| 88 | * CMCOR 0xffca0518 32-bit | ||
| 82 | */ | 89 | */ |
| 83 | 90 | ||
| 84 | static unsigned long sh_cmt_read16(void __iomem *base, unsigned long offs) | 91 | static unsigned long sh_cmt_read16(void __iomem *base, unsigned long offs) |
| @@ -109,9 +116,7 @@ static void sh_cmt_write32(void __iomem *base, unsigned long offs, | |||
| 109 | 116 | ||
| 110 | static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p) | 117 | static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p) |
| 111 | { | 118 | { |
| 112 | struct sh_timer_config *cfg = p->pdev->dev.platform_data; | 119 | return p->read_control(p->mapbase_str, 0); |
| 113 | |||
| 114 | return p->read_control(p->mapbase - cfg->channel_offset, 0); | ||
| 115 | } | 120 | } |
| 116 | 121 | ||
| 117 | static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p) | 122 | static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p) |
| @@ -127,9 +132,7 @@ static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p) | |||
| 127 | static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p, | 132 | static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p, |
| 128 | unsigned long value) | 133 | unsigned long value) |
| 129 | { | 134 | { |
| 130 | struct sh_timer_config *cfg = p->pdev->dev.platform_data; | 135 | p->write_control(p->mapbase_str, 0, value); |
| 131 | |||
| 132 | p->write_control(p->mapbase - cfg->channel_offset, 0, value); | ||
| 133 | } | 136 | } |
| 134 | 137 | ||
| 135 | static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p, | 138 | static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p, |
| @@ -676,7 +679,7 @@ static int sh_cmt_register(struct sh_cmt_priv *p, char *name, | |||
| 676 | static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) | 679 | static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) |
| 677 | { | 680 | { |
| 678 | struct sh_timer_config *cfg = pdev->dev.platform_data; | 681 | struct sh_timer_config *cfg = pdev->dev.platform_data; |
| 679 | struct resource *res; | 682 | struct resource *res, *res2; |
| 680 | int irq, ret; | 683 | int irq, ret; |
| 681 | ret = -ENXIO; | 684 | ret = -ENXIO; |
| 682 | 685 | ||
| @@ -694,6 +697,9 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) | |||
| 694 | goto err0; | 697 | goto err0; |
| 695 | } | 698 | } |
| 696 | 699 | ||
| 700 | /* optional resource for the shared timer start/stop register */ | ||
| 701 | res2 = platform_get_resource(p->pdev, IORESOURCE_MEM, 1); | ||
| 702 | |||
| 697 | irq = platform_get_irq(p->pdev, 0); | 703 | irq = platform_get_irq(p->pdev, 0); |
| 698 | if (irq < 0) { | 704 | if (irq < 0) { |
| 699 | dev_err(&p->pdev->dev, "failed to get irq\n"); | 705 | dev_err(&p->pdev->dev, "failed to get irq\n"); |
| @@ -707,6 +713,15 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) | |||
| 707 | goto err0; | 713 | goto err0; |
| 708 | } | 714 | } |
| 709 | 715 | ||
| 716 | /* map second resource for CMSTR */ | ||
| 717 | p->mapbase_str = ioremap_nocache(res2 ? res2->start : | ||
| 718 | res->start - cfg->channel_offset, | ||
| 719 | res2 ? resource_size(res2) : 2); | ||
| 720 | if (p->mapbase_str == NULL) { | ||
| 721 | dev_err(&p->pdev->dev, "failed to remap I/O second memory\n"); | ||
| 722 | goto err1; | ||
| 723 | } | ||
| 724 | |||
| 710 | /* request irq using setup_irq() (too early for request_irq()) */ | 725 | /* request irq using setup_irq() (too early for request_irq()) */ |
| 711 | p->irqaction.name = dev_name(&p->pdev->dev); | 726 | p->irqaction.name = dev_name(&p->pdev->dev); |
| 712 | p->irqaction.handler = sh_cmt_interrupt; | 727 | p->irqaction.handler = sh_cmt_interrupt; |
| @@ -719,11 +734,17 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) | |||
| 719 | if (IS_ERR(p->clk)) { | 734 | if (IS_ERR(p->clk)) { |
| 720 | dev_err(&p->pdev->dev, "cannot get clock\n"); | 735 | dev_err(&p->pdev->dev, "cannot get clock\n"); |
| 721 | ret = PTR_ERR(p->clk); | 736 | ret = PTR_ERR(p->clk); |
| 722 | goto err1; | 737 | goto err2; |
| 723 | } | 738 | } |
| 724 | 739 | ||
| 725 | p->read_control = sh_cmt_read16; | 740 | if (res2 && (resource_size(res2) == 4)) { |
| 726 | p->write_control = sh_cmt_write16; | 741 | /* assume both CMSTR and CMCSR to be 32-bit */ |
| 742 | p->read_control = sh_cmt_read32; | ||
| 743 | p->write_control = sh_cmt_write32; | ||
| 744 | } else { | ||
| 745 | p->read_control = sh_cmt_read16; | ||
| 746 | p->write_control = sh_cmt_write16; | ||
| 747 | } | ||
| 727 | 748 | ||
| 728 | if (resource_size(res) == 6) { | 749 | if (resource_size(res) == 6) { |
| 729 | p->width = 16; | 750 | p->width = 16; |
| @@ -752,22 +773,23 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) | |||
| 752 | cfg->clocksource_rating); | 773 | cfg->clocksource_rating); |
| 753 | if (ret) { | 774 | if (ret) { |
| 754 | dev_err(&p->pdev->dev, "registration failed\n"); | 775 | dev_err(&p->pdev->dev, "registration failed\n"); |
| 755 | goto err2; | 776 | goto err3; |
| 756 | } | 777 | } |
| 757 | p->cs_enabled = false; | 778 | p->cs_enabled = false; |
| 758 | 779 | ||
| 759 | ret = setup_irq(irq, &p->irqaction); | 780 | ret = setup_irq(irq, &p->irqaction); |
| 760 | if (ret) { | 781 | if (ret) { |
| 761 | dev_err(&p->pdev->dev, "failed to request irq %d\n", irq); | 782 | dev_err(&p->pdev->dev, "failed to request irq %d\n", irq); |
| 762 | goto err2; | 783 | goto err3; |
| 763 | } | 784 | } |
| 764 | 785 | ||
| 765 | platform_set_drvdata(pdev, p); | 786 | platform_set_drvdata(pdev, p); |
| 766 | 787 | ||
| 767 | return 0; | 788 | return 0; |
| 768 | err2: | 789 | err3: |
| 769 | clk_put(p->clk); | 790 | clk_put(p->clk); |
| 770 | 791 | err2: | |
| 792 | iounmap(p->mapbase_str); | ||
| 771 | err1: | 793 | err1: |
| 772 | iounmap(p->mapbase); | 794 | iounmap(p->mapbase); |
| 773 | err0: | 795 | err0: |
