xref: /freebsd/sys/dev/iicbus/rtc/hym8563.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
181359d59SMichal Meloun /*-
281359d59SMichal Meloun  * Copyright (c) 2017 Hiroki Mori.  All rights reserved.
381359d59SMichal Meloun  * Copyright (c) 2017 Ian Lepore.  All rights reserved.
481359d59SMichal Meloun  *
581359d59SMichal Meloun  * Redistribution and use in source and binary forms, with or without
681359d59SMichal Meloun  * modification, are permitted provided that the following conditions
781359d59SMichal Meloun  * are met:
881359d59SMichal Meloun  * 1. Redistributions of source code must retain the above copyright
981359d59SMichal Meloun  *    notice, this list of conditions and the following disclaimer.
1081359d59SMichal Meloun  * 2. Redistributions in binary form must reproduce the above copyright
1181359d59SMichal Meloun  *    notice, this list of conditions and the following disclaimer in the
1281359d59SMichal Meloun  *    documentation and/or other materials provided with the distribution.
1381359d59SMichal Meloun  *
1481359d59SMichal Meloun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1581359d59SMichal Meloun  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1681359d59SMichal Meloun  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1781359d59SMichal Meloun  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1881359d59SMichal Meloun  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1981359d59SMichal Meloun  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2081359d59SMichal Meloun  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2181359d59SMichal Meloun  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2281359d59SMichal Meloun  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2381359d59SMichal Meloun  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2481359d59SMichal Meloun  *
2581359d59SMichal Meloun  * This code base on isl12xx.c
2681359d59SMichal Meloun  */
2781359d59SMichal Meloun 
2881359d59SMichal Meloun #include <sys/cdefs.h>
2981359d59SMichal Meloun /*
3081359d59SMichal Meloun  * Driver for realtime clock HAOYU HYM8563
3181359d59SMichal Meloun  */
3281359d59SMichal Meloun 
3381359d59SMichal Meloun #include "opt_platform.h"
3481359d59SMichal Meloun 
3581359d59SMichal Meloun #include <sys/param.h>
3681359d59SMichal Meloun #include <sys/systm.h>
3781359d59SMichal Meloun #include <sys/bus.h>
3881359d59SMichal Meloun #include <sys/clock.h>
3981359d59SMichal Meloun #include <sys/kernel.h>
4081359d59SMichal Meloun #include <sys/lock.h>
4181359d59SMichal Meloun #include <sys/module.h>
4281359d59SMichal Meloun 
4381359d59SMichal Meloun #ifdef FDT
4481359d59SMichal Meloun #include <dev/ofw/ofw_bus.h>
4581359d59SMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
4681359d59SMichal Meloun #endif
4781359d59SMichal Meloun 
4881359d59SMichal Meloun #include <dev/iicbus/iiconf.h>
4981359d59SMichal Meloun #include <dev/iicbus/iicbus.h>
5081359d59SMichal Meloun 
5181359d59SMichal Meloun #include "clock_if.h"
5281359d59SMichal Meloun #include "iicbus_if.h"
5381359d59SMichal Meloun 
5481359d59SMichal Meloun /* Registers */
5581359d59SMichal Meloun #define	HYM8563_CTRL1		0x00
5681359d59SMichal Meloun #define	 HYM8563_CTRL1_TEST		(1 << 7)
5781359d59SMichal Meloun #define	 HYM8563_CTRL1_STOP		(1 << 5)
5881359d59SMichal Meloun #define	 HYM8563_CTRL1_TESTC		(1 << 3)
5981359d59SMichal Meloun 
6081359d59SMichal Meloun #define	HYM8563_CTRL2		0x01
6181359d59SMichal Meloun #define	 HYM8563_CTRL2_TI_TP		(1 << 4)
6281359d59SMichal Meloun #define	 HYM8563_CTRL2_AF		(1 << 3)
6381359d59SMichal Meloun #define	 HYM8563_CTRL2_TF		(1 << 2)
6481359d59SMichal Meloun #define	 HYM8563_CTRL2_AIE		(1 << 1)
6581359d59SMichal Meloun #define	 HYM8563_CTRL2_TIE		(1 << 0)
6681359d59SMichal Meloun 
6781359d59SMichal Meloun #define	HYM8563_SEC		0x02	/* plus battery low bit */
6881359d59SMichal Meloun #define	 HYM8563_SEC_VL			(1 << 7)
6981359d59SMichal Meloun 
7081359d59SMichal Meloun #define	HYM8563_MIN		0x03
7181359d59SMichal Meloun #define	HYM8563_HOUR		0x04
7281359d59SMichal Meloun #define	HYM8563_DAY		0x05
7381359d59SMichal Meloun #define	HYM8563_WEEKDAY		0x06
7481359d59SMichal Meloun #define	HYM8563_MONTH		0x07	/* plus 1 bit for century */
7581359d59SMichal Meloun #define	 HYM8563_MONTH_CENTURY		(1 << 7)
7681359d59SMichal Meloun #define HYM8563_YEAR		0x08
7781359d59SMichal Meloun 
7881359d59SMichal Meloun struct hym8563_softc {
7981359d59SMichal Meloun 	device_t			dev;
8081359d59SMichal Meloun 	struct intr_config_hook		init_hook;
8181359d59SMichal Meloun };
8281359d59SMichal Meloun 
8381359d59SMichal Meloun #ifdef FDT
8481359d59SMichal Meloun static struct ofw_compat_data compat_data[] = {
8581359d59SMichal Meloun 	{"haoyu,hym8563", 1},
8681359d59SMichal Meloun 	{NULL,           0},
8781359d59SMichal Meloun };
8881359d59SMichal Meloun #endif
8981359d59SMichal Meloun 
9081359d59SMichal Meloun 
9181359d59SMichal Meloun static inline int
hym8563_read_buf(struct hym8563_softc * sc,uint8_t reg,uint8_t * buf,uint16_t buflen)9281359d59SMichal Meloun hym8563_read_buf(struct hym8563_softc *sc, uint8_t reg, uint8_t *buf,
9381359d59SMichal Meloun     uint16_t buflen)
9481359d59SMichal Meloun {
9581359d59SMichal Meloun 
9681359d59SMichal Meloun 	return (iicdev_readfrom(sc->dev, reg, buf, buflen, IIC_WAIT));
9781359d59SMichal Meloun }
9881359d59SMichal Meloun 
9981359d59SMichal Meloun static inline int
hym8563_write_buf(struct hym8563_softc * sc,uint8_t reg,uint8_t * buf,uint16_t buflen)10081359d59SMichal Meloun hym8563_write_buf(struct hym8563_softc *sc, uint8_t reg, uint8_t *buf,
10181359d59SMichal Meloun     uint16_t buflen)
10281359d59SMichal Meloun {
10381359d59SMichal Meloun 
10481359d59SMichal Meloun 	return (iicdev_writeto(sc->dev, reg, buf, buflen, IIC_WAIT));
10581359d59SMichal Meloun }
10681359d59SMichal Meloun 
10781359d59SMichal Meloun static inline int
hym8563_read_1(struct hym8563_softc * sc,uint8_t reg,uint8_t * data)10881359d59SMichal Meloun hym8563_read_1(struct hym8563_softc *sc, uint8_t reg, uint8_t *data)
10981359d59SMichal Meloun {
11081359d59SMichal Meloun 
11181359d59SMichal Meloun 	return (iicdev_readfrom(sc->dev, reg, data, 1, IIC_WAIT));
11281359d59SMichal Meloun }
11381359d59SMichal Meloun 
11481359d59SMichal Meloun static inline int
hym8563_write_1(struct hym8563_softc * sc,uint8_t reg,uint8_t val)11581359d59SMichal Meloun hym8563_write_1(struct hym8563_softc *sc, uint8_t reg, uint8_t val)
11681359d59SMichal Meloun {
11781359d59SMichal Meloun 
11881359d59SMichal Meloun 	return (iicdev_writeto(sc->dev, reg, &val, 1, IIC_WAIT));
11981359d59SMichal Meloun }
12081359d59SMichal Meloun 
12181359d59SMichal Meloun static int
hym8563_gettime(device_t dev,struct timespec * ts)12281359d59SMichal Meloun hym8563_gettime(device_t dev, struct timespec *ts)
12381359d59SMichal Meloun {
12481359d59SMichal Meloun 	struct hym8563_softc	*sc;
12581359d59SMichal Meloun 	struct bcd_clocktime	 bct;
12681359d59SMichal Meloun 	uint8_t			 buf[7];
12781359d59SMichal Meloun 	int 			 rv;
12881359d59SMichal Meloun 
12981359d59SMichal Meloun 	sc = device_get_softc(dev);
13081359d59SMichal Meloun 
13181359d59SMichal Meloun 	/* Read all RTC data */
13281359d59SMichal Meloun 	rv = hym8563_read_buf(sc, HYM8563_SEC, buf, sizeof(buf));
13381359d59SMichal Meloun 	if (rv != 0) {
13481359d59SMichal Meloun 		device_printf(sc->dev, "Cannot read time registers: %d\n", rv);
13581359d59SMichal Meloun 		return (rv);
13681359d59SMichal Meloun 	}
13781359d59SMichal Meloun 
13881359d59SMichal Meloun 	/* Check for low voltage flag */
13981359d59SMichal Meloun 	if (buf[0] & HYM8563_SEC_VL)
14081359d59SMichal Meloun 	{
14181359d59SMichal Meloun 		device_printf(sc->dev,
14281359d59SMichal Meloun 		    "WARNING: RTC battery failed; time is invalid\n");
14381359d59SMichal Meloun 		return (EINVAL);
14481359d59SMichal Meloun 	}
14581359d59SMichal Meloun 
14681359d59SMichal Meloun 	bzero(&bct, sizeof(bct));
14781359d59SMichal Meloun 	bct.sec  = buf[0] & 0x7F;
14881359d59SMichal Meloun 	bct.min  = buf[1] & 0x7F;
14981359d59SMichal Meloun 	bct.hour = buf[2] & 0x3f;
15081359d59SMichal Meloun 	bct.day  = buf[3] & 0x3f;
15181359d59SMichal Meloun 	/* buf[4] is  weekday */
15281359d59SMichal Meloun 	bct.mon  = buf[5] & 0x1f;
15381359d59SMichal Meloun 	bct.year = buf[6] & 0xff;
15481359d59SMichal Meloun 	if (buf[5] & HYM8563_MONTH_CENTURY)
15581359d59SMichal Meloun 		bct.year += 0x100;
15681359d59SMichal Meloun 
15781359d59SMichal Meloun 	clock_dbgprint_bcd(sc->dev, CLOCK_DBG_READ, &bct);
15881359d59SMichal Meloun 	return (clock_bcd_to_ts(&bct, ts, false));
15981359d59SMichal Meloun }
16081359d59SMichal Meloun 
16181359d59SMichal Meloun static int
hym8563_settime(device_t dev,struct timespec * ts)16281359d59SMichal Meloun hym8563_settime(device_t dev, struct timespec *ts)
16381359d59SMichal Meloun {
16481359d59SMichal Meloun 	struct hym8563_softc	*sc;
16581359d59SMichal Meloun 	struct bcd_clocktime 	 bct;
16681359d59SMichal Meloun 	uint8_t			 buf[7];
16781359d59SMichal Meloun 	int 			 rv;
16881359d59SMichal Meloun 
16981359d59SMichal Meloun 	sc = device_get_softc(dev);
17081359d59SMichal Meloun 	ts->tv_sec -= utc_offset();
17181359d59SMichal Meloun 	clock_ts_to_bcd(ts, &bct, false);
17281359d59SMichal Meloun 	clock_dbgprint_bcd(sc->dev, CLOCK_DBG_WRITE, &bct);
17381359d59SMichal Meloun 
17481359d59SMichal Meloun 	buf[0] = bct.sec;	/* Also clear VL flag */
17581359d59SMichal Meloun 	buf[1] = bct.min;
17681359d59SMichal Meloun 	buf[2] = bct.hour;
17781359d59SMichal Meloun 	buf[3] = bct.day;
17881359d59SMichal Meloun 	buf[4] = bct.dow;
17981359d59SMichal Meloun 	buf[5] = bct.mon;
18081359d59SMichal Meloun 	buf[6] = bct.year & 0xFF;
18181359d59SMichal Meloun 	if (bct.year > 0x99)
18281359d59SMichal Meloun 		buf[5] |= HYM8563_MONTH_CENTURY;
18381359d59SMichal Meloun 
18481359d59SMichal Meloun 	/* Stop RTC */
18581359d59SMichal Meloun 	rv = hym8563_write_1(sc, HYM8563_CTRL1, HYM8563_CTRL1_STOP);
18681359d59SMichal Meloun 	if (rv != 0) {
18781359d59SMichal Meloun 		device_printf(sc->dev, "Cannot write CTRL1 register: %d\n", rv);
18881359d59SMichal Meloun 		return (rv);
18981359d59SMichal Meloun 	}
19081359d59SMichal Meloun 
19181359d59SMichal Meloun 	/* Write all RTC data */
19281359d59SMichal Meloun 	rv = hym8563_write_buf(sc, HYM8563_SEC, buf, sizeof(buf));
19381359d59SMichal Meloun 	if (rv != 0) {
19481359d59SMichal Meloun 		device_printf(sc->dev, "Cannot write time registers: %d\n", rv);
19581359d59SMichal Meloun 		return (rv);
19681359d59SMichal Meloun 	}
19781359d59SMichal Meloun 	return (rv);
19881359d59SMichal Meloun 
19981359d59SMichal Meloun 	/* Start RTC again */
20081359d59SMichal Meloun 	rv = hym8563_write_1(sc, HYM8563_CTRL1, 0);
20181359d59SMichal Meloun 	if (rv != 0) {
20281359d59SMichal Meloun 		device_printf(sc->dev, "Cannot write CTRL1 register: %d\n", rv);
20381359d59SMichal Meloun 		return (rv);
20481359d59SMichal Meloun 	}
20581359d59SMichal Meloun 
20681359d59SMichal Meloun 	return (0);
20781359d59SMichal Meloun }
20881359d59SMichal Meloun 
20981359d59SMichal Meloun static void
hym8563_init(void * arg)21081359d59SMichal Meloun hym8563_init(void *arg)
21181359d59SMichal Meloun {
21281359d59SMichal Meloun 	struct hym8563_softc *sc;
21381359d59SMichal Meloun 	uint8_t reg;
21481359d59SMichal Meloun 	int rv;
21581359d59SMichal Meloun 
21681359d59SMichal Meloun 	sc = (struct hym8563_softc*)arg;
21781359d59SMichal Meloun 	config_intrhook_disestablish(&sc->init_hook);
21881359d59SMichal Meloun 
21981359d59SMichal Meloun 	/* Clear CTL1 register (stop and test bits) */
22081359d59SMichal Meloun 	rv = hym8563_write_1(sc, HYM8563_CTRL1, 0);
22181359d59SMichal Meloun 	if (rv != 0) {
22281359d59SMichal Meloun 		device_printf(sc->dev, "Cannot init CTRL1 register: %d\n", rv);
22381359d59SMichal Meloun 		return;
22481359d59SMichal Meloun 	}
22581359d59SMichal Meloun 
22681359d59SMichal Meloun 	/* Disable interrupts and alarms */
22781359d59SMichal Meloun 	rv = hym8563_read_1(sc, HYM8563_CTRL2, &reg);
22881359d59SMichal Meloun 	if (rv != 0) {
22981359d59SMichal Meloun 		device_printf(sc->dev, "Cannot read CTRL2 register: %d\n", rv);
23081359d59SMichal Meloun 		return;
23181359d59SMichal Meloun 	}
23281359d59SMichal Meloun 	rv &= ~HYM8563_CTRL2_TI_TP;
23381359d59SMichal Meloun 	rv &= ~HYM8563_CTRL2_AF;
23481359d59SMichal Meloun 	rv &= ~HYM8563_CTRL2_TF;
23581359d59SMichal Meloun 	rv = hym8563_write_1(sc, HYM8563_CTRL2, 0);
23681359d59SMichal Meloun 	if (rv != 0) {
23781359d59SMichal Meloun 		device_printf(sc->dev, "Cannot write CTRL2 register: %d\n", rv);
23881359d59SMichal Meloun 		return;
23981359d59SMichal Meloun 	}
24081359d59SMichal Meloun 
24181359d59SMichal Meloun 	/*
24281359d59SMichal Meloun 	 * Register as a system realtime clock.
24381359d59SMichal Meloun 	 */
24481359d59SMichal Meloun 	clock_register_flags(sc->dev, 1000000, 0);
24581359d59SMichal Meloun 	clock_schedule(sc->dev, 1);
24681359d59SMichal Meloun 	return;
24781359d59SMichal Meloun }
24881359d59SMichal Meloun 
24981359d59SMichal Meloun static int
hym8563_probe(device_t dev)25081359d59SMichal Meloun hym8563_probe(device_t dev)
25181359d59SMichal Meloun {
25281359d59SMichal Meloun 
25381359d59SMichal Meloun #ifdef FDT
25481359d59SMichal Meloun 	if (!ofw_bus_status_okay(dev))
25581359d59SMichal Meloun 		return (ENXIO);
25681359d59SMichal Meloun 
25781359d59SMichal Meloun 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
25881359d59SMichal Meloun 		device_set_desc(dev, "HYM8694 RTC");
25981359d59SMichal Meloun 		return (BUS_PROBE_DEFAULT);
26081359d59SMichal Meloun 	}
26181359d59SMichal Meloun #endif
26281359d59SMichal Meloun 	return (ENXIO);
26381359d59SMichal Meloun }
26481359d59SMichal Meloun 
26581359d59SMichal Meloun static int
hym8563_attach(device_t dev)26681359d59SMichal Meloun hym8563_attach(device_t dev)
26781359d59SMichal Meloun {
26881359d59SMichal Meloun 	struct hym8563_softc *sc;
26981359d59SMichal Meloun 
27081359d59SMichal Meloun 	sc = device_get_softc(dev);
27181359d59SMichal Meloun 	sc->dev = dev;
27281359d59SMichal Meloun 
27381359d59SMichal Meloun 	/*
27481359d59SMichal Meloun 	 * Chip init must wait until interrupts are enabled.  Often i2c access
27581359d59SMichal Meloun 	 * works only when the interrupts are available.
27681359d59SMichal Meloun 	 */
27781359d59SMichal Meloun 	sc->init_hook.ich_func = hym8563_init;
27881359d59SMichal Meloun 	sc->init_hook.ich_arg = sc;
27981359d59SMichal Meloun 	if (config_intrhook_establish(&sc->init_hook) != 0)
28081359d59SMichal Meloun 		return (ENOMEM);
28181359d59SMichal Meloun 
28281359d59SMichal Meloun 	return (0);
28381359d59SMichal Meloun }
28481359d59SMichal Meloun 
28581359d59SMichal Meloun static int
hym8563_detach(device_t dev)28681359d59SMichal Meloun hym8563_detach(device_t dev)
28781359d59SMichal Meloun {
28881359d59SMichal Meloun 
28981359d59SMichal Meloun 	clock_unregister(dev);
29081359d59SMichal Meloun 	return (0);
29181359d59SMichal Meloun }
29281359d59SMichal Meloun 
29381359d59SMichal Meloun static device_method_t hym8563_methods[] = {
29481359d59SMichal Meloun         /* device_if methods */
29581359d59SMichal Meloun 	DEVMETHOD(device_probe,		hym8563_probe),
29681359d59SMichal Meloun 	DEVMETHOD(device_attach,	hym8563_attach),
29781359d59SMichal Meloun 	DEVMETHOD(device_detach,	hym8563_detach),
29881359d59SMichal Meloun 
29981359d59SMichal Meloun         /* clock_if methods */
30081359d59SMichal Meloun 	DEVMETHOD(clock_gettime,	hym8563_gettime),
30181359d59SMichal Meloun 	DEVMETHOD(clock_settime,	hym8563_settime),
30281359d59SMichal Meloun 
30381359d59SMichal Meloun 	DEVMETHOD_END,
30481359d59SMichal Meloun };
30581359d59SMichal Meloun 
30681359d59SMichal Meloun static DEFINE_CLASS_0(hym8563_rtc, hym8563_driver, hym8563_methods,
30781359d59SMichal Meloun     sizeof(struct hym8563_softc));
308*3a866152SJohn Baldwin DRIVER_MODULE(hym8563, iicbus, hym8563_driver, NULL, NULL);
30981359d59SMichal Meloun MODULE_VERSION(hym8563, 1);
31081359d59SMichal Meloun MODULE_DEPEND(hym8563, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
31181359d59SMichal Meloun IICBUS_FDT_PNP_INFO(compat_data);
312