149 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2#include <linux/i2c.h>3#include <linux/mfd/core.h>4#include <linux/module.h>5#include <linux/notifier.h>6#include <linux/of.h>7#include <linux/platform_device.h>8#include <linux/reboot.h>9#include <linux/regmap.h>10 11#include <linux/mfd/88pm886.h>12 13static const struct regmap_config pm886_regmap_config = {14 .reg_bits = 8,15 .val_bits = 8,16 .max_register = PM886_REG_RTC_SPARE6,17};18 19static struct regmap_irq pm886_regmap_irqs[] = {20 REGMAP_IRQ_REG(PM886_IRQ_ONKEY, 0, PM886_INT_ENA1_ONKEY),21};22 23static struct regmap_irq_chip pm886_regmap_irq_chip = {24 .name = "88pm886",25 .irqs = pm886_regmap_irqs,26 .num_irqs = ARRAY_SIZE(pm886_regmap_irqs),27 .num_regs = 4,28 .status_base = PM886_REG_INT_STATUS1,29 .ack_base = PM886_REG_INT_STATUS1,30 .unmask_base = PM886_REG_INT_ENA_1,31};32 33static struct resource pm886_onkey_resources[] = {34 DEFINE_RES_IRQ_NAMED(PM886_IRQ_ONKEY, "88pm886-onkey"),35};36 37static struct mfd_cell pm886_devs[] = {38 MFD_CELL_RES("88pm886-onkey", pm886_onkey_resources),39 MFD_CELL_NAME("88pm886-regulator"),40};41 42static int pm886_power_off_handler(struct sys_off_data *sys_off_data)43{44 struct pm886_chip *chip = sys_off_data->cb_data;45 struct regmap *regmap = chip->regmap;46 struct device *dev = &chip->client->dev;47 int err;48 49 err = regmap_update_bits(regmap, PM886_REG_MISC_CONFIG1, PM886_SW_PDOWN, PM886_SW_PDOWN);50 if (err) {51 dev_err(dev, "Failed to power off the device: %d\n", err);52 return NOTIFY_BAD;53 }54 return NOTIFY_DONE;55}56 57static int pm886_setup_irq(struct pm886_chip *chip,58 struct regmap_irq_chip_data **irq_data)59{60 struct regmap *regmap = chip->regmap;61 struct device *dev = &chip->client->dev;62 int err;63 64 /* Set interrupt clearing mode to clear on write. */65 err = regmap_update_bits(regmap, PM886_REG_MISC_CONFIG2,66 PM886_INT_INV | PM886_INT_CLEAR | PM886_INT_MASK_MODE,67 PM886_INT_WC);68 if (err) {69 dev_err(dev, "Failed to set interrupt clearing mode: %d\n", err);70 return err;71 }72 73 err = devm_regmap_add_irq_chip(dev, regmap, chip->client->irq,74 IRQF_ONESHOT, 0, &pm886_regmap_irq_chip,75 irq_data);76 if (err) {77 dev_err(dev, "Failed to request IRQ: %d\n", err);78 return err;79 }80 81 return 0;82}83 84static int pm886_probe(struct i2c_client *client)85{86 struct regmap_irq_chip_data *irq_data;87 struct device *dev = &client->dev;88 struct pm886_chip *chip;89 struct regmap *regmap;90 unsigned int chip_id;91 int err;92 93 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);94 if (!chip)95 return -ENOMEM;96 97 chip->client = client;98 chip->chip_id = (uintptr_t)device_get_match_data(dev);99 i2c_set_clientdata(client, chip);100 101 regmap = devm_regmap_init_i2c(client, &pm886_regmap_config);102 if (IS_ERR(regmap))103 return dev_err_probe(dev, PTR_ERR(regmap), "Failed to initialize regmap\n");104 chip->regmap = regmap;105 106 err = regmap_read(regmap, PM886_REG_ID, &chip_id);107 if (err)108 return dev_err_probe(dev, err, "Failed to read chip ID\n");109 110 if (chip->chip_id != chip_id)111 return dev_err_probe(dev, -EINVAL, "Unsupported chip: 0x%x\n", chip_id);112 113 err = pm886_setup_irq(chip, &irq_data);114 if (err)115 return err;116 117 err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, pm886_devs, ARRAY_SIZE(pm886_devs),118 NULL, 0, regmap_irq_get_domain(irq_data));119 if (err)120 return dev_err_probe(dev, err, "Failed to add devices\n");121 122 err = devm_register_power_off_handler(dev, pm886_power_off_handler, chip);123 if (err)124 return dev_err_probe(dev, err, "Failed to register power off handler\n");125 126 device_init_wakeup(dev, device_property_read_bool(dev, "wakeup-source"));127 128 return 0;129}130 131static const struct of_device_id pm886_of_match[] = {132 { .compatible = "marvell,88pm886-a1", .data = (void *)PM886_A1_CHIP_ID },133 { }134};135MODULE_DEVICE_TABLE(of, pm886_of_match);136 137static struct i2c_driver pm886_i2c_driver = {138 .driver = {139 .name = "88pm886",140 .of_match_table = pm886_of_match,141 },142 .probe = pm886_probe,143};144module_i2c_driver(pm886_i2c_driver);145 146MODULE_DESCRIPTION("Marvell 88PM886 PMIC driver");147MODULE_AUTHOR("Karel Balej <balejk@matfyz.cz>");148MODULE_LICENSE("GPL");149