diff options
Diffstat (limited to 'drivers/rtc')
28 files changed, 1357 insertions, 460 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 71fbd6e8edf..8167e9e6827 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -242,6 +242,15 @@ config RTC_DRV_M41T80_WDT If you say Y here you will get support for the watchdog timer in the ST M41T60 and M41T80 RTC chips series. +config RTC_DRV_BQ32K + tristate "TI BQ32000" + help + If you say Y here you will get support for the TI + BQ32000 I2C RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-bq32k. + config RTC_DRV_DM355EVM tristate "TI DaVinci DM355 EVM RTC" depends on MFD_DM355EVM_MSP @@ -592,15 +601,22 @@ config RTC_DRV_AB3100 Select this to enable the ST-Ericsson AB3100 Mixed Signal IC RTC support. This chip contains a battery- and capacitor-backed RTC. +config RTC_DRV_NUC900 + tristate "NUC910/NUC920 RTC driver" + depends on RTC_CLASS && ARCH_W90X900 + help + If you say yes here you get support for the RTC subsystem of the + NUC910/NUC920 used in embedded systems. comment "on-CPU RTC drivers" config RTC_DRV_OMAP tristate "TI OMAP1" - depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 + depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 || ARCH_DAVINCI_DA8XX help - Say "yes" here to support the real time clock on TI OMAP1 chips. - This driver can also be built as a module called rtc-omap. + Say "yes" here to support the real time clock on TI OMAP1 and + DA8xx/OMAP-L13x chips. This driver can also be built as a + module called rtc-omap. config RTC_DRV_S3C tristate "Samsung S3C series SoC RTC" @@ -846,4 +862,10 @@ config RTC_DRV_PCAP If you say Y here you will get support for the RTC found on the PCAP2 ASIC used on some Motorola phones. +config RTC_DRV_MC13783 + depends on MFD_MC13783 + tristate "Freescale MC13783 RTC" + help + This enables support for the Freescale MC13783 PMIC RTC + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 7da6efb3e95..e5160fddc44 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o +obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o @@ -52,8 +53,10 @@ obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o obj-$(CONFIG_RTC_MXC) += rtc-mxc.o obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o +obj-$(CONFIG_RTC_DRV_MC13783) += rtc-mc13783.o obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o +obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c index e1ec33e40e3..8825695777d 100644 --- a/drivers/rtc/rtc-at32ap700x.c +++ b/drivers/rtc/rtc-at32ap700x.c @@ -256,6 +256,8 @@ static int __init at32_rtc_probe(struct platform_device *pdev) goto out_iounmap; } + platform_set_drvdata(pdev, rtc); + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &at32_rtc_ops, THIS_MODULE); if (IS_ERR(rtc->rtc)) { @@ -264,7 +266,6 @@ static int __init at32_rtc_probe(struct platform_device *pdev) goto out_free_irq; } - platform_set_drvdata(pdev, rtc); device_init_wakeup(&pdev->dev, 1); dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n", @@ -273,6 +274,7 @@ static int __init at32_rtc_probe(struct platform_device *pdev) return 0; out_free_irq: + platform_set_drvdata(pdev, NULL); free_irq(irq, rtc); out_iounmap: iounmap(rtc->regs); diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c new file mode 100644 index 00000000000..408cc8f735b --- /dev/null +++ b/drivers/rtc/rtc-bq32k.c @@ -0,0 +1,204 @@ +/* + * Driver for TI BQ32000 RTC. + * + * Copyright (C) 2009 Semihalf. + * + * 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/i2c.h> +#include <linux/rtc.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/bcd.h> + +#define BQ32K_SECONDS 0x00 /* Seconds register address */ +#define BQ32K_SECONDS_MASK 0x7F /* Mask over seconds value */ +#define BQ32K_STOP 0x80 /* Oscillator Stop flat */ + +#define BQ32K_MINUTES 0x01 /* Minutes register address */ +#define BQ32K_MINUTES_MASK 0x7F /* Mask over minutes value */ +#define BQ32K_OF 0x80 /* Oscillator Failure flag */ + +#define BQ32K_HOURS_MASK 0x3F /* Mask over hours value */ +#define BQ32K_CENT 0x40 /* Century flag */ +#define BQ32K_CENT_EN 0x80 /* Century flag enable bit */ + +struct bq32k_regs { + uint8_t seconds; + uint8_t minutes; + uint8_t cent_hours; + uint8_t day; + uint8_t date; + uint8_t month; + uint8_t years; +}; + +static struct i2c_driver bq32k_driver; + +static int bq32k_read(struct device *dev, void *data, uint8_t off, uint8_t len) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &off, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = data, + } + }; + + if (i2c_transfer(client->adapter, msgs, 2) == 2) + return 0; + + return -EIO; +} + +static int bq32k_write(struct device *dev, void *data, uint8_t off, uint8_t len) +{ + struct i2c_client *client = to_i2c_client(dev); + uint8_t buffer[len + 1]; + + buffer[0] = off; + memcpy(&buffer[1], data, len); + + if (i2c_master_send(client, buffer, len + 1) == len + 1) + return 0; + + return -EIO; +} + +static int bq32k_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct bq32k_regs regs; + int error; + + error = bq32k_read(dev, ®s, 0, sizeof(regs)); + if (error) + return error; + + tm->tm_sec = bcd2bin(regs.seconds & BQ32K_SECONDS_MASK); + tm->tm_min = bcd2bin(regs.minutes & BQ32K_SECONDS_MASK); + tm->tm_hour = bcd2bin(regs.cent_hours & BQ32K_HOURS_MASK); + tm->tm_mday = bcd2bin(regs.date); + tm->tm_wday = bcd2bin(regs.day) - 1; + tm->tm_mon = bcd2bin(regs.month) - 1; + tm->tm_year = bcd2bin(regs.years) + + ((regs.cent_hours & BQ32K_CENT) ? 100 : 0); + + return rtc_valid_tm(tm); +} + +static int bq32k_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct bq32k_regs regs; + + regs.seconds = bin2bcd(tm->tm_sec); + regs.minutes = bin2bcd(tm->tm_min); + regs.cent_hours = bin2bcd(tm->tm_hour) | BQ32K_CENT_EN; + regs.day = bin2bcd(tm->tm_wday + 1); + regs.date = bin2bcd(tm->tm_mday); + regs.month = bin2bcd(tm->tm_mon + 1); + + if (tm->tm_year >= 100) { + regs.cent_hours |= BQ32K_CENT; + regs.years = bin2bcd(tm->tm_year - 100); + } else + regs.years = bin2bcd(tm->tm_year); + + return bq32k_write(dev, ®s, 0, sizeof(regs)); +} + +static const struct rtc_class_ops bq32k_rtc_ops = { + .read_time = bq32k_rtc_read_time, + .set_time = bq32k_rtc_set_time, +}; + +static int bq32k_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct rtc_device *rtc; + uint8_t reg; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + /* Check Oscillator Stop flag */ + error = bq32k_read(dev, ®, BQ32K_SECONDS, 1); + if (!error && (reg & BQ32K_STOP)) { + dev_warn(dev, "Oscillator was halted. Restarting...\n"); + reg &= ~BQ32K_STOP; + error = bq32k_write(dev, ®, BQ32K_SECONDS, 1); + } + if (error) + return error; + + /* Check Oscillator Failure flag */ + error = bq32k_read(dev, ®, BQ32K_MINUTES, 1); + if (!error && (reg & BQ32K_OF)) { + dev_warn(dev, "Oscillator Failure. Check RTC battery.\n"); + reg &= ~BQ32K_OF; + error = bq32k_write(dev, ®, BQ32K_MINUTES, 1); + } + if (error) + return error; + + rtc = rtc_device_register(bq32k_driver.driver.name, &client->dev, + &bq32k_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + i2c_set_clientdata(client, rtc); + + return 0; +} + +static int __devexit bq32k_remove(struct i2c_client *client) +{ + struct rtc_device *rtc = i2c_get_clientdata(client); + + rtc_device_unregister(rtc); + return 0; +} + +static const struct i2c_device_id bq32k_id[] = { + { "bq32000", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bq32k_id); + +static struct i2c_driver bq32k_driver = { + .driver = { + .name = "bq32k", + .owner = THIS_MODULE, + }, + .probe = bq32k_probe, + .remove = __devexit_p(bq32k_remove), + .id_table = bq32k_id, +}; + +static __init int bq32k_init(void) +{ + return i2c_add_driver(&bq32k_driver); +} +module_init(bq32k_init); + +static __exit void bq32k_exit(void) +{ + i2c_del_driver(&bq32k_driver); +} +module_exit(bq32k_exit); + +MODULE_AUTHOR("Semihalf, Piotr Ziecik <kosmo@semihalf.com>"); +MODULE_DESCRIPTION("TI BQ32000 I2C RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-bq4802.c b/drivers/rtc/rtc-bq4802.c index d00a274df8f..280fe48ada0 100644 --- a/drivers/rtc/rtc-bq4802.c +++ b/drivers/rtc/rtc-bq4802.c @@ -169,6 +169,8 @@ static int __devinit bq4802_probe(struct platform_device *pdev) goto out_free; } + platform_set_drvdata(pdev, p); + p->rtc = rtc_device_register("bq4802", &pdev->dev, &bq4802_ops, THIS_MODULE); if (IS_ERR(p->rtc)) { @@ -176,7 +178,6 @@ static int __devinit bq4802_probe(struct platform_device *pdev) goto out_iounmap; } - platform_set_drvdata(pdev, p); err = 0; out: return err; diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index f7a4701bf86..eb154dc5716 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -420,49 +420,43 @@ static int cmos_irq_set_state(struct device *dev, int enabled) return 0; } -#if defined(CONFIG_RTC_INTF_DEV) || defined(CONFIG_RTC_INTF_DEV_MODULE) - -static int -cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +static int cmos_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct cmos_rtc *cmos = dev_get_drvdata(dev); unsigned long flags; - switch (cmd) { - case RTC_AIE_OFF: - case RTC_AIE_ON: - case RTC_UIE_OFF: - case RTC_UIE_ON: - if (!is_valid_irq(cmos->irq)) - return -EINVAL; - break; - /* PIE ON/OFF is handled by cmos_irq_set_state() */ - default: - return -ENOIOCTLCMD; - } + if (!is_valid_irq(cmos->irq)) + return -EINVAL; spin_lock_irqsave(&rtc_lock, flags); - switch (cmd) { - case RTC_AIE_OFF: /* alarm off */ - cmos_irq_disable(cmos, RTC_AIE); - break; - case RTC_AIE_ON: /* alarm on */ + + if (enabled) cmos_irq_enable(cmos, RTC_AIE); - break; - case RTC_UIE_OFF: /* update off */ - cmos_irq_disable(cmos, RTC_UIE); - break; - case RTC_UIE_ON: /* update on */ - cmos_irq_enable(cmos, RTC_UIE); - break; - } + else + cmos_irq_disable(cmos, RTC_AIE); + spin_unlock_irqrestore(&rtc_lock, flags); return 0; } -#else -#define cmos_rtc_ioctl NULL -#endif +static int cmos_update_irq_enable(struct device *dev, unsigned int enabled) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + unsigned long flags; + + if (!is_valid_irq(cmos->irq)) + return -EINVAL; + + spin_lock_irqsave(&rtc_lock, flags); + + if (enabled) + cmos_irq_enable(cmos, RTC_UIE); + else + cmos_irq_disable(cmos, RTC_UIE); + + spin_unlock_irqrestore(&rtc_lock, flags); + return 0; +} #if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE) @@ -503,14 +497,15 @@ static int cmos_procfs(struct device *dev, struct seq_file *seq) #endif static const struct rtc_class_ops cmos_rtc_ops = { - .ioctl = cmos_rtc_ioctl, - .read_time = cmos_read_time, - .set_time = cmos_set_time, - .read_alarm = cmos_read_alarm, - .set_alarm = cmos_set_alarm, - .proc = cmos_procfs, - .irq_set_freq = cmos_irq_set_freq, - .irq_set_state = cmos_irq_set_state, + .read_time = cmos_read_time, + .set_time = cmos_set_time, + .read_alarm = cmos_read_alarm, + .set_alarm = cmos_set_alarm, + .proc = cmos_procfs, + .irq_set_freq = cmos_irq_set_freq, + .irq_set_state = cmos_irq_set_state, + .alarm_irq_enable = cmos_alarm_irq_enable, + .update_irq_enable = cmos_update_irq_enable, }; /*----------------------------------------------------------------*/ @@ -871,8 +866,9 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg) mask = RTC_IRQMASK; tmp &= ~mask; CMOS_WRITE(tmp, RTC_CONTROL); - hpet_mask_rtc_irq_bit(mask); + /* shut down hpet emulation - we don't need it for alarm */ + hpet_mask_rtc_irq_bit(RTC_PIE|RTC_AIE|RTC_UIE); cmos_checkintr(cmos, tmp); } spin_unlock_irq(&rtc_lock); diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c index 1e73c8f42e3..532acf9b05d 100644 --- a/drivers/rtc/rtc-ds1302.c +++ b/drivers/rtc/rtc-ds1302.c @@ -143,7 +143,6 @@ static int ds1302_rtc_ioctl(struct device *dev, unsigned int cmd, #ifdef RTC_SET_CHARGE case RTC_SET_CHARGE: { - struct ds1302_rtc *rtc = dev_get_drvdata(dev); int tcs_val; if (copy_from_user(&tcs_val, (int __user *)arg, sizeof(int))) diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c index 2736b11a1b1..259db7f3535 100644 --- a/drivers/rtc/rtc-ds1305.c +++ b/drivers/rtc/rtc-ds1305.c @@ -617,7 +617,6 @@ static struct bin_attribute nvram = { static int __devinit ds1305_probe(struct spi_device *spi) { struct ds1305 *ds1305; - struct rtc_device *rtc; int status; u8 addr, value; struct ds1305_platform_data *pdata = spi->dev.platform_data; @@ -756,14 +755,13 @@ static int __devinit ds1305_probe(struct spi_device *spi) dev_dbg(&spi->dev, "AM/PM\n"); /* register RTC ... from here on, ds1305->ctrl needs locking */ - rtc = rtc_device_register("ds1305", &spi->dev, + ds1305->rtc = rtc_device_register("ds1305", &spi->dev, &ds1305_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - status = PTR_ERR(rtc); + if (IS_ERR(ds1305->rtc)) { + status = PTR_ERR(ds1305->rtc); dev_dbg(&spi->dev, "register rtc --> %d\n", status); goto fail0; } - ds1305->rtc = rtc; /* Maybe set up alarm IRQ; be ready to handle it triggering right * away. NOTE that we don't share this. The signal is active low, @@ -774,7 +772,7 @@ static int __devinit ds1305_probe(struct spi_device *spi) if (spi->irq) { INIT_WORK(&ds1305->work, ds1305_work); status = request_irq(spi->irq, ds1305_irq, - 0, dev_name(&rtc->dev), ds1305); + 0, dev_name(&ds1305->rtc->dev), ds1305); if (status < 0) { dev_dbg(&spi->dev, "request_irq %d --> %d\n", spi->irq, status); @@ -794,7 +792,7 @@ static int __devinit ds1305_probe(struct spi_device *spi) fail2: free_irq(spi->irq, ds1305); fail1: - rtc_device_unregister(rtc); + rtc_device_unregister(ds1305->rtc); fail0: kfree(ds1305); return status; @@ -802,7 +800,7 @@ fail0: static int __devexit ds1305_remove(struct spi_device *spi) { - struct ds1305 *ds1305 = spi_get_drvdata(spi); + struct ds1305 *ds1305 = spi_get_drvdata(spi); sysfs_remove_bin_file(&spi->dev.kobj, &nvram); diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index eb99ee4fa0f..8a99da6f2f2 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -874,7 +874,7 @@ read_rtc: } if (want_irq) { - err = request_irq(client->irq, ds1307_irq, 0, + err = request_irq(client->irq, ds1307_irq, IRQF_SHARED, ds1307->rtc->name, client); if (err) { dev_err(&client->dev, diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index 539676e25fd..4166b84cb51 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -87,7 +87,6 @@ enum ds1511reg { struct rtc_plat_data { struct rtc_device *rtc; void __iomem *ioaddr; /* virtual base address */ - unsigned long baseaddr; /* physical base address */ int size; /* amount of memory mapped */ int irq; unsigned int irqen; @@ -95,6 +94,7 @@ struct rtc_plat_data { int alrm_min; int alrm_hour; int alrm_mday; + spinlock_t lock; }; static DEFINE_SPINLOCK(ds1511_lock); @@ -302,7 +302,7 @@ ds1511_rtc_update_alarm(struct rtc_plat_data *pdata) { unsigned long flags; - spin_lock_irqsave(&pdata->rtc->irq_lock, flags); + spin_lock_irqsave(&pdata->lock, flags); rtc_write(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ? 0x80 : bin2bcd(pdata->alrm_mday) & 0x3f, RTC_ALARM_DATE); @@ -317,7 +317,7 @@ ds1511_rtc_update_alarm(struct rtc_plat_data *pdata) RTC_ALARM_SEC); rtc_write(rtc_read(RTC_CMD) | (pdata->irqen ? RTC_TIE : 0), RTC_CMD); rtc_read(RTC_CMD1); /* clear interrupts */ - spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags); + spin_unlock_irqrestore(&pdata->lock, flags); } static int @@ -362,61 +362,63 @@ ds1511_interrupt(int irq, void *dev_id) { struct platform_device *pdev = dev_id; struct rtc_plat_data *pdata = platform_get_drvdata(pdev); - unsigned long events = RTC_IRQF; + unsigned long events = 0; + spin_lock(&pdata->lock); /* * read and clear interrupt */ - if (!(rtc_read(RTC_CMD1) & DS1511_IRQF)) { - return IRQ_NONE; - } - if (rtc_read(RTC_ALARM_SEC) & 0x80) { - events |= RTC_UF; - } else { - events |= RTC_AF; - } - rtc_update_irq(pdata->rtc, 1, events); - return IRQ_HANDLED; + if (rtc_read(RTC_CMD1) & DS1511_IRQF) { + events = RTC_IRQF; + if (rtc_read(RTC_ALARM_SEC) & 0x80) + events |= RTC_UF; + else + events |= RTC_AF; + if (likely(pdata->rtc)) + rtc_update_irq(pdata->rtc, 1, events); + } + spin_unlock(&pdata->lock); + return events ? IRQ_HANDLED : IRQ_NONE; } - static int -ds1511_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +static int ds1511_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct platform_device *pdev = to_platform_device(dev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev); - if (pdata->irq <= 0) { - return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */ - } - switch (cmd) { - case RTC_AIE_OFF: - pdata->irqen &= ~RTC_AF; - ds1511_rtc_update_alarm(pdata); - break; - case RTC_AIE_ON: + if (pdata->irq <= 0) + return -EINVAL; + if (enabled) pdata->irqen |= RTC_AF; - ds1511_rtc_update_alarm(pdata); - break; - case RTC_UIE_OFF: - pdata->irqen &= ~RTC_UF; - ds1511_rtc_update_alarm(pdata); - break; - case RTC_UIE_ON: + else + pdata->irqen &= ~RTC_AF; + ds1511_rtc_update_alarm(pdata); + return 0; +} + +static int ds1511_rtc_update_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + + if (pdata->irq <= 0) + return -EINVAL; + if (enabled) pdata->irqen |= RTC_UF; - ds1511_rtc_update_alarm(pdata); - break; - default: - return -ENOIOCTLCMD; - } + else + pdata->irqen &= ~RTC_UF; + ds1511_rtc_update_alarm(pdata); return 0; } static const struct rtc_class_ops ds1511_rtc_ops = { - .read_time = ds1511_rtc_read_time, - .set_time = ds1511_rtc_set_time, - .read_alarm = ds1511_rtc_read_alarm, - .set_alarm = ds1511_rtc_set_alarm, - .ioctl = ds1511_rtc_ioctl, + .read_time = ds1511_rtc_read_time, + .set_time = ds1511_rtc_set_time, + .read_alarm = ds1511_rtc_read_alarm, + .set_alarm = ds1511_rtc_set_alarm, + .alarm_irq_enable = ds1511_rtc_alarm_irq_enable, + .update_irq_enable = ds1511_rtc_update_irq_enable, }; static ssize_t @@ -492,29 +494,23 @@ ds1511_rtc_probe(struct platform_device *pdev) { struct rtc_device *rtc; struct resource *res; - struct rtc_plat_data *pdata = NULL; + struct rtc_plat_data *pdata; int ret = 0; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { return -ENODEV; } - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); - if (!pdata) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) return -ENOMEM; - } pdata->size = res->end - res->start + 1; - if (!request_mem_region(res->start, pdata->size, pdev->name)) { - ret = -EBUSY; - goto out; - } - pdata->baseaddr = res->start; - pdata->size = pdata->size; - ds1511_base = ioremap(pdata->baseaddr, pdata->size); - if (!ds1511_base) { - ret = -ENOMEM; - goto out; - } + if (!devm_request_mem_region(&pdev->dev, res->start, pdata->size, + pdev->name)) + return -EBUSY; + ds1511_base = devm_ioremap(&pdev->dev, res->start, pdata->size); + if (!ds1511_base) + return -ENOMEM; pdata->ioaddr = ds1511_base; pdata->irq = platform_get_irq(pdev, 0); @@ -540,13 +536,15 @@ ds1511_rtc_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "voltage-low detected.\n"); } + spin_lock_init(&pdata->lock); + platform_set_drvdata(pdev, pdata); /* * if the platform has an interrupt in mind for this device, * then by all means, set it */ if (pdata->irq > 0) { rtc_read(RTC_CMD1); - if (request_irq(pdata->irq, ds1511_interrupt, + if (devm_request_irq(&pdev->dev, pdata->irq, ds1511_interrupt, IRQF_DISABLED | IRQF_SHARED, pdev->name, pdev) < 0) { dev_warn(&pdev->dev, "interrupt not available.\n"); @@ -556,33 +554,13 @@ ds1511_rtc_probe(struct platform_device *pdev) rtc = rtc_device_register(pdev->name, &pdev->dev, &ds1511_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - ret = PTR_ERR(rtc); - goto out; - } + if (IS_ERR(rtc)) + return PTR_ERR(rtc); pdata->rtc = rtc; - platform_set_drvdata(pdev, pdata); + ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr); - if (ret) { - goto out; - } - return 0; - out: - if (pdata->rtc) { + if (ret) rtc_device_unregister(pdata->rtc); - } - if (pdata->irq > 0) { - free_irq(pdata->irq, pdev); - } - if (ds1511_base) { - iounmap(ds1511_base); - ds1511_base = NULL; - } - if (pdata->baseaddr) { - release_mem_region(pdata->baseaddr, pdata->size); - } - - kfree(pdata); return ret; } @@ -593,19 +571,13 @@ ds1511_rtc_remove(struct platform_device *pdev) sysfs_remove_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr); rtc_device_unregister(pdata->rtc); - pdata->rtc = NULL; if (pdata->irq > 0) { /* * disable the alarm interrupt */ rtc_write(rtc_read(RTC_CMD) & ~RTC_TIE, RTC_CMD); rtc_read(RTC_CMD1); - free_irq(pdata->irq, pdev); } - iounmap(pdata->ioaddr); - ds1511_base = NULL; - release_mem_region(pdata->baseaddr, pdata->size); - kfree(pdata); return 0; } diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c index 717288527c6..ed1ef7c9cc0 100644 --- a/drivers/rtc/rtc-ds1553.c +++ b/drivers/rtc/rtc-ds1553.c @@ -18,7 +18,7 @@ #include <linux/platform_device.h> #include <linux/io.h> -#define DRV_VERSION "0.2" +#define DRV_VERSION "0.3" #define RTC_REG_SIZE 0x2000 #define RTC_OFFSET 0x1ff0 @@ -61,7 +61,6 @@ struct rtc_plat_data { struct rtc_device *rtc; void __iomem *ioaddr; - resource_size_t baseaddr; unsigned long last_jiffies; int irq; unsigned int irqen; @@ -69,6 +68,7 @@ struct rtc_plat_data { int alrm_min; int alrm_hour; int alrm_mday; + spinlock_t lock; }; static int ds1553_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -139,7 +139,7 @@ static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata) void __iomem *ioaddr = pdata->ioaddr; unsigned long flags; - spin_lock_irqsave(&pdata->rtc->irq_lock, flags); + spin_lock_irqsave(&pdata->lock, flags); writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ? 0x80 : bin2bcd(pdata->alrm_mday), ioaddr + RTC_DATE_ALARM); @@ -154,7 +154,7 @@ static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata) ioaddr + RTC_SECONDS_ALARM); writeb(pdata->irqen ? RTC_INTS_AE : 0, ioaddr + RTC_INTERRUPTS); readb(ioaddr + RTC_FLAGS); /* clear interrupts */ - spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags); + spin_unlock_irqrestore(&pdata->lock, flags); } static int ds1553_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) @@ -194,64 +194,69 @@ static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id) struct platform_device *pdev = dev_id; struct rtc_plat_data *pdata = platform_get_drvdata(pdev); void __iomem *ioaddr = pdata->ioaddr; - unsigned long events = RTC_IRQF; + unsigned long events = 0; + spin_lock(&pdata->lock); /* read and clear interrupt */ - if (!(readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF)) - return IRQ_NONE; - if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80) - events |= RTC_UF; - else - events |= RTC_AF; - rtc_update_irq(pdata->rtc, 1, events); - return IRQ_HANDLED; + if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF) { + events = RTC_IRQF; + if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80) + events |= RTC_UF; + else + events |= RTC_AF; + if (likely(pdata->rtc)) + rtc_update_irq(pdata->rtc, 1, events); + } + spin_unlock(&pdata->lock); + return events ? IRQ_HANDLED : IRQ_NONE; } -static int ds1553_rtc_ioctl(struct device *dev, unsigned int cmd, - unsigned long arg) +static int ds1553_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct platform_device *pdev = to_platform_device(dev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev); if (pdata->irq <= 0) - return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */ - switch (cmd) { - case RTC_AIE_OFF: - pdata->irqen &= ~RTC_AF; - ds1553_rtc_update_alarm(pdata); - break; - case RTC_AIE_ON: + return -EINVAL; + if (enabled) pdata->irqen |= RTC_AF; - ds1553_rtc_update_alarm(pdata); - break; - case RTC_UIE_OFF: - pdata->irqen &= ~RTC_UF; - ds1553_rtc_update_alarm(pdata); - break; - case RTC_UIE_ON: + else + pdata->irqen &= ~RTC_AF; + ds1553_rtc_update_alarm(pdata); + return 0; +} + +static int ds1553_rtc_update_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + + if (pdata->irq <= 0) + return -EINVAL; + if (enabled) pdata->irqen |= RTC_UF; - ds1553_rtc_update_alarm(pdata); - break; - default: - return -ENOIOCTLCMD; - } + else + pdata->irqen &= ~RTC_UF; + ds1553_rtc_update_alarm(pdata); return 0; } static const struct rtc_class_ops ds1553_rtc_ops = { - .read_ |