[<prev] [next>] [day] [month] [year] [list]
Message-ID: <cabda6420909032359y4c36596at37961b4f24a45a98@mail.gmail.com>
Date: Fri, 4 Sep 2009 08:59:51 +0200
From: christian pellegrin <chripell@...il.com>
To: Chris Verges <chrisv@...erswitching.com>,
Andrew Morton <akpm@...ux-foundation.org>,
linux-kernel@...r.kernel.org
Subject: [PATCH -mm] rtc-pcf2123 fixes
Hi, the following patch:
fixes bug with write command bitfield
fixes bug on crashing when module unloaded but no chip present
adds access to registers via sysfs (needed for accessing calibration
register which is needed for a good stability of the RTC)
Signed-off: Christian Pellegrin <chripell@...e.org>
Tested-by: Christian Pellegrin <chripell@...e.org>
---
diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c
index 2fcff82..0c55f7d 100644
--- a/drivers/rtc/rtc-pcf2123.c
+++ b/drivers/rtc/rtc-pcf2123.c
@@ -10,6 +10,23 @@
* 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.
+ *
+ * Please note that the CS is active high, so platform data
+ * should look something like:
+
+ static struct spi_board_info ek_spi_devices[] = {
+ ...
+ {
+ .modalias = "rtc-pcf2123",
+ .chip_select = 1,
+ .controller_data = (void *) AT91_PIN_PA10,
+ .max_speed_hz = 1000 * 1000,
+ .mode = SPI_CS_HIGH,
+ .bus_num = 0,
+ },
+...
+};
+
*/
#include <linux/bcd.h>
@@ -20,10 +37,9 @@
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/rtc.h>
-#include <linux/workqueue.h>
#include <linux/spi/spi.h>
-#define DRV_VERSION "0.3"
+#define DRV_VERSION "0.4"
#define PCF2123_REG_CTRL1 0x00 /* Control Register 1 */
#define PCF2123_REG_CTRL2 0x01 /* Control Register 2 */
@@ -36,13 +52,19 @@
#define PCF2123_REG_MO 0x07
#define PCF2123_REG_YR 0x08
-#define PCF2123_CMD_W(addr) (((addr) & 0x0F) | 0x40) /* single write */
+#define PCF2123_CMD_W(addr) (((addr) & 0x0F) | 0x10) /* single write */
#define PCF2123_CMD_R(addr) (((addr) & 0x0F) | 0x90) /* single read */
static struct spi_driver pcf2123_driver;
+struct pcf2123_sysfs_reg {
+ struct device_attribute attr; /* must be first */
+ char name[2];
+};
+
struct pcf2123_plat_data {
struct rtc_device *rtc;
+ struct pcf2123_sysfs_reg regs[16];
};
/*
@@ -55,6 +77,39 @@ static inline void pcf2123_delay_trec(void)
ndelay(30);
}
+static ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr,
+ char *buffer)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct pcf2123_sysfs_reg *r = (struct pcf2123_sysfs_reg *) attr;
+ u8 txbuf[1], rxbuf[1];
+ int ret;
+
+ txbuf[0] = PCF2123_CMD_R(simple_strtoul(r->name, NULL, 16));
+ ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 1);
+ if (ret < 0)
+ return sprintf(buffer, "error: %d", ret);
+ pcf2123_delay_trec();
+ return sprintf(buffer, "0x%x", rxbuf[0]);
+}
+
+static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct pcf2123_sysfs_reg *r = (struct pcf2123_sysfs_reg *) attr;
+ u8 txbuf[2];
+ int ret;
+
+ txbuf[0] = PCF2123_CMD_W(simple_strtoul(r->name, NULL, 16));
+ txbuf[1] = simple_strtoul(buffer, NULL, 0);
+ ret = spi_write(spi, txbuf, sizeof(txbuf));
+ if (ret < 0)
+ return -EIO;
+ pcf2123_delay_trec();
+ return count;
+}
+
static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct spi_device *spi = to_spi_device(dev);
@@ -149,7 +204,7 @@ static int __devinit pcf2123_probe(struct spi_device *spi)
struct rtc_device *rtc;
struct pcf2123_plat_data *pdata;
u8 txbuf[2], rxbuf[1];
- int ret;
+ int ret, i;
pdata = kzalloc(sizeof(struct pcf2123_plat_data), GFP_KERNEL);
if (!pdata)
@@ -201,28 +256,50 @@ static int __devinit pcf2123_probe(struct spi_device *spi)
&pcf2123_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
+ dev_err(&spi->dev, "failed to register.\n");
ret = PTR_ERR(rtc);
goto kfree_exit;
}
pdata->rtc = rtc;
+ for (i = 0; i < 16; i++) {
+ sprintf(pdata->regs[i].name, "%1x", i);
+ pdata->regs[i].attr.attr.mode = S_IRUGO | S_IWUSR;
+ pdata->regs[i].attr.attr.name = pdata->regs[i].name;
+ pdata->regs[i].attr.show = pcf2123_show;
+ pdata->regs[i].attr.store = pcf2123_store;
+ ret = device_create_file(&spi->dev, &pdata->regs[i].attr);
+ if (ret) {
+ dev_err(&spi->dev, "Unable to create sysfs %s\n",
+ pdata->regs[i].name);
+ pdata->regs[i].name[0] = '\0';
+ }
+ }
+
return 0;
kfree_exit:
kfree(pdata);
+ spi->dev.platform_data = NULL;
return ret;
}
static int pcf2123_remove(struct spi_device *spi)
{
struct pcf2123_plat_data *pdata = spi->dev.platform_data;
- struct rtc_device *rtc = pdata->rtc;
-
- if (rtc)
- rtc_device_unregister(rtc);
-
- kfree(pdata);
-
+ int i;
+
+ if (pdata) {
+ struct rtc_device *rtc = pdata->rtc;
+
+ if (rtc)
+ rtc_device_unregister(rtc);
+ for (i = 0; i < 16; i++)
+ if (pdata->regs[i].name[0])
+ device_remove_file(&spi->dev,
+ &pdata->regs[i].attr);
+ kfree(pdata);
+ }
return 0;
}
--
Christian Pellegrin, see http://www.evolware.org/chri/
"Real Programmers don't play tennis, or any other sport which requires
you to change clothes. Mountain climbing is OK, and Real Programmers
wear their climbing boots to work in case a mountain should suddenly
spring up in the middle of the computer room."
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists