···190190 To compile this driver as a module, choose M here: the191191 module will be called pcspkr.192192193193+config INPUT_PF1550_ONKEY194194+ tristate "NXP PF1550 Onkey support"195195+ depends on MFD_PF1550196196+ help197197+ Say Y here if you want support for PF1550 PMIC. Onkey can trigger198198+ release and 1s(push hold), 2s, 3s, 4s, 8s interrupt for long press199199+ detect.200200+201201+ To compile this driver as a module, choose M here. The module will be202202+ called pf1550-onkey.203203+193204config INPUT_PM8941_PWRKEY194205 tristate "Qualcomm PM8941 power key support"195206 depends on MFD_SPMI_PMIC
···11+// SPDX-License-Identifier: GPL-2.022+/*33+ * Driver for the PF1550 ONKEY44+ * Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved.55+ *66+ * Portions Copyright (c) 2025 Savoir-faire Linux Inc.77+ * Samuel Kayode <samuel.kayode@savoirfairelinux.com>88+ */99+1010+#include <linux/err.h>1111+#include <linux/input.h>1212+#include <linux/interrupt.h>1313+#include <linux/kernel.h>1414+#include <linux/module.h>1515+#include <linux/mfd/pf1550.h>1616+#include <linux/platform_device.h>1717+1818+#define PF1550_ONKEY_IRQ_NR 61919+2020+struct onkey_drv_data {2121+ struct device *dev;2222+ const struct pf1550_ddata *pf1550;2323+ bool wakeup;2424+ struct input_dev *input;2525+};2626+2727+static irqreturn_t pf1550_onkey_irq_handler(int irq, void *data)2828+{2929+ struct onkey_drv_data *onkey = data;3030+ struct platform_device *pdev = to_platform_device(onkey->dev);3131+ int i, state, irq_type = -1;3232+3333+ for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++)3434+ if (irq == platform_get_irq(pdev, i))3535+ irq_type = i;3636+3737+ switch (irq_type) {3838+ case PF1550_ONKEY_IRQ_PUSHI:3939+ state = 0;4040+ break;4141+ case PF1550_ONKEY_IRQ_1SI:4242+ case PF1550_ONKEY_IRQ_2SI:4343+ case PF1550_ONKEY_IRQ_3SI:4444+ case PF1550_ONKEY_IRQ_4SI:4545+ case PF1550_ONKEY_IRQ_8SI:4646+ state = 1;4747+ break;4848+ default:4949+ dev_err(onkey->dev, "onkey interrupt: irq %d occurred\n",5050+ irq_type);5151+ return IRQ_HANDLED;5252+ }5353+5454+ input_event(onkey->input, EV_KEY, KEY_POWER, state);5555+ input_sync(onkey->input);5656+5757+ return IRQ_HANDLED;5858+}5959+6060+static int pf1550_onkey_probe(struct platform_device *pdev)6161+{6262+ struct onkey_drv_data *onkey;6363+ struct input_dev *input;6464+ bool key_power = false;6565+ int i, irq, error;6666+6767+ onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);6868+ if (!onkey)6969+ return -ENOMEM;7070+7171+ onkey->dev = &pdev->dev;7272+7373+ onkey->pf1550 = dev_get_drvdata(pdev->dev.parent);7474+ if (!onkey->pf1550->regmap)7575+ return dev_err_probe(&pdev->dev, -ENODEV,7676+ "failed to get regmap\n");7777+7878+ onkey->wakeup = device_property_read_bool(pdev->dev.parent,7979+ "wakeup-source");8080+8181+ if (device_property_read_bool(pdev->dev.parent,8282+ "nxp,disable-key-power")) {8383+ error = regmap_clear_bits(onkey->pf1550->regmap,8484+ PF1550_PMIC_REG_PWRCTRL1,8585+ PF1550_ONKEY_RST_EN);8686+ if (error)8787+ return dev_err_probe(&pdev->dev, error,8888+ "failed: disable turn system off");8989+ } else {9090+ key_power = true;9191+ }9292+9393+ input = devm_input_allocate_device(&pdev->dev);9494+ if (!input)9595+ return dev_err_probe(&pdev->dev, -ENOMEM,9696+ "failed to allocate the input device\n");9797+9898+ input->name = pdev->name;9999+ input->phys = "pf1550-onkey/input0";100100+ input->id.bustype = BUS_HOST;101101+102102+ if (key_power)103103+ input_set_capability(input, EV_KEY, KEY_POWER);104104+105105+ onkey->input = input;106106+ platform_set_drvdata(pdev, onkey);107107+108108+ for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {109109+ irq = platform_get_irq(pdev, i);110110+ if (irq < 0)111111+ return irq;112112+113113+ error = devm_request_threaded_irq(&pdev->dev, irq, NULL,114114+ pf1550_onkey_irq_handler,115115+ IRQF_NO_SUSPEND,116116+ "pf1550-onkey", onkey);117117+ if (error)118118+ return dev_err_probe(&pdev->dev, error,119119+ "failed: irq request (IRQ: %d)\n",120120+ i);121121+ }122122+123123+ error = input_register_device(input);124124+ if (error)125125+ return dev_err_probe(&pdev->dev, error,126126+ "failed to register input device\n");127127+128128+ device_init_wakeup(&pdev->dev, onkey->wakeup);129129+130130+ return 0;131131+}132132+133133+static int pf1550_onkey_suspend(struct device *dev)134134+{135135+ struct platform_device *pdev = to_platform_device(dev);136136+ struct onkey_drv_data *onkey = platform_get_drvdata(pdev);137137+ int i, irq;138138+139139+ if (!device_may_wakeup(&pdev->dev))140140+ regmap_write(onkey->pf1550->regmap,141141+ PF1550_PMIC_REG_ONKEY_INT_MASK0,142142+ ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI | ONKEY_IRQ_2SI |143143+ ONKEY_IRQ_3SI | ONKEY_IRQ_4SI | ONKEY_IRQ_8SI);144144+ else145145+ for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {146146+ irq = platform_get_irq(pdev, i);147147+ if (irq > 0)148148+ enable_irq_wake(irq);149149+ }150150+151151+ return 0;152152+}153153+154154+static int pf1550_onkey_resume(struct device *dev)155155+{156156+ struct platform_device *pdev = to_platform_device(dev);157157+ struct onkey_drv_data *onkey = platform_get_drvdata(pdev);158158+ int i, irq;159159+160160+ if (!device_may_wakeup(&pdev->dev))161161+ regmap_write(onkey->pf1550->regmap,162162+ PF1550_PMIC_REG_ONKEY_INT_MASK0,163163+ ~((u8)(ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI |164164+ ONKEY_IRQ_2SI | ONKEY_IRQ_3SI | ONKEY_IRQ_4SI |165165+ ONKEY_IRQ_8SI)));166166+ else167167+ for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {168168+ irq = platform_get_irq(pdev, i);169169+ if (irq > 0)170170+ disable_irq_wake(irq);171171+ }172172+173173+ return 0;174174+}175175+176176+static SIMPLE_DEV_PM_OPS(pf1550_onkey_pm_ops, pf1550_onkey_suspend,177177+ pf1550_onkey_resume);178178+179179+static const struct platform_device_id pf1550_onkey_id[] = {180180+ { "pf1550-onkey", },181181+ { /* sentinel */ }182182+};183183+MODULE_DEVICE_TABLE(platform, pf1550_onkey_id);184184+185185+static struct platform_driver pf1550_onkey_driver = {186186+ .driver = {187187+ .name = "pf1550-onkey",188188+ .pm = pm_sleep_ptr(&pf1550_onkey_pm_ops),189189+ },190190+ .probe = pf1550_onkey_probe,191191+ .id_table = pf1550_onkey_id,192192+};193193+module_platform_driver(pf1550_onkey_driver);194194+195195+MODULE_AUTHOR("Freescale Semiconductor");196196+MODULE_DESCRIPTION("PF1550 onkey Driver");197197+MODULE_LICENSE("GPL");
+16
drivers/mfd/Kconfig
···605605 i.MX25 processors. They consist of a conversion queue for general606606 purpose ADC and a queue for Touchscreens.607607608608+config MFD_PF1550609609+ tristate "NXP PF1550 PMIC Support"610610+ depends on I2C=y && OF611611+ select MFD_CORE612612+ select REGMAP_I2C613613+ select REGMAP_IRQ614614+ help615615+ Say yes here to add support for NXP PF1550. This is a companion Power616616+ Management IC with regulators, onkey, and charger control on chip.617617+ This driver provides common support for accessing the device;618618+ additional drivers must be enabled in order to use the functionality619619+ of the device.620620+621621+ This driver can also be built as a module and if so will be called622622+ pf1550.623623+608624config MFD_HI6421_PMIC609625 tristate "HiSilicon Hi6421 PMU/Codec IC"610626 depends on OF
···11+// SPDX-License-Identifier: GPL-2.022+/*33+ * Core driver for the PF155044+ *55+ * Copyright (C) 2016 Freescale Semiconductor, Inc.66+ * Robin Gong <yibin.gong@freescale.com>77+ *88+ * Portions Copyright (c) 2025 Savoir-faire Linux Inc.99+ * Samuel Kayode <samuel.kayode@savoirfairelinux.com>1010+ */1111+1212+#include <linux/err.h>1313+#include <linux/i2c.h>1414+#include <linux/interrupt.h>1515+#include <linux/mfd/core.h>1616+#include <linux/mfd/pf1550.h>1717+#include <linux/module.h>1818+#include <linux/of.h>1919+#include <linux/regmap.h>2020+2121+static const struct regmap_config pf1550_regmap_config = {2222+ .reg_bits = 8,2323+ .val_bits = 8,2424+ .max_register = PF1550_PMIC_REG_END,2525+};2626+2727+static const struct regmap_irq pf1550_irqs[] = {2828+ REGMAP_IRQ_REG(PF1550_IRQ_CHG, 0, IRQ_CHG),2929+ REGMAP_IRQ_REG(PF1550_IRQ_REGULATOR, 0, IRQ_REGULATOR),3030+ REGMAP_IRQ_REG(PF1550_IRQ_ONKEY, 0, IRQ_ONKEY),3131+};3232+3333+static const struct regmap_irq_chip pf1550_irq_chip = {3434+ .name = "pf1550",3535+ .status_base = PF1550_PMIC_REG_INT_CATEGORY,3636+ .init_ack_masked = 1,3737+ .num_regs = 1,3838+ .irqs = pf1550_irqs,3939+ .num_irqs = ARRAY_SIZE(pf1550_irqs),4040+};4141+4242+static const struct regmap_irq pf1550_regulator_irqs[] = {4343+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_LS, 0, PMIC_IRQ_SW1_LS),4444+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_LS, 0, PMIC_IRQ_SW2_LS),4545+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_LS, 0, PMIC_IRQ_SW3_LS),4646+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_HS, 3, PMIC_IRQ_SW1_HS),4747+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_HS, 3, PMIC_IRQ_SW2_HS),4848+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_HS, 3, PMIC_IRQ_SW3_HS),4949+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO1_FAULT, 16, PMIC_IRQ_LDO1_FAULT),5050+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO2_FAULT, 16, PMIC_IRQ_LDO2_FAULT),5151+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO3_FAULT, 16, PMIC_IRQ_LDO3_FAULT),5252+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_110, 24, PMIC_IRQ_TEMP_110),5353+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_125, 24, PMIC_IRQ_TEMP_125),5454+};5555+5656+static const struct regmap_irq_chip pf1550_regulator_irq_chip = {5757+ .name = "pf1550-regulator",5858+ .status_base = PF1550_PMIC_REG_SW_INT_STAT0,5959+ .ack_base = PF1550_PMIC_REG_SW_INT_STAT0,6060+ .mask_base = PF1550_PMIC_REG_SW_INT_MASK0,6161+ .use_ack = 1,6262+ .init_ack_masked = 1,6363+ .num_regs = 25,6464+ .irqs = pf1550_regulator_irqs,6565+ .num_irqs = ARRAY_SIZE(pf1550_regulator_irqs),6666+};6767+6868+static const struct resource regulator_resources[] = {6969+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_LS),7070+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_LS),7171+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_LS),7272+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_HS),7373+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_HS),7474+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_HS),7575+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO1_FAULT),7676+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO2_FAULT),7777+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO3_FAULT),7878+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_110),7979+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_125),8080+};8181+8282+static const struct regmap_irq pf1550_onkey_irqs[] = {8383+ REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_PUSHI, 0, ONKEY_IRQ_PUSHI),8484+ REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_1SI, 0, ONKEY_IRQ_1SI),8585+ REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_2SI, 0, ONKEY_IRQ_2SI),8686+ REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_3SI, 0, ONKEY_IRQ_3SI),8787+ REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_4SI, 0, ONKEY_IRQ_4SI),8888+ REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_8SI, 0, ONKEY_IRQ_8SI),8989+};9090+9191+static const struct regmap_irq_chip pf1550_onkey_irq_chip = {9292+ .name = "pf1550-onkey",9393+ .status_base = PF1550_PMIC_REG_ONKEY_INT_STAT0,9494+ .ack_base = PF1550_PMIC_REG_ONKEY_INT_STAT0,9595+ .mask_base = PF1550_PMIC_REG_ONKEY_INT_MASK0,9696+ .use_ack = 1,9797+ .init_ack_masked = 1,9898+ .num_regs = 1,9999+ .irqs = pf1550_onkey_irqs,100100+ .num_irqs = ARRAY_SIZE(pf1550_onkey_irqs),101101+};102102+103103+static const struct resource onkey_resources[] = {104104+ DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_PUSHI),105105+ DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_1SI),106106+ DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_2SI),107107+ DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_3SI),108108+ DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_4SI),109109+ DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_8SI),110110+};111111+112112+static const struct regmap_irq pf1550_charger_irqs[] = {113113+ REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BAT2SOCI, 0, CHARG_IRQ_BAT2SOCI),114114+ REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BATI, 0, CHARG_IRQ_BATI),115115+ REGMAP_IRQ_REG(PF1550_CHARG_IRQ_CHGI, 0, CHARG_IRQ_CHGI),116116+ REGMAP_IRQ_REG(PF1550_CHARG_IRQ_VBUSI, 0, CHARG_IRQ_VBUSI),117117+ REGMAP_IRQ_REG(PF1550_CHARG_IRQ_THMI, 0, CHARG_IRQ_THMI),118118+};119119+120120+static const struct regmap_irq_chip pf1550_charger_irq_chip = {121121+ .name = "pf1550-charger",122122+ .status_base = PF1550_CHARG_REG_CHG_INT,123123+ .ack_base = PF1550_CHARG_REG_CHG_INT,124124+ .mask_base = PF1550_CHARG_REG_CHG_INT_MASK,125125+ .use_ack = 1,126126+ .init_ack_masked = 1,127127+ .num_regs = 1,128128+ .irqs = pf1550_charger_irqs,129129+ .num_irqs = ARRAY_SIZE(pf1550_charger_irqs),130130+};131131+132132+static const struct resource charger_resources[] = {133133+ DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BAT2SOCI),134134+ DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BATI),135135+ DEFINE_RES_IRQ(PF1550_CHARG_IRQ_CHGI),136136+ DEFINE_RES_IRQ(PF1550_CHARG_IRQ_VBUSI),137137+ DEFINE_RES_IRQ(PF1550_CHARG_IRQ_THMI),138138+};139139+140140+static const struct mfd_cell pf1550_regulator_cell = {141141+ .name = "pf1550-regulator",142142+ .num_resources = ARRAY_SIZE(regulator_resources),143143+ .resources = regulator_resources,144144+};145145+146146+static const struct mfd_cell pf1550_onkey_cell = {147147+ .name = "pf1550-onkey",148148+ .num_resources = ARRAY_SIZE(onkey_resources),149149+ .resources = onkey_resources,150150+};151151+152152+static const struct mfd_cell pf1550_charger_cell = {153153+ .name = "pf1550-charger",154154+ .num_resources = ARRAY_SIZE(charger_resources),155155+ .resources = charger_resources,156156+};157157+158158+/*159159+ * The PF1550 is shipped in variants of A0, A1,...A9. Each variant defines a160160+ * configuration of the PMIC in a One-Time Programmable (OTP) memory.161161+ * This memory is accessed indirectly by writing valid keys to specific162162+ * registers of the PMIC. To read the OTP memory after writing the valid keys,163163+ * the OTP register address to be read is written to pf1550 register 0xc4 and164164+ * its value read from pf1550 register 0xc5.165165+ */166166+static int pf1550_read_otp(const struct pf1550_ddata *pf1550, unsigned int index,167167+ unsigned int *val)168168+{169169+ int ret = 0;170170+171171+ ret = regmap_write(pf1550->regmap, PF1550_PMIC_REG_KEY, PF1550_OTP_PMIC_KEY);172172+ if (ret)173173+ goto read_err;174174+175175+ ret = regmap_write(pf1550->regmap, PF1550_CHARG_REG_CHGR_KEY2, PF1550_OTP_CHGR_KEY);176176+ if (ret)177177+ goto read_err;178178+179179+ ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_KEY3, PF1550_OTP_TEST_KEY);180180+ if (ret)181181+ goto read_err;182182+183183+ ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_FMRADDR, index);184184+ if (ret)185185+ goto read_err;186186+187187+ ret = regmap_read(pf1550->regmap, PF1550_TEST_REG_FMRDATA, val);188188+ if (ret)189189+ goto read_err;190190+191191+ return 0;192192+193193+read_err:194194+ return dev_err_probe(pf1550->dev, ret, "OTP reg %x not found!\n", index);195195+}196196+197197+static int pf1550_i2c_probe(struct i2c_client *i2c)198198+{199199+ const struct mfd_cell *regulator = &pf1550_regulator_cell;200200+ const struct mfd_cell *charger = &pf1550_charger_cell;201201+ const struct mfd_cell *onkey = &pf1550_onkey_cell;202202+ unsigned int reg_data = 0, otp_data = 0;203203+ struct pf1550_ddata *pf1550;204204+ struct irq_domain *domain;205205+ int irq, ret = 0;206206+207207+ pf1550 = devm_kzalloc(&i2c->dev, sizeof(*pf1550), GFP_KERNEL);208208+ if (!pf1550)209209+ return -ENOMEM;210210+211211+ i2c_set_clientdata(i2c, pf1550);212212+ pf1550->dev = &i2c->dev;213213+ pf1550->irq = i2c->irq;214214+215215+ pf1550->regmap = devm_regmap_init_i2c(i2c, &pf1550_regmap_config);216216+ if (IS_ERR(pf1550->regmap))217217+ return dev_err_probe(pf1550->dev, PTR_ERR(pf1550->regmap),218218+ "failed to allocate register map\n");219219+220220+ ret = regmap_read(pf1550->regmap, PF1550_PMIC_REG_DEVICE_ID, ®_data);221221+ if (ret < 0)222222+ return dev_err_probe(pf1550->dev, ret, "cannot read chip ID\n");223223+ if (reg_data != PF1550_DEVICE_ID)224224+ return dev_err_probe(pf1550->dev, -ENODEV, "invalid device ID: 0x%02x\n", reg_data);225225+226226+ /* Regulator DVS for SW2 */227227+ ret = pf1550_read_otp(pf1550, PF1550_OTP_SW2_SW3, &otp_data);228228+ if (ret)229229+ return ret;230230+231231+ /* When clear, DVS should be enabled */232232+ if (!(otp_data & OTP_SW2_DVS_ENB))233233+ pf1550->dvs2_enable = true;234234+235235+ /* Regulator DVS for SW1 */236236+ ret = pf1550_read_otp(pf1550, PF1550_OTP_SW1_SW2, &otp_data);237237+ if (ret)238238+ return ret;239239+240240+ if (!(otp_data & OTP_SW1_DVS_ENB))241241+ pf1550->dvs1_enable = true;242242+243243+ /* Add top level interrupts */244244+ ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, pf1550->irq,245245+ IRQF_ONESHOT | IRQF_SHARED |246246+ IRQF_TRIGGER_FALLING,247247+ 0, &pf1550_irq_chip,248248+ &pf1550->irq_data);249249+ if (ret)250250+ return ret;251251+252252+ /* Add regulator */253253+ irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_REGULATOR);254254+ if (irq < 0)255255+ return dev_err_probe(pf1550->dev, irq,256256+ "Failed to get parent vIRQ(%d) for chip %s\n",257257+ PF1550_IRQ_REGULATOR, pf1550_irq_chip.name);258258+259259+ ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,260260+ IRQF_ONESHOT | IRQF_SHARED |261261+ IRQF_TRIGGER_FALLING, 0,262262+ &pf1550_regulator_irq_chip,263263+ &pf1550->irq_data_regulator);264264+ if (ret)265265+ return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n",266266+ pf1550_regulator_irq_chip.name);267267+268268+ domain = regmap_irq_get_domain(pf1550->irq_data_regulator);269269+270270+ ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, regulator, 1, NULL, 0, domain);271271+ if (ret)272272+ return ret;273273+274274+ /* Add onkey */275275+ irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_ONKEY);276276+ if (irq < 0)277277+ return dev_err_probe(pf1550->dev, irq,278278+ "Failed to get parent vIRQ(%d) for chip %s\n",279279+ PF1550_IRQ_ONKEY, pf1550_irq_chip.name);280280+281281+ ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,282282+ IRQF_ONESHOT | IRQF_SHARED |283283+ IRQF_TRIGGER_FALLING, 0,284284+ &pf1550_onkey_irq_chip,285285+ &pf1550->irq_data_onkey);286286+ if (ret)287287+ return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n",288288+ pf1550_onkey_irq_chip.name);289289+290290+ domain = regmap_irq_get_domain(pf1550->irq_data_onkey);291291+292292+ ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, onkey, 1, NULL, 0, domain);293293+ if (ret)294294+ return ret;295295+296296+ /* Add battery charger */297297+ irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_CHG);298298+ if (irq < 0)299299+ return dev_err_probe(pf1550->dev, irq,300300+ "Failed to get parent vIRQ(%d) for chip %s\n",301301+ PF1550_IRQ_CHG, pf1550_irq_chip.name);302302+303303+ ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,304304+ IRQF_ONESHOT | IRQF_SHARED |305305+ IRQF_TRIGGER_FALLING, 0,306306+ &pf1550_charger_irq_chip,307307+ &pf1550->irq_data_charger);308308+ if (ret)309309+ return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n",310310+ pf1550_charger_irq_chip.name);311311+312312+ domain = regmap_irq_get_domain(pf1550->irq_data_charger);313313+314314+ return devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, charger, 1, NULL, 0, domain);315315+}316316+317317+static int pf1550_suspend(struct device *dev)318318+{319319+ struct pf1550_ddata *pf1550 = dev_get_drvdata(dev);320320+321321+ if (device_may_wakeup(dev)) {322322+ enable_irq_wake(pf1550->irq);323323+ disable_irq(pf1550->irq);324324+ }325325+326326+ return 0;327327+}328328+329329+static int pf1550_resume(struct device *dev)330330+{331331+ struct pf1550_ddata *pf1550 = dev_get_drvdata(dev);332332+333333+ if (device_may_wakeup(dev)) {334334+ disable_irq_wake(pf1550->irq);335335+ enable_irq(pf1550->irq);336336+ }337337+338338+ return 0;339339+}340340+static DEFINE_SIMPLE_DEV_PM_OPS(pf1550_pm, pf1550_suspend, pf1550_resume);341341+342342+static const struct i2c_device_id pf1550_i2c_id[] = {343343+ { "pf1550" },344344+ { /* sentinel */ }345345+};346346+MODULE_DEVICE_TABLE(i2c, pf1550_i2c_id);347347+348348+static const struct of_device_id pf1550_dt_match[] = {349349+ { .compatible = "nxp,pf1550" },350350+ { /* sentinel */ }351351+};352352+MODULE_DEVICE_TABLE(of, pf1550_dt_match);353353+354354+static struct i2c_driver pf1550_i2c_driver = {355355+ .driver = {356356+ .name = "pf1550",357357+ .pm = pm_sleep_ptr(&pf1550_pm),358358+ .of_match_table = pf1550_dt_match,359359+ },360360+ .probe = pf1550_i2c_probe,361361+ .id_table = pf1550_i2c_id,362362+};363363+module_i2c_driver(pf1550_i2c_driver);364364+365365+MODULE_DESCRIPTION("NXP PF1550 core driver");366366+MODULE_AUTHOR("Robin Gong <yibin.gong@freescale.com>");367367+MODULE_LICENSE("GPL");
+11
drivers/power/supply/Kconfig
···486486 help487487 Say Y here to enable charger for Marvell 88PM860x chip.488488489489+config CHARGER_PF1550490490+ tristate "NXP PF1550 battery charger driver"491491+ depends on MFD_PF1550492492+ help493493+ Say Y to enable support for the NXP PF1550 battery charger.494494+ The device is a single cell Li-Ion/Li-Polymer battery charger for495495+ portable application.496496+497497+ This driver can also be built as a module. If so, the module will be498498+ called pf1550-charger.499499+489500config BATTERY_RX51490501 tristate "Nokia RX-51 (N900) battery driver"491502 depends on TWL4030_MADC
···11+// SPDX-License-Identifier: GPL-2.022+/*33+ * charger driver for the PF155044+ *55+ * Copyright (C) 2016 Freescale Semiconductor, Inc.66+ * Robin Gong <yibin.gong@freescale.com>77+ *88+ * Portions Copyright (c) 2025 Savoir-faire Linux Inc.99+ * Samuel Kayode <samuel.kayode@savoirfairelinux.com>1010+ */1111+1212+#include <linux/devm-helpers.h>1313+#include <linux/interrupt.h>1414+#include <linux/mfd/pf1550.h>1515+#include <linux/module.h>1616+#include <linux/platform_device.h>1717+#include <linux/power_supply.h>1818+1919+#define PF1550_DEFAULT_CONSTANT_VOLT 42000002020+#define PF1550_DEFAULT_MIN_SYSTEM_VOLT 35000002121+#define PF1550_DEFAULT_THERMAL_TEMP 952222+#define PF1550_CHARGER_IRQ_NR 52323+2424+struct pf1550_charger {2525+ struct device *dev;2626+ const struct pf1550_ddata *pf1550;2727+ struct power_supply *charger;2828+ struct power_supply *battery;2929+ struct delayed_work vbus_sense_work;3030+ struct delayed_work chg_sense_work;3131+ struct delayed_work bat_sense_work;3232+ int virqs[PF1550_CHARGER_IRQ_NR];3333+3434+ u32 constant_volt;3535+ u32 min_system_volt;3636+ u32 thermal_regulation_temp;3737+};3838+3939+static int pf1550_get_charger_state(struct regmap *regmap, int *val)4040+{4141+ unsigned int data;4242+ int ret;4343+4444+ ret = regmap_read(regmap, PF1550_CHARG_REG_CHG_SNS, &data);4545+ if (ret < 0)4646+ return ret;4747+4848+ data &= PF1550_CHG_SNS_MASK;4949+5050+ switch (data) {5151+ case PF1550_CHG_PRECHARGE:5252+ case PF1550_CHG_CONSTANT_CURRENT:5353+ case PF1550_CHG_CONSTANT_VOL:5454+ case PF1550_CHG_EOC:5555+ *val = POWER_SUPPLY_STATUS_CHARGING;5656+ break;5757+ case PF1550_CHG_DONE:5858+ *val = POWER_SUPPLY_STATUS_FULL;5959+ break;6060+ case PF1550_CHG_TIMER_FAULT:6161+ case PF1550_CHG_SUSPEND:6262+ *val = POWER_SUPPLY_STATUS_NOT_CHARGING;6363+ break;6464+ case PF1550_CHG_OFF_INV:6565+ case PF1550_CHG_OFF_TEMP:6666+ case PF1550_CHG_LINEAR_ONLY:6767+ *val = POWER_SUPPLY_STATUS_DISCHARGING;6868+ break;6969+ default:7070+ *val = POWER_SUPPLY_STATUS_UNKNOWN;7171+ }7272+7373+ return 0;7474+}7575+7676+static int pf1550_get_charge_type(struct regmap *regmap, int *val)7777+{7878+ unsigned int data;7979+ int ret;8080+8181+ ret = regmap_read(regmap, PF1550_CHARG_REG_CHG_SNS, &data);8282+ if (ret < 0)8383+ return ret;8484+8585+ data &= PF1550_CHG_SNS_MASK;8686+8787+ switch (data) {8888+ case PF1550_CHG_SNS_MASK:8989+ *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;9090+ break;9191+ case PF1550_CHG_CONSTANT_CURRENT:9292+ case PF1550_CHG_CONSTANT_VOL:9393+ case PF1550_CHG_EOC:9494+ *val = POWER_SUPPLY_CHARGE_TYPE_FAST;9595+ break;9696+ case PF1550_CHG_DONE:9797+ case PF1550_CHG_TIMER_FAULT:9898+ case PF1550_CHG_SUSPEND:9999+ case PF1550_CHG_OFF_INV:100100+ case PF1550_CHG_BAT_OVER:101101+ case PF1550_CHG_OFF_TEMP:102102+ case PF1550_CHG_LINEAR_ONLY:103103+ *val = POWER_SUPPLY_CHARGE_TYPE_NONE;104104+ break;105105+ default:106106+ *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;107107+ }108108+109109+ return 0;110110+}111111+112112+/*113113+ * Supported health statuses:114114+ * - POWER_SUPPLY_HEALTH_DEAD115115+ * - POWER_SUPPLY_HEALTH_GOOD116116+ * - POWER_SUPPLY_HEALTH_OVERVOLTAGE117117+ * - POWER_SUPPLY_HEALTH_UNKNOWN118118+ */119119+static int pf1550_get_battery_health(struct regmap *regmap, int *val)120120+{121121+ unsigned int data;122122+ int ret;123123+124124+ ret = regmap_read(regmap, PF1550_CHARG_REG_BATT_SNS, &data);125125+ if (ret < 0)126126+ return ret;127127+128128+ data &= PF1550_BAT_SNS_MASK;129129+130130+ switch (data) {131131+ case PF1550_BAT_NO_DETECT:132132+ *val = POWER_SUPPLY_HEALTH_NO_BATTERY;133133+ break;134134+ case PF1550_BAT_NO_VBUS:135135+ case PF1550_BAT_LOW_THAN_PRECHARG:136136+ case PF1550_BAT_CHARG_FAIL:137137+ case PF1550_BAT_HIGH_THAN_PRECHARG:138138+ *val = POWER_SUPPLY_HEALTH_GOOD;139139+ break;140140+ case PF1550_BAT_OVER_VOL:141141+ *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;142142+ break;143143+ default:144144+ *val = POWER_SUPPLY_HEALTH_UNKNOWN;145145+ break;146146+ }147147+148148+ return 0;149149+}150150+151151+static int pf1550_get_present(struct regmap *regmap, int *val)152152+{153153+ unsigned int data;154154+ int ret;155155+156156+ ret = regmap_read(regmap, PF1550_CHARG_REG_BATT_SNS, &data);157157+ if (ret < 0)158158+ return ret;159159+160160+ data &= PF1550_BAT_SNS_MASK;161161+ *val = (data == PF1550_BAT_NO_DETECT) ? 0 : 1;162162+163163+ return 0;164164+}165165+166166+static int pf1550_get_online(struct regmap *regmap, int *val)167167+{168168+ unsigned int data;169169+ int ret;170170+171171+ ret = regmap_read(regmap, PF1550_CHARG_REG_VBUS_SNS, &data);172172+ if (ret < 0)173173+ return ret;174174+175175+ *val = (data & PF1550_VBUS_VALID) ? 1 : 0;176176+177177+ return 0;178178+}179179+180180+static void pf1550_chg_bat_work(struct work_struct *work)181181+{182182+ struct pf1550_charger *chg = container_of(to_delayed_work(work),183183+ struct pf1550_charger,184184+ bat_sense_work);185185+ unsigned int data;186186+187187+ if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_BATT_SNS, &data)) {188188+ dev_err(chg->dev, "Read BATT_SNS error.\n");189189+ return;190190+ }191191+192192+ switch (data & PF1550_BAT_SNS_MASK) {193193+ case PF1550_BAT_NO_VBUS:194194+ dev_dbg(chg->dev, "No valid VBUS input.\n");195195+ break;196196+ case PF1550_BAT_LOW_THAN_PRECHARG:197197+ dev_dbg(chg->dev, "VBAT < VPRECHG.LB.\n");198198+ break;199199+ case PF1550_BAT_CHARG_FAIL:200200+ dev_dbg(chg->dev, "Battery charging failed.\n");201201+ break;202202+ case PF1550_BAT_HIGH_THAN_PRECHARG:203203+ dev_dbg(chg->dev, "VBAT > VPRECHG.LB.\n");204204+ break;205205+ case PF1550_BAT_OVER_VOL:206206+ dev_dbg(chg->dev, "VBAT > VBATOV.\n");207207+ break;208208+ case PF1550_BAT_NO_DETECT:209209+ dev_dbg(chg->dev, "Battery not detected.\n");210210+ break;211211+ default:212212+ dev_err(chg->dev, "Unknown value read:%x\n",213213+ data & PF1550_CHG_SNS_MASK);214214+ }215215+}216216+217217+static void pf1550_chg_chg_work(struct work_struct *work)218218+{219219+ struct pf1550_charger *chg = container_of(to_delayed_work(work),220220+ struct pf1550_charger,221221+ chg_sense_work);222222+ unsigned int data;223223+224224+ if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_SNS, &data)) {225225+ dev_err(chg->dev, "Read CHG_SNS error.\n");226226+ return;227227+ }228228+229229+ switch (data & PF1550_CHG_SNS_MASK) {230230+ case PF1550_CHG_PRECHARGE:231231+ dev_dbg(chg->dev, "In pre-charger mode.\n");232232+ break;233233+ case PF1550_CHG_CONSTANT_CURRENT:234234+ dev_dbg(chg->dev, "In fast-charge constant current mode.\n");235235+ break;236236+ case PF1550_CHG_CONSTANT_VOL:237237+ dev_dbg(chg->dev, "In fast-charge constant voltage mode.\n");238238+ break;239239+ case PF1550_CHG_EOC:240240+ dev_dbg(chg->dev, "In EOC mode.\n");241241+ break;242242+ case PF1550_CHG_DONE:243243+ dev_dbg(chg->dev, "In DONE mode.\n");244244+ break;245245+ case PF1550_CHG_TIMER_FAULT:246246+ dev_info(chg->dev, "In timer fault mode.\n");247247+ break;248248+ case PF1550_CHG_SUSPEND:249249+ dev_info(chg->dev, "In thermistor suspend mode.\n");250250+ break;251251+ case PF1550_CHG_OFF_INV:252252+ dev_info(chg->dev, "Input invalid, charger off.\n");253253+ break;254254+ case PF1550_CHG_BAT_OVER:255255+ dev_warn(chg->dev, "Battery over-voltage.\n");256256+ break;257257+ case PF1550_CHG_OFF_TEMP:258258+ dev_info(chg->dev, "Temp high, charger off.\n");259259+ break;260260+ case PF1550_CHG_LINEAR_ONLY:261261+ dev_dbg(chg->dev, "In Linear mode, not charging.\n");262262+ break;263263+ default:264264+ dev_err(chg->dev, "Unknown value read:%x\n",265265+ data & PF1550_CHG_SNS_MASK);266266+ }267267+}268268+269269+static void pf1550_chg_vbus_work(struct work_struct *work)270270+{271271+ struct pf1550_charger *chg = container_of(to_delayed_work(work),272272+ struct pf1550_charger,273273+ vbus_sense_work);274274+ unsigned int data;275275+276276+ if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_VBUS_SNS, &data)) {277277+ dev_err(chg->dev, "Read VBUS_SNS error.\n");278278+ return;279279+ }280280+281281+ if (data & PF1550_VBUS_UVLO) {282282+ dev_dbg(chg->dev, "VBUS detached.\n");283283+ power_supply_changed(chg->battery);284284+ }285285+ if (data & PF1550_VBUS_IN2SYS)286286+ dev_dbg(chg->dev, "VBUS_IN2SYS_SNS.\n");287287+ if (data & PF1550_VBUS_OVLO)288288+ dev_dbg(chg->dev, "VBUS_OVLO_SNS.\n");289289+ if (data & PF1550_VBUS_VALID) {290290+ dev_dbg(chg->dev, "VBUS attached.\n");291291+ power_supply_changed(chg->charger);292292+ }293293+}294294+295295+static irqreturn_t pf1550_charger_irq_handler(int irq, void *data)296296+{297297+ struct pf1550_charger *chg = data;298298+ struct device *dev = chg->dev;299299+ int i, irq_type = -1;300300+301301+ for (i = 0; i < PF1550_CHARGER_IRQ_NR; i++)302302+ if (irq == chg->virqs[i])303303+ irq_type = i;304304+305305+ switch (irq_type) {306306+ case PF1550_CHARG_IRQ_BAT2SOCI:307307+ dev_info(dev, "BAT to SYS Overcurrent interrupt.\n");308308+ break;309309+ case PF1550_CHARG_IRQ_BATI:310310+ schedule_delayed_work(&chg->bat_sense_work,311311+ msecs_to_jiffies(10));312312+ break;313313+ case PF1550_CHARG_IRQ_CHGI:314314+ schedule_delayed_work(&chg->chg_sense_work,315315+ msecs_to_jiffies(10));316316+ break;317317+ case PF1550_CHARG_IRQ_VBUSI:318318+ schedule_delayed_work(&chg->vbus_sense_work,319319+ msecs_to_jiffies(10));320320+ break;321321+ case PF1550_CHARG_IRQ_THMI:322322+ dev_info(dev, "Thermal interrupt.\n");323323+ break;324324+ default:325325+ dev_err(dev, "unknown interrupt occurred.\n");326326+ }327327+328328+ return IRQ_HANDLED;329329+}330330+331331+static enum power_supply_property pf1550_charger_props[] = {332332+ POWER_SUPPLY_PROP_ONLINE,333333+ POWER_SUPPLY_PROP_MODEL_NAME,334334+ POWER_SUPPLY_PROP_MANUFACTURER,335335+};336336+337337+static enum power_supply_property pf1550_battery_props[] = {338338+ POWER_SUPPLY_PROP_STATUS,339339+ POWER_SUPPLY_PROP_CHARGE_TYPE,340340+ POWER_SUPPLY_PROP_HEALTH,341341+ POWER_SUPPLY_PROP_PRESENT,342342+ POWER_SUPPLY_PROP_MODEL_NAME,343343+ POWER_SUPPLY_PROP_MANUFACTURER,344344+};345345+346346+static int pf1550_charger_get_property(struct power_supply *psy,347347+ enum power_supply_property psp,348348+ union power_supply_propval *val)349349+{350350+ struct pf1550_charger *chg = power_supply_get_drvdata(psy);351351+ struct regmap *regmap = chg->pf1550->regmap;352352+ int ret = 0;353353+354354+ switch (psp) {355355+ case POWER_SUPPLY_PROP_STATUS:356356+ ret = pf1550_get_charger_state(regmap, &val->intval);357357+ break;358358+ case POWER_SUPPLY_PROP_CHARGE_TYPE:359359+ ret = pf1550_get_charge_type(regmap, &val->intval);360360+ break;361361+ case POWER_SUPPLY_PROP_HEALTH:362362+ ret = pf1550_get_battery_health(regmap, &val->intval);363363+ break;364364+ case POWER_SUPPLY_PROP_PRESENT:365365+ ret = pf1550_get_present(regmap, &val->intval);366366+ break;367367+ case POWER_SUPPLY_PROP_ONLINE:368368+ ret = pf1550_get_online(regmap, &val->intval);369369+ break;370370+ case POWER_SUPPLY_PROP_MODEL_NAME:371371+ val->strval = "PF1550";372372+ break;373373+ case POWER_SUPPLY_PROP_MANUFACTURER:374374+ val->strval = "NXP";375375+ break;376376+ default:377377+ return -EINVAL;378378+ }379379+380380+ return ret;381381+}382382+383383+static const struct power_supply_desc pf1550_charger_desc = {384384+ .name = "pf1550-charger",385385+ .type = POWER_SUPPLY_TYPE_MAINS,386386+ .properties = pf1550_charger_props,387387+ .num_properties = ARRAY_SIZE(pf1550_charger_props),388388+ .get_property = pf1550_charger_get_property,389389+};390390+391391+static const struct power_supply_desc pf1550_battery_desc = {392392+ .name = "pf1550-battery",393393+ .type = POWER_SUPPLY_TYPE_BATTERY,394394+ .properties = pf1550_battery_props,395395+ .num_properties = ARRAY_SIZE(pf1550_battery_props),396396+ .get_property = pf1550_charger_get_property,397397+};398398+399399+static int pf1550_set_constant_volt(struct pf1550_charger *chg,400400+ unsigned int uvolt)401401+{402402+ unsigned int data;403403+404404+ if (uvolt >= 3500000 && uvolt <= 4440000)405405+ data = 8 + (uvolt - 3500000) / 20000;406406+ else407407+ return dev_err_probe(chg->dev, -EINVAL,408408+ "Wrong value for constant voltage\n");409409+410410+ dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt,411411+ data);412412+413413+ return regmap_update_bits(chg->pf1550->regmap,414414+ PF1550_CHARG_REG_BATT_REG,415415+ PF1550_CHARG_REG_BATT_REG_CHGCV_MASK, data);416416+}417417+418418+static int pf1550_set_min_system_volt(struct pf1550_charger *chg,419419+ unsigned int uvolt)420420+{421421+ unsigned int data;422422+423423+ switch (uvolt) {424424+ case 3500000:425425+ data = 0x0;426426+ break;427427+ case 3700000:428428+ data = 0x1;429429+ break;430430+ case 4300000:431431+ data = 0x2;432432+ break;433433+ default:434434+ return dev_err_probe(chg->dev, -EINVAL,435435+ "Wrong value for minimum system voltage\n");436436+ }437437+438438+ data <<= PF1550_CHARG_REG_BATT_REG_VMINSYS_SHIFT;439439+440440+ dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n",441441+ uvolt, data);442442+443443+ return regmap_update_bits(chg->pf1550->regmap,444444+ PF1550_CHARG_REG_BATT_REG,445445+ PF1550_CHARG_REG_BATT_REG_VMINSYS_MASK, data);446446+}447447+448448+static int pf1550_set_thermal_regulation_temp(struct pf1550_charger *chg,449449+ unsigned int cells)450450+{451451+ unsigned int data;452452+453453+ switch (cells) {454454+ case 80:455455+ data = 0x0;456456+ break;457457+ case 95:458458+ data = 0x1;459459+ break;460460+ case 110:461461+ data = 0x2;462462+ break;463463+ case 125:464464+ data = 0x3;465465+ break;466466+ default:467467+ return dev_err_probe(chg->dev, -EINVAL,468468+ "Wrong value for thermal temperature\n");469469+ }470470+471471+ data <<= PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_SHIFT;472472+473473+ dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n",474474+ cells, data);475475+476476+ return regmap_update_bits(chg->pf1550->regmap,477477+ PF1550_CHARG_REG_THM_REG_CNFG,478478+ PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_MASK,479479+ data);480480+}481481+482482+/*483483+ * Sets charger registers to proper and safe default values.484484+ */485485+static int pf1550_reg_init(struct pf1550_charger *chg)486486+{487487+ struct power_supply_battery_info *info;488488+ struct device *dev = chg->dev;489489+ int ret;490490+491491+ /* Unmask charger interrupt, mask DPMI and reserved bit */492492+ ret = regmap_write(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_INT_MASK,493493+ PF1550_CHG_INT_MASK);494494+ if (ret)495495+ return dev_err_probe(dev, ret,496496+ "Error unmask charger interrupt\n");497497+498498+ ret = pf1550_set_constant_volt(chg, chg->constant_volt);499499+ if (ret)500500+ return ret;501501+502502+ ret = pf1550_set_min_system_volt(chg, chg->min_system_volt);503503+ if (ret)504504+ return ret;505505+506506+ ret = pf1550_set_thermal_regulation_temp(chg,507507+ chg->thermal_regulation_temp);508508+ if (ret)509509+ return ret;510510+511511+ /*512512+ * The PF1550 charger has 3 modes of operation. By default, the charger513513+ * is in mode 1; it remains off. Appropriate for applications not using514514+ * a battery. The other supported mode is mode 2, the charger is turned515515+ * on to charge a battery when present.516516+ */517517+ if (power_supply_get_battery_info(chg->charger, &info)) {518518+ ret = regmap_write(chg->pf1550->regmap,519519+ PF1550_CHARG_REG_CHG_OPER,520520+ PF1550_CHG_BAT_ON);521521+ if (ret)522522+ return dev_err_probe(dev, ret,523523+ "Error turn on charger\n");524524+ }525525+526526+ return 0;527527+}528528+529529+static void pf1550_dt_parse_dev_info(struct pf1550_charger *chg)530530+{531531+ struct power_supply_battery_info *info;532532+ struct device *dev = chg->dev;533533+534534+ if (device_property_read_u32(dev->parent, "nxp,min-system-microvolt",535535+ &chg->min_system_volt))536536+ chg->min_system_volt = PF1550_DEFAULT_MIN_SYSTEM_VOLT;537537+538538+ if (device_property_read_u32(dev->parent,539539+ "nxp,thermal-regulation-celsius",540540+ &chg->thermal_regulation_temp))541541+ chg->thermal_regulation_temp = PF1550_DEFAULT_THERMAL_TEMP;542542+543543+ if (power_supply_get_battery_info(chg->charger, &info))544544+ chg->constant_volt = PF1550_DEFAULT_CONSTANT_VOLT;545545+ else546546+ chg->constant_volt = info->constant_charge_voltage_max_uv;547547+}548548+549549+static int pf1550_charger_probe(struct platform_device *pdev)550550+{551551+ const struct pf1550_ddata *pf1550 = dev_get_drvdata(pdev->dev.parent);552552+ struct power_supply_config psy_cfg = {};553553+ struct pf1550_charger *chg;554554+ int i, irq, ret;555555+556556+ chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);557557+ if (!chg)558558+ return -ENOMEM;559559+560560+ chg->dev = &pdev->dev;561561+ chg->pf1550 = pf1550;562562+563563+ if (!chg->pf1550->regmap)564564+ return dev_err_probe(&pdev->dev, -ENODEV,565565+ "failed to get regmap\n");566566+567567+ platform_set_drvdata(pdev, chg);568568+569569+ ret = devm_delayed_work_autocancel(chg->dev, &chg->vbus_sense_work,570570+ pf1550_chg_vbus_work);571571+ if (ret)572572+ return dev_err_probe(chg->dev, ret,573573+ "failed to add vbus sense work\n");574574+575575+ ret = devm_delayed_work_autocancel(chg->dev, &chg->chg_sense_work,576576+ pf1550_chg_chg_work);577577+ if (ret)578578+ return dev_err_probe(chg->dev, ret,579579+ "failed to add charger sense work\n");580580+581581+ ret = devm_delayed_work_autocancel(chg->dev, &chg->bat_sense_work,582582+ pf1550_chg_bat_work);583583+ if (ret)584584+ return dev_err_probe(chg->dev, ret,585585+ "failed to add battery sense work\n");586586+587587+ for (i = 0; i < PF1550_CHARGER_IRQ_NR; i++) {588588+ irq = platform_get_irq(pdev, i);589589+ if (irq < 0)590590+ return irq;591591+592592+ chg->virqs[i] = irq;593593+594594+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,595595+ pf1550_charger_irq_handler,596596+ IRQF_NO_SUSPEND,597597+ "pf1550-charger", chg);598598+ if (ret)599599+ return dev_err_probe(&pdev->dev, ret,600600+ "failed irq request\n");601601+ }602602+603603+ psy_cfg.drv_data = chg;604604+605605+ chg->charger = devm_power_supply_register(&pdev->dev,606606+ &pf1550_charger_desc,607607+ &psy_cfg);608608+ if (IS_ERR(chg->charger))609609+ return dev_err_probe(&pdev->dev, PTR_ERR(chg->charger),610610+ "failed: power supply register\n");611611+612612+ chg->battery = devm_power_supply_register(&pdev->dev,613613+ &pf1550_battery_desc,614614+ &psy_cfg);615615+ if (IS_ERR(chg->battery))616616+ return dev_err_probe(&pdev->dev, PTR_ERR(chg->battery),617617+ "failed: power supply register\n");618618+619619+ pf1550_dt_parse_dev_info(chg);620620+621621+ return pf1550_reg_init(chg);622622+}623623+624624+static const struct platform_device_id pf1550_charger_id[] = {625625+ { "pf1550-charger", },626626+ { /* sentinel */ }627627+};628628+MODULE_DEVICE_TABLE(platform, pf1550_charger_id);629629+630630+static struct platform_driver pf1550_charger_driver = {631631+ .driver = {632632+ .name = "pf1550-charger",633633+ },634634+ .probe = pf1550_charger_probe,635635+ .id_table = pf1550_charger_id,636636+};637637+module_platform_driver(pf1550_charger_driver);638638+639639+MODULE_AUTHOR("Robin Gong <yibin.gong@freescale.com>");640640+MODULE_DESCRIPTION("PF1550 charger driver");641641+MODULE_LICENSE("GPL");
+9
drivers/regulator/Kconfig
···10861086 Say y here to support the voltage regulators and convertors10871087 on PV880901088108810891089+config REGULATOR_PF155010901090+ tristate "NXP PF1550 regulator"10911091+ depends on MFD_PF155010921092+ help10931093+ Say y here to select this option to enable the regulators on10941094+ the PF1550 PMICs.10951095+ This driver controls the PF1550 regulators via I2C bus.10961096+ The regulators include three bucks and three ldos.10971097+10891098config REGULATOR_PWM10901099 tristate "PWM voltage regulator"10911100 depends on PWM