0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/clk.h>
0009 #include <linux/clockchips.h>
0010 #include <linux/clocksource.h>
0011 #include <linux/interrupt.h>
0012 #include <linux/io.h>
0013 #include <linux/of_address.h>
0014 #include <linux/of_irq.h>
0015 #include <linux/sched_clock.h>
0016 #include <linux/slab.h>
0017
0018 enum {
0019 CLPS711X_CLKSRC_CLOCKSOURCE,
0020 CLPS711X_CLKSRC_CLOCKEVENT,
0021 };
0022
0023 static void __iomem *tcd;
0024
0025 static u64 notrace clps711x_sched_clock_read(void)
0026 {
0027 return ~readw(tcd);
0028 }
0029
0030 static void __init clps711x_clksrc_init(struct clk *clock, void __iomem *base)
0031 {
0032 unsigned long rate = clk_get_rate(clock);
0033
0034 tcd = base;
0035
0036 clocksource_mmio_init(tcd, "clps711x-clocksource", rate, 300, 16,
0037 clocksource_mmio_readw_down);
0038
0039 sched_clock_register(clps711x_sched_clock_read, 16, rate);
0040 }
0041
0042 static irqreturn_t clps711x_timer_interrupt(int irq, void *dev_id)
0043 {
0044 struct clock_event_device *evt = dev_id;
0045
0046 evt->event_handler(evt);
0047
0048 return IRQ_HANDLED;
0049 }
0050
0051 static int __init _clps711x_clkevt_init(struct clk *clock, void __iomem *base,
0052 unsigned int irq)
0053 {
0054 struct clock_event_device *clkevt;
0055 unsigned long rate;
0056
0057 clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL);
0058 if (!clkevt)
0059 return -ENOMEM;
0060
0061 rate = clk_get_rate(clock);
0062
0063
0064 writew(DIV_ROUND_CLOSEST(rate, HZ), base);
0065
0066 clkevt->name = "clps711x-clockevent";
0067 clkevt->rating = 300;
0068 clkevt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_C3STOP;
0069 clkevt->cpumask = cpumask_of(0);
0070 clockevents_config_and_register(clkevt, HZ, 0, 0);
0071
0072 return request_irq(irq, clps711x_timer_interrupt, IRQF_TIMER,
0073 "clps711x-timer", clkevt);
0074 }
0075
0076 static int __init clps711x_timer_init(struct device_node *np)
0077 {
0078 unsigned int irq = irq_of_parse_and_map(np, 0);
0079 struct clk *clock = of_clk_get(np, 0);
0080 void __iomem *base = of_iomap(np, 0);
0081
0082 if (!base)
0083 return -ENOMEM;
0084 if (!irq)
0085 return -EINVAL;
0086 if (IS_ERR(clock))
0087 return PTR_ERR(clock);
0088
0089 switch (of_alias_get_id(np, "timer")) {
0090 case CLPS711X_CLKSRC_CLOCKSOURCE:
0091 clps711x_clksrc_init(clock, base);
0092 break;
0093 case CLPS711X_CLKSRC_CLOCKEVENT:
0094 return _clps711x_clkevt_init(clock, base, irq);
0095 default:
0096 return -EINVAL;
0097 }
0098
0099 return 0;
0100 }
0101 TIMER_OF_DECLARE(clps711x, "cirrus,ep7209-timer", clps711x_timer_init);