diff options
Diffstat (limited to 'drivers/clocksource/h8300_timer8.c')
| -rw-r--r-- | drivers/clocksource/h8300_timer8.c | 264 | 
1 files changed, 81 insertions, 183 deletions
diff --git a/drivers/clocksource/h8300_timer8.c b/drivers/clocksource/h8300_timer8.c index 44375d8b9bc4..c151941e1956 100644 --- a/drivers/clocksource/h8300_timer8.c +++ b/drivers/clocksource/h8300_timer8.c @@ -8,19 +8,15 @@   */  #include <linux/errno.h> -#include <linux/sched.h>  #include <linux/kernel.h>  #include <linux/interrupt.h>  #include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/slab.h>  #include <linux/clockchips.h> -#include <linux/module.h>  #include <linux/clk.h>  #include <linux/io.h>  #include <linux/of.h> - -#include <asm/irq.h> +#include <linux/of_address.h> +#include <linux/of_irq.h>  #define _8TCR	0  #define _8TCSR	2 @@ -28,126 +24,74 @@  #define TCORB	6  #define _8TCNT	8 -#define FLAG_REPROGRAM (1 << 0) -#define FLAG_SKIPEVENT (1 << 1) -#define FLAG_IRQCONTEXT (1 << 2) +#define CMIEA	6 +#define CMFA	6 +  #define FLAG_STARTED (1 << 3) -#define ONESHOT  0 -#define PERIODIC 1 +#define SCALE 64 -#define RELATIVE 0 -#define ABSOLUTE 1 +#define bset(b, a) iowrite8(ioread8(a) | (1 << (b)), (a)) +#define bclr(b, a) iowrite8(ioread8(a) & ~(1 << (b)), (a))  struct timer8_priv { -	struct platform_device *pdev;  	struct clock_event_device ced; -	struct irqaction irqaction; -	unsigned long mapbase; -	raw_spinlock_t lock; +	void __iomem *mapbase;  	unsigned long flags;  	unsigned int rate; -	unsigned int tcora; -	struct clk *pclk;  }; -static unsigned long timer8_get_counter(struct timer8_priv *p) -{ -	unsigned long v1, v2, v3; -	int o1, o2; - -	o1 = ctrl_inb(p->mapbase + _8TCSR) & 0x20; - -	/* Make sure the timer value is stable. Stolen from acpi_pm.c */ -	do { -		o2 = o1; -		v1 = ctrl_inw(p->mapbase + _8TCNT); -		v2 = ctrl_inw(p->mapbase + _8TCNT); -		v3 = ctrl_inw(p->mapbase + _8TCNT); -		o1 = ctrl_inb(p->mapbase + _8TCSR) & 0x20; -	} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3) -			  || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); - -	v2 |= o1 << 10; -	return v2; -} -  static irqreturn_t timer8_interrupt(int irq, void *dev_id)  {  	struct timer8_priv *p = dev_id; -	ctrl_outb(ctrl_inb(p->mapbase + _8TCSR) & ~0x40, -		  p->mapbase + _8TCSR); -	p->flags |= FLAG_IRQCONTEXT; -	ctrl_outw(p->tcora, p->mapbase + TCORA); -	if (!(p->flags & FLAG_SKIPEVENT)) { -		if (clockevent_state_oneshot(&p->ced)) -			ctrl_outw(0x0000, p->mapbase + _8TCR); -		p->ced.event_handler(&p->ced); -	} -	p->flags &= ~(FLAG_SKIPEVENT | FLAG_IRQCONTEXT); +	if (clockevent_state_oneshot(&p->ced)) +		iowrite16be(0x0000, p->mapbase + _8TCR); + +	p->ced.event_handler(&p->ced); + +	bclr(CMFA, p->mapbase + _8TCSR);  	return IRQ_HANDLED;  }  static void timer8_set_next(struct timer8_priv *p, unsigned long delta)  { -	unsigned long flags; -	unsigned long now; - -	raw_spin_lock_irqsave(&p->lock, flags);  	if (delta >= 0x10000) -		dev_warn(&p->pdev->dev, "delta out of range\n"); -	now = timer8_get_counter(p); -	p->tcora = delta; -	ctrl_outb(ctrl_inb(p->mapbase + _8TCR) | 0x40, p->mapbase + _8TCR); -	if (delta > now) -		ctrl_outw(delta, p->mapbase + TCORA); -	else -		ctrl_outw(now + 1, p->mapbase + TCORA); - -	raw_spin_unlock_irqrestore(&p->lock, flags); +		pr_warn("delta out of range\n"); +	bclr(CMIEA, p->mapbase + _8TCR); +	iowrite16be(delta, p->mapbase + TCORA); +	iowrite16be(0x0000, p->mapbase + _8TCNT); +	bclr(CMFA, p->mapbase + _8TCSR); +	bset(CMIEA, p->mapbase + _8TCR);  }  static int timer8_enable(struct timer8_priv *p)  { -	p->rate = clk_get_rate(p->pclk) / 64; -	ctrl_outw(0xffff, p->mapbase + TCORA); -	ctrl_outw(0x0000, p->mapbase + _8TCNT); -	ctrl_outw(0x0c02, p->mapbase + _8TCR); +	iowrite16be(0xffff, p->mapbase + TCORA); +	iowrite16be(0x0000, p->mapbase + _8TCNT); +	iowrite16be(0x0c02, p->mapbase + _8TCR);  	return 0;  }  static int timer8_start(struct timer8_priv *p)  { -	int ret = 0; -	unsigned long flags; - -	raw_spin_lock_irqsave(&p->lock, flags); - -	if (!(p->flags & FLAG_STARTED)) -		ret = timer8_enable(p); +	int ret; -	if (ret) -		goto out; -	p->flags |= FLAG_STARTED; +	if ((p->flags & FLAG_STARTED)) +		return 0; - out: -	raw_spin_unlock_irqrestore(&p->lock, flags); +	ret = timer8_enable(p); +	if (!ret) +		p->flags |= FLAG_STARTED;  	return ret;  }  static void timer8_stop(struct timer8_priv *p)  { -	unsigned long flags; - -	raw_spin_lock_irqsave(&p->lock, flags); - -	ctrl_outw(0x0000, p->mapbase + _8TCR); - -	raw_spin_unlock_irqrestore(&p->lock, flags); +	iowrite16be(0x0000, p->mapbase + _8TCR);  }  static inline struct timer8_priv *ced_to_priv(struct clock_event_device *ced) @@ -155,7 +99,7 @@ static inline struct timer8_priv *ced_to_priv(struct clock_event_device *ced)  	return container_of(ced, struct timer8_priv, ced);  } -static void timer8_clock_event_start(struct timer8_priv *p, int periodic) +static void timer8_clock_event_start(struct timer8_priv *p, unsigned long delta)  {  	struct clock_event_device *ced = &p->ced; @@ -166,7 +110,7 @@ static void timer8_clock_event_start(struct timer8_priv *p, int periodic)  	ced->max_delta_ns = clockevent_delta2ns(0xffff, ced);  	ced->min_delta_ns = clockevent_delta2ns(0x0001, ced); -	timer8_set_next(p, periodic?(p->rate + HZ/2) / HZ:0x10000); +	timer8_set_next(p, delta);  }  static int timer8_clock_event_shutdown(struct clock_event_device *ced) @@ -179,9 +123,9 @@ static int timer8_clock_event_periodic(struct clock_event_device *ced)  {  	struct timer8_priv *p = ced_to_priv(ced); -	dev_info(&p->pdev->dev, "used for periodic clock events\n"); +	pr_info("%s: used for periodic clock events\n", ced->name);  	timer8_stop(p); -	timer8_clock_event_start(p, PERIODIC); +	timer8_clock_event_start(p, (p->rate + HZ/2) / HZ);  	return 0;  } @@ -190,9 +134,9 @@ static int timer8_clock_event_oneshot(struct clock_event_device *ced)  {  	struct timer8_priv *p = ced_to_priv(ced); -	dev_info(&p->pdev->dev, "used for oneshot clock events\n"); +	pr_info("%s: used for oneshot clock events\n", ced->name);  	timer8_stop(p); -	timer8_clock_event_start(p, ONESHOT); +	timer8_clock_event_start(p, 0x10000);  	return 0;  } @@ -208,110 +152,64 @@ static int timer8_clock_event_next(unsigned long delta,  	return 0;  } -static int timer8_setup(struct timer8_priv *p, -			struct platform_device *pdev) +static struct timer8_priv timer8_priv = { +	.ced = { +		.name = "h8300_8timer", +		.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, +		.rating = 200, +		.set_next_event = timer8_clock_event_next, +		.set_state_shutdown = timer8_clock_event_shutdown, +		.set_state_periodic = timer8_clock_event_periodic, +		.set_state_oneshot = timer8_clock_event_oneshot, +	}, +}; + +static void __init h8300_8timer_init(struct device_node *node)  { -	struct resource *res; +	void __iomem *base;  	int irq; -	int ret; - -	p->pdev = pdev; - -	res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0); -	if (!res) { -		dev_err(&p->pdev->dev, "failed to get I/O memory\n"); -		return -ENXIO; -	} +	struct clk *clk; -	irq = platform_get_irq(p->pdev, 0); -	if (irq < 0) { -		dev_err(&p->pdev->dev, "failed to get irq\n"); -		return -ENXIO; +	clk = of_clk_get(node, 0); +	if (IS_ERR(clk)) { +		pr_err("failed to get clock for clockevent\n"); +		return;  	} -	p->mapbase = res->start; - -	p->irqaction.name = dev_name(&p->pdev->dev); -	p->irqaction.handler = timer8_interrupt; -	p->irqaction.dev_id = p; -	p->irqaction.flags = IRQF_TIMER; - -	p->pclk = clk_get(&p->pdev->dev, "fck"); -	if (IS_ERR(p->pclk)) { -		dev_err(&p->pdev->dev, "can't get clk\n"); -		return PTR_ERR(p->pclk); +	base = of_iomap(node, 0); +	if (!base) { +		pr_err("failed to map registers for clockevent\n"); +		goto free_clk;  	} -	p->ced.name = pdev->name; -	p->ced.features = CLOCK_EVT_FEAT_PERIODIC | -		CLOCK_EVT_FEAT_ONESHOT; -	p->ced.rating = 200; -	p->ced.cpumask = cpumask_of(0); -	p->ced.set_next_event = timer8_clock_event_next; -	p->ced.set_state_shutdown = timer8_clock_event_shutdown; -	p->ced.set_state_periodic = timer8_clock_event_periodic; -	p->ced.set_state_oneshot = timer8_clock_event_oneshot; - -	ret = setup_irq(irq, &p->irqaction); -	if (ret < 0) { -		dev_err(&p->pdev->dev, -			"failed to request irq %d\n", irq); -		return ret; +	irq = irq_of_parse_and_map(node, 0); +	if (!irq) { +		pr_err("failed to get irq for clockevent\n"); +		goto unmap_reg;  	} -	clockevents_register_device(&p->ced); -	platform_set_drvdata(pdev, p); - -	return 0; -} -static int timer8_probe(struct platform_device *pdev) -{ -	struct timer8_priv *p = platform_get_drvdata(pdev); +	timer8_priv.mapbase = base; -	if (p) { -		dev_info(&pdev->dev, "kept as earlytimer\n"); -		return 0; +	timer8_priv.rate = clk_get_rate(clk) / SCALE; +	if (!timer8_priv.rate) { +		pr_err("Failed to get rate for the clocksource\n"); +		goto unmap_reg;  	} -	p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); -	if (!p) -		return -ENOMEM; - -	return timer8_setup(p, pdev); -} - -static int timer8_remove(struct platform_device *pdev) -{ -	return -EBUSY; -} - -static const struct of_device_id timer8_of_table[] __maybe_unused = { -	{ .compatible = "renesas,8bit-timer" }, -	{ } -}; - -MODULE_DEVICE_TABLE(of, timer8_of_table); -static struct platform_driver timer8_driver = { -	.probe		= timer8_probe, -	.remove		= timer8_remove, -	.driver		= { -		.name	= "h8300-8timer", -		.of_match_table = of_match_ptr(timer8_of_table), +	if (request_irq(irq, timer8_interrupt, IRQF_TIMER, +			timer8_priv.ced.name, &timer8_priv) < 0) { +		pr_err("failed to request irq %d for clockevent\n", irq); +		goto unmap_reg;  	} -}; -static int __init timer8_init(void) -{ -	return platform_driver_register(&timer8_driver); -} +	clockevents_config_and_register(&timer8_priv.ced, +					timer8_priv.rate, 1, 0x0000ffff); -static void __exit timer8_exit(void) -{ -	platform_driver_unregister(&timer8_driver); +	return; +unmap_reg: +	iounmap(base); +free_clk: +	clk_put(clk);  } -subsys_initcall(timer8_init); -module_exit(timer8_exit); -MODULE_AUTHOR("Yoshinori Sato"); -MODULE_DESCRIPTION("H8/300 8bit Timer Driver"); -MODULE_LICENSE("GPL v2"); +CLOCKSOURCE_OF_DECLARE(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init);  | 
