mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-20 21:04:40 +08:00
Merge branch 'clockevents/renesas-timers-dt' into clockevents/3.17
This commit is contained in:
commit
de2ea58155
47
Documentation/devicetree/bindings/timer/renesas,cmt.txt
Normal file
47
Documentation/devicetree/bindings/timer/renesas,cmt.txt
Normal file
@ -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>;
|
||||
};
|
39
Documentation/devicetree/bindings/timer/renesas,mtu2.txt
Normal file
39
Documentation/devicetree/bindings/timer/renesas,mtu2.txt
Normal file
@ -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";
|
||||
};
|
39
Documentation/devicetree/bindings/timer/renesas,tmu.txt
Normal file
39
Documentation/devicetree/bindings/timer/renesas,tmu.txt
Normal file
@ -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>;
|
||||
};
|
@ -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)
|
||||
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[] __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 int sh_cmt_parse_dt(struct sh_cmt_device *cmt)
|
||||
{
|
||||
struct sh_timer_config *cfg = cmt->pdev->dev.platform_data;
|
||||
struct resource *res, *res2;
|
||||
struct device_node *np = cmt->pdev->dev.of_node;
|
||||
|
||||
/* 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);
|
||||
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 (!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;
|
||||
|
||||
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,
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
dev_err(&mtu->pdev->dev, "ch%u: failed to get irq\n",
|
||||
ch->index);
|
||||
return ch->irq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev), clockevent);
|
||||
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;
|
||||
}
|
||||
|
||||
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 int sh_tmu_parse_dt(struct sh_tmu_device *tmu)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sh_tmu_unmap_memory(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;
|
||||
}
|
||||
|
||||
iounmap(tmu->mapbase);
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user