1fb6002a8SChris Brandt /* 2fb6002a8SChris Brandt * Renesas Timer Support - OSTM 3fb6002a8SChris Brandt * 4fb6002a8SChris Brandt * Copyright (C) 2017 Renesas Electronics America, Inc. 5fb6002a8SChris Brandt * Copyright (C) 2017 Chris Brandt 6fb6002a8SChris Brandt * 7fb6002a8SChris Brandt * This program is free software; you can redistribute it and/or modify 8fb6002a8SChris Brandt * it under the terms of the GNU General Public License as published by 9fb6002a8SChris Brandt * the Free Software Foundation; either version 2 of the License 10fb6002a8SChris Brandt * 11fb6002a8SChris Brandt * This program is distributed in the hope that it will be useful, 12fb6002a8SChris Brandt * but WITHOUT ANY WARRANTY; without even the implied warranty of 13fb6002a8SChris Brandt * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14fb6002a8SChris Brandt * GNU General Public License for more details. 15fb6002a8SChris Brandt * 16fb6002a8SChris Brandt */ 17fb6002a8SChris Brandt 18fb6002a8SChris Brandt #include <linux/of_address.h> 19fb6002a8SChris Brandt #include <linux/of_irq.h> 20fb6002a8SChris Brandt #include <linux/clk.h> 21fb6002a8SChris Brandt #include <linux/clockchips.h> 22fb6002a8SChris Brandt #include <linux/interrupt.h> 23fb6002a8SChris Brandt #include <linux/sched_clock.h> 24fb6002a8SChris Brandt #include <linux/slab.h> 25fb6002a8SChris Brandt 26fb6002a8SChris Brandt /* 27fb6002a8SChris Brandt * The OSTM contains independent channels. 28fb6002a8SChris Brandt * The first OSTM channel probed will be set up as a free running 29fb6002a8SChris Brandt * clocksource. Additionally we will use this clocksource for the system 30fb6002a8SChris Brandt * schedule timer sched_clock(). 31fb6002a8SChris Brandt * 32fb6002a8SChris Brandt * The second (or more) channel probed will be set up as an interrupt 33fb6002a8SChris Brandt * driven clock event. 34fb6002a8SChris Brandt */ 35fb6002a8SChris Brandt 36fb6002a8SChris Brandt struct ostm_device { 37fb6002a8SChris Brandt void __iomem *base; 38fb6002a8SChris Brandt unsigned long ticks_per_jiffy; 39fb6002a8SChris Brandt struct clock_event_device ced; 40fb6002a8SChris Brandt }; 41fb6002a8SChris Brandt 42fb6002a8SChris Brandt static void __iomem *system_clock; /* For sched_clock() */ 43fb6002a8SChris Brandt 44fb6002a8SChris Brandt /* OSTM REGISTERS */ 45fb6002a8SChris Brandt #define OSTM_CMP 0x000 /* RW,32 */ 46fb6002a8SChris Brandt #define OSTM_CNT 0x004 /* R,32 */ 47fb6002a8SChris Brandt #define OSTM_TE 0x010 /* R,8 */ 48fb6002a8SChris Brandt #define OSTM_TS 0x014 /* W,8 */ 49fb6002a8SChris Brandt #define OSTM_TT 0x018 /* W,8 */ 50fb6002a8SChris Brandt #define OSTM_CTL 0x020 /* RW,8 */ 51fb6002a8SChris Brandt 52fb6002a8SChris Brandt #define TE 0x01 53fb6002a8SChris Brandt #define TS 0x01 54fb6002a8SChris Brandt #define TT 0x01 55fb6002a8SChris Brandt #define CTL_PERIODIC 0x00 56fb6002a8SChris Brandt #define CTL_ONESHOT 0x02 57fb6002a8SChris Brandt #define CTL_FREERUN 0x02 58fb6002a8SChris Brandt 59fb6002a8SChris Brandt static struct ostm_device *ced_to_ostm(struct clock_event_device *ced) 60fb6002a8SChris Brandt { 61fb6002a8SChris Brandt return container_of(ced, struct ostm_device, ced); 62fb6002a8SChris Brandt } 63fb6002a8SChris Brandt 64fb6002a8SChris Brandt static void ostm_timer_stop(struct ostm_device *ostm) 65fb6002a8SChris Brandt { 66fb6002a8SChris Brandt if (readb(ostm->base + OSTM_TE) & TE) { 67fb6002a8SChris Brandt writeb(TT, ostm->base + OSTM_TT); 68fb6002a8SChris Brandt 69fb6002a8SChris Brandt /* 70fb6002a8SChris Brandt * Read back the register simply to confirm the write operation 71fb6002a8SChris Brandt * has completed since I/O writes can sometimes get queued by 72fb6002a8SChris Brandt * the bus architecture. 73fb6002a8SChris Brandt */ 74fb6002a8SChris Brandt while (readb(ostm->base + OSTM_TE) & TE) 75fb6002a8SChris Brandt ; 76fb6002a8SChris Brandt } 77fb6002a8SChris Brandt } 78fb6002a8SChris Brandt 79fb6002a8SChris Brandt static int __init ostm_init_clksrc(struct ostm_device *ostm, unsigned long rate) 80fb6002a8SChris Brandt { 81fb6002a8SChris Brandt /* 82fb6002a8SChris Brandt * irq not used (clock sources don't use interrupts) 83fb6002a8SChris Brandt */ 84fb6002a8SChris Brandt 85fb6002a8SChris Brandt ostm_timer_stop(ostm); 86fb6002a8SChris Brandt 87fb6002a8SChris Brandt writel(0, ostm->base + OSTM_CMP); 88fb6002a8SChris Brandt writeb(CTL_FREERUN, ostm->base + OSTM_CTL); 89fb6002a8SChris Brandt writeb(TS, ostm->base + OSTM_TS); 90fb6002a8SChris Brandt 91fb6002a8SChris Brandt return clocksource_mmio_init(ostm->base + OSTM_CNT, 92fb6002a8SChris Brandt "ostm", rate, 93fb6002a8SChris Brandt 300, 32, clocksource_mmio_readl_up); 94fb6002a8SChris Brandt } 95fb6002a8SChris Brandt 96fb6002a8SChris Brandt static u64 notrace ostm_read_sched_clock(void) 97fb6002a8SChris Brandt { 98fb6002a8SChris Brandt return readl(system_clock); 99fb6002a8SChris Brandt } 100fb6002a8SChris Brandt 101fb6002a8SChris Brandt static void __init ostm_init_sched_clock(struct ostm_device *ostm, 102fb6002a8SChris Brandt unsigned long rate) 103fb6002a8SChris Brandt { 104fb6002a8SChris Brandt system_clock = ostm->base + OSTM_CNT; 105fb6002a8SChris Brandt sched_clock_register(ostm_read_sched_clock, 32, rate); 106fb6002a8SChris Brandt } 107fb6002a8SChris Brandt 108fb6002a8SChris Brandt static int ostm_clock_event_next(unsigned long delta, 109fb6002a8SChris Brandt struct clock_event_device *ced) 110fb6002a8SChris Brandt { 111fb6002a8SChris Brandt struct ostm_device *ostm = ced_to_ostm(ced); 112fb6002a8SChris Brandt 113fb6002a8SChris Brandt ostm_timer_stop(ostm); 114fb6002a8SChris Brandt 115fb6002a8SChris Brandt writel(delta, ostm->base + OSTM_CMP); 116fb6002a8SChris Brandt writeb(CTL_ONESHOT, ostm->base + OSTM_CTL); 117fb6002a8SChris Brandt writeb(TS, ostm->base + OSTM_TS); 118fb6002a8SChris Brandt 119fb6002a8SChris Brandt return 0; 120fb6002a8SChris Brandt } 121fb6002a8SChris Brandt 122fb6002a8SChris Brandt static int ostm_shutdown(struct clock_event_device *ced) 123fb6002a8SChris Brandt { 124fb6002a8SChris Brandt struct ostm_device *ostm = ced_to_ostm(ced); 125fb6002a8SChris Brandt 126fb6002a8SChris Brandt ostm_timer_stop(ostm); 127fb6002a8SChris Brandt 128fb6002a8SChris Brandt return 0; 129fb6002a8SChris Brandt } 130fb6002a8SChris Brandt static int ostm_set_periodic(struct clock_event_device *ced) 131fb6002a8SChris Brandt { 132fb6002a8SChris Brandt struct ostm_device *ostm = ced_to_ostm(ced); 133fb6002a8SChris Brandt 134fb6002a8SChris Brandt if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced)) 135fb6002a8SChris Brandt ostm_timer_stop(ostm); 136fb6002a8SChris Brandt 137fb6002a8SChris Brandt writel(ostm->ticks_per_jiffy - 1, ostm->base + OSTM_CMP); 138fb6002a8SChris Brandt writeb(CTL_PERIODIC, ostm->base + OSTM_CTL); 139fb6002a8SChris Brandt writeb(TS, ostm->base + OSTM_TS); 140fb6002a8SChris Brandt 141fb6002a8SChris Brandt return 0; 142fb6002a8SChris Brandt } 143fb6002a8SChris Brandt 144fb6002a8SChris Brandt static int ostm_set_oneshot(struct clock_event_device *ced) 145fb6002a8SChris Brandt { 146fb6002a8SChris Brandt struct ostm_device *ostm = ced_to_ostm(ced); 147fb6002a8SChris Brandt 148fb6002a8SChris Brandt ostm_timer_stop(ostm); 149fb6002a8SChris Brandt 150fb6002a8SChris Brandt return 0; 151fb6002a8SChris Brandt } 152fb6002a8SChris Brandt 153fb6002a8SChris Brandt static irqreturn_t ostm_timer_interrupt(int irq, void *dev_id) 154fb6002a8SChris Brandt { 155fb6002a8SChris Brandt struct ostm_device *ostm = dev_id; 156fb6002a8SChris Brandt 157fb6002a8SChris Brandt if (clockevent_state_oneshot(&ostm->ced)) 158fb6002a8SChris Brandt ostm_timer_stop(ostm); 159fb6002a8SChris Brandt 160fb6002a8SChris Brandt /* notify clockevent layer */ 161fb6002a8SChris Brandt if (ostm->ced.event_handler) 162fb6002a8SChris Brandt ostm->ced.event_handler(&ostm->ced); 163fb6002a8SChris Brandt 164fb6002a8SChris Brandt return IRQ_HANDLED; 165fb6002a8SChris Brandt } 166fb6002a8SChris Brandt 167fb6002a8SChris Brandt static int __init ostm_init_clkevt(struct ostm_device *ostm, int irq, 168fb6002a8SChris Brandt unsigned long rate) 169fb6002a8SChris Brandt { 170fb6002a8SChris Brandt struct clock_event_device *ced = &ostm->ced; 171fb6002a8SChris Brandt int ret = -ENXIO; 172fb6002a8SChris Brandt 173fb6002a8SChris Brandt ret = request_irq(irq, ostm_timer_interrupt, 174fb6002a8SChris Brandt IRQF_TIMER | IRQF_IRQPOLL, 175fb6002a8SChris Brandt "ostm", ostm); 176fb6002a8SChris Brandt if (ret) { 177fb6002a8SChris Brandt pr_err("ostm: failed to request irq\n"); 178fb6002a8SChris Brandt return ret; 179fb6002a8SChris Brandt } 180fb6002a8SChris Brandt 181fb6002a8SChris Brandt ced->name = "ostm"; 182fb6002a8SChris Brandt ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC; 183fb6002a8SChris Brandt ced->set_state_shutdown = ostm_shutdown; 184fb6002a8SChris Brandt ced->set_state_periodic = ostm_set_periodic; 185fb6002a8SChris Brandt ced->set_state_oneshot = ostm_set_oneshot; 186fb6002a8SChris Brandt ced->set_next_event = ostm_clock_event_next; 187fb6002a8SChris Brandt ced->shift = 32; 188fb6002a8SChris Brandt ced->rating = 300; 189fb6002a8SChris Brandt ced->cpumask = cpumask_of(0); 190fb6002a8SChris Brandt clockevents_config_and_register(ced, rate, 0xf, 0xffffffff); 191fb6002a8SChris Brandt 192fb6002a8SChris Brandt return 0; 193fb6002a8SChris Brandt } 194fb6002a8SChris Brandt 195fb6002a8SChris Brandt static int __init ostm_init(struct device_node *np) 196fb6002a8SChris Brandt { 197fb6002a8SChris Brandt struct ostm_device *ostm; 198fb6002a8SChris Brandt int ret = -EFAULT; 199fb6002a8SChris Brandt struct clk *ostm_clk = NULL; 200fb6002a8SChris Brandt int irq; 201fb6002a8SChris Brandt unsigned long rate; 202fb6002a8SChris Brandt 203fb6002a8SChris Brandt ostm = kzalloc(sizeof(*ostm), GFP_KERNEL); 204fb6002a8SChris Brandt if (!ostm) 205fb6002a8SChris Brandt return -ENOMEM; 206fb6002a8SChris Brandt 207fb6002a8SChris Brandt ostm->base = of_iomap(np, 0); 208fb6002a8SChris Brandt if (!ostm->base) { 209fb6002a8SChris Brandt pr_err("ostm: failed to remap I/O memory\n"); 210fb6002a8SChris Brandt goto err; 211fb6002a8SChris Brandt } 212fb6002a8SChris Brandt 213fb6002a8SChris Brandt irq = irq_of_parse_and_map(np, 0); 214fb6002a8SChris Brandt if (irq < 0) { 215fb6002a8SChris Brandt pr_err("ostm: Failed to get irq\n"); 216fb6002a8SChris Brandt goto err; 217fb6002a8SChris Brandt } 218fb6002a8SChris Brandt 219fb6002a8SChris Brandt ostm_clk = of_clk_get(np, 0); 220fb6002a8SChris Brandt if (IS_ERR(ostm_clk)) { 221fb6002a8SChris Brandt pr_err("ostm: Failed to get clock\n"); 222fb6002a8SChris Brandt ostm_clk = NULL; 223fb6002a8SChris Brandt goto err; 224fb6002a8SChris Brandt } 225fb6002a8SChris Brandt 226fb6002a8SChris Brandt ret = clk_prepare_enable(ostm_clk); 227fb6002a8SChris Brandt if (ret) { 228fb6002a8SChris Brandt pr_err("ostm: Failed to enable clock\n"); 229fb6002a8SChris Brandt goto err; 230fb6002a8SChris Brandt } 231fb6002a8SChris Brandt 232fb6002a8SChris Brandt rate = clk_get_rate(ostm_clk); 233fb6002a8SChris Brandt ostm->ticks_per_jiffy = (rate + HZ / 2) / HZ; 234fb6002a8SChris Brandt 235fb6002a8SChris Brandt /* 236fb6002a8SChris Brandt * First probed device will be used as system clocksource. Any 237fb6002a8SChris Brandt * additional devices will be used as clock events. 238fb6002a8SChris Brandt */ 239fb6002a8SChris Brandt if (!system_clock) { 240fb6002a8SChris Brandt ret = ostm_init_clksrc(ostm, rate); 241fb6002a8SChris Brandt 242fb6002a8SChris Brandt if (!ret) { 243fb6002a8SChris Brandt ostm_init_sched_clock(ostm, rate); 244fb6002a8SChris Brandt pr_info("ostm: used for clocksource\n"); 245fb6002a8SChris Brandt } 246fb6002a8SChris Brandt 247fb6002a8SChris Brandt } else { 248fb6002a8SChris Brandt ret = ostm_init_clkevt(ostm, irq, rate); 249fb6002a8SChris Brandt 250fb6002a8SChris Brandt if (!ret) 251fb6002a8SChris Brandt pr_info("ostm: used for clock events\n"); 252fb6002a8SChris Brandt } 253fb6002a8SChris Brandt 254fb6002a8SChris Brandt err: 255fb6002a8SChris Brandt if (ret) { 256fb6002a8SChris Brandt clk_disable_unprepare(ostm_clk); 257fb6002a8SChris Brandt iounmap(ostm->base); 258fb6002a8SChris Brandt kfree(ostm); 259fb6002a8SChris Brandt return ret; 260fb6002a8SChris Brandt } 261fb6002a8SChris Brandt 262fb6002a8SChris Brandt return 0; 263fb6002a8SChris Brandt } 264fb6002a8SChris Brandt 265*17273395SDaniel Lezcano TIMER_OF_DECLARE(ostm, "renesas,ostm", ostm_init); 266