Adds interrupt handler for variants, and notifications for events; over temperature/voltage/current. Also handling of persistent events and respective timing configuration. Signed-off-by: Adam Ward <Adam.Ward.opensource@xxxxxxxxxxx> --- drivers/regulator/da9121-regulator.c | 548 +++++++++++++++++++++++++++++++++++ 1 file changed, 548 insertions(+) diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 3addbd2..37a767e 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -24,15 +24,22 @@ #include <linux/err.h> #include <linux/i2c.h> #include <linux/regulator/da9121.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> #include "da9121-regulator.h" /* Chip data */ struct da9121 { struct device *dev; + struct delayed_work work; + struct regmap *regmap; struct da9121_pdata *pdata; struct regmap *regmap; struct regulator_dev *rdev[DA9121_IDX_MAX]; + unsigned int persistent[2]; + unsigned int passive_delay; + int chip_irq; int variant_id; }; @@ -92,6 +99,104 @@ struct da9121_variant_info { { 1, 2, &da9121_6A_2phase_current }, //DA9121_TYPE_DA9217 }; +static void da9121_status_poll_on(struct work_struct *work) +{ + struct da9121 *chip = container_of(work, struct da9121, work.work); + enum { R0 = 0, R1, R2, REG_MAX_NUM }; + int status[REG_MAX_NUM] = {0}; + int clear[REG_MAX_NUM] = {0}; + unsigned long delay; + int i; + int ret; + + /* If persistent-notification, status will be true + * If not persistent-notification any longer, status will be false + */ + ret = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_STATUS_0, + (void *)status, (size_t)REG_MAX_NUM); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read STATUS registers: %d\n", ret); + goto error; + } + + /* 0 TEMP_CRIT */ + if ((chip->persistent[R0] & DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT) && + !(status[R0] & DA9121_MASK_SYS_STATUS_0_TEMP_CRIT)) { + clear[R0] |= DA9121_MASK_SYS_MASK_0_M_TEMP_CRIT; + chip->persistent[R0] &= ~DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT; + } + /* 0 TEMP_WARN */ + if ((chip->persistent[R0] & DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN) && + !(status[R0] & DA9121_MASK_SYS_STATUS_0_TEMP_WARN)) { + clear[R0] |= DA9121_MASK_SYS_MASK_0_M_TEMP_WARN; + chip->persistent[R0] &= ~DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN; + } + + /* 1 OV1 */ + if ((chip->persistent[R1] & DA9121_MASK_SYS_EVENT_1_E_OV1) && + !(status[R1] & DA9121_MASK_SYS_STATUS_1_OV1)) { + clear[R1] |= DA9121_MASK_SYS_MASK_1_M_OV1; + chip->persistent[R1] &= ~DA9121_MASK_SYS_EVENT_1_E_OV1; + } + /* 1 UV1 */ + if ((chip->persistent[R1] & DA9121_MASK_SYS_EVENT_1_E_UV1) && + !(status[R1] & DA9121_MASK_SYS_STATUS_1_UV1)) { + clear[R1] |= DA9121_MASK_SYS_MASK_1_M_UV1; + chip->persistent[R1] &= ~DA9121_MASK_SYS_EVENT_1_E_UV1; + } + /* 1 OC1 */ + if ((chip->persistent[R1] & DA9121_MASK_SYS_EVENT_1_E_OC1) && + !(status[R1] & DA9121_MASK_SYS_STATUS_1_OC1)) { + clear[R1] |= DA9121_MASK_SYS_MASK_1_M_OC1; + chip->persistent[R1] &= ~DA9121_MASK_SYS_EVENT_1_E_OC1; + } + + if (variant_parameters[chip->variant_id].num_bucks == 2) { + /* 1 OV2 */ + if ((chip->persistent[R1] & DA9xxx_MASK_SYS_EVENT_1_E_OV2) && + !(status[R1] & DA9xxx_MASK_SYS_STATUS_1_OV2)) { + clear[R1] |= DA9xxx_MASK_SYS_MASK_1_M_OV2; + chip->persistent[R1] &= ~DA9xxx_MASK_SYS_EVENT_1_E_OV2; + } + /* 1 UV2 */ + if ((chip->persistent[R1] & DA9xxx_MASK_SYS_EVENT_1_E_UV2) && + !(status[R1] & DA9xxx_MASK_SYS_STATUS_1_UV2)) { + clear[R1] |= DA9xxx_MASK_SYS_MASK_1_M_UV2; + chip->persistent[R1] &= ~DA9xxx_MASK_SYS_EVENT_1_E_UV2; + } + /* 1 OC2 */ + if ((chip->persistent[R1] & DA9xxx_MASK_SYS_EVENT_1_E_OC2) && + !(status[R1] & DA9xxx_MASK_SYS_STATUS_1_OC2)) { + clear[R1] |= DA9xxx_MASK_SYS_MASK_1_M_OC2; + chip->persistent[R1] &= ~DA9xxx_MASK_SYS_EVENT_1_E_OC2; + } + } + + for (i = R0; i < REG_MAX_NUM-1; i++) { + if (clear[i]) { + unsigned int reg = DA9121_REG_SYS_MASK_0 + i; + unsigned int mbit = clear[i]; + + ret = regmap_update_bits(chip->regmap, reg, mbit, 0); + if (ret < 0) { + dev_err(chip->dev, + "Failed to unmask 0x%02x %d\n", + reg, ret); + goto error; + } + } + } + + if (chip->persistent[R0] | chip->persistent[R1]) { + delay = msecs_to_jiffies(chip->passive_delay); + queue_delayed_work(system_freezable_wq, &chip->work, delay); + } + +error: + return; +} + static bool da9121_rdev_to_buck_reg_mask(struct regulator_dev *rdev, bool mode, unsigned int *reg, unsigned int *msk) { @@ -528,6 +633,311 @@ static struct da9121_pdata *da9121_parse_regulators_dt(struct da9121 *chip) } #endif +static inline int da9121_handle_notifier( + struct da9121 *chip, struct regulator_dev *rdev, + unsigned int event_bank, unsigned int event, unsigned int ebit) +{ + enum { R0 = 0, R1, R2, REG_MAX_NUM }; + unsigned long notification = 0; + int ret = 0; + + if (event & ebit) { + switch (event_bank) { + case DA9121_REG_SYS_EVENT_0: + switch (event & ebit) { + case DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT: + chip->persistent[R0] |= DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT; + notification |= REGULATOR_EVENT_OVER_TEMP | + REGULATOR_EVENT_DISABLE; + break; + case DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN: + chip->persistent[R0] |= DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN; + notification |= REGULATOR_EVENT_OVER_TEMP; + break; + default: + dev_warn(chip->dev, + "Unhandled event in bank0 0x%02x\n", + event & ebit); + ret = -EINVAL; + break; + } + break; + case DA9121_REG_SYS_EVENT_1: + switch (event & ebit) { + case DA9121_MASK_SYS_EVENT_1_E_OV1: + chip->persistent[R1] |= DA9121_MASK_SYS_EVENT_1_E_OV1; + notification |= REGULATOR_EVENT_REGULATION_OUT; + break; + case DA9121_MASK_SYS_EVENT_1_E_UV1: + chip->persistent[R1] |= DA9121_MASK_SYS_EVENT_1_E_UV1; + notification |= REGULATOR_EVENT_UNDER_VOLTAGE; + break; + case DA9121_MASK_SYS_EVENT_1_E_OC1: + chip->persistent[R1] |= DA9121_MASK_SYS_EVENT_1_E_OC1; + notification |= REGULATOR_EVENT_OVER_CURRENT; + break; + case DA9xxx_MASK_SYS_EVENT_1_E_OV2: + chip->persistent[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_OV2; + notification |= REGULATOR_EVENT_REGULATION_OUT; + break; + case DA9xxx_MASK_SYS_EVENT_1_E_UV2: + chip->persistent[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_UV2; + notification |= REGULATOR_EVENT_UNDER_VOLTAGE; + break; + case DA9xxx_MASK_SYS_EVENT_1_E_OC2: + chip->persistent[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_OC2; + notification |= REGULATOR_EVENT_OVER_CURRENT; + break; + default: + dev_warn(chip->dev, + "Unhandled event in bank1 0x%02x\n", + event & ebit); + ret = -EINVAL; + break; + } + break; + default: + dev_warn(chip->dev, + "Unhandled event bank 0x%02x\n", event_bank); + ret = -EINVAL; + goto error; + } + + regulator_notifier_call_chain(rdev, notification, NULL); + } + +error: + return ret; +} + +static irqreturn_t da9121_irq_handler(int irq, void *data) +{ + struct da9121 *chip = data; + struct regulator_dev *rdev; + enum { R0 = 0, R1, R2, REG_MAX_NUM }; + int event[REG_MAX_NUM] = {0}; + int handled[REG_MAX_NUM] = {0}; + int mask[REG_MAX_NUM] = {0}; + int ret = IRQ_NONE; + int i; + int err; + + err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_EVENT_0, + (void *)event, (size_t)REG_MAX_NUM); + if (err < 0) { + dev_err(chip->dev, "Failed to read EVENT registers %d\n", err); + ret = IRQ_NONE; + goto error; + } + + err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_MASK_0, + (void *)mask, (size_t)REG_MAX_NUM); + if (err < 0) { + dev_err(chip->dev, + "Failed to read MASK registers: %d\n", ret); + ret = IRQ_NONE; + goto error; + } + + rdev = chip->rdev[DA9121_IDX_BUCK1]; + + /* 0 SYSTEM_GOOD */ + if (!(mask[R0] & DA9xxx_MASK_SYS_MASK_0_M_SG) && + (event[R0] & DA9xxx_MASK_SYS_EVENT_0_E_SG)) { + dev_warn(chip->dev, "Handled E_SG\n"); + handled[R0] |= DA9xxx_MASK_SYS_EVENT_0_E_SG; + ret = IRQ_HANDLED; + } + + /* 0 TEMP_CRIT */ + if (!(mask[R0] & DA9121_MASK_SYS_MASK_0_M_TEMP_CRIT) && + (event[R0] & DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT)) { + err = da9121_handle_notifier(chip, rdev, + DA9121_REG_SYS_EVENT_0, event[R0], + DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT); + if (!err) { + handled[R0] |= DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT; + ret = IRQ_HANDLED; + } + } + + /* 0 TEMP_WARN */ + if (!(mask[R0] & DA9121_MASK_SYS_MASK_0_M_TEMP_WARN) && + (event[R0] & DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN)) { + err = da9121_handle_notifier(chip, rdev, + DA9121_REG_SYS_EVENT_0, event[R0], + DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN); + if (!err) { + handled[R0] |= DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN; + ret = IRQ_HANDLED; + } + } + + if (event[R0] != handled[R0]) { + dev_warn(chip->dev, + "Unhandled event in bank0 0x%02x\n", + event[R0] ^ handled[R0]); + } + + /* 1 PG1 */ + if (!(mask[R1] & DA9121_MASK_SYS_MASK_1_M_PG1) && + (event[R1] & DA9121_MASK_SYS_EVENT_1_E_PG1)) { + dev_warn(chip->dev, "Handled E_PG1\n"); + handled[R1] |= DA9121_MASK_SYS_EVENT_1_E_PG1; + ret = IRQ_HANDLED; + } + + /* 1 OV1 */ + if (!(mask[R1] & DA9121_MASK_SYS_MASK_1_M_OV1) && + (event[R1] & DA9121_MASK_SYS_EVENT_1_E_OV1)) { + err = da9121_handle_notifier(chip, rdev, + DA9121_REG_SYS_EVENT_1, event[R1], + DA9121_MASK_SYS_EVENT_1_E_OV1); + if (!err) { + handled[R1] |= DA9121_MASK_SYS_EVENT_1_E_OV1; + ret = IRQ_HANDLED; + } + } + + /* 1 UV1 */ + if (!(mask[R1] & DA9121_MASK_SYS_MASK_1_M_UV1) && + (event[R1] & DA9121_MASK_SYS_EVENT_1_E_UV1)) { + err = da9121_handle_notifier(chip, rdev, + DA9121_REG_SYS_EVENT_1, event[R1], + DA9121_MASK_SYS_EVENT_1_E_UV1); + if (!err) { + handled[R1] |= DA9121_MASK_SYS_EVENT_1_E_UV1; + ret = IRQ_HANDLED; + } + } + + /* 1 OC1 */ + if (!(mask[R1] & DA9121_MASK_SYS_MASK_1_M_OC1) && + (event[R1] & DA9121_MASK_SYS_EVENT_1_E_OC1)) { + err = da9121_handle_notifier(chip, rdev, + DA9121_REG_SYS_EVENT_1, event[R1], + DA9121_MASK_SYS_EVENT_1_E_OC1); + if (!err) { + handled[R1] |= DA9121_MASK_SYS_EVENT_1_E_OC1; + ret = IRQ_HANDLED; + } + } + + if (variant_parameters[chip->variant_id].num_bucks == 2) { + struct regulator_dev *rdev2 = chip->rdev[DA9121_IDX_BUCK2]; + + /* 1 PG2 */ + if (!(mask[R1] & DA9xxx_MASK_SYS_MASK_1_M_PG2) && + (event[R1] & DA9xxx_MASK_SYS_EVENT_1_E_PG2)) { + dev_warn(chip->dev, "Handled E_PG2\n"); + handled[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_PG2; + ret = IRQ_HANDLED; + } + + /* 1 OV2 */ + if (!(mask[R1] & DA9xxx_MASK_SYS_MASK_1_M_OV2) && + (event[R1] & DA9xxx_MASK_SYS_EVENT_1_E_OV2)) { + err = da9121_handle_notifier(chip, rdev2, + DA9121_REG_SYS_EVENT_1, event[R1], + DA9xxx_MASK_SYS_EVENT_1_E_OV2); + if (!err) { + handled[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_OV2; + ret = IRQ_HANDLED; + } + } + + /* 1 UV2 */ + if (!(mask[R1] & DA9xxx_MASK_SYS_MASK_1_M_UV2) && + (event[R1] & DA9xxx_MASK_SYS_EVENT_1_E_UV2)) { + err = da9121_handle_notifier(chip, rdev2, + DA9121_REG_SYS_EVENT_1, event[R1], + DA9xxx_MASK_SYS_EVENT_1_E_UV2); + if (!err) { + handled[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_UV2; + ret = IRQ_HANDLED; + } + } + + /* 1 OC2 */ + if (!(mask[R1] & DA9xxx_MASK_SYS_MASK_1_M_OC2) && + (event[R1] & DA9xxx_MASK_SYS_EVENT_1_E_OC2)) { + err = da9121_handle_notifier(chip, rdev2, + DA9121_REG_SYS_EVENT_1, event[R1], + DA9xxx_MASK_SYS_EVENT_1_E_OC2); + if (!err) { + handled[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_OC2; + ret = IRQ_HANDLED; + } + } + } + + if (event[R1] != handled[R1]) { + dev_warn(chip->dev, + "Unhandled event in bank1 0x%02x\n", + event[R1] ^ handled[R1]); + } + + /* DA9121_REG_SYS_EVENT_2 */ + if (!(mask[R2] & DA9121_MASK_SYS_MASK_2_M_GPIO2) && + (event[R2] & DA9121_MASK_SYS_EVENT_2_E_GPIO2)) { + dev_warn(chip->dev, "Handled E_GPIO2\n"); + handled[R2] |= DA9121_MASK_SYS_EVENT_2_E_GPIO2; + ret = IRQ_HANDLED; + } + + if (!(mask[R2] & DA9121_MASK_SYS_MASK_2_M_GPIO1) && + (event[R2] & DA9121_MASK_SYS_EVENT_2_E_GPIO1)) { + dev_warn(chip->dev, "Handled E_GPIO1\n"); + handled[R2] |= DA9121_MASK_SYS_EVENT_2_E_GPIO1; + ret = IRQ_HANDLED; + } + + if (!(mask[R2] & DA9121_MASK_SYS_MASK_2_M_GPIO0) && + (event[R2] & DA9121_MASK_SYS_EVENT_2_E_GPIO0)) { + dev_warn(chip->dev, "Handled E_GPIO0\n"); + handled[R2] |= DA9121_MASK_SYS_EVENT_2_E_GPIO0; + ret = IRQ_HANDLED; + } + + if (event[R2] != handled[R2]) { + dev_warn(chip->dev, + "Unhandled event in bank2 0x%02x\n", + event[R2] ^ handled[R2]); + } + + /* Mask the interrupts for persistent events OV, OC, UV, WARN, CRIT */ + for (i = R0; i < REG_MAX_NUM-1; i++) { + if (handled[i]) { + unsigned int reg = DA9121_REG_SYS_MASK_0 + i; + unsigned int mbit = handled[i]; + + err = regmap_update_bits(chip->regmap, reg, mbit, mbit); + if (err < 0) { + dev_err(chip->dev, + "Failed to mask 0x%02x interrupt %d\n", + reg, err); + ret = IRQ_NONE; + goto error; + } + } + } + + /* clear the events */ + if (handled[R0] | handled[R1] | handled[R2]) { + err = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_EVENT_0, + (const void *)handled, (size_t)REG_MAX_NUM); + if (err < 0) { + dev_err(chip->dev, "Fail to write EVENTs %d\n", err); + ret = IRQ_NONE; + goto error; + } + } + + queue_delayed_work(system_freezable_wq, &chip->work, 0); +error: + return ret; +} + static int da9121_set_regulator_config(struct da9121 *chip) { struct regulator_config config = { }; @@ -835,6 +1245,117 @@ static int da9121_assign_chip_model(struct i2c_client *i2c, return ret; } +static int da9121_set_irq_masks(struct da9121 *chip, bool mask_irqs) +{ + unsigned int mask0, update0; + unsigned int mask1, update1; + unsigned int mask3; + int ret = 0; + + if (chip->chip_irq != 0) { + mask0 = DA9121_MASK_SYS_MASK_0_M_TEMP_CRIT | + DA9121_MASK_SYS_MASK_0_M_TEMP_WARN; + + mask1 = DA9121_MASK_SYS_MASK_1_M_OV1 | + DA9121_MASK_SYS_MASK_1_M_UV1 | + DA9121_MASK_SYS_MASK_1_M_OC1; + + if (mask_irqs) { + update0 = mask0; + update1 = mask1; + } else { + update0 = 0; + update1 = 0; + } + + ret = regmap_update_bits(chip->regmap, + DA9121_REG_SYS_MASK_0, + mask0, + update0); + if (ret < 0) { + dev_err(chip->dev, "Failed to write MASK 0 reg %d\n", + ret); + goto error; + } + + ret = regmap_update_bits(chip->regmap, + DA9121_REG_SYS_MASK_1, + mask1, + update1); + if (ret < 0) { + dev_err(chip->dev, "Failed to write MASK 1 reg %d\n", + ret); + goto error; + } + + /* permanently disable IRQs for VR_HOT and PG1_STAT */ + mask3 = DA9121_MASK_SYS_MASK_3_M_VR_HOT | + DA9121_MASK_SYS_MASK_3_M_PG1_STAT; + + ret = regmap_update_bits(chip->regmap, + DA9121_REG_SYS_MASK_3, + mask3, + mask3); + if (ret < 0) { + dev_err(chip->dev, "Failed to write MASK 3 reg %d\n", + ret); + goto error; + } + } + +error: + return ret; +} + +static int da9121_config_irq(struct i2c_client *i2c, + struct da9121 *chip) +{ + unsigned int p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS; + int ret = 0; + + chip->chip_irq = i2c->irq; + + if (chip->chip_irq != 0) { + if (!of_property_read_u32(chip->dev->of_node, + "dlg,irq-polling-delay-passive", + &p_delay)) { + if (p_delay < DA9121_MIN_POLLING_PERIOD_MS || + p_delay > DA9121_MAX_POLLING_PERIOD_MS) { + dev_warn(chip->dev, + "Out-of-range polling period %d ms\n", + p_delay); + p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS; + } + } + + chip->passive_delay = p_delay; + + ret = devm_request_threaded_irq(chip->dev, + chip->chip_irq, NULL, + da9121_irq_handler, + IRQF_TRIGGER_LOW|IRQF_ONESHOT, + "da9121", chip); + if (ret != 0) { + dev_err(chip->dev, "Failed IRQ request: %d\n", + chip->chip_irq); + goto error; + } + + ret = da9121_set_irq_masks(chip, false); + if (ret != 0) { + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", + ret); + goto error; + } + + INIT_DELAYED_WORK(&chip->work, da9121_status_poll_on); + dev_info(chip->dev, "Interrupt polling period set at %d ms\n", + chip->passive_delay); + } +error: + return ret; +} + static const struct of_device_id da9121_dt_ids[] = { { .compatible = "dlg,da9121", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, { .compatible = "dlg,da9130", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, @@ -877,6 +1398,12 @@ static int da9121_i2c_probe(struct i2c_client *i2c, if (ret < 0) goto error; + ret = da9121_set_irq_masks(chip, true); + if (ret != 0) { + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret); + goto error; + } + if (!chip->pdata) chip->pdata = da9121_parse_regulators_dt(chip); @@ -889,6 +1416,26 @@ static int da9121_i2c_probe(struct i2c_client *i2c, if (ret < 0) goto error; + ret = da9121_config_irq(i2c, chip); + if (ret < 0) + goto error; + +error: + return ret; +} + +static int da9121_i2c_remove(struct i2c_client *i2c) +{ + struct da9121 *chip = i2c_get_clientdata(i2c); + int ret = 0; + + ret = da9121_set_irq_masks(chip, true); + if (ret != 0) { + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret); + goto error; + } + + cancel_delayed_work(&chip->work); error: return ret; } @@ -911,6 +1458,7 @@ static int da9121_i2c_probe(struct i2c_client *i2c, .of_match_table = of_match_ptr(da9121_dt_ids), }, .probe = da9121_i2c_probe, + .remove = da9121_i2c_remove, .id_table = da9121_i2c_id, }; -- 1.9.1