123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- /*
- * H8S TPU Driver
- *
- * Copyright 2015 Yoshinori Sato <[email protected]>
- *
- */
- #include <linux/errno.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/clocksource.h>
- #include <linux/clk.h>
- #include <linux/io.h>
- #include <linux/of.h>
- #include <linux/of_address.h>
- #include <linux/of_irq.h>
- #define TCR 0x0
- #define TSR 0x5
- #define TCNT 0x6
- #define TCFV 0x10
- struct tpu_priv {
- struct clocksource cs;
- void __iomem *mapbase1;
- void __iomem *mapbase2;
- raw_spinlock_t lock;
- unsigned int cs_enabled;
- };
- static inline unsigned long read_tcnt32(struct tpu_priv *p)
- {
- unsigned long tcnt;
- tcnt = ioread16be(p->mapbase1 + TCNT) << 16;
- tcnt |= ioread16be(p->mapbase2 + TCNT);
- return tcnt;
- }
- static int tpu_get_counter(struct tpu_priv *p, unsigned long long *val)
- {
- unsigned long v1, v2, v3;
- int o1, o2;
- o1 = ioread8(p->mapbase1 + TSR) & TCFV;
- /* Make sure the timer value is stable. Stolen from acpi_pm.c */
- do {
- o2 = o1;
- v1 = read_tcnt32(p);
- v2 = read_tcnt32(p);
- v3 = read_tcnt32(p);
- o1 = ioread8(p->mapbase1 + TSR) & TCFV;
- } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
- || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
- *val = v2;
- return o1;
- }
- static inline struct tpu_priv *cs_to_priv(struct clocksource *cs)
- {
- return container_of(cs, struct tpu_priv, cs);
- }
- static cycle_t tpu_clocksource_read(struct clocksource *cs)
- {
- struct tpu_priv *p = cs_to_priv(cs);
- unsigned long flags;
- unsigned long long value;
- raw_spin_lock_irqsave(&p->lock, flags);
- if (tpu_get_counter(p, &value))
- value += 0x100000000;
- raw_spin_unlock_irqrestore(&p->lock, flags);
- return value;
- }
- static int tpu_clocksource_enable(struct clocksource *cs)
- {
- struct tpu_priv *p = cs_to_priv(cs);
- WARN_ON(p->cs_enabled);
- iowrite16be(0, p->mapbase1 + TCNT);
- iowrite16be(0, p->mapbase2 + TCNT);
- iowrite8(0x0f, p->mapbase1 + TCR);
- iowrite8(0x03, p->mapbase2 + TCR);
- p->cs_enabled = true;
- return 0;
- }
- static void tpu_clocksource_disable(struct clocksource *cs)
- {
- struct tpu_priv *p = cs_to_priv(cs);
- WARN_ON(!p->cs_enabled);
- iowrite8(0, p->mapbase1 + TCR);
- iowrite8(0, p->mapbase2 + TCR);
- p->cs_enabled = false;
- }
- static struct tpu_priv tpu_priv = {
- .cs = {
- .name = "H8S_TPU",
- .rating = 200,
- .read = tpu_clocksource_read,
- .enable = tpu_clocksource_enable,
- .disable = tpu_clocksource_disable,
- .mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8),
- .flags = CLOCK_SOURCE_IS_CONTINUOUS,
- },
- };
- #define CH_L 0
- #define CH_H 1
- static int __init h8300_tpu_init(struct device_node *node)
- {
- void __iomem *base[2];
- struct clk *clk;
- int ret = -ENXIO;
- clk = of_clk_get(node, 0);
- if (IS_ERR(clk)) {
- pr_err("failed to get clock for clocksource\n");
- return PTR_ERR(clk);
- }
- base[CH_L] = of_iomap(node, CH_L);
- if (!base[CH_L]) {
- pr_err("failed to map registers for clocksource\n");
- goto free_clk;
- }
- base[CH_H] = of_iomap(node, CH_H);
- if (!base[CH_H]) {
- pr_err("failed to map registers for clocksource\n");
- goto unmap_L;
- }
- tpu_priv.mapbase1 = base[CH_L];
- tpu_priv.mapbase2 = base[CH_H];
- return clocksource_register_hz(&tpu_priv.cs, clk_get_rate(clk) / 64);
- unmap_L:
- iounmap(base[CH_H]);
- free_clk:
- clk_put(clk);
- return ret;
- }
- CLOCKSOURCE_OF_DECLARE(h8300_tpu, "renesas,tpu", h8300_tpu_init);
|