xref: /netbsd/sys/arch/dreamcast/dev/g2/g2rtc.c (revision 6550d01e)
1 /* $NetBSD: g2rtc.c,v 1.5 2010/09/01 17:06:00 tsutsui Exp $ */
2 
3 /*-
4  * Copyright (c) 2002 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: g2rtc.c,v 1.5 2010/09/01 17:06:00 tsutsui Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/device.h>
35 
36 #include <dev/clock_subr.h>
37 
38 #include <machine/bus.h>
39 #include <dreamcast/dev/g2/g2busvar.h>
40 
41 
42 #define G2RTC_REG_BASE	0x00710000
43 #define G2RTC_REG_SIZE	12
44 
45 /* Offset by 20 years, 5 of them are leap */
46 #define G2RTC_OFFSET	(20 * SECYR + 5 * SECDAY)
47 
48 struct g2rtc_softc {
49 	device_t sc_dev;
50 
51 	bus_space_tag_t sc_bt;
52 	bus_space_handle_t sc_bh;
53 	struct todr_chip_handle sc_tch;
54 };
55 
56 /* autoconf glue */
57 static int g2rtc_match(device_t, cfdata_t, void *);
58 static void g2rtc_attach(device_t, device_t, void *);
59 
60 CFATTACH_DECL_NEW(g2rtc, sizeof(struct g2rtc_softc),
61     g2rtc_match, g2rtc_attach, NULL, NULL);
62 
63 
64 /* todr(9) methods */
65 static int g2rtc_todr_gettime(todr_chip_handle_t, struct timeval *);
66 static int g2rtc_todr_settime(todr_chip_handle_t, struct timeval *);
67 
68 static inline uint32_t g2rtc_read(bus_space_tag_t, bus_space_handle_t);
69 
70 
71 static int
72 g2rtc_match(device_t parent, cfdata_t cf, void *aux)
73 {
74 	static int g2rtc_matched = 0;
75 
76 	if (g2rtc_matched)
77 		return 0;
78 
79 	g2rtc_matched = 1;
80 	return 1;
81 }
82 
83 
84 static void
85 g2rtc_attach(device_t parent, device_t self, void *aux)
86 {
87 	struct g2rtc_softc *sc = device_private(self);
88 	struct g2bus_attach_args *ga = aux;
89 	todr_chip_handle_t tch;
90 
91 	sc->sc_dev = self;
92 	sc->sc_bt = ga->ga_memt;
93 	if (bus_space_map(sc->sc_bt, G2RTC_REG_BASE, G2RTC_REG_SIZE, 0,
94 	    &sc->sc_bh) != 0) {
95 		printf(": unable to map registers\n");
96 		return;
97 	}
98 	printf(": time-of-day clock\n");
99 
100 	tch = &sc->sc_tch;
101 	tch->cookie = sc;
102 	tch->todr_gettime = g2rtc_todr_gettime,
103 	tch->todr_settime = g2rtc_todr_settime,
104 	todr_attach(tch);
105 }
106 
107 
108 static inline uint32_t
109 g2rtc_read(bus_space_tag_t bt, bus_space_handle_t bh)
110 {
111 
112 	return ((bus_space_read_4(bt, bh, 0) & 0xffff) << 16)
113 	    | (bus_space_read_4(bt, bh, 4) & 0xffff);
114 }
115 
116 
117 /*
118  * Get time-of-day and convert to `struct timeval'.
119  * Return 0 on success; an error number otherwise.
120  */
121 static int
122 g2rtc_todr_gettime(todr_chip_handle_t handle, struct timeval *tv)
123 {
124 	struct g2rtc_softc *sc = handle->cookie;
125 	uint32_t new, old;
126 	int i;
127 
128 	for (old = 0;;) {
129 		for (i = 0; i < 3; i++) {
130 			new = g2rtc_read(sc->sc_bt, sc->sc_bh);
131 			if (new != old)
132 				break;
133 		}
134 		if (i < 3)
135 			old = new;
136 		else
137 			break;
138 	}
139 
140 	tv->tv_sec = new - G2RTC_OFFSET;
141 	tv->tv_usec = 0;
142 
143 	return 0;
144 }
145 
146 
147 /*
148  * Set the time-of-day clock based on the value of the `struct timeval' arg.
149  * Return 0 on success; an error number otherwise.
150  */
151 static int
152 g2rtc_todr_settime(todr_chip_handle_t handle, struct timeval *tv)
153 {
154 	struct g2rtc_softc *sc = handle->cookie;
155 	uint32_t secs;
156 	int i, retry;
157 
158 	secs = (uint32_t)tv->tv_sec + G2RTC_OFFSET;
159 
160 	for (retry = 0; retry < 5; retry++) {
161 
162 		/* Don't change the order */
163 		bus_space_write_4(sc->sc_bt, sc->sc_bh, 8, 1);
164 		bus_space_write_4(sc->sc_bt, sc->sc_bh, 4, secs & 0xffff);
165 		bus_space_write_4(sc->sc_bt, sc->sc_bh, 0, secs >> 16);
166 
167 		/* verify */
168 		for (i = 0; i < 3; i++)
169 			if (g2rtc_read(sc->sc_bt, sc->sc_bh) == secs)
170 				return 0; /* ok */
171 	}
172 
173 	return EIO;
174 }
175