forked from 96boards-bubblegum/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
h8300_timer8: 8bit clockevent device h8300_timer16 / h8300_tpu: 16bit clocksource Signed-off-by: Yoshinori Sato <[email protected]>
- Loading branch information
Showing
8 changed files
with
855 additions
and
0 deletions.
There are no files selected for viewing
25 changes: 25 additions & 0 deletions
25
Documentation/devicetree/bindings/timer/renesas,16bit-timer.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
* Renesas H8/300 16bit timer | ||
|
||
The 16bit timer is a 16bit timer/counter with configurable clock inputs and | ||
programmable compare match. | ||
|
||
Required Properties: | ||
|
||
- compatible: must contain "renesas,16bit-timer" | ||
- reg: base address and length of the registers block for the timer module. | ||
- interrupts: interrupt-specifier for the timer, IMIA | ||
- clocks: a list of phandle, one for each entry in clock-names. | ||
- clock-names: must contain "peripheral_clk" for the functional clock. | ||
- renesas,channel: timer channel number. | ||
|
||
Example: | ||
|
||
timer16: timer@ffff68 { | ||
compatible = "reneas,16bit-timer"; | ||
reg = <0xffff68 8>, <0xffff60 8>; | ||
interrupts = <24>; | ||
renesas,channel = <0>; | ||
clocks = <&pclk>; | ||
clock-names = "peripheral_clk"; | ||
}; | ||
|
25 changes: 25 additions & 0 deletions
25
Documentation/devicetree/bindings/timer/renesas,8bit-timer.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
* Renesas H8/300 8bit timer | ||
|
||
The 8bit timer is a 8bit timer/counter with configurable clock inputs and | ||
programmable compare match. | ||
|
||
This implement only supported cascade mode. | ||
|
||
Required Properties: | ||
|
||
- compatible: must contain "renesas,8bit-timer" | ||
- reg: base address and length of the registers block for the timer module. | ||
- interrupts: interrupt-specifier for the timer, CMIA and TOVI | ||
- clocks: a list of phandle, one for each entry in clock-names. | ||
- clock-names: must contain "fck" for the functional clock. | ||
|
||
Example: | ||
|
||
timer8_0: timer@ffff80 { | ||
compatible = "renesas,8bit-timer"; | ||
reg = <0xffff80 10>; | ||
interrupts = <36>; | ||
clocks = <&fclk>; | ||
clock-names = "fck"; | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
* Renesas H8/300 Timer Pluse Unit | ||
|
||
The TPU is a 16bit timer/counter with configurable clock inputs and | ||
programmable compare match. | ||
This implementation support only cascade mode. | ||
|
||
Required Properties: | ||
|
||
- compatible: must contain "renesas,tpu" | ||
- reg: base address and length of the registers block in 2 channel. | ||
- clocks: a list of phandle, one for each entry in clock-names. | ||
- clock-names: must contain "peripheral_clk" for the functional clock. | ||
|
||
|
||
Example: | ||
tpu: tpu@ffffe0 { | ||
compatible = "renesas,tpu"; | ||
reg = <0xffffe0 16>, <0xfffff0 12>; | ||
clocks = <&pclk>; | ||
clock-names = "peripheral_clk"; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,254 @@ | ||
/* | ||
* H8/300 16bit Timer driver | ||
* | ||
* Copyright 2015 Yoshinori Sato <[email protected]> | ||
*/ | ||
|
||
#include <linux/errno.h> | ||
#include <linux/kernel.h> | ||
#include <linux/param.h> | ||
#include <linux/string.h> | ||
#include <linux/slab.h> | ||
#include <linux/interrupt.h> | ||
#include <linux/init.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/clocksource.h> | ||
#include <linux/module.h> | ||
#include <linux/clk.h> | ||
#include <linux/io.h> | ||
#include <linux/of.h> | ||
|
||
#include <asm/segment.h> | ||
#include <asm/irq.h> | ||
|
||
#define TSTR 0 | ||
#define TSNC 1 | ||
#define TMDR 2 | ||
#define TOLR 3 | ||
#define TISRA 4 | ||
#define TISRB 5 | ||
#define TISRC 6 | ||
|
||
#define TCR 0 | ||
#define TIOR 1 | ||
#define TCNT 2 | ||
#define GRA 4 | ||
#define GRB 6 | ||
|
||
#define FLAG_REPROGRAM (1 << 0) | ||
#define FLAG_SKIPEVENT (1 << 1) | ||
#define FLAG_IRQCONTEXT (1 << 2) | ||
#define FLAG_STARTED (1 << 3) | ||
|
||
#define ONESHOT 0 | ||
#define PERIODIC 1 | ||
|
||
#define RELATIVE 0 | ||
#define ABSOLUTE 1 | ||
|
||
struct timer16_priv { | ||
struct platform_device *pdev; | ||
struct clocksource cs; | ||
struct irqaction irqaction; | ||
unsigned long total_cycles; | ||
unsigned long mapbase; | ||
unsigned long mapcommon; | ||
unsigned long flags; | ||
unsigned short gra; | ||
unsigned short cs_enabled; | ||
unsigned char enb; | ||
unsigned char imfa; | ||
unsigned char imiea; | ||
unsigned char ovf; | ||
raw_spinlock_t lock; | ||
struct clk *clk; | ||
}; | ||
|
||
static unsigned long timer16_get_counter(struct timer16_priv *p) | ||
{ | ||
unsigned long v1, v2, v3; | ||
int o1, o2; | ||
|
||
o1 = ctrl_inb(p->mapcommon + TISRC) & p->ovf; | ||
|
||
/* Make sure the timer value is stable. Stolen from acpi_pm.c */ | ||
do { | ||
o2 = o1; | ||
v1 = ctrl_inw(p->mapbase + TCNT); | ||
v2 = ctrl_inw(p->mapbase + TCNT); | ||
v3 = ctrl_inw(p->mapbase + TCNT); | ||
o1 = ctrl_inb(p->mapcommon + TISRC) & p->ovf; | ||
} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3) | ||
|| (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); | ||
|
||
v2 |= 0x10000; | ||
return v2; | ||
} | ||
|
||
|
||
static irqreturn_t timer16_interrupt(int irq, void *dev_id) | ||
{ | ||
struct timer16_priv *p = (struct timer16_priv *)dev_id; | ||
|
||
ctrl_outb(ctrl_inb(p->mapcommon + TISRA) & ~p->imfa, | ||
p->mapcommon + TISRA); | ||
p->total_cycles += 0x10000; | ||
|
||
return IRQ_HANDLED; | ||
} | ||
|
||
static inline struct timer16_priv *cs_to_priv(struct clocksource *cs) | ||
{ | ||
return container_of(cs, struct timer16_priv, cs); | ||
} | ||
|
||
static cycle_t timer16_clocksource_read(struct clocksource *cs) | ||
{ | ||
struct timer16_priv *p = cs_to_priv(cs); | ||
unsigned long flags, raw; | ||
unsigned long value; | ||
|
||
raw_spin_lock_irqsave(&p->lock, flags); | ||
value = p->total_cycles; | ||
raw = timer16_get_counter(p); | ||
raw_spin_unlock_irqrestore(&p->lock, flags); | ||
|
||
return value + raw; | ||
} | ||
|
||
static int timer16_enable(struct clocksource *cs) | ||
{ | ||
struct timer16_priv *p = cs_to_priv(cs); | ||
|
||
WARN_ON(p->cs_enabled); | ||
|
||
p->total_cycles = 0; | ||
ctrl_outw(0x0000, p->mapbase + TCNT); | ||
ctrl_outb(0x83, p->mapbase + TCR); | ||
ctrl_outb(ctrl_inb(p->mapcommon + TSTR) | p->enb, | ||
p->mapcommon + TSTR); | ||
|
||
p->cs_enabled = true; | ||
return 0; | ||
} | ||
|
||
static void timer16_disable(struct clocksource *cs) | ||
{ | ||
struct timer16_priv *p = cs_to_priv(cs); | ||
|
||
WARN_ON(!p->cs_enabled); | ||
|
||
ctrl_outb(ctrl_inb(p->mapcommon + TSTR) & ~p->enb, | ||
p->mapcommon + TSTR); | ||
|
||
p->cs_enabled = false; | ||
} | ||
|
||
#define REG_CH 0 | ||
#define REG_COMM 1 | ||
|
||
static int timer16_setup(struct timer16_priv *p, struct platform_device *pdev) | ||
{ | ||
struct resource *res[2]; | ||
int ret, irq; | ||
unsigned int ch; | ||
|
||
memset(p, 0, sizeof(*p)); | ||
p->pdev = pdev; | ||
|
||
res[REG_CH] = platform_get_resource(p->pdev, | ||
IORESOURCE_MEM, REG_CH); | ||
res[REG_COMM] = platform_get_resource(p->pdev, | ||
IORESOURCE_MEM, REG_COMM); | ||
if (!res[REG_CH] || !res[REG_COMM]) { | ||
dev_err(&p->pdev->dev, "failed to get I/O memory\n"); | ||
return -ENXIO; | ||
} | ||
irq = platform_get_irq(p->pdev, 0); | ||
if (irq < 0) { | ||
dev_err(&p->pdev->dev, "failed to get irq\n"); | ||
return irq; | ||
} | ||
|
||
p->clk = clk_get(&p->pdev->dev, "fck"); | ||
if (IS_ERR(p->clk)) { | ||
dev_err(&p->pdev->dev, "can't get clk\n"); | ||
return PTR_ERR(p->clk); | ||
} | ||
of_property_read_u32(p->pdev->dev.of_node, "renesas,channel", &ch); | ||
|
||
p->pdev = pdev; | ||
p->mapbase = res[REG_CH]->start; | ||
p->mapcommon = res[REG_COMM]->start; | ||
p->enb = 1 << ch; | ||
p->imfa = 1 << ch; | ||
p->imiea = 1 << (4 + ch); | ||
p->cs.name = pdev->name; | ||
p->cs.rating = 200; | ||
p->cs.read = timer16_clocksource_read; | ||
p->cs.enable = timer16_enable; | ||
p->cs.disable = timer16_disable; | ||
p->cs.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8); | ||
p->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; | ||
|
||
ret = request_irq(irq, timer16_interrupt, | ||
IRQF_TIMER, pdev->name, p); | ||
if (ret < 0) { | ||
dev_err(&p->pdev->dev, "failed to request irq %d\n", irq); | ||
return ret; | ||
} | ||
|
||
clocksource_register_hz(&p->cs, clk_get_rate(p->clk) / 8); | ||
|
||
return 0; | ||
} | ||
|
||
static int timer16_probe(struct platform_device *pdev) | ||
{ | ||
struct timer16_priv *p = platform_get_drvdata(pdev); | ||
|
||
if (p) { | ||
dev_info(&pdev->dev, "kept as earlytimer\n"); | ||
return 0; | ||
} | ||
|
||
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); | ||
if (!p) | ||
return -ENOMEM; | ||
|
||
return timer16_setup(p, pdev); | ||
} | ||
|
||
static int timer16_remove(struct platform_device *pdev) | ||
{ | ||
return -EBUSY; | ||
} | ||
|
||
static const struct of_device_id timer16_of_table[] = { | ||
{ .compatible = "renesas,16bit-timer" }, | ||
{ } | ||
}; | ||
static struct platform_driver timer16_driver = { | ||
.probe = timer16_probe, | ||
.remove = timer16_remove, | ||
.driver = { | ||
.name = "h8300h-16timer", | ||
.of_match_table = of_match_ptr(timer16_of_table), | ||
} | ||
}; | ||
|
||
static int __init timer16_init(void) | ||
{ | ||
return platform_driver_register(&timer16_driver); | ||
} | ||
|
||
static void __exit timer16_exit(void) | ||
{ | ||
platform_driver_unregister(&timer16_driver); | ||
} | ||
|
||
subsys_initcall(timer16_init); | ||
module_exit(timer16_exit); | ||
MODULE_AUTHOR("Yoshinori Sato"); | ||
MODULE_DESCRIPTION("H8/300H 16bit Timer Driver"); | ||
MODULE_LICENSE("GPL v2"); |
Oops, something went wrong.