xref: /freebsd/sys/dev/iicbus/rtc/pcf85063.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
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