126d65475SHubert Mazur /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
326d65475SHubert Mazur *
426d65475SHubert Mazur * Copyright (c) 2021 Alstom Group.
526d65475SHubert Mazur * Copyright (c) 2021 Semihalf.
626d65475SHubert Mazur *
726d65475SHubert Mazur * Redistribution and use in source and binary forms, with or without
826d65475SHubert Mazur * modification, are permitted provided that the following conditions
926d65475SHubert Mazur * are met:
1026d65475SHubert Mazur * 1. Redistributions of source code must retain the above copyright
1126d65475SHubert Mazur * notice, this list of conditions and the following disclaimer.
1226d65475SHubert Mazur * 2. Redistributions in binary form must reproduce the above copyright
1326d65475SHubert Mazur * notice, this list of conditions and the following disclaimer in the
1426d65475SHubert Mazur * documentation and/or other materials provided with the distribution.
1526d65475SHubert Mazur *
1626d65475SHubert Mazur * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1726d65475SHubert Mazur * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1826d65475SHubert Mazur * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1926d65475SHubert Mazur * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2026d65475SHubert Mazur * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2126d65475SHubert Mazur * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2226d65475SHubert Mazur * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2326d65475SHubert Mazur * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2426d65475SHubert Mazur * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2526d65475SHubert Mazur * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2626d65475SHubert Mazur */
2726d65475SHubert Mazur
2826d65475SHubert Mazur #include <sys/cdefs.h>
2926d65475SHubert Mazur #include "opt_platform.h"
3026d65475SHubert Mazur
3126d65475SHubert Mazur #include <sys/param.h>
3226d65475SHubert Mazur #include <sys/systm.h>
3326d65475SHubert Mazur #include <sys/bus.h>
3426d65475SHubert Mazur #include <sys/clock.h>
3526d65475SHubert Mazur #include <sys/kernel.h>
3626d65475SHubert Mazur #include <sys/lock.h>
3726d65475SHubert Mazur #include <sys/module.h>
3826d65475SHubert Mazur
3926d65475SHubert Mazur #include <dev/ofw/ofw_bus.h>
4026d65475SHubert Mazur #include <dev/ofw/ofw_bus_subr.h>
4126d65475SHubert Mazur
4226d65475SHubert Mazur #include <dev/iicbus/iiconf.h>
4326d65475SHubert Mazur #include <dev/iicbus/iicbus.h>
4426d65475SHubert Mazur
4526d65475SHubert Mazur #include "clock_if.h"
4626d65475SHubert Mazur #include "iicbus_if.h"
4726d65475SHubert Mazur
4826d65475SHubert Mazur #define BIT(x) (1 << (x))
4926d65475SHubert Mazur
5026d65475SHubert Mazur #define PCF85063_CTRL1_REG 0x0
5126d65475SHubert Mazur #define PCF85063_TIME_REG 0x4
5226d65475SHubert Mazur
5326d65475SHubert Mazur #define PCF85063_CTRL1_TIME_FORMAT BIT(1)
5426d65475SHubert Mazur #define PCF85063_CTRL1_RTC_CLK_STOP BIT(5)
5526d65475SHubert Mazur #define PCF85063_TIME_REG_OSC_STOP BIT(7)
5626d65475SHubert Mazur
5726d65475SHubert Mazur #define PCF85063_HALF_OF_SEC_NS 500000000
5826d65475SHubert Mazur
5926d65475SHubert Mazur struct pcf85063_time {
6026d65475SHubert Mazur uint8_t sec;
6126d65475SHubert Mazur uint8_t min;
6226d65475SHubert Mazur uint8_t hour;
6326d65475SHubert Mazur uint8_t day;
6426d65475SHubert Mazur uint8_t dow;
6526d65475SHubert Mazur uint8_t mon;
6626d65475SHubert Mazur uint8_t year;
6726d65475SHubert Mazur };
6826d65475SHubert Mazur
6926d65475SHubert Mazur static int pcf85063_attach(device_t dev);
7026d65475SHubert Mazur static int pcf85063_detach(device_t dev);
7126d65475SHubert Mazur static int pcf85063_probe(device_t dev);
7226d65475SHubert Mazur
7326d65475SHubert Mazur static int pcf85063_get_time(device_t dev, struct timespec *ts);
7426d65475SHubert Mazur static int pcf85063_set_time(device_t dev, struct timespec *ts);
7526d65475SHubert Mazur
7626d65475SHubert Mazur static int pcf85063_check_status(device_t dev);
7726d65475SHubert Mazur
7826d65475SHubert Mazur static struct ofw_compat_data pcf85063_compat_data[] = {
7926d65475SHubert Mazur { "nxp,pcf85063", 1},
8026d65475SHubert Mazur { NULL, 0}
8126d65475SHubert Mazur };
8226d65475SHubert Mazur
8326d65475SHubert Mazur static int
pcf85063_check_status(device_t dev)8426d65475SHubert Mazur pcf85063_check_status(device_t dev)
8526d65475SHubert Mazur {
8626d65475SHubert Mazur uint8_t flags;
8726d65475SHubert Mazur int error;
8826d65475SHubert Mazur
8926d65475SHubert Mazur error = iicdev_readfrom(dev, PCF85063_TIME_REG, &flags, 1, IIC_WAIT);
9026d65475SHubert Mazur if (error != 0)
9126d65475SHubert Mazur return (error);
9226d65475SHubert Mazur
9326d65475SHubert Mazur if (flags & PCF85063_TIME_REG_OSC_STOP) {
9426d65475SHubert Mazur device_printf(dev,
9526d65475SHubert Mazur "Low voltage flag set, date is incorrect.\n");
9626d65475SHubert Mazur return (ENXIO);
9726d65475SHubert Mazur }
9826d65475SHubert Mazur
9926d65475SHubert Mazur return (0);
10026d65475SHubert Mazur }
10126d65475SHubert Mazur
10226d65475SHubert Mazur static int
pcf85063_attach(device_t dev)10326d65475SHubert Mazur pcf85063_attach(device_t dev)
10426d65475SHubert Mazur {
10526d65475SHubert Mazur
10626d65475SHubert Mazur clock_register_flags(dev, 1000000, 0);
10726d65475SHubert Mazur clock_schedule(dev, 1);
10826d65475SHubert Mazur
10926d65475SHubert Mazur return (0);
11026d65475SHubert Mazur }
11126d65475SHubert Mazur
11226d65475SHubert Mazur static int
pcf85063_detach(device_t dev)11326d65475SHubert Mazur pcf85063_detach(device_t dev)
11426d65475SHubert Mazur {
11526d65475SHubert Mazur
11626d65475SHubert Mazur clock_unregister(dev);
11726d65475SHubert Mazur
11826d65475SHubert Mazur return (0);
11926d65475SHubert Mazur }
12026d65475SHubert Mazur
12126d65475SHubert Mazur static int
pcf85063_get_time(device_t dev,struct timespec * ts)12226d65475SHubert Mazur pcf85063_get_time(device_t dev, struct timespec *ts)
12326d65475SHubert Mazur {
12426d65475SHubert Mazur struct pcf85063_time data;
12526d65475SHubert Mazur struct bcd_clocktime bcd;
12626d65475SHubert Mazur uint8_t control_reg;
12726d65475SHubert Mazur int error;
12826d65475SHubert Mazur
12926d65475SHubert Mazur error = pcf85063_check_status(dev);
13026d65475SHubert Mazur if (error != 0)
13126d65475SHubert Mazur return (error);
13226d65475SHubert Mazur
13326d65475SHubert Mazur /* read hour format (12/24 hour mode) */
13426d65475SHubert Mazur error = iicdev_readfrom(dev, PCF85063_CTRL1_REG, &control_reg,
13526d65475SHubert Mazur sizeof(uint8_t), IIC_WAIT);
13626d65475SHubert Mazur if (error != 0)
13726d65475SHubert Mazur return (error);
13826d65475SHubert Mazur
13926d65475SHubert Mazur /* read current date and time */
14026d65475SHubert Mazur error = iicdev_readfrom(dev, PCF85063_TIME_REG, &data,
14126d65475SHubert Mazur sizeof(struct pcf85063_time), IIC_WAIT);
14226d65475SHubert Mazur if (error != 0)
14326d65475SHubert Mazur return (error);
14426d65475SHubert Mazur
14526d65475SHubert Mazur bcd.nsec = 0;
14626d65475SHubert Mazur bcd.sec = data.sec & 0x7F;
14726d65475SHubert Mazur bcd.min = data.min & 0x7F;
14826d65475SHubert Mazur
1493662b8f1SHubert Mazur if (control_reg & PCF85063_CTRL1_TIME_FORMAT) {
15026d65475SHubert Mazur /* 12 hour mode */
15126d65475SHubert Mazur bcd.hour = data.hour & 0x1F;
1523662b8f1SHubert Mazur /* Check if hour is pm */
1533662b8f1SHubert Mazur bcd.ispm = data.hour & 0x20;
1543662b8f1SHubert Mazur } else {
15526d65475SHubert Mazur /* 24 hour mode */
15626d65475SHubert Mazur bcd.hour = data.hour & 0x3F;
1573662b8f1SHubert Mazur bcd.ispm = false;
1583662b8f1SHubert Mazur }
15926d65475SHubert Mazur
16026d65475SHubert Mazur bcd.dow = (data.dow & 0x7) + 1;
16126d65475SHubert Mazur bcd.day = data.day & 0x3F;
16226d65475SHubert Mazur bcd.mon = data.mon & 0x1F;
16326d65475SHubert Mazur bcd.year = data.year;
16426d65475SHubert Mazur
16526d65475SHubert Mazur clock_dbgprint_bcd(dev, CLOCK_DBG_READ, &bcd);
1663662b8f1SHubert Mazur error = clock_bcd_to_ts(&bcd, ts,
1673662b8f1SHubert Mazur control_reg & PCF85063_CTRL1_TIME_FORMAT);
16826d65475SHubert Mazur
16926d65475SHubert Mazur return (error);
17026d65475SHubert Mazur }
17126d65475SHubert Mazur
17226d65475SHubert Mazur static int
pcf85063_set_time(device_t dev,struct timespec * ts)17326d65475SHubert Mazur pcf85063_set_time(device_t dev, struct timespec *ts)
17426d65475SHubert Mazur {
17526d65475SHubert Mazur uint8_t time_reg, ctrl_reg;
17626d65475SHubert Mazur struct pcf85063_time data;
17726d65475SHubert Mazur struct bcd_clocktime bcd;
17826d65475SHubert Mazur int error;
17926d65475SHubert Mazur
1803662b8f1SHubert Mazur error = iicdev_readfrom(dev, PCF85063_TIME_REG, &ctrl_reg,
1813662b8f1SHubert Mazur sizeof(uint8_t), IIC_WAIT);
1823662b8f1SHubert Mazur
18326d65475SHubert Mazur ts->tv_sec -= utc_offset();
1843e5fe3d5SHubert Mazur clock_ts_to_bcd(ts, &bcd, false);
18526d65475SHubert Mazur clock_dbgprint_bcd(dev, CLOCK_DBG_WRITE, &bcd);
18626d65475SHubert Mazur
18726d65475SHubert Mazur data.sec = bcd.sec;
18826d65475SHubert Mazur data.min = bcd.min;
18926d65475SHubert Mazur data.hour = bcd.hour;
19026d65475SHubert Mazur data.dow = bcd.dow - 1;
19126d65475SHubert Mazur data.day = bcd.day;
19226d65475SHubert Mazur data.mon = bcd.mon;
19326d65475SHubert Mazur data.year = bcd.year;
19426d65475SHubert Mazur
19526d65475SHubert Mazur if (ts->tv_nsec > PCF85063_HALF_OF_SEC_NS)
19626d65475SHubert Mazur data.sec++;
19726d65475SHubert Mazur
19826d65475SHubert Mazur /* disable clock */
19926d65475SHubert Mazur error = iicdev_readfrom(dev, PCF85063_CTRL1_REG, &ctrl_reg,
20026d65475SHubert Mazur sizeof(uint8_t), IIC_WAIT);
20126d65475SHubert Mazur if (error != 0)
20226d65475SHubert Mazur return (error);
20326d65475SHubert Mazur
20426d65475SHubert Mazur ctrl_reg |= PCF85063_CTRL1_RTC_CLK_STOP;
2053e5fe3d5SHubert Mazur /* Explicitly set 24-hour mode. */
2063e5fe3d5SHubert Mazur ctrl_reg &= ~PCF85063_CTRL1_TIME_FORMAT;
20726d65475SHubert Mazur
20826d65475SHubert Mazur error = iicdev_writeto(dev, PCF85063_CTRL1_REG, &ctrl_reg,
20926d65475SHubert Mazur sizeof(uint8_t), IIC_WAIT);
21026d65475SHubert Mazur if (error != 0)
21126d65475SHubert Mazur return (error);
21226d65475SHubert Mazur
21326d65475SHubert Mazur /* clock is disabled now, write time and date */
21426d65475SHubert Mazur error = iicdev_writeto(dev, PCF85063_TIME_REG, &data,
21526d65475SHubert Mazur sizeof(struct pcf85063_time), IIC_WAIT);
21626d65475SHubert Mazur if (error != 0)
21726d65475SHubert Mazur return (error);
21826d65475SHubert Mazur
21926d65475SHubert Mazur /* restart clock */
22026d65475SHubert Mazur ctrl_reg &= ~PCF85063_CTRL1_RTC_CLK_STOP;
22126d65475SHubert Mazur error = iicdev_writeto(dev, PCF85063_CTRL1_REG, &ctrl_reg,
22226d65475SHubert Mazur sizeof(uint8_t), IIC_WAIT);
22326d65475SHubert Mazur if (error != 0)
22426d65475SHubert Mazur return (error);
22526d65475SHubert Mazur
22626d65475SHubert Mazur /* reset low voltage flag */
22726d65475SHubert Mazur error = iicdev_readfrom(dev, PCF85063_TIME_REG, &time_reg,
22826d65475SHubert Mazur sizeof(uint8_t), IIC_WAIT);
22926d65475SHubert Mazur if (error != 0)
23026d65475SHubert Mazur return (error);
23126d65475SHubert Mazur
23226d65475SHubert Mazur time_reg &= ~PCF85063_TIME_REG_OSC_STOP;
23326d65475SHubert Mazur
23426d65475SHubert Mazur error = iicdev_writeto(dev, PCF85063_TIME_REG, &time_reg,
23526d65475SHubert Mazur sizeof(uint8_t), IIC_WAIT);
23626d65475SHubert Mazur
23726d65475SHubert Mazur return (error);
23826d65475SHubert Mazur }
23926d65475SHubert Mazur
24026d65475SHubert Mazur static int
pcf85063_probe(device_t dev)24126d65475SHubert Mazur pcf85063_probe(device_t dev)
24226d65475SHubert Mazur {
24326d65475SHubert Mazur
24426d65475SHubert Mazur if (!ofw_bus_status_okay(dev))
24526d65475SHubert Mazur return (ENXIO);
24626d65475SHubert Mazur
24726d65475SHubert Mazur if (!ofw_bus_search_compatible(dev, pcf85063_compat_data)->ocd_data)
24826d65475SHubert Mazur return (ENXIO);
24926d65475SHubert Mazur
25026d65475SHubert Mazur device_set_desc(dev, "NXP pcf85063 Real Time Clock");
25126d65475SHubert Mazur
25226d65475SHubert Mazur return (BUS_PROBE_GENERIC);
25326d65475SHubert Mazur }
25426d65475SHubert Mazur
25526d65475SHubert Mazur static device_method_t pcf85063_methods [] = {
25626d65475SHubert Mazur DEVMETHOD(device_attach, pcf85063_attach),
25726d65475SHubert Mazur DEVMETHOD(device_detach, pcf85063_detach),
25826d65475SHubert Mazur DEVMETHOD(device_probe, pcf85063_probe),
25926d65475SHubert Mazur
26026d65475SHubert Mazur DEVMETHOD(clock_gettime, pcf85063_get_time),
26126d65475SHubert Mazur DEVMETHOD(clock_settime, pcf85063_set_time),
26226d65475SHubert Mazur
26326d65475SHubert Mazur DEVMETHOD_END
26426d65475SHubert Mazur };
26526d65475SHubert Mazur
26626d65475SHubert Mazur static driver_t pcf85063_driver = {
26726d65475SHubert Mazur "pcf85063",
26826d65475SHubert Mazur pcf85063_methods,
26926d65475SHubert Mazur 0
27026d65475SHubert Mazur };
27126d65475SHubert Mazur
2723a866152SJohn Baldwin DRIVER_MODULE(pcf85063, iicbus, pcf85063_driver, NULL, NULL);
27326d65475SHubert Mazur IICBUS_FDT_PNP_INFO(pcf85063_compat_data);
274