No empty .Rs/.Re
[netbsd-mini2440.git] / sys / arch / arm / omap / omap_rtc.c
blob17a478f4cfd1b0aebea9452c26d8fe8d3fd5f927
1 /* $NetBSD: omap_rtc.c,v 1.3 2008/11/21 17:13:07 matt Exp $ */
3 /*
4 * OMAP RTC driver, based on i80321_timer.c.
6 * Copyright (c) 2007 Microsoft
7 * All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Microsoft
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
22 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTERS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: omap_rtc.c,v 1.3 2008/11/21 17:13:07 matt Exp $");
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/time.h>
42 #include <sys/device.h>
43 #include <dev/clock_subr.h>
45 #include <machine/bus.h>
46 #include <machine/intr.h>
48 #include <arm/omap/omap_reg.h>
49 #include <arm/omap/omap_tipb.h>
51 /* RTC year values relative to this year */
53 #define BASEYEAR 2000
55 /* Register offsets and bit fields. */
57 #define SECONDS_REG 0x00
58 #define MINUTES_REG 0x04
59 #define HOURS_REG 0x08
60 #define DAYS_REG 0x0c
61 #define MONTHS_REG 0x10
62 #define YEARS_REG 0x14
63 #define WEEKS_REG 0x18
64 #define ALARM_SECONDS_REG 0x20
65 #define ALARM_MINUTES_REG 0x24
66 #define ALARM_HOURS_REG 0x28
67 #define ALARM_DAYS_REG 0x2c
68 #define ALARM_MONTHS_REG 0x30
69 #define ALARM_YEARS_REG 0x34
71 #define RTC_CTRL_REG 0x40
72 #define STOP_RTC 0
74 #define RTC_STATUS_REG 0x44
75 #define ALARM 6
76 #define BUSY 0
78 #define RTC_INTERRUPTS_REG 0x48
79 #define IT_ALARM 3
81 static int omaprtc_match(device_t, cfdata_t, void *);
82 static void omaprtc_attach(device_t, device_t, void *);
84 struct omaprtc_softc {
85 device_t sc_dev;
86 bus_space_tag_t sc_iot;
87 bus_space_handle_t sc_ioh;
88 int sc_intr;
89 void *sc_irqcookie;
90 struct todr_chip_handle sc_todr;
93 CFATTACH_DECL_NEW(omaprtc, sizeof(struct omaprtc_softc),
94 omaprtc_match, omaprtc_attach, NULL, NULL);
96 static int omaprtc_gettime(todr_chip_handle_t, struct clock_ymdhms *);
97 static int omaprtc_settime(todr_chip_handle_t, struct clock_ymdhms *);
99 #define rtc_is_busy() (bus_space_read_1(sc->sc_iot, sc->sc_ioh, \
100 RTC_STATUS_REG) & 1<<BUSY)
102 static int
103 omaprtc_gettime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
105 struct omaprtc_softc *sc = tch->cookie;
106 int s;
108 /* Wait for RTC_STATUS_REG:BUSY to go low to
109 * guarantee our read is correct. BUSY will
110 * only be high for one 32kHz period (30.5us)
111 * each second, so we'll usually pass right
112 * through.
114 * Watch RTC_CTRL_REG:STOP_RTC as well so
115 * we don't spin forever if someone disables the RTC.
117 * Turn interrupts off, because we are only allowed
118 * to read/write the registers for 1/2 of a 32kHz
119 * clock period (= 15us) after detecting that BUSY
120 * is low.
123 s = disable_interrupts(I32_bit);
125 while (rtc_is_busy()) {
129 dt->dt_year =
130 FROMBCD(bus_space_read_1(sc->sc_iot,
131 sc->sc_ioh,
132 YEARS_REG)) + BASEYEAR;
133 dt->dt_mon =
134 FROMBCD(bus_space_read_1(sc->sc_iot,
135 sc->sc_ioh,
136 MONTHS_REG));
137 dt->dt_wday =
138 FROMBCD(bus_space_read_1(sc->sc_iot,
139 sc->sc_ioh,
140 WEEKS_REG) & 0x0f);
141 dt->dt_day =
142 FROMBCD(bus_space_read_1(sc->sc_iot,
143 sc->sc_ioh,
144 DAYS_REG));
145 dt->dt_sec =
146 FROMBCD(bus_space_read_1(sc->sc_iot,
147 sc->sc_ioh,
148 SECONDS_REG));
149 dt->dt_hour =
150 FROMBCD(bus_space_read_1(sc->sc_iot,
151 sc->sc_ioh,
152 HOURS_REG));
153 dt->dt_min =
154 FROMBCD(bus_space_read_1(sc->sc_iot,
155 sc->sc_ioh,
156 MINUTES_REG));
157 restore_interrupts(s);
158 return 0;
161 static int
162 omaprtc_settime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
164 struct omaprtc_softc *sc = tch->cookie;
165 int s;
167 s = disable_interrupts(I32_bit);
169 while (rtc_is_busy()) {
173 /* It's ok to write these without stopping the
174 * RTC, because the BUSY mechanism lets us guarantee
175 * that we're not in the middle of, e.g., rolling
176 * seconds into minutes.
179 bus_space_write_1(sc->sc_iot, sc->sc_ioh,
180 YEARS_REG, TOBCD(dt->dt_year - BASEYEAR));
181 bus_space_write_1(sc->sc_iot, sc->sc_ioh,
182 MONTHS_REG, TOBCD(dt->dt_mon));
183 bus_space_write_1(sc->sc_iot, sc->sc_ioh,
184 WEEKS_REG, TOBCD(dt->dt_wday & 0x0f));
185 bus_space_write_1(sc->sc_iot, sc->sc_ioh,
186 DAYS_REG, TOBCD(dt->dt_day));
187 bus_space_write_1(sc->sc_iot, sc->sc_ioh,
188 SECONDS_REG, TOBCD(dt->dt_sec));
189 bus_space_write_1(sc->sc_iot, sc->sc_ioh,
190 HOURS_REG, TOBCD(dt->dt_hour));
191 bus_space_write_1(sc->sc_iot, sc->sc_ioh,
192 MINUTES_REG, TOBCD(dt->dt_min));
193 restore_interrupts(s);
194 return 0;
197 static int
198 omaprtc_match(device_t parent, cfdata_t match, void *aux)
200 struct tipb_attach_args *tipb = aux;
202 if (tipb->tipb_addr == -1)
203 panic("omaprtc must have addr specified in config.");
205 if (tipb->tipb_size == 0)
206 tipb->tipb_size = 2048; /* Per the OMAP TRM. */
208 /* We implicitly trust the config file. */
209 return (1);
212 void
213 omaprtc_attach(device_t parent, device_t self, void *aux)
215 struct omaprtc_softc *sc = device_private(self);
216 struct tipb_attach_args *tipb = aux;
218 sc->sc_iot = tipb->tipb_iot;
219 sc->sc_intr = tipb->tipb_intr;
221 if (bus_space_map(tipb->tipb_iot, tipb->tipb_addr, tipb->tipb_size, 0,
222 &sc->sc_ioh))
223 panic("%s: Cannot map registers", device_xname(self));
225 aprint_normal(": OMAP RTC\n");
226 aprint_naive("\n");
229 * Start RTC (STOP_RTC=1 starts it, go figure), 24 hour mode,
230 * no autocompensation, no rounding, 32KHz clock enabled,
231 * cannot use split power, no test mode.
234 bus_space_write_1(sc->sc_iot, sc->sc_ioh, RTC_CTRL_REG,
235 1 << STOP_RTC);
237 sc->sc_todr.cookie = sc;
238 sc->sc_todr.todr_gettime_ymdhms = omaprtc_gettime;
239 sc->sc_todr.todr_settime_ymdhms = omaprtc_settime;
240 todr_attach(&sc->sc_todr);