1157086Simp/*-
2157086Simp * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
3157086Simp *
4157086Simp * Redistribution and use in source and binary forms, with or without
5157086Simp * modification, are permitted provided that the following conditions
6157086Simp * are met:
7157086Simp * 1. Redistributions of source code must retain the above copyright
8157086Simp *    notice, this list of conditions and the following disclaimer.
9157086Simp * 2. Redistributions in binary form must reproduce the above copyright
10157086Simp *    notice, this list of conditions and the following disclaimer in the
11157086Simp *    documentation and/or other materials provided with the distribution.
12157086Simp *
13185265Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14185265Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15185265Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16185265Simp * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17185265Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18185265Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19185265Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20185265Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21185265Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22185265Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23185265Simp * SUCH DAMAGE.
24157086Simp */
25157086Simp
26157086Simp#include <sys/cdefs.h>
27157086Simp__FBSDID("$FreeBSD$");
28157086Simp
29157086Simp#include <sys/param.h>
30157086Simp#include <sys/systm.h>
31157086Simp#include <sys/bus.h>
32157086Simp#include <sys/clock.h>
33157086Simp#include <sys/conf.h>
34157086Simp#include <sys/kernel.h>
35157086Simp#include <sys/lock.h>
36157086Simp#include <sys/mbuf.h>
37157086Simp#include <sys/malloc.h>
38157086Simp#include <sys/module.h>
39157086Simp#include <sys/mutex.h>
40157086Simp#include <sys/rman.h>
41157086Simp#include <machine/bus.h>
42157086Simp
43157086Simp#include <arm/at91/at91_rtcreg.h>
44157086Simp
45157086Simp#include "clock_if.h"
46157086Simp
47157086Simpstruct at91_rtc_softc
48157086Simp{
49157086Simp	device_t dev;			/* Myself */
50157086Simp	void *intrhand;			/* Interrupt handle */
51157086Simp	struct resource *irq_res;	/* IRQ resource */
52157086Simp	struct resource	*mem_res;	/* Memory resource */
53157086Simp	struct mtx sc_mtx;		/* basically a perimeter lock */
54157086Simp};
55157086Simp
56157086Simpstatic inline uint32_t
57157086SimpRD4(struct at91_rtc_softc *sc, bus_size_t off)
58157086Simp{
59157086Simp	return bus_read_4(sc->mem_res, off);
60157086Simp}
61157086Simp
62157086Simpstatic inline void
63157086SimpWR4(struct at91_rtc_softc *sc, bus_size_t off, uint32_t val)
64157086Simp{
65157086Simp	bus_write_4(sc->mem_res, off, val);
66157086Simp}
67157086Simp
68157086Simp#define AT91_RTC_LOCK(_sc)		mtx_lock_spin(&(_sc)->sc_mtx)
69157086Simp#define	AT91_RTC_UNLOCK(_sc)		mtx_unlock_spin(&(_sc)->sc_mtx)
70157086Simp#define AT91_RTC_LOCK_INIT(_sc) \
71157086Simp	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
72157086Simp	    "rtc", MTX_SPIN)
73157086Simp#define AT91_RTC_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
74157086Simp#define AT91_RTC_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
75157086Simp#define AT91_RTC_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
76157086Simp
77157086Simpstatic devclass_t at91_rtc_devclass;
78157086Simp
79157086Simp/* bus entry points */
80157086Simp
81157086Simpstatic int at91_rtc_probe(device_t dev);
82157086Simpstatic int at91_rtc_attach(device_t dev);
83157086Simpstatic int at91_rtc_detach(device_t dev);
84167069Spisostatic int at91_rtc_intr(void *);
85157086Simp
86157086Simp/* helper routines */
87157086Simpstatic int at91_rtc_activate(device_t dev);
88157086Simpstatic void at91_rtc_deactivate(device_t dev);
89157086Simp
90157086Simpstatic int
91157086Simpat91_rtc_probe(device_t dev)
92157086Simp{
93157086Simp	device_set_desc(dev, "RTC");
94157086Simp	return (0);
95157086Simp}
96157086Simp
97157086Simpstatic int
98157086Simpat91_rtc_attach(device_t dev)
99157086Simp{
100157086Simp	struct at91_rtc_softc *sc = device_get_softc(dev);
101157086Simp	int err;
102157086Simp
103157086Simp	sc->dev = dev;
104157086Simp	err = at91_rtc_activate(dev);
105157086Simp	if (err)
106157086Simp		goto out;
107157086Simp
108157086Simp	AT91_RTC_LOCK_INIT(sc);
109157086Simp
110157086Simp	/*
111157086Simp	 * Activate the interrupt, but disable all interrupts in the hardware
112157086Simp	 */
113157086Simp	WR4(sc, RTC_IDR, 0xffffffff);
114166901Spiso	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC,
115166901Spiso	    at91_rtc_intr, NULL, sc, &sc->intrhand);
116157086Simp	if (err) {
117157086Simp		AT91_RTC_LOCK_DESTROY(sc);
118157086Simp		goto out;
119157086Simp	}
120157086Simp	clock_register(dev, 1000000);
121237093Smariusout:
122157086Simp	if (err)
123157086Simp		at91_rtc_deactivate(dev);
124157086Simp	return (err);
125157086Simp}
126157086Simp
127157086Simpstatic int
128157086Simpat91_rtc_detach(device_t dev)
129157086Simp{
130157086Simp	return (EBUSY);	/* XXX */
131157086Simp}
132157086Simp
133157086Simpstatic int
134157086Simpat91_rtc_activate(device_t dev)
135157086Simp{
136157086Simp	struct at91_rtc_softc *sc;
137157086Simp	int rid;
138157086Simp
139157086Simp	sc = device_get_softc(dev);
140157086Simp	rid = 0;
141157086Simp	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
142157086Simp	    RF_ACTIVE);
143157086Simp	if (sc->mem_res == NULL)
144157086Simp		goto errout;
145157086Simp	rid = 0;
146157086Simp	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
147157086Simp	    RF_ACTIVE | RF_SHAREABLE);
148157086Simp	if (sc->irq_res == NULL)
149157086Simp		goto errout;
150157086Simp	return (0);
151157086Simperrout:
152157086Simp	at91_rtc_deactivate(dev);
153157086Simp	return (ENOMEM);
154157086Simp}
155157086Simp
156157086Simpstatic void
157157086Simpat91_rtc_deactivate(device_t dev)
158157086Simp{
159157086Simp	struct at91_rtc_softc *sc;
160157086Simp
161157086Simp	sc = device_get_softc(dev);
162157086Simp	if (sc->intrhand)
163157086Simp		bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
164157086Simp	sc->intrhand = 0;
165157086Simp	bus_generic_detach(sc->dev);
166157086Simp	if (sc->mem_res)
167157086Simp		bus_release_resource(dev, SYS_RES_IOPORT,
168157086Simp		    rman_get_rid(sc->mem_res), sc->mem_res);
169157086Simp	sc->mem_res = 0;
170157086Simp	if (sc->irq_res)
171157086Simp		bus_release_resource(dev, SYS_RES_IRQ,
172157086Simp		    rman_get_rid(sc->irq_res), sc->irq_res);
173157086Simp	sc->irq_res = 0;
174157086Simp	return;
175157086Simp}
176157086Simp
177166901Spisostatic int
178157086Simpat91_rtc_intr(void *xsc)
179157086Simp{
180157086Simp	struct at91_rtc_softc *sc = xsc;
181157086Simp#if 0
182157086Simp	uint32_t status;
183157086Simp
184157086Simp	/* Reading the status also clears the interrupt */
185157086Simp	status = RD4(sc, RTC_SR);
186157086Simp	if (status == 0)
187157086Simp		return;
188157086Simp	AT91_RTC_LOCK(sc);
189157086Simp	AT91_RTC_UNLOCK(sc);
190157086Simp#endif
191157086Simp	wakeup(sc);
192166901Spiso	return (FILTER_HANDLED);
193157086Simp}
194157086Simp
195157086Simp/*
196157086Simp * Get the time of day clock and return it in ts.
197157086Simp * Return 0 on success, an error number otherwise.
198157086Simp */
199157086Simpstatic int
200157086Simpat91_rtc_gettime(device_t dev, struct timespec *ts)
201157086Simp{
202157086Simp	struct clocktime ct;
203157086Simp	uint32_t timr, calr;
204157086Simp	struct at91_rtc_softc *sc;
205157086Simp
206157086Simp	sc = device_get_softc(dev);
207157086Simp	timr = RD4(sc, RTC_TIMR);
208157086Simp	calr = RD4(sc, RTC_CALR);
209157086Simp	ct.nsec = 0;
210157086Simp	ct.sec = RTC_TIMR_SEC(timr);
211157086Simp	ct.min = RTC_TIMR_MIN(timr);
212157086Simp	ct.hour = RTC_TIMR_HR(timr);
213157086Simp	ct.year = RTC_CALR_CEN(calr) * 100 + RTC_CALR_YEAR(calr);
214157086Simp	ct.mon = RTC_CALR_MON(calr);
215157086Simp	ct.day = RTC_CALR_DAY(calr);
216157086Simp	ct.dow = -1;
217157086Simp	return clock_ct_to_ts(&ct, ts);
218157086Simp}
219157086Simp
220157086Simp/*
221157086Simp * Set the time of day clock based on the value of the struct timespec arg.
222157086Simp * Return 0 on success, an error number otherwise.
223157086Simp */
224157086Simpstatic int
225157086Simpat91_rtc_settime(device_t dev, struct timespec *ts)
226157086Simp{
227160359Simp	struct at91_rtc_softc *sc;
228160359Simp	struct clocktime ct;
229160359Simp
230160359Simp	sc = device_get_softc(dev);
231160359Simp	clock_ts_to_ct(ts, &ct);
232160359Simp	WR4(sc, RTC_TIMR, RTC_TIMR_MK(ct.hour, ct.min, ct.sec));
233160359Simp	WR4(sc, RTC_CALR, RTC_CALR_MK(ct.year, ct.mon, ct.day, ct.dow));
234160359Simp	return (0);
235157086Simp}
236157086Simp
237157086Simpstatic device_method_t at91_rtc_methods[] = {
238157086Simp	/* Device interface */
239157086Simp	DEVMETHOD(device_probe,		at91_rtc_probe),
240157086Simp	DEVMETHOD(device_attach,	at91_rtc_attach),
241157086Simp	DEVMETHOD(device_detach,	at91_rtc_detach),
242157086Simp
243157086Simp        /* clock interface */
244157086Simp        DEVMETHOD(clock_gettime,        at91_rtc_gettime),
245157086Simp        DEVMETHOD(clock_settime,        at91_rtc_settime),
246157086Simp
247157086Simp	{ 0, 0 }
248157086Simp};
249157086Simp
250157086Simpstatic driver_t at91_rtc_driver = {
251157086Simp	"at91_rtc",
252157086Simp	at91_rtc_methods,
253157086Simp	sizeof(struct at91_rtc_softc),
254157086Simp};
255157086Simp
256157086SimpDRIVER_MODULE(at91_rtc, atmelarm, at91_rtc_driver, at91_rtc_devclass, 0, 0);
257