18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2010, 2011 Texas Instruments Incorporated 48c2ecf20Sopenharmony_ci * Contributed by: Mark Salter (msalter@redhat.com) 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/of.h> 118c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 128c2ecf20Sopenharmony_ci#include <linux/of_address.h> 138c2ecf20Sopenharmony_ci#include <asm/soc.h> 148c2ecf20Sopenharmony_ci#include <asm/dscr.h> 158c2ecf20Sopenharmony_ci#include <asm/special_insns.h> 168c2ecf20Sopenharmony_ci#include <asm/timer64.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct timer_regs { 198c2ecf20Sopenharmony_ci u32 reserved0; 208c2ecf20Sopenharmony_ci u32 emumgt; 218c2ecf20Sopenharmony_ci u32 reserved1; 228c2ecf20Sopenharmony_ci u32 reserved2; 238c2ecf20Sopenharmony_ci u32 cntlo; 248c2ecf20Sopenharmony_ci u32 cnthi; 258c2ecf20Sopenharmony_ci u32 prdlo; 268c2ecf20Sopenharmony_ci u32 prdhi; 278c2ecf20Sopenharmony_ci u32 tcr; 288c2ecf20Sopenharmony_ci u32 tgcr; 298c2ecf20Sopenharmony_ci u32 wdtcr; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic struct timer_regs __iomem *timer; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define TCR_TSTATLO 0x001 358c2ecf20Sopenharmony_ci#define TCR_INVOUTPLO 0x002 368c2ecf20Sopenharmony_ci#define TCR_INVINPLO 0x004 378c2ecf20Sopenharmony_ci#define TCR_CPLO 0x008 388c2ecf20Sopenharmony_ci#define TCR_ENAMODELO_ONCE 0x040 398c2ecf20Sopenharmony_ci#define TCR_ENAMODELO_CONT 0x080 408c2ecf20Sopenharmony_ci#define TCR_ENAMODELO_MASK 0x0c0 418c2ecf20Sopenharmony_ci#define TCR_PWIDLO_MASK 0x030 428c2ecf20Sopenharmony_ci#define TCR_CLKSRCLO 0x100 438c2ecf20Sopenharmony_ci#define TCR_TIENLO 0x200 448c2ecf20Sopenharmony_ci#define TCR_TSTATHI (0x001 << 16) 458c2ecf20Sopenharmony_ci#define TCR_INVOUTPHI (0x002 << 16) 468c2ecf20Sopenharmony_ci#define TCR_CPHI (0x008 << 16) 478c2ecf20Sopenharmony_ci#define TCR_PWIDHI_MASK (0x030 << 16) 488c2ecf20Sopenharmony_ci#define TCR_ENAMODEHI_ONCE (0x040 << 16) 498c2ecf20Sopenharmony_ci#define TCR_ENAMODEHI_CONT (0x080 << 16) 508c2ecf20Sopenharmony_ci#define TCR_ENAMODEHI_MASK (0x0c0 << 16) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define TGCR_TIMLORS 0x001 538c2ecf20Sopenharmony_ci#define TGCR_TIMHIRS 0x002 548c2ecf20Sopenharmony_ci#define TGCR_TIMMODE_UD32 0x004 558c2ecf20Sopenharmony_ci#define TGCR_TIMMODE_WDT64 0x008 568c2ecf20Sopenharmony_ci#define TGCR_TIMMODE_CD32 0x00c 578c2ecf20Sopenharmony_ci#define TGCR_TIMMODE_MASK 0x00c 588c2ecf20Sopenharmony_ci#define TGCR_PSCHI_MASK (0x00f << 8) 598c2ecf20Sopenharmony_ci#define TGCR_TDDRHI_MASK (0x00f << 12) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* 628c2ecf20Sopenharmony_ci * Timer clocks are divided down from the CPU clock 638c2ecf20Sopenharmony_ci * The divisor is in the EMUMGTCLKSPD register 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci#define TIMER_DIVISOR \ 668c2ecf20Sopenharmony_ci ((soc_readl(&timer->emumgt) & (0xf << 16)) >> 16) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define TIMER64_RATE (c6x_core_freq / TIMER_DIVISOR) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define TIMER64_MODE_DISABLED 0 718c2ecf20Sopenharmony_ci#define TIMER64_MODE_ONE_SHOT TCR_ENAMODELO_ONCE 728c2ecf20Sopenharmony_ci#define TIMER64_MODE_PERIODIC TCR_ENAMODELO_CONT 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int timer64_mode; 758c2ecf20Sopenharmony_cistatic int timer64_devstate_id = -1; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void timer64_config(unsigned long period) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci u32 tcr = soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci soc_writel(tcr, &timer->tcr); 828c2ecf20Sopenharmony_ci soc_writel(period - 1, &timer->prdlo); 838c2ecf20Sopenharmony_ci soc_writel(0, &timer->cntlo); 848c2ecf20Sopenharmony_ci tcr |= timer64_mode; 858c2ecf20Sopenharmony_ci soc_writel(tcr, &timer->tcr); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void timer64_enable(void) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci u32 val; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (timer64_devstate_id >= 0) 938c2ecf20Sopenharmony_ci dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_ENABLED); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* disable timer, reset count */ 968c2ecf20Sopenharmony_ci soc_writel(soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK, &timer->tcr); 978c2ecf20Sopenharmony_ci soc_writel(0, &timer->prdlo); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* use internal clock and 1 cycle pulse width */ 1008c2ecf20Sopenharmony_ci val = soc_readl(&timer->tcr); 1018c2ecf20Sopenharmony_ci soc_writel(val & ~(TCR_CLKSRCLO | TCR_PWIDLO_MASK), &timer->tcr); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* dual 32-bit unchained mode */ 1048c2ecf20Sopenharmony_ci val = soc_readl(&timer->tgcr) & ~TGCR_TIMMODE_MASK; 1058c2ecf20Sopenharmony_ci soc_writel(val, &timer->tgcr); 1068c2ecf20Sopenharmony_ci soc_writel(val | (TGCR_TIMLORS | TGCR_TIMMODE_UD32), &timer->tgcr); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void timer64_disable(void) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci /* disable timer, reset count */ 1128c2ecf20Sopenharmony_ci soc_writel(soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK, &timer->tcr); 1138c2ecf20Sopenharmony_ci soc_writel(0, &timer->prdlo); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (timer64_devstate_id >= 0) 1168c2ecf20Sopenharmony_ci dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_DISABLED); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int next_event(unsigned long delta, 1208c2ecf20Sopenharmony_ci struct clock_event_device *evt) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci timer64_config(delta); 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int set_periodic(struct clock_event_device *evt) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci timer64_enable(); 1298c2ecf20Sopenharmony_ci timer64_mode = TIMER64_MODE_PERIODIC; 1308c2ecf20Sopenharmony_ci timer64_config(TIMER64_RATE / HZ); 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int set_oneshot(struct clock_event_device *evt) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci timer64_enable(); 1378c2ecf20Sopenharmony_ci timer64_mode = TIMER64_MODE_ONE_SHOT; 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int shutdown(struct clock_event_device *evt) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci timer64_mode = TIMER64_MODE_DISABLED; 1448c2ecf20Sopenharmony_ci timer64_disable(); 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic struct clock_event_device t64_clockevent_device = { 1498c2ecf20Sopenharmony_ci .name = "TIMER64_EVT32_TIMER", 1508c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_ONESHOT | 1518c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_PERIODIC, 1528c2ecf20Sopenharmony_ci .rating = 200, 1538c2ecf20Sopenharmony_ci .set_state_shutdown = shutdown, 1548c2ecf20Sopenharmony_ci .set_state_periodic = set_periodic, 1558c2ecf20Sopenharmony_ci .set_state_oneshot = set_oneshot, 1568c2ecf20Sopenharmony_ci .set_next_event = next_event, 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic irqreturn_t timer_interrupt(int irq, void *dev_id) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct clock_event_device *cd = &t64_clockevent_device; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci cd->event_handler(cd); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_civoid __init timer64_init(void) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct clock_event_device *cd = &t64_clockevent_device; 1718c2ecf20Sopenharmony_ci struct device_node *np, *first = NULL; 1728c2ecf20Sopenharmony_ci u32 val; 1738c2ecf20Sopenharmony_ci int err, found = 0; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci for_each_compatible_node(np, NULL, "ti,c64x+timer64") { 1768c2ecf20Sopenharmony_ci err = of_property_read_u32(np, "ti,core-mask", &val); 1778c2ecf20Sopenharmony_ci if (!err) { 1788c2ecf20Sopenharmony_ci if (val & (1 << get_coreid())) { 1798c2ecf20Sopenharmony_ci found = 1; 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } else if (!first) 1838c2ecf20Sopenharmony_ci first = np; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci if (!found) { 1868c2ecf20Sopenharmony_ci /* try first one with no core-mask */ 1878c2ecf20Sopenharmony_ci if (first) 1888c2ecf20Sopenharmony_ci np = of_node_get(first); 1898c2ecf20Sopenharmony_ci else { 1908c2ecf20Sopenharmony_ci pr_debug("Cannot find ti,c64x+timer64 timer.\n"); 1918c2ecf20Sopenharmony_ci return; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci timer = of_iomap(np, 0); 1968c2ecf20Sopenharmony_ci if (!timer) { 1978c2ecf20Sopenharmony_ci pr_debug("%pOF: Cannot map timer registers.\n", np); 1988c2ecf20Sopenharmony_ci goto out; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci pr_debug("%pOF: Timer registers=%p.\n", np, timer); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci cd->irq = irq_of_parse_and_map(np, 0); 2038c2ecf20Sopenharmony_ci if (cd->irq == NO_IRQ) { 2048c2ecf20Sopenharmony_ci pr_debug("%pOF: Cannot find interrupt.\n", np); 2058c2ecf20Sopenharmony_ci iounmap(timer); 2068c2ecf20Sopenharmony_ci goto out; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* If there is a device state control, save the ID. */ 2108c2ecf20Sopenharmony_ci err = of_property_read_u32(np, "ti,dscr-dev-enable", &val); 2118c2ecf20Sopenharmony_ci if (!err) { 2128c2ecf20Sopenharmony_ci timer64_devstate_id = val; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * It is necessary to enable the timer block here because 2168c2ecf20Sopenharmony_ci * the TIMER_DIVISOR macro needs to read a timer register 2178c2ecf20Sopenharmony_ci * to get the divisor. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_ENABLED); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci pr_debug("%pOF: Timer irq=%d.\n", np, cd->irq); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci clockevents_calc_mult_shift(cd, c6x_core_freq / TIMER_DIVISOR, 5); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd); 2278c2ecf20Sopenharmony_ci cd->max_delta_ticks = 0x7fffffff; 2288c2ecf20Sopenharmony_ci cd->min_delta_ns = clockevent_delta2ns(250, cd); 2298c2ecf20Sopenharmony_ci cd->min_delta_ticks = 250; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci cd->cpumask = cpumask_of(smp_processor_id()); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci clockevents_register_device(cd); 2348c2ecf20Sopenharmony_ci if (request_irq(cd->irq, timer_interrupt, IRQF_TIMER, "timer", 2358c2ecf20Sopenharmony_ci &t64_clockevent_device)) 2368c2ecf20Sopenharmony_ci pr_err("Failed to request irq %d (timer)\n", cd->irq); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ciout: 2398c2ecf20Sopenharmony_ci of_node_put(np); 2408c2ecf20Sopenharmony_ci return; 2418c2ecf20Sopenharmony_ci} 242