rtc.c revision 171553
1259269Spfg/*-
2255252Spfg * Copyright (c) 2004 Marius Strobl <marius@FreeBSD.org>
3259269Spfg * All rights reserved.
4259269Spfg *
5259269Spfg * Redistribution and use in source and binary forms, with or without
6259269Spfg * modification, are permitted provided that the following conditions
7259705Spfg * are met:
8259705Spfg * 1. Redistributions of source code must retain the above copyright
9259705Spfg *    notice, this list of conditions and the following disclaimer.
10259705Spfg * 2. Redistributions in binary form must reproduce the above copyright
11259705Spfg *    notice, this list of conditions and the following disclaimer in the
12259705Spfg *    documentation and/or other materials provided with the distribution.
13259705Spfg *
14259705Spfg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15259705Spfg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16259705Spfg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17259705Spfg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18259705Spfg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19259705Spfg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20259705Spfg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21259705Spfg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22259705Spfg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23259705Spfg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24259705Spfg * SUCH DAMAGE.
25259705Spfg */
26259705Spfg
27259705Spfg#include <sys/cdefs.h>
28259705Spfg__FBSDID("$FreeBSD: head/sys/sparc64/sparc64/rtc.c 171553 2007-07-23 09:42:32Z dwmalone $");
29259705Spfg
30259705Spfg/*
31259705Spfg * The `rtc' device is a MC146818 compatible clock found on the ISA
32259705Spfg * bus and EBus. The EBus variant actually is the Real-Time Clock
33259705Spfg * function of a National Semiconductor PC87317/PC97317 which also
34259705Spfg * provides Advanced Power Control functionality.
35259705Spfg */
36259705Spfg
37259705Spfg#include "opt_isa.h"
38259705Spfg
39259705Spfg#include <sys/param.h>
40259705Spfg#include <sys/systm.h>
41259705Spfg#include <sys/bus.h>
42259705Spfg#include <sys/kernel.h>
43259705Spfg#include <sys/lock.h>
44259705Spfg#include <sys/module.h>
45259705Spfg#include <sys/mutex.h>
46259705Spfg#include <sys/resource.h>
47259705Spfg
48260140Spfg#include <dev/ofw/ofw_bus.h>
49260140Spfg
50260140Spfg#include <machine/bus.h>
51260140Spfg#include <machine/resource.h>
52260140Spfg
53260140Spfg#include <sys/rman.h>
54260140Spfg
55260140Spfg#include <isa/isavar.h>
56260140Spfg
57260140Spfg#include <dev/mc146818/mc146818reg.h>
58260140Spfg#include <dev/mc146818/mc146818var.h>
59260140Spfg
60260140Spfg#include "clock_if.h"
61260140Spfg
62260075Spfg#define	RTC_DESC	"Real-Time Clock"
63260075Spfg
64260075Spfg#define	RTC_READ	mc146818_def_read
65260075Spfg#define	RTC_WRITE	mc146818_def_write
66260075Spfg
67260075Spfg#define	PC87317_COMMON		MC_REGA_DV0	/* bank 0 */
68259269Spfg#define	PC87317_RTC		(MC_REGA_DV1 | MC_REGA_DV0) /* bank 1 */
69259269Spfg#define	PC87317_RTC_CR		0x48		/* Century Register */
70255252Spfg#define	PC87317_APC		MC_REGA_DV2	/* bank 2 */
71255252Spfg#define	PC87317_APC_CADDR	0x51		/* Century Address Register */
72255252Spfg#define	PC87317_APC_CADDR_BANK0	0x00		/* locate CR in bank 0 */
73255252Spfg#define	PC87317_APC_CADDR_BANK1	0x80		/* locate CR in bank 1 */
74259948Spfg
75259948Spfgstatic devclass_t rtc_devclass;
76259948Spfg
77259948Spfgstatic device_attach_t rtc_attach;
78259948Spfgstatic device_probe_t rtc_ebus_probe;
79259948Spfg#ifdef DEV_ISA
80259948Spfgstatic device_probe_t rtc_isa_probe;
81259948Spfg#endif
82259707Spfg
83259707Spfgstatic device_method_t rtc_ebus_methods[] = {
84259707Spfg	/* Device interface */
85259707Spfg	DEVMETHOD(device_probe,		rtc_ebus_probe),
86259707Spfg	DEVMETHOD(device_attach,	rtc_attach),
87259707Spfg
88259707Spfg	/* clock interface */
89259707Spfg	DEVMETHOD(clock_gettime,	mc146818_gettime),
90259269Spfg	DEVMETHOD(clock_settime,	mc146818_settime),
91259269Spfg
92259269Spfg	{ 0, 0 }
93259269Spfg};
94259269Spfg
95259269Spfgstatic driver_t rtc_ebus_driver = {
96259269Spfg	"rtc",
97259269Spfg	rtc_ebus_methods,
98259269Spfg	sizeof(struct mc146818_softc),
99259269Spfg};
100259269Spfg
101259269SpfgDRIVER_MODULE(rtc, ebus, rtc_ebus_driver, rtc_devclass, 0, 0);
102259269Spfg
103259269Spfg#ifdef DEV_ISA
104259269Spfgstatic device_method_t rtc_isa_methods[] = {
105259948Spfg	/* Device interface */
106259948Spfg	DEVMETHOD(device_probe,		rtc_isa_probe),
107259948Spfg	DEVMETHOD(device_attach,	rtc_attach),
108259948Spfg
109259948Spfg	/* clock interface */
110259948Spfg	DEVMETHOD(clock_gettime,	mc146818_gettime),
111259948Spfg	DEVMETHOD(clock_settime,	mc146818_settime),
112259948Spfg
113259707Spfg	{ 0, 0 }
114259707Spfg};
115259707Spfg
116259707Spfgstatic driver_t rtc_isa_driver = {
117259707Spfg	"rtc",
118259707Spfg	rtc_isa_methods,
119259269Spfg	sizeof(struct mc146818_softc),
120259269Spfg};
121259269Spfg
122259269SpfgDRIVER_MODULE(rtc, isa, rtc_isa_driver, rtc_devclass, 0, 0);
123259269Spfg#endif
124252080Spfg
125252080Spfgstatic u_int pc87317_getcent(device_t);
126252080Spfgstatic void pc87317_setcent(device_t, u_int);
127252080Spfg
128252080Spfgstatic int
129221282Smmrtc_ebus_probe(device_t dev)
130221282Smm{
131221282Smm
132221282Smm	if (strcmp(ofw_bus_get_name(dev), "rtc") == 0) {
133221282Smm		device_set_desc(dev, RTC_DESC);
134221282Smm		return (0);
135221282Smm	}
136252080Spfg
137252080Spfg	return (ENXIO);
138252080Spfg}
139252080Spfg
140252080Spfg#ifdef DEV_ISA
141252080Spfgstatic struct isa_pnp_id rtc_isa_ids[] = {
142260231Spfg	{ 0x000bd041, RTC_DESC }, /* PNP0B00 */
143260231Spfg	{ 0 }
144260231Spfg};
145260231Spfg
146260231Spfgstatic int
147259705Spfgrtc_isa_probe(device_t dev)
148259269Spfg{
149259269Spfg
150259269Spfg	if (ISA_PNP_PROBE(device_get_parent(dev), dev, rtc_isa_ids) == 0)
151259269Spfg		return (0);
152259269Spfg
153259269Spfg	return (ENXIO);
154259269Spfg}
155259269Spfg#endif
156259269Spfg
157259269Spfgstatic int
158259269Spfgrtc_attach(device_t dev)
159259269Spfg{
160259269Spfg	struct timespec ts;
161259269Spfg	struct mc146818_softc *sc;
162259269Spfg	struct resource *res;
163259269Spfg	int ebus, error, rid;
164259269Spfg
165259269Spfg	sc = device_get_softc(dev);
166259269Spfg
167259269Spfg	mtx_init(&sc->sc_mtx, "rtc_mtx", NULL, MTX_SPIN);
168259269Spfg
169259269Spfg	ebus = 0;
170259269Spfg	if (strcmp(device_get_name(device_get_parent(dev)), "ebus") == 0)
171259269Spfg		ebus = 1;
172259269Spfg
173259269Spfg	rid = 0;
174259269Spfg	res = bus_alloc_resource_any(dev, ebus ? SYS_RES_MEMORY :
175259269Spfg	    SYS_RES_IOPORT, &rid, RF_ACTIVE);
176259269Spfg	if (res == NULL) {
177259269Spfg		device_printf(dev, "cannot allocate resources\n");
178259269Spfg		error = ENXIO;
179259269Spfg		goto fail_mtx;
180259269Spfg	}
181259269Spfg	sc->sc_bst = rman_get_bustag(res);
182259269Spfg	sc->sc_bsh = rman_get_bushandle(res);
183259269Spfg
184259269Spfg	sc->sc_mcread = RTC_READ;
185259269Spfg	sc->sc_mcwrite = RTC_WRITE;
186259269Spfg	/* The TOD clock year 0 is 0. */
187259269Spfg	sc->sc_year0 = 0;
188259269Spfg	/*
189259269Spfg	 * For ISA use the default century get/set functions, for EBus we
190259269Spfg	 * provide our own versions.
191259269Spfg	 */
192259269Spfg	sc->sc_flag = MC146818_NO_CENT_ADJUST;
193259269Spfg	if (ebus) {
194259269Spfg		/*
195259269Spfg		 * Make sure the CR is at the default location (also used
196259269Spfg		 * by Solaris).
197259269Spfg		 */
198237678Spfg		RTC_WRITE(dev, MC_REGA, PC87317_APC);
199237678Spfg		RTC_WRITE(dev, PC87317_APC_CADDR, PC87317_APC_CADDR_BANK1 |
200237678Spfg		    PC87317_RTC_CR);
201237678Spfg		RTC_WRITE(dev, MC_REGA, PC87317_COMMON);
202237678Spfg		sc->sc_getcent = pc87317_getcent;
203237678Spfg		sc->sc_setcent = pc87317_setcent;
204237678Spfg	}
205237678Spfg	if ((error = mc146818_attach(dev)) != 0) {
206237678Spfg		device_printf(dev, "cannot attach time of day clock\n");
207237678Spfg		goto fail_res;
208237678Spfg	}
209237678Spfg
210221282Smm	if (bootverbose) {
211221282Smm		if (mc146818_gettime(dev, &ts) != 0)
212221282Smm			device_printf(dev, "invalid time");
213221282Smm		else
214252080Spfg			device_printf(dev, "current time: %ld.%09ld\n",
215252080Spfg			    (long)ts.tv_sec, ts.tv_nsec);
216252080Spfg	}
217252080Spfg
218252080Spfg	return (0);
219252080Spfg
220259705Spfg fail_res:
221259269Spfg	bus_release_resource(dev, ebus ? SYS_RES_MEMORY : SYS_RES_IOPORT, rid,
222259269Spfg	    res);
223259269Spfg fail_mtx:
224259269Spfg	mtx_destroy(&sc->sc_mtx);
225259269Spfg
226259269Spfg	return (error);
227259269Spfg}
228259269Spfg
229259269Spfgstatic u_int
230259269Spfgpc87317_getcent(device_t dev)
231259269Spfg{
232259269Spfg	u_int cent;
233252080Spfg
234252080Spfg	RTC_WRITE(dev, MC_REGA, PC87317_RTC);
235252080Spfg	cent = RTC_READ(dev, PC87317_RTC_CR);
236252080Spfg	RTC_WRITE(dev, MC_REGA, PC87317_COMMON);
237252080Spfg	return (cent);
238221282Smm}
239221282Smm
240221282Smmstatic void
241221282Smmpc87317_setcent(device_t dev, u_int cent)
242221282Smm{
243221282Smm
244221282Smm	RTC_WRITE(dev, MC_REGA, PC87317_RTC);
245221282Smm	RTC_WRITE(dev, PC87317_RTC_CR, cent);
246221282Smm	RTC_WRITE(dev, MC_REGA, PC87317_COMMON);
247221282Smm}
248221282Smm