18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * H8/300 16bit Timer driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 118c2ecf20Sopenharmony_ci#include <linux/clk.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/of_address.h> 158c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define TSTR 0 188c2ecf20Sopenharmony_ci#define TISRC 6 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define TCR 0 218c2ecf20Sopenharmony_ci#define TCNT 2 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define bset(b, a) iowrite8(ioread8(a) | (1 << (b)), (a)) 248c2ecf20Sopenharmony_ci#define bclr(b, a) iowrite8(ioread8(a) & ~(1 << (b)), (a)) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct timer16_priv { 278c2ecf20Sopenharmony_ci struct clocksource cs; 288c2ecf20Sopenharmony_ci unsigned long total_cycles; 298c2ecf20Sopenharmony_ci void __iomem *mapbase; 308c2ecf20Sopenharmony_ci void __iomem *mapcommon; 318c2ecf20Sopenharmony_ci unsigned short cs_enabled; 328c2ecf20Sopenharmony_ci unsigned char enb; 338c2ecf20Sopenharmony_ci unsigned char ovf; 348c2ecf20Sopenharmony_ci unsigned char ovie; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic unsigned long timer16_get_counter(struct timer16_priv *p) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci unsigned short v1, v2, v3; 408c2ecf20Sopenharmony_ci unsigned char o1, o2; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci o1 = ioread8(p->mapcommon + TISRC) & p->ovf; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* Make sure the timer value is stable. Stolen from acpi_pm.c */ 458c2ecf20Sopenharmony_ci do { 468c2ecf20Sopenharmony_ci o2 = o1; 478c2ecf20Sopenharmony_ci v1 = ioread16be(p->mapbase + TCNT); 488c2ecf20Sopenharmony_ci v2 = ioread16be(p->mapbase + TCNT); 498c2ecf20Sopenharmony_ci v3 = ioread16be(p->mapbase + TCNT); 508c2ecf20Sopenharmony_ci o1 = ioread8(p->mapcommon + TISRC) & p->ovf; 518c2ecf20Sopenharmony_ci } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3) 528c2ecf20Sopenharmony_ci || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (likely(!o1)) 558c2ecf20Sopenharmony_ci return v2; 568c2ecf20Sopenharmony_ci else 578c2ecf20Sopenharmony_ci return v2 + 0x10000; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic irqreturn_t timer16_interrupt(int irq, void *dev_id) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct timer16_priv *p = (struct timer16_priv *)dev_id; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci bclr(p->ovf, p->mapcommon + TISRC); 668c2ecf20Sopenharmony_ci p->total_cycles += 0x10000; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return IRQ_HANDLED; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic inline struct timer16_priv *cs_to_priv(struct clocksource *cs) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci return container_of(cs, struct timer16_priv, cs); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic u64 timer16_clocksource_read(struct clocksource *cs) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct timer16_priv *p = cs_to_priv(cs); 798c2ecf20Sopenharmony_ci unsigned long raw, value; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci value = p->total_cycles; 828c2ecf20Sopenharmony_ci raw = timer16_get_counter(p); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return value + raw; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int timer16_enable(struct clocksource *cs) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct timer16_priv *p = cs_to_priv(cs); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci WARN_ON(p->cs_enabled); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci p->total_cycles = 0; 948c2ecf20Sopenharmony_ci iowrite16be(0x0000, p->mapbase + TCNT); 958c2ecf20Sopenharmony_ci iowrite8(0x83, p->mapbase + TCR); 968c2ecf20Sopenharmony_ci bset(p->ovie, p->mapcommon + TISRC); 978c2ecf20Sopenharmony_ci bset(p->enb, p->mapcommon + TSTR); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci p->cs_enabled = true; 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void timer16_disable(struct clocksource *cs) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct timer16_priv *p = cs_to_priv(cs); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci WARN_ON(!p->cs_enabled); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci bclr(p->ovie, p->mapcommon + TISRC); 1108c2ecf20Sopenharmony_ci bclr(p->enb, p->mapcommon + TSTR); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci p->cs_enabled = false; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic struct timer16_priv timer16_priv = { 1168c2ecf20Sopenharmony_ci .cs = { 1178c2ecf20Sopenharmony_ci .name = "h8300_16timer", 1188c2ecf20Sopenharmony_ci .rating = 200, 1198c2ecf20Sopenharmony_ci .read = timer16_clocksource_read, 1208c2ecf20Sopenharmony_ci .enable = timer16_enable, 1218c2ecf20Sopenharmony_ci .disable = timer16_disable, 1228c2ecf20Sopenharmony_ci .mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8), 1238c2ecf20Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 1248c2ecf20Sopenharmony_ci }, 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci#define REG_CH 0 1288c2ecf20Sopenharmony_ci#define REG_COMM 1 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int __init h8300_16timer_init(struct device_node *node) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci void __iomem *base[2]; 1338c2ecf20Sopenharmony_ci int ret, irq; 1348c2ecf20Sopenharmony_ci unsigned int ch; 1358c2ecf20Sopenharmony_ci struct clk *clk; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci clk = of_clk_get(node, 0); 1388c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 1398c2ecf20Sopenharmony_ci pr_err("failed to get clock for clocksource\n"); 1408c2ecf20Sopenharmony_ci return PTR_ERR(clk); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci ret = -ENXIO; 1448c2ecf20Sopenharmony_ci base[REG_CH] = of_iomap(node, 0); 1458c2ecf20Sopenharmony_ci if (!base[REG_CH]) { 1468c2ecf20Sopenharmony_ci pr_err("failed to map registers for clocksource\n"); 1478c2ecf20Sopenharmony_ci goto free_clk; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci base[REG_COMM] = of_iomap(node, 1); 1518c2ecf20Sopenharmony_ci if (!base[REG_COMM]) { 1528c2ecf20Sopenharmony_ci pr_err("failed to map registers for clocksource\n"); 1538c2ecf20Sopenharmony_ci goto unmap_ch; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = -EINVAL; 1578c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(node, 0); 1588c2ecf20Sopenharmony_ci if (!irq) { 1598c2ecf20Sopenharmony_ci pr_err("failed to get irq for clockevent\n"); 1608c2ecf20Sopenharmony_ci goto unmap_comm; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci of_property_read_u32(node, "renesas,channel", &ch); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci timer16_priv.mapbase = base[REG_CH]; 1668c2ecf20Sopenharmony_ci timer16_priv.mapcommon = base[REG_COMM]; 1678c2ecf20Sopenharmony_ci timer16_priv.enb = ch; 1688c2ecf20Sopenharmony_ci timer16_priv.ovf = ch; 1698c2ecf20Sopenharmony_ci timer16_priv.ovie = 4 + ch; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ret = request_irq(irq, timer16_interrupt, 1728c2ecf20Sopenharmony_ci IRQF_TIMER, timer16_priv.cs.name, &timer16_priv); 1738c2ecf20Sopenharmony_ci if (ret < 0) { 1748c2ecf20Sopenharmony_ci pr_err("failed to request irq %d of clocksource\n", irq); 1758c2ecf20Sopenharmony_ci goto unmap_comm; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci clocksource_register_hz(&timer16_priv.cs, 1798c2ecf20Sopenharmony_ci clk_get_rate(clk) / 8); 1808c2ecf20Sopenharmony_ci return 0; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ciunmap_comm: 1838c2ecf20Sopenharmony_ci iounmap(base[REG_COMM]); 1848c2ecf20Sopenharmony_ciunmap_ch: 1858c2ecf20Sopenharmony_ci iounmap(base[REG_CH]); 1868c2ecf20Sopenharmony_cifree_clk: 1878c2ecf20Sopenharmony_ci clk_put(clk); 1888c2ecf20Sopenharmony_ci return ret; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(h8300_16bit, "renesas,16bit-timer", 1928c2ecf20Sopenharmony_ci h8300_16timer_init); 193