Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.12-rc2 157 lines 3.3 kB view raw
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);