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 __FBSDID("$FreeBSD$"); 3026d65475SHubert Mazur 3126d65475SHubert Mazur #include "opt_platform.h" 3226d65475SHubert Mazur 3326d65475SHubert Mazur #include <sys/param.h> 3426d65475SHubert Mazur #include <sys/systm.h> 3526d65475SHubert Mazur #include <sys/bus.h> 3626d65475SHubert Mazur #include <sys/clock.h> 3726d65475SHubert Mazur #include <sys/kernel.h> 3826d65475SHubert Mazur #include <sys/lock.h> 3926d65475SHubert Mazur #include <sys/module.h> 4026d65475SHubert Mazur 4126d65475SHubert Mazur #include <dev/ofw/ofw_bus.h> 4226d65475SHubert Mazur #include <dev/ofw/ofw_bus_subr.h> 4326d65475SHubert Mazur 4426d65475SHubert Mazur #include <dev/iicbus/iiconf.h> 4526d65475SHubert Mazur #include <dev/iicbus/iicbus.h> 4626d65475SHubert Mazur 4726d65475SHubert Mazur #include "clock_if.h" 4826d65475SHubert Mazur #include "iicbus_if.h" 4926d65475SHubert Mazur 5026d65475SHubert Mazur #define BIT(x) (1 << (x)) 5126d65475SHubert Mazur 5226d65475SHubert Mazur #define PCF85063_CTRL1_REG 0x0 5326d65475SHubert Mazur #define PCF85063_TIME_REG 0x4 5426d65475SHubert Mazur 5526d65475SHubert Mazur #define PCF85063_CTRL1_TIME_FORMAT BIT(1) 5626d65475SHubert Mazur #define PCF85063_CTRL1_RTC_CLK_STOP BIT(5) 5726d65475SHubert Mazur #define PCF85063_TIME_REG_OSC_STOP BIT(7) 5826d65475SHubert Mazur 5926d65475SHubert Mazur #define PCF85063_HALF_OF_SEC_NS 500000000 6026d65475SHubert Mazur 6126d65475SHubert Mazur struct pcf85063_time { 6226d65475SHubert Mazur uint8_t sec; 6326d65475SHubert Mazur uint8_t min; 6426d65475SHubert Mazur uint8_t hour; 6526d65475SHubert Mazur uint8_t day; 6626d65475SHubert Mazur uint8_t dow; 6726d65475SHubert Mazur uint8_t mon; 6826d65475SHubert Mazur uint8_t year; 6926d65475SHubert Mazur }; 7026d65475SHubert Mazur 7126d65475SHubert Mazur static int pcf85063_attach(device_t dev); 7226d65475SHubert Mazur static int pcf85063_detach(device_t dev); 7326d65475SHubert Mazur static int pcf85063_probe(device_t dev); 7426d65475SHubert Mazur 7526d65475SHubert Mazur static int pcf85063_get_time(device_t dev, struct timespec *ts); 7626d65475SHubert Mazur static int pcf85063_set_time(device_t dev, struct timespec *ts); 7726d65475SHubert Mazur 7826d65475SHubert Mazur static int pcf85063_check_status(device_t dev); 7926d65475SHubert Mazur 8026d65475SHubert Mazur static struct ofw_compat_data pcf85063_compat_data[] = { 8126d65475SHubert Mazur { "nxp,pcf85063", 1}, 8226d65475SHubert Mazur { NULL, 0} 8326d65475SHubert Mazur }; 8426d65475SHubert Mazur 8526d65475SHubert Mazur static int 8626d65475SHubert Mazur pcf85063_check_status(device_t dev) 8726d65475SHubert Mazur { 8826d65475SHubert Mazur uint8_t flags; 8926d65475SHubert Mazur int error; 9026d65475SHubert Mazur 9126d65475SHubert Mazur error = iicdev_readfrom(dev, PCF85063_TIME_REG, &flags, 1, IIC_WAIT); 9226d65475SHubert Mazur if (error != 0) 9326d65475SHubert Mazur return (error); 9426d65475SHubert Mazur 9526d65475SHubert Mazur if (flags & PCF85063_TIME_REG_OSC_STOP) { 9626d65475SHubert Mazur device_printf(dev, 9726d65475SHubert Mazur "Low voltage flag set, date is incorrect.\n"); 9826d65475SHubert Mazur return (ENXIO); 9926d65475SHubert Mazur } 10026d65475SHubert Mazur 10126d65475SHubert Mazur return (0); 10226d65475SHubert Mazur } 10326d65475SHubert Mazur 10426d65475SHubert Mazur static int 10526d65475SHubert Mazur pcf85063_attach(device_t dev) 10626d65475SHubert Mazur { 10726d65475SHubert Mazur 10826d65475SHubert Mazur clock_register_flags(dev, 1000000, 0); 10926d65475SHubert Mazur clock_schedule(dev, 1); 11026d65475SHubert Mazur 11126d65475SHubert Mazur return (0); 11226d65475SHubert Mazur } 11326d65475SHubert Mazur 11426d65475SHubert Mazur static int 11526d65475SHubert Mazur pcf85063_detach(device_t dev) 11626d65475SHubert Mazur { 11726d65475SHubert Mazur 11826d65475SHubert Mazur clock_unregister(dev); 11926d65475SHubert Mazur 12026d65475SHubert Mazur return (0); 12126d65475SHubert Mazur } 12226d65475SHubert Mazur 12326d65475SHubert Mazur static int 12426d65475SHubert Mazur pcf85063_get_time(device_t dev, struct timespec *ts) 12526d65475SHubert Mazur { 12626d65475SHubert Mazur struct pcf85063_time data; 12726d65475SHubert Mazur struct bcd_clocktime bcd; 12826d65475SHubert Mazur uint8_t control_reg; 12926d65475SHubert Mazur int error; 13026d65475SHubert Mazur 13126d65475SHubert Mazur error = pcf85063_check_status(dev); 13226d65475SHubert Mazur if (error != 0) 13326d65475SHubert Mazur return (error); 13426d65475SHubert Mazur 13526d65475SHubert Mazur /* read hour format (12/24 hour mode) */ 13626d65475SHubert Mazur error = iicdev_readfrom(dev, PCF85063_CTRL1_REG, &control_reg, 13726d65475SHubert Mazur sizeof(uint8_t), IIC_WAIT); 13826d65475SHubert Mazur if (error != 0) 13926d65475SHubert Mazur return (error); 14026d65475SHubert Mazur 14126d65475SHubert Mazur /* read current date and time */ 14226d65475SHubert Mazur error = iicdev_readfrom(dev, PCF85063_TIME_REG, &data, 14326d65475SHubert Mazur sizeof(struct pcf85063_time), IIC_WAIT); 14426d65475SHubert Mazur if (error != 0) 14526d65475SHubert Mazur return (error); 14626d65475SHubert Mazur 14726d65475SHubert Mazur bcd.nsec = 0; 14826d65475SHubert Mazur bcd.sec = data.sec & 0x7F; 14926d65475SHubert Mazur bcd.min = data.min & 0x7F; 15026d65475SHubert Mazur 1513662b8f1SHubert Mazur if (control_reg & PCF85063_CTRL1_TIME_FORMAT) { 15226d65475SHubert Mazur /* 12 hour mode */ 15326d65475SHubert Mazur bcd.hour = data.hour & 0x1F; 1543662b8f1SHubert Mazur /* Check if hour is pm */ 1553662b8f1SHubert Mazur bcd.ispm = data.hour & 0x20; 1563662b8f1SHubert Mazur } else { 15726d65475SHubert Mazur /* 24 hour mode */ 15826d65475SHubert Mazur bcd.hour = data.hour & 0x3F; 1593662b8f1SHubert Mazur bcd.ispm = false; 1603662b8f1SHubert Mazur } 16126d65475SHubert Mazur 16226d65475SHubert Mazur bcd.dow = (data.dow & 0x7) + 1; 16326d65475SHubert Mazur bcd.day = data.day & 0x3F; 16426d65475SHubert Mazur bcd.mon = data.mon & 0x1F; 16526d65475SHubert Mazur bcd.year = data.year; 16626d65475SHubert Mazur 16726d65475SHubert Mazur clock_dbgprint_bcd(dev, CLOCK_DBG_READ, &bcd); 1683662b8f1SHubert Mazur error = clock_bcd_to_ts(&bcd, ts, 1693662b8f1SHubert Mazur control_reg & PCF85063_CTRL1_TIME_FORMAT); 17026d65475SHubert Mazur 17126d65475SHubert Mazur return (error); 17226d65475SHubert Mazur } 17326d65475SHubert Mazur 17426d65475SHubert Mazur static int 17526d65475SHubert Mazur pcf85063_set_time(device_t dev, struct timespec *ts) 17626d65475SHubert Mazur { 17726d65475SHubert Mazur uint8_t time_reg, ctrl_reg; 17826d65475SHubert Mazur struct pcf85063_time data; 17926d65475SHubert Mazur struct bcd_clocktime bcd; 18026d65475SHubert Mazur int error; 18126d65475SHubert Mazur 1823662b8f1SHubert Mazur error = iicdev_readfrom(dev, PCF85063_TIME_REG, &ctrl_reg, 1833662b8f1SHubert Mazur sizeof(uint8_t), IIC_WAIT); 1843662b8f1SHubert Mazur 18526d65475SHubert Mazur ts->tv_sec -= utc_offset(); 1863e5fe3d5SHubert Mazur clock_ts_to_bcd(ts, &bcd, false); 18726d65475SHubert Mazur clock_dbgprint_bcd(dev, CLOCK_DBG_WRITE, &bcd); 18826d65475SHubert Mazur 18926d65475SHubert Mazur data.sec = bcd.sec; 19026d65475SHubert Mazur data.min = bcd.min; 19126d65475SHubert Mazur data.hour = bcd.hour; 19226d65475SHubert Mazur data.dow = bcd.dow - 1; 19326d65475SHubert Mazur data.day = bcd.day; 19426d65475SHubert Mazur data.mon = bcd.mon; 19526d65475SHubert Mazur data.year = bcd.year; 19626d65475SHubert Mazur 19726d65475SHubert Mazur if (ts->tv_nsec > PCF85063_HALF_OF_SEC_NS) 19826d65475SHubert Mazur data.sec++; 19926d65475SHubert Mazur 20026d65475SHubert Mazur /* disable clock */ 20126d65475SHubert Mazur error = iicdev_readfrom(dev, PCF85063_CTRL1_REG, &ctrl_reg, 20226d65475SHubert Mazur sizeof(uint8_t), IIC_WAIT); 20326d65475SHubert Mazur if (error != 0) 20426d65475SHubert Mazur return (error); 20526d65475SHubert Mazur 20626d65475SHubert Mazur ctrl_reg |= PCF85063_CTRL1_RTC_CLK_STOP; 2073e5fe3d5SHubert Mazur /* Explicitly set 24-hour mode. */ 2083e5fe3d5SHubert Mazur ctrl_reg &= ~PCF85063_CTRL1_TIME_FORMAT; 20926d65475SHubert Mazur 21026d65475SHubert Mazur error = iicdev_writeto(dev, PCF85063_CTRL1_REG, &ctrl_reg, 21126d65475SHubert Mazur sizeof(uint8_t), IIC_WAIT); 21226d65475SHubert Mazur if (error != 0) 21326d65475SHubert Mazur return (error); 21426d65475SHubert Mazur 21526d65475SHubert Mazur /* clock is disabled now, write time and date */ 21626d65475SHubert Mazur error = iicdev_writeto(dev, PCF85063_TIME_REG, &data, 21726d65475SHubert Mazur sizeof(struct pcf85063_time), IIC_WAIT); 21826d65475SHubert Mazur if (error != 0) 21926d65475SHubert Mazur return (error); 22026d65475SHubert Mazur 22126d65475SHubert Mazur /* restart clock */ 22226d65475SHubert Mazur ctrl_reg &= ~PCF85063_CTRL1_RTC_CLK_STOP; 22326d65475SHubert Mazur error = iicdev_writeto(dev, PCF85063_CTRL1_REG, &ctrl_reg, 22426d65475SHubert Mazur sizeof(uint8_t), IIC_WAIT); 22526d65475SHubert Mazur if (error != 0) 22626d65475SHubert Mazur return (error); 22726d65475SHubert Mazur 22826d65475SHubert Mazur /* reset low voltage flag */ 22926d65475SHubert Mazur error = iicdev_readfrom(dev, PCF85063_TIME_REG, &time_reg, 23026d65475SHubert Mazur sizeof(uint8_t), IIC_WAIT); 23126d65475SHubert Mazur if (error != 0) 23226d65475SHubert Mazur return (error); 23326d65475SHubert Mazur 23426d65475SHubert Mazur time_reg &= ~PCF85063_TIME_REG_OSC_STOP; 23526d65475SHubert Mazur 23626d65475SHubert Mazur error = iicdev_writeto(dev, PCF85063_TIME_REG, &time_reg, 23726d65475SHubert Mazur sizeof(uint8_t), IIC_WAIT); 23826d65475SHubert Mazur 23926d65475SHubert Mazur return (error); 24026d65475SHubert Mazur } 24126d65475SHubert Mazur 24226d65475SHubert Mazur static int 24326d65475SHubert Mazur pcf85063_probe(device_t dev) 24426d65475SHubert Mazur { 24526d65475SHubert Mazur 24626d65475SHubert Mazur if (!ofw_bus_status_okay(dev)) 24726d65475SHubert Mazur return (ENXIO); 24826d65475SHubert Mazur 24926d65475SHubert Mazur if (!ofw_bus_search_compatible(dev, pcf85063_compat_data)->ocd_data) 25026d65475SHubert Mazur return (ENXIO); 25126d65475SHubert Mazur 25226d65475SHubert Mazur device_set_desc(dev, "NXP pcf85063 Real Time Clock"); 25326d65475SHubert Mazur 25426d65475SHubert Mazur return (BUS_PROBE_GENERIC); 25526d65475SHubert Mazur } 25626d65475SHubert Mazur 25726d65475SHubert Mazur static device_method_t pcf85063_methods [] = { 25826d65475SHubert Mazur DEVMETHOD(device_attach, pcf85063_attach), 25926d65475SHubert Mazur DEVMETHOD(device_detach, pcf85063_detach), 26026d65475SHubert Mazur DEVMETHOD(device_probe, pcf85063_probe), 26126d65475SHubert Mazur 26226d65475SHubert Mazur DEVMETHOD(clock_gettime, pcf85063_get_time), 26326d65475SHubert Mazur DEVMETHOD(clock_settime, pcf85063_set_time), 26426d65475SHubert Mazur 26526d65475SHubert Mazur DEVMETHOD_END 26626d65475SHubert Mazur }; 26726d65475SHubert Mazur 26826d65475SHubert Mazur static driver_t pcf85063_driver = { 26926d65475SHubert Mazur "pcf85063", 27026d65475SHubert Mazur pcf85063_methods, 27126d65475SHubert Mazur 0 27226d65475SHubert Mazur }; 27326d65475SHubert Mazur 2743a866152SJohn Baldwin DRIVER_MODULE(pcf85063, iicbus, pcf85063_driver, NULL, NULL); 27526d65475SHubert Mazur IICBUS_FDT_PNP_INFO(pcf85063_compat_data); 276