diff options
-rw-r--r-- | drivers/i2c/busses/i2c-i801.c | 19 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-rcar.c | 41 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-riic.c | 2 | ||||
-rw-r--r-- | drivers/i2c/i2c-core.h | 2 | ||||
-rw-r--r-- | drivers/mfd/tps6586x.c | 50 | ||||
-rw-r--r-- | include/linux/reboot.h | 3 | ||||
-rw-r--r-- | kernel/reboot.c | 4 |
7 files changed, 78 insertions, 43 deletions
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 1d855258a45d..9bd712eafa8e 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -679,15 +679,11 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, return result ? priv->status : -ETIMEDOUT; } - for (i = 1; i <= len; i++) { - if (i == len && read_write == I2C_SMBUS_READ) - smbcmd |= SMBHSTCNT_LAST_BYTE; - outb_p(smbcmd, SMBHSTCNT(priv)); - - if (i == 1) - outb_p(inb(SMBHSTCNT(priv)) | SMBHSTCNT_START, - SMBHSTCNT(priv)); + if (len == 1 && read_write == I2C_SMBUS_READ) + smbcmd |= SMBHSTCNT_LAST_BYTE; + outb_p(smbcmd | SMBHSTCNT_START, SMBHSTCNT(priv)); + for (i = 1; i <= len; i++) { status = i801_wait_byte_done(priv); if (status) return status; @@ -710,9 +706,12 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, data->block[0] = len; } - /* Retrieve/store value in SMBBLKDAT */ - if (read_write == I2C_SMBUS_READ) + if (read_write == I2C_SMBUS_READ) { data->block[i] = inb_p(SMBBLKDAT(priv)); + if (i == len - 1) + outb_p(smbcmd | SMBHSTCNT_LAST_BYTE, SMBHSTCNT(priv)); + } + if (read_write == I2C_SMBUS_WRITE && i+1 <= len) outb_p(data->block[i+1], SMBBLKDAT(priv)); diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index a32a93f9a60d..5e97635faf78 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -291,45 +291,40 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv) ick = rate / (cdf + 1); /* - * it is impossible to calculate large scale - * number on u32. separate it + * It is impossible to calculate a large scale number on u32. Separate it. * * F[(ticf + tr + intd) * ick] with sum = (ticf + tr + intd) * = F[sum * ick / 1000000000] * = F[(ick / 1000000) * sum / 1000] */ sum = t.scl_fall_ns + t.scl_rise_ns + t.scl_int_delay_ns; - round = (ick + 500000) / 1000000 * sum; - round = (round + 500) / 1000; + round = DIV_ROUND_CLOSEST(ick, 1000000); + round = DIV_ROUND_CLOSEST(round * sum, 1000); /* - * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick]) - * - * Calculation result (= SCL) should be less than - * bus_speed for hardware safety - * - * We could use something along the lines of - * div = ick / (bus_speed + 1) + 1; - * scgd = (div - 20 - round + 7) / 8; - * scl = ick / (20 + (scgd * 8) + round); - * (not fully verified) but that would get pretty involved + * SCL = ick / (20 + 8 * SCGD + F[(ticf + tr + intd) * ick]) + * 20 + 8 * SCGD + F[...] = ick / SCL + * SCGD = ((ick / SCL) - 20 - F[...]) / 8 + * Result (= SCL) should be less than bus_speed for hardware safety */ - for (scgd = 0; scgd < 0x40; scgd++) { - scl = ick / (20 + (scgd * 8) + round); - if (scl <= t.bus_freq_hz) - goto scgd_find; - } - dev_err(dev, "it is impossible to calculate best SCL\n"); - return -EIO; + scgd = DIV_ROUND_UP(ick, t.bus_freq_hz ?: 1); + scgd = DIV_ROUND_UP(scgd - 20 - round, 8); + scl = ick / (20 + 8 * scgd + round); + + if (scgd > 0x3f) + goto err_no_val; -scgd_find: - dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n", + dev_dbg(dev, "clk %u/%u(%lu), round %u, CDF: %u, SCGD: %u\n", scl, t.bus_freq_hz, rate, round, cdf, scgd); /* keep icccr value */ priv->icccr = scgd << cdf_width | cdf; return 0; + +err_no_val: + dev_err(dev, "it is impossible to calculate best SCL\n"); + return -EINVAL; } /* diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c index f0ee8871d5ae..e43ff483c56e 100644 --- a/drivers/i2c/busses/i2c-riic.c +++ b/drivers/i2c/busses/i2c-riic.c @@ -313,7 +313,7 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t) * frequency with only 62 clock ticks max (31 high, 31 low). * Aim for a duty of 60% LOW, 40% HIGH. */ - total_ticks = DIV_ROUND_UP(rate, t->bus_freq_hz); + total_ticks = DIV_ROUND_UP(rate, t->bus_freq_hz ?: 1); for (cks = 0; cks < 7; cks++) { /* diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h index 1247e6e6e975..05b8b8dfa9bd 100644 --- a/drivers/i2c/i2c-core.h +++ b/drivers/i2c/i2c-core.h @@ -29,7 +29,7 @@ int i2c_dev_irq_from_resources(const struct resource *resources, */ static inline bool i2c_in_atomic_xfer_mode(void) { - return system_state > SYSTEM_RUNNING && irqs_disabled(); + return system_state > SYSTEM_RUNNING && !preemptible(); } static inline int __i2c_lock_bus_helper(struct i2c_adapter *adap) diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 55675ceedcd3..7e8160b94784 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -22,6 +22,7 @@ #include <linux/err.h> #include <linux/i2c.h> #include <linux/platform_device.h> +#include <linux/reboot.h> #include <linux/regmap.h> #include <linux/of.h> @@ -29,6 +30,7 @@ #include <linux/mfd/tps6586x.h> #define TPS6586X_SUPPLYENE 0x14 +#define SOFT_RST_BIT BIT(0) #define EXITSLREQ_BIT BIT(1) #define SLEEP_MODE_BIT BIT(3) @@ -457,13 +459,34 @@ static const struct regmap_config tps6586x_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -static struct device *tps6586x_dev; -static void tps6586x_power_off(void) +static int tps6586x_power_off_handler(struct sys_off_data *data) { - if (tps6586x_clr_bits(tps6586x_dev, TPS6586X_SUPPLYENE, EXITSLREQ_BIT)) - return; + int ret; + + /* Put the PMIC into sleep state. This takes at least 20ms. */ + ret = tps6586x_clr_bits(data->dev, TPS6586X_SUPPLYENE, EXITSLREQ_BIT); + if (ret) + return notifier_from_errno(ret); + + ret = tps6586x_set_bits(data->dev, TPS6586X_SUPPLYENE, SLEEP_MODE_BIT); + if (ret) + return notifier_from_errno(ret); + + mdelay(50); + return notifier_from_errno(-ETIME); +} + +static int tps6586x_restart_handler(struct sys_off_data *data) +{ + int ret; + + /* Put the PMIC into hard reboot state. This takes at least 20ms. */ + ret = tps6586x_set_bits(data->dev, TPS6586X_SUPPLYENE, SOFT_RST_BIT); + if (ret) + return notifier_from_errno(ret); - tps6586x_set_bits(tps6586x_dev, TPS6586X_SUPPLYENE, SLEEP_MODE_BIT); + mdelay(50); + return notifier_from_errno(-ETIME); } static void tps6586x_print_version(struct i2c_client *client, int version) @@ -559,9 +582,20 @@ static int tps6586x_i2c_probe(struct i2c_client *client) goto err_add_devs; } - if (pdata->pm_off && !pm_power_off) { - tps6586x_dev = &client->dev; - pm_power_off = tps6586x_power_off; + if (pdata->pm_off) { + ret = devm_register_power_off_handler(&client->dev, &tps6586x_power_off_handler, + NULL); + if (ret) { + dev_err(&client->dev, "register power off handler failed: %d\n", ret); + goto err_add_devs; + } + + ret = devm_register_restart_handler(&client->dev, &tps6586x_restart_handler, + NULL); + if (ret) { + dev_err(&client->dev, "register restart handler failed: %d\n", ret); + goto err_add_devs; + } } return 0; diff --git a/include/linux/reboot.h b/include/linux/reboot.h index 2b6bb593be5b..c4cc3b89ced1 100644 --- a/include/linux/reboot.h +++ b/include/linux/reboot.h @@ -129,11 +129,14 @@ enum sys_off_mode { * @cb_data: User's callback data. * @cmd: Command string. Currently used only by the sys-off restart mode, * NULL otherwise. + * @dev: Device of the sys-off handler. Only if known (devm_register_*), + * NULL otherwise. */ struct sys_off_data { int mode; void *cb_data; const char *cmd; + struct device *dev; }; struct sys_off_handler * diff --git a/kernel/reboot.c b/kernel/reboot.c index 3bba88c7ffc6..395a0ea3c7a8 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -55,6 +55,7 @@ struct sys_off_handler { enum sys_off_mode mode; bool blocking; void *list; + struct device *dev; }; /* @@ -74,6 +75,7 @@ void __weak (*pm_power_off)(void); void emergency_restart(void) { kmsg_dump(KMSG_DUMP_EMERG); + system_state = SYSTEM_RESTART; machine_emergency_restart(); } EXPORT_SYMBOL_GPL(emergency_restart); @@ -323,6 +325,7 @@ static int sys_off_notify(struct notifier_block *nb, data.cb_data = handler->cb_data; data.mode = mode; data.cmd = cmd; + data.dev = handler->dev; return handler->sys_off_cb(&data); } @@ -510,6 +513,7 @@ int devm_register_sys_off_handler(struct device *dev, handler = register_sys_off_handler(mode, priority, callback, cb_data); if (IS_ERR(handler)) return PTR_ERR(handler); + handler->dev = dev; return devm_add_action_or_reset(dev, devm_unregister_sys_off_handler, handler); |