18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* drivers/rtc/rtc-v3020.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2006 8D Technologies inc. 58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Compulab Ltd. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Driver for the V3020 RTC 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Changelog: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * 10-May-2006: Raphael Assenat <raph@8d.com> 128c2ecf20Sopenharmony_ci * - Converted to platform driver 138c2ecf20Sopenharmony_ci * - Use the generic rtc class 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * ??-???-2004: Someone at Compulab 168c2ecf20Sopenharmony_ci * - Initial driver creation. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/rtc.h> 228c2ecf20Sopenharmony_ci#include <linux/types.h> 238c2ecf20Sopenharmony_ci#include <linux/bcd.h> 248c2ecf20Sopenharmony_ci#include <linux/platform_data/rtc-v3020.h> 258c2ecf20Sopenharmony_ci#include <linux/delay.h> 268c2ecf20Sopenharmony_ci#include <linux/gpio.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/io.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#undef DEBUG 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct v3020; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct v3020_chip_ops { 368c2ecf20Sopenharmony_ci int (*map_io)(struct v3020 *chip, struct platform_device *pdev, 378c2ecf20Sopenharmony_ci struct v3020_platform_data *pdata); 388c2ecf20Sopenharmony_ci void (*unmap_io)(struct v3020 *chip); 398c2ecf20Sopenharmony_ci unsigned char (*read_bit)(struct v3020 *chip); 408c2ecf20Sopenharmony_ci void (*write_bit)(struct v3020 *chip, unsigned char bit); 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define V3020_CS 0 448c2ecf20Sopenharmony_ci#define V3020_WR 1 458c2ecf20Sopenharmony_ci#define V3020_RD 2 468c2ecf20Sopenharmony_ci#define V3020_IO 3 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct v3020 { 498c2ecf20Sopenharmony_ci /* MMIO access */ 508c2ecf20Sopenharmony_ci void __iomem *ioaddress; 518c2ecf20Sopenharmony_ci int leftshift; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* GPIO access */ 548c2ecf20Sopenharmony_ci struct gpio *gpio; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci const struct v3020_chip_ops *ops; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci struct rtc_device *rtc; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int v3020_mmio_map(struct v3020 *chip, struct platform_device *pdev, 638c2ecf20Sopenharmony_ci struct v3020_platform_data *pdata) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci if (pdev->num_resources != 1) 668c2ecf20Sopenharmony_ci return -EBUSY; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (pdev->resource[0].flags != IORESOURCE_MEM) 698c2ecf20Sopenharmony_ci return -EBUSY; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci chip->leftshift = pdata->leftshift; 728c2ecf20Sopenharmony_ci chip->ioaddress = ioremap(pdev->resource[0].start, 1); 738c2ecf20Sopenharmony_ci if (chip->ioaddress == NULL) 748c2ecf20Sopenharmony_ci return -EBUSY; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return 0; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void v3020_mmio_unmap(struct v3020 *chip) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci iounmap(chip->ioaddress); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void v3020_mmio_write_bit(struct v3020 *chip, unsigned char bit) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci writel(bit << chip->leftshift, chip->ioaddress); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic unsigned char v3020_mmio_read_bit(struct v3020 *chip) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci return !!(readl(chip->ioaddress) & (1 << chip->leftshift)); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic const struct v3020_chip_ops v3020_mmio_ops = { 958c2ecf20Sopenharmony_ci .map_io = v3020_mmio_map, 968c2ecf20Sopenharmony_ci .unmap_io = v3020_mmio_unmap, 978c2ecf20Sopenharmony_ci .read_bit = v3020_mmio_read_bit, 988c2ecf20Sopenharmony_ci .write_bit = v3020_mmio_write_bit, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic struct gpio v3020_gpio[] = { 1028c2ecf20Sopenharmony_ci { 0, GPIOF_OUT_INIT_HIGH, "RTC CS"}, 1038c2ecf20Sopenharmony_ci { 0, GPIOF_OUT_INIT_HIGH, "RTC WR"}, 1048c2ecf20Sopenharmony_ci { 0, GPIOF_OUT_INIT_HIGH, "RTC RD"}, 1058c2ecf20Sopenharmony_ci { 0, GPIOF_OUT_INIT_HIGH, "RTC IO"}, 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic int v3020_gpio_map(struct v3020 *chip, struct platform_device *pdev, 1098c2ecf20Sopenharmony_ci struct v3020_platform_data *pdata) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci int err; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci v3020_gpio[V3020_CS].gpio = pdata->gpio_cs; 1148c2ecf20Sopenharmony_ci v3020_gpio[V3020_WR].gpio = pdata->gpio_wr; 1158c2ecf20Sopenharmony_ci v3020_gpio[V3020_RD].gpio = pdata->gpio_rd; 1168c2ecf20Sopenharmony_ci v3020_gpio[V3020_IO].gpio = pdata->gpio_io; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci err = gpio_request_array(v3020_gpio, ARRAY_SIZE(v3020_gpio)); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (!err) 1218c2ecf20Sopenharmony_ci chip->gpio = v3020_gpio; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return err; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void v3020_gpio_unmap(struct v3020 *chip) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci gpio_free_array(v3020_gpio, ARRAY_SIZE(v3020_gpio)); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void v3020_gpio_write_bit(struct v3020 *chip, unsigned char bit) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci gpio_direction_output(chip->gpio[V3020_IO].gpio, bit); 1348c2ecf20Sopenharmony_ci gpio_set_value(chip->gpio[V3020_CS].gpio, 0); 1358c2ecf20Sopenharmony_ci gpio_set_value(chip->gpio[V3020_WR].gpio, 0); 1368c2ecf20Sopenharmony_ci udelay(1); 1378c2ecf20Sopenharmony_ci gpio_set_value(chip->gpio[V3020_WR].gpio, 1); 1388c2ecf20Sopenharmony_ci gpio_set_value(chip->gpio[V3020_CS].gpio, 1); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic unsigned char v3020_gpio_read_bit(struct v3020 *chip) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci int bit; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci gpio_direction_input(chip->gpio[V3020_IO].gpio); 1468c2ecf20Sopenharmony_ci gpio_set_value(chip->gpio[V3020_CS].gpio, 0); 1478c2ecf20Sopenharmony_ci gpio_set_value(chip->gpio[V3020_RD].gpio, 0); 1488c2ecf20Sopenharmony_ci udelay(1); 1498c2ecf20Sopenharmony_ci bit = !!gpio_get_value(chip->gpio[V3020_IO].gpio); 1508c2ecf20Sopenharmony_ci udelay(1); 1518c2ecf20Sopenharmony_ci gpio_set_value(chip->gpio[V3020_RD].gpio, 1); 1528c2ecf20Sopenharmony_ci gpio_set_value(chip->gpio[V3020_CS].gpio, 1); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return bit; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic const struct v3020_chip_ops v3020_gpio_ops = { 1588c2ecf20Sopenharmony_ci .map_io = v3020_gpio_map, 1598c2ecf20Sopenharmony_ci .unmap_io = v3020_gpio_unmap, 1608c2ecf20Sopenharmony_ci .read_bit = v3020_gpio_read_bit, 1618c2ecf20Sopenharmony_ci .write_bit = v3020_gpio_write_bit, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void v3020_set_reg(struct v3020 *chip, unsigned char address, 1658c2ecf20Sopenharmony_ci unsigned char data) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci int i; 1688c2ecf20Sopenharmony_ci unsigned char tmp; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci tmp = address; 1718c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 1728c2ecf20Sopenharmony_ci chip->ops->write_bit(chip, (tmp & 1)); 1738c2ecf20Sopenharmony_ci tmp >>= 1; 1748c2ecf20Sopenharmony_ci udelay(1); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* Commands dont have data */ 1788c2ecf20Sopenharmony_ci if (!V3020_IS_COMMAND(address)) { 1798c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 1808c2ecf20Sopenharmony_ci chip->ops->write_bit(chip, (data & 1)); 1818c2ecf20Sopenharmony_ci data >>= 1; 1828c2ecf20Sopenharmony_ci udelay(1); 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic unsigned char v3020_get_reg(struct v3020 *chip, unsigned char address) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci unsigned int data = 0; 1908c2ecf20Sopenharmony_ci int i; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 1938c2ecf20Sopenharmony_ci chip->ops->write_bit(chip, (address & 1)); 1948c2ecf20Sopenharmony_ci address >>= 1; 1958c2ecf20Sopenharmony_ci udelay(1); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 1998c2ecf20Sopenharmony_ci data >>= 1; 2008c2ecf20Sopenharmony_ci if (chip->ops->read_bit(chip)) 2018c2ecf20Sopenharmony_ci data |= 0x80; 2028c2ecf20Sopenharmony_ci udelay(1); 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return data; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int v3020_read_time(struct device *dev, struct rtc_time *dt) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct v3020 *chip = dev_get_drvdata(dev); 2118c2ecf20Sopenharmony_ci int tmp; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* Copy the current time to ram... */ 2148c2ecf20Sopenharmony_ci v3020_set_reg(chip, V3020_CMD_CLOCK2RAM, 0); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* ...and then read constant values. */ 2178c2ecf20Sopenharmony_ci tmp = v3020_get_reg(chip, V3020_SECONDS); 2188c2ecf20Sopenharmony_ci dt->tm_sec = bcd2bin(tmp); 2198c2ecf20Sopenharmony_ci tmp = v3020_get_reg(chip, V3020_MINUTES); 2208c2ecf20Sopenharmony_ci dt->tm_min = bcd2bin(tmp); 2218c2ecf20Sopenharmony_ci tmp = v3020_get_reg(chip, V3020_HOURS); 2228c2ecf20Sopenharmony_ci dt->tm_hour = bcd2bin(tmp); 2238c2ecf20Sopenharmony_ci tmp = v3020_get_reg(chip, V3020_MONTH_DAY); 2248c2ecf20Sopenharmony_ci dt->tm_mday = bcd2bin(tmp); 2258c2ecf20Sopenharmony_ci tmp = v3020_get_reg(chip, V3020_MONTH); 2268c2ecf20Sopenharmony_ci dt->tm_mon = bcd2bin(tmp) - 1; 2278c2ecf20Sopenharmony_ci tmp = v3020_get_reg(chip, V3020_WEEK_DAY); 2288c2ecf20Sopenharmony_ci dt->tm_wday = bcd2bin(tmp); 2298c2ecf20Sopenharmony_ci tmp = v3020_get_reg(chip, V3020_YEAR); 2308c2ecf20Sopenharmony_ci dt->tm_year = bcd2bin(tmp)+100; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci dev_dbg(dev, "\n%s : Read RTC values\n", __func__); 2338c2ecf20Sopenharmony_ci dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour); 2348c2ecf20Sopenharmony_ci dev_dbg(dev, "tm_min : %i\n", dt->tm_min); 2358c2ecf20Sopenharmony_ci dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec); 2368c2ecf20Sopenharmony_ci dev_dbg(dev, "tm_year: %i\n", dt->tm_year); 2378c2ecf20Sopenharmony_ci dev_dbg(dev, "tm_mon : %i\n", dt->tm_mon); 2388c2ecf20Sopenharmony_ci dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday); 2398c2ecf20Sopenharmony_ci dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int v3020_set_time(struct device *dev, struct rtc_time *dt) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct v3020 *chip = dev_get_drvdata(dev); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci dev_dbg(dev, "\n%s : Setting RTC values\n", __func__); 2508c2ecf20Sopenharmony_ci dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec); 2518c2ecf20Sopenharmony_ci dev_dbg(dev, "tm_min : %i\n", dt->tm_min); 2528c2ecf20Sopenharmony_ci dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour); 2538c2ecf20Sopenharmony_ci dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday); 2548c2ecf20Sopenharmony_ci dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday); 2558c2ecf20Sopenharmony_ci dev_dbg(dev, "tm_year: %i\n", dt->tm_year); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* Write all the values to ram... */ 2588c2ecf20Sopenharmony_ci v3020_set_reg(chip, V3020_SECONDS, bin2bcd(dt->tm_sec)); 2598c2ecf20Sopenharmony_ci v3020_set_reg(chip, V3020_MINUTES, bin2bcd(dt->tm_min)); 2608c2ecf20Sopenharmony_ci v3020_set_reg(chip, V3020_HOURS, bin2bcd(dt->tm_hour)); 2618c2ecf20Sopenharmony_ci v3020_set_reg(chip, V3020_MONTH_DAY, bin2bcd(dt->tm_mday)); 2628c2ecf20Sopenharmony_ci v3020_set_reg(chip, V3020_MONTH, bin2bcd(dt->tm_mon + 1)); 2638c2ecf20Sopenharmony_ci v3020_set_reg(chip, V3020_WEEK_DAY, bin2bcd(dt->tm_wday)); 2648c2ecf20Sopenharmony_ci v3020_set_reg(chip, V3020_YEAR, bin2bcd(dt->tm_year % 100)); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* ...and set the clock. */ 2678c2ecf20Sopenharmony_ci v3020_set_reg(chip, V3020_CMD_RAM2CLOCK, 0); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* Compulab used this delay here. I dont know why, 2708c2ecf20Sopenharmony_ci * the datasheet does not specify a delay. */ 2718c2ecf20Sopenharmony_ci /*mdelay(5);*/ 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic const struct rtc_class_ops v3020_rtc_ops = { 2778c2ecf20Sopenharmony_ci .read_time = v3020_read_time, 2788c2ecf20Sopenharmony_ci .set_time = v3020_set_time, 2798c2ecf20Sopenharmony_ci}; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic int rtc_probe(struct platform_device *pdev) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct v3020_platform_data *pdata = dev_get_platdata(&pdev->dev); 2848c2ecf20Sopenharmony_ci struct v3020 *chip; 2858c2ecf20Sopenharmony_ci int retval = -EBUSY; 2868c2ecf20Sopenharmony_ci int i; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); 2898c2ecf20Sopenharmony_ci if (!chip) 2908c2ecf20Sopenharmony_ci return -ENOMEM; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (pdata->use_gpio) 2938c2ecf20Sopenharmony_ci chip->ops = &v3020_gpio_ops; 2948c2ecf20Sopenharmony_ci else 2958c2ecf20Sopenharmony_ci chip->ops = &v3020_mmio_ops; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci retval = chip->ops->map_io(chip, pdev, pdata); 2988c2ecf20Sopenharmony_ci if (retval) 2998c2ecf20Sopenharmony_ci return retval; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Make sure the v3020 expects a communication cycle 3028c2ecf20Sopenharmony_ci * by reading 8 times */ 3038c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 3048c2ecf20Sopenharmony_ci chip->ops->read_bit(chip); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Test chip by doing a write/read sequence 3078c2ecf20Sopenharmony_ci * to the chip ram */ 3088c2ecf20Sopenharmony_ci v3020_set_reg(chip, V3020_SECONDS, 0x33); 3098c2ecf20Sopenharmony_ci if (v3020_get_reg(chip, V3020_SECONDS) != 0x33) { 3108c2ecf20Sopenharmony_ci retval = -ENODEV; 3118c2ecf20Sopenharmony_ci goto err_io; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* Make sure frequency measurement mode, test modes, and lock 3158c2ecf20Sopenharmony_ci * are all disabled */ 3168c2ecf20Sopenharmony_ci v3020_set_reg(chip, V3020_STATUS_0, 0x0); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (pdata->use_gpio) 3198c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Chip available at GPIOs " 3208c2ecf20Sopenharmony_ci "%d, %d, %d, %d\n", 3218c2ecf20Sopenharmony_ci chip->gpio[V3020_CS].gpio, chip->gpio[V3020_WR].gpio, 3228c2ecf20Sopenharmony_ci chip->gpio[V3020_RD].gpio, chip->gpio[V3020_IO].gpio); 3238c2ecf20Sopenharmony_ci else 3248c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Chip available at " 3258c2ecf20Sopenharmony_ci "physical address 0x%llx," 3268c2ecf20Sopenharmony_ci "data connected to D%d\n", 3278c2ecf20Sopenharmony_ci (unsigned long long)pdev->resource[0].start, 3288c2ecf20Sopenharmony_ci chip->leftshift); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, chip); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci chip->rtc = devm_rtc_device_register(&pdev->dev, "v3020", 3338c2ecf20Sopenharmony_ci &v3020_rtc_ops, THIS_MODULE); 3348c2ecf20Sopenharmony_ci if (IS_ERR(chip->rtc)) { 3358c2ecf20Sopenharmony_ci retval = PTR_ERR(chip->rtc); 3368c2ecf20Sopenharmony_ci goto err_io; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cierr_io: 3428c2ecf20Sopenharmony_ci chip->ops->unmap_io(chip); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return retval; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int rtc_remove(struct platform_device *dev) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct v3020 *chip = platform_get_drvdata(dev); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci chip->ops->unmap_io(chip); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic struct platform_driver rtc_device_driver = { 3578c2ecf20Sopenharmony_ci .probe = rtc_probe, 3588c2ecf20Sopenharmony_ci .remove = rtc_remove, 3598c2ecf20Sopenharmony_ci .driver = { 3608c2ecf20Sopenharmony_ci .name = "v3020", 3618c2ecf20Sopenharmony_ci }, 3628c2ecf20Sopenharmony_ci}; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cimodule_platform_driver(rtc_device_driver); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("V3020 RTC"); 3678c2ecf20Sopenharmony_ciMODULE_AUTHOR("Raphael Assenat"); 3688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3698c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:v3020"); 370