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 34f0b7fabeSAlexander Shiyan static int __init _clps711x_clksrc_init(struct clk *clock, void __iomem *base) 35f0b7fabeSAlexander Shiyan { 36f0b7fabeSAlexander Shiyan unsigned long rate; 37f0b7fabeSAlexander Shiyan 38f0b7fabeSAlexander Shiyan if (!base) 39f0b7fabeSAlexander Shiyan return -ENOMEM; 40f0b7fabeSAlexander Shiyan if (IS_ERR(clock)) 41f0b7fabeSAlexander Shiyan return PTR_ERR(clock); 42f0b7fabeSAlexander Shiyan 43f0b7fabeSAlexander Shiyan rate = clk_get_rate(clock); 44f0b7fabeSAlexander Shiyan 45f0b7fabeSAlexander Shiyan tcd = base; 46f0b7fabeSAlexander Shiyan 47f0b7fabeSAlexander Shiyan clocksource_mmio_init(tcd, "clps711x-clocksource", rate, 300, 16, 48f0b7fabeSAlexander Shiyan clocksource_mmio_readw_down); 49f0b7fabeSAlexander Shiyan 50f0b7fabeSAlexander Shiyan sched_clock_register(clps711x_sched_clock_read, 16, rate); 51f0b7fabeSAlexander Shiyan 52f0b7fabeSAlexander Shiyan return 0; 53f0b7fabeSAlexander Shiyan } 54f0b7fabeSAlexander Shiyan 55f0b7fabeSAlexander Shiyan static irqreturn_t clps711x_timer_interrupt(int irq, void *dev_id) 56f0b7fabeSAlexander Shiyan { 57f0b7fabeSAlexander Shiyan struct clock_event_device *evt = dev_id; 58f0b7fabeSAlexander Shiyan 59f0b7fabeSAlexander Shiyan evt->event_handler(evt); 60f0b7fabeSAlexander Shiyan 61f0b7fabeSAlexander Shiyan return IRQ_HANDLED; 62f0b7fabeSAlexander Shiyan } 63f0b7fabeSAlexander Shiyan 64f0b7fabeSAlexander Shiyan static int __init _clps711x_clkevt_init(struct clk *clock, void __iomem *base, 65f0b7fabeSAlexander Shiyan unsigned int irq) 66f0b7fabeSAlexander Shiyan { 67f0b7fabeSAlexander Shiyan struct clock_event_device *clkevt; 68f0b7fabeSAlexander Shiyan unsigned long rate; 69f0b7fabeSAlexander Shiyan 70f0b7fabeSAlexander Shiyan if (!irq) 71f0b7fabeSAlexander Shiyan return -EINVAL; 72f0b7fabeSAlexander Shiyan if (!base) 73f0b7fabeSAlexander Shiyan return -ENOMEM; 74f0b7fabeSAlexander Shiyan if (IS_ERR(clock)) 75f0b7fabeSAlexander Shiyan return PTR_ERR(clock); 76f0b7fabeSAlexander Shiyan 77f0b7fabeSAlexander Shiyan clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL); 78f0b7fabeSAlexander Shiyan if (!clkevt) 79f0b7fabeSAlexander Shiyan return -ENOMEM; 80f0b7fabeSAlexander Shiyan 81f0b7fabeSAlexander Shiyan rate = clk_get_rate(clock); 82f0b7fabeSAlexander Shiyan 83f0b7fabeSAlexander Shiyan /* Set Timer prescaler */ 84f0b7fabeSAlexander Shiyan writew(DIV_ROUND_CLOSEST(rate, HZ), base); 85f0b7fabeSAlexander Shiyan 86f0b7fabeSAlexander Shiyan clkevt->name = "clps711x-clockevent"; 87f0b7fabeSAlexander Shiyan clkevt->rating = 300; 88f0b7fabeSAlexander Shiyan clkevt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_C3STOP; 89f0b7fabeSAlexander Shiyan clkevt->cpumask = cpumask_of(0); 90f0b7fabeSAlexander Shiyan clockevents_config_and_register(clkevt, HZ, 0, 0); 91f0b7fabeSAlexander Shiyan 92f0b7fabeSAlexander Shiyan return request_irq(irq, clps711x_timer_interrupt, IRQF_TIMER, 93f0b7fabeSAlexander Shiyan "clps711x-timer", clkevt); 94f0b7fabeSAlexander Shiyan } 95f0b7fabeSAlexander Shiyan 96f0b7fabeSAlexander Shiyan void __init clps711x_clksrc_init(void __iomem *tc1_base, void __iomem *tc2_base, 97f0b7fabeSAlexander Shiyan unsigned int irq) 98f0b7fabeSAlexander Shiyan { 99f0b7fabeSAlexander Shiyan struct clk *tc1 = clk_get_sys("clps711x-timer.0", NULL); 100f0b7fabeSAlexander Shiyan struct clk *tc2 = clk_get_sys("clps711x-timer.1", NULL); 101f0b7fabeSAlexander Shiyan 102f0b7fabeSAlexander Shiyan BUG_ON(_clps711x_clksrc_init(tc1, tc1_base)); 103f0b7fabeSAlexander Shiyan BUG_ON(_clps711x_clkevt_init(tc2, tc2_base, irq)); 104f0b7fabeSAlexander Shiyan } 105f0b7fabeSAlexander Shiyan 106f0b7fabeSAlexander Shiyan #ifdef CONFIG_CLKSRC_OF 10704410efbSDaniel Lezcano static int __init clps711x_timer_init(struct device_node *np) 108f0b7fabeSAlexander Shiyan { 109f0b7fabeSAlexander Shiyan unsigned int irq = irq_of_parse_and_map(np, 0); 110f0b7fabeSAlexander Shiyan struct clk *clock = of_clk_get(np, 0); 111f0b7fabeSAlexander Shiyan void __iomem *base = of_iomap(np, 0); 112f0b7fabeSAlexander Shiyan 113f0b7fabeSAlexander Shiyan switch (of_alias_get_id(np, "timer")) { 114f0b7fabeSAlexander Shiyan case CLPS711X_CLKSRC_CLOCKSOURCE: 11504410efbSDaniel Lezcano return _clps711x_clksrc_init(clock, base); 116f0b7fabeSAlexander Shiyan case CLPS711X_CLKSRC_CLOCKEVENT: 11704410efbSDaniel Lezcano return _clps711x_clkevt_init(clock, base, irq); 118f0b7fabeSAlexander Shiyan default: 11904410efbSDaniel Lezcano return -EINVAL; 120f0b7fabeSAlexander Shiyan } 121f0b7fabeSAlexander Shiyan } 12204410efbSDaniel Lezcano CLOCKSOURCE_OF_DECLARE_RET(clps711x, "cirrus,clps711x-timer", clps711x_timer_init); 123f0b7fabeSAlexander Shiyan #endif 124