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$");
29137822Smarius
30137822Smarius/*
31200915Smarius * The `rtc' device is found on the ISA bus and the EBus.  The ISA version
32200915Smarius * always is a MC146818 compatible clock while the EBus variant either is the
33200915Smarius * MC146818 compatible Real-Time Clock function of a National Semiconductor
34200915Smarius * PC87317/PC97317 which also provides Advanced Power Control functionality
35200915Smarius * or a Texas Instruments bq4802.
36137822Smarius */
37137822Smarius
38137822Smarius#include "opt_isa.h"
39137822Smarius
40137822Smarius#include <sys/param.h>
41137822Smarius#include <sys/systm.h>
42137822Smarius#include <sys/bus.h>
43137822Smarius#include <sys/kernel.h>
44146417Smarius#include <sys/lock.h>
45137822Smarius#include <sys/module.h>
46146417Smarius#include <sys/mutex.h>
47137822Smarius#include <sys/resource.h>
48137822Smarius
49137822Smarius#include <dev/ofw/ofw_bus.h>
50137822Smarius
51137822Smarius#include <machine/bus.h>
52137822Smarius#include <machine/resource.h>
53137822Smarius
54137822Smarius#include <sys/rman.h>
55137822Smarius
56137822Smarius#include <isa/isavar.h>
57137822Smarius
58170845Smarius#include <dev/mc146818/mc146818reg.h>
59137822Smarius#include <dev/mc146818/mc146818var.h>
60137822Smarius
61137822Smarius#include "clock_if.h"
62137822Smarius
63170845Smarius#define	RTC_DESC	"Real-Time Clock"
64170845Smarius
65170845Smarius#define	RTC_READ	mc146818_def_read
66170845Smarius#define	RTC_WRITE	mc146818_def_write
67170845Smarius
68170845Smarius#define	PC87317_COMMON		MC_REGA_DV0	/* bank 0 */
69170845Smarius#define	PC87317_RTC		(MC_REGA_DV1 | MC_REGA_DV0) /* bank 1 */
70170845Smarius#define	PC87317_RTC_CR		0x48		/* Century Register */
71170845Smarius#define	PC87317_APC		MC_REGA_DV2	/* bank 2 */
72170845Smarius#define	PC87317_APC_CADDR	0x51		/* Century Address Register */
73170845Smarius#define	PC87317_APC_CADDR_BANK0	0x00		/* locate CR in bank 0 */
74170845Smarius#define	PC87317_APC_CADDR_BANK1	0x80		/* locate CR in bank 1 */
75170845Smarius
76137822Smariusstatic devclass_t rtc_devclass;
77137822Smarius
78170845Smariusstatic device_attach_t rtc_attach;
79170845Smariusstatic device_probe_t rtc_ebus_probe;
80137822Smarius#ifdef DEV_ISA
81170845Smariusstatic device_probe_t rtc_isa_probe;
82137822Smarius#endif
83137822Smarius
84137822Smariusstatic device_method_t rtc_ebus_methods[] = {
85137822Smarius	/* Device interface */
86137822Smarius	DEVMETHOD(device_probe,		rtc_ebus_probe),
87137822Smarius	DEVMETHOD(device_attach,	rtc_attach),
88137822Smarius
89137822Smarius	/* clock interface */
90137822Smarius	DEVMETHOD(clock_gettime,	mc146818_gettime),
91137822Smarius	DEVMETHOD(clock_settime,	mc146818_settime),
92137822Smarius
93229093Shselasky	DEVMETHOD_END
94137822Smarius};
95137822Smarius
96137822Smariusstatic driver_t rtc_ebus_driver = {
97137822Smarius	"rtc",
98137822Smarius	rtc_ebus_methods,
99137822Smarius	sizeof(struct mc146818_softc),
100137822Smarius};
101137822Smarius
102137822SmariusDRIVER_MODULE(rtc, ebus, rtc_ebus_driver, rtc_devclass, 0, 0);
103137822Smarius
104137822Smarius#ifdef DEV_ISA
105137822Smariusstatic device_method_t rtc_isa_methods[] = {
106137822Smarius	/* Device interface */
107137822Smarius	DEVMETHOD(device_probe,		rtc_isa_probe),
108137822Smarius	DEVMETHOD(device_attach,	rtc_attach),
109137822Smarius
110137822Smarius	/* clock interface */
111137822Smarius	DEVMETHOD(clock_gettime,	mc146818_gettime),
112137822Smarius	DEVMETHOD(clock_settime,	mc146818_settime),
113137822Smarius
114229093Shselasky	DEVMETHOD_END
115137822Smarius};
116137822Smarius
117137822Smariusstatic driver_t rtc_isa_driver = {
118137822Smarius	"rtc",
119137822Smarius	rtc_isa_methods,
120137822Smarius	sizeof(struct mc146818_softc),
121137822Smarius};
122137822Smarius
123137822SmariusDRIVER_MODULE(rtc, isa, rtc_isa_driver, rtc_devclass, 0, 0);
124137822Smarius#endif
125137822Smarius
126201008Smariusstatic u_int pc87317_getcent(device_t dev);
127201008Smariusstatic void pc87317_setcent(device_t dev, u_int cent);
128170845Smarius
129137822Smariusstatic int
130137822Smariusrtc_ebus_probe(device_t dev)
131137822Smarius{
132170845Smarius
133137822Smarius	if (strcmp(ofw_bus_get_name(dev), "rtc") == 0) {
134200915Smarius		/* The bq4802 is not supported, yet. */
135200915Smarius		if (ofw_bus_get_compat(dev) != NULL &&
136200915Smarius		    strcmp(ofw_bus_get_compat(dev), "bq4802") == 0)
137200915Smarius			return (ENXIO);
138170845Smarius		device_set_desc(dev, RTC_DESC);
139137822Smarius		return (0);
140137822Smarius	}
141137822Smarius
142137822Smarius	return (ENXIO);
143137822Smarius}
144137822Smarius
145137822Smarius#ifdef DEV_ISA
146137822Smariusstatic struct isa_pnp_id rtc_isa_ids[] = {
147170845Smarius	{ 0x000bd041, RTC_DESC }, /* PNP0B00 */
148137822Smarius	{ 0 }
149137822Smarius};
150137822Smarius
151137822Smariusstatic int
152137822Smariusrtc_isa_probe(device_t dev)
153137822Smarius{
154137822Smarius
155170845Smarius	if (ISA_PNP_PROBE(device_get_parent(dev), dev, rtc_isa_ids) == 0)
156137822Smarius		return (0);
157137822Smarius
158137822Smarius	return (ENXIO);
159137822Smarius}
160137822Smarius#endif
161137822Smarius
162137822Smariusstatic int
163137822Smariusrtc_attach(device_t dev)
164137822Smarius{
165137822Smarius	struct timespec ts;
166137822Smarius	struct mc146818_softc *sc;
167137822Smarius	struct resource *res;
168170845Smarius	int ebus, error, rid;
169137822Smarius
170137822Smarius	sc = device_get_softc(dev);
171137822Smarius
172146982Smarius	mtx_init(&sc->sc_mtx, "rtc_mtx", NULL, MTX_SPIN);
173146417Smarius
174170845Smarius	ebus = 0;
175170845Smarius	if (strcmp(device_get_name(device_get_parent(dev)), "ebus") == 0)
176170845Smarius		ebus = 1;
177146417Smarius
178137822Smarius	rid = 0;
179170845Smarius	res = bus_alloc_resource_any(dev, ebus ? SYS_RES_MEMORY :
180170845Smarius	    SYS_RES_IOPORT, &rid, RF_ACTIVE);
181137822Smarius	if (res == NULL) {
182146417Smarius		device_printf(dev, "cannot allocate resources\n");
183146417Smarius		error = ENXIO;
184146417Smarius		goto fail_mtx;
185137822Smarius	}
186137822Smarius	sc->sc_bst = rman_get_bustag(res);
187137822Smarius	sc->sc_bsh = rman_get_bushandle(res);
188137822Smarius
189170845Smarius	sc->sc_mcread = RTC_READ;
190170845Smarius	sc->sc_mcwrite = RTC_WRITE;
191137822Smarius	/* The TOD clock year 0 is 0. */
192137822Smarius	sc->sc_year0 = 0;
193170845Smarius	/*
194170845Smarius	 * For ISA use the default century get/set functions, for EBus we
195170845Smarius	 * provide our own versions.
196170845Smarius	 */
197137822Smarius	sc->sc_flag = MC146818_NO_CENT_ADJUST;
198170845Smarius	if (ebus) {
199170845Smarius		/*
200170845Smarius		 * Make sure the CR is at the default location (also used
201170845Smarius		 * by Solaris).
202170845Smarius		 */
203170845Smarius		RTC_WRITE(dev, MC_REGA, PC87317_APC);
204170845Smarius		RTC_WRITE(dev, PC87317_APC_CADDR, PC87317_APC_CADDR_BANK1 |
205170845Smarius		    PC87317_RTC_CR);
206170845Smarius		RTC_WRITE(dev, MC_REGA, PC87317_COMMON);
207170845Smarius		sc->sc_getcent = pc87317_getcent;
208170845Smarius		sc->sc_setcent = pc87317_setcent;
209170845Smarius	}
210137822Smarius	if ((error = mc146818_attach(dev)) != 0) {
211137822Smarius		device_printf(dev, "cannot attach time of day clock\n");
212146417Smarius		goto fail_res;
213137822Smarius	}
214137822Smarius
215137822Smarius	if (bootverbose) {
216171553Sdwmalone		if (mc146818_gettime(dev, &ts) != 0)
217171553Sdwmalone			device_printf(dev, "invalid time");
218171553Sdwmalone		else
219171553Sdwmalone			device_printf(dev, "current time: %ld.%09ld\n",
220171553Sdwmalone			    (long)ts.tv_sec, ts.tv_nsec);
221170845Smarius	}
222137822Smarius
223137822Smarius	return (0);
224146417Smarius
225146417Smarius fail_res:
226170845Smarius	bus_release_resource(dev, ebus ? SYS_RES_MEMORY : SYS_RES_IOPORT, rid,
227170845Smarius	    res);
228146417Smarius fail_mtx:
229146417Smarius	mtx_destroy(&sc->sc_mtx);
230146417Smarius
231146417Smarius	return (error);
232137822Smarius}
233170845Smarius
234170845Smariusstatic u_int
235170845Smariuspc87317_getcent(device_t dev)
236170845Smarius{
237170845Smarius	u_int cent;
238170845Smarius
239170845Smarius	RTC_WRITE(dev, MC_REGA, PC87317_RTC);
240170845Smarius	cent = RTC_READ(dev, PC87317_RTC_CR);
241170845Smarius	RTC_WRITE(dev, MC_REGA, PC87317_COMMON);
242170845Smarius	return (cent);
243170845Smarius}
244170845Smarius
245170845Smariusstatic void
246170845Smariuspc87317_setcent(device_t dev, u_int cent)
247170845Smarius{
248170845Smarius
249170845Smarius	RTC_WRITE(dev, MC_REGA, PC87317_RTC);
250170845Smarius	RTC_WRITE(dev, PC87317_RTC_CR, cent);
251170845Smarius	RTC_WRITE(dev, MC_REGA, PC87317_COMMON);
252170845Smarius}
253