1183840Sraj/*-
2183840Sraj * Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
3183840Sraj * All rights reserved.
4183840Sraj *
5183840Sraj * Developed by Semihalf.
6183840Sraj *
7183840Sraj * Redistribution and use in source and binary forms, with or without
8183840Sraj * modification, are permitted provided that the following conditions
9183840Sraj * are met:
10183840Sraj * 1. Redistributions of source code must retain the above copyright
11183840Sraj *    notice, this list of conditions and the following disclaimer.
12183840Sraj * 2. Redistributions in binary form must reproduce the above copyright
13183840Sraj *    notice, this list of conditions and the following disclaimer in the
14183840Sraj *    documentation and/or other materials provided with the distribution.
15183840Sraj * 3. Neither the name of MARVELL nor the names of contributors
16183840Sraj *    may be used to endorse or promote products derived from this software
17183840Sraj *    without specific prior written permission.
18183840Sraj *
19183840Sraj * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20183840Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21183840Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22183840Sraj * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
23183840Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24183840Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25183840Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26183840Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27183840Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28183840Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29183840Sraj * SUCH DAMAGE.
30183840Sraj */
31183840Sraj
32183840Sraj#include <sys/cdefs.h>
33183840Sraj__FBSDID("$FreeBSD$");
34183840Sraj#include <sys/param.h>
35183840Sraj#include <sys/bus.h>
36183840Sraj#include <sys/lock.h>
37183840Sraj#include <sys/time.h>
38183840Sraj#include <sys/clock.h>
39183840Sraj#include <sys/resource.h>
40183840Sraj#include <sys/systm.h>
41183840Sraj#include <sys/rman.h>
42183840Sraj#include <sys/kernel.h>
43183840Sraj#include <sys/module.h>
44183840Sraj
45183840Sraj#include <machine/bus.h>
46183840Sraj#include <machine/resource.h>
47183840Sraj
48209131Sraj#include <dev/ofw/ofw_bus.h>
49209131Sraj#include <dev/ofw/ofw_bus_subr.h>
50209131Sraj
51183840Sraj#include "clock_if.h"
52183840Sraj
53183840Sraj#define MV_RTC_TIME_REG		0x00
54183840Sraj#define MV_RTC_DATE_REG		0x04
55183840Sraj#define YEAR_BASE		2000
56183840Sraj
57183840Srajstruct mv_rtc_softc {
58183840Sraj	device_t dev;
59183840Sraj	struct resource *res[1];
60183840Sraj};
61183840Sraj
62183840Srajstatic struct resource_spec res_spec[] = {
63183840Sraj	{ SYS_RES_MEMORY, 0, RF_ACTIVE },
64183840Sraj	{ -1, 0 }
65183840Sraj};
66183840Sraj
67183840Srajstatic int mv_rtc_probe(device_t dev);
68183840Srajstatic int mv_rtc_attach(device_t dev);
69183840Sraj
70183840Srajstatic int mv_rtc_gettime(device_t dev, struct timespec *ts);
71183840Srajstatic int mv_rtc_settime(device_t dev, struct timespec *ts);
72183840Sraj
73183840Srajstatic uint32_t mv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off);
74183840Srajstatic int mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off,
75183840Sraj    uint32_t val);
76183840Sraj
77183840Srajstatic device_method_t mv_rtc_methods[] = {
78183840Sraj	DEVMETHOD(device_probe,		mv_rtc_probe),
79183840Sraj	DEVMETHOD(device_attach,	mv_rtc_attach),
80183840Sraj
81183840Sraj	DEVMETHOD(clock_gettime,	mv_rtc_gettime),
82183840Sraj	DEVMETHOD(clock_settime,	mv_rtc_settime),
83183840Sraj
84183840Sraj	{ 0, 0 },
85183840Sraj};
86183840Sraj
87183840Srajstatic driver_t mv_rtc_driver = {
88183840Sraj	"rtc",
89183840Sraj	mv_rtc_methods,
90183840Sraj	sizeof(struct mv_rtc_softc),
91183840Sraj};
92183840Srajstatic devclass_t mv_rtc_devclass;
93183840Sraj
94209131SrajDRIVER_MODULE(mv_rtc, simplebus, mv_rtc_driver, mv_rtc_devclass, 0, 0);
95183840Sraj
96183840Srajstatic int
97183840Srajmv_rtc_probe(device_t dev)
98183840Sraj{
99183840Sraj
100209131Sraj	if (!ofw_bus_is_compatible(dev, "mrvl,rtc"))
101209131Sraj		return (ENXIO);
102209131Sraj
103183840Sraj	device_set_desc(dev, "Marvell Integrated RTC");
104209131Sraj	return (BUS_PROBE_DEFAULT);
105183840Sraj}
106183840Sraj
107183840Srajstatic int
108183840Srajmv_rtc_attach(device_t dev)
109183840Sraj{
110183840Sraj	struct mv_rtc_softc *sc;
111183840Sraj
112183840Sraj	sc = device_get_softc(dev);
113183840Sraj	sc->dev = dev;
114183840Sraj
115183840Sraj	clock_register(dev, 1000000);
116183840Sraj
117183840Sraj	if (bus_alloc_resources(dev, res_spec, sc->res)) {
118183840Sraj		device_printf(dev, "could not allocate resources\n");
119183840Sraj		return (ENXIO);
120183840Sraj	}
121183840Sraj
122183840Sraj	return (0);
123183840Sraj}
124183840Sraj
125183840Srajstatic int
126183840Srajmv_rtc_gettime(device_t dev, struct timespec *ts)
127183840Sraj{
128183840Sraj	struct clocktime ct;
129183840Sraj	struct mv_rtc_softc *sc;
130183840Sraj	uint32_t val;
131183840Sraj
132183840Sraj	sc = device_get_softc(dev);
133183840Sraj
134183840Sraj	val = mv_rtc_reg_read(sc, MV_RTC_TIME_REG);
135183840Sraj
136183840Sraj	ct.nsec = 0;
137183840Sraj	ct.sec = FROMBCD(val & 0x7f);
138183840Sraj	ct.min = FROMBCD((val & 0x7f00) >> 8);
139183840Sraj	ct.hour = FROMBCD((val & 0x3f0000) >> 16);
140183840Sraj	ct.dow = FROMBCD((val & 0x7000000) >> 24) - 1;
141183840Sraj
142183840Sraj	val = mv_rtc_reg_read(sc, MV_RTC_DATE_REG);
143183840Sraj
144183840Sraj	ct.day = FROMBCD(val & 0x7f);
145183840Sraj	ct.mon = FROMBCD((val & 0x1f00) >> 8);
146183840Sraj	ct.year = YEAR_BASE + FROMBCD((val & 0xff0000) >> 16);
147183840Sraj
148183840Sraj	return (clock_ct_to_ts(&ct, ts));
149183840Sraj}
150183840Sraj
151183840Srajstatic int
152183840Srajmv_rtc_settime(device_t dev, struct timespec *ts)
153183840Sraj{
154183840Sraj	struct clocktime ct;
155183840Sraj	struct mv_rtc_softc *sc;
156183840Sraj	uint32_t val;
157183840Sraj
158183840Sraj	sc = device_get_softc(dev);
159183840Sraj
160183840Sraj	/* Resolution: 1 sec */
161183840Sraj	if (ts->tv_nsec >= 500000000)
162183840Sraj		ts->tv_sec++;
163183840Sraj	ts->tv_nsec = 0;
164183840Sraj	clock_ts_to_ct(ts, &ct);
165183840Sraj
166183840Sraj	val = TOBCD(ct.sec) | (TOBCD(ct.min) << 8) |
167183840Sraj	    (TOBCD(ct.hour) << 16) | (TOBCD( ct.dow + 1) << 24);
168183840Sraj	mv_rtc_reg_write(sc, MV_RTC_TIME_REG, val);
169183840Sraj
170183840Sraj	val = TOBCD(ct.day) | (TOBCD(ct.mon) << 8) |
171183840Sraj	    (TOBCD(ct.year - YEAR_BASE) << 16);
172183840Sraj	mv_rtc_reg_write(sc, MV_RTC_DATE_REG, val);
173183840Sraj
174183840Sraj	return (0);
175183840Sraj}
176183840Sraj
177183840Srajstatic uint32_t
178183840Srajmv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off)
179183840Sraj{
180183840Sraj
181183840Sraj	return (bus_read_4(sc->res[0], off));
182183840Sraj}
183183840Sraj
184183840Srajstatic int
185183840Srajmv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off, uint32_t val)
186183840Sraj{
187183840Sraj
188183840Sraj	bus_write_4(sc->res[0], off, val);
189183840Sraj	return (0);
190183840Sraj}
191