1210397Sandrew/*
2210397Sandrew * Copyright (C) 2010 Andrew Turner
3210397Sandrew * All rights reserved.
4210397Sandrew *
5210397Sandrew * Redistribution and use in source and binary forms, with or without
6210397Sandrew * modification, are permitted provided that the following conditions
7210397Sandrew * are met:
8210397Sandrew * 1. Redistributions of source code must retain the above copyright
9210397Sandrew *    notice, this list of conditions and the following disclaimer.
10210397Sandrew * 2. Redistributions in binary form must reproduce the above copyright
11210397Sandrew *    notice, this list of conditions and the following disclaimer in the
12210397Sandrew *    documentation and/or other materials provided with the distribution.
13210397Sandrew *
14210397Sandrew * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15210397Sandrew * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16210397Sandrew * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17210397Sandrew * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18210397Sandrew * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19210397Sandrew * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20210397Sandrew * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21210397Sandrew * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22210397Sandrew * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23210397Sandrew * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24210397Sandrew * SUCH DAMAGE.
25210397Sandrew *
26210397Sandrew */
27210397Sandrew
28210397Sandrew#include <sys/cdefs.h>
29210397Sandrew__FBSDID("$FreeBSD$");
30210397Sandrew#include <sys/param.h>
31210397Sandrew#include <sys/bus.h>
32210397Sandrew#include <sys/time.h>
33210397Sandrew#include <sys/clock.h>
34210397Sandrew#include <sys/resource.h>
35210397Sandrew#include <sys/systm.h>
36210397Sandrew#include <sys/rman.h>
37210397Sandrew#include <sys/kernel.h>
38210397Sandrew#include <sys/module.h>
39210397Sandrew
40210397Sandrew#include <machine/bus.h>
41210397Sandrew
42210397Sandrew#include <arm/s3c2xx0/s3c24x0reg.h>
43210397Sandrew
44210397Sandrew#include "clock_if.h"
45210397Sandrew
46210397Sandrew#define YEAR_BASE		2000
47210397Sandrew
48210397Sandrewstruct s3c2xx0_rtc_softc {
49210397Sandrew	struct resource *mem_res;
50210397Sandrew};
51210397Sandrew
52210397Sandrewstatic int
53210397Sandrews3c2xx0_rtc_probe(device_t dev)
54210397Sandrew{
55210397Sandrew
56210397Sandrew	device_set_desc(dev, "Samsung Integrated RTC");
57210397Sandrew	return (0);
58210397Sandrew}
59210397Sandrew
60210397Sandrewstatic int
61210397Sandrews3c2xx0_rtc_attach(device_t dev)
62210397Sandrew{
63210397Sandrew	struct s3c2xx0_rtc_softc *sc;
64210397Sandrew	int error, rid;
65210397Sandrew
66210397Sandrew	sc = device_get_softc(dev);
67210397Sandrew 	error = 0;
68210397Sandrew
69210397Sandrew	rid = 0;
70210397Sandrew	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
71210397Sandrew	    RF_ACTIVE);
72210397Sandrew	if (sc->mem_res == NULL) {
73210397Sandrew		error = ENOMEM;
74210397Sandrew		goto out;
75210397Sandrew	}
76210397Sandrew
77210397Sandrew	bus_write_1(sc->mem_res, RTC_RTCCON, RTCCON_RTCEN);
78210397Sandrew	clock_register(dev, 1000000);
79210397Sandrew
80210397Sandrewout:
81210397Sandrew	return (error);
82210397Sandrew}
83210397Sandrew
84210397Sandrewstatic int
85210397Sandrews3c2xx0_rtc_gettime(device_t dev, struct timespec *ts)
86210397Sandrew{
87210397Sandrew	struct s3c2xx0_rtc_softc *sc;
88210397Sandrew	struct clocktime ct;
89210397Sandrew
90210397Sandrew#define READ_TIME() do {						\
91210397Sandrew	ct.year = YEAR_BASE + FROMBCD(bus_read_1(sc->mem_res, RTC_BCDYEAR)); \
92210397Sandrew	ct.mon = FROMBCD(bus_read_1(sc->mem_res, RTC_BCDMON));		\
93210397Sandrew	ct.dow = FROMBCD(bus_read_1(sc->mem_res, RTC_BCDDAY));		\
94210397Sandrew	ct.day = FROMBCD(bus_read_1(sc->mem_res, RTC_BCDDATE));		\
95210397Sandrew	ct.hour = FROMBCD(bus_read_1(sc->mem_res, RTC_BCDHOUR));	\
96210397Sandrew	ct.min = FROMBCD(bus_read_1(sc->mem_res, RTC_BCDMIN));		\
97210397Sandrew	ct.sec = FROMBCD(bus_read_1(sc->mem_res, RTC_BCDSEC));		\
98210397Sandrew} while (0)
99210397Sandrew
100210397Sandrew	sc = device_get_softc(dev);
101210397Sandrew
102210397Sandrew	ct.nsec = 0;
103210397Sandrew	READ_TIME();
104210397Sandrew	/*
105210397Sandrew	 * Check if we could have read incorrect values
106210397Sandrew	 * as the values could have changed.
107210397Sandrew	 */
108210397Sandrew	if (ct.sec == 0) {
109210397Sandrew		READ_TIME();
110210397Sandrew	}
111210397Sandrew
112210397Sandrew	ct.dow = -1;
113210397Sandrew
114210397Sandrew#undef READ_TIME
115210397Sandrew	return (clock_ct_to_ts(&ct, ts));
116210397Sandrew}
117210397Sandrew
118210397Sandrewstatic int
119210397Sandrews3c2xx0_rtc_settime(device_t dev, struct timespec *ts)
120210397Sandrew{
121210397Sandrew	struct s3c2xx0_rtc_softc *sc;
122210397Sandrew	struct clocktime ct;
123210397Sandrew
124210397Sandrew	sc = device_get_softc(dev);
125210397Sandrew
126210397Sandrew	/* Resolution: 1 sec */
127210397Sandrew	if (ts->tv_nsec >= 500000000)
128210397Sandrew		ts->tv_sec++;
129210397Sandrew	ts->tv_nsec = 0;
130210397Sandrew	clock_ts_to_ct(ts, &ct);
131210397Sandrew
132210397Sandrew	bus_write_1(sc->mem_res, RTC_BCDSEC, TOBCD(ct.sec));
133210397Sandrew	bus_write_1(sc->mem_res, RTC_BCDMIN, TOBCD(ct.min));
134210397Sandrew	bus_write_1(sc->mem_res, RTC_BCDHOUR, TOBCD(ct.hour));
135210397Sandrew	bus_write_1(sc->mem_res, RTC_BCDDATE, TOBCD(ct.day));
136210397Sandrew	bus_write_1(sc->mem_res, RTC_BCDDAY, TOBCD(ct.dow));
137210397Sandrew	bus_write_1(sc->mem_res, RTC_BCDMON, TOBCD(ct.mon));
138210397Sandrew	bus_write_1(sc->mem_res, RTC_BCDYEAR, TOBCD(ct.year - YEAR_BASE));
139210397Sandrew
140210397Sandrew	return (0);
141210397Sandrew}
142210397Sandrew
143210397Sandrewstatic device_method_t s3c2xx0_rtc_methods[] = {
144210397Sandrew	DEVMETHOD(device_probe,		s3c2xx0_rtc_probe),
145210397Sandrew	DEVMETHOD(device_attach,	s3c2xx0_rtc_attach),
146210397Sandrew
147210397Sandrew	DEVMETHOD(clock_gettime,	s3c2xx0_rtc_gettime),
148210397Sandrew	DEVMETHOD(clock_settime,	s3c2xx0_rtc_settime),
149210397Sandrew
150210397Sandrew	{ 0, 0 },
151210397Sandrew};
152210397Sandrew
153210397Sandrewstatic driver_t s3c2xx0_rtc_driver = {
154210397Sandrew	"rtc",
155210397Sandrew	s3c2xx0_rtc_methods,
156210397Sandrew	sizeof(struct s3c2xx0_rtc_softc),
157210397Sandrew};
158210397Sandrewstatic devclass_t s3c2xx0_rtc_devclass;
159210397Sandrew
160210397SandrewDRIVER_MODULE(s3c2xx0_rtc, s3c24x0, s3c2xx0_rtc_driver, s3c2xx0_rtc_devclass,
161210397Sandrew    0, 0);
162210397Sandrew
163