/* * rtc-ds13307.c - RTC driver for the DS1307 and DS1337 I2C chips. * * Copyright (C) 2018 Helmut Pozimski * * 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 #include #include #include #include #include #define M_NAME "rtc-ds13307" /* Module name */ #define DEVICE_DS1307 1 #define DEVICE_DS1337 2 #define COMMON_SEC 0x00 #define COMMON_MIN 0x01 #define COMMON_HOUR 0x02 #define COMMON_DAY 0x03 #define COMMON_DATE 0x04 #define COMMON_MONTH 0x05 #define COMMON_YEAR 0x06 #define COMMON_HIGH_BIT 0x80 #define DS1307_NVRAM_BASE 0x08 #define DS1307_MAX_ADDR 0x3F #define DS1337_CTL 0x0E #define DS1337_STAT 0x0F #define DS1337_ALRM1 0x07 #define IOCTL_DS13307_ALRM _IOR('p', 0x15, u8) /* ioctl to read alarm bit */ static int model_detected; static struct i2c_driver ds13307_driver; static struct nvmem_config ds1307_nvmem; static struct regmap_config regmap_config = { .reg_bits = 8, .val_bits = 8, }; /* Reads a specified number of bytes via i2c, returns 0 on success */ static int ds13307_read_bytes(struct i2c_client *client, u8 *addr, u8 *bytes, int length) { int r; struct i2c_msg msgs[] = { { .addr = client->addr, .len = 1, .buf = addr }, { .addr = client->addr, .flags = I2C_M_RD, .len = length, .buf = bytes } }; r = i2c_transfer(client->adapter, msgs, 2); if (r == 2) { return 0; } else { return r; } } /* Writes a specified number of bytes via 2ic, returns 0 on success */ static int ds13307_write_bytes(struct i2c_client *client, u8 *addr, u8 *bytes, int length) { int r; u8 *buf; struct i2c_msg msg; buf = (u8*) kmalloc(length +1, GFP_KERNEL); if (buf == NULL) { printk(KERN_ERR "%s: Could not allocate memory for buffer\n", M_NAME); return -EIO; } buf[0] = (*addr); memmove(buf + 1, bytes, length); msg.addr = client->addr; msg.len = length + 1; msg.buf = buf; r = i2c_transfer(client->adapter, &msg, 1); kfree(buf); if (r == 1) { return 0; } else { return r; } } /* The oscillator is stopped for both chips when power is first applied, * therefore this function checks its status and clears the stop bit. */ static int ds13307_start_oscillator(struct i2c_client *client) { u8 data, addr, buf; int r, v = 0; if (model_detected == DEVICE_DS1307) { addr = COMMON_SEC; } else { addr = DS1337_STAT; } r = ds13307_read_bytes(client, &addr, &data, 1); if (data & COMMON_HIGH_BIT) { buf = data ^ COMMON_HIGH_BIT; v = ds13307_write_bytes(client, &addr, &buf, 1); if (!v) { printk(KERN_DEBUG "%s: oscillator stop bit successfully cleared\n", M_NAME); } } if ((r<0) || (v)) { return -EIO; } return 0; } /* Checks if the hour value is set to 12h format and checks if it * was AM or PM, returns 0 if the value was not in 12h format * or the value was AM so no conversion is necessary */ static int ds13307_check_12h_am_pm(u8 *byte) { if (*byte & 0x40) { (*byte) = *byte ^ 0x40; if (*byte & 0x20) { (*byte) = *byte ^ 0x20; return 1; } } return 0; } static int ds13307_read_time(struct device *dev, struct rtc_time *time) { struct i2c_client *client; int r, century = 1, h12 = 0; u8 buf[7], stopbit; u8 addr = COMMON_SEC; client = to_i2c_client(dev); r = ds13307_read_bytes(client, &addr, buf, 7); if (model_detected == DEVICE_DS1337) { addr = DS1337_STAT; r = ds13307_read_bytes(client, &addr, &stopbit, 1); stopbit = stopbit & COMMON_HIGH_BIT; } else { stopbit = buf[COMMON_SEC] & COMMON_HIGH_BIT; } if (r) { return -EIO; } if (stopbit) { printk(KERN_ERR "%s: Oscillator stop bit is set, values read from rtc device cannot be trusted\n", M_NAME); return -EINVAL; } h12 = ds13307_check_12h_am_pm(&buf[COMMON_HOUR]); if (model_detected == DEVICE_DS1337) { century = buf[COMMON_MONTH] & COMMON_HIGH_BIT; buf[COMMON_MONTH] = buf[COMMON_MONTH] & 0x7F; } time->tm_sec = bcd2bin(buf[COMMON_SEC]); time->tm_min = bcd2bin(buf[COMMON_MIN]); time->tm_hour = bcd2bin(buf[COMMON_HOUR]); if(h12) { time->tm_hour += 12; } time->tm_mday = bcd2bin(buf[COMMON_DATE]); time->tm_wday = bcd2bin(buf[COMMON_DAY]); time->tm_mon = bcd2bin(buf[COMMON_MONTH]) - 1 ; if (century) { time->tm_year = bcd2bin(buf[COMMON_YEAR]) + 100; } else { time->tm_year = bcd2bin(buf[COMMON_YEAR]); } return 0; } static int ds13307_set_time(struct device *dev, struct rtc_time *time) { struct i2c_client *client; u8 buf[7], addr; client = to_i2c_client(dev); if (ds13307_start_oscillator(client)) { printk(KERN_ERR "%s: failed to initialize the oscillator\n", M_NAME); return -EIO; } addr = COMMON_SEC; buf[0] = bin2bcd(time->tm_sec); buf[1] = bin2bcd(time->tm_min); buf[2] = bin2bcd(time->tm_hour); buf[3] = bin2bcd(time->tm_wday); buf[4] = bin2bcd(time->tm_mday); if ((model_detected == DEVICE_DS1337) && (time->tm_year >= 100)) { buf[5] = bin2bcd(time->tm_mon + 1) | COMMON_HIGH_BIT; } else if ((model_detected == DEVICE_DS1307) && (time->tm_year < 100)) { printk(KERN_ERR "%s: device does not support century information, dates before 2000 are not possible\n", M_NAME); return -EINVAL; } else { buf[5] = bin2bcd(time->tm_mon + 1); } buf[6] = bin2bcd(time->tm_year % 100); if(ds13307_write_bytes(client, &addr, buf, 7)) { return -EIO; } return 0; } static int ds13307_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) { struct i2c_client *client; u8 buf[9], addr = DS1337_ALRM1; int h12; client = to_i2c_client(dev); if (!ds13307_read_bytes(client, &addr, buf, 9)) { // select for A1IE bit alarm->enabled = buf[7] & 0x01; // select for A1F bit alarm->pending = buf[8] & 0x01; alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F); alarm->time.tm_min = bcd2bin(buf[1] & 0x7F); h12 = ds13307_check_12h_am_pm(&buf[2]); alarm->time.tm_hour = bcd2bin(buf[2] & 0x7F); if(h12) { alarm->time.tm_hour += 12; } /* if bit is set to 1, it matches the day of the week, * otherwise the day of the month. */ if (buf[3] & 0x40) { alarm->time.tm_wday = bcd2bin(buf[3] & 0x3F); } else { alarm->time.tm_mday = bcd2bin(buf[3] & 0x3F); } return 0; } return -EIO; } /* Sets the alarm to the values passed to the function */ static int ds13307_write_alarm(struct device *dev, struct rtc_wkalrm *alarm) { struct i2c_client *client; u8 buf[9], addr = DS1337_ALRM1; client = to_i2c_client(dev); if(!ds13307_read_bytes(client, &addr, buf, 9)) { // enable or disable the alarm as requested if ((alarm->enabled && (!(buf[7] & 0x01))) || ((!alarm->enabled && (buf[7] & 0x01)))) { buf[7] ^= 0x01; } // disable alarm 2 if (buf[7] & 0x02) { buf[7] ^= 0x02; } // clear the status bit if (buf[8] & 0x01) { buf[8] ^= 0x01; } buf[0] = bin2bcd(alarm->time.tm_sec); buf[1] = bin2bcd(alarm->time.tm_min); buf[2] = bin2bcd(alarm->time.tm_hour); buf[3] = bin2bcd(alarm->time.tm_mday); if(!ds13307_write_bytes(client, &addr, buf, 9)) { return 0; } } return -EIO; } static int ds13307_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct i2c_client *client; u8 buf, addr = DS1337_CTL; client = to_i2c_client(dev); if (ds13307_read_bytes(client, &addr, &buf, 1)) { return -EIO; } if ((!enabled && (buf & 0x01)) || (enabled & (!(buf & 0x01)))) { buf ^= 0x01; } if(!ds13307_write_bytes(client, &addr, &buf, 1)) { return 0; } return -EIO; } /* Reads the alarm bit and copies it to userspace */ static int ds13307_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { struct i2c_client *client; u8 addr, buf; switch(cmd) { case IOCTL_DS13307_ALRM: addr = DS1337_STAT; client = to_i2c_client(dev); if (ds13307_read_bytes(client, &addr, &buf, 1)) { return -EIO; } buf &= 0x01; if (copy_to_user((void __user *)arg, &buf, sizeof(u8))) { return -EFAULT; } return 0; default: return -ENOIOCTLCMD; } } static struct rtc_class_ops ds13307_rtc_ops = { .read_time = ds13307_read_time, .set_time = ds13307_set_time }; /* Performs the device detection to distinguish between the * DS1307 and DS1337 chips. Returns the defined device ID or * -1 on failure */ static int ds13307_detect_device(struct i2c_client *client) { u8 data, addr = DS1307_MAX_ADDR; int result; result = ds13307_read_bytes(client, &addr, &data, 1); if (!result) { printk(KERN_INFO "%s: Detected device DS1307\n", M_NAME); return DEVICE_DS1307; } addr = DS1337_CTL; result = ds13307_read_bytes(client, &addr, &data, 1); if (!result) { printk(KERN_INFO "%s: Detected device DS1337\n", M_NAME); return DEVICE_DS1337; } printk(KERN_ERR "%s: Could not talk to I2C device at addr %x, is it connected?\n", M_NAME, client->addr); return -1; } static int ds13307_nvram_read(void *priv, unsigned int offset, void *buf, size_t count) { struct regmap *regmap = priv; return regmap_bulk_read(regmap, DS1307_NVRAM_BASE + offset, buf, count); } static int ds13307_nvram_write(void *priv, unsigned int offset, void *buf, size_t count) { struct regmap * regmap = priv; return regmap_bulk_write(regmap, DS1307_NVRAM_BASE + offset, buf, count); } static int ds13307_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct rtc_device *rtc; int error; struct regmap * regmap; model_detected = ds13307_detect_device(client); if ((model_detected != DEVICE_DS1307) && (model_detected != DEVICE_DS1337)) { return -EIO; } rtc = devm_rtc_allocate_device(&client->dev); if (IS_ERR(rtc)) { return PTR_ERR(rtc); } regmap = devm_regmap_init_i2c(client, ®map_config); if (IS_ERR(regmap)) { return PTR_ERR(regmap); } rtc->uie_unsupported = 1; device_set_wakeup_capable(&client->dev, 0); client->irq = 0; if (model_detected == DEVICE_DS1307) { ds1307_nvmem.name = "ds1307_nvram"; ds1307_nvmem.word_size = 1; ds1307_nvmem.size = 56; ds1307_nvmem.reg_read = ds13307_nvram_read; ds1307_nvmem.reg_write = ds13307_nvram_write; ds1307_nvmem.priv = regmap; rtc->nvmem_config = &ds1307_nvmem; } else { ds13307_rtc_ops.read_alarm = ds13307_read_alarm; ds13307_rtc_ops.set_alarm = ds13307_write_alarm; ds13307_rtc_ops.alarm_irq_enable = ds13307_alarm_irq_enable; ds13307_rtc_ops.ioctl = ds13307_ioctl; } rtc->ops = &ds13307_rtc_ops; i2c_set_clientdata(client,rtc); error = rtc_register_device(rtc); return error ? error: 0; } static struct i2c_device_id ds13307_idtable[] = { { "ds1307", 0 }, { "ds1337", 0 }, {} }; MODULE_DEVICE_TABLE(i2c, ds13307_idtable); static struct of_device_id ds13307_of_match[] = { { .compatible = "dallas,ds1307" }, { .compatible = "dallas,ds1337" }, {} }; MODULE_DEVICE_TABLE(of, ds13307_of_match); static struct i2c_driver ds13307_driver = { .driver = { .name = M_NAME, .of_match_table = of_match_ptr(ds13307_of_match), .owner = THIS_MODULE }, .id_table = ds13307_idtable, .probe = ds13307_probe, }; module_i2c_driver(ds13307_driver); MODULE_AUTHOR("Helmut Pozimski "); MODULE_DESCRIPTION("DS1307 and DS1337 RTC driver"); MODULE_LICENSE("GPL");