clocksource: sh_cmt: Split static information from sh_cmt_device

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>
This commit is contained in:
Laurent Pinchart 2014-02-11 23:46:48 +01:00
parent f5ec9b194a
commit 2cda3ac49d

View File

@ -37,6 +37,52 @@
struct sh_cmt_device; 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_channel {
struct sh_cmt_device *cmt; struct sh_cmt_device *cmt;
unsigned int index; unsigned int index;
@ -58,49 +104,16 @@ struct sh_cmt_channel {
struct sh_cmt_device { struct sh_cmt_device {
struct platform_device *pdev; struct platform_device *pdev;
const struct sh_cmt_info *info;
void __iomem *mapbase_ch; void __iomem *mapbase_ch;
void __iomem *mapbase; void __iomem *mapbase;
struct clk *clk; struct clk *clk;
struct sh_cmt_channel *channels; struct sh_cmt_channel *channels;
unsigned int num_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) static unsigned long sh_cmt_read16(void __iomem *base, unsigned long offs)
{ {
return ioread16(base + (offs << 1)); 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)); 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 CMCSR 0 /* channel register */
#define CMCNT 1 /* channel register */ #define CMCNT 1 /* channel register */
#define CMCOR 2 /* channel register */ #define CMCOR 2 /* channel register */
static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch) 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) 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) 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, static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch,
unsigned long value) 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, static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch,
unsigned long value) 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, static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch,
unsigned long value) 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, static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch,
unsigned long value) 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, 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; unsigned long v1, v2, v3;
int o1, o2; 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 */ /* Make sure the timer value is stable. Stolen from acpi_pm.c */
do { do {
@ -180,7 +246,7 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch,
v1 = sh_cmt_read_cmcnt(ch); v1 = sh_cmt_read_cmcnt(ch);
v2 = sh_cmt_read_cmcnt(ch); v2 = sh_cmt_read_cmcnt(ch);
v3 = 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) } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
|| (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); || (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); sh_cmt_start_stop_ch(ch, 0);
/* configure channel, periodic mode and maximum timeout */ /* 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; *rate = clk_get_rate(ch->cmt->clk) / 512;
sh_cmt_write_cmcsr(ch, 0x43); sh_cmt_write_cmcsr(ch, 0x43);
} else { } else {
@ -405,7 +471,8 @@ static irqreturn_t sh_cmt_interrupt(int irq, void *dev_id)
struct sh_cmt_channel *ch = dev_id; struct sh_cmt_channel *ch = dev_id;
/* clear flags */ /* 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 /* update clock source counter to begin with if enabled
* the wrap flag should be cleared by the timer specific * 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; 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; ch->max_match_value = ~0;
else 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; ch->match_value = ch->max_match_value;
raw_spin_lock_init(&ch->lock); 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) if (ret < 0)
goto err3; goto err3;
if (res2 && (resource_size(res2) == 4)) { /* identify the model based on the resources */
/* assume both CMSTR and CMCSR to be 32-bit */ if (resource_size(res) == 6)
cmt->read_control = sh_cmt_read32; cmt->info = &sh_cmt_info[SH_CMT_16BIT];
cmt->write_control = sh_cmt_write32; else if (res2 && (resource_size(res2) == 4))
} else { cmt->info = &sh_cmt_info[SH_CMT_48BIT_GEN2];
cmt->read_control = sh_cmt_read16; else
cmt->write_control = sh_cmt_write16; cmt->info = &sh_cmt_info[SH_CMT_32BIT];
}
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;
}
cmt->channels = kzalloc(sizeof(*cmt->channels), GFP_KERNEL); cmt->channels = kzalloc(sizeof(*cmt->channels), GFP_KERNEL);
if (cmt->channels == NULL) { if (cmt->channels == NULL) {