Contributors: 1
Author Tokens Token Proportion Commits Commit Proportion
Andre Draszik 2866 100.00% 1 100.00%
Total 2866 1


// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2020 Google Inc
 * Copyright 2025 Linaro Ltd.
 *
 * Core driver for Maxim MAX77759 companion PMIC for USB Type-C
 */

#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/completion.h>
#include <linux/dev_printk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/jiffies.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max77759.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/overflow.h>
#include <linux/regmap.h>

/* Chip ID as per MAX77759_PMIC_REG_PMIC_ID */
enum {
	MAX77759_CHIP_ID = 59,
};

enum max77759_i2c_subdev_id {
	/*
	 * These are arbitrary and simply used to match struct
	 * max77759_i2c_subdev entries to the regmap pointers in struct
	 * max77759 during probe().
	 */
	MAX77759_I2C_SUBDEV_ID_MAXQ,
	MAX77759_I2C_SUBDEV_ID_CHARGER,
};

struct max77759_i2c_subdev {
	enum max77759_i2c_subdev_id id;
	const struct regmap_config *cfg;
	u16 i2c_address;
};

static const struct regmap_range max77759_top_registers[] = {
	regmap_reg_range(0x00, 0x02), /* PMIC_ID / PMIC_REVISION / OTP_REVISION */
	regmap_reg_range(0x22, 0x24), /* INTSRC / INTSRCMASK / TOPSYS_INT */
	regmap_reg_range(0x26, 0x26), /* TOPSYS_INT_MASK */
	regmap_reg_range(0x40, 0x40), /* I2C_CNFG */
	regmap_reg_range(0x50, 0x51), /* SWRESET / CONTROL_FG */
};

static const struct regmap_range max77759_top_ro_registers[] = {
	regmap_reg_range(0x00, 0x02),
	regmap_reg_range(0x22, 0x22),
};

static const struct regmap_range max77759_top_volatile_registers[] = {
	regmap_reg_range(0x22, 0x22),
	regmap_reg_range(0x24, 0x24),
};

static const struct regmap_access_table max77759_top_wr_table = {
	.yes_ranges = max77759_top_registers,
	.n_yes_ranges = ARRAY_SIZE(max77759_top_registers),
	.no_ranges = max77759_top_ro_registers,
	.n_no_ranges = ARRAY_SIZE(max77759_top_ro_registers),
};

static const struct regmap_access_table max77759_top_rd_table = {
	.yes_ranges = max77759_top_registers,
	.n_yes_ranges = ARRAY_SIZE(max77759_top_registers),
};

static const struct regmap_access_table max77759_top_volatile_table = {
	.yes_ranges = max77759_top_volatile_registers,
	.n_yes_ranges = ARRAY_SIZE(max77759_top_volatile_registers),
};

static const struct regmap_config max77759_regmap_config_top = {
	.name = "top",
	.reg_bits = 8,
	.val_bits = 8,
	.max_register = MAX77759_PMIC_REG_CONTROL_FG,
	.wr_table = &max77759_top_wr_table,
	.rd_table = &max77759_top_rd_table,
	.volatile_table = &max77759_top_volatile_table,
	.num_reg_defaults_raw = MAX77759_PMIC_REG_CONTROL_FG + 1,
	.cache_type = REGCACHE_FLAT,
};

static const struct regmap_range max77759_maxq_registers[] = {
	regmap_reg_range(0x60, 0x73), /* Device ID, Rev, INTx, STATUSx, MASKx */
	regmap_reg_range(0x81, 0xa1), /* AP_DATAOUTx */
	regmap_reg_range(0xb1, 0xd1), /* AP_DATAINx */
	regmap_reg_range(0xe0, 0xe0), /* UIC_SWRST */
};

static const struct regmap_range max77759_maxq_ro_registers[] = {
	regmap_reg_range(0x60, 0x63), /* Device ID, Rev */
	regmap_reg_range(0x68, 0x6f), /* STATUSx */
	regmap_reg_range(0xb1, 0xd1),
};

static const struct regmap_range max77759_maxq_volatile_registers[] = {
	regmap_reg_range(0x64, 0x6f), /* INTx, STATUSx */
	regmap_reg_range(0xb1, 0xd1),
	regmap_reg_range(0xe0, 0xe0),
};

static const struct regmap_access_table max77759_maxq_wr_table = {
	.yes_ranges = max77759_maxq_registers,
	.n_yes_ranges = ARRAY_SIZE(max77759_maxq_registers),
	.no_ranges = max77759_maxq_ro_registers,
	.n_no_ranges = ARRAY_SIZE(max77759_maxq_ro_registers),
};

static const struct regmap_access_table max77759_maxq_rd_table = {
	.yes_ranges = max77759_maxq_registers,
	.n_yes_ranges = ARRAY_SIZE(max77759_maxq_registers),
};

static const struct regmap_access_table max77759_maxq_volatile_table = {
	.yes_ranges = max77759_maxq_volatile_registers,
	.n_yes_ranges = ARRAY_SIZE(max77759_maxq_volatile_registers),
};

static const struct regmap_config max77759_regmap_config_maxq = {
	.name = "maxq",
	.reg_bits = 8,
	.val_bits = 8,
	.max_register = MAX77759_MAXQ_REG_UIC_SWRST,
	.wr_table = &max77759_maxq_wr_table,
	.rd_table = &max77759_maxq_rd_table,
	.volatile_table = &max77759_maxq_volatile_table,
	.num_reg_defaults_raw = MAX77759_MAXQ_REG_UIC_SWRST + 1,
	.cache_type = REGCACHE_FLAT,
};

static const struct regmap_range max77759_charger_registers[] = {
	regmap_reg_range(0xb0, 0xcc),
};

static const struct regmap_range max77759_charger_ro_registers[] = {
	regmap_reg_range(0xb4, 0xb8), /* INT_OK, DETAILS_0x */
};

static const struct regmap_range max77759_charger_volatile_registers[] = {
	regmap_reg_range(0xb0, 0xb1), /* INTx */
	regmap_reg_range(0xb4, 0xb8),
};

static const struct regmap_access_table max77759_charger_wr_table = {
	.yes_ranges = max77759_charger_registers,
	.n_yes_ranges = ARRAY_SIZE(max77759_charger_registers),
	.no_ranges = max77759_charger_ro_registers,
	.n_no_ranges = ARRAY_SIZE(max77759_charger_ro_registers),
};

static const struct regmap_access_table max77759_charger_rd_table = {
	.yes_ranges = max77759_charger_registers,
	.n_yes_ranges = ARRAY_SIZE(max77759_charger_registers),
};

static const struct regmap_access_table max77759_charger_volatile_table = {
	.yes_ranges = max77759_charger_volatile_registers,
	.n_yes_ranges = ARRAY_SIZE(max77759_charger_volatile_registers),
};

static const struct regmap_config max77759_regmap_config_charger = {
	.name = "charger",
	.reg_bits = 8,
	.val_bits = 8,
	.max_register = MAX77759_CHGR_REG_CHG_CNFG_19,
	.wr_table = &max77759_charger_wr_table,
	.rd_table = &max77759_charger_rd_table,
	.volatile_table = &max77759_charger_volatile_table,
	.num_reg_defaults_raw = MAX77759_CHGR_REG_CHG_CNFG_19 + 1,
	.cache_type = REGCACHE_FLAT,
};

/*
 * Interrupts - with the following interrupt hierarchy:
 *   pmic IRQs (INTSRC)
 *     - MAXQ_INT: MaxQ IRQs
 *       - UIC_INT1
 *         - APCmdResI
 *         - SysMsgI
 *         - GPIOxI
 *     - TOPSYS_INT: topsys
 *       - TOPSYS_INT
 *         - TSHDN_INT
 *         - SYSOVLO_INT
 *         - SYSUVLO_INT
 *         - FSHIP_NOT_RD
 *     - CHGR_INT: charger
 *       - CHG_INT
 *       - CHG_INT2
 */
enum {
	MAX77759_INT_MAXQ,
	MAX77759_INT_TOPSYS,
	MAX77759_INT_CHGR,
};

enum {
	MAX77759_TOPSYS_INT_TSHDN,
	MAX77759_TOPSYS_INT_SYSOVLO,
	MAX77759_TOPSYS_INT_SYSUVLO,
	MAX77759_TOPSYS_INT_FSHIP_NOT_RD,
};

enum {
	MAX77759_MAXQ_INT_APCMDRESI,
	MAX77759_MAXQ_INT_SYSMSGI,
	MAX77759_MAXQ_INT_GPIO,
	MAX77759_MAXQ_INT_UIC1,
	MAX77759_MAXQ_INT_UIC2,
	MAX77759_MAXQ_INT_UIC3,
	MAX77759_MAXQ_INT_UIC4,
};

enum {
	MAX77759_CHARGER_INT_1,
	MAX77759_CHARGER_INT_2,
};

static const struct regmap_irq max77759_pmic_irqs[] = {
	REGMAP_IRQ_REG(MAX77759_INT_MAXQ, 0, MAX77759_PMIC_REG_INTSRC_MAXQ),
	REGMAP_IRQ_REG(MAX77759_INT_TOPSYS, 0, MAX77759_PMIC_REG_INTSRC_TOPSYS),
	REGMAP_IRQ_REG(MAX77759_INT_CHGR, 0, MAX77759_PMIC_REG_INTSRC_CHGR),
};

static const struct regmap_irq max77759_maxq_irqs[] = {
	REGMAP_IRQ_REG(MAX77759_MAXQ_INT_APCMDRESI, 0, MAX77759_MAXQ_REG_UIC_INT1_APCMDRESI),
	REGMAP_IRQ_REG(MAX77759_MAXQ_INT_SYSMSGI, 0, MAX77759_MAXQ_REG_UIC_INT1_SYSMSGI),
	REGMAP_IRQ_REG(MAX77759_MAXQ_INT_GPIO, 0, GENMASK(1, 0)),
	REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC1, 0, GENMASK(5, 2)),
	REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC2, 1, GENMASK(7, 0)),
	REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC3, 2, GENMASK(7, 0)),
	REGMAP_IRQ_REG(MAX77759_MAXQ_INT_UIC4, 3, GENMASK(7, 0)),
};

static const struct regmap_irq max77759_topsys_irqs[] = {
	REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_TSHDN, 0, MAX77759_PMIC_REG_TOPSYS_INT_TSHDN),
	REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_SYSOVLO, 0, MAX77759_PMIC_REG_TOPSYS_INT_SYSOVLO),
	REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_SYSUVLO, 0, MAX77759_PMIC_REG_TOPSYS_INT_SYSUVLO),
	REGMAP_IRQ_REG(MAX77759_TOPSYS_INT_FSHIP_NOT_RD, 0, MAX77759_PMIC_REG_TOPSYS_INT_FSHIP),
};

static const struct regmap_irq max77759_chgr_irqs[] = {
	REGMAP_IRQ_REG(MAX77759_CHARGER_INT_1, 0, GENMASK(7, 0)),
	REGMAP_IRQ_REG(MAX77759_CHARGER_INT_2, 1, GENMASK(7, 0)),
};

static const struct regmap_irq_chip max77759_pmic_irq_chip = {
	.name = "max77759-pmic",
	/* INTSRC is read-only and doesn't require clearing */
	.status_base = MAX77759_PMIC_REG_INTSRC,
	.mask_base = MAX77759_PMIC_REG_INTSRCMASK,
	.num_regs = 1,
	.irqs = max77759_pmic_irqs,
	.num_irqs = ARRAY_SIZE(max77759_pmic_irqs),
};

/*
 * We can let regmap-irq auto-ack the topsys interrupt bits as required, but
 * for all others the individual drivers need to know which interrupt bit
 * exactly is set inside their interrupt handlers, and therefore we can not set
 * .ack_base for those.
 */
static const struct regmap_irq_chip max77759_maxq_irq_chip = {
	.name = "max77759-maxq",
	.domain_suffix = "MAXQ",
	.status_base = MAX77759_MAXQ_REG_UIC_INT1,
	.mask_base = MAX77759_MAXQ_REG_UIC_INT1_M,
	.num_regs = 4,
	.irqs = max77759_maxq_irqs,
	.num_irqs = ARRAY_SIZE(max77759_maxq_irqs),
};

static const struct regmap_irq_chip max77759_topsys_irq_chip = {
	.name = "max77759-topsys",
	.domain_suffix = "TOPSYS",
	.status_base = MAX77759_PMIC_REG_TOPSYS_INT,
	.mask_base = MAX77759_PMIC_REG_TOPSYS_INT_MASK,
	.ack_base = MAX77759_PMIC_REG_TOPSYS_INT,
	.num_regs = 1,
	.irqs = max77759_topsys_irqs,
	.num_irqs = ARRAY_SIZE(max77759_topsys_irqs),
};

static const struct regmap_irq_chip max77759_chrg_irq_chip = {
	.name = "max77759-chgr",
	.domain_suffix = "CHGR",
	.status_base = MAX77759_CHGR_REG_CHG_INT,
	.mask_base = MAX77759_CHGR_REG_CHG_INT_MASK,
	.num_regs = 2,
	.irqs = max77759_chgr_irqs,
	.num_irqs = ARRAY_SIZE(max77759_chgr_irqs),
};

static const struct max77759_i2c_subdev max77759_i2c_subdevs[] = {
	{
		.id = MAX77759_I2C_SUBDEV_ID_MAXQ,
		.cfg = &max77759_regmap_config_maxq,
		/* I2C address is same as for sub-block 'top' */
	},
	{
		.id = MAX77759_I2C_SUBDEV_ID_CHARGER,
		.cfg = &max77759_regmap_config_charger,
		.i2c_address = 0x69,
	},
};

static const struct resource max77759_gpio_resources[] = {
	DEFINE_RES_IRQ_NAMED(MAX77759_MAXQ_INT_GPIO, "GPI"),
};

static const struct resource max77759_charger_resources[] = {
	DEFINE_RES_IRQ_NAMED(MAX77759_CHARGER_INT_1, "INT1"),
	DEFINE_RES_IRQ_NAMED(MAX77759_CHARGER_INT_2, "INT2"),
};

static const struct mfd_cell max77759_cells[] = {
	MFD_CELL_OF("max77759-nvmem", NULL, NULL, 0, 0,
		    "maxim,max77759-nvmem"),
};

static const struct mfd_cell max77759_maxq_cells[] = {
	MFD_CELL_OF("max77759-gpio", max77759_gpio_resources, NULL, 0, 0,
		    "maxim,max77759-gpio"),
};

static const struct mfd_cell max77759_charger_cells[] = {
	MFD_CELL_RES("max77759-charger", max77759_charger_resources),
};

int max77759_maxq_command(struct max77759 *max77759,
			  const struct max77759_maxq_command *cmd,
			  struct max77759_maxq_response *rsp)
{
	DEFINE_FLEX(struct max77759_maxq_response, _rsp, rsp, length, 1);
	struct device *dev = regmap_get_device(max77759->regmap_maxq);
	static const unsigned int timeout_ms = 200;
	int ret;

	if (cmd->length > MAX77759_MAXQ_OPCODE_MAXLENGTH)
		return -EINVAL;

	/*
	 * As a convenience for API users when issuing simple commands, rsp is
	 * allowed to be NULL. In that case we need a temporary here to write
	 * the response to, as we need to verify that the command was indeed
	 * completed correctly.
	 */
	if (!rsp)
		rsp = _rsp;

	if (!rsp->length || rsp->length > MAX77759_MAXQ_OPCODE_MAXLENGTH)
		return -EINVAL;

	guard(mutex)(&max77759->maxq_lock);

	reinit_completion(&max77759->cmd_done);

	/*
	 * MaxQ latches the message when the DATAOUT32 register is written. If
	 * cmd->length is shorter we still need to write 0 to it.
	 */
	ret = regmap_bulk_write(max77759->regmap_maxq,
				MAX77759_MAXQ_REG_AP_DATAOUT0, cmd->cmd,
				cmd->length);
	if (!ret && cmd->length < MAX77759_MAXQ_OPCODE_MAXLENGTH)
		ret = regmap_write(max77759->regmap_maxq,
				   MAX77759_MAXQ_REG_AP_DATAOUT32, 0);
	if (ret) {
		dev_err(dev, "writing command failed: %d\n", ret);
		return ret;
	}

	/* Wait for response from MaxQ */
	if (!wait_for_completion_timeout(&max77759->cmd_done,
					 msecs_to_jiffies(timeout_ms))) {
		dev_err(dev, "timed out waiting for response\n");
		return -ETIMEDOUT;
	}

	ret = regmap_bulk_read(max77759->regmap_maxq,
			       MAX77759_MAXQ_REG_AP_DATAIN0,
			       rsp->rsp, rsp->length);
	if (ret) {
		dev_err(dev, "reading response failed: %d\n", ret);
		return ret;
	}

	/*
	 * As per the protocol, the first byte of the reply will match the
	 * request.
	 */
	if (cmd->cmd[0] != rsp->rsp[0]) {
		dev_err(dev, "unexpected opcode response for %#.2x: %*ph\n",
			cmd->cmd[0], (int)rsp->length, rsp->rsp);
		return -EIO;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(max77759_maxq_command);

static irqreturn_t apcmdres_irq_handler(int irq, void *irq_data)
{
	struct max77759 *max77759 = irq_data;

	regmap_write(max77759->regmap_maxq, MAX77759_MAXQ_REG_UIC_INT1,
		     MAX77759_MAXQ_REG_UIC_INT1_APCMDRESI);

	complete(&max77759->cmd_done);

	return IRQ_HANDLED;
}

static int max77759_create_i2c_subdev(struct i2c_client *client,
				      struct max77759 *max77759,
				      const struct max77759_i2c_subdev *sd)
{
	struct i2c_client *sub;
	struct regmap *regmap;
	int ret;

	/*
	 * If 'sd' has an I2C address, 'sub' will be assigned a new 'dummy'
	 * device, otherwise use it as-is.
	 */
	sub = client;
	if (sd->i2c_address) {
		sub = devm_i2c_new_dummy_device(&client->dev,
						client->adapter,
						sd->i2c_address);

		if (IS_ERR(sub))
			return dev_err_probe(&client->dev, PTR_ERR(sub),
					"failed to claim I2C device %s\n",
					sd->cfg->name);
	}

	regmap = devm_regmap_init_i2c(sub, sd->cfg);
	if (IS_ERR(regmap))
		return dev_err_probe(&sub->dev, PTR_ERR(regmap),
				     "regmap init for '%s' failed\n",
				     sd->cfg->name);

	ret = regmap_attach_dev(&client->dev, regmap, sd->cfg);
	if (ret)
		return dev_err_probe(&client->dev, ret,
				     "regmap attach of '%s' failed\n",
				     sd->cfg->name);

	if (sd->id == MAX77759_I2C_SUBDEV_ID_MAXQ)
		max77759->regmap_maxq = regmap;
	else if (sd->id == MAX77759_I2C_SUBDEV_ID_CHARGER)
		max77759->regmap_charger = regmap;

	return 0;
}

static int max77759_add_chained_irq_chip(struct device *dev,
					 struct regmap *regmap,
					 int pirq,
					 struct regmap_irq_chip_data *parent,
					 const struct regmap_irq_chip *chip,
					 struct regmap_irq_chip_data **data)
{
	int irq, ret;

	irq = regmap_irq_get_virq(parent, pirq);
	if (irq < 0)
		return dev_err_probe(dev, irq,
				     "failed to get parent vIRQ(%d) for chip %s\n",
				     pirq, chip->name);

	ret = devm_regmap_add_irq_chip(dev, regmap, irq,
				       IRQF_ONESHOT | IRQF_SHARED, 0, chip,
				       data);
	if (ret)
		return dev_err_probe(dev, ret, "failed to add %s IRQ chip\n",
				     chip->name);

	return 0;
}

static int max77759_add_chained_maxq(struct i2c_client *client,
				     struct max77759 *max77759,
				     struct regmap_irq_chip_data *parent)
{
	struct regmap_irq_chip_data *irq_chip_data;
	int apcmdres_irq;
	int ret;

	ret = max77759_add_chained_irq_chip(&client->dev,
					    max77759->regmap_maxq,
					    MAX77759_INT_MAXQ,
					    parent,
					    &max77759_maxq_irq_chip,
					    &irq_chip_data);
	if (ret)
		return ret;

	init_completion(&max77759->cmd_done);
	apcmdres_irq = regmap_irq_get_virq(irq_chip_data,
					   MAX77759_MAXQ_INT_APCMDRESI);

	ret = devm_request_threaded_irq(&client->dev, apcmdres_irq,
					NULL, apcmdres_irq_handler,
					IRQF_ONESHOT | IRQF_SHARED,
					dev_name(&client->dev), max77759);
	if (ret)
		return dev_err_probe(&client->dev, ret,
				     "MAX77759_MAXQ_INT_APCMDRESI failed\n");

	ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO,
				   max77759_maxq_cells,
				   ARRAY_SIZE(max77759_maxq_cells),
				   NULL, 0,
				   regmap_irq_get_domain(irq_chip_data));
	if (ret)
		return dev_err_probe(&client->dev, ret,
				     "failed to add child devices (MaxQ)\n");

	return 0;
}

static int max77759_add_chained_topsys(struct i2c_client *client,
				       struct max77759 *max77759,
				       struct regmap_irq_chip_data *parent)
{
	struct regmap_irq_chip_data *irq_chip_data;
	int ret;

	ret = max77759_add_chained_irq_chip(&client->dev,
					    max77759->regmap_top,
					    MAX77759_INT_TOPSYS,
					    parent,
					    &max77759_topsys_irq_chip,
					    &irq_chip_data);
	if (ret)
		return ret;

	return 0;
}

static int max77759_add_chained_charger(struct i2c_client *client,
					struct max77759 *max77759,
					struct regmap_irq_chip_data *parent)
{
	struct regmap_irq_chip_data *irq_chip_data;
	int ret;

	ret = max77759_add_chained_irq_chip(&client->dev,
					    max77759->regmap_charger,
					    MAX77759_INT_CHGR,
					    parent,
					    &max77759_chrg_irq_chip,
					    &irq_chip_data);
	if (ret)
		return ret;

	ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO,
				   max77759_charger_cells,
				   ARRAY_SIZE(max77759_charger_cells),
				   NULL, 0,
				   regmap_irq_get_domain(irq_chip_data));
	if (ret)
		return dev_err_probe(&client->dev, ret,
				     "failed to add child devices (charger)\n");

	return 0;
}

static int max77759_probe(struct i2c_client *client)
{
	struct regmap_irq_chip_data *irq_chip_data_pmic;
	struct irq_data *irq_data;
	struct max77759 *max77759;
	unsigned long irq_flags;
	unsigned int pmic_id;
	int ret;

	max77759 = devm_kzalloc(&client->dev, sizeof(*max77759), GFP_KERNEL);
	if (!max77759)
		return -ENOMEM;

	i2c_set_clientdata(client, max77759);

	max77759->regmap_top = devm_regmap_init_i2c(client,
						    &max77759_regmap_config_top);
	if (IS_ERR(max77759->regmap_top))
		return dev_err_probe(&client->dev, PTR_ERR(max77759->regmap_top),
				     "regmap init for '%s' failed\n",
				     max77759_regmap_config_top.name);

	ret = regmap_read(max77759->regmap_top,
			  MAX77759_PMIC_REG_PMIC_ID, &pmic_id);
	if (ret)
		return dev_err_probe(&client->dev, ret,
				     "unable to read device ID\n");

	if (pmic_id != MAX77759_CHIP_ID)
		return dev_err_probe(&client->dev, -ENODEV,
				     "unsupported device ID %#.2x (%d)\n",
				     pmic_id, pmic_id);

	ret = devm_mutex_init(&client->dev, &max77759->maxq_lock);
	if (ret)
		return ret;

	for (int i = 0; i < ARRAY_SIZE(max77759_i2c_subdevs); i++) {
		ret = max77759_create_i2c_subdev(client, max77759,
						 &max77759_i2c_subdevs[i]);
		if (ret)
			return ret;
	}

	irq_data = irq_get_irq_data(client->irq);
	if (!irq_data)
		return dev_err_probe(&client->dev, -EINVAL,
				     "invalid IRQ: %d\n", client->irq);

	irq_flags = IRQF_ONESHOT | IRQF_SHARED;
	irq_flags |= irqd_get_trigger_type(irq_data);

	ret = devm_regmap_add_irq_chip(&client->dev, max77759->regmap_top,
				       client->irq, irq_flags, 0,
				       &max77759_pmic_irq_chip,
				       &irq_chip_data_pmic);
	if (ret)
		return dev_err_probe(&client->dev, ret,
				     "failed to add IRQ chip '%s'\n",
				     max77759_pmic_irq_chip.name);

	ret = max77759_add_chained_maxq(client, max77759, irq_chip_data_pmic);
	if (ret)
		return ret;

	ret = max77759_add_chained_topsys(client, max77759, irq_chip_data_pmic);
	if (ret)
		return ret;

	ret = max77759_add_chained_charger(client, max77759, irq_chip_data_pmic);
	if (ret)
		return ret;

	return devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO,
				    max77759_cells, ARRAY_SIZE(max77759_cells),
				    NULL, 0,
				    regmap_irq_get_domain(irq_chip_data_pmic));
}

static const struct i2c_device_id max77759_i2c_id[] = {
	{ "max77759" },
	{ }
};
MODULE_DEVICE_TABLE(i2c, max77759_i2c_id);

static const struct of_device_id max77759_of_id[] = {
	{ .compatible = "maxim,max77759", },
	{ }
};
MODULE_DEVICE_TABLE(of, max77759_of_id);

static struct i2c_driver max77759_i2c_driver = {
	.driver = {
		.name = "max77759",
		.of_match_table = max77759_of_id,
	},
	.probe = max77759_probe,
	.id_table = max77759_i2c_id,
};
module_i2c_driver(max77759_i2c_driver);

MODULE_AUTHOR("André Draszik <andre.draszik@linaro.org>");
MODULE_DESCRIPTION("Maxim MAX77759 core driver");
MODULE_LICENSE("GPL");