linux/drivers/clocksource/h8300_tpu.c
<<
>>
Prefs
   1/*
   2 *  H8S TPU Driver
   3 *
   4 *  Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
   5 *
   6 */
   7
   8#include <linux/errno.h>
   9#include <linux/kernel.h>
  10#include <linux/init.h>
  11#include <linux/clocksource.h>
  12#include <linux/clk.h>
  13#include <linux/io.h>
  14#include <linux/of.h>
  15#include <linux/of_address.h>
  16#include <linux/of_irq.h>
  17
  18#define TCR     0x0
  19#define TSR     0x5
  20#define TCNT    0x6
  21
  22#define TCFV    0x10
  23
  24struct tpu_priv {
  25        struct clocksource cs;
  26        void __iomem *mapbase1;
  27        void __iomem *mapbase2;
  28        raw_spinlock_t lock;
  29        unsigned int cs_enabled;
  30};
  31
  32static inline unsigned long read_tcnt32(struct tpu_priv *p)
  33{
  34        unsigned long tcnt;
  35
  36        tcnt = ioread16be(p->mapbase1 + TCNT) << 16;
  37        tcnt |= ioread16be(p->mapbase2 + TCNT);
  38        return tcnt;
  39}
  40
  41static int tpu_get_counter(struct tpu_priv *p, unsigned long long *val)
  42{
  43        unsigned long v1, v2, v3;
  44        int o1, o2;
  45
  46        o1 = ioread8(p->mapbase1 + TSR) & TCFV;
  47
  48        /* Make sure the timer value is stable. Stolen from acpi_pm.c */
  49        do {
  50                o2 = o1;
  51                v1 = read_tcnt32(p);
  52                v2 = read_tcnt32(p);
  53                v3 = read_tcnt32(p);
  54                o1 = ioread8(p->mapbase1 + TSR) & TCFV;
  55        } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
  56                          || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
  57
  58        *val = v2;
  59        return o1;
  60}
  61
  62static inline struct tpu_priv *cs_to_priv(struct clocksource *cs)
  63{
  64        return container_of(cs, struct tpu_priv, cs);
  65}
  66
  67static u64 tpu_clocksource_read(struct clocksource *cs)
  68{
  69        struct tpu_priv *p = cs_to_priv(cs);
  70        unsigned long flags;
  71        unsigned long long value;
  72
  73        raw_spin_lock_irqsave(&p->lock, flags);
  74        if (tpu_get_counter(p, &value))
  75                value += 0x100000000;
  76        raw_spin_unlock_irqrestore(&p->lock, flags);
  77
  78        return value;
  79}
  80
  81static int tpu_clocksource_enable(struct clocksource *cs)
  82{
  83        struct tpu_priv *p = cs_to_priv(cs);
  84
  85        WARN_ON(p->cs_enabled);
  86
  87        iowrite16be(0, p->mapbase1 + TCNT);
  88        iowrite16be(0, p->mapbase2 + TCNT);
  89        iowrite8(0x0f, p->mapbase1 + TCR);
  90        iowrite8(0x03, p->mapbase2 + TCR);
  91
  92        p->cs_enabled = true;
  93        return 0;
  94}
  95
  96static void tpu_clocksource_disable(struct clocksource *cs)
  97{
  98        struct tpu_priv *p = cs_to_priv(cs);
  99
 100        WARN_ON(!p->cs_enabled);
 101
 102        iowrite8(0, p->mapbase1 + TCR);
 103        iowrite8(0, p->mapbase2 + TCR);
 104        p->cs_enabled = false;
 105}
 106
 107static struct tpu_priv tpu_priv = {
 108        .cs = {
 109                .name = "H8S_TPU",
 110                .rating = 200,
 111                .read = tpu_clocksource_read,
 112                .enable = tpu_clocksource_enable,
 113                .disable = tpu_clocksource_disable,
 114                .mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8),
 115                .flags = CLOCK_SOURCE_IS_CONTINUOUS,
 116        },
 117};
 118
 119#define CH_L 0
 120#define CH_H 1
 121
 122static int __init h8300_tpu_init(struct device_node *node)
 123{
 124        void __iomem *base[2];
 125        struct clk *clk;
 126        int ret = -ENXIO;
 127
 128        clk = of_clk_get(node, 0);
 129        if (IS_ERR(clk)) {
 130                pr_err("failed to get clock for clocksource\n");
 131                return PTR_ERR(clk);
 132        }
 133
 134        base[CH_L] = of_iomap(node, CH_L);
 135        if (!base[CH_L]) {
 136                pr_err("failed to map registers for clocksource\n");
 137                goto free_clk;
 138        }
 139        base[CH_H] = of_iomap(node, CH_H);
 140        if (!base[CH_H]) {
 141                pr_err("failed to map registers for clocksource\n");
 142                goto unmap_L;
 143        }
 144
 145        tpu_priv.mapbase1 = base[CH_L];
 146        tpu_priv.mapbase2 = base[CH_H];
 147
 148        return clocksource_register_hz(&tpu_priv.cs, clk_get_rate(clk) / 64);
 149
 150unmap_L:
 151        iounmap(base[CH_H]);
 152free_clk:
 153        clk_put(clk);
 154        return ret;
 155}
 156
 157CLOCKSOURCE_OF_DECLARE(h8300_tpu, "renesas,tpu", h8300_tpu_init);
 158