diff options
Diffstat (limited to 'drivers/rtc/rtc-rx8025.c')
| -rw-r--r-- | drivers/rtc/rtc-rx8025.c | 228 |
1 files changed, 123 insertions, 105 deletions
diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c index a24f85893f90..7e9f7cb90c28 100644 --- a/drivers/rtc/rtc-rx8025.c +++ b/drivers/rtc/rtc-rx8025.c @@ -19,6 +19,7 @@ #include <linux/bitops.h> #include <linux/i2c.h> #include <linux/kernel.h> +#include <linux/kstrtox.h> #include <linux/module.h> #include <linux/rtc.h> @@ -55,20 +56,32 @@ #define RX8025_BIT_CTRL2_XST BIT(5) #define RX8025_BIT_CTRL2_VDET BIT(6) +#define RX8035_BIT_HOUR_1224 BIT(7) + /* Clock precision adjustment */ #define RX8025_ADJ_RESOLUTION 3050 /* in ppb */ #define RX8025_ADJ_DATA_MAX 62 #define RX8025_ADJ_DATA_MIN -62 +enum rx_model { + model_rx_unknown, + model_rx_8025, + model_rx_8035, + model_last +}; + static const struct i2c_device_id rx8025_id[] = { - { "rx8025", 0 }, + { "rx8025", model_rx_8025 }, + { "rx8035", model_rx_8035 }, { } }; MODULE_DEVICE_TABLE(i2c, rx8025_id); struct rx8025_data { struct rtc_device *rtc; + enum rx_model model; u8 ctrl1; + int is_24; }; static s32 rx8025_read_reg(const struct i2c_client *client, u8 number) @@ -100,10 +113,26 @@ static s32 rx8025_write_regs(const struct i2c_client *client, length, values); } +static int rx8025_is_osc_stopped(enum rx_model model, int ctrl2) +{ + int xstp = ctrl2 & RX8025_BIT_CTRL2_XST; + /* XSTP bit has different polarity on RX-8025 vs RX-8035. + * RX-8025: 0 == oscillator stopped + * RX-8035: 1 == oscillator stopped + */ + + if (model == model_rx_8025) + xstp = !xstp; + + return xstp; +} + static int rx8025_check_validity(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + struct rx8025_data *drvdata = dev_get_drvdata(dev); int ctrl2; + int xstp; ctrl2 = rx8025_read_reg(client, RX8025_REG_CTRL2); if (ctrl2 < 0) @@ -117,7 +146,8 @@ static int rx8025_check_validity(struct device *dev) return -EINVAL; } - if (!(ctrl2 & RX8025_BIT_CTRL2_XST)) { + xstp = rx8025_is_osc_stopped(drvdata->model, ctrl2); + if (xstp) { dev_warn(dev, "crystal stopped, date is invalid\n"); return -EINVAL; } @@ -127,6 +157,7 @@ static int rx8025_check_validity(struct device *dev) static int rx8025_reset_validity(struct i2c_client *client) { + struct rx8025_data *drvdata = i2c_get_clientdata(client); int ctrl2 = rx8025_read_reg(client, RX8025_REG_CTRL2); if (ctrl2 < 0) @@ -134,23 +165,28 @@ static int rx8025_reset_validity(struct i2c_client *client) ctrl2 &= ~(RX8025_BIT_CTRL2_PON | RX8025_BIT_CTRL2_VDET); + if (drvdata->model == model_rx_8025) + ctrl2 |= RX8025_BIT_CTRL2_XST; + else + ctrl2 &= ~(RX8025_BIT_CTRL2_XST); + return rx8025_write_reg(client, RX8025_REG_CTRL2, - ctrl2 | RX8025_BIT_CTRL2_XST); + ctrl2); } static irqreturn_t rx8025_handle_irq(int irq, void *dev_id) { struct i2c_client *client = dev_id; struct rx8025_data *rx8025 = i2c_get_clientdata(client); - struct mutex *lock = &rx8025->rtc->ops_lock; - int status; + int status, xstp; - mutex_lock(lock); + rtc_lock(rx8025->rtc); status = rx8025_read_reg(client, RX8025_REG_CTRL2); if (status < 0) goto out; - if (!(status & RX8025_BIT_CTRL2_XST)) + xstp = rx8025_is_osc_stopped(rx8025->model, status); + if (xstp) dev_warn(&client->dev, "Oscillation stop was detected," "you may have to readjust the clock\n"); @@ -170,7 +206,7 @@ static irqreturn_t rx8025_handle_irq(int irq, void *dev_id) } out: - mutex_unlock(lock); + rtc_unlock(rx8025->rtc); return IRQ_HANDLED; } @@ -194,7 +230,7 @@ static int rx8025_get_time(struct device *dev, struct rtc_time *dt) dt->tm_sec = bcd2bin(date[RX8025_REG_SEC] & 0x7f); dt->tm_min = bcd2bin(date[RX8025_REG_MIN] & 0x7f); - if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + if (rx8025->is_24) dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x3f); else dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x1f) % 12 @@ -216,16 +252,13 @@ static int rx8025_set_time(struct device *dev, struct rtc_time *dt) u8 date[7]; int ret; - if ((dt->tm_year < 100) || (dt->tm_year > 199)) - return -EINVAL; - /* * Here the read-only bits are written as "0". I'm not sure if that * is sound. */ date[RX8025_REG_SEC] = bin2bcd(dt->tm_sec); date[RX8025_REG_MIN] = bin2bcd(dt->tm_min); - if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + if (rx8025->is_24) date[RX8025_REG_HOUR] = bin2bcd(dt->tm_hour); else date[RX8025_REG_HOUR] = (dt->tm_hour >= 12 ? 0x20 : 0) @@ -250,6 +283,7 @@ static int rx8025_init_client(struct i2c_client *client) struct rx8025_data *rx8025 = i2c_get_clientdata(client); u8 ctrl[2], ctrl2; int need_clear = 0; + int hour_reg; int err; err = rx8025_read_regs(client, RX8025_REG_CTRL1, 2, ctrl); @@ -274,6 +308,16 @@ static int rx8025_init_client(struct i2c_client *client) err = rx8025_write_reg(client, RX8025_REG_CTRL2, ctrl2); } + + if (rx8025->model == model_rx_8035) { + /* In RX-8035, 12/24 flag is in the hour register */ + hour_reg = rx8025_read_reg(client, RX8025_REG_HOUR); + if (hour_reg < 0) + return hour_reg; + rx8025->is_24 = (hour_reg & RX8035_BIT_HOUR_1224); + } else { + rx8025->is_24 = (ctrl[0] & RX8025_BIT_CTRL1_1224); + } out: return err; } @@ -286,9 +330,6 @@ static int rx8025_read_alarm(struct device *dev, struct rtc_wkalrm *t) u8 ald[2]; int ctrl2, err; - if (client->irq <= 0) - return -EINVAL; - err = rx8025_read_regs(client, RX8025_REG_ALDMIN, 2, ald); if (err) return err; @@ -303,7 +344,7 @@ static int rx8025_read_alarm(struct device *dev, struct rtc_wkalrm *t) /* Hardware alarms precision is 1 minute! */ t->time.tm_sec = 0; t->time.tm_min = bcd2bin(ald[0] & 0x7f); - if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + if (rx8025->is_24) t->time.tm_hour = bcd2bin(ald[1] & 0x3f); else t->time.tm_hour = bcd2bin(ald[1] & 0x1f) % 12 @@ -323,22 +364,8 @@ static int rx8025_set_alarm(struct device *dev, struct rtc_wkalrm *t) u8 ald[2]; int err; - if (client->irq <= 0) - return -EINVAL; - - /* - * Hardware alarm precision is 1 minute! - * round up to nearest minute - */ - if (t->time.tm_sec) { - time64_t alarm_time = rtc_tm_to_time64(&t->time); - - alarm_time += 60 - t->time.tm_sec; - rtc_time64_to_tm(alarm_time, &t->time); - } - ald[0] = bin2bcd(t->time.tm_min); - if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + if (rx8025->is_24) ald[1] = bin2bcd(t->time.tm_hour); else ald[1] = (t->time.tm_hour >= 12 ? 0x20 : 0) @@ -391,17 +418,7 @@ static int rx8025_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } -static const struct rtc_class_ops rx8025_rtc_ops = { - .read_time = rx8025_get_time, - .set_time = rx8025_set_time, - .read_alarm = rx8025_read_alarm, - .set_alarm = rx8025_set_alarm, - .alarm_irq_enable = rx8025_alarm_irq_enable, -}; - /* - * Clock precision adjustment support - * * According to the RX8025 SA/NB application manual the frequency and * temperature characteristics can be approximated using the following * equation: @@ -412,11 +429,8 @@ static const struct rtc_class_ops rx8025_rtc_ops = { * a : Coefficient = (-35 +-5) * 10**-9 * ut: Ultimate temperature in degree = +25 +-5 degree * t : Any temperature in degree - * - * Note that the clock adjustment in ppb must be entered (which is - * the negative value of the deviation). */ -static int rx8025_get_clock_adjust(struct device *dev, int *adj) +static int rx8025_read_offset(struct device *dev, long *offset) { struct i2c_client *client = to_i2c_client(dev); int digoff; @@ -425,63 +439,70 @@ static int rx8025_get_clock_adjust(struct device *dev, int *adj) if (digoff < 0) return digoff; - *adj = digoff >= 64 ? digoff - 128 : digoff; - if (*adj > 0) - (*adj)--; - *adj *= -RX8025_ADJ_RESOLUTION; + *offset = digoff >= 64 ? digoff - 128 : digoff; + if (*offset > 0) + (*offset)--; + *offset *= RX8025_ADJ_RESOLUTION; return 0; } -static int rx8025_set_clock_adjust(struct device *dev, int adj) +static int rx8025_set_offset(struct device *dev, long offset) { struct i2c_client *client = to_i2c_client(dev); u8 digoff; - int err; - - adj /= -RX8025_ADJ_RESOLUTION; - if (adj > RX8025_ADJ_DATA_MAX) - adj = RX8025_ADJ_DATA_MAX; - else if (adj < RX8025_ADJ_DATA_MIN) - adj = RX8025_ADJ_DATA_MIN; - else if (adj > 0) - adj++; - else if (adj < 0) - adj += 128; - digoff = adj; - - err = rx8025_write_reg(client, RX8025_REG_DIGOFF, digoff); - if (err) - return err; - dev_dbg(dev, "%s: write 0x%02x\n", __func__, digoff); - - return 0; + offset /= RX8025_ADJ_RESOLUTION; + if (offset > RX8025_ADJ_DATA_MAX) + offset = RX8025_ADJ_DATA_MAX; + else if (offset < RX8025_ADJ_DATA_MIN) + offset = RX8025_ADJ_DATA_MIN; + else if (offset > 0) + offset++; + else if (offset < 0) + offset += 128; + digoff = offset; + + return rx8025_write_reg(client, RX8025_REG_DIGOFF, digoff); } +static const struct rtc_class_ops rx8025_rtc_ops = { + .read_time = rx8025_get_time, + .set_time = rx8025_set_time, + .read_alarm = rx8025_read_alarm, + .set_alarm = rx8025_set_alarm, + .alarm_irq_enable = rx8025_alarm_irq_enable, + .read_offset = rx8025_read_offset, + .set_offset = rx8025_set_offset, +}; + static ssize_t rx8025_sysfs_show_clock_adjust(struct device *dev, struct device_attribute *attr, char *buf) { - int err, adj; + long adj; + int err; - err = rx8025_get_clock_adjust(dev, &adj); + dev_warn_once(dev, "clock_adjust_ppb is deprecated, use offset\n"); + err = rx8025_read_offset(dev, &adj); if (err) return err; - return sprintf(buf, "%d\n", adj); + return sprintf(buf, "%ld\n", -adj); } static ssize_t rx8025_sysfs_store_clock_adjust(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int adj, err; + long adj; + int err; - if (sscanf(buf, "%i", &adj) != 1) + dev_warn_once(dev, "clock_adjust_ppb is deprecated, use offset\n"); + if (kstrtol(buf, 10, &adj) != 0) return -EINVAL; - err = rx8025_set_clock_adjust(dev, adj); + err = rx8025_set_offset(dev, -adj); return err ? err : count; } @@ -490,19 +511,18 @@ static DEVICE_ATTR(clock_adjust_ppb, S_IRUGO | S_IWUSR, rx8025_sysfs_show_clock_adjust, rx8025_sysfs_store_clock_adjust); -static int rx8025_sysfs_register(struct device *dev) -{ - return device_create_file(dev, &dev_attr_clock_adjust_ppb); -} +static struct attribute *rx8025_attrs[] = { + &dev_attr_clock_adjust_ppb.attr, + NULL +}; -static void rx8025_sysfs_unregister(struct device *dev) -{ - device_remove_file(dev, &dev_attr_clock_adjust_ppb); -} +static const struct attribute_group rx8025_attr_group = { + .attrs = rx8025_attrs, +}; -static int rx8025_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int rx8025_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_match_id(rx8025_id, client); struct i2c_adapter *adapter = client->adapter; struct rx8025_data *rx8025; int err = 0; @@ -520,16 +540,20 @@ static int rx8025_probe(struct i2c_client *client, i2c_set_clientdata(client, rx8025); + if (id) + rx8025->model = id->driver_data; + err = rx8025_init_client(client); if (err) return err; - rx8025->rtc = devm_rtc_device_register(&client->dev, client->name, - &rx8025_rtc_ops, THIS_MODULE); - if (IS_ERR(rx8025->rtc)) { - dev_err(&client->dev, "unable to register the class device\n"); + rx8025->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rx8025->rtc)) return PTR_ERR(rx8025->rtc); - } + + rx8025->rtc->ops = &rx8025_rtc_ops; + rx8025->rtc->range_min = RTC_TIMESTAMP_BEGIN_1900; + rx8025->rtc->range_max = RTC_TIMESTAMP_END_2099; if (client->irq > 0) { dev_info(&client->dev, "IRQ %d supplied\n", client->irq); @@ -537,25 +561,20 @@ static int rx8025_probe(struct i2c_client *client, rx8025_handle_irq, IRQF_ONESHOT, "rx8025", client); - if (err) { - dev_err(&client->dev, "unable to request IRQ, alarms disabled\n"); - client->irq = 0; - } + if (err) + clear_bit(RTC_FEATURE_ALARM, rx8025->rtc->features); } rx8025->rtc->max_user_freq = 1; - /* the rx8025 alarm only supports a minute accuracy */ - rx8025->rtc->uie_unsupported = 1; + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rx8025->rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rx8025->rtc->features); - err = rx8025_sysfs_register(&client->dev); - return err; -} + err = rtc_add_group(rx8025->rtc, &rx8025_attr_group); + if (err) + return err; -static int rx8025_remove(struct i2c_client *client) -{ - rx8025_sysfs_unregister(&client->dev); - return 0; + return devm_rtc_register_device(rx8025->rtc); } static struct i2c_driver rx8025_driver = { @@ -563,7 +582,6 @@ static struct i2c_driver rx8025_driver = { .name = "rtc-rx8025", }, .probe = rx8025_probe, - .remove = rx8025_remove, .id_table = rx8025_id, }; |
