// SPDX-License-Identifier: GPL-2.0 /* * Core driver for the PF1550 * * Copyright (C) 2016 Freescale Semiconductor, Inc. * Robin Gong * * Portions Copyright (c) 2025 Savoir-faire Linux Inc. * Samuel Kayode */ #include #include #include #include #include #include #include #include static const struct regmap_config pf1550_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = PF1550_PMIC_REG_END, }; static const struct regmap_irq pf1550_irqs[] = { REGMAP_IRQ_REG(PF1550_IRQ_CHG, 0, IRQ_CHG), REGMAP_IRQ_REG(PF1550_IRQ_REGULATOR, 0, IRQ_REGULATOR), REGMAP_IRQ_REG(PF1550_IRQ_ONKEY, 0, IRQ_ONKEY), }; static const struct regmap_irq_chip pf1550_irq_chip = { .name = "pf1550", .status_base = PF1550_PMIC_REG_INT_CATEGORY, .init_ack_masked = 1, .num_regs = 1, .irqs = pf1550_irqs, .num_irqs = ARRAY_SIZE(pf1550_irqs), }; static const struct regmap_irq pf1550_regulator_irqs[] = { REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_LS, 0, PMIC_IRQ_SW1_LS), REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_LS, 0, PMIC_IRQ_SW2_LS), REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_LS, 0, PMIC_IRQ_SW3_LS), REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_HS, 3, PMIC_IRQ_SW1_HS), REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_HS, 3, PMIC_IRQ_SW2_HS), REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_HS, 3, PMIC_IRQ_SW3_HS), REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO1_FAULT, 16, PMIC_IRQ_LDO1_FAULT), REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO2_FAULT, 16, PMIC_IRQ_LDO2_FAULT), REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO3_FAULT, 16, PMIC_IRQ_LDO3_FAULT), REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_110, 24, PMIC_IRQ_TEMP_110), REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_125, 24, PMIC_IRQ_TEMP_125), }; static const struct regmap_irq_chip pf1550_regulator_irq_chip = { .name = "pf1550-regulator", .status_base = PF1550_PMIC_REG_SW_INT_STAT0, .ack_base = PF1550_PMIC_REG_SW_INT_STAT0, .mask_base = PF1550_PMIC_REG_SW_INT_MASK0, .use_ack = 1, .init_ack_masked = 1, .num_regs = 25, .irqs = pf1550_regulator_irqs, .num_irqs = ARRAY_SIZE(pf1550_regulator_irqs), }; static const struct resource regulator_resources[] = { DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_LS), DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_LS), DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_LS), DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_HS), DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_HS), DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_HS), DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO1_FAULT), DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO2_FAULT), DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO3_FAULT), DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_110), DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_125), }; static const struct regmap_irq pf1550_onkey_irqs[] = { REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_PUSHI, 0, ONKEY_IRQ_PUSHI), REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_1SI, 0, ONKEY_IRQ_1SI), REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_2SI, 0, ONKEY_IRQ_2SI), REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_3SI, 0, ONKEY_IRQ_3SI), REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_4SI, 0, ONKEY_IRQ_4SI), REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_8SI, 0, ONKEY_IRQ_8SI), }; static const struct regmap_irq_chip pf1550_onkey_irq_chip = { .name = "pf1550-onkey", .status_base = PF1550_PMIC_REG_ONKEY_INT_STAT0, .ack_base = PF1550_PMIC_REG_ONKEY_INT_STAT0, .mask_base = PF1550_PMIC_REG_ONKEY_INT_MASK0, .use_ack = 1, .init_ack_masked = 1, .num_regs = 1, .irqs = pf1550_onkey_irqs, .num_irqs = ARRAY_SIZE(pf1550_onkey_irqs), }; static const struct resource onkey_resources[] = { DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_PUSHI), DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_1SI), DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_2SI), DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_3SI), DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_4SI), DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_8SI), }; static const struct regmap_irq pf1550_charger_irqs[] = { REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BAT2SOCI, 0, CHARG_IRQ_BAT2SOCI), REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BATI, 0, CHARG_IRQ_BATI), REGMAP_IRQ_REG(PF1550_CHARG_IRQ_CHGI, 0, CHARG_IRQ_CHGI), REGMAP_IRQ_REG(PF1550_CHARG_IRQ_VBUSI, 0, CHARG_IRQ_VBUSI), REGMAP_IRQ_REG(PF1550_CHARG_IRQ_THMI, 0, CHARG_IRQ_THMI), }; static const struct regmap_irq_chip pf1550_charger_irq_chip = { .name = "pf1550-charger", .status_base = PF1550_CHARG_REG_CHG_INT, .ack_base = PF1550_CHARG_REG_CHG_INT, .mask_base = PF1550_CHARG_REG_CHG_INT_MASK, .use_ack = 1, .init_ack_masked = 1, .num_regs = 1, .irqs = pf1550_charger_irqs, .num_irqs = ARRAY_SIZE(pf1550_charger_irqs), }; static const struct resource charger_resources[] = { DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BAT2SOCI), DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BATI), DEFINE_RES_IRQ(PF1550_CHARG_IRQ_CHGI), DEFINE_RES_IRQ(PF1550_CHARG_IRQ_VBUSI), DEFINE_RES_IRQ(PF1550_CHARG_IRQ_THMI), }; static const struct mfd_cell pf1550_regulator_cell = { .name = "pf1550-regulator", .num_resources = ARRAY_SIZE(regulator_resources), .resources = regulator_resources, }; static const struct mfd_cell pf1550_onkey_cell = { .name = "pf1550-onkey", .num_resources = ARRAY_SIZE(onkey_resources), .resources = onkey_resources, }; static const struct mfd_cell pf1550_charger_cell = { .name = "pf1550-charger", .num_resources = ARRAY_SIZE(charger_resources), .resources = charger_resources, }; /* * The PF1550 is shipped in variants of A0, A1,...A9. Each variant defines a * configuration of the PMIC in a One-Time Programmable (OTP) memory. * This memory is accessed indirectly by writing valid keys to specific * registers of the PMIC. To read the OTP memory after writing the valid keys, * the OTP register address to be read is written to pf1550 register 0xc4 and * its value read from pf1550 register 0xc5. */ static int pf1550_read_otp(const struct pf1550_ddata *pf1550, unsigned int index, unsigned int *val) { int ret = 0; ret = regmap_write(pf1550->regmap, PF1550_PMIC_REG_KEY, PF1550_OTP_PMIC_KEY); if (ret) goto read_err; ret = regmap_write(pf1550->regmap, PF1550_CHARG_REG_CHGR_KEY2, PF1550_OTP_CHGR_KEY); if (ret) goto read_err; ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_KEY3, PF1550_OTP_TEST_KEY); if (ret) goto read_err; ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_FMRADDR, index); if (ret) goto read_err; ret = regmap_read(pf1550->regmap, PF1550_TEST_REG_FMRDATA, val); if (ret) goto read_err; return 0; read_err: return dev_err_probe(pf1550->dev, ret, "OTP reg %x not found!\n", index); } static int pf1550_i2c_probe(struct i2c_client *i2c) { const struct mfd_cell *regulator = &pf1550_regulator_cell; const struct mfd_cell *charger = &pf1550_charger_cell; const struct mfd_cell *onkey = &pf1550_onkey_cell; unsigned int reg_data = 0, otp_data = 0; struct pf1550_ddata *pf1550; struct irq_domain *domain; int irq, ret = 0; pf1550 = devm_kzalloc(&i2c->dev, sizeof(*pf1550), GFP_KERNEL); if (!pf1550) return -ENOMEM; i2c_set_clientdata(i2c, pf1550); pf1550->dev = &i2c->dev; pf1550->irq = i2c->irq; pf1550->regmap = devm_regmap_init_i2c(i2c, &pf1550_regmap_config); if (IS_ERR(pf1550->regmap)) return dev_err_probe(pf1550->dev, PTR_ERR(pf1550->regmap), "failed to allocate register map\n"); ret = regmap_read(pf1550->regmap, PF1550_PMIC_REG_DEVICE_ID, ®_data); if (ret < 0) return dev_err_probe(pf1550->dev, ret, "cannot read chip ID\n"); if (reg_data != PF1550_DEVICE_ID) return dev_err_probe(pf1550->dev, -ENODEV, "invalid device ID: 0x%02x\n", reg_data); /* Regulator DVS for SW2 */ ret = pf1550_read_otp(pf1550, PF1550_OTP_SW2_SW3, &otp_data); if (ret) return ret; /* When clear, DVS should be enabled */ if (!(otp_data & OTP_SW2_DVS_ENB)) pf1550->dvs2_enable = true; /* Regulator DVS for SW1 */ ret = pf1550_read_otp(pf1550, PF1550_OTP_SW1_SW2, &otp_data); if (ret) return ret; if (!(otp_data & OTP_SW1_DVS_ENB)) pf1550->dvs1_enable = true; /* Add top level interrupts */ ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, pf1550->irq, IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_FALLING, 0, &pf1550_irq_chip, &pf1550->irq_data); if (ret) return ret; /* Add regulator */ irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_REGULATOR); if (irq < 0) return dev_err_probe(pf1550->dev, irq, "Failed to get parent vIRQ(%d) for chip %s\n", PF1550_IRQ_REGULATOR, pf1550_irq_chip.name); ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq, IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_FALLING, 0, &pf1550_regulator_irq_chip, &pf1550->irq_data_regulator); if (ret) return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n", pf1550_regulator_irq_chip.name); domain = regmap_irq_get_domain(pf1550->irq_data_regulator); ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, regulator, 1, NULL, 0, domain); if (ret) return ret; /* Add onkey */ irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_ONKEY); if (irq < 0) return dev_err_probe(pf1550->dev, irq, "Failed to get parent vIRQ(%d) for chip %s\n", PF1550_IRQ_ONKEY, pf1550_irq_chip.name); ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq, IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_FALLING, 0, &pf1550_onkey_irq_chip, &pf1550->irq_data_onkey); if (ret) return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n", pf1550_onkey_irq_chip.name); domain = regmap_irq_get_domain(pf1550->irq_data_onkey); ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, onkey, 1, NULL, 0, domain); if (ret) return ret; /* Add battery charger */ irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_CHG); if (irq < 0) return dev_err_probe(pf1550->dev, irq, "Failed to get parent vIRQ(%d) for chip %s\n", PF1550_IRQ_CHG, pf1550_irq_chip.name); ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq, IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_FALLING, 0, &pf1550_charger_irq_chip, &pf1550->irq_data_charger); if (ret) return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n", pf1550_charger_irq_chip.name); domain = regmap_irq_get_domain(pf1550->irq_data_charger); return devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, charger, 1, NULL, 0, domain); } static int pf1550_suspend(struct device *dev) { struct pf1550_ddata *pf1550 = dev_get_drvdata(dev); if (device_may_wakeup(dev)) { enable_irq_wake(pf1550->irq); disable_irq(pf1550->irq); } return 0; } static int pf1550_resume(struct device *dev) { struct pf1550_ddata *pf1550 = dev_get_drvdata(dev); if (device_may_wakeup(dev)) { disable_irq_wake(pf1550->irq); enable_irq(pf1550->irq); } return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(pf1550_pm, pf1550_suspend, pf1550_resume); static const struct i2c_device_id pf1550_i2c_id[] = { { "pf1550" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, pf1550_i2c_id); static const struct of_device_id pf1550_dt_match[] = { { .compatible = "nxp,pf1550" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, pf1550_dt_match); static struct i2c_driver pf1550_i2c_driver = { .driver = { .name = "pf1550", .pm = pm_sleep_ptr(&pf1550_pm), .of_match_table = pf1550_dt_match, }, .probe = pf1550_i2c_probe, .id_table = pf1550_i2c_id, }; module_i2c_driver(pf1550_i2c_driver); MODULE_DESCRIPTION("NXP PF1550 core driver"); MODULE_AUTHOR("Robin Gong "); MODULE_LICENSE("GPL");