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; 185*8aec4b87SNicholas Mc Guire __le32 hw_time = 0; 1867418a119SSven Schnelle 187*8aec4b87SNicholas Mc Guire ds2404_read_memory(dev, 0x203, 4, (u8 *)&hw_time); 188*8aec4b87SNicholas 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 23713bfa942SAlexandre Belloni retval = 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