|
@@ -37,6 +37,52 @@
|
|
|
|
|
|
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) {
|