summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/timer/renesas,cmt.txt47
-rw-r--r--Documentation/devicetree/bindings/timer/renesas,mtu2.txt39
-rw-r--r--Documentation/devicetree/bindings/timer/renesas,tmu.txt39
-rw-r--r--drivers/clocksource/sh_cmt.c233
-rw-r--r--drivers/clocksource/sh_mtu2.c146
-rw-r--r--drivers/clocksource/sh_tmu.c127
-rw-r--r--include/linux/sh_timer.h5
7 files changed, 309 insertions, 327 deletions
diff --git a/Documentation/devicetree/bindings/timer/renesas,cmt.txt b/Documentation/devicetree/bindings/timer/renesas,cmt.txt
new file mode 100644
index 000000000000..a17418b0ece3
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/renesas,cmt.txt
@@ -0,0 +1,47 @@
+* 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 hardware 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. Channel indices represent the hardware
+position of the channel in the CMT and don't match the channel numbers in the
+datasheets.
+
+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)
+ - "renesas,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.
+ - interrupts: interrupt-specifier for the timer, one per channel.
+ - clocks: a list of phandle + clock-specifier pairs, one for each entry
+ in clock-names.
+ - clock-names: must contain "fck" for the functional clock.
+
+ - renesas,channels-mask: bitmask of the available channels.
+
+
+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>;
+ interrupts = <0 142 IRQ_TYPE_LEVEL_HIGH>,
+ <0 142 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp1_clks R8A7790_CLK_CMT0>;
+ clock-names = "fck";
+
+ renesas,channels-mask = <0x60>;
+ };
diff --git a/Documentation/devicetree/bindings/timer/renesas,mtu2.txt b/Documentation/devicetree/bindings/timer/renesas,mtu2.txt
new file mode 100644
index 000000000000..917453f826bc
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/renesas,mtu2.txt
@@ -0,0 +1,39 @@
+* Renesas R-Car Multi-Function Timer Pulse Unit 2 (MTU2)
+
+The MTU2 is a multi-purpose, multi-channel timer/counter with configurable
+clock inputs and programmable compare match.
+
+Channels share hardware resources but their counter and compare match value
+are independent. The MTU2 hardware supports five channels indexed from 0 to 4.
+
+Required Properties:
+
+ - compatible: must contain "renesas,mtu2"
+
+ - reg: base address and length of the registers block for the timer module.
+
+ - interrupts: interrupt specifiers for the timer, one for each entry in
+ interrupt-names.
+ - interrupt-names: must contain one entry named "tgi?a" for each enabled
+ channel, where "?" is the channel index expressed as one digit from "0" to
+ "4".
+
+ - clocks: a list of phandle + clock-specifier pairs, one for each entry
+ in clock-names.
+ - clock-names: must contain "fck" for the functional clock.
+
+
+Example: R7S72100 (RZ/A1H) MTU2 node
+
+ mtu2: timer@fcff0000 {
+ compatible = "renesas,mtu2";
+ reg = <0xfcff0000 0x400>;
+ interrupts = <0 139 IRQ_TYPE_LEVEL_HIGH>,
+ <0 146 IRQ_TYPE_LEVEL_HIGH>,
+ <0 150 IRQ_TYPE_LEVEL_HIGH>,
+ <0 154 IRQ_TYPE_LEVEL_HIGH>,
+ <0 159 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "tgi0a", "tgi1a", "tgi2a", "tgi3a", "tgi4a";
+ clocks = <&mstp3_clks R7S72100_CLK_MTU2>;
+ clock-names = "fck";
+ };
diff --git a/Documentation/devicetree/bindings/timer/renesas,tmu.txt b/Documentation/devicetree/bindings/timer/renesas,tmu.txt
new file mode 100644
index 000000000000..425d0c5f4aee
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/renesas,tmu.txt
@@ -0,0 +1,39 @@
+* Renesas R-Car Timer Unit (TMU)
+
+The TMU is a 32-bit timer/counter with configurable clock inputs and
+programmable compare match.
+
+Channels share hardware resources but their counter and compare match value
+are independent. The TMU hardware supports up to three channels.
+
+Required Properties:
+
+ - compatible: must contain "renesas,tmu"
+
+ - reg: base address and length of the registers block for the timer module.
+
+ - interrupts: interrupt-specifier for the timer, one per channel.
+
+ - clocks: a list of phandle + clock-specifier pairs, one for each entry
+ in clock-names.
+ - clock-names: must contain "fck" for the functional clock.
+
+Optional Properties:
+
+ - #renesas,channels: number of channels implemented by the timer, must be 2
+ or 3 (if not specified the value defaults to 3).
+
+
+Example: R8A7779 (R-Car H1) TMU0 node
+
+ tmu0: timer@ffd80000 {
+ compatible = "renesas,tmu";
+ reg = <0xffd80000 0x30>;
+ interrupts = <0 32 IRQ_TYPE_LEVEL_HIGH>,
+ <0 33 IRQ_TYPE_LEVEL_HIGH>,
+ <0 34 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp0_clks R8A7779_CLK_TMU0>;
+ clock-names = "fck";
+
+ #renesas,channels = <3>;
+ };
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index dfa780396b91..2bd13b53b727 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>
@@ -114,14 +115,15 @@ 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;
+ raw_spinlock_t lock; /* Protect the shared start/stop register */
+
struct sh_cmt_channel *channels;
unsigned int num_channels;
+ unsigned int hw_channels;
bool has_clockevent;
bool has_clocksource;
@@ -301,14 +303,12 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch,
return v2;
}
-static DEFINE_RAW_SPINLOCK(sh_cmt_lock);
-
static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start)
{
unsigned long flags, value;
/* start stop register shared by multiple timer channels */
- raw_spin_lock_irqsave(&sh_cmt_lock, flags);
+ raw_spin_lock_irqsave(&ch->cmt->lock, flags);
value = sh_cmt_read_cmstr(ch);
if (start)
@@ -317,7 +317,7 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start)
value &= ~(1 << ch->timer_bit);
sh_cmt_write_cmstr(ch, value);
- raw_spin_unlock_irqrestore(&sh_cmt_lock, flags);
+ raw_spin_unlock_irqrestore(&ch->cmt->lock, flags);
}
static int sh_cmt_enable(struct sh_cmt_channel *ch, unsigned long *rate)
@@ -792,7 +792,7 @@ static int sh_cmt_register_clockevent(struct sh_cmt_channel *ch,
int irq;
int ret;
- irq = platform_get_irq(ch->cmt->pdev, ch->cmt->legacy ? 0 : ch->index);
+ irq = platform_get_irq(ch->cmt->pdev, ch->index);
if (irq < 0) {
dev_err(&ch->cmt->pdev->dev, "ch%u: failed to get irq\n",
ch->index);
@@ -863,33 +863,26 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index,
* 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;
}
if (cmt->info->width == (sizeof(ch->max_match_value) * 8))
@@ -900,12 +893,7 @@ 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 (cmt->legacy) {
- ch->timer_bit = ch->hwidx;
- } else {
- ch->timer_bit = cmt->info->model == SH_CMT_48BIT_GEN2
- ? 0 : ch->hwidx;
- }
+ ch->timer_bit = cmt->info->model == SH_CMT_48BIT_GEN2 ? 0 : ch->hwidx;
ret = sh_cmt_register(ch, dev_name(&cmt->pdev->dev),
clockevent, clocksource);
@@ -938,75 +926,65 @@ 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];
+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);
- return 0;
-}
+static const struct of_device_id sh_cmt_of_table[] __maybe_unused = {
+ { .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 = "renesas,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 void sh_cmt_unmap_memory(struct sh_cmt_device *cmt)
+static int sh_cmt_parse_dt(struct sh_cmt_device *cmt)
{
- iounmap(cmt->mapbase);
- if (cmt->mapbase_ch)
- iounmap(cmt->mapbase_ch);
+ struct device_node *np = cmt->pdev->dev.of_node;
+
+ return of_property_read_u32(np, "renesas,channels-mask",
+ &cmt->hw_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;
- unsigned int hw_channels;
+ unsigned int mask;
+ unsigned int i;
int ret;
memset(cmt, 0, sizeof(*cmt));
cmt->pdev = pdev;
+ raw_spin_lock_init(&cmt->lock);
+
+ 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;
- if (!cfg) {
+ ret = sh_cmt_parse_dt(cmt);
+ if (ret < 0)
+ return ret;
+ } 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->hw_channels = cfg->channels_mask;
+ } else {
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->legacy ? "cmt_fck" : "fck");
+ cmt->clk = clk_get(&cmt->pdev->dev, "fck");
if (IS_ERR(cmt->clk)) {
dev_err(&cmt->pdev->dev, "cannot get clock\n");
return PTR_ERR(cmt->clk);
@@ -1016,28 +994,13 @@ 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;
- hw_channels = 0;
- } else {
- cmt->num_channels = hweight8(cfg->channels_mask);
- hw_channels = cfg->channels_mask;
- }
-
+ cmt->num_channels = hweight8(cmt->hw_channels);
cmt->channels = kzalloc(cmt->num_channels * sizeof(*cmt->channels),
GFP_KERNEL);
if (cmt->channels == NULL) {
@@ -1045,35 +1008,21 @@ 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, cfg->timer_bit,
- cfg->clockevent_rating != 0,
- cfg->clocksource_rating != 0, cmt);
+ /*
+ * Use the first channel as a clock event device and the second channel
+ * as a clock source. If only one channel is available use it for both.
+ */
+ for (i = 0, mask = cmt->hw_channels; i < cmt->num_channels; ++i) {
+ unsigned int hwidx = ffs(mask) - 1;
+ bool clocksource = i == 1 || cmt->num_channels == 1;
+ bool clockevent = i == 0;
+
+ ret = sh_cmt_setup_channel(&cmt->channels[i], i, hwidx,
+ clockevent, clocksource, cmt);
if (ret < 0)
goto err_unmap;
- } else {
- unsigned int mask = hw_channels;
- unsigned int i;
- /*
- * Use the first channel as a clock event device and the second
- * channel as a clock source. If only one channel is available
- * use it for both.
- */
- for (i = 0; i < cmt->num_channels; ++i) {
- unsigned int hwidx = ffs(mask) - 1;
- bool clocksource = i == 1 || cmt->num_channels == 1;
- bool clockevent = i == 0;
-
- ret = sh_cmt_setup_channel(&cmt->channels[i], i, hwidx,
- clockevent, clocksource,
- cmt);
- if (ret < 0)
- goto err_unmap;
-
- mask &= ~(1 << hwidx);
- }
+ mask &= ~(1 << hwidx);
}
platform_set_drvdata(pdev, cmt);
@@ -1082,7 +1031,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:
@@ -1132,22 +1081,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", 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",
+ .of_match_table = of_match_ptr(sh_cmt_of_table),
},
.id_table = sh_cmt_id_table,
};
diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c
index 188d4e092efc..3d88698cf2b8 100644
--- a/drivers/clocksource/sh_mtu2.c
+++ b/drivers/clocksource/sh_mtu2.c
@@ -23,6 +23,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>
@@ -37,7 +38,6 @@ struct sh_mtu2_channel {
unsigned int index;
void __iomem *base;
- int irq;
struct clock_event_device ced;
};
@@ -48,15 +48,14 @@ struct sh_mtu2_device {
void __iomem *mapbase;
struct clk *clk;
+ raw_spinlock_t lock; /* Protect the shared registers */
+
struct sh_mtu2_channel *channels;
unsigned int num_channels;
- bool legacy;
bool has_clockevent;
};
-static DEFINE_RAW_SPINLOCK(sh_mtu2_lock);
-
#define TSTR -1 /* shared register */
#define TCR 0 /* channel register */
#define TMDR 1 /* channel register */
@@ -162,12 +161,8 @@ static inline unsigned long sh_mtu2_read(struct sh_mtu2_channel *ch, int reg_nr)
{
unsigned long offs;
- if (reg_nr == TSTR) {
- if (ch->mtu->legacy)
- return ioread8(ch->mtu->mapbase);
- else
- return ioread8(ch->mtu->mapbase + 0x280);
- }
+ if (reg_nr == TSTR)
+ return ioread8(ch->mtu->mapbase + 0x280);
offs = mtu2_reg_offs[reg_nr];
@@ -182,12 +177,8 @@ static inline void sh_mtu2_write(struct sh_mtu2_channel *ch, int reg_nr,
{
unsigned long offs;
- if (reg_nr == TSTR) {
- if (ch->mtu->legacy)
- return iowrite8(value, ch->mtu->mapbase);
- else
- return iowrite8(value, ch->mtu->mapbase + 0x280);
- }
+ if (reg_nr == TSTR)
+ return iowrite8(value, ch->mtu->mapbase + 0x280);
offs = mtu2_reg_offs[reg_nr];
@@ -202,7 +193,7 @@ static void sh_mtu2_start_stop_ch(struct sh_mtu2_channel *ch, int start)
unsigned long flags, value;
/* start stop register shared by multiple timer channels */
- raw_spin_lock_irqsave(&sh_mtu2_lock, flags);
+ raw_spin_lock_irqsave(&ch->mtu->lock, flags);
value = sh_mtu2_read(ch, TSTR);
if (start)
@@ -211,7 +202,7 @@ static void sh_mtu2_start_stop_ch(struct sh_mtu2_channel *ch, int start)
value &= ~(1 << ch->index);
sh_mtu2_write(ch, TSTR, value);
- raw_spin_unlock_irqrestore(&sh_mtu2_lock, flags);
+ raw_spin_unlock_irqrestore(&ch->mtu->lock, flags);
}
static int sh_mtu2_enable(struct sh_mtu2_channel *ch)
@@ -331,7 +322,6 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch,
const char *name)
{
struct clock_event_device *ced = &ch->ced;
- int ret;
ced->name = name;
ced->features = CLOCK_EVT_FEAT_PERIODIC;
@@ -344,24 +334,12 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch,
dev_info(&ch->mtu->pdev->dev, "ch%u: used for clock events\n",
ch->index);
clockevents_register_device(ced);
-
- ret = request_irq(ch->irq, sh_mtu2_interrupt,
- IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
- dev_name(&ch->mtu->pdev->dev), ch);
- if (ret) {
- dev_err(&ch->mtu->pdev->dev, "ch%u: failed to request irq %d\n",
- ch->index, ch->irq);
- return;
- }
}
-static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name,
- bool clockevent)
+static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name)
{
- if (clockevent) {
- ch->mtu->has_clockevent = true;
- sh_mtu2_register_clockevent(ch, name);
- }
+ ch->mtu->has_clockevent = true;
+ sh_mtu2_register_clockevent(ch, name);
return 0;
}
@@ -372,40 +350,32 @@ static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, unsigned int index,
static const unsigned int channel_offsets[] = {
0x300, 0x380, 0x000,
};
- bool clockevent;
+ char name[6];
+ int irq;
+ int ret;
ch->mtu = mtu;
- if (mtu->legacy) {
- struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
-
- clockevent = cfg->clockevent_rating != 0;
-
- ch->irq = platform_get_irq(mtu->pdev, 0);
- ch->base = mtu->mapbase - cfg->channel_offset;
- ch->index = cfg->timer_bit;
- } else {
- char name[6];
-
- clockevent = true;
-
- sprintf(name, "tgi%ua", index);
- ch->irq = platform_get_irq_byname(mtu->pdev, name);
- ch->base = mtu->mapbase + channel_offsets[index];
- ch->index = index;
- }
-
- if (ch->irq < 0) {
+ sprintf(name, "tgi%ua", index);
+ irq = platform_get_irq_byname(mtu->pdev, name);
+ if (irq < 0) {
/* Skip channels with no declared interrupt. */
- if (!mtu->legacy)
- return 0;
+ return 0;
+ }
- dev_err(&mtu->pdev->dev, "ch%u: failed to get irq\n",
- ch->index);
- return ch->irq;
+ ret = request_irq(irq, sh_mtu2_interrupt,
+ IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
+ dev_name(&ch->mtu->pdev->dev), ch);
+ if (ret) {
+ dev_err(&ch->mtu->pdev->dev, "ch%u: failed to request irq %d\n",
+ index, irq);
+ return ret;
}
- return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev), clockevent);
+ ch->base = mtu->mapbase + channel_offsets[index];
+ ch->index = index;
+
+ return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev));
}
static int sh_mtu2_map_memory(struct sh_mtu2_device *mtu)
@@ -422,46 +392,21 @@ static int sh_mtu2_map_memory(struct sh_mtu2_device *mtu)
if (mtu->mapbase == NULL)
return -ENXIO;
- /*
- * In legacy platform device configuration (with one device per channel)
- * the resource points to the channel base address.
- */
- if (mtu->legacy) {
- struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
- mtu->mapbase += cfg->channel_offset;
- }
-
return 0;
}
-static void sh_mtu2_unmap_memory(struct sh_mtu2_device *mtu)
-{
- if (mtu->legacy) {
- struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
- mtu->mapbase -= cfg->channel_offset;
- }
-
- iounmap(mtu->mapbase);
-}
-
static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
struct platform_device *pdev)
{
- struct sh_timer_config *cfg = pdev->dev.platform_data;
- const struct platform_device_id *id = pdev->id_entry;
unsigned int i;
int ret;
mtu->pdev = pdev;
- mtu->legacy = id->driver_data;
- if (mtu->legacy && !cfg) {
- dev_err(&mtu->pdev->dev, "missing platform data\n");
- return -ENXIO;
- }
+ raw_spin_lock_init(&mtu->lock);
/* Get hold of clock. */
- mtu->clk = clk_get(&mtu->pdev->dev, mtu->legacy ? "mtu2_fck" : "fck");
+ mtu->clk = clk_get(&mtu->pdev->dev, "fck");
if (IS_ERR(mtu->clk)) {
dev_err(&mtu->pdev->dev, "cannot get clock\n");
return PTR_ERR(mtu->clk);
@@ -479,10 +424,7 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
}
/* Allocate and setup the channels. */
- if (mtu->legacy)
- mtu->num_channels = 1;
- else
- mtu->num_channels = 3;
+ mtu->num_channels = 3;
mtu->channels = kzalloc(sizeof(*mtu->channels) * mtu->num_channels,
GFP_KERNEL);
@@ -491,16 +433,10 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
goto err_unmap;
}
- if (mtu->legacy) {
- ret = sh_mtu2_setup_channel(&mtu->channels[0], 0, mtu);
+ for (i = 0; i < mtu->num_channels; ++i) {
+ ret = sh_mtu2_setup_channel(&mtu->channels[i], i, mtu);
if (ret < 0)
goto err_unmap;
- } else {
- for (i = 0; i < mtu->num_channels; ++i) {
- ret = sh_mtu2_setup_channel(&mtu->channels[i], i, mtu);
- if (ret < 0)
- goto err_unmap;
- }
}
platform_set_drvdata(pdev, mtu);
@@ -509,7 +445,7 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
err_unmap:
kfree(mtu->channels);
- sh_mtu2_unmap_memory(mtu);
+ iounmap(mtu->mapbase);
err_clk_unprepare:
clk_unprepare(mtu->clk);
err_clk_put:
@@ -560,17 +496,23 @@ static int sh_mtu2_remove(struct platform_device *pdev)
}
static const struct platform_device_id sh_mtu2_id_table[] = {
- { "sh_mtu2", 1 },
{ "sh-mtu2", 0 },
{ },
};
MODULE_DEVICE_TABLE(platform, sh_mtu2_id_table);
+static const struct of_device_id sh_mtu2_of_table[] __maybe_unused = {
+ { .compatible = "renesas,mtu2" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sh_mtu2_of_table);
+
static struct platform_driver sh_mtu2_device_driver = {
.probe = sh_mtu2_probe,
.remove = sh_mtu2_remove,
.driver = {
.name = "sh_mtu2",
+ .of_match_table = of_match_ptr(sh_mtu2_of_table),
},
.id_table = sh_mtu2_id_table,
};
diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c
index 6bd17a8f3dd4..0f665b8f2461 100644
--- a/drivers/clocksource/sh_tmu.c
+++ b/drivers/clocksource/sh_tmu.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>
@@ -32,7 +33,6 @@
#include <linux/spinlock.h>
enum sh_tmu_model {
- SH_TMU_LEGACY,
SH_TMU,
SH_TMU_SH3,
};
@@ -62,6 +62,8 @@ struct sh_tmu_device {
enum sh_tmu_model model;
+ raw_spinlock_t lock; /* Protect the shared start/stop register */
+
struct sh_tmu_channel *channels;
unsigned int num_channels;
@@ -69,8 +71,6 @@ struct sh_tmu_device {
bool has_clocksource;
};
-static DEFINE_RAW_SPINLOCK(sh_tmu_lock);
-
#define TSTR -1 /* shared register */
#define TCOR 0 /* channel register */
#define TCNT 1 /* channel register */
@@ -91,8 +91,6 @@ static inline unsigned long sh_tmu_read(struct sh_tmu_channel *ch, int reg_nr)
if (reg_nr == TSTR) {
switch (ch->tmu->model) {
- case SH_TMU_LEGACY:
- return ioread8(ch->tmu->mapbase);
case SH_TMU_SH3:
return ioread8(ch->tmu->mapbase + 2);
case SH_TMU:
@@ -115,8 +113,6 @@ static inline void sh_tmu_write(struct sh_tmu_channel *ch, int reg_nr,
if (reg_nr == TSTR) {
switch (ch->tmu->model) {
- case SH_TMU_LEGACY:
- return iowrite8(value, ch->tmu->mapbase);
case SH_TMU_SH3:
return iowrite8(value, ch->tmu->mapbase + 2);
case SH_TMU:
@@ -137,7 +133,7 @@ static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start)
unsigned long flags, value;
/* start stop register shared by multiple timer channels */
- raw_spin_lock_irqsave(&sh_tmu_lock, flags);
+ raw_spin_lock_irqsave(&ch->tmu->lock, flags);
value = sh_tmu_read(ch, TSTR);
if (start)
@@ -146,7 +142,7 @@ static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start)
value &= ~(1 << ch->index);
sh_tmu_write(ch, TSTR, value);
- raw_spin_unlock_irqrestore(&sh_tmu_lock, flags);
+ raw_spin_unlock_irqrestore(&ch->tmu->lock, flags);
}
static int __sh_tmu_enable(struct sh_tmu_channel *ch)
@@ -476,27 +472,12 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, unsigned int index,
return 0;
ch->tmu = tmu;
+ ch->index = index;
- if (tmu->model == SH_TMU_LEGACY) {
- struct sh_timer_config *cfg = tmu->pdev->dev.platform_data;
-
- /*
- * The SH3 variant (SH770x, SH7705, SH7710 and SH7720) maps
- * channel registers blocks at base + 2 + 12 * index, while all
- * other variants map them at base + 4 + 12 * index. We can
- * compute the index by just dividing by 12, the 2 bytes or 4
- * bytes offset being hidden by the integer division.
- */
- ch->index = cfg->channel_offset / 12;
- ch->base = tmu->mapbase + cfg->channel_offset;
- } else {
- ch->index = index;
-
- if (tmu->model == SH_TMU_SH3)
- ch->base = tmu->mapbase + 4 + ch->index * 12;
- else
- ch->base = tmu->mapbase + 8 + ch->index * 12;
- }
+ if (tmu->model == SH_TMU_SH3)
+ ch->base = tmu->mapbase + 4 + ch->index * 12;
+ else
+ ch->base = tmu->mapbase + 8 + ch->index * 12;
ch->irq = platform_get_irq(tmu->pdev, index);
if (ch->irq < 0) {
@@ -526,46 +507,53 @@ static int sh_tmu_map_memory(struct sh_tmu_device *tmu)
if (tmu->mapbase == NULL)
return -ENXIO;
- /*
- * In legacy platform device configuration (with one device per channel)
- * the resource points to the channel base address.
- */
- if (tmu->model == SH_TMU_LEGACY) {
- struct sh_timer_config *cfg = tmu->pdev->dev.platform_data;
- tmu->mapbase -= cfg->channel_offset;
- }
-
return 0;
}
-static void sh_tmu_unmap_memory(struct sh_tmu_device *tmu)
+static int sh_tmu_parse_dt(struct sh_tmu_device *tmu)
{
- if (tmu->model == SH_TMU_LEGACY) {
- struct sh_timer_config *cfg = tmu->pdev->dev.platform_data;
- tmu->mapbase += cfg->channel_offset;
+ struct device_node *np = tmu->pdev->dev.of_node;
+
+ tmu->model = SH_TMU;
+ tmu->num_channels = 3;
+
+ of_property_read_u32(np, "#renesas,channels", &tmu->num_channels);
+
+ if (tmu->num_channels != 2 && tmu->num_channels != 3) {
+ dev_err(&tmu->pdev->dev, "invalid number of channels %u\n",
+ tmu->num_channels);
+ return -EINVAL;
}
- iounmap(tmu->mapbase);
+ return 0;
}
static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
{
- struct sh_timer_config *cfg = pdev->dev.platform_data;
- const struct platform_device_id *id = pdev->id_entry;
unsigned int i;
int ret;
- if (!cfg) {
+ tmu->pdev = pdev;
+
+ raw_spin_lock_init(&tmu->lock);
+
+ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+ ret = sh_tmu_parse_dt(tmu);
+ if (ret < 0)
+ return ret;
+ } else if (pdev->dev.platform_data) {
+ const struct platform_device_id *id = pdev->id_entry;
+ struct sh_timer_config *cfg = pdev->dev.platform_data;
+
+ tmu->model = id->driver_data;
+ tmu->num_channels = hweight8(cfg->channels_mask);
+ } else {
dev_err(&tmu->pdev->dev, "missing platform data\n");
return -ENXIO;
}
- tmu->pdev = pdev;
- tmu->model = id->driver_data;
-
/* Get hold of clock. */
- tmu->clk = clk_get(&tmu->pdev->dev,
- tmu->model == SH_TMU_LEGACY ? "tmu_fck" : "fck");
+ tmu->clk = clk_get(&tmu->pdev->dev, "fck");
if (IS_ERR(tmu->clk)) {
dev_err(&tmu->pdev->dev, "cannot get clock\n");
return PTR_ERR(tmu->clk);
@@ -583,11 +571,6 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
}
/* Allocate and setup the channels. */
- if (tmu->model == SH_TMU_LEGACY)
- tmu->num_channels = 1;
- else
- tmu->num_channels = hweight8(cfg->channels_mask);
-
tmu->channels = kzalloc(sizeof(*tmu->channels) * tmu->num_channels,
GFP_KERNEL);
if (tmu->channels == NULL) {
@@ -595,23 +578,15 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
goto err_unmap;
}
- if (tmu->model == SH_TMU_LEGACY) {
- ret = sh_tmu_channel_setup(&tmu->channels[0], 0,
- cfg->clockevent_rating != 0,
- cfg->clocksource_rating != 0, tmu);
+ /*
+ * Use the first channel as a clock event device and the second channel
+ * as a clock source.
+ */
+ for (i = 0; i < tmu->num_channels; ++i) {
+ ret = sh_tmu_channel_setup(&tmu->channels[i], i,
+ i == 0, i == 1, tmu);
if (ret < 0)
goto err_unmap;
- } else {
- /*
- * Use the first channel as a clock event device and the second
- * channel as a clock source.
- */
- for (i = 0; i < tmu->num_channels; ++i) {
- ret = sh_tmu_channel_setup(&tmu->channels[i], i,
- i == 0, i == 1, tmu);
- if (ret < 0)
- goto err_unmap;
- }
}
platform_set_drvdata(pdev, tmu);
@@ -620,7 +595,7 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
err_unmap:
kfree(tmu->channels);
- sh_tmu_unmap_memory(tmu);
+ iounmap(tmu->mapbase);
err_clk_unprepare:
clk_unprepare(tmu->clk);
err_clk_put:
@@ -671,18 +646,24 @@ static int sh_tmu_remove(struct platform_device *pdev)
}
static const struct platform_device_id sh_tmu_id_table[] = {
- { "sh_tmu", SH_TMU_LEGACY },
{ "sh-tmu", SH_TMU },
{ "sh-tmu-sh3", SH_TMU_SH3 },
{ }
};
MODULE_DEVICE_TABLE(platform, sh_tmu_id_table);
+static const struct of_device_id sh_tmu_of_table[] __maybe_unused = {
+ { .compatible = "renesas,tmu" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sh_tmu_of_table);
+
static struct platform_driver sh_tmu_device_driver = {
.probe = sh_tmu_probe,
.remove = sh_tmu_remove,
.driver = {
.name = "sh_tmu",
+ .of_match_table = of_match_ptr(sh_tmu_of_table),
},
.id_table = sh_tmu_id_table,
};
diff --git a/include/linux/sh_timer.h b/include/linux/sh_timer.h
index 8e1e036d6d45..64638b058076 100644
--- a/include/linux/sh_timer.h
+++ b/include/linux/sh_timer.h
@@ -2,11 +2,6 @@
#define __SH_TIMER_H__
struct sh_timer_config {
- char *name;
- long channel_offset;
- int timer_bit;
- unsigned long clockevent_rating;
- unsigned long clocksource_rating;
unsigned int channels_mask;
};