summaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/rtc-ds1307.c176
1 files changed, 124 insertions, 52 deletions
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 3f0f7b8fa813..75e30c6a7d5d 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -24,15 +24,22 @@
* setting the date and time), Linux can ignore the non-clock features.
* That's a natural job for a factory or repair bench.
*
+ * This is currently a simple no-alarms driver. If your board has the
+ * alarm irq wired up on a ds1337 or ds1339, and you want to use that,
+ * then look at the rtc-rs5c372 driver for code to steal...
+ *
* If the I2C "force" mechanism is used, we assume the chip is a ds1337.
* (Much better would be board-specific tables of I2C devices, along with
* the platform_data drivers would use to sort such issues out.)
*/
enum ds_type {
unknown = 0,
- ds_1307, /* or ds1338, ... */
- ds_1337, /* or ds1339, ... */
- ds_1340, /* or st m41t00, ... */
+ ds_1307,
+ ds_1337,
+ ds_1338,
+ ds_1339,
+ ds_1340,
+ m41t00,
// rs5c372 too? different address...
};
@@ -56,11 +63,12 @@ I2C_CLIENT_INSMOD;
#define DS1307_REG_YEAR 0x06 /* 00-99 */
/* Other registers (control, status, alarms, trickle charge, NVRAM, etc)
- * start at 7, and they differ a lot. Only control and status matter for RTC;
- * be careful using them.
+ * start at 7, and they differ a LOT. Only control and status matter for
+ * basic RTC date and time functionality; be careful using them.
*/
-#define DS1307_REG_CONTROL 0x07
+#define DS1307_REG_CONTROL 0x07 /* or ds1338 */
# define DS1307_BIT_OUT 0x80
+# define DS1338_BIT_STOP 0x20
# define DS1307_BIT_SQWE 0x10
# define DS1307_BIT_RS1 0x02
# define DS1307_BIT_RS0 0x01
@@ -71,6 +79,13 @@ I2C_CLIENT_INSMOD;
# define DS1337_BIT_INTCN 0x04
# define DS1337_BIT_A2IE 0x02
# define DS1337_BIT_A1IE 0x01
+#define DS1340_REG_CONTROL 0x07
+# define DS1340_BIT_OUT 0x80
+# define DS1340_BIT_FT 0x40
+# define DS1340_BIT_CALIB_SIGN 0x20
+# define DS1340_M_CALIBRATION 0x1f
+#define DS1338_REG_FLAG 0x09
+# define DS1338_BIT_OSF 0x80
#define DS1337_REG_STATUS 0x0f
# define DS1337_BIT_OSF 0x80
# define DS1337_BIT_A2I 0x02
@@ -84,21 +99,63 @@ struct ds1307 {
u8 regs[8];
enum ds_type type;
struct i2c_msg msg[2];
- struct i2c_client client;
+ struct i2c_client *client;
+ struct i2c_client dev;
struct rtc_device *rtc;
};
+struct chip_desc {
+ char name[9];
+ unsigned nvram56:1;
+ unsigned alarm:1;
+ enum ds_type type;
+};
+
+static const struct chip_desc chips[] = { {
+ .name = "ds1307",
+ .type = ds_1307,
+ .nvram56 = 1,
+}, {
+ .name = "ds1337",
+ .type = ds_1337,
+ .alarm = 1,
+}, {
+ .name = "ds1338",
+ .type = ds_1338,
+ .nvram56 = 1,
+}, {
+ .name = "ds1339",
+ .type = ds_1339,
+ .alarm = 1,
+}, {
+ .name = "ds1340",
+ .type = ds_1340,
+}, {
+ .name = "m41t00",
+ .type = m41t00,
+}, };
+
+static inline const struct chip_desc *find_chip(const char *s)
+{
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(chips); i++)
+ if (strnicmp(s, chips[i].name, sizeof chips[i].name) == 0)
+ return &chips[i];
+ return NULL;
+}
static int ds1307_get_time(struct device *dev, struct rtc_time *t)
{
struct ds1307 *ds1307 = dev_get_drvdata(dev);
int tmp;
- /* read the RTC registers all at once */
+ /* read the RTC date and time registers all at once */
ds1307->msg[1].flags = I2C_M_RD;
ds1307->msg[1].len = 7;
- tmp = i2c_transfer(ds1307->client.adapter, ds1307->msg, 2);
+ tmp = i2c_transfer(to_i2c_adapter(ds1307->client->dev.parent),
+ ds1307->msg, 2);
if (tmp != 2) {
dev_err(dev, "%s error %d\n", "read", tmp);
return -EIO;
@@ -129,7 +186,8 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
t->tm_hour, t->tm_mday,
t->tm_mon, t->tm_year, t->tm_wday);
- return 0;
+ /* initial clock setting can be undefined */
+ return rtc_valid_tm(t);
}
static int ds1307_set_time(struct device *dev, struct rtc_time *t)
@@ -170,7 +228,8 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
"write", buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6]);
- result = i2c_transfer(ds1307->client.adapter, &ds1307->msg[1], 1);
+ result = i2c_transfer(to_i2c_adapter(ds1307->client->dev.parent),
+ &ds1307->msg[1], 1);
if (result != 1) {
dev_err(dev, "%s error %d\n", "write", tmp);
return -EIO;
@@ -192,18 +251,34 @@ ds1307_detect(struct i2c_adapter *adapter, int address, int kind)
int err = -ENODEV;
struct i2c_client *client;
int tmp;
+ const struct chip_desc *chip;
if (!(ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
- client = &ds1307->client;
+ /* REVISIT: pending driver model conversion, set up "client"
+ * ourselves, and use a hack to determine the RTC type (instead
+ * of reading the client->name we're given)
+ */
+ client = &ds1307->dev;
client->addr = address;
client->adapter = adapter;
client->driver = &ds1307_driver;
- client->flags = 0;
+ /* HACK: "force" implies "needs ds1337-style-oscillator setup", and
+ * that's the only kind of chip setup we'll know about. Until the
+ * driver model conversion, here's where to add any board-specific
+ * code to say what kind of chip is present...
+ */
+ if (kind >= 0)
+ chip = find_chip("ds1337");
+ else
+ chip = find_chip("ds1307");
+ strlcpy(client->name, chip->name, I2C_NAME_SIZE);
+
+ ds1307->client = client;
i2c_set_clientdata(client, ds1307);
ds1307->msg[0].addr = client->addr;
@@ -216,14 +291,17 @@ ds1307_detect(struct i2c_adapter *adapter, int address, int kind)
ds1307->msg[1].len = sizeof(ds1307->regs);
ds1307->msg[1].buf = ds1307->regs;
- /* HACK: "force" implies "needs ds1337-style-oscillator setup" */
- if (kind >= 0) {
+ ds1307->type = chip->type;
+
+ switch (ds1307->type) {
+ case ds_1337:
+ case ds_1339:
ds1307->type = ds_1337;
ds1307->reg_addr = DS1337_REG_CONTROL;
ds1307->msg[1].len = 2;
- tmp = i2c_transfer(client->adapter, ds1307->msg, 2);
+ tmp = i2c_transfer(adapter, ds1307->msg, 2);
if (tmp != 2) {
pr_debug("read error %d\n", tmp);
err = -EIO;
@@ -236,16 +314,20 @@ ds1307_detect(struct i2c_adapter *adapter, int address, int kind)
/* oscillator is off; need to turn it on */
if ((ds1307->regs[0] & DS1337_BIT_nEOSC)
|| (ds1307->regs[1] & DS1337_BIT_OSF)) {
- printk(KERN_ERR "no ds1337 oscillator code\n");
+no_osc_start:
+ printk(KERN_ERR "no %s oscillator code\n",
+ chip->name);
goto exit_free;
}
- } else
- ds1307->type = ds_1307;
+ break;
+ default:
+ break;
+ }
read_rtc:
/* read RTC registers */
- tmp = i2c_transfer(client->adapter, ds1307->msg, 2);
+ tmp = i2c_transfer(adapter, ds1307->msg, 2);
if (tmp != 2) {
pr_debug("read error %d\n", tmp);
err = -EIO;
@@ -257,20 +339,27 @@ read_rtc:
* still a few values that are clearly out-of-range.
*/
tmp = ds1307->regs[DS1307_REG_SECS];
- if (tmp & DS1307_BIT_CH) {
- if (ds1307->type && ds1307->type != ds_1307) {
- pr_debug("not a ds1307?\n");
- goto exit_free;
+ switch (ds1307->type) {
+ case ds_1307:
+ case ds_1338:
+ case m41t00:
+ if (tmp & DS1307_BIT_CH) {
+ i2c_smbus_write_byte_data(client, 0, 0);
+ dev_warn(&client->dev,
+ "oscillator started; SET TIME!\n");
+ goto read_rtc;
}
- ds1307->type = ds_1307;
-
- /* this partial initialization should work for ds1307,
- * ds1338, ds1340, st m41t00, and more.
- */
- dev_warn(&client->dev, "oscillator started; SET TIME!\n");
- i2c_smbus_write_byte_data(client, 0, 0);
- goto read_rtc;
+ break;
+ case ds_1340:
+ /* FIXME write code to start the oscillator */
+ if (tmp & DS1307_BIT_CH)
+ goto no_osc_start;
+ break;
+ default:
+ break;
}
+
+ tmp = ds1307->regs[DS1307_REG_SECS];
tmp = BCD2BIN(tmp & 0x7f);
if (tmp > 60)
goto exit_free;
@@ -288,6 +377,9 @@ read_rtc:
/* force into in 24 hour mode (most chips) or
* disable century bit (ds1340)
+ *
+ * REVISIT forcing 24 hour mode can prevent multi-master
+ * configs from sharing this RTC ... don't do this.
*/
tmp = ds1307->regs[DS1307_REG_HOUR];
if (tmp & (1 << 6)) {
@@ -300,26 +392,6 @@ read_rtc:
BIN2BCD(tmp));
}
- /* FIXME chips like 1337 can generate alarm irqs too; those are
- * worth exposing through the API (especially when the irq is
- * wakeup-capable).
- */
-
- switch (ds1307->type) {
- case unknown:
- strlcpy(client->name, "unknown", I2C_NAME_SIZE);
- break;
- case ds_1307:
- strlcpy(client->name, "ds1307", I2C_NAME_SIZE);
- break;
- case ds_1337:
- strlcpy(client->name, "ds1337", I2C_NAME_SIZE);
- break;
- case ds_1340:
- strlcpy(client->name, "ds1340", I2C_NAME_SIZE);
- break;
- }
-
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(client)))
goto exit_free;
OpenPOWER on IntegriCloud