From f05be589ff32e87821b86845625ed3d402d37dc7 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 10 Apr 2015 12:09:01 +0800 Subject: mfd: axp20x: Add AXP22x PMIC support Add support for the AXP22x PMIC devices to the existing AXP20x driver. This includes the AXP221 and AXP223, which are identical except for the external data bus. Only AXP221 is added for now. AXP223 will be added after it's Reduced Serial Bus (RSB) interface is supported. AXP22x defines a new set of registers, power supplies and regulators, but most of the API is similar to the AXP20x ones. A new irq chip definition is used, even though the available interrupts on AXP22x is a subset of those on AXP20x. This is done so the interrupt numbers match those on the datasheet. This patch only enables the interrupts, system power-off function, and PEK sub-device. The regulator driver must first support different variants before we enable it from the mfd driver. Signed-off-by: Boris BREZILLON [wens@csie.org: fix interrupts and move regulators to separate patch] Signed-off-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index d18029be6a78..cfbb7d7aead6 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -32,6 +32,7 @@ static const char * const axp20x_model_names[] = { "AXP202", "AXP209", + "AXP221", "AXP288", }; @@ -54,6 +55,25 @@ static const struct regmap_access_table axp20x_volatile_table = { .n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges), }; +static const struct regmap_range axp22x_writeable_ranges[] = { + regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE), + regmap_reg_range(AXP20X_DCDC_MODE, AXP22X_BATLOW_THRES1), +}; + +static const struct regmap_range axp22x_volatile_ranges[] = { + regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE), +}; + +static const struct regmap_access_table axp22x_writeable_table = { + .yes_ranges = axp22x_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(axp22x_writeable_ranges), +}; + +static const struct regmap_access_table axp22x_volatile_table = { + .yes_ranges = axp22x_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(axp22x_volatile_ranges), +}; + static const struct regmap_range axp288_writeable_ranges[] = { regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE), regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5), @@ -87,6 +107,20 @@ static struct resource axp20x_pek_resources[] = { }, }; +static struct resource axp22x_pek_resources[] = { + { + .name = "PEK_DBR", + .start = AXP22X_IRQ_PEK_RIS_EDGE, + .end = AXP22X_IRQ_PEK_RIS_EDGE, + .flags = IORESOURCE_IRQ, + }, { + .name = "PEK_DBF", + .start = AXP22X_IRQ_PEK_FAL_EDGE, + .end = AXP22X_IRQ_PEK_FAL_EDGE, + .flags = IORESOURCE_IRQ, + }, +}; + static struct resource axp288_fuel_gauge_resources[] = { { .start = AXP288_IRQ_QWBTU, @@ -129,6 +163,15 @@ static const struct regmap_config axp20x_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +static const struct regmap_config axp22x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .wr_table = &axp22x_writeable_table, + .volatile_table = &axp22x_volatile_table, + .max_register = AXP22X_BATLOW_THRES1, + .cache_type = REGCACHE_RBTREE, +}; + static const struct regmap_config axp288_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -181,6 +224,34 @@ static const struct regmap_irq axp20x_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP20X, GPIO0_INPUT, 4, 0), }; +static const struct regmap_irq axp22x_regmap_irqs[] = { + INIT_REGMAP_IRQ(AXP22X, ACIN_OVER_V, 0, 7), + INIT_REGMAP_IRQ(AXP22X, ACIN_PLUGIN, 0, 6), + INIT_REGMAP_IRQ(AXP22X, ACIN_REMOVAL, 0, 5), + INIT_REGMAP_IRQ(AXP22X, VBUS_OVER_V, 0, 4), + INIT_REGMAP_IRQ(AXP22X, VBUS_PLUGIN, 0, 3), + INIT_REGMAP_IRQ(AXP22X, VBUS_REMOVAL, 0, 2), + INIT_REGMAP_IRQ(AXP22X, VBUS_V_LOW, 0, 1), + INIT_REGMAP_IRQ(AXP22X, BATT_PLUGIN, 1, 7), + INIT_REGMAP_IRQ(AXP22X, BATT_REMOVAL, 1, 6), + INIT_REGMAP_IRQ(AXP22X, BATT_ENT_ACT_MODE, 1, 5), + INIT_REGMAP_IRQ(AXP22X, BATT_EXIT_ACT_MODE, 1, 4), + INIT_REGMAP_IRQ(AXP22X, CHARG, 1, 3), + INIT_REGMAP_IRQ(AXP22X, CHARG_DONE, 1, 2), + INIT_REGMAP_IRQ(AXP22X, BATT_TEMP_HIGH, 1, 1), + INIT_REGMAP_IRQ(AXP22X, BATT_TEMP_LOW, 1, 0), + INIT_REGMAP_IRQ(AXP22X, DIE_TEMP_HIGH, 2, 7), + INIT_REGMAP_IRQ(AXP22X, PEK_SHORT, 2, 1), + INIT_REGMAP_IRQ(AXP22X, PEK_LONG, 2, 0), + INIT_REGMAP_IRQ(AXP22X, LOW_PWR_LVL1, 3, 1), + INIT_REGMAP_IRQ(AXP22X, LOW_PWR_LVL2, 3, 0), + INIT_REGMAP_IRQ(AXP22X, TIMER, 4, 7), + INIT_REGMAP_IRQ(AXP22X, PEK_RIS_EDGE, 4, 6), + INIT_REGMAP_IRQ(AXP22X, PEK_FAL_EDGE, 4, 5), + INIT_REGMAP_IRQ(AXP22X, GPIO1_INPUT, 4, 1), + INIT_REGMAP_IRQ(AXP22X, GPIO0_INPUT, 4, 0), +}; + /* some IRQs are compatible with axp20x models */ static const struct regmap_irq axp288_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP288, VBUS_FALL, 0, 2), @@ -224,6 +295,7 @@ static const struct regmap_irq axp288_regmap_irqs[] = { static const struct of_device_id axp20x_of_match[] = { { .compatible = "x-powers,axp202", .data = (void *) AXP202_ID }, { .compatible = "x-powers,axp209", .data = (void *) AXP209_ID }, + { .compatible = "x-powers,axp221", .data = (void *) AXP221_ID }, { }, }; MODULE_DEVICE_TABLE(of, axp20x_of_match); @@ -258,6 +330,18 @@ static const struct regmap_irq_chip axp20x_regmap_irq_chip = { }; +static const struct regmap_irq_chip axp22x_regmap_irq_chip = { + .name = "axp22x_irq_chip", + .status_base = AXP20X_IRQ1_STATE, + .ack_base = AXP20X_IRQ1_STATE, + .mask_base = AXP20X_IRQ1_EN, + .mask_invert = true, + .init_ack_masked = true, + .irqs = axp22x_regmap_irqs, + .num_irqs = ARRAY_SIZE(axp22x_regmap_irqs), + .num_regs = 5, +}; + static const struct regmap_irq_chip axp288_regmap_irq_chip = { .name = "axp288_irq_chip", .status_base = AXP20X_IRQ1_STATE, @@ -281,6 +365,14 @@ static struct mfd_cell axp20x_cells[] = { }, }; +static struct mfd_cell axp22x_cells[] = { + { + .name = "axp20x-pek", + .num_resources = ARRAY_SIZE(axp22x_pek_resources), + .resources = axp22x_pek_resources, + }, +}; + static struct resource axp288_adc_resources[] = { { .name = "GPADC", @@ -426,6 +518,12 @@ static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev) axp20x->regmap_cfg = &axp20x_regmap_config; axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip; break; + case AXP221_ID: + axp20x->nr_cells = ARRAY_SIZE(axp22x_cells); + axp20x->cells = axp22x_cells; + axp20x->regmap_cfg = &axp22x_regmap_config; + axp20x->regmap_irq_chip = &axp22x_regmap_irq_chip; + break; case AXP288_ID: axp20x->cells = axp288_cells; axp20x->nr_cells = ARRAY_SIZE(axp288_cells); -- cgit v1.2.3 From 6d4fa89dcd85e2427da83319ce75e5df5febcc96 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 10 Apr 2015 12:09:06 +0800 Subject: mfd: axp20x: Enable AXP22X regulators Now that the axp20x-regulators driver supports different variants of the AXP family, we can enable regulator support for AXP22X without the risk of incorrectly configuring regulators. Signed-off-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index cfbb7d7aead6..6df91556faf3 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -370,6 +370,8 @@ static struct mfd_cell axp22x_cells[] = { .name = "axp20x-pek", .num_resources = ARRAY_SIZE(axp22x_pek_resources), .resources = axp22x_pek_resources, + }, { + .name = "axp20x-regulator", }, }; -- cgit v1.2.3 From e7b707f96820161820a864d2ee81740a14da6b93 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Wed, 20 May 2015 11:31:27 +0200 Subject: mfd: cros_ec: Remove parent field Parent and device were pointing to the same device structure. Parent is unused, removed. Signed-off-by: Gwendal Grignou Tested-by: Stephen Barber Tested-by: Heiko Stuebner Tested-by: Gwendal Grignou Reviewed-by: Puthikorn Voravootivat Signed-off-by: Javier Martinez Canillas Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_i2c.c | 1 - drivers/mfd/cros_ec_spi.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index c0c30f4f946f..82b4d6148698 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -145,7 +145,6 @@ static int cros_ec_i2c_probe(struct i2c_client *client, ec_dev->cmd_xfer = cros_ec_cmd_xfer_i2c; ec_dev->ec_name = client->name; ec_dev->phys_name = client->adapter->name; - ec_dev->parent = &client->dev; err = cros_ec_register(ec_dev); if (err) { diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index bf6e08e8013e..27bd52e5e8b7 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -363,7 +363,6 @@ static int cros_ec_spi_probe(struct spi_device *spi) ec_dev->cmd_xfer = cros_ec_cmd_xfer_spi; ec_dev->ec_name = ec_spi->spi->modalias; ec_dev->phys_name = dev_name(&ec_spi->spi->dev); - ec_dev->parent = &ec_spi->spi->dev; ec_dev->din_size = EC_MSG_BYTES + EC_MSG_PREAMBLE_COUNT; ec_dev->dout_size = EC_MSG_BYTES; -- cgit v1.2.3 From bb03ffb96c72418d06e75c7b74ea62e04c78d322 Mon Sep 17 00:00:00 2001 From: Todd Broch Date: Wed, 20 May 2015 11:31:28 +0200 Subject: mfd: cros_ec: Instantiate sub-devices from device tree If the EC device tree node has sub-nodes, try to instantiate them as MFD sub-devices. We can configure the EC features provided by the board. Signed-off-by: Todd Broch Signed-off-by: Javier Martinez Canillas Reviewed-by: Gwendal Grignou Tested-by: Heiko Stuebner Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index c4aecc6f8373..1574a9352a6d 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -17,6 +17,7 @@ * battery charging and regulator control, firmware update. */ +#include #include #include #include @@ -108,19 +109,9 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, EXPORT_SYMBOL(cros_ec_cmd_xfer); static const struct mfd_cell cros_devs[] = { - { - .name = "cros-ec-keyb", - .id = 1, - .of_compatible = "google,cros-ec-keyb", - }, - { - .name = "cros-ec-i2c-tunnel", - .id = 2, - .of_compatible = "google,cros-ec-i2c-tunnel", - }, { .name = "cros-ec-ctl", - .id = 3, + .id = PLATFORM_DEVID_AUTO, }, }; @@ -150,6 +141,15 @@ int cros_ec_register(struct cros_ec_device *ec_dev) return err; } + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + err = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (err) { + mfd_remove_devices(dev); + dev_err(dev, "Failed to register sub-devices\n"); + return err; + } + } + dev_info(dev, "Chrome EC device registered\n"); return 0; -- cgit v1.2.3 From a841178445bb72a3d566b4e6ab9d19e9b002eb47 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 9 Jun 2015 13:04:42 +0200 Subject: mfd: cros_ec: Use a zero-length array for command data Commit 1b84f2a4cd4a ("mfd: cros_ec: Use fixed size arrays to transfer data with the EC") modified the struct cros_ec_command fields to not use pointers for the input and output buffers and use fixed length arrays instead. This change was made because the cros_ec ioctl API uses that struct cros_ec_command to allow user-space to send commands to the EC and to get data from the EC. So using pointers made the API not 64-bit safe. Unfortunately this approach was not flexible enough for all the use-cases since there may be a need to send larger commands on newer versions of the EC command protocol. So to avoid to choose a constant length that it may be too big for most commands and thus wasting memory and CPU cycles on copy from and to user-space or having a size that is too small for some big commands, use a zero-length array that is both 64-bit safe and flexible. The same buffer is used for both output and input data so the maximum of these values should be used to allocate it. Suggested-by: Gwendal Grignou Signed-off-by: Javier Martinez Canillas Tested-by: Heiko Stuebner Acked-by: Lee Jones Acked-by: Olof Johansson Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 28 ++++++++++++++++++++-------- drivers/mfd/cros_ec_i2c.c | 4 ++-- drivers/mfd/cros_ec_spi.c | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 1574a9352a6d..4a0f6dfcd376 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -41,7 +41,7 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, out[2] = msg->outsize; csum = out[0] + out[1] + out[2]; for (i = 0; i < msg->outsize; i++) - csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->outdata[i]; + csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i]; out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff); return EC_MSG_TX_PROTO_BYTES + msg->outsize; @@ -75,11 +75,20 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, ret = ec_dev->cmd_xfer(ec_dev, msg); if (msg->result == EC_RES_IN_PROGRESS) { int i; - struct cros_ec_command status_msg = { }; + struct cros_ec_command *status_msg; struct ec_response_get_comms_status *status; - status_msg.command = EC_CMD_GET_COMMS_STATUS; - status_msg.insize = sizeof(*status); + status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status), + GFP_KERNEL); + if (!status_msg) { + ret = -ENOMEM; + goto exit; + } + + status_msg->version = 0; + status_msg->command = EC_CMD_GET_COMMS_STATUS; + status_msg->insize = sizeof(*status); + status_msg->outsize = 0; /* * Query the EC's status until it's no longer busy or @@ -88,20 +97,23 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, for (i = 0; i < EC_COMMAND_RETRIES; i++) { usleep_range(10000, 11000); - ret = ec_dev->cmd_xfer(ec_dev, &status_msg); + ret = ec_dev->cmd_xfer(ec_dev, status_msg); if (ret < 0) break; - msg->result = status_msg.result; - if (status_msg.result != EC_RES_SUCCESS) + msg->result = status_msg->result; + if (status_msg->result != EC_RES_SUCCESS) break; status = (struct ec_response_get_comms_status *) - status_msg.indata; + status_msg->data; if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) break; } + + kfree(status_msg); } +exit: mutex_unlock(&ec_dev->lock); return ret; diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index 82b4d6148698..fbf7819f5de5 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -76,7 +76,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, /* copy message payload and compute checksum */ sum = out_buf[0] + out_buf[1] + out_buf[2]; for (i = 0; i < msg->outsize; i++) { - out_buf[3 + i] = msg->outdata[i]; + out_buf[3 + i] = msg->data[i]; sum += out_buf[3 + i]; } out_buf[3 + msg->outsize] = sum; @@ -109,7 +109,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, /* copy response packet payload and compute checksum */ sum = in_buf[0] + in_buf[1]; for (i = 0; i < len; i++) { - msg->indata[i] = in_buf[2 + i]; + msg->data[i] = in_buf[2 + i]; sum += in_buf[2 + i]; } dev_dbg(ec_dev->dev, "packet: %*ph, sum = %02x\n", diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index 27bd52e5e8b7..573730fec947 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -299,7 +299,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, for (i = 0; i < len; i++) { sum += ptr[i + 2]; if (ec_msg->insize) - ec_msg->indata[i] = ptr[i + 2]; + ec_msg->data[i] = ptr[i + 2]; } sum &= 0xff; -- cgit v1.2.3 From 062476f24aa7cf714169342cc50626fd9bbb93da Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 9 Jun 2015 13:04:44 +0200 Subject: mfd: cros_ec: Move protocol helpers out of the MFD driver The MFD driver should only have the logic to instantiate its child devices and setup any shared resources that will be used by the subdevices drivers. The cros_ec MFD is more complex than expected since it also has helpers to communicate with the EC. So the driver will only get more bigger as other protocols are supported in the future. So move the communication protocol helpers to its own driver as drivers/platform/chrome/cros_ec_proto.c. Suggested-by: Lee Jones Signed-off-by: Javier Martinez Canillas Tested-by: Heiko Stuebner Acked-by: Lee Jones Acked-by: Olof Johansson Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 6 ++-- drivers/mfd/cros_ec.c | 96 --------------------------------------------------- 2 files changed, 4 insertions(+), 98 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d5ad04dad081..cf3b86d441f7 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -94,6 +94,8 @@ config MFD_AXP20X config MFD_CROS_EC tristate "ChromeOS Embedded Controller" select MFD_CORE + select CHROME_PLATFORMS + select CROS_EC_PROTO help If you say Y here you get support for the ChromeOS Embedded Controller (EC) providing keyboard, battery and power services. @@ -102,7 +104,7 @@ config MFD_CROS_EC config MFD_CROS_EC_I2C tristate "ChromeOS Embedded Controller (I2C)" - depends on MFD_CROS_EC && I2C + depends on MFD_CROS_EC && CROS_EC_PROTO && I2C help If you say Y here, you get support for talking to the ChromeOS @@ -112,7 +114,7 @@ config MFD_CROS_EC_I2C config MFD_CROS_EC_SPI tristate "ChromeOS Embedded Controller (SPI)" - depends on MFD_CROS_EC && SPI && OF + depends on MFD_CROS_EC && CROS_EC_PROTO && SPI && OF ---help--- If you say Y here, you get support for talking to the ChromeOS EC diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 4a0f6dfcd376..d857f6a2b57b 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -23,102 +23,6 @@ #include #include #include -#include -#include - -#define EC_COMMAND_RETRIES 50 - -int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, - struct cros_ec_command *msg) -{ - uint8_t *out; - int csum, i; - - BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE); - out = ec_dev->dout; - out[0] = EC_CMD_VERSION0 + msg->version; - out[1] = msg->command; - out[2] = msg->outsize; - csum = out[0] + out[1] + out[2]; - for (i = 0; i < msg->outsize; i++) - csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i]; - out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff); - - return EC_MSG_TX_PROTO_BYTES + msg->outsize; -} -EXPORT_SYMBOL(cros_ec_prepare_tx); - -int cros_ec_check_result(struct cros_ec_device *ec_dev, - struct cros_ec_command *msg) -{ - switch (msg->result) { - case EC_RES_SUCCESS: - return 0; - case EC_RES_IN_PROGRESS: - dev_dbg(ec_dev->dev, "command 0x%02x in progress\n", - msg->command); - return -EAGAIN; - default: - dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n", - msg->command, msg->result); - return 0; - } -} -EXPORT_SYMBOL(cros_ec_check_result); - -int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, - struct cros_ec_command *msg) -{ - int ret; - - mutex_lock(&ec_dev->lock); - ret = ec_dev->cmd_xfer(ec_dev, msg); - if (msg->result == EC_RES_IN_PROGRESS) { - int i; - struct cros_ec_command *status_msg; - struct ec_response_get_comms_status *status; - - status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status), - GFP_KERNEL); - if (!status_msg) { - ret = -ENOMEM; - goto exit; - } - - status_msg->version = 0; - status_msg->command = EC_CMD_GET_COMMS_STATUS; - status_msg->insize = sizeof(*status); - status_msg->outsize = 0; - - /* - * Query the EC's status until it's no longer busy or - * we encounter an error. - */ - for (i = 0; i < EC_COMMAND_RETRIES; i++) { - usleep_range(10000, 11000); - - ret = ec_dev->cmd_xfer(ec_dev, status_msg); - if (ret < 0) - break; - - msg->result = status_msg->result; - if (status_msg->result != EC_RES_SUCCESS) - break; - - status = (struct ec_response_get_comms_status *) - status_msg->data; - if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) - break; - } - - kfree(status_msg); - } -exit: - mutex_unlock(&ec_dev->lock); - - return ret; -} -EXPORT_SYMBOL(cros_ec_cmd_xfer); static const struct mfd_cell cros_devs[] = { { -- cgit v1.2.3 From 2c7589af3c4dee844e6a4174f2aa8996cf837604 Mon Sep 17 00:00:00 2001 From: Stephen Barber Date: Tue, 9 Jun 2015 13:04:45 +0200 Subject: mfd: cros_ec: add proto v3 skeleton Add support in cros_ec.c to handle EC host command protocol v3. For v3+, probe for maximum shared protocol version and max request, response, and passthrough sizes. For now, this will always fall back to v2, since there is no bus-specific code for handling proto v3 packets. Signed-off-by: Stephen Barber Signed-off-by: Javier Martinez Canillas Reviewed-by: Gwendal Grignou Tested-by: Gwendal Grignou Tested-by: Heiko Stuebner Acked-by: Lee Jones Acked-by: Olof Johansson Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 23 +++++++++++++---------- drivers/mfd/cros_ec_i2c.c | 4 ++++ drivers/mfd/cros_ec_spi.c | 7 +++++-- 3 files changed, 22 insertions(+), 12 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index d857f6a2b57b..08d82bfc5268 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -36,19 +36,22 @@ int cros_ec_register(struct cros_ec_device *ec_dev) struct device *dev = ec_dev->dev; int err = 0; - if (ec_dev->din_size) { - ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); - if (!ec_dev->din) - return -ENOMEM; - } - if (ec_dev->dout_size) { - ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); - if (!ec_dev->dout) - return -ENOMEM; - } + ec_dev->max_request = sizeof(struct ec_params_hello); + ec_dev->max_response = sizeof(struct ec_response_get_protocol_info); + ec_dev->max_passthru = 0; + + ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); + if (!ec_dev->din) + return -ENOMEM; + + ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); + if (!ec_dev->dout) + return -ENOMEM; mutex_init(&ec_dev->lock); + cros_ec_query_all(ec_dev); + err = mfd_add_devices(dev, 0, cros_devs, ARRAY_SIZE(cros_devs), NULL, ec_dev->irq, NULL); diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index fbf7819f5de5..b400bfa2772a 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -143,8 +143,12 @@ static int cros_ec_i2c_probe(struct i2c_client *client, ec_dev->priv = client; ec_dev->irq = client->irq; ec_dev->cmd_xfer = cros_ec_cmd_xfer_i2c; + ec_dev->pkt_xfer = NULL; ec_dev->ec_name = client->name; ec_dev->phys_name = client->adapter->name; + ec_dev->din_size = sizeof(struct ec_host_response) + + sizeof(struct ec_response_get_protocol_info); + ec_dev->dout_size = sizeof(struct ec_host_request); err = cros_ec_register(ec_dev); if (err) { diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index 573730fec947..04da2f288ef8 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -361,10 +361,13 @@ static int cros_ec_spi_probe(struct spi_device *spi) ec_dev->priv = ec_spi; ec_dev->irq = spi->irq; ec_dev->cmd_xfer = cros_ec_cmd_xfer_spi; + ec_dev->pkt_xfer = NULL; ec_dev->ec_name = ec_spi->spi->modalias; ec_dev->phys_name = dev_name(&ec_spi->spi->dev); - ec_dev->din_size = EC_MSG_BYTES + EC_MSG_PREAMBLE_COUNT; - ec_dev->dout_size = EC_MSG_BYTES; + ec_dev->din_size = EC_MSG_PREAMBLE_COUNT + + sizeof(struct ec_host_response) + + sizeof(struct ec_response_get_protocol_info); + ec_dev->dout_size = sizeof(struct ec_host_request); err = cros_ec_register(ec_dev); if (err) { -- cgit v1.2.3 From d365407079d33106f76bd486a863de05eb5ae95d Mon Sep 17 00:00:00 2001 From: Stephen Barber Date: Tue, 9 Jun 2015 13:04:46 +0200 Subject: mfd: cros_ec: add bus-specific proto v3 code Add proto v3 support to the SPI, I2C, and LPC. Signed-off-by: Stephen Barber Signed-off-by: Javier Martinez Canillas Tested-by: Heiko Stuebner Reviewed-by: Gwendal Grignou Tested-by: Gwendal Grignou Acked-by: Lee Jones Acked-by: Olof Johansson Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_i2c.c | 166 +++++++++++++++++++- drivers/mfd/cros_ec_spi.c | 382 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 491 insertions(+), 57 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index b400bfa2772a..22e8a4ae1711 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -13,6 +13,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -22,6 +23,32 @@ #include #include +/** + * Request format for protocol v3 + * byte 0 0xda (EC_COMMAND_PROTOCOL_3) + * byte 1-8 struct ec_host_request + * byte 10- response data + */ +struct ec_host_request_i2c { + /* Always 0xda to backward compatible with v2 struct */ + uint8_t command_protocol; + struct ec_host_request ec_request; +} __packed; + + +/* + * Response format for protocol v3 + * byte 0 result code + * byte 1 packet_length + * byte 2-9 struct ec_host_response + * byte 10- response data + */ +struct ec_host_response_i2c { + uint8_t result; + uint8_t packet_length; + struct ec_host_response ec_response; +} __packed; + static inline struct cros_ec_device *to_ec_dev(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -29,6 +56,134 @@ static inline struct cros_ec_device *to_ec_dev(struct device *dev) return i2c_get_clientdata(client); } +static int cros_ec_pkt_xfer_i2c(struct cros_ec_device *ec_dev, + struct cros_ec_command *msg) +{ + struct i2c_client *client = ec_dev->priv; + int ret = -ENOMEM; + int i; + int packet_len; + u8 *out_buf = NULL; + u8 *in_buf = NULL; + u8 sum; + struct i2c_msg i2c_msg[2]; + struct ec_host_response *ec_response; + struct ec_host_request_i2c *ec_request_i2c; + struct ec_host_response_i2c *ec_response_i2c; + int request_header_size = sizeof(struct ec_host_request_i2c); + int response_header_size = sizeof(struct ec_host_response_i2c); + + i2c_msg[0].addr = client->addr; + i2c_msg[0].flags = 0; + i2c_msg[1].addr = client->addr; + i2c_msg[1].flags = I2C_M_RD; + + packet_len = msg->insize + response_header_size; + BUG_ON(packet_len > ec_dev->din_size); + in_buf = ec_dev->din; + i2c_msg[1].len = packet_len; + i2c_msg[1].buf = (char *) in_buf; + + packet_len = msg->outsize + request_header_size; + BUG_ON(packet_len > ec_dev->dout_size); + out_buf = ec_dev->dout; + i2c_msg[0].len = packet_len; + i2c_msg[0].buf = (char *) out_buf; + + /* create request data */ + ec_request_i2c = (struct ec_host_request_i2c *) out_buf; + ec_request_i2c->command_protocol = EC_COMMAND_PROTOCOL_3; + + ec_dev->dout++; + ret = cros_ec_prepare_tx(ec_dev, msg); + ec_dev->dout--; + + /* send command to EC and read answer */ + ret = i2c_transfer(client->adapter, i2c_msg, 2); + if (ret < 0) { + dev_dbg(ec_dev->dev, "i2c transfer failed: %d\n", ret); + goto done; + } else if (ret != 2) { + dev_err(ec_dev->dev, "failed to get response: %d\n", ret); + ret = -EIO; + goto done; + } + + ec_response_i2c = (struct ec_host_response_i2c *) in_buf; + msg->result = ec_response_i2c->result; + ec_response = &ec_response_i2c->ec_response; + + switch (msg->result) { + case EC_RES_SUCCESS: + break; + case EC_RES_IN_PROGRESS: + ret = -EAGAIN; + dev_dbg(ec_dev->dev, "command 0x%02x in progress\n", + msg->command); + goto done; + + default: + dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n", + msg->command, msg->result); + /* + * When we send v3 request to v2 ec, ec won't recognize the + * 0xda (EC_COMMAND_PROTOCOL_3) and will return with status + * EC_RES_INVALID_COMMAND with zero data length. + * + * In case of invalid command for v3 protocol the data length + * will be at least sizeof(struct ec_host_response) + */ + if (ec_response_i2c->result == EC_RES_INVALID_COMMAND && + ec_response_i2c->packet_length == 0) { + ret = -EPROTONOSUPPORT; + goto done; + } + } + + if (ec_response_i2c->packet_length < sizeof(struct ec_host_response)) { + dev_err(ec_dev->dev, + "response of %u bytes too short; not a full header\n", + ec_response_i2c->packet_length); + ret = -EBADMSG; + goto done; + } + + if (msg->insize < ec_response->data_len) { + dev_err(ec_dev->dev, + "response data size is too large: expected %u, got %u\n", + msg->insize, + ec_response->data_len); + ret = -EMSGSIZE; + goto done; + } + + /* copy response packet payload and compute checksum */ + sum = 0; + for (i = 0; i < sizeof(struct ec_host_response); i++) + sum += ((u8 *)ec_response)[i]; + + memcpy(msg->data, + in_buf + response_header_size, + ec_response->data_len); + for (i = 0; i < ec_response->data_len; i++) + sum += msg->data[i]; + + /* All bytes should sum to zero */ + if (sum) { + dev_err(ec_dev->dev, "bad packet checksum\n"); + ret = -EBADMSG; + goto done; + } + + ret = ec_response->data_len; + +done: + if (msg->command == EC_CMD_REBOOT_EC) + msleep(EC_REBOOT_DELAY_MS); + + return ret; +} + static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) { @@ -121,9 +276,12 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, } ret = len; - done: +done: kfree(in_buf); kfree(out_buf); + if (msg->command == EC_CMD_REBOOT_EC) + msleep(EC_REBOOT_DELAY_MS); + return ret; } @@ -143,12 +301,12 @@ static int cros_ec_i2c_probe(struct i2c_client *client, ec_dev->priv = client; ec_dev->irq = client->irq; ec_dev->cmd_xfer = cros_ec_cmd_xfer_i2c; - ec_dev->pkt_xfer = NULL; + ec_dev->pkt_xfer = cros_ec_pkt_xfer_i2c; ec_dev->ec_name = client->name; ec_dev->phys_name = client->adapter->name; - ec_dev->din_size = sizeof(struct ec_host_response) + + ec_dev->din_size = sizeof(struct ec_host_response_i2c) + sizeof(struct ec_response_get_protocol_info); - ec_dev->dout_size = sizeof(struct ec_host_request); + ec_dev->dout_size = sizeof(struct ec_host_request_i2c); err = cros_ec_register(ec_dev); if (err) { diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index 04da2f288ef8..4e6f2f6b1095 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -65,12 +65,6 @@ */ #define EC_SPI_RECOVERY_TIME_NS (200 * 1000) -/* - * The EC is unresponsive for a time after a reboot command. Add a - * simple delay to make sure that the bus stays locked. - */ -#define EC_REBOOT_DELAY_MS 50 - /** * struct cros_ec_spi - information about a SPI-connected EC * @@ -87,7 +81,7 @@ struct cros_ec_spi { }; static void debug_packet(struct device *dev, const char *name, u8 *ptr, - int len) + int len) { #ifdef DEBUG int i; @@ -100,6 +94,172 @@ static void debug_packet(struct device *dev, const char *name, u8 *ptr, #endif } +static int terminate_request(struct cros_ec_device *ec_dev) +{ + struct cros_ec_spi *ec_spi = ec_dev->priv; + struct spi_message msg; + struct spi_transfer trans; + int ret; + + /* + * Turn off CS, possibly adding a delay to ensure the rising edge + * doesn't come too soon after the end of the data. + */ + spi_message_init(&msg); + memset(&trans, 0, sizeof(trans)); + trans.delay_usecs = ec_spi->end_of_msg_delay; + spi_message_add_tail(&trans, &msg); + + ret = spi_sync(ec_spi->spi, &msg); + + /* Reset end-of-response timer */ + ec_spi->last_transfer_ns = ktime_get_ns(); + if (ret < 0) { + dev_err(ec_dev->dev, + "cs-deassert spi transfer failed: %d\n", + ret); + } + + return ret; +} + +/** + * receive_n_bytes - receive n bytes from the EC. + * + * Assumes buf is a pointer into the ec_dev->din buffer + */ +static int receive_n_bytes(struct cros_ec_device *ec_dev, u8 *buf, int n) +{ + struct cros_ec_spi *ec_spi = ec_dev->priv; + struct spi_transfer trans; + struct spi_message msg; + int ret; + + BUG_ON(buf - ec_dev->din + n > ec_dev->din_size); + + memset(&trans, 0, sizeof(trans)); + trans.cs_change = 1; + trans.rx_buf = buf; + trans.len = n; + + spi_message_init(&msg); + spi_message_add_tail(&trans, &msg); + ret = spi_sync(ec_spi->spi, &msg); + if (ret < 0) + dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); + + return ret; +} + +/** + * cros_ec_spi_receive_packet - Receive a packet from the EC. + * + * This function has two phases: reading the preamble bytes (since if we read + * data from the EC before it is ready to send, we just get preamble) and + * reading the actual message. + * + * The received data is placed into ec_dev->din. + * + * @ec_dev: ChromeOS EC device + * @need_len: Number of message bytes we need to read + */ +static int cros_ec_spi_receive_packet(struct cros_ec_device *ec_dev, + int need_len) +{ + struct ec_host_response *response; + u8 *ptr, *end; + int ret; + unsigned long deadline; + int todo; + + BUG_ON(EC_MSG_PREAMBLE_COUNT > ec_dev->din_size); + + /* Receive data until we see the header byte */ + deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS); + while (true) { + unsigned long start_jiffies = jiffies; + + ret = receive_n_bytes(ec_dev, + ec_dev->din, + EC_MSG_PREAMBLE_COUNT); + if (ret < 0) + return ret; + + ptr = ec_dev->din; + for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) { + if (*ptr == EC_SPI_FRAME_START) { + dev_dbg(ec_dev->dev, "msg found at %zd\n", + ptr - ec_dev->din); + break; + } + } + if (ptr != end) + break; + + /* + * Use the time at the start of the loop as a timeout. This + * gives us one last shot at getting the transfer and is useful + * in case we got context switched out for a while. + */ + if (time_after(start_jiffies, deadline)) { + dev_warn(ec_dev->dev, "EC failed to respond in time\n"); + return -ETIMEDOUT; + } + } + + /* + * ptr now points to the header byte. Copy any valid data to the + * start of our buffer + */ + todo = end - ++ptr; + BUG_ON(todo < 0 || todo > ec_dev->din_size); + todo = min(todo, need_len); + memmove(ec_dev->din, ptr, todo); + ptr = ec_dev->din + todo; + dev_dbg(ec_dev->dev, "need %d, got %d bytes from preamble\n", + need_len, todo); + need_len -= todo; + + /* If the entire response struct wasn't read, get the rest of it. */ + if (todo < sizeof(*response)) { + ret = receive_n_bytes(ec_dev, ptr, sizeof(*response) - todo); + if (ret < 0) + return -EBADMSG; + ptr += (sizeof(*response) - todo); + todo = sizeof(*response); + } + + response = (struct ec_host_response *)ec_dev->din; + + /* Abort if data_len is too large. */ + if (response->data_len > ec_dev->din_size) + return -EMSGSIZE; + + /* Receive data until we have it all */ + while (need_len > 0) { + /* + * We can't support transfers larger than the SPI FIFO size + * unless we have DMA. We don't have DMA on the ISP SPI ports + * for Exynos. We need a way of asking SPI driver for + * maximum-supported transfer size. + */ + todo = min(need_len, 256); + dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n", + todo, need_len, ptr - ec_dev->din); + + ret = receive_n_bytes(ec_dev, ptr, todo); + if (ret < 0) + return ret; + + ptr += todo; + need_len -= todo; + } + + dev_dbg(ec_dev->dev, "loop done, ptr=%zd\n", ptr - ec_dev->din); + + return 0; +} + /** * cros_ec_spi_receive_response - Receive a response from the EC. * @@ -115,34 +275,27 @@ static void debug_packet(struct device *dev, const char *name, u8 *ptr, static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, int need_len) { - struct cros_ec_spi *ec_spi = ec_dev->priv; - struct spi_transfer trans; - struct spi_message msg; u8 *ptr, *end; int ret; unsigned long deadline; int todo; + BUG_ON(EC_MSG_PREAMBLE_COUNT > ec_dev->din_size); + /* Receive data until we see the header byte */ deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS); while (true) { unsigned long start_jiffies = jiffies; - memset(&trans, 0, sizeof(trans)); - trans.cs_change = 1; - trans.rx_buf = ptr = ec_dev->din; - trans.len = EC_MSG_PREAMBLE_COUNT; - - spi_message_init(&msg); - spi_message_add_tail(&trans, &msg); - ret = spi_sync(ec_spi->spi, &msg); - if (ret < 0) { - dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); + ret = receive_n_bytes(ec_dev, + ec_dev->din, + EC_MSG_PREAMBLE_COUNT); + if (ret < 0) return ret; - } + ptr = ec_dev->din; for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) { - if (*ptr == EC_MSG_HEADER) { + if (*ptr == EC_SPI_FRAME_START) { dev_dbg(ec_dev->dev, "msg found at %zd\n", ptr - ec_dev->din); break; @@ -187,21 +340,9 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n", todo, need_len, ptr - ec_dev->din); - memset(&trans, 0, sizeof(trans)); - trans.cs_change = 1; - trans.rx_buf = ptr; - trans.len = todo; - spi_message_init(&msg); - spi_message_add_tail(&trans, &msg); - - /* send command to EC and read answer */ - BUG_ON((u8 *)trans.rx_buf - ec_dev->din + todo > - ec_dev->din_size); - ret = spi_sync(ec_spi->spi, &msg); - if (ret < 0) { - dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); + ret = receive_n_bytes(ec_dev, ptr, todo); + if (ret < 0) return ret; - } debug_packet(ec_dev->dev, "interim", ptr, todo); ptr += todo; @@ -213,6 +354,128 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, return 0; } +/** + * cros_ec_pkt_xfer_spi - Transfer a packet over SPI and receive the reply + * + * @ec_dev: ChromeOS EC device + * @ec_msg: Message to transfer + */ +static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev, + struct cros_ec_command *ec_msg) +{ + struct ec_host_request *request; + struct ec_host_response *response; + struct cros_ec_spi *ec_spi = ec_dev->priv; + struct spi_transfer trans; + struct spi_message msg; + int i, len; + u8 *ptr; + u8 *rx_buf; + u8 sum; + int ret = 0, final_ret; + + len = cros_ec_prepare_tx(ec_dev, ec_msg); + request = (struct ec_host_request *)ec_dev->dout; + dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); + + /* If it's too soon to do another transaction, wait */ + if (ec_spi->last_transfer_ns) { + unsigned long delay; /* The delay completed so far */ + + delay = ktime_get_ns() - ec_spi->last_transfer_ns; + if (delay < EC_SPI_RECOVERY_TIME_NS) + ndelay(EC_SPI_RECOVERY_TIME_NS - delay); + } + + rx_buf = kzalloc(len, GFP_KERNEL); + if (!rx_buf) { + ret = -ENOMEM; + goto exit; + } + + /* Transmit phase - send our message */ + memset(&trans, 0, sizeof(trans)); + trans.tx_buf = ec_dev->dout; + trans.rx_buf = rx_buf; + trans.len = len; + trans.cs_change = 1; + spi_message_init(&msg); + spi_message_add_tail(&trans, &msg); + ret = spi_sync(ec_spi->spi, &msg); + + /* Get the response */ + if (!ret) { + /* Verify that EC can process command */ + for (i = 0; i < len; i++) { + switch (rx_buf[i]) { + case EC_SPI_PAST_END: + case EC_SPI_RX_BAD_DATA: + case EC_SPI_NOT_READY: + ret = -EAGAIN; + ec_msg->result = EC_RES_IN_PROGRESS; + default: + break; + } + if (ret) + break; + } + if (!ret) + ret = cros_ec_spi_receive_packet(ec_dev, + ec_msg->insize + sizeof(*response)); + } else { + dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); + } + + final_ret = terminate_request(ec_dev); + if (!ret) + ret = final_ret; + if (ret < 0) + goto exit; + + ptr = ec_dev->din; + + /* check response error code */ + response = (struct ec_host_response *)ptr; + ec_msg->result = response->result; + + ret = cros_ec_check_result(ec_dev, ec_msg); + if (ret) + goto exit; + + len = response->data_len; + sum = 0; + if (len > ec_msg->insize) { + dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)", + len, ec_msg->insize); + ret = -EMSGSIZE; + goto exit; + } + + for (i = 0; i < sizeof(*response); i++) + sum += ptr[i]; + + /* copy response packet payload and compute checksum */ + memcpy(ec_msg->data, ptr + sizeof(*response), len); + for (i = 0; i < len; i++) + sum += ec_msg->data[i]; + + if (sum) { + dev_err(ec_dev->dev, + "bad packet checksum, calculated %x\n", + sum); + ret = -EBADMSG; + goto exit; + } + + ret = len; +exit: + kfree(rx_buf); + if (ec_msg->command == EC_CMD_REBOOT_EC) + msleep(EC_REBOOT_DELAY_MS); + + return ret; +} + /** * cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply * @@ -227,6 +490,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, struct spi_message msg; int i, len; u8 *ptr; + u8 *rx_buf; int sum; int ret = 0, final_ret; @@ -242,10 +506,17 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, ndelay(EC_SPI_RECOVERY_TIME_NS - delay); } + rx_buf = kzalloc(len, GFP_KERNEL); + if (!rx_buf) { + ret = -ENOMEM; + goto exit; + } + /* Transmit phase - send our message */ debug_packet(ec_dev->dev, "out", ec_dev->dout, len); memset(&trans, 0, sizeof(trans)); trans.tx_buf = ec_dev->dout; + trans.rx_buf = rx_buf; trans.len = len; trans.cs_change = 1; spi_message_init(&msg); @@ -254,29 +525,32 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, /* Get the response */ if (!ret) { - ret = cros_ec_spi_receive_response(ec_dev, - ec_msg->insize + EC_MSG_TX_PROTO_BYTES); + /* Verify that EC can process command */ + for (i = 0; i < len; i++) { + switch (rx_buf[i]) { + case EC_SPI_PAST_END: + case EC_SPI_RX_BAD_DATA: + case EC_SPI_NOT_READY: + ret = -EAGAIN; + ec_msg->result = EC_RES_IN_PROGRESS; + default: + break; + } + if (ret) + break; + } + if (!ret) + ret = cros_ec_spi_receive_response(ec_dev, + ec_msg->insize + EC_MSG_TX_PROTO_BYTES); } else { dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); } - /* - * Turn off CS, possibly adding a delay to ensure the rising edge - * doesn't come too soon after the end of the data. - */ - spi_message_init(&msg); - memset(&trans, 0, sizeof(trans)); - trans.delay_usecs = ec_spi->end_of_msg_delay; - spi_message_add_tail(&trans, &msg); - - final_ret = spi_sync(ec_spi->spi, &msg); - ec_spi->last_transfer_ns = ktime_get_ns(); + final_ret = terminate_request(ec_dev); if (!ret) ret = final_ret; - if (ret < 0) { - dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); + if (ret < 0) goto exit; - } ptr = ec_dev->din; @@ -315,6 +589,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, ret = len; exit: + kfree(rx_buf); if (ec_msg->command == EC_CMD_REBOOT_EC) msleep(EC_REBOOT_DELAY_MS); @@ -361,7 +636,7 @@ static int cros_ec_spi_probe(struct spi_device *spi) ec_dev->priv = ec_spi; ec_dev->irq = spi->irq; ec_dev->cmd_xfer = cros_ec_cmd_xfer_spi; - ec_dev->pkt_xfer = NULL; + ec_dev->pkt_xfer = cros_ec_pkt_xfer_spi; ec_dev->ec_name = ec_spi->spi->modalias; ec_dev->phys_name = dev_name(&ec_spi->spi->dev); ec_dev->din_size = EC_MSG_PREAMBLE_COUNT + @@ -369,6 +644,7 @@ static int cros_ec_spi_probe(struct spi_device *spi) sizeof(struct ec_response_get_protocol_info); ec_dev->dout_size = sizeof(struct ec_host_request); + err = cros_ec_register(ec_dev); if (err) { dev_err(dev, "cannot register EC\n"); -- cgit v1.2.3 From 57b33ff077beebb68481a2b6b8e5fe58ca998169 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 9 Jun 2015 13:04:47 +0200 Subject: mfd: cros_ec: Support multiple EC in a system Chromebooks can have more than one Embedded Controller so the cros_ec device id has to be incremented for each EC registered. Add a new structure to represent multiple EC as different char devices (e.g: /dev/cros_ec, /dev/cros_pd). It connects to cros_ec_device and allows sysfs inferface for cros_pd. Also reduce number of allocated objects, make chromeos sysfs class object a static and add refcounting to prevent object deletion while command is in progress. Signed-off-by: Gwendal Grignou Reviewed-by: Dmitry Torokhov Signed-off-by: Javier Martinez Canillas Tested-by: Heiko Stuebner Acked-by: Lee Jones Acked-by: Olof Johansson Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 54 ++++++++++++++++++++++++++++++++++++++++------- drivers/mfd/cros_ec_i2c.c | 1 - drivers/mfd/cros_ec_spi.c | 1 - 3 files changed, 46 insertions(+), 10 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 08d82bfc5268..11b1884bce62 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -24,11 +24,29 @@ #include #include -static const struct mfd_cell cros_devs[] = { - { - .name = "cros-ec-ctl", - .id = PLATFORM_DEVID_AUTO, - }, +#define CROS_EC_DEV_EC_INDEX 0 +#define CROS_EC_DEV_PD_INDEX 1 + +struct cros_ec_platform ec_p = { + .ec_name = CROS_EC_DEV_NAME, + .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_EC_INDEX), +}; + +struct cros_ec_platform pd_p = { + .ec_name = CROS_EC_DEV_PD_NAME, + .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX), +}; + +struct mfd_cell ec_cell = { + .name = "cros-ec-ctl", + .platform_data = &ec_p, + .pdata_size = sizeof(ec_p), +}; + +struct mfd_cell ec_pd_cell = { + .name = "cros-ec-ctl", + .platform_data = &pd_p, + .pdata_size = sizeof(pd_p), }; int cros_ec_register(struct cros_ec_device *ec_dev) @@ -52,14 +70,34 @@ int cros_ec_register(struct cros_ec_device *ec_dev) cros_ec_query_all(ec_dev); - err = mfd_add_devices(dev, 0, cros_devs, - ARRAY_SIZE(cros_devs), + err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1, NULL, ec_dev->irq, NULL); if (err) { - dev_err(dev, "failed to add mfd devices\n"); + dev_err(dev, + "Failed to register Embedded Controller subdevice %d\n", + err); return err; } + if (ec_dev->max_passthru) { + /* + * Register a PD device as well on top of this device. + * We make the following assumptions: + * - behind an EC, we have a pd + * - only one device added. + * - the EC is responsive at init time (it is not true for a + * sensor hub. + */ + err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, + &ec_pd_cell, 1, NULL, ec_dev->irq, NULL); + if (err) { + dev_err(dev, + "Failed to register Power Delivery subdevice %d\n", + err); + return err; + } + } + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { err = of_platform_populate(dev->of_node, NULL, NULL, dev); if (err) { diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index 22e8a4ae1711..b9a0963ca5c3 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -302,7 +302,6 @@ static int cros_ec_i2c_probe(struct i2c_client *client, ec_dev->irq = client->irq; ec_dev->cmd_xfer = cros_ec_cmd_xfer_i2c; ec_dev->pkt_xfer = cros_ec_pkt_xfer_i2c; - ec_dev->ec_name = client->name; ec_dev->phys_name = client->adapter->name; ec_dev->din_size = sizeof(struct ec_host_response_i2c) + sizeof(struct ec_response_get_protocol_info); diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index 4e6f2f6b1095..faba03e2f1ef 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -637,7 +637,6 @@ static int cros_ec_spi_probe(struct spi_device *spi) ec_dev->irq = spi->irq; ec_dev->cmd_xfer = cros_ec_cmd_xfer_spi; ec_dev->pkt_xfer = cros_ec_pkt_xfer_spi; - ec_dev->ec_name = ec_spi->spi->modalias; ec_dev->phys_name = dev_name(&ec_spi->spi->dev); ec_dev->din_size = EC_MSG_PREAMBLE_COUNT + sizeof(struct ec_host_response) + -- cgit v1.2.3 From ff4378f4b813d5aa26bbf814a9060638dab1fbbf Mon Sep 17 00:00:00 2001 From: Alexandru M Stan Date: Tue, 9 Jun 2015 13:04:49 +0200 Subject: mfd: cros_ec: spi: Add delay for asserting CS Some ECs need a little time for waking up before they can accept SPI data at a high speed. This is configurable via a DT property "google,cros-ec-spi-pre-delay". This patch makes the cros_ec_spi driver to cause a delay before the beginning of a SPI transaction, to make sure that the EC has already woken up, if the property has been defined in the DTS. Signed-off-by: Alexandru M Stan Reviewed-by: Doug Anderson Signed-off-by: Chris Zhong Signed-off-by: Javier Martinez Canillas Tested-by: Heiko Stuebner Acked-by: Lee Jones Acked-by: Olof Johansson Signed-off-by: Lee Jones --- drivers/mfd/cros_ec_spi.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index faba03e2f1ef..16f228dc243f 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -71,12 +71,15 @@ * @spi: SPI device we are connected to * @last_transfer_ns: time that we last finished a transfer, or 0 if there * if no record + * @start_of_msg_delay: used to set the delay_usecs on the spi_transfer that + * is sent when we want to turn on CS at the start of a transaction. * @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that * is sent when we want to turn off CS at the end of a transaction. */ struct cros_ec_spi { struct spi_device *spi; s64 last_transfer_ns; + unsigned int start_of_msg_delay; unsigned int end_of_msg_delay; }; @@ -366,7 +369,7 @@ static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev, struct ec_host_request *request; struct ec_host_response *response; struct cros_ec_spi *ec_spi = ec_dev->priv; - struct spi_transfer trans; + struct spi_transfer trans, trans_delay; struct spi_message msg; int i, len; u8 *ptr; @@ -393,13 +396,23 @@ static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev, goto exit; } + /* + * Leave a gap between CS assertion and clocking of data to allow the + * EC time to wakeup. + */ + spi_message_init(&msg); + if (ec_spi->start_of_msg_delay) { + memset(&trans_delay, 0, sizeof(trans_delay)); + trans_delay.delay_usecs = ec_spi->start_of_msg_delay; + spi_message_add_tail(&trans_delay, &msg); + } + /* Transmit phase - send our message */ memset(&trans, 0, sizeof(trans)); trans.tx_buf = ec_dev->dout; trans.rx_buf = rx_buf; trans.len = len; trans.cs_change = 1; - spi_message_init(&msg); spi_message_add_tail(&trans, &msg); ret = spi_sync(ec_spi->spi, &msg); @@ -602,6 +615,10 @@ static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev) u32 val; int ret; + ret = of_property_read_u32(np, "google,cros-ec-spi-pre-delay", &val); + if (!ret) + ec_spi->start_of_msg_delay = val; + ret = of_property_read_u32(np, "google,cros-ec-spi-msg-delay", &val); if (!ret) ec_spi->end_of_msg_delay = val; -- cgit v1.2.3 From cf649e00769a401d317352c4fe405169ddf61e1f Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 15 Jun 2015 21:39:39 +0800 Subject: mfd: cros_ec: Staticise some newly introduced structures Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 11b1884bce62..0eee63542038 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -27,23 +27,23 @@ #define CROS_EC_DEV_EC_INDEX 0 #define CROS_EC_DEV_PD_INDEX 1 -struct cros_ec_platform ec_p = { +static struct cros_ec_platform ec_p = { .ec_name = CROS_EC_DEV_NAME, .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_EC_INDEX), }; -struct cros_ec_platform pd_p = { +static struct cros_ec_platform pd_p = { .ec_name = CROS_EC_DEV_PD_NAME, .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX), }; -struct mfd_cell ec_cell = { +static const struct mfd_cell ec_cell = { .name = "cros-ec-ctl", .platform_data = &ec_p, .pdata_size = sizeof(ec_p), }; -struct mfd_cell ec_pd_cell = { +static const struct mfd_cell ec_pd_cell = { .name = "cros-ec-ctl", .platform_data = &pd_p, .pdata_size = sizeof(pd_p), -- cgit v1.2.3 From 7ce7b26f84cfcbcb04f526f56f685a56ccddf355 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Apr 2015 21:54:13 +0900 Subject: mfd: Constify regmap and irq configuration data Constify in various drivers configuration data which is not modified: - regmap_irq_chip, - individual regmap_irq's in array, - regmap_config, - irq_domain_ops, Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/88pm860x-core.c | 2 +- drivers/mfd/ab8500-core.c | 2 +- drivers/mfd/arizona-irq.c | 2 +- drivers/mfd/da9052-irq.c | 4 ++-- drivers/mfd/da9055-core.c | 6 +++--- drivers/mfd/da9063-irq.c | 4 ++-- drivers/mfd/da9150-core.c | 4 ++-- drivers/mfd/db8500-prcmu.c | 2 +- drivers/mfd/intel_soc_pmic_core.h | 2 +- drivers/mfd/intel_soc_pmic_crc.c | 2 +- drivers/mfd/lp8788-irq.c | 2 +- drivers/mfd/max8925-core.c | 2 +- drivers/mfd/max8997-irq.c | 2 +- drivers/mfd/max8998-irq.c | 2 +- drivers/mfd/mt6397-core.c | 2 +- drivers/mfd/stmpe.c | 2 +- drivers/mfd/tc3589x.c | 2 +- drivers/mfd/tps6586x.c | 2 +- drivers/mfd/twl6030-irq.c | 2 +- drivers/mfd/wm831x-irq.c | 2 +- drivers/mfd/wm8994-irq.c | 6 +++--- 21 files changed, 28 insertions(+), 28 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index d2a85cde68da..e03b7f45b8f7 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -566,7 +566,7 @@ static int pm860x_irq_domain_map(struct irq_domain *d, unsigned int virq, return 0; } -static struct irq_domain_ops pm860x_irq_domain_ops = { +static const struct irq_domain_ops pm860x_irq_domain_ops = { .map = pm860x_irq_domain_map, .xlate = irq_domain_xlate_onetwocell, }; diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index c80a2925f8e5..000da72a0ae9 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -574,7 +574,7 @@ static int ab8500_irq_map(struct irq_domain *d, unsigned int virq, return 0; } -static struct irq_domain_ops ab8500_irq_ops = { +static const struct irq_domain_ops ab8500_irq_ops = { .map = ab8500_irq_map, .xlate = irq_domain_xlate_twocell, }; diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index d063b94b94b5..2b9965d53e4e 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -186,7 +186,7 @@ static int arizona_irq_map(struct irq_domain *h, unsigned int virq, return 0; } -static struct irq_domain_ops arizona_domain_ops = { +static const struct irq_domain_ops arizona_domain_ops = { .map = arizona_irq_map, .xlate = irq_domain_xlate_twocell, }; diff --git a/drivers/mfd/da9052-irq.c b/drivers/mfd/da9052-irq.c index e65ca194fa98..f4cb4613140b 100644 --- a/drivers/mfd/da9052-irq.c +++ b/drivers/mfd/da9052-irq.c @@ -35,7 +35,7 @@ #define DA9052_IRQ_MASK_POS_7 0x40 #define DA9052_IRQ_MASK_POS_8 0x80 -static struct regmap_irq da9052_irqs[] = { +static const struct regmap_irq da9052_irqs[] = { [DA9052_IRQ_DCIN] = { .reg_offset = 0, .mask = DA9052_IRQ_MASK_POS_1, @@ -166,7 +166,7 @@ static struct regmap_irq da9052_irqs[] = { }, }; -static struct regmap_irq_chip da9052_regmap_irq_chip = { +static const struct regmap_irq_chip da9052_regmap_irq_chip = { .name = "da9052_irq", .status_base = DA9052_EVENT_A_REG, .mask_base = DA9052_IRQ_MASK_A_REG, diff --git a/drivers/mfd/da9055-core.c b/drivers/mfd/da9055-core.c index b4d920c1ead1..177e65a12c12 100644 --- a/drivers/mfd/da9055-core.c +++ b/drivers/mfd/da9055-core.c @@ -222,7 +222,7 @@ static bool da9055_register_volatile(struct device *dev, unsigned int reg) } } -static struct regmap_irq da9055_irqs[] = { +static const struct regmap_irq da9055_irqs[] = { [DA9055_IRQ_NONKEY] = { .reg_offset = 0, .mask = DA9055_IRQ_NONKEY_MASK, @@ -245,7 +245,7 @@ static struct regmap_irq da9055_irqs[] = { }, }; -struct regmap_config da9055_regmap_config = { +const struct regmap_config da9055_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -367,7 +367,7 @@ static const struct mfd_cell da9055_devs[] = { }, }; -static struct regmap_irq_chip da9055_regmap_irq_chip = { +static const struct regmap_irq_chip da9055_regmap_irq_chip = { .name = "da9055_irq", .status_base = DA9055_REG_EVENT_A, .mask_base = DA9055_REG_IRQ_MASK_A, diff --git a/drivers/mfd/da9063-irq.c b/drivers/mfd/da9063-irq.c index 822922602ce9..eaf1ec9208b2 100644 --- a/drivers/mfd/da9063-irq.c +++ b/drivers/mfd/da9063-irq.c @@ -34,7 +34,7 @@ struct da9063_irq_data { u8 mask; }; -static struct regmap_irq da9063_irqs[] = { +static const struct regmap_irq da9063_irqs[] = { /* DA9063 event A register */ [DA9063_IRQ_ONKEY] = { .reg_offset = DA9063_REG_EVENT_A_OFFSET, @@ -153,7 +153,7 @@ static struct regmap_irq da9063_irqs[] = { }, }; -static struct regmap_irq_chip da9063_irq_chip = { +static const struct regmap_irq_chip da9063_irq_chip = { .name = "da9063-irq", .irqs = da9063_irqs, .num_irqs = DA9063_NUM_IRQ, diff --git a/drivers/mfd/da9150-core.c b/drivers/mfd/da9150-core.c index 5549817df32e..94b9bbd1a69b 100644 --- a/drivers/mfd/da9150-core.c +++ b/drivers/mfd/da9150-core.c @@ -164,7 +164,7 @@ void da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf) } EXPORT_SYMBOL_GPL(da9150_bulk_write); -static struct regmap_irq da9150_irqs[] = { +static const struct regmap_irq da9150_irqs[] = { [DA9150_IRQ_VBUS] = { .reg_offset = 0, .mask = DA9150_E_VBUS_MASK, @@ -251,7 +251,7 @@ static struct regmap_irq da9150_irqs[] = { }, }; -static struct regmap_irq_chip da9150_regmap_irq_chip = { +static const struct regmap_irq_chip da9150_regmap_irq_chip = { .name = "da9150_irq", .status_base = DA9150_EVENT_E, .mask_base = DA9150_IRQ_MASK_E, diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index cc1a404328c2..8b14740f9fca 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2659,7 +2659,7 @@ static int db8500_irq_map(struct irq_domain *d, unsigned int virq, return 0; } -static struct irq_domain_ops db8500_irq_ops = { +static const struct irq_domain_ops db8500_irq_ops = { .map = db8500_irq_map, .xlate = irq_domain_xlate_twocell, }; diff --git a/drivers/mfd/intel_soc_pmic_core.h b/drivers/mfd/intel_soc_pmic_core.h index 9498d6719847..ff2464bc172f 100644 --- a/drivers/mfd/intel_soc_pmic_core.h +++ b/drivers/mfd/intel_soc_pmic_core.h @@ -24,7 +24,7 @@ struct intel_soc_pmic_config { struct mfd_cell *cell_dev; int n_cell_devs; const struct regmap_config *regmap_config; - struct regmap_irq_chip *irq_chip; + const struct regmap_irq_chip *irq_chip; }; extern struct intel_soc_pmic_config intel_soc_pmic_config_crc; diff --git a/drivers/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c index 4cc1b324e971..7436075e8983 100644 --- a/drivers/mfd/intel_soc_pmic_crc.c +++ b/drivers/mfd/intel_soc_pmic_crc.c @@ -143,7 +143,7 @@ static const struct regmap_irq crystal_cove_irqs[] = { }, }; -static struct regmap_irq_chip crystal_cove_irq_chip = { +static const struct regmap_irq_chip crystal_cove_irq_chip = { .name = "Crystal Cove", .irqs = crystal_cove_irqs, .num_irqs = ARRAY_SIZE(crystal_cove_irqs), diff --git a/drivers/mfd/lp8788-irq.c b/drivers/mfd/lp8788-irq.c index 23982dbf014d..a87f2b548f71 100644 --- a/drivers/mfd/lp8788-irq.c +++ b/drivers/mfd/lp8788-irq.c @@ -151,7 +151,7 @@ static int lp8788_irq_map(struct irq_domain *d, unsigned int virq, return 0; } -static struct irq_domain_ops lp8788_domain_ops = { +static const struct irq_domain_ops lp8788_domain_ops = { .map = lp8788_irq_map, }; diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index 97a787ab3d51..8520bd68c1ff 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -658,7 +658,7 @@ static int max8925_irq_domain_map(struct irq_domain *d, unsigned int virq, return 0; } -static struct irq_domain_ops max8925_irq_domain_ops = { +static const struct irq_domain_ops max8925_irq_domain_ops = { .map = max8925_irq_domain_map, .xlate = irq_domain_xlate_onetwocell, }; diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c index 43fa61413e93..d3025be57f39 100644 --- a/drivers/mfd/max8997-irq.c +++ b/drivers/mfd/max8997-irq.c @@ -303,7 +303,7 @@ static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq, return 0; } -static struct irq_domain_ops max8997_irq_domain_ops = { +static const struct irq_domain_ops max8997_irq_domain_ops = { .map = max8997_irq_domain_map, }; diff --git a/drivers/mfd/max8998-irq.c b/drivers/mfd/max8998-irq.c index c469477eb778..3702056628a8 100644 --- a/drivers/mfd/max8998-irq.c +++ b/drivers/mfd/max8998-irq.c @@ -214,7 +214,7 @@ static int max8998_irq_domain_map(struct irq_domain *d, unsigned int irq, return 0; } -static struct irq_domain_ops max8998_irq_domain_ops = { +static const struct irq_domain_ops max8998_irq_domain_ops = { .map = max8998_irq_domain_map, }; diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index 09bc7804952a..32775ebbce18 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -130,7 +130,7 @@ static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq, return 0; } -static struct irq_domain_ops mt6397_irq_domain_ops = { +static const struct irq_domain_ops mt6397_irq_domain_ops = { .map = mt6397_irq_domain_map, }; diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 2d7fae94c861..18c4d72d1d2a 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -989,7 +989,7 @@ static void stmpe_irq_unmap(struct irq_domain *d, unsigned int virq) irq_set_chip_data(virq, NULL); } -static struct irq_domain_ops stmpe_irq_ops = { +static const struct irq_domain_ops stmpe_irq_ops = { .map = stmpe_irq_map, .unmap = stmpe_irq_unmap, .xlate = irq_domain_xlate_twocell, diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index cf356395c9e9..96d420dfc15d 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -233,7 +233,7 @@ static void tc3589x_irq_unmap(struct irq_domain *d, unsigned int virq) irq_set_chip_data(virq, NULL); } -static struct irq_domain_ops tc3589x_irq_ops = { +static const struct irq_domain_ops tc3589x_irq_ops = { .map = tc3589x_irq_map, .unmap = tc3589x_irq_unmap, .xlate = irq_domain_xlate_onecell, diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 8e1dbc469580..e0a2583916ce 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -311,7 +311,7 @@ static int tps6586x_irq_map(struct irq_domain *h, unsigned int virq, return 0; } -static struct irq_domain_ops tps6586x_domain_ops = { +static const struct irq_domain_ops tps6586x_domain_ops = { .map = tps6586x_irq_map, .xlate = irq_domain_xlate_twocell, }; diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index 2807e1a95663..20fb58179ada 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -376,7 +376,7 @@ static void twl6030_irq_unmap(struct irq_domain *d, unsigned int virq) irq_set_chip_data(virq, NULL); } -static struct irq_domain_ops twl6030_irq_domain_ops = { +static const struct irq_domain_ops twl6030_irq_domain_ops = { .map = twl6030_irq_map, .unmap = twl6030_irq_unmap, .xlate = irq_domain_xlate_onetwocell, diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index 64e512eadf17..3da81263c764 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -564,7 +564,7 @@ static int wm831x_irq_map(struct irq_domain *h, unsigned int virq, return 0; } -static struct irq_domain_ops wm831x_irq_domain_ops = { +static const struct irq_domain_ops wm831x_irq_domain_ops = { .map = wm831x_irq_map, .xlate = irq_domain_xlate_twocell, }; diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c index a14407edbd89..55c380a67686 100644 --- a/drivers/mfd/wm8994-irq.c +++ b/drivers/mfd/wm8994-irq.c @@ -28,7 +28,7 @@ #include -static struct regmap_irq wm8994_irqs[] = { +static const struct regmap_irq wm8994_irqs[] = { [WM8994_IRQ_TEMP_SHUT] = { .reg_offset = 1, .mask = WM8994_TEMP_SHUT_EINT, @@ -128,7 +128,7 @@ static struct regmap_irq wm8994_irqs[] = { }, }; -static struct regmap_irq_chip wm8994_irq_chip = { +static const struct regmap_irq_chip wm8994_irq_chip = { .name = "wm8994", .irqs = wm8994_irqs, .num_irqs = ARRAY_SIZE(wm8994_irqs), @@ -184,7 +184,7 @@ static int wm8994_edge_irq_map(struct irq_domain *h, unsigned int virq, return 0; } -static struct irq_domain_ops wm8994_edge_irq_ops = { +static const struct irq_domain_ops wm8994_edge_irq_ops = { .map = wm8994_edge_irq_map, .xlate = irq_domain_xlate_twocell, }; -- cgit v1.2.3 From b375e9f90ff8dc645c9d6a57f08d59f0db6aaab3 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 22 Apr 2015 14:34:48 +0100 Subject: mfd: arizona: Remove redundant register sync This soft reset used to be located after the register patch had been applied, but has since moved to before the patch is applied. At the new location there is no requirement to do a register sync as no register writes will have happened yet. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 6ca6dfab50eb..aed43a549f77 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -777,8 +777,6 @@ int arizona_dev_init(struct arizona *arizona) /* If we have a /RESET GPIO we'll already be reset */ if (!arizona->pdata.reset) { - regcache_mark_dirty(arizona->regmap); - ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0); if (ret != 0) { dev_err(dev, "Failed to reset device: %d\n", ret); @@ -786,12 +784,6 @@ int arizona_dev_init(struct arizona *arizona) } msleep(1); - - ret = regcache_sync(arizona->regmap); - if (ret != 0) { - dev_err(dev, "Failed to sync device: %d\n", ret); - goto err_reset; - } } /* Ensure device startup is complete */ -- cgit v1.2.3 From 5c188d748216f67c928d67a42f14b5569b6404a5 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 27 Apr 2015 10:18:14 -0700 Subject: mfd: twl4030-power: Fix pmic for boards that need AC charger disabled I noticed the PMIC configuration on 37xx-evm won't actually shut down the voltages during off-idle. Turns out 37xx-evm needs the AC charger state transitions disabled like we are doing for SDP and LDP in the legacy booting case. Let's fix this for device tree based booting by setting up the quirk flag based on the compatible flag. And let's also use the existing define for STARTON_CHG. Note that SDP and EVM do not have the PMIC clken wired to gate the the oscillator while LDP has. Signed-off-by: Tony Lindgren Signed-off-by: Lee Jones --- drivers/mfd/twl4030-power.c | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index f440aed61305..04b539850e72 100644 --- a/drivers/mfd/twl4030-power.c +++ b/drivers/mfd/twl4030-power.c @@ -264,7 +264,9 @@ out: return err; } -static int twl4030_config_wakeup12_sequence(u8 address) +static int +twl4030_config_wakeup12_sequence(const struct twl4030_power_data *pdata, + u8 address) { int err = 0; u8 data; @@ -293,13 +295,14 @@ static int twl4030_config_wakeup12_sequence(u8 address) if (err) goto out; - if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) { + if (pdata->ac_charger_quirk || machine_is_omap_3430sdp() || + machine_is_omap_ldp()) { /* Disabling AC charger effect on sleep-active transitions */ err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, R_CFG_P1_TRANSITION); if (err) goto out; - data &= ~(1<<1); + data &= ~STARTON_CHG; err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_CFG_P1_TRANSITION); if (err) @@ -459,8 +462,9 @@ static int twl4030_configure_resource(struct twl4030_resconfig *rconfig) return 0; } -static int load_twl4030_script(struct twl4030_script *tscript, - u8 address) +static int load_twl4030_script(const struct twl4030_power_data *pdata, + struct twl4030_script *tscript, + u8 address) { int err; static int order; @@ -487,7 +491,7 @@ static int load_twl4030_script(struct twl4030_script *tscript, if (err) goto out; - err = twl4030_config_wakeup12_sequence(address); + err = twl4030_config_wakeup12_sequence(pdata, address); if (err) goto out; order = 1; @@ -567,7 +571,7 @@ twl4030_power_configure_scripts(const struct twl4030_power_data *pdata) u8 address = twl4030_start_script_address; for (i = 0; i < pdata->num; i++) { - err = load_twl4030_script(pdata->scripts[i], address); + err = load_twl4030_script(pdata, pdata->scripts[i], address); if (err) return err; address += pdata->scripts[i]->size; @@ -829,6 +833,21 @@ static struct twl4030_power_data osc_off_idle = { .board_config = osc_off_rconfig, }; +static struct twl4030_power_data omap3_idle_ac_quirk = { + .scripts = omap3_idle_scripts, + .num = ARRAY_SIZE(omap3_idle_scripts), + .resource_config = omap3_idle_rconfig, + .ac_charger_quirk = true, +}; + +static struct twl4030_power_data omap3_idle_ac_quirk_osc_off = { + .scripts = omap3_idle_scripts, + .num = ARRAY_SIZE(omap3_idle_scripts), + .resource_config = omap3_idle_rconfig, + .board_config = osc_off_rconfig, + .ac_charger_quirk = true, +}; + static const struct of_device_id twl4030_power_of_match[] = { { .compatible = "ti,twl4030-power", @@ -845,6 +864,18 @@ static const struct of_device_id twl4030_power_of_match[] = { .compatible = "ti,twl4030-power-idle-osc-off", .data = &osc_off_idle, }, + { + .compatible = "ti,twl4030-power-omap3-sdp", + .data = &omap3_idle_ac_quirk, + }, + { + .compatible = "ti,twl4030-power-omap3-ldp", + .data = &omap3_idle_ac_quirk_osc_off, + }, + { + .compatible = "ti,twl4030-power-omap3-evm", + .data = &omap3_idle_ac_quirk, + }, { }, }; MODULE_DEVICE_TABLE(of, twl4030_power_of_match); -- cgit v1.2.3 From 6fafecc12ddfd5d02be531de081e4ea9cb8db02f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 5 May 2015 18:32:27 +0200 Subject: mfd: Allow compile test of GPIO consumers if !GPIOLIB The GPIO subsystem provides dummy GPIO consumer functions if GPIOLIB is not enabled. Hence drivers that depend on GPIOLIB, but use GPIO consumer functionality only, can still be compiled if GPIOLIB is not enabled. Relax the dependency on GPIOLIB if COMPILE_TEST is enabled, where appropriate. Signed-off-by: Geert Uytterhoeven Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index cf3b86d441f7..653815950aa2 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -52,7 +52,8 @@ config PMIC_ADP5520 config MFD_AAT2870_CORE bool "AnalogicTech AAT2870" select MFD_CORE - depends on I2C=y && GPIOLIB + depends on I2C=y + depends on GPIOLIB || COMPILE_TEST help If you say yes here you get support for the AAT2870. This driver provides common support for accessing the device, @@ -1117,7 +1118,8 @@ config MFD_TPS6586X config MFD_TPS65910 bool "TI TPS65910 Power Management chip" - depends on I2C=y && GPIOLIB + depends on I2C=y + depends on GPIOLIB || COMPILE_TEST select MFD_CORE select REGMAP_I2C select REGMAP_IRQ -- cgit v1.2.3 From 5f056bf04201dc533f5d10acf028435ede0a1442 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 11 May 2015 13:58:01 +0100 Subject: mfd: arizona: Use devres to manage reset GPIO This also handily fixes a leak of the GPIO in arizona_dev_exit. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index aed43a549f77..915cc28585d2 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -728,9 +728,9 @@ int arizona_dev_init(struct arizona *arizona) if (arizona->pdata.reset) { /* Start out with /RESET low to put the chip into reset */ - ret = gpio_request_one(arizona->pdata.reset, - GPIOF_DIR_OUT | GPIOF_INIT_LOW, - "arizona /RESET"); + ret = devm_gpio_request_one(arizona->dev, arizona->pdata.reset, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, + "arizona /RESET"); if (ret != 0) { dev_err(dev, "Failed to request /RESET: %d\n", ret); goto err_dcvdd; @@ -1046,10 +1046,8 @@ int arizona_dev_init(struct arizona *arizona) err_irq: arizona_irq_exit(arizona); err_reset: - if (arizona->pdata.reset) { + if (arizona->pdata.reset) gpio_set_value_cansleep(arizona->pdata.reset, 0); - gpio_free(arizona->pdata.reset); - } regulator_disable(arizona->dcvdd); err_enable: regulator_bulk_disable(arizona->num_core_supplies, -- cgit v1.2.3 From 2229875dc00f38b95d04fecfc67858ba2221a563 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 11 May 2015 13:58:02 +0100 Subject: mfd: arizona: Factor out hard reset into helper functions This patch adds functions for enabling and disabling the physical reset line. This will be helpful in future refactoring. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 915cc28585d2..f30d92538584 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -250,6 +250,20 @@ static int arizona_wait_for_boot(struct arizona *arizona) return ret; } +static inline void arizona_enable_reset(struct arizona *arizona) +{ + if (arizona->pdata.reset) + gpio_set_value_cansleep(arizona->pdata.reset, 0); +} + +static void arizona_disable_reset(struct arizona *arizona) +{ + if (arizona->pdata.reset) { + gpio_set_value_cansleep(arizona->pdata.reset, 1); + msleep(1); + } +} + static int arizona_apply_hardware_patch(struct arizona* arizona) { unsigned int fll, sysclk; @@ -751,10 +765,7 @@ int arizona_dev_init(struct arizona *arizona) goto err_enable; } - if (arizona->pdata.reset) { - gpio_set_value_cansleep(arizona->pdata.reset, 1); - msleep(1); - } + arizona_disable_reset(arizona); regcache_cache_only(arizona->regmap, false); @@ -1046,8 +1057,7 @@ int arizona_dev_init(struct arizona *arizona) err_irq: arizona_irq_exit(arizona); err_reset: - if (arizona->pdata.reset) - gpio_set_value_cansleep(arizona->pdata.reset, 0); + arizona_enable_reset(arizona); regulator_disable(arizona->dcvdd); err_enable: regulator_bulk_disable(arizona->num_core_supplies, @@ -1072,8 +1082,7 @@ int arizona_dev_exit(struct arizona *arizona) arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona); arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona); arizona_irq_exit(arizona); - if (arizona->pdata.reset) - gpio_set_value_cansleep(arizona->pdata.reset, 0); + arizona_enable_reset(arizona); regulator_bulk_disable(arizona->num_core_supplies, arizona->core_supplies); -- cgit v1.2.3 From 1c1c6bba57f51331f64f32d0ce9a0f9883041205 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 11 May 2015 13:58:03 +0100 Subject: mfd: wm5102: Ensure we always boot the device fully The wm5102 uses a custom boot sequence and the standard boot sequence is disabled. However, the standard boot sequence must be run at least once after a cold boot (caused by either a hard reset or removal of AVDD). Unfortunately the register WRITE_SEQUENCER_CTRL_3 is not affected by the hardware reset. This means if the device has been previously booted but the AVDD supply has never been removed, arizona_dev_init will reset the chip but the boot sequence will not run, which can cause numerous problems. The solution is to manually clear this register and then boot the chip again, which is what this patch does. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 50 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index f30d92538584..0df12995ed1b 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -344,6 +344,33 @@ err_fll: return err; } +static int wm5102_clear_write_sequencer(struct arizona *arizona) +{ + int ret; + + ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_3, + 0x0); + if (ret) { + dev_err(arizona->dev, + "Failed to clear write sequencer state: %d\n", ret); + return ret; + } + + arizona_enable_reset(arizona); + regulator_disable(arizona->dcvdd); + + msleep(20); + + ret = regulator_enable(arizona->dcvdd); + if (ret) { + dev_err(arizona->dev, "Failed to re-enable DCVDD: %d\n", ret); + return ret; + } + arizona_disable_reset(arizona); + + return 0; +} + #ifdef CONFIG_PM static int arizona_runtime_resume(struct device *dev) { @@ -802,21 +829,24 @@ int arizona_dev_init(struct arizona *arizona) case WM5102: ret = regmap_read(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_3, &val); - if (ret != 0) + if (ret) { dev_err(dev, "Failed to check write sequencer state: %d\n", ret); - else if (val & 0x01) - break; - /* Fall through */ - default: - ret = arizona_wait_for_boot(arizona); - if (ret != 0) { - dev_err(arizona->dev, - "Device failed initial boot: %d\n", ret); - goto err_reset; + } else if (val & 0x01) { + ret = wm5102_clear_write_sequencer(arizona); + if (ret) + return ret; } break; + default: + break; + } + + ret = arizona_wait_for_boot(arizona); + if (ret) { + dev_err(arizona->dev, "Device failed initial boot: %d\n", ret); + goto err_reset; } /* Read the device ID information & do device specific stuff */ -- cgit v1.2.3 From 0be068a077a06fa3382ab6d8d290078967bb8f64 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 11 May 2015 13:58:04 +0100 Subject: mfd: arizona: Fix formating/style issues in arizona_apply_hardware_patch In preparation for some refactoring fixup some minor style, formating and code clarity issues. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 0df12995ed1b..326a4afdd832 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -264,20 +264,20 @@ static void arizona_disable_reset(struct arizona *arizona) } } -static int arizona_apply_hardware_patch(struct arizona* arizona) +static int wm5102_apply_hardware_patch(struct arizona *arizona) { unsigned int fll, sysclk; int ret, err; /* Cache existing FLL and SYSCLK settings */ ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &fll); - if (ret != 0) { + if (ret) { dev_err(arizona->dev, "Failed to cache FLL settings: %d\n", ret); return ret; } ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &sysclk); - if (ret != 0) { + if (ret) { dev_err(arizona->dev, "Failed to cache SYSCLK settings: %d\n", ret); return ret; @@ -286,7 +286,7 @@ static int arizona_apply_hardware_patch(struct arizona* arizona) /* Start up SYSCLK using the FLL in free running mode */ ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, ARIZONA_FLL1_ENA | ARIZONA_FLL1_FREERUN); - if (ret != 0) { + if (ret) { dev_err(arizona->dev, "Failed to start FLL in freerunning mode: %d\n", ret); @@ -295,36 +295,36 @@ static int arizona_apply_hardware_patch(struct arizona* arizona) ret = arizona_poll_reg(arizona, 25, ARIZONA_INTERRUPT_RAW_STATUS_5, ARIZONA_FLL1_CLOCK_OK_STS, ARIZONA_FLL1_CLOCK_OK_STS); - if (ret != 0) { + if (ret) { ret = -ETIMEDOUT; goto err_fll; } ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, 0x0144); - if (ret != 0) { + if (ret) { dev_err(arizona->dev, "Failed to start SYSCLK: %d\n", ret); goto err_fll; } /* Start the write sequencer and wait for it to finish */ ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0, - ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160); - if (ret != 0) { + ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160); + if (ret) { dev_err(arizona->dev, "Failed to start write sequencer: %d\n", ret); goto err_sysclk; } ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1, ARIZONA_WSEQ_BUSY, 0); - if (ret != 0) { + if (ret) { regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0, - ARIZONA_WSEQ_ABORT); + ARIZONA_WSEQ_ABORT); ret = -ETIMEDOUT; } err_sysclk: err = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, sysclk); - if (err != 0) { + if (err) { dev_err(arizona->dev, "Failed to re-apply old SYSCLK settings: %d\n", err); @@ -332,16 +332,13 @@ err_sysclk: err_fll: err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, fll); - if (err != 0) { + if (err) { dev_err(arizona->dev, "Failed to re-apply old FLL settings: %d\n", err); } - if (ret != 0) - return ret; - else - return err; + return ret ?: err; } static int wm5102_clear_write_sequencer(struct arizona *arizona) @@ -407,8 +404,8 @@ static int arizona_runtime_resume(struct device *dev) goto err; } - ret = arizona_apply_hardware_patch(arizona); - if (ret != 0) { + ret = wm5102_apply_hardware_patch(arizona); + if (ret) { dev_err(arizona->dev, "Failed to apply hardware patch: %d\n", ret); @@ -924,8 +921,8 @@ int arizona_dev_init(struct arizona *arizona) switch (arizona->type) { case WM5102: - ret = arizona_apply_hardware_patch(arizona); - if (ret != 0) { + ret = wm5102_apply_hardware_patch(arizona); + if (ret) { dev_err(arizona->dev, "Failed to apply hardware patch: %d\n", ret); -- cgit v1.2.3 From 3850e3ee86c90e22767907fbd7505a717300c533 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 11 May 2015 13:58:05 +0100 Subject: mfd: arizona: Factor out SYSCLK enable from wm5102 hardware patch wm5102 applies a custom hardware boot sequence, for this the SYSCLK needs to be enabled. This patch factors out the code that enables SYSCLK for this sequence such that it can be used for other boot time operations that require SYSCLK. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 77 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 20 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 326a4afdd832..b5a7e0efcf3e 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -264,19 +264,25 @@ static void arizona_disable_reset(struct arizona *arizona) } } -static int wm5102_apply_hardware_patch(struct arizona *arizona) +struct arizona_sysclk_state { + unsigned int fll; + unsigned int sysclk; +}; + +static int arizona_enable_freerun_sysclk(struct arizona *arizona, + struct arizona_sysclk_state *state) { - unsigned int fll, sysclk; int ret, err; /* Cache existing FLL and SYSCLK settings */ - ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &fll); + ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &state->fll); if (ret) { dev_err(arizona->dev, "Failed to cache FLL settings: %d\n", ret); return ret; } - ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &sysclk); + ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, + &state->sysclk); if (ret) { dev_err(arizona->dev, "Failed to cache SYSCLK settings: %d\n", ret); @@ -306,14 +312,58 @@ static int wm5102_apply_hardware_patch(struct arizona *arizona) goto err_fll; } + return 0; + +err_fll: + err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, state->fll); + if (err) + dev_err(arizona->dev, + "Failed to re-apply old FLL settings: %d\n", err); + + return ret; +} + +static int arizona_disable_freerun_sysclk(struct arizona *arizona, + struct arizona_sysclk_state *state) +{ + int ret; + + ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, + state->sysclk); + if (ret) { + dev_err(arizona->dev, + "Failed to re-apply old SYSCLK settings: %d\n", ret); + return ret; + } + + ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, state->fll); + if (ret) { + dev_err(arizona->dev, + "Failed to re-apply old FLL settings: %d\n", ret); + return ret; + } + + return 0; +} + +static int wm5102_apply_hardware_patch(struct arizona *arizona) +{ + struct arizona_sysclk_state state; + int err, ret; + + ret = arizona_enable_freerun_sysclk(arizona, &state); + if (ret) + return ret; + /* Start the write sequencer and wait for it to finish */ ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0, ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160); if (ret) { dev_err(arizona->dev, "Failed to start write sequencer: %d\n", ret); - goto err_sysclk; + goto err; } + ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1, ARIZONA_WSEQ_BUSY, 0); if (ret) { @@ -322,21 +372,8 @@ static int wm5102_apply_hardware_patch(struct arizona *arizona) ret = -ETIMEDOUT; } -err_sysclk: - err = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, sysclk); - if (err) { - dev_err(arizona->dev, - "Failed to re-apply old SYSCLK settings: %d\n", - err); - } - -err_fll: - err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, fll); - if (err) { - dev_err(arizona->dev, - "Failed to re-apply old FLL settings: %d\n", - err); - } +err: + err = arizona_disable_freerun_sysclk(arizona, &state); return ret ?: err; } -- cgit v1.2.3 From 882bc46813177640cfe01fb4f9b3e7af60201512 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 11 May 2015 13:58:06 +0100 Subject: mfd: wm5110: Add register patch required for low power sleep Some register settings must be applied before the first time low power sleep mode is entered on the wm5110 to ensure optimium performance. These settings require SYSCLK to be enabled whilst they are being applied. This patch applies the settings using the recently factored out boot time SYSCLK functionality. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index b5a7e0efcf3e..c80ab7910615 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -378,6 +378,35 @@ err: return ret ?: err; } +/* + * Register patch to some of the CODECs internal write sequences + * to ensure a clean exit from the low power sleep state. + */ +static const struct reg_default wm5110_sleep_patch[] = { + { 0x337A, 0xC100 }, + { 0x337B, 0x0041 }, + { 0x3300, 0xA210 }, + { 0x3301, 0x050C }, +}; + +static int wm5110_apply_sleep_patch(struct arizona *arizona) +{ + struct arizona_sysclk_state state; + int err, ret; + + ret = arizona_enable_freerun_sysclk(arizona, &state); + if (ret) + return ret; + + ret = regmap_multi_reg_write_bypassed(arizona->regmap, + wm5110_sleep_patch, + ARRAY_SIZE(wm5110_sleep_patch)); + + err = arizona_disable_freerun_sysclk(arizona, &state); + + return ret ?: err; +} + static int wm5102_clear_write_sequencer(struct arizona *arizona) { int ret; @@ -966,6 +995,16 @@ int arizona_dev_init(struct arizona *arizona) goto err_reset; } break; + case WM5110: + case WM8280: + ret = wm5110_apply_sleep_patch(arizona); + if (ret) { + dev_err(arizona->dev, + "Failed to apply sleep patch: %d\n", + ret); + goto err_reset; + } + break; default: break; } -- cgit v1.2.3 From 96129a0eaa3f5232f433812972c7bf38004ebf6b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 11 May 2015 13:58:08 +0100 Subject: mfd: wm5110: Set DCVDD voltage to 1.175V before entering sleep mode The low power sleep mode on wm5110 requires that the LDO1 regulator be set to 1.175V prior to entering sleep, then returned to 1.2V after exiting sleep mode. This patch apply these regulator settings. Signed-off-by: Charles Keepax Acked-by: Mark Brown Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index c80ab7910615..93024acc145e 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -478,6 +478,37 @@ static int arizona_runtime_resume(struct device *dev) goto err; } break; + case WM5110: + case WM8280: + ret = arizona_wait_for_boot(arizona); + if (ret) + goto err; + + if (arizona->external_dcvdd) { + ret = regmap_update_bits(arizona->regmap, + ARIZONA_ISOLATION_CONTROL, + ARIZONA_ISOLATE_DCVDD1, 0); + if (ret) { + dev_err(arizona->dev, + "Failed to connect DCVDD: %d\n", ret); + goto err; + } + } else { + /* + * As this is only called for the internal regulator + * (where we know voltage ranges available) it is ok + * to request an exact range. + */ + ret = regulator_set_voltage(arizona->dcvdd, + 1200000, 1200000); + if (ret < 0) { + dev_err(arizona->dev, + "Failed to set resume voltage: %d\n", + ret); + goto err; + } + } + break; default: ret = arizona_wait_for_boot(arizona); if (ret != 0) { @@ -528,6 +559,27 @@ static int arizona_runtime_suspend(struct device *dev) ret); return ret; } + } else { + switch (arizona->type) { + case WM5110: + case WM8280: + /* + * As this is only called for the internal regulator + * (where we know voltage ranges available) it is ok + * to request an exact range. + */ + ret = regulator_set_voltage(arizona->dcvdd, + 1175000, 1175000); + if (ret < 0) { + dev_err(arizona->dev, + "Failed to set suspend voltage: %d\n", + ret); + return ret; + } + break; + default: + break; + } } regcache_cache_only(arizona->regmap, true); -- cgit v1.2.3 From e6cb73410a6db70eab266f15b7e25053a45b842d Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 11 May 2015 13:58:09 +0100 Subject: mfd: arizona: Add better support for system suspend Allow the chip to completely power off if we enter runtime suspend and there is no jack detection active. This is helpful for systems where system suspend might remove the supplies to the CODEC, without informing us. Note the powering off is done in runtime suspend rather than system suspend, because we need to hold reset until the first time DCVDD is powered anyway (which would be in runtime resume), and we might as well save the extra power. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 93 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 16 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 93024acc145e..4f38ce654558 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -442,12 +442,33 @@ static int arizona_runtime_resume(struct device *dev) dev_dbg(arizona->dev, "Leaving AoD mode\n"); + if (arizona->has_fully_powered_off) { + dev_dbg(arizona->dev, "Re-enabling core supplies\n"); + + ret = regulator_bulk_enable(arizona->num_core_supplies, + arizona->core_supplies); + if (ret) { + dev_err(dev, "Failed to enable core supplies: %d\n", + ret); + return ret; + } + } + ret = regulator_enable(arizona->dcvdd); if (ret != 0) { dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret); + if (arizona->has_fully_powered_off) + regulator_bulk_disable(arizona->num_core_supplies, + arizona->core_supplies); return ret; } + if (arizona->has_fully_powered_off) { + arizona_disable_reset(arizona); + enable_irq(arizona->irq); + arizona->has_fully_powered_off = false; + } + regcache_cache_only(arizona->regmap, false); switch (arizona->type) { @@ -508,6 +529,14 @@ static int arizona_runtime_resume(struct device *dev) goto err; } } + + ret = wm5110_apply_sleep_patch(arizona); + if (ret) { + dev_err(arizona->dev, + "Failed to re-apply sleep patch: %d\n", + ret); + goto err; + } break; default: ret = arizona_wait_for_boot(arizona); @@ -545,10 +574,17 @@ err: static int arizona_runtime_suspend(struct device *dev) { struct arizona *arizona = dev_get_drvdata(dev); + unsigned int val; int ret; dev_dbg(arizona->dev, "Entering AoD mode\n"); + ret = regmap_read(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, &val); + if (ret) { + dev_err(dev, "Failed to check jack det status: %d\n", ret); + return ret; + } + if (arizona->external_dcvdd) { ret = regmap_update_bits(arizona->regmap, ARIZONA_ISOLATION_CONTROL, @@ -559,33 +595,58 @@ static int arizona_runtime_suspend(struct device *dev) ret); return ret; } - } else { - switch (arizona->type) { - case WM5110: - case WM8280: - /* - * As this is only called for the internal regulator - * (where we know voltage ranges available) it is ok - * to request an exact range. - */ - ret = regulator_set_voltage(arizona->dcvdd, - 1175000, 1175000); - if (ret < 0) { + } + + switch (arizona->type) { + case WM5110: + case WM8280: + if (arizona->external_dcvdd) + break; + + /* + * As this is only called for the internal regulator + * (where we know voltage ranges available) it is ok + * to request an exact range. + */ + ret = regulator_set_voltage(arizona->dcvdd, 1175000, 1175000); + if (ret < 0) { + dev_err(arizona->dev, + "Failed to set suspend voltage: %d\n", ret); + return ret; + } + break; + case WM5102: + if (!(val & ARIZONA_JD1_ENA)) { + ret = regmap_write(arizona->regmap, + ARIZONA_WRITE_SEQUENCER_CTRL_3, 0x0); + if (ret) { dev_err(arizona->dev, - "Failed to set suspend voltage: %d\n", + "Failed to clear write sequencer: %d\n", ret); return ret; } - break; - default: - break; } + break; + default: + break; } regcache_cache_only(arizona->regmap, true); regcache_mark_dirty(arizona->regmap); regulator_disable(arizona->dcvdd); + /* Allow us to completely power down if no jack detection */ + if (!(val & ARIZONA_JD1_ENA)) { + dev_dbg(arizona->dev, "Fully powering off\n"); + + arizona->has_fully_powered_off = true; + + disable_irq(arizona->irq); + arizona_enable_reset(arizona); + regulator_bulk_disable(arizona->num_core_supplies, + arizona->core_supplies); + } + return 0; } #endif -- cgit v1.2.3 From 121c075c1f845c2e75d61a4e7aac92d05d2def6e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 11 May 2015 13:58:10 +0100 Subject: mfd: wm5110: Add delay before releasing reset line On the wm5110 it is important the reset line is held for slightly longer to ensure the device starts up well. This patch adds a 5mS delay for this. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 4f38ce654558..cb8a1b2f07f1 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -259,6 +259,16 @@ static inline void arizona_enable_reset(struct arizona *arizona) static void arizona_disable_reset(struct arizona *arizona) { if (arizona->pdata.reset) { + switch (arizona->type) { + case WM5110: + case WM8280: + /* Meet requirements for minimum reset duration */ + msleep(5); + break; + default: + break; + } + gpio_set_value_cansleep(arizona->pdata.reset, 1); msleep(1); } -- cgit v1.2.3 From fc027d138b79537e5353f3d3bad2bcaac787cd17 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 1 May 2015 16:15:12 +0100 Subject: mfd: arizona: Split INx_MODE into two fields Later arizona silicon has the single/differential selector in a different register, and IN1_MODE only selects between analogue or digital. Prepare for this by splitting the INx_MODE definition into two fields. Signed-off-by: Richard Fitzgerald Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index cb8a1b2f07f1..bebf58a06a6b 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -1206,12 +1206,16 @@ int arizona_dev_init(struct arizona *arizona) /* Default for both is 0 so noop with defaults */ val = arizona->pdata.dmic_ref[i] << ARIZONA_IN1_DMIC_SUP_SHIFT; - val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT; + if (arizona->pdata.inmode[i] & ARIZONA_INMODE_DMIC) + val |= 1 << ARIZONA_IN1_MODE_SHIFT; + if (arizona->pdata.inmode[i] & ARIZONA_INMODE_SE) + val |= 1 << ARIZONA_IN1_SINGLE_ENDED_SHIFT; regmap_update_bits(arizona->regmap, ARIZONA_IN1L_CONTROL + (i * 8), ARIZONA_IN1_DMIC_SUP_MASK | - ARIZONA_IN1_MODE_MASK, val); + ARIZONA_IN1_MODE_MASK | + ARIZONA_IN1_SINGLE_ENDED_MASK, val); } for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) { -- cgit v1.2.3 From ec40c606c7aebb8d61cb47af27e2f62c26e09e29 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Fri, 1 May 2015 10:41:10 +0100 Subject: mfd: Check ACPI device companion before checking resources Current code in mfd-core calls into ACPI to check resources even on a system that booted with a DT (on kernels with both DT and ACPI support compiled in). This triggers ACPI exceptions since we may end up calling the ACPI interpreter when it has not been initialized: "ACPI Exception: AE_BAD_PARAMETER, Thread 2064154624 could not acquire Mutex [0x1] (20150410/utmutex-285)" This patch fixes the issues by adding a check for an ACPI companion device before carrying out ACPI resources checks to avoid calling the ACPI interpreter if the fwnode representing the device is an OF one. Cc: Sudeep Holla Signed-off-by: Lorenzo Pieralisi Signed-off-by: Lee Jones --- drivers/mfd/mfd-core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 1aed3b7b8d9b..14fd5cbcf0f2 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -207,9 +207,11 @@ static int mfd_add_device(struct device *parent, int id, } if (!cell->ignore_resource_conflicts) { - ret = acpi_check_resource_conflict(&res[r]); - if (ret) - goto fail_alias; + if (has_acpi_companion(&pdev->dev)) { + ret = acpi_check_resource_conflict(&res[r]); + if (ret) + goto fail_alias; + } } } -- cgit v1.2.3 From e2ae0fbc738fa207cfb712657d339a1863c6bd3e Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 13 May 2015 16:52:26 +0100 Subject: mfd: arizona: Fix incorrect Makefile conditionals The use of ifneq against 'n' to conditionally compile codec-specific parts is wrong and was resulting in all the codec tables being built even for deselected codecs. Signed-off-by: Richard Fitzgerald Signed-off-by: Lee Jones --- drivers/mfd/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 0e5cfeba107c..ea40e076cb61 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -39,13 +39,13 @@ obj-$(CONFIG_MFD_ARIZONA) += arizona-core.o obj-$(CONFIG_MFD_ARIZONA) += arizona-irq.o obj-$(CONFIG_MFD_ARIZONA_I2C) += arizona-i2c.o obj-$(CONFIG_MFD_ARIZONA_SPI) += arizona-spi.o -ifneq ($(CONFIG_MFD_WM5102),n) +ifeq ($(CONFIG_MFD_WM5102),y) obj-$(CONFIG_MFD_ARIZONA) += wm5102-tables.o endif -ifneq ($(CONFIG_MFD_WM5110),n) +ifeq ($(CONFIG_MFD_WM5110),y) obj-$(CONFIG_MFD_ARIZONA) += wm5110-tables.o endif -ifneq ($(CONFIG_MFD_WM8997),n) +ifeq ($(CONFIG_MFD_WM8997),y) obj-$(CONFIG_MFD_ARIZONA) += wm8997-tables.o endif obj-$(CONFIG_MFD_WM8400) += wm8400-core.o -- cgit v1.2.3 From 9011e4a8a6fe57f76511609930ed00d305389089 Mon Sep 17 00:00:00 2001 From: Steve Twiss Date: Tue, 19 May 2015 11:32:45 +0100 Subject: mfd: da9063: Add support for OnKey driver Add MFD support for the DA9063 OnKey driver The function da9063_clear_fault_log() is added to mitigate the case of a hardware power-cut after a long-long OnKey press. Although there is no software intervention in this case (by definition) such a shutdown would cause persistent information within the DA9063 FAULT_LOG that would be available during the next device restart. Clearance of this persistent register must be completed after such a hardware power-cut operation has happened so that the FAULT_LOG does not continue with previous values. The clearance function has been added here in the kernel driver because wiping the fault-log cannot be counted on outside the Linux kernel. Signed-off-by: Steve Twiss [Lee: Removed 'key_power' for Dmitry to take through the Input Tree] Signed-off-by: Lee Jones --- drivers/mfd/da9063-core.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c index facd3610ac77..af841c165787 100644 --- a/drivers/mfd/da9063-core.c +++ b/drivers/mfd/da9063-core.c @@ -60,6 +60,7 @@ static struct resource da9063_rtc_resources[] = { static struct resource da9063_onkey_resources[] = { { + .name = "ONKEY", .start = DA9063_IRQ_ONKEY, .end = DA9063_IRQ_ONKEY, .flags = IORESOURCE_IRQ, @@ -97,6 +98,7 @@ static const struct mfd_cell da9063_devs[] = { .name = DA9063_DRVNAME_ONKEY, .num_resources = ARRAY_SIZE(da9063_onkey_resources), .resources = da9063_onkey_resources, + .of_compatible = "dlg,da9063-onkey", }, { .name = DA9063_DRVNAME_RTC, @@ -109,12 +111,64 @@ static const struct mfd_cell da9063_devs[] = { }, }; +static int da9063_clear_fault_log(struct da9063 *da9063) +{ + int ret = 0; + int fault_log = 0; + + ret = regmap_read(da9063->regmap, DA9063_REG_FAULT_LOG, &fault_log); + if (ret < 0) { + dev_err(da9063->dev, "Cannot read FAULT_LOG.\n"); + return -EIO; + } + + if (fault_log) { + if (fault_log & DA9063_TWD_ERROR) + dev_dbg(da9063->dev, + "Fault log entry detected: DA9063_TWD_ERROR\n"); + if (fault_log & DA9063_POR) + dev_dbg(da9063->dev, + "Fault log entry detected: DA9063_POR\n"); + if (fault_log & DA9063_VDD_FAULT) + dev_dbg(da9063->dev, + "Fault log entry detected: DA9063_VDD_FAULT\n"); + if (fault_log & DA9063_VDD_START) + dev_dbg(da9063->dev, + "Fault log entry detected: DA9063_VDD_START\n"); + if (fault_log & DA9063_TEMP_CRIT) + dev_dbg(da9063->dev, + "Fault log entry detected: DA9063_TEMP_CRIT\n"); + if (fault_log & DA9063_KEY_RESET) + dev_dbg(da9063->dev, + "Fault log entry detected: DA9063_KEY_RESET\n"); + if (fault_log & DA9063_NSHUTDOWN) + dev_dbg(da9063->dev, + "Fault log entry detected: DA9063_NSHUTDOWN\n"); + if (fault_log & DA9063_WAIT_SHUT) + dev_dbg(da9063->dev, + "Fault log entry detected: DA9063_WAIT_SHUT\n"); + } + + ret = regmap_write(da9063->regmap, + DA9063_REG_FAULT_LOG, + fault_log); + if (ret < 0) + dev_err(da9063->dev, + "Cannot reset FAULT_LOG values %d\n", ret); + + return ret; +} + int da9063_device_init(struct da9063 *da9063, unsigned int irq) { struct da9063_pdata *pdata = da9063->dev->platform_data; int model, variant_id, variant_code; int ret; + ret = da9063_clear_fault_log(da9063); + if (ret < 0) + dev_err(da9063->dev, "Cannot clear fault log\n"); + if (pdata) { da9063->flags = pdata->flags; da9063->irq_base = pdata->irq_base; -- cgit v1.2.3 From cf55078b88c83bbf18e46dc4160d0e5b7e60799d Mon Sep 17 00:00:00 2001 From: Hongzhou Yang Date: Wed, 27 May 2015 02:10:35 -0700 Subject: mfd: mt6397-core: Add GPIO sub-module support Signed-off-by: Hongzhou Yang Acked-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/mt6397-core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index 32775ebbce18..38a0458f7834 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -34,6 +34,9 @@ static const struct mfd_cell mt6397_devs[] = { }, { .name = "mt6397-clk", .of_compatible = "mediatek,mt6397-clk", + }, { + .name = "mt6397-pinctrl", + .of_compatible = "mediatek,mt6397-pinctrl", }, }; -- cgit v1.2.3 From 7d5b1ed89bbb3614830c96420050088f3e0fb886 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 16 May 2015 15:42:10 -0300 Subject: mfd: twl4030-irq: Pass the IRQF_ONESHOT flag Since commit 1c6c69525b40eb76de8adf039409722015927dc3 ("genirq: Reject bogus threaded irq requests") threaded IRQs without a primary handler need to be requested with IRQF_ONESHOT, otherwise the request will fail. So pass the IRQF_ONESHOT flag in this case. The semantic patch that makes this change is available in scripts/coccinelle/misc/irqf_oneshot.cocci. Signed-off-by: Fabio Estevam Signed-off-by: Lee Jones --- drivers/mfd/twl4030-irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 1b772ef761cb..a3fa7f4f1fb4 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -674,7 +674,7 @@ int twl4030_sih_setup(struct device *dev, int module, int irq_base) irq_set_handler_data(irq, agent); agent->irq_name = kasprintf(GFP_KERNEL, "twl4030_%s", sih->name); status = request_threaded_irq(irq, NULL, handle_twl4030_sih, - IRQF_EARLY_RESUME, + IRQF_EARLY_RESUME | IRQF_ONESHOT, agent->irq_name ?: sih->name, NULL); dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", sih->name, -- cgit v1.2.3 From 0b182c287c5a219f7ae35d977de4de331215820e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 16 May 2015 15:42:11 -0300 Subject: mfd: mc13xxx-core: Pass the IRQF_ONESHOT flag Since commit 1c6c69525b40eb76de8adf039409722015927dc3 ("genirq: Reject bogus threaded irq requests") threaded IRQs without a primary handler need to be requested with IRQF_ONESHOT, otherwise the request will fail. So pass the IRQF_ONESHOT flag in this case. The semantic patch that makes this change is available in scripts/coccinelle/misc/irqf_oneshot.cocci. Signed-off-by: Fabio Estevam Signed-off-by: Lee Jones --- drivers/mfd/mc13xxx-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 25fd7116493a..3f9f4c874d2a 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -163,7 +163,7 @@ int mc13xxx_irq_request(struct mc13xxx *mc13xxx, int irq, int virq = regmap_irq_get_virq(mc13xxx->irq_data, irq); return devm_request_threaded_irq(mc13xxx->dev, virq, NULL, handler, - 0, name, dev); + IRQF_ONESHOT, name, dev); } EXPORT_SYMBOL(mc13xxx_irq_request); -- cgit v1.2.3 From 1a52598c4b1d358af7ac562defd98c395842dbdd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 16 May 2015 15:42:12 -0300 Subject: mfd: wm831x-auxadc: Pass the IRQF_ONESHOT flag Since commit 1c6c69525b40eb76de8adf039409722015927dc3 ("genirq: Reject bogus threaded irq requests") threaded IRQs without a primary handler need to be requested with IRQF_ONESHOT, otherwise the request will fail. So pass the IRQF_ONESHOT flag in this case. The semantic patch that makes this change is available in scripts/coccinelle/misc/irqf_oneshot.cocci. Signed-off-by: Fabio Estevam Signed-off-by: Lee Jones --- drivers/mfd/wm831x-auxadc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm831x-auxadc.c b/drivers/mfd/wm831x-auxadc.c index 6ee3018d8653..fd789d2eb0f5 100644 --- a/drivers/mfd/wm831x-auxadc.c +++ b/drivers/mfd/wm831x-auxadc.c @@ -285,7 +285,8 @@ void wm831x_auxadc_init(struct wm831x *wm831x) ret = request_threaded_irq(wm831x_irq(wm831x, WM831X_IRQ_AUXADC_DATA), - NULL, wm831x_auxadc_irq, 0, + NULL, wm831x_auxadc_irq, + IRQF_ONESHOT, "auxadc", wm831x); if (ret < 0) { dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n", -- cgit v1.2.3 From cea2cc735ae125187a28cd30a000ad62749f948e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 16 May 2015 15:42:13 -0300 Subject: mfd: htc-i2cpld: Pass the IRQF_ONESHOT flag Since commit 1c6c69525b40eb76de8adf039409722015927dc3 ("genirq: Reject bogus threaded irq requests") threaded IRQs without a primary handler need to be requested with IRQF_ONESHOT, otherwise the request will fail. So pass the IRQF_ONESHOT flag in this case. The semantic patch that makes this change is available in scripts/coccinelle/misc/irqf_oneshot.cocci. Signed-off-by: Fabio Estevam Signed-off-by: Lee Jones --- drivers/mfd/htc-i2cpld.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/htc-i2cpld.c b/drivers/mfd/htc-i2cpld.c index ebb9cf19e347..b54baad30164 100644 --- a/drivers/mfd/htc-i2cpld.c +++ b/drivers/mfd/htc-i2cpld.c @@ -564,7 +564,8 @@ static int htcpld_core_probe(struct platform_device *pdev) htcpld->chained_irq = res->start; /* Setup the chained interrupt handler */ - flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING; + flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT; ret = request_threaded_irq(htcpld->chained_irq, NULL, htcpld_handler, flags, pdev->name, htcpld); -- cgit v1.2.3 From b3c8c82b48968fa816922114e259620921c3a29a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 16 May 2015 15:42:14 -0300 Subject: mfd: wm8350-core: Pass the IRQF_ONESHOT flag Since commit 1c6c69525b40eb76de8adf039409722015927dc3 ("genirq: Reject bogus threaded irq requests") threaded IRQs without a primary handler need to be requested with IRQF_ONESHOT, otherwise the request will fail. So pass the IRQF_ONESHOT flag in this case. The semantic patch that makes this change is available in scripts/coccinelle/misc/irqf_oneshot.cocci. Signed-off-by: Fabio Estevam Signed-off-by: Lee Jones --- drivers/mfd/wm8350-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index f5124a8acad8..8a07c5634aee 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -404,7 +404,8 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, if (wm8350->irq_base) { ret = request_threaded_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, - NULL, wm8350_auxadc_irq, 0, + NULL, wm8350_auxadc_irq, + IRQF_ONESHOT, "auxadc", wm8350); if (ret < 0) dev_warn(wm8350->dev, -- cgit v1.2.3 From abe5b47c3c158b682c8ed7becd59249d6d19938c Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 16 May 2015 15:42:15 -0300 Subject: mfd: ab8500-debugfs: Pass the IRQF_ONESHOT flag Since commit 1c6c69525b40eb76de8adf039409722015927dc3 ("genirq: Reject bogus threaded irq requests") threaded IRQs without a primary handler need to be requested with IRQF_ONESHOT, otherwise the request will fail. So pass the IRQF_ONESHOT flag in this case. The semantic patch that makes this change is available in scripts/coccinelle/misc/irqf_oneshot.cocci. Signed-off-by: Fabio Estevam Signed-off-by: Lee Jones --- drivers/mfd/ab8500-debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index cdd6f3d63314..0236cd7cdce4 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -2885,7 +2885,7 @@ static ssize_t ab8500_subscribe_write(struct file *file, } err = request_threaded_irq(user_val, NULL, ab8500_debug_handler, - IRQF_SHARED | IRQF_NO_SUSPEND, + IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, "ab8500-debug", &dev->kobj); if (err < 0) { pr_info("request_threaded_irq failed %d, %lu\n", -- cgit v1.2.3 From e7d8ae3e7b01931ad2d75e4e2840313a3d05c85e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 16 May 2015 15:42:16 -0300 Subject: mfd: ab8500-gpadc: Pass the IRQF_ONESHOT flag Since commit 1c6c69525b40eb76de8adf039409722015927dc3 ("genirq: Reject bogus threaded irq requests") threaded IRQs without a primary handler need to be requested with IRQF_ONESHOT, otherwise the request will fail. So pass the IRQF_ONESHOT flag in this case. The semantic patch that makes this change is available in scripts/coccinelle/misc/irqf_oneshot.cocci. Signed-off-by: Fabio Estevam Signed-off-by: Lee Jones --- drivers/mfd/ab8500-gpadc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index dabbc93abdd7..c51c1b188d64 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -948,7 +948,8 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) if (gpadc->irq_sw >= 0) { ret = request_threaded_irq(gpadc->irq_sw, NULL, ab8500_bm_gpadcconvend_handler, - IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-sw", + IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT, + "ab8500-gpadc-sw", gpadc); if (ret < 0) { dev_err(gpadc->dev, @@ -961,7 +962,8 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) if (gpadc->irq_hw >= 0) { ret = request_threaded_irq(gpadc->irq_hw, NULL, ab8500_bm_gpadcconvend_handler, - IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-hw", + IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT, + "ab8500-gpadc-hw", gpadc); if (ret < 0) { dev_err(gpadc->dev, -- cgit v1.2.3 From ef76cc5bae0be6e382af0596fc9eb59d0546d50b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 16 May 2015 15:42:17 -0300 Subject: mfd: si476x-i2c: Pass the IRQF_ONESHOT flag Since commit 1c6c69525b40eb76de8adf039409722015927dc3 ("genirq: Reject bogus threaded irq requests") threaded IRQs without a primary handler need to be requested with IRQF_ONESHOT, otherwise the request will fail. So pass the IRQF_ONESHOT flag in this case. The semantic patch that makes this change is available in scripts/coccinelle/misc/irqf_oneshot.cocci. Signed-off-by: Fabio Estevam Signed-off-by: Lee Jones --- drivers/mfd/si476x-i2c.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c index 7f87c62d91b3..e3deb466628b 100644 --- a/drivers/mfd/si476x-i2c.c +++ b/drivers/mfd/si476x-i2c.c @@ -777,7 +777,8 @@ static int si476x_core_probe(struct i2c_client *client, rval = devm_request_threaded_irq(&client->dev, client->irq, NULL, si476x_core_interrupt, - IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, client->name, core); if (rval < 0) { dev_err(&client->dev, "Could not request IRQ %d\n", -- cgit v1.2.3 From 1abf25a25b86dcfe28d243a5af71bd1c9d6de1ef Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 9 Jun 2015 12:17:07 +0300 Subject: mfd: lpc_ich: Assign subdevice ids automatically Using -1 as platform device id means that the platform driver core will not assign any id to the device (the device name will not have id at all). This results problems on systems that have multiple PCHs (Platform Controller HUBs) because all of them also include their own copy of LPC device. All the subsequent device creations will fail because there already exists platform device with the same name. Fix this by passing PLATFORM_DEVID_AUTO as platform device id. This makes the platform device core to allocate new ids automatically. Signed-off-by: Mika Westerberg Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 12d960a60ec4..8de34398abc0 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -934,8 +934,8 @@ gpe0_done: lpc_ich_enable_gpio_space(dev); lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_GPIO]); - ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO], - 1, NULL, 0, NULL); + ret = mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO, + &lpc_ich_cells[LPC_GPIO], 1, NULL, 0, NULL); gpio_done: if (acpi_conflict) @@ -1008,8 +1008,8 @@ static int lpc_ich_init_wdt(struct pci_dev *dev) } lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]); - ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT], - 1, NULL, 0, NULL); + ret = mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO, + &lpc_ich_cells[LPC_WDT], 1, NULL, 0, NULL); wdt_done: return ret; -- cgit v1.2.3