clocksource: sh_cmt: Split static information from sh_cmt_device
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Tue, 11 Feb 2014 22:46:48 +0000 (23:46 +0100)
committerSimon Horman <horms@verge.net.au>
Fri, 5 Dec 2014 00:22:48 +0000 (09:22 +0900)
Create a new sh_cmt_info structure to hold static information about the
device model and reference that structure from the sh_cmt_device
structure.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
(cherry picked from commit 2cda3ac49d5744432e9ebffb8ba47bef6eca053d)
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
drivers/clocksource/sh_cmt.c

index f94db32..879b8c2 100644 (file)
 
 struct sh_cmt_device;
 
+/*
+ * The CMT comes in 5 different identified flavours, depending not only on the
+ * SoC but also on the particular instance. The following table lists the main
+ * characteristics of those flavours.
+ *
+ *                     16B     32B     32B-F   48B     48B-2
+ * -----------------------------------------------------------------------------
+ * Channels            2       1/4     1       6       2/8
+ * Control Width       16      16      16      16      32
+ * Counter Width       16      32      32      32/48   32/48
+ * Shared Start/Stop   Y       Y       Y       Y       N
+ *
+ * The 48-bit gen2 version has a per-channel start/stop register located in the
+ * channel registers block. All other versions have a shared start/stop register
+ * located in the global space.
+ *
+ * Note that CMT0 on r8a73a4, r8a7790 and r8a7791, while implementing 32-bit
+ * channels only, is a 48-bit gen2 CMT with the 48-bit channels unavailable.
+ */
+
+enum sh_cmt_model {
+       SH_CMT_16BIT,
+       SH_CMT_32BIT,
+       SH_CMT_32BIT_FAST,
+       SH_CMT_48BIT,
+       SH_CMT_48BIT_GEN2,
+};
+
+struct sh_cmt_info {
+       enum sh_cmt_model model;
+
+       unsigned long width; /* 16 or 32 bit version of hardware block */
+       unsigned long overflow_bit;
+       unsigned long clear_bits;
+
+       /* callbacks for CMSTR and CMCSR access */
+       unsigned long (*read_control)(void __iomem *base, unsigned long offs);
+       void (*write_control)(void __iomem *base, unsigned long offs,
+                             unsigned long value);
+
+       /* callbacks for CMCNT and CMCOR access */
+       unsigned long (*read_count)(void __iomem *base, unsigned long offs);
+       void (*write_count)(void __iomem *base, unsigned long offs,
+                           unsigned long value);
+};
+
 struct sh_cmt_channel {
        struct sh_cmt_device *cmt;
        unsigned int index;
@@ -58,49 +104,16 @@ struct sh_cmt_channel {
 struct sh_cmt_device {
        struct platform_device *pdev;
 
+       const struct sh_cmt_info *info;
+
        void __iomem *mapbase_ch;
        void __iomem *mapbase;
        struct clk *clk;
 
        struct sh_cmt_channel *channels;
        unsigned int num_channels;
-
-       unsigned long width; /* 16 or 32 bit version of hardware block */
-       unsigned long overflow_bit;
-       unsigned long clear_bits;
-
-       /* callbacks for CMSTR and CMCSR access */
-       unsigned long (*read_control)(void __iomem *base, unsigned long offs);
-       void (*write_control)(void __iomem *base, unsigned long offs,
-                             unsigned long value);
-
-       /* callbacks for CMCNT and CMCOR access */
-       unsigned long (*read_count)(void __iomem *base, unsigned long offs);
-       void (*write_count)(void __iomem *base, unsigned long offs,
-                           unsigned long value);
 };
 
-/* Examples of supported CMT timer register layouts and I/O access widths:
- *
- * "16-bit counter and 16-bit control" as found on sh7263:
- * CMSTR 0xfffec000 16-bit
- * CMCSR 0xfffec002 16-bit
- * CMCNT 0xfffec004 16-bit
- * CMCOR 0xfffec006 16-bit
- *
- * "32-bit counter and 16-bit control" as found on sh7372, sh73a0, r8a7740:
- * CMSTR 0xffca0000 16-bit
- * CMCSR 0xffca0060 16-bit
- * CMCNT 0xffca0064 32-bit
- * CMCOR 0xffca0068 32-bit
- *
- * "32-bit counter and 32-bit control" as found on r8a73a4 and r8a7790:
- * CMSTR 0xffca0500 32-bit
- * CMCSR 0xffca0510 32-bit
- * CMCNT 0xffca0514 32-bit
- * CMCOR 0xffca0518 32-bit
- */
-
 static unsigned long sh_cmt_read16(void __iomem *base, unsigned long offs)
 {
        return ioread16(base + (offs << 1));
@@ -123,47 +136,100 @@ static void sh_cmt_write32(void __iomem *base, unsigned long offs,
        iowrite32(value, base + (offs << 2));
 }
 
+static const struct sh_cmt_info sh_cmt_info[] = {
+       [SH_CMT_16BIT] = {
+               .model = SH_CMT_16BIT,
+               .width = 16,
+               .overflow_bit = 0x80,
+               .clear_bits = ~0x80,
+               .read_control = sh_cmt_read16,
+               .write_control = sh_cmt_write16,
+               .read_count = sh_cmt_read16,
+               .write_count = sh_cmt_write16,
+       },
+       [SH_CMT_32BIT] = {
+               .model = SH_CMT_32BIT,
+               .width = 32,
+               .overflow_bit = 0x8000,
+               .clear_bits = ~0xc000,
+               .read_control = sh_cmt_read16,
+               .write_control = sh_cmt_write16,
+               .read_count = sh_cmt_read32,
+               .write_count = sh_cmt_write32,
+       },
+       [SH_CMT_32BIT_FAST] = {
+               .model = SH_CMT_32BIT_FAST,
+               .width = 32,
+               .overflow_bit = 0x8000,
+               .clear_bits = ~0xc000,
+               .read_control = sh_cmt_read16,
+               .write_control = sh_cmt_write16,
+               .read_count = sh_cmt_read32,
+               .write_count = sh_cmt_write32,
+       },
+       [SH_CMT_48BIT] = {
+               .model = SH_CMT_48BIT,
+               .width = 32,
+               .overflow_bit = 0x8000,
+               .clear_bits = ~0xc000,
+               .read_control = sh_cmt_read32,
+               .write_control = sh_cmt_write32,
+               .read_count = sh_cmt_read32,
+               .write_count = sh_cmt_write32,
+       },
+       [SH_CMT_48BIT_GEN2] = {
+               .model = SH_CMT_48BIT_GEN2,
+               .width = 32,
+               .overflow_bit = 0x8000,
+               .clear_bits = ~0xc000,
+               .read_control = sh_cmt_read32,
+               .write_control = sh_cmt_write32,
+               .read_count = sh_cmt_read32,
+               .write_count = sh_cmt_write32,
+       },
+};
+
 #define CMCSR 0 /* channel register */
 #define CMCNT 1 /* channel register */
 #define CMCOR 2 /* channel register */
 
 static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch)
 {
-       return ch->cmt->read_control(ch->cmt->mapbase, 0);
+       return ch->cmt->info->read_control(ch->cmt->mapbase, 0);
 }
 
 static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch)
 {
-       return ch->cmt->read_control(ch->base, CMCSR);
+       return ch->cmt->info->read_control(ch->base, CMCSR);
 }
 
 static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch)
 {
-       return ch->cmt->read_count(ch->base, CMCNT);
+       return ch->cmt->info->read_count(ch->base, CMCNT);
 }
 
 static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch,
                                      unsigned long value)
 {
-       ch->cmt->write_control(ch->cmt->mapbase, 0, value);
+       ch->cmt->info->write_control(ch->cmt->mapbase, 0, value);
 }
 
 static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch,
                                      unsigned long value)
 {
-       ch->cmt->write_control(ch->base, CMCSR, value);
+       ch->cmt->info->write_control(ch->base, CMCSR, value);
 }
 
 static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch,
                                      unsigned long value)
 {
-       ch->cmt->write_count(ch->base, CMCNT, value);
+       ch->cmt->info->write_count(ch->base, CMCNT, value);
 }
 
 static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch,
                                      unsigned long value)
 {
-       ch->cmt->write_count(ch->base, CMCOR, value);
+       ch->cmt->info->write_count(ch->base, CMCOR, value);
 }
 
 static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch,
@@ -172,7 +238,7 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch,
        unsigned long v1, v2, v3;
        int o1, o2;
 
-       o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->overflow_bit;
+       o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->info->overflow_bit;
 
        /* Make sure the timer value is stable. Stolen from acpi_pm.c */
        do {
@@ -180,7 +246,7 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch,
                v1 = sh_cmt_read_cmcnt(ch);
                v2 = sh_cmt_read_cmcnt(ch);
                v3 = sh_cmt_read_cmcnt(ch);
-               o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->overflow_bit;
+               o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->info->overflow_bit;
        } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
                          || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
 
@@ -227,7 +293,7 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch, unsigned long *rate)
        sh_cmt_start_stop_ch(ch, 0);
 
        /* configure channel, periodic mode and maximum timeout */
-       if (ch->cmt->width == 16) {
+       if (ch->cmt->info->width == 16) {
                *rate = clk_get_rate(ch->cmt->clk) / 512;
                sh_cmt_write_cmcsr(ch, 0x43);
        } else {
@@ -405,7 +471,8 @@ static irqreturn_t sh_cmt_interrupt(int irq, void *dev_id)
        struct sh_cmt_channel *ch = dev_id;
 
        /* clear flags */
-       sh_cmt_write_cmcsr(ch, sh_cmt_read_cmcsr(ch) & ch->cmt->clear_bits);
+       sh_cmt_write_cmcsr(ch, sh_cmt_read_cmcsr(ch) &
+                          ch->cmt->info->clear_bits);
 
        /* update clock source counter to begin with if enabled
         * the wrap flag should be cleared by the timer specific
@@ -719,10 +786,10 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index,
                return irq;
        }
 
-       if (cmt->width == (sizeof(ch->max_match_value) * 8))
+       if (cmt->info->width == (sizeof(ch->max_match_value) * 8))
                ch->max_match_value = ~0;
        else
-               ch->max_match_value = (1 << cmt->width) - 1;
+               ch->max_match_value = (1 << cmt->info->width) - 1;
 
        ch->match_value = ch->max_match_value;
        raw_spin_lock_init(&ch->lock);
@@ -800,28 +867,13 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
        if (ret < 0)
                goto err3;
 
-       if (res2 && (resource_size(res2) == 4)) {
-               /* assume both CMSTR and CMCSR to be 32-bit */
-               cmt->read_control = sh_cmt_read32;
-               cmt->write_control = sh_cmt_write32;
-       } else {
-               cmt->read_control = sh_cmt_read16;
-               cmt->write_control = sh_cmt_write16;
-       }
-
-       if (resource_size(res) == 6) {
-               cmt->width = 16;
-               cmt->read_count = sh_cmt_read16;
-               cmt->write_count = sh_cmt_write16;
-               cmt->overflow_bit = 0x80;
-               cmt->clear_bits = ~0x80;
-       } else {
-               cmt->width = 32;
-               cmt->read_count = sh_cmt_read32;
-               cmt->write_count = sh_cmt_write32;
-               cmt->overflow_bit = 0x8000;
-               cmt->clear_bits = ~0xc000;
-       }
+       /* identify the model based on the resources */
+       if (resource_size(res) == 6)
+               cmt->info = &sh_cmt_info[SH_CMT_16BIT];
+       else if (res2 && (resource_size(res2) == 4))
+               cmt->info = &sh_cmt_info[SH_CMT_48BIT_GEN2];
+       else
+               cmt->info = &sh_cmt_info[SH_CMT_32BIT];
 
        cmt->channels = kzalloc(sizeof(*cmt->channels), GFP_KERNEL);
        if (cmt->channels == NULL) {