Commit de2ea581 authored by Daniel Lezcano's avatar Daniel Lezcano

Merge branch 'clockevents/renesas-timers-dt' into clockevents/3.17

parents afdb0943 cca8d059
* 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>;
};
* 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";
};
* 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>;
};
This diff is collapsed.
......@@ -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,
};
......
......@@ -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,
};
......
......@@ -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;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment