rtc.c revision 170845
1139825Simp/*-
2137822Smarius * Copyright (c) 2004 Marius Strobl <marius@FreeBSD.org>
3137822Smarius * All rights reserved.
4137822Smarius *
5137822Smarius * Redistribution and use in source and binary forms, with or without
6137822Smarius * modification, are permitted provided that the following conditions
7137822Smarius * are met:
8137822Smarius * 1. Redistributions of source code must retain the above copyright
9137822Smarius *    notice, this list of conditions and the following disclaimer.
10137822Smarius * 2. Redistributions in binary form must reproduce the above copyright
11137822Smarius *    notice, this list of conditions and the following disclaimer in the
12137822Smarius *    documentation and/or other materials provided with the distribution.
13137822Smarius *
14137822Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15137822Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16137822Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17137822Smarius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18137822Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19137822Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20137822Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21137822Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22137822Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23137822Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24137822Smarius * SUCH DAMAGE.
25137822Smarius */
26137822Smarius
27137822Smarius#include <sys/cdefs.h>
28137822Smarius__FBSDID("$FreeBSD: head/sys/sparc64/sparc64/rtc.c 170845 2007-06-16 23:17:23Z marius $");
29137822Smarius
30137822Smarius/*
31170845Smarius * The `rtc' device is a MC146818 compatible clock found on the ISA
32170845Smarius * bus and EBus. The EBus variant actually is the Real-Time Clock
33170845Smarius * function of a National Semiconductor PC87317/PC97317 which also
34170845Smarius * provides Advanced Power Control functionality.
35137822Smarius */
36137822Smarius
37137822Smarius#include "opt_isa.h"
38137822Smarius
39137822Smarius#include <sys/param.h>
40137822Smarius#include <sys/systm.h>
41137822Smarius#include <sys/bus.h>
42137822Smarius#include <sys/kernel.h>
43146417Smarius#include <sys/lock.h>
44137822Smarius#include <sys/module.h>
45146417Smarius#include <sys/mutex.h>
46137822Smarius#include <sys/resource.h>
47137822Smarius
48137822Smarius#include <dev/ofw/ofw_bus.h>
49137822Smarius
50137822Smarius#include <machine/bus.h>
51137822Smarius#include <machine/resource.h>
52137822Smarius
53137822Smarius#include <sys/rman.h>
54137822Smarius
55137822Smarius#include <isa/isavar.h>
56137822Smarius
57170845Smarius#include <dev/mc146818/mc146818reg.h>
58137822Smarius#include <dev/mc146818/mc146818var.h>
59137822Smarius
60137822Smarius#include "clock_if.h"
61137822Smarius
62170845Smarius#define	RTC_DESC	"Real-Time Clock"
63170845Smarius
64170845Smarius#define	RTC_READ	mc146818_def_read
65170845Smarius#define	RTC_WRITE	mc146818_def_write
66170845Smarius
67170845Smarius#define	PC87317_COMMON		MC_REGA_DV0	/* bank 0 */
68170845Smarius#define	PC87317_RTC		(MC_REGA_DV1 | MC_REGA_DV0) /* bank 1 */
69170845Smarius#define	PC87317_RTC_CR		0x48		/* Century Register */
70170845Smarius#define	PC87317_APC		MC_REGA_DV2	/* bank 2 */
71170845Smarius#define	PC87317_APC_CADDR	0x51		/* Century Address Register */
72170845Smarius#define	PC87317_APC_CADDR_BANK0	0x00		/* locate CR in bank 0 */
73170845Smarius#define	PC87317_APC_CADDR_BANK1	0x80		/* locate CR in bank 1 */
74170845Smarius
75137822Smariusstatic devclass_t rtc_devclass;
76137822Smarius
77170845Smariusstatic device_attach_t rtc_attach;
78170845Smariusstatic device_probe_t rtc_ebus_probe;
79137822Smarius#ifdef DEV_ISA
80170845Smariusstatic device_probe_t rtc_isa_probe;
81137822Smarius#endif
82137822Smarius
83137822Smariusstatic device_method_t rtc_ebus_methods[] = {
84137822Smarius	/* Device interface */
85137822Smarius	DEVMETHOD(device_probe,		rtc_ebus_probe),
86137822Smarius	DEVMETHOD(device_attach,	rtc_attach),
87137822Smarius
88137822Smarius	/* clock interface */
89137822Smarius	DEVMETHOD(clock_gettime,	mc146818_gettime),
90137822Smarius	DEVMETHOD(clock_settime,	mc146818_settime),
91137822Smarius
92137822Smarius	{ 0, 0 }
93137822Smarius};
94137822Smarius
95137822Smariusstatic driver_t rtc_ebus_driver = {
96137822Smarius	"rtc",
97137822Smarius	rtc_ebus_methods,
98137822Smarius	sizeof(struct mc146818_softc),
99137822Smarius};
100137822Smarius
101137822SmariusDRIVER_MODULE(rtc, ebus, rtc_ebus_driver, rtc_devclass, 0, 0);
102137822Smarius
103137822Smarius#ifdef DEV_ISA
104137822Smariusstatic device_method_t rtc_isa_methods[] = {
105137822Smarius	/* Device interface */
106137822Smarius	DEVMETHOD(device_probe,		rtc_isa_probe),
107137822Smarius	DEVMETHOD(device_attach,	rtc_attach),
108137822Smarius
109137822Smarius	/* clock interface */
110137822Smarius	DEVMETHOD(clock_gettime,	mc146818_gettime),
111137822Smarius	DEVMETHOD(clock_settime,	mc146818_settime),
112137822Smarius
113137822Smarius	{ 0, 0 }
114137822Smarius};
115137822Smarius
116137822Smariusstatic driver_t rtc_isa_driver = {
117137822Smarius	"rtc",
118137822Smarius	rtc_isa_methods,
119137822Smarius	sizeof(struct mc146818_softc),
120137822Smarius};
121137822Smarius
122137822SmariusDRIVER_MODULE(rtc, isa, rtc_isa_driver, rtc_devclass, 0, 0);
123137822Smarius#endif
124137822Smarius
125170845Smariusstatic u_int pc87317_getcent(device_t);
126170845Smariusstatic void pc87317_setcent(device_t, u_int);
127170845Smarius
128137822Smariusstatic int
129137822Smariusrtc_ebus_probe(device_t dev)
130137822Smarius{
131170845Smarius
132137822Smarius	if (strcmp(ofw_bus_get_name(dev), "rtc") == 0) {
133170845Smarius		device_set_desc(dev, RTC_DESC);
134137822Smarius		return (0);
135137822Smarius	}
136137822Smarius
137137822Smarius	return (ENXIO);
138137822Smarius}
139137822Smarius
140137822Smarius#ifdef DEV_ISA
141137822Smariusstatic struct isa_pnp_id rtc_isa_ids[] = {
142170845Smarius	{ 0x000bd041, RTC_DESC }, /* PNP0B00 */
143137822Smarius	{ 0 }
144137822Smarius};
145137822Smarius
146137822Smariusstatic int
147137822Smariusrtc_isa_probe(device_t dev)
148137822Smarius{
149137822Smarius
150170845Smarius	if (ISA_PNP_PROBE(device_get_parent(dev), dev, rtc_isa_ids) == 0)
151137822Smarius		return (0);
152137822Smarius
153137822Smarius	return (ENXIO);
154137822Smarius}
155137822Smarius#endif
156137822Smarius
157137822Smariusstatic int
158137822Smariusrtc_attach(device_t dev)
159137822Smarius{
160137822Smarius	struct timespec ts;
161137822Smarius	struct mc146818_softc *sc;
162137822Smarius	struct resource *res;
163170845Smarius	int ebus, error, rid;
164137822Smarius
165137822Smarius	sc = device_get_softc(dev);
166137822Smarius
167146982Smarius	mtx_init(&sc->sc_mtx, "rtc_mtx", NULL, MTX_SPIN);
168146417Smarius
169170845Smarius	ebus = 0;
170170845Smarius	if (strcmp(device_get_name(device_get_parent(dev)), "ebus") == 0)
171170845Smarius		ebus = 1;
172146417Smarius
173137822Smarius	rid = 0;
174170845Smarius	res = bus_alloc_resource_any(dev, ebus ? SYS_RES_MEMORY :
175170845Smarius	    SYS_RES_IOPORT, &rid, RF_ACTIVE);
176137822Smarius	if (res == NULL) {
177146417Smarius		device_printf(dev, "cannot allocate resources\n");
178146417Smarius		error = ENXIO;
179146417Smarius		goto fail_mtx;
180137822Smarius	}
181137822Smarius	sc->sc_bst = rman_get_bustag(res);
182137822Smarius	sc->sc_bsh = rman_get_bushandle(res);
183137822Smarius
184170845Smarius	sc->sc_mcread = RTC_READ;
185170845Smarius	sc->sc_mcwrite = RTC_WRITE;
186137822Smarius	/* The TOD clock year 0 is 0. */
187137822Smarius	sc->sc_year0 = 0;
188170845Smarius	/*
189170845Smarius	 * For ISA use the default century get/set functions, for EBus we
190170845Smarius	 * provide our own versions.
191170845Smarius	 */
192137822Smarius	sc->sc_flag = MC146818_NO_CENT_ADJUST;
193170845Smarius	if (ebus) {
194170845Smarius		/*
195170845Smarius		 * Make sure the CR is at the default location (also used
196170845Smarius		 * by Solaris).
197170845Smarius		 */
198170845Smarius		RTC_WRITE(dev, MC_REGA, PC87317_APC);
199170845Smarius		RTC_WRITE(dev, PC87317_APC_CADDR, PC87317_APC_CADDR_BANK1 |
200170845Smarius		    PC87317_RTC_CR);
201170845Smarius		RTC_WRITE(dev, MC_REGA, PC87317_COMMON);
202170845Smarius		sc->sc_getcent = pc87317_getcent;
203170845Smarius		sc->sc_setcent = pc87317_setcent;
204170845Smarius	}
205137822Smarius	if ((error = mc146818_attach(dev)) != 0) {
206137822Smarius		device_printf(dev, "cannot attach time of day clock\n");
207146417Smarius		goto fail_res;
208137822Smarius	}
209137822Smarius
210137822Smarius	if (bootverbose) {
211137822Smarius		mc146818_gettime(dev, &ts);
212137822Smarius		device_printf(dev, "current time: %ld.%09ld\n", (long)ts.tv_sec,
213137822Smarius		    ts.tv_nsec);
214170845Smarius	}
215137822Smarius
216137822Smarius	return (0);
217146417Smarius
218146417Smarius fail_res:
219170845Smarius	bus_release_resource(dev, ebus ? SYS_RES_MEMORY : SYS_RES_IOPORT, rid,
220170845Smarius	    res);
221146417Smarius fail_mtx:
222146417Smarius	mtx_destroy(&sc->sc_mtx);
223146417Smarius
224146417Smarius	return (error);
225137822Smarius}
226170845Smarius
227170845Smariusstatic u_int
228170845Smariuspc87317_getcent(device_t dev)
229170845Smarius{
230170845Smarius	u_int cent;
231170845Smarius
232170845Smarius	RTC_WRITE(dev, MC_REGA, PC87317_RTC);
233170845Smarius	cent = RTC_READ(dev, PC87317_RTC_CR);
234170845Smarius	RTC_WRITE(dev, MC_REGA, PC87317_COMMON);
235170845Smarius	return (cent);
236170845Smarius}
237170845Smarius
238170845Smariusstatic void
239170845Smariuspc87317_setcent(device_t dev, u_int cent)
240170845Smarius{
241170845Smarius
242170845Smarius	RTC_WRITE(dev, MC_REGA, PC87317_RTC);
243170845Smarius	RTC_WRITE(dev, PC87317_RTC_CR, cent);
244170845Smarius	RTC_WRITE(dev, MC_REGA, PC87317_COMMON);
245170845Smarius}
246