diff options
author | Pantelis Antoniou <panto@antoniou-consulting.com> | 2013-05-27 17:14:23 +0300 |
---|---|---|
committer | Jerome Glisse <jglisse@redhat.com> | 2013-07-28 13:44:56 -0400 |
commit | 20b2ffd42feb5c8eb2ccc530adc7b02a3de0b4ee (patch) | |
tree | 807ba857f0ac7320093014903c5cce5707310703 | |
parent | 8a345c17d9d7399a55c22df4b82e6948b07776e5 (diff) |
tps65217: Enable KEY_POWER press on AC loss / PWR_BUT
Adaption of the original patch by Andrew Bradford <andrew.bradford@omni-id.com>
Some minor devm_* changes and DT support.
Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com>
-rw-r--r-- | drivers/mfd/tps65217.c | 121 | ||||
-rw-r--r-- | include/linux/mfd/tps65217.h | 6 |
2 files changed, 125 insertions, 2 deletions
diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index b8f48647661e..818f2cdd0d10 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -26,6 +26,9 @@ #include <linux/err.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> +#include <linux/interrupt.h> #include <linux/mfd/core.h> #include <linux/mfd/tps65217.h> @@ -153,6 +156,82 @@ static const struct of_device_id tps65217_of_match[] = { { /* sentinel */ }, }; +static irqreturn_t tps65217_irq(int irq, void *irq_data) +{ + struct tps65217 *tps = irq_data; + unsigned int int_reg = 0, status_reg = 0; + + tps65217_reg_read(tps, TPS65217_REG_INT, &int_reg); + tps65217_reg_read(tps, TPS65217_REG_STATUS, &status_reg); + if (status_reg) + dev_dbg(tps->dev, "status now: 0x%X\n", status_reg); + + if (!int_reg) + return IRQ_NONE; + + if (int_reg & TPS65217_INT_PBI) { + /* Handle push button */ + dev_dbg(tps->dev, "power button status change\n"); + input_report_key(tps->pwr_but, KEY_POWER, + status_reg & TPS65217_STATUS_PB); + input_sync(tps->pwr_but); + } + if (int_reg & TPS65217_INT_ACI) { + /* Handle AC power status change */ + dev_dbg(tps->dev, "AC power status change\n"); + /* Press KEY_POWER when AC not present */ + input_report_key(tps->pwr_but, KEY_POWER, + ~status_reg & TPS65217_STATUS_ACPWR); + input_sync(tps->pwr_but); + } + if (int_reg & TPS65217_INT_USBI) { + /* Handle USB power status change */ + dev_dbg(tps->dev, "USB power status change\n"); + } + + return IRQ_HANDLED; +} + +static int tps65217_probe_pwr_but(struct tps65217 *tps) +{ + int ret; + unsigned int int_reg; + + tps->pwr_but = devm_input_allocate_device(tps->dev); + if (!tps->pwr_but) { + dev_err(tps->dev, + "Failed to allocated pwr_but input device\n"); + return -ENOMEM; + } + + tps->pwr_but->evbit[0] = BIT_MASK(EV_KEY); + tps->pwr_but->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); + tps->pwr_but->name = "tps65217_pwr_but"; + ret = input_register_device(tps->pwr_but); + if (ret) { + /* NOTE: devm managed device */ + dev_err(tps->dev, "Failed to register button device\n"); + return ret; + } + ret = devm_request_threaded_irq(tps->dev, + tps->irq, NULL, tps65217_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "tps65217", tps); + if (ret != 0) { + dev_err(tps->dev, "Failed to request IRQ %d\n", tps->irq); + return ret; + } + + /* enable the power button interrupt */ + ret = tps65217_reg_read(tps, TPS65217_REG_INT, &int_reg); + if (ret < 0) { + dev_err(tps->dev, "Failed to read INT reg\n"); + return ret; + } + int_reg &= ~TPS65217_INT_PBM; + tps65217_reg_write(tps, TPS65217_REG_INT, int_reg, TPS65217_PROTECT_NONE); + return 0; +} + static int tps65217_probe(struct i2c_client *client, const struct i2c_device_id *ids) { @@ -160,10 +239,13 @@ static int tps65217_probe(struct i2c_client *client, unsigned int version; unsigned int chip_id = ids->driver_data; const struct of_device_id *match; + struct device_node *node; bool status_off = false; + int irq = -1, irq_gpio = -1; int ret; - if (client->dev.of_node) { + node = client->dev.of_node; + if (node) { match = of_match_device(tps65217_of_match, &client->dev); if (!match) { dev_err(&client->dev, @@ -171,8 +253,31 @@ static int tps65217_probe(struct i2c_client *client, return -EINVAL; } chip_id = (unsigned int)match->data; - status_off = of_property_read_bool(client->dev.of_node, + status_off = of_property_read_bool(node, "ti,pmic-shutdown-controller"); + + /* at first try to get irq via OF method */ + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) { + irq = -1; + irq_gpio = of_get_named_gpio(node, "irq-gpio", 0); + if (irq_gpio >= 0) { + /* valid gpio; convert to irq */ + ret = devm_gpio_request_one(&client->dev, + irq_gpio, GPIOF_DIR_IN, + "tps65217-gpio-irq"); + if (ret != 0) + dev_warn(&client->dev, "Failed to " + "request gpio #%d\n", irq_gpio); + irq = gpio_to_irq(irq_gpio); + if (irq <= 0) { + dev_warn(&client->dev, "Failed to " + "convert gpio #%d to irq\n", + irq_gpio); + irq = -1; + } + } + } } if (!chip_id) { @@ -196,6 +301,18 @@ static int tps65217_probe(struct i2c_client *client, return ret; } + tps->irq = irq; + tps->irq_gpio = irq_gpio; + + /* we got an irq, request it */ + if (tps->irq >= 0) { + ret = tps65217_probe_pwr_but(tps); + if (ret < 0) { + dev_err(tps->dev, "Failed to probe pwr_but\n"); + return ret; + } + } + ret = mfd_add_devices(tps->dev, -1, tps65217s, ARRAY_SIZE(tps65217s), NULL, 0, NULL); if (ret < 0) { diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h index 290762f93930..b138fa35a4db 100644 --- a/include/linux/mfd/tps65217.h +++ b/include/linux/mfd/tps65217.h @@ -21,6 +21,7 @@ #include <linux/i2c.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> +#include <linux/input.h> /* TPS chip id list */ #define TPS65217 0xF0 @@ -274,6 +275,11 @@ struct tps65217 { struct regulator_dev *rdev[TPS65217_NUM_REGULATOR]; struct tps_info *info[TPS65217_NUM_REGULATOR]; struct regmap *regmap; + + /* Power button and IRQ handling */ + int irq_gpio; /* might not be set */ + int irq; + struct input_dev *pwr_but; }; static inline struct tps65217 *dev_to_tps65217(struct device *dev) |