11f2acc5aSJohn Crispin /* 21f2acc5aSJohn Crispin * This file is subject to the terms and conditions of the GNU General Public 31f2acc5aSJohn Crispin * License. See the file "COPYING" in the main directory of this archive 41f2acc5aSJohn Crispin * for more details. 51f2acc5aSJohn Crispin * 61f2acc5aSJohn Crispin * Copyright (C) 2013 by John Crispin <blogic@openwrt.org> 71f2acc5aSJohn Crispin */ 81f2acc5aSJohn Crispin 91f2acc5aSJohn Crispin #include <linux/clockchips.h> 101f2acc5aSJohn Crispin #include <linux/clocksource.h> 111f2acc5aSJohn Crispin #include <linux/interrupt.h> 121f2acc5aSJohn Crispin #include <linux/reset.h> 131f2acc5aSJohn Crispin #include <linux/init.h> 141f2acc5aSJohn Crispin #include <linux/time.h> 151f2acc5aSJohn Crispin #include <linux/of.h> 161f2acc5aSJohn Crispin #include <linux/of_irq.h> 171f2acc5aSJohn Crispin #include <linux/of_address.h> 181f2acc5aSJohn Crispin 191f2acc5aSJohn Crispin #include <asm/mach-ralink/ralink_regs.h> 201f2acc5aSJohn Crispin 211f2acc5aSJohn Crispin #define SYSTICK_FREQ (50 * 1000) 221f2acc5aSJohn Crispin 231f2acc5aSJohn Crispin #define SYSTICK_CONFIG 0x00 241f2acc5aSJohn Crispin #define SYSTICK_COMPARE 0x04 251f2acc5aSJohn Crispin #define SYSTICK_COUNT 0x08 261f2acc5aSJohn Crispin 271f2acc5aSJohn Crispin /* route systick irq to mips irq 7 instead of the r4k-timer */ 281f2acc5aSJohn Crispin #define CFG_EXT_STK_EN 0x2 291f2acc5aSJohn Crispin /* enable the counter */ 301f2acc5aSJohn Crispin #define CFG_CNT_EN 0x1 311f2acc5aSJohn Crispin 321f2acc5aSJohn Crispin struct systick_device { 331f2acc5aSJohn Crispin void __iomem *membase; 341f2acc5aSJohn Crispin struct clock_event_device dev; 351f2acc5aSJohn Crispin int irq_requested; 361f2acc5aSJohn Crispin int freq_scale; 371f2acc5aSJohn Crispin }; 381f2acc5aSJohn Crispin 3959113d93SViresh Kumar static int systick_set_oneshot(struct clock_event_device *evt); 4059113d93SViresh Kumar static int systick_shutdown(struct clock_event_device *evt); 411f2acc5aSJohn Crispin 421f2acc5aSJohn Crispin static int systick_next_event(unsigned long delta, 431f2acc5aSJohn Crispin struct clock_event_device *evt) 441f2acc5aSJohn Crispin { 451f2acc5aSJohn Crispin struct systick_device *sdev; 461f2acc5aSJohn Crispin u32 count; 471f2acc5aSJohn Crispin 481f2acc5aSJohn Crispin sdev = container_of(evt, struct systick_device, dev); 491f2acc5aSJohn Crispin count = ioread32(sdev->membase + SYSTICK_COUNT); 501f2acc5aSJohn Crispin count = (count + delta) % SYSTICK_FREQ; 51*37bcc03fSJohn Crispin iowrite32(count, sdev->membase + SYSTICK_COMPARE); 521f2acc5aSJohn Crispin 531f2acc5aSJohn Crispin return 0; 541f2acc5aSJohn Crispin } 551f2acc5aSJohn Crispin 561f2acc5aSJohn Crispin static void systick_event_handler(struct clock_event_device *dev) 571f2acc5aSJohn Crispin { 581f2acc5aSJohn Crispin /* noting to do here */ 591f2acc5aSJohn Crispin } 601f2acc5aSJohn Crispin 611f2acc5aSJohn Crispin static irqreturn_t systick_interrupt(int irq, void *dev_id) 621f2acc5aSJohn Crispin { 631f2acc5aSJohn Crispin struct clock_event_device *dev = (struct clock_event_device *) dev_id; 641f2acc5aSJohn Crispin 651f2acc5aSJohn Crispin dev->event_handler(dev); 661f2acc5aSJohn Crispin 671f2acc5aSJohn Crispin return IRQ_HANDLED; 681f2acc5aSJohn Crispin } 691f2acc5aSJohn Crispin 701f2acc5aSJohn Crispin static struct systick_device systick = { 711f2acc5aSJohn Crispin .dev = { 721f2acc5aSJohn Crispin /* 731f2acc5aSJohn Crispin * cevt-r4k uses 300, make sure systick 741f2acc5aSJohn Crispin * gets used if available 751f2acc5aSJohn Crispin */ 761f2acc5aSJohn Crispin .rating = 310, 771f2acc5aSJohn Crispin .features = CLOCK_EVT_FEAT_ONESHOT, 781f2acc5aSJohn Crispin .set_next_event = systick_next_event, 7959113d93SViresh Kumar .set_state_shutdown = systick_shutdown, 8059113d93SViresh Kumar .set_state_oneshot = systick_set_oneshot, 811f2acc5aSJohn Crispin .event_handler = systick_event_handler, 821f2acc5aSJohn Crispin }, 831f2acc5aSJohn Crispin }; 841f2acc5aSJohn Crispin 851f2acc5aSJohn Crispin static struct irqaction systick_irqaction = { 861f2acc5aSJohn Crispin .handler = systick_interrupt, 871f2acc5aSJohn Crispin .flags = IRQF_PERCPU | IRQF_TIMER, 881f2acc5aSJohn Crispin .dev_id = &systick.dev, 891f2acc5aSJohn Crispin }; 901f2acc5aSJohn Crispin 9159113d93SViresh Kumar static int systick_shutdown(struct clock_event_device *evt) 921f2acc5aSJohn Crispin { 931f2acc5aSJohn Crispin struct systick_device *sdev; 941f2acc5aSJohn Crispin 951f2acc5aSJohn Crispin sdev = container_of(evt, struct systick_device, dev); 961f2acc5aSJohn Crispin 9759113d93SViresh Kumar if (sdev->irq_requested) 9859113d93SViresh Kumar free_irq(systick.dev.irq, &systick_irqaction); 9959113d93SViresh Kumar sdev->irq_requested = 0; 10059113d93SViresh Kumar iowrite32(0, systick.membase + SYSTICK_CONFIG); 10159113d93SViresh Kumar 10259113d93SViresh Kumar return 0; 10359113d93SViresh Kumar } 10459113d93SViresh Kumar 10559113d93SViresh Kumar static int systick_set_oneshot(struct clock_event_device *evt) 10659113d93SViresh Kumar { 10759113d93SViresh Kumar struct systick_device *sdev; 10859113d93SViresh Kumar 10959113d93SViresh Kumar sdev = container_of(evt, struct systick_device, dev); 11059113d93SViresh Kumar 1111f2acc5aSJohn Crispin if (!sdev->irq_requested) 1121f2acc5aSJohn Crispin setup_irq(systick.dev.irq, &systick_irqaction); 1131f2acc5aSJohn Crispin sdev->irq_requested = 1; 1141f2acc5aSJohn Crispin iowrite32(CFG_EXT_STK_EN | CFG_CNT_EN, 1151f2acc5aSJohn Crispin systick.membase + SYSTICK_CONFIG); 1161f2acc5aSJohn Crispin 11759113d93SViresh Kumar return 0; 1181f2acc5aSJohn Crispin } 1191f2acc5aSJohn Crispin 1201f2acc5aSJohn Crispin static void __init ralink_systick_init(struct device_node *np) 1211f2acc5aSJohn Crispin { 1221f2acc5aSJohn Crispin systick.membase = of_iomap(np, 0); 1231f2acc5aSJohn Crispin if (!systick.membase) 1241f2acc5aSJohn Crispin return; 1251f2acc5aSJohn Crispin 1261f2acc5aSJohn Crispin systick_irqaction.name = np->name; 1271f2acc5aSJohn Crispin systick.dev.name = np->name; 1281f2acc5aSJohn Crispin clockevents_calc_mult_shift(&systick.dev, SYSTICK_FREQ, 60); 1291f2acc5aSJohn Crispin systick.dev.max_delta_ns = clockevent_delta2ns(0x7fff, &systick.dev); 1301f2acc5aSJohn Crispin systick.dev.min_delta_ns = clockevent_delta2ns(0x3, &systick.dev); 1311f2acc5aSJohn Crispin systick.dev.irq = irq_of_parse_and_map(np, 0); 1321f2acc5aSJohn Crispin if (!systick.dev.irq) { 1331f2acc5aSJohn Crispin pr_err("%s: request_irq failed", np->name); 1341f2acc5aSJohn Crispin return; 1351f2acc5aSJohn Crispin } 1361f2acc5aSJohn Crispin 1371f2acc5aSJohn Crispin clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name, 1381f2acc5aSJohn Crispin SYSTICK_FREQ, 301, 16, clocksource_mmio_readl_up); 1391f2acc5aSJohn Crispin 1401f2acc5aSJohn Crispin clockevents_register_device(&systick.dev); 1411f2acc5aSJohn Crispin 14277d84ff8SMasanari Iida pr_info("%s: running - mult: %d, shift: %d\n", 1431f2acc5aSJohn Crispin np->name, systick.dev.mult, systick.dev.shift); 1441f2acc5aSJohn Crispin } 1451f2acc5aSJohn Crispin 1461f2acc5aSJohn Crispin CLOCKSOURCE_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init); 147