xref: /openbmc/linux/drivers/rtc/rtc-ds2404.c (revision fdcfd854333be5b30377dc5daa9cd0fa1643a979)
114556f04SAlexandre Belloni // SPDX-License-Identifier: GPL-2.0
214556f04SAlexandre Belloni // Copyright (C) 2012 Sven Schnelle <svens@stackframe.org>
37418a119SSven Schnelle 
47418a119SSven Schnelle #include <linux/platform_device.h>
57418a119SSven Schnelle #include <linux/module.h>
67418a119SSven Schnelle #include <linux/init.h>
77418a119SSven Schnelle #include <linux/rtc.h>
87418a119SSven Schnelle #include <linux/types.h>
97418a119SSven Schnelle #include <linux/bcd.h>
1010c2a2e7SAlexandre Belloni #include <linux/platform_data/rtc-ds2404.h>
117418a119SSven Schnelle #include <linux/delay.h>
127418a119SSven Schnelle #include <linux/gpio.h>
137418a119SSven Schnelle #include <linux/slab.h>
147418a119SSven Schnelle 
157418a119SSven Schnelle #include <linux/io.h>
167418a119SSven Schnelle 
177418a119SSven Schnelle #define DS2404_STATUS_REG 0x200
187418a119SSven Schnelle #define DS2404_CONTROL_REG 0x201
197418a119SSven Schnelle #define DS2404_RTC_REG 0x202
207418a119SSven Schnelle 
217418a119SSven Schnelle #define DS2404_WRITE_SCRATCHPAD_CMD 0x0f
227418a119SSven Schnelle #define DS2404_READ_SCRATCHPAD_CMD 0xaa
237418a119SSven Schnelle #define DS2404_COPY_SCRATCHPAD_CMD 0x55
247418a119SSven Schnelle #define DS2404_READ_MEMORY_CMD 0xf0
257418a119SSven Schnelle 
267418a119SSven Schnelle #define DS2404_RST	0
277418a119SSven Schnelle #define DS2404_CLK	1
287418a119SSven Schnelle #define DS2404_DQ	2
297418a119SSven Schnelle 
307418a119SSven Schnelle struct ds2404_gpio {
317418a119SSven Schnelle 	const char *name;
327418a119SSven Schnelle 	unsigned int gpio;
337418a119SSven Schnelle };
347418a119SSven Schnelle 
357418a119SSven Schnelle struct ds2404 {
367418a119SSven Schnelle 	struct ds2404_gpio *gpio;
377418a119SSven Schnelle 	struct rtc_device *rtc;
387418a119SSven Schnelle };
397418a119SSven Schnelle 
407418a119SSven Schnelle static struct ds2404_gpio ds2404_gpio[] = {
417418a119SSven Schnelle 	{ "RTC RST", 0 },
427418a119SSven Schnelle 	{ "RTC CLK", 0 },
437418a119SSven Schnelle 	{ "RTC DQ", 0 },
447418a119SSven Schnelle };
457418a119SSven Schnelle 
467418a119SSven Schnelle static int ds2404_gpio_map(struct ds2404 *chip, struct platform_device *pdev,
477418a119SSven Schnelle 			  struct ds2404_platform_data *pdata)
487418a119SSven Schnelle {
497418a119SSven Schnelle 	int i, err;
507418a119SSven Schnelle 
517418a119SSven Schnelle 	ds2404_gpio[DS2404_RST].gpio = pdata->gpio_rst;
527418a119SSven Schnelle 	ds2404_gpio[DS2404_CLK].gpio = pdata->gpio_clk;
537418a119SSven Schnelle 	ds2404_gpio[DS2404_DQ].gpio = pdata->gpio_dq;
547418a119SSven Schnelle 
557418a119SSven Schnelle 	for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++) {
567418a119SSven Schnelle 		err = gpio_request(ds2404_gpio[i].gpio, ds2404_gpio[i].name);
577418a119SSven Schnelle 		if (err) {
580fae8237SJingoo Han 			dev_err(&pdev->dev, "error mapping gpio %s: %d\n",
597418a119SSven Schnelle 				ds2404_gpio[i].name, err);
607418a119SSven Schnelle 			goto err_request;
617418a119SSven Schnelle 		}
627418a119SSven Schnelle 		if (i != DS2404_DQ)
637418a119SSven Schnelle 			gpio_direction_output(ds2404_gpio[i].gpio, 1);
647418a119SSven Schnelle 	}
657418a119SSven Schnelle 
667418a119SSven Schnelle 	chip->gpio = ds2404_gpio;
677418a119SSven Schnelle 	return 0;
687418a119SSven Schnelle 
697418a119SSven Schnelle err_request:
707418a119SSven Schnelle 	while (--i >= 0)
717418a119SSven Schnelle 		gpio_free(ds2404_gpio[i].gpio);
727418a119SSven Schnelle 	return err;
737418a119SSven Schnelle }
747418a119SSven Schnelle 
75d9aa5ca4SAlexandre Belloni static void ds2404_gpio_unmap(void *data)
767418a119SSven Schnelle {
777418a119SSven Schnelle 	int i;
787418a119SSven Schnelle 
797418a119SSven Schnelle 	for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++)
807418a119SSven Schnelle 		gpio_free(ds2404_gpio[i].gpio);
817418a119SSven Schnelle }
827418a119SSven Schnelle 
837418a119SSven Schnelle static void ds2404_reset(struct device *dev)
847418a119SSven Schnelle {
857418a119SSven Schnelle 	gpio_set_value(ds2404_gpio[DS2404_RST].gpio, 0);
867418a119SSven Schnelle 	udelay(1000);
877418a119SSven Schnelle 	gpio_set_value(ds2404_gpio[DS2404_RST].gpio, 1);
887418a119SSven Schnelle 	gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0);
897418a119SSven Schnelle 	gpio_direction_output(ds2404_gpio[DS2404_DQ].gpio, 0);
907418a119SSven Schnelle 	udelay(10);
917418a119SSven Schnelle }
927418a119SSven Schnelle 
937418a119SSven Schnelle static void ds2404_write_byte(struct device *dev, u8 byte)
947418a119SSven Schnelle {
957418a119SSven Schnelle 	int i;
967418a119SSven Schnelle 
977418a119SSven Schnelle 	gpio_direction_output(ds2404_gpio[DS2404_DQ].gpio, 1);
987418a119SSven Schnelle 	for (i = 0; i < 8; i++) {
997418a119SSven Schnelle 		gpio_set_value(ds2404_gpio[DS2404_DQ].gpio, byte & (1 << i));
1007418a119SSven Schnelle 		udelay(10);
1017418a119SSven Schnelle 		gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 1);
1027418a119SSven Schnelle 		udelay(10);
1037418a119SSven Schnelle 		gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0);
1047418a119SSven Schnelle 		udelay(10);
1057418a119SSven Schnelle 	}
1067418a119SSven Schnelle }
1077418a119SSven Schnelle 
1087418a119SSven Schnelle static u8 ds2404_read_byte(struct device *dev)
1097418a119SSven Schnelle {
1107418a119SSven Schnelle 	int i;
1117418a119SSven Schnelle 	u8 ret = 0;
1127418a119SSven Schnelle 
1137418a119SSven Schnelle 	gpio_direction_input(ds2404_gpio[DS2404_DQ].gpio);
1147418a119SSven Schnelle 
1157418a119SSven Schnelle 	for (i = 0; i < 8; i++) {
1167418a119SSven Schnelle 		gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0);
1177418a119SSven Schnelle 		udelay(10);
1187418a119SSven Schnelle 		if (gpio_get_value(ds2404_gpio[DS2404_DQ].gpio))
1197418a119SSven Schnelle 			ret |= 1 << i;
1207418a119SSven Schnelle 		gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 1);
1217418a119SSven Schnelle 		udelay(10);
1227418a119SSven Schnelle 	}
1237418a119SSven Schnelle 	return ret;
1247418a119SSven Schnelle }
1257418a119SSven Schnelle 
1267418a119SSven Schnelle static void ds2404_read_memory(struct device *dev, u16 offset,
1277418a119SSven Schnelle 			       int length, u8 *out)
1287418a119SSven Schnelle {
1297418a119SSven Schnelle 	ds2404_reset(dev);
1307418a119SSven Schnelle 	ds2404_write_byte(dev, DS2404_READ_MEMORY_CMD);
1317418a119SSven Schnelle 	ds2404_write_byte(dev, offset & 0xff);
1327418a119SSven Schnelle 	ds2404_write_byte(dev, (offset >> 8) & 0xff);
1337418a119SSven Schnelle 	while (length--)
1347418a119SSven Schnelle 		*out++ = ds2404_read_byte(dev);
1357418a119SSven Schnelle }
1367418a119SSven Schnelle 
1377418a119SSven Schnelle static void ds2404_write_memory(struct device *dev, u16 offset,
1387418a119SSven Schnelle 				int length, u8 *out)
1397418a119SSven Schnelle {
1407418a119SSven Schnelle 	int i;
1417418a119SSven Schnelle 	u8 ta01, ta02, es;
1427418a119SSven Schnelle 
1437418a119SSven Schnelle 	ds2404_reset(dev);
1447418a119SSven Schnelle 	ds2404_write_byte(dev, DS2404_WRITE_SCRATCHPAD_CMD);
1457418a119SSven Schnelle 	ds2404_write_byte(dev, offset & 0xff);
1467418a119SSven Schnelle 	ds2404_write_byte(dev, (offset >> 8) & 0xff);
1477418a119SSven Schnelle 
1487418a119SSven Schnelle 	for (i = 0; i < length; i++)
1497418a119SSven Schnelle 		ds2404_write_byte(dev, out[i]);
1507418a119SSven Schnelle 
1517418a119SSven Schnelle 	ds2404_reset(dev);
1527418a119SSven Schnelle 	ds2404_write_byte(dev, DS2404_READ_SCRATCHPAD_CMD);
1537418a119SSven Schnelle 
1547418a119SSven Schnelle 	ta01 = ds2404_read_byte(dev);
1557418a119SSven Schnelle 	ta02 = ds2404_read_byte(dev);
1567418a119SSven Schnelle 	es = ds2404_read_byte(dev);
1577418a119SSven Schnelle 
1587418a119SSven Schnelle 	for (i = 0; i < length; i++) {
1597418a119SSven Schnelle 		if (out[i] != ds2404_read_byte(dev)) {
1600fae8237SJingoo Han 			dev_err(dev, "read invalid data\n");
1617418a119SSven Schnelle 			return;
1627418a119SSven Schnelle 		}
1637418a119SSven Schnelle 	}
1647418a119SSven Schnelle 
1657418a119SSven Schnelle 	ds2404_reset(dev);
1667418a119SSven Schnelle 	ds2404_write_byte(dev, DS2404_COPY_SCRATCHPAD_CMD);
1677418a119SSven Schnelle 	ds2404_write_byte(dev, ta01);
1687418a119SSven Schnelle 	ds2404_write_byte(dev, ta02);
1697418a119SSven Schnelle 	ds2404_write_byte(dev, es);
1707418a119SSven Schnelle 
1717418a119SSven Schnelle 	gpio_direction_input(ds2404_gpio[DS2404_DQ].gpio);
1727418a119SSven Schnelle 	while (gpio_get_value(ds2404_gpio[DS2404_DQ].gpio))
1737418a119SSven Schnelle 		;
1747418a119SSven Schnelle }
1757418a119SSven Schnelle 
1767418a119SSven Schnelle static void ds2404_enable_osc(struct device *dev)
1777418a119SSven Schnelle {
1787418a119SSven Schnelle 	u8 in[1] = { 0x10 }; /* enable oscillator */
1797418a119SSven Schnelle 	ds2404_write_memory(dev, 0x201, 1, in);
1807418a119SSven Schnelle }
1817418a119SSven Schnelle 
1827418a119SSven Schnelle static int ds2404_read_time(struct device *dev, struct rtc_time *dt)
1837418a119SSven Schnelle {
1847418a119SSven Schnelle 	unsigned long time = 0;
1858aec4b87SNicholas Mc Guire 	__le32 hw_time = 0;
1867418a119SSven Schnelle 
1878aec4b87SNicholas Mc Guire 	ds2404_read_memory(dev, 0x203, 4, (u8 *)&hw_time);
1888aec4b87SNicholas Mc Guire 	time = le32_to_cpu(hw_time);
1897418a119SSven Schnelle 
19053523216SAlexandre Belloni 	rtc_time64_to_tm(time, dt);
19122652ba7SAlexandre Belloni 	return 0;
1927418a119SSven Schnelle }
1937418a119SSven Schnelle 
194be2b0437SAlexandre Belloni static int ds2404_set_time(struct device *dev, struct rtc_time *dt)
1957418a119SSven Schnelle {
196be2b0437SAlexandre Belloni 	u32 time = cpu_to_le32(rtc_tm_to_time64(dt));
1977418a119SSven Schnelle 	ds2404_write_memory(dev, 0x203, 4, (u8 *)&time);
1987418a119SSven Schnelle 	return 0;
1997418a119SSven Schnelle }
2007418a119SSven Schnelle 
2017418a119SSven Schnelle static const struct rtc_class_ops ds2404_rtc_ops = {
2027418a119SSven Schnelle 	.read_time	= ds2404_read_time,
203be2b0437SAlexandre Belloni 	.set_time	= ds2404_set_time,
2047418a119SSven Schnelle };
2057418a119SSven Schnelle 
2067418a119SSven Schnelle static int rtc_probe(struct platform_device *pdev)
2077418a119SSven Schnelle {
20877bf3822SJingoo Han 	struct ds2404_platform_data *pdata = dev_get_platdata(&pdev->dev);
2097418a119SSven Schnelle 	struct ds2404 *chip;
2107418a119SSven Schnelle 	int retval = -EBUSY;
2117418a119SSven Schnelle 
2122a444cf7SJingoo Han 	chip = devm_kzalloc(&pdev->dev, sizeof(struct ds2404), GFP_KERNEL);
2137418a119SSven Schnelle 	if (!chip)
2147418a119SSven Schnelle 		return -ENOMEM;
2157418a119SSven Schnelle 
21613bfa942SAlexandre Belloni 	chip->rtc = devm_rtc_allocate_device(&pdev->dev);
21713bfa942SAlexandre Belloni 	if (IS_ERR(chip->rtc))
21813bfa942SAlexandre Belloni 		return PTR_ERR(chip->rtc);
21913bfa942SAlexandre Belloni 
220c7ac260fSAlexandre Belloni 	retval = ds2404_gpio_map(chip, pdev, pdata);
2217418a119SSven Schnelle 	if (retval)
222d9aa5ca4SAlexandre Belloni 		return retval;
223d9aa5ca4SAlexandre Belloni 
224d9aa5ca4SAlexandre Belloni 	retval = devm_add_action_or_reset(&pdev->dev, ds2404_gpio_unmap, chip);
225d9aa5ca4SAlexandre Belloni 	if (retval)
226d9aa5ca4SAlexandre Belloni 		return retval;
2277418a119SSven Schnelle 
2287418a119SSven Schnelle 	dev_info(&pdev->dev, "using GPIOs RST:%d, CLK:%d, DQ:%d\n",
2297418a119SSven Schnelle 		 chip->gpio[DS2404_RST].gpio, chip->gpio[DS2404_CLK].gpio,
2307418a119SSven Schnelle 		 chip->gpio[DS2404_DQ].gpio);
2317418a119SSven Schnelle 
2327418a119SSven Schnelle 	platform_set_drvdata(pdev, chip);
2337418a119SSven Schnelle 
23413bfa942SAlexandre Belloni 	chip->rtc->ops = &ds2404_rtc_ops;
23513bfa942SAlexandre Belloni 	chip->rtc->range_max = U32_MAX;
23613bfa942SAlexandre Belloni 
237*fdcfd854SBartosz Golaszewski 	retval = devm_rtc_register_device(chip->rtc);
23813bfa942SAlexandre Belloni 	if (retval)
239d9aa5ca4SAlexandre Belloni 		return retval;
2407418a119SSven Schnelle 
2417418a119SSven Schnelle 	ds2404_enable_osc(&pdev->dev);
2427418a119SSven Schnelle 	return 0;
2437418a119SSven Schnelle }
2447418a119SSven Schnelle 
2457418a119SSven Schnelle static struct platform_driver rtc_device_driver = {
2467418a119SSven Schnelle 	.probe	= rtc_probe,
2477418a119SSven Schnelle 	.driver = {
2487418a119SSven Schnelle 		.name	= "ds2404",
2497418a119SSven Schnelle 	},
2507418a119SSven Schnelle };
25156ae1b8eSSrinivas Kandagatla module_platform_driver(rtc_device_driver);
2527418a119SSven Schnelle 
2537418a119SSven Schnelle MODULE_DESCRIPTION("DS2404 RTC");
2547418a119SSven Schnelle MODULE_AUTHOR("Sven Schnelle");
2557418a119SSven Schnelle MODULE_LICENSE("GPL");
2567418a119SSven Schnelle MODULE_ALIAS("platform:ds2404");
257