diff options
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 31 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-bq32k.c | 62 | ||||
-rw-r--r-- | drivers/rtc/rtc-cmos.c | 5 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1307.c | 67 | ||||
-rw-r--r-- | drivers/rtc/rtc-isl12022.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-max77686.c | 140 | ||||
-rw-r--r-- | drivers/rtc/rtc-max77802.c | 502 | ||||
-rw-r--r-- | drivers/rtc/rtc-mpc5121.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf8563.c | 4 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf8583.c | 18 | ||||
-rw-r--r-- | drivers/rtc/rtc-rk808.c | 414 | ||||
-rw-r--r-- | drivers/rtc/rtc-rs5c372.c | 11 | ||||
-rw-r--r-- | drivers/rtc/rtc-s3c.c | 851 |
14 files changed, 1664 insertions, 447 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 1bea0fc43464..8cd0beebdc3f 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -288,6 +288,26 @@ config RTC_DRV_MAX77686 This driver can also be built as a module. If so, the module will be called rtc-max77686. +config RTC_DRV_RK808 + tristate "Rockchip RK808 RTC" + depends on MFD_RK808 + help + If you say yes here you will get support for the + RTC of RK808 PMIC. + + This driver can also be built as a module. If so, the module + will be called rk808-rtc. + +config RTC_DRV_MAX77802 + tristate "Maxim 77802 RTC" + depends on MFD_MAX77686 + help + If you say yes here you will get support for the + RTC of Maxim MAX77802 PMIC. + + This driver can also be built as a module. If so, the module + will be called rtc-max77802. + config RTC_DRV_RS5C372 tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A" help @@ -732,6 +752,7 @@ config RTC_DRV_DS1216 config RTC_DRV_DS1286 tristate "Dallas DS1286" + depends on HAS_IOMEM help If you say yes here you get support for the Dallas DS1286 RTC chips. @@ -743,6 +764,7 @@ config RTC_DRV_DS1302 config RTC_DRV_DS1511 tristate "Dallas DS1511" + depends on HAS_IOMEM help If you say yes here you get support for the Dallas DS1511 timekeeping/watchdog chip. @@ -752,6 +774,7 @@ config RTC_DRV_DS1511 config RTC_DRV_DS1553 tristate "Maxim/Dallas DS1553" + depends on HAS_IOMEM help If you say yes here you get support for the Maxim/Dallas DS1553 timekeeping chip. @@ -761,6 +784,7 @@ config RTC_DRV_DS1553 config RTC_DRV_DS1742 tristate "Maxim/Dallas DS1742/1743" + depends on HAS_IOMEM help If you say yes here you get support for the Maxim/Dallas DS1742/1743 timekeeping chip. @@ -816,6 +840,7 @@ config RTC_DRV_EFI config RTC_DRV_STK17TA8 tristate "Simtek STK17TA8" + depends on HAS_IOMEM help If you say yes here you get support for the Simtek STK17TA8 timekeeping chip. @@ -834,6 +859,7 @@ config RTC_DRV_M48T86 config RTC_DRV_M48T35 tristate "ST M48T35" + depends on HAS_IOMEM help If you say Y here you will get support for the ST M48T35 RTC chip. @@ -843,6 +869,7 @@ config RTC_DRV_M48T35 config RTC_DRV_M48T59 tristate "ST M48T59/M48T08/M48T02" + depends on HAS_IOMEM help If you say Y here you will get support for the ST M48T59 RTC chip and compatible ST M48T08 and M48T02. @@ -855,6 +882,7 @@ config RTC_DRV_M48T59 config RTC_DRV_MSM6242 tristate "Oki MSM6242" + depends on HAS_IOMEM help If you say yes here you get support for the Oki MSM6242 timekeeping chip. It is used in some Amiga models (e.g. A2000). @@ -864,6 +892,7 @@ config RTC_DRV_MSM6242 config RTC_DRV_BQ4802 tristate "TI BQ4802" + depends on HAS_IOMEM help If you say Y here you will get support for the TI BQ4802 RTC chip. @@ -873,6 +902,7 @@ config RTC_DRV_BQ4802 config RTC_DRV_RP5C01 tristate "Ricoh RP5C01" + depends on HAS_IOMEM help If you say yes here you get support for the Ricoh RP5C01 timekeeping chip. It is used in some Amiga models (e.g. A3000 @@ -1374,6 +1404,7 @@ config RTC_DRV_MOXART config RTC_DRV_XGENE tristate "APM X-Gene RTC" + depends on HAS_IOMEM help If you say yes here you get support for the APM X-Gene SoC real time clock. diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 9055b7dd3dc5..b188323c096a 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o obj-$(CONFIG_RTC_DRV_MAX77686) += rtc-max77686.o +obj-$(CONFIG_RTC_DRV_MAX77802) += rtc-max77802.o obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o obj-$(CONFIG_RTC_DRV_MCP795) += rtc-mcp795.o obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o @@ -109,6 +110,7 @@ obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5t583.o +obj-$(CONFIG_RTC_DRV_RK808) += rtc-rk808.o obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c index c74bf0dc52cc..314129e66d6e 100644 --- a/drivers/rtc/rtc-bq32k.c +++ b/drivers/rtc/rtc-bq32k.c @@ -2,10 +2,14 @@ * Driver for TI BQ32000 RTC. * * Copyright (C) 2009 Semihalf. + * Copyright (C) 2014 Pavel Machek <pavel@denx.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * You can get hardware description at + * http://www.ti.com/lit/ds/symlink/bq32000.pdf */ #include <linux/module.h> @@ -27,6 +31,10 @@ #define BQ32K_CENT 0x40 /* Century flag */ #define BQ32K_CENT_EN 0x80 /* Century flag enable bit */ +#define BQ32K_CALIBRATION 0x07 /* CAL_CFG1, calibration and control */ +#define BQ32K_TCH2 0x08 /* Trickle charge enable */ +#define BQ32K_CFG2 0x09 /* Trickle charger control */ + struct bq32k_regs { uint8_t seconds; uint8_t minutes; @@ -122,6 +130,57 @@ static const struct rtc_class_ops bq32k_rtc_ops = { .set_time = bq32k_rtc_set_time, }; +static int trickle_charger_of_init(struct device *dev, struct device_node *node) +{ + unsigned char reg; + int error; + u32 ohms = 0; + + if (of_property_read_u32(node, "trickle-resistor-ohms" , &ohms)) + return 0; + + switch (ohms) { + case 180+940: + /* + * TCHE[3:0] == 0x05, TCH2 == 1, TCFE == 0 (charging + * over diode and 940ohm resistor) + */ + + if (of_property_read_bool(node, "trickle-diode-disable")) { + dev_err(dev, "diode and resistor mismatch\n"); + return -EINVAL; + } + reg = 0x05; + break; + + case 180+20000: + /* diode disabled */ + + if (!of_property_read_bool(node, "trickle-diode-disable")) { + dev_err(dev, "bq32k: diode and resistor mismatch\n"); + return -EINVAL; + } + reg = 0x25; + break; + + default: + dev_err(dev, "invalid resistor value (%d)\n", ohms); + return -EINVAL; + } + + error = bq32k_write(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + reg = 0x20; + error = bq32k_write(dev, ®, BQ32K_TCH2, 1); + if (error) + return error; + + dev_info(dev, "Enabled trickle RTC battery charge.\n"); + return 0; +} + static int bq32k_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -153,6 +212,9 @@ static int bq32k_probe(struct i2c_client *client, if (error) return error; + if (client && client->dev.of_node) + trickle_charger_of_init(dev, client->dev.of_node); + rtc = devm_rtc_device_register(&client->dev, bq32k_driver.driver.name, &bq32k_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index b0e4a3eb33c7..5b2e76159b41 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -856,7 +856,7 @@ static void __exit cmos_do_remove(struct device *dev) cmos->dev = NULL; } -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM static int cmos_suspend(struct device *dev) { @@ -907,6 +907,8 @@ static inline int cmos_poweroff(struct device *dev) return cmos_suspend(dev); } +#ifdef CONFIG_PM_SLEEP + static int cmos_resume(struct device *dev) { struct cmos_rtc *cmos = dev_get_drvdata(dev); @@ -954,6 +956,7 @@ static int cmos_resume(struct device *dev) return 0; } +#endif #else static inline int cmos_poweroff(struct device *dev) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index f03d5ba96db1..bb43cf703efc 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -126,9 +126,14 @@ struct chip_desc { u16 nvram_offset; u16 nvram_size; u16 trickle_charger_reg; + u8 trickle_charger_setup; + u8 (*do_trickle_setup)(struct i2c_client *, uint32_t, bool); }; -static const struct chip_desc chips[last_ds_type] = { +static u8 do_trickle_setup_ds1339(struct i2c_client *, + uint32_t ohms, bool diode); + +static struct chip_desc chips[last_ds_type] = { [ds_1307] = { .nvram_offset = 8, .nvram_size = 56, @@ -143,6 +148,7 @@ static const struct chip_desc chips[last_ds_type] = { [ds_1339] = { .alarm = 1, .trickle_charger_reg = 0x10, + .do_trickle_setup = &do_trickle_setup_ds1339, }, [ds_1340] = { .trickle_charger_reg = 0x08, @@ -833,15 +839,58 @@ ds1307_nvram_write(struct file *filp, struct kobject *kobj, return count; } + /*----------------------------------------------------------------------*/ +static u8 do_trickle_setup_ds1339(struct i2c_client *client, + uint32_t ohms, bool diode) +{ + u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE : + DS1307_TRICKLE_CHARGER_NO_DIODE; + + switch (ohms) { + case 250: + setup |= DS1307_TRICKLE_CHARGER_250_OHM; + break; + case 2000: + setup |= DS1307_TRICKLE_CHARGER_2K_OHM; + break; + case 4000: + setup |= DS1307_TRICKLE_CHARGER_4K_OHM; + break; + default: + dev_warn(&client->dev, + "Unsupported ohm value %u in dt\n", ohms); + return 0; + } + return setup; +} + +static void ds1307_trickle_of_init(struct i2c_client *client, + struct chip_desc *chip) +{ + uint32_t ohms = 0; + bool diode = true; + + if (!chip->do_trickle_setup) + goto out; + if (of_property_read_u32(client->dev.of_node, "trickle-resistor-ohms" , &ohms)) + goto out; + if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable")) + diode = false; + chip->trickle_charger_setup = chip->do_trickle_setup(client, + ohms, diode); +out: + return; +} + static int ds1307_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ds1307 *ds1307; int err = -ENODEV; int tmp; - const struct chip_desc *chip = &chips[id->driver_data]; + struct chip_desc *chip = &chips[id->driver_data]; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); bool want_irq = false; unsigned char *buf; @@ -866,9 +915,19 @@ static int ds1307_probe(struct i2c_client *client, ds1307->client = client; ds1307->type = id->driver_data; - if (pdata && pdata->trickle_charger_setup && chip->trickle_charger_reg) + if (!pdata && client->dev.of_node) + ds1307_trickle_of_init(client, chip); + else if (pdata && pdata->trickle_charger_setup) + chip->trickle_charger_setup = pdata->trickle_charger_setup; + + if (chip->trickle_charger_setup && chip->trickle_charger_reg) { + dev_dbg(&client->dev, "writing trickle charger info 0x%x to 0x%x\n", + DS13XX_TRICKLE_CHARGER_MAGIC | chip->trickle_charger_setup, + chip->trickle_charger_reg); i2c_smbus_write_byte_data(client, chip->trickle_charger_reg, - DS13XX_TRICKLE_CHARGER_MAGIC | pdata->trickle_charger_setup); + DS13XX_TRICKLE_CHARGER_MAGIC | + chip->trickle_charger_setup); + } buf = ds1307->regs; if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index aa55f081c505..ee3ba7e6b45e 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -274,7 +274,7 @@ static int isl12022_probe(struct i2c_client *client, } #ifdef CONFIG_OF -static struct of_device_id isl12022_dt_match[] = { +static const struct of_device_id isl12022_dt_match[] = { { .compatible = "isl,isl12022" }, { }, }; diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index d20a7f0786eb..cf73e969c8cc 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -32,15 +32,6 @@ #define RTC_UDR_MASK (1 << RTC_UDR_SHIFT) #define RTC_RBUDR_SHIFT 4 #define RTC_RBUDR_MASK (1 << RTC_RBUDR_SHIFT) -/* WTSR and SMPL Register */ -#define WTSRT_SHIFT 0 -#define SMPLT_SHIFT 2 -#define WTSR_EN_SHIFT 6 -#define SMPL_EN_SHIFT 7 -#define WTSRT_MASK (3 << WTSRT_SHIFT) -#define SMPLT_MASK (3 << SMPLT_SHIFT) -#define WTSR_EN_MASK (1 << WTSR_EN_SHIFT) -#define SMPL_EN_MASK (1 << SMPL_EN_SHIFT) /* RTC Hour register */ #define HOUR_PM_SHIFT 6 #define HOUR_PM_MASK (1 << HOUR_PM_SHIFT) @@ -49,7 +40,6 @@ #define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT) #define MAX77686_RTC_UPDATE_DELAY 16 -#undef MAX77686_RTC_WTSR_SMPL enum { RTC_SEC = 0, @@ -80,16 +70,6 @@ enum MAX77686_RTC_OP { MAX77686_RTC_READ, }; -static inline int max77686_rtc_calculate_wday(u8 shifted) -{ - int counter = -1; - while (shifted) { - shifted >>= 1; - counter++; - } - return counter; -} - static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm, int rtc_24hr_mode) { @@ -103,7 +83,8 @@ static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm, tm->tm_hour += 12; } - tm->tm_wday = max77686_rtc_calculate_wday(data[RTC_WEEKDAY] & 0x7f); + /* Only a single bit is set in data[], so fls() would be equivalent */ + tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0x7f) - 1; tm->tm_mday = data[RTC_DATE] & 0x1f; tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; tm->tm_year = (data[RTC_YEAR] & 0x7f) + 100; @@ -412,64 +393,6 @@ static const struct rtc_class_ops max77686_rtc_ops = { .alarm_irq_enable = max77686_rtc_alarm_irq_enable, }; -#ifdef MAX77686_RTC_WTSR_SMPL -static void max77686_rtc_enable_wtsr(struct max77686_rtc_info *info, bool enable) -{ - int ret; - unsigned int val, mask; - - if (enable) - val = (1 << WTSR_EN_SHIFT) | (3 << WTSRT_SHIFT); - else - val = 0; - - mask = WTSR_EN_MASK | WTSRT_MASK; - - dev_info(info->dev, "%s: %s WTSR\n", __func__, - enable ? "enable" : "disable"); - - ret = regmap_update_bits(info->max77686->rtc_regmap, - MAX77686_WTSR_SMPL_CNTL, mask, val); - if (ret < 0) { - dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n", - __func__, ret); - return; - } - - max77686_rtc_update(info, MAX77686_RTC_WRITE); -} - -static void max77686_rtc_enable_smpl(struct max77686_rtc_info *info, bool enable) -{ - int ret; - unsigned int val, mask; - - if (enable) - val = (1 << SMPL_EN_SHIFT) | (0 << SMPLT_SHIFT); - else - val = 0; - - mask = SMPL_EN_MASK | SMPLT_MASK; - - dev_info(info->dev, "%s: %s SMPL\n", __func__, - enable ? "enable" : "disable"); - - ret = regmap_update_bits(info->max77686->rtc_regmap, - MAX77686_WTSR_SMPL_CNTL, mask, val); - if (ret < 0) { - dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n", - __func__, ret); - return; - } - - max77686_rtc_update(info, MAX77686_RTC_WRITE); - - val = 0; - regmap_read(info->max77686->rtc_regmap, MAX77686_WTSR_SMPL_CNTL, &val); - dev_info(info->dev, "%s: WTSR_SMPL(0x%02x)\n", __func__, val); -} -#endif /* MAX77686_RTC_WTSR_SMPL */ - static int max77686_rtc_init_reg(struct max77686_rtc_info *info) { u8 data[2]; @@ -519,19 +442,12 @@ static int max77686_rtc_probe(struct platform_device *pdev) goto err_rtc; } -#ifdef MAX77686_RTC_WTSR_SMPL - max77686_rtc_enable_wtsr(info, true); - max77686_rtc_enable_smpl(info, true); -#endif - device_init_wakeup(&pdev->dev, 1); info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max77686-rtc", &max77686_rtc_ops, THIS_MODULE); if (IS_ERR(info->rtc_dev)) { - dev_info(&pdev->dev, "%s: fail\n", __func__); - ret = PTR_ERR(info->rtc_dev); dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); if (ret == 0) @@ -539,6 +455,12 @@ static int max77686_rtc_probe(struct platform_device *pdev) goto err_rtc; } + if (!max77686->rtc_irq_data) { + ret = -EINVAL; + dev_err(&pdev->dev, "%s: no RTC regmap IRQ chip\n", __func__); + goto err_rtc; + } + info->virq = regmap_irq_get_virq(max77686->rtc_irq_data, MAX77686_RTCIRQ_RTCA1); if (!info->virq) { @@ -556,33 +478,33 @@ err_rtc: return ret; } -static void max77686_rtc_shutdown(struct platform_device *pdev) +#ifdef CONFIG_PM_SLEEP +static int max77686_rtc_suspend(struct device *dev) { -#ifdef MAX77686_RTC_WTSR_SMPL - struct max77686_rtc_info *info = platform_get_drvdata(pdev); - int i; - u8 val = 0; - - for (i = 0; i < 3; i++) { - max77686_rtc_enable_wtsr(info, false); - regmap_read(info->max77686->rtc_regmap, MAX77686_WTSR_SMPL_CNTL, &val); - dev_info(info->dev, "%s: WTSR_SMPL reg(0x%02x)\n", __func__, - val); - if (val & WTSR_EN_MASK) { - dev_emerg(info->dev, "%s: fail to disable WTSR\n", - __func__); - } else { - dev_info(info->dev, "%s: success to disable WTSR\n", - __func__); - break; - } + if (device_may_wakeup(dev)) { + struct max77686_rtc_info *info = dev_get_drvdata(dev); + + return enable_irq_wake(info->virq); } - /* Disable SMPL when power off */ - max77686_rtc_enable_smpl(info, false); -#endif /* MAX77686_RTC_WTSR_SMPL */ + return 0; } +static int max77686_rtc_resume(struct device *dev) +{ + if (device_may_wakeup(dev)) { + struct max77686_rtc_info *info = dev_get_drvdata(dev); + + return disable_irq_wake(info->virq); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max77686_rtc_pm_ops, + max77686_rtc_suspend, max77686_rtc_resume); + static const struct platform_device_id rtc_id[] = { { "max77686-rtc", 0 }, {}, @@ -592,9 +514,9 @@ static struct platform_driver max77686_rtc_driver = { .driver = { .name = "max77686-rtc", .owner = THIS_MODULE, + .pm = &max77686_rtc_pm_ops, }, .probe = max77686_rtc_probe, - .shutdown = max77686_rtc_shutdown, .id_table = rtc_id, }; diff --git a/drivers/rtc/rtc-max77802.c b/drivers/rtc/rtc-max77802.c new file mode 100644 index 000000000000..566471335b33 --- /dev/null +++ b/drivers/rtc/rtc-max77802.c @@ -0,0 +1,502 @@ +/* + * RTC driver for Maxim MAX77802 + * + * Copyright (C) 2013 Google, Inc + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * + * based on rtc-max8997.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/slab.h> +#include <linux/rtc.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mfd/max77686-private.h> +#include <linux/irqdomain.h> +#include <linux/regmap.h> + +/* RTC Control Register */ +#define BCD_EN_SHIFT 0 +#define BCD_EN_MASK (1 << BCD_EN_SHIFT) +#define MODEL24_SHIFT 1 +#define MODEL24_MASK (1 << MODEL24_SHIFT) +/* RTC Update Register1 */ +#define RTC_UDR_SHIFT 0 +#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT) +#define RTC_RBUDR_SHIFT 4 +#define RTC_RBUDR_MASK (1 << RTC_RBUDR_SHIFT) +/* RTC Hour register */ +#define HOUR_PM_SHIFT 6 +#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT) +/* RTC Alarm Enable */ +#define ALARM_ENABLE_SHIFT 7 +#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT) + +/* For the RTCAE1 register, we write this value to enable the alarm */ +#define ALARM_ENABLE_VALUE 0x77 + +#define MAX77802_RTC_UPDATE_DELAY_US 200 + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_MONTH, + RTC_YEAR, + RTC_DATE, + RTC_NR_TIME +}; + +struct max77802_rtc_info { + struct device *dev; + struct max77686_dev *max77802; + struct i2c_client *rtc; + struct rtc_device *rtc_dev; + struct mutex lock; + + struct regmap *regmap; + + int virq; + int rtc_24hr_mode; +}; + +enum MAX77802_RTC_OP { + MAX77802_RTC_WRITE, + MAX77802_RTC_READ, +}; + +static void max77802_rtc_data_to_tm(u8 *data, struct rtc_time *tm, + int rtc_24hr_mode) +{ + tm->tm_sec = data[RTC_SEC] & 0xff; + tm->tm_min = data[RTC_MIN] & 0xff; + if (rtc_24hr_mode) + tm->tm_hour = data[RTC_HOUR] & 0x1f; + else { + tm->tm_hour = data[RTC_HOUR] & 0x0f; + if (data[RTC_HOUR] & HOUR_PM_MASK) + tm->tm_hour += 12; + } + + /* Only a single bit is set in data[], so fls() would be equivalent */ + tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0xff) - 1; + tm->tm_mday = data[RTC_DATE] & 0x1f; + tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; + + tm->tm_year = data[RTC_YEAR] & 0xff; + tm->tm_yday = 0; + tm->tm_isdst = 0; +} + +static int max77802_rtc_tm_to_data(struct rtc_time *tm, u8 *data) +{ + data[RTC_SEC] = tm->tm_sec; + data[RTC_MIN] = tm->tm_min; + data[RTC_HOUR] = tm->tm_hour; + data[RTC_WEEKDAY] = 1 << tm->tm_wday; + data[RTC_DATE] = tm->tm_mday; + data[RTC_MONTH] = tm->tm_mon + 1; + data[RTC_YEAR] = tm->tm_year; + + return 0; +} + +static int max77802_rtc_update(struct max77802_rtc_info *info, + enum MAX77802_RTC_OP op) +{ + int ret; + unsigned int data; + + if (op == MAX77802_RTC_WRITE) + data = 1 << RTC_UDR_SHIFT; + else + data = 1 << RTC_RBUDR_SHIFT; + + ret = regmap_update_bits(info->max77802->regmap, + MAX77802_RTC_UPDATE0, data, data); + if (ret < 0) + dev_err(info->dev, "%s: fail to write update reg(ret=%d, data=0x%x)\n", + __func__, ret, data); + else { + /* Minimum delay required before RTC update. */ + usleep_range(MAX77802_RTC_UPDATE_DELAY_US, + MAX77802_RTC_UPDATE_DELAY_US * 2); + } + + return ret; +} + +static int max77802_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max77802_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + mutex_lock(&info->lock); + + ret = max77802_rtc_update(info, MAX77802_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(info->max77802->regmap, + MAX77802_RTC_SEC, data, RTC_NR_TIME); + if (ret < 0) { + dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__, + ret); + goto out; + } + + max77802_rtc_data_to_tm(data, tm, info->rtc_24hr_mode); + + ret = rtc_valid_tm(tm); + +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77802_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max77802_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + ret = max77802_rtc_tm_to_data(tm, data); + if (ret < 0) + return ret; + + mutex_lock(&info->lock); + + ret = regmap_bulk_write(info->max77802->regmap, + MAX77802_RTC_SEC, data, RTC_NR_TIME); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__, + ret); + goto out; + } + + ret = max77802_rtc_update(info, MAX77802_RTC_WRITE); + +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77802_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max77802_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + unsigned int val; + int ret; + + mutex_lock(&info->lock); + + ret = max77802_rtc_update(info, MAX77802_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(info->max77802->regmap, + MAX77802_ALARM1_SEC, data, RTC_NR_TIME); + if (ret < 0) { + dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n", + __func__, __LINE__, ret); + goto out; + } + + max77802_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode); + + alrm->enabled = 0; + ret = regmap_read(info->max77802->regmap, + MAX77802_RTC_AE1, &val); + if (ret < 0) { + dev_err(info->dev, "%s:%d fail to read alarm enable(%d)\n", + __func__, __LINE__, ret); + goto out; + } + if (val) + alrm->enabled = 1; + + alrm->pending = 0; + ret = regmap_read(info->max77802->regmap, MAX77802_REG_STATUS2, &val); + if (ret < 0) { + dev_err(info->dev, "%s:%d fail to read status2 reg(%d)\n", + __func__, __LINE__, ret); + goto out; + } + + if (val & (1 << 2)) /* RTCA1 */ + alrm->pending = 1; + +out: + mutex_unlock(&info->lock); + return 0; +} + +static int max77802_rtc_stop_alarm(struct max77802_rtc_info *info) +{ + int ret; + + if (!mutex_is_locked(&info->lock)) + dev_warn(info->dev, "%s: should have mutex locked\n", __func__); + + ret = max77802_rtc_update(info, MAX77802_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_write(info->max77802->regmap, + MAX77802_RTC_AE1, 0); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write alarm reg(%d)\n", + __func__, ret); + goto out; + } + + ret = max77802_rtc_update(info, MAX77802_RTC_WRITE); +out: + return ret; +} + +static int max77802_rtc_start_alarm(struct max77802_rtc_info *info) +{ + int ret; + + if (!mutex_is_locked(&info->lock)) + dev_warn(info->dev, "%s: should have mutex locked\n", + __func__); + + ret = max77802_rtc_update(info, MAX77802_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_write(info->max77802->regmap, + MAX77802_RTC_AE1, + ALARM_ENABLE_VALUE); + + if (ret < 0) { + dev_err(info->dev, "%s: fail to read alarm reg(%d)\n", + __func__, ret); + goto out; + } + + ret = max77802_rtc_update(info, MAX77802_RTC_WRITE); +out: + return ret; +} + +static int max77802_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max77802_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + ret = max77802_rtc_tm_to_data(&alrm->time, data); + if (ret < 0) + return ret; + + mutex_lock(&info->lock); + + ret = max77802_rtc_stop_alarm(info); + if (ret < 0) + goto out; + + ret = regmap_bulk_write(info->max77802->regmap, + MAX77802_ALARM1_SEC, data, RTC_NR_TIME); + + if (ret < 0) { + dev_err(info->dev, "%s: fail to write alarm reg(%d)\n", + __func__, ret); + goto out; + } + + ret = max77802_rtc_update(info, MAX77802_RTC_WRITE); + if (ret < 0) + goto out; + + if (alrm->enabled) + ret = max77802_rtc_start_alarm(info); +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77802_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct max77802_rtc_info *info = dev_get_drvdata(dev); + int ret; + + mutex_lock(&info->lock); + if (enabled) + ret = max77802_rtc_start_alarm(info); + else + ret = max77802_rtc_stop_alarm(info); + mutex_unlock(&info->lock); + + return ret; +} + +static irqreturn_t max77802_rtc_alarm_irq(int irq, void *data) +{ + struct max77802_rtc_info *info = data; + + dev_dbg(info->dev, "%s:irq(%d)\n", __func__, irq); + + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops max77802_rtc_ops = { + .read_time = max77802_rtc_read_time, + .set_time = max77802_rtc_set_time, + .read_alarm = max77802_rtc_read_alarm, + .set_alarm = max77802_rtc_set_alarm, + .alarm_irq_enable = max77802_rtc_alarm_irq_enable, +}; + +static int max77802_rtc_init_reg(struct max77802_rtc_info *info) +{ + u8 data[2]; + int ret; + + max77802_rtc_update(info, MAX77802_RTC_READ); + + /* Set RTC control register : Binary mode, 24hour mdoe */ + data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + + info->rtc_24hr_mode = 1; + + ret = regmap_bulk_write(info->max77802->regmap, + MAX77802_RTC_CONTROLM, data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write controlm reg(%d)\n", + __func__, ret); + return ret; + } + + ret = max77802_rtc_update(info, MAX77802_RTC_WRITE); + return ret; +} + +static int max77802_rtc_probe(struct platform_device *pdev) +{ + struct max77686_dev *max77802 = dev_get_drvdata(pdev->dev.parent); + struct max77802_rtc_info *info; + int ret; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + info = devm_kzalloc(&pdev->dev, sizeof(struct max77802_rtc_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + mutex_init(&info->lock); + info->dev = &pdev->dev; + info->max77802 = max77802; + info->rtc = max77802->i2c; + + platform_set_drvdata(pdev, info); + + ret = max77802_rtc_init_reg(info); + + if (ret < 0) { + dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret); + return ret; + } + + device_init_wakeup(&pdev->dev, 1); + + info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max77802-rtc", + &max77802_rtc_ops, THIS_MODULE); + + if (IS_ERR(info->rtc_dev)) { + ret = PTR_ERR(info->rtc_dev); + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); + if (ret == 0) + ret = -EINVAL; + return ret; + } + + if (!max77802->rtc_irq_data) { + dev_err(&pdev->dev, "No RTC regmap IRQ chip\n"); + return -EINVAL; + } + + info->virq = regmap_irq_get_virq(max77802->rtc_irq_data, + MAX77686_RTCIRQ_RTCA1); + + if (info->virq <= 0) { + dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n", + MAX77686_RTCIRQ_RTCA1); + return -EINVAL; + } + + ret = devm_request_threaded_irq(&pdev->dev, info->virq, NULL, + max77802_rtc_alarm_irq, 0, "rtc-alarm1", + info); + if (ret < 0) + dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", + info->virq, ret); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int max77802_rtc_suspend(struct device *dev) +{ + if (device_may_wakeup(dev)) { + struct max77802_rtc_info *info = dev_get_drvdata(dev); + + return enable_irq_wake(info->virq); + } + + return 0; +} + +static int max77802_rtc_resume(struct device *dev) +{ + if (device_may_wakeup(dev)) { + struct max77802_rtc_info *info = dev_get_drvdata(dev); + + return disable_irq_wake(info->virq); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max77802_rtc_pm_ops, + max77802_rtc_suspend, max77802_rtc_resume); + +static const struct platform_device_id rtc_id[] = { + { "max77802-rtc", 0 }, + {}, +}; + +static struct platform_driver max77802_rtc_driver = { + .driver = { + .name = "max77802-rtc", + .owner = THIS_MODULE, + .pm = &max77802_rtc_pm_ops, + }, + .probe = max77802_rtc_probe, + .id_table = rtc_id, +}; + +module_platform_driver(max77802_rtc_driver); + +MODULE_DESCRIPTION("Maxim MAX77802 RTC driver"); +MODULE_AUTHOR("Simon Glass <sjg@chromium.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index dc4f14255cc3..3b965ad6f4d5 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -401,7 +401,7 @@ static int mpc5121_rtc_remove(struct platform_device *op) } #ifdef CONFIG_OF -static struct of_device_id mpc5121_rtc_match[] = { +static const struct of_device_id mpc5121_rtc_match[] = { { .compatible = "fsl,mpc5121-rtc", }, { .compatible = "fsl,mpc5200-rtc", }, {}, diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 5a197d9dc7e7..c2ef0a22ee94 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -167,8 +167,8 @@ static irqreturn_t pcf8563_irq(int irq, void *dev_id) char pending; err = pcf8563_get_alarm_mode(pcf8563->client, NULL, &pending); - if (err < 0) - return err; + if (err) + return IRQ_NONE; if (pending) { rtc_update_irq(pcf8563->rtc, 1, RTC_IRQF | RTC_AF); diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index c2639845186b..5911a6dca291 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -176,7 +176,11 @@ static int pcf8583_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct i2c_client *client = to_i2c_client(dev); unsigned char ctrl, year[2]; - struct rtc_mem mem = { CMOS_YEAR, sizeof(year), year }; + struct rtc_mem mem = { + .loc = CMOS_YEAR, + .nr = sizeof(year), + .data = year + }; int real_year, year_offset, err; /* @@ -222,8 +226,16 @@ static int pcf8583_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct i2c_client *client = to_i2c_client(dev); unsigned char year[2], chk; - struct rtc_mem cmos_year = { CMOS_YEAR, sizeof(year), year }; - struct rtc_mem cmos_check = { CMOS_CHECKSUM, 1, &chk }; + struct rtc_mem cmos_year = { + .loc = CMOS_YEAR, + .nr = sizeof(year), + .data = year + }; + struct rtc_mem cmos_check = { + .loc = CMOS_CHECKSUM, + .nr = 1, + .data = &chk + }; unsigned int proper_year = tm->tm_year + 1900; int ret; diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk808.c new file mode 100644 index 000000000000..df42257668ac --- /dev/null +++ b/drivers/rtc/rtc-rk808.c @@ -0,0 +1,414 @@ +/* + * RTC driver for Rockchip RK808 + * + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * + * Author: Chris Zhong <zyw@rock-chips.com> + * Author: Zhang Qing <zhangqing@rock-chips.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#include <linux/mfd/rk808.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> + +/* RTC_CTRL_REG bitfields */ +#define BIT_RTC_CTRL_REG_STOP_RTC_M BIT(0) + +/* RK808 has a shadowed register for saving a "frozen" RTC time. + * When user setting "GET_TIME" to 1, the time will save in this shadowed + * register. If set "READSEL" to 1, user read rtc time register, actually + * get the time of that moment. If we need the real time, clr this bit. + */ +#define BIT_RTC_CTRL_REG_RTC_GET_TIME BIT(6) +#define BIT_RTC_CTRL_REG_RTC_READSEL_M BIT(7) +#define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M BIT(3) +#define RTC_STATUS_MASK 0xFE + +#define SECONDS_REG_MSK 0x7F +#define MINUTES_REG_MAK 0x7F +#define HOURS_REG_MSK 0x3F +#define DAYS_REG_MSK 0x3F +#define MONTHS_REG_MSK 0x1F +#define YEARS_REG_MSK 0xFF +#define WEEKS_REG_MSK 0x7 + +/* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */ + +#define NUM_TIME_REGS (RK808_WEEKS_REG - RK808_SECONDS_REG + 1) +#define NUM_ALARM_REGS (RK808_ALARM_YEARS_REG - RK808_ALARM_SECONDS_REG + 1) + +struct rk808_rtc { + struct rk808 *rk808; + struct rtc_device *rtc; + int irq; +}; + +/* Read current time and date in RTC */ +static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + struct rk808 *rk808 = rk808_rtc->rk808; + u8 rtc_data[NUM_TIME_REGS]; + int ret; + + /* Force an update of the shadowed registers right now */ + ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG, + BIT_RTC_CTRL_REG_RTC_GET_TIME, + 0); + if (ret) { + dev_err(dev, "Failed to update bits rtc_ctrl: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG, + BIT_RTC_CTRL_REG_RTC_GET_TIME, + BIT_RTC_CTRL_REG_RTC_GET_TIME); + if (ret) { + dev_err(dev, "Failed to update bits rtc_ctrl: %d\n", ret); + return ret; + } + + ret = regmap_bulk_read(rk808->regmap, RK808_SECONDS_REG, + rtc_data, NUM_TIME_REGS); + if (ret) { + dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[0] & SECONDS_REG_MSK); + tm->tm_min = bcd2bin(rtc_data[1] & MINUTES_REG_MAK); + tm->tm_hour = bcd2bin(rtc_data[2] & HOURS_REG_MSK); + tm->tm_mday = bcd2bin(rtc_data[3] & DAYS_REG_MSK); + tm->tm_mon = (bcd2bin(rtc_data[4] & MONTHS_REG_MSK)) - 1; + tm->tm_year = (bcd2bin(rtc_data[5] & YEARS_REG_MSK)) + 100; + tm->tm_wday = bcd2bin(rtc_data[6] & WEEKS_REG_MSK); + dev_dbg(dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, + tm->tm_wday, tm->tm_hour , tm->tm_min, tm->tm_sec); + + return ret; +} + +/* Set current time and date in RTC */ +static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + struct rk808 *rk808 = rk808_rtc->rk808; + u8 rtc_data[NUM_TIME_REGS]; + int ret; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + rtc_data[6] = bin2bcd(tm->tm_wday); + dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, + tm->tm_wday, tm->tm_hour , tm->tm_min, tm->tm_sec); + + /* Stop RTC while updating the RTC registers */ + ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG, + BIT_RTC_CTRL_REG_STOP_RTC_M, + BIT_RTC_CTRL_REG_STOP_RTC_M); + if (ret) { + dev_err(dev, "Failed to update RTC control: %d\n", ret); + return ret; + } + + ret = regmap_bulk_write(rk808->regmap, RK808_SECONDS_REG, + rtc_data, NUM_TIME_REGS); + if (ret) { + dev_err(dev, "Failed to bull write rtc_data: %d\n", ret); + return ret; + } + /* Start RTC again */ + ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG, + BIT_RTC_CTRL_REG_STOP_RTC_M, 0); + if (ret) { + dev_err(dev, "Failed to update RTC control: %d\n", ret); + return ret; + } + return 0; +} + +/* Read alarm time and date in RTC */ +static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + struct rk808 *rk808 = rk808_rtc->rk808; + u8 alrm_data[NUM_ALARM_REGS]; + uint32_t int_reg; + int ret; + + ret = regmap_bulk_read(rk808->regmap, RK808_ALARM_SECONDS_REG, + alrm_data, NUM_ALARM_REGS); + + alrm->time.tm_sec = bcd2bin(alrm_data[0] & SECONDS_REG_MSK); + alrm->time.tm_min = bcd2bin(alrm_data[1] & MINUTES_REG_MAK); + alrm->time.tm_hour = bcd2bin(alrm_data[2] & HOURS_REG_MSK); + alrm->time.tm_mday = bcd2bin(alrm_data[3] & DAYS_REG_MSK); + alrm->time.tm_mon = (bcd2bin(alrm_data[4] & MONTHS_REG_MSK)) - 1; + alrm->time.tm_year = (bcd2bin(alrm_data[5] & YEARS_REG_MSK)) + 100; + + ret = regmap_read(rk808->regmap, RK808_RTC_INT_REG, &int_reg); + if (ret) { + dev_err(dev, "Failed to read RTC INT REG: %d\n", ret); + return ret; + } + + dev_dbg(dev, "alrm read RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1, + alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour, + alrm->time.tm_min, alrm->time.tm_sec); + + alrm->enabled = (int_reg & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) ? 1 : 0; + + return 0; +} + +static int rk808_rtc_stop_alarm(struct rk808_rtc *rk808_rtc) +{ + struct rk808 *rk808 = rk808_rtc->rk808; + int ret; + + ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, 0); + + return ret; +} + +static int rk808_rtc_start_alarm(struct rk808_rtc *rk808_rtc) +{ + struct rk808 *rk808 = rk808_rtc->rk808; + int ret; + + ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + + return ret; +} + +static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + struct rk808 *rk808 = rk808_rtc->rk808; + u8 alrm_data[NUM_ALARM_REGS]; + int ret; + + ret = rk808_rtc_stop_alarm(rk808_rtc); + if (ret) { + dev_err(dev, "Failed to stop alarm: %d\n", ret); + return ret; + } + dev_dbg(dev, "alrm set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n", + 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1, + alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour, + alrm->time.tm_min, alrm->time.tm_sec); + + alrm_data[0] = bin2bcd(alrm->time.tm_sec); + alrm_data[1] = bin2bcd(alrm->time.tm_min); + alrm_data[2] = bin2bcd(alrm->time.tm_hour); + alrm_data[3] = bin2bcd(alrm->time.tm_mday); + alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1); + alrm_data[5] = bin2bcd(alrm->time.tm_year - 100); + + ret = regmap_bulk_write(rk808->regmap, RK808_ALARM_SECONDS_REG, + alrm_data, NUM_ALARM_REGS); + if (ret) { + dev_err(dev, "Failed to bulk write: %d\n", ret); + return ret; + } + if (alrm->enabled) { + ret = rk808_rtc_start_alarm(rk808_rtc); + if (ret) { + dev_err(dev, "Failed to start alarm: %d\n", ret); + return ret; + } + } + return 0; +} + +static int rk808_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + + if (enabled) + return rk808_rtc_start_alarm(rk808_rtc); + + return rk808_rtc_stop_alarm(rk808_rtc); +} + +/* + * We will just handle setting the frequency and make use the framework for + * reading the periodic interupts. + * + * @freq: Current periodic IRQ freq: + * bit 0: every second + * bit 1: every minute + * bit 2: every hour + * bit 3: every day + */ +static irqreturn_t rk808_alarm_irq(int irq, void *data) +{ + struct rk808_rtc *rk808_rtc = data; + struct rk808 *rk808 = rk808_rtc->rk808; + struct i2c_client *client = rk808->i2c; + int ret; + + ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG, + RTC_STATUS_MASK); + if (ret) { + dev_err(&client->dev, + "%s:Failed to update RTC status: %d\n", __func__, ret); + return ret; + } + + rtc_update_irq(rk808_rtc->rtc, 1, RTC_IRQF | RTC_AF); + dev_dbg(&client->dev, + "%s:irq=%d\n", __func__, irq); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops rk808_rtc_ops = { + .read_time = rk808_rtc_readtime, + .set_time = rk808_rtc_set_time, + .read_alarm = rk808_rtc_readalarm, + .set_alarm = rk808_rtc_setalarm, + .alarm_irq_enable = rk808_rtc_alarm_irq_enable, +}; + +#ifdef CONFIG_PM_SLEEP +/* Turn off the alarm if it should not be a wake source. */ +static int rk808_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(rk808_rtc->irq); + + return 0; +} + +/* Enable the alarm if it should be enabled (in case it was disabled to + * prevent use as a wake source). + */ +static int rk808_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(rk808_rtc->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(rk808_rtc_pm_ops, + rk808_rtc_suspend, rk808_rtc_resume); + +static int rk808_rtc_probe(struct platform_device *pdev) +{ + struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); + struct rk808_rtc *rk808_rtc; + struct rtc_time tm; + int ret; + + rk808_rtc = devm_kzalloc(&pdev->dev, sizeof(*rk808_rtc), GFP_KERNEL); + if (rk808_rtc == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, rk808_rtc); + rk808_rtc->rk808 = rk808; + + /* start rtc running by default, and use shadowed timer. */ + ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG, + BIT_RTC_CTRL_REG_STOP_RTC_M | + BIT_RTC_CTRL_REG_RTC_READSEL_M, + BIT_RTC_CTRL_REG_RTC_READSEL_M); + if (ret) { + dev_err(&pdev->dev, + "Failed to update RTC control: %d\n", ret); + return ret; + } + + ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG, + RTC_STATUS_MASK); + if (ret) { + dev_err(&pdev->dev, + "Failed to write RTC status: %d\n", ret); + return ret; + } + + /* set init time */ + ret = rk808_rtc_readtime(&pdev->dev, &tm); + if (ret) { + dev_err(&pdev->dev, "Failed to read RTC time\n"); + return ret; + } + ret = rtc_valid_tm(&tm); + if (ret) + dev_warn(&pdev->dev, "invalid date/time\n"); + + device_init_wakeup(&pdev->dev, 1); + + rk808_rtc->rtc = devm_rtc_device_register(&pdev->dev, "rk808-rtc", + &rk808_rtc_ops, THIS_MODULE); + if (IS_ERR(rk808_rtc->rtc)) { + ret = PTR_ERR(rk808_rtc->rtc); + return ret; + } + + rk808_rtc->irq = platform_get_irq(pdev, 0); + if (rk808_rtc->irq < 0) { + if (rk808_rtc->irq != -EPROBE_DEFER) + dev_err(&pdev->dev, "Wake up is not possible as irq = %d\n", + rk808_rtc->irq); + return rk808_rtc->irq; + } + + /* request alarm irq of rk808 */ + ret = devm_request_threaded_irq(&pdev->dev, rk808_rtc->irq, NULL, + rk808_alarm_irq, 0, + "RTC alarm", rk808_rtc); + if (ret) { + dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n", + rk808_rtc->irq, ret); + } + + return ret; +} + +static struct platform_driver rk808_rtc_driver = { + .probe = rk808_rtc_probe, + .driver = { + .name = "rk808-rtc", + .pm = &rk808_rtc_pm_ops, + }, +}; + +module_platform_driver(rk808_rtc_driver); + +MODULE_DESCRIPTION("RTC driver for the rk808 series PMICs"); +MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>"); +MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rk808-rtc"); diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index ccf54f06396b..28871cd7e3b5 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -142,12 +142,11 @@ static int rs5c_get_regs(struct rs5c372 *rs5c) } dev_dbg(&client->dev, - "%02x %02x %02x (%02x) %02x %02x %02x (%02x), " - "%02x %02x %02x, %02x %02x %02x; %02x %02x\n", - rs5c->regs[0], rs5c->regs[1], rs5c->regs[2], rs5c->regs[3], - rs5c->regs[4], rs5c->regs[5], rs5c->regs[6], rs5c->regs[7], - rs5c->regs[8], rs5c->regs[9], rs5c->regs[10], rs5c->regs[11], - rs5c->regs[12], rs5c->regs[13], rs5c->regs[14], rs5c->regs[15]); + "%3ph (%02x) %3ph (%02x), %3ph, %3ph; %02x %02x\n", + rs5c->regs + 0, rs5c->regs[3], + rs5c->regs + 4, rs5c->regs[7], + rs5c->regs + 8, rs5c->regs + 11, + rs5c->regs[14], rs5c->regs[15]); return 0; } diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 4958a363b2c7..a6b1252c9941 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -32,155 +32,150 @@ #include <asm/irq.h> #include "rtc-s3c.h" -enum s3c_cpu_type { - TYPE_S3C2410, - TYPE_S3C2416, - TYPE_S3C2443, - TYPE_S3C64XX, -}; +struct s3c_rtc { + struct device *dev; + struct rtc_device *rtc; -struct s3c_rtc_drv_data { - int cpu_type; -}; + void __iomem *base; + struct clk *rtc_clk; + struct clk *rtc_src_clk; + bool enabled; + + struct s3c_rtc_data *data; -/* I have yet to find an S3C implementation with more than one - * of these rtc blocks in */ + int irq_alarm; + int irq_tick; -static struct clk *rtc_clk; -static void __iomem *s3c_rtc_base; -static int s3c_rtc_alarmno; -static int s3c_rtc_tickno; -static enum s3c_cpu_type s3c_rtc_cpu_type; + spinlock_t pie_lock; + spinlock_t alarm_clk_lock; -static DEFINE_SPINLOCK(s3c_rtc_pie_lock); + int ticnt_save, ticnt_en_save; + bool wake_en; +}; + +struct s3c_rtc_data { + int max_user_freq; + bool needs_src_clk; + + void (*irq_handler) (struct s3c_rtc *info, int mask); + void (*set_freq) (struct s3c_rtc *info, int freq); + void (*enable_tick) (struct s3c_rtc *info, struct seq_file *seq); + void (*select_tick_clk) (struct s3c_rtc *info); + void (*save_tick_cnt) (struct s3c_rtc *info); + void (*restore_tick_cnt) (struct s3c_rtc *info); + void (*enable) (struct s3c_rtc *info); + void (*disable) (struct s3c_rtc *info); +}; -static void s3c_rtc_alarm_clk_enable(bool enable) +static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable) { - static DEFINE_SPINLOCK(s3c_rtc_alarm_clk_lock); - static bool alarm_clk_enabled; unsigned long irq_flags; - spin_lock_irqsave(&s3c_rtc_alarm_clk_lock, irq_flags); + spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); if (enable) { - if (!alarm_clk_enabled) { - clk_enable(rtc_clk); - alarm_clk_enabled = true; + if (!info->enabled) { + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + info->enabled = true; } } else { - if (alarm_clk_enabled) { - clk_disable(rtc_clk); - alarm_clk_enabled = false; + if (info->enabled) { + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); + info->enabled = false; } } - spin_unlock_irqrestore(&s3c_rtc_alarm_clk_lock, irq_flags); + spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); } /* IRQ Handlers */ - -static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) +static irqreturn_t s3c_rtc_tickirq(int irq, void *id) { - struct rtc_device *rdev = id; - - clk_enable(rtc_clk); - rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); - - if (s3c_rtc_cpu_type == TYPE_S3C64XX) - writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP); - - clk_disable(rtc_clk); + struct s3c_rtc *info = (struct s3c_rtc *)id; - s3c_rtc_alarm_clk_enable(false); + if (info->data->irq_handler) + info->data->irq_handler(info, S3C2410_INTP_TIC); return IRQ_HANDLED; } -static irqreturn_t s3c_rtc_tickirq(int irq, void *id) +static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) { - struct rtc_device *rdev = id; + struct s3c_rtc *info = (struct s3c_rtc *)id; - clk_enable(rtc_clk); - rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); + if (info->data->irq_handler) + info->data->irq_handler(info, S3C2410_INTP_ALM); - if (s3c_rtc_cpu_type == TYPE_S3C64XX) - writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP); - - clk_disable(rtc_clk); return IRQ_HANDLED; } /* Update control registers */ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) { + struct s3c_rtc *info = dev_get_drvdata(dev); unsigned int tmp; - dev_dbg(dev, "%s: aie=%d\n", __func__, enabled); + dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled); - clk_enable(rtc_clk); - tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; if (enabled) tmp |= S3C2410_RTCALM_ALMEN; - writeb(tmp, s3c_rtc_base + S3C2410_RTCALM); - clk_disable(rtc_clk); + writeb(tmp, info->base + S3C2410_RTCALM); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); - s3c_rtc_alarm_clk_enable(enabled); + s3c_rtc_alarm_clk_enable(info, enabled); return 0; } -static int s3c_rtc_setfreq(struct device *dev, int freq) +/* Set RTC frequency */ +static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) { - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc_dev = platform_get_drvdata(pdev); - unsigned int tmp = 0; - int val; - if (!is_power_of_2(freq)) return -EINVAL; - clk_enable(rtc_clk); - spin_lock_irq(&s3c_rtc_pie_lock); + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + spin_lock_irq(&info->pie_lock); - if (s3c_rtc_cpu_type != TYPE_S3C64XX) { - tmp = readb(s3c_rtc_base + S3C2410_TICNT); - tmp &= S3C2410_TICNT_ENABLE; - } + if (info->data->set_freq) + info->data->set_freq(info, freq); - val = (rtc_dev->max_user_freq / freq) - 1; - - if (s3c_rtc_cpu_type == TYPE_S3C2416 || s3c_rtc_cpu_type == TYPE_S3C2443) { - tmp |= S3C2443_TICNT_PART(val); - writel(S3C2443_TICNT1_PART(val), s3c_rtc_base + S3C2443_TICNT1); - - if (s3c_rtc_cpu_type == TYPE_S3C2416) - writel(S3C2416_TICNT2_PART(val), s3c_rtc_base + S3C2416_TICNT2); - } else { - tmp |= val; - } - - writel(tmp, s3c_rtc_base + S3C2410_TICNT); - spin_unlock_irq(&s3c_rtc_pie_lock); - clk_disable(rtc_clk); + spin_unlock_irq(&info->pie_lock); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); return 0; } /* Time read/write */ - static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) { + struct s3c_rtc *info = dev_get_drvdata(dev); unsigned int have_retried = 0; - void __iomem *base = s3c_rtc_base; - clk_enable(rtc_clk); + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + retry_get_time: - rtc_tm->tm_min = readb(base + S3C2410_RTCMIN); - rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR); - rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE); - rtc_tm->tm_mon = readb(base + S3C2410_RTCMON); - rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR); - rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC); + rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN); + rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR); + rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE); + rtc_tm->tm_mon = readb(info->base + S3C2410_RTCMON); + rtc_tm->tm_year = readb(info->base + S3C2410_RTCYEAR); + rtc_tm->tm_sec = readb(info->base + S3C2410_RTCSEC); /* the only way to work out whether the system was mid-update * when we read it is to check the second counter, and if it @@ -207,13 +202,16 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) rtc_tm->tm_mon -= 1; - clk_disable(rtc_clk); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); + return rtc_valid_tm(rtc_tm); } static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) { - void __iomem *base = s3c_rtc_base; + struct s3c_rtc *info = dev_get_drvdata(dev); int year = tm->tm_year - 100; dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n", @@ -227,33 +225,42 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) return -EINVAL; } - clk_enable(rtc_clk); - writeb(bin2bcd(tm->tm_sec), base + S3C2410_RTCSEC); - writeb(bin2bcd(tm->tm_min), base + S3C2410_RTCMIN); - writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR); - writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE); - writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON); - writeb(bin2bcd(year), base + S3C2410_RTCYEAR); - clk_disable(rtc_clk); + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + + writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC); + writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN); + writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_RTCHOUR); + writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_RTCDATE); + writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_RTCMON); + writeb(bin2bcd(year), info->base + S3C2410_RTCYEAR); + + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); return 0; } static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) { + struct s3c_rtc *info = dev_get_drvdata(dev); struct rtc_time *alm_tm = &alrm->time; - void __iomem *base = s3c_rtc_base; unsigned int alm_en; - clk_enable(rtc_clk); - alm_tm->tm_sec = readb(base + S3C2410_ALMSEC); - alm_tm->tm_min = readb(base + S3C2410_ALMMIN); - alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR); - alm_tm->tm_mon = readb(base + S3C2410_ALMMON); - alm_tm->tm_mday = readb(base + S3C2410_ALMDATE); - alm_tm->tm_year = readb(base + S3C2410_ALMYEAR); + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); - alm_en = readb(base + S3C2410_RTCALM); + alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC); + alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN); + alm_tm->tm_hour = readb(info->base + S3C2410_ALMHOUR); + alm_tm->tm_mon = readb(info->base + S3C2410_ALMMON); + alm_tm->tm_mday = readb(info->base + S3C2410_ALMDATE); + alm_tm->tm_year = readb(info->base + S3C2410_ALMYEAR); + + alm_en = readb(info->base + S3C2410_RTCALM); alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; @@ -297,65 +304,74 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) else alm_tm->tm_year = -1; - clk_disable(rtc_clk); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); + return 0; } static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) { + struct s3c_rtc *info = dev_get_drvdata(dev); struct rtc_time *tm = &alrm->time; - void __iomem *base = s3c_rtc_base; unsigned int alrm_en; - clk_enable(rtc_clk); + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", alrm->enabled, 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; - writeb(0x00, base + S3C2410_RTCALM); + alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; + writeb(0x00, info->base + S3C2410_RTCALM); if (tm->tm_sec < 60 && tm->tm_sec >= 0) { alrm_en |= S3C2410_RTCALM_SECEN; - writeb(bin2bcd(tm->tm_sec), base + S3C2410_ALMSEC); + writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_ALMSEC); } if (tm->tm_min < 60 && tm->tm_min >= 0) { alrm_en |= S3C2410_RTCALM_MINEN; - writeb(bin2bcd(tm->tm_min), base + S3C2410_ALMMIN); + writeb(bin2bcd(tm->tm_min), info->base + S3C2410_ALMMIN); } if (tm->tm_hour < 24 && tm->tm_hour >= 0) { alrm_en |= S3C2410_RTCALM_HOUREN; - writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR); + writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR); } dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en); - writeb(alrm_en, base + S3C2410_RTCALM); + writeb(alrm_en, info->base + S3C2410_RTCALM); s3c_rtc_setaie(dev, alrm->enabled); - clk_disable(rtc_clk); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); + return 0; } static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) { - unsigned int ticnt; + struct s3c_rtc *info = dev_get_drvdata(dev); - clk_enable(rtc_clk); - if (s3c_rtc_cpu_type == TYPE_S3C64XX) { - ticnt = readw(s3c_rtc_base + S3C2410_RTCCON); - ticnt &= S3C64XX_RTCCON_TICEN; - } else { - ticnt = readb(s3c_rtc_base + S3C2410_TICNT); - ticnt &= S3C2410_TICNT_ENABLE; - } + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + + if (info->data->enable_tick) + info->data->enable_tick(info, seq); + + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); - seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); - clk_disable(rtc_clk); return 0; } @@ -368,152 +384,199 @@ static const struct rtc_class_ops s3c_rtcops = { .alarm_irq_enable = s3c_rtc_setaie, }; -static void s3c_rtc_enable(struct platform_device *pdev, int en) +static void s3c24xx_rtc_enable(struct s3c_rtc *info) { - void __iomem *base = s3c_rtc_base; - unsigned int tmp; + unsigned int con, tmp; - if (s3c_rtc_base == NULL) - return; - - clk_enable(rtc_clk); - if (!en) { - tmp = readw(base + S3C2410_RTCCON); - if (s3c_rtc_cpu_type == TYPE_S3C64XX) - tmp &= ~S3C64XX_RTCCON_TICEN; - tmp &= ~S3C2410_RTCCON_RTCEN; - writew(tmp, base + S3C2410_RTCCON); - - if (s3c_rtc_cpu_type != TYPE_S3C64XX) { - tmp = readb(base + S3C2410_TICNT); - tmp &= ~S3C2410_TICNT_ENABLE; - writeb(tmp, base + S3C2410_TICNT); - } - } else { - /* re-enable the device, and check it is ok */ + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); - if ((readw(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) { - dev_info(&pdev->dev, "rtc disabled, re-enabling\n"); + con = readw(info->base + S3C2410_RTCCON); + /* re-enable the device, and check it is ok */ + if ((con & S3C2410_RTCCON_RTCEN) == 0) { + dev_info(info->dev, "rtc disabled, re-enabling\n"); - tmp = readw(base + S3C2410_RTCCON); - writew(tmp | S3C2410_RTCCON_RTCEN, - base + S3C2410_RTCCON); - } + tmp = readw(info->base + S3C2410_RTCCON); + writew(tmp | S3C2410_RTCCON_RTCEN, + info->base + S3C2410_RTCCON); + } - if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) { - dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n"); + if (con & S3C2410_RTCCON_CNTSEL) { + dev_info(info->dev, "removing RTCCON_CNTSEL\n"); - tmp = readw(base + S3C2410_RTCCON); - writew(tmp & ~S3C2410_RTCCON_CNTSEL, - base + S3C2410_RTCCON); - } + tmp = readw(info->base + S3C2410_RTCCON); + writew(tmp & ~S3C2410_RTCCON_CNTSEL, + info->base + S3C2410_RTCCON); + } - if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) { - dev_info(&pdev->dev, "removing RTCCON_CLKRST\n"); + if (con & S3C2410_RTCCON_CLKRST) { + dev_info(info->dev, "removing RTCCON_CLKRST\n"); - tmp = readw(base + S3C2410_RTCCON); - writew(tmp & ~S3C2410_RTCCON_CLKRST, - base + S3C2410_RTCCON); - } + tmp = readw(info->base + S3C2410_RTCCON); + writew(tmp & ~S3C2410_RTCCON_CLKRST, + info->base + S3C2410_RTCCON); } - clk_disable(rtc_clk); + + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); } -static int s3c_rtc_remove(struct platform_device *dev) +static void s3c24xx_rtc_disable(struct s3c_rtc *info) { - s3c_rtc_setaie(&dev->dev, 0); + unsigned int con; + + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + + con = readw(info->base + S3C2410_RTCCON); + con &= ~S3C2410_RTCCON_RTCEN; + writew(con, info->base + S3C2410_RTCCON); - clk_unprepare(rtc_clk); - rtc_clk = NULL; + con = readb(info->base + S3C2410_TICNT); + con &= ~S3C2410_TICNT_ENABLE; + writeb(con, info->base + S3C2410_TICNT); + + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); +} + +static void s3c6410_rtc_disable(struct s3c_rtc *info) +{ + unsigned int con; + + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + + con = readw(info->base + S3C2410_RTCCON); + con &= ~S3C64XX_RTCCON_TICEN; + con &= ~S3C2410_RTCCON_RTCEN; + writew(con, info->base + S3C2410_RTCCON); + + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); +} + +static int s3c_rtc_remove(struct platform_device *pdev) +{ + struct s3c_rtc *info = platform_get_drvdata(pdev); + + s3c_rtc_setaie(info->dev, 0); + + clk_unprepare(info->rtc_clk); + info->rtc_clk = NULL; return 0; } static const struct of_device_id s3c_rtc_dt_match[]; -static inline int s3c_rtc_get_driver_data(struct platform_device *pdev) +static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev) { -#ifdef CONFIG_OF - struct s3c_rtc_drv_data *data; - if (pdev->dev.of_node) { - const struct of_device_id *match; - match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); - data = (struct s3c_rtc_drv_data *) match->data; - return data->cpu_type; - } -#endif - return platform_get_device_id(pdev)->driver_data; + const struct of_device_id *match; + + match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); + return (struct s3c_rtc_data *)match->data; } static int s3c_rtc_probe(struct platform_device *pdev) { - struct rtc_device *rtc; + struct s3c_rtc *info = NULL; struct rtc_time rtc_tm; struct resource *res; int ret; - int tmp; - dev_dbg(&pdev->dev, "%s: probe=%p\n", __func__, pdev); + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; /* find the IRQs */ - - s3c_rtc_tickno = platform_get_irq(pdev, 1); - if (s3c_rtc_tickno < 0) { + info->irq_tick = platform_get_irq(pdev, 1); + if (info->irq_tick < 0) { dev_err(&pdev->dev, "no irq for rtc tick\n"); - return s3c_rtc_tickno; + return info->irq_tick; + } + + info->dev = &pdev->dev; + info->data = s3c_rtc_get_data(pdev); + if (!info->data) { + dev_err(&pdev->dev, "failed getting s3c_rtc_data\n"); + return -EINVAL; } + spin_lock_init(&info->pie_lock); + spin_lock_init(&info->alarm_clk_lock); + + platform_set_drvdata(pdev, info); - s3c_rtc_alarmno = platform_get_irq(pdev, 0); - if (s3c_rtc_alarmno < 0) { + info->irq_alarm = platform_get_irq(pdev, 0); + if (info->irq_alarm < 0) { dev_err(&pdev->dev, "no irq for alarm\n"); - return s3c_rtc_alarmno; + return info->irq_alarm; } dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n", - s3c_rtc_tickno, s3c_rtc_alarmno); + info->irq_tick, info->irq_alarm); /* get the memory region */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - s3c_rtc_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(s3c_rtc_base)) - return PTR_ERR(s3c_rtc_base); - - rtc_clk = devm_clk_get(&pdev->dev, "rtc"); - if (IS_ERR(rtc_clk)) { - dev_err(&pdev->dev, "failed to find rtc clock source\n"); - ret = PTR_ERR(rtc_clk); - rtc_clk = NULL; - return ret; + info->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->base)) + return PTR_ERR(info->base); + + info->rtc_clk = devm_clk_get(&pdev->dev, "rtc"); + if (IS_ERR(info->rtc_clk)) { + dev_err(&pdev->dev, "failed to find rtc clock\n"); + return PTR_ERR(info->rtc_clk); } + clk_prepare_enable(info->rtc_clk); - clk_prepare_enable(rtc_clk); + info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src"); + if (IS_ERR(info->rtc_src_clk)) { + dev_err(&pdev->dev, "failed to find rtc source clock\n"); + return PTR_ERR(info->rtc_src_clk); + } + clk_prepare_enable(info->rtc_src_clk); - /* check to see if everything is setup correctly */ - s3c_rtc_enable(pdev, 1); + /* check to see if everything is setup correctly */ + if (info->data->enable) + info->data->enable(info); dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", - readw(s3c_rtc_base + S3C2410_RTCCON)); + readw(info->base + S3C2410_RTCCON)); device_init_wakeup(&pdev->dev, 1); /* register RTC and exit */ - - rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops, + info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops, THIS_MODULE); - - if (IS_ERR(rtc)) { + if (IS_ERR(info->rtc)) { dev_err(&pdev->dev, "cannot attach rtc\n"); - ret = PTR_ERR(rtc); + ret = PTR_ERR(info->rtc); goto err_nortc; } - s3c_rtc_cpu_type = s3c_rtc_get_driver_data(pdev); + ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq, + 0, "s3c2410-rtc alarm", info); + if (ret) { + dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret); + goto err_nortc; + } - /* Check RTC Time */ + ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq, + 0, "s3c2410-rtc tick", info); + if (ret) { + dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_tick, ret); + goto err_nortc; + } - s3c_rtc_gettime(NULL, &rtc_tm); + /* Check RTC Time */ + s3c_rtc_gettime(&pdev->dev, &rtc_tm); if (rtc_valid_tm(&rtc_tm)) { rtc_tm.tm_year = 100; @@ -523,163 +586,312 @@ static int s3c_rtc_probe(struct platform_device *pdev) rtc_tm.tm_min = 0; rtc_tm.tm_sec = 0; - s3c_rtc_settime(NULL, &rtc_tm); + s3c_rtc_settime(&pdev->dev, &rtc_tm); dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); } - if (s3c_rtc_cpu_type != TYPE_S3C2410) - rtc->max_user_freq = 32768; - else - rtc->max_user_freq = 128; - - if (s3c_rtc_cpu_type == TYPE_S3C2416 || s3c_rtc_cpu_type == TYPE_S3C2443) { - tmp = readw(s3c_rtc_base + S3C2410_RTCCON); - tmp |= S3C2443_RTCCON_TICSEL; - writew(tmp, s3c_rtc_base + S3C2410_RTCCON); - } + if (info->data->select_tick_clk) + info->data->select_tick_clk(info); - platform_set_drvdata(pdev, rtc); + s3c_rtc_setfreq(info, 1); - s3c_rtc_setfreq(&pdev->dev, 1); - - ret = devm_request_irq(&pdev->dev, s3c_rtc_alarmno, s3c_rtc_alarmirq, - 0, "s3c2410-rtc alarm", rtc); - if (ret) { - dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret); - goto err_nortc; - } - - ret = devm_request_irq(&pdev->dev, s3c_rtc_tickno, s3c_rtc_tickirq, - 0, "s3c2410-rtc tick", rtc); - if (ret) { - dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret); - goto err_nortc; - } - - clk_disable(rtc_clk); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); return 0; err_nortc: - s3c_rtc_enable(pdev, 0); - clk_disable_unprepare(rtc_clk); + if (info->data->disable) + info->data->disable(info); + clk_disable_unprepare(info->rtc_clk); return ret; } #ifdef CONFIG_PM_SLEEP -/* RTC Power management control */ - -static int ticnt_save, ticnt_en_save; -static bool wake_en; static int s3c_rtc_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); + struct s3c_rtc *info = dev_get_drvdata(dev); + + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); - clk_enable(rtc_clk); /* save TICNT for anyone using periodic interrupts */ - if (s3c_rtc_cpu_type == TYPE_S3C64XX) { - ticnt_en_save = readw(s3c_rtc_base + S3C2410_RTCCON); - ticnt_en_save &= S3C64XX_RTCCON_TICEN; - ticnt_save = readl(s3c_rtc_base + S3C2410_TICNT); - } else { - ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); - } - s3c_rtc_enable(pdev, 0); + if (info->data->save_tick_cnt) + info->data->save_tick_cnt(info); + + if (info->data->disable) + info->data->disable(info); - if (device_may_wakeup(dev) && !wake_en) { - if (enable_irq_wake(s3c_rtc_alarmno) == 0) - wake_en = true; + if (device_may_wakeup(dev) && !info->wake_en) { + if (enable_irq_wake(info->irq_alarm) == 0) + info->wake_en = true; else dev_err(dev, "enable_irq_wake failed\n"); } - clk_disable(rtc_clk); + + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); return 0; } static int s3c_rtc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - unsigned int tmp; + struct s3c_rtc *info = dev_get_drvdata(dev); - clk_enable(rtc_clk); - s3c_rtc_enable(pdev, 1); - if (s3c_rtc_cpu_type == TYPE_S3C64XX) { - writel(ticnt_save, s3c_rtc_base + S3C2410_TICNT); - if (ticnt_en_save) { - tmp = readw(s3c_rtc_base + S3C2410_RTCCON); - writew(tmp | ticnt_en_save, - s3c_rtc_base + S3C2410_RTCCON); - } - } else { - writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); - } + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); - if (device_may_wakeup(dev) && wake_en) { - disable_irq_wake(s3c_rtc_alarmno); - wake_en = false; + if (info->data->enable) + info->data->enable(info); + + if (info->data->restore_tick_cnt) + info->data->restore_tick_cnt(info); + + if (device_may_wakeup(dev) && info->wake_en) { + disable_irq_wake(info->irq_alarm); + info->wake_en = false; } - clk_disable(rtc_clk); + + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); return 0; } #endif - static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); -#ifdef CONFIG_OF -static struct s3c_rtc_drv_data s3c_rtc_drv_data_array[] = { - [TYPE_S3C2410] = { TYPE_S3C2410 }, - [TYPE_S3C2416] = { TYPE_S3C2416 }, - [TYPE_S3C2443] = { TYPE_S3C2443 }, - [TYPE_S3C64XX] = { TYPE_S3C64XX }, +static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask) +{ + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); + + s3c_rtc_alarm_clk_enable(info, false); +} + +static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask) +{ + clk_enable(info->rtc_clk); + if (info->data->needs_src_clk) + clk_enable(info->rtc_src_clk); + rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); + writeb(mask, info->base + S3C2410_INTP); + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); + + s3c_rtc_alarm_clk_enable(info, false); +} + +static void s3c2410_rtc_setfreq(struct s3c_rtc *info, int freq) +{ + unsigned int tmp = 0; + int val; + + tmp = readb(info->base + S3C2410_TICNT); + tmp &= S3C2410_TICNT_ENABLE; + + val = (info->rtc->max_user_freq / freq) - 1; + tmp |= val; + + writel(tmp, info->base + S3C2410_TICNT); +} + +static void s3c2416_rtc_setfreq(struct s3c_rtc *info, int freq) +{ + unsigned int tmp = 0; + int val; + + tmp = readb(info->base + S3C2410_TICNT); + tmp &= S3C2410_TICNT_ENABLE; + + val = (info->rtc->max_user_freq / freq) - 1; + + tmp |= S3C2443_TICNT_PART(val); + writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); + + writel(S3C2416_TICNT2_PART(val), info->base + S3C2416_TICNT2); + + writel(tmp, info->base + S3C2410_TICNT); +} + +static void s3c2443_rtc_setfreq(struct s3c_rtc *info, int freq) +{ + unsigned int tmp = 0; + int val; + + tmp = readb(info->base + S3C2410_TICNT); + tmp &= S3C2410_TICNT_ENABLE; + + val = (info->rtc->max_user_freq / freq) - 1; + + tmp |= S3C2443_TICNT_PART(val); + writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); + + writel(tmp, info->base + S3C2410_TICNT); +} + +static void s3c6410_rtc_setfreq(struct s3c_rtc *info, int freq) +{ + int val; + + val = (info->rtc->max_user_freq / freq) - 1; + writel(val, info->base + S3C2410_TICNT); +} + +static void s3c24xx_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) +{ + unsigned int ticnt; + + ticnt = readb(info->base + S3C2410_TICNT); + ticnt &= S3C2410_TICNT_ENABLE; + + seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); +} + +static void s3c2416_rtc_select_tick_clk(struct s3c_rtc *info) +{ + unsigned int con; + + con = readw(info->base + S3C2410_RTCCON); + con |= S3C2443_RTCCON_TICSEL; + writew(con, info->base + S3C2410_RTCCON); +} + +static void s3c6410_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) +{ + unsigned int ticnt; + + ticnt = readw(info->base + S3C2410_RTCCON); + ticnt &= S3C64XX_RTCCON_TICEN; + + seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); +} + +static void s3c24xx_rtc_save_tick_cnt(struct s3c_rtc *info) +{ + info->ticnt_save = readb(info->base + S3C2410_TICNT); +} + +static void s3c24xx_rtc_restore_tick_cnt(struct s3c_rtc *info) +{ + writeb(info->ticnt_save, info->base + S3C2410_TICNT); +} + +static void s3c6410_rtc_save_tick_cnt(struct s3c_rtc *info) +{ + info->ticnt_en_save = readw(info->base + S3C2410_RTCCON); + info->ticnt_en_save &= S3C64XX_RTCCON_TICEN; + info->ticnt_save = readl(info->base + S3C2410_TICNT); +} + +static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info) +{ + unsigned int con; + + writel(info->ticnt_save, info->base + S3C2410_TICNT); + if (info->ticnt_en_save) { + con = readw(info->base + S3C2410_RTCCON); + writew(con | info->ticnt_en_save, + info->base + S3C2410_RTCCON); + } +} + +static struct s3c_rtc_data const s3c2410_rtc_data = { + .max_user_freq = 128, + .irq_handler = s3c24xx_rtc_irq, + .set_freq = s3c2410_rtc_setfreq, + .enable_tick = s3c24xx_rtc_enable_tick, + .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, + .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, + .enable = s3c24xx_rtc_enable, + .disable = s3c24xx_rtc_disable, +}; + +static struct s3c_rtc_data const s3c2416_rtc_data = { + .max_user_freq = 32768, + .irq_handler = s3c24xx_rtc_irq, + .set_freq = s3c2416_rtc_setfreq, + .enable_tick = s3c24xx_rtc_enable_tick, + .select_tick_clk = s3c2416_rtc_select_tick_clk, + .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, + .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, + .enable = s3c24xx_rtc_enable, + .disable = s3c24xx_rtc_disable, +}; + +static struct s3c_rtc_data const s3c2443_rtc_data = { + .max_user_freq = 32768, + .irq_handler = s3c24xx_rtc_irq, + .set_freq = s3c2443_rtc_setfreq, + .enable_tick = s3c24xx_rtc_enable_tick, + .select_tick_clk = s3c2416_rtc_select_tick_clk, + .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, + .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, + .enable = s3c24xx_rtc_enable, + .disable = s3c24xx_rtc_disable, +}; + +static struct s3c_rtc_data const s3c6410_rtc_data = { + .max_user_freq = 32768, + .irq_handler = s3c6410_rtc_irq, + .set_freq = s3c6410_rtc_setfreq, + .enable_tick = s3c6410_rtc_enable_tick, + .save_tick_cnt = s3c6410_rtc_save_tick_cnt, + .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt, + .enable = s3c24xx_rtc_enable, + .disable = s3c6410_rtc_disable, +}; + +static struct s3c_rtc_data const exynos3250_rtc_data = { + .max_user_freq = 32768, + .needs_src_clk = true, + .irq_handler = s3c6410_rtc_irq, + .set_freq = s3c6410_rtc_setfreq, + .enable_tick = s3c6410_rtc_enable_tick, + .save_tick_cnt = s3c6410_rtc_save_tick_cnt, + .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt, + .enable = s3c24xx_rtc_enable, + .disable = s3c6410_rtc_disable, }; static const struct of_device_id s3c_rtc_dt_match[] = { { .compatible = "samsung,s3c2410-rtc", - .data = &s3c_rtc_drv_data_array[TYPE_S3C2410], + .data = (void *)&s3c2410_rtc_data, }, { .compatible = "samsung,s3c2416-rtc", - .data = &s3c_rtc_drv_data_array[TYPE_S3C2416], + .data = (void *)&s3c2416_rtc_data, }, { .compatible = "samsung,s3c2443-rtc", - .data = &s3c_rtc_drv_data_array[TYPE_S3C2443], + .data = (void *)&s3c2443_rtc_data, }, { .compatible = "samsung,s3c6410-rtc", - .data = &s3c_rtc_drv_data_array[TYPE_S3C64XX], - }, - {}, -}; -MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); -#endif - -static struct platform_device_id s3c_rtc_driver_ids[] = { - { - .name = "s3c2410-rtc", - .driver_data = TYPE_S3C2410, - }, { - .name = "s3c2416-rtc", - .driver_data = TYPE_S3C2416, - }, { - .name = "s3c2443-rtc", - .driver_data = TYPE_S3C2443, + .data = (void *)&s3c6410_rtc_data, }, { - .name = "s3c64xx-rtc", - .driver_data = TYPE_S3C64XX, + .compatible = "samsung,exynos3250-rtc", + .data = (void *)&exynos3250_rtc_data, }, - { } + { /* sentinel */ }, }; - -MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids); +MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); static struct platform_driver s3c_rtc_driver = { .probe = s3c_rtc_probe, .remove = s3c_rtc_remove, - .id_table = s3c_rtc_driver_ids, .driver = { .name = "s3c-rtc", .owner = THIS_MODULE, @@ -687,7 +899,6 @@ static struct platform_driver s3c_rtc_driver = { .of_match_table = of_match_ptr(s3c_rtc_dt_match), }, }; - module_platform_driver(s3c_rtc_driver); MODULE_DESCRIPTION("Samsung S3C RTC Driver"); |