1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 28d8bd7beSOleksij Rempel /* 38d8bd7beSOleksij Rempel * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de> 48d8bd7beSOleksij Rempel */ 58d8bd7beSOleksij Rempel 68d8bd7beSOleksij Rempel #include <linux/kernel.h> 78d8bd7beSOleksij Rempel #include <linux/init.h> 88d8bd7beSOleksij Rempel #include <linux/interrupt.h> 98d8bd7beSOleksij Rempel #include <linux/sched.h> 108d8bd7beSOleksij Rempel #include <linux/clk.h> 118d8bd7beSOleksij Rempel #include <linux/clocksource.h> 128d8bd7beSOleksij Rempel #include <linux/clockchips.h> 138d8bd7beSOleksij Rempel #include <linux/io.h> 148d8bd7beSOleksij Rempel #include <linux/of.h> 158d8bd7beSOleksij Rempel #include <linux/of_address.h> 168d8bd7beSOleksij Rempel #include <linux/of_irq.h> 178d8bd7beSOleksij Rempel #include <linux/bitops.h> 188d8bd7beSOleksij Rempel 198d8bd7beSOleksij Rempel #define DRIVER_NAME "asm9260-timer" 208d8bd7beSOleksij Rempel 218d8bd7beSOleksij Rempel /* 228d8bd7beSOleksij Rempel * this device provide 4 offsets for each register: 238d8bd7beSOleksij Rempel * 0x0 - plain read write mode 248d8bd7beSOleksij Rempel * 0x4 - set mode, OR logic. 258d8bd7beSOleksij Rempel * 0x8 - clr mode, XOR logic. 268d8bd7beSOleksij Rempel * 0xc - togle mode. 278d8bd7beSOleksij Rempel */ 288d8bd7beSOleksij Rempel #define SET_REG 4 298d8bd7beSOleksij Rempel #define CLR_REG 8 308d8bd7beSOleksij Rempel 318d8bd7beSOleksij Rempel #define HW_IR 0x0000 /* RW. Interrupt */ 328d8bd7beSOleksij Rempel #define BM_IR_CR0 BIT(4) 338d8bd7beSOleksij Rempel #define BM_IR_MR3 BIT(3) 348d8bd7beSOleksij Rempel #define BM_IR_MR2 BIT(2) 358d8bd7beSOleksij Rempel #define BM_IR_MR1 BIT(1) 368d8bd7beSOleksij Rempel #define BM_IR_MR0 BIT(0) 378d8bd7beSOleksij Rempel 388d8bd7beSOleksij Rempel #define HW_TCR 0x0010 /* RW. Timer controller */ 398d8bd7beSOleksij Rempel /* BM_C*_RST 408d8bd7beSOleksij Rempel * Timer Counter and the Prescale Counter are synchronously reset on the 418d8bd7beSOleksij Rempel * next positive edge of PCLK. The counters remain reset until TCR[1] is 428d8bd7beSOleksij Rempel * returned to zero. */ 438d8bd7beSOleksij Rempel #define BM_C3_RST BIT(7) 448d8bd7beSOleksij Rempel #define BM_C2_RST BIT(6) 458d8bd7beSOleksij Rempel #define BM_C1_RST BIT(5) 468d8bd7beSOleksij Rempel #define BM_C0_RST BIT(4) 478d8bd7beSOleksij Rempel /* BM_C*_EN 488d8bd7beSOleksij Rempel * 1 - Timer Counter and Prescale Counter are enabled for counting 498d8bd7beSOleksij Rempel * 0 - counters are disabled */ 508d8bd7beSOleksij Rempel #define BM_C3_EN BIT(3) 518d8bd7beSOleksij Rempel #define BM_C2_EN BIT(2) 528d8bd7beSOleksij Rempel #define BM_C1_EN BIT(1) 538d8bd7beSOleksij Rempel #define BM_C0_EN BIT(0) 548d8bd7beSOleksij Rempel 558d8bd7beSOleksij Rempel #define HW_DIR 0x0020 /* RW. Direction? */ 568d8bd7beSOleksij Rempel /* 00 - count up 578d8bd7beSOleksij Rempel * 01 - count down 588d8bd7beSOleksij Rempel * 10 - ?? 2^n/2 */ 598d8bd7beSOleksij Rempel #define BM_DIR_COUNT_UP 0 608d8bd7beSOleksij Rempel #define BM_DIR_COUNT_DOWN 1 618d8bd7beSOleksij Rempel #define BM_DIR0_SHIFT 0 628d8bd7beSOleksij Rempel #define BM_DIR1_SHIFT 4 638d8bd7beSOleksij Rempel #define BM_DIR2_SHIFT 8 648d8bd7beSOleksij Rempel #define BM_DIR3_SHIFT 12 658d8bd7beSOleksij Rempel #define BM_DIR_DEFAULT (BM_DIR_COUNT_UP << BM_DIR0_SHIFT | \ 668d8bd7beSOleksij Rempel BM_DIR_COUNT_UP << BM_DIR1_SHIFT | \ 678d8bd7beSOleksij Rempel BM_DIR_COUNT_UP << BM_DIR2_SHIFT | \ 688d8bd7beSOleksij Rempel BM_DIR_COUNT_UP << BM_DIR3_SHIFT) 698d8bd7beSOleksij Rempel 708d8bd7beSOleksij Rempel #define HW_TC0 0x0030 /* RO. Timer counter 0 */ 718d8bd7beSOleksij Rempel /* HW_TC*. Timer counter owerflow (0xffff.ffff to 0x0000.0000) do not generate 728d8bd7beSOleksij Rempel * interrupt. This registers can be used to detect overflow */ 738d8bd7beSOleksij Rempel #define HW_TC1 0x0040 748d8bd7beSOleksij Rempel #define HW_TC2 0x0050 758d8bd7beSOleksij Rempel #define HW_TC3 0x0060 768d8bd7beSOleksij Rempel 778d8bd7beSOleksij Rempel #define HW_PR 0x0070 /* RW. prescaler */ 788d8bd7beSOleksij Rempel #define BM_PR_DISABLE 0 798d8bd7beSOleksij Rempel #define HW_PC 0x0080 /* RO. Prescaler counter */ 808d8bd7beSOleksij Rempel #define HW_MCR 0x0090 /* RW. Match control */ 818d8bd7beSOleksij Rempel /* enable interrupt on match */ 828d8bd7beSOleksij Rempel #define BM_MCR_INT_EN(n) (1 << (n * 3 + 0)) 838d8bd7beSOleksij Rempel /* enable TC reset on match */ 848d8bd7beSOleksij Rempel #define BM_MCR_RES_EN(n) (1 << (n * 3 + 1)) 858d8bd7beSOleksij Rempel /* enable stop TC on match */ 868d8bd7beSOleksij Rempel #define BM_MCR_STOP_EN(n) (1 << (n * 3 + 2)) 878d8bd7beSOleksij Rempel 888d8bd7beSOleksij Rempel #define HW_MR0 0x00a0 /* RW. Match reg */ 898d8bd7beSOleksij Rempel #define HW_MR1 0x00b0 908d8bd7beSOleksij Rempel #define HW_MR2 0x00C0 918d8bd7beSOleksij Rempel #define HW_MR3 0x00D0 928d8bd7beSOleksij Rempel 938d8bd7beSOleksij Rempel #define HW_CTCR 0x0180 /* Counter control */ 948d8bd7beSOleksij Rempel #define BM_CTCR0_SHIFT 0 958d8bd7beSOleksij Rempel #define BM_CTCR1_SHIFT 2 968d8bd7beSOleksij Rempel #define BM_CTCR2_SHIFT 4 978d8bd7beSOleksij Rempel #define BM_CTCR3_SHIFT 6 988d8bd7beSOleksij Rempel #define BM_CTCR_TM 0 /* Timer mode. Every rising PCLK edge. */ 998d8bd7beSOleksij Rempel #define BM_CTCR_DEFAULT (BM_CTCR_TM << BM_CTCR0_SHIFT | \ 1008d8bd7beSOleksij Rempel BM_CTCR_TM << BM_CTCR1_SHIFT | \ 1018d8bd7beSOleksij Rempel BM_CTCR_TM << BM_CTCR2_SHIFT | \ 1028d8bd7beSOleksij Rempel BM_CTCR_TM << BM_CTCR3_SHIFT) 1038d8bd7beSOleksij Rempel 1048d8bd7beSOleksij Rempel static struct asm9260_timer_priv { 1058d8bd7beSOleksij Rempel void __iomem *base; 1068d8bd7beSOleksij Rempel unsigned long ticks_per_jiffy; 1078d8bd7beSOleksij Rempel } priv; 1088d8bd7beSOleksij Rempel 1098d8bd7beSOleksij Rempel static int asm9260_timer_set_next_event(unsigned long delta, 1108d8bd7beSOleksij Rempel struct clock_event_device *evt) 1118d8bd7beSOleksij Rempel { 1128d8bd7beSOleksij Rempel /* configure match count for TC0 */ 1138d8bd7beSOleksij Rempel writel_relaxed(delta, priv.base + HW_MR0); 1148d8bd7beSOleksij Rempel /* enable TC0 */ 1158d8bd7beSOleksij Rempel writel_relaxed(BM_C0_EN, priv.base + HW_TCR + SET_REG); 1168d8bd7beSOleksij Rempel return 0; 1178d8bd7beSOleksij Rempel } 1188d8bd7beSOleksij Rempel 1193465f609SViresh Kumar static inline void __asm9260_timer_shutdown(struct clock_event_device *evt) 1208d8bd7beSOleksij Rempel { 1218d8bd7beSOleksij Rempel /* stop timer0 */ 1228d8bd7beSOleksij Rempel writel_relaxed(BM_C0_EN, priv.base + HW_TCR + CLR_REG); 1233465f609SViresh Kumar } 1248d8bd7beSOleksij Rempel 1253465f609SViresh Kumar static int asm9260_timer_shutdown(struct clock_event_device *evt) 1263465f609SViresh Kumar { 1273465f609SViresh Kumar __asm9260_timer_shutdown(evt); 1283465f609SViresh Kumar return 0; 1293465f609SViresh Kumar } 1303465f609SViresh Kumar 1313465f609SViresh Kumar static int asm9260_timer_set_oneshot(struct clock_event_device *evt) 1323465f609SViresh Kumar { 1333465f609SViresh Kumar __asm9260_timer_shutdown(evt); 1343465f609SViresh Kumar 1353465f609SViresh Kumar /* enable reset and stop on match */ 1363465f609SViresh Kumar writel_relaxed(BM_MCR_RES_EN(0) | BM_MCR_STOP_EN(0), 1373465f609SViresh Kumar priv.base + HW_MCR + SET_REG); 1383465f609SViresh Kumar return 0; 1393465f609SViresh Kumar } 1403465f609SViresh Kumar 1413465f609SViresh Kumar static int asm9260_timer_set_periodic(struct clock_event_device *evt) 1423465f609SViresh Kumar { 1433465f609SViresh Kumar __asm9260_timer_shutdown(evt); 1443465f609SViresh Kumar 1458d8bd7beSOleksij Rempel /* disable reset and stop on match */ 1468d8bd7beSOleksij Rempel writel_relaxed(BM_MCR_RES_EN(0) | BM_MCR_STOP_EN(0), 1478d8bd7beSOleksij Rempel priv.base + HW_MCR + CLR_REG); 1488d8bd7beSOleksij Rempel /* configure match count for TC0 */ 1498d8bd7beSOleksij Rempel writel_relaxed(priv.ticks_per_jiffy, priv.base + HW_MR0); 1508d8bd7beSOleksij Rempel /* enable TC0 */ 1518d8bd7beSOleksij Rempel writel_relaxed(BM_C0_EN, priv.base + HW_TCR + SET_REG); 1523465f609SViresh Kumar return 0; 1538d8bd7beSOleksij Rempel } 1548d8bd7beSOleksij Rempel 1558d8bd7beSOleksij Rempel static struct clock_event_device event_dev = { 1568d8bd7beSOleksij Rempel .name = DRIVER_NAME, 1578d8bd7beSOleksij Rempel .rating = 200, 1583465f609SViresh Kumar .features = CLOCK_EVT_FEAT_PERIODIC | 1593465f609SViresh Kumar CLOCK_EVT_FEAT_ONESHOT, 1608d8bd7beSOleksij Rempel .set_next_event = asm9260_timer_set_next_event, 1613465f609SViresh Kumar .set_state_shutdown = asm9260_timer_shutdown, 1623465f609SViresh Kumar .set_state_periodic = asm9260_timer_set_periodic, 1633465f609SViresh Kumar .set_state_oneshot = asm9260_timer_set_oneshot, 1643465f609SViresh Kumar .tick_resume = asm9260_timer_shutdown, 1658d8bd7beSOleksij Rempel }; 1668d8bd7beSOleksij Rempel 1678d8bd7beSOleksij Rempel static irqreturn_t asm9260_timer_interrupt(int irq, void *dev_id) 1688d8bd7beSOleksij Rempel { 1698d8bd7beSOleksij Rempel struct clock_event_device *evt = dev_id; 1708d8bd7beSOleksij Rempel 1718d8bd7beSOleksij Rempel evt->event_handler(evt); 1728d8bd7beSOleksij Rempel 1738d8bd7beSOleksij Rempel writel_relaxed(BM_IR_MR0, priv.base + HW_IR); 1748d8bd7beSOleksij Rempel 1758d8bd7beSOleksij Rempel return IRQ_HANDLED; 1768d8bd7beSOleksij Rempel } 1778d8bd7beSOleksij Rempel 1788d8bd7beSOleksij Rempel /* 1798d8bd7beSOleksij Rempel * --------------------------------------------------------------------------- 1808d8bd7beSOleksij Rempel * Timer initialization 1818d8bd7beSOleksij Rempel * --------------------------------------------------------------------------- 1828d8bd7beSOleksij Rempel */ 183be5eb33dSDaniel Lezcano static int __init asm9260_timer_init(struct device_node *np) 1848d8bd7beSOleksij Rempel { 1858d8bd7beSOleksij Rempel int irq; 1868d8bd7beSOleksij Rempel struct clk *clk; 1878d8bd7beSOleksij Rempel int ret; 1888d8bd7beSOleksij Rempel unsigned long rate; 1898d8bd7beSOleksij Rempel 1908d8bd7beSOleksij Rempel priv.base = of_io_request_and_map(np, 0, np->name); 191be5eb33dSDaniel Lezcano if (IS_ERR(priv.base)) { 1922a4849d2SRob Herring pr_err("%pOFn: unable to map resource\n", np); 193be5eb33dSDaniel Lezcano return PTR_ERR(priv.base); 194be5eb33dSDaniel Lezcano } 1958d8bd7beSOleksij Rempel 1968d8bd7beSOleksij Rempel clk = of_clk_get(np, 0); 1978d8bd7beSOleksij Rempel 1988d8bd7beSOleksij Rempel ret = clk_prepare_enable(clk); 199be5eb33dSDaniel Lezcano if (ret) { 200be5eb33dSDaniel Lezcano pr_err("Failed to enable clk!\n"); 201be5eb33dSDaniel Lezcano return ret; 202be5eb33dSDaniel Lezcano } 2038d8bd7beSOleksij Rempel 2048d8bd7beSOleksij Rempel irq = irq_of_parse_and_map(np, 0); 2058d8bd7beSOleksij Rempel ret = request_irq(irq, asm9260_timer_interrupt, IRQF_TIMER, 2068d8bd7beSOleksij Rempel DRIVER_NAME, &event_dev); 207be5eb33dSDaniel Lezcano if (ret) { 208be5eb33dSDaniel Lezcano pr_err("Failed to setup irq!\n"); 209be5eb33dSDaniel Lezcano return ret; 210be5eb33dSDaniel Lezcano } 2118d8bd7beSOleksij Rempel 2128d8bd7beSOleksij Rempel /* set all timers for count-up */ 2138d8bd7beSOleksij Rempel writel_relaxed(BM_DIR_DEFAULT, priv.base + HW_DIR); 2148d8bd7beSOleksij Rempel /* disable divider */ 2158d8bd7beSOleksij Rempel writel_relaxed(BM_PR_DISABLE, priv.base + HW_PR); 2168d8bd7beSOleksij Rempel /* make sure all timers use every rising PCLK edge. */ 2178d8bd7beSOleksij Rempel writel_relaxed(BM_CTCR_DEFAULT, priv.base + HW_CTCR); 2188d8bd7beSOleksij Rempel /* enable interrupt for TC0 and clean setting for all other lines */ 2198d8bd7beSOleksij Rempel writel_relaxed(BM_MCR_INT_EN(0) , priv.base + HW_MCR); 2208d8bd7beSOleksij Rempel 2218d8bd7beSOleksij Rempel rate = clk_get_rate(clk); 2228d8bd7beSOleksij Rempel clocksource_mmio_init(priv.base + HW_TC1, DRIVER_NAME, rate, 2238d8bd7beSOleksij Rempel 200, 32, clocksource_mmio_readl_up); 2248d8bd7beSOleksij Rempel 2258d8bd7beSOleksij Rempel /* Seems like we can't use counter without match register even if 2268d8bd7beSOleksij Rempel * actions for MR are disabled. So, set MR to max value. */ 2278d8bd7beSOleksij Rempel writel_relaxed(0xffffffff, priv.base + HW_MR1); 2288d8bd7beSOleksij Rempel /* enable TC1 */ 2298d8bd7beSOleksij Rempel writel_relaxed(BM_C1_EN, priv.base + HW_TCR + SET_REG); 2308d8bd7beSOleksij Rempel 2318d8bd7beSOleksij Rempel priv.ticks_per_jiffy = DIV_ROUND_CLOSEST(rate, HZ); 2328d8bd7beSOleksij Rempel event_dev.cpumask = cpumask_of(0); 2338d8bd7beSOleksij Rempel clockevents_config_and_register(&event_dev, rate, 0x2c00, 0xfffffffe); 234be5eb33dSDaniel Lezcano 235be5eb33dSDaniel Lezcano return 0; 2368d8bd7beSOleksij Rempel } 23717273395SDaniel Lezcano TIMER_OF_DECLARE(asm9260_timer, "alphascale,asm9260-timer", 2388d8bd7beSOleksij Rempel asm9260_timer_init); 239