This patch adds support for reading and writing the alarm setting
on the PCF8563 RTC.
Signed-Off-By: Steve Bennett <[EMAIL PROTECTED]>
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/rtc/rtc-pcf8563.c
uClinux-dist/linux-2.6.x/drivers/rtc/rtc-pcf8563.c
--- uClinux-dist.orig/linux-2.6.x/drivers/rtc/rtc-pcf8563.c 2006-11-30
09:27:49.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/rtc/rtc-pcf8563.c 2007-05-11
16:12:16.000000000 +1000
@@ -53,14 +53,32 @@
#define PCF8563_SC_LV 0x80 /* low voltage */
#define PCF8563_MO_C 0x80 /* century */
+/* Define various control and status bits in the registers. */
+#define PCF8563_ST1_TEST1 0x80 // 1 = test mode.
+#define PCF8563_ST1_STOP 0x20 // 1 = stop RTC.
+#define PCF8563_ST1_TESTC 0x8 // 1 = override
power-on reset.
+
+#define PCF8563_ST2_TI 0x10 // 1 = output clock on
/int.
+#define PCF8563_ST2_AF 0x8 // Alarm flag.
+#define PCF8563_ST2_TF 0x4 // Timer flag.
+#define PCF8563_ST2_AIE 0x2 // Alarm interrupt
enable.
+#define PCF8563_ST2_TIE 0x1 // Timer interrupt
enable.
+
+#define PCF8563_ALARM_AE 0x80 // 1 = enable alarm
output.
+
static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind);
static int pcf8563_detach(struct i2c_client *client);
+#define ALARM_BCD2BIN(B) (((B) & PCF8563_ALARM_AE) ? 0xFF : BCD2BIN(B))
+#define ALARM_BIN(B) (((B) & PCF8563_ALARM_AE) ? 0xFF : (B))
+
/*
* In the routines that deal directly with the pcf8563 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ *
+ * If 'alrm' is not NULL, the alarm state is retrieved into *tm, alrm->pending
and alrm->enabled.
*/
-static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time
*tm, struct rtc_wkalrm *alrm)
{
unsigned char buf[13] = { PCF8563_REG_ST1 };
@@ -87,39 +105,63 @@
buf[4], buf[5], buf[6], buf[7],
buf[8]);
+ if (alrm) {
+ dev_dbg(&client->dev,
+ "%s: alarm raw data is min=%02x, hr=%02x, "
+ "mday=%02x, wday=%02x\n",
+ __FUNCTION__,
+ buf[9], buf[10], buf[11], buf[12]);
+ }
- tm->tm_sec = BCD2BIN(buf[PCF8563_REG_SC] & 0x7F);
- tm->tm_min = BCD2BIN(buf[PCF8563_REG_MN] & 0x7F);
- tm->tm_hour = BCD2BIN(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */
- tm->tm_mday = BCD2BIN(buf[PCF8563_REG_DM] & 0x3F);
- tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
+ /* Alarm has no month and year, so these are the same for both */
tm->tm_mon = BCD2BIN(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
tm->tm_year = BCD2BIN(buf[PCF8563_REG_YR])
+ (buf[PCF8563_REG_MO] & PCF8563_MO_C ? 0 : 100);
- dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ if (alrm) {
+ tm->tm_sec = 0;
+ tm->tm_min = ALARM_BCD2BIN(buf[PCF8563_REG_AMN]);
+ tm->tm_hour = ALARM_BCD2BIN(buf[PCF8563_REG_AHR] & 0xBF);
+ tm->tm_mday = ALARM_BCD2BIN(buf[PCF8563_REG_ADM] & 0xBF);
+ tm->tm_wday = ALARM_BIN(buf[PCF8563_REG_ADW] & 0x87);
+ /* tm_mon and tm_year are set below */
+ alrm->enabled = buf[PCF8563_REG_ST2] & PCF8563_ST2_AIE;
+ alrm->pending = buf[PCF8563_REG_ST2] & PCF8563_ST2_AF;
+ }
+ else {
+ tm->tm_sec = BCD2BIN(buf[PCF8563_REG_SC] & 0x7F);
+ tm->tm_min = BCD2BIN(buf[PCF8563_REG_MN] & 0x7F);
+ tm->tm_hour = BCD2BIN(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr
0-23 */
+ tm->tm_mday = BCD2BIN(buf[PCF8563_REG_DM] & 0x3F);
+ tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
+
+ /* the clock can give out invalid datetime, but we cannot return
+ * -EINVAL otherwise hwclock will refuse to set the time on
bootup.
+ */
+ if (rtc_valid_tm(tm) < 0) {
+ dev_err(&client->dev, "retrieved date/time is not
valid.\n");
+ }
+ }
+
+ dev_dbg(&client->dev, "%s: %s tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
+ alrm ? "alarm" : "time",
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
- /* the clock can give out invalid datetime, but we cannot return
- * -EINVAL otherwise hwclock will refuse to set the time on bootup.
- */
- if (rtc_valid_tm(tm) < 0)
- dev_err(&client->dev, "retrieved date/time is not valid.\n");
-
return 0;
}
-static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time
*tm, struct rtc_wkalrm *alrm)
{
int i, err;
- unsigned char buf[9];
+ unsigned char buf[13];
- dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ dev_dbg(&client->dev, "%s: %s secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
+ alrm ? "alarm" : "time",
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
@@ -157,6 +199,48 @@
return 0;
}
+static int pcf8563_set_alarm(struct i2c_client *client, struct rtc_wkalrm
*alrm)
+{
+ int err;
+ unsigned char buf[5];
+
+ dev_dbg(&client->dev, "%s: alarm mins=%d, hours=%d, "
+ "mday=%d, wday=%d, enabled=%d\n",
+ __FUNCTION__,
+ alrm->time.tm_min, alrm->time.tm_hour,
+ alrm->time.tm_mday, alrm->time.tm_wday, alrm->enabled);
+
+ /* First set the alarm time/date */
+ /* Wild card (0xff) value in field means that alarm is not set for that
field. */
+ buf[0] = PCF8563_REG_AMN; /* Address to write data */
+ buf[1] = (alrm->time.tm_min == 0xff) ? 0x80 :
BIN_TO_BCD(alrm->time.tm_min);
+ buf[2] = (alrm->time.tm_hour == 0xff) ? 0x80 :
BIN_TO_BCD(alrm->time.tm_hour);
+ buf[3] = (alrm->time.tm_mday == 0xff) ? 0x81 :
BIN_TO_BCD(alrm->time.tm_mday);
+ buf[4] = (alrm->time.tm_wday == 0xff) ? 0x80 : alrm->time.tm_wday;
+
+ /* write alarm registers */
+ err = i2c_master_send(client, buf, sizeof(buf));
+ if (err != sizeof(buf)) {
+ dev_err(&client->dev,
+ "%s: err=%d addr=%02x\n",
+ __FUNCTION__, err, buf[0]);
+ return -EIO;
+ }
+
+ /* Now write the status/control register 2 */
+ buf[0] = PCF8563_REG_ST2; /* Address to write data */
+ buf[1] = alrm->enabled ? PCF8563_ST2_AIE : 0;
+ err = i2c_master_send(client, buf, 2);
+ if (err != 2) {
+ dev_err(&client->dev,
+ "%s: err=%d addr=%02x\n",
+ __FUNCTION__, err, buf[0]);
+ return -EIO;
+ }
+
+ return 0;
+}
+
struct pcf8563_limit
{
unsigned char reg;
@@ -217,19 +301,31 @@
return 0;
}
+static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ return pcf8563_get_datetime(to_i2c_client(dev), &alrm->time, alrm);
+}
+
+static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ return pcf8563_set_alarm(to_i2c_client(dev), alrm);
+}
+
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- return pcf8563_get_datetime(to_i2c_client(dev), tm);
+ return pcf8563_get_datetime(to_i2c_client(dev), tm, 0);
}
static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- return pcf8563_set_datetime(to_i2c_client(dev), tm);
+ return pcf8563_set_datetime(to_i2c_client(dev), tm, 0);
}
static const struct rtc_class_ops pcf8563_rtc_ops = {
.read_time = pcf8563_rtc_read_time,
.set_time = pcf8563_rtc_set_time,
+ .read_alarm = pcf8563_rtc_read_alarm,
+ .set_alarm = pcf8563_rtc_set_alarm,
};
static int pcf8563_attach(struct i2c_adapter *adapter)
_______________________________________________
uClinux-dev mailing list
[email protected]
http://mailman.uclinux.org/mailman/listinfo/uclinux-dev
This message was resent by [email protected]
To unsubscribe see:
http://mailman.uclinux.org/mailman/options/uclinux-dev