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