diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-06-10 05:30:17 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-06-10 05:30:17 -0300 |
commit | d635711daa98be86d4c7fd01499c34f566b54ccb (patch) | |
tree | aa5cc3760a27c3d57146498cb82fa549547de06c /drivers/rtc/rtc-ds1307.c | |
parent | c91265cd0efb83778f015b4d4b1129bd2cfd075e (diff) |
Linux-libre 4.6.2-gnu
Diffstat (limited to 'drivers/rtc/rtc-ds1307.c')
-rw-r--r-- | drivers/rtc/rtc-ds1307.c | 411 |
1 files changed, 409 insertions, 2 deletions
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index cf685f67b..ecb7dbae9 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -19,6 +19,9 @@ #include <linux/rtc.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/clk-provider.h> /* * We can't determine type by probing, but if we expect pre-Linux code @@ -89,6 +92,7 @@ enum ds_type { # define DS1340_BIT_OSF 0x80 #define DS1337_REG_STATUS 0x0f # define DS1337_BIT_OSF 0x80 +# define DS3231_BIT_EN32KHZ 0x08 # define DS1337_BIT_A2I 0x02 # define DS1337_BIT_A1I 0x01 #define DS1339_REG_ALARM1_SECS 0x07 @@ -118,6 +122,9 @@ struct ds1307 { 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 }; struct chip_desc { @@ -842,6 +849,378 @@ out: return; } +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_RTC_DRV_DS1307_HWMON + +/* + * Temperature sensor support for ds3231 devices. + */ + +#define DS3231_REG_TEMPERATURE 0x11 + +/* + * A user-initiated temperature conversion is not started by this function, + * so the temperature is updated once every 64 seconds. + */ +static int ds3231_hwmon_read_temp(struct device *dev, s32 *mC) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 temp_buf[2]; + s16 temp; + int ret; + + ret = ds1307->read_block_data(ds1307->client, DS3231_REG_TEMPERATURE, + sizeof(temp_buf), temp_buf); + if (ret < 0) + 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. + */ + temp = (temp_buf[0] << 8) | temp_buf[1]; + temp >>= 6; + *mC = temp * 250; + + return 0; +} + +static ssize_t ds3231_hwmon_show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + s32 temp; + + ret = ds3231_hwmon_read_temp(dev, &temp); + if (ret) + return ret; + + return sprintf(buf, "%d\n", temp); +} +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ds3231_hwmon_show_temp, + NULL, 0); + +static struct attribute *ds3231_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ds3231_hwmon); + +static void ds1307_hwmon_register(struct ds1307 *ds1307) +{ + struct device *dev; + + if (ds1307->type != ds_3231) + return; + + dev = devm_hwmon_device_register_with_groups(&ds1307->client->dev, + ds1307->client->name, + ds1307, ds3231_hwmon_groups); + if (IS_ERR(dev)) { + dev_warn(&ds1307->client->dev, + "unable to register hwmon device %ld\n", PTR_ERR(dev)); + } +} + +#else + +static void ds1307_hwmon_register(struct ds1307 *ds1307) +{ +} + +#endif /* CONFIG_RTC_DRV_DS1307_HWMON */ + +/*----------------------------------------------------------------------*/ + +/* + * Square-wave output support for DS3231 + * Datasheet: https://datasheets.maximintegrated.com/en/ds/DS3231.pdf + */ +#ifdef CONFIG_COMMON_CLK + +enum { + DS3231_CLK_SQW = 0, + DS3231_CLK_32KHZ, +}; + +#define clk_sqw_to_ds1307(clk) \ + container_of(clk, struct ds1307, clks[DS3231_CLK_SQW]) +#define clk_32khz_to_ds1307(clk) \ + container_of(clk, struct ds1307, clks[DS3231_CLK_32KHZ]) + +static int ds3231_clk_sqw_rates[] = { + 1, + 1024, + 4096, + 8192, +}; + +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; + goto out; + } + + control &= ~mask; + control |= value; + + ret = i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, control); +out: + mutex_unlock(lock); + + return ret; +} + +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 rate_sel = 0; + + control = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_CONTROL); + if (control < 0) + return control; + if (control & DS1337_BIT_RS1) + rate_sel += 1; + if (control & DS1337_BIT_RS2) + rate_sel += 2; + + return ds3231_clk_sqw_rates[rate_sel]; +} + +static long ds3231_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int i; + + for (i = ARRAY_SIZE(ds3231_clk_sqw_rates) - 1; i >= 0; i--) { + if (ds3231_clk_sqw_rates[i] <= rate) + return ds3231_clk_sqw_rates[i]; + } + + return 0; +} + +static int ds3231_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + int control = 0; + int rate_sel; + + for (rate_sel = 0; rate_sel < ARRAY_SIZE(ds3231_clk_sqw_rates); + rate_sel++) { + if (ds3231_clk_sqw_rates[rate_sel] == rate) + break; + } + + if (rate_sel == ARRAY_SIZE(ds3231_clk_sqw_rates)) + return -EINVAL; + + if (rate_sel & 1) + control |= DS1337_BIT_RS1; + if (rate_sel & 2) + control |= DS1337_BIT_RS2; + + return ds1337_write_control(ds1307, DS1337_BIT_RS1 | DS1337_BIT_RS2, + control); +} + +static int ds3231_clk_sqw_prepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + return ds1337_write_control(ds1307, DS1337_BIT_INTCN, 0); +} + +static void ds3231_clk_sqw_unprepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + ds1337_write_control(ds1307, DS1337_BIT_INTCN, DS1337_BIT_INTCN); +} + +static int ds3231_clk_sqw_is_prepared(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + int control; + + control = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_CONTROL); + if (control < 0) + return control; + + return !(control & DS1337_BIT_INTCN); +} + +static const struct clk_ops ds3231_clk_sqw_ops = { + .prepare = ds3231_clk_sqw_prepare, + .unprepare = ds3231_clk_sqw_unprepare, + .is_prepared = ds3231_clk_sqw_is_prepared, + .recalc_rate = ds3231_clk_sqw_recalc_rate, + .round_rate = ds3231_clk_sqw_round_rate, + .set_rate = ds3231_clk_sqw_set_rate, +}; + +static unsigned long ds3231_clk_32khz_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 32768; +} + +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; + goto out; + } + + if (enable) + status |= DS3231_BIT_EN32KHZ; + else + status &= ~DS3231_BIT_EN32KHZ; + + ret = i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, status); +out: + mutex_unlock(lock); + + return ret; +} + +static int ds3231_clk_32khz_prepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw); + + return ds3231_clk_32khz_control(ds1307, true); +} + +static void ds3231_clk_32khz_unprepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw); + + ds3231_clk_32khz_control(ds1307, false); +} + +static int ds3231_clk_32khz_is_prepared(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw); + int status; + + status = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_STATUS); + if (status < 0) + return status; + + return !!(status & DS3231_BIT_EN32KHZ); +} + +static const struct clk_ops ds3231_clk_32khz_ops = { + .prepare = ds3231_clk_32khz_prepare, + .unprepare = ds3231_clk_32khz_unprepare, + .is_prepared = ds3231_clk_32khz_is_prepared, + .recalc_rate = ds3231_clk_32khz_recalc_rate, +}; + +static struct clk_init_data ds3231_clks_init[] = { + [DS3231_CLK_SQW] = { + .name = "ds3231_clk_sqw", + .ops = &ds3231_clk_sqw_ops, + .flags = CLK_IS_ROOT, + }, + [DS3231_CLK_32KHZ] = { + .name = "ds3231_clk_32khz", + .ops = &ds3231_clk_32khz_ops, + .flags = CLK_IS_ROOT, + }, +}; + +static int ds3231_clks_register(struct ds1307 *ds1307) +{ + struct i2c_client *client = ds1307->client; + struct device_node *node = client->dev.of_node; + struct clk_onecell_data *onecell; + int i; + + onecell = devm_kzalloc(&client->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); + if (!onecell->clks) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(ds3231_clks_init); i++) { + struct clk_init_data init = ds3231_clks_init[i]; + + /* + * Interrupt signal due to alarm conditions and square-wave + * output share same pin, so don't initialize both. + */ + if (i == DS3231_CLK_SQW && test_bit(HAS_ALARM, &ds1307->flags)) + continue; + + /* optional override of the clockname */ + of_property_read_string_index(node, "clock-output-names", i, + &init.name); + ds1307->clks[i].init = &init; + + onecell->clks[i] = devm_clk_register(&client->dev, + &ds1307->clks[i]); + if (IS_ERR(onecell->clks[i])) + return PTR_ERR(onecell->clks[i]); + } + + if (!node) + return 0; + + of_clk_add_provider(node, of_clk_src_onecell_get, onecell); + + return 0; +} + +static void ds1307_clks_register(struct ds1307 *ds1307) +{ + int ret; + + if (ds1307->type != ds_3231) + return; + + ret = ds3231_clks_register(ds1307); + if (ret) { + dev_warn(&ds1307->client->dev, + "unable to register clock device %d\n", ret); + } +} + +#else + +static void ds1307_clks_register(struct ds1307 *ds1307) +{ +} + +#endif /* CONFIG_COMMON_CLK */ + static int ds1307_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -851,6 +1230,7 @@ static int ds1307_probe(struct i2c_client *client, struct chip_desc *chip = &chips[id->driver_data]; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); bool want_irq = false; + bool ds1307_can_wakeup_device = false; unsigned char *buf; struct ds1307_platform_data *pdata = dev_get_platdata(&client->dev); irq_handler_t irq_handler = ds1307_irq; @@ -898,6 +1278,20 @@ static int ds1307_probe(struct i2c_client *client, ds1307->write_block_data = ds1307_write_block_data; } +#ifdef CONFIG_OF +/* + * For devices with no IRQ directly connected to the SoC, the RTC chip + * can be forced as a wakeup source by stating that explicitly in + * the device's .dts file using the "wakeup-source" boolean property. + * If the "wakeup-source" property is set, don't request an IRQ. + * This will guarantee the 'wakealarm' sysfs entry is available on the device, + * if supported by the RTC. + */ + if (of_property_read_bool(client->dev.of_node, "wakeup-source")) { + ds1307_can_wakeup_device = true; + } +#endif + switch (ds1307->type) { case ds_1337: case ds_1339: @@ -916,11 +1310,13 @@ static int ds1307_probe(struct i2c_client *client, ds1307->regs[0] &= ~DS1337_BIT_nEOSC; /* - * Using IRQ? Disable the square wave and both alarms. + * Using IRQ or defined as wakeup-source? + * Disable the square wave and both alarms. * For some variants, be sure alarms can trigger when we're * running on Vbackup (BBSQI/BBSQW) */ - if (ds1307->client->irq > 0 && chip->alarm) { + if (chip->alarm && (ds1307->client->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); @@ -1135,6 +1531,14 @@ read_rtc: return PTR_ERR(ds1307->rtc); } + if (ds1307_can_wakeup_device && ds1307->client->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"); + /* 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, @@ -1182,6 +1586,9 @@ read_rtc: } } + ds1307_hwmon_register(ds1307); + ds1307_clks_register(ds1307); + return 0; exit: |