xref: /freebsd/sys/dev/iicbus/rtc/rx8803.c (revision 4d846d260e2b9a3d4d0a701462568268cbfe7a5b)
1d97d8385SMarcin Wojtas /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3d97d8385SMarcin Wojtas  *
4d97d8385SMarcin Wojtas  * Copyright (c) 2020 Alstom Group.
5d97d8385SMarcin Wojtas  * Copyright (c) 2020 Semihalf.
6d97d8385SMarcin Wojtas  *
7d97d8385SMarcin Wojtas  * Redistribution and use in source and binary forms, with or without
8d97d8385SMarcin Wojtas  * modification, are permitted provided that the following conditions
9d97d8385SMarcin Wojtas  * are met:
10d97d8385SMarcin Wojtas  * 1. Redistributions of source code must retain the above copyright
11d97d8385SMarcin Wojtas  *    notice, this list of conditions and the following disclaimer.
12d97d8385SMarcin Wojtas  * 2. Redistributions in binary form must reproduce the above copyright
13d97d8385SMarcin Wojtas  *    notice, this list of conditions and the following disclaimer in the
14d97d8385SMarcin Wojtas  *    documentation and/or other materials provided with the distribution.
15d97d8385SMarcin Wojtas  *
16d97d8385SMarcin Wojtas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17d97d8385SMarcin Wojtas  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18d97d8385SMarcin Wojtas  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19d97d8385SMarcin Wojtas  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20d97d8385SMarcin Wojtas  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21d97d8385SMarcin Wojtas  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22d97d8385SMarcin Wojtas  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23d97d8385SMarcin Wojtas  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24d97d8385SMarcin Wojtas  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25d97d8385SMarcin Wojtas  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26d97d8385SMarcin Wojtas  */
27d97d8385SMarcin Wojtas 
28d97d8385SMarcin Wojtas #include <sys/cdefs.h>
29d97d8385SMarcin Wojtas __FBSDID("$FreeBSD$");
30d97d8385SMarcin Wojtas 
316f9c6226SKornel Dulęba #include "opt_platform.h"
326f9c6226SKornel Dulęba 
33d97d8385SMarcin Wojtas #include <sys/param.h>
34d97d8385SMarcin Wojtas #include <sys/systm.h>
35d97d8385SMarcin Wojtas #include <sys/bus.h>
36d97d8385SMarcin Wojtas #include <sys/clock.h>
37d97d8385SMarcin Wojtas #include <sys/kernel.h>
38d97d8385SMarcin Wojtas #include <sys/lock.h>
39d97d8385SMarcin Wojtas #include <sys/module.h>
40d97d8385SMarcin Wojtas 
41d97d8385SMarcin Wojtas #include <dev/ofw/ofw_bus.h>
42d97d8385SMarcin Wojtas #include <dev/ofw/ofw_bus_subr.h>
43d97d8385SMarcin Wojtas 
44d97d8385SMarcin Wojtas #include <dev/iicbus/iiconf.h>
45d97d8385SMarcin Wojtas #include <dev/iicbus/iicbus.h>
46d97d8385SMarcin Wojtas 
47d97d8385SMarcin Wojtas #include "clock_if.h"
48d97d8385SMarcin Wojtas #include "iicbus_if.h"
49d97d8385SMarcin Wojtas 
50d97d8385SMarcin Wojtas #define	BIT(x)			(1 << (x))
51d97d8385SMarcin Wojtas 
52d97d8385SMarcin Wojtas #define RX8803_TIME		0x0
53d97d8385SMarcin Wojtas #define RX8803_FLAGS		0xE
54d97d8385SMarcin Wojtas #define RX8803_CTRL		0xF
55d97d8385SMarcin Wojtas 
56d97d8385SMarcin Wojtas #define RX8803_FLAGS_V1F	BIT(0)
57d97d8385SMarcin Wojtas #define RX8803_FLAGS_V2F	BIT(1)
58d97d8385SMarcin Wojtas 
59d97d8385SMarcin Wojtas #define RX8803_CTRL_DISABLE	BIT(0)
60d97d8385SMarcin Wojtas 
61d97d8385SMarcin Wojtas #define HALF_OF_SEC_NS		500000000
62d97d8385SMarcin Wojtas #define MAX_WRITE_LEN		16
63d97d8385SMarcin Wojtas 
64d97d8385SMarcin Wojtas struct rx8803_time {
65d97d8385SMarcin Wojtas 	uint8_t sec;
66d97d8385SMarcin Wojtas 	uint8_t min;
67d97d8385SMarcin Wojtas 	uint8_t hour;
68d97d8385SMarcin Wojtas 	uint8_t dow;
69d97d8385SMarcin Wojtas 	uint8_t day;
70d97d8385SMarcin Wojtas 	uint8_t mon;
71d97d8385SMarcin Wojtas 	uint8_t year;
72d97d8385SMarcin Wojtas };
73d97d8385SMarcin Wojtas 
746f9c6226SKornel Dulęba static struct ofw_compat_data compat_data[] = {
756f9c6226SKornel Dulęba 	{"epson,rx8803", 1},
766f9c6226SKornel Dulęba 	{NULL,           0},
776f9c6226SKornel Dulęba };
786f9c6226SKornel Dulęba 
79d97d8385SMarcin Wojtas static int rx8803_probe(device_t dev);
80d97d8385SMarcin Wojtas static int rx8803_attach(device_t dev);
81d97d8385SMarcin Wojtas static int rx8803_detach(device_t dev);
82d97d8385SMarcin Wojtas 
83d97d8385SMarcin Wojtas static int rx8803_gettime(device_t dev, struct timespec *ts);
84d97d8385SMarcin Wojtas static int rx8803_settime(device_t dev, struct timespec *ts);
85d97d8385SMarcin Wojtas 
86d97d8385SMarcin Wojtas static int rx8803_check_status(device_t dev);
87d97d8385SMarcin Wojtas 
88d97d8385SMarcin Wojtas static int
89d97d8385SMarcin Wojtas rx8803_check_status(device_t dev)
90d97d8385SMarcin Wojtas {
91d97d8385SMarcin Wojtas 	uint8_t flags;
92d97d8385SMarcin Wojtas 	int rc;
93d97d8385SMarcin Wojtas 
94d97d8385SMarcin Wojtas 	rc = iicdev_readfrom(dev, RX8803_FLAGS, &flags, 1, IIC_WAIT);
95d97d8385SMarcin Wojtas 	if (rc != 0)
96d97d8385SMarcin Wojtas 		return (rc);
97d97d8385SMarcin Wojtas 
98d97d8385SMarcin Wojtas 	if (flags & RX8803_FLAGS_V2F) {
99d97d8385SMarcin Wojtas 		device_printf(dev, "Low voltage flag set, date is incorrect\n");
100d97d8385SMarcin Wojtas 		return (ENXIO);
101d97d8385SMarcin Wojtas 	}
102d97d8385SMarcin Wojtas 
103d97d8385SMarcin Wojtas 	return (0);
104d97d8385SMarcin Wojtas }
105d97d8385SMarcin Wojtas 
106d97d8385SMarcin Wojtas static int
107d97d8385SMarcin Wojtas rx8803_gettime(device_t dev, struct timespec *ts)
108d97d8385SMarcin Wojtas {
109d97d8385SMarcin Wojtas 	struct rx8803_time data;
110d97d8385SMarcin Wojtas 	struct bcd_clocktime bcd;
111d97d8385SMarcin Wojtas 	int rc;
112d97d8385SMarcin Wojtas 
113d97d8385SMarcin Wojtas 	rc = rx8803_check_status(dev);
114d97d8385SMarcin Wojtas 	if (rc != 0)
115d97d8385SMarcin Wojtas 		return (rc);
116d97d8385SMarcin Wojtas 
117d97d8385SMarcin Wojtas 	rc = iicdev_readfrom(dev,
118d97d8385SMarcin Wojtas 	    RX8803_TIME,
119d97d8385SMarcin Wojtas 	    &data, sizeof(struct rx8803_time),
120d97d8385SMarcin Wojtas 	    IIC_WAIT);
121d97d8385SMarcin Wojtas 	if (rc != 0)
122d97d8385SMarcin Wojtas 		return (rc);
123d97d8385SMarcin Wojtas 
124d97d8385SMarcin Wojtas 	bcd.nsec = 0;
125d97d8385SMarcin Wojtas 	bcd.sec = data.sec & 0x7F;
126d97d8385SMarcin Wojtas 	bcd.min = data.min & 0x7F;
127d97d8385SMarcin Wojtas 	bcd.hour = data.hour & 0x3F;
128d97d8385SMarcin Wojtas 	bcd.dow = flsl(data.dow & 0x7F) - 1;
129d97d8385SMarcin Wojtas 	bcd.day = data.day & 0x3F;
130d97d8385SMarcin Wojtas 	bcd.mon = (data.mon & 0x1F);
131d97d8385SMarcin Wojtas 	bcd.year = data.year;
132d97d8385SMarcin Wojtas 
133d97d8385SMarcin Wojtas 	clock_dbgprint_bcd(dev, CLOCK_DBG_READ, &bcd);
134d97d8385SMarcin Wojtas 
135d97d8385SMarcin Wojtas 	rc = clock_bcd_to_ts(&bcd, ts, false);
136d97d8385SMarcin Wojtas 	return (rc);
137d97d8385SMarcin Wojtas }
138d97d8385SMarcin Wojtas 
139d97d8385SMarcin Wojtas static int
140d97d8385SMarcin Wojtas rx8803_settime(device_t dev, struct timespec *ts)
141d97d8385SMarcin Wojtas {
142d97d8385SMarcin Wojtas 	struct rx8803_time data;
143d97d8385SMarcin Wojtas 	struct bcd_clocktime bcd;
144d97d8385SMarcin Wojtas 	uint8_t reg;
145d97d8385SMarcin Wojtas 	int rc;
146d97d8385SMarcin Wojtas 
147d97d8385SMarcin Wojtas 	ts->tv_sec -= utc_offset();
148d97d8385SMarcin Wojtas 	clock_ts_to_bcd(ts, &bcd, false);
149d97d8385SMarcin Wojtas 	clock_dbgprint_bcd(dev, CLOCK_DBG_WRITE, &bcd);
150d97d8385SMarcin Wojtas 
151d97d8385SMarcin Wojtas 	data.sec = bcd.sec;
152d97d8385SMarcin Wojtas 	data.min = bcd.min;
153d97d8385SMarcin Wojtas 	data.hour = bcd.hour;
154d97d8385SMarcin Wojtas 	data.dow = 1 << bcd.dow;
155d97d8385SMarcin Wojtas 	data.day = bcd.day;
156d97d8385SMarcin Wojtas 	data.mon = bcd.mon;
157d97d8385SMarcin Wojtas 	data.year = bcd.year;
158d97d8385SMarcin Wojtas 
159d97d8385SMarcin Wojtas 	if (ts->tv_nsec > HALF_OF_SEC_NS)
160d97d8385SMarcin Wojtas 		data.sec++;
161d97d8385SMarcin Wojtas 
162d97d8385SMarcin Wojtas 	/* First disable clock. */
163d97d8385SMarcin Wojtas 	rc = iicdev_readfrom(dev, RX8803_CTRL, &reg, sizeof(uint8_t), IIC_WAIT);
164d97d8385SMarcin Wojtas 	if (rc != 0)
165d97d8385SMarcin Wojtas 		return (rc);
166d97d8385SMarcin Wojtas 
167d97d8385SMarcin Wojtas 	reg |= RX8803_CTRL_DISABLE;
168d97d8385SMarcin Wojtas 
169d97d8385SMarcin Wojtas 	rc = iicdev_writeto(dev, RX8803_CTRL, &reg, sizeof(uint8_t), IIC_WAIT);
170d97d8385SMarcin Wojtas 	if (rc != 0)
171d97d8385SMarcin Wojtas 		return (rc);
172d97d8385SMarcin Wojtas 
173d97d8385SMarcin Wojtas 	/* Update the date. */
174d97d8385SMarcin Wojtas 	rc = iicdev_writeto(dev,
175d97d8385SMarcin Wojtas 	    RX8803_TIME,
176d97d8385SMarcin Wojtas 	    &data, sizeof(struct rx8803_time),
177d97d8385SMarcin Wojtas 	    IIC_WAIT);
178d97d8385SMarcin Wojtas 	if (rc != 0)
179d97d8385SMarcin Wojtas 		return (rc);
180d97d8385SMarcin Wojtas 
181d97d8385SMarcin Wojtas 	/* Now restart it. */
182d97d8385SMarcin Wojtas 	reg &= ~RX8803_CTRL_DISABLE;
183d97d8385SMarcin Wojtas 	rc = iicdev_writeto(dev, RX8803_CTRL, &reg, sizeof(uint8_t), IIC_WAIT);
184d97d8385SMarcin Wojtas 	if (rc != 0)
185d97d8385SMarcin Wojtas 		return (rc);
186d97d8385SMarcin Wojtas 
187d97d8385SMarcin Wojtas 	/* Clear low voltage flags, as we have just updated the clock. */
188d97d8385SMarcin Wojtas 	rc = iicdev_readfrom(dev, RX8803_FLAGS, &reg, sizeof(uint8_t), IIC_WAIT);
189d97d8385SMarcin Wojtas 	if (rc != 0)
190d97d8385SMarcin Wojtas 		return (rc);
191d97d8385SMarcin Wojtas 
192d97d8385SMarcin Wojtas 	reg &= ~(RX8803_FLAGS_V1F | RX8803_FLAGS_V2F);
193d97d8385SMarcin Wojtas 	rc = iicdev_writeto(dev, RX8803_FLAGS, &reg, sizeof(uint8_t), IIC_WAIT);
194d97d8385SMarcin Wojtas 	return (rc);
195d97d8385SMarcin Wojtas }
196d97d8385SMarcin Wojtas 
197d97d8385SMarcin Wojtas static int
198d97d8385SMarcin Wojtas rx8803_probe(device_t dev)
199d97d8385SMarcin Wojtas {
200d97d8385SMarcin Wojtas 
2016f9c6226SKornel Dulęba 	if (!ofw_bus_status_okay(dev))
2026f9c6226SKornel Dulęba 		return (ENXIO);
2036f9c6226SKornel Dulęba 
2046f9c6226SKornel Dulęba 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
205d97d8385SMarcin Wojtas 		return (ENXIO);
206d97d8385SMarcin Wojtas 
207d97d8385SMarcin Wojtas 	device_set_desc(dev, "Epson RX8803 Real Time Clock");
208d97d8385SMarcin Wojtas 
209d97d8385SMarcin Wojtas 	return (BUS_PROBE_GENERIC);
210d97d8385SMarcin Wojtas }
211d97d8385SMarcin Wojtas 
212d97d8385SMarcin Wojtas static int
213d97d8385SMarcin Wojtas rx8803_attach(device_t dev)
214d97d8385SMarcin Wojtas {
215d97d8385SMarcin Wojtas 
216d97d8385SMarcin Wojtas 	/* Set 1 sec resolution. */
217d97d8385SMarcin Wojtas 	clock_register_flags(dev, 1000000, 0);
218d97d8385SMarcin Wojtas 	clock_schedule(dev, 1);
219d97d8385SMarcin Wojtas 
220d97d8385SMarcin Wojtas 	return (0);
221d97d8385SMarcin Wojtas 
222d97d8385SMarcin Wojtas }
223d97d8385SMarcin Wojtas 
224d97d8385SMarcin Wojtas static int
225d97d8385SMarcin Wojtas rx8803_detach(device_t dev)
226d97d8385SMarcin Wojtas {
227d97d8385SMarcin Wojtas 
228d97d8385SMarcin Wojtas 	clock_unregister(dev);
229d97d8385SMarcin Wojtas 
230d97d8385SMarcin Wojtas 	return (0);
231d97d8385SMarcin Wojtas }
232d97d8385SMarcin Wojtas 
233d97d8385SMarcin Wojtas static device_method_t rx8803_methods[] = {
234d97d8385SMarcin Wojtas 	DEVMETHOD(device_probe, rx8803_probe),
235d97d8385SMarcin Wojtas 	DEVMETHOD(device_attach, rx8803_attach),
236d97d8385SMarcin Wojtas 	DEVMETHOD(device_detach, rx8803_detach),
237d97d8385SMarcin Wojtas 
238d97d8385SMarcin Wojtas 	DEVMETHOD(clock_gettime, rx8803_gettime),
239d97d8385SMarcin Wojtas 	DEVMETHOD(clock_settime, rx8803_settime),
240d97d8385SMarcin Wojtas 
241d97d8385SMarcin Wojtas 	DEVMETHOD_END,
242d97d8385SMarcin Wojtas };
243d97d8385SMarcin Wojtas 
244d97d8385SMarcin Wojtas static driver_t rx8803_driver = {
245d97d8385SMarcin Wojtas 	"rx8803",
246d97d8385SMarcin Wojtas 	rx8803_methods,
247d97d8385SMarcin Wojtas 	0,			/* We don't need softc for this one. */
248d97d8385SMarcin Wojtas };
249d97d8385SMarcin Wojtas 
2503a866152SJohn Baldwin DRIVER_MODULE(rx8803, iicbus, rx8803_driver, NULL, NULL);
2516f9c6226SKornel Dulęba MODULE_VERSION(rx8803, 1);
252d97d8385SMarcin Wojtas MODULE_DEPEND(rx8803, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
2536f9c6226SKornel Dulęba IICBUS_FDT_PNP_INFO(compat_data);
254