1  // SPDX-License-Identifier: GPL-2.0
2  
3  #include <linux/interrupt.h>
4  #include <linux/ioport.h>
5  #include <linux/clocksource.h>
6  #include <linux/clockchips.h>
7  #include <linux/module.h>
8  #include <linux/slab.h>
9  #include <linux/goldfish.h>
10  #include <clocksource/timer-goldfish.h>
11  
12  struct goldfish_timer {
13  	struct clocksource cs;
14  	struct clock_event_device ced;
15  	struct resource res;
16  	void __iomem *base;
17  };
18  
ced_to_gf(struct clock_event_device * ced)19  static struct goldfish_timer *ced_to_gf(struct clock_event_device *ced)
20  {
21  	return container_of(ced, struct goldfish_timer, ced);
22  }
23  
cs_to_gf(struct clocksource * cs)24  static struct goldfish_timer *cs_to_gf(struct clocksource *cs)
25  {
26  	return container_of(cs, struct goldfish_timer, cs);
27  }
28  
goldfish_timer_read(struct clocksource * cs)29  static u64 goldfish_timer_read(struct clocksource *cs)
30  {
31  	struct goldfish_timer *timerdrv = cs_to_gf(cs);
32  	void __iomem *base = timerdrv->base;
33  	u32 time_low, time_high;
34  	u64 ticks;
35  
36  	/*
37  	 * time_low: get low bits of current time and update time_high
38  	 * time_high: get high bits of time at last time_low read
39  	 */
40  	time_low = gf_ioread32(base + TIMER_TIME_LOW);
41  	time_high = gf_ioread32(base + TIMER_TIME_HIGH);
42  
43  	ticks = ((u64)time_high << 32) | time_low;
44  
45  	return ticks;
46  }
47  
goldfish_timer_set_oneshot(struct clock_event_device * evt)48  static int goldfish_timer_set_oneshot(struct clock_event_device *evt)
49  {
50  	struct goldfish_timer *timerdrv = ced_to_gf(evt);
51  	void __iomem *base = timerdrv->base;
52  
53  	gf_iowrite32(0, base + TIMER_ALARM_HIGH);
54  	gf_iowrite32(0, base + TIMER_ALARM_LOW);
55  	gf_iowrite32(1, base + TIMER_IRQ_ENABLED);
56  
57  	return 0;
58  }
59  
goldfish_timer_shutdown(struct clock_event_device * evt)60  static int goldfish_timer_shutdown(struct clock_event_device *evt)
61  {
62  	struct goldfish_timer *timerdrv = ced_to_gf(evt);
63  	void __iomem *base = timerdrv->base;
64  
65  	gf_iowrite32(0, base + TIMER_IRQ_ENABLED);
66  
67  	return 0;
68  }
69  
goldfish_timer_next_event(unsigned long delta,struct clock_event_device * evt)70  static int goldfish_timer_next_event(unsigned long delta,
71  				     struct clock_event_device *evt)
72  {
73  	struct goldfish_timer *timerdrv = ced_to_gf(evt);
74  	void __iomem *base = timerdrv->base;
75  	u64 now;
76  
77  	now = goldfish_timer_read(&timerdrv->cs);
78  
79  	now += delta;
80  
81  	gf_iowrite32(upper_32_bits(now), base + TIMER_ALARM_HIGH);
82  	gf_iowrite32(lower_32_bits(now), base + TIMER_ALARM_LOW);
83  
84  	return 0;
85  }
86  
goldfish_timer_irq(int irq,void * dev_id)87  static irqreturn_t goldfish_timer_irq(int irq, void *dev_id)
88  {
89  	struct goldfish_timer *timerdrv = dev_id;
90  	struct clock_event_device *evt = &timerdrv->ced;
91  	void __iomem *base = timerdrv->base;
92  
93  	gf_iowrite32(1, base + TIMER_CLEAR_INTERRUPT);
94  
95  	evt->event_handler(evt);
96  
97  	return IRQ_HANDLED;
98  }
99  
goldfish_timer_init(int irq,void __iomem * base)100  int __init goldfish_timer_init(int irq, void __iomem *base)
101  {
102  	struct goldfish_timer *timerdrv;
103  	int ret;
104  
105  	timerdrv = kzalloc(sizeof(*timerdrv), GFP_KERNEL);
106  	if (!timerdrv)
107  		return -ENOMEM;
108  
109  	timerdrv->base = base;
110  
111  	timerdrv->ced = (struct clock_event_device){
112  		.name			= "goldfish_timer",
113  		.features		= CLOCK_EVT_FEAT_ONESHOT,
114  		.set_state_shutdown	= goldfish_timer_shutdown,
115  		.set_state_oneshot      = goldfish_timer_set_oneshot,
116  		.set_next_event		= goldfish_timer_next_event,
117  	};
118  
119  	timerdrv->res = (struct resource){
120  		.name  = "goldfish_timer",
121  		.start = (unsigned long)base,
122  		.end   = (unsigned long)base + 0xfff,
123  	};
124  
125  	ret = request_resource(&iomem_resource, &timerdrv->res);
126  	if (ret) {
127  		pr_err("Cannot allocate '%s' resource\n", timerdrv->res.name);
128  		return ret;
129  	}
130  
131  	timerdrv->cs = (struct clocksource){
132  		.name		= "goldfish_timer",
133  		.rating		= 400,
134  		.read		= goldfish_timer_read,
135  		.mask		= CLOCKSOURCE_MASK(64),
136  		.flags		= 0,
137  		.max_idle_ns	= LONG_MAX,
138  	};
139  
140  	clocksource_register_hz(&timerdrv->cs, NSEC_PER_SEC);
141  
142  	ret = request_irq(irq, goldfish_timer_irq, IRQF_TIMER,
143  			  "goldfish_timer", timerdrv);
144  	if (ret) {
145  		pr_err("Couldn't register goldfish-timer interrupt\n");
146  		return ret;
147  	}
148  
149  	clockevents_config_and_register(&timerdrv->ced, NSEC_PER_SEC,
150  					1, 0xffffffff);
151  
152  	return 0;
153  }
154