1f0b7fabeSAlexander Shiyan /* 2f0b7fabeSAlexander Shiyan * Cirrus Logic CLPS711X clocksource driver 3f0b7fabeSAlexander Shiyan * 4f0b7fabeSAlexander Shiyan * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru> 5f0b7fabeSAlexander Shiyan * 6f0b7fabeSAlexander Shiyan * This program is free software; you can redistribute it and/or modify 7f0b7fabeSAlexander Shiyan * it under the terms of the GNU General Public License as published by 8f0b7fabeSAlexander Shiyan * the Free Software Foundation; either version 2 of the License, or 9f0b7fabeSAlexander Shiyan * (at your option) any later version. 10f0b7fabeSAlexander Shiyan */ 11f0b7fabeSAlexander Shiyan 12f0b7fabeSAlexander Shiyan #include <linux/clk.h> 13f0b7fabeSAlexander Shiyan #include <linux/clockchips.h> 14f0b7fabeSAlexander Shiyan #include <linux/clocksource.h> 15f0b7fabeSAlexander Shiyan #include <linux/interrupt.h> 16f0b7fabeSAlexander Shiyan #include <linux/io.h> 17f0b7fabeSAlexander Shiyan #include <linux/of_address.h> 18f0b7fabeSAlexander Shiyan #include <linux/of_irq.h> 19f0b7fabeSAlexander Shiyan #include <linux/sched_clock.h> 20f0b7fabeSAlexander Shiyan #include <linux/slab.h> 21f0b7fabeSAlexander Shiyan 22f0b7fabeSAlexander Shiyan enum { 23f0b7fabeSAlexander Shiyan CLPS711X_CLKSRC_CLOCKSOURCE, 24f0b7fabeSAlexander Shiyan CLPS711X_CLKSRC_CLOCKEVENT, 25f0b7fabeSAlexander Shiyan }; 26f0b7fabeSAlexander Shiyan 27f0b7fabeSAlexander Shiyan static void __iomem *tcd; 28f0b7fabeSAlexander Shiyan 29f0b7fabeSAlexander Shiyan static u64 notrace clps711x_sched_clock_read(void) 30f0b7fabeSAlexander Shiyan { 31f0b7fabeSAlexander Shiyan return ~readw(tcd); 32f0b7fabeSAlexander Shiyan } 33f0b7fabeSAlexander Shiyan 342a6a8e2dSAlexander Shiyan static void __init clps711x_clksrc_init(struct clk *clock, void __iomem *base) 35f0b7fabeSAlexander Shiyan { 362a6a8e2dSAlexander Shiyan unsigned long rate = clk_get_rate(clock); 37f0b7fabeSAlexander Shiyan 38f0b7fabeSAlexander Shiyan tcd = base; 39f0b7fabeSAlexander Shiyan 40f0b7fabeSAlexander Shiyan clocksource_mmio_init(tcd, "clps711x-clocksource", rate, 300, 16, 41f0b7fabeSAlexander Shiyan clocksource_mmio_readw_down); 42f0b7fabeSAlexander Shiyan 43f0b7fabeSAlexander Shiyan sched_clock_register(clps711x_sched_clock_read, 16, rate); 44f0b7fabeSAlexander Shiyan } 45f0b7fabeSAlexander Shiyan 46f0b7fabeSAlexander Shiyan static irqreturn_t clps711x_timer_interrupt(int irq, void *dev_id) 47f0b7fabeSAlexander Shiyan { 48f0b7fabeSAlexander Shiyan struct clock_event_device *evt = dev_id; 49f0b7fabeSAlexander Shiyan 50f0b7fabeSAlexander Shiyan evt->event_handler(evt); 51f0b7fabeSAlexander Shiyan 52f0b7fabeSAlexander Shiyan return IRQ_HANDLED; 53f0b7fabeSAlexander Shiyan } 54f0b7fabeSAlexander Shiyan 55f0b7fabeSAlexander Shiyan static int __init _clps711x_clkevt_init(struct clk *clock, void __iomem *base, 56f0b7fabeSAlexander Shiyan unsigned int irq) 57f0b7fabeSAlexander Shiyan { 58f0b7fabeSAlexander Shiyan struct clock_event_device *clkevt; 59f0b7fabeSAlexander Shiyan unsigned long rate; 60f0b7fabeSAlexander Shiyan 61f0b7fabeSAlexander Shiyan clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL); 62f0b7fabeSAlexander Shiyan if (!clkevt) 63f0b7fabeSAlexander Shiyan return -ENOMEM; 64f0b7fabeSAlexander Shiyan 65f0b7fabeSAlexander Shiyan rate = clk_get_rate(clock); 66f0b7fabeSAlexander Shiyan 67f0b7fabeSAlexander Shiyan /* Set Timer prescaler */ 68f0b7fabeSAlexander Shiyan writew(DIV_ROUND_CLOSEST(rate, HZ), base); 69f0b7fabeSAlexander Shiyan 70f0b7fabeSAlexander Shiyan clkevt->name = "clps711x-clockevent"; 71f0b7fabeSAlexander Shiyan clkevt->rating = 300; 72f0b7fabeSAlexander Shiyan clkevt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_C3STOP; 73f0b7fabeSAlexander Shiyan clkevt->cpumask = cpumask_of(0); 74f0b7fabeSAlexander Shiyan clockevents_config_and_register(clkevt, HZ, 0, 0); 75f0b7fabeSAlexander Shiyan 76f0b7fabeSAlexander Shiyan return request_irq(irq, clps711x_timer_interrupt, IRQF_TIMER, 77f0b7fabeSAlexander Shiyan "clps711x-timer", clkevt); 78f0b7fabeSAlexander Shiyan } 79f0b7fabeSAlexander Shiyan 8004410efbSDaniel Lezcano static int __init clps711x_timer_init(struct device_node *np) 81f0b7fabeSAlexander Shiyan { 82f0b7fabeSAlexander Shiyan unsigned int irq = irq_of_parse_and_map(np, 0); 83f0b7fabeSAlexander Shiyan struct clk *clock = of_clk_get(np, 0); 84f0b7fabeSAlexander Shiyan void __iomem *base = of_iomap(np, 0); 85f0b7fabeSAlexander Shiyan 862a6a8e2dSAlexander Shiyan if (!base) 872a6a8e2dSAlexander Shiyan return -ENOMEM; 882a6a8e2dSAlexander Shiyan if (!irq) 892a6a8e2dSAlexander Shiyan return -EINVAL; 902a6a8e2dSAlexander Shiyan if (IS_ERR(clock)) 912a6a8e2dSAlexander Shiyan return PTR_ERR(clock); 922a6a8e2dSAlexander Shiyan 93f0b7fabeSAlexander Shiyan switch (of_alias_get_id(np, "timer")) { 94f0b7fabeSAlexander Shiyan case CLPS711X_CLKSRC_CLOCKSOURCE: 952a6a8e2dSAlexander Shiyan clps711x_clksrc_init(clock, base); 962a6a8e2dSAlexander Shiyan break; 97f0b7fabeSAlexander Shiyan case CLPS711X_CLKSRC_CLOCKEVENT: 9804410efbSDaniel Lezcano return _clps711x_clkevt_init(clock, base, irq); 99f0b7fabeSAlexander Shiyan default: 10004410efbSDaniel Lezcano return -EINVAL; 101f0b7fabeSAlexander Shiyan } 1022a6a8e2dSAlexander Shiyan 1032a6a8e2dSAlexander Shiyan return 0; 104f0b7fabeSAlexander Shiyan } 10517273395SDaniel Lezcano TIMER_OF_DECLARE(clps711x, "cirrus,ep7209-timer", clps711x_timer_init); 106