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, ®);
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