Hello,
This patch set adds DT support to the Renesas CMT driver.
The first 14 patches are a bunch of necessary cleanups that reorganize the
driver, its platform data, and the memory, interrupt and clock resources it
expects. As a result the driver accepts a new platform data model close to the
hardware with supports for all the timer channels using a single device.
The next 7 patches (15 to 21) move all platforms from the old to the new
platform data model. Patch 22 then drops support for the old model, and patch
23 finally adds DT support.
Patches 24 to 27 then add the CMT devices to the r8a7790 and r8a7791 device
trees, and enable them for the lager-reference and koelsch-reference boards.
The patches are based on top of Simon's latest devel branch with the
"[PATCH v4 0/2] [RESEND] Lager and Koelsch reference serial port support"
series applied.
Laurent Pinchart (27):
clocksource: sh_cmt: Split channel fields from sh_cmt_priv
clocksource: sh_cmt: Rename struct sh_cmt_priv to sh_cmt_device
clocksource: sh_cmt: Split channel setup to separate function
clocksource: sh_cmt: Rename mapbase/mapbase_str to mapbase_ch/mapbase
clocksource: sh_cmt: Add memory base to sh_cmt_channel structure
clocksource: sh_cmt: Add index to struct sh_cmt_channel
clocksource: sh_cmt: Replace kmalloc + memset with kzalloc
clocksource: sh_cmt: Allocate channels dynamically
clocksource: sh_cmt: Split static information from sh_cmt_device
clocksource: sh_cmt: Replace hardcoded register values with macros
clocksource: sh_cmt: Add support for multiple channels per device
clocksource: sh_cmt: Acquire default clock in the non-legacy case
clocksource: sh_cmt: Remove FSF mail address from GPL notice
clocksource: sh_cmt: Sort headers alphabetically
sh: Switch to new style CMT device
ARM: shmobile: sh7372: Switch to new style CMT device
ARM: shmobile: sh73a0: Switch to new style CMT device
ARM: shmobile: r8a73a4: Switch to new style CMT device
ARM: shmobile: r8a7740: Switch to new style CMT device
ARM: shmobile: r8a7790: Switch to new style CMT device
ARM: shmobile: r8a7791: Switch to new style CMT device
clocksource: sh_cmt: Drop support for legacy platform data
clocksource: sh_cmt: Add DT support
ARM: shmobile: r8a7790: Add CMT devices to DT
ARM: shmobile: r8a7791: Add CMT devices to DT
ARM: shmobile: lager-reference: Enable CMT0 in device tree
ARM: shmobile: koelsch-reference: Enable CMT0 in device tree
.../devicetree/bindings/timer/renesas,cmt.txt | 75 ++
arch/arm/boot/dts/r8a7790-lager.dts | 9 +
arch/arm/boot/dts/r8a7790.dtsi | 38 +
arch/arm/boot/dts/r8a7791-koelsch.dts | 9 +
arch/arm/boot/dts/r8a7791.dtsi | 38 +
arch/arm/mach-shmobile/board-koelsch-reference.c | 15 +-
arch/arm/mach-shmobile/board-lager-reference.c | 15 +-
arch/arm/mach-shmobile/clock-r8a73a4.c | 2 +-
arch/arm/mach-shmobile/clock-r8a7740.c | 2 +-
arch/arm/mach-shmobile/clock-r8a7790.c | 2 +-
arch/arm/mach-shmobile/clock-r8a7791.c | 2 +-
arch/arm/mach-shmobile/clock-sh7372.c | 6 +-
arch/arm/mach-shmobile/clock-sh73a0.c | 2 +-
arch/arm/mach-shmobile/include/mach/r8a7790.h | 1 -
arch/arm/mach-shmobile/include/mach/r8a7791.h | 1 -
arch/arm/mach-shmobile/setup-r8a73a4.c | 25 +-
arch/arm/mach-shmobile/setup-r8a7740.c | 45 +-
arch/arm/mach-shmobile/setup-r8a7790.c | 31 +-
arch/arm/mach-shmobile/setup-r8a7791.c | 31 +-
arch/arm/mach-shmobile/setup-sh7372.c | 30 +-
arch/arm/mach-shmobile/setup-sh73a0.c | 45 +-
arch/sh/kernel/cpu/clock-cpg.c | 3 +-
arch/sh/kernel/cpu/sh2/setup-sh7619.c | 73 +-
arch/sh/kernel/cpu/sh2a/clock-sh7264.c | 2 +-
arch/sh/kernel/cpu/sh2a/clock-sh7269.c | 2 +-
arch/sh/kernel/cpu/sh2a/setup-sh7203.c | 73 +-
arch/sh/kernel/cpu/sh2a/setup-sh7206.c | 73 +-
arch/sh/kernel/cpu/sh2a/setup-sh7264.c | 76 +-
arch/sh/kernel/cpu/sh2a/setup-sh7269.c | 73 +-
arch/sh/kernel/cpu/sh3/setup-sh7720.c | 155 +---
arch/sh/kernel/cpu/sh4a/clock-sh7343.c | 2 +-
arch/sh/kernel/cpu/sh4a/clock-sh7366.c | 2 +-
arch/sh/kernel/cpu/sh4a/clock-sh7722.c | 2 +-
arch/sh/kernel/cpu/sh4a/clock-sh7723.c | 2 +-
arch/sh/kernel/cpu/sh4a/clock-sh7724.c | 2 +-
arch/sh/kernel/cpu/sh4a/setup-sh7343.c | 28 +-
arch/sh/kernel/cpu/sh4a/setup-sh7366.c | 27 +-
arch/sh/kernel/cpu/sh4a/setup-sh7722.c | 28 +-
arch/sh/kernel/cpu/sh4a/setup-sh7723.c | 28 +-
arch/sh/kernel/cpu/sh4a/setup-sh7724.c | 28 +-
drivers/clocksource/sh_cmt.c | 903 +++++++++++++--------
include/linux/sh_timer.h | 9 +
42 files changed, 1078 insertions(+), 937 deletions(-)
create mode 100644 Documentation/devicetree/bindings/timer/renesas,cmt.txt
--
Regards,
Laurent Pinchart
The CMT (Compare Match Timer) driver implements a new style of platform
data that handles the timer as a single device with multiple channel.
Switch from the old-style platform data to the new-style platform data.
Signed-off-by: Laurent Pinchart <[email protected]>
---
arch/arm/mach-shmobile/board-koelsch-reference.c | 2 +-
arch/arm/mach-shmobile/clock-r8a7791.c | 2 +-
arch/arm/mach-shmobile/setup-r8a7791.c | 26 +++++++++++++++---------
3 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/arch/arm/mach-shmobile/board-koelsch-reference.c b/arch/arm/mach-shmobile/board-koelsch-reference.c
index 46879d6..e67a4be7 100644
--- a/arch/arm/mach-shmobile/board-koelsch-reference.c
+++ b/arch/arm/mach-shmobile/board-koelsch-reference.c
@@ -39,7 +39,7 @@ static void __init koelsch_add_standard_devices(void)
clk = clk_get(NULL, "cmt0");
if (!IS_ERR(clk)) {
- clk_register_clkdev(clk, NULL, "sh_cmt.0");
+ clk_register_clkdev(clk, NULL, "sh-cmt-48-gen2.0");
clk_put(clk);
}
#else
diff --git a/arch/arm/mach-shmobile/clock-r8a7791.c b/arch/arm/mach-shmobile/clock-r8a7791.c
index fc9248d..4dac973 100644
--- a/arch/arm/mach-shmobile/clock-r8a7791.c
+++ b/arch/arm/mach-shmobile/clock-r8a7791.c
@@ -224,7 +224,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_DEV_ID("sh-sci.12", &mstp_clks[MSTP1105]), /* SCIFA3 */
CLKDEV_DEV_ID("sh-sci.13", &mstp_clks[MSTP1106]), /* SCIFA4 */
CLKDEV_DEV_ID("sh-sci.14", &mstp_clks[MSTP1107]), /* SCIFA5 */
- CLKDEV_DEV_ID("sh_cmt.0", &mstp_clks[MSTP124]),
+ CLKDEV_DEV_ID("sh-cmt-48-gen2.0", &mstp_clks[MSTP124]),
CLKDEV_DEV_ID("qspi.0", &mstp_clks[MSTP917]),
CLKDEV_DEV_ID("e61f0000.thermal", &mstp_clks[MSTP522]),
CLKDEV_DEV_ID("rcar_thermal", &mstp_clks[MSTP522]),
diff --git a/arch/arm/mach-shmobile/setup-r8a7791.c b/arch/arm/mach-shmobile/setup-r8a7791.c
index 605f4cf..a17ebb8 100644
--- a/arch/arm/mach-shmobile/setup-r8a7791.c
+++ b/arch/arm/mach-shmobile/setup-r8a7791.c
@@ -128,20 +128,26 @@ R8A7791_SCIFA(14, 0xe6c80000, gic_spi(31)); /* SCIFA5 */
&scif##index##_platform_data, \
sizeof(scif##index##_platform_data))
-static const struct sh_timer_config cmt00_platform_data __initconst = {
- .name = "CMT00",
- .timer_bit = 0,
- .clockevent_rating = 80,
+static struct sh_timer_channel_config cmt0_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 80,
+ },
};
-static const struct resource cmt00_resources[] __initconst = {
- DEFINE_RES_MEM(0xffca0510, 0x0c),
- DEFINE_RES_MEM(0xffca0500, 0x04),
- DEFINE_RES_IRQ(gic_spi(142)), /* CMT0_0 */
+static struct sh_timer_config cmt0_platform_data = {
+ .channels = cmt0_channels,
+ .num_channels = ARRAY_SIZE(cmt0_channels),
+ .channels_mask = 0x1f,
+};
+
+static struct resource cmt0_resources[] = {
+ DEFINE_RES_MEM(0xffca0000, 0x1004),
+ DEFINE_RES_IRQ(gic_spi(142)),
};
#define r8a7791_register_cmt(idx) \
- platform_device_register_resndata(&platform_bus, "sh_cmt", \
+ platform_device_register_resndata(&platform_bus, "sh-cmt-48-gen2", \
idx, cmt##idx##_resources, \
ARRAY_SIZE(cmt##idx##_resources), \
&cmt##idx##_platform_data, \
@@ -185,7 +191,7 @@ static const struct resource thermal_resources[] __initconst = {
void __init r8a7791_add_dt_devices(void)
{
- r8a7791_register_cmt(00);
+ r8a7791_register_cmt(0);
}
void __init r8a7791_add_standard_devices(void)
--
1.8.3.2
Channel data is private as well, rename priv to device to make the
distrinction between the core device and the channels clearer.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 116 +++++++++++++++++++++----------------------
1 file changed, 58 insertions(+), 58 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 12ccd11..68cca0a 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -35,10 +35,10 @@
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
-struct sh_cmt_priv;
+struct sh_cmt_device;
struct sh_cmt_channel {
- struct sh_cmt_priv *cmt;
+ struct sh_cmt_device *cmt;
struct irqaction irqaction;
@@ -54,7 +54,7 @@ struct sh_cmt_channel {
bool cs_enabled;
};
-struct sh_cmt_priv {
+struct sh_cmt_device {
struct platform_device *pdev;
void __iomem *mapbase;
@@ -695,136 +695,136 @@ static int sh_cmt_register(struct sh_cmt_channel *ch, char *name,
return 0;
}
-static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
+static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
{
struct sh_timer_config *cfg = pdev->dev.platform_data;
- struct sh_cmt_channel *ch = &p->channel;
+ struct sh_cmt_channel *ch = &cmt->channel;
struct resource *res, *res2;
int irq, ret;
ret = -ENXIO;
- memset(p, 0, sizeof(*p));
- p->pdev = pdev;
+ memset(cmt, 0, sizeof(*cmt));
+ cmt->pdev = pdev;
if (!cfg) {
- dev_err(&p->pdev->dev, "missing platform data\n");
+ dev_err(&cmt->pdev->dev, "missing platform data\n");
goto err0;
}
- res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
+ res = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 0);
if (!res) {
- dev_err(&p->pdev->dev, "failed to get I/O memory\n");
+ dev_err(&cmt->pdev->dev, "failed to get I/O memory\n");
goto err0;
}
/* optional resource for the shared timer start/stop register */
- res2 = platform_get_resource(p->pdev, IORESOURCE_MEM, 1);
+ res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1);
- irq = platform_get_irq(p->pdev, 0);
+ irq = platform_get_irq(cmt->pdev, 0);
if (irq < 0) {
- dev_err(&p->pdev->dev, "failed to get irq\n");
+ dev_err(&cmt->pdev->dev, "failed to get irq\n");
goto err0;
}
/* map memory, let mapbase point to our channel */
- p->mapbase = ioremap_nocache(res->start, resource_size(res));
- if (p->mapbase == NULL) {
- dev_err(&p->pdev->dev, "failed to remap I/O memory\n");
+ cmt->mapbase = ioremap_nocache(res->start, resource_size(res));
+ if (cmt->mapbase == NULL) {
+ dev_err(&cmt->pdev->dev, "failed to remap I/O memory\n");
goto err0;
}
/* map second resource for CMSTR */
- p->mapbase_str = ioremap_nocache(res2 ? res2->start :
- res->start - cfg->channel_offset,
- res2 ? resource_size(res2) : 2);
- if (p->mapbase_str == NULL) {
- dev_err(&p->pdev->dev, "failed to remap I/O second memory\n");
+ cmt->mapbase_str = ioremap_nocache(res2 ? res2->start :
+ res->start - cfg->channel_offset,
+ res2 ? resource_size(res2) : 2);
+ if (cmt->mapbase_str == NULL) {
+ dev_err(&cmt->pdev->dev, "failed to remap I/O second memory\n");
goto err1;
}
/* request irq using setup_irq() (too early for request_irq()) */
- ch->irqaction.name = dev_name(&p->pdev->dev);
+ ch->irqaction.name = dev_name(&cmt->pdev->dev);
ch->irqaction.handler = sh_cmt_interrupt;
ch->irqaction.dev_id = ch;
ch->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING;
/* get hold of clock */
- p->clk = clk_get(&p->pdev->dev, "cmt_fck");
- if (IS_ERR(p->clk)) {
- dev_err(&p->pdev->dev, "cannot get clock\n");
- ret = PTR_ERR(p->clk);
+ cmt->clk = clk_get(&cmt->pdev->dev, "cmt_fck");
+ if (IS_ERR(cmt->clk)) {
+ dev_err(&cmt->pdev->dev, "cannot get clock\n");
+ ret = PTR_ERR(cmt->clk);
goto err2;
}
- ret = clk_prepare(p->clk);
+ ret = clk_prepare(cmt->clk);
if (ret < 0)
goto err3;
if (res2 && (resource_size(res2) == 4)) {
/* assume both CMSTR and CMCSR to be 32-bit */
- p->read_control = sh_cmt_read32;
- p->write_control = sh_cmt_write32;
+ cmt->read_control = sh_cmt_read32;
+ cmt->write_control = sh_cmt_write32;
} else {
- p->read_control = sh_cmt_read16;
- p->write_control = sh_cmt_write16;
+ cmt->read_control = sh_cmt_read16;
+ cmt->write_control = sh_cmt_write16;
}
if (resource_size(res) == 6) {
- p->width = 16;
- p->read_count = sh_cmt_read16;
- p->write_count = sh_cmt_write16;
- p->overflow_bit = 0x80;
- p->clear_bits = ~0x80;
+ cmt->width = 16;
+ cmt->read_count = sh_cmt_read16;
+ cmt->write_count = sh_cmt_write16;
+ cmt->overflow_bit = 0x80;
+ cmt->clear_bits = ~0x80;
} else {
- p->width = 32;
- p->read_count = sh_cmt_read32;
- p->write_count = sh_cmt_write32;
- p->overflow_bit = 0x8000;
- p->clear_bits = ~0xc000;
+ cmt->width = 32;
+ cmt->read_count = sh_cmt_read32;
+ cmt->write_count = sh_cmt_write32;
+ cmt->overflow_bit = 0x8000;
+ cmt->clear_bits = ~0xc000;
}
- if (p->width == (sizeof(ch->max_match_value) * 8))
+ if (cmt->width == (sizeof(ch->max_match_value) * 8))
ch->max_match_value = ~0;
else
- ch->max_match_value = (1 << p->width) - 1;
+ ch->max_match_value = (1 << cmt->width) - 1;
- ch->cmt = p;
+ ch->cmt = cmt;
ch->match_value = ch->max_match_value;
raw_spin_lock_init(&ch->lock);
- ret = sh_cmt_register(ch, (char *)dev_name(&p->pdev->dev),
+ ret = sh_cmt_register(ch, (char *)dev_name(&cmt->pdev->dev),
cfg->clockevent_rating,
cfg->clocksource_rating);
if (ret) {
- dev_err(&p->pdev->dev, "registration failed\n");
+ dev_err(&cmt->pdev->dev, "registration failed\n");
goto err4;
}
ch->cs_enabled = false;
ret = setup_irq(irq, &ch->irqaction);
if (ret) {
- dev_err(&p->pdev->dev, "failed to request irq %d\n", irq);
+ dev_err(&cmt->pdev->dev, "failed to request irq %d\n", irq);
goto err4;
}
- platform_set_drvdata(pdev, p);
+ platform_set_drvdata(pdev, cmt);
return 0;
err4:
- clk_unprepare(p->clk);
+ clk_unprepare(cmt->clk);
err3:
- clk_put(p->clk);
+ clk_put(cmt->clk);
err2:
- iounmap(p->mapbase_str);
+ iounmap(cmt->mapbase_str);
err1:
- iounmap(p->mapbase);
+ iounmap(cmt->mapbase);
err0:
return ret;
}
static int sh_cmt_probe(struct platform_device *pdev)
{
- struct sh_cmt_priv *p = platform_get_drvdata(pdev);
+ struct sh_cmt_device *cmt = platform_get_drvdata(pdev);
struct sh_timer_config *cfg = pdev->dev.platform_data;
int ret;
@@ -833,20 +833,20 @@ static int sh_cmt_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
}
- if (p) {
+ if (cmt) {
dev_info(&pdev->dev, "kept as earlytimer\n");
goto out;
}
- p = kmalloc(sizeof(*p), GFP_KERNEL);
- if (p == NULL) {
+ cmt = kmalloc(sizeof(*cmt), GFP_KERNEL);
+ if (cmt == NULL) {
dev_err(&pdev->dev, "failed to allocate driver data\n");
return -ENOMEM;
}
- ret = sh_cmt_setup(p, pdev);
+ ret = sh_cmt_setup(cmt, pdev);
if (ret) {
- kfree(p);
+ kfree(cmt);
pm_runtime_idle(&pdev->dev);
return ret;
}
--
1.8.3.2
Enable the CMT0 device and configure channel 0 as a clock event
provider.
Signed-off-by: Laurent Pinchart <[email protected]>
---
arch/arm/boot/dts/r8a7791-koelsch.dts | 9 +++++++++
arch/arm/mach-shmobile/board-koelsch-reference.c | 15 +--------------
arch/arm/mach-shmobile/include/mach/r8a7791.h | 1 -
arch/arm/mach-shmobile/setup-r8a7791.c | 7 +------
4 files changed, 11 insertions(+), 21 deletions(-)
diff --git a/arch/arm/boot/dts/r8a7791-koelsch.dts b/arch/arm/boot/dts/r8a7791-koelsch.dts
index 42fd5eb..36e1c33 100644
--- a/arch/arm/boot/dts/r8a7791-koelsch.dts
+++ b/arch/arm/boot/dts/r8a7791-koelsch.dts
@@ -130,6 +130,15 @@
};
};
+&cmt0 {
+ status = "ok";
+
+ channel@0 {
+ reg = <0>;
+ clock-event-rating = <80>;
+ };
+};
+
&sata0 {
status = "okay";
};
diff --git a/arch/arm/mach-shmobile/board-koelsch-reference.c b/arch/arm/mach-shmobile/board-koelsch-reference.c
index e67a4be7..abcb8da 100644
--- a/arch/arm/mach-shmobile/board-koelsch-reference.c
+++ b/arch/arm/mach-shmobile/board-koelsch-reference.c
@@ -30,22 +30,9 @@
static void __init koelsch_add_standard_devices(void)
{
-#ifdef CONFIG_COMMON_CLK
- /*
- * This is a really crude hack to provide clkdev support to the CMT
- * device until they get moved to DT.
- */
- struct clk *clk;
-
- clk = clk_get(NULL, "cmt0");
- if (!IS_ERR(clk)) {
- clk_register_clkdev(clk, NULL, "sh-cmt-48-gen2.0");
- clk_put(clk);
- }
-#else
+#ifndef CONFIG_COMMON_CLK
r8a7791_clock_init();
#endif
- r8a7791_add_dt_devices();
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
diff --git a/arch/arm/mach-shmobile/include/mach/r8a7791.h b/arch/arm/mach-shmobile/include/mach/r8a7791.h
index 200fa69..57a37f1 100644
--- a/arch/arm/mach-shmobile/include/mach/r8a7791.h
+++ b/arch/arm/mach-shmobile/include/mach/r8a7791.h
@@ -2,7 +2,6 @@
#define __ASM_R8A7791_H__
void r8a7791_add_standard_devices(void);
-void r8a7791_add_dt_devices(void);
void r8a7791_clock_init(void);
void r8a7791_pinmux_init(void);
void r8a7791_init_early(void);
diff --git a/arch/arm/mach-shmobile/setup-r8a7791.c b/arch/arm/mach-shmobile/setup-r8a7791.c
index a17ebb8..c1e9c0f 100644
--- a/arch/arm/mach-shmobile/setup-r8a7791.c
+++ b/arch/arm/mach-shmobile/setup-r8a7791.c
@@ -189,11 +189,6 @@ static const struct resource thermal_resources[] __initconst = {
thermal_resources, \
ARRAY_SIZE(thermal_resources))
-void __init r8a7791_add_dt_devices(void)
-{
- r8a7791_register_cmt(0);
-}
-
void __init r8a7791_add_standard_devices(void)
{
r8a7791_register_scif(0);
@@ -211,7 +206,7 @@ void __init r8a7791_add_standard_devices(void)
r8a7791_register_scif(12);
r8a7791_register_scif(13);
r8a7791_register_scif(14);
- r8a7791_add_dt_devices();
+ r8a7791_register_cmt(0);
r8a7791_register_irqc(0);
r8a7791_register_thermal();
}
--
1.8.3.2
Cc: [email protected]
Signed-off-by: Laurent Pinchart <[email protected]>
---
.../devicetree/bindings/timer/renesas,cmt.txt | 75 +++++++++++++++
drivers/clocksource/sh_cmt.c | 104 +++++++++++++++++----
2 files changed, 160 insertions(+), 19 deletions(-)
create mode 100644 Documentation/devicetree/bindings/timer/renesas,cmt.txt
diff --git a/Documentation/devicetree/bindings/timer/renesas,cmt.txt b/Documentation/devicetree/bindings/timer/renesas,cmt.txt
new file mode 100644
index 0000000..28d4ab5
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/renesas,cmt.txt
@@ -0,0 +1,75 @@
+* Renesas R-Car Compare Match Timer (CMT)
+
+The CMT is a multi-channel 16/32/48-bit timer/counter with configurable clock
+inputs and programmable compare match.
+
+Channels share hadware resources but their counter and compare match value are
+independent. A particular CMT instance can implement only a subset of the
+channels supported by the CMT model. Channels indices start from 0 and are
+consecutive.
+
+Required Properties:
+
+ - compatible: must contain one of the following.
+ - "renesas,cmt-32" for the 32-bit CMT
+ (CMT0 on sh7372, sh73a0 and r8a7740)
+ - "renesas,cmt-32-fast" for the 32-bit CMT with fast clock support
+ (CMT[234] on sh7372, sh73a0 and r8a7740)
+ - "renasas,cmt-48" for the 48-bit CMT
+ (CMT1 on sh7372, sh73a0 and r8a7740)
+ - "renesas,cmt-48-gen2" for the second generation 48-bit CMT
+ (CMT[01] on r8a73a4, r8a7790 and r8a7791)
+
+ - reg: base address and length of the registers block for the timer module.
+ - interrupt-parent, interrupts: interrupt-specifier for the timer, one per
+ channel.
+ - clocks: phandle and clock-specifier pair for the functional clock.
+ - clock-names: must be "fck".
+
+ - #address-cells: must be 1
+ - #size-cells: must be 0
+
+ - renesas,channels-mask: integer bitmask of the channels implemented by the
+ timer instance.
+
+
+Each channel is described by a sub-node named "channel@<idx>", where <idx> is
+the channel index.
+
+Channels Required Properties:
+
+ - reg: the channel index.
+
+Channels Optional Properties:
+
+ - clock-source-rating: rating of the timer as a clock source device.
+ - clock-event-rating: rating of the timer as a clock event device.
+
+
+Example: R8A7790 (R-Car H2) CMT0 node
+
+ CMT0 on R8A7790 implements hardware channels 5 and 6 only and names
+ them channels 0 and 1 in the documentation.
+
+ cmt0: timer@ffca0000 {
+ compatible = "renesas,cmt-48-gen2";
+ reg = <0 0xffca0000 0 0x1004>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 142 IRQ_TYPE_LEVEL_HIGH>,
+ <0 142 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp1_clks R8A7790_CLK_CMT0>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ renesas,channels-mask = <0x60>;
+
+ channel@0 {
+ reg = <0>;
+ clock-event-rating = <80>;
+ };
+ channel@0 {
+ reg = <0>;
+ clock-source-rating = <80>;
+ };
+ };
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index fe9694b..8e46c40 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -24,6 +24,7 @@
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
@@ -918,10 +919,72 @@ static int sh_cmt_map_memory(struct sh_cmt_device *cmt)
return 0;
}
+static const struct platform_device_id sh_cmt_id_table[] = {
+ { "sh-cmt-16", (kernel_ulong_t)&sh_cmt_info[SH_CMT_16BIT] },
+ { "sh-cmt-32", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT] },
+ { "sh-cmt-32-fast", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT_FAST] },
+ { "sh-cmt-48", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT] },
+ { "sh-cmt-48-gen2", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT_GEN2] },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, sh_cmt_id_table);
+
+static const struct of_device_id sh_cmt_of_table[] = {
+ { .compatible = "renesas,cmt-32", .data = &sh_cmt_info[SH_CMT_32BIT] },
+ { .compatible = "renesas,cmt-32-fast", .data = &sh_cmt_info[SH_CMT_32BIT_FAST] },
+ { .compatible = "renasas,cmt-48", .data = &sh_cmt_info[SH_CMT_48BIT] },
+ { .compatible = "renesas,cmt-48-gen2", .data = &sh_cmt_info[SH_CMT_48BIT_GEN2] },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sh_cmt_of_table);
+
+static struct sh_timer_channel_config *
+sh_cmt_parse_dt(struct sh_cmt_device *cmt)
+{
+ struct sh_timer_channel_config *channels;
+ struct sh_timer_channel_config *channel;
+ struct device_node *np = cmt->pdev->dev.of_node;
+ struct device_node *child;
+ int ret;
+
+ cmt->num_channels = of_get_child_count(np);
+ if (cmt->num_channels == 0)
+ return ERR_PTR(-EINVAL);
+
+ ret = of_property_read_u32(np, "renesas,channels-mask",
+ &cmt->hw_channels);
+ if (ret < 0)
+ return ERR_PTR(-EINVAL);
+
+ channels = devm_kzalloc(&cmt->pdev->dev, sizeof(*channels), GFP_KERNEL);
+ if (channels == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ channel = channels;
+
+ for_each_child_of_node(np, child) {
+ u32 val;
+
+ ret = of_property_read_u32(child, "reg", &channel->index);
+ if (ret < 0)
+ return ERR_PTR(-EINVAL);
+
+ ret = of_property_read_u32(child, "clock-source-rating", &val);
+ if (ret == 0)
+ channel->clocksource_rating = val;
+ ret = of_property_read_u32(child, "clock-event-rating", &val);
+ if (ret == 0)
+ channel->clockevent_rating = val;
+
+ channel++;
+ }
+
+ return channels;
+}
+
static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
{
- struct sh_timer_config *cfg = pdev->dev.platform_data;
- const struct platform_device_id *id = pdev->id_entry;
+ const struct sh_timer_channel_config *ch_cfg;
unsigned int mask;
unsigned int i;
int ret;
@@ -929,13 +992,28 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
memset(cmt, 0, sizeof(*cmt));
cmt->pdev = pdev;
- if (!cfg) {
+ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+ const struct of_device_id *id;
+
+ id = of_match_node(sh_cmt_of_table, pdev->dev.of_node);
+ cmt->info = id->data;
+
+ ch_cfg = sh_cmt_parse_dt(cmt);
+ if (IS_ERR(ch_cfg))
+ return PTR_ERR(ch_cfg);
+ } else if (pdev->dev.platform_data) {
+ struct sh_timer_config *cfg = pdev->dev.platform_data;
+ const struct platform_device_id *id = pdev->id_entry;
+
+ cmt->info = (const struct sh_cmt_info *)id->driver_data;
+ cmt->num_channels = cfg->num_channels;
+ cmt->hw_channels = ~cfg->channels_mask;
+ ch_cfg = cfg->channels;
+ } else {
dev_err(&cmt->pdev->dev, "missing platform data\n");
return -ENXIO;
}
- cmt->info = (const struct sh_cmt_info *)id->driver_data;
-
/* Get hold of clock. */
cmt->clk = clk_get(&cmt->pdev->dev, NULL);
if (IS_ERR(cmt->clk)) {
@@ -953,9 +1031,6 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
goto err_clk_unprepare;
/* Allocate and setup the channels. */
- cmt->num_channels = cfg->num_channels;
- cmt->hw_channels = ~cfg->channels_mask;
-
cmt->channels = kzalloc(cmt->num_channels * sizeof(*cmt->channels),
GFP_KERNEL);
if (cmt->channels == NULL) {
@@ -966,7 +1041,7 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
for (i = 0, mask = cmt->hw_channels; i < cmt->num_channels; ++i) {
unsigned int hwidx = ffs(mask) - 1;
- ret = sh_cmt_setup_channel(&cmt->channels[i], &cfg->channels[i],
+ ret = sh_cmt_setup_channel(&cmt->channels[i], &ch_cfg[i],
hwidx, cmt);
if (ret < 0)
goto err_unmap;
@@ -1032,21 +1107,12 @@ static int sh_cmt_remove(struct platform_device *pdev)
return -EBUSY; /* cannot unregister clockevent and clocksource */
}
-static const struct platform_device_id sh_cmt_id_table[] = {
- { "sh-cmt-16", (kernel_ulong_t)&sh_cmt_info[SH_CMT_16BIT] },
- { "sh-cmt-32", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT] },
- { "sh-cmt-32-fast", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT_FAST] },
- { "sh-cmt-48", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT] },
- { "sh-cmt-48-gen2", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT_GEN2] },
- { }
-};
-MODULE_DEVICE_TABLE(platform, sh_cmt_id_table);
-
static struct platform_driver sh_cmt_device_driver = {
.probe = sh_cmt_probe,
.remove = sh_cmt_remove,
.driver = {
.name = "sh_cmt",
+ .of_match_table = of_match_ptr(sh_cmt_of_table),
},
.id_table = sh_cmt_id_table,
};
--
1.8.3.2
Enable the CMT0 device and configure channel 0 as a clock event
provider.
Signed-off-by: Laurent Pinchart <[email protected]>
---
arch/arm/boot/dts/r8a7790-lager.dts | 9 +++++++++
arch/arm/mach-shmobile/board-lager-reference.c | 15 +--------------
arch/arm/mach-shmobile/include/mach/r8a7790.h | 1 -
arch/arm/mach-shmobile/setup-r8a7790.c | 7 +------
4 files changed, 11 insertions(+), 21 deletions(-)
diff --git a/arch/arm/boot/dts/r8a7790-lager.dts b/arch/arm/boot/dts/r8a7790-lager.dts
index 4199ae4..54ab17d 100644
--- a/arch/arm/boot/dts/r8a7790-lager.dts
+++ b/arch/arm/boot/dts/r8a7790-lager.dts
@@ -84,6 +84,15 @@
};
};
+&cmt0 {
+ status = "ok";
+
+ channel@0 {
+ reg = <0>;
+ clock-event-rating = <80>;
+ };
+};
+
&mmcif1 {
pinctrl-0 = <&mmc1_pins>;
pinctrl-names = "default";
diff --git a/arch/arm/mach-shmobile/board-lager-reference.c b/arch/arm/mach-shmobile/board-lager-reference.c
index 4dd43b1..ee95a8c 100644
--- a/arch/arm/mach-shmobile/board-lager-reference.c
+++ b/arch/arm/mach-shmobile/board-lager-reference.c
@@ -29,23 +29,10 @@
static void __init lager_add_standard_devices(void)
{
-#ifdef CONFIG_COMMON_CLK
- /*
- * This is a really crude hack to provide clkdev support to the CMT
- * device until it gets moved to DT.
- */
- struct clk *clk;
-
- clk = clk_get(NULL, "cmt0");
- if (!IS_ERR(clk)) {
- clk_register_clkdev(clk, NULL, "sh-cmt-48-gen2.0");
- clk_put(clk);
- }
-#else
+#ifndef CONFIG_COMMON_CLK
r8a7790_clock_init();
#endif
- r8a7790_add_dt_devices();
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
diff --git a/arch/arm/mach-shmobile/include/mach/r8a7790.h b/arch/arm/mach-shmobile/include/mach/r8a7790.h
index 0b95bab..62b31f3 100644
--- a/arch/arm/mach-shmobile/include/mach/r8a7790.h
+++ b/arch/arm/mach-shmobile/include/mach/r8a7790.h
@@ -29,7 +29,6 @@ enum {
};
void r8a7790_add_standard_devices(void);
-void r8a7790_add_dt_devices(void);
void r8a7790_clock_init(void);
void r8a7790_pinmux_init(void);
void r8a7790_pm_init(void);
diff --git a/arch/arm/mach-shmobile/setup-r8a7790.c b/arch/arm/mach-shmobile/setup-r8a7790.c
index 3e5813f..462c81f 100644
--- a/arch/arm/mach-shmobile/setup-r8a7790.c
+++ b/arch/arm/mach-shmobile/setup-r8a7790.c
@@ -294,11 +294,6 @@ static struct resource cmt0_resources[] = {
&cmt##idx##_platform_data, \
sizeof(struct sh_timer_config))
-void __init r8a7790_add_dt_devices(void)
-{
- r8a7790_register_cmt(0);
-}
-
void __init r8a7790_add_standard_devices(void)
{
r8a7790_register_scif(0);
@@ -311,7 +306,7 @@ void __init r8a7790_add_standard_devices(void)
r8a7790_register_scif(7);
r8a7790_register_scif(8);
r8a7790_register_scif(9);
- r8a7790_add_dt_devices();
+ r8a7790_register_cmt(0);
r8a7790_register_irqc(0);
r8a7790_register_thermal();
}
--
1.8.3.2
Signed-off-by: Laurent Pinchart <[email protected]>
---
arch/arm/boot/dts/r8a7790.dtsi | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi
index 54ab318..a236c7e 100644
--- a/arch/arm/boot/dts/r8a7790.dtsi
+++ b/arch/arm/boot/dts/r8a7790.dtsi
@@ -179,6 +179,44 @@
<1 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
};
+ cmt0: timer@ffca0000 {
+ compatible = "renesas,cmt-48-gen2";
+ reg = <0 0xffca0000 0 0x1004>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 142 IRQ_TYPE_LEVEL_HIGH>,
+ <0 142 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp1_clks R8A7790_CLK_CMT0>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ renesas,channels-mask = <0x60>;
+
+ status = "disabled";
+ };
+
+ cmt1: timer@e6130000 {
+ compatible = "renesas,cmt-48-gen2";
+ reg = <0 0xe6130000 0 0x1004>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 120 IRQ_TYPE_LEVEL_HIGH>,
+ <0 121 IRQ_TYPE_LEVEL_HIGH>,
+ <0 122 IRQ_TYPE_LEVEL_HIGH>,
+ <0 123 IRQ_TYPE_LEVEL_HIGH>,
+ <0 124 IRQ_TYPE_LEVEL_HIGH>,
+ <0 125 IRQ_TYPE_LEVEL_HIGH>,
+ <0 126 IRQ_TYPE_LEVEL_HIGH>,
+ <0 127 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp3_clks R8A7790_CLK_CMT1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ renesas,channels-mask = <0xff>;
+
+ status = "disabled";
+ };
+
irqc0: interrupt-controller@e61c0000 {
compatible = "renesas,irqc-r8a7790", "renesas,irqc";
#interrupt-cells = <2>;
--
1.8.3.2
Signed-off-by: Laurent Pinchart <[email protected]>
---
arch/arm/boot/dts/r8a7791.dtsi | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi
index 240c4ec..bcfb998 100644
--- a/arch/arm/boot/dts/r8a7791.dtsi
+++ b/arch/arm/boot/dts/r8a7791.dtsi
@@ -162,6 +162,44 @@
<1 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
};
+ cmt0: timer@ffca0000 {
+ compatible = "renesas,cmt-48-gen2";
+ reg = <0 0xffca0000 0 0x1004>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 142 IRQ_TYPE_LEVEL_HIGH>,
+ <0 142 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp1_clks R8A7791_CLK_CMT0>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ renesas,channels-mask = <0x60>;
+
+ status = "disabled";
+ };
+
+ cmt1: timer@e6130000 {
+ compatible = "renesas,cmt-48-gen2";
+ reg = <0 0xe6130000 0 0x1004>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 120 IRQ_TYPE_LEVEL_HIGH>,
+ <0 121 IRQ_TYPE_LEVEL_HIGH>,
+ <0 122 IRQ_TYPE_LEVEL_HIGH>,
+ <0 123 IRQ_TYPE_LEVEL_HIGH>,
+ <0 124 IRQ_TYPE_LEVEL_HIGH>,
+ <0 125 IRQ_TYPE_LEVEL_HIGH>,
+ <0 126 IRQ_TYPE_LEVEL_HIGH>,
+ <0 127 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp3_clks R8A7791_CLK_CMT1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ renesas,channels-mask = <0xff>;
+
+ status = "disabled";
+ };
+
irqc0: interrupt-controller@e61c0000 {
compatible = "renesas,irqc-r8a7791", "renesas,irqc";
#interrupt-cells = <2>;
--
1.8.3.2
Now that all platforms have switched to the new-style platform data,
drop support for the legacy version.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 174 ++++++++++---------------------------------
1 file changed, 39 insertions(+), 135 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 077879b..fe9694b 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -115,9 +115,7 @@ struct sh_cmt_device {
struct platform_device *pdev;
const struct sh_cmt_info *info;
- bool legacy;
- void __iomem *mapbase_ch;
void __iomem *mapbase;
struct clk *clk;
@@ -824,58 +822,45 @@ static int sh_cmt_register(struct sh_cmt_channel *ch, char *name,
return 0;
}
-static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index,
+static int sh_cmt_setup_channel(struct sh_cmt_channel *ch,
const struct sh_timer_channel_config *ch_cfg,
unsigned int hwidx, struct sh_cmt_device *cmt)
{
- unsigned long clockevent_rating;
- unsigned long clocksource_rating;
int irq;
int ret;
ch->cmt = cmt;
- ch->index = ch_cfg ? ch_cfg->index : index;
+ ch->index = ch_cfg->index;
ch->hwidx = hwidx;
/*
* Compute the address of the channel control register block. For the
* timers with a per-channel start/stop register, compute its address
* as well.
- *
- * For legacy configuration the address has been mapped explicitly.
*/
- if (cmt->legacy) {
- ch->ioctrl = cmt->mapbase_ch;
- } else {
- switch (cmt->info->model) {
- case SH_CMT_16BIT:
- ch->ioctrl = cmt->mapbase + 2 + ch->hwidx * 6;
- break;
- case SH_CMT_32BIT:
- case SH_CMT_48BIT:
- ch->ioctrl = cmt->mapbase + 0x10 + ch->hwidx * 0x10;
- break;
- case SH_CMT_32BIT_FAST:
- /*
- * The 32-bit "fast" timer has a single channel at hwidx
- * 5 but is located at offset 0x40 instead of 0x60 for
- * some reason.
- */
- ch->ioctrl = cmt->mapbase + 0x40;
- break;
- case SH_CMT_48BIT_GEN2:
- ch->iostart = cmt->mapbase + ch->hwidx * 0x100;
- ch->ioctrl = ch->iostart + 0x10;
- break;
- }
+ switch (cmt->info->model) {
+ case SH_CMT_16BIT:
+ ch->ioctrl = cmt->mapbase + 2 + ch->hwidx * 6;
+ break;
+ case SH_CMT_32BIT:
+ case SH_CMT_48BIT:
+ ch->ioctrl = cmt->mapbase + 0x10 + ch->hwidx * 0x10;
+ break;
+ case SH_CMT_32BIT_FAST:
+ /*
+ * The 32-bit "fast" timer has a single channel at hwidx 5 but
+ * is located at offset 0x40 instead of 0x60 for some reason.
+ */
+ ch->ioctrl = cmt->mapbase + 0x40;
+ break;
+ case SH_CMT_48BIT_GEN2:
+ ch->iostart = cmt->mapbase + ch->hwidx * 0x100;
+ ch->ioctrl = ch->iostart + 0x10;
+ break;
}
/* Request irq using setup_irq() (too early for request_irq()). */
- if (cmt->legacy)
- irq = platform_get_irq(cmt->pdev, 0);
- else
- irq = platform_get_irq(cmt->pdev, ch->index);
-
+ irq = platform_get_irq(cmt->pdev, ch->index);
if (irq < 0) {
dev_err(&cmt->pdev->dev, "failed to get irq\n");
return irq;
@@ -894,20 +879,11 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index,
ch->match_value = ch->max_match_value;
raw_spin_lock_init(&ch->lock);
- if (ch_cfg) {
- ch->timer_bit = cmt->info->model == SH_CMT_48BIT_GEN2
- ? 0 : ch->hwidx;
- clockevent_rating = ch_cfg->clockevent_rating;
- clocksource_rating = ch_cfg->clocksource_rating;
- } else {
- struct sh_timer_config *cfg = cmt->pdev->dev.platform_data;
- ch->timer_bit = cfg->timer_bit;
- clockevent_rating = cfg->clockevent_rating;
- clocksource_rating = cfg->clocksource_rating;
- }
+ ch->timer_bit = cmt->info->model == SH_CMT_48BIT_GEN2 ? 0 : ch->hwidx;
ret = sh_cmt_register(ch, (char *)dev_name(&cmt->pdev->dev),
- clockevent_rating, clocksource_rating);
+ ch_cfg->clockevent_rating,
+ ch_cfg->clocksource_rating);
if (ret) {
dev_err(&cmt->pdev->dev, "registration failed\n");
return ret;
@@ -942,59 +918,12 @@ static int sh_cmt_map_memory(struct sh_cmt_device *cmt)
return 0;
}
-static int sh_cmt_map_memory_legacy(struct sh_cmt_device *cmt)
-{
- struct sh_timer_config *cfg = cmt->pdev->dev.platform_data;
- struct resource *res, *res2;
-
- /* map memory, let mapbase_ch point to our channel */
- res = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&cmt->pdev->dev, "failed to get I/O memory\n");
- return -ENXIO;
- }
-
- cmt->mapbase_ch = ioremap_nocache(res->start, resource_size(res));
- if (cmt->mapbase_ch == NULL) {
- dev_err(&cmt->pdev->dev, "failed to remap I/O memory\n");
- return -ENXIO;
- }
-
- /* optional resource for the shared timer start/stop register */
- res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1);
-
- /* map second resource for CMSTR */
- cmt->mapbase = ioremap_nocache(res2 ? res2->start :
- res->start - cfg->channel_offset,
- res2 ? resource_size(res2) : 2);
- if (cmt->mapbase == NULL) {
- dev_err(&cmt->pdev->dev, "failed to remap I/O second memory\n");
- iounmap(cmt->mapbase_ch);
- return -ENXIO;
- }
-
- /* 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];
-
- return 0;
-}
-
-static void sh_cmt_unmap_memory(struct sh_cmt_device *cmt)
-{
- iounmap(cmt->mapbase);
- if (cmt->mapbase_ch)
- iounmap(cmt->mapbase_ch);
-}
-
static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
{
struct sh_timer_config *cfg = pdev->dev.platform_data;
const struct platform_device_id *id = pdev->id_entry;
+ unsigned int mask;
+ unsigned int i;
int ret;
memset(cmt, 0, sizeof(*cmt));
@@ -1006,10 +935,9 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
}
cmt->info = (const struct sh_cmt_info *)id->driver_data;
- cmt->legacy = cmt->info ? false : true;
/* Get hold of clock. */
- cmt->clk = clk_get(&cmt->pdev->dev, cmt->legacy ? "cmt_fck" : NULL);
+ cmt->clk = clk_get(&cmt->pdev->dev, NULL);
if (IS_ERR(cmt->clk)) {
dev_err(&cmt->pdev->dev, "cannot get clock\n");
return PTR_ERR(cmt->clk);
@@ -1019,26 +947,14 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
if (ret < 0)
goto err_clk_put;
- /*
- * Map the memory resource(s). We need to support both the legacy
- * platform device configuration (with one device per channel) and the
- * new version (with multiple channels per device).
- */
- if (cmt->legacy)
- ret = sh_cmt_map_memory_legacy(cmt);
- else
- ret = sh_cmt_map_memory(cmt);
-
+ /* Map the memory resource(s). */
+ ret = sh_cmt_map_memory(cmt);
if (ret < 0)
goto err_clk_unprepare;
/* Allocate and setup the channels. */
- if (cmt->legacy) {
- cmt->num_channels = 1;
- } else {
- cmt->num_channels = cfg->num_channels;
- cmt->hw_channels = ~cfg->channels_mask;
- }
+ cmt->num_channels = cfg->num_channels;
+ cmt->hw_channels = ~cfg->channels_mask;
cmt->channels = kzalloc(cmt->num_channels * sizeof(*cmt->channels),
GFP_KERNEL);
@@ -1047,26 +963,15 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
goto err_unmap;
}
- if (cmt->legacy) {
- ret = sh_cmt_setup_channel(&cmt->channels[0], cfg->timer_bit,
- NULL, cfg->timer_bit, cmt);
+ for (i = 0, mask = cmt->hw_channels; i < cmt->num_channels; ++i) {
+ unsigned int hwidx = ffs(mask) - 1;
+
+ ret = sh_cmt_setup_channel(&cmt->channels[i], &cfg->channels[i],
+ hwidx, cmt);
if (ret < 0)
goto err_unmap;
- } else {
- const struct sh_timer_channel_config *ch_cfg = cfg->channels;
- unsigned int mask = cmt->hw_channels;
- unsigned int i;
-
- for (i = 0; i < cmt->num_channels; ++i) {
- unsigned int hwidx = ffs(mask) - 1;
- ret = sh_cmt_setup_channel(&cmt->channels[i], 0, ch_cfg,
- hwidx, cmt);
- if (ret < 0)
- goto err_unmap;
-
- mask &= ~(1 << hwidx);
- }
+ mask &= ~(1 << hwidx);
}
platform_set_drvdata(pdev, cmt);
@@ -1075,7 +980,7 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
err_unmap:
kfree(cmt->channels);
- sh_cmt_unmap_memory(cmt);
+ iounmap(cmt->mapbase);
err_clk_unprepare:
clk_unprepare(cmt->clk);
err_clk_put:
@@ -1128,7 +1033,6 @@ static int sh_cmt_remove(struct platform_device *pdev)
}
static const struct platform_device_id sh_cmt_id_table[] = {
- { "sh_cmt", 0 },
{ "sh-cmt-16", (kernel_ulong_t)&sh_cmt_info[SH_CMT_16BIT] },
{ "sh-cmt-32", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT] },
{ "sh-cmt-32-fast", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT_FAST] },
--
1.8.3.2
The CMT (Compare Match Timer) driver implements a new style of platform
data that handles the timer as a single device with multiple channel.
Switch from the old-style platform data to the new-style platform data.
Signed-off-by: Laurent Pinchart <[email protected]>
---
arch/arm/mach-shmobile/clock-r8a7740.c | 2 +-
arch/arm/mach-shmobile/setup-r8a7740.c | 45 ++++++++++++++++------------------
2 files changed, 22 insertions(+), 25 deletions(-)
diff --git a/arch/arm/mach-shmobile/clock-r8a7740.c b/arch/arm/mach-shmobile/clock-r8a7740.c
index dd989f9..50d3073 100644
--- a/arch/arm/mach-shmobile/clock-r8a7740.c
+++ b/arch/arm/mach-shmobile/clock-r8a7740.c
@@ -583,7 +583,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_DEV_ID("sh-sci.6", &mstp_clks[MSTP230]),
CLKDEV_DEV_ID("e6cc0000.sci", &mstp_clks[MSTP230]),
- CLKDEV_DEV_ID("sh_cmt.10", &mstp_clks[MSTP329]),
+ CLKDEV_DEV_ID("sh-cmt-48.1", &mstp_clks[MSTP329]),
CLKDEV_DEV_ID("sh_fsi2", &mstp_clks[MSTP328]),
CLKDEV_DEV_ID("fe1f0000.sound", &mstp_clks[MSTP328]),
CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[MSTP323]),
diff --git a/arch/arm/mach-shmobile/setup-r8a7740.c b/arch/arm/mach-shmobile/setup-r8a7740.c
index 8f3c681..f88856e 100644
--- a/arch/arm/mach-shmobile/setup-r8a7740.c
+++ b/arch/arm/mach-shmobile/setup-r8a7740.c
@@ -237,35 +237,32 @@ R8A7740_SCIF(PORT_SCIFA, 7, 0xe6cd0000, gic_spi(107));
R8A7740_SCIF(PORT_SCIFB, 8, 0xe6c30000, gic_spi(108));
/* CMT */
-static struct sh_timer_config cmt10_platform_data = {
- .name = "CMT10",
- .channel_offset = 0x10,
- .timer_bit = 0,
- .clockevent_rating = 125,
- .clocksource_rating = 125,
+static struct sh_timer_channel_config cmt1_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 125,
+ .clocksource_rating = 125,
+ },
};
-static struct resource cmt10_resources[] = {
- [0] = {
- .name = "CMT10",
- .start = 0xe6138010,
- .end = 0xe613801b,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = gic_spi(58),
- .flags = IORESOURCE_IRQ,
- },
+static struct sh_timer_config cmt1_platform_data = {
+ .channels = cmt1_channels,
+ .num_channels = ARRAY_SIZE(cmt1_channels),
};
-static struct platform_device cmt10_device = {
- .name = "sh_cmt",
- .id = 10,
+static struct resource cmt1_resources[] = {
+ DEFINE_RES_MEM(0xe6138000, 0x170),
+ DEFINE_RES_IRQ(gic_spi(58)),
+};
+
+static struct platform_device cmt1_device = {
+ .name = "sh-cmt-48",
+ .id = 1,
.dev = {
- .platform_data = &cmt10_platform_data,
+ .platform_data = &cmt1_platform_data,
},
- .resource = cmt10_resources,
- .num_resources = ARRAY_SIZE(cmt10_resources),
+ .resource = cmt1_resources,
+ .num_resources = ARRAY_SIZE(cmt1_resources),
};
/* TMU */
@@ -400,7 +397,7 @@ static struct platform_device *r8a7740_devices_dt[] __initdata = {
&scif6_device,
&scif7_device,
&scif8_device,
- &cmt10_device,
+ &cmt1_device,
};
static struct platform_device *r8a7740_early_devices[] __initdata = {
--
1.8.3.2
The CMT (Compare Match Timer) driver implements a new style of platform
data that handles the timer as a single device with multiple channel.
Switch from the old-style platform data to the new-style platform data.
Signed-off-by: Laurent Pinchart <[email protected]>
---
arch/arm/mach-shmobile/clock-r8a73a4.c | 2 +-
arch/arm/mach-shmobile/setup-r8a73a4.c | 25 +++++++++++++++----------
2 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/arch/arm/mach-shmobile/clock-r8a73a4.c b/arch/arm/mach-shmobile/clock-r8a73a4.c
index 7348d58..cd75821 100644
--- a/arch/arm/mach-shmobile/clock-r8a73a4.c
+++ b/arch/arm/mach-shmobile/clock-r8a73a4.c
@@ -597,7 +597,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_DEV_ID("e6560000.i2c", &mstp_clks[MSTP317]),
CLKDEV_DEV_ID("e6500000.i2c", &mstp_clks[MSTP318]),
CLKDEV_DEV_ID("e6510000.i2c", &mstp_clks[MSTP323]),
- CLKDEV_DEV_ID("sh_cmt.10", &mstp_clks[MSTP329]),
+ CLKDEV_DEV_ID("sh-cmt-48-gen2.1", &mstp_clks[MSTP329]),
CLKDEV_DEV_ID("e60b0000.i2c", &mstp_clks[MSTP409]),
CLKDEV_DEV_ID("e6540000.i2c", &mstp_clks[MSTP410]),
CLKDEV_DEV_ID("e6530000.i2c", &mstp_clks[MSTP411]),
diff --git a/arch/arm/mach-shmobile/setup-r8a73a4.c b/arch/arm/mach-shmobile/setup-r8a73a4.c
index cd36f80..5794229 100644
--- a/arch/arm/mach-shmobile/setup-r8a73a4.c
+++ b/arch/arm/mach-shmobile/setup-r8a73a4.c
@@ -169,20 +169,25 @@ static const struct resource thermal0_resources[] = {
thermal0_resources, \
ARRAY_SIZE(thermal0_resources))
-static struct sh_timer_config cmt10_platform_data = {
- .name = "CMT10",
- .timer_bit = 0,
- .clockevent_rating = 80,
+static struct sh_timer_channel_config cmt1_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 80,
+ },
+};
+
+static struct sh_timer_config cmt1_platform_data = {
+ .channels = cmt1_channels,
+ .num_channels = ARRAY_SIZE(cmt1_channels),
};
-static struct resource cmt10_resources[] = {
- DEFINE_RES_MEM(0xe6130010, 0x0c),
- DEFINE_RES_MEM(0xe6130000, 0x04),
- DEFINE_RES_IRQ(gic_spi(120)), /* CMT1_0 */
+static struct resource cmt1_resources[] = {
+ DEFINE_RES_MEM(0xe6130000, 0x1004),
+ DEFINE_RES_IRQ(gic_spi(120)),
};
#define r8a7790_register_cmt(idx) \
- platform_device_register_resndata(&platform_bus, "sh_cmt", \
+ platform_device_register_resndata(&platform_bus, "sh-cmt-48-gen2", \
idx, cmt##idx##_resources, \
ARRAY_SIZE(cmt##idx##_resources), \
&cmt##idx##_platform_data, \
@@ -196,7 +201,7 @@ void __init r8a73a4_add_dt_devices(void)
r8a73a4_register_scif(3);
r8a73a4_register_scif(4);
r8a73a4_register_scif(5);
- r8a7790_register_cmt(10);
+ r8a7790_register_cmt(1);
}
/* DMA */
--
1.8.3.2
The CMT (Compare Match Timer) driver implements a new style of platform
data that handles the timer as a single device with multiple channel.
Switch from the old-style platform data to the new-style platform data.
Signed-off-by: Laurent Pinchart <[email protected]>
---
arch/arm/mach-shmobile/clock-sh73a0.c | 2 +-
arch/arm/mach-shmobile/setup-sh73a0.c | 45 ++++++++++++++++-------------------
2 files changed, 22 insertions(+), 25 deletions(-)
diff --git a/arch/arm/mach-shmobile/clock-sh73a0.c b/arch/arm/mach-shmobile/clock-sh73a0.c
index 23edf83..723c5c5 100644
--- a/arch/arm/mach-shmobile/clock-sh73a0.c
+++ b/arch/arm/mach-shmobile/clock-sh73a0.c
@@ -650,7 +650,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_DEV_ID("sh-sci.3", &mstp_clks[MSTP201]), /* SCIFA3 */
CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[MSTP200]), /* SCIFA4 */
CLKDEV_DEV_ID("sh-sci.6", &mstp_clks[MSTP331]), /* SCIFA6 */
- CLKDEV_DEV_ID("sh_cmt.10", &mstp_clks[MSTP329]), /* CMT10 */
+ CLKDEV_DEV_ID("sh-cmt-48.1", &mstp_clks[MSTP329]), /* CMT1 */
CLKDEV_DEV_ID("sh_fsi2", &mstp_clks[MSTP328]), /* FSI */
CLKDEV_DEV_ID("ec230000.sound", &mstp_clks[MSTP328]), /* FSI */
CLKDEV_DEV_ID("sh_irda.0", &mstp_clks[MSTP325]), /* IrDA */
diff --git a/arch/arm/mach-shmobile/setup-sh73a0.c b/arch/arm/mach-shmobile/setup-sh73a0.c
index f74ab53..738bbe4 100644
--- a/arch/arm/mach-shmobile/setup-sh73a0.c
+++ b/arch/arm/mach-shmobile/setup-sh73a0.c
@@ -104,35 +104,32 @@ SH73A0_SCIF(PORT_SCIFA, 6, 0xe6cc0000, gic_spi(156));
SH73A0_SCIF(PORT_SCIFA, 7, 0xe6cd0000, gic_spi(143));
SH73A0_SCIF(PORT_SCIFB, 8, 0xe6c30000, gic_spi(80));
-static struct sh_timer_config cmt10_platform_data = {
- .name = "CMT10",
- .channel_offset = 0x10,
- .timer_bit = 0,
- .clockevent_rating = 80,
- .clocksource_rating = 125,
+static struct sh_timer_channel_config cmt1_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 80,
+ .clocksource_rating = 125,
+ },
};
-static struct resource cmt10_resources[] = {
- [0] = {
- .name = "CMT10",
- .start = 0xe6138010,
- .end = 0xe613801b,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = gic_spi(65),
- .flags = IORESOURCE_IRQ,
- },
+static struct sh_timer_config cmt1_platform_data = {
+ .channels = cmt1_channels,
+ .num_channels = ARRAY_SIZE(cmt1_channels),
};
-static struct platform_device cmt10_device = {
- .name = "sh_cmt",
- .id = 10,
+static struct resource cmt1_resources[] = {
+ DEFINE_RES_MEM(0xe6138010, 0x200),
+ DEFINE_RES_IRQ(gic_spi(65)),
+};
+
+static struct platform_device cmt1_device = {
+ .name = "sh-cmt-48",
+ .id = 1,
.dev = {
- .platform_data = &cmt10_platform_data,
+ .platform_data = &cmt1_platform_data,
},
- .resource = cmt10_resources,
- .num_resources = ARRAY_SIZE(cmt10_resources),
+ .resource = cmt1_resources,
+ .num_resources = ARRAY_SIZE(cmt1_resources),
};
/* TMU */
@@ -746,7 +743,7 @@ static struct platform_device *sh73a0_devices_dt[] __initdata = {
&scif6_device,
&scif7_device,
&scif8_device,
- &cmt10_device,
+ &cmt1_device,
};
static struct platform_device *sh73a0_early_devices[] __initdata = {
--
1.8.3.2
The CMT (Compare Match Timer) driver implements a new style of platform
data that handles the timer as a single device with multiple channel.
Switch from the old-style platform data to the new-style platform data.
Signed-off-by: Laurent Pinchart <[email protected]>
---
arch/sh/kernel/cpu/clock-cpg.c | 3 +-
arch/sh/kernel/cpu/sh2/setup-sh7619.c | 73 +++++-----------
arch/sh/kernel/cpu/sh2a/clock-sh7264.c | 2 +-
arch/sh/kernel/cpu/sh2a/clock-sh7269.c | 2 +-
arch/sh/kernel/cpu/sh2a/setup-sh7203.c | 73 +++++-----------
arch/sh/kernel/cpu/sh2a/setup-sh7206.c | 73 +++++-----------
arch/sh/kernel/cpu/sh2a/setup-sh7264.c | 76 +++++-----------
arch/sh/kernel/cpu/sh2a/setup-sh7269.c | 73 +++++-----------
arch/sh/kernel/cpu/sh3/setup-sh7720.c | 155 ++++-----------------------------
arch/sh/kernel/cpu/sh4a/clock-sh7343.c | 2 +-
arch/sh/kernel/cpu/sh4a/clock-sh7366.c | 2 +-
arch/sh/kernel/cpu/sh4a/clock-sh7722.c | 2 +-
arch/sh/kernel/cpu/sh4a/clock-sh7723.c | 2 +-
arch/sh/kernel/cpu/sh4a/clock-sh7724.c | 2 +-
arch/sh/kernel/cpu/sh4a/setup-sh7343.c | 28 +++---
arch/sh/kernel/cpu/sh4a/setup-sh7366.c | 27 +++---
arch/sh/kernel/cpu/sh4a/setup-sh7722.c | 28 +++---
arch/sh/kernel/cpu/sh4a/setup-sh7723.c | 28 +++---
arch/sh/kernel/cpu/sh4a/setup-sh7724.c | 28 +++---
19 files changed, 207 insertions(+), 472 deletions(-)
diff --git a/arch/sh/kernel/cpu/clock-cpg.c b/arch/sh/kernel/cpu/clock-cpg.c
index f59b1f3..3f425f5 100644
--- a/arch/sh/kernel/cpu/clock-cpg.c
+++ b/arch/sh/kernel/cpu/clock-cpg.c
@@ -58,7 +58,8 @@ int __init __deprecated cpg_clk_init(void)
clk_add_alias("tmu_fck", NULL, "peripheral_clk", NULL);
clk_add_alias("mtu2_fck", NULL, "peripheral_clk", NULL);
- clk_add_alias("cmt_fck", NULL, "peripheral_clk", NULL);
+ clk_add_alias(NULL, "sh-cmt-16.0", "peripheral_clk", NULL);
+ clk_add_alias(NULL, "sh-cmt-32.0", "peripheral_clk", NULL);
clk_add_alias("sci_ick", NULL, "peripheral_clk", NULL);
return ret;
diff --git a/arch/sh/kernel/cpu/sh2/setup-sh7619.c b/arch/sh/kernel/cpu/sh2/setup-sh7619.c
index 3860b0b..73bdf66 100644
--- a/arch/sh/kernel/cpu/sh2/setup-sh7619.c
+++ b/arch/sh/kernel/cpu/sh2/setup-sh7619.c
@@ -152,62 +152,35 @@ static struct platform_device eth_device = {
.resource = eth_resources,
};
-static struct sh_timer_config cmt0_platform_data = {
- .channel_offset = 0x02,
- .timer_bit = 0,
- .clockevent_rating = 125,
- .clocksource_rating = 0, /* disabled due to code generation issues */
-};
-
-static struct resource cmt0_resources[] = {
- [0] = {
- .start = 0xf84a0072,
- .end = 0xf84a0077,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 86,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device cmt0_device = {
- .name = "sh_cmt",
- .id = 0,
- .dev = {
- .platform_data = &cmt0_platform_data,
+static struct sh_timer_channel_config cmt_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 125,
+ }, {
+ .index = 1,
+ .clockevent_rating = 125,
},
- .resource = cmt0_resources,
- .num_resources = ARRAY_SIZE(cmt0_resources),
};
-static struct sh_timer_config cmt1_platform_data = {
- .channel_offset = 0x08,
- .timer_bit = 1,
- .clockevent_rating = 125,
- .clocksource_rating = 0, /* disabled due to code generation issues */
+static struct sh_timer_config cmt_platform_data = {
+ .channels = cmt_channels,
+ .num_channels = ARRAY_SIZE(cmt_channels),
};
-static struct resource cmt1_resources[] = {
- [0] = {
- .start = 0xf84a0078,
- .end = 0xf84a007d,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 87,
- .flags = IORESOURCE_IRQ,
- },
+static struct resource cmt_resources[] = {
+ DEFINE_RES_MEM(0xf84a0078, 0x10),
+ DEFINE_RES_IRQ(86),
+ DEFINE_RES_IRQ(87),
};
-static struct platform_device cmt1_device = {
- .name = "sh_cmt",
- .id = 1,
+static struct platform_device cmt_device = {
+ .name = "sh-cmt-16",
+ .id = 0,
.dev = {
- .platform_data = &cmt1_platform_data,
+ .platform_data = &cmt_platform_data,
},
- .resource = cmt1_resources,
- .num_resources = ARRAY_SIZE(cmt1_resources),
+ .resource = cmt_resources,
+ .num_resources = ARRAY_SIZE(cmt_resources),
};
static struct platform_device *sh7619_devices[] __initdata = {
@@ -215,8 +188,7 @@ static struct platform_device *sh7619_devices[] __initdata = {
&scif1_device,
&scif2_device,
ð_device,
- &cmt0_device,
- &cmt1_device,
+ &cmt_device,
};
static int __init sh7619_devices_setup(void)
@@ -235,8 +207,7 @@ static struct platform_device *sh7619_early_devices[] __initdata = {
&scif0_device,
&scif1_device,
&scif2_device,
- &cmt0_device,
- &cmt1_device,
+ &cmt_device,
};
#define STBCR3 0xf80a0000
diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7264.c b/arch/sh/kernel/cpu/sh2a/clock-sh7264.c
index fdf585c..b8dcd34 100644
--- a/arch/sh/kernel/cpu/sh2a/clock-sh7264.c
+++ b/arch/sh/kernel/cpu/sh2a/clock-sh7264.c
@@ -117,7 +117,7 @@ static struct clk_lookup lookups[] = {
/* MSTP clocks */
CLKDEV_CON_ID("sci_ick", &mstp_clks[MSTP77]),
CLKDEV_CON_ID("vdc3", &mstp_clks[MSTP74]),
- CLKDEV_CON_ID("cmt_fck", &mstp_clks[MSTP72]),
+ CLKDEV_DEV_ID("sh-cmt-16.0", &mstp_clks[MSTP72]),
CLKDEV_CON_ID("usb0", &mstp_clks[MSTP60]),
CLKDEV_CON_ID("mtu2_fck", &mstp_clks[MSTP35]),
CLKDEV_CON_ID("sdhi0", &mstp_clks[MSTP34]),
diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7269.c b/arch/sh/kernel/cpu/sh2a/clock-sh7269.c
index 6b78762..f7346b4 100644
--- a/arch/sh/kernel/cpu/sh2a/clock-sh7269.c
+++ b/arch/sh/kernel/cpu/sh2a/clock-sh7269.c
@@ -158,7 +158,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_ICK_ID("sci_fck", "sh-sci.5", &mstp_clks[MSTP42]),
CLKDEV_ICK_ID("sci_fck", "sh-sci.6", &mstp_clks[MSTP41]),
CLKDEV_ICK_ID("sci_fck", "sh-sci.7", &mstp_clks[MSTP40]),
- CLKDEV_CON_ID("cmt_fck", &mstp_clks[MSTP72]),
+ CLKDEV_DEV_ID("sh-cmt-16.0", &mstp_clks[MSTP72]),
CLKDEV_CON_ID("usb0", &mstp_clks[MSTP60]),
CLKDEV_CON_ID("mtu2_fck", &mstp_clks[MSTP35]),
CLKDEV_CON_ID("adc0", &mstp_clks[MSTP32]),
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7203.c b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c
index d55a0f3..ef9d37a 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-sh7203.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c
@@ -265,62 +265,35 @@ static struct platform_device scif3_device = {
},
};
-static struct sh_timer_config cmt0_platform_data = {
- .channel_offset = 0x02,
- .timer_bit = 0,
- .clockevent_rating = 125,
- .clocksource_rating = 0, /* disabled due to code generation issues */
-};
-
-static struct resource cmt0_resources[] = {
- [0] = {
- .start = 0xfffec002,
- .end = 0xfffec007,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 142,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device cmt0_device = {
- .name = "sh_cmt",
- .id = 0,
- .dev = {
- .platform_data = &cmt0_platform_data,
+static struct sh_timer_channel_config cmt_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 125,
+ }, {
+ .index = 1,
+ .clockevent_rating = 125,
},
- .resource = cmt0_resources,
- .num_resources = ARRAY_SIZE(cmt0_resources),
};
-static struct sh_timer_config cmt1_platform_data = {
- .channel_offset = 0x08,
- .timer_bit = 1,
- .clockevent_rating = 125,
- .clocksource_rating = 0, /* disabled due to code generation issues */
+static struct sh_timer_config cmt_platform_data = {
+ .channels = cmt_channels,
+ .num_channels = ARRAY_SIZE(cmt_channels),
};
-static struct resource cmt1_resources[] = {
- [0] = {
- .start = 0xfffec008,
- .end = 0xfffec00d,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 143,
- .flags = IORESOURCE_IRQ,
- },
+static struct resource cmt_resources[] = {
+ DEFINE_RES_MEM(0xfffec000, 0x10),
+ DEFINE_RES_IRQ(142),
+ DEFINE_RES_IRQ(143),
};
-static struct platform_device cmt1_device = {
- .name = "sh_cmt",
- .id = 1,
+static struct platform_device cmt_device = {
+ .name = "sh-cmt-16",
+ .id = 0,
.dev = {
- .platform_data = &cmt1_platform_data,
+ .platform_data = &cmt_platform_data,
},
- .resource = cmt1_resources,
- .num_resources = ARRAY_SIZE(cmt1_resources),
+ .resource = cmt_resources,
+ .num_resources = ARRAY_SIZE(cmt_resources),
};
static struct sh_timer_config mtu2_0_platform_data = {
@@ -404,8 +377,7 @@ static struct platform_device *sh7203_devices[] __initdata = {
&scif1_device,
&scif2_device,
&scif3_device,
- &cmt0_device,
- &cmt1_device,
+ &cmt_device,
&mtu2_0_device,
&mtu2_1_device,
&rtc_device,
@@ -428,8 +400,7 @@ static struct platform_device *sh7203_early_devices[] __initdata = {
&scif1_device,
&scif2_device,
&scif3_device,
- &cmt0_device,
- &cmt1_device,
+ &cmt_device,
&mtu2_0_device,
&mtu2_1_device,
};
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
index 241e745..f8c93a4 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
@@ -217,62 +217,35 @@ static struct platform_device scif3_device = {
},
};
-static struct sh_timer_config cmt0_platform_data = {
- .channel_offset = 0x02,
- .timer_bit = 0,
- .clockevent_rating = 125,
- .clocksource_rating = 0, /* disabled due to code generation issues */
-};
-
-static struct resource cmt0_resources[] = {
- [0] = {
- .start = 0xfffec002,
- .end = 0xfffec007,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 140,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device cmt0_device = {
- .name = "sh_cmt",
- .id = 0,
- .dev = {
- .platform_data = &cmt0_platform_data,
+static struct sh_timer_channel_config cmt_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 125,
+ }, {
+ .index = 1,
+ .clockevent_rating = 125,
},
- .resource = cmt0_resources,
- .num_resources = ARRAY_SIZE(cmt0_resources),
};
-static struct sh_timer_config cmt1_platform_data = {
- .channel_offset = 0x08,
- .timer_bit = 1,
- .clockevent_rating = 125,
- .clocksource_rating = 0, /* disabled due to code generation issues */
+static struct sh_timer_config cmt_platform_data = {
+ .channels = cmt_channels,
+ .num_channels = ARRAY_SIZE(cmt_channels),
};
-static struct resource cmt1_resources[] = {
- [0] = {
- .start = 0xfffec008,
- .end = 0xfffec00d,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 144,
- .flags = IORESOURCE_IRQ,
- },
+static struct resource cmt_resources[] = {
+ DEFINE_RES_MEM(0xfffec000, 0x10),
+ DEFINE_RES_IRQ(140),
+ DEFINE_RES_IRQ(144),
};
-static struct platform_device cmt1_device = {
- .name = "sh_cmt",
- .id = 1,
+static struct platform_device cmt_device = {
+ .name = "sh-cmt-16",
+ .id = 0,
.dev = {
- .platform_data = &cmt1_platform_data,
+ .platform_data = &cmt_platform_data,
},
- .resource = cmt1_resources,
- .num_resources = ARRAY_SIZE(cmt1_resources),
+ .resource = cmt_resources,
+ .num_resources = ARRAY_SIZE(cmt_resources),
};
static struct sh_timer_config mtu2_0_platform_data = {
@@ -364,8 +337,7 @@ static struct platform_device *sh7206_devices[] __initdata = {
&scif1_device,
&scif2_device,
&scif3_device,
- &cmt0_device,
- &cmt1_device,
+ &cmt_device,
&mtu2_0_device,
&mtu2_1_device,
&mtu2_2_device,
@@ -388,8 +360,7 @@ static struct platform_device *sh7206_early_devices[] __initdata = {
&scif1_device,
&scif2_device,
&scif3_device,
- &cmt0_device,
- &cmt1_device,
+ &cmt_device,
&mtu2_0_device,
&mtu2_1_device,
&mtu2_2_device,
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7264.c b/arch/sh/kernel/cpu/sh2a/setup-sh7264.c
index ad5b0f4..838d05f 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-sh7264.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7264.c
@@ -433,65 +433,35 @@ static struct platform_device scif7_device = {
},
};
-static struct sh_timer_config cmt0_platform_data = {
- .channel_offset = 0x02,
- .timer_bit = 0,
- .clockevent_rating = 125,
- .clocksource_rating = 0, /* disabled due to code generation issues */
-};
-
-static struct resource cmt0_resources[] = {
- [0] = {
- .name = "CMT0",
- .start = 0xfffec002,
- .end = 0xfffec007,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 175,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device cmt0_device = {
- .name = "sh_cmt",
- .id = 0,
- .dev = {
- .platform_data = &cmt0_platform_data,
+static struct sh_timer_channel_config cmt_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 125,
+ }, {
+ .index = 1,
+ .clockevent_rating = 125,
},
- .resource = cmt0_resources,
- .num_resources = ARRAY_SIZE(cmt0_resources),
};
-static struct sh_timer_config cmt1_platform_data = {
- .name = "CMT1",
- .channel_offset = 0x08,
- .timer_bit = 1,
- .clockevent_rating = 125,
- .clocksource_rating = 0, /* disabled due to code generation issues */
+static struct sh_timer_config cmt_platform_data = {
+ .channels = cmt_channels,
+ .num_channels = ARRAY_SIZE(cmt_channels),
};
-static struct resource cmt1_resources[] = {
- [0] = {
- .name = "CMT1",
- .start = 0xfffec008,
- .end = 0xfffec00d,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 176,
- .flags = IORESOURCE_IRQ,
- },
+static struct resource cmt_resources[] = {
+ DEFINE_RES_MEM(0xfffec000, 0x10),
+ DEFINE_RES_IRQ(175),
+ DEFINE_RES_IRQ(176),
};
-static struct platform_device cmt1_device = {
- .name = "sh_cmt",
- .id = 1,
+static struct platform_device cmt_device = {
+ .name = "sh-cmt-16",
+ .id = 0,
.dev = {
- .platform_data = &cmt1_platform_data,
+ .platform_data = &cmt_platform_data,
},
- .resource = cmt1_resources,
- .num_resources = ARRAY_SIZE(cmt1_resources),
+ .resource = cmt_resources,
+ .num_resources = ARRAY_SIZE(cmt_resources),
};
static struct sh_timer_config mtu2_0_platform_data = {
@@ -620,8 +590,7 @@ static struct platform_device *sh7264_devices[] __initdata = {
&scif5_device,
&scif6_device,
&scif7_device,
- &cmt0_device,
- &cmt1_device,
+ &cmt_device,
&mtu2_0_device,
&mtu2_1_device,
&rtc_device,
@@ -649,8 +618,7 @@ static struct platform_device *sh7264_early_devices[] __initdata = {
&scif5_device,
&scif6_device,
&scif7_device,
- &cmt0_device,
- &cmt1_device,
+ &cmt_device,
&mtu2_0_device,
&mtu2_1_device,
};
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7269.c b/arch/sh/kernel/cpu/sh2a/setup-sh7269.c
index 3995119..3e66297 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-sh7269.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7269.c
@@ -455,62 +455,35 @@ static struct platform_device scif7_device = {
},
};
-static struct sh_timer_config cmt0_platform_data = {
- .channel_offset = 0x02,
- .timer_bit = 0,
- .clockevent_rating = 125,
- .clocksource_rating = 0, /* disabled due to code generation issues */
-};
-
-static struct resource cmt0_resources[] = {
- [0] = {
- .start = 0xfffec002,
- .end = 0xfffec007,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 188,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device cmt0_device = {
- .name = "sh_cmt",
- .id = 0,
- .dev = {
- .platform_data = &cmt0_platform_data,
+static struct sh_timer_channel_config cmt_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 125,
+ }, {
+ .index = 1,
+ .clockevent_rating = 125,
},
- .resource = cmt0_resources,
- .num_resources = ARRAY_SIZE(cmt0_resources),
};
-static struct sh_timer_config cmt1_platform_data = {
- .channel_offset = 0x08,
- .timer_bit = 1,
- .clockevent_rating = 125,
- .clocksource_rating = 0, /* disabled due to code generation issues */
+static struct sh_timer_config cmt_platform_data = {
+ .channels = cmt_channels,
+ .num_channels = ARRAY_SIZE(cmt_channels),
};
-static struct resource cmt1_resources[] = {
- [0] = {
- .start = 0xfffec008,
- .end = 0xfffec00d,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 189,
- .flags = IORESOURCE_IRQ,
- },
+static struct resource cmt_resources[] = {
+ DEFINE_RES_MEM(0xfffec000, 0x10),
+ DEFINE_RES_IRQ(188),
+ DEFINE_RES_IRQ(189),
};
-static struct platform_device cmt1_device = {
- .name = "sh_cmt",
- .id = 1,
+static struct platform_device cmt_device = {
+ .name = "sh-cmt-16",
+ .id = 0,
.dev = {
- .platform_data = &cmt1_platform_data,
+ .platform_data = &cmt_platform_data,
},
- .resource = cmt1_resources,
- .num_resources = ARRAY_SIZE(cmt1_resources),
+ .resource = cmt_resources,
+ .num_resources = ARRAY_SIZE(cmt_resources),
};
static struct sh_timer_config mtu2_0_platform_data = {
@@ -629,8 +602,7 @@ static struct platform_device *sh7269_devices[] __initdata = {
&scif5_device,
&scif6_device,
&scif7_device,
- &cmt0_device,
- &cmt1_device,
+ &cmt_device,
&mtu2_0_device,
&mtu2_1_device,
&rtc_device,
@@ -658,8 +630,7 @@ static struct platform_device *sh7269_early_devices[] __initdata = {
&scif5_device,
&scif6_device,
&scif7_device,
- &cmt0_device,
- &cmt1_device,
+ &cmt_device,
&mtu2_0_device,
&mtu2_1_device,
};
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7720.c b/arch/sh/kernel/cpu/sh3/setup-sh7720.c
index 1d5729d..3cafd93 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh7720.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh7720.c
@@ -152,141 +152,32 @@ static struct platform_device usbf_device = {
.resource = usbf_resources,
};
-static struct sh_timer_config cmt0_platform_data = {
- .channel_offset = 0x10,
- .timer_bit = 0,
- .clockevent_rating = 125,
- .clocksource_rating = 125,
-};
-
-static struct resource cmt0_resources[] = {
- [0] = {
- .start = 0x044a0010,
- .end = 0x044a001b,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = evt2irq(0xf00),
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device cmt0_device = {
- .name = "sh_cmt",
- .id = 0,
- .dev = {
- .platform_data = &cmt0_platform_data,
- },
- .resource = cmt0_resources,
- .num_resources = ARRAY_SIZE(cmt0_resources),
-};
-
-static struct sh_timer_config cmt1_platform_data = {
- .channel_offset = 0x20,
- .timer_bit = 1,
-};
-
-static struct resource cmt1_resources[] = {
- [0] = {
- .start = 0x044a0020,
- .end = 0x044a002b,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = evt2irq(0xf00),
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device cmt1_device = {
- .name = "sh_cmt",
- .id = 1,
- .dev = {
- .platform_data = &cmt1_platform_data,
- },
- .resource = cmt1_resources,
- .num_resources = ARRAY_SIZE(cmt1_resources),
-};
-
-static struct sh_timer_config cmt2_platform_data = {
- .channel_offset = 0x30,
- .timer_bit = 2,
-};
-
-static struct resource cmt2_resources[] = {
- [0] = {
- .start = 0x044a0030,
- .end = 0x044a003b,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = evt2irq(0xf00),
- .flags = IORESOURCE_IRQ,
+static struct sh_timer_channel_config cmt_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 125,
+ .clocksource_rating = 125,
},
};
-static struct platform_device cmt2_device = {
- .name = "sh_cmt",
- .id = 2,
- .dev = {
- .platform_data = &cmt2_platform_data,
- },
- .resource = cmt2_resources,
- .num_resources = ARRAY_SIZE(cmt2_resources),
+static struct sh_timer_config cmt_platform_data = {
+ .channels = cmt_channels,
+ .num_channels = ARRAY_SIZE(cmt_channels),
};
-static struct sh_timer_config cmt3_platform_data = {
- .channel_offset = 0x40,
- .timer_bit = 3,
+static struct resource cmt_resources[] = {
+ DEFINE_RES_MEM(0x044a0000, 0x60),
+ DEFINE_RES_IRQ(evt2irq(0xf00)),
};
-static struct resource cmt3_resources[] = {
- [0] = {
- .start = 0x044a0040,
- .end = 0x044a004b,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = evt2irq(0xf00),
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device cmt3_device = {
- .name = "sh_cmt",
- .id = 3,
- .dev = {
- .platform_data = &cmt3_platform_data,
- },
- .resource = cmt3_resources,
- .num_resources = ARRAY_SIZE(cmt3_resources),
-};
-
-static struct sh_timer_config cmt4_platform_data = {
- .channel_offset = 0x50,
- .timer_bit = 4,
-};
-
-static struct resource cmt4_resources[] = {
- [0] = {
- .start = 0x044a0050,
- .end = 0x044a005b,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = evt2irq(0xf00),
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device cmt4_device = {
- .name = "sh_cmt",
- .id = 4,
+static struct platform_device cmt_device = {
+ .name = "sh-cmt-32",
+ .id = 0,
.dev = {
- .platform_data = &cmt4_platform_data,
+ .platform_data = &cmt_platform_data,
},
- .resource = cmt4_resources,
- .num_resources = ARRAY_SIZE(cmt4_resources),
+ .resource = cmt_resources,
+ .num_resources = ARRAY_SIZE(cmt_resources),
};
static struct sh_timer_config tmu0_platform_data = {
@@ -375,11 +266,7 @@ static struct platform_device tmu2_device = {
static struct platform_device *sh7720_devices[] __initdata = {
&scif0_device,
&scif1_device,
- &cmt0_device,
- &cmt1_device,
- &cmt2_device,
- &cmt3_device,
- &cmt4_device,
+ &cmt_device,
&tmu0_device,
&tmu1_device,
&tmu2_device,
@@ -398,11 +285,7 @@ arch_initcall(sh7720_devices_setup);
static struct platform_device *sh7720_early_devices[] __initdata = {
&scif0_device,
&scif1_device,
- &cmt0_device,
- &cmt1_device,
- &cmt2_device,
- &cmt3_device,
- &cmt4_device,
+ &cmt_device,
&tmu0_device,
&tmu1_device,
&tmu2_device,
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7343.c b/arch/sh/kernel/cpu/sh4a/clock-sh7343.c
index 53638e2..1628f82 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7343.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7343.c
@@ -227,7 +227,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("hudi0", &mstp_clks[MSTP019]),
CLKDEV_CON_ID("ubc0", &mstp_clks[MSTP017]),
CLKDEV_CON_ID("tmu_fck", &mstp_clks[MSTP015]),
- CLKDEV_CON_ID("cmt_fck", &mstp_clks[MSTP014]),
+ CLKDEV_DEV_ID("sh-cmt-32.0k", &mstp_clks[MSTP014]),
CLKDEV_CON_ID("rwdt0", &mstp_clks[MSTP013]),
CLKDEV_CON_ID("mfi0", &mstp_clks[MSTP011]),
CLKDEV_CON_ID("flctl0", &mstp_clks[MSTP010]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7366.c b/arch/sh/kernel/cpu/sh4a/clock-sh7366.c
index 22e485d..8a9e8af 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7366.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7366.c
@@ -225,7 +225,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("hudi0", &mstp_clks[MSTP019]),
CLKDEV_CON_ID("ubc0", &mstp_clks[MSTP017]),
CLKDEV_CON_ID("tmu_fck", &mstp_clks[MSTP015]),
- CLKDEV_CON_ID("cmt_fck", &mstp_clks[MSTP014]),
+ CLKDEV_DEV_ID("sh-cmt-32.0", &mstp_clks[MSTP014]),
CLKDEV_CON_ID("rwdt0", &mstp_clks[MSTP013]),
CLKDEV_CON_ID("mfi0", &mstp_clks[MSTP011]),
CLKDEV_CON_ID("flctl0", &mstp_clks[MSTP010]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7722.c b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c
index c4cb740..e40bd2a 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7722.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c
@@ -207,7 +207,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_ICK_ID("tmu_fck", "sh_tmu.1", &mstp_clks[HWBLK_TMU]),
CLKDEV_ICK_ID("tmu_fck", "sh_tmu.2", &mstp_clks[HWBLK_TMU]),
- CLKDEV_CON_ID("cmt_fck", &mstp_clks[HWBLK_CMT]),
+ CLKDEV_DEV_ID("sh-cmt-32.0", &mstp_clks[HWBLK_CMT]),
CLKDEV_DEV_ID("sh-wdt.0", &mstp_clks[HWBLK_RWDT]),
CLKDEV_CON_ID("flctl0", &mstp_clks[HWBLK_FLCTL]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7723.c b/arch/sh/kernel/cpu/sh4a/clock-sh7723.c
index 37c41c7..3e0a6d8 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7723.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7723.c
@@ -236,7 +236,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("sh0", &mstp_clks[HWBLK_SHYWAY]),
CLKDEV_CON_ID("hudi0", &mstp_clks[HWBLK_HUDI]),
CLKDEV_CON_ID("ubc0", &mstp_clks[HWBLK_UBC]),
- CLKDEV_CON_ID("cmt_fck", &mstp_clks[HWBLK_CMT]),
+ CLKDEV_DEV_ID("sh-cmt-32.0", &mstp_clks[HWBLK_CMT]),
CLKDEV_DEV_ID("sh-wdt.0", &mstp_clks[HWBLK_RWDT]),
CLKDEV_DEV_ID("sh-dma-engine.1", &mstp_clks[HWBLK_DMAC1]),
CLKDEV_CON_ID("flctl0", &mstp_clks[HWBLK_FLCTL]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7724.c b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c
index 0128af3..e836d64 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7724.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c
@@ -309,7 +309,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_ICK_ID("tmu_fck", "sh_tmu.2", &mstp_clks[HWBLK_TMU0]),
CLKDEV_ICK_ID("tmu_fck", "sh_tmu.3", &mstp_clks[HWBLK_TMU1]),
- CLKDEV_CON_ID("cmt_fck", &mstp_clks[HWBLK_CMT]),
+ CLKDEV_DEV_ID("sh-cmt-16.0", &mstp_clks[HWBLK_CMT]),
CLKDEV_DEV_ID("sh-wdt.0", &mstp_clks[HWBLK_RWDT]),
CLKDEV_DEV_ID("sh-dma-engine.1", &mstp_clks[HWBLK_DMAC1]),
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
index 245d192..820ce40 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
@@ -227,27 +227,27 @@ static struct platform_device jpu_device = {
.num_resources = ARRAY_SIZE(jpu_resources),
};
+static struct sh_timer_channel_config cmt_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 125,
+ .clocksource_rating = 200,
+ },
+};
+
static struct sh_timer_config cmt_platform_data = {
- .channel_offset = 0x60,
- .timer_bit = 5,
- .clockevent_rating = 125,
- .clocksource_rating = 200,
+ .channels = cmt_channels,
+ .num_channels = ARRAY_SIZE(cmt_channels),
+ .channels_mask = 0x1f,
};
static struct resource cmt_resources[] = {
- [0] = {
- .start = 0x044a0060,
- .end = 0x044a006b,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = evt2irq(0xf00),
- .flags = IORESOURCE_IRQ,
- },
+ DEFINE_RES_MEM(0x044a0000, 0x70),
+ DEFINE_RES_IRQ(evt2irq(0xf00)),
};
static struct platform_device cmt_device = {
- .name = "sh_cmt",
+ .name = "sh-cmt-32",
.id = 0,
.dev = {
.platform_data = &cmt_platform_data,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
index 6f56cbd..c878686 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
@@ -175,27 +175,26 @@ static struct platform_device veu1_device = {
.num_resources = ARRAY_SIZE(veu1_resources),
};
+static struct sh_timer_channel_config cmt_channels[] = {
+ {
+ .index = 5,
+ .clockevent_rating = 125,
+ .clocksource_rating = 200,
+ },
+};
+
static struct sh_timer_config cmt_platform_data = {
- .channel_offset = 0x60,
- .timer_bit = 5,
- .clockevent_rating = 125,
- .clocksource_rating = 200,
+ .channels = cmt_channels,
+ .num_channels = ARRAY_SIZE(cmt_channels),
};
static struct resource cmt_resources[] = {
- [0] = {
- .start = 0x044a0060,
- .end = 0x044a006b,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = evt2irq(0xf00),
- .flags = IORESOURCE_IRQ,
- },
+ DEFINE_RES_MEM(0x044a0000, 0x70),
+ DEFINE_RES_IRQ(evt2irq(0xf00)),
};
static struct platform_device cmt_device = {
- .name = "sh_cmt",
+ .name = "sh-cmt-32",
.id = 0,
.dev = {
.platform_data = &cmt_platform_data,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
index 5a94efc..1c0e14b 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
@@ -412,27 +412,27 @@ static struct platform_device jpu_device = {
.num_resources = ARRAY_SIZE(jpu_resources),
};
+static struct sh_timer_channel_config cmt_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 125,
+ .clocksource_rating = 125,
+ },
+};
+
static struct sh_timer_config cmt_platform_data = {
- .channel_offset = 0x60,
- .timer_bit = 5,
- .clockevent_rating = 125,
- .clocksource_rating = 125,
+ .channels = cmt_channels,
+ .num_channels = ARRAY_SIZE(cmt_channels),
+ .channels_mask = 0x1f,
};
static struct resource cmt_resources[] = {
- [0] = {
- .start = 0x044a0060,
- .end = 0x044a006b,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = evt2irq(0xf00),
- .flags = IORESOURCE_IRQ,
- },
+ DEFINE_RES_MEM(0x044a0000, 0x70),
+ DEFINE_RES_IRQ(evt2irq(0xf00)),
};
static struct platform_device cmt_device = {
- .name = "sh_cmt",
+ .name = "sh-cmt-32",
.id = 0,
.dev = {
.platform_data = &cmt_platform_data,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
index 3c5eb09..9c08917 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
@@ -244,27 +244,27 @@ static struct platform_device veu1_device = {
.num_resources = ARRAY_SIZE(veu1_resources),
};
+static struct sh_timer_channel_config cmt_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 125,
+ .clocksource_rating = 125,
+ },
+};
+
static struct sh_timer_config cmt_platform_data = {
- .channel_offset = 0x60,
- .timer_bit = 5,
- .clockevent_rating = 125,
- .clocksource_rating = 125,
+ .channels = cmt_channels,
+ .num_channels = ARRAY_SIZE(cmt_channels),
+ .channels_mask = 0x1f,
};
static struct resource cmt_resources[] = {
- [0] = {
- .start = 0x044a0060,
- .end = 0x044a006b,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = evt2irq(0xf00),
- .flags = IORESOURCE_IRQ,
- },
+ DEFINE_RES_MEM(0x044a0000, 0x70),
+ DEFINE_RES_IRQ(evt2irq(0xf00)),
};
static struct platform_device cmt_device = {
- .name = "sh_cmt",
+ .name = "sh-cmt-32",
.id = 0,
.dev = {
.platform_data = &cmt_platform_data,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c
index 60ebbc6..be587a9 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c
@@ -647,27 +647,27 @@ static struct platform_device beu1_device = {
.num_resources = ARRAY_SIZE(beu1_resources),
};
+static struct sh_timer_channel_config cmt_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 125,
+ .clocksource_rating = 200,
+ },
+};
+
static struct sh_timer_config cmt_platform_data = {
- .channel_offset = 0x60,
- .timer_bit = 5,
- .clockevent_rating = 125,
- .clocksource_rating = 200,
+ .channels = cmt_channels,
+ .num_channels = ARRAY_SIZE(cmt_channels),
+ .channels_mask = 0x1f,
};
static struct resource cmt_resources[] = {
- [0] = {
- .start = 0x044a0060,
- .end = 0x044a006b,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = evt2irq(0xf00),
- .flags = IORESOURCE_IRQ,
- },
+ DEFINE_RES_MEM(0x044a0000, 0x70),
+ DEFINE_RES_IRQ(evt2irq(0xf00)),
};
static struct platform_device cmt_device = {
- .name = "sh_cmt",
+ .name = "sh-cmt-32",
.id = 0,
.dev = {
.platform_data = &cmt_platform_data,
--
1.8.3.2
The CMT (Compare Match Timer) driver implements a new style of platform
data that handles the timer as a single device with multiple channel.
Switch from the old-style platform data to the new-style platform data.
Signed-off-by: Laurent Pinchart <[email protected]>
---
arch/arm/mach-shmobile/board-lager-reference.c | 2 +-
arch/arm/mach-shmobile/clock-r8a7790.c | 2 +-
arch/arm/mach-shmobile/setup-r8a7790.c | 26 ++++++++++++++++----------
3 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/arch/arm/mach-shmobile/board-lager-reference.c b/arch/arm/mach-shmobile/board-lager-reference.c
index 35fc0f2..4dd43b1 100644
--- a/arch/arm/mach-shmobile/board-lager-reference.c
+++ b/arch/arm/mach-shmobile/board-lager-reference.c
@@ -38,7 +38,7 @@ static void __init lager_add_standard_devices(void)
clk = clk_get(NULL, "cmt0");
if (!IS_ERR(clk)) {
- clk_register_clkdev(clk, NULL, "sh_cmt.0");
+ clk_register_clkdev(clk, NULL, "sh-cmt-48-gen2.0");
clk_put(clk);
}
#else
diff --git a/arch/arm/mach-shmobile/clock-r8a7790.c b/arch/arm/mach-shmobile/clock-r8a7790.c
index 4b276ef..780466e 100644
--- a/arch/arm/mach-shmobile/clock-r8a7790.c
+++ b/arch/arm/mach-shmobile/clock-r8a7790.c
@@ -357,7 +357,7 @@ static struct clk_lookup lookups[] = {
CLKDEV_DEV_ID("sh_mobile_sdhi.3", &mstp_clks[MSTP311]),
CLKDEV_DEV_ID("ee220000.mmc", &mstp_clks[MSTP305]),
CLKDEV_DEV_ID("sh_mmcif.1", &mstp_clks[MSTP305]),
- CLKDEV_DEV_ID("sh_cmt.0", &mstp_clks[MSTP124]),
+ CLKDEV_DEV_ID("sh-cmt-48-gen2.0", &mstp_clks[MSTP124]),
CLKDEV_DEV_ID("qspi.0", &mstp_clks[MSTP917]),
CLKDEV_DEV_ID("renesas_usbhs", &mstp_clks[MSTP704]),
CLKDEV_DEV_ID("pci-rcar-gen2.0", &mstp_clks[MSTP703]),
diff --git a/arch/arm/mach-shmobile/setup-r8a7790.c b/arch/arm/mach-shmobile/setup-r8a7790.c
index b237268..3e5813f 100644
--- a/arch/arm/mach-shmobile/setup-r8a7790.c
+++ b/arch/arm/mach-shmobile/setup-r8a7790.c
@@ -269,20 +269,26 @@ static const struct resource thermal_resources[] __initconst = {
thermal_resources, \
ARRAY_SIZE(thermal_resources))
-static const struct sh_timer_config cmt00_platform_data __initconst = {
- .name = "CMT00",
- .timer_bit = 0,
- .clockevent_rating = 80,
+static struct sh_timer_channel_config cmt0_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 80,
+ },
};
-static const struct resource cmt00_resources[] __initconst = {
- DEFINE_RES_MEM(0xffca0510, 0x0c),
- DEFINE_RES_MEM(0xffca0500, 0x04),
- DEFINE_RES_IRQ(gic_spi(142)), /* CMT0_0 */
+static struct sh_timer_config cmt0_platform_data = {
+ .channels = cmt0_channels,
+ .num_channels = ARRAY_SIZE(cmt0_channels),
+ .channels_mask = 0x1f,
+};
+
+static struct resource cmt0_resources[] = {
+ DEFINE_RES_MEM(0xffca0000, 0x1004),
+ DEFINE_RES_IRQ(gic_spi(142)),
};
#define r8a7790_register_cmt(idx) \
- platform_device_register_resndata(&platform_bus, "sh_cmt", \
+ platform_device_register_resndata(&platform_bus, "sh-cmt-48-gen2", \
idx, cmt##idx##_resources, \
ARRAY_SIZE(cmt##idx##_resources), \
&cmt##idx##_platform_data, \
@@ -290,7 +296,7 @@ static const struct resource cmt00_resources[] __initconst = {
void __init r8a7790_add_dt_devices(void)
{
- r8a7790_register_cmt(00);
+ r8a7790_register_cmt(0);
}
void __init r8a7790_add_standard_devices(void)
--
1.8.3.2
Move the channel setup code from sh_cmt_setup to a new
sh_cmt_setup_channel function and call it from sh_cmt_setup.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 87 ++++++++++++++++++++++++++------------------
1 file changed, 51 insertions(+), 36 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 68cca0a..3a3cc83 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -695,12 +695,59 @@ static int sh_cmt_register(struct sh_cmt_channel *ch, char *name,
return 0;
}
+static int sh_cmt_setup_channel(struct sh_cmt_channel *ch,
+ struct sh_cmt_device *cmt)
+{
+ struct sh_timer_config *cfg = cmt->pdev->dev.platform_data;
+ int irq;
+ int ret;
+
+ memset(ch, 0, sizeof(*ch));
+ ch->cmt = cmt;
+
+ /* Request irq using setup_irq() (too early for request_irq()). */
+ irq = platform_get_irq(cmt->pdev, 0);
+ if (irq < 0) {
+ dev_err(&cmt->pdev->dev, "failed to get irq\n");
+ return irq;
+ }
+
+ ch->irqaction.name = dev_name(&cmt->pdev->dev);
+ ch->irqaction.handler = sh_cmt_interrupt;
+ ch->irqaction.dev_id = ch;
+ ch->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING;
+
+ if (cmt->width == (sizeof(ch->max_match_value) * 8))
+ ch->max_match_value = ~0;
+ else
+ ch->max_match_value = (1 << cmt->width) - 1;
+
+ ch->match_value = ch->max_match_value;
+ raw_spin_lock_init(&ch->lock);
+
+ ret = sh_cmt_register(ch, (char *)dev_name(&cmt->pdev->dev),
+ cfg->clockevent_rating,
+ cfg->clocksource_rating);
+ if (ret) {
+ dev_err(&cmt->pdev->dev, "registration failed\n");
+ return ret;
+ }
+ ch->cs_enabled = false;
+
+ ret = setup_irq(irq, &ch->irqaction);
+ if (ret) {
+ dev_err(&cmt->pdev->dev, "failed to request irq %d\n", irq);
+ return ret;
+ }
+
+ return 0;
+}
+
static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
{
struct sh_timer_config *cfg = pdev->dev.platform_data;
- struct sh_cmt_channel *ch = &cmt->channel;
struct resource *res, *res2;
- int irq, ret;
+ int ret;
ret = -ENXIO;
memset(cmt, 0, sizeof(*cmt));
@@ -720,12 +767,6 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
/* optional resource for the shared timer start/stop register */
res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1);
- irq = platform_get_irq(cmt->pdev, 0);
- if (irq < 0) {
- dev_err(&cmt->pdev->dev, "failed to get irq\n");
- goto err0;
- }
-
/* map memory, let mapbase point to our channel */
cmt->mapbase = ioremap_nocache(res->start, resource_size(res));
if (cmt->mapbase == NULL) {
@@ -742,12 +783,6 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
goto err1;
}
- /* request irq using setup_irq() (too early for request_irq()) */
- ch->irqaction.name = dev_name(&cmt->pdev->dev);
- ch->irqaction.handler = sh_cmt_interrupt;
- ch->irqaction.dev_id = ch;
- ch->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING;
-
/* get hold of clock */
cmt->clk = clk_get(&cmt->pdev->dev, "cmt_fck");
if (IS_ERR(cmt->clk)) {
@@ -783,29 +818,9 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
cmt->clear_bits = ~0xc000;
}
- if (cmt->width == (sizeof(ch->max_match_value) * 8))
- ch->max_match_value = ~0;
- else
- ch->max_match_value = (1 << cmt->width) - 1;
-
- ch->cmt = cmt;
- ch->match_value = ch->max_match_value;
- raw_spin_lock_init(&ch->lock);
-
- ret = sh_cmt_register(ch, (char *)dev_name(&cmt->pdev->dev),
- cfg->clockevent_rating,
- cfg->clocksource_rating);
- if (ret) {
- dev_err(&cmt->pdev->dev, "registration failed\n");
- goto err4;
- }
- ch->cs_enabled = false;
-
- ret = setup_irq(irq, &ch->irqaction);
- if (ret) {
- dev_err(&cmt->pdev->dev, "failed to request irq %d\n", irq);
+ ret = sh_cmt_setup_channel(&cmt->channel, cmt);
+ if (ret < 0)
goto err4;
- }
platform_set_drvdata(pdev, cmt);
--
1.8.3.2
The CMT (Compare Match Timer) driver implements a new style of platform
data that handles the timer as a single device with multiple channel.
Switch from the old-style platform data to the new-style platform data.
Signed-off-by: Laurent Pinchart <[email protected]>
---
arch/arm/mach-shmobile/clock-sh7372.c | 6 +++---
arch/arm/mach-shmobile/setup-sh7372.c | 30 ++++++++++++++----------------
2 files changed, 17 insertions(+), 19 deletions(-)
diff --git a/arch/arm/mach-shmobile/clock-sh7372.c b/arch/arm/mach-shmobile/clock-sh7372.c
index 2848997..5557b3d 100644
--- a/arch/arm/mach-shmobile/clock-sh7372.c
+++ b/arch/arm/mach-shmobile/clock-sh7372.c
@@ -565,10 +565,10 @@ static struct clk_lookup lookups[] = {
CLKDEV_DEV_ID("r8a66597_hcd.1", &mstp_clks[MSTP406]), /* USB1 */
CLKDEV_DEV_ID("r8a66597_udc.1", &mstp_clks[MSTP406]), /* USB1 */
CLKDEV_DEV_ID("renesas_usbhs.1", &mstp_clks[MSTP406]), /* USB1 */
- CLKDEV_DEV_ID("sh_cmt.4", &mstp_clks[MSTP405]), /* CMT4 */
- CLKDEV_DEV_ID("sh_cmt.3", &mstp_clks[MSTP404]), /* CMT3 */
+ CLKDEV_DEV_ID("sh-cmt-32-fast.4", &mstp_clks[MSTP405]), /* CMT4 */
+ CLKDEV_DEV_ID("sh-cmt-32-fast.3", &mstp_clks[MSTP404]), /* CMT3 */
CLKDEV_DEV_ID("sh_keysc.0", &mstp_clks[MSTP403]), /* KEYSC */
- CLKDEV_DEV_ID("sh_cmt.2", &mstp_clks[MSTP400]), /* CMT2 */
+ CLKDEV_DEV_ID("sh-cmt-32-fast.2", &mstp_clks[MSTP400]), /* CMT2 */
/* ICK */
CLKDEV_ICK_ID("dsit_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSIT]),
diff --git a/arch/arm/mach-shmobile/setup-sh7372.c b/arch/arm/mach-shmobile/setup-sh7372.c
index 2730127..3e524d5 100644
--- a/arch/arm/mach-shmobile/setup-sh7372.c
+++ b/arch/arm/mach-shmobile/setup-sh7372.c
@@ -118,29 +118,27 @@ SH7372_SCIF(PORT_SCIFA, 5, 0xe6cb0000, evt2irq(0x0d40));
SH7372_SCIF(PORT_SCIFB, 6, 0xe6c30000, evt2irq(0x0d60));
/* CMT */
+static struct sh_timer_channel_config cmt2_channels[] = {
+ {
+ .index = 0,
+ .clockevent_rating = 125,
+ .clocksource_rating = 125,
+ },
+};
+
static struct sh_timer_config cmt2_platform_data = {
- .name = "CMT2",
- .channel_offset = 0x40,
- .timer_bit = 5,
- .clockevent_rating = 125,
- .clocksource_rating = 125,
+ .channels = cmt2_channels,
+ .num_channels = ARRAY_SIZE(cmt2_channels),
+ .channels_mask = 0x1f,
};
static struct resource cmt2_resources[] = {
- [0] = {
- .name = "CMT2",
- .start = 0xe6130040,
- .end = 0xe613004b,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = evt2irq(0x0b80), /* CMT2 */
- .flags = IORESOURCE_IRQ,
- },
+ DEFINE_RES_MEM(0xe6130000, 0x50),
+ DEFINE_RES_IRQ(evt2irq(0x0b80)),
};
static struct platform_device cmt2_device = {
- .name = "sh_cmt",
+ .name = "sh-cmt-32-fast",
.id = 2,
.dev = {
.platform_data = &cmt2_platform_data,
--
1.8.3.2
This helps locating duplicates and inserting new headers.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 9f5616b..077879b 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -13,23 +13,23 @@
* GNU General Public License for more details.
*/
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/delay.h>
+#include <linux/err.h>
#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
#include <linux/interrupt.h>
-#include <linux/ioport.h>
#include <linux/io.h>
-#include <linux/clk.h>
+#include <linux/ioport.h>
#include <linux/irq.h>
-#include <linux/err.h>
-#include <linux/delay.h>
-#include <linux/clocksource.h>
-#include <linux/clockchips.h>
-#include <linux/sh_timer.h>
-#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
+#include <linux/sh_timer.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
struct sh_cmt_device;
--
1.8.3.2
Do not include the paragraph about writing to the Free Software
Foundation's mailing address from the sample GPL notice. The FSF has
changed addresses in the past, and may do so again. Linux already
includes a copy of the GPL.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 2389e33..9f5616b 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -11,10 +11,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
--
1.8.3.2
The sh_cmt driver gets the CMT functional clock using a connection ID of
"cmt_fck". While all SH SoCs create clock lookup entries with a NULL
device ID and a "cmt_fck" connection ID, the ARM SoCs use the device ID
only with a NULL connection ID. This works on legacy platforms but will
break on ARM with DT boot.
Fix the situation by using a NULL connection ID in the non-legacy
platform data case. Clock lookup entries will be renamed as platforms
get moved to new platform data. The legacy code will eventually be
dropped, leaving us with device ID based clock lookup only, compatible
with DT boot.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 8390f0f..2389e33 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -1013,7 +1013,7 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
cmt->legacy = cmt->info ? false : true;
/* Get hold of clock. */
- cmt->clk = clk_get(&cmt->pdev->dev, "cmt_fck");
+ cmt->clk = clk_get(&cmt->pdev->dev, cmt->legacy ? "cmt_fck" : NULL);
if (IS_ERR(cmt->clk)) {
dev_err(&cmt->pdev->dev, "cannot get clock\n");
return PTR_ERR(cmt->clk);
--
1.8.3.2
CMT hardware devices can support multiple channels, with global
registers and per-channel registers. The sh_cmt driver currently models
the hardware with one Linux device per channel. This model makes it
difficult to handle global registers in a clean way.
Add support for a new model that uses one Linux device per timer with
multiple channels per device. This requires changes to platform data,
add new channel configuration fields.
Support for the legacy model is kept and will be removed after all
platforms switch to the new model.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 299 +++++++++++++++++++++++++++++++++----------
include/linux/sh_timer.h | 9 ++
2 files changed, 239 insertions(+), 69 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 5280231..8390f0f 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -53,7 +53,16 @@ struct sh_cmt_device;
* 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 are indexed from 0 to N-1 in the documentation. The channel index
+ * infers the start/stop bit position in the control register and the channel
+ * registers block address. Some CMT instances have a subset of channels
+ * available, in which case the index in the documentation doesn't match the
+ * "real" index as implemented in hardware. This is for instance the case with
+ * CMT0 on r8a7740, which is a 32-bit variant with a single channel numbered 0
+ * in the documentation but using start/stop bit 5 and having its registers
+ * block at 0x60.
+ *
+ * Similarly CMT0 on r8a73a4, r8a7790 and r8a7791, while implementing 32-bit
* channels only, is a 48-bit gen2 CMT with the 48-bit channels unavailable.
*/
@@ -85,11 +94,15 @@ struct sh_cmt_info {
struct sh_cmt_channel {
struct sh_cmt_device *cmt;
- unsigned int index;
- void __iomem *base;
+ unsigned int index; /* Index in the documentation */
+ unsigned int hwidx; /* Real hardware index */
+
+ void __iomem *iostart;
+ void __iomem *ioctrl;
struct irqaction irqaction;
+ unsigned int timer_bit;
unsigned long flags;
unsigned long match_value;
unsigned long next_match_value;
@@ -106,6 +119,7 @@ struct sh_cmt_device {
struct platform_device *pdev;
const struct sh_cmt_info *info;
+ bool legacy;
void __iomem *mapbase_ch;
void __iomem *mapbase;
@@ -113,6 +127,10 @@ struct sh_cmt_device {
struct sh_cmt_channel *channels;
unsigned int num_channels;
+ unsigned int hw_channels;
+
+ bool has_clockevent;
+ bool has_clocksource;
};
#define SH_CMT16_CMCSR_CMF (1 << 7)
@@ -224,41 +242,47 @@ static const struct sh_cmt_info sh_cmt_info[] = {
static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch)
{
- return ch->cmt->info->read_control(ch->cmt->mapbase, 0);
+ if (ch->iostart)
+ return ch->cmt->info->read_control(ch->iostart, 0);
+ else
+ 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 void sh_cmt_write_cmstr(struct sh_cmt_channel *ch,
+ unsigned long value)
{
- return ch->cmt->info->read_control(ch->base, CMCSR);
+ if (ch->iostart)
+ ch->cmt->info->write_control(ch->iostart, 0, value);
+ else
+ ch->cmt->info->write_control(ch->cmt->mapbase, 0, value);
}
-static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch)
+static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch)
{
- return ch->cmt->info->read_count(ch->base, CMCNT);
+ return ch->cmt->info->read_control(ch->ioctrl, CMCSR);
}
-static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch,
+static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch,
unsigned long value)
{
- ch->cmt->info->write_control(ch->cmt->mapbase, 0, value);
+ ch->cmt->info->write_control(ch->ioctrl, CMCSR, value);
}
-static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch,
- unsigned long value)
+static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch)
{
- ch->cmt->info->write_control(ch->base, CMCSR, value);
+ return ch->cmt->info->read_count(ch->ioctrl, CMCNT);
}
static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch,
unsigned long value)
{
- ch->cmt->info->write_count(ch->base, CMCNT, value);
+ ch->cmt->info->write_count(ch->ioctrl, CMCNT, value);
}
static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch,
unsigned long value)
{
- ch->cmt->info->write_count(ch->base, CMCOR, value);
+ ch->cmt->info->write_count(ch->ioctrl, CMCOR, value);
}
static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch,
@@ -287,7 +311,6 @@ static DEFINE_RAW_SPINLOCK(sh_cmt_lock);
static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start)
{
- struct sh_timer_config *cfg = ch->cmt->pdev->dev.platform_data;
unsigned long flags, value;
/* start stop register shared by multiple timer channels */
@@ -295,9 +318,9 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start)
value = sh_cmt_read_cmstr(ch);
if (start)
- value |= 1 << cfg->timer_bit;
+ value |= 1 << ch->timer_bit;
else
- value &= ~(1 << cfg->timer_bit);
+ value &= ~(1 << ch->timer_bit);
sh_cmt_write_cmstr(ch, value);
raw_spin_unlock_irqrestore(&sh_cmt_lock, flags);
@@ -792,28 +815,71 @@ static int sh_cmt_register(struct sh_cmt_channel *ch, char *name,
unsigned long clockevent_rating,
unsigned long clocksource_rating)
{
- if (clockevent_rating)
+ if (clockevent_rating) {
+ ch->cmt->has_clockevent = true;
sh_cmt_register_clockevent(ch, name, clockevent_rating);
+ }
- if (clocksource_rating)
+ if (clocksource_rating) {
+ ch->cmt->has_clocksource = true;
sh_cmt_register_clocksource(ch, name, clocksource_rating);
+ }
return 0;
}
static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index,
- struct sh_cmt_device *cmt)
+ const struct sh_timer_channel_config *ch_cfg,
+ unsigned int hwidx, struct sh_cmt_device *cmt)
{
- struct sh_timer_config *cfg = cmt->pdev->dev.platform_data;
+ unsigned long clockevent_rating;
+ unsigned long clocksource_rating;
int irq;
int ret;
ch->cmt = cmt;
- ch->base = cmt->mapbase_ch;
- ch->index = index;
+ ch->index = ch_cfg ? ch_cfg->index : index;
+ ch->hwidx = hwidx;
+
+ /*
+ * Compute the address of the channel control register block. For the
+ * timers with a per-channel start/stop register, compute its address
+ * as well.
+ *
+ * For legacy configuration the address has been mapped explicitly.
+ */
+ if (cmt->legacy) {
+ ch->ioctrl = cmt->mapbase_ch;
+ } else {
+ switch (cmt->info->model) {
+ case SH_CMT_16BIT:
+ ch->ioctrl = cmt->mapbase + 2 + ch->hwidx * 6;
+ break;
+ case SH_CMT_32BIT:
+ case SH_CMT_48BIT:
+ ch->ioctrl = cmt->mapbase + 0x10 + ch->hwidx * 0x10;
+ break;
+ case SH_CMT_32BIT_FAST:
+ /*
+ * The 32-bit "fast" timer has a single channel at hwidx
+ * 5 but is located at offset 0x40 instead of 0x60 for
+ * some reason.
+ */
+ ch->ioctrl = cmt->mapbase + 0x40;
+ break;
+ case SH_CMT_48BIT_GEN2:
+ ch->iostart = cmt->mapbase + ch->hwidx * 0x100;
+ ch->ioctrl = ch->iostart + 0x10;
+ break;
+ }
+ }
/* Request irq using setup_irq() (too early for request_irq()). */
- irq = platform_get_irq(cmt->pdev, 0);
+ if (cmt->legacy)
+ irq = platform_get_irq(cmt->pdev, 0);
+ else
+ irq = platform_get_irq(cmt->pdev, ch->index);
+
if (irq < 0) {
dev_err(&cmt->pdev->dev, "failed to get irq\n");
return irq;
@@ -832,9 +898,20 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index,
ch->match_value = ch->max_match_value;
raw_spin_lock_init(&ch->lock);
+ if (ch_cfg) {
+ ch->timer_bit = cmt->info->model == SH_CMT_48BIT_GEN2
+ ? 0 : ch->hwidx;
+ clockevent_rating = ch_cfg->clockevent_rating;
+ clocksource_rating = ch_cfg->clocksource_rating;
+ } else {
+ struct sh_timer_config *cfg = cmt->pdev->dev.platform_data;
+ ch->timer_bit = cfg->timer_bit;
+ clockevent_rating = cfg->clockevent_rating;
+ clocksource_rating = cfg->clocksource_rating;
+ }
+
ret = sh_cmt_register(ch, (char *)dev_name(&cmt->pdev->dev),
- cfg->clockevent_rating,
- cfg->clocksource_rating);
+ clockevent_rating, clocksource_rating);
if (ret) {
dev_err(&cmt->pdev->dev, "registration failed\n");
return ret;
@@ -850,97 +927,169 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index,
return 0;
}
-static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
+static int sh_cmt_map_memory(struct sh_cmt_device *cmt)
{
- struct sh_timer_config *cfg = pdev->dev.platform_data;
- struct resource *res, *res2;
- int ret;
- ret = -ENXIO;
+ struct resource *mem;
- cmt->pdev = pdev;
+ mem = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&cmt->pdev->dev, "failed to get I/O memory\n");
+ return -ENXIO;
+ }
- if (!cfg) {
- dev_err(&cmt->pdev->dev, "missing platform data\n");
- goto err0;
+ cmt->mapbase = ioremap_nocache(mem->start, resource_size(mem));
+ if (cmt->mapbase == NULL) {
+ dev_err(&cmt->pdev->dev, "failed to remap I/O memory\n");
+ return -ENXIO;
}
+ return 0;
+}
+
+static int sh_cmt_map_memory_legacy(struct sh_cmt_device *cmt)
+{
+ struct sh_timer_config *cfg = cmt->pdev->dev.platform_data;
+ struct resource *res, *res2;
+
+ /* map memory, let mapbase_ch point to our channel */
res = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&cmt->pdev->dev, "failed to get I/O memory\n");
- goto err0;
+ return -ENXIO;
}
- /* optional resource for the shared timer start/stop register */
- res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1);
-
- /* map memory, let mapbase_ch point to our channel */
cmt->mapbase_ch = ioremap_nocache(res->start, resource_size(res));
if (cmt->mapbase_ch == NULL) {
dev_err(&cmt->pdev->dev, "failed to remap I/O memory\n");
- goto err0;
+ return -ENXIO;
}
+ /* optional resource for the shared timer start/stop register */
+ res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1);
+
/* map second resource for CMSTR */
cmt->mapbase = ioremap_nocache(res2 ? res2->start :
res->start - cfg->channel_offset,
res2 ? resource_size(res2) : 2);
if (cmt->mapbase == NULL) {
dev_err(&cmt->pdev->dev, "failed to remap I/O second memory\n");
- goto err1;
+ iounmap(cmt->mapbase_ch);
+ return -ENXIO;
}
- /* get hold of clock */
+ /* 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];
+
+ return 0;
+}
+
+static void sh_cmt_unmap_memory(struct sh_cmt_device *cmt)
+{
+ iounmap(cmt->mapbase);
+ if (cmt->mapbase_ch)
+ iounmap(cmt->mapbase_ch);
+}
+
+static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
+{
+ struct sh_timer_config *cfg = pdev->dev.platform_data;
+ const struct platform_device_id *id = pdev->id_entry;
+ int ret;
+
+ memset(cmt, 0, sizeof(*cmt));
+ cmt->pdev = pdev;
+
+ if (!cfg) {
+ dev_err(&cmt->pdev->dev, "missing platform data\n");
+ return -ENXIO;
+ }
+
+ cmt->info = (const struct sh_cmt_info *)id->driver_data;
+ cmt->legacy = cmt->info ? false : true;
+
+ /* Get hold of clock. */
cmt->clk = clk_get(&cmt->pdev->dev, "cmt_fck");
if (IS_ERR(cmt->clk)) {
dev_err(&cmt->pdev->dev, "cannot get clock\n");
- ret = PTR_ERR(cmt->clk);
- goto err2;
+ return PTR_ERR(cmt->clk);
}
ret = clk_prepare(cmt->clk);
if (ret < 0)
- goto err3;
+ goto err_clk_put;
- /* 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];
+ /*
+ * Map the memory resource(s). We need to support both the legacy
+ * platform device configuration (with one device per channel) and the
+ * new version (with multiple channels per device).
+ */
+ if (cmt->legacy)
+ ret = sh_cmt_map_memory_legacy(cmt);
else
- cmt->info = &sh_cmt_info[SH_CMT_32BIT];
+ ret = sh_cmt_map_memory(cmt);
- cmt->channels = kzalloc(sizeof(*cmt->channels), GFP_KERNEL);
+ if (ret < 0)
+ goto err_clk_unprepare;
+
+ /* Allocate and setup the channels. */
+ if (cmt->legacy) {
+ cmt->num_channels = 1;
+ } else {
+ cmt->num_channels = cfg->num_channels;
+ cmt->hw_channels = ~cfg->channels_mask;
+ }
+
+ cmt->channels = kzalloc(cmt->num_channels * sizeof(*cmt->channels),
+ GFP_KERNEL);
if (cmt->channels == NULL) {
ret = -ENOMEM;
- goto err4;
+ goto err_unmap;
}
- cmt->num_channels = 1;
+ if (cmt->legacy) {
+ ret = sh_cmt_setup_channel(&cmt->channels[0], cfg->timer_bit,
+ NULL, cfg->timer_bit, cmt);
+ if (ret < 0)
+ goto err_unmap;
+ } else {
+ const struct sh_timer_channel_config *ch_cfg = cfg->channels;
+ unsigned int mask = cmt->hw_channels;
+ unsigned int i;
+
+ for (i = 0; i < cmt->num_channels; ++i) {
+ unsigned int hwidx = ffs(mask) - 1;
- ret = sh_cmt_setup_channel(&cmt->channels[0], cfg->timer_bit, cmt);
- if (ret < 0)
- goto err4;
+ ret = sh_cmt_setup_channel(&cmt->channels[i], 0, ch_cfg,
+ hwidx, cmt);
+ if (ret < 0)
+ goto err_unmap;
+
+ mask &= ~(1 << hwidx);
+ }
+ }
platform_set_drvdata(pdev, cmt);
return 0;
-err4:
+
+err_unmap:
kfree(cmt->channels);
+ sh_cmt_unmap_memory(cmt);
+err_clk_unprepare:
clk_unprepare(cmt->clk);
-err3:
+err_clk_put:
clk_put(cmt->clk);
-err2:
- iounmap(cmt->mapbase);
-err1:
- iounmap(cmt->mapbase_ch);
-err0:
return ret;
}
static int sh_cmt_probe(struct platform_device *pdev)
{
struct sh_cmt_device *cmt = platform_get_drvdata(pdev);
- struct sh_timer_config *cfg = pdev->dev.platform_data;
int ret;
if (!is_early_platform_device(pdev)) {
@@ -969,7 +1118,7 @@ static int sh_cmt_probe(struct platform_device *pdev)
return 0;
out:
- if (cfg->clockevent_rating || cfg->clocksource_rating)
+ if (cmt->has_clockevent || cmt->has_clocksource)
pm_runtime_irq_safe(&pdev->dev);
else
pm_runtime_idle(&pdev->dev);
@@ -982,12 +1131,24 @@ static int sh_cmt_remove(struct platform_device *pdev)
return -EBUSY; /* cannot unregister clockevent and clocksource */
}
+static const struct platform_device_id sh_cmt_id_table[] = {
+ { "sh_cmt", 0 },
+ { "sh-cmt-16", (kernel_ulong_t)&sh_cmt_info[SH_CMT_16BIT] },
+ { "sh-cmt-32", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT] },
+ { "sh-cmt-32-fast", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT_FAST] },
+ { "sh-cmt-48", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT] },
+ { "sh-cmt-48-gen2", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT_GEN2] },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, sh_cmt_id_table);
+
static struct platform_driver sh_cmt_device_driver = {
.probe = sh_cmt_probe,
.remove = sh_cmt_remove,
.driver = {
.name = "sh_cmt",
- }
+ },
+ .id_table = sh_cmt_id_table,
};
static int __init sh_cmt_init(void)
diff --git a/include/linux/sh_timer.h b/include/linux/sh_timer.h
index 4d9dcd1..b8273e2 100644
--- a/include/linux/sh_timer.h
+++ b/include/linux/sh_timer.h
@@ -1,12 +1,21 @@
#ifndef __SH_TIMER_H__
#define __SH_TIMER_H__
+struct sh_timer_channel_config {
+ unsigned int index;
+ unsigned long clockevent_rating;
+ unsigned long clocksource_rating;
+};
+
struct sh_timer_config {
char *name;
long channel_offset;
int timer_bit;
unsigned long clockevent_rating;
unsigned long clocksource_rating;
+ const struct sh_timer_channel_config *channels;
+ unsigned int num_channels;
+ unsigned int channels_mask;
};
#endif /* __SH_TIMER_H__ */
--
1.8.3.2
Define symbolic macros for all used registers bits.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 56 ++++++++++++++++++++++++++++++++++----------
1 file changed, 44 insertions(+), 12 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 3946c14..5280231 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -115,6 +115,34 @@ struct sh_cmt_device {
unsigned int num_channels;
};
+#define SH_CMT16_CMCSR_CMF (1 << 7)
+#define SH_CMT16_CMCSR_CMIE (1 << 6)
+#define SH_CMT16_CMCSR_CKS8 (0 << 0)
+#define SH_CMT16_CMCSR_CKS32 (1 << 0)
+#define SH_CMT16_CMCSR_CKS128 (2 << 0)
+#define SH_CMT16_CMCSR_CKS512 (3 << 0)
+#define SH_CMT16_CMCSR_CKS_MASK (3 << 0)
+
+#define SH_CMT32_CMCSR_CMF (1 << 15)
+#define SH_CMT32_CMCSR_OVF (1 << 14)
+#define SH_CMT32_CMCSR_WRFLG (1 << 13)
+#define SH_CMT32_CMCSR_STTF (1 << 12)
+#define SH_CMT32_CMCSR_STPF (1 << 11)
+#define SH_CMT32_CMCSR_SSIE (1 << 10)
+#define SH_CMT32_CMCSR_CMS (1 << 9)
+#define SH_CMT32_CMCSR_CMM (1 << 8)
+#define SH_CMT32_CMCSR_CMTOUT_IE (1 << 7)
+#define SH_CMT32_CMCSR_CMR_NONE (0 << 4)
+#define SH_CMT32_CMCSR_CMR_DMA (1 << 4)
+#define SH_CMT32_CMCSR_CMR_IRQ (2 << 4)
+#define SH_CMT32_CMCSR_CMR_MASK (3 << 4)
+#define SH_CMT32_CMCSR_DBGIVD (1 << 3)
+#define SH_CMT32_CMCSR_CKS_RCLK8 (4 << 0)
+#define SH_CMT32_CMCSR_CKS_RCLK32 (5 << 0)
+#define SH_CMT32_CMCSR_CKS_RCLK128 (6 << 0)
+#define SH_CMT32_CMCSR_CKS_RCLK1 (7 << 0)
+#define SH_CMT32_CMCSR_CKS_MASK (7 << 0)
+
static unsigned long sh_cmt_read16(void __iomem *base, unsigned long offs)
{
return ioread16(base + (offs << 1));
@@ -141,8 +169,8 @@ static const struct sh_cmt_info sh_cmt_info[] = {
[SH_CMT_16BIT] = {
.model = SH_CMT_16BIT,
.width = 16,
- .overflow_bit = 0x80,
- .clear_bits = ~0x80,
+ .overflow_bit = SH_CMT16_CMCSR_CMF,
+ .clear_bits = ~SH_CMT16_CMCSR_CMF,
.read_control = sh_cmt_read16,
.write_control = sh_cmt_write16,
.read_count = sh_cmt_read16,
@@ -151,8 +179,8 @@ static const struct sh_cmt_info sh_cmt_info[] = {
[SH_CMT_32BIT] = {
.model = SH_CMT_32BIT,
.width = 32,
- .overflow_bit = 0x8000,
- .clear_bits = ~0xc000,
+ .overflow_bit = SH_CMT32_CMCSR_CMF,
+ .clear_bits = ~(SH_CMT32_CMCSR_CMF | SH_CMT32_CMCSR_OVF),
.read_control = sh_cmt_read16,
.write_control = sh_cmt_write16,
.read_count = sh_cmt_read32,
@@ -161,8 +189,8 @@ static const struct sh_cmt_info sh_cmt_info[] = {
[SH_CMT_32BIT_FAST] = {
.model = SH_CMT_32BIT_FAST,
.width = 32,
- .overflow_bit = 0x8000,
- .clear_bits = ~0xc000,
+ .overflow_bit = SH_CMT32_CMCSR_CMF,
+ .clear_bits = ~(SH_CMT32_CMCSR_CMF | SH_CMT32_CMCSR_OVF),
.read_control = sh_cmt_read16,
.write_control = sh_cmt_write16,
.read_count = sh_cmt_read32,
@@ -171,8 +199,8 @@ static const struct sh_cmt_info sh_cmt_info[] = {
[SH_CMT_48BIT] = {
.model = SH_CMT_48BIT,
.width = 32,
- .overflow_bit = 0x8000,
- .clear_bits = ~0xc000,
+ .overflow_bit = SH_CMT32_CMCSR_CMF,
+ .clear_bits = ~(SH_CMT32_CMCSR_CMF | SH_CMT32_CMCSR_OVF),
.read_control = sh_cmt_read32,
.write_control = sh_cmt_write32,
.read_count = sh_cmt_read32,
@@ -181,8 +209,8 @@ static const struct sh_cmt_info sh_cmt_info[] = {
[SH_CMT_48BIT_GEN2] = {
.model = SH_CMT_48BIT_GEN2,
.width = 32,
- .overflow_bit = 0x8000,
- .clear_bits = ~0xc000,
+ .overflow_bit = SH_CMT32_CMCSR_CMF,
+ .clear_bits = ~(SH_CMT32_CMCSR_CMF | SH_CMT32_CMCSR_OVF),
.read_control = sh_cmt_read32,
.write_control = sh_cmt_write32,
.read_count = sh_cmt_read32,
@@ -296,10 +324,14 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch, unsigned long *rate)
/* configure channel, periodic mode and maximum timeout */
if (ch->cmt->info->width == 16) {
*rate = clk_get_rate(ch->cmt->clk) / 512;
- sh_cmt_write_cmcsr(ch, 0x43);
+ sh_cmt_write_cmcsr(ch, SH_CMT16_CMCSR_CMIE |
+ SH_CMT16_CMCSR_CKS512);
} else {
*rate = clk_get_rate(ch->cmt->clk) / 8;
- sh_cmt_write_cmcsr(ch, 0x01a4);
+ sh_cmt_write_cmcsr(ch, SH_CMT32_CMCSR_CMM |
+ SH_CMT32_CMCSR_CMTOUT_IE |
+ SH_CMT32_CMCSR_CMR_IRQ |
+ SH_CMT32_CMCSR_CKS_RCLK8);
}
sh_cmt_write_cmcor(ch, 0xffffffff);
--
1.8.3.2
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 <[email protected]>
---
drivers/clocksource/sh_cmt.c | 192 +++++++++++++++++++++++++++----------------
1 file changed, 122 insertions(+), 70 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 7a65d64..3946c14 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -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;
@@ -59,49 +105,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));
@@ -124,47 +137,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,
@@ -173,7 +239,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 {
@@ -181,7 +247,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)));
@@ -228,7 +294,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 {
@@ -406,7 +472,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
@@ -725,10 +792,10 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index,
ch->irqaction.dev_id = ch;
ch->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING;
- 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);
@@ -802,28 +869,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) {
--
1.8.3.2
This prepares the driver for multi-channel support.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index cd3121d..7a65d64 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -63,7 +63,8 @@ struct sh_cmt_device {
void __iomem *mapbase;
struct clk *clk;
- struct sh_cmt_channel channel;
+ struct sh_cmt_channel *channels;
+ unsigned int num_channels;
unsigned long width; /* 16 or 32 bit version of hardware block */
unsigned long overflow_bit;
@@ -824,7 +825,15 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
cmt->clear_bits = ~0xc000;
}
- ret = sh_cmt_setup_channel(&cmt->channel, cfg->timer_bit, cmt);
+ cmt->channels = kzalloc(sizeof(*cmt->channels), GFP_KERNEL);
+ if (cmt->channels == NULL) {
+ ret = -ENOMEM;
+ goto err4;
+ }
+
+ cmt->num_channels = 1;
+
+ ret = sh_cmt_setup_channel(&cmt->channels[0], cfg->timer_bit, cmt);
if (ret < 0)
goto err4;
@@ -832,6 +841,7 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
return 0;
err4:
+ kfree(cmt->channels);
clk_unprepare(cmt->clk);
err3:
clk_put(cmt->clk);
--
1.8.3.2
One kzalloc a day keeps the bugs away.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 0796460..cd3121d 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -673,8 +673,6 @@ static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch,
{
struct clock_event_device *ced = &ch->ced;
- memset(ced, 0, sizeof(*ced));
-
ced->name = name;
ced->features = CLOCK_EVT_FEAT_PERIODIC;
ced->features |= CLOCK_EVT_FEAT_ONESHOT;
@@ -710,7 +708,6 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index,
int irq;
int ret;
- memset(ch, 0, sizeof(*ch));
ch->cmt = cmt;
ch->base = cmt->mapbase_ch;
ch->index = index;
@@ -760,7 +757,6 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
int ret;
ret = -ENXIO;
- memset(cmt, 0, sizeof(*cmt));
cmt->pdev = pdev;
if (!cfg) {
@@ -863,7 +859,7 @@ static int sh_cmt_probe(struct platform_device *pdev)
goto out;
}
- cmt = kmalloc(sizeof(*cmt), GFP_KERNEL);
+ cmt = kzalloc(sizeof(*cmt), GFP_KERNEL);
if (cmt == NULL) {
dev_err(&pdev->dev, "failed to allocate driver data\n");
return -ENOMEM;
--
1.8.3.2
Use the index when printing messages to identify the channel.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 28 ++++++++++++++++++----------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index ec10cf4..0796460 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -39,6 +39,7 @@ struct sh_cmt_device;
struct sh_cmt_channel {
struct sh_cmt_device *cmt;
+ unsigned int index;
void __iomem *base;
struct irqaction irqaction;
@@ -217,7 +218,8 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch, unsigned long *rate)
/* enable clock */
ret = clk_enable(ch->cmt->clk);
if (ret) {
- dev_err(&ch->cmt->pdev->dev, "cannot enable clock\n");
+ dev_err(&ch->cmt->pdev->dev, "ch%u: cannot enable clock\n",
+ ch->index);
goto err0;
}
@@ -254,7 +256,8 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch, unsigned long *rate)
}
if (sh_cmt_read_cmcnt(ch)) {
- dev_err(&ch->cmt->pdev->dev, "cannot clear CMCNT\n");
+ dev_err(&ch->cmt->pdev->dev, "ch%u: cannot clear CMCNT\n",
+ ch->index);
ret = -ETIMEDOUT;
goto err1;
}
@@ -372,7 +375,8 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_channel *ch,
delay = 1;
if (!delay)
- dev_warn(&ch->cmt->pdev->dev, "too long delay\n");
+ dev_warn(&ch->cmt->pdev->dev, "ch%u: too long delay\n",
+ ch->index);
} while (delay);
}
@@ -380,7 +384,8 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_channel *ch,
static void __sh_cmt_set_next(struct sh_cmt_channel *ch, unsigned long delta)
{
if (delta > ch->max_match_value)
- dev_warn(&ch->cmt->pdev->dev, "delta out of range\n");
+ dev_warn(&ch->cmt->pdev->dev, "ch%u: delta out of range\n",
+ ch->index);
ch->next_match_value = delta;
sh_cmt_clock_event_program_verify(ch, 0);
@@ -566,7 +571,8 @@ static int sh_cmt_register_clocksource(struct sh_cmt_channel *ch,
cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
- dev_info(&ch->cmt->pdev->dev, "used as clock source\n");
+ dev_info(&ch->cmt->pdev->dev, "ch%u: used as clock source\n",
+ ch->index);
/* Register with dummy 1 Hz value, gets updated in ->enable() */
clocksource_register_hz(cs, 1);
@@ -615,12 +621,12 @@ static void sh_cmt_clock_event_mode(enum clock_event_mode mode,
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
dev_info(&ch->cmt->pdev->dev,
- "used for periodic clock events\n");
+ "ch%u: used for periodic clock events\n", ch->index);
sh_cmt_clock_event_start(ch, 1);
break;
case CLOCK_EVT_MODE_ONESHOT:
dev_info(&ch->cmt->pdev->dev,
- "used for oneshot clock events\n");
+ "ch%u: used for oneshot clock events\n", ch->index);
sh_cmt_clock_event_start(ch, 0);
break;
case CLOCK_EVT_MODE_SHUTDOWN:
@@ -679,7 +685,8 @@ static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch,
ced->suspend = sh_cmt_clock_event_suspend;
ced->resume = sh_cmt_clock_event_resume;
- dev_info(&ch->cmt->pdev->dev, "used for clock events\n");
+ dev_info(&ch->cmt->pdev->dev, "ch%u: used for clock events\n",
+ ch->index);
clockevents_register_device(ced);
}
@@ -696,7 +703,7 @@ static int sh_cmt_register(struct sh_cmt_channel *ch, char *name,
return 0;
}
-static int sh_cmt_setup_channel(struct sh_cmt_channel *ch,
+static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index,
struct sh_cmt_device *cmt)
{
struct sh_timer_config *cfg = cmt->pdev->dev.platform_data;
@@ -706,6 +713,7 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch,
memset(ch, 0, sizeof(*ch));
ch->cmt = cmt;
ch->base = cmt->mapbase_ch;
+ ch->index = index;
/* Request irq using setup_irq() (too early for request_irq()). */
irq = platform_get_irq(cmt->pdev, 0);
@@ -820,7 +828,7 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
cmt->clear_bits = ~0xc000;
}
- ret = sh_cmt_setup_channel(&cmt->channel, cmt);
+ ret = sh_cmt_setup_channel(&cmt->channel, cfg->timer_bit, cmt);
if (ret < 0)
goto err4;
--
1.8.3.2
The channel memory base is channel-specific, add it to the channel
structure in preparation for support of multiple channels per device.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 1ac84ff..ec10cf4 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -40,6 +40,7 @@ struct sh_cmt_device;
struct sh_cmt_channel {
struct sh_cmt_device *cmt;
+ void __iomem *base;
struct irqaction irqaction;
unsigned long flags;
@@ -132,12 +133,12 @@ static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch)
static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch)
{
- return ch->cmt->read_control(ch->cmt->mapbase_ch, CMCSR);
+ return ch->cmt->read_control(ch->base, CMCSR);
}
static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch)
{
- return ch->cmt->read_count(ch->cmt->mapbase_ch, CMCNT);
+ return ch->cmt->read_count(ch->base, CMCNT);
}
static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch,
@@ -149,19 +150,19 @@ static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch,
static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch,
unsigned long value)
{
- ch->cmt->write_control(ch->cmt->mapbase_ch, CMCSR, value);
+ ch->cmt->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->cmt->mapbase_ch, CMCNT, value);
+ ch->cmt->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->cmt->mapbase_ch, CMCOR, value);
+ ch->cmt->write_count(ch->base, CMCOR, value);
}
static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch,
@@ -704,6 +705,7 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch,
memset(ch, 0, sizeof(*ch));
ch->cmt = cmt;
+ ch->base = cmt->mapbase_ch;
/* Request irq using setup_irq() (too early for request_irq()). */
irq = platform_get_irq(cmt->pdev, 0);
--
1.8.3.2
The mapbase variable points to the mapped base address of the channel,
rename it to mapbase_sh. mapbase_str points to the mapped base address
of the CMT device, rename it to mapbase.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 3a3cc83..1ac84ff 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -57,8 +57,8 @@ struct sh_cmt_channel {
struct sh_cmt_device {
struct platform_device *pdev;
+ void __iomem *mapbase_ch;
void __iomem *mapbase;
- void __iomem *mapbase_str;
struct clk *clk;
struct sh_cmt_channel channel;
@@ -127,41 +127,41 @@ static void sh_cmt_write32(void __iomem *base, unsigned long offs,
static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch)
{
- return ch->cmt->read_control(ch->cmt->mapbase_str, 0);
+ return ch->cmt->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->cmt->mapbase, CMCSR);
+ return ch->cmt->read_control(ch->cmt->mapbase_ch, CMCSR);
}
static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch)
{
- return ch->cmt->read_count(ch->cmt->mapbase, CMCNT);
+ return ch->cmt->read_count(ch->cmt->mapbase_ch, CMCNT);
}
static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch,
unsigned long value)
{
- ch->cmt->write_control(ch->cmt->mapbase_str, 0, value);
+ ch->cmt->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->cmt->mapbase, CMCSR, value);
+ ch->cmt->write_control(ch->cmt->mapbase_ch, CMCSR, value);
}
static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch,
unsigned long value)
{
- ch->cmt->write_count(ch->cmt->mapbase, CMCNT, value);
+ ch->cmt->write_count(ch->cmt->mapbase_ch, CMCNT, value);
}
static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch,
unsigned long value)
{
- ch->cmt->write_count(ch->cmt->mapbase, CMCOR, value);
+ ch->cmt->write_count(ch->cmt->mapbase_ch, CMCOR, value);
}
static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch,
@@ -767,18 +767,18 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
/* optional resource for the shared timer start/stop register */
res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1);
- /* map memory, let mapbase point to our channel */
- cmt->mapbase = ioremap_nocache(res->start, resource_size(res));
- if (cmt->mapbase == NULL) {
+ /* map memory, let mapbase_ch point to our channel */
+ cmt->mapbase_ch = ioremap_nocache(res->start, resource_size(res));
+ if (cmt->mapbase_ch == NULL) {
dev_err(&cmt->pdev->dev, "failed to remap I/O memory\n");
goto err0;
}
/* map second resource for CMSTR */
- cmt->mapbase_str = ioremap_nocache(res2 ? res2->start :
- res->start - cfg->channel_offset,
- res2 ? resource_size(res2) : 2);
- if (cmt->mapbase_str == NULL) {
+ cmt->mapbase = ioremap_nocache(res2 ? res2->start :
+ res->start - cfg->channel_offset,
+ res2 ? resource_size(res2) : 2);
+ if (cmt->mapbase == NULL) {
dev_err(&cmt->pdev->dev, "failed to remap I/O second memory\n");
goto err1;
}
@@ -830,9 +830,9 @@ err4:
err3:
clk_put(cmt->clk);
err2:
- iounmap(cmt->mapbase_str);
-err1:
iounmap(cmt->mapbase);
+err1:
+ iounmap(cmt->mapbase_ch);
err0:
return ret;
}
--
1.8.3.2
Create a new sh_cmt_channel structure to hold the channel-specific
field in preparation for multiple channels per device support.
Signed-off-by: Laurent Pinchart <[email protected]>
---
drivers/clocksource/sh_cmt.c | 393 ++++++++++++++++++++++---------------------
1 file changed, 204 insertions(+), 189 deletions(-)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 0b1836a..12ccd11 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -35,15 +35,12 @@
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
-struct sh_cmt_priv {
- void __iomem *mapbase;
- void __iomem *mapbase_str;
- struct clk *clk;
- unsigned long width; /* 16 or 32 bit version of hardware block */
- unsigned long overflow_bit;
- unsigned long clear_bits;
+struct sh_cmt_priv;
+
+struct sh_cmt_channel {
+ struct sh_cmt_priv *cmt;
+
struct irqaction irqaction;
- struct platform_device *pdev;
unsigned long flags;
unsigned long match_value;
@@ -55,6 +52,20 @@ struct sh_cmt_priv {
struct clocksource cs;
unsigned long total_cycles;
bool cs_enabled;
+};
+
+struct sh_cmt_priv {
+ struct platform_device *pdev;
+
+ void __iomem *mapbase;
+ void __iomem *mapbase_str;
+ struct clk *clk;
+
+ struct sh_cmt_channel channel;
+
+ 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);
@@ -114,60 +125,60 @@ static void sh_cmt_write32(void __iomem *base, unsigned long offs,
#define CMCNT 1 /* channel register */
#define CMCOR 2 /* channel register */
-static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_priv *p)
+static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch)
{
- return p->read_control(p->mapbase_str, 0);
+ return ch->cmt->read_control(ch->cmt->mapbase_str, 0);
}
-static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_priv *p)
+static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch)
{
- return p->read_control(p->mapbase, CMCSR);
+ return ch->cmt->read_control(ch->cmt->mapbase, CMCSR);
}
-static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_priv *p)
+static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch)
{
- return p->read_count(p->mapbase, CMCNT);
+ return ch->cmt->read_count(ch->cmt->mapbase, CMCNT);
}
-static inline void sh_cmt_write_cmstr(struct sh_cmt_priv *p,
+static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch,
unsigned long value)
{
- p->write_control(p->mapbase_str, 0, value);
+ ch->cmt->write_control(ch->cmt->mapbase_str, 0, value);
}
-static inline void sh_cmt_write_cmcsr(struct sh_cmt_priv *p,
+static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch,
unsigned long value)
{
- p->write_control(p->mapbase, CMCSR, value);
+ ch->cmt->write_control(ch->cmt->mapbase, CMCSR, value);
}
-static inline void sh_cmt_write_cmcnt(struct sh_cmt_priv *p,
+static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch,
unsigned long value)
{
- p->write_count(p->mapbase, CMCNT, value);
+ ch->cmt->write_count(ch->cmt->mapbase, CMCNT, value);
}
-static inline void sh_cmt_write_cmcor(struct sh_cmt_priv *p,
+static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch,
unsigned long value)
{
- p->write_count(p->mapbase, CMCOR, value);
+ ch->cmt->write_count(ch->cmt->mapbase, CMCOR, value);
}
-static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p,
+static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch,
int *has_wrapped)
{
unsigned long v1, v2, v3;
int o1, o2;
- o1 = sh_cmt_read_cmcsr(p) & p->overflow_bit;
+ o1 = sh_cmt_read_cmcsr(ch) & ch->cmt->overflow_bit;
/* Make sure the timer value is stable. Stolen from acpi_pm.c */
do {
o2 = o1;
- v1 = sh_cmt_read_cmcnt(p);
- v2 = sh_cmt_read_cmcnt(p);
- v3 = sh_cmt_read_cmcnt(p);
- o1 = sh_cmt_read_cmcsr(p) & p->overflow_bit;
+ 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;
} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
|| (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
@@ -177,52 +188,52 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p,
static DEFINE_RAW_SPINLOCK(sh_cmt_lock);
-static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start)
+static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start)
{
- struct sh_timer_config *cfg = p->pdev->dev.platform_data;
+ struct sh_timer_config *cfg = ch->cmt->pdev->dev.platform_data;
unsigned long flags, value;
/* start stop register shared by multiple timer channels */
raw_spin_lock_irqsave(&sh_cmt_lock, flags);
- value = sh_cmt_read_cmstr(p);
+ value = sh_cmt_read_cmstr(ch);
if (start)
value |= 1 << cfg->timer_bit;
else
value &= ~(1 << cfg->timer_bit);
- sh_cmt_write_cmstr(p, value);
+ sh_cmt_write_cmstr(ch, value);
raw_spin_unlock_irqrestore(&sh_cmt_lock, flags);
}
-static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)
+static int sh_cmt_enable(struct sh_cmt_channel *ch, unsigned long *rate)
{
int k, ret;
- pm_runtime_get_sync(&p->pdev->dev);
- dev_pm_syscore_device(&p->pdev->dev, true);
+ pm_runtime_get_sync(&ch->cmt->pdev->dev);
+ dev_pm_syscore_device(&ch->cmt->pdev->dev, true);
/* enable clock */
- ret = clk_enable(p->clk);
+ ret = clk_enable(ch->cmt->clk);
if (ret) {
- dev_err(&p->pdev->dev, "cannot enable clock\n");
+ dev_err(&ch->cmt->pdev->dev, "cannot enable clock\n");
goto err0;
}
/* make sure channel is disabled */
- sh_cmt_start_stop_ch(p, 0);
+ sh_cmt_start_stop_ch(ch, 0);
/* configure channel, periodic mode and maximum timeout */
- if (p->width == 16) {
- *rate = clk_get_rate(p->clk) / 512;
- sh_cmt_write_cmcsr(p, 0x43);
+ if (ch->cmt->width == 16) {
+ *rate = clk_get_rate(ch->cmt->clk) / 512;
+ sh_cmt_write_cmcsr(ch, 0x43);
} else {
- *rate = clk_get_rate(p->clk) / 8;
- sh_cmt_write_cmcsr(p, 0x01a4);
+ *rate = clk_get_rate(ch->cmt->clk) / 8;
+ sh_cmt_write_cmcsr(ch, 0x01a4);
}
- sh_cmt_write_cmcor(p, 0xffffffff);
- sh_cmt_write_cmcnt(p, 0);
+ sh_cmt_write_cmcor(ch, 0xffffffff);
+ sh_cmt_write_cmcnt(ch, 0);
/*
* According to the sh73a0 user's manual, as CMCNT can be operated
@@ -236,41 +247,41 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)
* take RCLKx2 at maximum.
*/
for (k = 0; k < 100; k++) {
- if (!sh_cmt_read_cmcnt(p))
+ if (!sh_cmt_read_cmcnt(ch))
break;
udelay(1);
}
- if (sh_cmt_read_cmcnt(p)) {
- dev_err(&p->pdev->dev, "cannot clear CMCNT\n");
+ if (sh_cmt_read_cmcnt(ch)) {
+ dev_err(&ch->cmt->pdev->dev, "cannot clear CMCNT\n");
ret = -ETIMEDOUT;
goto err1;
}
/* enable channel */
- sh_cmt_start_stop_ch(p, 1);
+ sh_cmt_start_stop_ch(ch, 1);
return 0;
err1:
/* stop clock */
- clk_disable(p->clk);
+ clk_disable(ch->cmt->clk);
err0:
return ret;
}
-static void sh_cmt_disable(struct sh_cmt_priv *p)
+static void sh_cmt_disable(struct sh_cmt_channel *ch)
{
/* disable channel */
- sh_cmt_start_stop_ch(p, 0);
+ sh_cmt_start_stop_ch(ch, 0);
/* disable interrupts in CMT block */
- sh_cmt_write_cmcsr(p, 0);
+ sh_cmt_write_cmcsr(ch, 0);
/* stop clock */
- clk_disable(p->clk);
+ clk_disable(ch->cmt->clk);
- dev_pm_syscore_device(&p->pdev->dev, false);
- pm_runtime_put(&p->pdev->dev);
+ dev_pm_syscore_device(&ch->cmt->pdev->dev, false);
+ pm_runtime_put(&ch->cmt->pdev->dev);
}
/* private flags */
@@ -280,24 +291,24 @@ static void sh_cmt_disable(struct sh_cmt_priv *p)
#define FLAG_SKIPEVENT (1 << 3)
#define FLAG_IRQCONTEXT (1 << 4)
-static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p,
+static void sh_cmt_clock_event_program_verify(struct sh_cmt_channel *ch,
int absolute)
{
unsigned long new_match;
- unsigned long value = p->next_match_value;
+ unsigned long value = ch->next_match_value;
unsigned long delay = 0;
unsigned long now = 0;
int has_wrapped;
- now = sh_cmt_get_counter(p, &has_wrapped);
- p->flags |= FLAG_REPROGRAM; /* force reprogram */
+ now = sh_cmt_get_counter(ch, &has_wrapped);
+ ch->flags |= FLAG_REPROGRAM; /* force reprogram */
if (has_wrapped) {
/* we're competing with the interrupt handler.
* -> let the interrupt handler reprogram the timer.
* -> interrupt number two handles the event.
*/
- p->flags |= FLAG_SKIPEVENT;
+ ch->flags |= FLAG_SKIPEVENT;
return;
}
@@ -309,20 +320,20 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p,
* but don't save the new match value yet.
*/
new_match = now + value + delay;
- if (new_match > p->max_match_value)
- new_match = p->max_match_value;
+ if (new_match > ch->max_match_value)
+ new_match = ch->max_match_value;
- sh_cmt_write_cmcor(p, new_match);
+ sh_cmt_write_cmcor(ch, new_match);
- now = sh_cmt_get_counter(p, &has_wrapped);
- if (has_wrapped && (new_match > p->match_value)) {
+ now = sh_cmt_get_counter(ch, &has_wrapped);
+ if (has_wrapped && (new_match > ch->match_value)) {
/* we are changing to a greater match value,
* so this wrap must be caused by the counter
* matching the old value.
* -> first interrupt reprograms the timer.
* -> interrupt number two handles the event.
*/
- p->flags |= FLAG_SKIPEVENT;
+ ch->flags |= FLAG_SKIPEVENT;
break;
}
@@ -333,7 +344,7 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p,
* -> save programmed match value.
* -> let isr handle the event.
*/
- p->match_value = new_match;
+ ch->match_value = new_match;
break;
}
@@ -344,7 +355,7 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p,
* -> save programmed match value.
* -> let isr handle the event.
*/
- p->match_value = new_match;
+ ch->match_value = new_match;
break;
}
@@ -360,138 +371,138 @@ static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p,
delay = 1;
if (!delay)
- dev_warn(&p->pdev->dev, "too long delay\n");
+ dev_warn(&ch->cmt->pdev->dev, "too long delay\n");
} while (delay);
}
-static void __sh_cmt_set_next(struct sh_cmt_priv *p, unsigned long delta)
+static void __sh_cmt_set_next(struct sh_cmt_channel *ch, unsigned long delta)
{
- if (delta > p->max_match_value)
- dev_warn(&p->pdev->dev, "delta out of range\n");
+ if (delta > ch->max_match_value)
+ dev_warn(&ch->cmt->pdev->dev, "delta out of range\n");
- p->next_match_value = delta;
- sh_cmt_clock_event_program_verify(p, 0);
+ ch->next_match_value = delta;
+ sh_cmt_clock_event_program_verify(ch, 0);
}
-static void sh_cmt_set_next(struct sh_cmt_priv *p, unsigned long delta)
+static void sh_cmt_set_next(struct sh_cmt_channel *ch, unsigned long delta)
{
unsigned long flags;
- raw_spin_lock_irqsave(&p->lock, flags);
- __sh_cmt_set_next(p, delta);
- raw_spin_unlock_irqrestore(&p->lock, flags);
+ raw_spin_lock_irqsave(&ch->lock, flags);
+ __sh_cmt_set_next(ch, delta);
+ raw_spin_unlock_irqrestore(&ch->lock, flags);
}
static irqreturn_t sh_cmt_interrupt(int irq, void *dev_id)
{
- struct sh_cmt_priv *p = dev_id;
+ struct sh_cmt_channel *ch = dev_id;
/* clear flags */
- sh_cmt_write_cmcsr(p, sh_cmt_read_cmcsr(p) & p->clear_bits);
+ sh_cmt_write_cmcsr(ch, sh_cmt_read_cmcsr(ch) & ch->cmt->clear_bits);
/* update clock source counter to begin with if enabled
* the wrap flag should be cleared by the timer specific
* isr before we end up here.
*/
- if (p->flags & FLAG_CLOCKSOURCE)
- p->total_cycles += p->match_value + 1;
+ if (ch->flags & FLAG_CLOCKSOURCE)
+ ch->total_cycles += ch->match_value + 1;
- if (!(p->flags & FLAG_REPROGRAM))
- p->next_match_value = p->max_match_value;
+ if (!(ch->flags & FLAG_REPROGRAM))
+ ch->next_match_value = ch->max_match_value;
- p->flags |= FLAG_IRQCONTEXT;
+ ch->flags |= FLAG_IRQCONTEXT;
- if (p->flags & FLAG_CLOCKEVENT) {
- if (!(p->flags & FLAG_SKIPEVENT)) {
- if (p->ced.mode == CLOCK_EVT_MODE_ONESHOT) {
- p->next_match_value = p->max_match_value;
- p->flags |= FLAG_REPROGRAM;
+ if (ch->flags & FLAG_CLOCKEVENT) {
+ if (!(ch->flags & FLAG_SKIPEVENT)) {
+ if (ch->ced.mode == CLOCK_EVT_MODE_ONESHOT) {
+ ch->next_match_value = ch->max_match_value;
+ ch->flags |= FLAG_REPROGRAM;
}
- p->ced.event_handler(&p->ced);
+ ch->ced.event_handler(&ch->ced);
}
}
- p->flags &= ~FLAG_SKIPEVENT;
+ ch->flags &= ~FLAG_SKIPEVENT;
- if (p->flags & FLAG_REPROGRAM) {
- p->flags &= ~FLAG_REPROGRAM;
- sh_cmt_clock_event_program_verify(p, 1);
+ if (ch->flags & FLAG_REPROGRAM) {
+ ch->flags &= ~FLAG_REPROGRAM;
+ sh_cmt_clock_event_program_verify(ch, 1);
- if (p->flags & FLAG_CLOCKEVENT)
- if ((p->ced.mode == CLOCK_EVT_MODE_SHUTDOWN)
- || (p->match_value == p->next_match_value))
- p->flags &= ~FLAG_REPROGRAM;
+ if (ch->flags & FLAG_CLOCKEVENT)
+ if ((ch->ced.mode == CLOCK_EVT_MODE_SHUTDOWN)
+ || (ch->match_value == ch->next_match_value))
+ ch->flags &= ~FLAG_REPROGRAM;
}
- p->flags &= ~FLAG_IRQCONTEXT;
+ ch->flags &= ~FLAG_IRQCONTEXT;
return IRQ_HANDLED;
}
-static int sh_cmt_start(struct sh_cmt_priv *p, unsigned long flag)
+static int sh_cmt_start(struct sh_cmt_channel *ch, unsigned long flag)
{
int ret = 0;
unsigned long flags;
- raw_spin_lock_irqsave(&p->lock, flags);
+ raw_spin_lock_irqsave(&ch->lock, flags);
- if (!(p->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
- ret = sh_cmt_enable(p, &p->rate);
+ if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
+ ret = sh_cmt_enable(ch, &ch->rate);
if (ret)
goto out;
- p->flags |= flag;
+ ch->flags |= flag;
/* setup timeout if no clockevent */
- if ((flag == FLAG_CLOCKSOURCE) && (!(p->flags & FLAG_CLOCKEVENT)))
- __sh_cmt_set_next(p, p->max_match_value);
+ if ((flag == FLAG_CLOCKSOURCE) && (!(ch->flags & FLAG_CLOCKEVENT)))
+ __sh_cmt_set_next(ch, ch->max_match_value);
out:
- raw_spin_unlock_irqrestore(&p->lock, flags);
+ raw_spin_unlock_irqrestore(&ch->lock, flags);
return ret;
}
-static void sh_cmt_stop(struct sh_cmt_priv *p, unsigned long flag)
+static void sh_cmt_stop(struct sh_cmt_channel *ch, unsigned long flag)
{
unsigned long flags;
unsigned long f;
- raw_spin_lock_irqsave(&p->lock, flags);
+ raw_spin_lock_irqsave(&ch->lock, flags);
- f = p->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE);
- p->flags &= ~flag;
+ f = ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE);
+ ch->flags &= ~flag;
- if (f && !(p->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
- sh_cmt_disable(p);
+ if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
+ sh_cmt_disable(ch);
/* adjust the timeout to maximum if only clocksource left */
- if ((flag == FLAG_CLOCKEVENT) && (p->flags & FLAG_CLOCKSOURCE))
- __sh_cmt_set_next(p, p->max_match_value);
+ if ((flag == FLAG_CLOCKEVENT) && (ch->flags & FLAG_CLOCKSOURCE))
+ __sh_cmt_set_next(ch, ch->max_match_value);
- raw_spin_unlock_irqrestore(&p->lock, flags);
+ raw_spin_unlock_irqrestore(&ch->lock, flags);
}
-static struct sh_cmt_priv *cs_to_sh_cmt(struct clocksource *cs)
+static struct sh_cmt_channel *cs_to_sh_cmt(struct clocksource *cs)
{
- return container_of(cs, struct sh_cmt_priv, cs);
+ return container_of(cs, struct sh_cmt_channel, cs);
}
static cycle_t sh_cmt_clocksource_read(struct clocksource *cs)
{
- struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
+ struct sh_cmt_channel *ch = cs_to_sh_cmt(cs);
unsigned long flags, raw;
unsigned long value;
int has_wrapped;
- raw_spin_lock_irqsave(&p->lock, flags);
- value = p->total_cycles;
- raw = sh_cmt_get_counter(p, &has_wrapped);
+ raw_spin_lock_irqsave(&ch->lock, flags);
+ value = ch->total_cycles;
+ raw = sh_cmt_get_counter(ch, &has_wrapped);
if (unlikely(has_wrapped))
- raw += p->match_value + 1;
- raw_spin_unlock_irqrestore(&p->lock, flags);
+ raw += ch->match_value + 1;
+ raw_spin_unlock_irqrestore(&ch->lock, flags);
return value + raw;
}
@@ -499,50 +510,50 @@ static cycle_t sh_cmt_clocksource_read(struct clocksource *cs)
static int sh_cmt_clocksource_enable(struct clocksource *cs)
{
int ret;
- struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
+ struct sh_cmt_channel *ch = cs_to_sh_cmt(cs);
- WARN_ON(p->cs_enabled);
+ WARN_ON(ch->cs_enabled);
- p->total_cycles = 0;
+ ch->total_cycles = 0;
- ret = sh_cmt_start(p, FLAG_CLOCKSOURCE);
+ ret = sh_cmt_start(ch, FLAG_CLOCKSOURCE);
if (!ret) {
- __clocksource_updatefreq_hz(cs, p->rate);
- p->cs_enabled = true;
+ __clocksource_updatefreq_hz(cs, ch->rate);
+ ch->cs_enabled = true;
}
return ret;
}
static void sh_cmt_clocksource_disable(struct clocksource *cs)
{
- struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
+ struct sh_cmt_channel *ch = cs_to_sh_cmt(cs);
- WARN_ON(!p->cs_enabled);
+ WARN_ON(!ch->cs_enabled);
- sh_cmt_stop(p, FLAG_CLOCKSOURCE);
- p->cs_enabled = false;
+ sh_cmt_stop(ch, FLAG_CLOCKSOURCE);
+ ch->cs_enabled = false;
}
static void sh_cmt_clocksource_suspend(struct clocksource *cs)
{
- struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
+ struct sh_cmt_channel *ch = cs_to_sh_cmt(cs);
- sh_cmt_stop(p, FLAG_CLOCKSOURCE);
- pm_genpd_syscore_poweroff(&p->pdev->dev);
+ sh_cmt_stop(ch, FLAG_CLOCKSOURCE);
+ pm_genpd_syscore_poweroff(&ch->cmt->pdev->dev);
}
static void sh_cmt_clocksource_resume(struct clocksource *cs)
{
- struct sh_cmt_priv *p = cs_to_sh_cmt(cs);
+ struct sh_cmt_channel *ch = cs_to_sh_cmt(cs);
- pm_genpd_syscore_poweron(&p->pdev->dev);
- sh_cmt_start(p, FLAG_CLOCKSOURCE);
+ pm_genpd_syscore_poweron(&ch->cmt->pdev->dev);
+ sh_cmt_start(ch, FLAG_CLOCKSOURCE);
}
-static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
+static int sh_cmt_register_clocksource(struct sh_cmt_channel *ch,
char *name, unsigned long rating)
{
- struct clocksource *cs = &p->cs;
+ struct clocksource *cs = &ch->cs;
cs->name = name;
cs->rating = rating;
@@ -554,47 +565,47 @@ static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
- dev_info(&p->pdev->dev, "used as clock source\n");
+ dev_info(&ch->cmt->pdev->dev, "used as clock source\n");
/* Register with dummy 1 Hz value, gets updated in ->enable() */
clocksource_register_hz(cs, 1);
return 0;
}
-static struct sh_cmt_priv *ced_to_sh_cmt(struct clock_event_device *ced)
+static struct sh_cmt_channel *ced_to_sh_cmt(struct clock_event_device *ced)
{
- return container_of(ced, struct sh_cmt_priv, ced);
+ return container_of(ced, struct sh_cmt_channel, ced);
}
-static void sh_cmt_clock_event_start(struct sh_cmt_priv *p, int periodic)
+static void sh_cmt_clock_event_start(struct sh_cmt_channel *ch, int periodic)
{
- struct clock_event_device *ced = &p->ced;
+ struct clock_event_device *ced = &ch->ced;
- sh_cmt_start(p, FLAG_CLOCKEVENT);
+ sh_cmt_start(ch, FLAG_CLOCKEVENT);
/* TODO: calculate good shift from rate and counter bit width */
ced->shift = 32;
- ced->mult = div_sc(p->rate, NSEC_PER_SEC, ced->shift);
- ced->max_delta_ns = clockevent_delta2ns(p->max_match_value, ced);
+ ced->mult = div_sc(ch->rate, NSEC_PER_SEC, ced->shift);
+ ced->max_delta_ns = clockevent_delta2ns(ch->max_match_value, ced);
ced->min_delta_ns = clockevent_delta2ns(0x1f, ced);
if (periodic)
- sh_cmt_set_next(p, ((p->rate + HZ/2) / HZ) - 1);
+ sh_cmt_set_next(ch, ((ch->rate + HZ/2) / HZ) - 1);
else
- sh_cmt_set_next(p, p->max_match_value);
+ sh_cmt_set_next(ch, ch->max_match_value);
}
static void sh_cmt_clock_event_mode(enum clock_event_mode mode,
struct clock_event_device *ced)
{
- struct sh_cmt_priv *p = ced_to_sh_cmt(ced);
+ struct sh_cmt_channel *ch = ced_to_sh_cmt(ced);
/* deal with old setting first */
switch (ced->mode) {
case CLOCK_EVT_MODE_PERIODIC:
case CLOCK_EVT_MODE_ONESHOT:
- sh_cmt_stop(p, FLAG_CLOCKEVENT);
+ sh_cmt_stop(ch, FLAG_CLOCKEVENT);
break;
default:
break;
@@ -602,16 +613,18 @@ static void sh_cmt_clock_event_mode(enum clock_event_mode mode,
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
- dev_info(&p->pdev->dev, "used for periodic clock events\n");
- sh_cmt_clock_event_start(p, 1);
+ dev_info(&ch->cmt->pdev->dev,
+ "used for periodic clock events\n");
+ sh_cmt_clock_event_start(ch, 1);
break;
case CLOCK_EVT_MODE_ONESHOT:
- dev_info(&p->pdev->dev, "used for oneshot clock events\n");
- sh_cmt_clock_event_start(p, 0);
+ dev_info(&ch->cmt->pdev->dev,
+ "used for oneshot clock events\n");
+ sh_cmt_clock_event_start(ch, 0);
break;
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_UNUSED:
- sh_cmt_stop(p, FLAG_CLOCKEVENT);
+ sh_cmt_stop(ch, FLAG_CLOCKEVENT);
break;
default:
break;
@@ -621,37 +634,37 @@ static void sh_cmt_clock_event_mode(enum clock_event_mode mode,
static int sh_cmt_clock_event_next(unsigned long delta,
struct clock_event_device *ced)
{
- struct sh_cmt_priv *p = ced_to_sh_cmt(ced);
+ struct sh_cmt_channel *ch = ced_to_sh_cmt(ced);
BUG_ON(ced->mode != CLOCK_EVT_MODE_ONESHOT);
- if (likely(p->flags & FLAG_IRQCONTEXT))
- p->next_match_value = delta - 1;
+ if (likely(ch->flags & FLAG_IRQCONTEXT))
+ ch->next_match_value = delta - 1;
else
- sh_cmt_set_next(p, delta - 1);
+ sh_cmt_set_next(ch, delta - 1);
return 0;
}
static void sh_cmt_clock_event_suspend(struct clock_event_device *ced)
{
- struct sh_cmt_priv *p = ced_to_sh_cmt(ced);
+ struct sh_cmt_channel *ch = ced_to_sh_cmt(ced);
- pm_genpd_syscore_poweroff(&p->pdev->dev);
- clk_unprepare(p->clk);
+ pm_genpd_syscore_poweroff(&ch->cmt->pdev->dev);
+ clk_unprepare(ch->cmt->clk);
}
static void sh_cmt_clock_event_resume(struct clock_event_device *ced)
{
- struct sh_cmt_priv *p = ced_to_sh_cmt(ced);
+ struct sh_cmt_channel *ch = ced_to_sh_cmt(ced);
- clk_prepare(p->clk);
- pm_genpd_syscore_poweron(&p->pdev->dev);
+ clk_prepare(ch->cmt->clk);
+ pm_genpd_syscore_poweron(&ch->cmt->pdev->dev);
}
-static void sh_cmt_register_clockevent(struct sh_cmt_priv *p,
+static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch,
char *name, unsigned long rating)
{
- struct clock_event_device *ced = &p->ced;
+ struct clock_event_device *ced = &ch->ced;
memset(ced, 0, sizeof(*ced));
@@ -665,19 +678,19 @@ static void sh_cmt_register_clockevent(struct sh_cmt_priv *p,
ced->suspend = sh_cmt_clock_event_suspend;
ced->resume = sh_cmt_clock_event_resume;
- dev_info(&p->pdev->dev, "used for clock events\n");
+ dev_info(&ch->cmt->pdev->dev, "used for clock events\n");
clockevents_register_device(ced);
}
-static int sh_cmt_register(struct sh_cmt_priv *p, char *name,
+static int sh_cmt_register(struct sh_cmt_channel *ch, char *name,
unsigned long clockevent_rating,
unsigned long clocksource_rating)
{
if (clockevent_rating)
- sh_cmt_register_clockevent(p, name, clockevent_rating);
+ sh_cmt_register_clockevent(ch, name, clockevent_rating);
if (clocksource_rating)
- sh_cmt_register_clocksource(p, name, clocksource_rating);
+ sh_cmt_register_clocksource(ch, name, clocksource_rating);
return 0;
}
@@ -685,6 +698,7 @@ static int sh_cmt_register(struct sh_cmt_priv *p, char *name,
static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
{
struct sh_timer_config *cfg = pdev->dev.platform_data;
+ struct sh_cmt_channel *ch = &p->channel;
struct resource *res, *res2;
int irq, ret;
ret = -ENXIO;
@@ -729,10 +743,10 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
}
/* request irq using setup_irq() (too early for request_irq()) */
- p->irqaction.name = dev_name(&p->pdev->dev);
- p->irqaction.handler = sh_cmt_interrupt;
- p->irqaction.dev_id = p;
- p->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING;
+ ch->irqaction.name = dev_name(&p->pdev->dev);
+ ch->irqaction.handler = sh_cmt_interrupt;
+ ch->irqaction.dev_id = ch;
+ ch->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING;
/* get hold of clock */
p->clk = clk_get(&p->pdev->dev, "cmt_fck");
@@ -769,24 +783,25 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
p->clear_bits = ~0xc000;
}
- if (p->width == (sizeof(p->max_match_value) * 8))
- p->max_match_value = ~0;
+ if (p->width == (sizeof(ch->max_match_value) * 8))
+ ch->max_match_value = ~0;
else
- p->max_match_value = (1 << p->width) - 1;
+ ch->max_match_value = (1 << p->width) - 1;
- p->match_value = p->max_match_value;
- raw_spin_lock_init(&p->lock);
+ ch->cmt = p;
+ ch->match_value = ch->max_match_value;
+ raw_spin_lock_init(&ch->lock);
- ret = sh_cmt_register(p, (char *)dev_name(&p->pdev->dev),
+ ret = sh_cmt_register(ch, (char *)dev_name(&p->pdev->dev),
cfg->clockevent_rating,
cfg->clocksource_rating);
if (ret) {
dev_err(&p->pdev->dev, "registration failed\n");
goto err4;
}
- p->cs_enabled = false;
+ ch->cs_enabled = false;
- ret = setup_irq(irq, &p->irqaction);
+ ret = setup_irq(irq, &ch->irqaction);
if (ret) {
dev_err(&p->pdev->dev, "failed to request irq %d\n", irq);
goto err4;
--
1.8.3.2
On Fri, Feb 14, 2014 at 2:00 AM, Laurent Pinchart
<[email protected]> wrote:
> +++ b/Documentation/devicetree/bindings/timer/renesas,cmt.txt
> @@ -0,0 +1,75 @@
> +* Renesas R-Car Compare Match Timer (CMT)
> +
> +The CMT is a multi-channel 16/32/48-bit timer/counter with configurable clock
16-bit is mentioned here ...
> +inputs and programmable compare match.
> +
> +Channels share hadware resources but their counter and compare match value are
hardware
> +independent. A particular CMT instance can implement only a subset of the
> +channels supported by the CMT model. Channels indices start from 0 and are
Channel indices
> +consecutive.
> +
> +Required Properties:
> +
> + - compatible: must contain one of the following.
... why not add "renesas,cmt-16" here (and one extra line in the actual driver),
while you're at it?
> + - "renesas,cmt-32" for the 32-bit CMT
> + (CMT0 on sh7372, sh73a0 and r8a7740)
> + - "renesas,cmt-32-fast" for the 32-bit CMT with fast clock support
> + (CMT[234] on sh7372, sh73a0 and r8a7740)
> + - "renasas,cmt-48" for the 48-bit CMT
> + (CMT1 on sh7372, sh73a0 and r8a7740)
> + - "renesas,cmt-48-gen2" for the second generation 48-bit CMT
> + (CMT[01] on r8a73a4, r8a7790 and r8a7791)
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
On Fri, Feb 14, 2014 at 01:00:01AM +0000, Laurent Pinchart wrote:
> Cc: [email protected]
> Signed-off-by: Laurent Pinchart <[email protected]>
> ---
> .../devicetree/bindings/timer/renesas,cmt.txt | 75 +++++++++++++++
> drivers/clocksource/sh_cmt.c | 104 +++++++++++++++++----
> 2 files changed, 160 insertions(+), 19 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/timer/renesas,cmt.txt
>
> diff --git a/Documentation/devicetree/bindings/timer/renesas,cmt.txt b/Documentation/devicetree/bindings/timer/renesas,cmt.txt
> new file mode 100644
> index 0000000..28d4ab5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/timer/renesas,cmt.txt
> @@ -0,0 +1,75 @@
> +* Renesas R-Car Compare Match Timer (CMT)
> +
> +The CMT is a multi-channel 16/32/48-bit timer/counter with configurable clock
> +inputs and programmable compare match.
> +
> +Channels share hadware resources but their counter and compare match value are
> +independent. A particular CMT instance can implement only a subset of the
> +channels supported by the CMT model. Channels indices start from 0 and are
> +consecutive.
> +
> +Required Properties:
> +
> + - compatible: must contain one of the following.
> + - "renesas,cmt-32" for the 32-bit CMT
> + (CMT0 on sh7372, sh73a0 and r8a7740)
> + - "renesas,cmt-32-fast" for the 32-bit CMT with fast clock support
> + (CMT[234] on sh7372, sh73a0 and r8a7740)
> + - "renasas,cmt-48" for the 48-bit CMT
> + (CMT1 on sh7372, sh73a0 and r8a7740)
> + - "renesas,cmt-48-gen2" for the second generation 48-bit CMT
> + (CMT[01] on r8a73a4, r8a7790 and r8a7791)
> +
> + - reg: base address and length of the registers block for the timer module.
> + - interrupt-parent, interrupts: interrupt-specifier for the timer, one per
> + channel.
It might make more sense to describe the interrupt on the channel
subnode. It makes it far clearer which channel has which interrupt.
> + - clocks: phandle and clock-specifier pair for the functional clock.
> + - clock-names: must be "fck".
It would be nice to define the list once:
- clocks: A list of phandle + clock-specifier pairs, one for each entry
in clock-names.
- clock-names: Should contain "fck" for the functional clock.
> +
> + - #address-cells: must be 1
> + - #size-cells: must be 0
> +
> + - renesas,channels-mask: integer bitmask of the channels implemented by the
> + timer instance.
This is implied by the presence of a subnode. Either remove this or the
subnodes.
> +
> +
> +Each channel is described by a sub-node named "channel@<idx>", where <idx> is
> +the channel index.
> +
> +Channels Required Properties:
> +
> + - reg: the channel index.
> +
> +Channels Optional Properties:
> +
> + - clock-source-rating: rating of the timer as a clock source device.
> + - clock-event-rating: rating of the timer as a clock event device.
This feels like a leak of Linux internals. Why do you need this?
> +
> +
> +Example: R8A7790 (R-Car H2) CMT0 node
> +
> + CMT0 on R8A7790 implements hardware channels 5 and 6 only and names
> + them channels 0 and 1 in the documentation.
> +
> + cmt0: timer@ffca0000 {
> + compatible = "renesas,cmt-48-gen2";
> + reg = <0 0xffca0000 0 0x1004>;
> + interrupt-parent = <&gic>;
> + interrupts = <0 142 IRQ_TYPE_LEVEL_HIGH>,
> + <0 142 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&mstp1_clks R8A7790_CLK_CMT0>;
> +
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + renesas,channels-mask = <0x60>;
> +
> + channel@0 {
> + reg = <0>;
> + clock-event-rating = <80>;
> + };
> + channel@0 {
> + reg = <0>;
> + clock-source-rating = <80>;
> + };
Aaargh. Use the _real_ channel IDs for the reg proeprties and get rid of
the mask. It's pointlessly confusing.
Thanks,
Mark
Hello.
On 14-02-2014 5:00, Laurent Pinchart wrote:
> Enable the CMT0 device and configure channel 0 as a clock event
> provider.
> Signed-off-by: Laurent Pinchart <[email protected]>
> diff --git a/arch/arm/mach-shmobile/include/mach/r8a7790.h b/arch/arm/mach-shmobile/include/mach/r8a7790.h
> index 0b95bab..62b31f3 100644
> --- a/arch/arm/mach-shmobile/include/mach/r8a7790.h
> +++ b/arch/arm/mach-shmobile/include/mach/r8a7790.h
> @@ -29,7 +29,6 @@ enum {
> };
>
> void r8a7790_add_standard_devices(void);
> -void r8a7790_add_dt_devices(void);
> void r8a7790_clock_init(void);
> void r8a7790_pinmux_init(void);
> void r8a7790_pm_init(void);
> diff --git a/arch/arm/mach-shmobile/setup-r8a7790.c b/arch/arm/mach-shmobile/setup-r8a7790.c
> index 3e5813f..462c81f 100644
> --- a/arch/arm/mach-shmobile/setup-r8a7790.c
> +++ b/arch/arm/mach-shmobile/setup-r8a7790.c
> @@ -294,11 +294,6 @@ static struct resource cmt0_resources[] = {
> &cmt##idx##_platform_data, \
> sizeof(struct sh_timer_config))
>
> -void __init r8a7790_add_dt_devices(void)
> -{
> - r8a7790_register_cmt(0);
> -}
> -
> void __init r8a7790_add_standard_devices(void)
> {
> r8a7790_register_scif(0);
> @@ -311,7 +306,7 @@ void __init r8a7790_add_standard_devices(void)
> r8a7790_register_scif(7);
> r8a7790_register_scif(8);
> r8a7790_register_scif(9);
> - r8a7790_add_dt_devices();
> + r8a7790_register_cmt(0);
> r8a7790_register_irqc(0);
> r8a7790_register_thermal();
> }
IMHO, these 2 files should be split into a separate patch.
WBR, Sergei
Hi Sergei,
Thank you for the review.
On Friday 14 February 2014 17:45:56 Sergei Shtylyov wrote:
> On 14-02-2014 5:00, Laurent Pinchart wrote:
> > Enable the CMT0 device and configure channel 0 as a clock event
> > provider.
> >
> > Signed-off-by: Laurent Pinchart
> > <[email protected]>
> >
> > diff --git a/arch/arm/mach-shmobile/include/mach/r8a7790.h
> > b/arch/arm/mach-shmobile/include/mach/r8a7790.h index 0b95bab..62b31f3
> > 100644
> > --- a/arch/arm/mach-shmobile/include/mach/r8a7790.h
> > +++ b/arch/arm/mach-shmobile/include/mach/r8a7790.h
> > @@ -29,7 +29,6 @@ enum {
> > };
> >
> > void r8a7790_add_standard_devices(void);
> > -void r8a7790_add_dt_devices(void);
> > void r8a7790_clock_init(void);
> > void r8a7790_pinmux_init(void);
> > void r8a7790_pm_init(void);
> > diff --git a/arch/arm/mach-shmobile/setup-r8a7790.c
> > b/arch/arm/mach-shmobile/setup-r8a7790.c index 3e5813f..462c81f 100644
> > --- a/arch/arm/mach-shmobile/setup-r8a7790.c
> > +++ b/arch/arm/mach-shmobile/setup-r8a7790.c
> > @@ -294,11 +294,6 @@ static struct resource cmt0_resources[] = {
> > &cmt##idx##_platform_data, \
> > sizeof(struct sh_timer_config))
> >
> > -void __init r8a7790_add_dt_devices(void)
> > -{
> > - r8a7790_register_cmt(0);
> > -}
> > -
> > void __init r8a7790_add_standard_devices(void)
> > {
> > r8a7790_register_scif(0);
> > @@ -311,7 +306,7 @@ void __init r8a7790_add_standard_devices(void)
> > r8a7790_register_scif(7);
> > r8a7790_register_scif(8);
> > r8a7790_register_scif(9);
> > - r8a7790_add_dt_devices();
> > + r8a7790_register_cmt(0);
> > r8a7790_register_irqc(0);
> > r8a7790_register_thermal();
> > }
>
> IMHO, these 2 files should be split into a separate patch.
That could easily be done, but why ?
--
Regards,
Laurent Pinchart
Hello.
On 14-02-2014 5:00, Laurent Pinchart wrote:
> Enable the CMT0 device and configure channel 0 as a clock event
> provider.
> Signed-off-by: Laurent Pinchart <[email protected]>
> ---
> arch/arm/boot/dts/r8a7791-koelsch.dts | 9 +++++++++
> arch/arm/mach-shmobile/board-koelsch-reference.c | 15 +--------------
> arch/arm/mach-shmobile/include/mach/r8a7791.h | 1 -
> arch/arm/mach-shmobile/setup-r8a7791.c | 7 +------
The latter 2 files are worth splitting into a separate patch as well...
> 4 files changed, 11 insertions(+), 21 deletions(-)
WBR, Sergei
On 14-02-2014 17:48, Laurent Pinchart wrote:
>>> Enable the CMT0 device and configure channel 0 as a clock event
>>> provider.
>>> Signed-off-by: Laurent Pinchart <[email protected]>
>>> diff --git a/arch/arm/mach-shmobile/include/mach/r8a7790.h
>>> b/arch/arm/mach-shmobile/include/mach/r8a7790.h index 0b95bab..62b31f3 100644
>>> --- a/arch/arm/mach-shmobile/include/mach/r8a7790.h
>>> +++ b/arch/arm/mach-shmobile/include/mach/r8a7790.h
>>> @@ -29,7 +29,6 @@ enum {
>>> };
>>>
>>> void r8a7790_add_standard_devices(void);
>>> -void r8a7790_add_dt_devices(void);
>>> void r8a7790_clock_init(void);
>>> void r8a7790_pinmux_init(void);
>>> void r8a7790_pm_init(void);
>>> diff --git a/arch/arm/mach-shmobile/setup-r8a7790.c
>>> b/arch/arm/mach-shmobile/setup-r8a7790.c index 3e5813f..462c81f 100644
>>> --- a/arch/arm/mach-shmobile/setup-r8a7790.c
>>> +++ b/arch/arm/mach-shmobile/setup-r8a7790.c
>>> @@ -294,11 +294,6 @@ static struct resource cmt0_resources[] = {
>>> &cmt##idx##_platform_data, \
>>> sizeof(struct sh_timer_config))
>>>
>>> -void __init r8a7790_add_dt_devices(void)
>>> -{
>>> - r8a7790_register_cmt(0);
>>> -}
>>> -
>>> void __init r8a7790_add_standard_devices(void)
>>> {
>>> r8a7790_register_scif(0);
>>> @@ -311,7 +306,7 @@ void __init r8a7790_add_standard_devices(void)
>>> r8a7790_register_scif(7);
>>> r8a7790_register_scif(8);
>>> r8a7790_register_scif(9);
>>> - r8a7790_add_dt_devices();
>>> + r8a7790_register_cmt(0);
>>> r8a7790_register_irqc(0);
>>> r8a7790_register_thermal();
>>> }
>> IMHO, these 2 files should be split into a separate patch.
> That could easily be done, but why ?
It does not seem necessary to combine these changes in one patch.
Remember, Simon has separate branches for boards and SoCs. So finally it's up
to him to decide on this...
WBR, Sergei
Hi Sergei,
On Friday 14 February 2014 18:13:44 Sergei Shtylyov wrote:
> On 14-02-2014 17:48, Laurent Pinchart wrote:
> >>> Enable the CMT0 device and configure channel 0 as a clock event
> >>> provider.
> >>>
> >>> Signed-off-by: Laurent Pinchart
> >>> <[email protected]>
> >>>
> >>> diff --git a/arch/arm/mach-shmobile/include/mach/r8a7790.h
> >>> b/arch/arm/mach-shmobile/include/mach/r8a7790.h index 0b95bab..62b31f3
> >>> 100644 --- a/arch/arm/mach-shmobile/include/mach/r8a7790.h
> >>> +++ b/arch/arm/mach-shmobile/include/mach/r8a7790.h
> >>> @@ -29,7 +29,6 @@ enum {
> >>> };
> >>>
> >>> void r8a7790_add_standard_devices(void);
> >>> -void r8a7790_add_dt_devices(void);
> >>> void r8a7790_clock_init(void);
> >>> void r8a7790_pinmux_init(void);
> >>> void r8a7790_pm_init(void);
> >>> diff --git a/arch/arm/mach-shmobile/setup-r8a7790.c
> >>> b/arch/arm/mach-shmobile/setup-r8a7790.c index 3e5813f..462c81f 100644
> >>> --- a/arch/arm/mach-shmobile/setup-r8a7790.c
> >>> +++ b/arch/arm/mach-shmobile/setup-r8a7790.c
> >>> @@ -294,11 +294,6 @@ static struct resource cmt0_resources[] = {
> >>> &cmt##idx##_platform_data, \
> >>> sizeof(struct sh_timer_config))
> >>>
> >>> -void __init r8a7790_add_dt_devices(void)
> >>> -{
> >>> - r8a7790_register_cmt(0);
> >>> -}
> >>> -
> >>> void __init r8a7790_add_standard_devices(void)
> >>> {
> >>> r8a7790_register_scif(0);
> >>> @@ -311,7 +306,7 @@ void __init r8a7790_add_standard_devices(void)
> >>> r8a7790_register_scif(7);
> >>> r8a7790_register_scif(8);
> >>> r8a7790_register_scif(9);
> >>> - r8a7790_add_dt_devices();
> >>> + r8a7790_register_cmt(0);
> >>> r8a7790_register_irqc(0);
> >>> r8a7790_register_thermal();
> >>> }
> >>>
> >> IMHO, these 2 files should be split into a separate patch.
> >
> > That could easily be done, but why ?
>
> It does not seem necessary to combine these changes in one patch. Remember,
> Simon has separate branches for boards and SoCs. So finally it's up to him
> to decide on this...
Right, but in this case I need to remove the CMT platform device registration
from r8a7790_add_dt_devices() at the same time as I enable it in DT, otherwise
we'll have two instances of the same device.
--
Regards,
Laurent Pinchart
Hi Geert,
Thank you for the review.
On Friday 14 February 2014 10:18:58 Geert Uytterhoeven wrote:
> On Fri, Feb 14, 2014 at 2:00 AM, Laurent Pinchart wrote:
> > +++ b/Documentation/devicetree/bindings/timer/renesas,cmt.txt
> > @@ -0,0 +1,75 @@
> > +* Renesas R-Car Compare Match Timer (CMT)
> > +
> > +The CMT is a multi-channel 16/32/48-bit timer/counter with configurable
> > clock
> 16-bit is mentioned here ...
>
> > +inputs and programmable compare match.
> > +
> > +Channels share hadware resources but their counter and compare match
> > value are
>
> hardware
>
> > +independent. A particular CMT instance can implement only a subset of the
> > +channels supported by the CMT model. Channels indices start from 0 and
> > are
>
> Channel indices
>
> > +consecutive.
> > +
> > +Required Properties:
> > +
> > + - compatible: must contain one of the following.
>
> ... why not add "renesas,cmt-16" here (and one extra line in the actual
> driver), while you're at it?
Because the 16-bit variant is only used on SuperH SoCs, so there's not much
point in adding DT bindings for it.
> > + - "renesas,cmt-32" for the 32-bit CMT
> > + (CMT0 on sh7372, sh73a0 and r8a7740)
> > + - "renesas,cmt-32-fast" for the 32-bit CMT with fast clock support
> > + (CMT[234] on sh7372, sh73a0 and r8a7740)
> > + - "renasas,cmt-48" for the 48-bit CMT
> > + (CMT1 on sh7372, sh73a0 and r8a7740)
> > + - "renesas,cmt-48-gen2" for the second generation 48-bit CMT
> > + (CMT[01] on r8a73a4, r8a7790 and r8a7791)
--
Regards,
Laurent Pinchart
Hello.
On 14-02-2014 18:22, Laurent Pinchart wrote:
>>>>> Enable the CMT0 device and configure channel 0 as a clock event
>>>>> provider.
>>>>> Signed-off-by: Laurent Pinchart <[email protected]>
>>>>> diff --git a/arch/arm/mach-shmobile/include/mach/r8a7790.h
>>>>> b/arch/arm/mach-shmobile/include/mach/r8a7790.h index 0b95bab..62b31f3
>>>>> 100644 --- a/arch/arm/mach-shmobile/include/mach/r8a7790.h
>>>>> +++ b/arch/arm/mach-shmobile/include/mach/r8a7790.h
>>>>> @@ -29,7 +29,6 @@ enum {
>>>>> };
>>>>>
>>>>> void r8a7790_add_standard_devices(void);
>>>>> -void r8a7790_add_dt_devices(void);
>>>>> void r8a7790_clock_init(void);
>>>>> void r8a7790_pinmux_init(void);
>>>>> void r8a7790_pm_init(void);
>>>>> diff --git a/arch/arm/mach-shmobile/setup-r8a7790.c
>>>>> b/arch/arm/mach-shmobile/setup-r8a7790.c index 3e5813f..462c81f 100644
>>>>> --- a/arch/arm/mach-shmobile/setup-r8a7790.c
>>>>> +++ b/arch/arm/mach-shmobile/setup-r8a7790.c
>>>>> @@ -294,11 +294,6 @@ static struct resource cmt0_resources[] = {
>>>>> &cmt##idx##_platform_data, \
>>>>> sizeof(struct sh_timer_config))
>>>>>
>>>>> -void __init r8a7790_add_dt_devices(void)
>>>>> -{
>>>>> - r8a7790_register_cmt(0);
>>>>> -}
>>>>> -
>>>>> void __init r8a7790_add_standard_devices(void)
>>>>> {
>>>>> r8a7790_register_scif(0);
>>>>> @@ -311,7 +306,7 @@ void __init r8a7790_add_standard_devices(void)
>>>>> r8a7790_register_scif(7);
>>>>> r8a7790_register_scif(8);
>>>>> r8a7790_register_scif(9);
>>>>> - r8a7790_add_dt_devices();
>>>>> + r8a7790_register_cmt(0);
>>>>> r8a7790_register_irqc(0);
>>>>> r8a7790_register_thermal();
>>>>> }
>>>>>
>>>> IMHO, these 2 files should be split into a separate patch.
>>>
>>> That could easily be done, but why ?
>>
>> It does not seem necessary to combine these changes in one patch. Remember,
>> Simon has separate branches for boards and SoCs. So finally it's up to him
>> to decide on this...
> Right, but in this case I need to remove the CMT platform device registration
> from r8a7790_add_dt_devices()
You're not removing anything in these 2 files, you're just replacing
"indirect" call to r8a7790_register_cmt(0) with direct.
WBR, Sergei
Hi Mark,
Thank you for the review.
On Friday 14 February 2014 10:58:22 Mark Rutland wrote:
> On Fri, Feb 14, 2014 at 01:00:01AM +0000, Laurent Pinchart wrote:
> > Cc: [email protected]
> > Signed-off-by: Laurent Pinchart
> > <[email protected]>
> > ---
> >
> > .../devicetree/bindings/timer/renesas,cmt.txt | 75 +++++++++++++++
> > drivers/clocksource/sh_cmt.c | 104 +++++++++++++---
> > 2 files changed, 160 insertions(+), 19 deletions(-)
> > create mode 100644
> > Documentation/devicetree/bindings/timer/renesas,cmt.txt
> >
> > diff --git a/Documentation/devicetree/bindings/timer/renesas,cmt.txt
> > b/Documentation/devicetree/bindings/timer/renesas,cmt.txt new file mode
> > 100644
> > index 0000000..28d4ab5
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/timer/renesas,cmt.txt
> > @@ -0,0 +1,75 @@
> > +* Renesas R-Car Compare Match Timer (CMT)
> > +
> > +The CMT is a multi-channel 16/32/48-bit timer/counter with configurable
> > clock
> > +inputs and programmable compare match.
> > +
> > +Channels share hadware resources but their counter and compare match
> > value are
> > +independent. A particular CMT instance can implement only a subset of the
> > +channels supported by the CMT model. Channels indices start from 0 and
> > are
> > +consecutive.
> > +
> > +Required Properties:
> > +
> > + - compatible: must contain one of the following.
> > + - "renesas,cmt-32" for the 32-bit CMT
> > + (CMT0 on sh7372, sh73a0 and r8a7740)
> > + - "renesas,cmt-32-fast" for the 32-bit CMT with fast clock support
> > + (CMT[234] on sh7372, sh73a0 and r8a7740)
> > + - "renasas,cmt-48" for the 48-bit CMT
> > + (CMT1 on sh7372, sh73a0 and r8a7740)
> > + - "renesas,cmt-48-gen2" for the second generation 48-bit CMT
> > + (CMT[01] on r8a73a4, r8a7790 and r8a7791)
> > +
> > + - reg: base address and length of the registers block for the timer
> > module.
> > + - interrupt-parent, interrupts: interrupt-specifier for the timer, one
> > per
> > + channel.
>
> It might make more sense to describe the interrupt on the channel
> subnode. It makes it far clearer which channel has which interrupt.
That's a good point. I'm relying on platform_get_irq() which won't support
that usage, but I can switch to of_irq_to_resource() instead.
> > + - clocks: phandle and clock-specifier pair for the functional clock.
> > + - clock-names: must be "fck".
>
> It would be nice to define the list once:
>
> - clocks: A list of phandle + clock-specifier pairs, one for each entry
> in clock-names.
> - clock-names: Should contain "fck" for the functional clock.
OK.
> > +
> > + - #address-cells: must be 1
> > + - #size-cells: must be 0
> > +
> > + - renesas,channels-mask: integer bitmask of the channels implemented by
> > the
> > + timer instance.
>
> This is implied by the presence of a subnode. Either remove this or the
> subnodes.
>
> > +
> > +
> > +Each channel is described by a sub-node named "channel@<idx>", where
> > <idx> is +the channel index.
> > +
> > +Channels Required Properties:
> > +
> > + - reg: the channel index.
> > +
> > +Channels Optional Properties:
> > +
> > + - clock-source-rating: rating of the timer as a clock source device.
> > + - clock-event-rating: rating of the timer as a clock event device.
>
> This feels like a leak of Linux internals. Why do you need this?
You're right, it is. The clock source and clock event ratings are currently
configured through platform data, I'll need to find a way to compute them in
the driver instead.
There's still one piece of Linux-specific data I need though, as I need to
specify for each channel whether to use it as a clock source device, a clock
event device, both of them or none. That's configuration information that
needs to be provided somehow.
> > +
> > +
> > +Example: R8A7790 (R-Car H2) CMT0 node
> > +
> > + CMT0 on R8A7790 implements hardware channels 5 and 6 only and names
> > + them channels 0 and 1 in the documentation.
> > +
> > + cmt0: timer@ffca0000 {
> > + compatible = "renesas,cmt-48-gen2";
> > + reg = <0 0xffca0000 0 0x1004>;
> > + interrupt-parent = <&gic>;
> > + interrupts = <0 142 IRQ_TYPE_LEVEL_HIGH>,
> > + <0 142 IRQ_TYPE_LEVEL_HIGH>;
> > + clocks = <&mstp1_clks R8A7790_CLK_CMT0>;
> > +
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + renesas,channels-mask = <0x60>;
> > +
> > + channel@0 {
> > + reg = <0>;
> > + clock-event-rating = <80>;
> > + };
> > + channel@0 {
> > + reg = <0>;
> > + clock-source-rating = <80>;
> > + };
>
> Aaargh. Use the _real_ channel IDs for the reg proeprties and get rid of
> the mask. It's pointlessly confusing.
There's two real channel IDs. One of them is the value used in the hardware
implementation (5 and 6 in this case, used to compute the channel registers
block address) and the other one is the value used throughout the datasheet, 0
and 1 in this case.
The later is used by the driver to reference the correct interrupt, which
won't be needed anymore when referencing interrupts in the channel subnodes
directly. It's also used to print messages to the kernel log and match the
channel numbers specified in the datasheets. I could use the hardware channel
number instead, but that might become confusing.
--
Regards,
Laurent Pinchart
On Sat, Feb 15, 2014 at 12:53 AM, Laurent Pinchart
<[email protected]> wrote:
> On Friday 14 February 2014 10:58:22 Mark Rutland wrote:
>> On Fri, Feb 14, 2014 at 01:00:01AM +0000, Laurent Pinchart wrote:
>> > +Channels Optional Properties:
>> > +
>> > + - clock-source-rating: rating of the timer as a clock source device.
>> > + - clock-event-rating: rating of the timer as a clock event device.
>>
>> This feels like a leak of Linux internals. Why do you need this?
>
> You're right, it is. The clock source and clock event ratings are currently
> configured through platform data, I'll need to find a way to compute them in
> the driver instead.
That would be very good!
> There's still one piece of Linux-specific data I need though, as I need to
> specify for each channel whether to use it as a clock source device, a clock
> event device, both of them or none. That's configuration information that
> needs to be provided somehow.
I think you can decide clock source or clock event assignment based on
number of channels available. If you have only a single channel then
both clock event and clock source need to be supported. Otherwise use
one channel for clock source and the rest for clock events.
This is probably out of scope for this DT conversion, but it would be
neat if you somehow could specify the CPU affinity for a channel to
tie a clock event to an individual CPU core. This would make a a
per-cpu timer unless I'm mistaken. But that's more of a software
policy than anything else.
Thanks,
/ magnus
On Fri, Feb 14, 2014 at 04:53:08PM +0100, Laurent Pinchart wrote:
> On Friday 14 February 2014 10:58:22 Mark Rutland wrote:
> > On Fri, Feb 14, 2014 at 01:00:01AM +0000, Laurent Pinchart wrote:
> > > +Channels Optional Properties:
> > > +
> > > + - clock-source-rating: rating of the timer as a clock source device.
> > > + - clock-event-rating: rating of the timer as a clock event device.
> >
> > This feels like a leak of Linux internals. Why do you need this?
>
> You're right, it is. The clock source and clock event ratings are currently
> configured through platform data, I'll need to find a way to compute them in
> the driver instead.
>
> There's still one piece of Linux-specific data I need though, as I need to
> specify for each channel whether to use it as a clock source device, a clock
> event device, both of them or none. That's configuration information that
> needs to be provided somehow.
Are all the channels equally capable? We had this problem for the
cadence_ttc timer used on Zynq, and decided to just statically allocate
the first timer to be the clocksource, and the second to be the
clockevent.
Also, should the rating really be user configurable?
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
Hi Magnus,
On Saturday 15 February 2014 01:01:30 Magnus Damm wrote:
> On Sat, Feb 15, 2014 at 12:53 AM, Laurent Pinchart wrote:
> > On Friday 14 February 2014 10:58:22 Mark Rutland wrote:
> >> On Fri, Feb 14, 2014 at 01:00:01AM +0000, Laurent Pinchart wrote:
> >> > +Channels Optional Properties:
> >> > +
> >> > + - clock-source-rating: rating of the timer as a clock source device.
> >> > + - clock-event-rating: rating of the timer as a clock event device.
> >>
> >> This feels like a leak of Linux internals. Why do you need this?
> >
> > You're right, it is. The clock source and clock event ratings are
> > currently configured through platform data, I'll need to find a way to
> > compute them in the driver instead.
>
> That would be very good!
Any pointer would be appreciated :-) How did you compute the various ratings
used in platform data all over the place ?
> > There's still one piece of Linux-specific data I need though, as I need to
> > specify for each channel whether to use it as a clock source device, a
> > clock event device, both of them or none. That's configuration
> > information that needs to be provided somehow.
>
> I think you can decide clock source or clock event assignment based on
> number of channels available. If you have only a single channel then both
> clock event and clock source need to be supported. Otherwise use one channel
> for clock source and the rest for clock events.
That won't match the current situation. Look at CMT0 in r8a7790 for instance.
There's two hardware channels available, and we only use the first one, for
clock events only.
> This is probably out of scope for this DT conversion, but it would be neat
> if you somehow could specify the CPU affinity for a channel to tie a clock
> event to an individual CPU core. This would make a a per-cpu timer unless
> I'm mistaken. But that's more of a software policy than anything else.
Yes, that's a configuration that needs to be specified somewhere. I don't know
where though.
--
Regards,
Laurent Pinchart
Hi Josh,
On Friday 14 February 2014 09:59:51 Josh Cartwright wrote:
> On Fri, Feb 14, 2014 at 04:53:08PM +0100, Laurent Pinchart wrote:
> > On Friday 14 February 2014 10:58:22 Mark Rutland wrote:
> > > On Fri, Feb 14, 2014 at 01:00:01AM +0000, Laurent Pinchart wrote:
> > > > +Channels Optional Properties:
> > > > +
> > > > + - clock-source-rating: rating of the timer as a clock source
> > > > device.
> > > > + - clock-event-rating: rating of the timer as a clock event device.
> > >
> > > This feels like a leak of Linux internals. Why do you need this?
> >
> > You're right, it is. The clock source and clock event ratings are
> > currently configured through platform data, I'll need to find a way to
> > compute them in the driver instead.
> >
> > There's still one piece of Linux-specific data I need though, as I need to
> > specify for each channel whether to use it as a clock source device, a
> > clock event device, both of them or none. That's configuration
> > information that needs to be provided somehow.
>
> Are all the channels equally capable? We had this problem for the
> cadence_ttc timer used on Zynq, and decided to just statically allocate
> the first timer to be the clocksource, and the second to be the
> clockevent.
No, they're not. The channels can be implemented with different counter
widths, different available prescalers and source clocks and different power
management features (not all of them are capable to run in all sleep states).
> Also, should the rating really be user configurable?
Probably not. I suppose the rating should be computed by the driver based on
the source clock frequency, prescaler and counter width. Any help there would
be very appreciated, I'm pretty new to clock source and clock event devices.
--
Regards,
Laurent Pinchart
Hi Sergei,
On Friday 14 February 2014 18:36:11 Sergei Shtylyov wrote:
> Hello.
>
> On 14-02-2014 18:22, Laurent Pinchart wrote:
> >>>>> Enable the CMT0 device and configure channel 0 as a clock event
> >>>>> provider.
> >>>>>
> >>>>> Signed-off-by: Laurent Pinchart
> >>>>> <[email protected]>
> >>>>>
> >>>>> diff --git a/arch/arm/mach-shmobile/include/mach/r8a7790.h
> >>>>> b/arch/arm/mach-shmobile/include/mach/r8a7790.h index 0b95bab..62b31f3
> >>>>> 100644 --- a/arch/arm/mach-shmobile/include/mach/r8a7790.h
> >>>>> +++ b/arch/arm/mach-shmobile/include/mach/r8a7790.h
> >>>>> @@ -29,7 +29,6 @@ enum {
> >>>>>
> >>>>> };
> >>>>>
> >>>>> void r8a7790_add_standard_devices(void);
> >>>>>
> >>>>> -void r8a7790_add_dt_devices(void);
> >>>>>
> >>>>> void r8a7790_clock_init(void);
> >>>>> void r8a7790_pinmux_init(void);
> >>>>> void r8a7790_pm_init(void);
> >>>>>
> >>>>> diff --git a/arch/arm/mach-shmobile/setup-r8a7790.c
> >>>>> b/arch/arm/mach-shmobile/setup-r8a7790.c index 3e5813f..462c81f 100644
> >>>>> --- a/arch/arm/mach-shmobile/setup-r8a7790.c
> >>>>> +++ b/arch/arm/mach-shmobile/setup-r8a7790.c
> >>>>> @@ -294,11 +294,6 @@ static struct resource cmt0_resources[] = {
> >>>>> &cmt##idx##_platform_data, \
> >>>>> sizeof(struct sh_timer_config))
> >>>>>
> >>>>> -void __init r8a7790_add_dt_devices(void)
> >>>>> -{
> >>>>> - r8a7790_register_cmt(0);
> >>>>> -}
> >>>>> -
> >>>>> void __init r8a7790_add_standard_devices(void)
> >>>>> {
> >>>>> r8a7790_register_scif(0);
> >>>>> @@ -311,7 +306,7 @@ void __init r8a7790_add_standard_devices(void)
> >>>>> r8a7790_register_scif(7);
> >>>>> r8a7790_register_scif(8);
> >>>>> r8a7790_register_scif(9);
> >>>>> - r8a7790_add_dt_devices();
> >>>>> + r8a7790_register_cmt(0);
> >>>>> r8a7790_register_irqc(0);
> >>>>> r8a7790_register_thermal();
> >>>>> }
> >>>>
> >>>> IMHO, these 2 files should be split into a separate patch.
> >>>
> >>> That could easily be done, but why ?
> >>
> >> It does not seem necessary to combine these changes in one patch.
> >> Remember, Simon has separate branches for boards and SoCs. So finally
> >> it's up to him to decide on this...
> >
> > Right, but in this case I need to remove the CMT platform device
> > registration from r8a7790_add_dt_devices()
>
> You're not removing anything in these 2 files, you're just replacing
> "indirect" call to r8a7790_register_cmt(0) with direct.
OK, I see what you mean now. Given that the patch removes the
r8a7790_add_dt_devices() call from arch/arm/mach-shmobile/board-lager-
reference.c, I could indeed split those two changes into a separate patch as
there's no-one calling the function anymore. That would introduce a dependency
between the branches, which might not be better. I'll let Simon comment on
what he would prefer and will act accordingly.
--
Regards,
Laurent Pinchart
Hi Laurent,
On Sat, Feb 15, 2014 at 1:12 AM, Laurent Pinchart
<[email protected]> wrote:
> Hi Magnus,
>
> On Saturday 15 February 2014 01:01:30 Magnus Damm wrote:
>> On Sat, Feb 15, 2014 at 12:53 AM, Laurent Pinchart wrote:
>> > On Friday 14 February 2014 10:58:22 Mark Rutland wrote:
>> >> On Fri, Feb 14, 2014 at 01:00:01AM +0000, Laurent Pinchart wrote:
>> >> > +Channels Optional Properties:
>> >> > +
>> >> > + - clock-source-rating: rating of the timer as a clock source device.
>> >> > + - clock-event-rating: rating of the timer as a clock event device.
>> >>
>> >> This feels like a leak of Linux internals. Why do you need this?
>> >
>> > You're right, it is. The clock source and clock event ratings are
>> > currently configured through platform data, I'll need to find a way to
>> > compute them in the driver instead.
>>
>> That would be very good!
>
> Any pointer would be appreciated :-) How did you compute the various ratings
> used in platform data all over the place ?
Historically we used the rating to select between CMT and TMU. For
clock sources I suppose you also have the jiffy rating to consider.
And for the SMP parts we have ARM IP for TWD and arch timers that have
their ratings too. So you need to check all the timers on a particular
system and consider what you want to have operating by default. The
ARM IP timers should be preferred if available. For clock sources the
rule is probably the higher resolution the better.
>> > There's still one piece of Linux-specific data I need though, as I need to
>> > specify for each channel whether to use it as a clock source device, a
>> > clock event device, both of them or none. That's configuration
>> > information that needs to be provided somehow.
>>
>> I think you can decide clock source or clock event assignment based on
>> number of channels available. If you have only a single channel then both
>> clock event and clock source need to be supported. Otherwise use one channel
>> for clock source and the rest for clock events.
>
> That won't match the current situation. Look at CMT0 in r8a7790 for instance.
> There's two hardware channels available, and we only use the first one, for
> clock events only.
You are correct. The reason for that is that the CMT driver today is
optimized for combined clock event and clock source operation.
Historically the hardware it initially was written for (sh-mobile on
the SH arch) only had a single timer channel so combined operation was
required for tickless to work. But since you're asking how to allocate
channels then I propose checking numbers of channels available and go
from there. With that the r8a7790 support can only get better. =)
>> This is probably out of scope for this DT conversion, but it would be neat
>> if you somehow could specify the CPU affinity for a channel to tie a clock
>> event to an individual CPU core. This would make a a per-cpu timer unless
>> I'm mistaken. But that's more of a software policy than anything else.
>
> Yes, that's a configuration that needs to be specified somewhere. I don't know
> where though.
As long as you have per-channel interrupts described in DT you can
probably handle this in a generic way in the driver.
Thanks,
/ magnus
On 02/14/2014 07:26 PM, Laurent Pinchart wrote:
>>>>>>> Enable the CMT0 device and configure channel 0 as a clock event
>>>>>>> provider.
>>>>>>> Signed-off-by: Laurent Pinchart <[email protected]>
>>>>>>> diff --git a/arch/arm/mach-shmobile/include/mach/r8a7790.h
>>>>>>> b/arch/arm/mach-shmobile/include/mach/r8a7790.h index 0b95bab..62b31f3
>>>>>>> 100644 --- a/arch/arm/mach-shmobile/include/mach/r8a7790.h
>>>>>>> +++ b/arch/arm/mach-shmobile/include/mach/r8a7790.h
>>>>>>> @@ -29,7 +29,6 @@ enum {
>>>>>>>
>>>>>>> };
>>>>>>>
>>>>>>> void r8a7790_add_standard_devices(void);
>>>>>>>
>>>>>>> -void r8a7790_add_dt_devices(void);
>>>>>>>
>>>>>>> void r8a7790_clock_init(void);
>>>>>>> void r8a7790_pinmux_init(void);
>>>>>>> void r8a7790_pm_init(void);
>>>>>>>
>>>>>>> diff --git a/arch/arm/mach-shmobile/setup-r8a7790.c
>>>>>>> b/arch/arm/mach-shmobile/setup-r8a7790.c index 3e5813f..462c81f 100644
>>>>>>> --- a/arch/arm/mach-shmobile/setup-r8a7790.c
>>>>>>> +++ b/arch/arm/mach-shmobile/setup-r8a7790.c
>>>>>>> @@ -294,11 +294,6 @@ static struct resource cmt0_resources[] = {
>>>>>>> &cmt##idx##_platform_data, \
>>>>>>> sizeof(struct sh_timer_config))
>>>>>>>
>>>>>>> -void __init r8a7790_add_dt_devices(void)
>>>>>>> -{
>>>>>>> - r8a7790_register_cmt(0);
>>>>>>> -}
>>>>>>> -
>>>>>>> void __init r8a7790_add_standard_devices(void)
>>>>>>> {
>>>>>>> r8a7790_register_scif(0);
>>>>>>> @@ -311,7 +306,7 @@ void __init r8a7790_add_standard_devices(void)
>>>>>>> r8a7790_register_scif(7);
>>>>>>> r8a7790_register_scif(8);
>>>>>>> r8a7790_register_scif(9);
>>>>>>> - r8a7790_add_dt_devices();
>>>>>>> + r8a7790_register_cmt(0);
>>>>>>> r8a7790_register_irqc(0);
>>>>>>> r8a7790_register_thermal();
>>>>>>> }
>>>>>> IMHO, these 2 files should be split into a separate patch.
>>>>> That could easily be done, but why ?
>>>> It does not seem necessary to combine these changes in one patch.
>>>> Remember, Simon has separate branches for boards and SoCs. So finally
>>>> it's up to him to decide on this...
>>> Right, but in this case I need to remove the CMT platform device
>>> registration from r8a7790_add_dt_devices()
>> You're not removing anything in these 2 files, you're just replacing
>> "indirect" call to r8a7790_register_cmt(0) with direct.
> OK, I see what you mean now. Given that the patch removes the
> r8a7790_add_dt_devices() call from arch/arm/mach-shmobile/board-lager-
> reference.c, I could indeed split those two changes into a separate patch as
> there's no-one calling the function anymore. That would introduce a dependency
> between the branches, which might not be better. I'll let Simon comment on
> what he would prefer and will act accordingly.
Yes, the branch inter-dependencies are bad too. I didn't think about it,
sorry.
WBR, Sergei
On Fri, 14 Feb 2014, Laurent Pinchart wrote:
> CMT hardware devices can support multiple channels, with global
> registers and per-channel registers. The sh_cmt driver currently models
> the hardware with one Linux device per channel. This model makes it
> difficult to handle global registers in a clean way.
>
> Add support for a new model that uses one Linux device per timer with
> multiple channels per device. This requires changes to platform data,
> add new channel configuration fields.
>
> Support for the legacy model is kept and will be removed after all
> platforms switch to the new model.
>
> Signed-off-by: Laurent Pinchart <[email protected]>
> ---
> drivers/clocksource/sh_cmt.c | 299 +++++++++++++++++++++++++++++++++----------
> include/linux/sh_timer.h | 9 ++
> 2 files changed, 239 insertions(+), 69 deletions(-)
>
> diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
> index 5280231..8390f0f 100644
> --- a/drivers/clocksource/sh_cmt.c
> +++ b/drivers/clocksource/sh_cmt.c
> @@ -53,7 +53,16 @@ struct sh_cmt_device;
> * 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 are indexed from 0 to N-1 in the documentation. The channel index
> + * infers the start/stop bit position in the control register and the channel
> + * registers block address. Some CMT instances have a subset of channels
> + * available, in which case the index in the documentation doesn't match the
> + * "real" index as implemented in hardware. This is for instance the case with
> + * CMT0 on r8a7740, which is a 32-bit variant with a single channel numbered 0
> + * in the documentation but using start/stop bit 5 and having its registers
> + * block at 0x60.
> + *
> + * Similarly CMT0 on r8a73a4, r8a7790 and r8a7791, while implementing 32-bit
> * channels only, is a 48-bit gen2 CMT with the 48-bit channels unavailable.
> */
>
> @@ -85,11 +94,15 @@ struct sh_cmt_info {
>
> struct sh_cmt_channel {
> struct sh_cmt_device *cmt;
> - unsigned int index;
>
> - void __iomem *base;
> + unsigned int index; /* Index in the documentation */
> + unsigned int hwidx; /* Real hardware index */
> +
> + void __iomem *iostart;
> + void __iomem *ioctrl;
> struct irqaction irqaction;
While you are at it, can you please get rid of that irqaction and use
request_irq() ?
Thanks,
tglx
Hi Thomas,
On Saturday 15 February 2014 13:46:54 Thomas Gleixner wrote:
> On Fri, 14 Feb 2014, Laurent Pinchart wrote:
> > CMT hardware devices can support multiple channels, with global
> > registers and per-channel registers. The sh_cmt driver currently models
> > the hardware with one Linux device per channel. This model makes it
> > difficult to handle global registers in a clean way.
> >
> > Add support for a new model that uses one Linux device per timer with
> > multiple channels per device. This requires changes to platform data,
> > add new channel configuration fields.
> >
> > Support for the legacy model is kept and will be removed after all
> > platforms switch to the new model.
> >
> > Signed-off-by: Laurent Pinchart
> > <[email protected]>
> > ---
> >
> > drivers/clocksource/sh_cmt.c | 299 +++++++++++++++++++++++++++++---------
> > include/linux/sh_timer.h | 9 ++
> > 2 files changed, 239 insertions(+), 69 deletions(-)
> >
> > diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
> > index 5280231..8390f0f 100644
> > --- a/drivers/clocksource/sh_cmt.c
> > +++ b/drivers/clocksource/sh_cmt.c
> > @@ -53,7 +53,16 @@ struct sh_cmt_device;
> >
> > * 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 are indexed from 0 to N-1 in the documentation. The
> > channel index + * infers the start/stop bit position in the control
> > register and the channel + * registers block address. Some CMT instances
> > have a subset of channels + * available, in which case the index in the
> > documentation doesn't match the + * "real" index as implemented in
> > hardware. This is for instance the case with + * CMT0 on r8a7740, which
> > is a 32-bit variant with a single channel numbered 0 + * in the
> > documentation but using start/stop bit 5 and having its registers + *
> > block at 0x60.
> > + *
> > + * Similarly CMT0 on r8a73a4, r8a7790 and r8a7791, while implementing
> > 32-bit>
> > * channels only, is a 48-bit gen2 CMT with the 48-bit channels
> > unavailable.
> > */
> >
> > @@ -85,11 +94,15 @@ struct sh_cmt_info {
> >
> > struct sh_cmt_channel {
> >
> > struct sh_cmt_device *cmt;
> >
> > - unsigned int index;
> >
> > - void __iomem *base;
> > + unsigned int index; /* Index in the documentation */
> > + unsigned int hwidx; /* Real hardware index */
> > +
> > + void __iomem *iostart;
> > + void __iomem *ioctrl;
> >
> > struct irqaction irqaction;
>
> While you are at it, can you please get rid of that irqaction and use
> request_irq() ?
The driver claims it can't use request_irq() because the function is not
available for early platform devices. If the situation has changed I'd gladly
get rid of irqaction.
--
Regards,
Laurent Pinchart
Hi Laurent,
On Mon, Feb 17, 2014 at 3:18 AM, Laurent Pinchart
<[email protected]> wrote:
> Hi Thomas,
>
> On Saturday 15 February 2014 13:46:54 Thomas Gleixner wrote:
>> On Fri, 14 Feb 2014, Laurent Pinchart wrote:
>> > CMT hardware devices can support multiple channels, with global
>> > registers and per-channel registers. The sh_cmt driver currently models
>> > the hardware with one Linux device per channel. This model makes it
>> > difficult to handle global registers in a clean way.
>> >
>> > Add support for a new model that uses one Linux device per timer with
>> > multiple channels per device. This requires changes to platform data,
>> > add new channel configuration fields.
>> >
>> > Support for the legacy model is kept and will be removed after all
>> > platforms switch to the new model.
>> >
>> > Signed-off-by: Laurent Pinchart
>> > <[email protected]>
>> > ---
>> >
>> > drivers/clocksource/sh_cmt.c | 299 +++++++++++++++++++++++++++++---------
>> > include/linux/sh_timer.h | 9 ++
>> > 2 files changed, 239 insertions(+), 69 deletions(-)
>> >
>> > diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
>> > index 5280231..8390f0f 100644
>> > --- a/drivers/clocksource/sh_cmt.c
>> > +++ b/drivers/clocksource/sh_cmt.c
>> > @@ -53,7 +53,16 @@ struct sh_cmt_device;
>> >
>> > * 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 are indexed from 0 to N-1 in the documentation. The
>> > channel index + * infers the start/stop bit position in the control
>> > register and the channel + * registers block address. Some CMT instances
>> > have a subset of channels + * available, in which case the index in the
>> > documentation doesn't match the + * "real" index as implemented in
>> > hardware. This is for instance the case with + * CMT0 on r8a7740, which
>> > is a 32-bit variant with a single channel numbered 0 + * in the
>> > documentation but using start/stop bit 5 and having its registers + *
>> > block at 0x60.
>> > + *
>> > + * Similarly CMT0 on r8a73a4, r8a7790 and r8a7791, while implementing
>> > 32-bit>
>> > * channels only, is a 48-bit gen2 CMT with the 48-bit channels
>> > unavailable.
>> > */
>> >
>> > @@ -85,11 +94,15 @@ struct sh_cmt_info {
>> >
>> > struct sh_cmt_channel {
>> >
>> > struct sh_cmt_device *cmt;
>> >
>> > - unsigned int index;
>> >
>> > - void __iomem *base;
>> > + unsigned int index; /* Index in the documentation */
>> > + unsigned int hwidx; /* Real hardware index */
>> > +
>> > + void __iomem *iostart;
>> > + void __iomem *ioctrl;
>> >
>> > struct irqaction irqaction;
>>
>> While you are at it, can you please get rid of that irqaction and use
>> request_irq() ?
>
> The driver claims it can't use request_irq() because the function is not
> available for early platform devices. If the situation has changed I'd gladly
> get rid of irqaction.
With the risk of stating the obvious, this depends on how early the
early platform device stuff is being run. The actual location may not
be the same on SH and ARM for instance.
On ARM we are doing all we can to initialize these devices as late as
ever possible in the MULTIPLAFORM (DT reference) case. Once we manage
to remove the legacy ARM case then there is one less user of early
platform timers.
One perhaps reasonable way forward could be to use request_irq() in
case of DT and leave the legacy platform data case to rely on
irqaction.
Cheers,
/ magnus
Hi Magnus,
On Saturday 15 February 2014 02:22:00 Magnus Damm wrote:
> On Sat, Feb 15, 2014 at 1:12 AM, Laurent Pinchart wrote:
> > On Saturday 15 February 2014 01:01:30 Magnus Damm wrote:
> >> On Sat, Feb 15, 2014 at 12:53 AM, Laurent Pinchart wrote:
> >> > On Friday 14 February 2014 10:58:22 Mark Rutland wrote:
> >> >> On Fri, Feb 14, 2014 at 01:00:01AM +0000, Laurent Pinchart wrote:
> >> >> > +Channels Optional Properties:
> >> >> > +
> >> >> > + - clock-source-rating: rating of the timer as a clock source
> >> >> > device.
> >> >> > + - clock-event-rating: rating of the timer as a clock event
> >> >> > device.
> >> >>
> >> >> This feels like a leak of Linux internals. Why do you need this?
> >> >
> >> > You're right, it is. The clock source and clock event ratings are
> >> > currently configured through platform data, I'll need to find a way to
> >> > compute them in the driver instead.
> >>
> >> That would be very good!
> >
> > Any pointer would be appreciated :-) How did you compute the various
> > ratings used in platform data all over the place ?
>
> Historically we used the rating to select between CMT and TMU. For
> clock sources I suppose you also have the jiffy rating to consider.
> And for the SMP parts we have ARM IP for TWD and arch timers that have
> their ratings too. So you need to check all the timers on a particular
> system and consider what you want to have operating by default. The
> ARM IP timers should be preferred if available. For clock sources the
> rule is probably the higher resolution the better.
>
> >> > There's still one piece of Linux-specific data I need though, as I need
> >> > to specify for each channel whether to use it as a clock source device,
> >> > a clock event device, both of them or none. That's configuration
> >> > information that needs to be provided somehow.
> >>
> >> I think you can decide clock source or clock event assignment based on
> >> number of channels available. If you have only a single channel then both
> >> clock event and clock source need to be supported. Otherwise use one
> >> channel for clock source and the rest for clock events.
> >
> > That won't match the current situation. Look at CMT0 in r8a7790 for
> > instance. There's two hardware channels available, and we only use the
> > first one, for clock events only.
>
> You are correct. The reason for that is that the CMT driver today is
> optimized for combined clock event and clock source operation.
>
> Historically the hardware it initially was written for (sh-mobile on
> the SH arch) only had a single timer channel so combined operation was
> required for tickless to work. But since you're asking how to allocate
> channels then I propose checking numbers of channels available and go
> from there. With that the r8a7790 support can only get better. =)
>
> >> This is probably out of scope for this DT conversion, but it would be
> >> neat if you somehow could specify the CPU affinity for a channel to tie a
> >> clock event to an individual CPU core. This would make a a per-cpu timer
> >> unless I'm mistaken. But that's more of a software policy than anything
> >> else.
> >
> > Yes, that's a configuration that needs to be specified somewhere. I don't
> > know where though.
>
> As long as you have per-channel interrupts described in DT you can
> probably handle this in a generic way in the driver.
But how do we decide whether to use a single timer channel or one channel per
CPU ? Will the kernel use one clock event device per CPU automatically ? I
have to confess I have no idea how this works.
--
Regards,
Laurent Pinchart
Hi Magnus,
On Monday 17 February 2014 10:41:31 Magnus Damm wrote:
> On Mon, Feb 17, 2014 at 3:18 AM, Laurent Pinchart wrote:
> > On Saturday 15 February 2014 13:46:54 Thomas Gleixner wrote:
> >> On Fri, 14 Feb 2014, Laurent Pinchart wrote:
> >> > CMT hardware devices can support multiple channels, with global
> >> > registers and per-channel registers. The sh_cmt driver currently models
> >> > the hardware with one Linux device per channel. This model makes it
> >> > difficult to handle global registers in a clean way.
> >> >
> >> > Add support for a new model that uses one Linux device per timer with
> >> > multiple channels per device. This requires changes to platform data,
> >> > add new channel configuration fields.
> >> >
> >> > Support for the legacy model is kept and will be removed after all
> >> > platforms switch to the new model.
> >> >
> >> > Signed-off-by: Laurent Pinchart
> >> > <[email protected]>
> >> > ---
> >> >
> >> > drivers/clocksource/sh_cmt.c | 299 ++++++++++++++++++++++++++---------
> >> > include/linux/sh_timer.h | 9 ++
> >> > 2 files changed, 239 insertions(+), 69 deletions(-)
> >> >
> >> > diff --git a/drivers/clocksource/sh_cmt.c
> >> > b/drivers/clocksource/sh_cmt.c
> >> > index 5280231..8390f0f 100644
> >> > --- a/drivers/clocksource/sh_cmt.c
> >> > +++ b/drivers/clocksource/sh_cmt.c
[snip]
> >> > @@ -85,11 +94,15 @@ struct sh_cmt_info {
> >> >
> >> > struct sh_cmt_channel {
> >> > struct sh_cmt_device *cmt;
> >> > - unsigned int index;
> >> > - void __iomem *base;
> >> > + unsigned int index; /* Index in the documentation */
> >> > + unsigned int hwidx; /* Real hardware index */
> >> > +
> >> > + void __iomem *iostart;
> >> > + void __iomem *ioctrl;
> >> >
> >> > struct irqaction irqaction;
> >>
> >> While you are at it, can you please get rid of that irqaction and use
> >> request_irq() ?
> >
> > The driver claims it can't use request_irq() because the function is not
> > available for early platform devices. If the situation has changed I'd
> > gladly get rid of irqaction.
>
> With the risk of stating the obvious, this depends on how early the
> early platform device stuff is being run. The actual location may not
> be the same on SH and ARM for instance.
>
> On ARM we are doing all we can to initialize these devices as late as
> ever possible in the MULTIPLAFORM (DT reference) case. Once we manage
> to remove the legacy ARM case then there is one less user of early
> platform timers.
>
> One perhaps reasonable way forward could be to use request_irq() in
> case of DT and leave the legacy platform data case to rely on
> irqaction.
The whole point of switching from setup_irq() to request_irq() is to simplify
the code. Adding request_irq() as an option would go in the opposite
direction.
--
Regards,
Laurent Pinchart
Hi Laurent,
On Mon, Feb 17, 2014 at 10:45 AM, Laurent Pinchart
<[email protected]> wrote:
> Hi Magnus,
>
> On Saturday 15 February 2014 02:22:00 Magnus Damm wrote:
>> On Sat, Feb 15, 2014 at 1:12 AM, Laurent Pinchart wrote:
>> > On Saturday 15 February 2014 01:01:30 Magnus Damm wrote:
>> >> On Sat, Feb 15, 2014 at 12:53 AM, Laurent Pinchart wrote:
>> >> > On Friday 14 February 2014 10:58:22 Mark Rutland wrote:
>> >> >> On Fri, Feb 14, 2014 at 01:00:01AM +0000, Laurent Pinchart wrote:
>> >> >> > +Channels Optional Properties:
>> >> >> > +
>> >> >> > + - clock-source-rating: rating of the timer as a clock source
>> >> >> > device.
>> >> >> > + - clock-event-rating: rating of the timer as a clock event
>> >> >> > device.
>> >> >>
>> >> >> This feels like a leak of Linux internals. Why do you need this?
>> >> >
>> >> > You're right, it is. The clock source and clock event ratings are
>> >> > currently configured through platform data, I'll need to find a way to
>> >> > compute them in the driver instead.
>> >>
>> >> That would be very good!
>> >
>> > Any pointer would be appreciated :-) How did you compute the various
>> > ratings used in platform data all over the place ?
>>
>> Historically we used the rating to select between CMT and TMU. For
>> clock sources I suppose you also have the jiffy rating to consider.
>> And for the SMP parts we have ARM IP for TWD and arch timers that have
>> their ratings too. So you need to check all the timers on a particular
>> system and consider what you want to have operating by default. The
>> ARM IP timers should be preferred if available. For clock sources the
>> rule is probably the higher resolution the better.
>>
>> >> > There's still one piece of Linux-specific data I need though, as I need
>> >> > to specify for each channel whether to use it as a clock source device,
>> >> > a clock event device, both of them or none. That's configuration
>> >> > information that needs to be provided somehow.
>> >>
>> >> I think you can decide clock source or clock event assignment based on
>> >> number of channels available. If you have only a single channel then both
>> >> clock event and clock source need to be supported. Otherwise use one
>> >> channel for clock source and the rest for clock events.
>> >
>> > That won't match the current situation. Look at CMT0 in r8a7790 for
>> > instance. There's two hardware channels available, and we only use the
>> > first one, for clock events only.
>>
>> You are correct. The reason for that is that the CMT driver today is
>> optimized for combined clock event and clock source operation.
>>
>> Historically the hardware it initially was written for (sh-mobile on
>> the SH arch) only had a single timer channel so combined operation was
>> required for tickless to work. But since you're asking how to allocate
>> channels then I propose checking numbers of channels available and go
>> from there. With that the r8a7790 support can only get better. =)
>>
>> >> This is probably out of scope for this DT conversion, but it would be
>> >> neat if you somehow could specify the CPU affinity for a channel to tie a
>> >> clock event to an individual CPU core. This would make a a per-cpu timer
>> >> unless I'm mistaken. But that's more of a software policy than anything
>> >> else.
>> >
>> > Yes, that's a configuration that needs to be specified somewhere. I don't
>> > know where though.
>>
>> As long as you have per-channel interrupts described in DT you can
>> probably handle this in a generic way in the driver.
>
> But how do we decide whether to use a single timer channel or one channel per
> CPU ? Will the kernel use one clock event device per CPU automatically ? I
> have to confess I have no idea how this works.
I guess that's the tricky bit about timer support, it is a mix of
hardware description and software configuration. So it sounds to me
that we need some kind of software configuration interface. But it can
probably be considered when/if we add such kind of support to the
driver. Probably out of scope for now.
Regardless it seems to me that the hardware description in DT doesn't
need to care about this.
Cheers,
/ magnus
Hi Laurent,
On Mon, Feb 17, 2014 at 10:48 AM, Laurent Pinchart
<[email protected]> wrote:
> Hi Magnus,
>
> On Monday 17 February 2014 10:41:31 Magnus Damm wrote:
>> On Mon, Feb 17, 2014 at 3:18 AM, Laurent Pinchart wrote:
>> > On Saturday 15 February 2014 13:46:54 Thomas Gleixner wrote:
>> >> On Fri, 14 Feb 2014, Laurent Pinchart wrote:
>> >> > CMT hardware devices can support multiple channels, with global
>> >> > registers and per-channel registers. The sh_cmt driver currently models
>> >> > the hardware with one Linux device per channel. This model makes it
>> >> > difficult to handle global registers in a clean way.
>> >> >
>> >> > Add support for a new model that uses one Linux device per timer with
>> >> > multiple channels per device. This requires changes to platform data,
>> >> > add new channel configuration fields.
>> >> >
>> >> > Support for the legacy model is kept and will be removed after all
>> >> > platforms switch to the new model.
>> >> >
>> >> > Signed-off-by: Laurent Pinchart
>> >> > <[email protected]>
>> >> > ---
>> >> >
>> >> > drivers/clocksource/sh_cmt.c | 299 ++++++++++++++++++++++++++---------
>> >> > include/linux/sh_timer.h | 9 ++
>> >> > 2 files changed, 239 insertions(+), 69 deletions(-)
>> >> >
>> >> > diff --git a/drivers/clocksource/sh_cmt.c
>> >> > b/drivers/clocksource/sh_cmt.c
>> >> > index 5280231..8390f0f 100644
>> >> > --- a/drivers/clocksource/sh_cmt.c
>> >> > +++ b/drivers/clocksource/sh_cmt.c
>
> [snip]
>
>> >> > @@ -85,11 +94,15 @@ struct sh_cmt_info {
>> >> >
>> >> > struct sh_cmt_channel {
>> >> > struct sh_cmt_device *cmt;
>> >> > - unsigned int index;
>> >> > - void __iomem *base;
>> >> > + unsigned int index; /* Index in the documentation */
>> >> > + unsigned int hwidx; /* Real hardware index */
>> >> > +
>> >> > + void __iomem *iostart;
>> >> > + void __iomem *ioctrl;
>> >> >
>> >> > struct irqaction irqaction;
>> >>
>> >> While you are at it, can you please get rid of that irqaction and use
>> >> request_irq() ?
>> >
>> > The driver claims it can't use request_irq() because the function is not
>> > available for early platform devices. If the situation has changed I'd
>> > gladly get rid of irqaction.
>>
>> With the risk of stating the obvious, this depends on how early the
>> early platform device stuff is being run. The actual location may not
>> be the same on SH and ARM for instance.
>>
>> On ARM we are doing all we can to initialize these devices as late as
>> ever possible in the MULTIPLAFORM (DT reference) case. Once we manage
>> to remove the legacy ARM case then there is one less user of early
>> platform timers.
>>
>> One perhaps reasonable way forward could be to use request_irq() in
>> case of DT and leave the legacy platform data case to rely on
>> irqaction.
>
> The whole point of switching from setup_irq() to request_irq() is to simplify
> the code. Adding request_irq() as an option would go in the opposite
> direction.
The point of switching to setup_irq() was not very clear to me. I
think it is fine to switch to using it over time, and I'm open to any
alternative ways forward. Usually moving in incremental steps means
more crap in the short term, but if someone could come up with a way
that involves little crap then I'm all for that! =)
Thanks,
/ magnus
Hi Magnus,
On Monday 17 February 2014 11:07:06 Magnus Damm wrote:
> On Mon, Feb 17, 2014 at 10:48 AM, Laurent Pinchart wrote:
> > On Monday 17 February 2014 10:41:31 Magnus Damm wrote:
> >> On Mon, Feb 17, 2014 at 3:18 AM, Laurent Pinchart wrote:
> >> > On Saturday 15 February 2014 13:46:54 Thomas Gleixner wrote:
> >> >> On Fri, 14 Feb 2014, Laurent Pinchart wrote:
> >> >> > CMT hardware devices can support multiple channels, with global
> >> >> > registers and per-channel registers. The sh_cmt driver currently
> >> >> > models the hardware with one Linux device per channel. This model
> >> >> > makes it difficult to handle global registers in a clean way.
> >> >> >
> >> >> > Add support for a new model that uses one Linux device per timer
> >> >> > with multiple channels per device. This requires changes to platform
> >> >> > data, add new channel configuration fields.
> >> >> >
> >> >> > Support for the legacy model is kept and will be removed after all
> >> >> > platforms switch to the new model.
> >> >> >
> >> >> > Signed-off-by: Laurent Pinchart
> >> >> > <[email protected]>
> >> >> > ---
> >> >> >
> >> >> > drivers/clocksource/sh_cmt.c | 299 ++++++++++++++++++++++++--------
> >> >> > include/linux/sh_timer.h | 9 ++
> >> >> > 2 files changed, 239 insertions(+), 69 deletions(-)
> >> >> >
> >> >> > diff --git a/drivers/clocksource/sh_cmt.c
> >> >> > b/drivers/clocksource/sh_cmt.c
> >> >> > index 5280231..8390f0f 100644
> >> >> > --- a/drivers/clocksource/sh_cmt.c
> >> >> > +++ b/drivers/clocksource/sh_cmt.c
> >
> > [snip]
> >
> >> >> > @@ -85,11 +94,15 @@ struct sh_cmt_info {
> >> >> >
> >> >> > struct sh_cmt_channel {
> >> >> > struct sh_cmt_device *cmt;
> >> >> >
> >> >> > - unsigned int index;
> >> >> > - void __iomem *base;
> >> >> > + unsigned int index; /* Index in the documentation */
> >> >> > + unsigned int hwidx; /* Real hardware index */
> >> >> > +
> >> >> > + void __iomem *iostart;
> >> >> > + void __iomem *ioctrl;
> >> >> >
> >> >> > struct irqaction irqaction;
> >> >>
> >> >> While you are at it, can you please get rid of that irqaction and use
> >> >> request_irq() ?
> >> >
> >> > The driver claims it can't use request_irq() because the function is
> >> > not available for early platform devices. If the situation has changed
> >> > I'd gladly get rid of irqaction.
> >>
> >> With the risk of stating the obvious, this depends on how early the
> >> early platform device stuff is being run. The actual location may not
> >> be the same on SH and ARM for instance.
> >>
> >> On ARM we are doing all we can to initialize these devices as late as
> >> ever possible in the MULTIPLAFORM (DT reference) case. Once we manage
> >> to remove the legacy ARM case then there is one less user of early
> >> platform timers.
> >>
> >> One perhaps reasonable way forward could be to use request_irq() in
> >> case of DT and leave the legacy platform data case to rely on
> >> irqaction.
> >
> > The whole point of switching from setup_irq() to request_irq() is to
> > simplify the code. Adding request_irq() as an option would go in the
> > opposite direction.
>
> The point of switching to setup_irq() was not very clear to me. I
> think it is fine to switch to using it over time, and I'm open to any
> alternative ways forward. Usually moving in incremental steps means
> more crap in the short term, but if someone could come up with a way
> that involves little crap then I'm all for that! =)
I've had a quick look at request_irq(). The function is defined as a static
inline in include/linux/interrupt.h:
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
request_threaded_irq() is implemented in kernel/irq/manage.c. I've removed the
code paths that would never be taken when called by the sh_cmt driver, due to
- handler not being NULL
- thread_fn being NULL
- CONFIG_DEBUG_SHIRQ_FIXME not being set
- IRQF_SHARED not being set
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
if (!irq_settings_can_request(desc) ||
WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
chip_bus_lock(desc);
retval = __setup_irq(irq, desc, action);
chip_bus_sync_unlock(desc);
if (retval)
kfree(action);
return retval;
}
setup_irq() is implemented in the same file as
int setup_irq(unsigned int irq, struct irqaction *act)
{
int retval;
struct irq_desc *desc = irq_to_desc(irq);
if (WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
chip_bus_lock(desc);
retval = __setup_irq(irq, desc, act);
chip_bus_sync_unlock(desc);
return retval;
}
The only two differences between request_irq() and setup_irq() would thus be
- the extra !irq_settings_can_request(desc) check
- the extra kzalloc() call
The former should only be true for chained IRQ handlers, which isn't the case
here. The latter is just a kmalloc + kmap_atomic + memset as far as I can
tell.
It thus seems safe to replace setup_irq() with request_irq().
--
Regards,
Laurent Pinchart
Hi Magnus,
On Monday 17 February 2014 10:48:55 Magnus Damm wrote:
> On Mon, Feb 17, 2014 at 10:45 AM, Laurent Pinchart wrote:
> > On Saturday 15 February 2014 02:22:00 Magnus Damm wrote:
> >> On Sat, Feb 15, 2014 at 1:12 AM, Laurent Pinchart wrote:
> >> > On Saturday 15 February 2014 01:01:30 Magnus Damm wrote:
> >> >> On Sat, Feb 15, 2014 at 12:53 AM, Laurent Pinchart wrote:
> >> >> > On Friday 14 February 2014 10:58:22 Mark Rutland wrote:
> >> >> >> On Fri, Feb 14, 2014 at 01:00:01AM +0000, Laurent Pinchart wrote:
> >> >> >> > +Channels Optional Properties:
> >> >> >> > +
> >> >> >> > + - clock-source-rating: rating of the timer as a clock source
> >> >> >> > device.
> >> >> >> > + - clock-event-rating: rating of the timer as a clock event
> >> >> >> > device.
> >> >> >>
> >> >> >> This feels like a leak of Linux internals. Why do you need this?
> >> >> >
> >> >> > You're right, it is. The clock source and clock event ratings are
> >> >> > currently configured through platform data, I'll need to find a way
> >> >> > to compute them in the driver instead.
> >> >>
> >> >> That would be very good!
> >> >
> >> > Any pointer would be appreciated :-) How did you compute the various
> >> > ratings used in platform data all over the place ?
> >>
> >> Historically we used the rating to select between CMT and TMU. For
> >> clock sources I suppose you also have the jiffy rating to consider.
> >> And for the SMP parts we have ARM IP for TWD and arch timers that have
> >> their ratings too. So you need to check all the timers on a particular
> >> system and consider what you want to have operating by default. The
> >> ARM IP timers should be preferred if available. For clock sources the
> >> rule is probably the higher resolution the better.
> >>
> >> >> > There's still one piece of Linux-specific data I need though, as I
> >> >> > need to specify for each channel whether to use it as a clock source
> >> >> > device, a clock event device, both of them or none. That's
> >> >> > configuration information that needs to be provided somehow.
> >> >>
> >> >> I think you can decide clock source or clock event assignment based on
> >> >> number of channels available. If you have only a single channel then
> >> >> both clock event and clock source need to be supported. Otherwise use
> >> >> one channel for clock source and the rest for clock events.
> >> >
> >> > That won't match the current situation. Look at CMT0 in r8a7790 for
> >> > instance. There's two hardware channels available, and we only use the
> >> > first one, for clock events only.
> >>
> >> You are correct. The reason for that is that the CMT driver today is
> >> optimized for combined clock event and clock source operation.
> >>
> >> Historically the hardware it initially was written for (sh-mobile on
> >> the SH arch) only had a single timer channel so combined operation was
> >> required for tickless to work. But since you're asking how to allocate
> >> channels then I propose checking numbers of channels available and go
> >> from there. With that the r8a7790 support can only get better. =)
> >>
> >> >> This is probably out of scope for this DT conversion, but it would be
> >> >> neat if you somehow could specify the CPU affinity for a channel to
> >> >> tie a clock event to an individual CPU core. This would make a a per-
> >> >> cpu timer unless I'm mistaken. But that's more of a software policy
> >> >> than anything else.
> >> >
> >> > Yes, that's a configuration that needs to be specified somewhere. I
> >> > don't know where though.
> >>
> >> As long as you have per-channel interrupts described in DT you can
> >> probably handle this in a generic way in the driver.
> >
> > But how do we decide whether to use a single timer channel or one channel
> > per CPU ? Will the kernel use one clock event device per CPU
> > automatically ? I have to confess I have no idea how this works.
>
> I guess that's the tricky bit about timer support, it is a mix of
> hardware description and software configuration. So it sounds to me
> that we need some kind of software configuration interface. But it can
> probably be considered when/if we add such kind of support to the
> driver. Probably out of scope for now.
>
> Regardless it seems to me that the hardware description in DT doesn't
> need to care about this.
I'll revisit that later. Per-CPU timers is not a high priority for now, so
I'll just return -EAGAIN :-)
Nonetheless, specifying which timer channel to use as a clock source and which
channel to use as a clock event device might need to be specified in DT (or
somewhere else, but I'm not sure what other options we have here).
--
Regards,
Laurent Pinchart
Hi Laurent,
On Tue, Feb 18, 2014 at 6:43 AM, Laurent Pinchart
<[email protected]> wrote:
> Hi Magnus,
>
> On Monday 17 February 2014 10:48:55 Magnus Damm wrote:
>> On Mon, Feb 17, 2014 at 10:45 AM, Laurent Pinchart wrote:
>> > On Saturday 15 February 2014 02:22:00 Magnus Damm wrote:
>> >> On Sat, Feb 15, 2014 at 1:12 AM, Laurent Pinchart wrote:
>> >> > On Saturday 15 February 2014 01:01:30 Magnus Damm wrote:
>> >> >> On Sat, Feb 15, 2014 at 12:53 AM, Laurent Pinchart wrote:
>> >> >> > On Friday 14 February 2014 10:58:22 Mark Rutland wrote:
>> >> >> >> On Fri, Feb 14, 2014 at 01:00:01AM +0000, Laurent Pinchart wrote:
>> >> >> >> > +Channels Optional Properties:
>> >> >> >> > +
>> >> >> >> > + - clock-source-rating: rating of the timer as a clock source
>> >> >> >> > device.
>> >> >> >> > + - clock-event-rating: rating of the timer as a clock event
>> >> >> >> > device.
>> >> >> >>
>> >> >> >> This feels like a leak of Linux internals. Why do you need this?
>> >> >> >
>> >> >> > You're right, it is. The clock source and clock event ratings are
>> >> >> > currently configured through platform data, I'll need to find a way
>> >> >> > to compute them in the driver instead.
>> >> >>
>> >> >> That would be very good!
>> >> >
>> >> > Any pointer would be appreciated :-) How did you compute the various
>> >> > ratings used in platform data all over the place ?
>> >>
>> >> Historically we used the rating to select between CMT and TMU. For
>> >> clock sources I suppose you also have the jiffy rating to consider.
>> >> And for the SMP parts we have ARM IP for TWD and arch timers that have
>> >> their ratings too. So you need to check all the timers on a particular
>> >> system and consider what you want to have operating by default. The
>> >> ARM IP timers should be preferred if available. For clock sources the
>> >> rule is probably the higher resolution the better.
>> >>
>> >> >> > There's still one piece of Linux-specific data I need though, as I
>> >> >> > need to specify for each channel whether to use it as a clock source
>> >> >> > device, a clock event device, both of them or none. That's
>> >> >> > configuration information that needs to be provided somehow.
>> >> >>
>> >> >> I think you can decide clock source or clock event assignment based on
>> >> >> number of channels available. If you have only a single channel then
>> >> >> both clock event and clock source need to be supported. Otherwise use
>> >> >> one channel for clock source and the rest for clock events.
>> >> >
>> >> > That won't match the current situation. Look at CMT0 in r8a7790 for
>> >> > instance. There's two hardware channels available, and we only use the
>> >> > first one, for clock events only.
>> >>
>> >> You are correct. The reason for that is that the CMT driver today is
>> >> optimized for combined clock event and clock source operation.
>> >>
>> >> Historically the hardware it initially was written for (sh-mobile on
>> >> the SH arch) only had a single timer channel so combined operation was
>> >> required for tickless to work. But since you're asking how to allocate
>> >> channels then I propose checking numbers of channels available and go
>> >> from there. With that the r8a7790 support can only get better. =)
>> >>
>> >> >> This is probably out of scope for this DT conversion, but it would be
>> >> >> neat if you somehow could specify the CPU affinity for a channel to
>> >> >> tie a clock event to an individual CPU core. This would make a a per-
>> >> >> cpu timer unless I'm mistaken. But that's more of a software policy
>> >> >> than anything else.
>> >> >
>> >> > Yes, that's a configuration that needs to be specified somewhere. I
>> >> > don't know where though.
>> >>
>> >> As long as you have per-channel interrupts described in DT you can
>> >> probably handle this in a generic way in the driver.
>> >
>> > But how do we decide whether to use a single timer channel or one channel
>> > per CPU ? Will the kernel use one clock event device per CPU
>> > automatically ? I have to confess I have no idea how this works.
>>
>> I guess that's the tricky bit about timer support, it is a mix of
>> hardware description and software configuration. So it sounds to me
>> that we need some kind of software configuration interface. But it can
>> probably be considered when/if we add such kind of support to the
>> driver. Probably out of scope for now.
>>
>> Regardless it seems to me that the hardware description in DT doesn't
>> need to care about this.
>
> I'll revisit that later. Per-CPU timers is not a high priority for now, so
> I'll just return -EAGAIN :-)
Thanks, that's fine.
> Nonetheless, specifying which timer channel to use as a clock source and which
> channel to use as a clock event device might need to be specified in DT (or
> somewhere else, but I'm not sure what other options we have here).
I disagree about the need for specifying clock source or clock event
channel in DT. Since per-cpu timers is out of scope for now then why
don't we simply just let the driver automatically allocate the during
run-time?
In case one CMT channel exists:
Use that for both clock source and clock event.
In case more than one CMT channel exists:
Use one separate channel for clock source and one separate channel for
clock event.
That would cover our existing use case, no?
Thanks,
/ magnus
Hi Magnus,
On Tuesday 18 February 2014 09:51:30 Magnus Damm wrote:
> On Tue, Feb 18, 2014 at 6:43 AM, Laurent Pinchart wrote:
> > On Monday 17 February 2014 10:48:55 Magnus Damm wrote:
> >> On Mon, Feb 17, 2014 at 10:45 AM, Laurent Pinchart wrote:
> >> > On Saturday 15 February 2014 02:22:00 Magnus Damm wrote:
> >> >> On Sat, Feb 15, 2014 at 1:12 AM, Laurent Pinchart wrote:
> >> >> > On Saturday 15 February 2014 01:01:30 Magnus Damm wrote:
> >> >> >> On Sat, Feb 15, 2014 at 12:53 AM, Laurent Pinchart wrote:
[snip]
> >> >> >> > There's still one piece of Linux-specific data I need though, as
> >> >> >> > I need to specify for each channel whether to use it as a clock
> >> >> >> > source device, a clock event device, both of them or none. That's
> >> >> >> > configuration information that needs to be provided somehow.
> >> >> >>
> >> >> >> I think you can decide clock source or clock event assignment based
> >> >> >> on number of channels available. If you have only a single channel
> >> >> >> then both clock event and clock source need to be supported.
> >> >> >> Otherwise use one channel for clock source and the rest for clock
> >> >> >> events.
> >> >> >
> >> >> > That won't match the current situation. Look at CMT0 in r8a7790 for
> >> >> > instance. There's two hardware channels available, and we only use
> >> >> > the first one, for clock events only.
> >> >>
> >> >> You are correct. The reason for that is that the CMT driver today is
> >> >> optimized for combined clock event and clock source operation.
> >> >>
> >> >> Historically the hardware it initially was written for (sh-mobile on
> >> >> the SH arch) only had a single timer channel so combined operation was
> >> >> required for tickless to work. But since you're asking how to allocate
> >> >> channels then I propose checking numbers of channels available and go
> >> >> from there. With that the r8a7790 support can only get better. =)
[snip]
> > Nonetheless, specifying which timer channel to use as a clock source and
> > which channel to use as a clock event device might need to be specified
> > in DT (or somewhere else, but I'm not sure what other options we have
> > here).
>
> I disagree about the need for specifying clock source or clock event channel
> in DT. Since per-cpu timers is out of scope for now then why don't we
> simply just let the driver automatically allocate the during run-time?
>
> In case one CMT channel exists:
> Use that for both clock source and clock event.
>
> In case more than one CMT channel exists:
> Use one separate channel for clock source and one separate channel for clock
> event.
>
> That would cover our existing use case, no?
One of the issues with that approach is that the decision on what to use a
channel for will be taken locally from a timer point of view, without a global
system-wide view. When more than one timer is available for a given platform
(several CMT instances for instance, or CMT + TMU) the driver will decide on
how to configure each instance without taking the other timers into account.
Beside leading to creating more clock sources and clock event devices than
today (is that a problem ?), couldn't it also lead to taking sub-optimal
decisions ?
You also mentioned that only a subset of channels have the ability to run
during sleep states. I suppose this needs to be taken into account as well.
Could you please elaborate on our requirements in that area ?
--
Regards,
Laurent Pinchart