From 11e5890b5342c82eefbaa39aec4767ae21ae8803 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 10 Mar 2017 18:52:34 +0100 Subject: rtc: ds1307: convert driver to regmap This patch converts the ds1307 driver to using regmap. It's a rather big patch and I can test with DS3231 only. With this chip it's working fine. I'd appreciate if people with other supported hardware could test as well. Signed-off-by: Heiner Kallweit Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 603 +++++++++++++++++------------------------------ 1 file changed, 221 insertions(+), 382 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 77339b3..94e13d7 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -24,6 +24,7 @@ #include #include #include +#include /* * We can't determine type by probing, but if we expect pre-Linux code @@ -120,12 +121,11 @@ struct ds1307 { unsigned long flags; #define HAS_NVRAM 0 /* bit 0 == sysfs file active */ #define HAS_ALARM 1 /* bit 1 == irq claimed */ - struct i2c_client *client; + struct device *dev; + struct regmap *regmap; + const char *name; + int irq; struct rtc_device *rtc; - s32 (*read_block_data)(const struct i2c_client *client, u8 command, - u8 length, u8 *values); - s32 (*write_block_data)(const struct i2c_client *client, u8 command, - u8 length, const u8 *values); #ifdef CONFIG_COMMON_CLK struct clk_hw clks[2]; #endif @@ -137,11 +137,11 @@ struct chip_desc { u16 nvram_size; u16 trickle_charger_reg; u8 trickle_charger_setup; - u8 (*do_trickle_setup)(struct i2c_client *, uint32_t, bool); + u8 (*do_trickle_setup)(struct ds1307 *, uint32_t, + bool); }; -static u8 do_trickle_setup_ds1339(struct i2c_client *, - uint32_t ohms, bool diode); +static u8 do_trickle_setup_ds1339(struct ds1307 *, uint32_t ohms, bool diode); static struct chip_desc chips[last_ds_type] = { [ds_1307] = { @@ -280,136 +280,6 @@ static const struct acpi_device_id ds1307_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids); #endif -/*----------------------------------------------------------------------*/ - -#define BLOCK_DATA_MAX_TRIES 10 - -static s32 ds1307_read_block_data_once(const struct i2c_client *client, - u8 command, u8 length, u8 *values) -{ - s32 i, data; - - for (i = 0; i < length; i++) { - data = i2c_smbus_read_byte_data(client, command + i); - if (data < 0) - return data; - values[i] = data; - } - return i; -} - -static s32 ds1307_read_block_data(const struct i2c_client *client, u8 command, - u8 length, u8 *values) -{ - u8 oldvalues[255]; - s32 ret; - int tries = 0; - - dev_dbg(&client->dev, "ds1307_read_block_data (length=%d)\n", length); - ret = ds1307_read_block_data_once(client, command, length, values); - if (ret < 0) - return ret; - do { - if (++tries > BLOCK_DATA_MAX_TRIES) { - dev_err(&client->dev, - "ds1307_read_block_data failed\n"); - return -EIO; - } - memcpy(oldvalues, values, length); - ret = ds1307_read_block_data_once(client, command, length, - values); - if (ret < 0) - return ret; - } while (memcmp(oldvalues, values, length)); - return length; -} - -static s32 ds1307_write_block_data(const struct i2c_client *client, u8 command, - u8 length, const u8 *values) -{ - u8 currvalues[255]; - int tries = 0; - - dev_dbg(&client->dev, "ds1307_write_block_data (length=%d)\n", length); - do { - s32 i, ret; - - if (++tries > BLOCK_DATA_MAX_TRIES) { - dev_err(&client->dev, - "ds1307_write_block_data failed\n"); - return -EIO; - } - for (i = 0; i < length; i++) { - ret = i2c_smbus_write_byte_data(client, command + i, - values[i]); - if (ret < 0) - return ret; - } - ret = ds1307_read_block_data_once(client, command, length, - currvalues); - if (ret < 0) - return ret; - } while (memcmp(currvalues, values, length)); - return length; -} - -/*----------------------------------------------------------------------*/ - -/* These RTC devices are not designed to be connected to a SMbus adapter. - SMbus limits block operations length to 32 bytes, whereas it's not - limited on I2C buses. As a result, accesses may exceed 32 bytes; - in that case, split them into smaller blocks */ - -static s32 ds1307_native_smbus_write_block_data(const struct i2c_client *client, - u8 command, u8 length, const u8 *values) -{ - u8 suboffset = 0; - - if (length <= I2C_SMBUS_BLOCK_MAX) { - s32 retval = i2c_smbus_write_i2c_block_data(client, - command, length, values); - if (retval < 0) - return retval; - return length; - } - - while (suboffset < length) { - s32 retval = i2c_smbus_write_i2c_block_data(client, - command + suboffset, - min(I2C_SMBUS_BLOCK_MAX, length - suboffset), - values + suboffset); - if (retval < 0) - return retval; - - suboffset += I2C_SMBUS_BLOCK_MAX; - } - return length; -} - -static s32 ds1307_native_smbus_read_block_data(const struct i2c_client *client, - u8 command, u8 length, u8 *values) -{ - u8 suboffset = 0; - - if (length <= I2C_SMBUS_BLOCK_MAX) - return i2c_smbus_read_i2c_block_data(client, - command, length, values); - - while (suboffset < length) { - s32 retval = i2c_smbus_read_i2c_block_data(client, - command + suboffset, - min(I2C_SMBUS_BLOCK_MAX, length - suboffset), - values + suboffset); - if (retval < 0) - return retval; - - suboffset += I2C_SMBUS_BLOCK_MAX; - } - return length; -} - -/*----------------------------------------------------------------------*/ - /* * The ds1337 and ds1339 both have two alarms, but we only use the first * one (with a "seconds" field). For ds1337 we expect nINTA is our alarm @@ -417,26 +287,25 @@ static s32 ds1307_native_smbus_read_block_data(const struct i2c_client *client, */ static irqreturn_t ds1307_irq(int irq, void *dev_id) { - struct i2c_client *client = dev_id; - struct ds1307 *ds1307 = i2c_get_clientdata(client); + struct ds1307 *ds1307 = dev_id; struct mutex *lock = &ds1307->rtc->ops_lock; - int stat, control; + int stat, control, ret; mutex_lock(lock); - stat = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS); - if (stat < 0) + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &stat); + if (ret) goto out; if (stat & DS1337_BIT_A1I) { stat &= ~DS1337_BIT_A1I; - i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, stat); + regmap_write(ds1307->regmap, DS1337_REG_STATUS, stat); - control = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); - if (control < 0) + ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control); + if (ret) goto out; control &= ~DS1337_BIT_A1IE; - i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, control); + regmap_write(ds1307->regmap, DS1337_REG_CONTROL, control); rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); } @@ -452,14 +321,13 @@ out: static int ds1307_get_time(struct device *dev, struct rtc_time *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); - int tmp; + int tmp, ret; /* read the RTC date and time registers all at once */ - tmp = ds1307->read_block_data(ds1307->client, - ds1307->offset, 7, ds1307->regs); - if (tmp != 7) { - dev_err(dev, "%s error %d\n", "read", tmp); - return -EIO; + ret = regmap_bulk_read(ds1307->regmap, ds1307->offset, ds1307->regs, 7); + if (ret) { + dev_err(dev, "%s error %d\n", "read", ret); + return ret; } dev_dbg(dev, "%s: %7ph\n", "read", ds1307->regs); @@ -580,9 +448,8 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) dev_dbg(dev, "%s: %7ph\n", "write", buf); - result = ds1307->write_block_data(ds1307->client, - ds1307->offset, 7, buf); - if (result < 0) { + result = regmap_bulk_write(ds1307->regmap, ds1307->offset, buf, 7); + if (result) { dev_err(dev, "%s error %d\n", "write", result); return result; } @@ -591,19 +458,18 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) { - struct i2c_client *client = to_i2c_client(dev); - struct ds1307 *ds1307 = i2c_get_clientdata(client); + struct ds1307 *ds1307 = dev_get_drvdata(dev); int ret; if (!test_bit(HAS_ALARM, &ds1307->flags)) return -EINVAL; /* read all ALARM1, ALARM2, and status registers at once */ - ret = ds1307->read_block_data(client, - DS1339_REG_ALARM1_SECS, 9, ds1307->regs); - if (ret != 9) { + ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, + ds1307->regs, 9); + if (ret) { dev_err(dev, "%s error %d\n", "alarm read", ret); - return -EIO; + return ret; } dev_dbg(dev, "%s: %4ph, %3ph, %2ph\n", "alarm read", @@ -633,8 +499,7 @@ static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) { - struct i2c_client *client = to_i2c_client(dev); - struct ds1307 *ds1307 = i2c_get_clientdata(client); + struct ds1307 *ds1307 = dev_get_drvdata(dev); unsigned char *buf = ds1307->regs; u8 control, status; int ret; @@ -649,11 +514,10 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) t->enabled, t->pending); /* read current status of both alarms and the chip */ - ret = ds1307->read_block_data(client, - DS1339_REG_ALARM1_SECS, 9, buf); - if (ret != 9) { + ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, buf, 9); + if (ret) { dev_err(dev, "%s error %d\n", "alarm write", ret); - return -EIO; + return ret; } control = ds1307->regs[7]; status = ds1307->regs[8]; @@ -676,9 +540,8 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) buf[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE); buf[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); - ret = ds1307->write_block_data(client, - DS1339_REG_ALARM1_SECS, 9, buf); - if (ret < 0) { + ret = regmap_bulk_write(ds1307->regmap, DS1339_REG_ALARM1_SECS, buf, 9); + if (ret) { dev_err(dev, "can't set alarm time\n"); return ret; } @@ -687,7 +550,7 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) if (t->enabled) { dev_dbg(dev, "alarm IRQ armed\n"); buf[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */ - i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, buf[7]); + regmap_write(ds1307->regmap, DS1337_REG_CONTROL, buf[7]); } return 0; @@ -695,27 +558,22 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled) { - struct i2c_client *client = to_i2c_client(dev); - struct ds1307 *ds1307 = i2c_get_clientdata(client); - int ret; + struct ds1307 *ds1307 = dev_get_drvdata(dev); + int control, ret; if (!test_bit(HAS_ALARM, &ds1307->flags)) return -ENOTTY; - ret = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); - if (ret < 0) + ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control); + if (ret) return ret; if (enabled) - ret |= DS1337_BIT_A1IE; + control |= DS1337_BIT_A1IE; else - ret &= ~DS1337_BIT_A1IE; - - ret = i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, ret); - if (ret < 0) - return ret; + control &= ~DS1337_BIT_A1IE; - return 0; + return regmap_write(ds1307->regmap, DS1337_REG_CONTROL, control); } static const struct rtc_class_ops ds13xx_rtc_ops = { @@ -752,31 +610,30 @@ static const struct rtc_class_ops ds13xx_rtc_ops = { static irqreturn_t mcp794xx_irq(int irq, void *dev_id) { - struct i2c_client *client = dev_id; - struct ds1307 *ds1307 = i2c_get_clientdata(client); + struct ds1307 *ds1307 = dev_id; struct mutex *lock = &ds1307->rtc->ops_lock; int reg, ret; mutex_lock(lock); /* Check and clear alarm 0 interrupt flag. */ - reg = i2c_smbus_read_byte_data(client, MCP794XX_REG_ALARM0_CTRL); - if (reg < 0) + ret = regmap_read(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, ®); + if (ret) goto out; if (!(reg & MCP794XX_BIT_ALMX_IF)) goto out; reg &= ~MCP794XX_BIT_ALMX_IF; - ret = i2c_smbus_write_byte_data(client, MCP794XX_REG_ALARM0_CTRL, reg); - if (ret < 0) + ret = regmap_write(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, reg); + if (ret) goto out; /* Disable alarm 0. */ - reg = i2c_smbus_read_byte_data(client, MCP794XX_REG_CONTROL); - if (reg < 0) + ret = regmap_read(ds1307->regmap, MCP794XX_REG_CONTROL, ®); + if (ret) goto out; reg &= ~MCP794XX_BIT_ALM0_EN; - ret = i2c_smbus_write_byte_data(client, MCP794XX_REG_CONTROL, reg); - if (ret < 0) + ret = regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, reg); + if (ret) goto out; rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); @@ -789,8 +646,7 @@ out: static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) { - struct i2c_client *client = to_i2c_client(dev); - struct ds1307 *ds1307 = i2c_get_clientdata(client); + struct ds1307 *ds1307 = dev_get_drvdata(dev); u8 *regs = ds1307->regs; int ret; @@ -798,8 +654,8 @@ static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) return -EINVAL; /* Read control and alarm 0 registers. */ - ret = ds1307->read_block_data(client, MCP794XX_REG_CONTROL, 10, regs); - if (ret < 0) + ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, 10); + if (ret) return ret; t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN); @@ -828,8 +684,7 @@ static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) { - struct i2c_client *client = to_i2c_client(dev); - struct ds1307 *ds1307 = i2c_get_clientdata(client); + struct ds1307 *ds1307 = dev_get_drvdata(dev); unsigned char *regs = ds1307->regs; int ret; @@ -843,8 +698,8 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) t->enabled, t->pending); /* Read control and alarm 0 registers. */ - ret = ds1307->read_block_data(client, MCP794XX_REG_CONTROL, 10, regs); - if (ret < 0) + ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, 10); + if (ret) return ret; /* Set alarm 0, using 24-hour and day-of-month modes. */ @@ -862,35 +717,34 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) /* Disable interrupt. We will not enable until completely programmed */ regs[0] &= ~MCP794XX_BIT_ALM0_EN; - ret = ds1307->write_block_data(client, MCP794XX_REG_CONTROL, 10, regs); - if (ret < 0) + ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs, 10); + if (ret) return ret; if (!t->enabled) return 0; regs[0] |= MCP794XX_BIT_ALM0_EN; - return i2c_smbus_write_byte_data(client, MCP794XX_REG_CONTROL, regs[0]); + return regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs[0]); } static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled) { - struct i2c_client *client = to_i2c_client(dev); - struct ds1307 *ds1307 = i2c_get_clientdata(client); - int reg; + struct ds1307 *ds1307 = dev_get_drvdata(dev); + int reg, ret; if (!test_bit(HAS_ALARM, &ds1307->flags)) return -EINVAL; - reg = i2c_smbus_read_byte_data(client, MCP794XX_REG_CONTROL); - if (reg < 0) - return reg; + ret = regmap_read(ds1307->regmap, MCP794XX_REG_CONTROL, ®); + if (ret) + return ret; if (enabled) reg |= MCP794XX_BIT_ALM0_EN; else reg &= ~MCP794XX_BIT_ALM0_EN; - return i2c_smbus_write_byte_data(client, MCP794XX_REG_CONTROL, reg); + return regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, reg); } static const struct rtc_class_ops mcp794xx_rtc_ops = { @@ -908,17 +762,15 @@ ds1307_nvram_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { - struct i2c_client *client; struct ds1307 *ds1307; int result; - client = kobj_to_i2c_client(kobj); - ds1307 = i2c_get_clientdata(client); + ds1307 = dev_get_drvdata(kobj_to_dev(kobj)); - result = ds1307->read_block_data(client, ds1307->nvram_offset + off, - count, buf); - if (result < 0) - dev_err(&client->dev, "%s error %d\n", "nvram read", result); + result = regmap_bulk_read(ds1307->regmap, ds1307->nvram_offset + off, + buf, count); + if (result) + dev_err(ds1307->dev, "%s error %d\n", "nvram read", result); return result; } @@ -927,17 +779,15 @@ ds1307_nvram_write(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { - struct i2c_client *client; struct ds1307 *ds1307; int result; - client = kobj_to_i2c_client(kobj); - ds1307 = i2c_get_clientdata(client); + ds1307 = dev_get_drvdata(kobj_to_dev(kobj)); - result = ds1307->write_block_data(client, ds1307->nvram_offset + off, - count, buf); - if (result < 0) { - dev_err(&client->dev, "%s error %d\n", "nvram write", result); + result = regmap_bulk_write(ds1307->regmap, ds1307->nvram_offset + off, + buf, count); + if (result) { + dev_err(ds1307->dev, "%s error %d\n", "nvram write", result); return result; } return count; @@ -946,7 +796,7 @@ ds1307_nvram_write(struct file *filp, struct kobject *kobj, /*----------------------------------------------------------------------*/ -static u8 do_trickle_setup_ds1339(struct i2c_client *client, +static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, uint32_t ohms, bool diode) { u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE : @@ -963,14 +813,14 @@ static u8 do_trickle_setup_ds1339(struct i2c_client *client, setup |= DS1307_TRICKLE_CHARGER_4K_OHM; break; default: - dev_warn(&client->dev, + dev_warn(ds1307->dev, "Unsupported ohm value %u in dt\n", ohms); return 0; } return setup; } -static void ds1307_trickle_init(struct i2c_client *client, +static void ds1307_trickle_init(struct ds1307 *ds1307, struct chip_desc *chip) { uint32_t ohms = 0; @@ -978,11 +828,12 @@ static void ds1307_trickle_init(struct i2c_client *client, if (!chip->do_trickle_setup) goto out; - if (device_property_read_u32(&client->dev, "trickle-resistor-ohms", &ohms)) + if (device_property_read_u32(ds1307->dev, "trickle-resistor-ohms", + &ohms)) goto out; - if (device_property_read_bool(&client->dev, "trickle-diode-disable")) + if (device_property_read_bool(ds1307->dev, "trickle-diode-disable")) diode = false; - chip->trickle_charger_setup = chip->do_trickle_setup(client, + chip->trickle_charger_setup = chip->do_trickle_setup(ds1307, ohms, diode); out: return; @@ -1009,13 +860,10 @@ static int ds3231_hwmon_read_temp(struct device *dev, s32 *mC) s16 temp; int ret; - ret = ds1307->read_block_data(ds1307->client, DS3231_REG_TEMPERATURE, - sizeof(temp_buf), temp_buf); - if (ret < 0) + ret = regmap_bulk_read(ds1307->regmap, DS3231_REG_TEMPERATURE, + temp_buf, sizeof(temp_buf)); + if (ret) return ret; - if (ret != sizeof(temp_buf)) - return -EIO; - /* * Temperature is represented as a 10-bit code with a resolution of * 0.25 degree celsius and encoded in two's complement format. @@ -1055,12 +903,11 @@ static void ds1307_hwmon_register(struct ds1307 *ds1307) if (ds1307->type != ds_3231) return; - dev = devm_hwmon_device_register_with_groups(&ds1307->client->dev, - ds1307->client->name, + dev = devm_hwmon_device_register_with_groups(ds1307->dev, ds1307->name, ds1307, ds3231_hwmon_groups); if (IS_ERR(dev)) { - dev_warn(&ds1307->client->dev, - "unable to register hwmon device %ld\n", PTR_ERR(dev)); + dev_warn(ds1307->dev, "unable to register hwmon device %ld\n", + PTR_ERR(dev)); } } @@ -1099,23 +946,20 @@ static int ds3231_clk_sqw_rates[] = { static int ds1337_write_control(struct ds1307 *ds1307, u8 mask, u8 value) { - struct i2c_client *client = ds1307->client; struct mutex *lock = &ds1307->rtc->ops_lock; int control; int ret; mutex_lock(lock); - control = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); - if (control < 0) { - ret = control; + ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control); + if (ret) goto out; - } control &= ~mask; control |= value; - ret = i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, control); + ret = regmap_write(ds1307->regmap, DS1337_REG_CONTROL, control); out: mutex_unlock(lock); @@ -1126,12 +970,12 @@ static unsigned long ds3231_clk_sqw_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); - int control; + int control, ret; int rate_sel = 0; - control = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_CONTROL); - if (control < 0) - return control; + ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control); + if (ret) + return ret; if (control & DS1337_BIT_RS1) rate_sel += 1; if (control & DS1337_BIT_RS2) @@ -1195,11 +1039,11 @@ static void ds3231_clk_sqw_unprepare(struct clk_hw *hw) static int ds3231_clk_sqw_is_prepared(struct clk_hw *hw) { struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); - int control; + int control, ret; - control = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_CONTROL); - if (control < 0) - return control; + ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control); + if (ret) + return ret; return !(control & DS1337_BIT_INTCN); } @@ -1221,25 +1065,22 @@ static unsigned long ds3231_clk_32khz_recalc_rate(struct clk_hw *hw, static int ds3231_clk_32khz_control(struct ds1307 *ds1307, bool enable) { - struct i2c_client *client = ds1307->client; struct mutex *lock = &ds1307->rtc->ops_lock; int status; int ret; mutex_lock(lock); - status = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS); - if (status < 0) { - ret = status; + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &status); + if (ret) goto out; - } if (enable) status |= DS3231_BIT_EN32KHZ; else status &= ~DS3231_BIT_EN32KHZ; - ret = i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, status); + ret = regmap_write(ds1307->regmap, DS1337_REG_STATUS, status); out: mutex_unlock(lock); @@ -1263,11 +1104,11 @@ static void ds3231_clk_32khz_unprepare(struct clk_hw *hw) static int ds3231_clk_32khz_is_prepared(struct clk_hw *hw) { struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw); - int status; + int status, ret; - status = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_STATUS); - if (status < 0) - return status; + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &status); + if (ret) + return ret; return !!(status & DS3231_BIT_EN32KHZ); } @@ -1292,18 +1133,17 @@ static struct clk_init_data ds3231_clks_init[] = { static int ds3231_clks_register(struct ds1307 *ds1307) { - struct i2c_client *client = ds1307->client; - struct device_node *node = client->dev.of_node; + struct device_node *node = ds1307->dev->of_node; struct clk_onecell_data *onecell; int i; - onecell = devm_kzalloc(&client->dev, sizeof(*onecell), GFP_KERNEL); + onecell = devm_kzalloc(ds1307->dev, sizeof(*onecell), GFP_KERNEL); if (!onecell) return -ENOMEM; onecell->clk_num = ARRAY_SIZE(ds3231_clks_init); - onecell->clks = devm_kcalloc(&client->dev, onecell->clk_num, - sizeof(onecell->clks[0]), GFP_KERNEL); + onecell->clks = devm_kcalloc(ds1307->dev, onecell->clk_num, + sizeof(onecell->clks[0]), GFP_KERNEL); if (!onecell->clks) return -ENOMEM; @@ -1322,8 +1162,8 @@ static int ds3231_clks_register(struct ds1307 *ds1307) &init.name); ds1307->clks[i].init = &init; - onecell->clks[i] = devm_clk_register(&client->dev, - &ds1307->clks[i]); + onecell->clks[i] = devm_clk_register(ds1307->dev, + &ds1307->clks[i]); if (IS_ERR(onecell->clks[i])) return PTR_ERR(onecell->clks[i]); } @@ -1345,8 +1185,8 @@ static void ds1307_clks_register(struct ds1307 *ds1307) ret = ds3231_clks_register(ds1307); if (ret) { - dev_warn(&ds1307->client->dev, - "unable to register clock device %d\n", ret); + dev_warn(ds1307->dev, "unable to register clock device %d\n", + ret); } } @@ -1358,6 +1198,12 @@ static void ds1307_clks_register(struct ds1307 *ds1307) #endif /* CONFIG_COMMON_CLK */ +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x12, +}; + static int ds1307_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1365,7 +1211,6 @@ static int ds1307_probe(struct i2c_client *client, int err = -ENODEV; int tmp, wday; struct chip_desc *chip; - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); bool want_irq = false; bool ds1307_can_wakeup_device = false; unsigned char *buf; @@ -1382,17 +1227,22 @@ static int ds1307_probe(struct i2c_client *client, }; const struct rtc_class_ops *rtc_ops = &ds13xx_rtc_ops; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA) - && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) - return -EIO; - ds1307 = devm_kzalloc(&client->dev, sizeof(struct ds1307), GFP_KERNEL); if (!ds1307) return -ENOMEM; - i2c_set_clientdata(client, ds1307); + dev_set_drvdata(&client->dev, ds1307); + ds1307->dev = &client->dev; + ds1307->name = client->name; + ds1307->irq = client->irq; + + ds1307->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(ds1307->regmap)) { + dev_err(ds1307->dev, "regmap allocation failed\n"); + return PTR_ERR(ds1307->regmap); + } - ds1307->client = client; + i2c_set_clientdata(client, ds1307); if (client->dev.of_node) { ds1307->type = (enum ds_type) @@ -1405,7 +1255,7 @@ static int ds1307_probe(struct i2c_client *client, const struct acpi_device_id *acpi_id; acpi_id = acpi_match_device(ACPI_PTR(ds1307_acpi_ids), - &client->dev); + ds1307->dev); if (!acpi_id) return -ENODEV; chip = &chips[acpi_id->driver_data]; @@ -1413,27 +1263,21 @@ static int ds1307_probe(struct i2c_client *client, } if (!pdata) - ds1307_trickle_init(client, chip); + ds1307_trickle_init(ds1307, chip); else if (pdata->trickle_charger_setup) chip->trickle_charger_setup = pdata->trickle_charger_setup; if (chip->trickle_charger_setup && chip->trickle_charger_reg) { - dev_dbg(&client->dev, "writing trickle charger info 0x%x to 0x%x\n", + dev_dbg(ds1307->dev, + "writing trickle charger info 0x%x to 0x%x\n", DS13XX_TRICKLE_CHARGER_MAGIC | chip->trickle_charger_setup, chip->trickle_charger_reg); - i2c_smbus_write_byte_data(client, chip->trickle_charger_reg, + regmap_write(ds1307->regmap, chip->trickle_charger_reg, DS13XX_TRICKLE_CHARGER_MAGIC | chip->trickle_charger_setup); } buf = ds1307->regs; - if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { - ds1307->read_block_data = ds1307_native_smbus_read_block_data; - ds1307->write_block_data = ds1307_native_smbus_write_block_data; - } else { - ds1307->read_block_data = ds1307_read_block_data; - ds1307->write_block_data = ds1307_write_block_data; - } #ifdef CONFIG_OF /* @@ -1459,11 +1303,10 @@ static int ds1307_probe(struct i2c_client *client, case ds_1339: case ds_3231: /* get registers that the "rtc" read below won't read... */ - tmp = ds1307->read_block_data(ds1307->client, - DS1337_REG_CONTROL, 2, buf); - if (tmp != 2) { - dev_dbg(&client->dev, "read error %d\n", tmp); - err = -EIO; + err = regmap_bulk_read(ds1307->regmap, DS1337_REG_CONTROL, + buf, 2); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); goto exit; } @@ -1477,8 +1320,8 @@ static int ds1307_probe(struct i2c_client *client, * For some variants, be sure alarms can trigger when we're * running on Vbackup (BBSQI/BBSQW) */ - if (chip->alarm && (ds1307->client->irq > 0 || - ds1307_can_wakeup_device)) { + if (chip->alarm && (ds1307->irq > 0 || + ds1307_can_wakeup_device)) { ds1307->regs[0] |= DS1337_BIT_INTCN | bbsqi_bitpos[ds1307->type]; ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE); @@ -1486,50 +1329,49 @@ static int ds1307_probe(struct i2c_client *client, want_irq = true; } - i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, - ds1307->regs[0]); + regmap_write(ds1307->regmap, DS1337_REG_CONTROL, + ds1307->regs[0]); /* oscillator fault? clear flag, and warn */ if (ds1307->regs[1] & DS1337_BIT_OSF) { - i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, - ds1307->regs[1] & ~DS1337_BIT_OSF); - dev_warn(&client->dev, "SET TIME!\n"); + regmap_write(ds1307->regmap, DS1337_REG_STATUS, + ds1307->regs[1] & ~DS1337_BIT_OSF); + dev_warn(ds1307->dev, "SET TIME!\n"); } break; case rx_8025: - tmp = i2c_smbus_read_i2c_block_data(ds1307->client, - RX8025_REG_CTRL1 << 4 | 0x08, 2, buf); - if (tmp != 2) { - dev_dbg(&client->dev, "read error %d\n", tmp); - err = -EIO; + err = regmap_bulk_read(ds1307->regmap, + RX8025_REG_CTRL1 << 4 | 0x08, buf, 2); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); goto exit; } /* oscillator off? turn it on, so clock can tick. */ if (!(ds1307->regs[1] & RX8025_BIT_XST)) { ds1307->regs[1] |= RX8025_BIT_XST; - i2c_smbus_write_byte_data(client, - RX8025_REG_CTRL2 << 4 | 0x08, - ds1307->regs[1]); - dev_warn(&client->dev, + regmap_write(ds1307->regmap, + RX8025_REG_CTRL2 << 4 | 0x08, + ds1307->regs[1]); + dev_warn(ds1307->dev, "oscillator stop detected - SET TIME!\n"); } if (ds1307->regs[1] & RX8025_BIT_PON) { ds1307->regs[1] &= ~RX8025_BIT_PON; - i2c_smbus_write_byte_data(client, - RX8025_REG_CTRL2 << 4 | 0x08, - ds1307->regs[1]); - dev_warn(&client->dev, "power-on detected\n"); + regmap_write(ds1307->regmap, + RX8025_REG_CTRL2 << 4 | 0x08, + ds1307->regs[1]); + dev_warn(ds1307->dev, "power-on detected\n"); } if (ds1307->regs[1] & RX8025_BIT_VDET) { ds1307->regs[1] &= ~RX8025_BIT_VDET; - i2c_smbus_write_byte_data(client, - RX8025_REG_CTRL2 << 4 | 0x08, - ds1307->regs[1]); - dev_warn(&client->dev, "voltage drop detected\n"); + regmap_write(ds1307->regmap, + RX8025_REG_CTRL2 << 4 | 0x08, + ds1307->regs[1]); + dev_warn(ds1307->dev, "voltage drop detected\n"); } /* make sure we are running in 24hour mode */ @@ -1537,16 +1379,15 @@ static int ds1307_probe(struct i2c_client *client, u8 hour; /* switch to 24 hour mode */ - i2c_smbus_write_byte_data(client, - RX8025_REG_CTRL1 << 4 | 0x08, - ds1307->regs[0] | - RX8025_BIT_2412); - - tmp = i2c_smbus_read_i2c_block_data(ds1307->client, - RX8025_REG_CTRL1 << 4 | 0x08, 2, buf); - if (tmp != 2) { - dev_dbg(&client->dev, "read error %d\n", tmp); - err = -EIO; + regmap_write(ds1307->regmap, + RX8025_REG_CTRL1 << 4 | 0x08, + ds1307->regs[0] | RX8025_BIT_2412); + + err = regmap_bulk_read(ds1307->regmap, + RX8025_REG_CTRL1 << 4 | 0x08, + buf, 2); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); goto exit; } @@ -1557,9 +1398,8 @@ static int ds1307_probe(struct i2c_client *client, if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM) hour += 12; - i2c_smbus_write_byte_data(client, - DS1307_REG_HOUR << 4 | 0x08, - hour); + regmap_write(ds1307->regmap, + DS1307_REG_HOUR << 4 | 0x08, hour); } break; case ds_1388: @@ -1567,7 +1407,7 @@ static int ds1307_probe(struct i2c_client *client, break; case mcp794xx: rtc_ops = &mcp794xx_rtc_ops; - if (ds1307->client->irq > 0 && chip->alarm) { + if (ds1307->irq > 0 && chip->alarm) { irq_handler = mcp794xx_irq; want_irq = true; } @@ -1578,10 +1418,9 @@ static int ds1307_probe(struct i2c_client *client, read_rtc: /* read RTC registers */ - tmp = ds1307->read_block_data(ds1307->client, ds1307->offset, 8, buf); - if (tmp != 8) { - dev_dbg(&client->dev, "read error %d\n", tmp); - err = -EIO; + err = regmap_bulk_read(ds1307->regmap, ds1307->offset, buf, 8); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); goto exit; } @@ -1597,56 +1436,55 @@ read_rtc: case m41t00: /* clock halted? turn it on, so clock can tick. */ if (tmp & DS1307_BIT_CH) { - i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0); - dev_warn(&client->dev, "SET TIME!\n"); + regmap_write(ds1307->regmap, DS1307_REG_SECS, 0); + dev_warn(ds1307->dev, "SET TIME!\n"); goto read_rtc; } break; case ds_1338: /* clock halted? turn it on, so clock can tick. */ if (tmp & DS1307_BIT_CH) - i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0); + regmap_write(ds1307->regmap, DS1307_REG_SECS, 0); /* oscillator fault? clear flag, and warn */ if (ds1307->regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) { - i2c_smbus_write_byte_data(client, DS1307_REG_CONTROL, - ds1307->regs[DS1307_REG_CONTROL] - & ~DS1338_BIT_OSF); - dev_warn(&client->dev, "SET TIME!\n"); + regmap_write(ds1307->regmap, DS1307_REG_CONTROL, + ds1307->regs[DS1307_REG_CONTROL] & + ~DS1338_BIT_OSF); + dev_warn(ds1307->dev, "SET TIME!\n"); goto read_rtc; } break; case ds_1340: /* clock halted? turn it on, so clock can tick. */ if (tmp & DS1340_BIT_nEOSC) - i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0); + regmap_write(ds1307->regmap, DS1307_REG_SECS, 0); - tmp = i2c_smbus_read_byte_data(client, DS1340_REG_FLAG); - if (tmp < 0) { - dev_dbg(&client->dev, "read error %d\n", tmp); - err = -EIO; + err = regmap_read(ds1307->regmap, DS1340_REG_FLAG, &tmp); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); goto exit; } /* oscillator fault? clear flag, and warn */ if (tmp & DS1340_BIT_OSF) { - i2c_smbus_write_byte_data(client, DS1340_REG_FLAG, 0); - dev_warn(&client->dev, "SET TIME!\n"); + regmap_write(ds1307->regmap, DS1340_REG_FLAG, 0); + dev_warn(ds1307->dev, "SET TIME!\n"); } break; case mcp794xx: /* make sure that the backup battery is enabled */ if (!(ds1307->regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) { - i2c_smbus_write_byte_data(client, DS1307_REG_WDAY, - ds1307->regs[DS1307_REG_WDAY] - | MCP794XX_BIT_VBATEN); + regmap_write(ds1307->regmap, DS1307_REG_WDAY, + ds1307->regs[DS1307_REG_WDAY] | + MCP794XX_BIT_VBATEN); } /* clock halted? turn it on, so clock can tick. */ if (!(tmp & MCP794XX_BIT_ST)) { - i2c_smbus_write_byte_data(client, DS1307_REG_SECS, - MCP794XX_BIT_ST); - dev_warn(&client->dev, "SET TIME!\n"); + regmap_write(ds1307->regmap, DS1307_REG_SECS, + MCP794XX_BIT_ST); + dev_warn(ds1307->dev, "SET TIME!\n"); goto read_rtc; } @@ -1680,16 +1518,15 @@ read_rtc: tmp = 0; if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM) tmp += 12; - i2c_smbus_write_byte_data(client, - ds1307->offset + DS1307_REG_HOUR, - bin2bcd(tmp)); + regmap_write(ds1307->regmap, ds1307->offset + DS1307_REG_HOUR, + bin2bcd(tmp)); } /* * Some IPs have weekday reset value = 0x1 which might not correct * hence compute the wday using the current date/month/year values */ - ds1307_get_time(&client->dev, &tm); + ds1307_get_time(ds1307->dev, &tm); wday = tm.tm_wday; timestamp = rtc_tm_to_time64(&tm); rtc_time64_to_tm(timestamp, &tm); @@ -1700,51 +1537,53 @@ read_rtc: * timestamp */ if (wday != tm.tm_wday) { - wday = i2c_smbus_read_byte_data(client, MCP794XX_REG_WEEKDAY); + regmap_read(ds1307->regmap, MCP794XX_REG_WEEKDAY, &wday); wday = wday & ~MCP794XX_REG_WEEKDAY_WDAY_MASK; wday = wday | (tm.tm_wday + 1); - i2c_smbus_write_byte_data(client, MCP794XX_REG_WEEKDAY, wday); + regmap_write(ds1307->regmap, MCP794XX_REG_WEEKDAY, wday); } if (want_irq) { - device_set_wakeup_capable(&client->dev, true); + device_set_wakeup_capable(ds1307->dev, true); set_bit(HAS_ALARM, &ds1307->flags); } - ds1307->rtc = devm_rtc_device_register(&client->dev, client->name, + ds1307->rtc = devm_rtc_device_register(ds1307->dev, ds1307->name, rtc_ops, THIS_MODULE); if (IS_ERR(ds1307->rtc)) { return PTR_ERR(ds1307->rtc); } - if (ds1307_can_wakeup_device && ds1307->client->irq <= 0) { + if (ds1307_can_wakeup_device && ds1307->irq <= 0) { /* Disable request for an IRQ */ want_irq = false; - dev_info(&client->dev, "'wakeup-source' is set, request for an IRQ is disabled!\n"); + dev_info(ds1307->dev, + "'wakeup-source' is set, request for an IRQ is disabled!\n"); /* We cannot support UIE mode if we do not have an IRQ line */ ds1307->rtc->uie_unsupported = 1; } if (want_irq) { - err = devm_request_threaded_irq(&client->dev, - client->irq, NULL, irq_handler, + err = devm_request_threaded_irq(ds1307->dev, + ds1307->irq, NULL, irq_handler, IRQF_SHARED | IRQF_ONESHOT, - ds1307->rtc->name, client); + ds1307->rtc->name, ds1307); if (err) { client->irq = 0; - device_set_wakeup_capable(&client->dev, false); + device_set_wakeup_capable(ds1307->dev, false); clear_bit(HAS_ALARM, &ds1307->flags); - dev_err(&client->dev, "unable to request IRQ!\n"); + dev_err(ds1307->dev, "unable to request IRQ!\n"); } else - dev_dbg(&client->dev, "got IRQ %d\n", client->irq); + dev_dbg(ds1307->dev, "got IRQ %d\n", client->irq); } if (chip->nvram_size) { - ds1307->nvram = devm_kzalloc(&client->dev, + ds1307->nvram = devm_kzalloc(ds1307->dev, sizeof(struct bin_attribute), GFP_KERNEL); if (!ds1307->nvram) { - dev_err(&client->dev, "cannot allocate memory for nvram sysfs\n"); + dev_err(ds1307->dev, + "cannot allocate memory for nvram sysfs\n"); } else { ds1307->nvram->attr.name = "nvram"; @@ -1757,15 +1596,15 @@ read_rtc: ds1307->nvram->size = chip->nvram_size; ds1307->nvram_offset = chip->nvram_offset; - err = sysfs_create_bin_file(&client->dev.kobj, + err = sysfs_create_bin_file(&ds1307->dev->kobj, ds1307->nvram); if (err) { - dev_err(&client->dev, + dev_err(ds1307->dev, "unable to create sysfs file: %s\n", ds1307->nvram->attr.name); } else { set_bit(HAS_NVRAM, &ds1307->flags); - dev_info(&client->dev, "%zu bytes nvram\n", + dev_info(ds1307->dev, "%zu bytes nvram\n", ds1307->nvram->size); } } @@ -1785,7 +1624,7 @@ static int ds1307_remove(struct i2c_client *client) struct ds1307 *ds1307 = i2c_get_clientdata(client); if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags)) - sysfs_remove_bin_file(&client->dev.kobj, ds1307->nvram); + sysfs_remove_bin_file(&ds1307->dev->kobj, ds1307->nvram); return 0; } -- cgit v1.1 From 8066360744743fb7f2dee1e2041fc9fc92f9d6b5 Mon Sep 17 00:00:00 2001 From: David Lowe Date: Sat, 22 Apr 2017 18:28:00 +0100 Subject: rtc: rtc-ds1307: enable support for mcp794xx as a wakeup source without IRQ This patch extends the fixes for ds1337, ds1339, ds3231 in commit 8bc2a40730ec ("rtc: ds1307: add support for the DT property 'wakeup-source'") to mcp794xx devices, so that those parts can similarly be used as a wakeup source without an IRQ to the processor. Tested on Raspberry Pi ZeroW with MCP79400. Signed-off-by: David Lowe Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 94e13d7..a264938 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -1407,7 +1407,8 @@ static int ds1307_probe(struct i2c_client *client, break; case mcp794xx: rtc_ops = &mcp794xx_rtc_ops; - if (ds1307->irq > 0 && chip->alarm) { + if (chip->alarm && (ds1307->irq > 0 || + ds1307_can_wakeup_device)) { irq_handler = mcp794xx_irq; want_irq = true; } -- cgit v1.1 From 2de9261c18c2b09a8025872bb470853dc8db726c Mon Sep 17 00:00:00 2001 From: Gary Bisson Date: Tue, 25 Apr 2017 16:45:14 +0200 Subject: rtc: m41t80: fix SQWE override when setting an alarm Currently setting an alarm clears the SQWE bit which means that the clock output is disabled no matter its previous state. Signed-off-by: Gary Bisson Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-m41t80.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 5ec4653..b074964 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -332,6 +332,9 @@ static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) return err; } + /* Keep SQWE bit value */ + alarmvals[0] |= (ret & M41T80_ALMON_SQWE); + ret = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); if (ret < 0) return ret; -- cgit v1.1 From 0f546b058b86ea2f661cc7a6e931cee5a29959ef Mon Sep 17 00:00:00 2001 From: Gary Bisson Date: Tue, 25 Apr 2017 16:45:15 +0200 Subject: rtc: m41t80: fix SQW dividers override when setting a date This patch is only relevant for RTC with the SQ_ALT feature which means the clock output frequency divider is stored in the weekday register. Current implementation discards the previous dividers value and clear them as soon as the time is set. Signed-off-by: Gary Bisson Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-m41t80.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index b074964..1e8ba7a 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -227,6 +227,7 @@ static int m41t80_get_datetime(struct i2c_client *client, /* Sets the given date and time to the real time clock. */ static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm) { + struct m41t80_data *clientdata = i2c_get_clientdata(client); unsigned char buf[8]; int err, flags; @@ -242,6 +243,17 @@ static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm) buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100); buf[M41T80_REG_WDAY] = tm->tm_wday; + /* If the square wave output is controlled in the weekday register */ + if (clientdata->features & M41T80_FEATURE_SQ_ALT) { + int val; + + val = i2c_smbus_read_byte_data(client, M41T80_REG_WDAY); + if (val < 0) + return val; + + buf[M41T80_REG_WDAY] |= (val & 0xf0); + } + err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC, sizeof(buf), buf); if (err < 0) { -- cgit v1.1 From 17e296eb35dc5b15511ffa4ef5de67e53cb5a284 Mon Sep 17 00:00:00 2001 From: Gary Bisson Date: Tue, 25 Apr 2017 16:45:16 +0200 Subject: rtc: m41t80: remove sqw sysfs entry In order to use the proper clock framework to control this feature. Signed-off-by: Gary Bisson Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-m41t80.c | 88 ------------------------------------------------ 1 file changed, 88 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 1e8ba7a..6145bc1 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -446,96 +446,8 @@ static ssize_t flags_show(struct device *dev, } static DEVICE_ATTR_RO(flags); -static ssize_t sqwfreq_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - struct m41t80_data *clientdata = i2c_get_clientdata(client); - int val, reg_sqw; - - if (!(clientdata->features & M41T80_FEATURE_SQ)) - return -EINVAL; - - reg_sqw = M41T80_REG_SQW; - if (clientdata->features & M41T80_FEATURE_SQ_ALT) - reg_sqw = M41T80_REG_WDAY; - val = i2c_smbus_read_byte_data(client, reg_sqw); - if (val < 0) - return val; - val = (val >> 4) & 0xf; - switch (val) { - case 0: - break; - case 1: - val = 32768; - break; - default: - val = 32768 >> val; - } - return sprintf(buf, "%d\n", val); -} - -static ssize_t sqwfreq_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct m41t80_data *clientdata = i2c_get_clientdata(client); - int almon, sqw, reg_sqw, rc; - unsigned long val; - - rc = kstrtoul(buf, 0, &val); - if (rc < 0) - return rc; - - if (!(clientdata->features & M41T80_FEATURE_SQ)) - return -EINVAL; - - if (val) { - if (!is_power_of_2(val)) - return -EINVAL; - val = ilog2(val); - if (val == 15) - val = 1; - else if (val < 14) - val = 15 - val; - else - return -EINVAL; - } - /* disable SQW, set SQW frequency & re-enable */ - almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); - if (almon < 0) - return almon; - reg_sqw = M41T80_REG_SQW; - if (clientdata->features & M41T80_FEATURE_SQ_ALT) - reg_sqw = M41T80_REG_WDAY; - sqw = i2c_smbus_read_byte_data(client, reg_sqw); - if (sqw < 0) - return sqw; - sqw = (sqw & 0x0f) | (val << 4); - - rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, - almon & ~M41T80_ALMON_SQWE); - if (rc < 0) - return rc; - - if (val) { - rc = i2c_smbus_write_byte_data(client, reg_sqw, sqw); - if (rc < 0) - return rc; - - rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, - almon | M41T80_ALMON_SQWE); - if (rc < 0) - return rc; - } - return count; -} -static DEVICE_ATTR_RW(sqwfreq); - static struct attribute *attrs[] = { &dev_attr_flags.attr, - &dev_attr_sqwfreq.attr, NULL, }; -- cgit v1.1 From 1373e77b4f108929caa448bb23f341a4af774cad Mon Sep 17 00:00:00 2001 From: Gary Bisson Date: Tue, 25 Apr 2017 16:45:17 +0200 Subject: rtc: m41t80: add clock provider support Some devices supported by the m41t80 driver have a programmable square-wave output signal (see M41T80_FEATURE_SQ). This enables to use this feature as a clock provider of common clock framework. Signed-off-by: Gary Bisson Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-m41t80.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) (limited to 'drivers') diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 6145bc1..8940e9e 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -53,6 +54,8 @@ #define M41T80_ALARM_REG_SIZE \ (M41T80_REG_ALARM_SEC + 1 - M41T80_REG_ALARM_MON) +#define M41T80_SQW_MAX_FREQ 32768 + #define M41T80_SEC_ST BIT(7) /* ST: Stop Bit */ #define M41T80_ALMON_AFE BIT(7) /* AFE: AF Enable Bit */ #define M41T80_ALMON_SQWE BIT(6) /* SQWE: SQW Enable Bit */ @@ -147,7 +150,11 @@ MODULE_DEVICE_TABLE(of, m41t80_of_match); struct m41t80_data { unsigned long features; + struct i2c_client *client; struct rtc_device *rtc; +#ifdef CONFIG_COMMON_CLK + struct clk_hw sqw; +#endif }; static irqreturn_t m41t80_handle_irq(int irq, void *dev_id) @@ -455,6 +462,166 @@ static struct attribute_group attr_group = { .attrs = attrs, }; +#ifdef CONFIG_COMMON_CLK +#define sqw_to_m41t80_data(_hw) container_of(_hw, struct m41t80_data, sqw) + +static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw); + struct i2c_client *client = m41t80->client; + int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ? + M41T80_REG_WDAY : M41T80_REG_SQW; + int ret = i2c_smbus_read_byte_data(client, reg_sqw); + unsigned long val = M41T80_SQW_MAX_FREQ; + + if (ret < 0) + return 0; + + ret >>= 4; + if (ret == 0) + val = 0; + else if (ret > 1) + val = val / (1 << ret); + + return val; +} + +static long m41t80_sqw_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int i, freq = M41T80_SQW_MAX_FREQ; + + if (freq <= rate) + return freq; + + for (i = 2; i <= ilog2(M41T80_SQW_MAX_FREQ); i++) { + freq /= 1 << i; + if (freq <= rate) + return freq; + } + + return 0; +} + +static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw); + struct i2c_client *client = m41t80->client; + int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ? + M41T80_REG_WDAY : M41T80_REG_SQW; + int reg, ret, val = 0; + + if (rate) { + if (!is_power_of_2(rate)) + return -EINVAL; + val = ilog2(rate); + if (val == ilog2(M41T80_SQW_MAX_FREQ)) + val = 1; + else if (val < (ilog2(M41T80_SQW_MAX_FREQ) - 1)) + val = ilog2(M41T80_SQW_MAX_FREQ) - val; + else + return -EINVAL; + } + + reg = i2c_smbus_read_byte_data(client, reg_sqw); + if (reg < 0) + return reg; + + reg = (reg & 0x0f) | (val << 4); + + ret = i2c_smbus_write_byte_data(client, reg_sqw, reg); + if (ret < 0) + return ret; + + return -EINVAL; +} + +static int m41t80_sqw_control(struct clk_hw *hw, bool enable) +{ + struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw); + struct i2c_client *client = m41t80->client; + int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + + if (ret < 0) + return ret; + + if (enable) + ret |= M41T80_ALMON_SQWE; + else + ret &= ~M41T80_ALMON_SQWE; + + return i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret); +} + +static int m41t80_sqw_prepare(struct clk_hw *hw) +{ + return m41t80_sqw_control(hw, 1); +} + +static void m41t80_sqw_unprepare(struct clk_hw *hw) +{ + m41t80_sqw_control(hw, 0); +} + +static int m41t80_sqw_is_prepared(struct clk_hw *hw) +{ + struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw); + struct i2c_client *client = m41t80->client; + int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + + if (ret < 0) + return ret; + + return !!(ret & M41T80_ALMON_SQWE); +} + +static const struct clk_ops m41t80_sqw_ops = { + .prepare = m41t80_sqw_prepare, + .unprepare = m41t80_sqw_unprepare, + .is_prepared = m41t80_sqw_is_prepared, + .recalc_rate = m41t80_sqw_recalc_rate, + .round_rate = m41t80_sqw_round_rate, + .set_rate = m41t80_sqw_set_rate, +}; + +static struct clk *m41t80_sqw_register_clk(struct m41t80_data *m41t80) +{ + struct i2c_client *client = m41t80->client; + struct device_node *node = client->dev.of_node; + struct clk *clk; + struct clk_init_data init; + int ret; + + /* First disable the clock */ + ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + if (ret < 0) + return ERR_PTR(ret); + ret = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + ret & ~(M41T80_ALMON_SQWE)); + if (ret < 0) + return ERR_PTR(ret); + + init.name = "m41t80-sqw"; + init.ops = &m41t80_sqw_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + m41t80->sqw.init = &init; + + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + + /* register the clock */ + clk = clk_register(&client->dev, &m41t80->sqw); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return clk; +} +#endif + #ifdef CONFIG_RTC_DRV_M41T80_WDT /* ***************************************************************************** @@ -772,6 +939,7 @@ static int m41t80_probe(struct i2c_client *client, if (!m41t80_data) return -ENOMEM; + m41t80_data->client = client; if (client->dev.of_node) m41t80_data->features = (unsigned long) of_device_get_match_data(&client->dev); @@ -864,6 +1032,10 @@ static int m41t80_probe(struct i2c_client *client, } } #endif +#ifdef CONFIG_COMMON_CLK + if (m41t80_data->features & M41T80_FEATURE_SQ) + m41t80_sqw_register_clk(m41t80_data); +#endif return 0; } -- cgit v1.1 From 6dc1cf6f932bb0ea4d8f5e913a0a401ecacd2f03 Mon Sep 17 00:00:00 2001 From: Vaibhav Jain Date: Fri, 19 May 2017 15:35:09 +0530 Subject: rtc: opal: Handle disabled TPO in opal_get_tpo_time() On PowerNV platform when Timed-Power-On(TPO) is disabled, read of stored TPO yields value with all date components set to '0' inside opal_get_tpo_time(). The function opal_to_tm() then converts it to an offset from year 1900 yielding alarm-time == "1900-00-01 00:00:00". This causes problems with __rtc_read_alarm() that expecting an offset from "1970-00-01 00:00:00" and returned alarm-time results in a -ve value for time64_t. Which ultimately results in this error reported in kernel logs with a seemingly garbage value: "rtc rtc0: invalid alarm value: -2-1--1041528741 2005511117:71582844:32" We fix this by explicitly handling the case of all alarm date-time components being '0' inside opal_get_tpo_time() and returning -ENOENT in such a case. This signals generic rtc that no alarm is set and it bails out from the alarm initialization flow without reporting the above error. Signed-off-by: Vaibhav Jain Reported-by: Steve Best Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-opal.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/rtc/rtc-opal.c b/drivers/rtc/rtc-opal.c index ea20f62..2d84e2a 100644 --- a/drivers/rtc/rtc-opal.c +++ b/drivers/rtc/rtc-opal.c @@ -142,6 +142,16 @@ static int opal_get_tpo_time(struct device *dev, struct rtc_wkalrm *alarm) y_m_d = be32_to_cpu(__y_m_d); h_m_s_ms = ((u64)be32_to_cpu(__h_m) << 32); + + /* check if no alarm is set */ + if (y_m_d == 0 && h_m_s_ms == 0) { + pr_debug("No alarm is set\n"); + rc = -ENOENT; + goto exit; + } else { + pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms); + } + opal_to_tm(y_m_d, h_m_s_ms, &alarm->time); exit: -- cgit v1.1 From da96aea0ed177105cb13ee83b328f6c61e061d3f Mon Sep 17 00:00:00 2001 From: Vaibhav Jain Date: Fri, 19 May 2017 22:18:55 +0530 Subject: rtc: interface: Validate alarm-time before handling rollover In function __rtc_read_alarm() its possible for an alarm time-stamp to be invalid even after replacing missing components with current time-stamp. The condition 'alarm->time.tm_year < 70' will trigger this case and will cause the call to 'rtc_tm_to_time64(&alarm->time)' return a negative value for variable t_alm. While handling alarm rollover this negative t_alm (assumed to seconds offset from '1970-01-01 00:00:00') is converted back to rtc_time via rtc_time64_to_tm() which results in this error log with seemingly garbage values: "rtc rtc0: invalid alarm value: -2-1--1041528741 2005511117:71582844:32" This error was generated when the rtc driver (rtc-opal in this case) returned an alarm time-stamp of '00-00-00 00:00:00' to indicate that the alarm is disabled. Though I have submitted a separate fix for the rtc-opal driver, this issue may potentially impact other existing/future rtc drivers. To fix this issue the patch validates the alarm time-stamp just after filling up the missing datetime components and if rtc_valid_tm() still reports it to be invalid then bails out of the function without handling the rollover. Reported-by: Steve Best Signed-off-by: Vaibhav Jain Signed-off-by: Alexandre Belloni --- drivers/rtc/interface.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index fc0fa75..8cec9a0 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -227,6 +227,13 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) missing = year; } + /* Can't proceed if alarm is still invalid after replacing + * missing fields. + */ + err = rtc_valid_tm(&alarm->time); + if (err) + goto done; + /* with luck, no rollover is needed */ t_now = rtc_tm_to_time64(&now); t_alm = rtc_tm_to_time64(&alarm->time); @@ -278,9 +285,9 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) dev_warn(&rtc->dev, "alarm rollover not handled\n"); } -done: err = rtc_valid_tm(&alarm->time); +done: if (err) { dev_warn(&rtc->dev, "invalid alarm value: %d-%d-%d %d:%d:%d\n", alarm->time.tm_year + 1900, alarm->time.tm_mon + 1, -- cgit v1.1 From 77a73f3caece06d1d17268f43ec39c5779e6ce24 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 2 Jun 2017 13:57:03 +0200 Subject: rtc: sysfs: make name uniform The name sysfs attribute is not useful in its current form because of all the drivers: - 3 are using the feature correctly - 2 are clearly misusing it - 60 are using driver.name, either directly or indirectly - 46 are using pdev->name - 8 are using client->name - 31 are using a variation of driver.name (addition or removal of rtc-, -rtc, _rtc, rtc_) Make it uniform and use the driver name and the device name. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-sysfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c index 1218d5d..e364550 100644 --- a/drivers/rtc/rtc-sysfs.c +++ b/drivers/rtc/rtc-sysfs.c @@ -27,7 +27,8 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%s\n", to_rtc_device(dev)->name); + return sprintf(buf, "%s %s\n", dev_driver_string(dev->parent), + dev_name(dev->parent)); } static DEVICE_ATTR_RO(name); -- cgit v1.1 From 9f8606533b961b06c258cf4804e3379aa14c5951 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 2 Jun 2017 14:01:37 +0200 Subject: rtc: dev: remove rtc->name from debug message rtc->name is superfluous here because the rtc is already registered at that point and its name has already been printed. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index e81a871..794bc4f 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -464,7 +464,7 @@ void rtc_dev_prepare(struct rtc_device *rtc) return; if (rtc->id >= RTC_DEV_MAX) { - dev_dbg(&rtc->dev, "%s: too many RTC devices\n", rtc->name); + dev_dbg(&rtc->dev, "too many RTC devices\n"); return; } -- cgit v1.1 From f09e706992bd031f0d94b77e40074e7fea5c89a9 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 2 Jun 2017 14:03:39 +0200 Subject: rtc: pcf8563: avoid using rtc->name pcf8563->rtc->name is a copy of pcf8563_driver.driver.name, use it instead Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-pcf8563.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 1227cea..cea6ea4 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -606,7 +606,7 @@ static int pcf8563_probe(struct i2c_client *client, err = devm_request_threaded_irq(&client->dev, client->irq, NULL, pcf8563_irq, IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING, - pcf8563->rtc->name, client); + pcf8563_driver.driver.name, client); if (err) { dev_err(&client->dev, "unable to request IRQ %d\n", client->irq); -- cgit v1.1 From 4b9e2a0c0591d2789a43414b8299466800873aea Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 2 Jun 2017 14:13:21 +0200 Subject: rtc: ds1307: avoid using rtc-name ds1307->rtc->name is a copy of ds1307->name, use it instead. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index a264938..16ace2f 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -1567,7 +1567,7 @@ read_rtc: err = devm_request_threaded_irq(ds1307->dev, ds1307->irq, NULL, irq_handler, IRQF_SHARED | IRQF_ONESHOT, - ds1307->rtc->name, ds1307); + ds1307->name, ds1307); if (err) { client->irq = 0; device_set_wakeup_capable(ds1307->dev, false); -- cgit v1.1 From 5c82a6ae0242416cfead597bb2b42aa3481a0ba7 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 2 Jun 2017 14:15:15 +0200 Subject: rtc: remove rtc_device.name rtc->name is only used in messages were it is superfluous. Remove it completely from the structure. Signed-off-by: Alexandre Belloni --- drivers/rtc/class.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 5fb43989..543c64c 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -224,7 +224,6 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, rtc->pie_timer.function = rtc_pie_update_irq; rtc->pie_enabled = 0; - strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE); dev_set_name(&rtc->dev, "rtc%d", id); /* Check to see if there is an ALARM already set in hw */ @@ -238,20 +237,20 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, err = cdev_device_add(&rtc->char_dev, &rtc->dev); if (err) { dev_warn(&rtc->dev, "%s: failed to add char device %d:%d\n", - rtc->name, MAJOR(rtc->dev.devt), rtc->id); + name, MAJOR(rtc->dev.devt), rtc->id); /* This will free both memory and the ID */ put_device(&rtc->dev); goto exit; } else { - dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", rtc->name, + dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", name, MAJOR(rtc->dev.devt), rtc->id); } rtc_proc_add_device(rtc); dev_info(dev, "rtc core: registered %s as %s\n", - rtc->name, dev_name(&rtc->dev)); + name, dev_name(&rtc->dev)); return rtc; -- cgit v1.1 From 0ec7769a98b00866e37740328d65cba6594d178d Mon Sep 17 00:00:00 2001 From: Vaibhav Jain Date: Wed, 31 May 2017 18:39:01 +0530 Subject: rtc: opal: Implement rtc_class_ops.alarm_irq_enable callback Provide an implementation of the callback rtc_class_ops.alarm_irq_enable for rtc-opal driver. This callback is called when the wake alarm is disabled via the command: 'echo 0 > /sys/class/rtc/rtc0/wakealarm' Without this the Timed-Power-On(TPO) config remains set even when its disabled by the above command and FSP will still force machine boot at previously configured alarm time. The callback is implemented as function opal_tpo_alarm_irq_enable() which calls opal_set_tpo_time() with alarm.enabled == 0. A branch is added to opal_set_tpo_time() to handle this case by passing y_m_d == h_m_s_ms == 0 to opal as arguments for opal_tpo_write() call. Signed-off-by: Vaibhav Jain Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-opal.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-opal.c b/drivers/rtc/rtc-opal.c index 2d84e2a..e2a946c 100644 --- a/drivers/rtc/rtc-opal.c +++ b/drivers/rtc/rtc-opal.c @@ -167,7 +167,14 @@ static int opal_set_tpo_time(struct device *dev, struct rtc_wkalrm *alarm) u32 y_m_d = 0; int token, rc; - tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms); + /* if alarm is enabled */ + if (alarm->enabled) { + tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms); + pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms); + + } else { + pr_debug("Alarm getting disabled\n"); + } token = opal_async_get_token_interruptible(); if (token < 0) { @@ -200,6 +207,18 @@ exit: return rc; } +int opal_tpo_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtc_wkalrm alarm = { .enabled = 0 }; + + /* + * TPO is automatically enabled when opal_set_tpo_time() is called with + * non-zero rtc-time. We only handle disable case which needs to be + * explicitly told to opal. + */ + return enabled ? 0 : opal_set_tpo_time(dev, &alarm); +} + static struct rtc_class_ops opal_rtc_ops = { .read_time = opal_get_rtc_time, .set_time = opal_set_rtc_time, @@ -215,6 +234,7 @@ static int opal_rtc_probe(struct platform_device *pdev) device_set_wakeup_capable(&pdev->dev, true); opal_rtc_ops.read_alarm = opal_get_tpo_time; opal_rtc_ops.set_alarm = opal_set_tpo_time; + opal_rtc_ops.alarm_irq_enable = opal_tpo_alarm_irq_enable; } rtc = devm_rtc_device_register(&pdev->dev, DRVNAME, &opal_rtc_ops, -- cgit v1.1 From fe6d94fe66b50c10c94911e525ad680a8f2dfc21 Mon Sep 17 00:00:00 2001 From: "Diaz de Grenu, Jose" Date: Thu, 11 May 2017 17:38:39 +0200 Subject: rtc: mxc: remove unused variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This variable was never used. With GCC 6.2, we get the following warning: drivers/rtc/rtc-mxc.c:44:18: warning: ‘PIE_BIT_DEF’ defined but not used [-Wunused-const-variable=] static const u32 PIE_BIT_DEF[MAX_PIE_NUM][2] = { Signed-off-by: Diaz de Grenu, Jose Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-mxc.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index 7731912..401f46d 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -43,17 +43,6 @@ #define MAX_PIE_NUM 9 #define MAX_PIE_FREQ 512 -static const u32 PIE_BIT_DEF[MAX_PIE_NUM][2] = { - { 2, RTC_2HZ_BIT }, - { 4, RTC_SAM0_BIT }, - { 8, RTC_SAM1_BIT }, - { 16, RTC_SAM2_BIT }, - { 32, RTC_SAM3_BIT }, - { 64, RTC_SAM4_BIT }, - { 128, RTC_SAM5_BIT }, - { 256, RTC_SAM6_BIT }, - { MAX_PIE_FREQ, RTC_SAM7_BIT }, -}; #define MXC_RTC_TIME 0 #define MXC_RTC_ALARM 1 -- cgit v1.1 From 8768e7b3e3677fb51bf3ed5ceef174f40be4c287 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Jun 2017 21:28:02 +0200 Subject: rtc: s3c: Jump to central exit point on getting src clock error In other error paths in probe, centralized exit point was used so make this consistent. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-s3c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index d44fb34..c5aa7a3 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -510,8 +510,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) else dev_dbg(&pdev->dev, "probe deferred due to missing rtc src clk\n"); - clk_disable_unprepare(info->rtc_clk); - return ret; + goto err_src_clk; } clk_prepare_enable(info->rtc_src_clk); } @@ -575,6 +574,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) if (info->data->needs_src_clk) clk_disable_unprepare(info->rtc_src_clk); +err_src_clk: clk_disable_unprepare(info->rtc_clk); return ret; -- cgit v1.1 From fc1afe60535eeb433648ebf80e23868cc6dd4fca Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Jun 2017 21:28:03 +0200 Subject: rtc: s3c: Minor white-space cleanups Minor cleanups to make the code easier to read. No functional changes. 1. Remove one space before labels as this is nowadays mostly preferred. 2. Fix indentation of arguments in function calls. 3. Split structure member declaration. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-s3c.c | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index c5aa7a3..2b503da 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -49,7 +49,8 @@ struct s3c_rtc { spinlock_t pie_lock; spinlock_t alarm_clk_lock; - int ticnt_save, ticnt_en_save; + int ticnt_save; + int ticnt_en_save; bool wake_en; }; @@ -169,7 +170,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) s3c_rtc_enable_clk(info); - retry_get_time: +retry_get_time: rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN); rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR); rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE); @@ -199,8 +200,8 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) rtc_tm->tm_year += 100; dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\n", - 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, - rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); + 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, + rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); rtc_tm->tm_mon -= 1; @@ -213,8 +214,8 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) int year = tm->tm_year - 100; dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n", - 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); + 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); /* we get around y2k by simply not supporting it */ @@ -259,9 +260,9 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; dev_dbg(dev, "read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n", - alm_en, - 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, - alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); + alm_en, + 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, + alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); /* decode the alarm enable field */ if (alm_en & S3C2410_RTCALM_SECEN) @@ -295,9 +296,9 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) int year = tm->tm_year - 100; dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", - alrm->enabled, - 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); + alrm->enabled, + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); s3c_rtc_enable_clk(info); @@ -378,8 +379,7 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info) dev_info(info->dev, "rtc disabled, re-enabling\n"); tmp = readw(info->base + S3C2410_RTCCON); - writew(tmp | S3C2410_RTCCON_RTCEN, - info->base + S3C2410_RTCCON); + writew(tmp | S3C2410_RTCCON_RTCEN, info->base + S3C2410_RTCCON); } if (con & S3C2410_RTCCON_CNTSEL) { @@ -387,7 +387,7 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info) tmp = readw(info->base + S3C2410_RTCCON); writew(tmp & ~S3C2410_RTCCON_CNTSEL, - info->base + S3C2410_RTCCON); + info->base + S3C2410_RTCCON); } if (con & S3C2410_RTCCON_CLKRST) { @@ -395,7 +395,7 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info) tmp = readw(info->base + S3C2410_RTCCON); writew(tmp & ~S3C2410_RTCCON_CLKRST, - info->base + S3C2410_RTCCON); + info->base + S3C2410_RTCCON); } } @@ -481,7 +481,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) } dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n", - info->irq_tick, info->irq_alarm); + info->irq_tick, info->irq_alarm); /* get the memory region */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -520,7 +520,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) info->data->enable(info); dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", - readw(info->base + S3C2410_RTCCON)); + readw(info->base + S3C2410_RTCCON)); device_init_wakeup(&pdev->dev, 1); @@ -540,7 +540,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) /* register RTC and exit */ info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops, - THIS_MODULE); + THIS_MODULE); if (IS_ERR(info->rtc)) { dev_err(&pdev->dev, "cannot attach rtc\n"); ret = PTR_ERR(info->rtc); @@ -548,14 +548,14 @@ static int s3c_rtc_probe(struct platform_device *pdev) } ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq, - 0, "s3c2410-rtc alarm", info); + 0, "s3c2410-rtc alarm", info); if (ret) { dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret); goto err_nortc; } ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq, - 0, "s3c2410-rtc tick", info); + 0, "s3c2410-rtc tick", info); if (ret) { dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_tick, ret); goto err_nortc; @@ -568,7 +568,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) return 0; - err_nortc: +err_nortc: if (info->data->disable) info->data->disable(info); @@ -747,8 +747,7 @@ static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info) writel(info->ticnt_save, info->base + S3C2410_TICNT); if (info->ticnt_en_save) { con = readw(info->base + S3C2410_RTCCON); - writew(con | info->ticnt_en_save, - info->base + S3C2410_RTCCON); + writew(con | info->ticnt_en_save, info->base + S3C2410_RTCCON); } } -- cgit v1.1 From 21df6fed0db35cf4382734126bd206b6c750e17c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Jun 2017 21:28:04 +0200 Subject: rtc: s3c: Drop unneeded cast to void pointer There is no need for casting to void pointer for of_device_id data. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-s3c.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 2b503da..bfc8660 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -801,19 +801,19 @@ static struct s3c_rtc_data const s3c6410_rtc_data = { static const struct of_device_id s3c_rtc_dt_match[] = { { .compatible = "samsung,s3c2410-rtc", - .data = (void *)&s3c2410_rtc_data, + .data = &s3c2410_rtc_data, }, { .compatible = "samsung,s3c2416-rtc", - .data = (void *)&s3c2416_rtc_data, + .data = &s3c2416_rtc_data, }, { .compatible = "samsung,s3c2443-rtc", - .data = (void *)&s3c2443_rtc_data, + .data = &s3c2443_rtc_data, }, { .compatible = "samsung,s3c6410-rtc", - .data = (void *)&s3c6410_rtc_data, + .data = &s3c6410_rtc_data, }, { .compatible = "samsung,exynos3250-rtc", - .data = (void *)&s3c6410_rtc_data, + .data = &s3c6410_rtc_data, }, { /* sentinel */ }, }; -- cgit v1.1 From 6b72086d8594febb9e9d63cfb29d950da17e8f06 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Jun 2017 21:28:05 +0200 Subject: rtc: s3c: Do not remove const from rodata memory All instances of struct s3c_rtc_data are in fact static const thus put in rodata so we should not drop the const while getting the pointer to them. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-s3c.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index bfc8660..c666b95 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -41,7 +41,7 @@ struct s3c_rtc { struct clk *rtc_src_clk; bool clk_disabled; - struct s3c_rtc_data *data; + const struct s3c_rtc_data *data; int irq_alarm; int irq_tick; @@ -437,12 +437,12 @@ static int s3c_rtc_remove(struct platform_device *pdev) static const struct of_device_id s3c_rtc_dt_match[]; -static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev) +static const struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev) { const struct of_device_id *match; match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); - return (struct s3c_rtc_data *)match->data; + return match->data; } static int s3c_rtc_probe(struct platform_device *pdev) -- cgit v1.1 From 9903f68af6ca9d9b25f5c50bfedd2e0cb899f848 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Jun 2017 21:28:06 +0200 Subject: rtc: s3c: Handle clock prepare failures in probe clk_prepare_enable() can fail so handle such case. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-s3c.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index c666b95..0cb2f27 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -498,7 +498,9 @@ static int s3c_rtc_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "probe deferred due to missing rtc clk\n"); return ret; } - clk_prepare_enable(info->rtc_clk); + ret = clk_prepare_enable(info->rtc_clk); + if (ret) + return ret; if (info->data->needs_src_clk) { info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src"); @@ -512,7 +514,9 @@ static int s3c_rtc_probe(struct platform_device *pdev) "probe deferred due to missing rtc src clk\n"); goto err_src_clk; } - clk_prepare_enable(info->rtc_src_clk); + ret = clk_prepare_enable(info->rtc_src_clk); + if (ret) + goto err_src_clk; } /* check to see if everything is setup correctly */ -- cgit v1.1 From 498bcf313985c76c4572d8ce885a6480728d28f9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Jun 2017 21:28:07 +0200 Subject: rtc: s3c: Handle clock enable failures clk_enable() can fail so handle such case. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-s3c.c | 72 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 0cb2f27..a8992c2 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -68,18 +68,32 @@ struct s3c_rtc_data { void (*disable) (struct s3c_rtc *info); }; -static void s3c_rtc_enable_clk(struct s3c_rtc *info) +static int s3c_rtc_enable_clk(struct s3c_rtc *info) { unsigned long irq_flags; + int ret = 0; spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); + if (info->clk_disabled) { - clk_enable(info->rtc_clk); - if (info->data->needs_src_clk) - clk_enable(info->rtc_src_clk); + ret = clk_enable(info->rtc_clk); + if (ret) + goto out; + + if (info->data->needs_src_clk) { + ret = clk_enable(info->rtc_src_clk); + if (ret) { + clk_disable(info->rtc_clk); + goto out; + } + } info->clk_disabled = false; } + +out: spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); + + return ret; } static void s3c_rtc_disable_clk(struct s3c_rtc *info) @@ -122,10 +136,13 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) { struct s3c_rtc *info = dev_get_drvdata(dev); unsigned int tmp; + int ret; dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled); - s3c_rtc_enable_clk(info); + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; @@ -136,10 +153,13 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) s3c_rtc_disable_clk(info); - if (enabled) - s3c_rtc_enable_clk(info); - else + if (enabled) { + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + } else { s3c_rtc_disable_clk(info); + } return 0; } @@ -147,10 +167,14 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) /* Set RTC frequency */ static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) { + int ret; + if (!is_power_of_2(freq)) return -EINVAL; - s3c_rtc_enable_clk(info); + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; spin_lock_irq(&info->pie_lock); if (info->data->set_freq) @@ -167,8 +191,11 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) { struct s3c_rtc *info = dev_get_drvdata(dev); unsigned int have_retried = 0; + int ret; - s3c_rtc_enable_clk(info); + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; retry_get_time: rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN); @@ -212,6 +239,7 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) { struct s3c_rtc *info = dev_get_drvdata(dev); int year = tm->tm_year - 100; + int ret; dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n", 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, @@ -224,7 +252,9 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) return -EINVAL; } - s3c_rtc_enable_clk(info); + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC); writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN); @@ -243,8 +273,11 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) struct s3c_rtc *info = dev_get_drvdata(dev); struct rtc_time *alm_tm = &alrm->time; unsigned int alm_en; + int ret; - s3c_rtc_enable_clk(info); + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC); alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN); @@ -293,6 +326,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) struct s3c_rtc *info = dev_get_drvdata(dev); struct rtc_time *tm = &alrm->time; unsigned int alrm_en; + int ret; int year = tm->tm_year - 100; dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", @@ -300,7 +334,9 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - s3c_rtc_enable_clk(info); + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; writeb(0x00, info->base + S3C2410_RTCALM); @@ -349,8 +385,11 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) { struct s3c_rtc *info = dev_get_drvdata(dev); + int ret; - s3c_rtc_enable_clk(info); + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; if (info->data->enable_tick) info->data->enable_tick(info, seq); @@ -589,8 +628,11 @@ err_src_clk: static int s3c_rtc_suspend(struct device *dev) { struct s3c_rtc *info = dev_get_drvdata(dev); + int ret; - s3c_rtc_enable_clk(info); + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; /* save TICNT for anyone using periodic interrupts */ if (info->data->save_tick_cnt) -- cgit v1.1 From ee0981be7704589635de23cc32556399c86653a8 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 18 Jun 2017 22:55:28 +0200 Subject: rtc: ds1307: Add support for Epson RX8130CE Add support for yet another RTC chip, Epson RX8130CE. This time around, the chip has slightly permutated registers and also the register starts at 0x10 instead of 0x0 . So far, we only support the RTC and NVRAM parts of the chip, Alarm and Timer is not supported. Signed-off-by: Marek Vasut Cc: Alexandre Belloni Cc: Arnd Bergmann Cc: Felipe Balbi Cc: Nishanth Menon Cc: Tony Lindgren Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) (limited to 'drivers') diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 16ace2f..3cbb763 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -44,6 +44,7 @@ enum ds_type { m41t00, mcp794xx, rx_8025, + rx_8130, last_ds_type /* always last */ /* rs5c372 too? different address... */ }; @@ -169,6 +170,12 @@ static struct chip_desc chips[last_ds_type] = { [ds_3231] = { .alarm = 1, }, + [rx_8130] = { + .alarm = 1, + /* this is battery backed SRAM */ + .nvram_offset = 0x20, + .nvram_size = 4, /* 32bit (4 word x 8 bit) */ + }, [mcp794xx] = { .alarm = 1, /* this is battery backed SRAM */ @@ -192,6 +199,7 @@ static const struct i2c_device_id ds1307_id[] = { { "pt7c4338", ds_1307 }, { "rx8025", rx_8025 }, { "isl12057", ds_1337 }, + { "rx8130", rx_8130 }, { } }; MODULE_DEVICE_TABLE(i2c, ds1307_id); @@ -587,6 +595,165 @@ static const struct rtc_class_ops ds13xx_rtc_ops = { /*----------------------------------------------------------------------*/ /* + * Alarm support for rx8130 devices. + */ + +#define RX8130_REG_ALARM_MIN 0x07 +#define RX8130_REG_ALARM_HOUR 0x08 +#define RX8130_REG_ALARM_WEEK_OR_DAY 0x09 +#define RX8130_REG_EXTENSION 0x0c +#define RX8130_REG_EXTENSION_WADA (1 << 3) +#define RX8130_REG_FLAG 0x0d +#define RX8130_REG_FLAG_AF (1 << 3) +#define RX8130_REG_CONTROL0 0x0e +#define RX8130_REG_CONTROL0_AIE (1 << 3) + +static irqreturn_t rx8130_irq(int irq, void *dev_id) +{ + struct ds1307 *ds1307 = dev_id; + struct mutex *lock = &ds1307->rtc->ops_lock; + u8 ctl[3]; + int ret; + + mutex_lock(lock); + + /* Read control registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + if (ret < 0) + goto out; + if (!(ctl[1] & RX8130_REG_FLAG_AF)) + goto out; + ctl[1] &= ~RX8130_REG_FLAG_AF; + ctl[2] &= ~RX8130_REG_CONTROL0_AIE; + + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + if (ret < 0) + goto out; + + rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + +out: + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 ald[3], ctl[3]; + int ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + /* Read alarm registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, 3); + if (ret < 0) + return ret; + + /* Read control registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + if (ret < 0) + return ret; + + t->enabled = !!(ctl[2] & RX8130_REG_CONTROL0_AIE); + t->pending = !!(ctl[1] & RX8130_REG_FLAG_AF); + + /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ + t->time.tm_sec = -1; + t->time.tm_min = bcd2bin(ald[0] & 0x7f); + t->time.tm_hour = bcd2bin(ald[1] & 0x7f); + t->time.tm_wday = -1; + t->time.tm_mday = bcd2bin(ald[2] & 0x7f); + t->time.tm_mon = -1; + t->time.tm_year = -1; + t->time.tm_yday = -1; + t->time.tm_isdst = -1; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d enabled=%d\n", + __func__, t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled); + + return 0; +} + +static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 ald[3], ctl[3]; + int ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " + "enabled=%d pending=%d\n", __func__, + t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, + t->enabled, t->pending); + + /* Read control registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + if (ret < 0) + return ret; + + ctl[0] &= ~RX8130_REG_EXTENSION_WADA; + ctl[1] |= RX8130_REG_FLAG_AF; + ctl[2] &= ~RX8130_REG_CONTROL0_AIE; + + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + if (ret < 0) + return ret; + + /* Hardware alarm precision is 1 minute! */ + ald[0] = bin2bcd(t->time.tm_min); + ald[1] = bin2bcd(t->time.tm_hour); + ald[2] = bin2bcd(t->time.tm_mday); + + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, 3); + if (ret < 0) + return ret; + + if (!t->enabled) + return 0; + + ctl[2] |= RX8130_REG_CONTROL0_AIE; + + return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); +} + +static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + int ret, reg; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + ret = regmap_read(ds1307->regmap, RX8130_REG_CONTROL0, ®); + if (ret < 0) + return ret; + + if (enabled) + reg |= RX8130_REG_CONTROL0_AIE; + else + reg &= ~RX8130_REG_CONTROL0_AIE; + + return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg); +} + +static const struct rtc_class_ops rx8130_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = rx8130_read_alarm, + .set_alarm = rx8130_set_alarm, + .alarm_irq_enable = rx8130_alarm_irq_enable, +}; + +/*----------------------------------------------------------------------*/ + +/* * Alarm support for mcp794xx devices. */ @@ -1402,6 +1569,14 @@ static int ds1307_probe(struct i2c_client *client, DS1307_REG_HOUR << 4 | 0x08, hour); } break; + case rx_8130: + ds1307->offset = 0x10; /* Seconds starts at 0x10 */ + rtc_ops = &rx8130_rtc_ops; + if (chip->alarm && ds1307->irq > 0) { + irq_handler = rx8130_irq; + want_irq = true; + } + break; case ds_1388: ds1307->offset = 1; /* Seconds starts at 1 */ break; -- cgit v1.1 From c4f07ecee22ec720936eb58e7c63b89a1e86bf32 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 26 Jun 2017 14:15:03 -0700 Subject: rtc: brcmstb-waketimer: Add Broadcom STB wake-timer This adds support for the Broadcom STB wake-timer which is a timer in the chip's 27Mhz clock domain that offers the ability to wake the system (wake-up source) from suspend states (S2, S3, S5). It is supported using the rtc framework allowing us to configure alarms for system wake-up. Signed-off-by: Brian Norris Signed-off-by: Markus Mayer Signed-off-by: Florian Fainelli Signed-off-by: Arnd Bergmann Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 11 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-brcmstb-waketimer.c | 330 ++++++++++++++++++++++++++++++++++++ 3 files changed, 342 insertions(+) create mode 100644 drivers/rtc/rtc-brcmstb-waketimer.c (limited to 'drivers') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 8d3b957..8e1aa67 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -197,6 +197,17 @@ config RTC_DRV_AC100 This driver can also be built as a module. If so, the module will be called rtc-ac100. +config RTC_DRV_BRCMSTB + tristate "Broadcom STB wake-timer" + depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST + default ARCH_BRCMSTB || BMIPS_GENERIC + help + If you say yes here you get support for the wake-timer found on + Broadcom STB SoCs (BCM7xxx). + + This driver can also be built as a module. If so, the module will + be called rtc-brcmstb-waketimer. + config RTC_DRV_AS3722 tristate "ams AS3722 RTC driver" depends on MFD_AS3722 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 13857d2..df89cac 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o +obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o diff --git a/drivers/rtc/rtc-brcmstb-waketimer.c b/drivers/rtc/rtc-brcmstb-waketimer.c new file mode 100644 index 0000000..796ac79 --- /dev/null +++ b/drivers/rtc/rtc-brcmstb-waketimer.c @@ -0,0 +1,330 @@ +/* + * Copyright © 2014-2017 Broadcom + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct brcmstb_waketmr { + struct rtc_device *rtc; + struct device *dev; + void __iomem *base; + int irq; + struct notifier_block reboot_notifier; + struct clk *clk; + u32 rate; +}; + +#define BRCMSTB_WKTMR_EVENT 0x00 +#define BRCMSTB_WKTMR_COUNTER 0x04 +#define BRCMSTB_WKTMR_ALARM 0x08 +#define BRCMSTB_WKTMR_PRESCALER 0x0C +#define BRCMSTB_WKTMR_PRESCALER_VAL 0x10 + +#define BRCMSTB_WKTMR_DEFAULT_FREQ 27000000 + +static inline void brcmstb_waketmr_clear_alarm(struct brcmstb_waketmr *timer) +{ + writel_relaxed(1, timer->base + BRCMSTB_WKTMR_EVENT); + (void)readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT); +} + +static void brcmstb_waketmr_set_alarm(struct brcmstb_waketmr *timer, + unsigned int secs) +{ + brcmstb_waketmr_clear_alarm(timer); + + writel_relaxed(secs + 1, timer->base + BRCMSTB_WKTMR_ALARM); +} + +static irqreturn_t brcmstb_waketmr_irq(int irq, void *data) +{ + struct brcmstb_waketmr *timer = data; + + pm_wakeup_event(timer->dev, 0); + + return IRQ_HANDLED; +} + +struct wktmr_time { + u32 sec; + u32 pre; +}; + +static void wktmr_read(struct brcmstb_waketmr *timer, + struct wktmr_time *t) +{ + u32 tmp; + + do { + t->sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER); + tmp = readl_relaxed(timer->base + BRCMSTB_WKTMR_PRESCALER_VAL); + } while (tmp >= timer->rate); + + t->pre = timer->rate - tmp; +} + +static int brcmstb_waketmr_prepare_suspend(struct brcmstb_waketmr *timer) +{ + struct device *dev = timer->dev; + int ret = 0; + + if (device_may_wakeup(dev)) { + ret = enable_irq_wake(timer->irq); + if (ret) { + dev_err(dev, "failed to enable wake-up interrupt\n"); + return ret; + } + } + + return ret; +} + +/* If enabled as a wakeup-source, arm the timer when powering off */ +static int brcmstb_waketmr_reboot(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct brcmstb_waketmr *timer; + + timer = container_of(nb, struct brcmstb_waketmr, reboot_notifier); + + /* Set timer for cold boot */ + if (action == SYS_POWER_OFF) + brcmstb_waketmr_prepare_suspend(timer); + + return NOTIFY_DONE; +} + +static int brcmstb_waketmr_gettime(struct device *dev, + struct rtc_time *tm) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + struct wktmr_time now; + + wktmr_read(timer, &now); + + rtc_time_to_tm(now.sec, tm); + + return 0; +} + +static int brcmstb_waketmr_settime(struct device *dev, + struct rtc_time *tm) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + time64_t sec; + + sec = rtc_tm_to_time64(tm); + + if (sec > U32_MAX || sec < 0) + return -EINVAL; + + writel_relaxed(sec, timer->base + BRCMSTB_WKTMR_COUNTER); + + return 0; +} + +static int brcmstb_waketmr_getalarm(struct device *dev, + struct rtc_wkalrm *alarm) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + time64_t sec; + u32 reg; + + sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_ALARM); + if (sec != 0) { + /* Alarm is enabled */ + alarm->enabled = 1; + rtc_time64_to_tm(sec, &alarm->time); + } + + reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT); + alarm->pending = !!(reg & 1); + + return 0; +} + +static int brcmstb_waketmr_setalarm(struct device *dev, + struct rtc_wkalrm *alarm) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + time64_t sec; + + if (alarm->enabled) + sec = rtc_tm_to_time64(&alarm->time); + else + sec = 0; + + if (sec > U32_MAX || sec < 0) + return -EINVAL; + + brcmstb_waketmr_set_alarm(timer, sec); + + return 0; +} + +/* + * Does not do much but keep the RTC class happy. We always support + * alarms. + */ +static int brcmstb_waketmr_alarm_enable(struct device *dev, + unsigned int enabled) +{ + return 0; +} + +static const struct rtc_class_ops brcmstb_waketmr_ops = { + .read_time = brcmstb_waketmr_gettime, + .set_time = brcmstb_waketmr_settime, + .read_alarm = brcmstb_waketmr_getalarm, + .set_alarm = brcmstb_waketmr_setalarm, + .alarm_irq_enable = brcmstb_waketmr_alarm_enable, +}; + +static int brcmstb_waketmr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct brcmstb_waketmr *timer; + struct resource *res; + int ret; + + timer = devm_kzalloc(dev, sizeof(*timer), GFP_KERNEL); + if (!timer) + return -ENOMEM; + + platform_set_drvdata(pdev, timer); + timer->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + timer->base = devm_ioremap_resource(dev, res); + if (IS_ERR(timer->base)) + return PTR_ERR(timer->base); + + /* + * Set wakeup capability before requesting wakeup interrupt, so we can + * process boot-time "wakeups" (e.g., from S5 soft-off) + */ + device_set_wakeup_capable(dev, true); + device_wakeup_enable(dev); + + timer->irq = platform_get_irq(pdev, 0); + if (timer->irq < 0) + return -ENODEV; + + timer->clk = devm_clk_get(dev, NULL); + if (!IS_ERR(timer->clk)) { + ret = clk_prepare_enable(timer->clk); + if (ret) + return ret; + timer->rate = clk_get_rate(timer->clk); + if (!timer->rate) + timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ; + } else { + timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ; + timer->clk = NULL; + } + + ret = devm_request_irq(dev, timer->irq, brcmstb_waketmr_irq, 0, + "brcmstb-waketimer", timer); + if (ret < 0) + return ret; + + timer->reboot_notifier.notifier_call = brcmstb_waketmr_reboot; + register_reboot_notifier(&timer->reboot_notifier); + + timer->rtc = rtc_device_register("brcmstb-waketmr", dev, + &brcmstb_waketmr_ops, THIS_MODULE); + if (IS_ERR(timer->rtc)) { + dev_err(dev, "unable to register device\n"); + unregister_reboot_notifier(&timer->reboot_notifier); + return PTR_ERR(timer->rtc); + } + + dev_info(dev, "registered, with irq %d\n", timer->irq); + + return ret; +} + +static int brcmstb_waketmr_remove(struct platform_device *pdev) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(&pdev->dev); + + unregister_reboot_notifier(&timer->reboot_notifier); + rtc_device_unregister(timer->rtc); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int brcmstb_waketmr_suspend(struct device *dev) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + + return brcmstb_waketmr_prepare_suspend(timer); +} + +static int brcmstb_waketmr_resume(struct device *dev) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + int ret; + + if (!device_may_wakeup(dev)) + return 0; + + ret = disable_irq_wake(timer->irq); + + brcmstb_waketmr_clear_alarm(timer); + + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(brcmstb_waketmr_pm_ops, + brcmstb_waketmr_suspend, brcmstb_waketmr_resume); + +static const struct of_device_id brcmstb_waketmr_of_match[] = { + { .compatible = "brcm,brcmstb-waketimer" }, + { /* sentinel */ }, +}; + +static struct platform_driver brcmstb_waketmr_driver = { + .probe = brcmstb_waketmr_probe, + .remove = brcmstb_waketmr_remove, + .driver = { + .name = "brcmstb-waketimer", + .pm = &brcmstb_waketmr_pm_ops, + .of_match_table = of_match_ptr(brcmstb_waketmr_of_match), + } +}; +module_platform_driver(brcmstb_waketmr_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Brian Norris"); +MODULE_AUTHOR("Markus Mayer"); +MODULE_DESCRIPTION("Wake-up timer driver for STB chips"); -- cgit v1.1 From 078f3f6452c8be74fd406733f99f2cb0d9c50efc Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 5 Jun 2017 17:57:29 +0200 Subject: rtc: ds1307: use regmap_update_bits where applicable After the switch to regmap we can now make use of regmap_update_bits to simplify read/modify/write ops. Signed-off-by: Heiner Kallweit Reviewed-by: Linus Walleij Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 82 ++++++++++++------------------------------------ 1 file changed, 20 insertions(+), 62 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 3cbb763..9b4106a 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -297,7 +297,7 @@ static irqreturn_t ds1307_irq(int irq, void *dev_id) { struct ds1307 *ds1307 = dev_id; struct mutex *lock = &ds1307->rtc->ops_lock; - int stat, control, ret; + int stat, ret; mutex_lock(lock); ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &stat); @@ -308,13 +308,11 @@ static irqreturn_t ds1307_irq(int irq, void *dev_id) stat &= ~DS1337_BIT_A1I; regmap_write(ds1307->regmap, DS1337_REG_STATUS, stat); - ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control); + ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL, + DS1337_BIT_A1IE, 0); if (ret) goto out; - control &= ~DS1337_BIT_A1IE; - regmap_write(ds1307->regmap, DS1337_REG_CONTROL, control); - rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); } @@ -567,21 +565,13 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct ds1307 *ds1307 = dev_get_drvdata(dev); - int control, ret; if (!test_bit(HAS_ALARM, &ds1307->flags)) return -ENOTTY; - ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control); - if (ret) - return ret; - - if (enabled) - control |= DS1337_BIT_A1IE; - else - control &= ~DS1337_BIT_A1IE; - - return regmap_write(ds1307->regmap, DS1337_REG_CONTROL, control); + return regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL, + DS1337_BIT_A1IE, + enabled ? DS1337_BIT_A1IE : 0); } static const struct rtc_class_ops ds13xx_rtc_ops = { @@ -795,11 +785,8 @@ static irqreturn_t mcp794xx_irq(int irq, void *dev_id) goto out; /* Disable alarm 0. */ - ret = regmap_read(ds1307->regmap, MCP794XX_REG_CONTROL, ®); - if (ret) - goto out; - reg &= ~MCP794XX_BIT_ALM0_EN; - ret = regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, reg); + ret = regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, + MCP794XX_BIT_ALM0_EN, 0); if (ret) goto out; @@ -897,21 +884,13 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct ds1307 *ds1307 = dev_get_drvdata(dev); - int reg, ret; if (!test_bit(HAS_ALARM, &ds1307->flags)) return -EINVAL; - ret = regmap_read(ds1307->regmap, MCP794XX_REG_CONTROL, ®); - if (ret) - return ret; - - if (enabled) - reg |= MCP794XX_BIT_ALM0_EN; - else - reg &= ~MCP794XX_BIT_ALM0_EN; - - return regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, reg); + return regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, + MCP794XX_BIT_ALM0_EN, + enabled ? MCP794XX_BIT_ALM0_EN : 0); } static const struct rtc_class_ops mcp794xx_rtc_ops = { @@ -1114,20 +1093,11 @@ static int ds3231_clk_sqw_rates[] = { static int ds1337_write_control(struct ds1307 *ds1307, u8 mask, u8 value) { struct mutex *lock = &ds1307->rtc->ops_lock; - int control; int ret; mutex_lock(lock); - - ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control); - if (ret) - goto out; - - control &= ~mask; - control |= value; - - ret = regmap_write(ds1307->regmap, DS1337_REG_CONTROL, control); -out: + ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL, + mask, value); mutex_unlock(lock); return ret; @@ -1233,22 +1203,12 @@ static unsigned long ds3231_clk_32khz_recalc_rate(struct clk_hw *hw, static int ds3231_clk_32khz_control(struct ds1307 *ds1307, bool enable) { struct mutex *lock = &ds1307->rtc->ops_lock; - int status; int ret; mutex_lock(lock); - - ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &status); - if (ret) - goto out; - - if (enable) - status |= DS3231_BIT_EN32KHZ; - else - status &= ~DS3231_BIT_EN32KHZ; - - ret = regmap_write(ds1307->regmap, DS1337_REG_STATUS, status); -out: + ret = regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS, + DS3231_BIT_EN32KHZ, + enable ? DS3231_BIT_EN32KHZ : 0); mutex_unlock(lock); return ret; @@ -1712,12 +1672,10 @@ read_rtc: * If different then set the wday which we computed using * timestamp */ - if (wday != tm.tm_wday) { - regmap_read(ds1307->regmap, MCP794XX_REG_WEEKDAY, &wday); - wday = wday & ~MCP794XX_REG_WEEKDAY_WDAY_MASK; - wday = wday | (tm.tm_wday + 1); - regmap_write(ds1307->regmap, MCP794XX_REG_WEEKDAY, wday); - } + if (wday != tm.tm_wday) + regmap_update_bits(ds1307->regmap, MCP794XX_REG_WEEKDAY, + MCP794XX_REG_WEEKDAY_WDAY_MASK, + tm.tm_wday + 1); if (want_irq) { device_set_wakeup_capable(ds1307->dev, true); -- cgit v1.1 From e48585dec2bd0183436437995e30d20dfa92f6aa Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 5 Jun 2017 17:57:33 +0200 Subject: rtc: ds1307: factor out century bit handling The driver has lots of places with chip-specific code what doesn't necessarily facilitate maintenance. Let's describe chip-specific differences in century bit handling in struct chip_desc to improve this. Signed-off-by: Heiner Kallweit Reviewed-by: Linus Walleij Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 73 ++++++++++++++++++------------------------------ 1 file changed, 27 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 9b4106a..9226752 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -136,6 +136,9 @@ struct chip_desc { unsigned alarm:1; u16 nvram_offset; u16 nvram_size; + u8 century_reg; + u8 century_enable_bit; + u8 century_bit; u16 trickle_charger_reg; u8 trickle_charger_setup; u8 (*do_trickle_setup)(struct ds1307 *, uint32_t, @@ -151,6 +154,8 @@ static struct chip_desc chips[last_ds_type] = { }, [ds_1337] = { .alarm = 1, + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, }, [ds_1338] = { .nvram_offset = 8, @@ -158,10 +163,15 @@ static struct chip_desc chips[last_ds_type] = { }, [ds_1339] = { .alarm = 1, + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, .trickle_charger_reg = 0x10, .do_trickle_setup = &do_trickle_setup_ds1339, }, [ds_1340] = { + .century_reg = DS1307_REG_HOUR, + .century_enable_bit = DS1340_BIT_CENTURY_EN, + .century_bit = DS1340_BIT_CENTURY, .trickle_charger_reg = 0x08, }, [ds_1388] = { @@ -169,6 +179,8 @@ static struct chip_desc chips[last_ds_type] = { }, [ds_3231] = { .alarm = 1, + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, }, [rx_8130] = { .alarm = 1, @@ -328,6 +340,7 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); int tmp, ret; + const struct chip_desc *chip = &chips[ds1307->type]; /* read the RTC date and time registers all at once */ ret = regmap_bulk_read(ds1307->regmap, ds1307->offset, ds1307->regs, 7); @@ -355,22 +368,9 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) t->tm_mon = bcd2bin(tmp) - 1; t->tm_year = bcd2bin(ds1307->regs[DS1307_REG_YEAR]) + 100; -#ifdef CONFIG_RTC_DRV_DS1307_CENTURY - switch (ds1307->type) { - case ds_1337: - case ds_1339: - case ds_3231: - if (ds1307->regs[DS1307_REG_MONTH] & DS1337_BIT_CENTURY) - t->tm_year += 100; - break; - case ds_1340: - if (ds1307->regs[DS1307_REG_HOUR] & DS1340_BIT_CENTURY) - t->tm_year += 100; - break; - default: - break; - } -#endif + if (ds1307->regs[chip->century_reg] & chip->century_bit && + IS_ENABLED(CONFIG_RTC_DRV_DS1307_CENTURY)) + t->tm_year += 100; dev_dbg(dev, "%s secs=%d, mins=%d, " "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", @@ -385,6 +385,7 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) static int ds1307_set_time(struct device *dev, struct rtc_time *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); + const struct chip_desc *chip = &chips[ds1307->type]; int result; int tmp; u8 *buf = ds1307->regs; @@ -395,24 +396,14 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) t->tm_hour, t->tm_mday, t->tm_mon, t->tm_year, t->tm_wday); -#ifdef CONFIG_RTC_DRV_DS1307_CENTURY if (t->tm_year < 100) return -EINVAL; - switch (ds1307->type) { - case ds_1337: - case ds_1339: - case ds_3231: - case ds_1340: - if (t->tm_year > 299) - return -EINVAL; - default: - if (t->tm_year > 199) - return -EINVAL; - break; - } +#ifdef CONFIG_RTC_DRV_DS1307_CENTURY + if (t->tm_year > (chip->century_bit ? 299 : 199)) + return -EINVAL; #else - if (t->tm_year < 100 || t->tm_year > 199) + if (t->tm_year > 199) return -EINVAL; #endif @@ -427,19 +418,12 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) tmp = t->tm_year - 100; buf[DS1307_REG_YEAR] = bin2bcd(tmp); - switch (ds1307->type) { - case ds_1337: - case ds_1339: - case ds_3231: - if (t->tm_year > 199) - buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY; - break; - case ds_1340: - buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN; - if (t->tm_year > 199) - buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY; - break; - case mcp794xx: + if (chip->century_enable_bit) + buf[chip->century_reg] |= chip->century_enable_bit; + if (t->tm_year > 199 && chip->century_bit) + buf[chip->century_reg] |= chip->century_bit; + + if (ds1307->type == mcp794xx) { /* * these bits were cleared when preparing the date/time * values and need to be set again before writing the @@ -447,9 +431,6 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) */ buf[DS1307_REG_SECS] |= MCP794XX_BIT_ST; buf[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN; - break; - default: - break; } dev_dbg(dev, "%s: %7ph\n", "write", buf); -- cgit v1.1 From ac05fba39cc5a959e5d9ca24a059d884db0118a5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 30 May 2017 09:53:30 +0200 Subject: rtc: gemini: Add optional clock handling This makes the Gemini optionally take two clock references to the PCLK and EXTCLK. As we are adding a clock framework to the Gemini platform we need to make sure that we get the right references. Acked-by: Hans Ulli Kroll Signed-off-by: Linus Walleij Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-gemini.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'drivers') diff --git a/drivers/rtc/rtc-gemini.c b/drivers/rtc/rtc-gemini.c index 5279390..cf766e0 100644 --- a/drivers/rtc/rtc-gemini.c +++ b/drivers/rtc/rtc-gemini.c @@ -26,6 +26,7 @@ #include #include #include +#include #define DRV_NAME "rtc-gemini" @@ -38,6 +39,8 @@ struct gemini_rtc { struct rtc_device *rtc_dev; void __iomem *rtc_base; int rtc_irq; + struct clk *pclk; + struct clk *extclk; }; enum gemini_rtc_offsets { @@ -127,6 +130,27 @@ static int gemini_rtc_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, rtc); + rtc->pclk = devm_clk_get(dev, "PCLK"); + if (IS_ERR(rtc->pclk)) { + dev_err(dev, "could not get PCLK\n"); + } else { + ret = clk_prepare_enable(rtc->pclk); + if (ret) { + dev_err(dev, "failed to enable PCLK\n"); + return ret; + } + } + rtc->extclk = devm_clk_get(dev, "EXTCLK"); + if (IS_ERR(rtc->extclk)) { + dev_err(dev, "could not get EXTCLK\n"); + } else { + ret = clk_prepare_enable(rtc->extclk); + if (ret) { + dev_err(dev, "failed to enable EXTCLK\n"); + return ret; + } + } + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) return -ENODEV; @@ -156,6 +180,10 @@ static int gemini_rtc_remove(struct platform_device *pdev) { struct gemini_rtc *rtc = platform_get_drvdata(pdev); + if (!IS_ERR(rtc->extclk)) + clk_disable_unprepare(rtc->extclk); + if (!IS_ERR(rtc->pclk)) + clk_disable_unprepare(rtc->pclk); rtc_device_unregister(rtc->rtc_dev); return 0; -- cgit v1.1 From 1d61d2592c1f70d74112ed1ee8f182694dc43c48 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 30 May 2017 09:53:32 +0200 Subject: rtc: gemini/ftrtc010: rename driver and symbols The Gemini RTC is actually a generic IP block from Faraday Technology names FTRTC010. Rename the driver file and all symbols to match this IP name. The relationship can be clearly seen in the U-Boot driver posted by Po-Yu Chuang for the Faraday A320 board: https://lists.denx.de/pipermail/u-boot/2009-September/061326.html Remove the dependency on ARCH_GEMINI but select the driver for ARCH_GEMINI so we get a smooth transition. The IP block is synthsized on different silicon and architectures. Cc: Po-Yu Chuang Acked-by: Hans Ulli Kroll Signed-off-by: Linus Walleij Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 10 +-- drivers/rtc/Makefile | 2 +- drivers/rtc/rtc-ftrtc010.c | 208 +++++++++++++++++++++++++++++++++++++++++++++ drivers/rtc/rtc-gemini.c | 207 -------------------------------------------- 4 files changed, 214 insertions(+), 213 deletions(-) create mode 100644 drivers/rtc/rtc-ftrtc010.c delete mode 100644 drivers/rtc/rtc-gemini.c (limited to 'drivers') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 8e1aa67..0e5044c 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1495,16 +1495,16 @@ config RTC_DRV_ARMADA38X This driver can also be built as a module. If so, the module will be called armada38x-rtc. -config RTC_DRV_GEMINI - tristate "Gemini SoC RTC" - depends on ARCH_GEMINI || COMPILE_TEST +config RTC_DRV_FTRTC010 + tristate "Faraday Technology FTRTC010 RTC" depends on HAS_IOMEM + default ARCH_GEMINI help If you say Y here you will get support for the - RTC found on Gemini SoC's. + Faraday Technolog FTRTC010 found on e.g. Gemini SoC's. This driver can also be built as a module. If so, the module - will be called rtc-gemini. + will be called rtc-ftrtc010. config RTC_DRV_PS3 tristate "PS3 RTC" diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index df89cac..4050fc8 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -68,7 +68,7 @@ obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o -obj-$(CONFIG_RTC_DRV_GEMINI) += rtc-gemini.o +obj-$(CONFIG_RTC_DRV_FTRTC010) += rtc-ftrtc010.o obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o diff --git a/drivers/rtc/rtc-ftrtc010.c b/drivers/rtc/rtc-ftrtc010.c new file mode 100644 index 0000000..af8d6be --- /dev/null +++ b/drivers/rtc/rtc-ftrtc010.c @@ -0,0 +1,208 @@ +/* + * Faraday Technology FTRTC010 driver + * + * Copyright (C) 2009 Janos Laube + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Original code for older kernel 2.6.15 are from Stormlinksemi + * first update from Janos Laube for > 2.6.29 kernels + * + * checkpatch fixes and usage of rtc-lib code + * Hans Ulli Kroll + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "rtc-ftrtc010" + +MODULE_AUTHOR("Hans Ulli Kroll "); +MODULE_DESCRIPTION("RTC driver for Gemini SoC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); + +struct ftrtc010_rtc { + struct rtc_device *rtc_dev; + void __iomem *rtc_base; + int rtc_irq; + struct clk *pclk; + struct clk *extclk; +}; + +enum ftrtc010_rtc_offsets { + FTRTC010_RTC_SECOND = 0x00, + FTRTC010_RTC_MINUTE = 0x04, + FTRTC010_RTC_HOUR = 0x08, + FTRTC010_RTC_DAYS = 0x0C, + FTRTC010_RTC_ALARM_SECOND = 0x10, + FTRTC010_RTC_ALARM_MINUTE = 0x14, + FTRTC010_RTC_ALARM_HOUR = 0x18, + FTRTC010_RTC_RECORD = 0x1C, + FTRTC010_RTC_CR = 0x20, +}; + +static irqreturn_t ftrtc010_rtc_interrupt(int irq, void *dev) +{ + return IRQ_HANDLED; +} + +/* + * Looks like the RTC in the Gemini SoC is (totaly) broken + * We can't read/write directly the time from RTC registers. + * We must do some "offset" calculation to get the real time + * + * This FIX works pretty fine and Stormlinksemi aka Cortina-Networks does + * the same thing, without the rtc-lib.c calls. + */ + +static int ftrtc010_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ftrtc010_rtc *rtc = dev_get_drvdata(dev); + + unsigned int days, hour, min, sec; + unsigned long offset, time; + + sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND); + min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE); + hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR); + days = readl(rtc->rtc_base + FTRTC010_RTC_DAYS); + offset = readl(rtc->rtc_base + FTRTC010_RTC_RECORD); + + time = offset + days * 86400 + hour * 3600 + min * 60 + sec; + + rtc_time_to_tm(time, tm); + + return 0; +} + +static int ftrtc010_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ftrtc010_rtc *rtc = dev_get_drvdata(dev); + unsigned int sec, min, hour, day; + unsigned long offset, time; + + if (tm->tm_year >= 2148) /* EPOCH Year + 179 */ + return -EINVAL; + + rtc_tm_to_time(tm, &time); + + sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND); + min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE); + hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR); + day = readl(rtc->rtc_base + FTRTC010_RTC_DAYS); + + offset = time - (day * 86400 + hour * 3600 + min * 60 + sec); + + writel(offset, rtc->rtc_base + FTRTC010_RTC_RECORD); + writel(0x01, rtc->rtc_base + FTRTC010_RTC_CR); + + return 0; +} + +static const struct rtc_class_ops ftrtc010_rtc_ops = { + .read_time = ftrtc010_rtc_read_time, + .set_time = ftrtc010_rtc_set_time, +}; + +static int ftrtc010_rtc_probe(struct platform_device *pdev) +{ + struct ftrtc010_rtc *rtc; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (unlikely(!rtc)) + return -ENOMEM; + platform_set_drvdata(pdev, rtc); + + rtc->pclk = devm_clk_get(dev, "PCLK"); + if (IS_ERR(rtc->pclk)) { + dev_err(dev, "could not get PCLK\n"); + } else { + ret = clk_prepare_enable(rtc->pclk); + if (ret) { + dev_err(dev, "failed to enable PCLK\n"); + return ret; + } + } + rtc->extclk = devm_clk_get(dev, "EXTCLK"); + if (IS_ERR(rtc->extclk)) { + dev_err(dev, "could not get EXTCLK\n"); + } else { + ret = clk_prepare_enable(rtc->extclk); + if (ret) { + dev_err(dev, "failed to enable EXTCLK\n"); + return ret; + } + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) + return -ENODEV; + + rtc->rtc_irq = res->start; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + rtc->rtc_base = devm_ioremap(dev, res->start, + resource_size(res)); + if (!rtc->rtc_base) + return -ENOMEM; + + ret = devm_request_irq(dev, rtc->rtc_irq, ftrtc010_rtc_interrupt, + IRQF_SHARED, pdev->name, dev); + if (unlikely(ret)) + return ret; + + rtc->rtc_dev = rtc_device_register(pdev->name, dev, + &ftrtc010_rtc_ops, THIS_MODULE); + return PTR_ERR_OR_ZERO(rtc->rtc_dev); +} + +static int ftrtc010_rtc_remove(struct platform_device *pdev) +{ + struct ftrtc010_rtc *rtc = platform_get_drvdata(pdev); + + if (!IS_ERR(rtc->extclk)) + clk_disable_unprepare(rtc->extclk); + if (!IS_ERR(rtc->pclk)) + clk_disable_unprepare(rtc->pclk); + rtc_device_unregister(rtc->rtc_dev); + + return 0; +} + +static const struct of_device_id ftrtc010_rtc_dt_match[] = { + { .compatible = "cortina,gemini-rtc" }, + { .compatible = "faraday,ftrtc010" }, + { } +}; +MODULE_DEVICE_TABLE(of, ftrtc010_rtc_dt_match); + +static struct platform_driver ftrtc010_rtc_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = ftrtc010_rtc_dt_match, + }, + .probe = ftrtc010_rtc_probe, + .remove = ftrtc010_rtc_remove, +}; + +module_platform_driver_probe(ftrtc010_rtc_driver, ftrtc010_rtc_probe); diff --git a/drivers/rtc/rtc-gemini.c b/drivers/rtc/rtc-gemini.c deleted file mode 100644 index cf766e0..0000000 --- a/drivers/rtc/rtc-gemini.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Gemini OnChip RTC - * - * Copyright (C) 2009 Janos Laube - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Original code for older kernel 2.6.15 are from Stormlinksemi - * first update from Janos Laube for > 2.6.29 kernels - * - * checkpatch fixes and usage of rtc-lib code - * Hans Ulli Kroll - */ - -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "rtc-gemini" - -MODULE_AUTHOR("Hans Ulli Kroll "); -MODULE_DESCRIPTION("RTC driver for Gemini SoC"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); - -struct gemini_rtc { - struct rtc_device *rtc_dev; - void __iomem *rtc_base; - int rtc_irq; - struct clk *pclk; - struct clk *extclk; -}; - -enum gemini_rtc_offsets { - GEMINI_RTC_SECOND = 0x00, - GEMINI_RTC_MINUTE = 0x04, - GEMINI_RTC_HOUR = 0x08, - GEMINI_RTC_DAYS = 0x0C, - GEMINI_RTC_ALARM_SECOND = 0x10, - GEMINI_RTC_ALARM_MINUTE = 0x14, - GEMINI_RTC_ALARM_HOUR = 0x18, - GEMINI_RTC_RECORD = 0x1C, - GEMINI_RTC_CR = 0x20 -}; - -static irqreturn_t gemini_rtc_interrupt(int irq, void *dev) -{ - return IRQ_HANDLED; -} - -/* - * Looks like the RTC in the Gemini SoC is (totaly) broken - * We can't read/write directly the time from RTC registers. - * We must do some "offset" calculation to get the real time - * - * This FIX works pretty fine and Stormlinksemi aka Cortina-Networks does - * the same thing, without the rtc-lib.c calls. - */ - -static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm) -{ - struct gemini_rtc *rtc = dev_get_drvdata(dev); - - unsigned int days, hour, min, sec; - unsigned long offset, time; - - sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND); - min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE); - hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR); - days = readl(rtc->rtc_base + GEMINI_RTC_DAYS); - offset = readl(rtc->rtc_base + GEMINI_RTC_RECORD); - - time = offset + days * 86400 + hour * 3600 + min * 60 + sec; - - rtc_time_to_tm(time, tm); - - return 0; -} - -static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm) -{ - struct gemini_rtc *rtc = dev_get_drvdata(dev); - unsigned int sec, min, hour, day; - unsigned long offset, time; - - if (tm->tm_year >= 2148) /* EPOCH Year + 179 */ - return -EINVAL; - - rtc_tm_to_time(tm, &time); - - sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND); - min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE); - hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR); - day = readl(rtc->rtc_base + GEMINI_RTC_DAYS); - - offset = time - (day * 86400 + hour * 3600 + min * 60 + sec); - - writel(offset, rtc->rtc_base + GEMINI_RTC_RECORD); - writel(0x01, rtc->rtc_base + GEMINI_RTC_CR); - - return 0; -} - -static const struct rtc_class_ops gemini_rtc_ops = { - .read_time = gemini_rtc_read_time, - .set_time = gemini_rtc_set_time, -}; - -static int gemini_rtc_probe(struct platform_device *pdev) -{ - struct gemini_rtc *rtc; - struct device *dev = &pdev->dev; - struct resource *res; - int ret; - - rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); - if (unlikely(!rtc)) - return -ENOMEM; - platform_set_drvdata(pdev, rtc); - - rtc->pclk = devm_clk_get(dev, "PCLK"); - if (IS_ERR(rtc->pclk)) { - dev_err(dev, "could not get PCLK\n"); - } else { - ret = clk_prepare_enable(rtc->pclk); - if (ret) { - dev_err(dev, "failed to enable PCLK\n"); - return ret; - } - } - rtc->extclk = devm_clk_get(dev, "EXTCLK"); - if (IS_ERR(rtc->extclk)) { - dev_err(dev, "could not get EXTCLK\n"); - } else { - ret = clk_prepare_enable(rtc->extclk); - if (ret) { - dev_err(dev, "failed to enable EXTCLK\n"); - return ret; - } - } - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) - return -ENODEV; - - rtc->rtc_irq = res->start; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - rtc->rtc_base = devm_ioremap(dev, res->start, - resource_size(res)); - if (!rtc->rtc_base) - return -ENOMEM; - - ret = devm_request_irq(dev, rtc->rtc_irq, gemini_rtc_interrupt, - IRQF_SHARED, pdev->name, dev); - if (unlikely(ret)) - return ret; - - rtc->rtc_dev = rtc_device_register(pdev->name, dev, - &gemini_rtc_ops, THIS_MODULE); - return PTR_ERR_OR_ZERO(rtc->rtc_dev); -} - -static int gemini_rtc_remove(struct platform_device *pdev) -{ - struct gemini_rtc *rtc = platform_get_drvdata(pdev); - - if (!IS_ERR(rtc->extclk)) - clk_disable_unprepare(rtc->extclk); - if (!IS_ERR(rtc->pclk)) - clk_disable_unprepare(rtc->pclk); - rtc_device_unregister(rtc->rtc_dev); - - return 0; -} - -static const struct of_device_id gemini_rtc_dt_match[] = { - { .compatible = "cortina,gemini-rtc" }, - { } -}; -MODULE_DEVICE_TABLE(of, gemini_rtc_dt_match); - -static struct platform_driver gemini_rtc_driver = { - .driver = { - .name = DRV_NAME, - .of_match_table = gemini_rtc_dt_match, - }, - .probe = gemini_rtc_probe, - .remove = gemini_rtc_remove, -}; - -module_platform_driver_probe(gemini_rtc_driver, gemini_rtc_probe); -- cgit v1.1 From d0a67c372df410b579197ea818596001fe20070d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Jun 2017 11:29:00 +0300 Subject: rtc: rtc-nuc900: fix loop timeout test We should change this post-op to a pre-op because we want the loop to exit with "timeout" set to zero. Fixes: 0a89b55364e0 ("nuc900/rtc: change the waiting for device ready implement") Signed-off-by: Dan Carpenter Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-nuc900.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c index b1b6b30..4ed8111 100644 --- a/drivers/rtc/rtc-nuc900.c +++ b/drivers/rtc/rtc-nuc900.c @@ -93,7 +93,7 @@ static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc) __raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER); while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB) - && timeout--) + && --timeout) mdelay(1); if (!timeout) -- cgit v1.1 From c35c4195f919a077ce106ec84cfcecbb0b67db30 Mon Sep 17 00:00:00 2001 From: Kirill Esipov Date: Wed, 28 Jun 2017 14:29:09 +0300 Subject: rtc: ds3232: add temperature support DS3232/DS3234 has the temperature registers with a resolution of 0.25 degree celsius. This enables to get the value through hwmon. # cat /sys/class/hwmon/hwmon0/temp1_input 37250 Signed-off-by: Kirill Esipov Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 8 ++++ drivers/rtc/rtc-ds3232.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) (limited to 'drivers') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 0e5044c..d0e4b4a 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -802,6 +802,14 @@ config RTC_DRV_DS3232 This driver can also be built as a module. If so, the module will be called rtc-ds3232. +config RTC_DRV_DS3232_HWMON + bool "HWMON support for Dallas/Maxim DS3232/DS3234" + depends on RTC_DRV_DS3232 && HWMON && !(RTC_DRV_DS3232=y && HWMON=m) + default y + help + Say Y here if you want to expose temperature sensor data on + rtc-ds3232 + config RTC_DRV_PCF2127 tristate "NXP PCF2127" depends on RTC_I2C_AND_SPI diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index deff431..0550f7b 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -22,6 +22,7 @@ #include #include #include +#include #define DS3232_REG_SECONDS 0x00 #define DS3232_REG_MINUTES 0x01 @@ -46,6 +47,8 @@ # define DS3232_REG_SR_A2F 0x02 # define DS3232_REG_SR_A1F 0x01 +#define DS3232_REG_TEMPERATURE 0x11 + struct ds3232 { struct device *dev; struct regmap *regmap; @@ -275,6 +278,120 @@ static int ds3232_update_alarm(struct device *dev, unsigned int enabled) return ret; } +/* + * Temperature sensor support for ds3232/ds3234 devices. + * A user-initiated temperature conversion is not started by this function, + * so the temperature is updated once every 64 seconds. + */ +static int ds3232_hwmon_read_temp(struct device *dev, long int *mC) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + u8 temp_buf[2]; + s16 temp; + int ret; + + ret = regmap_bulk_read(ds3232->regmap, DS3232_REG_TEMPERATURE, temp_buf, + sizeof(temp_buf)); + if (ret < 0) + return ret; + + /* + * Temperature is represented as a 10-bit code with a resolution of + * 0.25 degree celsius and encoded in two's complement format. + */ + temp = (temp_buf[0] << 8) | temp_buf[1]; + temp >>= 6; + *mC = temp * 250; + + return 0; +} + +static umode_t ds3232_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + return 0; + } +} + +static int ds3232_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + int err; + + switch (attr) { + case hwmon_temp_input: + err = ds3232_hwmon_read_temp(dev, temp); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static u32 ds3232_hwmon_chip_config[] = { + HWMON_C_REGISTER_TZ, + 0 +}; + +static const struct hwmon_channel_info ds3232_hwmon_chip = { + .type = hwmon_chip, + .config = ds3232_hwmon_chip_config, +}; + +static u32 ds3232_hwmon_temp_config[] = { + HWMON_T_INPUT, + 0 +}; + +static const struct hwmon_channel_info ds3232_hwmon_temp = { + .type = hwmon_temp, + .config = ds3232_hwmon_temp_config, +}; + +static const struct hwmon_channel_info *ds3232_hwmon_info[] = { + &ds3232_hwmon_chip, + &ds3232_hwmon_temp, + NULL +}; + +static const struct hwmon_ops ds3232_hwmon_hwmon_ops = { + .is_visible = ds3232_hwmon_is_visible, + .read = ds3232_hwmon_read, +}; + +static const struct hwmon_chip_info ds3232_hwmon_chip_info = { + .ops = &ds3232_hwmon_hwmon_ops, + .info = ds3232_hwmon_info, +}; + +static void ds3232_hwmon_register(struct device *dev, const char *name) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + struct device *hwmon_dev; + + if (!IS_ENABLED(CONFIG_RTC_DRV_DS3232_HWMON)) + return; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, name, ds3232, + &ds3232_hwmon_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) { + dev_err(dev, "unable to register hwmon device %ld\n", + PTR_ERR(hwmon_dev)); + } +} + static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct ds3232 *ds3232 = dev_get_drvdata(dev); @@ -366,6 +483,8 @@ static int ds3232_probe(struct device *dev, struct regmap *regmap, int irq, if (ds3232->irq > 0) device_init_wakeup(dev, 1); + ds3232_hwmon_register(dev, name); + ds3232->rtc = devm_rtc_device_register(dev, name, &ds3232_rtc_ops, THIS_MODULE); if (IS_ERR(ds3232->rtc)) -- cgit v1.1 From 300a7735becf55f7fd18f8cd3dc3b945a0cab712 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Thu, 8 Jun 2017 12:36:54 +0200 Subject: rtc: ds1307: add ds1308 variant The ds1308 variant is very similar to the already supported ds1338 variant, it have more debug registers and a square wave clock output. Signed-off-by: Sean Nyekjaer Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 9226752..57ea254 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -34,6 +34,7 @@ */ enum ds_type { ds_1307, + ds_1308, ds_1337, ds_1338, ds_1339, @@ -152,6 +153,10 @@ static struct chip_desc chips[last_ds_type] = { .nvram_offset = 8, .nvram_size = 56, }, + [ds_1308] = { + .nvram_offset = 8, + .nvram_size = 56, + }, [ds_1337] = { .alarm = 1, .century_reg = DS1307_REG_MONTH, @@ -198,6 +203,7 @@ static struct chip_desc chips[last_ds_type] = { static const struct i2c_device_id ds1307_id[] = { { "ds1307", ds_1307 }, + { "ds1308", ds_1308 }, { "ds1337", ds_1337 }, { "ds1338", ds_1338 }, { "ds1339", ds_1339 }, @@ -223,6 +229,10 @@ static const struct of_device_id ds1307_of_match[] = { .data = (void *)ds_1307 }, { + .compatible = "dallas,ds1308", + .data = (void *)ds_1308 + }, + { .compatible = "dallas,ds1337", .data = (void *)ds_1337 }, @@ -282,6 +292,7 @@ MODULE_DEVICE_TABLE(of, ds1307_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id ds1307_acpi_ids[] = { { .id = "DS1307", .driver_data = ds_1307 }, + { .id = "DS1308", .driver_data = ds_1308 }, { .id = "DS1337", .driver_data = ds_1337 }, { .id = "DS1338", .driver_data = ds_1338 }, { .id = "DS1339", .driver_data = ds_1339 }, @@ -1558,6 +1569,7 @@ read_rtc: goto read_rtc; } break; + case ds_1308: case ds_1338: /* clock halted? turn it on, so clock can tick. */ if (tmp & DS1307_BIT_CH) -- cgit v1.1 From 9a6757eadc14f01385fd41fe3906dc22dcdb919e Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Thu, 6 Jul 2017 10:47:45 +0200 Subject: rtc: stm32: add STM32H7 RTC support This patch adds support for STM32H7 RTC. On STM32H7, the RTC bus interface clock (APB clock) needs to be enabled. Signed-off-by: Amelie Delaunay Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-stm32.c | 82 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c index bd57eb1..3a5c3d7 100644 --- a/drivers/rtc/rtc-stm32.c +++ b/drivers/rtc/rtc-stm32.c @@ -94,11 +94,17 @@ /* STM32_PWR_CR bit field */ #define PWR_CR_DBP BIT(8) +struct stm32_rtc_data { + bool has_pclk; +}; + struct stm32_rtc { struct rtc_device *rtc_dev; void __iomem *base; struct regmap *dbp; - struct clk *ck_rtc; + struct stm32_rtc_data *data; + struct clk *pclk; + struct clk *rtc_ck; int irq_alarm; }; @@ -122,9 +128,9 @@ static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc) writel_relaxed(isr, rtc->base + STM32_RTC_ISR); /* - * It takes around 2 ck_rtc clock cycles to enter in + * It takes around 2 rtc_ck clock cycles to enter in * initialization phase mode (and have INITF flag set). As - * slowest ck_rtc frequency may be 32kHz and highest should be + * slowest rtc_ck frequency may be 32kHz and highest should be * 1MHz, we poll every 10 us with a timeout of 100ms. */ return readl_relaxed_poll_timeout_atomic( @@ -153,7 +159,7 @@ static int stm32_rtc_wait_sync(struct stm32_rtc *rtc) /* * Wait for RSF to be set to ensure the calendar registers are - * synchronised, it takes around 2 ck_rtc clock cycles + * synchronised, it takes around 2 rtc_ck clock cycles */ return readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR, isr, @@ -456,7 +462,7 @@ static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) /* * Poll Alarm write flag to be sure that Alarm update is allowed: it - * takes around 2 ck_rtc clock cycles + * takes around 2 rtc_ck clock cycles */ ret = readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR, isr, @@ -490,8 +496,17 @@ static const struct rtc_class_ops stm32_rtc_ops = { .alarm_irq_enable = stm32_rtc_alarm_irq_enable, }; +static const struct stm32_rtc_data stm32_rtc_data = { + .has_pclk = false, +}; + +static const struct stm32_rtc_data stm32h7_rtc_data = { + .has_pclk = true, +}; + static const struct of_device_id stm32_rtc_of_match[] = { - { .compatible = "st,stm32-rtc" }, + { .compatible = "st,stm32-rtc", .data = &stm32_rtc_data }, + { .compatible = "st,stm32h7-rtc", .data = &stm32h7_rtc_data }, {} }; MODULE_DEVICE_TABLE(of, stm32_rtc_of_match); @@ -503,7 +518,7 @@ static int stm32_rtc_init(struct platform_device *pdev, unsigned int rate; int ret = 0; - rate = clk_get_rate(rtc->ck_rtc); + rate = clk_get_rate(rtc->rtc_ck); /* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */ pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT; @@ -524,7 +539,7 @@ static int stm32_rtc_init(struct platform_device *pdev, pred_a = pred_a_max; pred_s = (rate / (pred_a + 1)) - 1; - dev_warn(&pdev->dev, "ck_rtc is %s\n", + dev_warn(&pdev->dev, "rtc_ck is %s\n", (rate < ((pred_a + 1) * (pred_s + 1))) ? "fast" : "slow"); } @@ -561,6 +576,7 @@ static int stm32_rtc_probe(struct platform_device *pdev) { struct stm32_rtc *rtc; struct resource *res; + const struct of_device_id *match; int ret; rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); @@ -579,15 +595,34 @@ static int stm32_rtc_probe(struct platform_device *pdev) return PTR_ERR(rtc->dbp); } - rtc->ck_rtc = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(rtc->ck_rtc)) { - dev_err(&pdev->dev, "no ck_rtc clock"); - return PTR_ERR(rtc->ck_rtc); + match = of_match_device(stm32_rtc_of_match, &pdev->dev); + rtc->data = (struct stm32_rtc_data *)match->data; + + if (!rtc->data->has_pclk) { + rtc->pclk = NULL; + rtc->rtc_ck = devm_clk_get(&pdev->dev, NULL); + } else { + rtc->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(rtc->pclk)) { + dev_err(&pdev->dev, "no pclk clock"); + return PTR_ERR(rtc->pclk); + } + rtc->rtc_ck = devm_clk_get(&pdev->dev, "rtc_ck"); + } + if (IS_ERR(rtc->rtc_ck)) { + dev_err(&pdev->dev, "no rtc_ck clock"); + return PTR_ERR(rtc->rtc_ck); + } + + if (rtc->data->has_pclk) { + ret = clk_prepare_enable(rtc->pclk); + if (ret) + return ret; } - ret = clk_prepare_enable(rtc->ck_rtc); + ret = clk_prepare_enable(rtc->rtc_ck); if (ret) - return ret; + goto err; regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP); @@ -595,7 +630,7 @@ static int stm32_rtc_probe(struct platform_device *pdev) * After a system reset, RTC_ISR.INITS flag can be read to check if * the calendar has been initalized or not. INITS flag is reset by a * power-on reset (no vbat, no power-supply). It is not reset if - * ck_rtc parent clock has changed (so RTC prescalers need to be + * rtc_ck parent clock has changed (so RTC prescalers need to be * changed). That's why we cannot rely on this flag to know if RTC * init has to be done. */ @@ -646,7 +681,9 @@ static int stm32_rtc_probe(struct platform_device *pdev) return 0; err: - clk_disable_unprepare(rtc->ck_rtc); + if (rtc->data->has_pclk) + clk_disable_unprepare(rtc->pclk); + clk_disable_unprepare(rtc->rtc_ck); regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0); @@ -667,7 +704,9 @@ static int stm32_rtc_remove(struct platform_device *pdev) writel_relaxed(cr, rtc->base + STM32_RTC_CR); stm32_rtc_wpr_lock(rtc); - clk_disable_unprepare(rtc->ck_rtc); + clk_disable_unprepare(rtc->rtc_ck); + if (rtc->data->has_pclk) + clk_disable_unprepare(rtc->pclk); /* Enable backup domain write protection */ regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0); @@ -682,6 +721,9 @@ static int stm32_rtc_suspend(struct device *dev) { struct stm32_rtc *rtc = dev_get_drvdata(dev); + if (rtc->data->has_pclk) + clk_disable_unprepare(rtc->pclk); + if (device_may_wakeup(dev)) return enable_irq_wake(rtc->irq_alarm); @@ -693,6 +735,12 @@ static int stm32_rtc_resume(struct device *dev) struct stm32_rtc *rtc = dev_get_drvdata(dev); int ret = 0; + if (rtc->data->has_pclk) { + ret = clk_prepare_enable(rtc->pclk); + if (ret) + return ret; + } + ret = stm32_rtc_wait_sync(rtc); if (ret < 0) return ret; -- cgit v1.1 From d1bec20facd6eae17cb2f39ebbf443c95c650490 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 6 Jul 2017 11:41:58 +0200 Subject: rtc: class separate device allocation from registration Create rtc_allocate_device to allocate memory for a struct rtc_device and initialize it. Signed-off-by: Alexandre Belloni --- drivers/rtc/class.c | 63 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 543c64c..fb39c13 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -150,6 +150,41 @@ static SIMPLE_DEV_PM_OPS(rtc_class_dev_pm_ops, rtc_suspend, rtc_resume); #define RTC_CLASS_DEV_PM_OPS NULL #endif +static struct rtc_device *rtc_allocate_device(void) +{ + struct rtc_device *rtc; + + rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return NULL; + + device_initialize(&rtc->dev); + + rtc->irq_freq = 1; + rtc->max_user_freq = 64; + rtc->dev.class = rtc_class; + rtc->dev.groups = rtc_get_dev_attribute_groups(); + rtc->dev.release = rtc_device_release; + + mutex_init(&rtc->ops_lock); + spin_lock_init(&rtc->irq_lock); + spin_lock_init(&rtc->irq_task_lock); + init_waitqueue_head(&rtc->irq_queue); + + /* Init timerqueue */ + timerqueue_init_head(&rtc->timerqueue); + INIT_WORK(&rtc->irqwork, rtc_timer_do_work); + /* Init aie timer */ + rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc); + /* Init uie timer */ + rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc); + /* Init pie timer */ + hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + rtc->pie_timer.function = rtc_pie_update_irq; + rtc->pie_enabled = 0; + + return rtc; +} /** * rtc_device_register - register w/ RTC class @@ -189,40 +224,16 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, } } - rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL); - if (rtc == NULL) { + rtc = rtc_allocate_device(); + if (!rtc) { err = -ENOMEM; goto exit_ida; } - device_initialize(&rtc->dev); - rtc->id = id; rtc->ops = ops; rtc->owner = owner; - rtc->irq_freq = 1; - rtc->max_user_freq = 64; rtc->dev.parent = dev; - rtc->dev.class = rtc_class; - rtc->dev.groups = rtc_get_dev_attribute_groups(); - rtc->dev.release = rtc_device_release; - - mutex_init(&rtc->ops_lock); - spin_lock_init(&rtc->irq_lock); - spin_lock_init(&rtc->irq_task_lock); - init_waitqueue_head(&rtc->irq_queue); - - /* Init timerqueue */ - timerqueue_init_head(&rtc->timerqueue); - INIT_WORK(&rtc->irqwork, rtc_timer_do_work); - /* Init aie timer */ - rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc); - /* Init uie timer */ - rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc); - /* Init pie timer */ - hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - rtc->pie_timer.function = rtc_pie_update_irq; - rtc->pie_enabled = 0; dev_set_name(&rtc->dev, "rtc%d", id); -- cgit v1.1 From b91336df8ac2f5d15a2132074ba596580526db1d Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 6 Jul 2017 11:41:59 +0200 Subject: rtc: class separate id allocation from registration Create rtc_device_get_id to allocate the id for an RTC. Signed-off-by: Alexandre Belloni --- drivers/rtc/class.c | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index fb39c13..93ce88e 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -186,6 +186,27 @@ static struct rtc_device *rtc_allocate_device(void) return rtc; } +static int rtc_device_get_id(struct device *dev) +{ + int of_id = -1, id = -1; + + if (dev->of_node) + of_id = of_alias_get_id(dev->of_node, "rtc"); + else if (dev->parent && dev->parent->of_node) + of_id = of_alias_get_id(dev->parent->of_node, "rtc"); + + if (of_id >= 0) { + id = ida_simple_get(&rtc_ida, of_id, of_id + 1, GFP_KERNEL); + if (id < 0) + dev_warn(dev, "/aliases ID %d not available\n", of_id); + } + + if (id < 0) + id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL); + + return id; +} + /** * rtc_device_register - register w/ RTC class * @dev: the device to register @@ -201,27 +222,12 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, { struct rtc_device *rtc; struct rtc_wkalrm alrm; - int of_id = -1, id = -1, err; - - if (dev->of_node) - of_id = of_alias_get_id(dev->of_node, "rtc"); - else if (dev->parent && dev->parent->of_node) - of_id = of_alias_get_id(dev->parent->of_node, "rtc"); - - if (of_id >= 0) { - id = ida_simple_get(&rtc_ida, of_id, of_id + 1, - GFP_KERNEL); - if (id < 0) - dev_warn(dev, "/aliases ID %d not available\n", - of_id); - } + int id, err; + id = rtc_device_get_id(dev); if (id < 0) { - id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL); - if (id < 0) { - err = id; - goto exit; - } + err = id; + goto exit; } rtc = rtc_allocate_device(); -- cgit v1.1 From 3068a254d5519cd5116f61297462da6d1aa84c20 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 6 Jul 2017 11:42:00 +0200 Subject: rtc: introduce new registration method Introduce rtc_register_device() to register an already allocated and initialized struct rtc_device. It automatically sets up the owner and the two steps allocation/registration will allow to remove race conditions in the IRQ handling of some driver. It also allows to properly extend the core without adding more arguments to rtc_device_register(). Signed-off-by: Alexandre Belloni --- drivers/rtc/class.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) (limited to 'drivers') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 93ce88e..58e2a05 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -150,6 +150,7 @@ static SIMPLE_DEV_PM_OPS(rtc_class_dev_pm_ops, rtc_suspend, rtc_resume); #define RTC_CLASS_DEV_PM_OPS NULL #endif +/* Ensure the caller will set the id before releasing the device */ static struct rtc_device *rtc_allocate_device(void) { struct rtc_device *rtc; @@ -372,6 +373,89 @@ void devm_rtc_device_unregister(struct device *dev, struct rtc_device *rtc) } EXPORT_SYMBOL_GPL(devm_rtc_device_unregister); +static void devm_rtc_release_device(struct device *dev, void *res) +{ + struct rtc_device *rtc = *(struct rtc_device **)res; + + if (rtc->registered) + rtc_device_unregister(rtc); + else + put_device(&rtc->dev); +} + +struct rtc_device *devm_rtc_allocate_device(struct device *dev) +{ + struct rtc_device **ptr, *rtc; + int id, err; + + id = rtc_device_get_id(dev); + if (id < 0) + return ERR_PTR(id); + + ptr = devres_alloc(devm_rtc_release_device, sizeof(*ptr), GFP_KERNEL); + if (!ptr) { + err = -ENOMEM; + goto exit_ida; + } + + rtc = rtc_allocate_device(); + if (!rtc) { + err = -ENOMEM; + goto exit_devres; + } + + *ptr = rtc; + devres_add(dev, ptr); + + rtc->id = id; + rtc->dev.parent = dev; + dev_set_name(&rtc->dev, "rtc%d", id); + + return rtc; + +exit_devres: + devres_free(ptr); +exit_ida: + ida_simple_remove(&rtc_ida, id); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(devm_rtc_allocate_device); + +int __rtc_register_device(struct module *owner, struct rtc_device *rtc) +{ + struct rtc_wkalrm alrm; + int err; + + if (!rtc->ops) + return -EINVAL; + + rtc->owner = owner; + + /* Check to see if there is an ALARM already set in hw */ + err = __rtc_read_alarm(rtc, &alrm); + if (!err && !rtc_valid_tm(&alrm.time)) + rtc_initialize_alarm(rtc, &alrm); + + rtc_dev_prepare(rtc); + + err = cdev_device_add(&rtc->char_dev, &rtc->dev); + if (err) + dev_warn(rtc->dev.parent, "failed to add char device %d:%d\n", + MAJOR(rtc->dev.devt), rtc->id); + else + dev_dbg(rtc->dev.parent, "char device (%d:%d)\n", + MAJOR(rtc->dev.devt), rtc->id); + + rtc_proc_add_device(rtc); + + rtc->registered = true; + dev_info(rtc->dev.parent, "registered as %s\n", + dev_name(&rtc->dev)); + + return 0; +} +EXPORT_SYMBOL_GPL(__rtc_register_device); + static int __init rtc_init(void) { rtc_class = class_create(THIS_MODULE, "rtc"); -- cgit v1.1 From 735ae2056b3c7296bb188d079c020f606a679c4a Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 6 Jul 2017 11:42:01 +0200 Subject: rtc: at91rm9200: remove race condition While highly unlikely, it is possible to get an interrupt as soon as it is requested. In that case, at91_rtc_interrupt() will be called with rtc == NULL. Solve that by using devm_rtc_allocate_device/rtc_register_device. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-at91rm9200.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index b60fd47..e221b78 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -409,6 +409,11 @@ static int __init at91_rtc_probe(struct platform_device *pdev) return -ENOMEM; } + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + platform_set_drvdata(pdev, rtc); + sclk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(sclk)) return PTR_ERR(sclk); @@ -441,13 +446,10 @@ static int __init at91_rtc_probe(struct platform_device *pdev) if (!device_can_wakeup(&pdev->dev)) device_init_wakeup(&pdev->dev, 1); - rtc = devm_rtc_device_register(&pdev->dev, pdev->name, - &at91_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - ret = PTR_ERR(rtc); + rtc->ops = &at91_rtc_ops; + ret = rtc_register_device(rtc); + if (ret) goto err_clk; - } - platform_set_drvdata(pdev, rtc); /* enable SECEV interrupt in order to initialize at91_rtc_upd_rdy * completion. -- cgit v1.1 From 697e5a47aa12cdab6f2a8b284cc923cdf704eafc Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 6 Jul 2017 11:42:02 +0200 Subject: rtc: add generic nvmem support Many RTCs have an on board non volatile storage. It can be battery backed RAM or an EEPROM. Use the nvmem subsystem to export it to both userspace and in-kernel consumers. This stays compatible with the previous (non documented) ABI that was using /sys/class/rtc/rtcx/device/nvram to export that memory. But will warn about the deprecation. Signed-off-by: Alexandre Belloni --- drivers/rtc/Kconfig | 8 ++++ drivers/rtc/Makefile | 1 + drivers/rtc/class.c | 4 ++ drivers/rtc/nvmem.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/rtc/rtc-core.h | 8 ++++ 5 files changed, 134 insertions(+) create mode 100644 drivers/rtc/nvmem.c (limited to 'drivers') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index d0e4b4a..72419ac 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -77,6 +77,14 @@ config RTC_DEBUG Say yes here to enable debugging support in the RTC framework and individual RTC drivers. +config RTC_NVMEM + bool "RTC non volatile storage support" + select NVMEM + default RTC_CLASS + help + Say yes here to add support for the non volatile (often battery + backed) storage present on RTCs. + comment "RTC interfaces" config RTC_INTF_SYSFS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 4050fc8..acd366b 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -15,6 +15,7 @@ ifdef CONFIG_RTC_DRV_EFI rtc-core-y += rtc-efi-platform.o endif +rtc-core-$(CONFIG_RTC_NVMEM) += nvmem.o rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 58e2a05..2ed970d 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -290,6 +290,8 @@ EXPORT_SYMBOL_GPL(rtc_device_register); */ void rtc_device_unregister(struct rtc_device *rtc) { + rtc_nvmem_unregister(rtc); + mutex_lock(&rtc->ops_lock); /* * Remove innards of this RTC, then disable it, before @@ -448,6 +450,8 @@ int __rtc_register_device(struct module *owner, struct rtc_device *rtc) rtc_proc_add_device(rtc); + rtc_nvmem_register(rtc); + rtc->registered = true; dev_info(rtc->dev.parent, "registered as %s\n", dev_name(&rtc->dev)); diff --git a/drivers/rtc/nvmem.c b/drivers/rtc/nvmem.c new file mode 100644 index 0000000..8567b4e --- /dev/null +++ b/drivers/rtc/nvmem.c @@ -0,0 +1,113 @@ +/* + * RTC subsystem, nvmem interface + * + * Copyright (C) 2017 Alexandre Belloni + * + * 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 "rtc-core.h" + +/* + * Deprecated ABI compatibility, this should be removed at some point + */ + +static const char nvram_warning[] = "Deprecated ABI, please use nvmem"; + +static ssize_t +rtc_nvram_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct rtc_device *rtc = attr->private; + + dev_warn_once(kobj_to_dev(kobj), nvram_warning); + + return nvmem_device_read(rtc->nvmem, off, count, buf); +} + +static ssize_t +rtc_nvram_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct rtc_device *rtc = attr->private; + + dev_warn_once(kobj_to_dev(kobj), nvram_warning); + + return nvmem_device_write(rtc->nvmem, off, count, buf); +} + +static int rtc_nvram_register(struct rtc_device *rtc) +{ + int err; + + rtc->nvram = devm_kzalloc(rtc->dev.parent, + sizeof(struct bin_attribute), + GFP_KERNEL); + if (!rtc->nvram) + return -ENOMEM; + + rtc->nvram->attr.name = "nvram"; + rtc->nvram->attr.mode = 0644; + rtc->nvram->private = rtc; + + sysfs_bin_attr_init(rtc->nvram); + + rtc->nvram->read = rtc_nvram_read; + rtc->nvram->write = rtc_nvram_write; + rtc->nvram->size = rtc->nvmem_config->size; + + err = sysfs_create_bin_file(&rtc->dev.parent->kobj, + rtc->nvram); + if (err) { + devm_kfree(rtc->dev.parent, rtc->nvram); + rtc->nvram = NULL; + } + + return err; +} + +static void rtc_nvram_unregister(struct rtc_device *rtc) +{ + sysfs_remove_bin_file(&rtc->dev.parent->kobj, rtc->nvram); +} + +/* + * New ABI, uses nvmem + */ +void rtc_nvmem_register(struct rtc_device *rtc) +{ + if (!rtc->nvmem_config) + return; + + rtc->nvmem_config->dev = &rtc->dev; + rtc->nvmem_config->owner = rtc->owner; + rtc->nvmem = nvmem_register(rtc->nvmem_config); + if (IS_ERR_OR_NULL(rtc->nvmem)) + return; + + /* Register the old ABI */ + if (rtc->nvram_old_abi) + rtc_nvram_register(rtc); +} + +void rtc_nvmem_unregister(struct rtc_device *rtc) +{ + if (IS_ERR_OR_NULL(rtc->nvmem)) + return; + + /* unregister the old ABI */ + if (rtc->nvram) + rtc_nvram_unregister(rtc); + + nvmem_unregister(rtc->nvmem); +} diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h index 7a4ed2f..ecab76a 100644 --- a/drivers/rtc/rtc-core.h +++ b/drivers/rtc/rtc-core.h @@ -45,3 +45,11 @@ static inline const struct attribute_group **rtc_get_dev_attribute_groups(void) return NULL; } #endif + +#ifdef CONFIG_RTC_NVMEM +void rtc_nvmem_register(struct rtc_device *rtc); +void rtc_nvmem_unregister(struct rtc_device *rtc); +#else +static inline void rtc_nvmem_register(struct rtc_device *rtc) {} +static inline void rtc_nvmem_unregister(struct rtc_device *rtc) {} +#endif -- cgit v1.1 From 7133eca19505defed19b1337f46b2cd5f7608814 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 6 Jul 2017 11:42:03 +0200 Subject: rtc: rv8803: switch to rtc_register_device This removes a possible race condition and allows for further improvement of the driver. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-rv8803.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c index 9ad97ab..209edbb 100644 --- a/drivers/rtc/rtc-rv8803.c +++ b/drivers/rtc/rtc-rv8803.c @@ -577,6 +577,11 @@ static int rv8803_probe(struct i2c_client *client, if (flags & RV8803_FLAG_AF) dev_warn(&client->dev, "An alarm maybe have been missed.\n"); + rv8803->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rv8803->rtc)) { + return PTR_ERR(rv8803->rtc); + } + if (client->irq > 0) { err = devm_request_threaded_irq(&client->dev, client->irq, NULL, rv8803_handle_irq, @@ -592,12 +597,10 @@ static int rv8803_probe(struct i2c_client *client, } } - rv8803->rtc = devm_rtc_device_register(&client->dev, client->name, - &rv8803_rtc_ops, THIS_MODULE); - if (IS_ERR(rv8803->rtc)) { - dev_err(&client->dev, "unable to register the class device\n"); - return PTR_ERR(rv8803->rtc); - } + rv8803->rtc->ops = &rv8803_rtc_ops; + err = rtc_register_device(rv8803->rtc); + if (err) + return err; err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA); if (err) -- cgit v1.1 From 16d70a78b45a65c88a3c614ebadbf2589f6f8efe Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 6 Jul 2017 11:42:04 +0200 Subject: rtc: rv8803: use generic nvmem support Instead of adding a binary sysfs attribute from the driver (which suffers from a race condition as the attribute appears after the device), use the core to register an nvmem device. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-rv8803.c | 51 +++++++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c index 209edbb..10474ab 100644 --- a/drivers/rtc/rtc-rv8803.c +++ b/drivers/rtc/rtc-rv8803.c @@ -68,6 +68,7 @@ struct rv8803_data { struct mutex flags_lock; u8 ctrl; enum rv8803_type type; + struct nvmem_config nvmem_cfg; }; static int rv8803_read_reg(const struct i2c_client *client, u8 reg) @@ -460,48 +461,32 @@ static int rv8803_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) } } -static ssize_t rv8803_nvram_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +static int rv8803_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) { - struct device *dev = kobj_to_dev(kobj); - struct i2c_client *client = to_i2c_client(dev); int ret; - ret = rv8803_write_reg(client, RV8803_RAM, buf[0]); + ret = rv8803_write_reg(priv, RV8803_RAM, *(u8 *)val); if (ret) return ret; - return 1; + return 0; } -static ssize_t rv8803_nvram_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +static int rv8803_nvram_read(void *priv, unsigned int offset, + void *val, size_t bytes) { - struct device *dev = kobj_to_dev(kobj); - struct i2c_client *client = to_i2c_client(dev); int ret; - ret = rv8803_read_reg(client, RV8803_RAM); + ret = rv8803_read_reg(priv, RV8803_RAM); if (ret < 0) return ret; - buf[0] = ret; + *(u8 *)val = ret; - return 1; + return 0; } -static struct bin_attribute rv8803_nvram_attr = { - .attr = { - .name = "nvram", - .mode = S_IRUGO | S_IWUSR, - }, - .size = 1, - .read = rv8803_nvram_read, - .write = rv8803_nvram_write, -}; - static struct rtc_class_ops rv8803_rtc_ops = { .read_time = rv8803_get_time, .set_time = rv8803_set_time, @@ -597,7 +582,17 @@ static int rv8803_probe(struct i2c_client *client, } } + rv8803->nvmem_cfg.name = "rv8803_nvram", + rv8803->nvmem_cfg.word_size = 1, + rv8803->nvmem_cfg.stride = 1, + rv8803->nvmem_cfg.size = 1, + rv8803->nvmem_cfg.reg_read = rv8803_nvram_read, + rv8803->nvmem_cfg.reg_write = rv8803_nvram_write, + rv8803->nvmem_cfg.priv = client; + rv8803->rtc->ops = &rv8803_rtc_ops; + rv8803->rtc->nvmem_config = &rv8803->nvmem_cfg; + rv8803->rtc->nvram_old_abi = true; err = rtc_register_device(rv8803->rtc); if (err) return err; @@ -612,10 +607,6 @@ static int rv8803_probe(struct i2c_client *client, return err; } - err = device_create_bin_file(&client->dev, &rv8803_nvram_attr); - if (err) - return err; - rv8803->rtc->max_user_freq = 1; return 0; @@ -623,8 +614,6 @@ static int rv8803_probe(struct i2c_client *client, static int rv8803_remove(struct i2c_client *client) { - device_remove_bin_file(&client->dev, &rv8803_nvram_attr); - return 0; } -- cgit v1.1 From 2a52482ffb1a65249049eaec9e3560a1e79abf3d Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 6 Jul 2017 11:42:05 +0200 Subject: rtc: rv8803: remove rv8803_remove rv8803_remove() is now empty, remove it. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-rv8803.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c index 10474ab..aae2576 100644 --- a/drivers/rtc/rtc-rv8803.c +++ b/drivers/rtc/rtc-rv8803.c @@ -612,11 +612,6 @@ static int rv8803_probe(struct i2c_client *client, return 0; } -static int rv8803_remove(struct i2c_client *client) -{ - return 0; -} - static const struct i2c_device_id rv8803_id[] = { { "rv8803", rv_8803 }, { "rx8900", rx_8900 }, @@ -643,7 +638,6 @@ static struct i2c_driver rv8803_driver = { .of_match_table = of_match_ptr(rv8803_of_match), }, .probe = rv8803_probe, - .remove = rv8803_remove, .id_table = rv8803_id, }; module_i2c_driver(rv8803_driver); -- cgit v1.1 From 69b119a642b7e143be08f9cbabca7bc910f5720d Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 6 Jul 2017 11:42:06 +0200 Subject: rtc: ds1307: switch to rtc_register_device This removes a possible race condition and crash and allows for further improvement of the driver. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 57ea254..6f94f20 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -1674,8 +1674,8 @@ read_rtc: device_set_wakeup_capable(ds1307->dev, true); set_bit(HAS_ALARM, &ds1307->flags); } - ds1307->rtc = devm_rtc_device_register(ds1307->dev, ds1307->name, - rtc_ops, THIS_MODULE); + + ds1307->rtc = devm_rtc_allocate_device(ds1307->dev); if (IS_ERR(ds1307->rtc)) { return PTR_ERR(ds1307->rtc); } @@ -1737,6 +1737,11 @@ read_rtc: } } + ds1307->rtc->ops = rtc_ops; + err = rtc_register_device(ds1307->rtc); + if (err) + return err; + ds1307_hwmon_register(ds1307); ds1307_clks_register(ds1307); -- cgit v1.1 From abc925f72c666042bf49fbf590bed01e5dbc2c97 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 6 Jul 2017 11:42:07 +0200 Subject: rtc: ds1307: use generic nvmem Instead of adding a binary sysfs attribute from the driver (which suffers from a race condition as the attribute appears after the device), use the core to register an nvmem device. Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 88 ++++++++++++------------------------------------ 1 file changed, 22 insertions(+), 66 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 6f94f20..7665256 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -118,7 +118,7 @@ struct ds1307 { u8 offset; /* register's offset */ u8 regs[11]; u16 nvram_offset; - struct bin_attribute *nvram; + struct nvmem_config nvmem_cfg; enum ds_type type; unsigned long flags; #define HAS_NVRAM 0 /* bit 0 == sysfs file active */ @@ -895,43 +895,24 @@ static const struct rtc_class_ops mcp794xx_rtc_ops = { /*----------------------------------------------------------------------*/ -static ssize_t -ds1307_nvram_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +static int ds1307_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) { - struct ds1307 *ds1307; - int result; - - ds1307 = dev_get_drvdata(kobj_to_dev(kobj)); + struct ds1307 *ds1307 = priv; - result = regmap_bulk_read(ds1307->regmap, ds1307->nvram_offset + off, - buf, count); - if (result) - dev_err(ds1307->dev, "%s error %d\n", "nvram read", result); - return result; + return regmap_bulk_read(ds1307->regmap, ds1307->nvram_offset + offset, + val, bytes); } -static ssize_t -ds1307_nvram_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +static int ds1307_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) { - struct ds1307 *ds1307; - int result; + struct ds1307 *ds1307 = priv; - ds1307 = dev_get_drvdata(kobj_to_dev(kobj)); - - result = regmap_bulk_write(ds1307->regmap, ds1307->nvram_offset + off, - buf, count); - if (result) { - dev_err(ds1307->dev, "%s error %d\n", "nvram write", result); - return result; - } - return count; + return regmap_bulk_write(ds1307->regmap, ds1307->nvram_offset + offset, + val, bytes); } - /*----------------------------------------------------------------------*/ static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, @@ -1704,37 +1685,17 @@ read_rtc: } if (chip->nvram_size) { - - ds1307->nvram = devm_kzalloc(ds1307->dev, - sizeof(struct bin_attribute), - GFP_KERNEL); - if (!ds1307->nvram) { - dev_err(ds1307->dev, - "cannot allocate memory for nvram sysfs\n"); - } else { - - ds1307->nvram->attr.name = "nvram"; - ds1307->nvram->attr.mode = S_IRUGO | S_IWUSR; - - sysfs_bin_attr_init(ds1307->nvram); - - ds1307->nvram->read = ds1307_nvram_read; - ds1307->nvram->write = ds1307_nvram_write; - ds1307->nvram->size = chip->nvram_size; - ds1307->nvram_offset = chip->nvram_offset; - - err = sysfs_create_bin_file(&ds1307->dev->kobj, - ds1307->nvram); - if (err) { - dev_err(ds1307->dev, - "unable to create sysfs file: %s\n", - ds1307->nvram->attr.name); - } else { - set_bit(HAS_NVRAM, &ds1307->flags); - dev_info(ds1307->dev, "%zu bytes nvram\n", - ds1307->nvram->size); - } - } + ds1307->nvmem_cfg.name = "ds1307_nvram"; + ds1307->nvmem_cfg.word_size = 1; + ds1307->nvmem_cfg.stride = 1; + ds1307->nvmem_cfg.size = chip->nvram_size; + ds1307->nvmem_cfg.reg_read = ds1307_nvram_read; + ds1307->nvmem_cfg.reg_write = ds1307_nvram_write; + ds1307->nvmem_cfg.priv = ds1307; + ds1307->nvram_offset = chip->nvram_offset; + + ds1307->rtc->nvmem_config = &ds1307->nvmem_cfg; + ds1307->rtc->nvram_old_abi = true; } ds1307->rtc->ops = rtc_ops; @@ -1753,11 +1714,6 @@ exit: static int ds1307_remove(struct i2c_client *client) { - struct ds1307 *ds1307 = i2c_get_clientdata(client); - - if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags)) - sysfs_remove_bin_file(&ds1307->dev->kobj, ds1307->nvram); - return 0; } -- cgit v1.1 From bed8e280476fc8652f4105fe430e10fcf4032392 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 6 Jul 2017 11:42:08 +0200 Subject: rtc: ds1307: remove ds1307_remove ds1307_remove() is now empty, remove it Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-ds1307.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 7665256..4fac49e 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -1712,11 +1712,6 @@ exit: return err; } -static int ds1307_remove(struct i2c_client *client) -{ - return 0; -} - static struct i2c_driver ds1307_driver = { .driver = { .name = "rtc-ds1307", @@ -1724,7 +1719,6 @@ static struct i2c_driver ds1307_driver = { .acpi_match_table = ACPI_PTR(ds1307_acpi_ids), }, .probe = ds1307_probe, - .remove = ds1307_remove, .id_table = ds1307_id, }; -- cgit v1.1 From f4b82d39e40fc9c3493826abb49c64c37c7025b9 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Mon, 19 Jun 2017 11:36:22 +0200 Subject: rtc: st-lpc: make it robust against y2038/2106 bug Make driver use u64 variables and functions to be sure that it will support dates after year 2038. Signed-off-by: Benjamin Gaignard Acked-by: Patrice Chotard Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-st-lpc.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c index 74c0a33..82b0af1 100644 --- a/drivers/rtc/rtc-st-lpc.c +++ b/drivers/rtc/rtc-st-lpc.c @@ -99,7 +99,7 @@ static int st_rtc_read_time(struct device *dev, struct rtc_time *tm) lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb; do_div(lpt, rtc->clkrate); - rtc_time_to_tm(lpt, tm); + rtc_time64_to_tm(lpt, tm); return 0; } @@ -107,13 +107,10 @@ static int st_rtc_read_time(struct device *dev, struct rtc_time *tm) static int st_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct st_rtc *rtc = dev_get_drvdata(dev); - unsigned long long lpt; - unsigned long secs, flags; - int ret; + unsigned long long lpt, secs; + unsigned long flags; - ret = rtc_tm_to_time(tm, &secs); - if (ret) - return ret; + secs = rtc_tm_to_time64(tm); lpt = (unsigned long long)secs * rtc->clkrate; @@ -161,13 +158,13 @@ static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct st_rtc *rtc = dev_get_drvdata(dev); struct rtc_time now; - unsigned long now_secs; - unsigned long alarm_secs; + unsigned long long now_secs; + unsigned long long alarm_secs; unsigned long long lpa; st_rtc_read_time(dev, &now); - rtc_tm_to_time(&now, &now_secs); - rtc_tm_to_time(&t->time, &alarm_secs); + now_secs = rtc_tm_to_time64(&now); + alarm_secs = rtc_tm_to_time64(&t->time); /* Invalid alarm time */ if (now_secs > alarm_secs) -- cgit v1.1