*
* Copyright (C) 2005 Pavel Mironchik <pmironchik@optifacio.net>
* Copyright (C) 2006 Tower Technologies
+ * Copyright (C) 2008 Paul Mundt
*
* 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
#include <linux/rtc.h>
#include <linux/bcd.h>
-#define DRV_VERSION "0.5"
+#define DRV_VERSION "0.6"
/*
enum rtc_type type;
unsigned time24:1;
unsigned has_irq:1;
+ unsigned smbus:1;
char buf[17];
char *regs;
};
*
* The first method doesn't work with the iop3xx adapter driver, on at
* least 80219 chips; this works around that bug.
+ *
+ * The third method on the other hand doesn't work for the SMBus-only
+ * configurations, so we use the the first method there, stripping off
+ * the extra register in the process.
*/
- if ((i2c_transfer(client->adapter, msgs, 1)) != 1) {
- dev_warn(&client->dev, "can't read registers\n");
- return -EIO;
+ if (rs5c->smbus) {
+ int addr = RS5C_ADDR(RS5C372_REG_SECS);
+ int size = sizeof(rs5c->buf) - 1;
+
+ if (i2c_smbus_read_i2c_block_data(client, addr, size,
+ rs5c->buf + 1) != size) {
+ dev_warn(&client->dev, "can't read registers\n");
+ return -EIO;
+ }
+ } else {
+ if ((i2c_transfer(client->adapter, msgs, 1)) != 1) {
+ dev_warn(&client->dev, "can't read registers\n");
+ return -EIO;
+ }
}
dev_dbg(&client->dev,
{
struct rs5c372 *rs5c = i2c_get_clientdata(client);
unsigned char buf[8];
+ int addr;
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d "
"mday=%d, mon=%d, year=%d, wday=%d\n",
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
- buf[0] = RS5C_ADDR(RS5C372_REG_SECS);
- buf[1] = BIN2BCD(tm->tm_sec);
- buf[2] = BIN2BCD(tm->tm_min);
- buf[3] = rs5c_hr2reg(rs5c, tm->tm_hour);
- buf[4] = BIN2BCD(tm->tm_wday);
- buf[5] = BIN2BCD(tm->tm_mday);
- buf[6] = BIN2BCD(tm->tm_mon + 1);
- buf[7] = BIN2BCD(tm->tm_year - 100);
+ addr = RS5C_ADDR(RS5C372_REG_SECS);
+ buf[0] = BIN2BCD(tm->tm_sec);
+ buf[1] = BIN2BCD(tm->tm_min);
+ buf[2] = rs5c_hr2reg(rs5c, tm->tm_hour);
+ buf[3] = BIN2BCD(tm->tm_wday);
+ buf[4] = BIN2BCD(tm->tm_mday);
+ buf[5] = BIN2BCD(tm->tm_mon + 1);
+ buf[6] = BIN2BCD(tm->tm_year - 100);
- if ((i2c_master_send(client, buf, 8)) != 8) {
+ if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) {
dev_err(&client->dev, "%s: write error\n", __func__);
return -EIO;
}
{
struct i2c_client *client = to_i2c_client(dev);
struct rs5c372 *rs5c = i2c_get_clientdata(client);
- unsigned char buf[2];
- int status;
+ unsigned char buf;
+ int status, addr;
- buf[1] = rs5c->regs[RS5C_REG_CTRL1];
+ buf = rs5c->regs[RS5C_REG_CTRL1];
switch (cmd) {
case RTC_UIE_OFF:
case RTC_UIE_ON:
/* some 327a modes use a different IRQ pin for 1Hz irqs */
if (rs5c->type == rtc_rs5c372a
- && (buf[1] & RS5C372A_CTRL1_SL1))
+ && (buf & RS5C372A_CTRL1_SL1))
return -ENOIOCTLCMD;
case RTC_AIE_OFF:
case RTC_AIE_ON:
if (status < 0)
return status;
- buf[0] = RS5C_ADDR(RS5C_REG_CTRL1);
+ addr = RS5C_ADDR(RS5C_REG_CTRL1);
switch (cmd) {
case RTC_AIE_OFF: /* alarm off */
- buf[1] &= ~RS5C_CTRL1_AALE;
+ buf &= ~RS5C_CTRL1_AALE;
break;
case RTC_AIE_ON: /* alarm on */
- buf[1] |= RS5C_CTRL1_AALE;
+ buf |= RS5C_CTRL1_AALE;
break;
case RTC_UIE_OFF: /* update off */
- buf[1] &= ~RS5C_CTRL1_CT_MASK;
+ buf &= ~RS5C_CTRL1_CT_MASK;
break;
case RTC_UIE_ON: /* update on */
- buf[1] &= ~RS5C_CTRL1_CT_MASK;
- buf[1] |= RS5C_CTRL1_CT4;
+ buf &= ~RS5C_CTRL1_CT_MASK;
+ buf |= RS5C_CTRL1_CT4;
break;
}
- if ((i2c_master_send(client, buf, 2)) != 2) {
+
+ if (i2c_smbus_write_byte_data(client, addr, buf) < 0) {
printk(KERN_WARNING "%s: can't update alarm\n",
rs5c->rtc->name);
status = -EIO;
} else
- rs5c->regs[RS5C_REG_CTRL1] = buf[1];
+ rs5c->regs[RS5C_REG_CTRL1] = buf;
+
return status;
}
{
struct i2c_client *client = to_i2c_client(dev);
struct rs5c372 *rs5c = i2c_get_clientdata(client);
- int status;
- unsigned char buf[4];
+ int status, addr, i;
+ unsigned char buf[3];
/* only handle up to 24 hours in the future, like RTC_ALM_SET */
if (t->time.tm_mday != -1
if (status < 0)
return status;
if (rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE) {
- buf[0] = RS5C_ADDR(RS5C_REG_CTRL1);
- buf[1] = rs5c->regs[RS5C_REG_CTRL1] & ~RS5C_CTRL1_AALE;
- if (i2c_master_send(client, buf, 2) != 2) {
+ addr = RS5C_ADDR(RS5C_REG_CTRL1);
+ buf[0] = rs5c->regs[RS5C_REG_CTRL1] & ~RS5C_CTRL1_AALE;
+ if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0) {
pr_debug("%s: can't disable alarm\n", rs5c->rtc->name);
return -EIO;
}
- rs5c->regs[RS5C_REG_CTRL1] = buf[1];
+ rs5c->regs[RS5C_REG_CTRL1] = buf[0];
}
/* set alarm */
- buf[0] = RS5C_ADDR(RS5C_REG_ALARM_A_MIN);
- buf[1] = BIN2BCD(t->time.tm_min);
- buf[2] = rs5c_hr2reg(rs5c, t->time.tm_hour);
- buf[3] = 0x7f; /* any/all days */
- if ((i2c_master_send(client, buf, 4)) != 4) {
- pr_debug("%s: can't set alarm time\n", rs5c->rtc->name);
- return -EIO;
+ buf[0] = BIN2BCD(t->time.tm_min);
+ buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour);
+ buf[2] = 0x7f; /* any/all days */
+
+ for (i = 0; i < sizeof(buf); i++) {
+ addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i);
+ if (i2c_smbus_write_byte_data(client, addr, buf[i]) < 0) {
+ pr_debug("%s: can't set alarm time\n", rs5c->rtc->name);
+ return -EIO;
+ }
}
/* ... and maybe enable its irq */
if (t->enabled) {
- buf[0] = RS5C_ADDR(RS5C_REG_CTRL1);
- buf[1] = rs5c->regs[RS5C_REG_CTRL1] | RS5C_CTRL1_AALE;
- if ((i2c_master_send(client, buf, 2)) != 2)
+ addr = RS5C_ADDR(RS5C_REG_CTRL1);
+ buf[0] = rs5c->regs[RS5C_REG_CTRL1] | RS5C_CTRL1_AALE;
+ if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0)
printk(KERN_WARNING "%s: can't enable alarm\n",
rs5c->rtc->name);
- rs5c->regs[RS5C_REG_CTRL1] = buf[1];
+ rs5c->regs[RS5C_REG_CTRL1] = buf[0];
}
return 0;
static struct i2c_driver rs5c372_driver;
+static int rs5c_oscillator_setup(struct rs5c372 *rs5c372)
+{
+ unsigned char buf[2];
+ int addr, i, ret = 0;
+
+ if (!(rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP))
+ return ret;
+ rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP;
+
+ addr = RS5C_ADDR(RS5C_REG_CTRL1);
+ buf[0] = rs5c372->regs[RS5C_REG_CTRL1];
+ buf[1] = rs5c372->regs[RS5C_REG_CTRL2];
+
+ /* use 24hr mode */
+ switch (rs5c372->type) {
+ case rtc_rs5c372a:
+ case rtc_rs5c372b:
+ buf[1] |= RS5C372_CTRL2_24;
+ rs5c372->time24 = 1;
+ break;
+ case rtc_rv5c386:
+ case rtc_rv5c387a:
+ buf[0] |= RV5C387_CTRL1_24;
+ rs5c372->time24 = 1;
+ break;
+ default:
+ /* impossible */
+ break;
+ }
+
+ for (i = 0; i < sizeof(buf); i++) {
+ addr = RS5C_ADDR(RS5C_REG_CTRL1 + i);
+ ret = i2c_smbus_write_byte_data(rs5c372->client, addr, buf[i]);
+ if (unlikely(ret < 0))
+ return ret;
+ }
+
+ rs5c372->regs[RS5C_REG_CTRL1] = buf[0];
+ rs5c372->regs[RS5C_REG_CTRL2] = buf[1];
+
+ return 0;
+}
+
static int rs5c372_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err = 0;
+ int smbus_mode = 0;
struct rs5c372 *rs5c372;
struct rtc_time tm;
dev_dbg(&client->dev, "%s\n", __func__);
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- err = -ENODEV;
- goto exit;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ /*
+ * If we don't have any master mode adapter, try breaking
+ * it down in to the barest of capabilities.
+ */
+ if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK))
+ smbus_mode = 1;
+ else {
+ /* Still no good, give up */
+ err = -ENODEV;
+ goto exit;
+ }
}
if (!(rs5c372 = kzalloc(sizeof(struct rs5c372), GFP_KERNEL))) {
/* we read registers 0x0f then 0x00-0x0f; skip the first one */
rs5c372->regs = &rs5c372->buf[1];
+ rs5c372->smbus = smbus_mode;
err = rs5c_get_regs(rs5c372);
if (err < 0)
/* if the oscillator lost power and no other software (like
* the bootloader) set it up, do it here.
*/
- if (rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP) {
- unsigned char buf[3];
-
- rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP;
-
- buf[0] = RS5C_ADDR(RS5C_REG_CTRL1);
- buf[1] = rs5c372->regs[RS5C_REG_CTRL1];
- buf[2] = rs5c372->regs[RS5C_REG_CTRL2];
-
- /* use 24hr mode */
- switch (rs5c372->type) {
- case rtc_rs5c372a:
- case rtc_rs5c372b:
- buf[2] |= RS5C372_CTRL2_24;
- rs5c372->time24 = 1;
- break;
- case rtc_rv5c386:
- case rtc_rv5c387a:
- buf[1] |= RV5C387_CTRL1_24;
- rs5c372->time24 = 1;
- break;
- default:
- /* impossible */
- break;
- }
-
- if ((i2c_master_send(client, buf, 3)) != 3) {
- dev_err(&client->dev, "setup error\n");
- goto exit_kfree;
- }
- rs5c372->regs[RS5C_REG_CTRL1] = buf[1];
- rs5c372->regs[RS5C_REG_CTRL2] = buf[2];
+ err = rs5c_oscillator_setup(rs5c372);
+ if (unlikely(err < 0)) {
+ dev_err(&client->dev, "setup error\n");
+ goto exit_kfree;
}
if (rs5c372_get_datetime(client, &tm) < 0)
MODULE_AUTHOR(
"Pavel Mironchik <pmironchik@optifacio.net>, "
- "Alessandro Zummo <a.zummo@towertech.it>");
+ "Alessandro Zummo <a.zummo@towertech.it>, "
+ "Paul Mundt <lethal@linux-sh.org>");
MODULE_DESCRIPTION("Ricoh RS5C372 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);