diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-08 11:31:16 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-08 11:31:16 -0700 |
commit | 3f17ea6dea8ba5668873afa54628a91aaa3fb1c0 (patch) | |
tree | afbeb2accd4c2199ddd705ae943995b143a0af02 /drivers/rtc | |
parent | 1860e379875dfe7271c649058aeddffe5afd9d0d (diff) | |
parent | 1a5700bc2d10cd379a795fd2bb377a190af5acd4 (diff) |
Merge branch 'next' (accumulated 3.16 merge window patches) into master
Now that 3.15 is released, this merges the 'next' branch into 'master',
bringing us to the normal situation where my 'master' branch is the
merge window.
* accumulated work in next: (6809 commits)
ufs: sb mutex merge + mutex_destroy
powerpc: update comments for generic idle conversion
cris: update comments for generic idle conversion
idle: remove cpu_idle() forward declarations
nbd: zero from and len fields in NBD_CMD_DISCONNECT.
mm: convert some level-less printks to pr_*
MAINTAINERS: adi-buildroot-devel is moderated
MAINTAINERS: add linux-api for review of API/ABI changes
mm/kmemleak-test.c: use pr_fmt for logging
fs/dlm/debug_fs.c: replace seq_printf by seq_puts
fs/dlm/lockspace.c: convert simple_str to kstr
fs/dlm/config.c: convert simple_str to kstr
mm: mark remap_file_pages() syscall as deprecated
mm: memcontrol: remove unnecessary memcg argument from soft limit functions
mm: memcontrol: clean up memcg zoneinfo lookup
mm/memblock.c: call kmemleak directly from memblock_(alloc|free)
mm/mempool.c: update the kmemleak stack trace for mempool allocations
lib/radix-tree.c: update the kmemleak stack trace for radix tree allocations
mm: introduce kmemleak_update_trace()
mm/kmemleak.c: use %u to print ->checksum
...
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 42 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 4 | ||||
-rw-r--r-- | drivers/rtc/interface.c | 14 | ||||
-rw-r--r-- | drivers/rtc/rtc-88pm860x.c | 3 | ||||
-rw-r--r-- | drivers/rtc/rtc-at91rm9200.c | 16 | ||||
-rw-r--r-- | drivers/rtc/rtc-bfin.c | 16 | ||||
-rw-r--r-- | drivers/rtc/rtc-cmos.c | 85 | ||||
-rw-r--r-- | drivers/rtc/rtc-da9052.c | 122 | ||||
-rw-r--r-- | drivers/rtc/rtc-da9063.c | 333 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1343.c | 689 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1742.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-efi.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-hym8563.c | 5 | ||||
-rw-r--r-- | drivers/rtc/rtc-isl12057.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-m41t80.c | 104 | ||||
-rw-r--r-- | drivers/rtc/rtc-mcp795.c | 199 | ||||
-rw-r--r-- | drivers/rtc/rtc-mv.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-omap.c | 138 | ||||
-rw-r--r-- | drivers/rtc/rtc-palmas.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-pxa.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-s5m.c | 75 | ||||
-rw-r--r-- | drivers/rtc/rtc-sa1100.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-xgene.c | 278 |
23 files changed, 1908 insertions, 229 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 2e565f8e516..71988b69eca 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -386,12 +386,12 @@ config RTC_DRV_PCF8583 will be called rtc-pcf8583. config RTC_DRV_M41T80 - tristate "ST M41T62/65/M41T80/81/82/83/84/85/87" + tristate "ST M41T62/65/M41T80/81/82/83/84/85/87 and compatible" help If you say Y here you will get support for the ST M41T60 and M41T80 RTC chips series. Currently, the following chips are supported: M41T62, M41T65, M41T80, M41T81, M41T82, M41T83, M41ST84, - M41ST85, and M41ST87. + M41ST85, M41ST87, and MicroCrystal RV4162. This driver can also be built as a module. If so, the module will be called rtc-m41t80. @@ -573,6 +573,17 @@ config RTC_DRV_DS1305 This driver can also be built as a module. If so, the module will be called rtc-ds1305. +config RTC_DRV_DS1343 + select REGMAP_SPI + tristate "Dallas/Maxim DS1343/DS1344" + help + If you say yes here you get support for the + Dallas/Maxim DS1343 and DS1344 real time clock chips. + Support for trickle charger, alarm is provided. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1343. + config RTC_DRV_DS1347 tristate "Dallas/Maxim DS1347" help @@ -650,6 +661,14 @@ config RTC_DRV_RX4581 This driver can also be built as a module. If so the module will be called rtc-rx4581. +config RTC_DRV_MCP795 + tristate "Microchip MCP795" + help + If you say yes here you will get support for the Microchip MCP795. + + This driver can also be built as a module. If so the module + will be called rtc-mcp795. + endif # SPI_MASTER comment "Platform RTC drivers" @@ -758,6 +777,16 @@ config RTC_DRV_DA9055 This driver can also be built as a module. If so, the module will be called rtc-da9055 +config RTC_DRV_DA9063 + tristate "Dialog Semiconductor DA9063 RTC" + depends on MFD_DA9063 + help + If you say yes here you will get support for the RTC subsystem + of the Dialog Semiconductor DA9063. + + This driver can also be built as a module. If so, the module + will be called "rtc-da9063". + config RTC_DRV_EFI tristate "EFI RTC" depends on IA64 @@ -1327,6 +1356,15 @@ config RTC_DRV_MOXART This driver can also be built as a module. If so, the module will be called rtc-moxart +config RTC_DRV_XGENE + tristate "APM X-Gene RTC" + help + If you say yes here you get support for the APM X-Gene SoC real time + clock. + + This driver can also be built as a module, if so, the module + will be called "rtc-xgene". + comment "HID Sensor RTC drivers" config RTC_DRV_HID_SENSOR_TIME diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 40a09915c8f..70347d041d1 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o +obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o @@ -40,6 +41,7 @@ obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o +obj-$(CONFIG_RTC_DRV_DS1343) += rtc-ds1343.o obj-$(CONFIG_RTC_DRV_DS1347) += rtc-ds1347.o obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o obj-$(CONFIG_RTC_DRV_DS1390) += rtc-ds1390.o @@ -80,6 +82,7 @@ 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_MC13XXX) += rtc-mc13xxx.o +obj-$(CONFIG_RTC_DRV_MCP795) += rtc-mcp795.o obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o @@ -135,5 +138,6 @@ obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o +obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index c2eff608236..5813fa52c3d 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -292,7 +292,8 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year"); do { alarm->time.tm_year++; - } while (rtc_valid_tm(&alarm->time) != 0); + } while (!is_leap_year(alarm->time.tm_year + 1900) + && rtc_valid_tm(&alarm->time) != 0); break; default: @@ -300,7 +301,16 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) } done: - return 0; + err = rtc_valid_tm(&alarm->time); + + if (err) { + dev_warn(&rtc->dev, "invalid alarm value: %d-%d-%d %d:%d:%d\n", + alarm->time.tm_year + 1900, alarm->time.tm_mon + 1, + alarm->time.tm_mday, alarm->time.tm_hour, alarm->time.tm_min, + alarm->time.tm_sec); + } + + return err; } int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c index 816504846cd..0c6add1a38d 100644 --- a/drivers/rtc/rtc-88pm860x.c +++ b/drivers/rtc/rtc-88pm860x.c @@ -293,7 +293,7 @@ static int pm860x_rtc_dt_init(struct platform_device *pdev, int ret; if (!np) return -ENODEV; - np = of_find_node_by_name(np, "rtc"); + np = of_get_child_by_name(np, "rtc"); if (!np) { dev_err(&pdev->dev, "failed to find rtc node\n"); return -ENODEV; @@ -301,6 +301,7 @@ static int pm860x_rtc_dt_init(struct platform_device *pdev, ret = of_property_read_u32(np, "marvell,88pm860x-vrtc", &info->vrtc); if (ret) info->vrtc = 0; + of_node_put(np); return 0; } #else diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index 3281c90691c..44fe83ee9be 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -48,6 +48,7 @@ struct at91_rtc_config { static const struct at91_rtc_config *at91_rtc_config; static DECLARE_COMPLETION(at91_rtc_updated); +static DECLARE_COMPLETION(at91_rtc_upd_rdy); static unsigned int at91_alarm_year = AT91_RTC_EPOCH; static void __iomem *at91_rtc_regs; static int irq; @@ -161,6 +162,8 @@ static int at91_rtc_settime(struct device *dev, struct rtc_time *tm) 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + wait_for_completion(&at91_rtc_upd_rdy); + /* Stop Time/Calendar from counting */ cr = at91_rtc_read(AT91_RTC_CR); at91_rtc_write(AT91_RTC_CR, cr | AT91_RTC_UPDCAL | AT91_RTC_UPDTIM); @@ -183,7 +186,9 @@ static int at91_rtc_settime(struct device *dev, struct rtc_time *tm) /* Restart Time/Calendar */ cr = at91_rtc_read(AT91_RTC_CR); + at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_SECEV); at91_rtc_write(AT91_RTC_CR, cr & ~(AT91_RTC_UPDCAL | AT91_RTC_UPDTIM)); + at91_rtc_write_ier(AT91_RTC_SECEV); return 0; } @@ -290,8 +295,10 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id) if (rtsr) { /* this interrupt is shared! Is it ours? */ if (rtsr & AT91_RTC_ALARM) events |= (RTC_AF | RTC_IRQF); - if (rtsr & AT91_RTC_SECEV) - events |= (RTC_UF | RTC_IRQF); + if (rtsr & AT91_RTC_SECEV) { + complete(&at91_rtc_upd_rdy); + at91_rtc_write_idr(AT91_RTC_SECEV); + } if (rtsr & AT91_RTC_ACKUPD) complete(&at91_rtc_updated); @@ -413,6 +420,11 @@ static int __init at91_rtc_probe(struct platform_device *pdev) return PTR_ERR(rtc); platform_set_drvdata(pdev, rtc); + /* enable SECEV interrupt in order to initialize at91_rtc_upd_rdy + * completion. + */ + at91_rtc_write_ier(AT91_RTC_SECEV); + dev_info(&pdev->dev, "AT91 Real Time Clock driver.\n"); return 0; } diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c index 0c53f452849..fe4bdb06a55 100644 --- a/drivers/rtc/rtc-bfin.c +++ b/drivers/rtc/rtc-bfin.c @@ -346,7 +346,7 @@ static int bfin_rtc_probe(struct platform_device *pdev) { struct bfin_rtc *rtc; struct device *dev = &pdev->dev; - int ret = 0; + int ret; unsigned long timeout = jiffies + HZ; dev_dbg_stamp(dev); @@ -361,16 +361,17 @@ static int bfin_rtc_probe(struct platform_device *pdev) /* Register our RTC with the RTC framework */ rtc->rtc_dev = devm_rtc_device_register(dev, pdev->name, &bfin_rtc_ops, THIS_MODULE); - if (unlikely(IS_ERR(rtc->rtc_dev))) { - ret = PTR_ERR(rtc->rtc_dev); - goto err; - } + if (unlikely(IS_ERR(rtc->rtc_dev))) + return PTR_ERR(rtc->rtc_dev); /* Grab the IRQ and init the hardware */ ret = devm_request_irq(dev, IRQ_RTC, bfin_rtc_interrupt, 0, pdev->name, dev); if (unlikely(ret)) - goto err; + dev_err(&pdev->dev, + "unable to request IRQ; alarm won't work, " + "and writes will be delayed\n"); + /* sometimes the bootloader touched things, but the write complete was not * enabled, so let's just do a quick timeout here since the IRQ will not fire ... */ @@ -381,9 +382,6 @@ static int bfin_rtc_probe(struct platform_device *pdev) bfin_write_RTC_SWCNT(0); return 0; - -err: - return ret; } static int bfin_rtc_remove(struct platform_device *pdev) diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 0963c9309c7..b0e4a3eb33c 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -647,6 +647,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) int retval = 0; unsigned char rtc_control; unsigned address_space; + u32 flags = 0; /* there can be only one ... */ if (cmos_rtc.dev) @@ -660,9 +661,12 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) * REVISIT non-x86 systems may instead use memory space resources * (needing ioremap etc), not i/o space resources like this ... */ - ports = request_region(ports->start, - resource_size(ports), - driver_name); + if (RTC_IOMAPPED) + ports = request_region(ports->start, resource_size(ports), + driver_name); + else + ports = request_mem_region(ports->start, resource_size(ports), + driver_name); if (!ports) { dev_dbg(dev, "i/o registers already in use\n"); return -EBUSY; @@ -699,6 +703,11 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) * expect CMOS_READ and friends to handle. */ if (info) { + if (info->flags) + flags = info->flags; + if (info->address_space) + address_space = info->address_space; + if (info->rtc_day_alarm && info->rtc_day_alarm < 128) cmos_rtc.day_alrm = info->rtc_day_alarm; if (info->rtc_mon_alarm && info->rtc_mon_alarm < 128) @@ -726,18 +735,21 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) spin_lock_irq(&rtc_lock); - /* force periodic irq to CMOS reset default of 1024Hz; - * - * REVISIT it's been reported that at least one x86_64 ALI mobo - * doesn't use 32KHz here ... for portability we might need to - * do something about other clock frequencies. - */ - cmos_rtc.rtc->irq_freq = 1024; - hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq); - CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT); + if (!(flags & CMOS_RTC_FLAGS_NOFREQ)) { + /* force periodic irq to CMOS reset default of 1024Hz; + * + * REVISIT it's been reported that at least one x86_64 ALI + * mobo doesn't use 32KHz here ... for portability we might + * need to do something about other clock frequencies. + */ + cmos_rtc.rtc->irq_freq = 1024; + hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq); + CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT); + } /* disable irqs */ - cmos_irq_disable(&cmos_rtc, RTC_PIE | RTC_AIE | RTC_UIE); + if (is_valid_irq(rtc_irq)) + cmos_irq_disable(&cmos_rtc, RTC_PIE | RTC_AIE | RTC_UIE); rtc_control = CMOS_READ(RTC_CONTROL); @@ -802,14 +814,18 @@ cleanup1: cmos_rtc.dev = NULL; rtc_device_unregister(cmos_rtc.rtc); cleanup0: - release_region(ports->start, resource_size(ports)); + if (RTC_IOMAPPED) + release_region(ports->start, resource_size(ports)); + else + release_mem_region(ports->start, resource_size(ports)); return retval; } -static void cmos_do_shutdown(void) +static void cmos_do_shutdown(int rtc_irq) { spin_lock_irq(&rtc_lock); - cmos_irq_disable(&cmos_rtc, RTC_IRQMASK); + if (is_valid_irq(rtc_irq)) + cmos_irq_disable(&cmos_rtc, RTC_IRQMASK); spin_unlock_irq(&rtc_lock); } @@ -818,7 +834,7 @@ static void __exit cmos_do_remove(struct device *dev) struct cmos_rtc *cmos = dev_get_drvdata(dev); struct resource *ports; - cmos_do_shutdown(); + cmos_do_shutdown(cmos->irq); sysfs_remove_bin_file(&dev->kobj, &nvram); @@ -831,7 +847,10 @@ static void __exit cmos_do_remove(struct device *dev) cmos->rtc = NULL; ports = cmos->iomem; - release_region(ports->start, resource_size(ports)); + if (RTC_IOMAPPED) + release_region(ports->start, resource_size(ports)); + else + release_mem_region(ports->start, resource_size(ports)); cmos->iomem = NULL; cmos->dev = NULL; @@ -1065,10 +1084,13 @@ static void __exit cmos_pnp_remove(struct pnp_dev *pnp) static void cmos_pnp_shutdown(struct pnp_dev *pnp) { - if (system_state == SYSTEM_POWER_OFF && !cmos_poweroff(&pnp->dev)) + struct device *dev = &pnp->dev; + struct cmos_rtc *cmos = dev_get_drvdata(dev); + + if (system_state == SYSTEM_POWER_OFF && !cmos_poweroff(dev)) return; - cmos_do_shutdown(); + cmos_do_shutdown(cmos->irq); } static const struct pnp_device_id rtc_ids[] = { @@ -1143,11 +1165,21 @@ static inline void cmos_of_init(struct platform_device *pdev) {} static int __init cmos_platform_probe(struct platform_device *pdev) { + struct resource *resource; + int irq; + cmos_of_init(pdev); cmos_wake_setup(&pdev->dev); - return cmos_do_probe(&pdev->dev, - platform_get_resource(pdev, IORESOURCE_IO, 0), - platform_get_irq(pdev, 0)); + + if (RTC_IOMAPPED) + resource = platform_get_resource(pdev, IORESOURCE_IO, 0); + else + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (irq < 0) + irq = -1; + + return cmos_do_probe(&pdev->dev, resource, irq); } static int __exit cmos_platform_remove(struct platform_device *pdev) @@ -1158,10 +1190,13 @@ static int __exit cmos_platform_remove(struct platform_device *pdev) static void cmos_platform_shutdown(struct platform_device *pdev) { - if (system_state == SYSTEM_POWER_OFF && !cmos_poweroff(&pdev->dev)) + struct device *dev = &pdev->dev; + struct cmos_rtc *cmos = dev_get_drvdata(dev); + + if (system_state == SYSTEM_POWER_OFF && !cmos_poweroff(dev)) return; - cmos_do_shutdown(); + cmos_do_shutdown(cmos->irq); } /* work with hotplug and coldplug */ diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c index a1cbf64242a..e5c9486cf45 100644 --- a/drivers/rtc/rtc-da9052.c +++ b/drivers/rtc/rtc-da9052.c @@ -20,28 +20,28 @@ #include <linux/mfd/da9052/da9052.h> #include <linux/mfd/da9052/reg.h> -#define rtc_err(da9052, fmt, ...) \ - dev_err(da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__) +#define rtc_err(rtc, fmt, ...) \ + dev_err(rtc->da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__) struct da9052_rtc { struct rtc_device *rtc; struct da9052 *da9052; }; -static int da9052_rtc_enable_alarm(struct da9052 *da9052, bool enable) +static int da9052_rtc_enable_alarm(struct da9052_rtc *rtc, bool enable) { int ret; if (enable) { - ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG, - DA9052_ALARM_Y_ALARM_ON, - DA9052_ALARM_Y_ALARM_ON); + ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG, + DA9052_ALARM_Y_ALARM_ON|DA9052_ALARM_Y_TICK_ON, + DA9052_ALARM_Y_ALARM_ON); if (ret != 0) - rtc_err(da9052, "Failed to enable ALM: %d\n", ret); + rtc_err(rtc, "Failed to enable ALM: %d\n", ret); } else { - ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG, - DA9052_ALARM_Y_ALARM_ON, 0); + ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG, + DA9052_ALARM_Y_ALARM_ON|DA9052_ALARM_Y_TICK_ON, 0); if (ret != 0) - rtc_err(da9052, "Write error: %d\n", ret); + rtc_err(rtc, "Write error: %d\n", ret); } return ret; } @@ -49,31 +49,20 @@ static int da9052_rtc_enable_alarm(struct da9052 *da9052, bool enable) static irqreturn_t da9052_rtc_irq(int irq, void *data) { struct da9052_rtc *rtc = data; - int ret; - ret = da9052_reg_read(rtc->da9052, DA9052_ALARM_MI_REG); - if (ret < 0) { - rtc_err(rtc->da9052, "Read error: %d\n", ret); - return IRQ_NONE; - } - - if (ret & DA9052_ALARMMI_ALARMTYPE) { - da9052_rtc_enable_alarm(rtc->da9052, 0); - rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); - } else - rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_PF); + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); return IRQ_HANDLED; } -static int da9052_read_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm) +static int da9052_read_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm) { int ret; uint8_t v[5]; - ret = da9052_group_read(da9052, DA9052_ALARM_MI_REG, 5, v); + ret = da9052_group_read(rtc->da9052, DA9052_ALARM_MI_REG, 5, v); if (ret != 0) { - rtc_err(da9052, "Failed to group read ALM: %d\n", ret); + rtc_err(rtc, "Failed to group read ALM: %d\n", ret); return ret; } @@ -84,23 +73,33 @@ static int da9052_read_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm) rtc_tm->tm_min = v[0] & DA9052_RTC_MIN; ret = rtc_valid_tm(rtc_tm); - if (ret != 0) - return ret; return ret; } -static int da9052_set_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm) +static int da9052_set_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm) { + struct da9052 *da9052 = rtc->da9052; + unsigned long alm_time; int ret; uint8_t v[3]; + ret = rtc_tm_to_time(rtc_tm, &alm_time); + if (ret != 0) + return ret; + + if (rtc_tm->tm_sec > 0) { + alm_time += 60 - rtc_tm->tm_sec; + rtc_time_to_tm(alm_time, rtc_tm); + } + BUG_ON(rtc_tm->tm_sec); /* it will cause repeated irqs if not zero */ + rtc_tm->tm_year -= 100; rtc_tm->tm_mon += 1; ret = da9052_reg_update(da9052, DA9052_ALARM_MI_REG, DA9052_RTC_MIN, rtc_tm->tm_min); if (ret != 0) { - rtc_err(da9052, "Failed to write ALRM MIN: %d\n", ret); + rtc_err(rtc, "Failed to write ALRM MIN: %d\n", ret); return ret; } @@ -115,22 +114,22 @@ static int da9052_set_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm) ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG, DA9052_RTC_YEAR, rtc_tm->tm_year); if (ret != 0) - rtc_err(da9052, "Failed to write ALRM YEAR: %d\n", ret); + rtc_err(rtc, "Failed to write ALRM YEAR: %d\n", ret); return ret; } -static int da9052_rtc_get_alarm_status(struct da9052 *da9052) +static int da9052_rtc_get_alarm_status(struct da9052_rtc *rtc) { int ret; - ret = da9052_reg_read(da9052, DA9052_ALARM_Y_REG); + ret = da9052_reg_read(rtc->da9052, DA9052_ALARM_Y_REG); if (ret < 0) { - rtc_err(da9052, "Failed to read ALM: %d\n", ret); + rtc_err(rtc, "Failed to read ALM: %d\n", ret); return ret; } - ret &= DA9052_ALARM_Y_ALARM_ON; - return (ret > 0) ? 1 : 0; + + return !!(ret&DA9052_ALARM_Y_ALARM_ON); } static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm) @@ -141,7 +140,7 @@ static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm) ret = da9052_group_read(rtc->da9052, DA9052_COUNT_S_REG, 6, v); if (ret < 0) { - rtc_err(rtc->da9052, "Failed to read RTC time : %d\n", ret); + rtc_err(rtc, "Failed to read RTC time : %d\n", ret); return ret; } @@ -153,18 +152,14 @@ static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm) rtc_tm->tm_sec = v[0] & DA9052_RTC_SEC; ret = rtc_valid_tm(rtc_tm); - if (ret != 0) { - rtc_err(rtc->da9052, "rtc_valid_tm failed: %d\n", ret); - return ret; - } - - return 0; + return ret; } static int da9052_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct da9052_rtc *rtc; uint8_t v[6]; + int ret; rtc = dev_get_drvdata(dev); @@ -175,7 +170,10 @@ static int da9052_rtc_set_time(struct device *dev, struct rtc_time *tm) v[4] = tm->tm_mon + 1; v[5] = tm->tm_year - 100; - return da9052_group_write(rtc->da9052, DA9052_COUNT_S_REG, 6, v); + ret = da9052_group_write(rtc->da9052, DA9052_COUNT_S_REG, 6, v); + if (ret < 0) + rtc_err(rtc, "failed to set RTC time: %d\n", ret); + return ret; } static int da9052_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) @@ -184,13 +182,13 @@ static int da9052_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) struct rtc_time *tm = &alrm->time; struct da9052_rtc *rtc = dev_get_drvdata(dev); - ret = da9052_read_alarm(rtc->da9052, tm); - - if (ret) + ret = da9052_read_alarm(rtc, tm); + if (ret < 0) { + rtc_err(rtc, "failed to read RTC alarm: %d\n", ret); return ret; + } - alrm->enabled = da9052_rtc_get_alarm_status(rtc->da9052); - + alrm->enabled = da9052_rtc_get_alarm_status(rtc); return 0; } @@ -200,16 +198,15 @@ static int da9052_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) struct rtc_time *tm = &alrm->time; struct da9052_rtc *rtc = dev_get_drvdata(dev); - ret = da9052_rtc_enable_alarm(rtc->da9052, 0); + ret = da9052_rtc_enable_alarm(rtc, 0); if (ret < 0) return ret; - ret = da9052_set_alarm(rtc->da9052, tm); - if (ret) + ret = da9052_set_alarm(rtc, tm); + if (ret < 0) return ret; - ret = da9052_rtc_enable_alarm(rtc->da9052, 1); - + ret = da9052_rtc_enable_alarm(rtc, 1); return ret; } @@ -217,7 +214,7 @@ static int da9052_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct da9052_rtc *rtc = dev_get_drvdata(dev); - return da9052_rtc_enable_alarm(rtc->da9052, enabled); + return da9052_rtc_enable_alarm(rtc, enabled); } static const struct rtc_class_ops da9052_rtc_ops = { @@ -239,10 +236,23 @@ static int da9052_rtc_probe(struct platform_device *pdev) rtc->da9052 = dev_get_drvdata(pdev->dev.parent); platform_set_drvdata(pdev, rtc); + + ret = da9052_reg_write(rtc->da9052, DA9052_BBAT_CONT_REG, 0xFE); + if (ret < 0) { + rtc_err(rtc, + "Failed to setup RTC battery charging: %d\n", ret); + return ret; + } + + ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG, + DA9052_ALARM_Y_TICK_ON, 0); + if (ret != 0) + rtc_err(rtc, "Failed to disable TICKS: %d\n", ret); + ret = da9052_request_irq(rtc->da9052, DA9052_IRQ_ALARM, "ALM", da9052_rtc_irq, rtc); if (ret != 0) { - rtc_err(rtc->da9052, "irq registration failed: %d\n", ret); + rtc_err(rtc, "irq registration failed: %d\n", ret); return ret; } @@ -261,7 +271,7 @@ static struct platform_driver da9052_rtc_driver = { module_platform_driver(da9052_rtc_driver); -MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); +MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>"); MODULE_DESCRIPTION("RTC driver for Dialog DA9052 PMIC"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:da9052-rtc"); diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c new file mode 100644 index 00000000000..595393098b0 --- /dev/null +++ b/drivers/rtc/rtc-da9063.c @@ -0,0 +1,333 @@ +/* rtc-da9063.c - Real time clock device driver for DA9063 + * Copyright (C) 2013-14 Dialog Semiconductor Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/rtc.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/regmap.h> +#include <linux/mfd/da9063/registers.h> +#include <linux/mfd/da9063/core.h> + +#define YEARS_TO_DA9063(year) ((year) - 100) +#define MONTHS_TO_DA9063(month) ((month) + 1) +#define YEARS_FROM_DA9063(year) ((year) + 100) +#define MONTHS_FROM_DA9063(month) ((month) - 1) + +#define RTC_DATA_LEN (DA9063_REG_COUNT_Y - DA9063_REG_COUNT_S + 1) +#define RTC_SEC 0 +#define RTC_MIN 1 +#define RTC_HOUR 2 +#define RTC_DAY 3 +#define RTC_MONTH 4 +#define RTC_YEAR 5 + +struct da9063_rtc { + struct rtc_device *rtc_dev; + struct da9063 *hw; + struct rtc_time alarm_time; + bool rtc_sync; +}; + +static void da9063_data_to_tm(u8 *data, struct rtc_time *tm) +{ + tm->tm_sec = data[RTC_SEC] & DA9063_COUNT_SEC_MASK; + tm->tm_min = data[RTC_MIN] & DA9063_COUNT_MIN_MASK; + tm->tm_hour = data[RTC_HOUR] & DA9063_COUNT_HOUR_MASK; + tm->tm_mday = data[RTC_DAY] & DA9063_COUNT_DAY_MASK; + tm->tm_mon = MONTHS_FROM_DA9063(data[RTC_MONTH] & + DA9063_COUNT_MONTH_MASK); + tm->tm_year = YEARS_FROM_DA9063(data[RTC_YEAR] & + DA9063_COUNT_YEAR_MASK); +} + +static void da9063_tm_to_data(struct rtc_time *tm, u8 *data) +{ + data[RTC_SEC] &= ~DA9063_COUNT_SEC_MASK; + data[RTC_SEC] |= tm->tm_sec & DA9063_COUNT_SEC_MASK; + + data[RTC_MIN] &= ~DA9063_COUNT_MIN_MASK; + data[RTC_MIN] |= tm->tm_min & DA9063_COUNT_MIN_MASK; + + data[RTC_HOUR] &= ~DA9063_COUNT_HOUR_MASK; + data[RTC_HOUR] |= tm->tm_hour & DA9063_COUNT_HOUR_MASK; + + data[RTC_DAY] &= ~DA9063_COUNT_DAY_MASK; + data[RTC_DAY] |= tm->tm_mday & DA9063_COUNT_DAY_MASK; + + data[RTC_MONTH] &= ~DA9063_COUNT_MONTH_MASK; + data[RTC_MONTH] |= MONTHS_TO_DA9063(tm->tm_mon) & + DA9063_COUNT_MONTH_MASK; + + data[RTC_YEAR] &= ~DA9063_COUNT_YEAR_MASK; + data[RTC_YEAR] |= YEARS_TO_DA9063(tm->tm_year) & + DA9063_COUNT_YEAR_MASK; +} + +static int da9063_rtc_stop_alarm(struct device *dev) +{ + struct da9063_rtc *rtc = dev_get_drvdata(dev); + + return regmap_update_bits(rtc->hw->regmap, DA9063_REG_ALARM_Y, + DA9063_ALARM_ON, 0); +} + +static int da9063_rtc_start_alarm(struct device *dev) +{ + struct da9063_rtc *rtc = dev_get_drvdata(dev); + + return regmap_update_bits(rtc->hw->regmap, DA9063_REG_ALARM_Y, + DA9063_ALARM_ON, DA9063_ALARM_ON); +} + +static int da9063_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct da9063_rtc *rtc = dev_get_drvdata(dev); + unsigned long tm_secs; + unsigned long al_secs; + u8 data[RTC_DATA_LEN]; + int ret; + + ret = regmap_bulk_read(rtc->hw->regmap, DA9063_REG_COUNT_S, + data, RTC_DATA_LEN); + if (ret < 0) { + dev_err(dev, "Failed to read RTC time data: %d\n", ret); + return ret; + } + + if (!(data[RTC_SEC] & DA9063_RTC_READ)) { + dev_dbg(dev, "RTC not yet ready to be read by the host\n"); + return -EINVAL; + } + + da9063_data_to_tm(data, tm); + + rtc_tm_to_time(tm, &tm_secs); + rtc_tm_to_time(&rtc->alarm_time, &al_secs); + + /* handle the rtc synchronisation delay */ + if (rtc->rtc_sync == true && al_secs - tm_secs == 1) + memcpy(tm, &rtc->alarm_time, sizeof(struct rtc_time)); + else + rtc->rtc_sync = false; + + return rtc_valid_tm(tm); +} + +static int da9063_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct da9063_rtc *rtc = dev_get_drvdata(dev); + u8 data[RTC_DATA_LEN]; + int ret; + + da9063_tm_to_data(tm, data); + ret = regmap_bulk_write(rtc->hw->regmap, DA9063_REG_COUNT_S, + data, RTC_DATA_LEN); + if (ret < 0) + dev_err(dev, "Failed to set RTC time data: %d\n", ret); + + return ret; +} + +static int da9063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct da9063_rtc *rtc = dev_get_drvdata(dev); + u8 data[RTC_DATA_LEN]; + int ret; + unsigned int val; + + ret = regmap_bulk_read(rtc->hw->regmap, DA9063_REG_ALARM_S, + &data[RTC_SEC], RTC_DATA_LEN); + if (ret < 0) + return ret; + + da9063_data_to_tm(data, &alrm->time); + + alrm->enabled = !!(data[RTC_YEAR] & DA9063_ALARM_ON); + + ret = regmap_read(rtc->hw->regmap, DA9063_REG_EVENT_A, &val); + if (ret < 0) + return ret; + + if (val & (DA9063_E_ALARM)) + alrm->pending = 1; + else + alrm->pending = 0; + + return 0; +} + +static int da9063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct da9063_rtc *rtc = dev_get_drvdata(dev); + u8 data[RTC_DATA_LEN]; + int ret; + + da9063_tm_to_data(&alrm->time, data); + + ret = da9063_rtc_stop_alarm(dev); + if (ret < 0) { + dev_err(dev, "Failed to stop alarm: %d\n", ret); + return ret; + } + + ret = regmap_bulk_write(rtc->hw->regmap, DA9063_REG_ALARM_S, + data, RTC_DATA_LEN); + if (ret < 0) { + dev_err(dev, "Failed to write alarm: %d\n", ret); + return ret; + } + + rtc->alarm_time = alrm->time; + + if (alrm->enabled) { + ret = da9063_rtc_start_alarm(dev); + if (ret < 0) { + dev_err(dev, "Failed to start alarm: %d\n", ret); + return ret; + } + } + + return ret; +} + +static int da9063_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + if (enabled) + return da9063_rtc_start_alarm(dev); + else + return da9063_rtc_stop_alarm(dev); +} + +static irqreturn_t da9063_alarm_event(int irq, void *data) +{ + struct da9063_rtc *rtc = data; + + regmap_update_bits(rtc->hw->regmap, DA9063_REG_ALARM_Y, + DA9063_ALARM_ON, 0); + + rtc->rtc_sync = true; + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops da9063_rtc_ops = { + .read_time = da9063_rtc_read_time, + .set_time = da9063_rtc_set_time, + .read_alarm = da9063_rtc_read_alarm, + .set_alarm = da9063_rtc_set_alarm, + .alarm_irq_enable = da9063_rtc_alarm_irq_enable, +}; + +static int da9063_rtc_probe(struct platform_device *pdev) +{ + struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent); + struct da9063_rtc *rtc; + int irq_alarm; + u8 data[RTC_DATA_LEN]; + int ret; + + ret = regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_E, + DA9063_RTC_EN, DA9063_RTC_EN); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable RTC\n"); + goto err; + } + + ret = regmap_update_bits(da9063->regmap, DA9063_REG_EN_32K, + DA9063_CRYSTAL, DA9063_CRYSTAL); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to run 32kHz oscillator\n"); + goto err; + } + + ret = regmap_update_bits(da9063->regmap, DA9063_REG_ALARM_S, + DA9063_ALARM_STATUS_TICK | DA9063_ALARM_STATUS_ALARM, + 0); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to access RTC alarm register\n"); + goto err; + } + + ret = regmap_update_bits(da9063->regmap, DA9063_REG_ALARM_S, + DA9063_ALARM_STATUS_ALARM, + DA9063_ALARM_STATUS_ALARM); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to access RTC alarm register\n"); + goto err; + } + + ret = regmap_update_bits(da9063->regmap, DA9063_REG_ALARM_Y, + DA9063_TICK_ON, 0); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to disable TICKs\n"); + goto err; + } + + ret = regmap_bulk_read(da9063->regmap, DA9063_REG_ALARM_S, + data, RTC_DATA_LEN); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read initial alarm data: %d\n", + ret); + goto err; + } + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + platform_set_drvdata(pdev, rtc); + + irq_alarm = platform_get_irq_byname(pdev, "ALARM"); + ret = devm_request_threaded_irq(&pdev->dev, irq_alarm, NULL, + da9063_alarm_event, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "ALARM", rtc); + if (ret) { + dev_err(&pdev->dev, "Failed to request ALARM IRQ %d: %d\n", + irq_alarm, ret); + goto err; + } + + rtc->hw = da9063; + rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, DA9063_DRVNAME_RTC, + &da9063_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + da9063_data_to_tm(data, &rtc->alarm_time); + rtc->rtc_sync = false; +err: + return ret; +} + +static struct platform_driver da9063_rtc_driver = { + .probe = da9063_rtc_probe, + .driver = { + .name = DA9063_DRVNAME_RTC, + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(da9063_rtc_driver); + +MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); +MODULE_DESCRIPTION("Real time clock device driver for Dialog DA9063"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DA9063_DRVNAME_RTC); diff --git a/drivers/rtc/rtc-ds1343.c b/drivers/rtc/rtc-ds1343.c new file mode 100644 index 00000000000..c3719189dd9 --- /dev/null +++ b/drivers/rtc/rtc-ds1343.c @@ -0,0 +1,689 @@ +/* rtc-ds1343.c + * + * Driver for Dallas Semiconductor DS1343 Low Current, SPI Compatible + * Real Time Clock + * + * Author : Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com> + * + * 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. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/regmap.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#include <linux/pm.h> +#include <linux/slab.h> + +#define DS1343_DRV_VERSION "01.00" +#define DALLAS_MAXIM_DS1343 0 +#define DALLAS_MAXIM_DS1344 1 + +/* RTC DS1343 Registers */ +#define DS1343_SECONDS_REG 0x00 +#define DS1343_MINUTES_REG 0x01 +#define DS1343_HOURS_REG 0x02 +#define DS1343_DAY_REG 0x03 +#define DS1343_DATE_REG 0x04 +#define DS1343_MONTH_REG 0x05 +#define DS1343_YEAR_REG 0x06 +#define DS1343_ALM0_SEC_REG 0x07 +#define DS1343_ALM0_MIN_REG 0x08 +#define DS1343_ALM0_HOUR_REG 0x09 +#define DS1343_ALM0_DAY_REG 0x0A +#define DS1343_ALM1_SEC_REG 0x0B +#define DS1343_ALM1_MIN_REG 0x0C +#define DS1343_ALM1_HOUR_REG 0x0D +#define DS1343_ALM1_DAY_REG 0x0E +#define DS1343_CONTROL_REG 0x0F +#define DS1343_STATUS_REG 0x10 +#define DS1343_TRICKLE_REG 0x11 + +/* DS1343 Control Registers bits */ +#define DS1343_EOSC 0x80 +#define DS1343_DOSF 0x20 +#define DS1343_EGFIL 0x10 +#define DS1343_SQW 0x08 +#define DS1343_INTCN 0x04 +#define DS1343_A1IE 0x02 +#define DS1343_A0IE 0x01 + +/* DS1343 Status Registers bits */ +#define DS1343_OSF 0x80 +#define DS1343_IRQF1 0x02 +#define DS1343_IRQF0 0x01 + +/* DS1343 Trickle Charger Registers bits */ +#define DS1343_TRICKLE_MAGIC 0xa0 +#define DS1343_TRICKLE_DS1 0x08 +#define DS1343_TRICKLE_1K 0x01 +#define DS1343_TRICKLE_2K 0x02 +#define DS1343_TRICKLE_4K 0x03 + +static const struct spi_device_id ds1343_id[] = { + { "ds1343", DALLAS_MAXIM_DS1343 }, + { "ds1344", DALLAS_MAXIM_DS1344 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ds1343_id); + +struct ds1343_priv { + struct spi_device *spi; + struct rtc_device *rtc; + struct regmap *map; + struct mutex mutex; + unsigned int irqen; + int irq; + int alarm_sec; + int alarm_min; + int alarm_hour; + int alarm_mday; +}; + +static int ds1343_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { +#ifdef RTC_SET_CHARGE + case RTC_SET_CHARGE: + { + int val; + + if (copy_from_user(&val, (int __user *)arg, sizeof(int))) + return -EFAULT; + + return regmap_write(priv->map, DS1343_TRICKLE_REG, val); + } + break; +#endif + } + + return -ENOIOCTLCMD; +} + +static ssize_t ds1343_show_glitchfilter(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + int glitch_filt_status, data; + + regmap_read(priv->map, DS1343_CONTROL_REG, &data); + + glitch_filt_status = !!(data & DS1343_EGFIL); + + if (glitch_filt_status) + return sprintf(buf, "enabled\n"); + else + return sprintf(buf, "disabled\n"); +} + +static ssize_t ds1343_store_glitchfilter(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + int data; + + regmap_read(priv->map, DS1343_CONTROL_REG, &data); + + if (strncmp(buf, "enabled", 7) == 0) + data |= DS1343_EGFIL; + + else if (strncmp(buf, "disabled", 8) == 0) + data &= ~(DS1343_EGFIL); + + else + return -EINVAL; + + regmap_write(priv->map, DS1343_CONTROL_REG, data); + + return count; +} + +static DEVICE_ATTR(glitch_filter, S_IRUGO | S_IWUSR, ds1343_show_glitchfilter, + ds1343_store_glitchfilter); + +static ssize_t ds1343_show_alarmstatus(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + int alarmstatus, data; + + regmap_read(priv->map, DS1343_CONTROL_REG, &data); + + alarmstatus = !!(data & DS1343_A0IE); + + if (alarmstatus) + return sprintf(buf, "enabled\n"); + else + return sprintf(buf, "disabled\n"); +} + +static DEVICE_ATTR(alarm_status, S_IRUGO, ds1343_show_alarmstatus, NULL); + +static ssize_t ds1343_show_alarmmode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + int alarm_mode, data; + char *alarm_str; + + regmap_read(priv->map, DS1343_ALM0_SEC_REG, &data); + alarm_mode = (data & 0x80) >> 4; + + regmap_read(priv->map, DS1343_ALM0_MIN_REG, &data); + alarm_mode |= (data & 0x80) >> 5; + + regmap_read(priv->map, DS1343_ALM0_HOUR_REG, &data); + alarm_mode |= (data & 0x80) >> 6; + + regmap_read(priv->map, DS1343_ALM0_DAY_REG, &data); + alarm_mode |= (data & 0x80) >> 7; + + switch (alarm_mode) { + case 15: + alarm_str = "each second"; + break; + + case 7: + alarm_str = "seconds match"; + break; + + case 3: + alarm_str = "minutes and seconds match"; + break; + + case 1: + alarm_str = "hours, minutes and seconds match"; + break; + + case 0: + alarm_str = "day, hours, minutes and seconds match"; + break; + + default: + alarm_str = "invalid"; + break; + } + + return sprintf(buf, "%s\n", alarm_str); +} + +static DEVICE_ATTR(alarm_mode, S_IRUGO, ds1343_show_alarmmode, NULL); + +static ssize_t ds1343_show_tricklecharger(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + int data; + char *diodes = "disabled", *resistors = " "; + + regmap_read(priv->map, DS1343_TRICKLE_REG, &data); + + if ((data & 0xf0) == DS1343_TRICKLE_MAGIC) { + switch (data & 0x0c) { + case DS1343_TRICKLE_DS1: + diodes = "one diode,"; + break; + + default: + diodes = "no diode,"; + break; + } + + switch (data & 0x03) { + case DS1343_TRICKLE_1K: + resistors = "1k Ohm"; + break; + + case DS1343_TRICKLE_2K: + resistors = "2k Ohm"; + break; + + case DS1343_TRICKLE_4K: + resistors = "4k Ohm"; + break; + + default: + diodes = "disabled"; + break; + } + } + + return sprintf(buf, "%s %s\n", diodes, resistors); +} + +static DEVICE_ATTR(trickle_charger, S_IRUGO, ds1343_show_tricklecharger, NULL); + +static int ds1343_sysfs_register(struct device *dev) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + int err; + + err = device_create_file(dev, &dev_attr_glitch_filter); + if (err) + return err; + + err = device_create_file(dev, &dev_attr_trickle_charger); + if (err) + goto error1; + + if (priv->irq <= 0) + return err; + + err = device_create_file(dev, &dev_attr_alarm_mode); + if (err) + goto error2; + + err = device_create_file(dev, &dev_attr_alarm_status); + if (!err) + return err; + + device_remove_file(dev, &dev_attr_alarm_mode); + +error2: + device_remove_file(dev, &dev_attr_trickle_charger); + +error1: + device_remove_file(dev, &dev_attr_glitch_filter); + + return err; +} + +static void ds1343_sysfs_unregister(struct device *dev) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + + device_remove_file(dev, &dev_attr_glitch_filter); + device_remove_file(dev, &dev_attr_trickle_charger); + + if (priv->irq <= 0) + return; + + device_remove_file(dev, &dev_attr_alarm_status); + device_remove_file(dev, &dev_attr_alarm_mode); +} + +static int ds1343_read_time(struct device *dev, struct rtc_time *dt) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + unsigned char buf[7]; + int res; + + res = regmap_bulk_read(priv->map, DS1343_SECONDS_REG, buf, 7); + if (res) + return res; + + dt->tm_sec = bcd2bin(buf[0]); + dt->tm_min = bcd2bin(buf[1]); + dt->tm_hour = bcd2bin(buf[2] & 0x3F); + dt->tm_wday = bcd2bin(buf[3]) - 1; + dt->tm_mday = bcd2bin(buf[4]); + dt->tm_mon = bcd2bin(buf[5] & 0x1F) - 1; + dt->tm_year = bcd2bin(buf[6]) + 100; /* year offset from 1900 */ + + return rtc_valid_tm(dt); +} + +static int ds1343_set_time(struct device *dev, struct rtc_time *dt) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + int res; + + res = regmap_write(priv->map, DS1343_SECONDS_REG, + bin2bcd(dt->tm_sec)); + if (res) + return res; + + res = regmap_write(priv->map, DS1343_MINUTES_REG, + bin2bcd(dt->tm_min)); + if (res) + return res; + + res = regmap_write(priv->map, DS1343_HOURS_REG, + bin2bcd(dt->tm_hour) & 0x3F); + if (res) + return res; + + res = regmap_write(priv->map, DS1343_DAY_REG, + bin2bcd(dt->tm_wday + 1)); + if (res) + return res; + + res = regmap_write(priv->map, DS1343_DATE_REG, + bin2bcd(dt->tm_mday)); + if (res) + return res; + + res = regmap_write(priv->map, DS1343_MONTH_REG, + bin2bcd(dt->tm_mon + 1)); + if (res) + return res; + + dt->tm_year %= 100; + + res = regmap_write(priv->map, DS1343_YEAR_REG, + bin2bcd(dt->tm_year)); + if (res) + return res; + + return 0; +} + +static int ds1343_update_alarm(struct device *dev) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + unsigned int control, stat; + unsigned char buf[4]; + int res = 0; + + res = regmap_read(priv->map, DS1343_CONTROL_REG, &control); + if (res) + return res; + + res = regmap_read(priv->map, DS1343_STATUS_REG, &stat); + if (res) + return res; + + control &= ~(DS1343_A0IE); + stat &= ~(DS1343_IRQF0); + + res = regmap_write(priv->map, DS1343_CONTROL_REG, control); + if (res) + return res; + + res = regmap_write(priv->map, DS1343_STATUS_REG, stat); + if (res) + return res; + + buf[0] = priv->alarm_sec < 0 || (priv->irqen & RTC_UF) ? + 0x80 : bin2bcd(priv->alarm_sec) & 0x7F; + buf[1] = priv->alarm_min < 0 || (priv->irqen & RTC_UF) ? + 0x80 : bin2bcd(priv->alarm_min) & 0x7F; + buf[2] = priv->alarm_hour < 0 || (priv->irqen & RTC_UF) ? + 0x80 : bin2bcd(priv->alarm_hour) & 0x3F; + buf[3] = priv->alarm_mday < 0 || (priv->irqen & RTC_UF) ? + 0x80 : bin2bcd(priv->alarm_mday) & 0x7F; + + res = regmap_bulk_write(priv->map, DS1343_ALM0_SEC_REG, buf, 4); + if (res) + return res; + + if (priv->irqen) { + control |= DS1343_A0IE; + res = regmap_write(priv->map, DS1343_CONTROL_REG, control); + } + + return res; +} + +static int ds1343_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + int res = 0; + unsigned int stat; + + if (priv->irq <= 0) + return -EINVAL; + + mutex_lock(&priv->mutex); + + res = regmap_read(priv->map, DS1343_STATUS_REG, &stat); + if (res) + goto out; + + alarm->enabled = !!(priv->irqen & RTC_AF); + alarm->pending = !!(stat & DS1343_IRQF0); + + alarm->time.tm_sec = priv->alarm_sec < 0 ? 0 : priv->alarm_sec; + alarm->time.tm_min = priv->alarm_min < 0 ? 0 : priv->alarm_min; + alarm->time.tm_hour = priv->alarm_hour < 0 ? 0 : priv->alarm_hour; + alarm->time.tm_mday = priv->alarm_mday < 0 ? 0 : priv->alarm_mday; + + alarm->time.tm_mon = -1; + alarm->time.tm_year = -1; + alarm->time.tm_wday = -1; + alarm->time.tm_yday = -1; + alarm->time.tm_isdst = -1; + +out: + mutex_unlock(&priv->mutex); + return res; +} + +static int ds1343_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + int res = 0; + + if (priv->irq <= 0) + return -EINVAL; + + mutex_lock(&priv->mutex); + + priv->alarm_sec = alarm->time.tm_sec; + priv->alarm_min = alarm->time.tm_min; + priv->alarm_hour = alarm->time.tm_hour; + priv->alarm_mday = alarm->time.tm_mday; + + if (alarm->enabled) + priv->irqen |= RTC_AF; + + res = ds1343_update_alarm(dev); + + mutex_unlock(&priv->mutex); + + return res; +} + +static int ds1343_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + int res = 0; + + if (priv->irq <= 0) + return -EINVAL; + + mutex_lock(&priv->mutex); + + if (enabled) + priv->irqen |= RTC_AF; + else + priv->irqen &= ~RTC_AF; + + res = ds1343_update_alarm(dev); + + mutex_unlock(&priv->mutex); + + return res; +} + +static irqreturn_t ds1343_thread(int irq, void *dev_id) +{ + struct ds1343_priv *priv = dev_id; + unsigned int stat, control; + int res = 0; + + mutex_lock(&priv->mutex); + + res = regmap_read(priv->map, DS1343_STATUS_REG, &stat); + if (res) + goto out; + + if (stat & DS1343_IRQF0) { + stat &= ~DS1343_IRQF0; + regmap_write(priv->map, DS1343_STATUS_REG, stat); + + res = regmap_read(priv->map, DS1343_CONTROL_REG, &control); + if (res) + goto out; + + control &= ~DS1343_A0IE; + regmap_write(priv->map, DS1343_CONTROL_REG, control); + + rtc_update_irq(priv->rtc, 1, RTC_AF | RTC_IRQF); + } + +out: + mutex_unlock(&priv->mutex); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops ds1343_rtc_ops = { + .ioctl = ds1343_ioctl, + .read_time = ds1343_read_time, + .set_time = ds1343_set_time, + .read_alarm = ds1343_read_alarm, + .set_alarm = ds1343_set_alarm, + .alarm_irq_enable = ds1343_alarm_irq_enable, +}; + +static int ds1343_probe(struct spi_device *spi) +{ + struct ds1343_priv *priv; + struct regmap_config config; + unsigned int data; + int res; + + memset(&config, 0, sizeof(config)); + config.reg_bits = 8; + config.val_bits = 8; + config.write_flag_mask = 0x80; + + priv = devm_kzalloc(&spi->dev, sizeof(struct ds1343_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->spi = spi; + mutex_init(&priv->mutex); + + /* RTC DS1347 works in spi mode 3 and + * its chip select is active high + */ + spi->mode = SPI_MODE_3 | SPI_CS_HIGH; + spi->bits_per_word = 8; + res = spi_setup(spi); + if (res) + return res; + + spi_set_drvdata(spi, priv); + + priv->map = devm_regmap_init_spi(spi, &config); + + if (IS_ERR(priv->map)) { + dev_err(&spi->dev, "spi regmap init failed for rtc ds1343\n"); + return PTR_ERR(priv->map); + } + + res = regmap_read(priv->map, DS1343_SECONDS_REG, &data); + if (res) + return res; + + regmap_read(priv->map, DS1343_CONTROL_REG, &data); + data |= DS1343_INTCN; + data &= ~(DS1343_EOSC | DS1343_A1IE | DS1343_A0IE); + regmap_write(priv->map, DS1343_CONTROL_REG, data); + + regmap_read(priv->map, DS1343_STATUS_REG, &data); + data &= ~(DS1343_OSF | DS1343_IRQF1 | DS1343_IRQF0); + regmap_write(priv->map, DS1343_STATUS_REG, data); + + priv->rtc = devm_rtc_device_register(&spi->dev, "ds1343", + &ds1343_rtc_ops, THIS_MODULE); + if (IS_ERR(priv->rtc)) { + dev_err(&spi->dev, "unable to register rtc ds1343\n"); + return PTR_ERR(priv->rtc); + } + + priv->irq = spi->irq; + + if (priv->irq >= 0) { + res = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, + ds1343_thread, + IRQF_NO_SUSPEND | IRQF_ONESHOT, + "ds1343", priv); + if (res) { + priv->irq = -1; + dev_err(&spi->dev, + "unable to request irq for rtc ds1343\n"); + } else { + device_set_wakeup_capable(&spi->dev, 1); + } + } + + res = ds1343_sysfs_register(&spi->dev); + if (res) + dev_err(&spi->dev, + "unable to create sysfs entries for rtc ds1343\n"); + + return 0; +} + +static int ds1343_remove(struct spi_device *spi) +{ + struct ds1343_priv *priv = spi_get_drvdata(spi); + + if (spi->irq) { + mutex_lock(&priv->mutex); + priv->irqen &= ~RTC_AF; + mutex_unlock(&priv->mutex); + + devm_free_irq(&spi->dev, spi->irq, priv); + } + + spi_set_drvdata(spi, NULL); + + ds1343_sysfs_unregister(&spi->dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static int ds1343_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + if (spi->irq >= 0 && device_may_wakeup(dev)) + enable_irq_wake(spi->irq); + + return 0; +} + +static int ds1343_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + if (spi->irq >= 0 && device_may_wakeup(dev)) + disable_irq_wake(spi->irq); + + return 0; +} + +#endif + +static SIMPLE_DEV_PM_OPS(ds1343_pm, ds1343_suspend, ds1343_resume); + +static struct spi_driver ds1343_driver = { + .driver = { + .name = "ds1343", + .owner = THIS_MODULE, + .pm = &ds1343_pm, + }, + .probe = ds1343_probe, + .remove = ds1343_remove, + .id_table = ds1343_id, +}; + +module_spi_driver(ds1343_driver); + +MODULE_DESCRIPTION("DS1343 RTC SPI Driver"); +MODULE_AUTHOR("Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DS1343_DRV_VERSION); diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c index 942103dac30..c6b2191a412 100644 --- a/drivers/rtc/rtc-ds1742.c +++ b/drivers/rtc/rtc-ds1742.c @@ -219,7 +219,7 @@ static int ds1742_rtc_remove(struct platform_device *pdev) return 0; } -static struct of_device_id __maybe_unused ds1742_rtc_of_match[] = { +static const struct of_device_id __maybe_unused ds1742_rtc_of_match[] = { { .compatible = "maxim,ds1742", }, { } }; diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c index 797aa0252ba..c4c38431012 100644 --- a/drivers/rtc/rtc-efi.c +++ b/drivers/rtc/rtc-efi.c @@ -35,7 +35,7 @@ static inline int compute_yday(efi_time_t *eft) { /* efi_time_t.month is in the [1-12] so, we need -1 */ - return rtc_year_days(eft->day - 1, eft->month - 1, eft->year); + return rtc_year_days(eft->day, eft->month - 1, eft->year); } /* * returns day of the week [0-6] 0=Sunday diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c index e5f13c4310f..b936bb4096b 100644 --- a/drivers/rtc/rtc-hym8563.c +++ b/drivers/rtc/rtc-hym8563.c @@ -418,6 +418,9 @@ static struct clk *hym8563_clkout_register_clk(struct hym8563 *hym8563) init.num_parents = 0; hym8563->clkout_hw.init = &init; + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + /* register the clock */ clk = clk_register(&client->dev, &hym8563->clkout_hw); @@ -585,7 +588,7 @@ static const struct i2c_device_id hym8563_id[] = { }; MODULE_DEVICE_TABLE(i2c, hym8563_id); -static struct of_device_id hym8563_dt_idtable[] = { +static const struct of_device_id hym8563_dt_idtable[] = { { .compatible = "haoyu,hym8563" }, {}, }; diff --git a/drivers/rtc/rtc-isl12057.c b/drivers/rtc/rtc-isl12057.c index 41bd76aaff7..455b601d731 100644 --- a/drivers/rtc/rtc-isl12057.c +++ b/drivers/rtc/rtc-isl12057.c @@ -278,7 +278,7 @@ static int isl12057_probe(struct i2c_client *client, } #ifdef CONFIG_OF -static struct of_device_id isl12057_dt_match[] = { +static const struct of_device_id isl12057_dt_match[] = { { .compatible = "isl,isl12057" }, { }, }; diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index a5248aa1abf..7ff7427c2e6 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -66,8 +66,6 @@ #define M41T80_FEATURE_WD (1 << 3) /* Extra watchdog resolution */ #define M41T80_FEATURE_SQ_ALT (1 << 4) /* RSx bits are in reg 4 */ -#define DRV_VERSION "0.05" - static DEFINE_MUTEX(m41t80_rtc_mutex); static const struct i2c_device_id m41t80_id[] = { { "m41t62", M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT }, @@ -80,6 +78,7 @@ static const struct i2c_device_id m41t80_id[] = { { "m41st84", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, { "m41st85", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, { "m41st87", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { "rv4162", M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT }, { } }; MODULE_DEVICE_TABLE(i2c, m41t80_id); @@ -232,7 +231,7 @@ static ssize_t m41t80_sysfs_show_flags(struct device *dev, val = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); if (val < 0) - return -EIO; + return val; return sprintf(buf, "%#x\n", val); } static DEVICE_ATTR(flags, S_IRUGO, m41t80_sysfs_show_flags, NULL); @@ -252,7 +251,7 @@ static ssize_t m41t80_sysfs_show_sqwfreq(struct device *dev, reg_sqw = M41T80_REG_WDAY; val = i2c_smbus_read_byte_data(client, reg_sqw); if (val < 0) - return -EIO; + return val; val = (val >> 4) & 0xf; switch (val) { case 0: @@ -271,7 +270,7 @@ static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct m41t80_data *clientdata = i2c_get_clientdata(client); - int almon, sqw, reg_sqw; + int almon, sqw, reg_sqw, rc; int val = simple_strtoul(buf, NULL, 0); if (!(clientdata->features & M41T80_FEATURE_SQ)) @@ -291,21 +290,30 @@ static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev, /* disable SQW, set SQW frequency & re-enable */ almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); if (almon < 0) - return -EIO; + return almon; reg_sqw = M41T80_REG_SQW; if (clientdata->features & M41T80_FEATURE_SQ_ALT) reg_sqw = M41T80_REG_WDAY; sqw = i2c_smbus_read_byte_data(client, reg_sqw); if (sqw < 0) - return -EIO; + return sqw; sqw = (sqw & 0x0f) | (val << 4); - if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, - almon & ~M41T80_ALMON_SQWE) < 0 || - i2c_smbus_write_byte_data(client, reg_sqw, sqw) < 0) - return -EIO; - if (val && i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, - almon | M41T80_ALMON_SQWE) < 0) - return -EIO; + + rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + almon & ~M41T80_ALMON_SQWE); + if (rc < 0) + return rc; + + if (val) { + rc = i2c_smbus_write_byte_data(client, reg_sqw, sqw); + if (rc < 0) + return rc; + + rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + almon | M41T80_ALMON_SQWE); + if (rc <0) + return rc; + } return count; } static DEVICE_ATTR(sqwfreq, S_IRUGO | S_IWUSR, @@ -629,40 +637,28 @@ static int m41t80_probe(struct i2c_client *client, struct m41t80_data *clientdata = NULL; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C - | I2C_FUNC_SMBUS_BYTE_DATA)) { - rc = -ENODEV; - goto exit; - } - - dev_info(&client->dev, - "chip found, driver version " DRV_VERSION "\n"); + | I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; clientdata = devm_kzalloc(&client->dev, sizeof(*clientdata), GFP_KERNEL); - if (!clientdata) { - rc = -ENOMEM; - goto exit; - } + if (!clientdata) + return -ENOMEM; clientdata->features = id->driver_data; i2c_set_clientdata(client, clientdata); rtc = devm_rtc_device_register(&client->dev, client->name, &m41t80_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - rc = PTR_ERR(rtc); - rtc = NULL; - goto exit; - } + if (IS_ERR(rtc)) + return PTR_ERR(rtc); clientdata->rtc = rtc; /* Make sure HT (Halt Update) bit is cleared */ rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR); - if (rc < 0) - goto ht_err; - if (rc & M41T80_ALHOUR_HT) { + if (rc >= 0 && rc & M41T80_ALHOUR_HT) { if (clientdata->features & M41T80_FEATURE_HT) { m41t80_get_datetime(client, &tm); dev_info(&client->dev, "HT bit was set!\n"); @@ -673,53 +669,44 @@ static int m41t80_probe(struct i2c_client *client, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); } - if (i2c_smbus_write_byte_data(client, - M41T80_REG_ALARM_HOUR, - rc & ~M41T80_ALHOUR_HT) < 0) - goto ht_err; + rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_HOUR, + rc & ~M41T80_ALHOUR_HT); + } + + if (rc < 0) { + dev_err(&client->dev, "Can't clear HT bit\n"); + return rc; } /* Make sure ST (stop) bit is cleared */ rc = i2c_smbus_read_byte_data(client, M41T80_REG_SEC); - if (rc < 0) - goto st_err; - if (rc & M41T80_SEC_ST) { - if (i2c_smbus_write_byte_data(client, M41T80_REG_SEC, - rc & ~M41T80_SEC_ST) < 0) - goto st_err; + if (rc >= 0 && rc & M41T80_SEC_ST) + rc = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, + rc & ~M41T80_SEC_ST); + if (rc < 0) { + dev_err(&client->dev, "Can't clear ST bit\n"); + return rc; } rc = m41t80_sysfs_register(&client->dev); if (rc) - goto exit; + return rc; #ifdef CONFIG_RTC_DRV_M41T80_WDT if (clientdata->features & M41T80_FEATURE_HT) { save_client = client; rc = misc_register(&wdt_dev); if (rc) - goto exit; + return rc; rc = register_reboot_notifier(&wdt_notifier); if (rc) { misc_deregister(&wdt_dev); - goto exit; + return rc; } } #endif return 0; - -st_err: - rc = -EIO; - dev_err(&client->dev, "Can't clear ST bit\n"); - goto exit; -ht_err: - rc = -EIO; - dev_err(&client->dev, "Can't clear HT bit\n"); - goto exit; - -exit: - return rc; } static int m41t80_remove(struct i2c_client *client) @@ -750,4 +737,3 @@ module_i2c_driver(m41t80_driver); MODULE_AUTHOR("Alexander Bigga <ab@mycable.de>"); MODULE_DESCRIPTION("ST Microelectronics M41T80 series RTC I2C Client Driver"); MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c new file mode 100644 index 00000000000..34295bf0041 --- /dev/null +++ b/drivers/rtc/rtc-mcp795.c @@ -0,0 +1,199 @@ +/* + * SPI Driver for Microchip MCP795 RTC + * + * Copyright (C) Josef Gajdusek <atx@atx.name> + * + * based on other Linux RTC drivers + * + * Device datasheet: + * http://ww1.microchip.com/downloads/en/DeviceDoc/22280A.pdf + * + * 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. + * + * */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/printk.h> +#include <linux/spi/spi.h> +#include <linux/rtc.h> + +/* MCP795 Instructions, see datasheet table 3-1 */ +#define MCP795_EEREAD 0x03 +#define MCP795_EEWRITE 0x02 +#define MCP795_EEWRDI 0x04 +#define MCP795_EEWREN 0x06 +#define MCP795_SRREAD 0x05 +#define MCP795_SRWRITE 0x01 +#define MCP795_READ 0x13 +#define MCP795_WRITE 0x12 +#define MCP795_UNLOCK 0x14 +#define MCP795_IDWRITE 0x32 +#define MCP795_IDREAD 0x33 +#define MCP795_CLRWDT 0x44 +#define MCP795_CLRRAM 0x54 + +#define MCP795_ST_BIT 0x80 +#define MCP795_24_BIT 0x40 + +static int mcp795_rtcc_read(struct device *dev, u8 addr, u8 *buf, u8 count) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + u8 tx[2]; + + tx[0] = MCP795_READ; + tx[1] = addr; + ret = spi_write_then_read(spi, tx, sizeof(tx), buf, count); + + if (ret) + dev_err(dev, "Failed reading %d bytes from address %x.\n", + count, addr); + + return ret; +} + +static int mcp795_rtcc_write(struct device *dev, u8 addr, u8 *data, u8 count) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + u8 tx[2 + count]; + + tx[0] = MCP795_WRITE; + tx[1] = addr; + memcpy(&tx[2], data, count); + + ret = spi_write(spi, tx, 2 + count); + + if (ret) + dev_err(dev, "Failed to write %d bytes to address %x.\n", + count, addr); + + return ret; +} + +static int mcp795_rtcc_set_bits(struct device *dev, u8 addr, u8 mask, u8 state) +{ + int ret; + u8 tmp; + + ret = mcp795_rtcc_read(dev, addr, &tmp, 1); + if (ret) + return ret; + + if ((tmp & mask) != state) { + tmp = (tmp & ~mask) | state; + ret = mcp795_rtcc_write(dev, addr, &tmp, 1); + } + + return ret; +} + +static int mcp795_set_time(struct device *dev, struct rtc_time *tim) +{ + int ret; + u8 data[7]; + + /* Read first, so we can leave config bits untouched */ + ret = mcp795_rtcc_read(dev, 0x01, data, sizeof(data)); + + if (ret) + return ret; + + data[0] = (data[0] & 0x80) | ((tim->tm_sec / 10) << 4) | (tim->tm_sec % 10); + data[1] = (data[1] & 0x80) | ((tim->tm_min / 10) << 4) | (tim->tm_min % 10); + data[2] = ((tim->tm_hour / 10) << 4) | (tim->tm_hour % 10); + data[4] = ((tim->tm_mday / 10) << 4) | ((tim->tm_mday) % 10); + data[5] = (data[5] & 0x10) | (tim->tm_mon / 10) | (tim->tm_mon % 10); + + if (tim->tm_year > 100) + tim->tm_year -= 100; + + data[6] = ((tim->tm_year / 10) << 4) | (tim->tm_year % 10); + + ret = mcp795_rtcc_write(dev, 0x01, data, sizeof(data)); + + if (ret) + return ret; + + dev_dbg(dev, "Set mcp795: %04d-%02d-%02d %02d:%02d:%02d\n", + tim->tm_year + 1900, tim->tm_mon, tim->tm_mday, + tim->tm_hour, tim->tm_min, tim->tm_sec); + + return 0; +} + +static int mcp795_read_time(struct device *dev, struct rtc_time *tim) +{ + int ret; + u8 data[7]; + + ret = mcp795_rtcc_read(dev, 0x01, data, sizeof(data)); + + if (ret) + return ret; + + tim->tm_sec = ((data[0] & 0x70) >> 4) * 10 + (data[0] & 0x0f); + tim->tm_min = ((data[1] & 0x70) >> 4) * 10 + (data[1] & 0x0f); + tim->tm_hour = ((data[2] & 0x30) >> 4) * 10 + (data[2] & 0x0f); + tim->tm_mday = ((data[4] & 0x30) >> 4) * 10 + (data[4] & 0x0f); + tim->tm_mon = ((data[5] & 0x10) >> 4) * 10 + (data[5] & 0x0f); + tim->tm_year = ((data[6] & 0xf0) >> 4) * 10 + (data[6] & 0x0f) + 100; /* Assume we are in 20xx */ + + dev_dbg(dev, "Read from mcp795: %04d-%02d-%02d %02d:%02d:%02d\n", + tim->tm_year + 1900, tim->tm_mon, tim->tm_mday, + tim->tm_hour, tim->tm_min, tim->tm_sec); + + return rtc_valid_tm(tim); +} + +static struct rtc_class_ops mcp795_rtc_ops = { + .read_time = mcp795_read_time, + .set_time = mcp795_set_time +}; + +static int mcp795_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + int ret; + + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret) { + dev_err(&spi->dev, "Unable to setup SPI\n"); + return ret; + } + + /* Start the oscillator */ + mcp795_rtcc_set_bits(&spi->dev, 0x01, MCP795_ST_BIT, MCP795_ST_BIT); + /* Clear the 12 hour mode flag*/ + mcp795_rtcc_set_bits(&spi->dev, 0x03, MCP795_24_BIT, 0); + + rtc = devm_rtc_device_register(&spi->dev, "rtc-mcp795", + &mcp795_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + spi_set_drvdata(spi, rtc); + + return 0; +} + +static struct spi_driver mcp795_driver = { + .driver = { + .name = "rtc-mcp795", + .owner = THIS_MODULE, + }, + .probe = mcp795_probe, +}; + +module_spi_driver(mcp795_driver); + +MODULE_DESCRIPTION("MCP795 RTC SPI Driver"); +MODULE_AUTHOR("Josef Gajdusek <atx@atx.name>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:mcp795"); diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c index d15a999363f..6aaec2fc7c0 100644 --- a/drivers/rtc/rtc-mv.c +++ b/drivers/rtc/rtc-mv.c @@ -319,7 +319,7 @@ static int __exit mv_rtc_remove(struct platform_device *pdev) } #ifdef CONFIG_OF -static struct of_device_id rtc_mv_of_match_table[] = { +static const struct of_device_id rtc_mv_of_match_table[] = { { .compatible = "marvell,orion-rtc", }, {} }; diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index 26de5f8c2ae..21142e6574a 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -73,43 +73,52 @@ #define OMAP_RTC_IRQWAKEEN 0x7c /* OMAP_RTC_CTRL_REG bit fields: */ -#define OMAP_RTC_CTRL_SPLIT (1<<7) -#define OMAP_RTC_CTRL_DISABLE (1<<6) -#define OMAP_RTC_CTRL_SET_32_COUNTER (1<<5) -#define OMAP_RTC_CTRL_TEST (1<<4) -#define OMAP_RTC_CTRL_MODE_12_24 (1<<3) -#define OMAP_RTC_CTRL_AUTO_COMP (1<<2) -#define OMAP_RTC_CTRL_ROUND_30S (1<<1) -#define OMAP_RTC_CTRL_STOP (1<<0) +#define OMAP_RTC_CTRL_SPLIT BIT(7) +#define OMAP_RTC_CTRL_DISABLE BIT(6) +#define OMAP_RTC_CTRL_SET_32_COUNTER BIT(5) +#define OMAP_RTC_CTRL_TEST BIT(4) +#define OMAP_RTC_CTRL_MODE_12_24 BIT(3) +#define OMAP_RTC_CTRL_AUTO_COMP BIT(2) +#define OMAP_RTC_CTRL_ROUND_30S BIT(1) +#define OMAP_RTC_CTRL_STOP BIT(0) /* OMAP_RTC_STATUS_REG bit fields: */ -#define OMAP_RTC_STATUS_POWER_UP (1<<7) -#define OMAP_RTC_STATUS_ALARM (1<<6) -#define OMAP_RTC_STATUS_1D_EVENT (1<<5) -#define OMAP_RTC_STATUS_1H_EVENT (1<<4) -#define OMAP_RTC_STATUS_1M_EVENT (1<<3) -#define OMAP_RTC_STATUS_1S_EVENT (1<<2) -#define OMAP_RTC_STATUS_RUN (1<<1) -#define OMAP_RTC_STATUS_BUSY (1<<0) +#define OMAP_RTC_STATUS_POWER_UP BIT(7) +#define OMAP_RTC_STATUS_ALARM BIT(6) +#define OMAP_RTC_STATUS_1D_EVENT BIT(5) +#define OMAP_RTC_STATUS_1H_EVENT BIT(4) +#define OMAP_RTC_STATUS_1M_EVENT BIT(3) +#define OMAP_RTC_STATUS_1S_EVENT BIT(2) +#define OMAP_RTC_STATUS_RUN BIT(1) +#define OMAP_RTC_STATUS_BUSY BIT(0) /* OMAP_RTC_INTERRUPTS_REG bit fields: */ -#define OMAP_RTC_INTERRUPTS_IT_ALARM (1<<3) -#define OMAP_RTC_INTERRUPTS_IT_TIMER (1<<2) +#define OMAP_RTC_INTERRUPTS_IT_ALARM BIT(3) +#define OMAP_RTC_INTERRUPTS_IT_TIMER BIT(2) + +/* OMAP_RTC_OSC_REG bit fields: */ +#define OMAP_RTC_OSC_32KCLK_EN BIT(6) /* OMAP_RTC_IRQWAKEEN bit fields: */ -#define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN (1<<1) +#define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN BIT(1) /* OMAP_RTC_KICKER values */ #define KICK0_VALUE 0x83e70b13 #define KICK1_VALUE 0x95a4f1e0 -#define OMAP_RTC_HAS_KICKER 0x1 +#define OMAP_RTC_HAS_KICKER BIT(0) /* * Few RTC IP revisions has special WAKE-EN Register to enable Wakeup * generation for event Alarm. */ -#define OMAP_RTC_HAS_IRQWAKEEN 0x2 +#define OMAP_RTC_HAS_IRQWAKEEN BIT(1) + +/* + * Some RTC IP revisions (like those in AM335x and DRA7x) need + * the 32KHz clock to be explicitly enabled. + */ +#define OMAP_RTC_HAS_32KCLK_EN BIT(2) static void __iomem *rtc_base; @@ -162,17 +171,28 @@ static irqreturn_t rtc_irq(int irq, void *rtc) static int omap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { - u8 reg; + u8 reg, irqwake_reg = 0; + struct platform_device *pdev = to_platform_device(dev); + const struct platform_device_id *id_entry = + platform_get_device_id(pdev); local_irq_disable(); rtc_wait_not_busy(); reg = rtc_read(OMAP_RTC_INTERRUPTS_REG); - if (enabled) + if (id_entry->driver_data & OMAP_RTC_HAS_IRQWAKEEN) + irqwake_reg = rtc_read(OMAP_RTC_IRQWAKEEN); + + if (enabled) { reg |= OMAP_RTC_INTERRUPTS_IT_ALARM; - else + irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; + } else { reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; + irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; + } rtc_wait_not_busy(); rtc_write(reg, OMAP_RTC_INTERRUPTS_REG); + if (id_entry->driver_data & OMAP_RTC_HAS_IRQWAKEEN) + rtc_write(irqwake_reg, OMAP_RTC_IRQWAKEEN); local_irq_enable(); return 0; @@ -272,7 +292,10 @@ static int omap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) { - u8 reg; + u8 reg, irqwake_reg = 0; + struct platform_device *pdev = to_platform_device(dev); + const struct platform_device_id *id_entry = + platform_get_device_id(pdev); if (tm2bcd(&alm->time) < 0) return -EINVAL; @@ -288,11 +311,19 @@ static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) rtc_write(alm->time.tm_sec, OMAP_RTC_ALARM_SECONDS_REG); reg = rtc_read(OMAP_RTC_INTERRUPTS_REG); - if (alm->enabled) + if (id_entry->driver_data & OMAP_RTC_HAS_IRQWAKEEN) + irqwake_reg = rtc_read(OMAP_RTC_IRQWAKEEN); + + if (alm->enabled) { reg |= OMAP_RTC_INTERRUPTS_IT_ALARM; - else + irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; + } else { reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; + irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; + } rtc_write(reg, OMAP_RTC_INTERRUPTS_REG); + if (id_entry->driver_data & OMAP_RTC_HAS_IRQWAKEEN) + rtc_write(irqwake_reg, OMAP_RTC_IRQWAKEEN); local_irq_enable(); @@ -319,7 +350,8 @@ static struct platform_device_id omap_rtc_devtype[] = { }, [OMAP_RTC_DATA_AM3352_IDX] = { .name = "am3352-rtc", - .driver_data = OMAP_RTC_HAS_KICKER | OMAP_RTC_HAS_IRQWAKEEN, + .driver_data = OMAP_RTC_HAS_KICKER | OMAP_RTC_HAS_IRQWAKEEN | + OMAP_RTC_HAS_32KCLK_EN, }, [OMAP_RTC_DATA_DA830_IDX] = { .name = "da830-rtc", @@ -352,6 +384,12 @@ static int __init omap_rtc_probe(struct platform_device *pdev) if (of_id) pdev->id_entry = of_id->data; + id_entry = platform_get_device_id(pdev); + if (!id_entry) { + dev_err(&pdev->dev, "no matching device entry\n"); + return -ENODEV; + } + omap_rtc_timer = platform_get_irq(pdev, 0); if (omap_rtc_timer <= 0) { pr_debug("%s: no update irq?\n", pdev->name); @@ -373,8 +411,7 @@ static int __init omap_rtc_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); - id_entry = platform_get_device_id(pdev); - if (id_entry && (id_entry->driver_data & OMAP_RTC_HAS_KICKER)) { + if (id_entry->driver_data & OMAP_RTC_HAS_KICKER) { rtc_writel(KICK0_VALUE, OMAP_RTC_KICK0_REG); rtc_writel(KICK1_VALUE, OMAP_RTC_KICK1_REG); } @@ -393,6 +430,10 @@ static int __init omap_rtc_probe(struct platform_device *pdev) */ rtc_write(0, OMAP_RTC_INTERRUPTS_REG); + /* enable RTC functional clock */ + if (id_entry->driver_data & OMAP_RTC_HAS_32KCLK_EN) + rtc_writel(OMAP_RTC_OSC_32KCLK_EN, OMAP_RTC_OSC_REG); + /* clear old status */ reg = rtc_read(OMAP_RTC_STATUS_REG); if (reg & (u8) OMAP_RTC_STATUS_POWER_UP) { @@ -452,7 +493,7 @@ static int __init omap_rtc_probe(struct platform_device *pdev) return 0; fail0: - if (id_entry && (id_entry->driver_data & OMAP_RTC_HAS_KICKER)) + if (id_entry->driver_data & OMAP_RTC_HAS_KICKER) rtc_writel(0, OMAP_RTC_KICK0_REG); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -469,7 +510,7 @@ static int __exit omap_rtc_remove(struct platform_device *pdev) /* leave rtc running, but disable irqs */ rtc_write(0, OMAP_RTC_INTERRUPTS_REG); - if (id_entry && (id_entry->driver_data & OMAP_RTC_HAS_KICKER)) + if (id_entry->driver_data & OMAP_RTC_HAS_KICKER) rtc_writel(0, OMAP_RTC_KICK0_REG); /* Disable the clock/module */ @@ -484,28 +525,16 @@ static u8 irqstat; static int omap_rtc_suspend(struct device *dev) { - u8 irqwake_stat; - struct platform_device *pdev = to_platform_device(dev); - const struct platform_device_id *id_entry = - platform_get_device_id(pdev); - irqstat = rtc_read(OMAP_RTC_INTERRUPTS_REG); /* FIXME the RTC alarm is not currently acting as a wakeup event * source on some platforms, and in fact this enable() call is just * saving a flag that's never used... */ - if (device_may_wakeup(dev)) { + if (device_may_wakeup(dev)) enable_irq_wake(omap_rtc_alarm); - - if (id_entry->driver_data & OMAP_RTC_HAS_IRQWAKEEN) { - irqwake_stat = rtc_read(OMAP_RTC_IRQWAKEEN); - irqwake_stat |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; - rtc_write(irqwake_stat, OMAP_RTC_IRQWAKEEN); - } - } else { + else rtc_write(0, OMAP_RTC_INTERRUPTS_REG); - } /* Disable the clock/module */ pm_runtime_put_sync(dev); @@ -515,25 +544,14 @@ static int omap_rtc_suspend(struct device *dev) static int omap_rtc_resume(struct device *dev) { - u8 irqwake_stat; - struct platform_device *pdev = to_platform_device(dev); - const struct platform_device_id *id_entry = - platform_get_device_id(pdev); - /* Enable the clock/module so that we can access the registers */ pm_runtime_get_sync(dev); - if (device_may_wakeup(dev)) { + if (device_may_wakeup(dev)) disable_irq_wake(omap_rtc_alarm); - - if (id_entry->driver_data & OMAP_RTC_HAS_IRQWAKEEN) { - irqwake_stat = rtc_read(OMAP_RTC_IRQWAKEEN); - irqwake_stat &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; - rtc_write(irqwake_stat, OMAP_RTC_IRQWAKEEN); - } - } else { + else rtc_write(irqstat, OMAP_RTC_INTERRUPTS_REG); - } + return 0; } #endif diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c index c360d62fb3f..4dfe2d793fa 100644 --- a/drivers/rtc/rtc-palmas.c +++ b/drivers/rtc/rtc-palmas.c @@ -352,7 +352,7 @@ static SIMPLE_DEV_PM_OPS(palmas_rtc_pm_ops, palmas_rtc_suspend, palmas_rtc_resume); #ifdef CONFIG_OF -static struct of_device_id of_palmas_rtc_match[] = { +static const struct of_device_id of_palmas_rtc_match[] = { { .compatible = "ti,palmas-rtc"}, { }, }; diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index cccbf9d8972..4561f375327 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -389,7 +389,7 @@ static int __exit pxa_rtc_remove(struct platform_device *pdev) } #ifdef CONFIG_OF -static struct of_device_id pxa_rtc_dt_ids[] = { +static const struct of_device_id pxa_rtc_dt_ids[] = { { .compatible = "marvell,pxa-rtc" }, {} }; diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c index 476af93543f..8ec2d6a1dbe 100644 --- a/drivers/rtc/rtc-s5m.c +++ b/drivers/rtc/rtc-s5m.c @@ -40,6 +40,7 @@ struct s5m_rtc_info { struct device *dev; + struct i2c_client *i2c; struct sec_pmic_dev *s5m87xx; struct regmap *regmap; struct rtc_device *rtc_dev; @@ -49,6 +50,20 @@ struct s5m_rtc_info { bool wtsr_smpl; }; +static const struct regmap_config s5m_rtc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = SEC_RTC_REG_MAX, +}; + +static const struct regmap_config s2mps14_rtc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = S2MPS_RTC_REG_MAX, +}; + static void s5m8767_data_to_tm(u8 *data, struct rtc_time *tm, int rtc_24hr_mode) { @@ -554,6 +569,7 @@ static int s5m_rtc_probe(struct platform_device *pdev) struct sec_pmic_dev *s5m87xx = dev_get_drvdata(pdev->dev.parent); struct sec_platform_data *pdata = s5m87xx->pdata; struct s5m_rtc_info *info; + const struct regmap_config *regmap_cfg; int ret; if (!pdata) { @@ -565,9 +581,37 @@ static int s5m_rtc_probe(struct platform_device *pdev) if (!info) return -ENOMEM; + switch (pdata->device_type) { + case S2MPS14X: + regmap_cfg = &s2mps14_rtc_regmap_config; + break; + case S5M8763X: + regmap_cfg = &s5m_rtc_regmap_config; + break; + case S5M8767X: + regmap_cfg = &s5m_rtc_regmap_config; + break; + default: + dev_err(&pdev->dev, "Device type is not supported by RTC driver\n"); + return -ENODEV; + } + + info->i2c = i2c_new_dummy(s5m87xx->i2c->adapter, RTC_I2C_ADDR); + if (!info->i2c) { + dev_err(&pdev->dev, "Failed to allocate I2C for RTC\n"); + return -ENODEV; + } + + info->regmap = devm_regmap_init_i2c(info->i2c, regmap_cfg); + if (IS_ERR(info->regmap)) { + ret = PTR_ERR(info->regmap); + dev_err(&pdev->dev, "Failed to allocate RTC register map: %d\n", + ret); + goto err; + } + info->dev = &pdev->dev; info->s5m87xx = s5m87xx; - info->regmap = s5m87xx->regmap_rtc; info->device_type = s5m87xx->device_type; info->wtsr_smpl = s5m87xx->wtsr_smpl; @@ -585,7 +629,7 @@ static int s5m_rtc_probe(struct platform_device *pdev) default: ret = -EINVAL; dev_err(&pdev->dev, "Unsupported device type: %d\n", ret); - return ret; + goto err; } platform_set_drvdata(pdev, info); @@ -602,15 +646,24 @@ static int s5m_rtc_probe(struct platform_device *pdev) info->rtc_dev = devm_rtc_device_register(&pdev->dev, "s5m-rtc", &s5m_rtc_ops, THIS_MODULE); - if (IS_ERR(info->rtc_dev)) - return PTR_ERR(info->rtc_dev); + if (IS_ERR(info->rtc_dev)) { + ret = PTR_ERR(info->rtc_dev); + goto err; + } ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, s5m_rtc_alarm_irq, 0, "rtc-alarm0", info); - if (ret < 0) + if (ret < 0) { dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", info->irq, ret); + goto err; + } + + return 0; + +err: + i2c_unregister_device(info->i2c); return ret; } @@ -639,6 +692,17 @@ static void s5m_rtc_shutdown(struct platform_device *pdev) s5m_rtc_enable_smpl(info, false); } +static int s5m_rtc_remove(struct platform_device *pdev) +{ + struct s5m_rtc_info *info = platform_get_drvdata(pdev); + + /* Perform also all shutdown steps when removing */ + s5m_rtc_shutdown(pdev); + i2c_unregister_device(info->i2c); + + return 0; +} + #ifdef CONFIG_PM_SLEEP static int s5m_rtc_resume(struct device *dev) { @@ -676,6 +740,7 @@ static struct platform_driver s5m_rtc_driver = { .pm = &s5m_rtc_pm_ops, }, .probe = s5m_rtc_probe, + .remove = s5m_rtc_remove, .shutdown = s5m_rtc_shutdown, .id_table = s5m_rtc_id, }; diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 0f7adeb1944..b6e1ca08c2c 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -338,7 +338,7 @@ static SIMPLE_DEV_PM_OPS(sa1100_rtc_pm_ops, sa1100_rtc_suspend, sa1100_rtc_resume); #ifdef CONFIG_OF -static struct of_device_id sa1100_rtc_dt_ids[] = { +static const struct of_device_id sa1100_rtc_dt_ids[] = { { .compatible = "mrvl,sa1100-rtc", }, { .compatible = "mrvl,mmp-rtc", }, {} diff --git a/drivers/rtc/rtc-xgene.c b/drivers/rtc/rtc-xgene.c new file mode 100644 index 00000000000..14129cc85bd --- /dev/null +++ b/drivers/rtc/rtc-xgene.c @@ -0,0 +1,278 @@ +/* + * APM X-Gene SoC Real Time Clock Driver + * + * Copyright (c) 2014, Applied Micro Circuits Corporation + * Author: Rameshwar Prasad Sahu <rsahu@apm.com> + * Loc Ho <lho@apm.com> + * + * 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. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/rtc.h> + +/* RTC CSR Registers */ +#define RTC_CCVR 0x00 +#define RTC_CMR 0x04 +#define RTC_CLR 0x08 +#define RTC_CCR 0x0C +#define RTC_CCR_IE BIT(0) +#define RTC_CCR_MASK BIT(1) +#define RTC_CCR_EN BIT(2) +#define RTC_CCR_WEN BIT(3) +#define RTC_STAT 0x10 +#define RTC_STAT_BIT BIT(0) +#define RTC_RSTAT 0x14 +#define RTC_EOI 0x18 +#define RTC_VER 0x1C + +struct xgene_rtc_dev { + struct rtc_device *rtc; + struct device *dev; + unsigned long alarm_time; + void __iomem *csr_base; + struct clk *clk; + unsigned int irq_wake; +}; + +static int xgene_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); + + rtc_time_to_tm(readl(pdata->csr_base + RTC_CCVR), tm); + return rtc_valid_tm(tm); +} + +static int xgene_rtc_set_mmss(struct device *dev, unsigned long secs) +{ + struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); + + /* + * NOTE: After the following write, the RTC_CCVR is only reflected + * after the update cycle of 1 seconds. + */ + writel((u32) secs, pdata->csr_base + RTC_CLR); + readl(pdata->csr_base + RTC_CLR); /* Force a barrier */ + + return 0; +} + +static int xgene_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); + + rtc_time_to_tm(pdata->alarm_time, &alrm->time); + alrm->enabled = readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE; + + return 0; +} + +static int xgene_rtc_alarm_irq_enable(struct device *dev, u32 enabled) +{ + struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); + u32 ccr; + + ccr = readl(pdata->csr_base + RTC_CCR); + if (enabled) { + ccr &= ~RTC_CCR_MASK; + ccr |= RTC_CCR_IE; + } else { + ccr &= ~RTC_CCR_IE; + ccr |= RTC_CCR_MASK; + } + writel(ccr, pdata->csr_base + RTC_CCR); + + return 0; +} + +static int xgene_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); + unsigned long rtc_time; + unsigned long alarm_time; + + rtc_time = readl(pdata->csr_base + RTC_CCVR); + rtc_tm_to_time(&alrm->time, &alarm_time); + + pdata->alarm_time = alarm_time; + writel((u32) pdata->alarm_time, pdata->csr_base + RTC_CMR); + + xgene_rtc_alarm_irq_enable(dev, alrm->enabled); + + return 0; +} + +static const struct rtc_class_ops xgene_rtc_ops = { + .read_time = xgene_rtc_read_time, + .set_mmss = xgene_rtc_set_mmss, + .read_alarm = xgene_rtc_read_alarm, + .set_alarm = xgene_rtc_set_alarm, + .alarm_irq_enable = xgene_rtc_alarm_irq_enable, +}; + +static irqreturn_t xgene_rtc_interrupt(int irq, void *id) +{ + struct xgene_rtc_dev *pdata = (struct xgene_rtc_dev *) id; + + /* Check if interrupt asserted */ + if (!(readl(pdata->csr_base + RTC_STAT) & RTC_STAT_BIT)) + return IRQ_NONE; + + /* Clear interrupt */ + readl(pdata->csr_base + RTC_EOI); + + rtc_update_irq(pdata->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static int xgene_rtc_probe(struct platform_device *pdev) +{ + struct xgene_rtc_dev *pdata; + struct resource *res; + int ret; + int irq; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + platform_set_drvdata(pdev, pdata); + pdata->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pdata->csr_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pdata->csr_base)) + return PTR_ERR(pdata->csr_base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "No IRQ resource\n"); + return irq; + } + ret = devm_request_irq(&pdev->dev, irq, xgene_rtc_interrupt, 0, + dev_name(&pdev->dev), pdata); + if (ret) { + dev_err(&pdev->dev, "Could not request IRQ\n"); + return ret; + } + + pdata->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pdata->clk)) { + dev_err(&pdev->dev, "Couldn't get the clock for RTC\n"); + return -ENODEV; + } + clk_prepare_enable(pdata->clk); + + /* Turn on the clock and the crystal */ + writel(RTC_CCR_EN, pdata->csr_base + RTC_CCR); + + device_init_wakeup(&pdev->dev, 1); + + pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &xgene_rtc_ops, THIS_MODULE); + if (IS_ERR(pdata->rtc)) { + clk_disable_unprepare(pdata->clk); + return PTR_ERR(pdata->rtc); + } + + /* HW does not support update faster than 1 seconds */ + pdata->rtc->uie_unsupported = 1; + + return 0; +} + +static int xgene_rtc_remove(struct platform_device *pdev) +{ + struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev); + + xgene_rtc_alarm_irq_enable(&pdev->dev, 0); + device_init_wakeup(&pdev->dev, 0); + clk_disable_unprepare(pdata->clk); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int xgene_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev); + int irq; + + irq = platform_get_irq(pdev, 0); + if (device_may_wakeup(&pdev->dev)) { + if (!enable_irq_wake(irq)) + pdata->irq_wake = 1; + } else { + xgene_rtc_alarm_irq_enable(dev, 0); + clk_disable(pdata->clk); + } + + return 0; +} + +static int xgene_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev); + int irq; + + irq = platform_get_irq(pdev, 0); + if (device_may_wakeup(&pdev->dev)) { + if (pdata->irq_wake) { + disable_irq_wake(irq); + pdata->irq_wake = 0; + } + } else { + clk_enable(pdata->clk); + xgene_rtc_alarm_irq_enable(dev, 1); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(xgene_rtc_pm_ops, xgene_rtc_suspend, xgene_rtc_resume); + +#ifdef CONFIG_OF +static const struct of_device_id xgene_rtc_of_match[] = { + {.compatible = "apm,xgene-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(of, xgene_rtc_of_match); +#endif + +static struct platform_driver xgene_rtc_driver = { + .probe = xgene_rtc_probe, + .remove = xgene_rtc_remove, + .driver = { + .owner = THIS_MODULE, + .name = "xgene-rtc", + .pm = &xgene_rtc_pm_ops, + .of_match_table = of_match_ptr(xgene_rtc_of_match), + }, +}; + +module_platform_driver(xgene_rtc_driver); + +MODULE_DESCRIPTION("APM X-Gene SoC RTC driver"); +MODULE_AUTHOR("Rameshwar Sahu <rsahu@apm.com>"); +MODULE_LICENSE("GPL"); |