summaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/88pm800.c4
-rw-r--r--drivers/mfd/88pm805.c6
-rw-r--r--drivers/mfd/88pm860x-core.c17
-rw-r--r--drivers/mfd/88pm886.c154
-rw-r--r--drivers/mfd/Kconfig425
-rw-r--r--drivers/mfd/Makefile45
-rw-r--r--drivers/mfd/aat2870-core.c6
-rw-r--r--drivers/mfd/ab8500-core.c6
-rw-r--r--drivers/mfd/ab8500-sysctrl.c6
-rw-r--r--drivers/mfd/ac100.c2
-rw-r--r--drivers/mfd/acer-ec-a500.c2
-rw-r--r--drivers/mfd/act8945a.c6
-rw-r--r--drivers/mfd/adp5585.c843
-rw-r--r--drivers/mfd/altera-a10sr.c2
-rw-r--r--drivers/mfd/altera-sysmgr.c9
-rw-r--r--drivers/mfd/arizona-core.c2
-rw-r--r--drivers/mfd/arizona-i2c.c11
-rw-r--r--drivers/mfd/arizona-irq.c9
-rw-r--r--drivers/mfd/arizona-spi.c12
-rw-r--r--drivers/mfd/as3711.c2
-rw-r--r--drivers/mfd/as3722.c10
-rw-r--r--drivers/mfd/atc260x-core.c5
-rw-r--r--drivers/mfd/atmel-flexcom.c2
-rw-r--r--drivers/mfd/atmel-hlcdc.c5
-rw-r--r--drivers/mfd/atmel-smc.c13
-rw-r--r--drivers/mfd/axp20x-i2c.c25
-rw-r--r--drivers/mfd/axp20x-rsb.c1
-rw-r--r--drivers/mfd/axp20x.c266
-rw-r--r--drivers/mfd/bcm2835-pm.c1
-rw-r--r--drivers/mfd/bcm590xx.c71
-rw-r--r--drivers/mfd/bd9571mwv.c10
-rw-r--r--drivers/mfd/bq257xx.c99
-rw-r--r--drivers/mfd/cgbc-core.c428
-rw-r--r--drivers/mfd/cros_ec_dev.c91
-rw-r--r--drivers/mfd/cs40l50-core.c569
-rw-r--r--drivers/mfd/cs40l50-i2c.c68
-rw-r--r--drivers/mfd/cs40l50-spi.c68
-rw-r--r--drivers/mfd/cs42l43-i2c.c91
-rw-r--r--drivers/mfd/cs42l43-sdw.c212
-rw-r--r--drivers/mfd/cs42l43.c1249
-rw-r--r--drivers/mfd/cs42l43.h29
-rw-r--r--drivers/mfd/cs47l15-tables.c8
-rw-r--r--drivers/mfd/cs47l24-tables.c2
-rw-r--r--drivers/mfd/cs47l35-tables.c8
-rw-r--r--drivers/mfd/cs47l85-tables.c8
-rw-r--r--drivers/mfd/cs47l90-tables.c8
-rw-r--r--drivers/mfd/cs47l92-tables.c8
-rw-r--r--drivers/mfd/da9052-core.c3
-rw-r--r--drivers/mfd/da9052-i2c.c5
-rw-r--r--drivers/mfd/da9052-spi.c2
-rw-r--r--drivers/mfd/da9055-core.c4
-rw-r--r--drivers/mfd/da9055-i2c.c3
-rw-r--r--drivers/mfd/da9062-core.c40
-rw-r--r--drivers/mfd/da9063-i2c.c32
-rw-r--r--drivers/mfd/da9150-core.c2
-rw-r--r--drivers/mfd/db8500-prcmu.c10
-rw-r--r--drivers/mfd/dln2.c1
-rw-r--r--drivers/mfd/ene-kb3930.c2
-rw-r--r--drivers/mfd/exynos-lpass.c40
-rw-r--r--drivers/mfd/ezx-pcap.c33
-rw-r--r--drivers/mfd/fsl-imx25-tsadc.c12
-rw-r--r--drivers/mfd/gateworks-gsc.c4
-rw-r--r--drivers/mfd/hi6421-pmic-core.c9
-rw-r--r--drivers/mfd/hi655x-pmic.c18
-rw-r--r--drivers/mfd/intel-lpss-acpi.c31
-rw-r--r--drivers/mfd/intel-lpss-pci.c276
-rw-r--r--drivers/mfd/intel-lpss.c63
-rw-r--r--drivers/mfd/intel-lpss.h42
-rw-r--r--drivers/mfd/intel-m10-bmc-core.c10
-rw-r--r--drivers/mfd/intel-m10-bmc-pmci.c5
-rw-r--r--drivers/mfd/intel-m10-bmc-spi.c5
-rw-r--r--drivers/mfd/intel_pmc_bxt.c3
-rw-r--r--drivers/mfd/intel_soc_pmic_bxtwc.c195
-rw-r--r--drivers/mfd/intel_soc_pmic_chtdc_ti.c5
-rw-r--r--drivers/mfd/intel_soc_pmic_chtwc.c3
-rw-r--r--drivers/mfd/intel_soc_pmic_crc.c14
-rw-r--r--drivers/mfd/intel_soc_pmic_mrfld.c3
-rw-r--r--drivers/mfd/ioc3.c2
-rw-r--r--drivers/mfd/ipaq-micro.c11
-rw-r--r--drivers/mfd/iqs62x.c6
-rw-r--r--drivers/mfd/kempld-core.c264
-rw-r--r--drivers/mfd/khadas-mcu.c2
-rw-r--r--drivers/mfd/lm3533-core.c28
-rw-r--r--drivers/mfd/lochnagar-i2c.c15
-rw-r--r--drivers/mfd/loongson-se.c253
-rw-r--r--drivers/mfd/lp3943.c2
-rw-r--r--drivers/mfd/lp873x.c6
-rw-r--r--drivers/mfd/lp87565.c13
-rw-r--r--drivers/mfd/lp8788-irq.c2
-rw-r--r--drivers/mfd/lp8788.c2
-rw-r--r--drivers/mfd/lpc_ich.c133
-rw-r--r--drivers/mfd/ls2k-bmc-core.c532
-rw-r--r--drivers/mfd/macsmc.c499
-rw-r--r--drivers/mfd/madera-core.c4
-rw-r--r--drivers/mfd/madera-i2c.c10
-rw-r--r--drivers/mfd/madera-spi.c10
-rw-r--r--drivers/mfd/max14577.c17
-rw-r--r--drivers/mfd/max7360.c171
-rw-r--r--drivers/mfd/max77541.c8
-rw-r--r--drivers/mfd/max77620.c32
-rw-r--r--drivers/mfd/max77686.c3
-rw-r--r--drivers/mfd/max77705.c180
-rw-r--r--drivers/mfd/max77759.c690
-rw-r--r--drivers/mfd/max77843.c2
-rw-r--r--drivers/mfd/max8907.c7
-rw-r--r--drivers/mfd/max8925-core.c7
-rw-r--r--drivers/mfd/max8925-i2c.c5
-rw-r--r--drivers/mfd/max8997-irq.c19
-rw-r--r--drivers/mfd/max8997.c17
-rw-r--r--drivers/mfd/max8998-irq.c2
-rw-r--r--drivers/mfd/max8998.c17
-rw-r--r--drivers/mfd/mc13xxx-core.c22
-rw-r--r--drivers/mfd/mc13xxx-i2c.c9
-rw-r--r--drivers/mfd/mc13xxx-spi.c16
-rw-r--r--drivers/mfd/mcp-core.c4
-rw-r--r--drivers/mfd/mcp-sa11x0.c4
-rw-r--r--drivers/mfd/menelaus.c3
-rw-r--r--drivers/mfd/mfd-core.c26
-rw-r--r--drivers/mfd/motorola-cpcap.c7
-rw-r--r--drivers/mfd/mt6358-irq.c11
-rw-r--r--drivers/mfd/mt6360-core.c23
-rw-r--r--drivers/mfd/mt6370.c2
-rw-r--r--drivers/mfd/mt6370.h2
-rw-r--r--drivers/mfd/mt6397-core.c72
-rw-r--r--drivers/mfd/mt6397-irq.c35
-rw-r--r--drivers/mfd/mxs-lradc.c13
-rw-r--r--drivers/mfd/nct6694.c388
-rw-r--r--drivers/mfd/ntxec.c2
-rw-r--r--drivers/mfd/ocelot-core.c6
-rw-r--r--drivers/mfd/ocelot-spi.c9
-rw-r--r--drivers/mfd/omap-usb-host.c10
-rw-r--r--drivers/mfd/omap-usb-tll.c14
-rw-r--r--drivers/mfd/palmas.c63
-rw-r--r--drivers/mfd/pcf50633-adc.c257
-rw-r--r--drivers/mfd/pcf50633-core.c304
-rw-r--r--drivers/mfd/pcf50633-gpio.c91
-rw-r--r--drivers/mfd/pcf50633-irq.c312
-rw-r--r--drivers/mfd/pf1550.c367
-rw-r--r--drivers/mfd/qcom-pm8008.c172
-rw-r--r--drivers/mfd/qcom-pm8xxx.c20
-rw-r--r--drivers/mfd/qcom-spmi-pmic.c107
-rw-r--r--drivers/mfd/qcom_rpm.c8
-rw-r--r--drivers/mfd/qnap-mcu.c417
-rw-r--r--drivers/mfd/rave-sp.c20
-rw-r--r--drivers/mfd/rc5t583.c2
-rw-r--r--drivers/mfd/retu-mfd.c16
-rw-r--r--drivers/mfd/rk8xx-core.c169
-rw-r--r--drivers/mfd/rk8xx-i2c.c76
-rw-r--r--drivers/mfd/rk8xx-spi.c2
-rw-r--r--drivers/mfd/rn5t618.c15
-rw-r--r--drivers/mfd/rohm-bd71828.c114
-rw-r--r--drivers/mfd/rohm-bd718x7.c15
-rw-r--r--drivers/mfd/rohm-bd9576.c10
-rw-r--r--drivers/mfd/rohm-bd96801.c793
-rw-r--r--drivers/mfd/rsmu_core.c2
-rw-r--r--drivers/mfd/rsmu_i2c.c111
-rw-r--r--drivers/mfd/rsmu_spi.c10
-rw-r--r--drivers/mfd/rt4831.c1
-rw-r--r--drivers/mfd/rt5033.c12
-rw-r--r--drivers/mfd/rz-mtu3.c36
-rw-r--r--drivers/mfd/sec-acpm.c421
-rw-r--r--drivers/mfd/sec-common.c301
-rw-r--r--drivers/mfd/sec-core.c460
-rw-r--r--drivers/mfd/sec-core.h23
-rw-r--r--drivers/mfd/sec-i2c.c239
-rw-r--r--drivers/mfd/sec-irq.c561
-rw-r--r--drivers/mfd/si476x-cmd.c2
-rw-r--r--drivers/mfd/si476x-prop.c2
-rw-r--r--drivers/mfd/simple-mfd-i2c.c45
-rw-r--r--drivers/mfd/sm501.c58
-rw-r--r--drivers/mfd/sprd-sc27xx-spi.c9
-rw-r--r--drivers/mfd/ssbi.c8
-rw-r--r--drivers/mfd/sta2x11-mfd.c645
-rw-r--r--drivers/mfd/stm32-lptimer.c35
-rw-r--r--drivers/mfd/stm32-timers.c86
-rw-r--r--drivers/mfd/stmfx.c7
-rw-r--r--drivers/mfd/stmpe-i2c.c16
-rw-r--r--drivers/mfd/stmpe-spi.c16
-rw-r--r--drivers/mfd/stmpe.c13
-rw-r--r--drivers/mfd/stpmic1.c10
-rw-r--r--drivers/mfd/stw481x.c4
-rw-r--r--drivers/mfd/sun4i-gpadc.c5
-rw-r--r--drivers/mfd/syscon.c200
-rw-r--r--drivers/mfd/tc3589x.c12
-rw-r--r--drivers/mfd/ti-lmu.c1
-rw-r--r--drivers/mfd/ti_am335x_tscadc.c15
-rw-r--r--drivers/mfd/timberdale.c19
-rw-r--r--drivers/mfd/tps6105x.c6
-rw-r--r--drivers/mfd/tps65010.c28
-rw-r--r--drivers/mfd/tps6507x.c3
-rw-r--r--drivers/mfd/tps65086.c24
-rw-r--r--drivers/mfd/tps65090.c9
-rw-r--r--drivers/mfd/tps65217.c5
-rw-r--r--drivers/mfd/tps65218.c5
-rw-r--r--drivers/mfd/tps65219.c301
-rw-r--r--drivers/mfd/tps6586x.c62
-rw-r--r--drivers/mfd/tps65910.c19
-rw-r--r--drivers/mfd/tps65911-comparator.c4
-rw-r--r--drivers/mfd/tps65912-core.c25
-rw-r--r--drivers/mfd/tps65912-i2c.c10
-rw-r--r--drivers/mfd/tps65912-spi.c8
-rw-r--r--drivers/mfd/tps6594-core.c387
-rw-r--r--drivers/mfd/tps6594-i2c.c26
-rw-r--r--drivers/mfd/tps6594-spi.c28
-rw-r--r--drivers/mfd/tqmx86.c123
-rw-r--r--drivers/mfd/twl-core.c56
-rw-r--r--drivers/mfd/twl4030-audio.c4
-rw-r--r--drivers/mfd/twl4030-irq.c5
-rw-r--r--drivers/mfd/twl4030-power.c12
-rw-r--r--drivers/mfd/twl6030-irq.c80
-rw-r--r--drivers/mfd/twl6040.c12
-rw-r--r--drivers/mfd/ucb1x00-core.c5
-rw-r--r--drivers/mfd/upboard-fpga.c324
-rw-r--r--drivers/mfd/vexpress-sysreg.c73
-rw-r--r--drivers/mfd/wcd934x.c6
-rw-r--r--drivers/mfd/wl1273-core.c262
-rw-r--r--drivers/mfd/wm5102-tables.c4
-rw-r--r--drivers/mfd/wm5110-tables.c4
-rw-r--r--drivers/mfd/wm831x-auxadc.c43
-rw-r--r--drivers/mfd/wm831x-core.c3
-rw-r--r--drivers/mfd/wm831x-i2c.c16
-rw-r--r--drivers/mfd/wm831x-irq.c13
-rw-r--r--drivers/mfd/wm831x-spi.c16
-rw-r--r--drivers/mfd/wm8350-i2c.c6
-rw-r--r--drivers/mfd/wm8350-regmap.c2
-rw-r--r--drivers/mfd/wm8400-core.c4
-rw-r--r--drivers/mfd/wm8994-core.c11
-rw-r--r--drivers/mfd/wm8994-irq.c4
-rw-r--r--drivers/mfd/wm8994-regmap.c6
-rw-r--r--drivers/mfd/wm8997-tables.c2
-rw-r--r--drivers/mfd/wm8998-tables.c2
-rw-r--r--drivers/mfd/wm97xx-core.c6
232 files changed, 13470 insertions, 4767 deletions
diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c
index 300caa067335..e9941da58b18 100644
--- a/drivers/mfd/88pm800.c
+++ b/drivers/mfd/88pm800.c
@@ -116,7 +116,7 @@ enum {
#define PM800_CHIP_GEN_ID_NUM 0x3
static const struct i2c_device_id pm80x_id_table[] = {
- {"88PM800", 0},
+ { "88PM800" },
{} /* NULL terminated */
};
MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
@@ -391,7 +391,7 @@ static void device_irq_exit_800(struct pm80x_chip *chip)
regmap_del_irq_chip(chip->irq, chip->irq_data);
}
-static struct regmap_irq_chip pm800_irq_chip = {
+static const struct regmap_irq_chip pm800_irq_chip = {
.name = "88pm800",
.irqs = pm800_irqs,
.num_irqs = ARRAY_SIZE(pm800_irqs),
diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c
index 68417c3c4f5a..f5d6663172ee 100644
--- a/drivers/mfd/88pm805.c
+++ b/drivers/mfd/88pm805.c
@@ -30,7 +30,7 @@
#include <linux/delay.h>
static const struct i2c_device_id pm80x_id_table[] = {
- {"88PM805", 0},
+ { "88PM805" },
{} /* NULL terminated */
};
MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
@@ -73,7 +73,7 @@ static const struct mfd_cell codec_devs[] = {
},
};
-static struct regmap_irq pm805_irqs[] = {
+static const struct regmap_irq pm805_irqs[] = {
/* INT0 */
[PM805_IRQ_LDO_OFF] = {
.mask = PM805_INT1_HP1_SHRT,
@@ -163,7 +163,7 @@ static void device_irq_exit_805(struct pm80x_chip *chip)
regmap_del_irq_chip(chip->irq, chip->irq_data);
}
-static struct regmap_irq_chip pm805_irq_chip = {
+static const struct regmap_irq_chip pm805_irq_chip = {
.name = "88pm805",
.irqs = pm805_irqs,
.num_irqs = ARRAY_SIZE(pm805_irqs),
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index 151bf03e772d..77230fbe07be 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -573,7 +573,6 @@ static int device_irq_init(struct pm860x_chip *chip,
unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
int data, mask, ret = -EINVAL;
int nr_irqs, irq_base = -1;
- struct device_node *node = i2c->dev.of_node;
mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
| PM8607_B0_MISC1_INT_MASK;
@@ -624,8 +623,8 @@ static int device_irq_init(struct pm860x_chip *chip,
ret = -EBUSY;
goto out;
}
- irq_domain_add_legacy(node, nr_irqs, chip->irq_base, 0,
- &pm860x_irq_domain_ops, chip);
+ irq_domain_create_legacy(dev_fwnode(&i2c->dev), nr_irqs, chip->irq_base, 0,
+ &pm860x_irq_domain_ops, chip);
chip->core_irq = i2c->irq;
if (!chip->core_irq)
goto out;
@@ -916,7 +915,7 @@ static void device_power_init(struct pm860x_chip *chip,
power_devs[0].platform_data = pdata->power;
power_devs[0].pdata_size = sizeof(struct pm860x_power_pdata);
power_devs[0].num_resources = ARRAY_SIZE(battery_resources);
- power_devs[0].resources = &battery_resources[0],
+ power_devs[0].resources = &battery_resources[0];
ret = mfd_add_devices(chip->dev, 0, &power_devs[0], 1,
&battery_resources[0], chip->irq_base, NULL);
if (ret < 0)
@@ -925,7 +924,7 @@ static void device_power_init(struct pm860x_chip *chip,
power_devs[1].platform_data = pdata->power;
power_devs[1].pdata_size = sizeof(struct pm860x_power_pdata);
power_devs[1].num_resources = ARRAY_SIZE(charger_resources);
- power_devs[1].resources = &charger_resources[0],
+ power_devs[1].resources = &charger_resources[0];
ret = mfd_add_devices(chip->dev, 0, &power_devs[1], 1,
&charger_resources[0], chip->irq_base, NULL);
if (ret < 0)
@@ -942,7 +941,7 @@ static void device_power_init(struct pm860x_chip *chip,
pdata->chg_desc->charger_regulators =
&chg_desc_regulator_data[0];
pdata->chg_desc->num_charger_regulators =
- ARRAY_SIZE(chg_desc_regulator_data),
+ ARRAY_SIZE(chg_desc_regulator_data);
power_devs[3].platform_data = pdata->chg_desc;
power_devs[3].pdata_size = sizeof(*pdata->chg_desc);
ret = mfd_add_devices(chip->dev, 0, &power_devs[3], 1,
@@ -958,7 +957,7 @@ static void device_onkey_init(struct pm860x_chip *chip,
int ret;
onkey_devs[0].num_resources = ARRAY_SIZE(onkey_resources);
- onkey_devs[0].resources = &onkey_resources[0],
+ onkey_devs[0].resources = &onkey_resources[0];
ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
ARRAY_SIZE(onkey_devs), &onkey_resources[0],
chip->irq_base, NULL);
@@ -972,7 +971,7 @@ static void device_codec_init(struct pm860x_chip *chip,
int ret;
codec_devs[0].num_resources = ARRAY_SIZE(codec_resources);
- codec_devs[0].resources = &codec_resources[0],
+ codec_devs[0].resources = &codec_resources[0];
ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
ARRAY_SIZE(codec_devs), &codec_resources[0], 0,
NULL);
@@ -1233,7 +1232,7 @@ static int pm860x_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(pm860x_pm_ops, pm860x_suspend, pm860x_resume);
static const struct i2c_device_id pm860x_id_table[] = {
- { "88PM860x", 0 },
+ { "88PM860x" },
{}
};
MODULE_DEVICE_TABLE(i2c, pm860x_id_table);
diff --git a/drivers/mfd/88pm886.c b/drivers/mfd/88pm886.c
new file mode 100644
index 000000000000..e411d8dee554
--- /dev/null
+++ b/drivers/mfd/88pm886.c
@@ -0,0 +1,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");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 6f5b259a6d6a..aace5766b38a 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -20,6 +20,18 @@ config MFD_CS5535
This is the core driver for CS5535/CS5536 MFD functions. This is
necessary for using the board's GPIO and MFGPT functionality.
+config MFD_ADP5585
+ tristate "Analog Devices ADP5585 keypad decoder and I/O expander driver"
+ select MFD_CORE
+ select REGMAP_I2C
+ depends on I2C
+ depends on OF
+ help
+ Say yes here to add support for the Analog Devices ADP5585 GPIO
+ expander, PWM and keypad controller. This includes the I2C driver and
+ the core APIs _only_, you have to select individual components like
+ the GPIO and PWM functions under the corresponding menus.
+
config MFD_ALTERA_A10SR
bool "Altera Arria10 DevKit System Resource chip"
depends on ARCH_INTEL_SOCFPGA && SPI_MASTER=y && OF
@@ -33,7 +45,8 @@ config MFD_ALTERA_A10SR
config MFD_ALTERA_SYSMGR
bool "Altera SOCFPGA System Manager"
- depends on ARCH_INTEL_SOCFPGA && OF
+ depends on ARCH_INTEL_SOCFPGA || COMPILE_TEST
+ depends on OF
select MFD_SYSCON
help
Select this to get System Manager support for all Altera branded
@@ -117,6 +130,7 @@ config MFD_AAT2870_CORE
select MFD_CORE
depends on I2C=y
depends on GPIOLIB || COMPILE_TEST
+ depends on GPIOLIB_LEGACY
help
If you say yes here you get support for the AAT2870.
This driver provides common support for accessing the device,
@@ -126,7 +140,7 @@ config MFD_AAT2870_CORE
config MFD_AT91_USART
tristate "AT91 USART Driver"
select MFD_CORE
- depends on ARCH_AT91 || COMPILE_TEST
+ depends on ARCH_MICROCHIP || COMPILE_TEST
help
Select this to get support for AT91 USART IP. This is a wrapper
over at91-usart-serial driver and usart-spi-driver. Only one function
@@ -224,6 +238,18 @@ config MFD_AXP20X_RSB
components like regulators or the PEK (Power Enable Key) under the
corresponding menus.
+config MFD_CGBC
+ tristate "Congatec Board Controller"
+ select MFD_CORE
+ depends on X86
+ help
+ This is the core driver of the Board Controller found on some Congatec
+ SMARC modules. The Board Controller provides functions like watchdog,
+ I2C busses, and GPIO controller.
+
+ To compile this driver as a module, choose M here: the module will be
+ called cgbc-core.
+
config MFD_CROS_EC_DEV
tristate "ChromeOS Embedded Controller multifunction device"
select MFD_CORE
@@ -237,6 +263,86 @@ config MFD_CROS_EC_DEV
To compile this driver as a module, choose M here: the module will be
called cros-ec-dev.
+config MFD_CS40L50_CORE
+ tristate
+ select MFD_CORE
+ select FW_CS_DSP
+ select REGMAP_IRQ
+
+config MFD_CS40L50_I2C
+ tristate "Cirrus Logic CS40L50 (I2C)"
+ select REGMAP_I2C
+ select MFD_CS40L50_CORE
+ depends on I2C
+ help
+ Select this to support the Cirrus Logic CS40L50 Haptic
+ Driver over I2C.
+
+ This driver can be built as a module. If built as a module it will be
+ called "cs40l50-i2c".
+
+config MFD_CS40L50_SPI
+ tristate "Cirrus Logic CS40L50 (SPI)"
+ select REGMAP_SPI
+ select MFD_CS40L50_CORE
+ depends on SPI
+ help
+ Select this to support the Cirrus Logic CS40L50 Haptic
+ Driver over SPI.
+
+ This driver can be built as a module. If built as a module it will be
+ called "cs40l50-spi".
+
+config MFD_CS42L43
+ tristate
+ select MFD_CORE
+ select REGMAP
+ select REGMAP_IRQ
+
+config MFD_CS42L43_I2C
+ tristate "Cirrus Logic CS42L43 (I2C)"
+ depends on I2C
+ select REGMAP_I2C
+ select MFD_CS42L43
+ help
+ Select this to support the Cirrus Logic CS42L43 PC CODEC with
+ headphone and class D speaker drivers over I2C.
+
+config MFD_CS42L43_SDW
+ tristate "Cirrus Logic CS42L43 (SoundWire)"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select MFD_CS42L43
+ help
+ Select this to support the Cirrus Logic CS42L43 PC CODEC with
+ headphone and class D speaker drivers over SoundWire.
+
+config MFD_LOCHNAGAR
+ bool "Cirrus Logic Lochnagar Audio Development Board"
+ select MFD_CORE
+ select REGMAP_I2C
+ depends on I2C=y && OF
+ help
+ Support for Cirrus Logic Lochnagar audio development board.
+
+config MFD_MACSMC
+ tristate "Apple Silicon System Management Controller (SMC)"
+ depends on ARCH_APPLE || COMPILE_TEST
+ depends on OF
+ depends on APPLE_RTKIT
+ select MFD_CORE
+ help
+ The System Management Controller (SMC) on Apple Silicon machines is a
+ piece of hardware that exposes various functionalities such as
+ temperature sensors, voltage/power meters, shutdown/reboot handling,
+ GPIOs and more.
+
+ Communication happens via a shared mailbox using the RTKit protocol
+ which is also used for other co-processors. The SMC protocol then
+ allows reading and writing many different keys which implement the
+ various features. The MFD core device handles this protocol and
+ exposes it to the sub-devices.
+
config MFD_MADERA
tristate "Cirrus Logic Madera codecs"
select MFD_CORE
@@ -266,16 +372,6 @@ config MFD_MADERA_SPI
Support for the Cirrus Logic Madera platform audio SoC
core functionality controlled via SPI.
-config MFD_MAX5970
- tristate "Maxim 5970/5978 power switch and monitor"
- depends on (I2C && OF)
- select MFD_SIMPLE_MFD_I2C
- help
- This driver controls a Maxim 5970/5978 switch via I2C bus.
- The MAX5970/5978 is a smart switch with no output regulation, but
- fault protection and voltage and current monitoring capabilities.
- Also it supports upto 4 indication leds.
-
config MFD_CS47L15
bool "Cirrus Logic CS47L15"
select PINCTRL_CS47L15
@@ -434,7 +530,7 @@ config MFD_EXYNOS_LPASS
config MFD_GATEWORKS_GSC
tristate "Gateworks System Controller"
- depends on (I2C && OF)
+ depends on I2C && OF
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
@@ -449,7 +545,7 @@ config MFD_GATEWORKS_GSC
config MFD_MC13XXX
tristate
- depends on (SPI_MASTER || I2C)
+ depends on SPI_MASTER || I2C
select MFD_CORE
select REGMAP_IRQ
help
@@ -510,6 +606,22 @@ config MFD_MX25_TSADC
i.MX25 processors. They consist of a conversion queue for general
purpose ADC and a queue for Touchscreens.
+config MFD_PF1550
+ tristate "NXP PF1550 PMIC Support"
+ depends on I2C=y && OF
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ Say yes here to add support for NXP PF1550. This is a companion Power
+ Management IC with regulators, onkey, and charger control on chip.
+ This driver provides common support for accessing the device;
+ additional drivers must be enabled in order to use the functionality
+ of the device.
+
+ This driver can also be built as a module and if so will be called
+ pf1550.
+
config MFD_HI6421_PMIC
tristate "HiSilicon Hi6421 PMU/Codec IC"
depends on OF
@@ -770,6 +882,28 @@ config MFD_88PM860X
select individual components like voltage regulators, RTC and
battery-charger under the corresponding menus.
+config MFD_88PM886_PMIC
+ bool "Marvell 88PM886 PMIC"
+ depends on I2C=y
+ depends on OF
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ select MFD_CORE
+ help
+ This enables support for Marvell 88PM886 Power Management IC.
+ This includes the I2C driver and the core APIs _only_, you have to
+ select individual components like onkey under the corresponding menus.
+
+config MFD_MAX5970
+ tristate "Maxim 5970/5978 power switch and monitor"
+ depends on I2C
+ select MFD_SIMPLE_MFD_I2C
+ help
+ This driver controls a Maxim 5970/5978 switch via I2C bus.
+ The MAX5970/5978 is a smart switch with no output regulation, but
+ fault protection and voltage and current monitoring capabilities.
+ Also it supports upto 4 indication leds.
+
config MFD_MAX14577
tristate "Maxim Semiconductor MAX14577/77836 MUIC + Charger Support"
depends on I2C
@@ -798,7 +932,7 @@ config MFD_MAX77541
There are regulators and adc.
config MFD_MAX77620
- bool "Maxim Semiconductor MAX77620 and MAX20024 PMIC Support"
+ tristate "Maxim Semiconductor MAX77620 and MAX20024 PMIC Support"
depends on I2C=y
depends on OF
select MFD_CORE
@@ -856,6 +990,19 @@ config MFD_MAX77693
additional drivers must be enabled in order to use the functionality
of the device.
+config MFD_MAX77705
+ tristate "Maxim MAX77705 PMIC Support"
+ depends on I2C
+ select MFD_CORE
+ select MFD_SIMPLE_MFD_I2C
+ help
+ Say yes here to add support for Maxim Integrated MAX77705 PMIC.
+ This is a Power Management IC with Charger, safe LDOs, Flash, Haptic
+ and MUIC controls on chip.
+ This driver provides common support for accessing the device;
+ additional drivers must be enabled in order to use the functionality
+ of the device.
+
config MFD_MAX77714
tristate "Maxim Semiconductor MAX77714 PMIC Support"
depends on I2C
@@ -870,6 +1017,26 @@ config MFD_MAX77714
drivers must be enabled in order to use each functionality of the
device.
+config MFD_MAX77759
+ tristate "Maxim Integrated MAX77759 PMIC"
+ depends on I2C
+ depends on OF
+ select IRQ_DOMAIN
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ Say yes here to add support for Maxim Integrated MAX77759.
+ This is a companion Power Management IC for USB Type-C applications
+ with Battery Charger, Fuel Gauge, temperature sensors, USB Type-C
+ Port Controller (TCPC), NVMEM, and additional GPIO interfaces.
+ This driver provides common support for accessing the device;
+ additional drivers must be enabled in order to use the functionality
+ of the device.
+
+ To compile this driver as a module, choose M here: the module will be
+ called max77759.
+
config MFD_MAX77843
bool "Maxim Semiconductor MAX77843 PMIC Support"
depends on I2C=y
@@ -985,6 +1152,21 @@ config MFD_MENF21BMC
This driver can also be built as a module. If so the module
will be called menf21bmc.
+config MFD_NCT6694
+ tristate "Nuvoton NCT6694 support"
+ select MFD_CORE
+ depends on USB
+ help
+ This enables support for the Nuvoton USB device NCT6694, which shares
+ peripherals.
+ The Nuvoton NCT6694 is a peripheral expander with 16 GPIO chips,
+ 6 I2C controllers, 2 CANfd controllers, 2 Watchdog timers, ADC,
+ PWM, and RTC.
+ This driver provides core APIs to access the NCT6694 hardware
+ monitoring and control features.
+ Additional drivers must be enabled to utilize the specific
+ functionalities of the device.
+
config MFD_OCELOT
tristate "Microsemi Ocelot External Control Support"
depends on SPI_MASTER
@@ -1059,33 +1241,9 @@ config MFD_RETU
Retu and Tahvo are a multi-function devices found on Nokia
Internet Tablets (770, N800 and N810).
-config MFD_PCF50633
- tristate "NXP PCF50633"
- depends on I2C
- select REGMAP_I2C
- help
- Say yes here if you have NXP PCF50633 chip on your board.
- This core driver provides register access and IRQ handling
- facilities, and registers devices for the various functions
- so that function-specific drivers can bind to them.
-
-config PCF50633_ADC
- tristate "NXP PCF50633 ADC"
- depends on MFD_PCF50633
- help
- Say yes here if you want to include support for ADC in the
- NXP PCF50633 chip.
-
-config PCF50633_GPIO
- tristate "NXP PCF50633 GPIO"
- depends on MFD_PCF50633
- help
- Say yes here if you want to include support GPIO for pins on
- the PCF50633 chip.
-
config MFD_PM8XXX
tristate "Qualcomm PM8xxx PMIC chips driver"
- depends on (ARM || HEXAGON || COMPILE_TEST)
+ depends on ARM || HEXAGON || COMPILE_TEST
select IRQ_DOMAIN_HIERARCHY
select MFD_CORE
select REGMAP
@@ -1113,6 +1271,18 @@ config MFD_QCOM_RPM
Say M here if you want to include support for the Qualcomm RPM as a
module. This will build a module called "qcom_rpm".
+config MFD_SPACEMIT_P1
+ tristate "SpacemiT P1 PMIC"
+ depends on ARCH_SPACEMIT || COMPILE_TEST
+ depends on I2C
+ select MFD_SIMPLE_MFD_I2C
+ help
+ This option supports the I2C-based SpacemiT P1 PMIC, which
+ contains regulators, a power switch, GPIOs, an RTC, and more.
+ This option is selected when any of the supported sub-devices
+ is configured. The basic functionality is implemented by the
+ simple MFD I2C driver.
+
config MFD_SPMI_PMIC
tristate "Qualcomm SPMI PMICs"
depends on ARCH_QCOM || COMPILE_TEST
@@ -1197,11 +1367,11 @@ config MFD_RC5T583
different functionality of the device.
config MFD_RK8XX
- bool
+ tristate
select MFD_CORE
config MFD_RK8XX_I2C
- tristate "Rockchip RK805/RK808/RK809/RK817/RK818 Power Management Chip"
+ tristate "Rockchip RK805/RK808/RK809/RK816/RK817/RK818 Power Management Chip"
depends on I2C && OF
select MFD_CORE
select REGMAP_I2C
@@ -1209,7 +1379,7 @@ config MFD_RK8XX_I2C
select MFD_RK8XX
help
If you say yes here you get support for the RK805, RK808, RK809,
- RK817 and RK818 Power Management chips.
+ RK816, RK817 and RK818 Power Management chips.
This driver provides common support for accessing the device
through I2C interface. The device supports multiple sub-devices
including interrupts, RTC, LDO & DCDC regulators, and onkey.
@@ -1243,21 +1413,42 @@ config MFD_RN5T618
functionality of the device.
config MFD_SEC_CORE
- tristate "Samsung Electronics PMIC Series Support"
+ tristate
+ select MFD_CORE
+ select REGMAP_IRQ
+
+config MFD_SEC_ACPM
+ tristate "Samsung Electronics S2MPG1x PMICs"
+ depends on EXYNOS_ACPM_PROTOCOL
+ depends on OF
+ select MFD_SEC_CORE
+ help
+ Support for the Samsung Electronics PMICs with ACPM interface.
+ This is a Power Management IC for mobile applications with buck
+ converters, various LDOs, power meters, RTC, clock outputs, and
+ additional GPIOs interfaces.
+ This driver provides common support for accessing the device;
+ additional drivers must be enabled in order to use the functionality
+ of the device.
+
+ To compile this driver as a module, choose M here: the module will be
+ called sec-acpm.
+
+config MFD_SEC_I2C
+ tristate "Samsung Electronics S2MPA/S2MPS1X/S2MPU/S5M series PMICs"
depends on I2C=y
depends on OF
- select MFD_CORE
+ select MFD_SEC_CORE
select REGMAP_I2C
- select REGMAP_IRQ
help
- Support for the Samsung Electronics PMIC devices coming
- usually along with Samsung Exynos SoC chipset.
+ Support for the Samsung Electronics PMIC devices with I2C interface
+ coming usually along with Samsung Exynos SoC chipset.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the functionality
- of the device
+ of the device.
To compile this driver as a module, choose M here: the
- module will be called sec-core.
+ module will be called sec-i2c.
Have in mind that important core drivers (like regulators) depend
on this driver so building this as a module might require proper
initial ramdisk or might not boot up as well in certain scenarios.
@@ -1265,6 +1456,7 @@ config MFD_SEC_CORE
config MFD_SI476X_CORE
tristate "Silicon Laboratories 4761/64/68 AM/FM radio."
depends on I2C
+ depends on GPIOLIB_LEGACY
select MFD_CORE
select REGMAP_I2C
help
@@ -1348,8 +1540,9 @@ config MFD_SC27XX_PMIC
and it also adds the irq_chip parts for handling the PMIC chip events.
config RZ_MTU3
- bool "Renesas RZ/G2L MTU3a core driver"
+ tristate "Renesas RZ/G2L MTU3a core driver"
depends on (ARCH_RZG2L && OF) || COMPILE_TEST
+ select MFD_CORE
help
Select this option to enable Renesas RZ/G2L MTU3a core driver for
the Multi-Function Timer Pulse Unit 3 (MTU3a) hardware available
@@ -1392,8 +1585,8 @@ config MFD_DB8500_PRCMU
through a register map.
config MFD_STMPE
- bool "STMicroelectronics STMPE"
- depends on (I2C=y || SPI_MASTER=y)
+ tristate "STMicroelectronics STMPE"
+ depends on I2C || SPI_MASTER
depends on OF
select MFD_CORE
help
@@ -1421,25 +1614,19 @@ menu "STMicroelectronics STMPE Interface Drivers"
depends on MFD_STMPE
config STMPE_I2C
- bool "STMicroelectronics STMPE I2C Interface"
- depends on I2C=y
+ tristate "STMicroelectronics STMPE I2C Interface"
+ depends on I2C
default y
help
This is used to enable I2C interface of STMPE
config STMPE_SPI
- bool "STMicroelectronics STMPE SPI Interface"
+ tristate "STMicroelectronics STMPE SPI Interface"
depends on SPI_MASTER
help
This is used to enable SPI interface of STMPE
endmenu
-config MFD_STA2X11
- bool "STMicroelectronics STA2X11"
- depends on STA2X11
- select MFD_CORE
- select REGMAP_MMIO
-
config MFD_SUN6I_PRCM
bool "Allwinner A31/A23/A33 PRCM controller"
depends on ARCH_SUNXI || COMPILE_TEST
@@ -1458,6 +1645,7 @@ config MFD_SYSCON
config MFD_TI_AM335X_TSCADC
tristate "TI ADC / Touch Screen chip support"
+ depends on ARCH_OMAP2PLUS || ARCH_K3 || COMPILE_TEST
select MFD_CORE
select REGMAP
select REGMAP_MMIO
@@ -1499,6 +1687,17 @@ config MFD_TI_LMU
LM36274. It consists of backlight, LED and regulator driver.
It provides consistent device controls for lighting functions.
+config MFD_BQ257XX
+ tristate "TI BQ257XX Buck/Boost Charge Controller"
+ depends on I2C
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ Support Texas Instruments BQ25703 Buck/Boost converter with
+ charge controller. It consists of regulators that provide
+ system voltage and OTG voltage, and a charger manager for
+ batteries containing one or more cells.
+
config MFD_OMAP_USB_HOST
bool "TI OMAP USBHS core and TLL driver"
depends on USB_EHCI_HCD_OMAP || USB_OHCI_HCD_OMAP3
@@ -1747,6 +1946,7 @@ config TWL4030_CORE
bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 Support"
depends on I2C=y
select IRQ_DOMAIN
+ select MFD_CORE
select REGMAP_I2C
help
Say yes here if you have TWL4030 / TWL6030 family chip on your board.
@@ -1802,16 +2002,6 @@ config MENELAUS
and other features that are often used in portable devices like
cell phones and PDAs.
-config MFD_WL1273_CORE
- tristate "TI WL1273 FM radio"
- depends on I2C
- select MFD_CORE
- default n
- help
- This is the core driver for the TI WL1273 FM radio. This MFD
- driver connects the radio-wl1273 V4L2 module and the wl1273
- audio codec.
-
config MFD_LM3533
tristate "TI/National Semiconductor LM3533 Lighting Power chip"
depends on I2C
@@ -1834,7 +2024,7 @@ config MFD_TIMBERDALE
multifunction device which exposes numerous platform devices.
The timberdale FPGA can be found on the Intel Atom development board
- for in-vehicle infontainment, called Russellville.
+ for in-vehicle infotainment, called Russellville.
config MFD_TC3589X
bool "Toshiba TC35892 and variants"
@@ -1865,14 +2055,6 @@ config MFD_VX855
VIA VX855/VX875 south bridge. You will need to enable the vx855_spi
and/or vx855_gpio drivers for this to do anything useful.
-config MFD_LOCHNAGAR
- bool "Cirrus Logic Lochnagar Audio Development Board"
- select MFD_CORE
- select REGMAP_I2C
- depends on I2C=y && OF
- help
- Support for Cirrus Logic Lochnagar audio development board.
-
config MFD_ARIZONA
select REGMAP
select REGMAP_IRQ
@@ -2062,6 +2244,19 @@ config MFD_ROHM_BD957XMUF
BD9573MUF Power Management ICs. BD9576 and BD9573 are primarily
designed to be used to power R-Car series processors.
+config MFD_ROHM_BD96801
+ tristate "ROHM BD96801 Power Management IC"
+ depends on I2C=y
+ depends on OF
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ select MFD_CORE
+ help
+ Select this option to get support for the ROHM BD96801 Power
+ Management IC. The ROHM BD96801 is a highly scalable Power Management
+ IC for industrial and automotive use. The BD96801 can be used as a
+ master PMIC in a chained PMIC solution with suitable companion PMICs.
+
config MFD_STM32_LPTIMER
tristate "Support for STM32 Low-Power Timer"
depends on (ARCH_STM32 && OF) || COMPILE_TEST
@@ -2089,7 +2284,7 @@ config MFD_STM32_TIMERS
config MFD_STPMIC1
tristate "Support for STPMIC1 PMIC"
- depends on (I2C=y && OF)
+ depends on I2C=y && OF
select REGMAP_I2C
select REGMAP_IRQ
select MFD_CORE
@@ -2181,6 +2376,7 @@ config MFD_ACER_A500_EC
config MFD_QCOM_PM8008
tristate "QCOM PM8008 Power Management IC"
depends on I2C && OF
+ select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
help
@@ -2230,7 +2426,7 @@ config MFD_VEXPRESS_SYSREG
config RAVE_SP_CORE
tristate "RAVE SP MCU core driver"
depends on SERIAL_DEV_BUS
- select CRC_CCITT
+ select CRC_ITU_T
help
Select this to get support for the Supervisory Processor
device found on several devices in RAVE line of hardware.
@@ -2279,6 +2475,43 @@ config MFD_INTEL_M10_BMC_PMCI
additional drivers must be enabled in order to use the functionality
of the device.
+config MFD_LOONGSON_SE
+ tristate "Loongson Security Engine chip controller driver"
+ depends on LOONGARCH && ACPI
+ select MFD_CORE
+ help
+ The Loongson Security Engine chip supports RNG, SM2, SM3 and
+ SM4 accelerator engines. Each engine have its own DMA buffer
+ provided by the controller. The kernel cannot directly send
+ commands to the engine and must first send them to the controller,
+ which will forward them to the corresponding engine.
+
+config MFD_LS2K_BMC_CORE
+ bool "Loongson-2K Board Management Controller Support"
+ depends on PCI && ACPI_GENERIC_GSI
+ select MFD_CORE
+ help
+ Say yes here to add support for the Loongson-2K BMC which is a Board
+ Management Controller connected to the PCIe bus. The device supports
+ multiple sub-devices like display and IPMI. This driver provides common
+ support for accessing the devices.
+
+ The display is enabled by default in the driver, while the IPMI interface
+ is enabled independently through the IPMI_LS2K option in the IPMI section.
+
+config MFD_QNAP_MCU
+ tristate "QNAP microcontroller unit core driver"
+ depends on SERIAL_DEV_BUS
+ select MFD_CORE
+ help
+ Select this to get support for the QNAP MCU device found in
+ several devices of QNAP network attached storage products that
+ implements additional functionality for the device, like fan
+ and LED control.
+
+ This driver implements the base serial protocol to talk to the
+ device and provides functions for the other parts to hook into.
+
config MFD_RSMU_I2C
tristate "Renesas Synchronization Management Unit with I2C"
depends on I2C && OF
@@ -2307,5 +2540,31 @@ config MFD_RSMU_SPI
Additional drivers must be enabled in order to use the functionality
of the device.
+config MFD_UPBOARD_FPGA
+ tristate "Support for the AAeon UP board FPGA"
+ depends on (X86 && ACPI)
+ select MFD_CORE
+ help
+ Select this option to enable the AAEON UP and UP^2 onboard FPGA.
+ This is the core driver of this FPGA, which has a pin controller and a
+ LED controller.
+
+ To compile this driver as a module, choose M here: the module will be
+ called upboard-fpga.
+
+config MFD_MAX7360
+ tristate "Maxim MAX7360 I2C IO Expander"
+ depends on I2C
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ Say yes here to add support for Maxim MAX7360 device, embedding
+ keypad, rotary encoder, PWM and GPIO features.
+
+ This driver provides common support for accessing the device;
+ additional drivers must be enabled in order to use the functionality
+ of the device.
+
endmenu
endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f3d1f1dc73b5..e75e8045c28a 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -7,21 +7,27 @@
obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o
obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o
obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
+obj-$(CONFIG_MFD_88PM886_PMIC) += 88pm886.o
obj-$(CONFIG_MFD_ACT8945A) += act8945a.o
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o
obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o
+obj-$(CONFIG_MFD_BQ257XX) += bq257xx.o
+obj-$(CONFIG_MFD_CGBC) += cgbc-core.o
obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o
+obj-$(CONFIG_MFD_CS42L43) += cs42l43.o
+obj-$(CONFIG_MFD_CS42L43_I2C) += cs42l43-i2c.o
+obj-$(CONFIG_MFD_CS42L43_SDW) += cs42l43-sdw.o
obj-$(CONFIG_MFD_ENE_KB3930) += ene-kb3930.o
obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o
+obj-$(CONFIG_MFD_MACSMC) += macsmc.o
obj-$(CONFIG_MFD_TI_LP873X) += lp873x.o
obj-$(CONFIG_MFD_TI_LP87565) += lp87565.o
obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o
-obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o
obj-$(CONFIG_MFD_STMPE) += stmpe.o
obj-$(CONFIG_STMPE_I2C) += stmpe-i2c.o
obj-$(CONFIG_STMPE_SPI) += stmpe-spi.o
@@ -85,6 +91,10 @@ obj-$(CONFIG_MFD_MADERA) += madera.o
obj-$(CONFIG_MFD_MADERA_I2C) += madera-i2c.o
obj-$(CONFIG_MFD_MADERA_SPI) += madera-spi.o
+obj-$(CONFIG_MFD_CS40L50_CORE) += cs40l50-core.o
+obj-$(CONFIG_MFD_CS40L50_I2C) += cs40l50-i2c.o
+obj-$(CONFIG_MFD_CS40L50_SPI) += cs40l50-spi.o
+
obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
@@ -112,6 +122,10 @@ obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
+obj-$(CONFIG_MFD_PF1550) += pf1550.o
+
+obj-$(CONFIG_MFD_NCT6694) += nct6694.o
+
obj-$(CONFIG_MFD_CORE) += mfd-core.o
ocelot-soc-objs := ocelot-core.o ocelot-spi.o
@@ -154,12 +168,15 @@ obj-$(CONFIG_MFD_DA9063) += da9063.o
obj-$(CONFIG_MFD_DA9150) += da9150-core.o
obj-$(CONFIG_MFD_MAX14577) += max14577.o
+obj-$(CONFIG_MFD_MAX7360) += max7360.o
obj-$(CONFIG_MFD_MAX77541) += max77541.o
obj-$(CONFIG_MFD_MAX77620) += max77620.o
obj-$(CONFIG_MFD_MAX77650) += max77650.o
obj-$(CONFIG_MFD_MAX77686) += max77686.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o
+obj-$(CONFIG_MFD_MAX77705) += max77705.o
obj-$(CONFIG_MFD_MAX77714) += max77714.o
+obj-$(CONFIG_MFD_MAX77759) += max77759.o
obj-$(CONFIG_MFD_MAX77843) += max77843.o
obj-$(CONFIG_MFD_MAX8907) += max8907.o
max8925-objs := max8925-core.o max8925-i2c.o
@@ -174,10 +191,6 @@ obj-$(CONFIG_MFD_MT6370) += mt6370.o
mt6397-objs := mt6397-core.o mt6397-irq.o mt6358-irq.o
obj-$(CONFIG_MFD_MT6397) += mt6397.o
-pcf50633-objs := pcf50633-core.o pcf50633-irq.o
-obj-$(CONFIG_MFD_PCF50633) += pcf50633.o
-obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
-obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
obj-$(CONFIG_RZ_MTU3) += rz-mtu3.o
obj-$(CONFIG_ABX500_CORE) += abx500-core.o
obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o
@@ -185,6 +198,7 @@ obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o
obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
+obj-$(CONFIG_MFD_ADP5585) += adp5585.o
obj-$(CONFIG_MFD_KEMPLD) += kempld-core.o
obj-$(CONFIG_MFD_INTEL_QUARK_I2C_GPIO) += intel_quark_i2c_gpio.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
@@ -193,7 +207,6 @@ obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o
obj-$(CONFIG_MFD_VX855) += vx855.o
-obj-$(CONFIG_MFD_WL1273_CORE) += wl1273-core.o
si476x-core-y := si476x-cmd.o si476x-prop.o si476x-i2c.o
obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o
@@ -222,7 +235,10 @@ obj-$(CONFIG_MFD_RK8XX) += rk8xx-core.o
obj-$(CONFIG_MFD_RK8XX_I2C) += rk8xx-i2c.o
obj-$(CONFIG_MFD_RK8XX_SPI) += rk8xx-spi.o
obj-$(CONFIG_MFD_RN5T618) += rn5t618.o
-obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o
+sec-core-objs := sec-common.o sec-irq.o
+obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o
+obj-$(CONFIG_MFD_SEC_ACPM) += sec-acpm.o
+obj-$(CONFIG_MFD_SEC_I2C) += sec-i2c.o
obj-$(CONFIG_MFD_SYSCON) += syscon.o
obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
obj-$(CONFIG_MFD_VEXPRESS_SYSREG) += vexpress-sysreg.o
@@ -261,6 +277,7 @@ obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o
obj-$(CONFIG_MFD_ROHM_BD71828) += rohm-bd71828.o
obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o
obj-$(CONFIG_MFD_ROHM_BD957XMUF) += rohm-bd9576.o
+obj-$(CONFIG_MFD_ROHM_BD96801) += rohm-bd96801.o
obj-$(CONFIG_MFD_STMFX) += stmfx.o
obj-$(CONFIG_MFD_KHADAS_MCU) += khadas-mcu.o
obj-$(CONFIG_MFD_ACER_A500_EC) += acer-ec-a500.o
@@ -274,10 +291,16 @@ obj-$(CONFIG_MFD_INTEL_M10_BMC_CORE) += intel-m10-bmc-core.o
obj-$(CONFIG_MFD_INTEL_M10_BMC_SPI) += intel-m10-bmc-spi.o
obj-$(CONFIG_MFD_INTEL_M10_BMC_PMCI) += intel-m10-bmc-pmci.o
+obj-$(CONFIG_MFD_LS2K_BMC_CORE) += ls2k-bmc-core.o
+
obj-$(CONFIG_MFD_ATC260X) += atc260x-core.o
obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o
-rsmu-i2c-objs := rsmu_core.o rsmu_i2c.o
-rsmu-spi-objs := rsmu_core.o rsmu_spi.o
-obj-$(CONFIG_MFD_RSMU_I2C) += rsmu-i2c.o
-obj-$(CONFIG_MFD_RSMU_SPI) += rsmu-spi.o
+obj-$(CONFIG_MFD_QNAP_MCU) += qnap-mcu.o
+
+obj-$(CONFIG_MFD_RSMU_I2C) += rsmu_i2c.o rsmu_core.o
+obj-$(CONFIG_MFD_RSMU_SPI) += rsmu_spi.o rsmu_core.o
+
+obj-$(CONFIG_MFD_UPBOARD_FPGA) += upboard-fpga.o
+
+obj-$(CONFIG_MFD_LOONGSON_SE) += loongson-se.o
diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c
index 2fee62f1016c..34d66ba9646a 100644
--- a/drivers/mfd/aat2870-core.c
+++ b/drivers/mfd/aat2870-core.c
@@ -320,9 +320,7 @@ static const struct file_operations aat2870_reg_fops = {
static void aat2870_init_debugfs(struct aat2870_data *aat2870)
{
- aat2870->dentry_root = debugfs_create_dir("aat2870", NULL);
-
- debugfs_create_file("regs", 0644, aat2870->dentry_root, aat2870,
+ debugfs_create_file("regs", 0644, aat2870->client->debugfs, aat2870,
&aat2870_reg_fops);
}
@@ -439,7 +437,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(aat2870_pm_ops, aat2870_i2c_suspend,
aat2870_i2c_resume);
static const struct i2c_device_id aat2870_i2c_id_table[] = {
- { "aat2870", 0 },
+ { "aat2870" },
{ }
};
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 9d9e9787d5e8..f0bc0b5a6f4a 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -21,7 +21,6 @@
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/dbx500-prcmu.h>
#include <linux/of.h>
-#include <linux/of_device.h>
/*
* Interrupt register offsets
@@ -581,9 +580,8 @@ static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np)
num_irqs = AB8500_NR_IRQS;
/* If ->irq_base is zero this will give a linear mapping */
- ab8500->domain = irq_domain_add_simple(ab8500->dev->of_node,
- num_irqs, 0,
- &ab8500_irq_ops, ab8500);
+ ab8500->domain = irq_domain_create_simple(dev_fwnode(ab8500->dev), num_irqs, 0,
+ &ab8500_irq_ops, ab8500);
if (!ab8500->domain) {
dev_err(ab8500->dev, "Failed to create irqdomain\n");
diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c
index eeeb62415f53..b6b44e2e3198 100644
--- a/drivers/mfd/ab8500-sysctrl.c
+++ b/drivers/mfd/ab8500-sysctrl.c
@@ -30,7 +30,7 @@ static void ab8500_power_off(void)
{
sigset_t old;
sigset_t all;
- static const char * const pss[] = {"ab8500_ac", "pm2301", "ab8500_usb"};
+ static const char * const pss[] = {"ab8500_ac", "ab8500_usb"};
int i;
bool charger_present = false;
union power_supply_propval val;
@@ -140,14 +140,12 @@ static int ab8500_sysctrl_probe(struct platform_device *pdev)
return 0;
}
-static int ab8500_sysctrl_remove(struct platform_device *pdev)
+static void ab8500_sysctrl_remove(struct platform_device *pdev)
{
sysctrl_dev = NULL;
if (pm_power_off == ab8500_power_off)
pm_power_off = NULL;
-
- return 0;
}
static const struct of_device_id ab8500_sysctrl_match[] = {
diff --git a/drivers/mfd/ac100.c b/drivers/mfd/ac100.c
index 6d49d7fb5f14..8f47c392cbd1 100644
--- a/drivers/mfd/ac100.c
+++ b/drivers/mfd/ac100.c
@@ -72,7 +72,7 @@ static const struct regmap_config ac100_regmap_config = {
.wr_table = &ac100_writeable_table,
.volatile_table = &ac100_volatile_table,
.max_register = AC100_RTC_GP(15),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static struct mfd_cell ac100_cells[] = {
diff --git a/drivers/mfd/acer-ec-a500.c b/drivers/mfd/acer-ec-a500.c
index feb757e90dc3..79405835ff8e 100644
--- a/drivers/mfd/acer-ec-a500.c
+++ b/drivers/mfd/acer-ec-a500.c
@@ -9,7 +9,7 @@
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
diff --git a/drivers/mfd/act8945a.c b/drivers/mfd/act8945a.c
index 2406fcdff5f9..cafefb4451cb 100644
--- a/drivers/mfd/act8945a.c
+++ b/drivers/mfd/act8945a.c
@@ -10,7 +10,7 @@
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/regmap.h>
static const struct mfd_cell act8945a_devs[] = {
@@ -54,7 +54,7 @@ static int act8945a_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id act8945a_i2c_id[] = {
- { "act8945a", 0 },
+ { "act8945a" },
{}
};
MODULE_DEVICE_TABLE(i2c, act8945a_i2c_id);
@@ -68,7 +68,7 @@ MODULE_DEVICE_TABLE(of, act8945a_of_match);
static struct i2c_driver act8945a_i2c_driver = {
.driver = {
.name = "act8945a",
- .of_match_table = of_match_ptr(act8945a_of_match),
+ .of_match_table = act8945a_of_match,
},
.probe = act8945a_i2c_probe,
.id_table = act8945a_i2c_id,
diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c
new file mode 100644
index 000000000000..46b3ce3d7bae
--- /dev/null
+++ b/drivers/mfd/adp5585.c
@@ -0,0 +1,843 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices ADP5585 I/O expander, PWM controller and keypad controller
+ *
+ * Copyright 2022 NXP
+ * Copyright 2024 Ideas on Board Oy
+ * Copyright 2025 Analog Devices Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mfd/adp5585.h>
+#include <linux/mfd/core.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+enum {
+ ADP5585_DEV_GPIO,
+ ADP5585_DEV_PWM,
+ ADP5585_DEV_INPUT,
+ ADP5585_DEV_MAX
+};
+
+static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
+ MFD_CELL_NAME("adp5585-gpio"),
+ MFD_CELL_NAME("adp5585-pwm"),
+ MFD_CELL_NAME("adp5585-keys"),
+};
+
+static const struct mfd_cell adp5589_devs[] = {
+ MFD_CELL_NAME("adp5589-gpio"),
+ MFD_CELL_NAME("adp5589-pwm"),
+ MFD_CELL_NAME("adp5589-keys"),
+};
+
+static const struct regmap_range adp5585_volatile_ranges[] = {
+ regmap_reg_range(ADP5585_ID, ADP5585_GPI_STATUS_B),
+};
+
+static const struct regmap_access_table adp5585_volatile_regs = {
+ .yes_ranges = adp5585_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(adp5585_volatile_ranges),
+};
+
+static const struct regmap_range adp5589_volatile_ranges[] = {
+ regmap_reg_range(ADP5585_ID, ADP5589_GPI_STATUS_C),
+};
+
+static const struct regmap_access_table adp5589_volatile_regs = {
+ .yes_ranges = adp5589_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(adp5589_volatile_ranges),
+};
+
+/*
+ * Chip variants differ in the default configuration of pull-up and pull-down
+ * resistors, and therefore have different default register values:
+ *
+ * - The -00, -01 and -03 variants (collectively referred to as
+ * ADP5585_REGMAP_00) have pull-up on all GPIO pins by default.
+ * - The -02 variant has no default pull-up or pull-down resistors.
+ * - The -04 variant has default pull-down resistors on all GPIO pins.
+ */
+
+static const u8 adp5585_regmap_defaults_00[ADP5585_MAX_REG + 1] = {
+ /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 adp5585_regmap_defaults_02[ADP5585_MAX_REG + 1] = {
+ /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3,
+ /* 0x18 */ 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 adp5585_regmap_defaults_04[ADP5585_MAX_REG + 1] = {
+ /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55,
+ /* 0x18 */ 0x05, 0x55, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 adp5589_regmap_defaults_00[ADP5589_MAX_REG + 1] = {
+ /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 adp5589_regmap_defaults_01[ADP5589_MAX_REG + 1] = {
+ /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+ /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+};
+
+static const u8 adp5589_regmap_defaults_02[ADP5589_MAX_REG + 1] = {
+ /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x18 */ 0x00, 0x41, 0x01, 0x00, 0x11, 0x04, 0x00, 0x00,
+ /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 *adp5585_regmap_defaults[ADP5585_MAX] = {
+ [ADP5585_00] = adp5585_regmap_defaults_00,
+ [ADP5585_01] = adp5585_regmap_defaults_00,
+ [ADP5585_02] = adp5585_regmap_defaults_02,
+ [ADP5585_03] = adp5585_regmap_defaults_00,
+ [ADP5585_04] = adp5585_regmap_defaults_04,
+ [ADP5589_00] = adp5589_regmap_defaults_00,
+ [ADP5589_01] = adp5589_regmap_defaults_01,
+ [ADP5589_02] = adp5589_regmap_defaults_02,
+};
+
+static const struct regmap_config adp5585_regmap_config_template = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ADP5585_MAX_REG,
+ .volatile_table = &adp5585_volatile_regs,
+ .cache_type = REGCACHE_MAPLE,
+ .num_reg_defaults_raw = ADP5585_MAX_REG + 1,
+};
+
+static const struct regmap_config adp5589_regmap_config_template = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ADP5589_MAX_REG,
+ .volatile_table = &adp5589_volatile_regs,
+ .cache_type = REGCACHE_MAPLE,
+ .num_reg_defaults_raw = ADP5589_MAX_REG + 1,
+};
+
+static const struct adp5585_regs adp5585_regs = {
+ .ext_cfg = ADP5585_PIN_CONFIG_C,
+ .int_en = ADP5585_INT_EN,
+ .gen_cfg = ADP5585_GENERAL_CFG,
+ .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
+ .reset_cfg = ADP5585_RESET_CFG,
+ .reset1_event_a = ADP5585_RESET1_EVENT_A,
+ .reset2_event_a = ADP5585_RESET2_EVENT_A,
+ .pin_cfg_a = ADP5585_PIN_CONFIG_A,
+};
+
+static const struct adp5585_regs adp5589_regs = {
+ .ext_cfg = ADP5589_PIN_CONFIG_D,
+ .int_en = ADP5589_INT_EN,
+ .gen_cfg = ADP5589_GENERAL_CFG,
+ .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
+ .reset_cfg = ADP5589_RESET_CFG,
+ .reset1_event_a = ADP5589_RESET1_EVENT_A,
+ .reset2_event_a = ADP5589_RESET2_EVENT_A,
+ .pin_cfg_a = ADP5589_PIN_CONFIG_A,
+};
+
+static int adp5585_validate_event(const struct adp5585_dev *adp5585, unsigned int ev)
+{
+ if (adp5585->has_pin6) {
+ if (ev >= ADP5585_ROW5_KEY_EVENT_START && ev <= ADP5585_ROW5_KEY_EVENT_END)
+ return 0;
+ if (ev >= ADP5585_GPI_EVENT_START && ev <= ADP5585_GPI_EVENT_END)
+ return 0;
+
+ return dev_err_probe(adp5585->dev, -EINVAL,
+ "Invalid unlock/reset event(%u) for this device\n", ev);
+ }
+
+ if (ev >= ADP5585_KEY_EVENT_START && ev <= ADP5585_KEY_EVENT_END)
+ return 0;
+ if (ev >= ADP5585_GPI_EVENT_START && ev <= ADP5585_GPI_EVENT_END) {
+ /*
+ * Some variants of the adp5585 do not have the Row 5
+ * (meaning pin 6 or GPIO 6) available. Instead that pin serves
+ * as a reset pin. So, we need to make sure no event is
+ * configured for it.
+ */
+ if (ev == (ADP5585_GPI_EVENT_START + 5))
+ return dev_err_probe(adp5585->dev, -EINVAL,
+ "Invalid unlock/reset event(%u). R5 not available\n",
+ ev);
+ return 0;
+ }
+
+ return dev_err_probe(adp5585->dev, -EINVAL,
+ "Invalid unlock/reset event(%u) for this device\n", ev);
+}
+
+static int adp5589_validate_event(const struct adp5585_dev *adp5585, unsigned int ev)
+{
+ if (ev >= ADP5589_KEY_EVENT_START && ev <= ADP5589_KEY_EVENT_END)
+ return 0;
+ if (ev >= ADP5589_GPI_EVENT_START && ev <= ADP5589_GPI_EVENT_END)
+ return 0;
+
+ return dev_err_probe(adp5585->dev, -EINVAL,
+ "Invalid unlock/reset event(%u) for this device\n", ev);
+}
+
+static struct regmap_config *adp5585_fill_variant_config(struct adp5585_dev *adp5585)
+{
+ struct regmap_config *regmap_config;
+
+ switch (adp5585->variant) {
+ case ADP5585_00:
+ case ADP5585_01:
+ case ADP5585_02:
+ case ADP5585_03:
+ case ADP5585_04:
+ adp5585->id = ADP5585_MAN_ID_VALUE;
+ adp5585->regs = &adp5585_regs;
+ adp5585->n_pins = ADP5585_PIN_MAX;
+ adp5585->reset2_out = ADP5585_RESET2_OUT;
+ if (adp5585->variant == ADP5585_01)
+ adp5585->has_pin6 = true;
+ regmap_config = devm_kmemdup(adp5585->dev, &adp5585_regmap_config_template,
+ sizeof(*regmap_config), GFP_KERNEL);
+ break;
+ case ADP5589_00:
+ case ADP5589_01:
+ case ADP5589_02:
+ adp5585->id = ADP5589_MAN_ID_VALUE;
+ adp5585->regs = &adp5589_regs;
+ adp5585->has_unlock = true;
+ adp5585->has_pin6 = true;
+ adp5585->n_pins = ADP5589_PIN_MAX;
+ adp5585->reset2_out = ADP5589_RESET2_OUT;
+ regmap_config = devm_kmemdup(adp5585->dev, &adp5589_regmap_config_template,
+ sizeof(*regmap_config), GFP_KERNEL);
+ break;
+ default:
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (!regmap_config)
+ return ERR_PTR(-ENOMEM);
+
+ regmap_config->reg_defaults_raw = adp5585_regmap_defaults[adp5585->variant];
+
+ return regmap_config;
+}
+
+static int adp5585_parse_ev_array(const struct adp5585_dev *adp5585, const char *prop, u32 *events,
+ u32 *n_events, u32 max_evs, bool reset_ev)
+{
+ struct device *dev = adp5585->dev;
+ unsigned int ev;
+ int ret;
+
+ /*
+ * The device has the capability of handling special events through GPIs or a Keypad:
+ * unlock events: Unlock the keymap until one of the configured events is detected.
+ * reset events: Generate a reset pulse when one of the configured events is detected.
+ */
+ ret = device_property_count_u32(dev, prop);
+ if (ret < 0)
+ return 0;
+
+ *n_events = ret;
+
+ if (!adp5585->has_unlock && !reset_ev)
+ return dev_err_probe(dev, -EOPNOTSUPP, "Unlock keys not supported\n");
+
+ if (*n_events > max_evs)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid number of keys(%u > %u) for %s\n",
+ *n_events, max_evs, prop);
+
+ ret = device_property_read_u32_array(dev, prop, events, *n_events);
+ if (ret)
+ return ret;
+
+ for (ev = 0; ev < *n_events; ev++) {
+ if (!reset_ev && events[ev] == ADP5589_UNLOCK_WILDCARD)
+ continue;
+
+ if (adp5585->id == ADP5585_MAN_ID_VALUE)
+ ret = adp5585_validate_event(adp5585, events[ev]);
+ else
+ ret = adp5589_validate_event(adp5585, events[ev]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adp5585_unlock_ev_parse(struct adp5585_dev *adp5585)
+{
+ struct device *dev = adp5585->dev;
+ int ret;
+
+ ret = adp5585_parse_ev_array(adp5585, "adi,unlock-events", adp5585->unlock_keys,
+ &adp5585->nkeys_unlock, ARRAY_SIZE(adp5585->unlock_keys),
+ false);
+ if (ret)
+ return ret;
+ if (!adp5585->nkeys_unlock)
+ return 0;
+
+ ret = device_property_read_u32(dev, "adi,unlock-trigger-sec", &adp5585->unlock_time);
+ if (!ret) {
+ if (adp5585->unlock_time > ADP5585_MAX_UNLOCK_TIME_SEC)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid unlock time(%u > %d)\n",
+ adp5585->unlock_time,
+ ADP5585_MAX_UNLOCK_TIME_SEC);
+ }
+
+ return 0;
+}
+
+static int adp5585_reset_ev_parse(struct adp5585_dev *adp5585)
+{
+ struct device *dev = adp5585->dev;
+ u32 prop_val;
+ int ret;
+
+ ret = adp5585_parse_ev_array(adp5585, "adi,reset1-events", adp5585->reset1_keys,
+ &adp5585->nkeys_reset1,
+ ARRAY_SIZE(adp5585->reset1_keys), true);
+ if (ret)
+ return ret;
+
+ ret = adp5585_parse_ev_array(adp5585, "adi,reset2-events",
+ adp5585->reset2_keys,
+ &adp5585->nkeys_reset2,
+ ARRAY_SIZE(adp5585->reset2_keys), true);
+ if (ret)
+ return ret;
+
+ if (!adp5585->nkeys_reset1 && !adp5585->nkeys_reset2)
+ return 0;
+
+ if (adp5585->nkeys_reset1 && device_property_read_bool(dev, "adi,reset1-active-high"))
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET1_POL, 1);
+
+ if (adp5585->nkeys_reset2 && device_property_read_bool(dev, "adi,reset2-active-high"))
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET2_POL, 1);
+
+ if (device_property_read_bool(dev, "adi,rst-passthrough-enable"))
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RST_PASSTHRU_EN, 1);
+
+ ret = device_property_read_u32(dev, "adi,reset-trigger-ms", &prop_val);
+ if (!ret) {
+ switch (prop_val) {
+ case 0:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 0);
+ break;
+ case 1000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 1);
+ break;
+ case 1500:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 2);
+ break;
+ case 2000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 3);
+ break;
+ case 2500:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 4);
+ break;
+ case 3000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 5);
+ break;
+ case 3500:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 6);
+ break;
+ case 4000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 7);
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid value(%u) for adi,reset-trigger-ms\n",
+ prop_val);
+ }
+ }
+
+ ret = device_property_read_u32(dev, "adi,reset-pulse-width-us", &prop_val);
+ if (!ret) {
+ switch (prop_val) {
+ case 500:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 0);
+ break;
+ case 1000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 1);
+ break;
+ case 2000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 2);
+ break;
+ case 10000:
+ adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 3);
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid value(%u) for adi,reset-pulse-width-us\n",
+ prop_val);
+ }
+ }
+
+ return 0;
+}
+
+static int adp5585_add_devices(const struct adp5585_dev *adp5585)
+{
+ struct device *dev = adp5585->dev;
+ const struct mfd_cell *cells;
+ int ret;
+
+ if (adp5585->id == ADP5585_MAN_ID_VALUE)
+ cells = adp5585_devs;
+ else
+ cells = adp5589_devs;
+
+ if (device_property_present(dev, "#pwm-cells")) {
+ /* Make sure the PWM output pin is not used by the GPIO or INPUT devices */
+ __set_bit(ADP5585_PWM_OUT, adp5585->pin_usage);
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
+ &cells[ADP5585_DEV_PWM], 1, NULL, 0, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add PWM device\n");
+ }
+
+ if (device_property_present(dev, "#gpio-cells")) {
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
+ &cells[ADP5585_DEV_GPIO], 1, NULL, 0, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add GPIO device\n");
+ }
+
+ if (device_property_present(adp5585->dev, "adi,keypad-pins")) {
+ ret = devm_mfd_add_devices(adp5585->dev, PLATFORM_DEVID_AUTO,
+ &cells[ADP5585_DEV_INPUT], 1, NULL, 0, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add input device\n");
+ }
+
+ return 0;
+}
+
+static void adp5585_osc_disable(void *data)
+{
+ const struct adp5585_dev *adp5585 = data;
+
+ regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
+}
+
+static void adp5585_report_events(struct adp5585_dev *adp5585, int ev_cnt)
+{
+ unsigned int i;
+
+ for (i = 0; i < ev_cnt; i++) {
+ unsigned long key_val, key_press;
+ unsigned int key;
+ int ret;
+
+ ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, &key);
+ if (ret)
+ return;
+
+ key_val = FIELD_GET(ADP5585_KEY_EVENT_MASK, key);
+ key_press = FIELD_GET(ADP5585_KEV_EV_PRESS_MASK, key);
+
+ blocking_notifier_call_chain(&adp5585->event_notifier, key_val, (void *)key_press);
+ }
+}
+
+static irqreturn_t adp5585_irq(int irq, void *data)
+{
+ struct adp5585_dev *adp5585 = data;
+ unsigned int status, ev_cnt;
+ int ret;
+
+ ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &status);
+ if (ret)
+ return IRQ_HANDLED;
+
+ if (status & ADP5585_OVRFLOW_INT)
+ dev_err_ratelimited(adp5585->dev, "Event overflow error\n");
+
+ if (!(status & ADP5585_EVENT_INT))
+ goto out_irq;
+
+ ret = regmap_read(adp5585->regmap, ADP5585_STATUS, &ev_cnt);
+ if (ret)
+ goto out_irq;
+
+ ev_cnt = FIELD_GET(ADP5585_EC_MASK, ev_cnt);
+ if (!ev_cnt)
+ goto out_irq;
+
+ adp5585_report_events(adp5585, ev_cnt);
+out_irq:
+ regmap_write(adp5585->regmap, ADP5585_INT_STATUS, status);
+ return IRQ_HANDLED;
+}
+
+static int adp5585_setup(struct adp5585_dev *adp5585)
+{
+ const struct adp5585_regs *regs = adp5585->regs;
+ unsigned int reg_val = 0, i;
+ int ret;
+
+ /* If pin_6 (ROW5/GPI6) is not available, make sure to mark it as "busy" */
+ if (!adp5585->has_pin6)
+ __set_bit(ADP5585_ROW5, adp5585->pin_usage);
+
+ /* Configure the device with reset and unlock events */
+ for (i = 0; i < adp5585->nkeys_unlock; i++) {
+ ret = regmap_write(adp5585->regmap, ADP5589_UNLOCK1 + i,
+ adp5585->unlock_keys[i] | ADP5589_UNLOCK_EV_PRESS);
+ if (ret)
+ return ret;
+ }
+
+ if (adp5585->nkeys_unlock) {
+ ret = regmap_update_bits(adp5585->regmap, ADP5589_UNLOCK_TIMERS,
+ ADP5589_UNLOCK_TIMER, adp5585->unlock_time);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(adp5585->regmap, ADP5589_LOCK_CFG, ADP5589_LOCK_EN);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < adp5585->nkeys_reset1; i++) {
+ ret = regmap_write(adp5585->regmap, regs->reset1_event_a + i,
+ adp5585->reset1_keys[i] | ADP5585_RESET_EV_PRESS);
+ if (ret)
+ return ret;
+
+ /* Mark that pin as not usable for the INPUT and GPIO devices. */
+ __set_bit(ADP5585_RESET1_OUT, adp5585->pin_usage);
+ }
+
+ for (i = 0; i < adp5585->nkeys_reset2; i++) {
+ ret = regmap_write(adp5585->regmap, regs->reset2_event_a + i,
+ adp5585->reset2_keys[i] | ADP5585_RESET_EV_PRESS);
+ if (ret)
+ return ret;
+
+ __set_bit(adp5585->reset2_out, adp5585->pin_usage);
+ }
+
+ if (adp5585->nkeys_reset1 || adp5585->nkeys_reset2) {
+ ret = regmap_write(adp5585->regmap, regs->reset_cfg, adp5585->reset_cfg);
+ if (ret)
+ return ret;
+
+ /* If there's a reset1 event, then R4 is used as an output for the reset signal */
+ if (adp5585->nkeys_reset1)
+ reg_val = ADP5585_R4_EXTEND_CFG_RESET1;
+ /* If there's a reset2 event, then C4 is used as an output for the reset signal */
+ if (adp5585->nkeys_reset2)
+ reg_val |= ADP5585_C4_EXTEND_CFG_RESET2;
+
+ ret = regmap_update_bits(adp5585->regmap, regs->ext_cfg,
+ ADP5585_C4_EXTEND_CFG_MASK | ADP5585_R4_EXTEND_CFG_MASK,
+ reg_val);
+ if (ret)
+ return ret;
+ }
+
+ /* Clear any possible event by reading all the FIFO entries */
+ for (i = 0; i < ADP5585_EV_MAX; i++) {
+ ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, &reg_val);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg, adp5585->ev_poll_time);
+ if (ret)
+ return ret;
+
+ /*
+ * Enable the internal oscillator, as it's shared between multiple
+ * functions.
+ */
+ ret = regmap_write(adp5585->regmap, regs->gen_cfg,
+ ADP5585_OSC_FREQ_500KHZ | ADP5585_INT_CFG | ADP5585_OSC_EN);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(adp5585->dev, adp5585_osc_disable, adp5585);
+}
+
+static int adp5585_parse_fw(struct adp5585_dev *adp5585)
+{
+ unsigned int prop_val;
+ int ret;
+
+ ret = device_property_read_u32(adp5585->dev, "poll-interval", &prop_val);
+ if (!ret) {
+ adp5585->ev_poll_time = prop_val / 10 - 1;
+ /*
+ * ev_poll_time is the raw value to be written on the register and 0 to 3 are the
+ * valid values.
+ */
+ if (adp5585->ev_poll_time > 3)
+ return dev_err_probe(adp5585->dev, -EINVAL,
+ "Invalid value(%u) for poll-interval\n", prop_val);
+ }
+
+ ret = adp5585_unlock_ev_parse(adp5585);
+ if (ret)
+ return ret;
+
+ return adp5585_reset_ev_parse(adp5585);
+}
+
+static void adp5585_irq_disable(void *data)
+{
+ struct adp5585_dev *adp5585 = data;
+
+ regmap_write(adp5585->regmap, adp5585->regs->int_en, 0);
+}
+
+static int adp5585_irq_enable(struct i2c_client *i2c,
+ struct adp5585_dev *adp5585)
+{
+ const struct adp5585_regs *regs = adp5585->regs;
+ unsigned int stat;
+ int ret;
+
+ if (i2c->irq <= 0)
+ return 0;
+
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, adp5585_irq,
+ IRQF_ONESHOT, i2c->name, adp5585);
+ if (ret)
+ return ret;
+
+ /*
+ * Clear any possible outstanding interrupt before enabling them. We do that by reading
+ * the status register and writing back the same value.
+ */
+ ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &stat);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(adp5585->regmap, ADP5585_INT_STATUS, stat);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(adp5585->regmap, regs->int_en, ADP5585_OVRFLOW_IEN | ADP5585_EVENT_IEN);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(&i2c->dev, adp5585_irq_disable, adp5585);
+}
+
+static int adp5585_i2c_probe(struct i2c_client *i2c)
+{
+ struct regmap_config *regmap_config;
+ struct adp5585_dev *adp5585;
+ struct gpio_desc *gpio;
+ unsigned int id;
+ int ret;
+
+ adp5585 = devm_kzalloc(&i2c->dev, sizeof(*adp5585), GFP_KERNEL);
+ if (!adp5585)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, adp5585);
+ adp5585->dev = &i2c->dev;
+ adp5585->irq = i2c->irq;
+ BLOCKING_INIT_NOTIFIER_HEAD(&adp5585->event_notifier);
+
+ adp5585->variant = (enum adp5585_variant)(uintptr_t)i2c_get_match_data(i2c);
+ if (!adp5585->variant)
+ return -ENODEV;
+
+ regmap_config = adp5585_fill_variant_config(adp5585);
+ if (IS_ERR(regmap_config))
+ return PTR_ERR(regmap_config);
+
+ ret = devm_regulator_get_enable(&i2c->dev, "vdd");
+ if (ret)
+ return ret;
+
+ gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+
+ /*
+ * Note the timings are not documented anywhere in the datasheet. They are just
+ * reasonable values that work.
+ */
+ if (gpio) {
+ fsleep(30);
+ gpiod_set_value_cansleep(gpio, 0);
+ fsleep(60);
+ }
+
+ adp5585->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+ if (IS_ERR(adp5585->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
+ "Failed to initialize register map\n");
+
+ ret = regmap_read(adp5585->regmap, ADP5585_ID, &id);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to read device ID\n");
+
+ id &= ADP5585_MAN_ID_MASK;
+ if (id != adp5585->id)
+ return dev_err_probe(&i2c->dev, -ENODEV,
+ "Invalid device ID 0x%02x\n", id);
+
+ adp5585->pin_usage = devm_bitmap_zalloc(&i2c->dev, adp5585->n_pins, GFP_KERNEL);
+ if (!adp5585->pin_usage)
+ return -ENOMEM;
+
+ ret = adp5585_parse_fw(adp5585);
+ if (ret)
+ return ret;
+
+ ret = adp5585_setup(adp5585);
+ if (ret)
+ return ret;
+
+ ret = adp5585_add_devices(adp5585);
+ if (ret)
+ return ret;
+
+ return adp5585_irq_enable(i2c, adp5585);
+}
+
+static int adp5585_suspend(struct device *dev)
+{
+ struct adp5585_dev *adp5585 = dev_get_drvdata(dev);
+
+ if (adp5585->irq)
+ disable_irq(adp5585->irq);
+
+ regcache_cache_only(adp5585->regmap, true);
+
+ return 0;
+}
+
+static int adp5585_resume(struct device *dev)
+{
+ struct adp5585_dev *adp5585 = dev_get_drvdata(dev);
+ int ret;
+
+ regcache_cache_only(adp5585->regmap, false);
+ regcache_mark_dirty(adp5585->regmap);
+
+ ret = regcache_sync(adp5585->regmap);
+ if (ret)
+ return ret;
+
+ if (adp5585->irq)
+ enable_irq(adp5585->irq);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
+
+static const struct of_device_id adp5585_of_match[] = {
+ {
+ .compatible = "adi,adp5585-00",
+ .data = (void *)ADP5585_00,
+ }, {
+ .compatible = "adi,adp5585-01",
+ .data = (void *)ADP5585_01,
+ }, {
+ .compatible = "adi,adp5585-02",
+ .data = (void *)ADP5585_02,
+ }, {
+ .compatible = "adi,adp5585-03",
+ .data = (void *)ADP5585_03,
+ }, {
+ .compatible = "adi,adp5585-04",
+ .data = (void *)ADP5585_04,
+ }, {
+ .compatible = "adi,adp5589-00",
+ .data = (void *)ADP5589_00,
+ }, {
+ .compatible = "adi,adp5589-01",
+ .data = (void *)ADP5589_01,
+ }, {
+ .compatible = "adi,adp5589-02",
+ .data = (void *)ADP5589_02,
+ }, {
+ .compatible = "adi,adp5589",
+ .data = (void *)ADP5589_00,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, adp5585_of_match);
+
+static struct i2c_driver adp5585_i2c_driver = {
+ .driver = {
+ .name = "adp5585",
+ .of_match_table = adp5585_of_match,
+ .pm = pm_sleep_ptr(&adp5585_pm),
+ },
+ .probe = adp5585_i2c_probe,
+};
+module_i2c_driver(adp5585_i2c_driver);
+
+MODULE_DESCRIPTION("ADP5585 core driver");
+MODULE_AUTHOR("Haibo Chen <haibo.chen@nxp.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/altera-a10sr.c b/drivers/mfd/altera-a10sr.c
index 34ef526f4aee..d53e433ab5c1 100644
--- a/drivers/mfd/altera-a10sr.c
+++ b/drivers/mfd/altera-a10sr.c
@@ -163,7 +163,7 @@ static struct spi_driver altr_a10sr_spi_driver = {
.probe = altr_a10sr_spi_probe,
.driver = {
.name = "altr_a10sr",
- .of_match_table = of_match_ptr(altr_a10sr_spi_of_match),
+ .of_match_table = altr_a10sr_spi_of_match,
},
.id_table = altr_a10sr_spi_ids,
};
diff --git a/drivers/mfd/altera-sysmgr.c b/drivers/mfd/altera-sysmgr.c
index af205813b281..90c6902d537d 100644
--- a/drivers/mfd/altera-sysmgr.c
+++ b/drivers/mfd/altera-sysmgr.c
@@ -14,8 +14,7 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -110,12 +109,16 @@ struct regmap *altr_sysmgr_regmap_lookup_by_phandle(struct device_node *np,
dev = driver_find_device_by_of_node(&altr_sysmgr_driver.driver,
(void *)sysmgr_np);
- of_node_put(sysmgr_np);
+ if (property)
+ of_node_put(sysmgr_np);
+
if (!dev)
return ERR_PTR(-EPROBE_DEFER);
sysmgr = dev_get_drvdata(dev);
+ put_device(dev);
+
return sysmgr->regmap;
}
EXPORT_SYMBOL_GPL(altr_sysmgr_regmap_lookup_by_phandle);
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index c166fcd331f1..85ff8717d850 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -15,7 +15,6 @@
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -1430,4 +1429,5 @@ int arizona_dev_exit(struct arizona *arizona)
}
EXPORT_SYMBOL_GPL(arizona_dev_exit);
+MODULE_DESCRIPTION("Wolfson Arizona core driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c
index 9b7183ffc928..10e76fc8f12e 100644
--- a/drivers/mfd/arizona-i2c.c
+++ b/drivers/mfd/arizona-i2c.c
@@ -22,19 +22,12 @@
static int arizona_i2c_probe(struct i2c_client *i2c)
{
- const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
- const void *match_data;
struct arizona *arizona;
const struct regmap_config *regmap_config = NULL;
- unsigned long type = 0;
+ unsigned long type;
int ret;
- match_data = device_get_match_data(&i2c->dev);
- if (match_data)
- type = (unsigned long)match_data;
- else if (id)
- type = id->driver_data;
-
+ type = (uintptr_t)i2c_get_match_data(i2c);
switch (type) {
case WM5102:
if (IS_ENABLED(CONFIG_MFD_WM5102))
diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c
index d919ae9691e2..544016d420fe 100644
--- a/drivers/mfd/arizona-irq.c
+++ b/drivers/mfd/arizona-irq.c
@@ -136,7 +136,7 @@ static irqreturn_t arizona_irq_thread(int irq, void *data)
dev_err(arizona->dev,
"Failed to read main IRQ status: %d\n", ret);
}
-
+#ifdef CONFIG_GPIOLIB_LEGACY
/*
* Poll the IRQ pin status to see if we're really done
* if the interrupt controller can't do it for us.
@@ -150,9 +150,9 @@ static irqreturn_t arizona_irq_thread(int irq, void *data)
!gpio_get_value_cansleep(arizona->pdata.irq_gpio)) {
poll = true;
}
+#endif
} while (poll);
- pm_runtime_mark_last_busy(arizona->dev);
pm_runtime_put_autosuspend(arizona->dev);
return IRQ_HANDLED;
@@ -312,8 +312,7 @@ int arizona_irq_init(struct arizona *arizona)
flags |= arizona->pdata.irq_flags;
/* Allocate a virtual IRQ domain to distribute to the regmap domains */
- arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops,
- arizona);
+ arizona->virq = irq_domain_create_linear(NULL, 2, &arizona_domain_ops, arizona);
if (!arizona->virq) {
dev_err(arizona->dev, "Failed to add core IRQ domain\n");
ret = -EINVAL;
@@ -351,6 +350,7 @@ int arizona_irq_init(struct arizona *arizona)
goto err_map_main_irq;
}
+#ifdef CONFIG_GPIOLIB_LEGACY
/* Used to emulate edge trigger and to work around broken pinmux */
if (arizona->pdata.irq_gpio) {
if (gpio_to_irq(arizona->pdata.irq_gpio) != arizona->irq) {
@@ -370,6 +370,7 @@ int arizona_irq_init(struct arizona *arizona)
arizona->pdata.irq_gpio = 0;
}
}
+#endif
ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread,
flags, "arizona", arizona);
diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c
index 02cf4f3e91d7..eaa2b2bc5dd0 100644
--- a/drivers/mfd/arizona-spi.c
+++ b/drivers/mfd/arizona-spi.c
@@ -159,6 +159,9 @@ static int arizona_spi_acpi_probe(struct arizona *arizona)
arizona->pdata.micd_ranges = arizona_micd_aosp_ranges;
arizona->pdata.num_micd_ranges = ARRAY_SIZE(arizona_micd_aosp_ranges);
+ /* Use left headphone speaker for HP vs line-out detection */
+ arizona->pdata.hpdet_channel = ARIZONA_ACCDET_MODE_HPL;
+
return 0;
}
@@ -187,19 +190,12 @@ static int arizona_spi_acpi_probe(struct arizona *arizona)
static int arizona_spi_probe(struct spi_device *spi)
{
- const struct spi_device_id *id = spi_get_device_id(spi);
- const void *match_data;
struct arizona *arizona;
const struct regmap_config *regmap_config = NULL;
unsigned long type = 0;
int ret;
- match_data = device_get_match_data(&spi->dev);
- if (match_data)
- type = (unsigned long)match_data;
- else if (id)
- type = id->driver_data;
-
+ type = (unsigned long)spi_get_device_match_data(spi);
switch (type) {
case WM5102:
if (IS_ENABLED(CONFIG_MFD_WM5102))
diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
index c7e85ff38013..9741977031df 100644
--- a/drivers/mfd/as3711.c
+++ b/drivers/mfd/as3711.c
@@ -106,7 +106,7 @@ static const struct regmap_config as3711_regmap_config = {
.precious_reg = as3711_precious_reg,
.max_register = AS3711_MAX_REG,
.num_reg_defaults_raw = AS3711_NUM_REGS,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
#ifdef CONFIG_OF
diff --git a/drivers/mfd/as3722.c b/drivers/mfd/as3722.c
index a2bf68afc131..7ab6fcc9c27c 100644
--- a/drivers/mfd/as3722.c
+++ b/drivers/mfd/as3722.c
@@ -299,7 +299,7 @@ static const struct regmap_config as3722_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = AS3722_MAX_REGISTER,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.rd_table = &as3722_readable_table,
.wr_table = &as3722_writable_table,
.volatile_table = &as3722_volatile_table,
@@ -394,7 +394,9 @@ static int as3722_i2c_probe(struct i2c_client *i2c)
return ret;
}
- device_init_wakeup(as3722->dev, true);
+ ret = devm_device_init_wakeup(as3722->dev);
+ if (ret)
+ return dev_err_probe(as3722->dev, ret, "Failed to init wakeup\n");
dev_dbg(as3722->dev, "AS3722 core driver initialized successfully\n");
return 0;
@@ -430,8 +432,8 @@ static const struct of_device_id as3722_of_match[] = {
MODULE_DEVICE_TABLE(of, as3722_of_match);
static const struct i2c_device_id as3722_i2c_id[] = {
- { "as3722", 0 },
- {},
+ { "as3722" },
+ {}
};
MODULE_DEVICE_TABLE(i2c, as3722_i2c_id);
diff --git a/drivers/mfd/atc260x-core.c b/drivers/mfd/atc260x-core.c
index 7c5de3ae776e..6b6d5f1b9d76 100644
--- a/drivers/mfd/atc260x-core.c
+++ b/drivers/mfd/atc260x-core.c
@@ -11,7 +11,6 @@
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/regmap.h>
#define ATC260X_CHIP_REV_MAX 31
@@ -236,8 +235,8 @@ int atc260x_match_device(struct atc260x *atc260x, struct regmap_config *regmap_c
mutex_init(atc260x->regmap_mutex);
- regmap_cfg->lock = regmap_lock_mutex,
- regmap_cfg->unlock = regmap_unlock_mutex,
+ regmap_cfg->lock = regmap_lock_mutex;
+ regmap_cfg->unlock = regmap_unlock_mutex;
regmap_cfg->lock_arg = atc260x->regmap_mutex;
return 0;
diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c
index b52f7ffdad35..d5df5466eaf5 100644
--- a/drivers/mfd/atmel-flexcom.c
+++ b/drivers/mfd/atmel-flexcom.c
@@ -95,7 +95,7 @@ static int __maybe_unused atmel_flexcom_resume_noirq(struct device *dev)
if (err)
return err;
- val = FLEX_MR_OPMODE(ddata->opmode),
+ val = FLEX_MR_OPMODE(ddata->opmode);
writel(val, ddata->base + FLEX_MR);
clk_disable_unprepare(ddata->clk);
diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c
index 3c2414ba4b01..4c4e35d404f3 100644
--- a/drivers/mfd/atmel-hlcdc.c
+++ b/drivers/mfd/atmel-hlcdc.c
@@ -83,7 +83,6 @@ static int atmel_hlcdc_probe(struct platform_device *pdev)
struct atmel_hlcdc_regmap *hregmap;
struct device *dev = &pdev->dev;
struct atmel_hlcdc *hlcdc;
- struct resource *res;
hregmap = devm_kzalloc(dev, sizeof(*hregmap), GFP_KERNEL);
if (!hregmap)
@@ -93,8 +92,7 @@ static int atmel_hlcdc_probe(struct platform_device *pdev)
if (!hlcdc)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hregmap->regs = devm_ioremap_resource(dev, res);
+ hregmap->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hregmap->regs))
return PTR_ERR(hregmap->regs);
@@ -141,6 +139,7 @@ static const struct of_device_id atmel_hlcdc_match[] = {
{ .compatible = "atmel,sama5d3-hlcdc" },
{ .compatible = "atmel,sama5d4-hlcdc" },
{ .compatible = "microchip,sam9x60-hlcdc" },
+ { .compatible = "microchip,sam9x75-xlcdc" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, atmel_hlcdc_match);
diff --git a/drivers/mfd/atmel-smc.c b/drivers/mfd/atmel-smc.c
index e560066e5885..0a5b42c83f17 100644
--- a/drivers/mfd/atmel-smc.c
+++ b/drivers/mfd/atmel-smc.c
@@ -8,9 +8,16 @@
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
*/
-#include <linux/mfd/syscon/atmel-smc.h>
+#include <linux/bits.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
#include <linux/string.h>
+#include <linux/mfd/syscon/atmel-smc.h>
+
/**
* atmel_smc_cs_conf_init - initialize a SMC CS conf
* @conf: the SMC CS conf to initialize
@@ -255,8 +262,8 @@ EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_apply);
/**
* atmel_hsmc_cs_conf_apply - apply an SMC CS conf
* @regmap: the HSMC regmap
- * @cs: the CS id
* @layout: the layout of registers
+ * @cs: the CS id
* @conf: the SMC CS conf to apply
*
* Applies an SMC CS configuration.
@@ -296,8 +303,8 @@ EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_get);
/**
* atmel_hsmc_cs_conf_get - retrieve the current SMC CS conf
* @regmap: the HSMC regmap
- * @cs: the CS id
* @layout: the layout of registers
+ * @cs: the CS id
* @conf: the SMC CS conf object to store the current conf
*
* Retrieve the SMC CS configuration.
diff --git a/drivers/mfd/axp20x-i2c.c b/drivers/mfd/axp20x-i2c.c
index 68d3560cfe4a..5c93136f977e 100644
--- a/drivers/mfd/axp20x-i2c.c
+++ b/drivers/mfd/axp20x-i2c.c
@@ -65,6 +65,8 @@ static const struct of_device_id axp20x_i2c_of_match[] = {
{ .compatible = "x-powers,axp221", .data = (void *)AXP221_ID },
{ .compatible = "x-powers,axp223", .data = (void *)AXP223_ID },
{ .compatible = "x-powers,axp313a", .data = (void *)AXP313A_ID },
+ { .compatible = "x-powers,axp323", .data = (void *)AXP323_ID },
+ { .compatible = "x-powers,axp717", .data = (void *)AXP717_ID },
{ .compatible = "x-powers,axp803", .data = (void *)AXP803_ID },
{ .compatible = "x-powers,axp806", .data = (void *)AXP806_ID },
{ .compatible = "x-powers,axp15060", .data = (void *)AXP15060_ID },
@@ -74,17 +76,18 @@ MODULE_DEVICE_TABLE(of, axp20x_i2c_of_match);
#endif
static const struct i2c_device_id axp20x_i2c_id[] = {
- { "axp152", 0 },
- { "axp192", 0 },
- { "axp202", 0 },
- { "axp209", 0 },
- { "axp221", 0 },
- { "axp223", 0 },
- { "axp313a", 0 },
- { "axp803", 0 },
- { "axp806", 0 },
- { "axp15060", 0 },
- { },
+ { "axp152" },
+ { "axp192" },
+ { "axp202" },
+ { "axp209" },
+ { "axp221" },
+ { "axp223" },
+ { "axp313a" },
+ { "axp717" },
+ { "axp803" },
+ { "axp806" },
+ { "axp15060" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id);
diff --git a/drivers/mfd/axp20x-rsb.c b/drivers/mfd/axp20x-rsb.c
index 214bc0d84d44..059656f2a1bd 100644
--- a/drivers/mfd/axp20x-rsb.c
+++ b/drivers/mfd/axp20x-rsb.c
@@ -58,6 +58,7 @@ static void axp20x_rsb_remove(struct sunxi_rsb_device *rdev)
static const struct of_device_id axp20x_rsb_of_match[] = {
{ .compatible = "x-powers,axp223", .data = (void *)AXP223_ID },
+ { .compatible = "x-powers,axp717", .data = (void *)AXP717_ID },
{ .compatible = "x-powers,axp803", .data = (void *)AXP803_ID },
{ .compatible = "x-powers,axp806", .data = (void *)AXP806_ID },
{ .compatible = "x-powers,axp809", .data = (void *)AXP809_ID },
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index c03bc5cda080..c5f0ebae327f 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -22,7 +22,8 @@
#include <linux/mfd/axp20x.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/property.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -33,19 +34,21 @@
#define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4)
static const char * const axp20x_model_names[] = {
- "AXP152",
- "AXP192",
- "AXP202",
- "AXP209",
- "AXP221",
- "AXP223",
- "AXP288",
- "AXP313a",
- "AXP803",
- "AXP806",
- "AXP809",
- "AXP813",
- "AXP15060",
+ [AXP152_ID] = "AXP152",
+ [AXP192_ID] = "AXP192",
+ [AXP202_ID] = "AXP202",
+ [AXP209_ID] = "AXP209",
+ [AXP221_ID] = "AXP221",
+ [AXP223_ID] = "AXP223",
+ [AXP288_ID] = "AXP288",
+ [AXP313A_ID] = "AXP313a",
+ [AXP323_ID] = "AXP323",
+ [AXP717_ID] = "AXP717",
+ [AXP803_ID] = "AXP803",
+ [AXP806_ID] = "AXP806",
+ [AXP809_ID] = "AXP809",
+ [AXP813_ID] = "AXP813",
+ [AXP15060_ID] = "AXP15060",
};
static const struct regmap_range axp152_writeable_ranges[] = {
@@ -191,6 +194,10 @@ static const struct regmap_range axp313a_writeable_ranges[] = {
regmap_reg_range(AXP313A_ON_INDICATE, AXP313A_IRQ_STATE),
};
+static const struct regmap_range axp323_writeable_ranges[] = {
+ regmap_reg_range(AXP313A_ON_INDICATE, AXP323_DCDC_MODE_CTRL2),
+};
+
static const struct regmap_range axp313a_volatile_ranges[] = {
regmap_reg_range(AXP313A_SHUTDOWN_CTRL, AXP313A_SHUTDOWN_CTRL),
regmap_reg_range(AXP313A_IRQ_STATE, AXP313A_IRQ_STATE),
@@ -201,11 +208,47 @@ static const struct regmap_access_table axp313a_writeable_table = {
.n_yes_ranges = ARRAY_SIZE(axp313a_writeable_ranges),
};
+static const struct regmap_access_table axp323_writeable_table = {
+ .yes_ranges = axp323_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp323_writeable_ranges),
+};
+
static const struct regmap_access_table axp313a_volatile_table = {
.yes_ranges = axp313a_volatile_ranges,
.n_yes_ranges = ARRAY_SIZE(axp313a_volatile_ranges),
};
+static const struct regmap_range axp717_writeable_ranges[] = {
+ regmap_reg_range(AXP717_PMU_FAULT, AXP717_MODULE_EN_CONTROL_1),
+ regmap_reg_range(AXP717_MIN_SYS_V_CONTROL, AXP717_BOOST_CONTROL),
+ regmap_reg_range(AXP717_VSYS_V_POWEROFF, AXP717_VSYS_V_POWEROFF),
+ regmap_reg_range(AXP717_IRQ0_EN, AXP717_IRQ4_EN),
+ regmap_reg_range(AXP717_IRQ0_STATE, AXP717_IRQ4_STATE),
+ regmap_reg_range(AXP717_TS_PIN_CFG, AXP717_TS_PIN_CFG),
+ regmap_reg_range(AXP717_ICC_CHG_SET, AXP717_CV_CHG_SET),
+ regmap_reg_range(AXP717_DCDC_OUTPUT_CONTROL, AXP717_CPUSLDO_CONTROL),
+ regmap_reg_range(AXP717_ADC_CH_EN_CONTROL, AXP717_ADC_CH_EN_CONTROL),
+ regmap_reg_range(AXP717_ADC_DATA_SEL, AXP717_ADC_DATA_SEL),
+};
+
+static const struct regmap_range axp717_volatile_ranges[] = {
+ regmap_reg_range(AXP717_ON_INDICATE, AXP717_PMU_FAULT),
+ regmap_reg_range(AXP717_IRQ0_STATE, AXP717_IRQ4_STATE),
+ regmap_reg_range(AXP717_BATT_PERCENT_DATA, AXP717_BATT_PERCENT_DATA),
+ regmap_reg_range(AXP717_BATT_V_H, AXP717_BATT_CHRG_I_L),
+ regmap_reg_range(AXP717_ADC_DATA_H, AXP717_ADC_DATA_L),
+};
+
+static const struct regmap_access_table axp717_writeable_table = {
+ .yes_ranges = axp717_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp717_writeable_ranges),
+};
+
+static const struct regmap_access_table axp717_volatile_table = {
+ .yes_ranges = axp717_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp717_volatile_ranges),
+};
+
static const struct regmap_range axp806_volatile_ranges[] = {
regmap_reg_range(AXP20X_IRQ1_STATE, AXP20X_IRQ2_STATE),
};
@@ -286,6 +329,12 @@ static const struct resource axp22x_usb_power_supply_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
};
+static const struct resource axp717_usb_power_supply_resources[] = {
+ DEFINE_RES_IRQ_NAMED(AXP717_IRQ_VBUS_OVER_V, "VBUS_OVER_V"),
+ DEFINE_RES_IRQ_NAMED(AXP717_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"),
+ DEFINE_RES_IRQ_NAMED(AXP717_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
+};
+
/* AXP803 and AXP813/AXP818 share the same interrupts */
static const struct resource axp803_usb_power_supply_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP803_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"),
@@ -316,6 +365,11 @@ static const struct resource axp313a_pek_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP313A_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
};
+static const struct resource axp717_pek_resources[] = {
+ DEFINE_RES_IRQ_NAMED(AXP717_IRQ_PEK_RIS_EDGE, "PEK_DBR"),
+ DEFINE_RES_IRQ_NAMED(AXP717_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
+};
+
static const struct resource axp803_pek_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP803_IRQ_PEK_RIS_EDGE, "PEK_DBR"),
DEFINE_RES_IRQ_NAMED(AXP803_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
@@ -342,7 +396,7 @@ static const struct regmap_config axp152_regmap_config = {
.wr_table = &axp152_writeable_table,
.volatile_table = &axp152_volatile_table,
.max_register = AXP152_PWM1_DUTY_CYCLE,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp192_regmap_config = {
@@ -351,7 +405,7 @@ static const struct regmap_config axp192_regmap_config = {
.wr_table = &axp192_writeable_table,
.volatile_table = &axp192_volatile_table,
.max_register = AXP20X_CC_CTRL,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp20x_regmap_config = {
@@ -360,7 +414,7 @@ static const struct regmap_config axp20x_regmap_config = {
.wr_table = &axp20x_writeable_table,
.volatile_table = &axp20x_volatile_table,
.max_register = AXP20X_OCV(AXP20X_OCV_MAX),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp22x_regmap_config = {
@@ -369,7 +423,7 @@ static const struct regmap_config axp22x_regmap_config = {
.wr_table = &axp22x_writeable_table,
.volatile_table = &axp22x_volatile_table,
.max_register = AXP22X_BATLOW_THRES1,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp288_regmap_config = {
@@ -378,7 +432,7 @@ static const struct regmap_config axp288_regmap_config = {
.wr_table = &axp288_writeable_table,
.volatile_table = &axp288_volatile_table,
.max_register = AXP288_FG_TUNE5,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp313a_regmap_config = {
@@ -387,7 +441,25 @@ static const struct regmap_config axp313a_regmap_config = {
.wr_table = &axp313a_writeable_table,
.volatile_table = &axp313a_volatile_table,
.max_register = AXP313A_IRQ_STATE,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct regmap_config axp323_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp323_writeable_table,
+ .volatile_table = &axp313a_volatile_table,
+ .max_register = AXP323_DCDC_MODE_CTRL2,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct regmap_config axp717_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp717_writeable_table,
+ .volatile_table = &axp717_volatile_table,
+ .max_register = AXP717_ADC_DATA_L,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp806_regmap_config = {
@@ -396,7 +468,7 @@ static const struct regmap_config axp806_regmap_config = {
.wr_table = &axp806_writeable_table,
.volatile_table = &axp806_volatile_table,
.max_register = AXP806_REG_ADDR_EXT,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config axp15060_regmap_config = {
@@ -405,7 +477,7 @@ static const struct regmap_config axp15060_regmap_config = {
.wr_table = &axp15060_writeable_table,
.volatile_table = &axp15060_volatile_table,
.max_register = AXP15060_IRQ2_STATE,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
#define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \
@@ -588,6 +660,40 @@ static const struct regmap_irq axp313a_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP313A, DIE_TEMP_HIGH, 0, 0),
};
+static const struct regmap_irq axp717_regmap_irqs[] = {
+ INIT_REGMAP_IRQ(AXP717, SOC_DROP_LVL2, 0, 7),
+ INIT_REGMAP_IRQ(AXP717, SOC_DROP_LVL1, 0, 6),
+ INIT_REGMAP_IRQ(AXP717, GAUGE_NEW_SOC, 0, 4),
+ INIT_REGMAP_IRQ(AXP717, BOOST_OVER_V, 0, 2),
+ INIT_REGMAP_IRQ(AXP717, VBUS_OVER_V, 0, 1),
+ INIT_REGMAP_IRQ(AXP717, VBUS_FAULT, 0, 0),
+ INIT_REGMAP_IRQ(AXP717, VBUS_PLUGIN, 1, 7),
+ INIT_REGMAP_IRQ(AXP717, VBUS_REMOVAL, 1, 6),
+ INIT_REGMAP_IRQ(AXP717, BATT_PLUGIN, 1, 5),
+ INIT_REGMAP_IRQ(AXP717, BATT_REMOVAL, 1, 4),
+ INIT_REGMAP_IRQ(AXP717, PEK_SHORT, 1, 3),
+ INIT_REGMAP_IRQ(AXP717, PEK_LONG, 1, 2),
+ INIT_REGMAP_IRQ(AXP717, PEK_FAL_EDGE, 1, 1),
+ INIT_REGMAP_IRQ(AXP717, PEK_RIS_EDGE, 1, 0),
+ INIT_REGMAP_IRQ(AXP717, WDOG_EXPIRE, 2, 7),
+ INIT_REGMAP_IRQ(AXP717, LDO_OVER_CURR, 2, 6),
+ INIT_REGMAP_IRQ(AXP717, BATT_OVER_CURR, 2, 5),
+ INIT_REGMAP_IRQ(AXP717, CHARG_DONE, 2, 4),
+ INIT_REGMAP_IRQ(AXP717, CHARG, 2, 3),
+ INIT_REGMAP_IRQ(AXP717, DIE_TEMP_HIGH, 2, 2),
+ INIT_REGMAP_IRQ(AXP717, CHARG_TIMER, 2, 1),
+ INIT_REGMAP_IRQ(AXP717, BATT_OVER_V, 2, 0),
+ INIT_REGMAP_IRQ(AXP717, BC_USB_DONE, 3, 7),
+ INIT_REGMAP_IRQ(AXP717, BC_USB_CHNG, 3, 6),
+ INIT_REGMAP_IRQ(AXP717, BATT_QUIT_TEMP_HIGH, 3, 4),
+ INIT_REGMAP_IRQ(AXP717, BATT_CHG_TEMP_HIGH, 3, 3),
+ INIT_REGMAP_IRQ(AXP717, BATT_CHG_TEMP_LOW, 3, 2),
+ INIT_REGMAP_IRQ(AXP717, BATT_ACT_TEMP_HIGH, 3, 1),
+ INIT_REGMAP_IRQ(AXP717, BATT_ACT_TEMP_LOW, 3, 0),
+ INIT_REGMAP_IRQ(AXP717, TYPEC_REMOVE, 4, 6),
+ INIT_REGMAP_IRQ(AXP717, TYPEC_PLUGIN, 4, 5),
+};
+
static const struct regmap_irq axp803_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP803, ACIN_OVER_V, 0, 7),
INIT_REGMAP_IRQ(AXP803, ACIN_PLUGIN, 0, 6),
@@ -775,6 +881,17 @@ static const struct regmap_irq_chip axp313a_regmap_irq_chip = {
.num_regs = 1,
};
+static const struct regmap_irq_chip axp717_regmap_irq_chip = {
+ .name = "axp717_irq_chip",
+ .status_base = AXP717_IRQ0_STATE,
+ .ack_base = AXP717_IRQ0_STATE,
+ .unmask_base = AXP717_IRQ0_EN,
+ .init_ack_masked = true,
+ .irqs = axp717_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(axp717_regmap_irqs),
+ .num_regs = 5,
+};
+
static const struct regmap_irq_chip axp803_regmap_irq_chip = {
.name = "axp803",
.status_base = AXP20X_IRQ1_STATE,
@@ -936,10 +1053,23 @@ static const struct mfd_cell axp152_cells[] = {
};
static struct mfd_cell axp313a_cells[] = {
- MFD_CELL_NAME("axp20x-regulator"),
+ /* AXP323 is sometimes paired with AXP717 as sub-PMIC */
+ MFD_CELL_BASIC("axp20x-regulator", NULL, NULL, 0, 1),
MFD_CELL_RES("axp313a-pek", axp313a_pek_resources),
};
+static struct mfd_cell axp717_cells[] = {
+ MFD_CELL_NAME("axp20x-regulator"),
+ MFD_CELL_RES("axp20x-pek", axp717_pek_resources),
+ MFD_CELL_OF("axp717-adc",
+ NULL, NULL, 0, 0, "x-powers,axp717-adc"),
+ MFD_CELL_OF("axp20x-usb-power-supply",
+ axp717_usb_power_supply_resources, NULL, 0, 0,
+ "x-powers,axp717-usb-power-supply"),
+ MFD_CELL_OF("axp20x-battery-power-supply",
+ NULL, NULL, 0, 0, "x-powers,axp717-battery-power-supply"),
+};
+
static const struct resource axp288_adc_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP288_IRQ_GPADC, "GPADC"),
};
@@ -1101,9 +1231,8 @@ static const struct mfd_cell axp15060_cells[] = {
/* For boards that don't have IRQ line connected to SOC. */
static const struct mfd_cell axp_regulator_only_cells[] = {
- {
- .name = "axp20x-regulator",
- },
+ /* PMIC without IRQ line may be secondary PMIC */
+ MFD_CELL_BASIC("axp20x-regulator", NULL, NULL, 0, 1),
};
static int axp20x_power_off(struct sys_off_data *data)
@@ -1112,6 +1241,7 @@ static int axp20x_power_off(struct sys_off_data *data)
unsigned int shutdown_reg;
switch (axp20x->variant) {
+ case AXP323_ID:
case AXP313A_ID:
shutdown_reg = AXP313A_SHUTDOWN_CTRL;
break;
@@ -1131,25 +1261,10 @@ static int axp20x_power_off(struct sys_off_data *data)
int axp20x_match_device(struct axp20x_dev *axp20x)
{
struct device *dev = axp20x->dev;
- const struct acpi_device_id *acpi_id;
- const struct of_device_id *of_id;
-
- if (dev->of_node) {
- of_id = of_match_device(dev->driver->of_match_table, dev);
- if (!of_id) {
- dev_err(dev, "Unable to match OF ID\n");
- return -ENODEV;
- }
- axp20x->variant = (long)of_id->data;
- } else {
- acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev);
- if (!acpi_id || !acpi_id->driver_data) {
- dev_err(dev, "Unable to match ACPI ID and data\n");
- return -ENODEV;
- }
- axp20x->variant = (long)acpi_id->driver_data;
- }
+ const struct mfd_cell *cells_no_irq = NULL;
+ int nr_cells_no_irq = 0;
+ axp20x->variant = (long)device_get_match_data(dev);
switch (axp20x->variant) {
case AXP152_ID:
axp20x->nr_cells = ARRAY_SIZE(axp152_cells);
@@ -1195,6 +1310,18 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
axp20x->regmap_cfg = &axp313a_regmap_config;
axp20x->regmap_irq_chip = &axp313a_regmap_irq_chip;
break;
+ case AXP323_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp313a_cells);
+ axp20x->cells = axp313a_cells;
+ axp20x->regmap_cfg = &axp323_regmap_config;
+ axp20x->regmap_irq_chip = &axp313a_regmap_irq_chip;
+ break;
+ case AXP717_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp717_cells);
+ axp20x->cells = axp717_cells;
+ axp20x->regmap_cfg = &axp717_regmap_config;
+ axp20x->regmap_irq_chip = &axp717_regmap_irq_chip;
+ break;
case AXP803_ID:
axp20x->nr_cells = ARRAY_SIZE(axp803_cells);
axp20x->cells = axp803_cells;
@@ -1207,14 +1334,15 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
* if there is no interrupt line.
*/
if (of_property_read_bool(axp20x->dev->of_node,
- "x-powers,self-working-mode") &&
- axp20x->irq > 0) {
+ "x-powers,self-working-mode")) {
axp20x->nr_cells = ARRAY_SIZE(axp806_self_working_cells);
axp20x->cells = axp806_self_working_cells;
} else {
axp20x->nr_cells = ARRAY_SIZE(axp806_cells);
axp20x->cells = axp806_cells;
}
+ nr_cells_no_irq = ARRAY_SIZE(axp806_cells);
+ cells_no_irq = axp806_cells;
axp20x->regmap_cfg = &axp806_regmap_config;
axp20x->regmap_irq_chip = &axp806_regmap_irq_chip;
break;
@@ -1238,31 +1366,32 @@ int axp20x_match_device(struct axp20x_dev *axp20x)
axp20x->regmap_irq_chip = &axp803_regmap_irq_chip;
break;
case AXP15060_ID:
- /*
- * Don't register the power key part if there is no interrupt
- * line.
- *
- * Since most use cases of AXP PMICs are Allwinner SOCs, board
- * designers follow Allwinner's reference design and connects
- * IRQ line to SOC, there's no need for those variants to deal
- * with cases that IRQ isn't connected. However, AXP15660 is
- * used by some other vendors' SOCs that didn't connect IRQ
- * line, we need to deal with this case.
- */
- if (axp20x->irq > 0) {
- axp20x->nr_cells = ARRAY_SIZE(axp15060_cells);
- axp20x->cells = axp15060_cells;
- } else {
- axp20x->nr_cells = ARRAY_SIZE(axp_regulator_only_cells);
- axp20x->cells = axp_regulator_only_cells;
- }
+ axp20x->nr_cells = ARRAY_SIZE(axp15060_cells);
+ axp20x->cells = axp15060_cells;
axp20x->regmap_cfg = &axp15060_regmap_config;
axp20x->regmap_irq_chip = &axp15060_regmap_irq_chip;
break;
default:
- dev_err(dev, "unsupported AXP20X ID %lu\n", axp20x->variant);
+ dev_err(dev, "unsupported AXP20X ID %u\n", axp20x->variant);
return -EINVAL;
}
+
+ /*
+ * Use an alternative cell array when no interrupt line is connected,
+ * since IRQs are required by some drivers.
+ * The default is the safe "regulator-only", as this works fine without
+ * an interrupt specified.
+ */
+ if (axp20x->irq <= 0) {
+ if (cells_no_irq) {
+ axp20x->nr_cells = nr_cells_no_irq;
+ axp20x->cells = cells_no_irq;
+ } else {
+ axp20x->nr_cells = ARRAY_SIZE(axp_regulator_only_cells);
+ axp20x->cells = axp_regulator_only_cells;
+ }
+ }
+
dev_info(dev, "AXP20x variant %s found\n",
axp20x_model_names[axp20x->variant]);
@@ -1317,7 +1446,7 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
}
}
- ret = mfd_add_devices(axp20x->dev, -1, axp20x->cells,
+ ret = mfd_add_devices(axp20x->dev, PLATFORM_DEVID_NONE, axp20x->cells,
axp20x->nr_cells, NULL, 0, NULL);
if (ret) {
@@ -1327,10 +1456,7 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
}
if (axp20x->variant != AXP288_ID)
- devm_register_sys_off_handler(axp20x->dev,
- SYS_OFF_MODE_POWER_OFF,
- SYS_OFF_PRIO_DEFAULT,
- axp20x_power_off, axp20x);
+ devm_register_power_off_handler(axp20x->dev, axp20x_power_off, axp20x);
dev_info(axp20x->dev, "AXP20X driver loaded\n");
diff --git a/drivers/mfd/bcm2835-pm.c b/drivers/mfd/bcm2835-pm.c
index 3cb2b9423121..8bed59816e82 100644
--- a/drivers/mfd/bcm2835-pm.c
+++ b/drivers/mfd/bcm2835-pm.c
@@ -108,6 +108,7 @@ static const struct of_device_id bcm2835_pm_of_match[] = {
{ .compatible = "brcm,bcm2835-pm-wdt", },
{ .compatible = "brcm,bcm2835-pm", },
{ .compatible = "brcm,bcm2711-pm", },
+ { .compatible = "brcm,bcm2712-pm", },
{},
};
MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match);
diff --git a/drivers/mfd/bcm590xx.c b/drivers/mfd/bcm590xx.c
index 9f39b46b87f4..5a8456bbd63f 100644
--- a/drivers/mfd/bcm590xx.c
+++ b/drivers/mfd/bcm590xx.c
@@ -14,10 +14,18 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+/* Under primary I2C address: */
+#define BCM590XX_REG_PMUID 0x1e
+
+#define BCM590XX_REG_PMUREV 0x1f
+#define BCM590XX_PMUREV_DIG_MASK 0xF
+#define BCM590XX_PMUREV_DIG_SHIFT 0
+#define BCM590XX_PMUREV_ANA_MASK 0xF0
+#define BCM590XX_PMUREV_ANA_SHIFT 4
+
static const struct mfd_cell bcm590xx_devs[] = {
{
.name = "bcm590xx-vregs",
@@ -28,16 +36,57 @@ static const struct regmap_config bcm590xx_regmap_config_pri = {
.reg_bits = 8,
.val_bits = 8,
.max_register = BCM590XX_MAX_REGISTER_PRI,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config bcm590xx_regmap_config_sec = {
.reg_bits = 8,
.val_bits = 8,
.max_register = BCM590XX_MAX_REGISTER_SEC,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+/* Map PMU ID value to model name string */
+static const char * const bcm590xx_names[] = {
+ [BCM590XX_PMUID_BCM59054] = "BCM59054",
+ [BCM590XX_PMUID_BCM59056] = "BCM59056",
};
+static int bcm590xx_parse_version(struct bcm590xx *bcm590xx)
+{
+ unsigned int id, rev;
+ int ret;
+
+ /* Get PMU ID and verify that it matches compatible */
+ ret = regmap_read(bcm590xx->regmap_pri, BCM590XX_REG_PMUID, &id);
+ if (ret) {
+ dev_err(bcm590xx->dev, "failed to read PMU ID: %d\n", ret);
+ return ret;
+ }
+
+ if (id != bcm590xx->pmu_id) {
+ dev_err(bcm590xx->dev, "Incorrect ID for %s: expected %x, got %x.\n",
+ bcm590xx_names[bcm590xx->pmu_id], bcm590xx->pmu_id, id);
+ return -ENODEV;
+ }
+
+ /* Get PMU revision and store it in the info struct */
+ ret = regmap_read(bcm590xx->regmap_pri, BCM590XX_REG_PMUREV, &rev);
+ if (ret) {
+ dev_err(bcm590xx->dev, "failed to read PMU revision: %d\n", ret);
+ return ret;
+ }
+
+ bcm590xx->rev_digital = (rev & BCM590XX_PMUREV_DIG_MASK) >> BCM590XX_PMUREV_DIG_SHIFT;
+
+ bcm590xx->rev_analog = (rev & BCM590XX_PMUREV_ANA_MASK) >> BCM590XX_PMUREV_ANA_SHIFT;
+
+ dev_dbg(bcm590xx->dev, "PMU ID 0x%x (%s), revision: digital %d, analog %d",
+ id, bcm590xx_names[id], bcm590xx->rev_digital, bcm590xx->rev_analog);
+
+ return 0;
+}
+
static int bcm590xx_i2c_probe(struct i2c_client *i2c_pri)
{
struct bcm590xx *bcm590xx;
@@ -51,6 +100,8 @@ static int bcm590xx_i2c_probe(struct i2c_client *i2c_pri)
bcm590xx->dev = &i2c_pri->dev;
bcm590xx->i2c_pri = i2c_pri;
+ bcm590xx->pmu_id = (uintptr_t) of_device_get_match_data(bcm590xx->dev);
+
bcm590xx->regmap_pri = devm_regmap_init_i2c(i2c_pri,
&bcm590xx_regmap_config_pri);
if (IS_ERR(bcm590xx->regmap_pri)) {
@@ -77,6 +128,10 @@ static int bcm590xx_i2c_probe(struct i2c_client *i2c_pri)
goto err;
}
+ ret = bcm590xx_parse_version(bcm590xx);
+ if (ret)
+ goto err;
+
ret = devm_mfd_add_devices(&i2c_pri->dev, -1, bcm590xx_devs,
ARRAY_SIZE(bcm590xx_devs), NULL, 0, NULL);
if (ret < 0) {
@@ -92,12 +147,20 @@ err:
}
static const struct of_device_id bcm590xx_of_match[] = {
- { .compatible = "brcm,bcm59056" },
+ {
+ .compatible = "brcm,bcm59054",
+ .data = (void *)BCM590XX_PMUID_BCM59054,
+ },
+ {
+ .compatible = "brcm,bcm59056",
+ .data = (void *)BCM590XX_PMUID_BCM59056,
+ },
{ }
};
MODULE_DEVICE_TABLE(of, bcm590xx_of_match);
static const struct i2c_device_id bcm590xx_i2c_id[] = {
+ { "bcm59054" },
{ "bcm59056" },
{ }
};
diff --git a/drivers/mfd/bd9571mwv.c b/drivers/mfd/bd9571mwv.c
index 819d09e4d100..db8c2963fb48 100644
--- a/drivers/mfd/bd9571mwv.c
+++ b/drivers/mfd/bd9571mwv.c
@@ -67,7 +67,7 @@ static const struct regmap_access_table bd9571mwv_volatile_table = {
static const struct regmap_config bd9571mwv_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.rd_table = &bd9571mwv_readable_table,
.wr_table = &bd9571mwv_writable_table,
.volatile_table = &bd9571mwv_volatile_table,
@@ -93,7 +93,7 @@ static const struct regmap_irq bd9571mwv_irqs[] = {
BD9571MWV_INT_INTREQ_BKUP_TRG_INT),
};
-static struct regmap_irq_chip bd9571mwv_irq_chip = {
+static const struct regmap_irq_chip bd9571mwv_irq_chip = {
.name = "bd9571mwv",
.status_base = BD9571MWV_INT_INTREQ,
.mask_base = BD9571MWV_INT_INTMASK,
@@ -152,14 +152,14 @@ static const struct regmap_access_table bd9574mwf_volatile_table = {
static const struct regmap_config bd9574mwf_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.rd_table = &bd9574mwf_readable_table,
.wr_table = &bd9574mwf_writable_table,
.volatile_table = &bd9574mwf_volatile_table,
.max_register = 0xff,
};
-static struct regmap_irq_chip bd9574mwf_irq_chip = {
+static const struct regmap_irq_chip bd9574mwf_irq_chip = {
.name = "bd9574mwf",
.status_base = BD9571MWV_INT_INTREQ,
.mask_base = BD9571MWV_INT_INTMASK,
@@ -268,7 +268,7 @@ static const struct of_device_id bd9571mwv_of_match_table[] = {
MODULE_DEVICE_TABLE(of, bd9571mwv_of_match_table);
static const struct i2c_device_id bd9571mwv_id_table[] = {
- { "bd9571mwv", 0 },
+ { "bd9571mwv" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, bd9571mwv_id_table);
diff --git a/drivers/mfd/bq257xx.c b/drivers/mfd/bq257xx.c
new file mode 100644
index 000000000000..e9d49dac0a16
--- /dev/null
+++ b/drivers/mfd/bq257xx.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * BQ257XX Core Driver
+ * Copyright (C) 2025 Chris Morgan <macromorgan@hotmail.com>
+ */
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/mfd/bq257xx.h>
+#include <linux/mfd/core.h>
+#include <linux/regmap.h>
+
+static const struct regmap_range bq25703_readonly_reg_ranges[] = {
+ regmap_reg_range(BQ25703_CHARGER_STATUS, BQ25703_MANUFACT_DEV_ID),
+};
+
+static const struct regmap_access_table bq25703_writeable_regs = {
+ .no_ranges = bq25703_readonly_reg_ranges,
+ .n_no_ranges = ARRAY_SIZE(bq25703_readonly_reg_ranges),
+};
+
+static const struct regmap_range bq25703_volatile_reg_ranges[] = {
+ regmap_reg_range(BQ25703_CHARGE_OPTION_0, BQ25703_IIN_HOST),
+ regmap_reg_range(BQ25703_CHARGER_STATUS, BQ25703_ADC_OPTION),
+};
+
+static const struct regmap_access_table bq25703_volatile_regs = {
+ .yes_ranges = bq25703_volatile_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(bq25703_volatile_reg_ranges),
+};
+
+static const struct regmap_config bq25703_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = BQ25703_ADC_OPTION,
+ .cache_type = REGCACHE_MAPLE,
+ .wr_table = &bq25703_writeable_regs,
+ .volatile_table = &bq25703_volatile_regs,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+static const struct mfd_cell cells[] = {
+ MFD_CELL_NAME("bq257xx-regulator"),
+ MFD_CELL_NAME("bq257xx-charger"),
+};
+
+static int bq257xx_probe(struct i2c_client *client)
+{
+ struct bq257xx_device *ddata;
+ int ret;
+
+ ddata = devm_kzalloc(&client->dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ ddata->client = client;
+
+ ddata->regmap = devm_regmap_init_i2c(client, &bq25703_regmap_config);
+ if (IS_ERR(ddata->regmap)) {
+ return dev_err_probe(&client->dev, PTR_ERR(ddata->regmap),
+ "Failed to allocate register map\n");
+ }
+
+ i2c_set_clientdata(client, ddata);
+
+ ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO,
+ cells, ARRAY_SIZE(cells), NULL, 0, NULL);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to register child devices\n");
+
+ return ret;
+}
+
+static const struct i2c_device_id bq257xx_i2c_ids[] = {
+ { "bq25703a" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, bq257xx_i2c_ids);
+
+static const struct of_device_id bq257xx_of_match[] = {
+ { .compatible = "ti,bq25703a" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, bq257xx_of_match);
+
+static struct i2c_driver bq257xx_driver = {
+ .driver = {
+ .name = "bq257xx",
+ .of_match_table = bq257xx_of_match,
+ },
+ .probe = bq257xx_probe,
+ .id_table = bq257xx_i2c_ids,
+};
+module_i2c_driver(bq257xx_driver);
+
+MODULE_DESCRIPTION("bq257xx buck/boost/charger driver");
+MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/cgbc-core.c b/drivers/mfd/cgbc-core.c
new file mode 100644
index 000000000000..4782ff1114a9
--- /dev/null
+++ b/drivers/mfd/cgbc-core.c
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Congatec Board Controller core driver.
+ *
+ * The x86 Congatec modules have an embedded micro controller named Board
+ * Controller. This Board Controller has a Watchdog timer, some GPIOs, and two
+ * I2C busses.
+ *
+ * Copyright (C) 2024 Bootlin
+ *
+ * Author: Thomas Richard <thomas.richard@bootlin.com>
+ */
+
+#include <linux/dmi.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/cgbc.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+
+#define CGBC_IO_SESSION_BASE 0x0E20
+#define CGBC_IO_SESSION_END 0x0E30
+#define CGBC_IO_CMD_BASE 0x0E00
+#define CGBC_IO_CMD_END 0x0E10
+
+#define CGBC_MASK_STATUS (BIT(6) | BIT(7))
+#define CGBC_MASK_DATA_COUNT 0x1F
+#define CGBC_MASK_ERROR_CODE 0x1F
+
+#define CGBC_STATUS_DATA_READY 0x00
+#define CGBC_STATUS_CMD_READY BIT(6)
+#define CGBC_STATUS_ERROR (BIT(6) | BIT(7))
+
+#define CGBC_SESSION_CMD 0x00
+#define CGBC_SESSION_CMD_IDLE 0x00
+#define CGBC_SESSION_CMD_REQUEST 0x01
+#define CGBC_SESSION_DATA 0x01
+#define CGBC_SESSION_STATUS 0x02
+#define CGBC_SESSION_STATUS_FREE 0x03
+#define CGBC_SESSION_ACCESS 0x04
+#define CGBC_SESSION_ACCESS_GAINED 0x00
+
+#define CGBC_SESSION_VALID_MIN 0x02
+#define CGBC_SESSION_VALID_MAX 0xFE
+
+#define CGBC_CMD_STROBE 0x00
+#define CGBC_CMD_INDEX 0x02
+#define CGBC_CMD_INDEX_CBM_MAN8 0x00
+#define CGBC_CMD_INDEX_CBM_AUTO32 0x03
+#define CGBC_CMD_DATA 0x04
+#define CGBC_CMD_ACCESS 0x0C
+
+#define CGBC_CMD_GET_FW_REV 0x21
+
+static struct platform_device *cgbc_pdev;
+
+/* Wait the Board Controller is ready to receive some session commands */
+static int cgbc_wait_device(struct cgbc_device_data *cgbc)
+{
+ u16 status;
+ int ret;
+
+ ret = readx_poll_timeout(ioread16, cgbc->io_session + CGBC_SESSION_STATUS, status,
+ status == CGBC_SESSION_STATUS_FREE, 0, 500000);
+
+ if (ret || ioread32(cgbc->io_session + CGBC_SESSION_ACCESS))
+ ret = -ENODEV;
+
+ return ret;
+}
+
+static int cgbc_session_command(struct cgbc_device_data *cgbc, u8 cmd)
+{
+ int ret;
+ u8 val;
+
+ ret = readx_poll_timeout(ioread8, cgbc->io_session + CGBC_SESSION_CMD, val,
+ val == CGBC_SESSION_CMD_IDLE, 0, 100000);
+ if (ret)
+ return ret;
+
+ iowrite8(cmd, cgbc->io_session + CGBC_SESSION_CMD);
+
+ ret = readx_poll_timeout(ioread8, cgbc->io_session + CGBC_SESSION_CMD, val,
+ val == CGBC_SESSION_CMD_IDLE, 0, 100000);
+ if (ret)
+ return ret;
+
+ ret = (int)ioread8(cgbc->io_session + CGBC_SESSION_DATA);
+
+ iowrite8(CGBC_SESSION_STATUS_FREE, cgbc->io_session + CGBC_SESSION_STATUS);
+
+ return ret;
+}
+
+static int cgbc_session_request(struct cgbc_device_data *cgbc)
+{
+ int ret;
+
+ ret = cgbc_wait_device(cgbc);
+
+ if (ret)
+ return dev_err_probe(cgbc->dev, ret, "device not found or not ready\n");
+
+ cgbc->session = cgbc_session_command(cgbc, CGBC_SESSION_CMD_REQUEST);
+
+ /* The Board Controller sent us a wrong session handle, we cannot communicate with it */
+ if (cgbc->session < CGBC_SESSION_VALID_MIN || cgbc->session > CGBC_SESSION_VALID_MAX)
+ return dev_err_probe(cgbc->dev, -ECONNREFUSED,
+ "failed to get a valid session handle\n");
+
+ return 0;
+}
+
+static void cgbc_session_release(struct cgbc_device_data *cgbc)
+{
+ if (cgbc_session_command(cgbc, cgbc->session) != cgbc->session)
+ dev_warn(cgbc->dev, "failed to release session\n");
+}
+
+static bool cgbc_command_lock(struct cgbc_device_data *cgbc)
+{
+ iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_ACCESS);
+
+ return ioread8(cgbc->io_cmd + CGBC_CMD_ACCESS) == cgbc->session;
+}
+
+static void cgbc_command_unlock(struct cgbc_device_data *cgbc)
+{
+ iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_ACCESS);
+}
+
+int cgbc_command(struct cgbc_device_data *cgbc, void *cmd, unsigned int cmd_size, void *data,
+ unsigned int data_size, u8 *status)
+{
+ u8 checksum = 0, data_checksum = 0, istatus = 0, val;
+ u8 *_data = (u8 *)data;
+ u8 *_cmd = (u8 *)cmd;
+ int mode_change = -1;
+ bool lock;
+ int ret, i;
+
+ mutex_lock(&cgbc->lock);
+
+ /* Request access */
+ ret = readx_poll_timeout(cgbc_command_lock, cgbc, lock, lock, 0, 100000);
+ if (ret)
+ goto out;
+
+ /* Wait board controller is ready */
+ ret = readx_poll_timeout(ioread8, cgbc->io_cmd + CGBC_CMD_STROBE, val,
+ val == CGBC_CMD_STROBE, 0, 100000);
+ if (ret)
+ goto release;
+
+ /* Write command packet */
+ if (cmd_size <= 2) {
+ iowrite8(CGBC_CMD_INDEX_CBM_MAN8, cgbc->io_cmd + CGBC_CMD_INDEX);
+ } else {
+ iowrite8(CGBC_CMD_INDEX_CBM_AUTO32, cgbc->io_cmd + CGBC_CMD_INDEX);
+ if ((cmd_size % 4) != 0x03)
+ mode_change = (cmd_size & 0xFFFC) - 1;
+ }
+
+ for (i = 0; i < cmd_size; i++) {
+ iowrite8(_cmd[i], cgbc->io_cmd + CGBC_CMD_DATA + (i % 4));
+ checksum ^= _cmd[i];
+ if (mode_change == i)
+ iowrite8((i + 1) | CGBC_CMD_INDEX_CBM_MAN8, cgbc->io_cmd + CGBC_CMD_INDEX);
+ }
+
+ /* Append checksum byte */
+ iowrite8(checksum, cgbc->io_cmd + CGBC_CMD_DATA + (i % 4));
+
+ /* Perform command strobe */
+ iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_STROBE);
+
+ /* Rewind cmd buffer index */
+ iowrite8(CGBC_CMD_INDEX_CBM_AUTO32, cgbc->io_cmd + CGBC_CMD_INDEX);
+
+ /* Wait command completion */
+ ret = read_poll_timeout(ioread8, val, val == CGBC_CMD_STROBE, 0, 100000, false,
+ cgbc->io_cmd + CGBC_CMD_STROBE);
+ if (ret)
+ goto release;
+
+ istatus = ioread8(cgbc->io_cmd + CGBC_CMD_DATA);
+ checksum = istatus;
+
+ /* Check command status */
+ switch (istatus & CGBC_MASK_STATUS) {
+ case CGBC_STATUS_DATA_READY:
+ if (istatus > data_size)
+ istatus = data_size;
+ for (i = 0; i < istatus; i++) {
+ _data[i] = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + ((i + 1) % 4));
+ checksum ^= _data[i];
+ }
+ data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + ((i + 1) % 4));
+ istatus &= CGBC_MASK_DATA_COUNT;
+ break;
+ case CGBC_STATUS_ERROR:
+ case CGBC_STATUS_CMD_READY:
+ data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + 1);
+ if ((istatus & CGBC_MASK_STATUS) == CGBC_STATUS_ERROR)
+ ret = -EIO;
+ istatus = istatus & CGBC_MASK_ERROR_CODE;
+ break;
+ default:
+ data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + 1);
+ istatus &= CGBC_MASK_ERROR_CODE;
+ ret = -EIO;
+ break;
+ }
+
+ /* Checksum verification */
+ if (ret == 0 && data_checksum != checksum)
+ ret = -EIO;
+
+release:
+ cgbc_command_unlock(cgbc);
+
+out:
+ mutex_unlock(&cgbc->lock);
+
+ if (status)
+ *status = istatus;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cgbc_command);
+
+static struct mfd_cell cgbc_devs[] = {
+ { .name = "cgbc-wdt" },
+ { .name = "cgbc-gpio" },
+ { .name = "cgbc-i2c", .id = 1 },
+ { .name = "cgbc-i2c", .id = 2 },
+ { .name = "cgbc-hwmon" },
+};
+
+static int cgbc_map(struct cgbc_device_data *cgbc)
+{
+ struct device *dev = cgbc->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *ioport;
+
+ ioport = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!ioport)
+ return -EINVAL;
+
+ cgbc->io_session = devm_ioport_map(dev, ioport->start, resource_size(ioport));
+ if (!cgbc->io_session)
+ return -ENOMEM;
+
+ ioport = platform_get_resource(pdev, IORESOURCE_IO, 1);
+ if (!ioport)
+ return -EINVAL;
+
+ cgbc->io_cmd = devm_ioport_map(dev, ioport->start, resource_size(ioport));
+ if (!cgbc->io_cmd)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const struct resource cgbc_resources[] = {
+ {
+ .start = CGBC_IO_SESSION_BASE,
+ .end = CGBC_IO_SESSION_END,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .start = CGBC_IO_CMD_BASE,
+ .end = CGBC_IO_CMD_END,
+ .flags = IORESOURCE_IO,
+ },
+};
+
+static ssize_t cgbc_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cgbc_device_data *cgbc = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "CGBCP%c%c%c\n", cgbc->version.feature, cgbc->version.major,
+ cgbc->version.minor);
+}
+
+static DEVICE_ATTR_RO(cgbc_version);
+
+static struct attribute *cgbc_attrs[] = {
+ &dev_attr_cgbc_version.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(cgbc);
+
+static int cgbc_get_version(struct cgbc_device_data *cgbc)
+{
+ u8 cmd = CGBC_CMD_GET_FW_REV;
+ u8 data[4];
+ int ret;
+
+ ret = cgbc_command(cgbc, &cmd, 1, &data, sizeof(data), NULL);
+ if (ret)
+ return ret;
+
+ cgbc->version.feature = data[0];
+ cgbc->version.major = data[1];
+ cgbc->version.minor = data[2];
+
+ return 0;
+}
+
+static int cgbc_init_device(struct cgbc_device_data *cgbc)
+{
+ int ret;
+
+ ret = cgbc_session_request(cgbc);
+ if (ret)
+ return ret;
+
+ ret = cgbc_get_version(cgbc);
+ if (ret)
+ goto release_session;
+
+ ret = mfd_add_devices(cgbc->dev, -1, cgbc_devs, ARRAY_SIZE(cgbc_devs),
+ NULL, 0, NULL);
+ if (ret)
+ goto release_session;
+
+ return 0;
+
+release_session:
+ cgbc_session_release(cgbc);
+ return ret;
+}
+
+static int cgbc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cgbc_device_data *cgbc;
+ int ret;
+
+ cgbc = devm_kzalloc(dev, sizeof(*cgbc), GFP_KERNEL);
+ if (!cgbc)
+ return -ENOMEM;
+
+ cgbc->dev = dev;
+
+ ret = cgbc_map(cgbc);
+ if (ret)
+ return ret;
+
+ mutex_init(&cgbc->lock);
+
+ platform_set_drvdata(pdev, cgbc);
+
+ return cgbc_init_device(cgbc);
+}
+
+static void cgbc_remove(struct platform_device *pdev)
+{
+ struct cgbc_device_data *cgbc = platform_get_drvdata(pdev);
+
+ cgbc_session_release(cgbc);
+
+ mfd_remove_devices(&pdev->dev);
+}
+
+static struct platform_driver cgbc_driver = {
+ .driver = {
+ .name = "cgbc",
+ .dev_groups = cgbc_groups,
+ },
+ .probe = cgbc_probe,
+ .remove = cgbc_remove,
+};
+
+static const struct dmi_system_id cgbc_dmi_table[] __initconst = {
+ {
+ .ident = "SA7",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "congatec"),
+ DMI_MATCH(DMI_BOARD_NAME, "conga-SA7"),
+ },
+ },
+ {
+ .ident = "SA8",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "congatec"),
+ DMI_MATCH(DMI_BOARD_NAME, "conga-SA8"),
+ },
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(dmi, cgbc_dmi_table);
+
+static int __init cgbc_init(void)
+{
+ const struct dmi_system_id *id;
+ int ret = -ENODEV;
+
+ id = dmi_first_match(cgbc_dmi_table);
+ if (IS_ERR_OR_NULL(id))
+ return ret;
+
+ cgbc_pdev = platform_device_register_simple("cgbc", PLATFORM_DEVID_NONE, cgbc_resources,
+ ARRAY_SIZE(cgbc_resources));
+ if (IS_ERR(cgbc_pdev))
+ return PTR_ERR(cgbc_pdev);
+
+ return platform_driver_register(&cgbc_driver);
+}
+
+static void __exit cgbc_exit(void)
+{
+ platform_device_unregister(cgbc_pdev);
+ platform_driver_unregister(&cgbc_driver);
+}
+
+module_init(cgbc_init);
+module_exit(cgbc_exit);
+
+MODULE_DESCRIPTION("Congatec Board Controller Core Driver");
+MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:cgbc-core");
diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
index 92f4dfccc3cc..dc80a272726b 100644
--- a/drivers/mfd/cros_ec_dev.c
+++ b/drivers/mfd/cros_ec_dev.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * cros_ec_dev - expose the Chrome OS Embedded Controller to user-space
+ * ChromeOS Embedded Controller
*
* Copyright (C) 2014 Google, Inc.
*/
@@ -10,7 +10,7 @@
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/platform_data/cros_ec_chardev.h>
#include <linux/platform_data/cros_ec_commands.h>
@@ -74,6 +74,10 @@ static const struct mfd_cell cros_ec_cec_cells[] = {
{ .name = "cros-ec-cec", },
};
+static const struct mfd_cell cros_ec_gpio_cells[] = {
+ { .name = "cros-ec-gpio", },
+};
+
static const struct mfd_cell cros_ec_rtc_cells[] = {
{ .name = "cros-ec-rtc", },
};
@@ -91,6 +95,26 @@ static const struct mfd_cell cros_usbpd_notify_cells[] = {
{ .name = "cros-usbpd-notify", },
};
+static const struct mfd_cell cros_ec_wdt_cells[] = {
+ { .name = "cros-ec-wdt", }
+};
+
+static const struct mfd_cell cros_ec_led_cells[] = {
+ { .name = "cros-ec-led", },
+};
+
+static const struct mfd_cell cros_ec_keyboard_leds_cells[] = {
+ { .name = "cros-keyboard-leds", },
+};
+
+static const struct mfd_cell cros_ec_ucsi_cells[] = {
+ { .name = "cros_ec_ucsi", },
+};
+
+static const struct mfd_cell cros_ec_charge_control_cells[] = {
+ { .name = "cros-charge-control", },
+};
+
static const struct cros_feature_to_cells cros_subdevices[] = {
{
.id = EC_FEATURE_CEC,
@@ -98,20 +122,46 @@ static const struct cros_feature_to_cells cros_subdevices[] = {
.num_cells = ARRAY_SIZE(cros_ec_cec_cells),
},
{
+ .id = EC_FEATURE_GPIO,
+ .mfd_cells = cros_ec_gpio_cells,
+ .num_cells = ARRAY_SIZE(cros_ec_gpio_cells),
+ },
+ {
.id = EC_FEATURE_RTC,
.mfd_cells = cros_ec_rtc_cells,
.num_cells = ARRAY_SIZE(cros_ec_rtc_cells),
},
{
- .id = EC_FEATURE_USB_PD,
- .mfd_cells = cros_usbpd_charger_cells,
- .num_cells = ARRAY_SIZE(cros_usbpd_charger_cells),
+ .id = EC_FEATURE_UCSI_PPM,
+ .mfd_cells = cros_ec_ucsi_cells,
+ .num_cells = ARRAY_SIZE(cros_ec_ucsi_cells),
+ },
+ {
+ .id = EC_FEATURE_HANG_DETECT,
+ .mfd_cells = cros_ec_wdt_cells,
+ .num_cells = ARRAY_SIZE(cros_ec_wdt_cells),
+ },
+ {
+ .id = EC_FEATURE_LED,
+ .mfd_cells = cros_ec_led_cells,
+ .num_cells = ARRAY_SIZE(cros_ec_led_cells),
+ },
+ {
+ .id = EC_FEATURE_PWM_KEYB,
+ .mfd_cells = cros_ec_keyboard_leds_cells,
+ .num_cells = ARRAY_SIZE(cros_ec_keyboard_leds_cells),
+ },
+ {
+ .id = EC_FEATURE_CHARGER,
+ .mfd_cells = cros_ec_charge_control_cells,
+ .num_cells = ARRAY_SIZE(cros_ec_charge_control_cells),
},
};
static const struct mfd_cell cros_ec_platform_cells[] = {
{ .name = "cros-ec-chardev", },
{ .name = "cros-ec-debugfs", },
+ { .name = "cros-ec-hwmon", },
{ .name = "cros-ec-sysfs", },
};
@@ -215,6 +265,21 @@ static int ec_device_probe(struct platform_device *pdev)
}
/*
+ * UCSI provides power supply information so we don't need to separately
+ * load the cros_usbpd_charger driver.
+ */
+ if (cros_ec_check_features(ec, EC_FEATURE_USB_PD) &&
+ !cros_ec_check_features(ec, EC_FEATURE_UCSI_PPM)) {
+ retval = mfd_add_hotplug_devices(ec->dev,
+ cros_usbpd_charger_cells,
+ ARRAY_SIZE(cros_usbpd_charger_cells));
+
+ if (retval)
+ dev_warn(ec->dev, "failed to add usbpd-charger: %d\n",
+ retval);
+ }
+
+ /*
* Lightbar is a special case. Newer devices support autodetection,
* but older ones do not.
*/
@@ -288,13 +353,12 @@ failed:
return retval;
}
-static int ec_device_remove(struct platform_device *pdev)
+static void ec_device_remove(struct platform_device *pdev)
{
struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
mfd_remove_devices(ec->dev);
device_unregister(&ec->class_dev);
- return 0;
}
static const struct platform_device_id cros_ec_id[] = {
@@ -316,22 +380,17 @@ static int __init cros_ec_dev_init(void)
{
int ret;
- ret = class_register(&cros_class);
+ ret = class_register(&cros_class);
if (ret) {
pr_err(CROS_EC_DEV_NAME ": failed to register device class\n");
return ret;
}
- /* Register the driver */
ret = platform_driver_register(&cros_ec_dev_driver);
- if (ret < 0) {
+ if (ret) {
pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret);
- goto failed_devreg;
+ class_unregister(&cros_class);
}
- return 0;
-
-failed_devreg:
- class_unregister(&cros_class);
return ret;
}
@@ -345,6 +404,6 @@ module_init(cros_ec_dev_init);
module_exit(cros_ec_dev_exit);
MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>");
-MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller");
+MODULE_DESCRIPTION("ChromeOS Embedded Controller");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/cs40l50-core.c b/drivers/mfd/cs40l50-core.c
new file mode 100644
index 000000000000..662d987b650b
--- /dev/null
+++ b/drivers/mfd/cs40l50-core.c
@@ -0,0 +1,569 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CS40L50 Advanced Haptic Driver with waveform memory,
+ * integrated DSP, and closed-loop algorithms
+ *
+ * Copyright 2024 Cirrus Logic, Inc.
+ *
+ * Author: James Ogletree <james.ogletree@cirrus.com>
+ */
+
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/cs40l50.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+static const struct mfd_cell cs40l50_devs[] = {
+ { .name = "cs40l50-codec", },
+ { .name = "cs40l50-vibra", },
+};
+
+const struct regmap_config cs40l50_regmap = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+EXPORT_SYMBOL_GPL(cs40l50_regmap);
+
+static const char * const cs40l50_supplies[] = {
+ "vdd-io",
+};
+
+static const struct regmap_irq cs40l50_reg_irqs[] = {
+ REGMAP_IRQ_REG(CS40L50_DSP_QUEUE_IRQ, CS40L50_IRQ1_INT_2_OFFSET,
+ CS40L50_DSP_QUEUE_MASK),
+ REGMAP_IRQ_REG(CS40L50_AMP_SHORT_IRQ, CS40L50_IRQ1_INT_1_OFFSET,
+ CS40L50_AMP_SHORT_MASK),
+ REGMAP_IRQ_REG(CS40L50_TEMP_ERR_IRQ, CS40L50_IRQ1_INT_8_OFFSET,
+ CS40L50_TEMP_ERR_MASK),
+ REGMAP_IRQ_REG(CS40L50_BST_UVP_IRQ, CS40L50_IRQ1_INT_9_OFFSET,
+ CS40L50_BST_UVP_MASK),
+ REGMAP_IRQ_REG(CS40L50_BST_SHORT_IRQ, CS40L50_IRQ1_INT_9_OFFSET,
+ CS40L50_BST_SHORT_MASK),
+ REGMAP_IRQ_REG(CS40L50_BST_ILIMIT_IRQ, CS40L50_IRQ1_INT_9_OFFSET,
+ CS40L50_BST_ILIMIT_MASK),
+ REGMAP_IRQ_REG(CS40L50_UVLO_VDDBATT_IRQ, CS40L50_IRQ1_INT_10_OFFSET,
+ CS40L50_UVLO_VDDBATT_MASK),
+ REGMAP_IRQ_REG(CS40L50_GLOBAL_ERROR_IRQ, CS40L50_IRQ1_INT_18_OFFSET,
+ CS40L50_GLOBAL_ERROR_MASK),
+};
+
+static const struct regmap_irq_chip cs40l50_irq_chip = {
+ .name = "cs40l50",
+ .status_base = CS40L50_IRQ1_INT_1,
+ .mask_base = CS40L50_IRQ1_MASK_1,
+ .ack_base = CS40L50_IRQ1_INT_1,
+ .num_regs = 22,
+ .irqs = cs40l50_reg_irqs,
+ .num_irqs = ARRAY_SIZE(cs40l50_reg_irqs),
+ .runtime_pm = true,
+};
+
+int cs40l50_dsp_write(struct device *dev, struct regmap *regmap, u32 val)
+{
+ int i, ret;
+ u32 ack;
+
+ /* Device NAKs if hibernating, so optionally retry */
+ for (i = 0; i < CS40L50_DSP_TIMEOUT_COUNT; i++) {
+ ret = regmap_write(regmap, CS40L50_DSP_QUEUE, val);
+ if (!ret)
+ break;
+
+ usleep_range(CS40L50_DSP_POLL_US, CS40L50_DSP_POLL_US + 100);
+ }
+
+ /* If the write never took place, no need to check for the ACK */
+ if (i == CS40L50_DSP_TIMEOUT_COUNT) {
+ dev_err(dev, "Timed out writing %#X to DSP: %d\n", val, ret);
+ return ret;
+ }
+
+ ret = regmap_read_poll_timeout(regmap, CS40L50_DSP_QUEUE, ack, !ack,
+ CS40L50_DSP_POLL_US,
+ CS40L50_DSP_POLL_US * CS40L50_DSP_TIMEOUT_COUNT);
+ if (ret)
+ dev_err(dev, "DSP failed to ACK %#X: %d\n", val, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cs40l50_dsp_write);
+
+static const struct cs_dsp_region cs40l50_dsp_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = CS40L50_PMEM_0 },
+ { .type = WMFW_HALO_XM_PACKED, .base = CS40L50_XMEM_PACKED_0 },
+ { .type = WMFW_HALO_YM_PACKED, .base = CS40L50_YMEM_PACKED_0 },
+ { .type = WMFW_ADSP2_XM, .base = CS40L50_XMEM_UNPACKED24_0 },
+ { .type = WMFW_ADSP2_YM, .base = CS40L50_YMEM_UNPACKED24_0 },
+};
+
+static const struct reg_sequence cs40l50_internal_vamp_config[] = {
+ { CS40L50_BST_LPMODE_SEL, CS40L50_DCM_LOW_POWER },
+ { CS40L50_BLOCK_ENABLES2, CS40L50_OVERTEMP_WARN },
+};
+
+static const struct reg_sequence cs40l50_irq_mask_override[] = {
+ { CS40L50_IRQ1_MASK_2, CS40L50_IRQ_MASK_2_OVERRIDE },
+ { CS40L50_IRQ1_MASK_20, CS40L50_IRQ_MASK_20_OVERRIDE },
+};
+
+static int cs40l50_wseq_init(struct cs40l50 *cs40l50)
+{
+ struct cs_dsp *dsp = &cs40l50->dsp;
+
+ cs40l50->wseqs[CS40L50_STANDBY].ctl = cs_dsp_get_ctl(dsp, "STANDBY_SEQUENCE",
+ WMFW_ADSP2_XM,
+ CS40L50_PM_ALGO);
+ if (!cs40l50->wseqs[CS40L50_STANDBY].ctl) {
+ dev_err(cs40l50->dev, "Control not found for standby sequence\n");
+ return -ENOENT;
+ }
+
+ cs40l50->wseqs[CS40L50_ACTIVE].ctl = cs_dsp_get_ctl(dsp, "ACTIVE_SEQUENCE",
+ WMFW_ADSP2_XM,
+ CS40L50_PM_ALGO);
+ if (!cs40l50->wseqs[CS40L50_ACTIVE].ctl) {
+ dev_err(cs40l50->dev, "Control not found for active sequence\n");
+ return -ENOENT;
+ }
+
+ cs40l50->wseqs[CS40L50_PWR_ON].ctl = cs_dsp_get_ctl(dsp, "PM_PWR_ON_SEQ",
+ WMFW_ADSP2_XM,
+ CS40L50_PM_ALGO);
+ if (!cs40l50->wseqs[CS40L50_PWR_ON].ctl) {
+ dev_err(cs40l50->dev, "Control not found for power-on sequence\n");
+ return -ENOENT;
+ }
+
+ return cs_dsp_wseq_init(&cs40l50->dsp, cs40l50->wseqs, ARRAY_SIZE(cs40l50->wseqs));
+}
+
+static int cs40l50_dsp_config(struct cs40l50 *cs40l50)
+{
+ int ret;
+
+ /* Configure internal V_AMP supply */
+ ret = regmap_multi_reg_write(cs40l50->regmap, cs40l50_internal_vamp_config,
+ ARRAY_SIZE(cs40l50_internal_vamp_config));
+ if (ret)
+ return ret;
+
+ ret = cs_dsp_wseq_multi_write(&cs40l50->dsp, &cs40l50->wseqs[CS40L50_PWR_ON],
+ cs40l50_internal_vamp_config, CS_DSP_WSEQ_FULL,
+ ARRAY_SIZE(cs40l50_internal_vamp_config), false);
+ if (ret)
+ return ret;
+
+ /* Override firmware defaults for IRQ masks */
+ ret = regmap_multi_reg_write(cs40l50->regmap, cs40l50_irq_mask_override,
+ ARRAY_SIZE(cs40l50_irq_mask_override));
+ if (ret)
+ return ret;
+
+ return cs_dsp_wseq_multi_write(&cs40l50->dsp, &cs40l50->wseqs[CS40L50_PWR_ON],
+ cs40l50_irq_mask_override, CS_DSP_WSEQ_FULL,
+ ARRAY_SIZE(cs40l50_irq_mask_override), false);
+}
+
+static int cs40l50_dsp_post_run(struct cs_dsp *dsp)
+{
+ struct cs40l50 *cs40l50 = container_of(dsp, struct cs40l50, dsp);
+ int ret;
+
+ ret = cs40l50_wseq_init(cs40l50);
+ if (ret)
+ return ret;
+
+ ret = cs40l50_dsp_config(cs40l50);
+ if (ret) {
+ dev_err(cs40l50->dev, "Failed to configure DSP: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_mfd_add_devices(cs40l50->dev, PLATFORM_DEVID_NONE, cs40l50_devs,
+ ARRAY_SIZE(cs40l50_devs), NULL, 0, NULL);
+ if (ret)
+ dev_err(cs40l50->dev, "Failed to add child devices: %d\n", ret);
+
+ return ret;
+}
+
+static const struct cs_dsp_client_ops client_ops = {
+ .post_run = cs40l50_dsp_post_run,
+};
+
+static void cs40l50_dsp_remove(void *data)
+{
+ cs_dsp_remove(data);
+}
+
+static int cs40l50_dsp_init(struct cs40l50 *cs40l50)
+{
+ int ret;
+
+ cs40l50->dsp.num = 1;
+ cs40l50->dsp.type = WMFW_HALO;
+ cs40l50->dsp.dev = cs40l50->dev;
+ cs40l50->dsp.regmap = cs40l50->regmap;
+ cs40l50->dsp.base = CS40L50_CORE_BASE;
+ cs40l50->dsp.base_sysinfo = CS40L50_SYS_INFO_ID;
+ cs40l50->dsp.mem = cs40l50_dsp_regions;
+ cs40l50->dsp.num_mems = ARRAY_SIZE(cs40l50_dsp_regions);
+ cs40l50->dsp.no_core_startstop = true;
+ cs40l50->dsp.client_ops = &client_ops;
+
+ ret = cs_dsp_halo_init(&cs40l50->dsp);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(cs40l50->dev, cs40l50_dsp_remove,
+ &cs40l50->dsp);
+}
+
+static int cs40l50_reset_dsp(struct cs40l50 *cs40l50)
+{
+ int ret;
+
+ mutex_lock(&cs40l50->lock);
+
+ if (cs40l50->dsp.running)
+ cs_dsp_stop(&cs40l50->dsp);
+
+ if (cs40l50->dsp.booted)
+ cs_dsp_power_down(&cs40l50->dsp);
+
+ ret = cs40l50_dsp_write(cs40l50->dev, cs40l50->regmap, CS40L50_SHUTDOWN);
+ if (ret)
+ goto err_mutex;
+
+ ret = cs_dsp_power_up(&cs40l50->dsp, cs40l50->fw, "cs40l50.wmfw",
+ cs40l50->bin, "cs40l50.bin", "cs40l50");
+ if (ret)
+ goto err_mutex;
+
+ ret = cs40l50_dsp_write(cs40l50->dev, cs40l50->regmap, CS40L50_SYSTEM_RESET);
+ if (ret)
+ goto err_mutex;
+
+ ret = cs40l50_dsp_write(cs40l50->dev, cs40l50->regmap, CS40L50_PREVENT_HIBER);
+ if (ret)
+ goto err_mutex;
+
+ ret = cs_dsp_run(&cs40l50->dsp);
+err_mutex:
+ mutex_unlock(&cs40l50->lock);
+
+ return ret;
+}
+
+static void cs40l50_dsp_power_down(void *data)
+{
+ cs_dsp_power_down(data);
+}
+
+static void cs40l50_dsp_stop(void *data)
+{
+ cs_dsp_stop(data);
+}
+
+static void cs40l50_dsp_bringup(const struct firmware *bin, void *context)
+{
+ struct cs40l50 *cs40l50 = context;
+ u32 nwaves;
+ int ret;
+
+ /* Wavetable is optional; bringup DSP regardless */
+ cs40l50->bin = bin;
+
+ ret = cs40l50_reset_dsp(cs40l50);
+ if (ret) {
+ dev_err(cs40l50->dev, "Failed to reset DSP: %d\n", ret);
+ goto err_fw;
+ }
+
+ ret = regmap_read(cs40l50->regmap, CS40L50_NUM_WAVES, &nwaves);
+ if (ret)
+ goto err_fw;
+
+ dev_info(cs40l50->dev, "%u RAM effects loaded\n", nwaves);
+
+ /* Add teardown actions for first-time bringup */
+ ret = devm_add_action_or_reset(cs40l50->dev, cs40l50_dsp_power_down,
+ &cs40l50->dsp);
+ if (ret) {
+ dev_err(cs40l50->dev, "Failed to add power down action: %d\n", ret);
+ goto err_fw;
+ }
+
+ ret = devm_add_action_or_reset(cs40l50->dev, cs40l50_dsp_stop, &cs40l50->dsp);
+ if (ret)
+ dev_err(cs40l50->dev, "Failed to add stop action: %d\n", ret);
+err_fw:
+ release_firmware(cs40l50->bin);
+ release_firmware(cs40l50->fw);
+}
+
+static void cs40l50_request_firmware(const struct firmware *fw, void *context)
+{
+ struct cs40l50 *cs40l50 = context;
+ int ret;
+
+ if (!fw) {
+ dev_err(cs40l50->dev, "No firmware file found\n");
+ return;
+ }
+
+ cs40l50->fw = fw;
+
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, CS40L50_WT,
+ cs40l50->dev, GFP_KERNEL, cs40l50,
+ cs40l50_dsp_bringup);
+ if (ret) {
+ dev_err(cs40l50->dev, "Failed to request %s: %d\n", CS40L50_WT, ret);
+ release_firmware(cs40l50->fw);
+ }
+}
+
+struct cs40l50_irq {
+ const char *name;
+ int virq;
+};
+
+static struct cs40l50_irq cs40l50_irqs[] = {
+ { "DSP", },
+ { "Global", },
+ { "Boost UVLO", },
+ { "Boost current limit", },
+ { "Boost short", },
+ { "Boost undervolt", },
+ { "Overtemp", },
+ { "Amp short", },
+};
+
+static const struct reg_sequence cs40l50_err_rls[] = {
+ { CS40L50_ERR_RLS, CS40L50_GLOBAL_ERR_RLS_SET },
+ { CS40L50_ERR_RLS, CS40L50_GLOBAL_ERR_RLS_CLEAR },
+};
+
+static irqreturn_t cs40l50_hw_err(int irq, void *data)
+{
+ struct cs40l50 *cs40l50 = data;
+ int ret = 0, i;
+
+ mutex_lock(&cs40l50->lock);
+
+ /* Log hardware interrupt and execute error release sequence */
+ for (i = 1; i < ARRAY_SIZE(cs40l50_irqs); i++) {
+ if (cs40l50_irqs[i].virq == irq) {
+ dev_err(cs40l50->dev, "%s error\n", cs40l50_irqs[i].name);
+ ret = regmap_multi_reg_write(cs40l50->regmap, cs40l50_err_rls,
+ ARRAY_SIZE(cs40l50_err_rls));
+ break;
+ }
+ }
+
+ mutex_unlock(&cs40l50->lock);
+ return IRQ_RETVAL(!ret);
+}
+
+static irqreturn_t cs40l50_dsp_queue(int irq, void *data)
+{
+ struct cs40l50 *cs40l50 = data;
+ u32 rd_ptr, val, wt_ptr;
+ int ret = 0;
+
+ mutex_lock(&cs40l50->lock);
+
+ /* Read from DSP queue, log, and update read pointer */
+ while (!ret) {
+ ret = regmap_read(cs40l50->regmap, CS40L50_DSP_QUEUE_WT, &wt_ptr);
+ if (ret)
+ break;
+
+ ret = regmap_read(cs40l50->regmap, CS40L50_DSP_QUEUE_RD, &rd_ptr);
+ if (ret)
+ break;
+
+ /* Check if queue is empty */
+ if (wt_ptr == rd_ptr)
+ break;
+
+ ret = regmap_read(cs40l50->regmap, rd_ptr, &val);
+ if (ret)
+ break;
+
+ dev_dbg(cs40l50->dev, "DSP payload: %#X", val);
+
+ rd_ptr += sizeof(u32);
+
+ if (rd_ptr > CS40L50_DSP_QUEUE_END)
+ rd_ptr = CS40L50_DSP_QUEUE_BASE;
+
+ ret = regmap_write(cs40l50->regmap, CS40L50_DSP_QUEUE_RD, rd_ptr);
+ }
+
+ mutex_unlock(&cs40l50->lock);
+
+ return IRQ_RETVAL(!ret);
+}
+
+static int cs40l50_irq_init(struct cs40l50 *cs40l50)
+{
+ int ret, i, virq;
+
+ ret = devm_regmap_add_irq_chip(cs40l50->dev, cs40l50->regmap, cs40l50->irq,
+ IRQF_ONESHOT | IRQF_SHARED, 0,
+ &cs40l50_irq_chip, &cs40l50->irq_data);
+ if (ret) {
+ dev_err(cs40l50->dev, "Failed adding IRQ chip\n");
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs40l50_irqs); i++) {
+ virq = regmap_irq_get_virq(cs40l50->irq_data, i);
+ if (virq < 0) {
+ dev_err(cs40l50->dev, "Failed getting virq for %s\n",
+ cs40l50_irqs[i].name);
+ return virq;
+ }
+
+ cs40l50_irqs[i].virq = virq;
+
+ /* Handle DSP and hardware interrupts separately */
+ ret = devm_request_threaded_irq(cs40l50->dev, virq, NULL,
+ i ? cs40l50_hw_err : cs40l50_dsp_queue,
+ IRQF_ONESHOT | IRQF_SHARED,
+ cs40l50_irqs[i].name, cs40l50);
+ if (ret) {
+ return dev_err_probe(cs40l50->dev, ret,
+ "Failed requesting %s IRQ\n",
+ cs40l50_irqs[i].name);
+ }
+ }
+
+ return 0;
+}
+
+static int cs40l50_get_model(struct cs40l50 *cs40l50)
+{
+ int ret;
+
+ ret = regmap_read(cs40l50->regmap, CS40L50_DEVID, &cs40l50->devid);
+ if (ret)
+ return ret;
+
+ if (cs40l50->devid != CS40L50_DEVID_A)
+ return -EINVAL;
+
+ ret = regmap_read(cs40l50->regmap, CS40L50_REVID, &cs40l50->revid);
+ if (ret)
+ return ret;
+
+ if (cs40l50->revid < CS40L50_REVID_B0)
+ return -EINVAL;
+
+ dev_dbg(cs40l50->dev, "Cirrus Logic CS40L50 rev. %02X\n", cs40l50->revid);
+
+ return 0;
+}
+
+static int cs40l50_pm_runtime_setup(struct device *dev)
+{
+ int ret;
+
+ pm_runtime_set_autosuspend_delay(dev, CS40L50_AUTOSUSPEND_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_get_noresume(dev);
+ ret = pm_runtime_set_active(dev);
+ if (ret)
+ return ret;
+
+ return devm_pm_runtime_enable(dev);
+}
+
+int cs40l50_probe(struct cs40l50 *cs40l50)
+{
+ struct device *dev = cs40l50->dev;
+ int ret;
+
+ mutex_init(&cs40l50->lock);
+
+ cs40l50->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(cs40l50->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(cs40l50->reset_gpio),
+ "Failed getting reset GPIO\n");
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(cs40l50_supplies),
+ cs40l50_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed getting supplies\n");
+
+ /* Ensure minimum reset pulse width */
+ usleep_range(CS40L50_RESET_PULSE_US, CS40L50_RESET_PULSE_US + 100);
+
+ gpiod_set_value_cansleep(cs40l50->reset_gpio, 0);
+
+ /* Wait for control port to be ready */
+ usleep_range(CS40L50_CP_READY_US, CS40L50_CP_READY_US + 100);
+
+ ret = cs40l50_get_model(cs40l50);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get part number\n");
+
+ ret = cs40l50_dsp_init(cs40l50);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize DSP\n");
+
+ ret = cs40l50_pm_runtime_setup(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize runtime PM\n");
+
+ ret = cs40l50_irq_init(cs40l50);
+ if (ret)
+ return ret;
+
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, CS40L50_FW,
+ dev, GFP_KERNEL, cs40l50, cs40l50_request_firmware);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request %s\n", CS40L50_FW);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs40l50_probe);
+
+int cs40l50_remove(struct cs40l50 *cs40l50)
+{
+ gpiod_set_value_cansleep(cs40l50->reset_gpio, 1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs40l50_remove);
+
+static int cs40l50_runtime_suspend(struct device *dev)
+{
+ struct cs40l50 *cs40l50 = dev_get_drvdata(dev);
+
+ return regmap_write(cs40l50->regmap, CS40L50_DSP_QUEUE, CS40L50_ALLOW_HIBER);
+}
+
+static int cs40l50_runtime_resume(struct device *dev)
+{
+ struct cs40l50 *cs40l50 = dev_get_drvdata(dev);
+
+ return cs40l50_dsp_write(dev, cs40l50->regmap, CS40L50_PREVENT_HIBER);
+}
+
+EXPORT_GPL_DEV_PM_OPS(cs40l50_pm_ops) = {
+ RUNTIME_PM_OPS(cs40l50_runtime_suspend, cs40l50_runtime_resume, NULL)
+};
+
+MODULE_DESCRIPTION("CS40L50 Advanced Haptic Driver");
+MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("FW_CS_DSP");
diff --git a/drivers/mfd/cs40l50-i2c.c b/drivers/mfd/cs40l50-i2c.c
new file mode 100644
index 000000000000..639be743d956
--- /dev/null
+++ b/drivers/mfd/cs40l50-i2c.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CS40L50 Advanced Haptic Driver with waveform memory,
+ * integrated DSP, and closed-loop algorithms
+ *
+ * Copyright 2024 Cirrus Logic, Inc.
+ *
+ * Author: James Ogletree <james.ogletree@cirrus.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mfd/cs40l50.h>
+
+static int cs40l50_i2c_probe(struct i2c_client *i2c)
+{
+ struct cs40l50 *cs40l50;
+
+ cs40l50 = devm_kzalloc(&i2c->dev, sizeof(*cs40l50), GFP_KERNEL);
+ if (!cs40l50)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, cs40l50);
+
+ cs40l50->dev = &i2c->dev;
+ cs40l50->irq = i2c->irq;
+
+ cs40l50->regmap = devm_regmap_init_i2c(i2c, &cs40l50_regmap);
+ if (IS_ERR(cs40l50->regmap))
+ return dev_err_probe(cs40l50->dev, PTR_ERR(cs40l50->regmap),
+ "Failed to initialize register map\n");
+
+ return cs40l50_probe(cs40l50);
+}
+
+static void cs40l50_i2c_remove(struct i2c_client *i2c)
+{
+ struct cs40l50 *cs40l50 = i2c_get_clientdata(i2c);
+
+ cs40l50_remove(cs40l50);
+}
+
+static const struct i2c_device_id cs40l50_id_i2c[] = {
+ { "cs40l50" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs40l50_id_i2c);
+
+static const struct of_device_id cs40l50_of_match[] = {
+ { .compatible = "cirrus,cs40l50" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs40l50_of_match);
+
+static struct i2c_driver cs40l50_i2c_driver = {
+ .driver = {
+ .name = "cs40l50",
+ .of_match_table = cs40l50_of_match,
+ .pm = pm_ptr(&cs40l50_pm_ops),
+ },
+ .id_table = cs40l50_id_i2c,
+ .probe = cs40l50_i2c_probe,
+ .remove = cs40l50_i2c_remove,
+};
+module_i2c_driver(cs40l50_i2c_driver);
+
+MODULE_DESCRIPTION("CS40L50 I2C Driver");
+MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/cs40l50-spi.c b/drivers/mfd/cs40l50-spi.c
new file mode 100644
index 000000000000..53526b595a0d
--- /dev/null
+++ b/drivers/mfd/cs40l50-spi.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CS40L50 Advanced Haptic Driver with waveform memory,
+ * integrated DSP, and closed-loop algorithms
+ *
+ * Copyright 2024 Cirrus Logic, Inc.
+ *
+ * Author: James Ogletree <james.ogletree@cirrus.com>
+ */
+
+#include <linux/mfd/cs40l50.h>
+#include <linux/spi/spi.h>
+
+static int cs40l50_spi_probe(struct spi_device *spi)
+{
+ struct cs40l50 *cs40l50;
+
+ cs40l50 = devm_kzalloc(&spi->dev, sizeof(*cs40l50), GFP_KERNEL);
+ if (!cs40l50)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, cs40l50);
+
+ cs40l50->dev = &spi->dev;
+ cs40l50->irq = spi->irq;
+
+ cs40l50->regmap = devm_regmap_init_spi(spi, &cs40l50_regmap);
+ if (IS_ERR(cs40l50->regmap))
+ return dev_err_probe(cs40l50->dev, PTR_ERR(cs40l50->regmap),
+ "Failed to initialize register map\n");
+
+ return cs40l50_probe(cs40l50);
+}
+
+static void cs40l50_spi_remove(struct spi_device *spi)
+{
+ struct cs40l50 *cs40l50 = spi_get_drvdata(spi);
+
+ cs40l50_remove(cs40l50);
+}
+
+static const struct spi_device_id cs40l50_id_spi[] = {
+ { "cs40l50" },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, cs40l50_id_spi);
+
+static const struct of_device_id cs40l50_of_match[] = {
+ { .compatible = "cirrus,cs40l50" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs40l50_of_match);
+
+static struct spi_driver cs40l50_spi_driver = {
+ .driver = {
+ .name = "cs40l50",
+ .of_match_table = cs40l50_of_match,
+ .pm = pm_ptr(&cs40l50_pm_ops),
+ },
+ .id_table = cs40l50_id_spi,
+ .probe = cs40l50_spi_probe,
+ .remove = cs40l50_spi_remove,
+};
+module_spi_driver(cs40l50_spi_driver);
+
+MODULE_DESCRIPTION("CS40L50 SPI Driver");
+MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/cs42l43-i2c.c b/drivers/mfd/cs42l43-i2c.c
new file mode 100644
index 000000000000..a2ab001a600a
--- /dev/null
+++ b/drivers/mfd/cs42l43-i2c.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CS42L43 I2C driver
+ *
+ * Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#include <linux/array_size.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+#include "cs42l43.h"
+
+static const struct regmap_config cs42l43_i2c_regmap = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+
+ .max_register = CS42L43_MCU_RAM_MAX,
+ .readable_reg = cs42l43_readable_register,
+ .volatile_reg = cs42l43_volatile_register,
+ .precious_reg = cs42l43_precious_register,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = cs42l43_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(cs42l43_reg_default),
+};
+
+static int cs42l43_i2c_probe(struct i2c_client *i2c)
+{
+ struct cs42l43 *cs42l43;
+
+ cs42l43 = devm_kzalloc(&i2c->dev, sizeof(*cs42l43), GFP_KERNEL);
+ if (!cs42l43)
+ return -ENOMEM;
+
+ cs42l43->dev = &i2c->dev;
+ cs42l43->irq = i2c->irq;
+ /* A device on an I2C is always attached by definition. */
+ cs42l43->attached = true;
+
+ cs42l43->regmap = devm_regmap_init_i2c(i2c, &cs42l43_i2c_regmap);
+ if (IS_ERR(cs42l43->regmap))
+ return dev_err_probe(cs42l43->dev, PTR_ERR(cs42l43->regmap),
+ "Failed to allocate regmap\n");
+
+ return cs42l43_dev_probe(cs42l43);
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id cs42l43_of_match[] = {
+ { .compatible = "cirrus,cs42l43", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs42l43_of_match);
+#endif
+
+#if IS_ENABLED(CONFIG_ACPI)
+static const struct acpi_device_id cs42l43_acpi_match[] = {
+ { "CSC4243", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, cs42l43_acpi_match);
+#endif
+
+static struct i2c_driver cs42l43_i2c_driver = {
+ .driver = {
+ .name = "cs42l43",
+ .pm = pm_ptr(&cs42l43_pm_ops),
+ .of_match_table = of_match_ptr(cs42l43_of_match),
+ .acpi_match_table = ACPI_PTR(cs42l43_acpi_match),
+ },
+
+ .probe = cs42l43_i2c_probe,
+};
+module_i2c_driver(cs42l43_i2c_driver);
+
+MODULE_IMPORT_NS("MFD_CS42L43");
+
+MODULE_DESCRIPTION("CS42L43 I2C Driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/cs42l43-sdw.c b/drivers/mfd/cs42l43-sdw.c
new file mode 100644
index 000000000000..023f7e1a30f8
--- /dev/null
+++ b/drivers/mfd/cs42l43-sdw.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CS42L43 SoundWire driver
+ *
+ * Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#include <linux/array_size.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+
+#include "cs42l43.h"
+
+#define CS42L43_SDW_PORT(port, chans) { \
+ .num = port, \
+ .max_ch = chans, \
+ .type = SDW_DPN_FULL, \
+ .max_word = 24, \
+}
+
+static const struct regmap_config cs42l43_sdw_regmap = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+
+ .max_register = CS42L43_MCU_RAM_MAX,
+ .readable_reg = cs42l43_readable_register,
+ .volatile_reg = cs42l43_volatile_register,
+ .precious_reg = cs42l43_precious_register,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = cs42l43_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(cs42l43_reg_default),
+};
+
+static const struct sdw_dpn_prop cs42l43_src_port_props[] = {
+ CS42L43_SDW_PORT(1, 4),
+ CS42L43_SDW_PORT(2, 2),
+ CS42L43_SDW_PORT(3, 2),
+ CS42L43_SDW_PORT(4, 2),
+};
+
+static const struct sdw_dpn_prop cs42l43_sink_port_props[] = {
+ CS42L43_SDW_PORT(5, 2),
+ CS42L43_SDW_PORT(6, 2),
+ CS42L43_SDW_PORT(7, 2),
+};
+
+static int cs42l43_read_prop(struct sdw_slave *sdw)
+{
+ struct sdw_slave_prop *prop = &sdw->prop;
+ struct device *dev = &sdw->dev;
+ int i;
+
+ prop->use_domain_irq = true;
+ prop->paging_support = true;
+ prop->wake_capable = true;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY |
+ SDW_SCP_INT1_IMPL_DEF;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_src_port_props); i++)
+ prop->source_ports |= BIT(cs42l43_src_port_props[i].num);
+
+ prop->src_dpn_prop = devm_kmemdup(dev, cs42l43_src_port_props,
+ sizeof(cs42l43_src_port_props), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_sink_port_props); i++)
+ prop->sink_ports |= BIT(cs42l43_sink_port_props[i].num);
+
+ prop->sink_dpn_prop = devm_kmemdup(dev, cs42l43_sink_port_props,
+ sizeof(cs42l43_sink_port_props), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int cs42l43_sdw_update_status(struct sdw_slave *sdw, enum sdw_slave_status status)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev);
+
+ switch (status) {
+ case SDW_SLAVE_ATTACHED:
+ dev_dbg(cs42l43->dev, "Device attach\n");
+
+ sdw_write_no_pm(sdw, CS42L43_GEN_INT_MASK_1,
+ CS42L43_INT_STAT_GEN1_MASK);
+
+ cs42l43->attached = true;
+
+ complete(&cs42l43->device_attach);
+ break;
+ case SDW_SLAVE_UNATTACHED:
+ dev_dbg(cs42l43->dev, "Device detach\n");
+
+ cs42l43->attached = false;
+
+ reinit_completion(&cs42l43->device_attach);
+ complete(&cs42l43->device_detach);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l43_sdw_interrupt(struct sdw_slave *sdw,
+ struct sdw_slave_intr_status *status)
+{
+ /*
+ * The IRQ itself was handled through the regmap_irq handler, this is
+ * just clearing up the additional Cirrus SoundWire registers that are
+ * not covered by the SoundWire framework or the IRQ handler itself.
+ * There is only a single bit in GEN_INT_STAT_1 and it doesn't clear if
+ * IRQs are still pending so doing a read/write here after handling the
+ * IRQ is fine.
+ */
+ sdw_read_no_pm(sdw, CS42L43_GEN_INT_STAT_1);
+ sdw_write_no_pm(sdw, CS42L43_GEN_INT_STAT_1, CS42L43_INT_STAT_GEN1_MASK);
+
+ return 0;
+}
+
+static int cs42l43_sdw_bus_config(struct sdw_slave *sdw,
+ struct sdw_bus_params *params)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev);
+ int ret = 0;
+
+ mutex_lock(&cs42l43->pll_lock);
+
+ if (cs42l43->sdw_freq != params->curr_dr_freq / 2) {
+ if (cs42l43->sdw_pll_active) {
+ dev_err(cs42l43->dev,
+ "PLL active can't change SoundWire bus clock\n");
+ ret = -EBUSY;
+ } else {
+ cs42l43->sdw_freq = params->curr_dr_freq / 2;
+ }
+ }
+
+ mutex_unlock(&cs42l43->pll_lock);
+
+ return ret;
+}
+
+static const struct sdw_slave_ops cs42l43_sdw_ops = {
+ .read_prop = cs42l43_read_prop,
+ .update_status = cs42l43_sdw_update_status,
+ .interrupt_callback = cs42l43_sdw_interrupt,
+ .bus_config = cs42l43_sdw_bus_config,
+};
+
+static int cs42l43_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id)
+{
+ struct cs42l43 *cs42l43;
+ struct device *dev = &sdw->dev;
+
+ cs42l43 = devm_kzalloc(dev, sizeof(*cs42l43), GFP_KERNEL);
+ if (!cs42l43)
+ return -ENOMEM;
+
+ cs42l43->dev = dev;
+ cs42l43->sdw = sdw;
+
+ cs42l43->regmap = devm_regmap_init_sdw(sdw, &cs42l43_sdw_regmap);
+ if (IS_ERR(cs42l43->regmap))
+ return dev_err_probe(cs42l43->dev, PTR_ERR(cs42l43->regmap),
+ "Failed to allocate regmap\n");
+
+ return cs42l43_dev_probe(cs42l43);
+}
+
+static const struct sdw_device_id cs42l43_sdw_id[] = {
+ SDW_SLAVE_ENTRY(0x01FA, 0x4243, 0),
+ {}
+};
+MODULE_DEVICE_TABLE(sdw, cs42l43_sdw_id);
+
+static struct sdw_driver cs42l43_sdw_driver = {
+ .driver = {
+ .name = "cs42l43",
+ .pm = pm_ptr(&cs42l43_pm_ops),
+ },
+
+ .probe = cs42l43_sdw_probe,
+ .id_table = cs42l43_sdw_id,
+ .ops = &cs42l43_sdw_ops,
+};
+module_sdw_driver(cs42l43_sdw_driver);
+
+MODULE_IMPORT_NS("MFD_CS42L43");
+
+MODULE_DESCRIPTION("CS42L43 SoundWire Driver");
+MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/cs42l43.c b/drivers/mfd/cs42l43.c
new file mode 100644
index 000000000000..107cfb983fec
--- /dev/null
+++ b/drivers/mfd/cs42l43.c
@@ -0,0 +1,1249 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CS42L43 core driver
+ *
+ * Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitops.h>
+#include <linux/build_bug.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/types.h>
+
+#include "cs42l43.h"
+
+#define CS42L43_RESET_DELAY_MS 20
+
+#define CS42L43_SDW_ATTACH_TIMEOUT_MS 5000
+#define CS42L43_SDW_DETACH_TIMEOUT_MS 100
+
+#define CS42L43_MCU_BOOT_STAGE1 1
+#define CS42L43_MCU_BOOT_STAGE2 2
+#define CS42L43_MCU_BOOT_STAGE3 3
+#define CS42L43_MCU_BOOT_STAGE4 4
+#define CS42L43_MCU_POLL_US 5000
+#define CS42L43_MCU_CMD_TIMEOUT_US 20000
+#define CS42L43_MCU_UPDATE_FORMAT 3
+#define CS42L43_MCU_UPDATE_OFFSET 0x100000
+#define CS42L43_MCU_UPDATE_TIMEOUT_US 500000
+#define CS42L43_MCU_UPDATE_RETRIES 5
+
+#define CS42L43_MCU_ROM_REV 0x2001
+#define CS42L43_MCU_ROM_BIOS_REV 0x0000
+
+#define CS42L43_MCU_SUPPORTED_REV 0x2105
+#define CS42L43_MCU_SHADOW_REGS_REQUIRED_REV 0x2200
+#define CS42L43_BIOS_SHADOW_REGS_REQUIRED_REV 0x1002
+#define CS42L43_MCU_SUPPORTED_BIOS_REV 0x0001
+
+#define CS42L43_VDDP_DELAY_US 50
+#define CS42L43_VDDD_DELAY_US 1000
+
+#define CS42L43_AUTOSUSPEND_TIME_MS 250
+
+struct cs42l43_patch_header {
+ __le16 version;
+ __le16 size;
+ __u8 reserved;
+ __u8 secure;
+ __le16 bss_size;
+ __le32 apply_addr;
+ __le32 checksum;
+ __le32 sha;
+ __le16 swrev;
+ __le16 patchid;
+ __le16 ipxid;
+ __le16 romver;
+ __le32 load_addr;
+} __packed;
+
+static const struct reg_sequence cs42l43_reva_patch[] = {
+ { 0x4000, 0x00000055 },
+ { 0x4000, 0x000000AA },
+ { 0x10084, 0x00000000 },
+ { 0x1741C, 0x00CD2000 },
+ { 0x1718C, 0x00000003 },
+ { 0x4000, 0x00000000 },
+ { CS42L43_CCM_BLK_CLK_CONTROL, 0x00000002 },
+ { CS42L43_HPPATHVOL, 0x011B011B },
+ { CS42L43_OSC_DIV_SEL, 0x00000001 },
+ { CS42L43_DACCNFG2, 0x00000005 },
+ { CS42L43_MIC_DETECT_CONTROL_ANDROID, 0x80790079 },
+ { CS42L43_RELID, 0x0000000F },
+};
+
+const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS] = {
+ { CS42L43_DRV_CTRL1, 0x000186C0 },
+ { CS42L43_DRV_CTRL3, 0x286DB018 },
+ { CS42L43_DRV_CTRL4, 0x000006D8 },
+ { CS42L43_DRV_CTRL_5, 0x136C00C0 },
+ { CS42L43_GPIO_CTRL1, 0x00000707 },
+ { CS42L43_GPIO_CTRL2, 0x00000000 },
+ { CS42L43_GPIO_FN_SEL, 0x00000004 },
+ { CS42L43_MCLK_SRC_SEL, 0x00000000 },
+ { CS42L43_SAMPLE_RATE1, 0x00000003 },
+ { CS42L43_SAMPLE_RATE2, 0x00000003 },
+ { CS42L43_SAMPLE_RATE3, 0x00000003 },
+ { CS42L43_SAMPLE_RATE4, 0x00000003 },
+ { CS42L43_PLL_CONTROL, 0x00000000 },
+ { CS42L43_FS_SELECT1, 0x00000000 },
+ { CS42L43_FS_SELECT2, 0x00000000 },
+ { CS42L43_FS_SELECT3, 0x00000000 },
+ { CS42L43_FS_SELECT4, 0x00000000 },
+ { CS42L43_PDM_CONTROL, 0x00000000 },
+ { CS42L43_ASP_CLK_CONFIG1, 0x00010001 },
+ { CS42L43_ASP_CLK_CONFIG2, 0x00000000 },
+ { CS42L43_OSC_DIV_SEL, 0x00000001 },
+ { CS42L43_ADC_B_CTRL1, 0x00000000 },
+ { CS42L43_ADC_B_CTRL2, 0x00000000 },
+ { CS42L43_DECIM_HPF_WNF_CTRL1, 0x00000001 },
+ { CS42L43_DECIM_HPF_WNF_CTRL2, 0x00000001 },
+ { CS42L43_DECIM_HPF_WNF_CTRL3, 0x00000001 },
+ { CS42L43_DECIM_HPF_WNF_CTRL4, 0x00000001 },
+ { CS42L43_DMIC_PDM_CTRL, 0x00000000 },
+ { CS42L43_DECIM_VOL_CTRL_CH1_CH2, 0x20122012 },
+ { CS42L43_DECIM_VOL_CTRL_CH3_CH4, 0x20122012 },
+ { CS42L43_INTP_VOLUME_CTRL1, 0x00000180 },
+ { CS42L43_INTP_VOLUME_CTRL2, 0x00000180 },
+ { CS42L43_AMP1_2_VOL_RAMP, 0x00000022 },
+ { CS42L43_ASP_CTRL, 0x00000004 },
+ { CS42L43_ASP_FSYNC_CTRL1, 0x000000FA },
+ { CS42L43_ASP_FSYNC_CTRL2, 0x00000001 },
+ { CS42L43_ASP_FSYNC_CTRL3, 0x00000000 },
+ { CS42L43_ASP_FSYNC_CTRL4, 0x000001F4 },
+ { CS42L43_ASP_DATA_CTRL, 0x0000003A },
+ { CS42L43_ASP_RX_EN, 0x00000000 },
+ { CS42L43_ASP_TX_EN, 0x00000000 },
+ { CS42L43_ASP_RX_CH1_CTRL, 0x00170001 },
+ { CS42L43_ASP_RX_CH2_CTRL, 0x00170031 },
+ { CS42L43_ASP_RX_CH3_CTRL, 0x00170061 },
+ { CS42L43_ASP_RX_CH4_CTRL, 0x00170091 },
+ { CS42L43_ASP_RX_CH5_CTRL, 0x001700C1 },
+ { CS42L43_ASP_RX_CH6_CTRL, 0x001700F1 },
+ { CS42L43_ASP_TX_CH1_CTRL, 0x00170001 },
+ { CS42L43_ASP_TX_CH2_CTRL, 0x00170031 },
+ { CS42L43_ASP_TX_CH3_CTRL, 0x00170061 },
+ { CS42L43_ASP_TX_CH4_CTRL, 0x00170091 },
+ { CS42L43_ASP_TX_CH5_CTRL, 0x001700C1 },
+ { CS42L43_ASP_TX_CH6_CTRL, 0x001700F1 },
+ { CS42L43_ASPTX1_INPUT, 0x00000000 },
+ { CS42L43_ASPTX2_INPUT, 0x00000000 },
+ { CS42L43_ASPTX3_INPUT, 0x00000000 },
+ { CS42L43_ASPTX4_INPUT, 0x00000000 },
+ { CS42L43_ASPTX5_INPUT, 0x00000000 },
+ { CS42L43_ASPTX6_INPUT, 0x00000000 },
+ { CS42L43_SWIRE_DP1_CH1_INPUT, 0x00000000 },
+ { CS42L43_SWIRE_DP1_CH2_INPUT, 0x00000000 },
+ { CS42L43_SWIRE_DP1_CH3_INPUT, 0x00000000 },
+ { CS42L43_SWIRE_DP1_CH4_INPUT, 0x00000000 },
+ { CS42L43_SWIRE_DP2_CH1_INPUT, 0x00000000 },
+ { CS42L43_SWIRE_DP2_CH2_INPUT, 0x00000000 },
+ { CS42L43_SWIRE_DP3_CH1_INPUT, 0x00000000 },
+ { CS42L43_SWIRE_DP3_CH2_INPUT, 0x00000000 },
+ { CS42L43_SWIRE_DP4_CH1_INPUT, 0x00000000 },
+ { CS42L43_SWIRE_DP4_CH2_INPUT, 0x00000000 },
+ { CS42L43_ASRC_INT1_INPUT1, 0x00000000 },
+ { CS42L43_ASRC_INT2_INPUT1, 0x00000000 },
+ { CS42L43_ASRC_INT3_INPUT1, 0x00000000 },
+ { CS42L43_ASRC_INT4_INPUT1, 0x00000000 },
+ { CS42L43_ASRC_DEC1_INPUT1, 0x00000000 },
+ { CS42L43_ASRC_DEC2_INPUT1, 0x00000000 },
+ { CS42L43_ASRC_DEC3_INPUT1, 0x00000000 },
+ { CS42L43_ASRC_DEC4_INPUT1, 0x00000000 },
+ { CS42L43_ISRC1INT1_INPUT1, 0x00000000 },
+ { CS42L43_ISRC1INT2_INPUT1, 0x00000000 },
+ { CS42L43_ISRC1DEC1_INPUT1, 0x00000000 },
+ { CS42L43_ISRC1DEC2_INPUT1, 0x00000000 },
+ { CS42L43_ISRC2INT1_INPUT1, 0x00000000 },
+ { CS42L43_ISRC2INT2_INPUT1, 0x00000000 },
+ { CS42L43_ISRC2DEC1_INPUT1, 0x00000000 },
+ { CS42L43_ISRC2DEC2_INPUT1, 0x00000000 },
+ { CS42L43_EQ1MIX_INPUT1, 0x00800000 },
+ { CS42L43_EQ1MIX_INPUT2, 0x00800000 },
+ { CS42L43_EQ1MIX_INPUT3, 0x00800000 },
+ { CS42L43_EQ1MIX_INPUT4, 0x00800000 },
+ { CS42L43_EQ2MIX_INPUT1, 0x00800000 },
+ { CS42L43_EQ2MIX_INPUT2, 0x00800000 },
+ { CS42L43_EQ2MIX_INPUT3, 0x00800000 },
+ { CS42L43_EQ2MIX_INPUT4, 0x00800000 },
+ { CS42L43_SPDIF1_INPUT1, 0x00000000 },
+ { CS42L43_SPDIF2_INPUT1, 0x00000000 },
+ { CS42L43_AMP1MIX_INPUT1, 0x00800000 },
+ { CS42L43_AMP1MIX_INPUT2, 0x00800000 },
+ { CS42L43_AMP1MIX_INPUT3, 0x00800000 },
+ { CS42L43_AMP1MIX_INPUT4, 0x00800000 },
+ { CS42L43_AMP2MIX_INPUT1, 0x00800000 },
+ { CS42L43_AMP2MIX_INPUT2, 0x00800000 },
+ { CS42L43_AMP2MIX_INPUT3, 0x00800000 },
+ { CS42L43_AMP2MIX_INPUT4, 0x00800000 },
+ { CS42L43_AMP3MIX_INPUT1, 0x00800000 },
+ { CS42L43_AMP3MIX_INPUT2, 0x00800000 },
+ { CS42L43_AMP3MIX_INPUT3, 0x00800000 },
+ { CS42L43_AMP3MIX_INPUT4, 0x00800000 },
+ { CS42L43_AMP4MIX_INPUT1, 0x00800000 },
+ { CS42L43_AMP4MIX_INPUT2, 0x00800000 },
+ { CS42L43_AMP4MIX_INPUT3, 0x00800000 },
+ { CS42L43_AMP4MIX_INPUT4, 0x00800000 },
+ { CS42L43_ASRC_INT_ENABLES, 0x00000100 },
+ { CS42L43_ASRC_DEC_ENABLES, 0x00000100 },
+ { CS42L43_PDNCNTL, 0x00000000 },
+ { CS42L43_RINGSENSE_DEB_CTRL, 0x0000001B },
+ { CS42L43_TIPSENSE_DEB_CTRL, 0x0000001B },
+ { CS42L43_HS2, 0x050106F3 },
+ { CS42L43_STEREO_MIC_CTRL, 0x00000000 },
+ { CS42L43_STEREO_MIC_CLAMP_CTRL, 0x00000001 },
+ { CS42L43_BLOCK_EN2, 0x00000000 },
+ { CS42L43_BLOCK_EN3, 0x00000000 },
+ { CS42L43_BLOCK_EN4, 0x00000000 },
+ { CS42L43_BLOCK_EN5, 0x00000000 },
+ { CS42L43_BLOCK_EN6, 0x00000000 },
+ { CS42L43_BLOCK_EN7, 0x00000000 },
+ { CS42L43_BLOCK_EN8, 0x00000000 },
+ { CS42L43_BLOCK_EN9, 0x00000000 },
+ { CS42L43_BLOCK_EN10, 0x00000000 },
+ { CS42L43_BLOCK_EN11, 0x00000000 },
+ { CS42L43_TONE_CH1_CTRL, 0x00000000 },
+ { CS42L43_TONE_CH2_CTRL, 0x00000000 },
+ { CS42L43_MIC_DETECT_CONTROL_1, 0x00000003 },
+ { CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, 0x02000003 },
+ { CS42L43_MIC_DETECT_CONTROL_ANDROID, 0x80790079 },
+ { CS42L43_ISRC1_CTRL, 0x00000000 },
+ { CS42L43_ISRC2_CTRL, 0x00000000 },
+ { CS42L43_CTRL_REG, 0x00000006 },
+ { CS42L43_FDIV_FRAC, 0x40000000 },
+ { CS42L43_CAL_RATIO, 0x00000080 },
+ { CS42L43_SPI_CLK_CONFIG1, 0x00000001 },
+ { CS42L43_SPI_CONFIG1, 0x00000000 },
+ { CS42L43_SPI_CONFIG2, 0x00000000 },
+ { CS42L43_SPI_CONFIG3, 0x00000001 },
+ { CS42L43_SPI_CONFIG4, 0x00000000 },
+ { CS42L43_TRAN_CONFIG3, 0x00000000 },
+ { CS42L43_TRAN_CONFIG4, 0x00000000 },
+ { CS42L43_TRAN_CONFIG5, 0x00000000 },
+ { CS42L43_TRAN_CONFIG6, 0x00000000 },
+ { CS42L43_TRAN_CONFIG7, 0x00000000 },
+ { CS42L43_TRAN_CONFIG8, 0x00000000 },
+ { CS42L43_DACCNFG1, 0x00000008 },
+ { CS42L43_DACCNFG2, 0x00000005 },
+ { CS42L43_HPPATHVOL, 0x011B011B },
+ { CS42L43_PGAVOL, 0x00003470 },
+ { CS42L43_LOADDETENA, 0x00000000 },
+ { CS42L43_CTRL, 0x00000037 },
+ { CS42L43_COEFF_DATA_IN0, 0x00000000 },
+ { CS42L43_COEFF_RD_WR0, 0x00000000 },
+ { CS42L43_START_EQZ0, 0x00000000 },
+ { CS42L43_MUTE_EQ_IN0, 0x00000000 },
+ { CS42L43_DECIM_MASK, 0x0000000F },
+ { CS42L43_EQ_MIX_MASK, 0x0000000F },
+ { CS42L43_ASP_MASK, 0x000000FF },
+ { CS42L43_PLL_MASK, 0x00000003 },
+ { CS42L43_SOFT_MASK, 0x0000FFFF },
+ { CS42L43_SWIRE_MASK, 0x00007FFF },
+ { CS42L43_MSM_MASK, 0x00000FFF },
+ { CS42L43_ACC_DET_MASK, 0x00000FFF },
+ { CS42L43_I2C_TGT_MASK, 0x00000003 },
+ { CS42L43_SPI_MSTR_MASK, 0x00000007 },
+ { CS42L43_SW_TO_SPI_BRIDGE_MASK, 0x00000001 },
+ { CS42L43_OTP_MASK, 0x00000007 },
+ { CS42L43_CLASS_D_AMP_MASK, 0x00003FFF },
+ { CS42L43_GPIO_INT_MASK, 0x0000003F },
+ { CS42L43_ASRC_MASK, 0x0000000F },
+ { CS42L43_HPOUT_MASK, 0x00000003 },
+};
+EXPORT_SYMBOL_NS_GPL(cs42l43_reg_default, "MFD_CS42L43");
+
+bool cs42l43_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L43_DEVID:
+ case CS42L43_REVID:
+ case CS42L43_RELID:
+ case CS42L43_SFT_RESET:
+ case CS42L43_DRV_CTRL1:
+ case CS42L43_DRV_CTRL3:
+ case CS42L43_DRV_CTRL4:
+ case CS42L43_DRV_CTRL_5:
+ case CS42L43_GPIO_CTRL1:
+ case CS42L43_GPIO_CTRL2:
+ case CS42L43_GPIO_STS:
+ case CS42L43_GPIO_FN_SEL:
+ case CS42L43_MCLK_SRC_SEL:
+ case CS42L43_SAMPLE_RATE1 ... CS42L43_SAMPLE_RATE4:
+ case CS42L43_PLL_CONTROL:
+ case CS42L43_FS_SELECT1 ... CS42L43_FS_SELECT4:
+ case CS42L43_PDM_CONTROL:
+ case CS42L43_ASP_CLK_CONFIG1 ... CS42L43_ASP_CLK_CONFIG2:
+ case CS42L43_OSC_DIV_SEL:
+ case CS42L43_ADC_B_CTRL1 ... CS42L43_ADC_B_CTRL2:
+ case CS42L43_DECIM_HPF_WNF_CTRL1 ... CS42L43_DECIM_HPF_WNF_CTRL4:
+ case CS42L43_DMIC_PDM_CTRL:
+ case CS42L43_DECIM_VOL_CTRL_CH1_CH2 ... CS42L43_DECIM_VOL_CTRL_CH3_CH4:
+ case CS42L43_INTP_VOLUME_CTRL1 ... CS42L43_INTP_VOLUME_CTRL2:
+ case CS42L43_AMP1_2_VOL_RAMP:
+ case CS42L43_ASP_CTRL:
+ case CS42L43_ASP_FSYNC_CTRL1 ... CS42L43_ASP_FSYNC_CTRL4:
+ case CS42L43_ASP_DATA_CTRL:
+ case CS42L43_ASP_RX_EN ... CS42L43_ASP_TX_EN:
+ case CS42L43_ASP_RX_CH1_CTRL ... CS42L43_ASP_RX_CH6_CTRL:
+ case CS42L43_ASP_TX_CH1_CTRL ... CS42L43_ASP_TX_CH6_CTRL:
+ case CS42L43_OTP_REVISION_ID:
+ case CS42L43_ASPTX1_INPUT:
+ case CS42L43_ASPTX2_INPUT:
+ case CS42L43_ASPTX3_INPUT:
+ case CS42L43_ASPTX4_INPUT:
+ case CS42L43_ASPTX5_INPUT:
+ case CS42L43_ASPTX6_INPUT:
+ case CS42L43_SWIRE_DP1_CH1_INPUT:
+ case CS42L43_SWIRE_DP1_CH2_INPUT:
+ case CS42L43_SWIRE_DP1_CH3_INPUT:
+ case CS42L43_SWIRE_DP1_CH4_INPUT:
+ case CS42L43_SWIRE_DP2_CH1_INPUT:
+ case CS42L43_SWIRE_DP2_CH2_INPUT:
+ case CS42L43_SWIRE_DP3_CH1_INPUT:
+ case CS42L43_SWIRE_DP3_CH2_INPUT:
+ case CS42L43_SWIRE_DP4_CH1_INPUT:
+ case CS42L43_SWIRE_DP4_CH2_INPUT:
+ case CS42L43_ASRC_INT1_INPUT1:
+ case CS42L43_ASRC_INT2_INPUT1:
+ case CS42L43_ASRC_INT3_INPUT1:
+ case CS42L43_ASRC_INT4_INPUT1:
+ case CS42L43_ASRC_DEC1_INPUT1:
+ case CS42L43_ASRC_DEC2_INPUT1:
+ case CS42L43_ASRC_DEC3_INPUT1:
+ case CS42L43_ASRC_DEC4_INPUT1:
+ case CS42L43_ISRC1INT1_INPUT1:
+ case CS42L43_ISRC1INT2_INPUT1:
+ case CS42L43_ISRC1DEC1_INPUT1:
+ case CS42L43_ISRC1DEC2_INPUT1:
+ case CS42L43_ISRC2INT1_INPUT1:
+ case CS42L43_ISRC2INT2_INPUT1:
+ case CS42L43_ISRC2DEC1_INPUT1:
+ case CS42L43_ISRC2DEC2_INPUT1:
+ case CS42L43_EQ1MIX_INPUT1 ... CS42L43_EQ1MIX_INPUT4:
+ case CS42L43_EQ2MIX_INPUT1 ... CS42L43_EQ2MIX_INPUT4:
+ case CS42L43_SPDIF1_INPUT1:
+ case CS42L43_SPDIF2_INPUT1:
+ case CS42L43_AMP1MIX_INPUT1 ... CS42L43_AMP1MIX_INPUT4:
+ case CS42L43_AMP2MIX_INPUT1 ... CS42L43_AMP2MIX_INPUT4:
+ case CS42L43_AMP3MIX_INPUT1 ... CS42L43_AMP3MIX_INPUT4:
+ case CS42L43_AMP4MIX_INPUT1 ... CS42L43_AMP4MIX_INPUT4:
+ case CS42L43_ASRC_INT_ENABLES ... CS42L43_ASRC_DEC_ENABLES:
+ case CS42L43_PDNCNTL:
+ case CS42L43_RINGSENSE_DEB_CTRL:
+ case CS42L43_TIPSENSE_DEB_CTRL:
+ case CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS:
+ case CS42L43_HS2:
+ case CS42L43_HS_STAT:
+ case CS42L43_MCU_SW_INTERRUPT:
+ case CS42L43_STEREO_MIC_CTRL:
+ case CS42L43_STEREO_MIC_CLAMP_CTRL:
+ case CS42L43_BLOCK_EN2 ... CS42L43_BLOCK_EN11:
+ case CS42L43_TONE_CH1_CTRL ... CS42L43_TONE_CH2_CTRL:
+ case CS42L43_MIC_DETECT_CONTROL_1:
+ case CS42L43_DETECT_STATUS_1:
+ case CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL:
+ case CS42L43_MIC_DETECT_CONTROL_ANDROID:
+ case CS42L43_ISRC1_CTRL:
+ case CS42L43_ISRC2_CTRL:
+ case CS42L43_CTRL_REG:
+ case CS42L43_FDIV_FRAC:
+ case CS42L43_CAL_RATIO:
+ case CS42L43_SPI_CLK_CONFIG1:
+ case CS42L43_SPI_CONFIG1 ... CS42L43_SPI_CONFIG4:
+ case CS42L43_SPI_STATUS1 ... CS42L43_SPI_STATUS2:
+ case CS42L43_TRAN_CONFIG1 ... CS42L43_TRAN_CONFIG8:
+ case CS42L43_TRAN_STATUS1 ... CS42L43_TRAN_STATUS3:
+ case CS42L43_TX_DATA:
+ case CS42L43_RX_DATA:
+ case CS42L43_DACCNFG1 ... CS42L43_DACCNFG2:
+ case CS42L43_HPPATHVOL:
+ case CS42L43_PGAVOL:
+ case CS42L43_LOADDETRESULTS:
+ case CS42L43_LOADDETENA:
+ case CS42L43_CTRL:
+ case CS42L43_COEFF_DATA_IN0:
+ case CS42L43_COEFF_RD_WR0:
+ case CS42L43_INIT_DONE0:
+ case CS42L43_START_EQZ0:
+ case CS42L43_MUTE_EQ_IN0:
+ case CS42L43_DECIM_INT ... CS42L43_HPOUT_INT:
+ case CS42L43_DECIM_MASK ... CS42L43_HPOUT_MASK:
+ case CS42L43_DECIM_INT_SHADOW ... CS42L43_HP_OUT_SHADOW:
+ case CS42L43_BOOT_CONTROL:
+ case CS42L43_BLOCK_EN:
+ case CS42L43_SHUTTER_CONTROL:
+ case CS42L43_MCU_SW_REV ... CS42L43_MCU_RAM_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(cs42l43_readable_register, "MFD_CS42L43");
+
+bool cs42l43_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L43_SFT_RESET:
+ case CS42L43_TX_DATA:
+ case CS42L43_RX_DATA:
+ case CS42L43_DECIM_INT ... CS42L43_HPOUT_INT:
+ case CS42L43_MCU_SW_REV ... CS42L43_MCU_RAM_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(cs42l43_precious_register, "MFD_CS42L43");
+
+bool cs42l43_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L43_DEVID:
+ case CS42L43_REVID:
+ case CS42L43_RELID:
+ case CS42L43_GPIO_STS:
+ case CS42L43_OTP_REVISION_ID:
+ case CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS:
+ case CS42L43_HS_STAT:
+ case CS42L43_MCU_SW_INTERRUPT:
+ case CS42L43_DETECT_STATUS_1:
+ case CS42L43_SPI_STATUS1 ... CS42L43_SPI_STATUS2:
+ case CS42L43_TRAN_CONFIG1 ... CS42L43_TRAN_CONFIG2:
+ case CS42L43_TRAN_CONFIG8:
+ case CS42L43_TRAN_STATUS1 ... CS42L43_TRAN_STATUS3:
+ case CS42L43_LOADDETRESULTS:
+ case CS42L43_INIT_DONE0:
+ case CS42L43_DECIM_INT_SHADOW ... CS42L43_HP_OUT_SHADOW:
+ case CS42L43_BOOT_CONTROL:
+ case CS42L43_BLOCK_EN:
+ return true;
+ default:
+ return cs42l43_precious_register(dev, reg);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(cs42l43_volatile_register, "MFD_CS42L43");
+
+#define CS42L43_IRQ_OFFSET(reg) ((CS42L43_##reg##_INT) - CS42L43_DECIM_INT)
+
+#define CS42L43_IRQ_REG(name, reg) REGMAP_IRQ_REG(CS42L43_##name, \
+ CS42L43_IRQ_OFFSET(reg), \
+ CS42L43_##name##_INT_MASK)
+
+static const struct regmap_irq cs42l43_regmap_irqs[] = {
+ CS42L43_IRQ_REG(PLL_LOST_LOCK, PLL),
+ CS42L43_IRQ_REG(PLL_READY, PLL),
+
+ CS42L43_IRQ_REG(HP_STARTUP_DONE, MSM),
+ CS42L43_IRQ_REG(HP_SHUTDOWN_DONE, MSM),
+ CS42L43_IRQ_REG(HSDET_DONE, MSM),
+ CS42L43_IRQ_REG(TIPSENSE_UNPLUG_DB, MSM),
+ CS42L43_IRQ_REG(TIPSENSE_PLUG_DB, MSM),
+ CS42L43_IRQ_REG(RINGSENSE_UNPLUG_DB, MSM),
+ CS42L43_IRQ_REG(RINGSENSE_PLUG_DB, MSM),
+ CS42L43_IRQ_REG(TIPSENSE_UNPLUG_PDET, MSM),
+ CS42L43_IRQ_REG(TIPSENSE_PLUG_PDET, MSM),
+ CS42L43_IRQ_REG(RINGSENSE_UNPLUG_PDET, MSM),
+ CS42L43_IRQ_REG(RINGSENSE_PLUG_PDET, MSM),
+
+ CS42L43_IRQ_REG(HS2_BIAS_SENSE, ACC_DET),
+ CS42L43_IRQ_REG(HS1_BIAS_SENSE, ACC_DET),
+ CS42L43_IRQ_REG(DC_DETECT1_FALSE, ACC_DET),
+ CS42L43_IRQ_REG(DC_DETECT1_TRUE, ACC_DET),
+ CS42L43_IRQ_REG(HSBIAS_CLAMPED, ACC_DET),
+ CS42L43_IRQ_REG(HS3_4_BIAS_SENSE, ACC_DET),
+
+ CS42L43_IRQ_REG(AMP2_CLK_STOP_FAULT, CLASS_D_AMP),
+ CS42L43_IRQ_REG(AMP1_CLK_STOP_FAULT, CLASS_D_AMP),
+ CS42L43_IRQ_REG(AMP2_VDDSPK_FAULT, CLASS_D_AMP),
+ CS42L43_IRQ_REG(AMP1_VDDSPK_FAULT, CLASS_D_AMP),
+ CS42L43_IRQ_REG(AMP2_SHUTDOWN_DONE, CLASS_D_AMP),
+ CS42L43_IRQ_REG(AMP1_SHUTDOWN_DONE, CLASS_D_AMP),
+ CS42L43_IRQ_REG(AMP2_STARTUP_DONE, CLASS_D_AMP),
+ CS42L43_IRQ_REG(AMP1_STARTUP_DONE, CLASS_D_AMP),
+ CS42L43_IRQ_REG(AMP2_THERM_SHDN, CLASS_D_AMP),
+ CS42L43_IRQ_REG(AMP1_THERM_SHDN, CLASS_D_AMP),
+ CS42L43_IRQ_REG(AMP2_THERM_WARN, CLASS_D_AMP),
+ CS42L43_IRQ_REG(AMP1_THERM_WARN, CLASS_D_AMP),
+ CS42L43_IRQ_REG(AMP2_SCDET, CLASS_D_AMP),
+ CS42L43_IRQ_REG(AMP1_SCDET, CLASS_D_AMP),
+
+ CS42L43_IRQ_REG(GPIO3_FALL, GPIO),
+ CS42L43_IRQ_REG(GPIO3_RISE, GPIO),
+ CS42L43_IRQ_REG(GPIO2_FALL, GPIO),
+ CS42L43_IRQ_REG(GPIO2_RISE, GPIO),
+ CS42L43_IRQ_REG(GPIO1_FALL, GPIO),
+ CS42L43_IRQ_REG(GPIO1_RISE, GPIO),
+
+ CS42L43_IRQ_REG(HP_ILIMIT, HPOUT),
+ CS42L43_IRQ_REG(HP_LOADDET_DONE, HPOUT),
+};
+
+static const struct regmap_irq_chip cs42l43_irq_chip = {
+ .name = "cs42l43",
+
+ .status_base = CS42L43_DECIM_INT,
+ .mask_base = CS42L43_DECIM_MASK,
+ .num_regs = 16,
+
+ .irqs = cs42l43_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(cs42l43_regmap_irqs),
+
+ .runtime_pm = true,
+};
+
+static const char * const cs42l43_core_supplies[] = {
+ "vdd-a", "vdd-io", "vdd-cp",
+};
+
+static const char * const cs42l43_parent_supplies[] = { "vdd-amp" };
+
+static const struct mfd_cell cs42l43_devs[] = {
+ { .name = "cs42l43-pinctrl", },
+ { .name = "cs42l43-spi", },
+ {
+ .name = "cs42l43-codec",
+ .parent_supplies = cs42l43_parent_supplies,
+ .num_parent_supplies = ARRAY_SIZE(cs42l43_parent_supplies),
+ },
+};
+
+/*
+ * If the device is connected over Soundwire, as well as soft resetting the
+ * device, this function will also way for the device to detach from the bus
+ * before returning.
+ */
+static int cs42l43_soft_reset(struct cs42l43 *cs42l43)
+{
+ static const struct reg_sequence reset[] = {
+ { CS42L43_SFT_RESET, CS42L43_SFT_RESET_VAL },
+ };
+
+ reinit_completion(&cs42l43->device_detach);
+
+ /*
+ * Apply cache only because the soft reset will cause the device to
+ * detach from the soundwire bus.
+ */
+ regcache_cache_only(cs42l43->regmap, true);
+ regmap_multi_reg_write_bypassed(cs42l43->regmap, reset, ARRAY_SIZE(reset));
+
+ msleep(CS42L43_RESET_DELAY_MS);
+
+ if (cs42l43->sdw) {
+ unsigned long timeout = msecs_to_jiffies(CS42L43_SDW_DETACH_TIMEOUT_MS);
+ unsigned long time;
+
+ time = wait_for_completion_timeout(&cs42l43->device_detach, timeout);
+ if (!time) {
+ dev_err(cs42l43->dev, "Timed out waiting for device detach\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return -EAGAIN;
+}
+
+/*
+ * This function is essentially a no-op on I2C, but will wait for the device to
+ * attach when the device is used on a SoundWire bus.
+ */
+static int cs42l43_wait_for_attach(struct cs42l43 *cs42l43)
+{
+ if (!cs42l43->attached) {
+ unsigned long timeout = msecs_to_jiffies(CS42L43_SDW_ATTACH_TIMEOUT_MS);
+ unsigned long time;
+
+ time = wait_for_completion_timeout(&cs42l43->device_attach, timeout);
+ if (!time) {
+ dev_err(cs42l43->dev, "Timed out waiting for device re-attach\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ regcache_cache_only(cs42l43->regmap, false);
+
+ /* The hardware requires enabling OSC_DIV before doing any SoundWire reads. */
+ if (cs42l43->sdw)
+ regmap_write(cs42l43->regmap, CS42L43_OSC_DIV_SEL,
+ CS42L43_OSC_DIV2_EN_MASK);
+
+ return 0;
+}
+
+/*
+ * This function will advance the firmware into boot stage 3 from boot stage 2.
+ * Boot stage 3 is required to send commands to the firmware. This is achieved
+ * by setting the firmware NEED configuration register to zero, this indicates
+ * no configuration is required forcing the firmware to advance to boot stage 3.
+ *
+ * Later revisions of the firmware require the use of an alternative register
+ * for this purpose, which is indicated through the shadow flag.
+ */
+static int cs42l43_mcu_stage_2_3(struct cs42l43 *cs42l43, bool shadow)
+{
+ unsigned int need_reg = CS42L43_NEED_CONFIGS;
+ unsigned int val;
+ int ret;
+
+ if (shadow)
+ need_reg = CS42L43_FW_SH_BOOT_CFG_NEED_CONFIGS;
+
+ regmap_write(cs42l43->regmap, need_reg, 0);
+
+ ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_BOOT_STATUS,
+ val, (val == CS42L43_MCU_BOOT_STAGE3),
+ CS42L43_MCU_POLL_US, CS42L43_MCU_CMD_TIMEOUT_US);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to move to stage 3: %d, 0x%x\n", ret, val);
+ return ret;
+ }
+
+ return -EAGAIN;
+}
+
+/*
+ * This function will return the firmware to boot stage 2 from boot stage 3.
+ * Boot stage 2 is required to apply updates to the firmware. This is achieved
+ * by setting the firmware NEED configuration register to FW_PATCH_NEED_CFG,
+ * setting the HAVE configuration register to 0, and soft resetting. The
+ * firmware will see it is missing a patch configuration and will pause in boot
+ * stage 2.
+ *
+ * Note: Unlike cs42l43_mcu_stage_2_3 there is no need to consider the shadow
+ * register here as the driver will only return to boot stage 2 if the firmware
+ * requires update which means the revision does not include shadow register
+ * support.
+ */
+static int cs42l43_mcu_stage_3_2(struct cs42l43 *cs42l43)
+{
+ regmap_write(cs42l43->regmap, CS42L43_FW_MISSION_CTRL_NEED_CONFIGS,
+ CS42L43_FW_PATCH_NEED_CFG_MASK);
+ regmap_write(cs42l43->regmap, CS42L43_FW_MISSION_CTRL_HAVE_CONFIGS, 0);
+
+ return cs42l43_soft_reset(cs42l43);
+}
+
+/*
+ * Disable the firmware running on the device such that the driver can access
+ * the registers without fear of the MCU changing them under it.
+ */
+static int cs42l43_mcu_disable(struct cs42l43 *cs42l43)
+{
+ unsigned int val;
+ int ret;
+
+ regmap_write(cs42l43->regmap, CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_REG,
+ CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_DISABLE_VAL);
+ regmap_write(cs42l43->regmap, CS42L43_FW_MISSION_CTRL_MM_CTRL_SELECTION,
+ CS42L43_FW_MM_CTRL_MCU_SEL_MASK);
+ regmap_write(cs42l43->regmap, CS42L43_MCU_SW_INTERRUPT, CS42L43_CONTROL_IND_MASK);
+ regmap_write(cs42l43->regmap, CS42L43_MCU_SW_INTERRUPT, 0);
+
+ ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_SOFT_INT_SHADOW, val,
+ (val & CS42L43_CONTROL_APPLIED_INT_MASK),
+ CS42L43_MCU_POLL_US, CS42L43_MCU_CMD_TIMEOUT_US);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to disable firmware: %d, 0x%x\n", ret, val);
+ return ret;
+ }
+
+ /* Soft reset to clear any register state the firmware left behind. */
+ return cs42l43_soft_reset(cs42l43);
+}
+
+/*
+ * Callback to load firmware updates.
+ */
+static void cs42l43_mcu_load_firmware(const struct firmware *firmware, void *context)
+{
+ struct cs42l43 *cs42l43 = context;
+ const struct cs42l43_patch_header *hdr;
+ unsigned int loadaddr, val;
+ int ret;
+
+ if (!firmware) {
+ dev_err(cs42l43->dev, "Failed to load firmware\n");
+ cs42l43->firmware_error = -ENODEV;
+ goto err;
+ }
+
+ hdr = (const struct cs42l43_patch_header *)&firmware->data[0];
+ loadaddr = le32_to_cpu(hdr->load_addr);
+
+ if (le16_to_cpu(hdr->version) != CS42L43_MCU_UPDATE_FORMAT) {
+ dev_err(cs42l43->dev, "Bad firmware file format: %d\n", hdr->version);
+ cs42l43->firmware_error = -EINVAL;
+ goto err_release;
+ }
+
+ regmap_write(cs42l43->regmap, CS42L43_PATCH_START_ADDR, loadaddr);
+ regmap_bulk_write(cs42l43->regmap, loadaddr + CS42L43_MCU_UPDATE_OFFSET,
+ &firmware->data[0], firmware->size / sizeof(u32));
+
+ regmap_write(cs42l43->regmap, CS42L43_MCU_SW_INTERRUPT, CS42L43_PATCH_IND_MASK);
+ regmap_write(cs42l43->regmap, CS42L43_MCU_SW_INTERRUPT, 0);
+
+ ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_SOFT_INT_SHADOW, val,
+ (val & CS42L43_PATCH_APPLIED_INT_MASK),
+ CS42L43_MCU_POLL_US, CS42L43_MCU_UPDATE_TIMEOUT_US);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to update firmware: %d, 0x%x\n", ret, val);
+ cs42l43->firmware_error = ret;
+ goto err_release;
+ }
+
+err_release:
+ release_firmware(firmware);
+err:
+ complete(&cs42l43->firmware_download);
+}
+
+static int cs42l43_mcu_is_hw_compatible(struct cs42l43 *cs42l43,
+ unsigned int mcu_rev,
+ unsigned int bios_rev)
+{
+ /*
+ * The firmware has two revision numbers bringing either of them up to a
+ * supported version will provide the disable the driver requires.
+ */
+ if (mcu_rev < CS42L43_MCU_SUPPORTED_REV &&
+ bios_rev < CS42L43_MCU_SUPPORTED_BIOS_REV) {
+ dev_err(cs42l43->dev, "Firmware too old to support disable\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * The process of updating the firmware is split into a series of steps, at the
+ * end of each step a soft reset of the device might be required which will
+ * require the driver to wait for the device to re-attach on the SoundWire bus,
+ * if that control bus is being used.
+ */
+static int cs42l43_mcu_update_step(struct cs42l43 *cs42l43)
+{
+ unsigned int mcu_rev, bios_rev, boot_status, secure_cfg;
+ bool patched, shadow;
+ int ret;
+
+ /* Clear any stale software interrupt bits. */
+ regmap_read(cs42l43->regmap, CS42L43_SOFT_INT, &mcu_rev);
+
+ ret = regmap_read(cs42l43->regmap, CS42L43_BOOT_STATUS, &boot_status);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to read boot status: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(cs42l43->regmap, CS42L43_MCU_SW_REV, &mcu_rev);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to read firmware revision: %d\n", ret);
+ return ret;
+ }
+
+ bios_rev = (((mcu_rev & CS42L43_BIOS_MAJOR_REV_MASK) << 12) |
+ ((mcu_rev & CS42L43_BIOS_MINOR_REV_MASK) << 4) |
+ ((mcu_rev & CS42L43_BIOS_SUBMINOR_REV_MASK) >> 8)) >>
+ CS42L43_BIOS_MAJOR_REV_SHIFT;
+ mcu_rev = ((mcu_rev & CS42L43_FW_MAJOR_REV_MASK) << 12) |
+ ((mcu_rev & CS42L43_FW_MINOR_REV_MASK) << 4) |
+ ((mcu_rev & CS42L43_FW_SUBMINOR_REV_MASK) >> 8);
+
+ /*
+ * The firmware has two revision numbers both of them being at the ROM
+ * revision indicates no patch has been applied.
+ */
+ patched = mcu_rev != CS42L43_MCU_ROM_REV || bios_rev != CS42L43_MCU_ROM_BIOS_REV;
+ /*
+ * Later versions of the firmwware require the driver to access some
+ * features through a set of shadow registers.
+ */
+ shadow = (mcu_rev >= CS42L43_MCU_SHADOW_REGS_REQUIRED_REV) ||
+ (bios_rev >= CS42L43_BIOS_SHADOW_REGS_REQUIRED_REV);
+
+ ret = regmap_read(cs42l43->regmap, CS42L43_BOOT_CONTROL, &secure_cfg);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to read security settings: %d\n", ret);
+ return ret;
+ }
+
+ cs42l43->hw_lock = secure_cfg & CS42L43_LOCK_HW_STS_MASK;
+
+ if (!patched && cs42l43->hw_lock) {
+ dev_err(cs42l43->dev, "Unpatched secure device\n");
+ return -EPERM;
+ }
+
+ dev_dbg(cs42l43->dev, "Firmware(0x%x, 0x%x) in boot stage %d\n",
+ mcu_rev, bios_rev, boot_status);
+
+ switch (boot_status) {
+ case CS42L43_MCU_BOOT_STAGE2:
+ if (!patched) {
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+ "cs42l43.bin", cs42l43->dev,
+ GFP_KERNEL, cs42l43,
+ cs42l43_mcu_load_firmware);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to request firmware: %d\n", ret);
+ return ret;
+ }
+
+ wait_for_completion(&cs42l43->firmware_download);
+
+ if (cs42l43->firmware_error)
+ return cs42l43->firmware_error;
+
+ return -EAGAIN;
+ } else {
+ return cs42l43_mcu_stage_2_3(cs42l43, shadow);
+ }
+ case CS42L43_MCU_BOOT_STAGE3:
+ if (patched) {
+ ret = cs42l43_mcu_is_hw_compatible(cs42l43, mcu_rev, bios_rev);
+ if (ret)
+ return ret;
+
+ return cs42l43_mcu_disable(cs42l43);
+ } else {
+ return cs42l43_mcu_stage_3_2(cs42l43);
+ }
+ case CS42L43_MCU_BOOT_STAGE4:
+ return 0;
+ default:
+ dev_err(cs42l43->dev, "Invalid boot status: %d\n", boot_status);
+ return -EINVAL;
+ }
+}
+
+/*
+ * Update the firmware running on the device.
+ */
+static int cs42l43_mcu_update(struct cs42l43 *cs42l43)
+{
+ int i, ret;
+
+ for (i = 0; i < CS42L43_MCU_UPDATE_RETRIES; i++) {
+ ret = cs42l43_mcu_update_step(cs42l43);
+ if (ret != -EAGAIN)
+ return ret;
+
+ ret = cs42l43_wait_for_attach(cs42l43);
+ if (ret)
+ return ret;
+ }
+
+ dev_err(cs42l43->dev, "Failed retrying update\n");
+ return -ETIMEDOUT;
+}
+
+static int cs42l43_irq_config(struct cs42l43 *cs42l43)
+{
+ struct irq_data *irq_data;
+ unsigned long irq_flags;
+ int ret;
+
+ if (cs42l43->sdw)
+ cs42l43->irq = cs42l43->sdw->irq;
+
+ cs42l43->irq_chip = cs42l43_irq_chip;
+ cs42l43->irq_chip.irq_drv_data = cs42l43;
+
+ irq_data = irq_get_irq_data(cs42l43->irq);
+ if (!irq_data) {
+ dev_err(cs42l43->dev, "Invalid IRQ: %d\n", cs42l43->irq);
+ return -EINVAL;
+ }
+
+ irq_flags = irqd_get_trigger_type(irq_data);
+ switch (irq_flags) {
+ case IRQF_TRIGGER_LOW:
+ case IRQF_TRIGGER_HIGH:
+ case IRQF_TRIGGER_RISING:
+ case IRQF_TRIGGER_FALLING:
+ break;
+ case IRQ_TYPE_NONE:
+ default:
+ irq_flags = IRQF_TRIGGER_LOW;
+ break;
+ }
+
+ irq_flags |= IRQF_ONESHOT;
+
+ ret = devm_regmap_add_irq_chip(cs42l43->dev, cs42l43->regmap,
+ cs42l43->irq, irq_flags, 0,
+ &cs42l43->irq_chip, &cs42l43->irq_data);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to add IRQ chip: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(cs42l43->dev, "Configured IRQ %d with flags 0x%lx\n",
+ cs42l43->irq, irq_flags);
+
+ return 0;
+}
+
+static void cs42l43_boot_work(struct work_struct *work)
+{
+ struct cs42l43 *cs42l43 = container_of(work, struct cs42l43, boot_work);
+ unsigned int devid, revid, otp;
+ int ret;
+
+ ret = cs42l43_wait_for_attach(cs42l43);
+ if (ret)
+ goto err;
+
+ ret = regmap_read(cs42l43->regmap, CS42L43_DEVID, &devid);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to read devid: %d\n", ret);
+ goto err;
+ }
+
+ switch (devid) {
+ case CS42L43_DEVID_VAL:
+ break;
+ default:
+ dev_err(cs42l43->dev, "Unrecognised devid: 0x%06x\n", devid);
+ goto err;
+ }
+
+ ret = regmap_read(cs42l43->regmap, CS42L43_REVID, &revid);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to read rev: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_read(cs42l43->regmap, CS42L43_OTP_REVISION_ID, &otp);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to read otp rev: %d\n", ret);
+ goto err;
+ }
+
+ dev_info(cs42l43->dev,
+ "devid: 0x%06x, rev: 0x%02x, otp: 0x%02x\n", devid, revid, otp);
+
+ ret = cs42l43_mcu_update(cs42l43);
+ if (ret)
+ goto err;
+
+ ret = regmap_register_patch(cs42l43->regmap, cs42l43_reva_patch,
+ ARRAY_SIZE(cs42l43_reva_patch));
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to apply register patch: %d\n", ret);
+ goto err;
+ }
+
+ ret = cs42l43_irq_config(cs42l43);
+ if (ret)
+ goto err;
+
+ ret = devm_mfd_add_devices(cs42l43->dev, PLATFORM_DEVID_NONE,
+ cs42l43_devs, ARRAY_SIZE(cs42l43_devs),
+ NULL, 0, NULL);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to add subdevices: %d\n", ret);
+ goto err;
+ }
+
+ pm_runtime_put_autosuspend(cs42l43->dev);
+
+ return;
+
+err:
+ pm_runtime_put_sync(cs42l43->dev);
+}
+
+static int cs42l43_power_up(struct cs42l43 *cs42l43)
+{
+ int ret;
+
+ ret = regulator_enable(cs42l43->vdd_p);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to enable vdd-p: %d\n", ret);
+ return ret;
+ }
+
+ /* vdd-p must be on for 50uS before any other supply */
+ usleep_range(CS42L43_VDDP_DELAY_US, 2 * CS42L43_VDDP_DELAY_US);
+
+ gpiod_set_raw_value_cansleep(cs42l43->reset, 1);
+
+ ret = regulator_bulk_enable(CS42L43_N_SUPPLIES, cs42l43->core_supplies);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to enable core supplies: %d\n", ret);
+ goto err_reset;
+ }
+
+ ret = regulator_enable(cs42l43->vdd_d);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to enable vdd-d: %d\n", ret);
+ goto err_core_supplies;
+ }
+
+ usleep_range(CS42L43_VDDD_DELAY_US, 2 * CS42L43_VDDD_DELAY_US);
+
+ return 0;
+
+err_core_supplies:
+ regulator_bulk_disable(CS42L43_N_SUPPLIES, cs42l43->core_supplies);
+err_reset:
+ gpiod_set_raw_value_cansleep(cs42l43->reset, 0);
+ regulator_disable(cs42l43->vdd_p);
+
+ return ret;
+}
+
+static int cs42l43_power_down(struct cs42l43 *cs42l43)
+{
+ int ret;
+
+ ret = regulator_disable(cs42l43->vdd_d);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to disable vdd-d: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_disable(CS42L43_N_SUPPLIES, cs42l43->core_supplies);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to disable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_set_raw_value_cansleep(cs42l43->reset, 0);
+
+ ret = regulator_disable(cs42l43->vdd_p);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to disable vdd-p: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void cs42l43_dev_remove(void *data)
+{
+ struct cs42l43 *cs42l43 = data;
+
+ cancel_work_sync(&cs42l43->boot_work);
+
+ cs42l43_power_down(cs42l43);
+}
+
+int cs42l43_dev_probe(struct cs42l43 *cs42l43)
+{
+ int i, ret;
+
+ dev_set_drvdata(cs42l43->dev, cs42l43);
+
+ mutex_init(&cs42l43->pll_lock);
+ init_completion(&cs42l43->device_attach);
+ init_completion(&cs42l43->device_detach);
+ init_completion(&cs42l43->firmware_download);
+ INIT_WORK(&cs42l43->boot_work, cs42l43_boot_work);
+
+ regcache_cache_only(cs42l43->regmap, true);
+
+ cs42l43->reset = devm_gpiod_get_optional(cs42l43->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(cs42l43->reset))
+ return dev_err_probe(cs42l43->dev, PTR_ERR(cs42l43->reset),
+ "Failed to get reset\n");
+
+ gpiod_set_raw_value_cansleep(cs42l43->reset, 0);
+
+ cs42l43->vdd_p = devm_regulator_get(cs42l43->dev, "vdd-p");
+ if (IS_ERR(cs42l43->vdd_p))
+ return dev_err_probe(cs42l43->dev, PTR_ERR(cs42l43->vdd_p),
+ "Failed to get vdd-p\n");
+
+ cs42l43->vdd_d = devm_regulator_get(cs42l43->dev, "vdd-d");
+ if (IS_ERR(cs42l43->vdd_d))
+ return dev_err_probe(cs42l43->dev, PTR_ERR(cs42l43->vdd_d),
+ "Failed to get vdd-d\n");
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs42l43_core_supplies) != CS42L43_N_SUPPLIES);
+
+ for (i = 0; i < CS42L43_N_SUPPLIES; i++)
+ cs42l43->core_supplies[i].supply = cs42l43_core_supplies[i];
+
+ ret = devm_regulator_bulk_get(cs42l43->dev, CS42L43_N_SUPPLIES,
+ cs42l43->core_supplies);
+ if (ret)
+ return dev_err_probe(cs42l43->dev, ret,
+ "Failed to get core supplies\n");
+
+ ret = cs42l43_power_up(cs42l43);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(cs42l43->dev, cs42l43_dev_remove, cs42l43);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(cs42l43->dev, CS42L43_AUTOSUSPEND_TIME_MS);
+ pm_runtime_use_autosuspend(cs42l43->dev);
+ pm_runtime_set_active(cs42l43->dev);
+ /*
+ * The device is already powered up, but keep it from suspending until
+ * the boot work runs.
+ */
+ pm_runtime_get_noresume(cs42l43->dev);
+ ret = devm_pm_runtime_enable(cs42l43->dev);
+ if (ret)
+ return ret;
+
+ queue_work(system_long_wq, &cs42l43->boot_work);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l43_dev_probe, "MFD_CS42L43");
+
+static int cs42l43_suspend(struct device *dev)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to resume for suspend: %d\n", ret);
+ return ret;
+ }
+
+ disable_irq(cs42l43->irq);
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to force suspend: %d\n", ret);
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
+
+ pm_runtime_put_noidle(dev);
+
+ ret = cs42l43_power_down(cs42l43);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cs42l43_suspend_noirq(struct device *dev)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
+
+ enable_irq(cs42l43->irq);
+
+ return 0;
+}
+
+static int cs42l43_resume_noirq(struct device *dev)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
+
+ disable_irq(cs42l43->irq);
+
+ return 0;
+}
+
+static int cs42l43_resume(struct device *dev)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = cs42l43_power_up(cs42l43);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to force resume: %d\n", ret);
+ return ret;
+ }
+
+ enable_irq(cs42l43->irq);
+
+ return 0;
+}
+
+static int cs42l43_runtime_suspend(struct device *dev)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
+
+ /*
+ * Whilst the driver doesn't power the chip down here, going into runtime
+ * suspend lets the SoundWire bus power down, which means the driver
+ * can't communicate with the device any more.
+ */
+ regcache_cache_only(cs42l43->regmap, true);
+
+ return 0;
+}
+
+static int cs42l43_runtime_resume(struct device *dev)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
+ unsigned int reset_canary;
+ int ret;
+
+ ret = cs42l43_wait_for_attach(cs42l43);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(cs42l43->regmap, CS42L43_RELID, &reset_canary);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to check reset canary: %d\n", ret);
+ goto err;
+ }
+
+ if (!reset_canary) {
+ /*
+ * If the canary has cleared the chip has reset, re-handle the
+ * MCU and mark the cache as dirty to indicate the chip reset.
+ */
+ ret = cs42l43_mcu_update(cs42l43);
+ if (ret)
+ goto err;
+
+ regcache_mark_dirty(cs42l43->regmap);
+ }
+
+ ret = regcache_sync(cs42l43->regmap);
+ if (ret) {
+ dev_err(cs42l43->dev, "Failed to restore register cache: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_cache_only(cs42l43->regmap, true);
+
+ return ret;
+}
+
+EXPORT_NS_GPL_DEV_PM_OPS(cs42l43_pm_ops, MFD_CS42L43) = {
+ SYSTEM_SLEEP_PM_OPS(cs42l43_suspend, cs42l43_resume)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs42l43_suspend_noirq, cs42l43_resume_noirq)
+ RUNTIME_PM_OPS(cs42l43_runtime_suspend, cs42l43_runtime_resume, NULL)
+};
+
+MODULE_DESCRIPTION("CS42L43 Core Driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("cs42l43.bin");
diff --git a/drivers/mfd/cs42l43.h b/drivers/mfd/cs42l43.h
new file mode 100644
index 000000000000..f3da783930f5
--- /dev/null
+++ b/drivers/mfd/cs42l43.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * CS42L43 core driver internal data
+ *
+ * Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CS42L43_CORE_INT_H
+#define CS42L43_CORE_INT_H
+
+#define CS42L43_N_DEFAULTS 176
+
+struct dev_pm_ops;
+struct device;
+struct reg_default;
+
+struct cs42l43;
+
+extern const struct dev_pm_ops cs42l43_pm_ops;
+extern const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS];
+
+bool cs42l43_readable_register(struct device *dev, unsigned int reg);
+bool cs42l43_precious_register(struct device *dev, unsigned int reg);
+bool cs42l43_volatile_register(struct device *dev, unsigned int reg);
+
+int cs42l43_dev_probe(struct cs42l43 *cs42l43);
+
+#endif /* CS42L43_CORE_INT_H */
diff --git a/drivers/mfd/cs47l15-tables.c b/drivers/mfd/cs47l15-tables.c
index 3c77f0a24e9b..59b005cc1e33 100644
--- a/drivers/mfd/cs47l15-tables.c
+++ b/drivers/mfd/cs47l15-tables.c
@@ -1249,7 +1249,7 @@ const struct regmap_config cs47l15_16bit_spi_regmap = {
.readable_reg = &cs47l15_16bit_readable_register,
.volatile_reg = &cs47l15_16bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = cs47l15_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs47l15_reg_default),
};
@@ -1264,7 +1264,7 @@ const struct regmap_config cs47l15_16bit_i2c_regmap = {
.readable_reg = &cs47l15_16bit_readable_register,
.volatile_reg = &cs47l15_16bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = cs47l15_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs47l15_reg_default),
};
@@ -1281,7 +1281,7 @@ const struct regmap_config cs47l15_32bit_spi_regmap = {
.readable_reg = &cs47l15_32bit_readable_register,
.volatile_reg = &cs47l15_32bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs47l15_32bit_spi_regmap);
@@ -1295,6 +1295,6 @@ const struct regmap_config cs47l15_32bit_i2c_regmap = {
.readable_reg = &cs47l15_32bit_readable_register,
.volatile_reg = &cs47l15_32bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs47l15_32bit_i2c_regmap);
diff --git a/drivers/mfd/cs47l24-tables.c b/drivers/mfd/cs47l24-tables.c
index c289d92a5c1d..878dfd298a17 100644
--- a/drivers/mfd/cs47l24-tables.c
+++ b/drivers/mfd/cs47l24-tables.c
@@ -1616,7 +1616,7 @@ const struct regmap_config cs47l24_spi_regmap = {
.readable_reg = cs47l24_readable_register,
.volatile_reg = cs47l24_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = cs47l24_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs47l24_reg_default),
};
diff --git a/drivers/mfd/cs47l35-tables.c b/drivers/mfd/cs47l35-tables.c
index a0bc6c5100d6..274f4b05850a 100644
--- a/drivers/mfd/cs47l35-tables.c
+++ b/drivers/mfd/cs47l35-tables.c
@@ -1498,7 +1498,7 @@ const struct regmap_config cs47l35_16bit_spi_regmap = {
.readable_reg = cs47l35_16bit_readable_register,
.volatile_reg = cs47l35_16bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = cs47l35_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs47l35_reg_default),
};
@@ -1515,7 +1515,7 @@ const struct regmap_config cs47l35_16bit_i2c_regmap = {
.readable_reg = cs47l35_16bit_readable_register,
.volatile_reg = cs47l35_16bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = cs47l35_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs47l35_reg_default),
};
@@ -1534,7 +1534,7 @@ const struct regmap_config cs47l35_32bit_spi_regmap = {
.readable_reg = cs47l35_32bit_readable_register,
.volatile_reg = cs47l35_32bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs47l35_32bit_spi_regmap);
@@ -1550,6 +1550,6 @@ const struct regmap_config cs47l35_32bit_i2c_regmap = {
.readable_reg = cs47l35_32bit_readable_register,
.volatile_reg = cs47l35_32bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs47l35_32bit_i2c_regmap);
diff --git a/drivers/mfd/cs47l85-tables.c b/drivers/mfd/cs47l85-tables.c
index 270d8eda3f5f..f397894827ce 100644
--- a/drivers/mfd/cs47l85-tables.c
+++ b/drivers/mfd/cs47l85-tables.c
@@ -2836,7 +2836,7 @@ const struct regmap_config cs47l85_16bit_spi_regmap = {
.readable_reg = cs47l85_16bit_readable_register,
.volatile_reg = cs47l85_16bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = cs47l85_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs47l85_reg_default),
};
@@ -2853,7 +2853,7 @@ const struct regmap_config cs47l85_16bit_i2c_regmap = {
.readable_reg = cs47l85_16bit_readable_register,
.volatile_reg = cs47l85_16bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = cs47l85_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs47l85_reg_default),
};
@@ -2872,7 +2872,7 @@ const struct regmap_config cs47l85_32bit_spi_regmap = {
.readable_reg = cs47l85_32bit_readable_register,
.volatile_reg = cs47l85_32bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs47l85_32bit_spi_regmap);
@@ -2888,6 +2888,6 @@ const struct regmap_config cs47l85_32bit_i2c_regmap = {
.readable_reg = cs47l85_32bit_readable_register,
.volatile_reg = cs47l85_32bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs47l85_32bit_i2c_regmap);
diff --git a/drivers/mfd/cs47l90-tables.c b/drivers/mfd/cs47l90-tables.c
index 7345fc09c0bb..6f9ceb36c533 100644
--- a/drivers/mfd/cs47l90-tables.c
+++ b/drivers/mfd/cs47l90-tables.c
@@ -2539,7 +2539,7 @@ const struct regmap_config cs47l90_16bit_spi_regmap = {
.readable_reg = cs47l90_16bit_readable_register,
.volatile_reg = cs47l90_16bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = cs47l90_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs47l90_reg_default),
};
@@ -2556,7 +2556,7 @@ const struct regmap_config cs47l90_16bit_i2c_regmap = {
.readable_reg = cs47l90_16bit_readable_register,
.volatile_reg = cs47l90_16bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = cs47l90_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs47l90_reg_default),
};
@@ -2575,7 +2575,7 @@ const struct regmap_config cs47l90_32bit_spi_regmap = {
.readable_reg = cs47l90_32bit_readable_register,
.volatile_reg = cs47l90_32bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs47l90_32bit_spi_regmap);
@@ -2591,6 +2591,6 @@ const struct regmap_config cs47l90_32bit_i2c_regmap = {
.readable_reg = cs47l90_32bit_readable_register,
.volatile_reg = cs47l90_32bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs47l90_32bit_i2c_regmap);
diff --git a/drivers/mfd/cs47l92-tables.c b/drivers/mfd/cs47l92-tables.c
index f296e355df4d..4d9ba865aaf6 100644
--- a/drivers/mfd/cs47l92-tables.c
+++ b/drivers/mfd/cs47l92-tables.c
@@ -1890,7 +1890,7 @@ const struct regmap_config cs47l92_16bit_spi_regmap = {
.readable_reg = &cs47l92_16bit_readable_register,
.volatile_reg = &cs47l92_16bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = cs47l92_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs47l92_reg_default),
};
@@ -1907,7 +1907,7 @@ const struct regmap_config cs47l92_16bit_i2c_regmap = {
.readable_reg = &cs47l92_16bit_readable_register,
.volatile_reg = &cs47l92_16bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = cs47l92_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs47l92_reg_default),
};
@@ -1926,7 +1926,7 @@ const struct regmap_config cs47l92_32bit_spi_regmap = {
.readable_reg = &cs47l92_32bit_readable_register,
.volatile_reg = &cs47l92_32bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs47l92_32bit_spi_regmap);
@@ -1942,6 +1942,6 @@ const struct regmap_config cs47l92_32bit_i2c_regmap = {
.readable_reg = &cs47l92_32bit_readable_register,
.volatile_reg = &cs47l92_32bit_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs47l92_32bit_i2c_regmap);
diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c
index 150448cd2eb0..b06cd518413b 100644
--- a/drivers/mfd/da9052-core.c
+++ b/drivers/mfd/da9052-core.c
@@ -533,7 +533,7 @@ const struct regmap_config da9052_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = DA9052_PAGE1_CON_REG,
.readable_reg = da9052_reg_readable,
@@ -585,6 +585,7 @@ static int da9052_clear_fault_log(struct da9052 *da9052)
"Cannot reset FAULT_LOG values %d\n", ret);
}
+ da9052->fault_log = fault_log;
return ret;
}
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
index 541e2d47677e..fd000a21bcba 100644
--- a/drivers/mfd/da9052-i2c.c
+++ b/drivers/mfd/da9052-i2c.c
@@ -13,14 +13,11 @@
#include <linux/mfd/core.h>
#include <linux/i2c.h>
#include <linux/err.h>
+#include <linux/of.h>
#include <linux/mfd/da9052/da9052.h>
#include <linux/mfd/da9052/reg.h>
-#ifdef CONFIG_OF
-#include <linux/of.h>
-#include <linux/of_device.h>
-#endif
/* I2C safe register check */
static inline bool i2c_safe_reg(unsigned char reg)
diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c
index be5f2b34e18a..80fc5c0cac2f 100644
--- a/drivers/mfd/da9052-spi.c
+++ b/drivers/mfd/da9052-spi.c
@@ -37,7 +37,7 @@ static int da9052_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, da9052);
config = da9052_regmap_config;
- config.read_flag_mask = 1;
+ config.write_flag_mask = 1;
config.reg_bits = 7;
config.pad_bits = 1;
config.val_bits = 8;
diff --git a/drivers/mfd/da9055-core.c b/drivers/mfd/da9055-core.c
index 768302e05baa..158590ad37d4 100644
--- a/drivers/mfd/da9055-core.c
+++ b/drivers/mfd/da9055-core.c
@@ -245,7 +245,7 @@ const struct regmap_config da9055_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = DA9055_MAX_REGISTER_CNT,
.readable_reg = da9055_register_readable,
@@ -387,7 +387,7 @@ int da9055_device_init(struct da9055 *da9055)
return 0;
err:
- mfd_remove_devices(da9055->dev);
+ regmap_del_irq_chip(da9055->chip_irq, da9055->irq_data);
return ret;
}
diff --git a/drivers/mfd/da9055-i2c.c b/drivers/mfd/da9055-i2c.c
index bbaf4f07f274..6c1981832aaf 100644
--- a/drivers/mfd/da9055-i2c.c
+++ b/drivers/mfd/da9055-i2c.c
@@ -11,7 +11,6 @@
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/mfd/da9055/core.h>
@@ -55,7 +54,7 @@ static void da9055_i2c_remove(struct i2c_client *i2c)
* and CODEC, which must be different to operate together.
*/
static const struct i2c_device_id da9055_i2c_id[] = {
- {"da9055-pmic", 0},
+ { "da9055-pmic" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da9055_i2c_id);
diff --git a/drivers/mfd/da9062-core.c b/drivers/mfd/da9062-core.c
index 48f58b6f5629..637c5f47a4b0 100644
--- a/drivers/mfd/da9062-core.c
+++ b/drivers/mfd/da9062-core.c
@@ -9,7 +9,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/interrupt.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
@@ -25,7 +25,7 @@
#define DA9062_IRQ_LOW 0
#define DA9062_IRQ_HIGH 1
-static struct regmap_irq da9061_irqs[] = {
+static const struct regmap_irq da9061_irqs[] = {
/* EVENT A */
[DA9061_IRQ_ONKEY] = {
.reg_offset = DA9062_REG_EVENT_A_OFFSET,
@@ -79,7 +79,7 @@ static struct regmap_irq da9061_irqs[] = {
},
};
-static struct regmap_irq_chip da9061_irq_chip = {
+static const struct regmap_irq_chip da9061_irq_chip = {
.name = "da9061-irq",
.irqs = da9061_irqs,
.num_irqs = DA9061_NUM_IRQ,
@@ -89,7 +89,7 @@ static struct regmap_irq_chip da9061_irq_chip = {
.ack_base = DA9062AA_EVENT_A,
};
-static struct regmap_irq da9062_irqs[] = {
+static const struct regmap_irq da9062_irqs[] = {
/* EVENT A */
[DA9062_IRQ_ONKEY] = {
.reg_offset = DA9062_REG_EVENT_A_OFFSET,
@@ -151,7 +151,7 @@ static struct regmap_irq da9062_irqs[] = {
},
};
-static struct regmap_irq_chip da9062_irq_chip = {
+static const struct regmap_irq_chip da9062_irq_chip = {
.name = "da9062-irq",
.irqs = da9062_irqs,
.num_irqs = DA9062_NUM_IRQ,
@@ -470,13 +470,13 @@ static const struct regmap_range_cfg da9061_range_cfg[] = {
}
};
-static struct regmap_config da9061_regmap_config = {
+static const struct regmap_config da9061_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.ranges = da9061_range_cfg,
.num_ranges = ARRAY_SIZE(da9061_range_cfg),
.max_register = DA9062AA_CONFIG_ID,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.rd_table = &da9061_aa_readable_table,
.wr_table = &da9061_aa_writeable_table,
.volatile_table = &da9061_aa_volatile_table,
@@ -576,28 +576,20 @@ static const struct regmap_range_cfg da9062_range_cfg[] = {
}
};
-static struct regmap_config da9062_regmap_config = {
+static const struct regmap_config da9062_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.ranges = da9062_range_cfg,
.num_ranges = ARRAY_SIZE(da9062_range_cfg),
.max_register = DA9062AA_CONFIG_ID,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.rd_table = &da9062_aa_readable_table,
.wr_table = &da9062_aa_writeable_table,
.volatile_table = &da9062_aa_volatile_table,
};
-static const struct of_device_id da9062_dt_ids[] = {
- { .compatible = "dlg,da9061", .data = (void *)COMPAT_TYPE_DA9061, },
- { .compatible = "dlg,da9062", .data = (void *)COMPAT_TYPE_DA9062, },
- { }
-};
-MODULE_DEVICE_TABLE(of, da9062_dt_ids);
-
static int da9062_i2c_probe(struct i2c_client *i2c)
{
- const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
struct da9062 *chip;
unsigned int irq_base = 0;
const struct mfd_cell *cell;
@@ -611,10 +603,7 @@ static int da9062_i2c_probe(struct i2c_client *i2c)
if (!chip)
return -ENOMEM;
- if (i2c->dev.of_node)
- chip->chip_type = (uintptr_t)of_device_get_match_data(&i2c->dev);
- else
- chip->chip_type = id->driver_data;
+ chip->chip_type = (uintptr_t)i2c_get_match_data(i2c);
i2c_set_clientdata(i2c, chip);
chip->dev = &i2c->dev;
@@ -714,10 +703,17 @@ static void da9062_i2c_remove(struct i2c_client *i2c)
regmap_del_irq_chip(i2c->irq, chip->regmap_irq);
}
+static const struct of_device_id da9062_dt_ids[] = {
+ { .compatible = "dlg,da9061", .data = (void *)COMPAT_TYPE_DA9061 },
+ { .compatible = "dlg,da9062", .data = (void *)COMPAT_TYPE_DA9062 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da9062_dt_ids);
+
static const struct i2c_device_id da9062_i2c_id[] = {
{ "da9061", COMPAT_TYPE_DA9061 },
{ "da9062", COMPAT_TYPE_DA9062 },
- { },
+ { }
};
MODULE_DEVICE_TABLE(i2c, da9062_i2c_id);
diff --git a/drivers/mfd/da9063-i2c.c b/drivers/mfd/da9063-i2c.c
index d715cf9a9e68..a803b7440f09 100644
--- a/drivers/mfd/da9063-i2c.c
+++ b/drivers/mfd/da9063-i2c.c
@@ -37,9 +37,13 @@ enum da9063_page_sel_buf_fmt {
DA9063_PAGE_SEL_BUF_SIZE,
};
+enum da9063_page_sel_msgs {
+ DA9063_PAGE_SEL_MSG = 0,
+ DA9063_PAGE_SEL_CNT,
+};
+
enum da9063_paged_read_msgs {
- DA9063_PAGED_READ_MSG_PAGE_SEL = 0,
- DA9063_PAGED_READ_MSG_REG_SEL,
+ DA9063_PAGED_READ_MSG_REG_SEL = 0,
DA9063_PAGED_READ_MSG_DATA,
DA9063_PAGED_READ_MSG_CNT,
};
@@ -65,10 +69,21 @@ static int da9063_i2c_blockreg_read(struct i2c_client *client, u16 addr,
(page_num << DA9063_I2C_PAGE_SEL_SHIFT) & DA9063_REG_PAGE_MASK;
/* Write reg address, page selection */
- xfer[DA9063_PAGED_READ_MSG_PAGE_SEL].addr = client->addr;
- xfer[DA9063_PAGED_READ_MSG_PAGE_SEL].flags = 0;
- xfer[DA9063_PAGED_READ_MSG_PAGE_SEL].len = DA9063_PAGE_SEL_BUF_SIZE;
- xfer[DA9063_PAGED_READ_MSG_PAGE_SEL].buf = page_sel_buf;
+ xfer[DA9063_PAGE_SEL_MSG].addr = client->addr;
+ xfer[DA9063_PAGE_SEL_MSG].flags = 0;
+ xfer[DA9063_PAGE_SEL_MSG].len = DA9063_PAGE_SEL_BUF_SIZE;
+ xfer[DA9063_PAGE_SEL_MSG].buf = page_sel_buf;
+
+ ret = i2c_transfer(client->adapter, xfer, DA9063_PAGE_SEL_CNT);
+ if (ret < 0) {
+ dev_err(&client->dev, "Page switch failed: %d\n", ret);
+ return ret;
+ }
+
+ if (ret != DA9063_PAGE_SEL_CNT) {
+ dev_err(&client->dev, "Page switch failed to complete\n");
+ return -EIO;
+ }
/* Select register address */
xfer[DA9063_PAGED_READ_MSG_REG_SEL].addr = client->addr;
@@ -342,7 +357,7 @@ static struct regmap_config da9063_regmap_config = {
.num_ranges = ARRAY_SIZE(da9063_range_cfg),
.max_register = DA9063_REG_CONFIG_ID,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct of_device_id da9063_dt_ids[] = {
@@ -454,6 +469,9 @@ static int da9063_i2c_probe(struct i2c_client *i2c)
}
}
+ /* Reserve our unused second address so userspace won't interfere */
+ devm_i2c_new_dummy_device(&i2c->dev, i2c->adapter, i2c->addr + 1);
+
return da9063_device_init(da9063, i2c->irq);
}
diff --git a/drivers/mfd/da9150-core.c b/drivers/mfd/da9150-core.c
index 94d621e20635..5c59cc869fb3 100644
--- a/drivers/mfd/da9150-core.c
+++ b/drivers/mfd/da9150-core.c
@@ -169,7 +169,7 @@ static const struct regmap_config da9150_regmap_config = {
.num_ranges = ARRAY_SIZE(da9150_range_cfg),
.max_register = DA9150_TBAT_RES_B,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = da9150_volatile_reg,
};
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 27a881da4d6e..21e68a382b11 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -2607,9 +2607,9 @@ static int db8500_irq_init(struct device_node *np)
{
int i;
- db8500_irq_domain = irq_domain_add_simple(
- np, NUM_PRCMU_WAKEUPS, 0,
- &db8500_irq_ops, NULL);
+ db8500_irq_domain = irq_domain_create_simple(of_fwnode_handle(np),
+ NUM_PRCMU_WAKEUPS, 0,
+ &db8500_irq_ops, NULL);
if (!db8500_irq_domain) {
pr_err("Failed to create irqdomain\n");
@@ -2639,9 +2639,9 @@ static void dbx500_fw_version_init(struct device_node *np)
fw_info.version.api_version = (version >> 8) & 0xFF;
fw_info.version.func_version = (version >> 16) & 0xFF;
fw_info.version.errata = (version >> 24) & 0xFF;
- strncpy(fw_info.version.project_name,
+ strscpy(fw_info.version.project_name,
fw_project_name(fw_info.version.project),
- PRCMU_FW_PROJECT_NAME_LEN);
+ sizeof(fw_info.version.project_name));
fw_info.valid = true;
pr_info("PRCMU firmware: %s(%d), version %d.%d.%d\n",
fw_info.version.project_name,
diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
index c7510434380a..fbbe82c6e75b 100644
--- a/drivers/mfd/dln2.c
+++ b/drivers/mfd/dln2.c
@@ -826,7 +826,6 @@ out_stop_rx:
dln2_stop_rx_urbs(dln2);
out_free:
- usb_put_dev(dln2->usb_dev);
dln2_free(dln2);
return ret;
diff --git a/drivers/mfd/ene-kb3930.c b/drivers/mfd/ene-kb3930.c
index fa0ad2f14a39..9460a67acb0b 100644
--- a/drivers/mfd/ene-kb3930.c
+++ b/drivers/mfd/ene-kb3930.c
@@ -162,7 +162,7 @@ static int kb3930_probe(struct i2c_client *client)
devm_gpiod_get_array_optional(dev, "off", GPIOD_IN);
if (IS_ERR(ddata->off_gpios))
return PTR_ERR(ddata->off_gpios);
- if (ddata->off_gpios->ndescs < 2) {
+ if (ddata->off_gpios && ddata->off_gpios->ndescs < 2) {
dev_err(dev, "invalid off-gpios property\n");
return -EINVAL;
}
diff --git a/drivers/mfd/exynos-lpass.c b/drivers/mfd/exynos-lpass.c
index 166cd21088cd..9bb2687c2835 100644
--- a/drivers/mfd/exynos-lpass.c
+++ b/drivers/mfd/exynos-lpass.c
@@ -101,22 +101,30 @@ static const struct regmap_config exynos_lpass_reg_conf = {
.reg_stride = 4,
.val_bits = 32,
.max_register = 0xfc,
- .fast_io = true,
};
+static void exynos_lpass_disable_lpass(void *data)
+{
+ struct platform_device *pdev = data;
+ struct exynos_lpass *lpass = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ exynos_lpass_disable(lpass);
+}
+
static int exynos_lpass_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct exynos_lpass *lpass;
void __iomem *base_top;
- struct resource *res;
+ int ret;
lpass = devm_kzalloc(dev, sizeof(*lpass), GFP_KERNEL);
if (!lpass)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base_top = devm_ioremap_resource(dev, res);
+ base_top = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base_top))
return PTR_ERR(base_top);
@@ -124,8 +132,8 @@ static int exynos_lpass_probe(struct platform_device *pdev)
if (IS_ERR(lpass->sfr0_clk))
return PTR_ERR(lpass->sfr0_clk);
- lpass->top = regmap_init_mmio(dev, base_top,
- &exynos_lpass_reg_conf);
+ lpass->top = devm_regmap_init_mmio(dev, base_top,
+ &exynos_lpass_reg_conf);
if (IS_ERR(lpass->top)) {
dev_err(dev, "LPASS top regmap initialization failed\n");
return PTR_ERR(lpass->top);
@@ -136,20 +144,11 @@ static int exynos_lpass_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
exynos_lpass_enable(lpass);
- return devm_of_platform_populate(dev);
-}
-
-static int exynos_lpass_remove(struct platform_device *pdev)
-{
- struct exynos_lpass *lpass = platform_get_drvdata(pdev);
-
- exynos_lpass_disable(lpass);
- pm_runtime_disable(&pdev->dev);
- if (!pm_runtime_status_suspended(&pdev->dev))
- exynos_lpass_disable(lpass);
- regmap_exit(lpass->top);
+ ret = devm_add_action_or_reset(dev, exynos_lpass_disable_lpass, pdev);
+ if (ret)
+ return ret;
- return 0;
+ return devm_of_platform_populate(dev);
}
static int __maybe_unused exynos_lpass_suspend(struct device *dev)
@@ -183,13 +182,12 @@ static const struct of_device_id exynos_lpass_of_match[] = {
MODULE_DEVICE_TABLE(of, exynos_lpass_of_match);
static struct platform_driver exynos_lpass_driver = {
- .driver = {
+ .driver = {
.name = "exynos-lpass",
.pm = &lpass_pm_ops,
.of_match_table = exynos_lpass_of_match,
},
.probe = exynos_lpass_probe,
- .remove = exynos_lpass_remove,
};
module_platform_driver(exynos_lpass_driver);
diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c
index 8d006f6be48c..1be4557b7bdd 100644
--- a/drivers/mfd/ezx-pcap.c
+++ b/drivers/mfd/ezx-pcap.c
@@ -25,11 +25,6 @@ struct pcap_adc_request {
void *data;
};
-struct pcap_adc_sync_request {
- u16 res[2];
- struct completion completion;
-};
-
struct pcap_chip {
struct spi_device *spi;
@@ -335,34 +330,6 @@ int pcap_adc_async(struct pcap_chip *pcap, u8 bank, u32 flags, u8 ch[],
}
EXPORT_SYMBOL_GPL(pcap_adc_async);
-static void pcap_adc_sync_cb(void *param, u16 res[])
-{
- struct pcap_adc_sync_request *req = param;
-
- req->res[0] = res[0];
- req->res[1] = res[1];
- complete(&req->completion);
-}
-
-int pcap_adc_sync(struct pcap_chip *pcap, u8 bank, u32 flags, u8 ch[],
- u16 res[])
-{
- struct pcap_adc_sync_request sync_data;
- int ret;
-
- init_completion(&sync_data.completion);
- ret = pcap_adc_async(pcap, bank, flags, ch, pcap_adc_sync_cb,
- &sync_data);
- if (ret)
- return ret;
- wait_for_completion(&sync_data.completion);
- res[0] = sync_data.res[0];
- res[1] = sync_data.res[1];
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(pcap_adc_sync);
-
/* subdevs */
static int pcap_remove_subdev(struct device *dev, void *unused)
{
diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c
index 089c2ce615b6..467b1a23faeb 100644
--- a/drivers/mfd/fsl-imx25-tsadc.c
+++ b/drivers/mfd/fsl-imx25-tsadc.c
@@ -16,8 +16,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
-static struct regmap_config mx25_tsadc_regmap_config = {
- .fast_io = true,
+static const struct regmap_config mx25_tsadc_regmap_config = {
.max_register = 8,
.reg_bits = 32,
.val_bits = 32,
@@ -65,15 +64,14 @@ static int mx25_tsadc_setup_irq(struct platform_device *pdev,
struct mx25_tsadc *tsadc)
{
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
int irq;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
- tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops,
- tsadc);
+ tsadc->domain = irq_domain_create_simple(dev_fwnode(dev), 2, 0, &mx25_tsadc_domain_ops,
+ tsadc);
if (!tsadc->domain) {
dev_err(dev, "Failed to add irq domain\n");
return -ENOMEM;
@@ -194,11 +192,9 @@ err_irq:
return ret;
}
-static int mx25_tsadc_remove(struct platform_device *pdev)
+static void mx25_tsadc_remove(struct platform_device *pdev)
{
mx25_tsadc_unset_irq(pdev);
-
- return 0;
}
static const struct of_device_id mx25_tsadc_ids[] = {
diff --git a/drivers/mfd/gateworks-gsc.c b/drivers/mfd/gateworks-gsc.c
index b02bfdc871e9..a3301502ce6a 100644
--- a/drivers/mfd/gateworks-gsc.c
+++ b/drivers/mfd/gateworks-gsc.c
@@ -20,7 +20,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
/*
* The GSC suffers from an errata where occasionally during
@@ -160,7 +160,7 @@ static const struct of_device_id gsc_of_match[] = {
};
MODULE_DEVICE_TABLE(of, gsc_of_match);
-static struct regmap_bus gsc_regmap_bus = {
+static const struct regmap_bus gsc_regmap_bus = {
.reg_read = gsc_read,
.reg_write = gsc_write,
};
diff --git a/drivers/mfd/hi6421-pmic-core.c b/drivers/mfd/hi6421-pmic-core.c
index cb5cf4a81c06..5af24a438329 100644
--- a/drivers/mfd/hi6421-pmic-core.c
+++ b/drivers/mfd/hi6421-pmic-core.c
@@ -15,8 +15,9 @@
#include <linux/mfd/core.h>
#include <linux/mfd/hi6421-pmic.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
static const struct mfd_cell hi6421_devs[] = {
@@ -50,16 +51,12 @@ MODULE_DEVICE_TABLE(of, of_hi6421_pmic_match);
static int hi6421_pmic_probe(struct platform_device *pdev)
{
struct hi6421_pmic *pmic;
- const struct of_device_id *id;
const struct mfd_cell *subdevs;
enum hi6421_type type;
void __iomem *base;
int n_subdevs, ret;
- id = of_match_device(of_hi6421_pmic_match, &pdev->dev);
- if (!id)
- return -EINVAL;
- type = (enum hi6421_type)id->data;
+ type = (uintptr_t)device_get_match_data(&pdev->dev);
pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
if (!pmic)
diff --git a/drivers/mfd/hi655x-pmic.c b/drivers/mfd/hi655x-pmic.c
index a58e42ddcd0c..3b4ffcbbee20 100644
--- a/drivers/mfd/hi655x-pmic.c
+++ b/drivers/mfd/hi655x-pmic.c
@@ -16,7 +16,7 @@
#include <linux/mfd/hi655x-pmic.h>
#include <linux/module.h>
#include <linux/gpio/consumer.h>
-#include <linux/of_platform.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -41,7 +41,7 @@ static const struct regmap_irq_chip hi655x_irq_chip = {
.mask_base = HI655X_IRQ_MASK_BASE,
};
-static struct regmap_config hi655x_regmap_config = {
+static const struct regmap_config hi655x_regmap_config = {
.reg_bits = 32,
.reg_stride = HI655X_STRIDE,
.val_bits = 8,
@@ -100,8 +100,7 @@ static int hi655x_pmic_probe(struct platform_device *pdev)
return -ENOMEM;
pmic->dev = dev;
- pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, pmic->res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -145,13 +144,12 @@ static int hi655x_pmic_probe(struct platform_device *pdev)
return 0;
}
-static int hi655x_pmic_remove(struct platform_device *pdev)
+static void hi655x_pmic_remove(struct platform_device *pdev)
{
struct hi655x_pmic *pmic = platform_get_drvdata(pdev);
regmap_del_irq_chip(gpiod_to_irq(pmic->gpio), pmic->irq_data);
mfd_remove_devices(&pdev->dev);
- return 0;
}
static const struct of_device_id hi655x_pmic_match[] = {
@@ -161,11 +159,11 @@ static const struct of_device_id hi655x_pmic_match[] = {
MODULE_DEVICE_TABLE(of, hi655x_pmic_match);
static struct platform_driver hi655x_pmic_driver = {
- .driver = {
- .name = "hi655x-pmic",
- .of_match_table = of_match_ptr(hi655x_pmic_match),
+ .driver = {
+ .name = "hi655x-pmic",
+ .of_match_table = hi655x_pmic_match,
},
- .probe = hi655x_pmic_probe,
+ .probe = hi655x_pmic_probe,
.remove = hi655x_pmic_remove,
};
module_platform_driver(hi655x_pmic_driver);
diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c
index 212818aef93e..63406026d809 100644
--- a/drivers/mfd/intel-lpss-acpi.c
+++ b/drivers/mfd/intel-lpss-acpi.c
@@ -8,15 +8,20 @@
* Mika Westerberg <mika.westerberg@linux.intel.com>
*/
-#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/gfp_types.h>
#include <linux/ioport.h>
-#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/property.h>
+
#include <linux/pxa2xx_ssp.h>
+#include <asm/errno.h>
+
#include "intel-lpss.h"
static const struct property_entry spt_spi_properties[] = {
@@ -169,23 +174,20 @@ MODULE_DEVICE_TABLE(acpi, intel_lpss_acpi_ids);
static int intel_lpss_acpi_probe(struct platform_device *pdev)
{
+ const struct intel_lpss_platform_info *data;
struct intel_lpss_platform_info *info;
- const struct acpi_device_id *id;
int ret;
- id = acpi_match_device(intel_lpss_acpi_ids, &pdev->dev);
- if (!id)
+ data = device_get_match_data(&pdev->dev);
+ if (!data)
return -ENODEV;
- info = devm_kmemdup(&pdev->dev, (void *)id->driver_data, sizeof(*info),
- GFP_KERNEL);
+ info = devm_kmemdup(&pdev->dev, data, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
+ /* No need to check mem and irq here as intel_lpss_probe() does it for us */
info->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!info->mem)
- return -ENODEV;
-
info->irq = platform_get_irq(pdev, 0);
ret = intel_lpss_probe(&pdev->dev, info);
@@ -198,23 +200,19 @@ static int intel_lpss_acpi_probe(struct platform_device *pdev)
return 0;
}
-static int intel_lpss_acpi_remove(struct platform_device *pdev)
+static void intel_lpss_acpi_remove(struct platform_device *pdev)
{
intel_lpss_remove(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
-static INTEL_LPSS_PM_OPS(intel_lpss_acpi_pm_ops);
-
static struct platform_driver intel_lpss_acpi_driver = {
.probe = intel_lpss_acpi_probe,
.remove = intel_lpss_acpi_remove,
.driver = {
.name = "intel-lpss",
.acpi_match_table = intel_lpss_acpi_ids,
- .pm = &intel_lpss_acpi_pm_ops,
+ .pm = pm_ptr(&intel_lpss_pm_ops),
},
};
@@ -224,3 +222,4 @@ MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
MODULE_DESCRIPTION("Intel LPSS ACPI driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("INTEL_LPSS");
diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c
index 699f44ffff0e..8d92c895d3ae 100644
--- a/drivers/mfd/intel-lpss-pci.c
+++ b/drivers/mfd/intel-lpss-pci.c
@@ -8,28 +8,45 @@
* Mika Westerberg <mika.westerberg@linux.intel.com>
*/
-#include <linux/ioport.h>
-#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/gfp_types.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
+
#include <linux/pxa2xx_ssp.h>
+#include <asm/errno.h>
+
#include "intel-lpss.h"
-/* Some DSDTs have an unused GEXP ACPI device conflicting with I2C4 resources */
-static const struct pci_device_id ignore_resource_conflicts_ids[] = {
- /* Microsoft Surface Go (version 1) I2C4 */
- { PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, 0x9d64, 0x152d, 0x1182), },
- /* Microsoft Surface Go 2 I2C4 */
- { PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, 0x9d64, 0x152d, 0x1237), },
+static const struct pci_device_id quirk_ids[] = {
+ {
+ /* Microsoft Surface Go (version 1) I2C4 */
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, 0x9d64, 0x152d, 0x1182),
+ .driver_data = QUIRK_IGNORE_RESOURCE_CONFLICTS,
+ },
+ {
+ /* Microsoft Surface Go 2 I2C4 */
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, 0x9d64, 0x152d, 0x1237),
+ .driver_data = QUIRK_IGNORE_RESOURCE_CONFLICTS,
+ },
+ {
+ /* Dell XPS 9530 (2023) */
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_INTEL, 0x51fb, 0x1028, 0x0beb),
+ .driver_data = QUIRK_CLOCK_DIVIDER_UNITY,
+ },
{ }
};
static int intel_lpss_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
+ const struct intel_lpss_platform_info *data = (void *)id->driver_data;
+ const struct pci_device_id *quirk_pci_info;
struct intel_lpss_platform_info *info;
int ret;
@@ -37,16 +54,21 @@ static int intel_lpss_pci_probe(struct pci_dev *pdev,
if (ret)
return ret;
- info = devm_kmemdup(&pdev->dev, (void *)id->driver_data, sizeof(*info),
- GFP_KERNEL);
+ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+ if (ret < 0)
+ return ret;
+
+ info = devm_kmemdup(&pdev->dev, data, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
- info->mem = &pdev->resource[0];
- info->irq = pdev->irq;
+ /* No need to check mem and irq here as intel_lpss_probe() does it for us */
+ info->mem = pci_resource_n(pdev, 0);
+ info->irq = pci_irq_vector(pdev, 0);
- if (pci_match_id(ignore_resource_conflicts_ids, pdev))
- info->ignore_resource_conflicts = true;
+ quirk_pci_info = pci_match_id(quirk_ids, pdev);
+ if (quirk_pci_info)
+ info->quirks = quirk_pci_info->driver_data;
pdev->d3cold_delay = 0;
@@ -72,8 +94,6 @@ static void intel_lpss_pci_remove(struct pci_dev *pdev)
intel_lpss_remove(&pdev->dev);
}
-static INTEL_LPSS_PM_OPS(intel_lpss_pci_pm_ops);
-
static const struct property_entry spt_spi_properties[] = {
PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_SPT_SSP),
{ }
@@ -83,7 +103,7 @@ static const struct software_node spt_spi_node = {
.properties = spt_spi_properties,
};
-static const struct intel_lpss_platform_info spt_info = {
+static const struct intel_lpss_platform_info spt_spi_info = {
.clk_rate = 120000000,
.swnode = &spt_spi_node,
};
@@ -128,7 +148,7 @@ static const struct software_node bxt_spi_node = {
.properties = bxt_spi_properties,
};
-static const struct intel_lpss_platform_info bxt_info = {
+static const struct intel_lpss_platform_info bxt_spi_info = {
.clk_rate = 100000000,
.swnode = &bxt_spi_node,
};
@@ -196,7 +216,7 @@ static const struct software_node cnl_spi_node = {
.properties = cnl_spi_properties,
};
-static const struct intel_lpss_platform_info cnl_info = {
+static const struct intel_lpss_platform_info cnl_spi_info = {
.clk_rate = 120000000,
.swnode = &cnl_spi_node,
};
@@ -220,7 +240,7 @@ static const struct software_node tgl_spi_node = {
.properties = tgl_spi_properties,
};
-static const struct intel_lpss_platform_info tgl_info = {
+static const struct intel_lpss_platform_info tgl_spi_info = {
.clk_rate = 100000000,
.swnode = &tgl_spi_node,
};
@@ -229,8 +249,8 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* CML-LP */
{ PCI_VDEVICE(INTEL, 0x02a8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x02a9), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0x02aa), (kernel_ulong_t)&cnl_info },
- { PCI_VDEVICE(INTEL, 0x02ab), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x02aa), (kernel_ulong_t)&cnl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x02ab), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x02c5), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02c6), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02c7), (kernel_ulong_t)&spt_uart_info },
@@ -238,18 +258,18 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x02e9), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02ea), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02eb), (kernel_ulong_t)&cnl_i2c_info },
- { PCI_VDEVICE(INTEL, 0x02fb), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x02fb), (kernel_ulong_t)&cnl_spi_info },
/* CML-H */
{ PCI_VDEVICE(INTEL, 0x06a8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x06a9), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0x06aa), (kernel_ulong_t)&cnl_info },
- { PCI_VDEVICE(INTEL, 0x06ab), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x06aa), (kernel_ulong_t)&cnl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x06ab), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x06c7), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x06e8), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x06e9), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x06ea), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x06eb), (kernel_ulong_t)&cnl_i2c_info },
- { PCI_VDEVICE(INTEL, 0x06fb), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x06fb), (kernel_ulong_t)&cnl_spi_info },
/* BXT A-Step */
{ PCI_VDEVICE(INTEL, 0x0aac), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x0aae), (kernel_ulong_t)&bxt_i2c_info },
@@ -262,9 +282,9 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x0abc), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x0abe), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x0ac0), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x0ac2), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x0ac4), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x0ac6), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x0ac2), (kernel_ulong_t)&bxt_spi_info },
+ { PCI_VDEVICE(INTEL, 0x0ac4), (kernel_ulong_t)&bxt_spi_info },
+ { PCI_VDEVICE(INTEL, 0x0ac6), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x0aee), (kernel_ulong_t)&bxt_uart_info },
/* BXT B-Step */
{ PCI_VDEVICE(INTEL, 0x1aac), (kernel_ulong_t)&bxt_i2c_info },
@@ -278,9 +298,9 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x1abc), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x1abe), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x1ac0), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x1ac2), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x1ac4), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x1ac6), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x1ac2), (kernel_ulong_t)&bxt_spi_info },
+ { PCI_VDEVICE(INTEL, 0x1ac4), (kernel_ulong_t)&bxt_spi_info },
+ { PCI_VDEVICE(INTEL, 0x1ac6), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x1aee), (kernel_ulong_t)&bxt_uart_info },
/* EBG */
{ PCI_VDEVICE(INTEL, 0x1bad), (kernel_ulong_t)&bxt_uart_info },
@@ -297,15 +317,15 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x31bc), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x31be), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x31c0), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x31c2), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x31c4), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x31c6), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x31c2), (kernel_ulong_t)&bxt_spi_info },
+ { PCI_VDEVICE(INTEL, 0x31c4), (kernel_ulong_t)&bxt_spi_info },
+ { PCI_VDEVICE(INTEL, 0x31c6), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x31ee), (kernel_ulong_t)&bxt_uart_info },
/* ICL-LP */
{ PCI_VDEVICE(INTEL, 0x34a8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x34a9), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0x34aa), (kernel_ulong_t)&cnl_info },
- { PCI_VDEVICE(INTEL, 0x34ab), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x34aa), (kernel_ulong_t)&cnl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x34ab), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x34c5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34c6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34c7), (kernel_ulong_t)&spt_uart_info },
@@ -313,15 +333,15 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x34e9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34ea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34eb), (kernel_ulong_t)&bxt_i2c_info },
- { PCI_VDEVICE(INTEL, 0x34fb), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x34fb), (kernel_ulong_t)&cnl_spi_info },
/* ICL-N */
{ PCI_VDEVICE(INTEL, 0x38a8), (kernel_ulong_t)&spt_uart_info },
/* TGL-H */
{ PCI_VDEVICE(INTEL, 0x43a7), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x43a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x43a9), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x43aa), (kernel_ulong_t)&tgl_info },
- { PCI_VDEVICE(INTEL, 0x43ab), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x43aa), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x43ab), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x43ad), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x43ae), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x43d8), (kernel_ulong_t)&bxt_i2c_info },
@@ -330,14 +350,14 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x43e9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x43ea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x43eb), (kernel_ulong_t)&bxt_i2c_info },
- { PCI_VDEVICE(INTEL, 0x43fb), (kernel_ulong_t)&tgl_info },
- { PCI_VDEVICE(INTEL, 0x43fd), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x43fb), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x43fd), (kernel_ulong_t)&tgl_spi_info },
/* EHL */
{ PCI_VDEVICE(INTEL, 0x4b28), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x4b29), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x4b2a), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x4b2b), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x4b37), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x4b2a), (kernel_ulong_t)&bxt_spi_info },
+ { PCI_VDEVICE(INTEL, 0x4b2b), (kernel_ulong_t)&bxt_spi_info },
+ { PCI_VDEVICE(INTEL, 0x4b37), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x4b44), (kernel_ulong_t)&ehl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4b45), (kernel_ulong_t)&ehl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4b4b), (kernel_ulong_t)&ehl_i2c_info },
@@ -347,11 +367,24 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x4b79), (kernel_ulong_t)&ehl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4b7a), (kernel_ulong_t)&ehl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4b7b), (kernel_ulong_t)&ehl_i2c_info },
+ /* WCL */
+ { PCI_VDEVICE(INTEL, 0x4d25), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x4d26), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x4d27), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x4d30), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x4d46), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x4d50), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x4d51), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x4d52), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x4d78), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x4d79), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x4d7a), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x4d7b), (kernel_ulong_t)&ehl_i2c_info },
/* JSL */
{ PCI_VDEVICE(INTEL, 0x4da8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x4da9), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0x4daa), (kernel_ulong_t)&cnl_info },
- { PCI_VDEVICE(INTEL, 0x4dab), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x4daa), (kernel_ulong_t)&cnl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x4dab), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x4dc5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4dc6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4dc7), (kernel_ulong_t)&spt_uart_info },
@@ -359,12 +392,12 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x4de9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4dea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4deb), (kernel_ulong_t)&bxt_i2c_info },
- { PCI_VDEVICE(INTEL, 0x4dfb), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x4dfb), (kernel_ulong_t)&cnl_spi_info },
/* ADL-P */
{ PCI_VDEVICE(INTEL, 0x51a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x51a9), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x51aa), (kernel_ulong_t)&tgl_info },
- { PCI_VDEVICE(INTEL, 0x51ab), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x51aa), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x51ab), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x51c5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x51c6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x51c7), (kernel_ulong_t)&bxt_uart_info },
@@ -374,12 +407,12 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x51e9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x51ea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x51eb), (kernel_ulong_t)&bxt_i2c_info },
- { PCI_VDEVICE(INTEL, 0x51fb), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x51fb), (kernel_ulong_t)&tgl_spi_info },
/* ADL-M */
{ PCI_VDEVICE(INTEL, 0x54a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x54a9), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x54aa), (kernel_ulong_t)&tgl_info },
- { PCI_VDEVICE(INTEL, 0x54ab), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x54aa), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x54ab), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x54c5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x54c6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x54c7), (kernel_ulong_t)&bxt_uart_info },
@@ -387,7 +420,7 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x54e9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x54ea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x54eb), (kernel_ulong_t)&bxt_i2c_info },
- { PCI_VDEVICE(INTEL, 0x54fb), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x54fb), (kernel_ulong_t)&tgl_spi_info },
/* APL */
{ PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&apl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&apl_i2c_info },
@@ -400,46 +433,59 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x5abc), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x5abe), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x5ac0), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x5ac2), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x5ac4), (kernel_ulong_t)&bxt_info },
- { PCI_VDEVICE(INTEL, 0x5ac6), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x5ac2), (kernel_ulong_t)&bxt_spi_info },
+ { PCI_VDEVICE(INTEL, 0x5ac4), (kernel_ulong_t)&bxt_spi_info },
+ { PCI_VDEVICE(INTEL, 0x5ac6), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x5aee), (kernel_ulong_t)&bxt_uart_info },
+ /* ARL-H */
+ { PCI_VDEVICE(INTEL, 0x7725), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x7726), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x7727), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x7730), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x7746), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x7750), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x7751), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x7752), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x7778), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x7779), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x777a), (kernel_ulong_t)&bxt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x777b), (kernel_ulong_t)&bxt_i2c_info },
/* RPL-S */
{ PCI_VDEVICE(INTEL, 0x7a28), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7a29), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x7a2a), (kernel_ulong_t)&tgl_info },
- { PCI_VDEVICE(INTEL, 0x7a2b), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x7a2a), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x7a2b), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7a4c), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a4d), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a4e), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a4f), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a5c), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x7a79), (kernel_ulong_t)&tgl_info },
- { PCI_VDEVICE(INTEL, 0x7a7b), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x7a79), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x7a7b), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7a7c), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a7d), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a7e), (kernel_ulong_t)&bxt_uart_info },
/* ADL-S */
{ PCI_VDEVICE(INTEL, 0x7aa8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7aa9), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x7aaa), (kernel_ulong_t)&tgl_info },
- { PCI_VDEVICE(INTEL, 0x7aab), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x7aaa), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x7aab), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7acc), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7acd), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7ace), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7acf), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7adc), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x7af9), (kernel_ulong_t)&tgl_info },
- { PCI_VDEVICE(INTEL, 0x7afb), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x7af9), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x7afb), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7afc), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7afd), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7afe), (kernel_ulong_t)&bxt_uart_info },
/* MTL-P */
{ PCI_VDEVICE(INTEL, 0x7e25), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7e26), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x7e27), (kernel_ulong_t)&tgl_info },
- { PCI_VDEVICE(INTEL, 0x7e30), (kernel_ulong_t)&tgl_info },
- { PCI_VDEVICE(INTEL, 0x7e46), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x7e27), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x7e30), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x7e46), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7e50), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7e51), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7e52), (kernel_ulong_t)&bxt_uart_info },
@@ -450,22 +496,22 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* MTP-S */
{ PCI_VDEVICE(INTEL, 0x7f28), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7f29), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x7f2a), (kernel_ulong_t)&tgl_info },
- { PCI_VDEVICE(INTEL, 0x7f2b), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x7f2a), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x7f2b), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7f4c), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7f4d), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7f4e), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7f4f), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7f5c), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7f5d), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x7f5e), (kernel_ulong_t)&tgl_info },
- { PCI_VDEVICE(INTEL, 0x7f5f), (kernel_ulong_t)&tgl_info },
+ { PCI_VDEVICE(INTEL, 0x7f5e), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x7f5f), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7f7a), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7f7b), (kernel_ulong_t)&bxt_i2c_info },
/* LKF */
{ PCI_VDEVICE(INTEL, 0x98a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x98a9), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0x98aa), (kernel_ulong_t)&bxt_info },
+ { PCI_VDEVICE(INTEL, 0x98aa), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x98c5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x98c6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x98c7), (kernel_ulong_t)&bxt_uart_info },
@@ -476,8 +522,8 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* SPT-LP */
{ PCI_VDEVICE(INTEL, 0x9d27), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x9d28), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0x9d29), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0x9d2a), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0x9d29), (kernel_ulong_t)&spt_spi_info },
+ { PCI_VDEVICE(INTEL, 0x9d2a), (kernel_ulong_t)&spt_spi_info },
{ PCI_VDEVICE(INTEL, 0x9d60), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9d61), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9d62), (kernel_ulong_t)&spt_i2c_info },
@@ -488,8 +534,8 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* CNL-LP */
{ PCI_VDEVICE(INTEL, 0x9da8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x9da9), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0x9daa), (kernel_ulong_t)&cnl_info },
- { PCI_VDEVICE(INTEL, 0x9dab), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x9daa), (kernel_ulong_t)&cnl_spi_info },
+ { PCI_VDEVICE(INTEL, 0x9dab), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x9dc5), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9dc6), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9dc7), (kernel_ulong_t)&spt_uart_info },
@@ -497,12 +543,12 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x9de9), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9dea), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9deb), (kernel_ulong_t)&cnl_i2c_info },
- { PCI_VDEVICE(INTEL, 0x9dfb), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0x9dfb), (kernel_ulong_t)&cnl_spi_info },
/* TGL-LP */
{ PCI_VDEVICE(INTEL, 0xa0a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa0a9), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0xa0aa), (kernel_ulong_t)&cnl_info },
- { PCI_VDEVICE(INTEL, 0xa0ab), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0xa0aa), (kernel_ulong_t)&cnl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xa0ab), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa0c5), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0c6), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0c7), (kernel_ulong_t)&bxt_uart_info },
@@ -512,20 +558,20 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0xa0db), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa0dc), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa0dd), (kernel_ulong_t)&bxt_uart_info },
- { PCI_VDEVICE(INTEL, 0xa0de), (kernel_ulong_t)&cnl_info },
- { PCI_VDEVICE(INTEL, 0xa0df), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0xa0de), (kernel_ulong_t)&cnl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xa0df), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa0e8), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0e9), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0ea), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0eb), (kernel_ulong_t)&spt_i2c_info },
- { PCI_VDEVICE(INTEL, 0xa0fb), (kernel_ulong_t)&cnl_info },
- { PCI_VDEVICE(INTEL, 0xa0fd), (kernel_ulong_t)&cnl_info },
- { PCI_VDEVICE(INTEL, 0xa0fe), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0xa0fb), (kernel_ulong_t)&cnl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xa0fd), (kernel_ulong_t)&cnl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xa0fe), (kernel_ulong_t)&cnl_spi_info },
/* SPT-H */
{ PCI_VDEVICE(INTEL, 0xa127), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa128), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0xa129), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0xa12a), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0xa129), (kernel_ulong_t)&spt_spi_info },
+ { PCI_VDEVICE(INTEL, 0xa12a), (kernel_ulong_t)&spt_spi_info },
{ PCI_VDEVICE(INTEL, 0xa160), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa161), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa162), (kernel_ulong_t)&spt_i2c_info },
@@ -533,8 +579,8 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* KBL-H */
{ PCI_VDEVICE(INTEL, 0xa2a7), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa2a8), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0xa2a9), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0xa2aa), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0xa2a9), (kernel_ulong_t)&spt_spi_info },
+ { PCI_VDEVICE(INTEL, 0xa2aa), (kernel_ulong_t)&spt_spi_info },
{ PCI_VDEVICE(INTEL, 0xa2e0), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa2e1), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa2e2), (kernel_ulong_t)&spt_i2c_info },
@@ -543,24 +589,63 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* CNL-H */
{ PCI_VDEVICE(INTEL, 0xa328), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa329), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0xa32a), (kernel_ulong_t)&cnl_info },
- { PCI_VDEVICE(INTEL, 0xa32b), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0xa32a), (kernel_ulong_t)&cnl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xa32b), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa347), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa368), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa369), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa36a), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa36b), (kernel_ulong_t)&cnl_i2c_info },
- { PCI_VDEVICE(INTEL, 0xa37b), (kernel_ulong_t)&cnl_info },
+ { PCI_VDEVICE(INTEL, 0xa37b), (kernel_ulong_t)&cnl_spi_info },
/* CML-V */
{ PCI_VDEVICE(INTEL, 0xa3a7), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa3a8), (kernel_ulong_t)&spt_uart_info },
- { PCI_VDEVICE(INTEL, 0xa3a9), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0xa3aa), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0xa3a9), (kernel_ulong_t)&spt_spi_info },
+ { PCI_VDEVICE(INTEL, 0xa3aa), (kernel_ulong_t)&spt_spi_info },
{ PCI_VDEVICE(INTEL, 0xa3e0), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa3e1), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa3e2), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa3e3), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa3e6), (kernel_ulong_t)&spt_uart_info },
+ /* LNL-M */
+ { PCI_VDEVICE(INTEL, 0xa825), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0xa826), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0xa827), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xa830), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xa846), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xa850), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xa851), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xa852), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0xa878), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xa879), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xa87a), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xa87b), (kernel_ulong_t)&ehl_i2c_info },
+ /* PTL-H */
+ { PCI_VDEVICE(INTEL, 0xe325), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0xe326), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0xe327), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xe330), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xe346), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xe350), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xe351), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xe352), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0xe378), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xe379), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xe37a), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xe37b), (kernel_ulong_t)&ehl_i2c_info },
+ /* PTL-P */
+ { PCI_VDEVICE(INTEL, 0xe425), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0xe426), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0xe427), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xe430), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xe446), (kernel_ulong_t)&tgl_spi_info },
+ { PCI_VDEVICE(INTEL, 0xe450), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xe451), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xe452), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0xe478), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xe479), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xe47a), (kernel_ulong_t)&ehl_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xe47b), (kernel_ulong_t)&ehl_i2c_info },
{ }
};
MODULE_DEVICE_TABLE(pci, intel_lpss_pci_ids);
@@ -571,7 +656,7 @@ static struct pci_driver intel_lpss_pci_driver = {
.probe = intel_lpss_pci_probe,
.remove = intel_lpss_pci_remove,
.driver = {
- .pm = &intel_lpss_pci_pm_ops,
+ .pm = pm_ptr(&intel_lpss_pm_ops),
},
};
@@ -581,3 +666,4 @@ MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
MODULE_DESCRIPTION("Intel LPSS PCI driver");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("INTEL_LPSS");
diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c
index 9591b354072a..63d6694f7145 100644
--- a/drivers/mfd/intel-lpss.c
+++ b/drivers/mfd/intel-lpss.c
@@ -10,26 +10,34 @@
* Jarkko Nikula <jarkko.nikula@linux.intel.com>
*/
-#include <linux/clk.h>
+#include <linux/array_size.h>
+#include <linux/bits.h>
#include <linux/clkdev.h>
+#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gfp_types.h>
#include <linux/idr.h>
#include <linux/io.h>
#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/pm.h>
#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
-#include <linux/property.h>
-#include <linux/seq_file.h>
+#include <linux/sprintf.h>
+#include <linux/types.h>
+
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/dma/idma64.h>
#include "intel-lpss.h"
+struct dentry;
+
#define LPSS_DEV_OFFSET 0x000
#define LPSS_DEV_SIZE 0x200
#define LPSS_PRIV_OFFSET 0x200
@@ -292,6 +300,7 @@ static int intel_lpss_register_clock_divider(struct intel_lpss *lpss,
{
char name[32];
struct clk *tmp = *clk;
+ int ret;
snprintf(name, sizeof(name), "%s-enable", devname);
tmp = clk_register_gate(NULL, name, __clk_get_name(tmp), 0,
@@ -301,13 +310,19 @@ static int intel_lpss_register_clock_divider(struct intel_lpss *lpss,
snprintf(name, sizeof(name), "%s-div", devname);
tmp = clk_register_fractional_divider(NULL, name, __clk_get_name(tmp),
+ 0, lpss->priv, 1, 15, 16, 15,
CLK_FRAC_DIVIDER_POWER_OF_TWO_PS,
- lpss->priv, 1, 15, 16, 15, 0,
NULL);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
*clk = tmp;
+ if (lpss->info->quirks & QUIRK_CLOCK_DIVIDER_UNITY) {
+ ret = clk_set_rate(tmp, lpss->info->clk_rate);
+ if (ret)
+ return ret;
+ }
+
snprintf(name, sizeof(name), "%s-update", devname);
tmp = clk_register_gate(NULL, name, __clk_get_name(tmp),
CLK_SET_RATE_PARENT, lpss->priv, 31, 0, NULL);
@@ -378,9 +393,12 @@ int intel_lpss_probe(struct device *dev,
struct intel_lpss *lpss;
int ret;
- if (!info || !info->mem || info->irq <= 0)
+ if (!info || !info->mem)
return -EINVAL;
+ if (info->irq < 0)
+ return info->irq;
+
lpss = devm_kzalloc(dev, sizeof(*lpss), GFP_KERNEL);
if (!lpss)
return -ENOMEM;
@@ -401,11 +419,11 @@ int intel_lpss_probe(struct device *dev,
return ret;
lpss->cell->swnode = info->swnode;
- lpss->cell->ignore_resource_conflicts = info->ignore_resource_conflicts;
+ lpss->cell->ignore_resource_conflicts = info->quirks & QUIRK_IGNORE_RESOURCE_CONFLICTS;
intel_lpss_init_dev(lpss);
- lpss->devid = ida_simple_get(&intel_lpss_devid_ida, 0, 0, GFP_KERNEL);
+ lpss->devid = ida_alloc(&intel_lpss_devid_ida, GFP_KERNEL);
if (lpss->devid < 0)
return lpss->devid;
@@ -442,11 +460,11 @@ err_remove_ltr:
intel_lpss_unregister_clock(lpss);
err_clk_register:
- ida_simple_remove(&intel_lpss_devid_ida, lpss->devid);
+ ida_free(&intel_lpss_devid_ida, lpss->devid);
return ret;
}
-EXPORT_SYMBOL_GPL(intel_lpss_probe);
+EXPORT_SYMBOL_NS_GPL(intel_lpss_probe, "INTEL_LPSS");
void intel_lpss_remove(struct device *dev)
{
@@ -456,20 +474,19 @@ void intel_lpss_remove(struct device *dev)
intel_lpss_debugfs_remove(lpss);
intel_lpss_ltr_hide(lpss);
intel_lpss_unregister_clock(lpss);
- ida_simple_remove(&intel_lpss_devid_ida, lpss->devid);
+ ida_free(&intel_lpss_devid_ida, lpss->devid);
}
-EXPORT_SYMBOL_GPL(intel_lpss_remove);
+EXPORT_SYMBOL_NS_GPL(intel_lpss_remove, "INTEL_LPSS");
-#ifdef CONFIG_PM
static int resume_lpss_device(struct device *dev, void *data)
{
- if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
+ if (!dev_pm_smart_suspend(dev))
pm_runtime_resume(dev);
return 0;
}
-int intel_lpss_prepare(struct device *dev)
+static int intel_lpss_prepare(struct device *dev)
{
/*
* Resume both child devices before entering system sleep. This
@@ -478,9 +495,8 @@ int intel_lpss_prepare(struct device *dev)
device_for_each_child_reverse(dev, NULL, resume_lpss_device);
return 0;
}
-EXPORT_SYMBOL_GPL(intel_lpss_prepare);
-int intel_lpss_suspend(struct device *dev)
+static int intel_lpss_suspend(struct device *dev)
{
struct intel_lpss *lpss = dev_get_drvdata(dev);
unsigned int i;
@@ -499,9 +515,8 @@ int intel_lpss_suspend(struct device *dev)
return 0;
}
-EXPORT_SYMBOL_GPL(intel_lpss_suspend);
-int intel_lpss_resume(struct device *dev)
+static int intel_lpss_resume(struct device *dev)
{
struct intel_lpss *lpss = dev_get_drvdata(dev);
unsigned int i;
@@ -514,8 +529,12 @@ int intel_lpss_resume(struct device *dev)
return 0;
}
-EXPORT_SYMBOL_GPL(intel_lpss_resume);
-#endif
+
+EXPORT_NS_GPL_DEV_PM_OPS(intel_lpss_pm_ops, INTEL_LPSS) = {
+ .prepare = pm_sleep_ptr(&intel_lpss_prepare),
+ LATE_SYSTEM_SLEEP_PM_OPS(intel_lpss_suspend, intel_lpss_resume)
+ RUNTIME_PM_OPS(intel_lpss_suspend, intel_lpss_resume, NULL)
+};
static int __init intel_lpss_init(void)
{
diff --git a/drivers/mfd/intel-lpss.h b/drivers/mfd/intel-lpss.h
index 062ce95b68b9..6f8f668f4c6f 100644
--- a/drivers/mfd/intel-lpss.h
+++ b/drivers/mfd/intel-lpss.h
@@ -11,16 +11,28 @@
#ifndef __MFD_INTEL_LPSS_H
#define __MFD_INTEL_LPSS_H
+#include <linux/bits.h>
#include <linux/pm.h>
+/*
+ * Some DSDTs have an unused GEXP ACPI device conflicting with I2C4 resources.
+ * Set to ignore resource conflicts with ACPI declared SystemMemory regions.
+ */
+#define QUIRK_IGNORE_RESOURCE_CONFLICTS BIT(0)
+/*
+ * Some devices have misconfigured clock divider due to a firmware bug.
+ * Set this to force the clock divider to 1:1 ratio.
+ */
+#define QUIRK_CLOCK_DIVIDER_UNITY BIT(1)
+
struct device;
struct resource;
struct software_node;
struct intel_lpss_platform_info {
struct resource *mem;
- bool ignore_resource_conflicts;
int irq;
+ unsigned int quirks;
unsigned long clk_rate;
const char *clk_con_id;
const struct software_node *swnode;
@@ -30,32 +42,6 @@ int intel_lpss_probe(struct device *dev,
const struct intel_lpss_platform_info *info);
void intel_lpss_remove(struct device *dev);
-#ifdef CONFIG_PM
-int intel_lpss_prepare(struct device *dev);
-int intel_lpss_suspend(struct device *dev);
-int intel_lpss_resume(struct device *dev);
-
-#ifdef CONFIG_PM_SLEEP
-#define INTEL_LPSS_SLEEP_PM_OPS \
- .prepare = intel_lpss_prepare, \
- SET_LATE_SYSTEM_SLEEP_PM_OPS(intel_lpss_suspend, intel_lpss_resume)
-#else
-#define INTEL_LPSS_SLEEP_PM_OPS
-#endif
-
-#define INTEL_LPSS_RUNTIME_PM_OPS \
- .runtime_suspend = intel_lpss_suspend, \
- .runtime_resume = intel_lpss_resume,
-
-#else /* !CONFIG_PM */
-#define INTEL_LPSS_SLEEP_PM_OPS
-#define INTEL_LPSS_RUNTIME_PM_OPS
-#endif /* CONFIG_PM */
-
-#define INTEL_LPSS_PM_OPS(name) \
-const struct dev_pm_ops name = { \
- INTEL_LPSS_SLEEP_PM_OPS \
- INTEL_LPSS_RUNTIME_PM_OPS \
-}
+extern const struct dev_pm_ops intel_lpss_pm_ops;
#endif /* __MFD_INTEL_LPSS_H */
diff --git a/drivers/mfd/intel-m10-bmc-core.c b/drivers/mfd/intel-m10-bmc-core.c
index 8ad5b3821584..e930161bf65e 100644
--- a/drivers/mfd/intel-m10-bmc-core.c
+++ b/drivers/mfd/intel-m10-bmc-core.c
@@ -22,7 +22,7 @@ void m10bmc_fw_state_set(struct intel_m10bmc *m10bmc, enum m10bmc_fw_state new_s
m10bmc->bmcfw_state = new_state;
up_write(&m10bmc->bmcfw_lock);
}
-EXPORT_SYMBOL_NS_GPL(m10bmc_fw_state_set, INTEL_M10_BMC_CORE);
+EXPORT_SYMBOL_NS_GPL(m10bmc_fw_state_set, "INTEL_M10_BMC_CORE");
/*
* For some Intel FPGA devices, the BMC firmware is not available to service
@@ -75,7 +75,7 @@ int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned i
return ret;
}
-EXPORT_SYMBOL_NS_GPL(m10bmc_sys_read, INTEL_M10_BMC_CORE);
+EXPORT_SYMBOL_NS_GPL(m10bmc_sys_read, "INTEL_M10_BMC_CORE");
int m10bmc_sys_update_bits(struct intel_m10bmc *m10bmc, unsigned int offset,
unsigned int msk, unsigned int val)
@@ -95,7 +95,7 @@ int m10bmc_sys_update_bits(struct intel_m10bmc *m10bmc, unsigned int offset,
return ret;
}
-EXPORT_SYMBOL_NS_GPL(m10bmc_sys_update_bits, INTEL_M10_BMC_CORE);
+EXPORT_SYMBOL_NS_GPL(m10bmc_sys_update_bits, "INTEL_M10_BMC_CORE");
static ssize_t bmc_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -183,7 +183,7 @@ const struct attribute_group *m10bmc_dev_groups[] = {
&m10bmc_group,
NULL,
};
-EXPORT_SYMBOL_NS_GPL(m10bmc_dev_groups, INTEL_M10_BMC_CORE);
+EXPORT_SYMBOL_NS_GPL(m10bmc_dev_groups, "INTEL_M10_BMC_CORE");
int m10bmc_dev_init(struct intel_m10bmc *m10bmc, const struct intel_m10bmc_platform_info *info)
{
@@ -201,7 +201,7 @@ int m10bmc_dev_init(struct intel_m10bmc *m10bmc, const struct intel_m10bmc_platf
return ret;
}
-EXPORT_SYMBOL_NS_GPL(m10bmc_dev_init, INTEL_M10_BMC_CORE);
+EXPORT_SYMBOL_NS_GPL(m10bmc_dev_init, "INTEL_M10_BMC_CORE");
MODULE_DESCRIPTION("Intel MAX 10 BMC core driver");
MODULE_AUTHOR("Intel Corporation");
diff --git a/drivers/mfd/intel-m10-bmc-pmci.c b/drivers/mfd/intel-m10-bmc-pmci.c
index 0392ef8b57d8..d213c6ec04ba 100644
--- a/drivers/mfd/intel-m10-bmc-pmci.c
+++ b/drivers/mfd/intel-m10-bmc-pmci.c
@@ -336,7 +336,7 @@ static const struct regmap_access_table m10bmc_pmci_access_table = {
.n_yes_ranges = ARRAY_SIZE(m10bmc_pmci_regmap_range),
};
-static struct regmap_config m10bmc_pmci_regmap_config = {
+static const struct regmap_config m10bmc_pmci_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
@@ -370,6 +370,7 @@ static const struct m10bmc_csr_map m10bmc_n6000_csr_map = {
.pr_reh_addr = M10BMC_N6000_PR_REH_ADDR,
.pr_magic = M10BMC_N6000_PR_PROG_MAGIC,
.rsu_update_counter = M10BMC_N6000_STAGING_FLASH_COUNT,
+ .staging_size = M10BMC_STAGING_SIZE,
};
static const struct intel_m10bmc_platform_info m10bmc_pmci_n6000 = {
@@ -453,4 +454,4 @@ module_dfl_driver(m10bmc_pmci_driver);
MODULE_DESCRIPTION("MAX10 BMC PMCI-based interface");
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(INTEL_M10_BMC_CORE);
+MODULE_IMPORT_NS("INTEL_M10_BMC_CORE");
diff --git a/drivers/mfd/intel-m10-bmc-spi.c b/drivers/mfd/intel-m10-bmc-spi.c
index cbeb7de9e041..cfa620f0c70e 100644
--- a/drivers/mfd/intel-m10-bmc-spi.c
+++ b/drivers/mfd/intel-m10-bmc-spi.c
@@ -24,7 +24,7 @@ static const struct regmap_access_table m10bmc_access_table = {
.n_yes_ranges = ARRAY_SIZE(m10bmc_regmap_range),
};
-static struct regmap_config intel_m10bmc_regmap_config = {
+static const struct regmap_config intel_m10bmc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
@@ -109,6 +109,7 @@ static const struct m10bmc_csr_map m10bmc_n3000_csr_map = {
.pr_reh_addr = M10BMC_N3000_PR_REH_ADDR,
.pr_magic = M10BMC_N3000_PR_PROG_MAGIC,
.rsu_update_counter = M10BMC_N3000_STAGING_FLASH_COUNT,
+ .staging_size = M10BMC_STAGING_SIZE,
};
static struct mfd_cell m10bmc_d5005_subdevs[] = {
@@ -180,4 +181,4 @@ MODULE_DESCRIPTION("Intel MAX 10 BMC SPI bus interface");
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("spi:intel-m10-bmc");
-MODULE_IMPORT_NS(INTEL_M10_BMC_CORE);
+MODULE_IMPORT_NS("INTEL_M10_BMC_CORE");
diff --git a/drivers/mfd/intel_pmc_bxt.c b/drivers/mfd/intel_pmc_bxt.c
index 9f01d38acc7f..e405d7513ca1 100644
--- a/drivers/mfd/intel_pmc_bxt.c
+++ b/drivers/mfd/intel_pmc_bxt.c
@@ -23,8 +23,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/platform_data/itco_wdt.h>
-
-#include <asm/intel_scu_ipc.h>
+#include <linux/platform_data/x86/intel_scu_ipc.h>
/* Residency with clock rate at 19.2MHz to usecs */
#define S0IX_RESIDENCY_IN_USECS(d, s) \
diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c
index 8dac0d41f64f..9d89171d83f9 100644
--- a/drivers/mfd/intel_soc_pmic_bxtwc.c
+++ b/drivers/mfd/intel_soc_pmic_bxtwc.c
@@ -6,17 +6,27 @@
*/
#include <linux/acpi.h>
+#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/delay.h>
+#include <linux/device.h>
#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gfp_types.h>
#include <linux/interrupt.h>
-#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/kstrtox.h>
#include <linux/mfd/core.h>
#include <linux/mfd/intel_soc_pmic.h>
#include <linux/mfd/intel_soc_pmic_bxtwc.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-
-#include <asm/intel_scu_ipc.h>
+#include <linux/platform_data/x86/intel_scu_ipc.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
/* PMIC device registers */
#define REG_ADDR_MASK GENMASK(15, 8)
@@ -138,7 +148,7 @@ static const struct regmap_irq bxtwc_regmap_irqs_crit[] = {
REGMAP_IRQ_REG(BXTWC_CRIT_IRQ, 0, GENMASK(1, 0)),
};
-static struct regmap_irq_chip bxtwc_regmap_irq_chip = {
+static const struct regmap_irq_chip bxtwc_regmap_irq_chip = {
.name = "bxtwc_irq_chip",
.status_base = BXTWC_IRQLVL1,
.mask_base = BXTWC_MIRQLVL1,
@@ -147,8 +157,9 @@ static struct regmap_irq_chip bxtwc_regmap_irq_chip = {
.num_regs = 1,
};
-static struct regmap_irq_chip bxtwc_regmap_irq_chip_pwrbtn = {
+static const struct regmap_irq_chip bxtwc_regmap_irq_chip_pwrbtn = {
.name = "bxtwc_irq_chip_pwrbtn",
+ .domain_suffix = "PWRBTN",
.status_base = BXTWC_PWRBTNIRQ,
.mask_base = BXTWC_MPWRBTNIRQ,
.irqs = bxtwc_regmap_irqs_pwrbtn,
@@ -156,8 +167,9 @@ static struct regmap_irq_chip bxtwc_regmap_irq_chip_pwrbtn = {
.num_regs = 1,
};
-static struct regmap_irq_chip bxtwc_regmap_irq_chip_tmu = {
+static const struct regmap_irq_chip bxtwc_regmap_irq_chip_tmu = {
.name = "bxtwc_irq_chip_tmu",
+ .domain_suffix = "TMU",
.status_base = BXTWC_TMUIRQ,
.mask_base = BXTWC_MTMUIRQ,
.irqs = bxtwc_regmap_irqs_tmu,
@@ -165,8 +177,9 @@ static struct regmap_irq_chip bxtwc_regmap_irq_chip_tmu = {
.num_regs = 1,
};
-static struct regmap_irq_chip bxtwc_regmap_irq_chip_bcu = {
+static const struct regmap_irq_chip bxtwc_regmap_irq_chip_bcu = {
.name = "bxtwc_irq_chip_bcu",
+ .domain_suffix = "BCU",
.status_base = BXTWC_BCUIRQ,
.mask_base = BXTWC_MBCUIRQ,
.irqs = bxtwc_regmap_irqs_bcu,
@@ -174,8 +187,9 @@ static struct regmap_irq_chip bxtwc_regmap_irq_chip_bcu = {
.num_regs = 1,
};
-static struct regmap_irq_chip bxtwc_regmap_irq_chip_adc = {
+static const struct regmap_irq_chip bxtwc_regmap_irq_chip_adc = {
.name = "bxtwc_irq_chip_adc",
+ .domain_suffix = "ADC",
.status_base = BXTWC_ADCIRQ,
.mask_base = BXTWC_MADCIRQ,
.irqs = bxtwc_regmap_irqs_adc,
@@ -183,8 +197,9 @@ static struct regmap_irq_chip bxtwc_regmap_irq_chip_adc = {
.num_regs = 1,
};
-static struct regmap_irq_chip bxtwc_regmap_irq_chip_chgr = {
+static const struct regmap_irq_chip bxtwc_regmap_irq_chip_chgr = {
.name = "bxtwc_irq_chip_chgr",
+ .domain_suffix = "CHGR",
.status_base = BXTWC_CHGR0IRQ,
.mask_base = BXTWC_MCHGR0IRQ,
.irqs = bxtwc_regmap_irqs_chgr,
@@ -192,8 +207,9 @@ static struct regmap_irq_chip bxtwc_regmap_irq_chip_chgr = {
.num_regs = 2,
};
-static struct regmap_irq_chip bxtwc_regmap_irq_chip_crit = {
+static const struct regmap_irq_chip bxtwc_regmap_irq_chip_crit = {
.name = "bxtwc_irq_chip_crit",
+ .domain_suffix = "CRIT",
.status_base = BXTWC_CRITIRQ,
.mask_base = BXTWC_MCRITIRQ,
.irqs = bxtwc_regmap_irqs_crit,
@@ -232,43 +248,54 @@ static const struct resource tmu_resources[] = {
static struct mfd_cell bxt_wc_dev[] = {
{
- .name = "bxt_wcove_gpadc",
- .num_resources = ARRAY_SIZE(adc_resources),
- .resources = adc_resources,
- },
- {
.name = "bxt_wcove_thermal",
.num_resources = ARRAY_SIZE(thermal_resources),
.resources = thermal_resources,
},
{
- .name = "bxt_wcove_usbc",
- .num_resources = ARRAY_SIZE(usbc_resources),
- .resources = usbc_resources,
+ .name = "bxt_wcove_gpio",
+ .num_resources = ARRAY_SIZE(gpio_resources),
+ .resources = gpio_resources,
},
{
- .name = "bxt_wcove_ext_charger",
- .num_resources = ARRAY_SIZE(charger_resources),
- .resources = charger_resources,
+ .name = "bxt_wcove_region",
+ },
+};
+
+static const struct mfd_cell bxt_wc_tmu_dev[] = {
+ {
+ .name = "bxt_wcove_tmu",
+ .num_resources = ARRAY_SIZE(tmu_resources),
+ .resources = tmu_resources,
},
+};
+
+static const struct mfd_cell bxt_wc_bcu_dev[] = {
{
.name = "bxt_wcove_bcu",
.num_resources = ARRAY_SIZE(bcu_resources),
.resources = bcu_resources,
},
+};
+
+static const struct mfd_cell bxt_wc_adc_dev[] = {
{
- .name = "bxt_wcove_tmu",
- .num_resources = ARRAY_SIZE(tmu_resources),
- .resources = tmu_resources,
+ .name = "bxt_wcove_gpadc",
+ .num_resources = ARRAY_SIZE(adc_resources),
+ .resources = adc_resources,
},
+};
+static struct mfd_cell bxt_wc_chgr_dev[] = {
{
- .name = "bxt_wcove_gpio",
- .num_resources = ARRAY_SIZE(gpio_resources),
- .resources = gpio_resources,
+ .name = "bxt_wcove_usbc",
+ .num_resources = ARRAY_SIZE(usbc_resources),
+ .resources = usbc_resources,
},
{
- .name = "bxt_wcove_region",
+ .name = "bxt_wcove_ext_charger",
+ .num_resources = ARRAY_SIZE(charger_resources),
+ .resources = charger_resources,
},
};
@@ -348,6 +375,7 @@ static ssize_t addr_store(struct device *dev,
return count;
}
+static DEVICE_ATTR_ADMIN_RW(addr);
static ssize_t val_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -384,23 +412,14 @@ static ssize_t val_store(struct device *dev,
}
return count;
}
-
-static DEVICE_ATTR_ADMIN_RW(addr);
static DEVICE_ATTR_ADMIN_RW(val);
+
static struct attribute *bxtwc_attrs[] = {
&dev_attr_addr.attr,
&dev_attr_val.attr,
NULL
};
-
-static const struct attribute_group bxtwc_group = {
- .attrs = bxtwc_attrs,
-};
-
-static const struct attribute_group *bxtwc_groups[] = {
- &bxtwc_group,
- NULL
-};
+ATTRIBUTE_GROUPS(bxtwc);
static const struct regmap_config bxtwc_regmap_config = {
.reg_bits = 16,
@@ -415,15 +434,39 @@ static int bxtwc_add_chained_irq_chip(struct intel_soc_pmic *pmic,
const struct regmap_irq_chip *chip,
struct regmap_irq_chip_data **data)
{
- int irq;
+ struct device *dev = pmic->dev;
+ int irq, ret;
irq = regmap_irq_get_virq(pdata, pirq);
if (irq < 0)
- return dev_err_probe(pmic->dev, irq, "Failed to get parent vIRQ(%d) for chip %s\n",
+ return dev_err_probe(dev, irq, "Failed to get parent vIRQ(%d) for chip %s\n",
pirq, chip->name);
- return devm_regmap_add_irq_chip(pmic->dev, pmic->regmap, irq, irq_flags,
- 0, chip, data);
+ ret = devm_regmap_add_irq_chip(dev, pmic->regmap, irq, irq_flags, 0, chip, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add %s IRQ chip\n", chip->name);
+
+ return 0;
+}
+
+static int bxtwc_add_chained_devices(struct intel_soc_pmic *pmic,
+ const struct mfd_cell *cells, int n_devs,
+ struct regmap_irq_chip_data *pdata,
+ int pirq, int irq_flags,
+ const struct regmap_irq_chip *chip,
+ struct regmap_irq_chip_data **data)
+{
+ struct device *dev = pmic->dev;
+ struct irq_domain *domain;
+ int ret;
+
+ ret = bxtwc_add_chained_irq_chip(pmic, pdata, pirq, irq_flags, chip, data);
+ if (ret)
+ return ret;
+
+ domain = regmap_irq_get_domain(*data);
+
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, cells, n_devs, NULL, 0, domain);
}
static int bxtwc_probe(struct platform_device *pdev)
@@ -467,48 +510,49 @@ static int bxtwc_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "Failed to add IRQ chip\n");
+ ret = bxtwc_add_chained_devices(pmic, bxt_wc_tmu_dev, ARRAY_SIZE(bxt_wc_tmu_dev),
+ pmic->irq_chip_data,
+ BXTWC_TMU_LVL1_IRQ,
+ IRQF_ONESHOT,
+ &bxtwc_regmap_irq_chip_tmu,
+ &pmic->irq_chip_data_tmu);
+ if (ret)
+ return ret;
+
ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data,
BXTWC_PWRBTN_LVL1_IRQ,
IRQF_ONESHOT,
&bxtwc_regmap_irq_chip_pwrbtn,
&pmic->irq_chip_data_pwrbtn);
if (ret)
- return dev_err_probe(dev, ret, "Failed to add PWRBTN IRQ chip\n");
-
- ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data,
- BXTWC_TMU_LVL1_IRQ,
- IRQF_ONESHOT,
- &bxtwc_regmap_irq_chip_tmu,
- &pmic->irq_chip_data_tmu);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to add TMU IRQ chip\n");
+ return ret;
- /* Add chained IRQ handler for BCU IRQs */
- ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data,
- BXTWC_BCU_LVL1_IRQ,
- IRQF_ONESHOT,
- &bxtwc_regmap_irq_chip_bcu,
- &pmic->irq_chip_data_bcu);
+ ret = bxtwc_add_chained_devices(pmic, bxt_wc_bcu_dev, ARRAY_SIZE(bxt_wc_bcu_dev),
+ pmic->irq_chip_data,
+ BXTWC_BCU_LVL1_IRQ,
+ IRQF_ONESHOT,
+ &bxtwc_regmap_irq_chip_bcu,
+ &pmic->irq_chip_data_bcu);
if (ret)
- return dev_err_probe(dev, ret, "Failed to add BUC IRQ chip\n");
+ return ret;
- /* Add chained IRQ handler for ADC IRQs */
- ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data,
- BXTWC_ADC_LVL1_IRQ,
- IRQF_ONESHOT,
- &bxtwc_regmap_irq_chip_adc,
- &pmic->irq_chip_data_adc);
+ ret = bxtwc_add_chained_devices(pmic, bxt_wc_adc_dev, ARRAY_SIZE(bxt_wc_adc_dev),
+ pmic->irq_chip_data,
+ BXTWC_ADC_LVL1_IRQ,
+ IRQF_ONESHOT,
+ &bxtwc_regmap_irq_chip_adc,
+ &pmic->irq_chip_data_adc);
if (ret)
- return dev_err_probe(dev, ret, "Failed to add ADC IRQ chip\n");
+ return ret;
- /* Add chained IRQ handler for CHGR IRQs */
- ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data,
- BXTWC_CHGR_LVL1_IRQ,
- IRQF_ONESHOT,
- &bxtwc_regmap_irq_chip_chgr,
- &pmic->irq_chip_data_chgr);
+ ret = bxtwc_add_chained_devices(pmic, bxt_wc_chgr_dev, ARRAY_SIZE(bxt_wc_chgr_dev),
+ pmic->irq_chip_data,
+ BXTWC_CHGR_LVL1_IRQ,
+ IRQF_ONESHOT,
+ &bxtwc_regmap_irq_chip_chgr,
+ &pmic->irq_chip_data_chgr);
if (ret)
- return dev_err_probe(dev, ret, "Failed to add CHGR IRQ chip\n");
+ return ret;
/* Add chained IRQ handler for CRIT IRQs */
ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data,
@@ -517,7 +561,7 @@ static int bxtwc_probe(struct platform_device *pdev)
&bxtwc_regmap_irq_chip_crit,
&pmic->irq_chip_data_crit);
if (ret)
- return dev_err_probe(dev, ret, "Failed to add CRIT IRQ chip\n");
+ return ret;
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, bxt_wc_dev, ARRAY_SIZE(bxt_wc_dev),
NULL, 0, NULL);
@@ -572,7 +616,7 @@ static struct platform_driver bxtwc_driver = {
.probe = bxtwc_probe,
.shutdown = bxtwc_shutdown,
.driver = {
- .name = "BXTWC PMIC",
+ .name = "intel_soc_pmic_bxtwc",
.pm = pm_sleep_ptr(&bxtwc_pm_ops),
.acpi_match_table = bxtwc_acpi_ids,
.dev_groups = bxtwc_groups,
@@ -581,5 +625,6 @@ static struct platform_driver bxtwc_driver = {
module_platform_driver(bxtwc_driver);
+MODULE_DESCRIPTION("Intel Broxton Whiskey Cove PMIC MFD core driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Qipeng Zha <qipeng.zha@intel.com>");
diff --git a/drivers/mfd/intel_soc_pmic_chtdc_ti.c b/drivers/mfd/intel_soc_pmic_chtdc_ti.c
index 992855bfda3e..6daf33e07ea0 100644
--- a/drivers/mfd/intel_soc_pmic_chtdc_ti.c
+++ b/drivers/mfd/intel_soc_pmic_chtdc_ti.c
@@ -81,8 +81,9 @@ static struct mfd_cell chtdc_ti_dev[] = {
static const struct regmap_config chtdc_ti_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .max_register = 128,
- .cache_type = REGCACHE_NONE,
+ .max_register = 0xff,
+ /* The hardware does not support reading multiple registers at once */
+ .use_single_read = true,
};
static const struct regmap_irq chtdc_ti_irqs[] = {
diff --git a/drivers/mfd/intel_soc_pmic_chtwc.c b/drivers/mfd/intel_soc_pmic_chtwc.c
index 7fce3ef7ab45..aa71a7d83fcd 100644
--- a/drivers/mfd/intel_soc_pmic_chtwc.c
+++ b/drivers/mfd/intel_soc_pmic_chtwc.c
@@ -178,7 +178,6 @@ static const struct dmi_system_id cht_wc_model_dmi_ids[] = {
.driver_data = (void *)(long)INTEL_CHT_WC_LENOVO_YT3_X90,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
- DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"),
},
},
@@ -268,7 +267,7 @@ static const struct acpi_device_id cht_wc_acpi_ids[] = {
static struct i2c_driver cht_wc_driver = {
.driver = {
- .name = "CHT Whiskey Cove PMIC",
+ .name = "intel_soc_pmic_chtwc",
.pm = pm_sleep_ptr(&cht_wc_pm_ops),
.acpi_match_table = cht_wc_acpi_ids,
},
diff --git a/drivers/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c
index 581f81cbaa24..41429f9bcb69 100644
--- a/drivers/mfd/intel_soc_pmic_crc.c
+++ b/drivers/mfd/intel_soc_pmic_crc.c
@@ -113,7 +113,6 @@ static const struct regmap_config crystal_cove_regmap_config = {
.val_bits = 8,
.max_register = CRYSTAL_COVE_MAX_REGISTER,
- .cache_type = REGCACHE_NONE,
};
static const struct regmap_irq crystal_cove_irqs[] = {
@@ -137,7 +136,9 @@ static const struct regmap_irq_chip crystal_cove_irq_chip = {
/* PWM consumed by the Intel GFX */
static struct pwm_lookup crc_pwm_lookup[] = {
- PWM_LOOKUP("crystal_cove_pwm", 0, "0000:00:02.0", "pwm_pmic_backlight", 0, PWM_POLARITY_NORMAL),
+ PWM_LOOKUP_WITH_MODULE("crystal_cove_pwm", 0, "0000:00:02.0",
+ "pwm_pmic_backlight", 0, PWM_POLARITY_NORMAL,
+ "pwm-crc"),
};
struct crystal_cove_config {
@@ -257,12 +258,19 @@ static const struct acpi_device_id crystal_cove_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, crystal_cove_acpi_match);
+static const struct i2c_device_id crystal_cove_i2c_match[] = {
+ { "intel_soc_pmic_crc" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, crystal_cove_i2c_match);
+
static struct i2c_driver crystal_cove_i2c_driver = {
.driver = {
- .name = "crystal_cove_i2c",
+ .name = "intel_soc_pmic_crc",
.pm = pm_sleep_ptr(&crystal_cove_pm_ops),
.acpi_match_table = crystal_cove_acpi_match,
},
+ .id_table = crystal_cove_i2c_match,
.probe = crystal_cove_i2c_probe,
.remove = crystal_cove_i2c_remove,
.shutdown = crystal_cove_shutdown,
diff --git a/drivers/mfd/intel_soc_pmic_mrfld.c b/drivers/mfd/intel_soc_pmic_mrfld.c
index 71da861e8c27..77121775c1a3 100644
--- a/drivers/mfd/intel_soc_pmic_mrfld.c
+++ b/drivers/mfd/intel_soc_pmic_mrfld.c
@@ -12,11 +12,10 @@
#include <linux/mfd/intel_soc_pmic.h>
#include <linux/mfd/intel_soc_pmic_mrfld.h>
#include <linux/module.h>
+#include <linux/platform_data/x86/intel_scu_ipc.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
-#include <asm/intel_scu_ipc.h>
-
/*
* Level 2 IRQs
*
diff --git a/drivers/mfd/ioc3.c b/drivers/mfd/ioc3.c
index 58656837b7c6..5f8ac364b610 100644
--- a/drivers/mfd/ioc3.c
+++ b/drivers/mfd/ioc3.c
@@ -6,7 +6,7 @@
*
* Based on work by:
* Stanislaw Skowronek <skylark@unaligned.org>
- * Joshua Kinard <kumba@gentoo.org>
+ * Joshua Kinard <linux@kumba.dev>
* Brent Casavant <bcasavan@sgi.com> - IOC4 master driver
* Pat Gefre <pfg@sgi.com> - IOC3 serial port IRQ demuxer
*/
diff --git a/drivers/mfd/ipaq-micro.c b/drivers/mfd/ipaq-micro.c
index 6d3968458e81..4b757d847282 100644
--- a/drivers/mfd/ipaq-micro.c
+++ b/drivers/mfd/ipaq-micro.c
@@ -22,6 +22,7 @@
#include <linux/mfd/core.h>
#include <linux/mfd/ipaq-micro.h>
#include <linux/string.h>
+#include <linux/string_choices.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/list.h>
@@ -78,8 +79,6 @@ EXPORT_SYMBOL(ipaq_micro_tx_msg);
static void micro_rx_msg(struct ipaq_micro *micro, u8 id, int len, u8 *data)
{
- int i;
-
dev_dbg(micro->dev, "RX msg: %02x, %d bytes\n", id, len);
spin_lock(&micro->lock);
@@ -131,10 +130,8 @@ static void micro_rx_msg(struct ipaq_micro *micro, u8 id, int len, u8 *data)
break;
default:
dev_err(micro->dev,
- "unknown msg %d [%d] ", id, len);
- for (i = 0; i < len; ++i)
- pr_cont("0x%02x ", data[i]);
- pr_cont("\n");
+ "unknown msg %d [%d] %*ph\n", id, len, len, data);
+ break;
}
spin_unlock(&micro->lock);
}
@@ -271,7 +268,7 @@ static void __init ipaq_micro_eeprom_dump(struct ipaq_micro *micro)
dev_info(micro->dev, "page mode: %u\n", ipaq_micro_to_u16(dump+84));
dev_info(micro->dev, "country ID: %u\n", ipaq_micro_to_u16(dump+86));
dev_info(micro->dev, "color display: %s\n",
- ipaq_micro_to_u16(dump+88) ? "yes" : "no");
+ str_yes_no(ipaq_micro_to_u16(dump + 88)));
dev_info(micro->dev, "ROM size: %u MiB\n", ipaq_micro_to_u16(dump+90));
dev_info(micro->dev, "RAM size: %u KiB\n", ipaq_micro_to_u16(dump+92));
dev_info(micro->dev, "screen: %u x %u\n",
diff --git a/drivers/mfd/iqs62x.c b/drivers/mfd/iqs62x.c
index dfe9cb79e6a1..ee017617d1d1 100644
--- a/drivers/mfd/iqs62x.c
+++ b/drivers/mfd/iqs62x.c
@@ -27,11 +27,11 @@
#include <linux/mfd/iqs62x.h>
#include <linux/module.h>
#include <linux/notifier.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define IQS62X_PROD_NUM 0x00
@@ -96,7 +96,7 @@ struct iqs62x_fw_blk {
u8 addr;
u8 mask;
u8 len;
- u8 data[];
+ u8 data[] __counted_by(len);
};
struct iqs62x_info {
diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c
index 33c6cfe9fe42..c2008d2dc95a 100644
--- a/drivers/mfd/kempld-core.c
+++ b/drivers/mfd/kempld-core.c
@@ -6,14 +6,17 @@
* Author: Michael Brunner <michael.brunner@kontron.com>
*/
+#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/kempld.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/property.h>
#include <linux/dmi.h>
#include <linux/io.h>
#include <linux/delay.h>
-#include <linux/acpi.h>
+#include <linux/sysfs.h>
#define MAX_ID_LEN 4
static char force_device_id[MAX_ID_LEN + 1] = "";
@@ -106,7 +109,7 @@ static int kempld_register_cells_generic(struct kempld_device_data *pld)
if (pld->feature_mask & KEMPLD_FEATURE_MASK_UART)
devs[i++].name = kempld_dev_names[KEMPLD_UART];
- return mfd_add_devices(pld->dev, -1, devs, i, NULL, 0, NULL);
+ return mfd_add_devices(pld->dev, PLATFORM_DEVID_NONE, devs, i, NULL, 0, NULL);
}
static struct resource kempld_ioresource = {
@@ -126,31 +129,20 @@ static const struct kempld_platform_data kempld_platform_data_generic = {
static struct platform_device *kempld_pdev;
-static int kempld_create_platform_device(const struct dmi_system_id *id)
+static int kempld_create_platform_device(const struct kempld_platform_data *pdata)
{
- const struct kempld_platform_data *pdata = id->driver_data;
- int ret;
-
- kempld_pdev = platform_device_alloc("kempld", -1);
- if (!kempld_pdev)
- return -ENOMEM;
-
- ret = platform_device_add_data(kempld_pdev, pdata, sizeof(*pdata));
- if (ret)
- goto err;
-
- ret = platform_device_add_resources(kempld_pdev, pdata->ioresource, 1);
- if (ret)
- goto err;
-
- ret = platform_device_add(kempld_pdev);
- if (ret)
- goto err;
-
- return 0;
-err:
- platform_device_put(kempld_pdev);
- return ret;
+ const struct platform_device_info pdevinfo = {
+ .name = "kempld",
+ .id = PLATFORM_DEVID_NONE,
+ .res = pdata->ioresource,
+ .num_res = 1,
+ .data = pdata,
+ .size_data = sizeof(*pdata),
+ };
+
+ kempld_pdev = platform_device_register_full(&pdevinfo);
+
+ return PTR_ERR_OR_ZERO(kempld_pdev);
}
/**
@@ -299,11 +291,8 @@ static int kempld_get_info(struct kempld_device_data *pld)
else
minor = (pld->info.minor - 10) + 'A';
- ret = scnprintf(pld->info.version, sizeof(pld->info.version),
- "P%X%c%c.%04X", pld->info.number, major, minor,
- pld->info.buildnr);
- if (ret < 0)
- return ret;
+ scnprintf(pld->info.version, sizeof(pld->info.version), "P%X%c%c.%04X",
+ pld->info.number, major, minor, pld->info.buildnr);
return 0;
}
@@ -372,16 +361,13 @@ static DEVICE_ATTR_RO(pld_version);
static DEVICE_ATTR_RO(pld_specification);
static DEVICE_ATTR_RO(pld_type);
-static struct attribute *pld_attributes[] = {
+static struct attribute *pld_attrs[] = {
&dev_attr_pld_version.attr,
&dev_attr_pld_specification.attr,
&dev_attr_pld_type.attr,
NULL
};
-
-static const struct attribute_group pld_attr_group = {
- .attrs = pld_attributes,
-};
+ATTRIBUTE_GROUPS(pld);
static int kempld_detect_device(struct kempld_device_data *pld)
{
@@ -414,73 +400,8 @@ static int kempld_detect_device(struct kempld_device_data *pld)
pld->info.version, kempld_get_type_string(pld),
pld->info.spec_major, pld->info.spec_minor);
- ret = sysfs_create_group(&pld->dev->kobj, &pld_attr_group);
- if (ret)
- return ret;
-
- ret = kempld_register_cells(pld);
- if (ret)
- sysfs_remove_group(&pld->dev->kobj, &pld_attr_group);
-
- return ret;
-}
-
-#ifdef CONFIG_ACPI
-static int kempld_get_acpi_data(struct platform_device *pdev)
-{
- struct list_head resource_list;
- struct resource *resources;
- struct resource_entry *rentry;
- struct device *dev = &pdev->dev;
- struct acpi_device *acpi_dev = ACPI_COMPANION(dev);
- const struct kempld_platform_data *pdata;
- int ret;
- int count;
-
- pdata = acpi_device_get_match_data(dev);
- ret = platform_device_add_data(pdev, pdata,
- sizeof(struct kempld_platform_data));
- if (ret)
- return ret;
-
- INIT_LIST_HEAD(&resource_list);
- ret = acpi_dev_get_resources(acpi_dev, &resource_list, NULL, NULL);
- if (ret < 0)
- goto out;
-
- count = ret;
-
- if (count == 0) {
- ret = platform_device_add_resources(pdev, pdata->ioresource, 1);
- goto out;
- }
-
- resources = devm_kcalloc(&acpi_dev->dev, count, sizeof(*resources),
- GFP_KERNEL);
- if (!resources) {
- ret = -ENOMEM;
- goto out;
- }
-
- count = 0;
- list_for_each_entry(rentry, &resource_list, node) {
- memcpy(&resources[count], rentry->res,
- sizeof(*resources));
- count++;
- }
- ret = platform_device_add_resources(pdev, resources, count);
-
-out:
- acpi_dev_free_resource_list(&resource_list);
-
- return ret;
-}
-#else
-static int kempld_get_acpi_data(struct platform_device *pdev)
-{
- return -ENODEV;
+ return kempld_register_cells(pld);
}
-#endif /* CONFIG_ACPI */
static int kempld_probe(struct platform_device *pdev)
{
@@ -490,15 +411,21 @@ static int kempld_probe(struct platform_device *pdev)
struct resource *ioport;
int ret;
- if (kempld_pdev == NULL) {
+ if (IS_ERR_OR_NULL(kempld_pdev)) {
/*
* No kempld_pdev device has been registered in kempld_init,
* so we seem to be probing an ACPI platform device.
*/
- ret = kempld_get_acpi_data(pdev);
+ pdata = device_get_match_data(dev);
+ if (!pdata)
+ return -ENODEV;
+
+ ret = platform_device_add_data(pdev, pdata, sizeof(*pdata));
if (ret)
return ret;
- } else if (kempld_pdev != pdev) {
+ } else if (kempld_pdev == pdev) {
+ pdata = dev_get_platdata(dev);
+ } else {
/*
* The platform device we are probing is not the one we
* registered in kempld_init using the DMI table, so this one
@@ -509,7 +436,6 @@ static int kempld_probe(struct platform_device *pdev)
dev_notice(dev, "platform device exists - not using ACPI\n");
return -ENODEV;
}
- pdata = dev_get_platdata(dev);
pld = devm_kzalloc(dev, sizeof(*pld), GFP_KERNEL);
if (!pld)
@@ -535,32 +461,27 @@ static int kempld_probe(struct platform_device *pdev)
return kempld_detect_device(pld);
}
-static int kempld_remove(struct platform_device *pdev)
+static void kempld_remove(struct platform_device *pdev)
{
struct kempld_device_data *pld = platform_get_drvdata(pdev);
const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev);
- sysfs_remove_group(&pld->dev->kobj, &pld_attr_group);
-
mfd_remove_devices(&pdev->dev);
pdata->release_hardware_mutex(pld);
-
- return 0;
}
-#ifdef CONFIG_ACPI
static const struct acpi_device_id kempld_acpi_table[] = {
{ "KEM0000", (kernel_ulong_t)&kempld_platform_data_generic },
{ "KEM0001", (kernel_ulong_t)&kempld_platform_data_generic },
{}
};
MODULE_DEVICE_TABLE(acpi, kempld_acpi_table);
-#endif
static struct platform_driver kempld_driver = {
.driver = {
.name = "kempld",
- .acpi_match_table = ACPI_PTR(kempld_acpi_table),
+ .acpi_match_table = kempld_acpi_table,
+ .dev_groups = pld_groups,
},
.probe = kempld_probe,
.remove = kempld_remove,
@@ -573,375 +494,281 @@ static const struct dmi_system_id kempld_dmi_table[] __initconst = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bBD"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "BBL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bBL6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "BDV7",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bDV7"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "BHL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bHL6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "BKL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bKL6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "BSL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bSL6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CAL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cAL"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CBL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cBL6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CBW6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cBW6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CCR2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bIP2"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CCR6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bIP6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CDV7",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cDV7"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CHL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cHL6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CHR2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "ETXexpress-SC T2"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CHR2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "ETXe-SC T2"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CHR2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bSC2"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CHR6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "ETXexpress-SC T6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CHR6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "ETXe-SC T6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CHR6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bSC6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CKL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cKL6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CNTG",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "ETXexpress-PC"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CNTG",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-bPC2"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CNTX",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "PXT"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CSL6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cSL6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "CVV6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cBT"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "FRI2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BIOS_VERSION, "FRI2"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "FRI2",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Fish River Island II"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "A203",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "KBox A-203"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "M4A1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-m4AL"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "MAL1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-mAL10"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "MAPL",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "mITX-APL"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "MBR1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "ETX-OH"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "MVV1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-mBT"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "NTC1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "nanoETXexpress-TT"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "NTC1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "nETXe-TT"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "NTC1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-mTT"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "NUP1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-mCT"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "PAPL",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "pITX-APL"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "SXAL",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "SMARC-sXAL"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "SXAL4",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "SMARC-sXA4"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "UNP1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "microETXexpress-DC"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "UNP1",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cDC2"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "UNTG",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "microETXexpress-PC"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "UNTG",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cPC2"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "UUP6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cCT6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "UTH6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "COMe-cTH6"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
}, {
.ident = "Q7AL",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"),
DMI_MATCH(DMI_BOARD_NAME, "Qseven-Q7AL"),
},
- .driver_data = (void *)&kempld_platform_data_generic,
- .callback = kempld_create_platform_device,
},
{}
};
@@ -951,26 +778,31 @@ static int __init kempld_init(void)
{
const struct dmi_system_id *id;
+ /*
+ * This custom DMI iteration allows the driver to be initialized in three ways:
+ * - When a forced_device_id string matches any ident in the kempld_dmi_table,
+ * regardless of whether the DMI device is present in the system dmi table.
+ * - When a matching entry is present in the DMI system tabe.
+ * - Through alternative mechanisms like ACPI.
+ */
if (force_device_id[0]) {
- for (id = kempld_dmi_table;
- id->matches[0].slot != DMI_NONE; id++)
+ for (id = kempld_dmi_table; id->matches[0].slot != DMI_NONE; id++)
if (strstr(id->ident, force_device_id))
- if (id->callback && !id->callback(id))
+ if (!kempld_create_platform_device(&kempld_platform_data_generic))
break;
if (id->matches[0].slot == DMI_NONE)
return -ENODEV;
} else {
- dmi_check_system(kempld_dmi_table);
+ for (id = dmi_first_match(kempld_dmi_table); id; id = dmi_first_match(id+1))
+ if (kempld_create_platform_device(&kempld_platform_data_generic))
+ break;
}
-
return platform_driver_register(&kempld_driver);
}
static void __exit kempld_exit(void)
{
- if (kempld_pdev)
- platform_device_unregister(kempld_pdev);
-
+ platform_device_unregister(kempld_pdev);
platform_driver_unregister(&kempld_driver);
}
diff --git a/drivers/mfd/khadas-mcu.c b/drivers/mfd/khadas-mcu.c
index 61396d824f16..ba981a788692 100644
--- a/drivers/mfd/khadas-mcu.c
+++ b/drivers/mfd/khadas-mcu.c
@@ -72,7 +72,7 @@ static const struct regmap_config khadas_mcu_regmap_config = {
.max_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG,
.volatile_reg = khadas_mcu_reg_volatile,
.writeable_reg = khadas_mcu_reg_writeable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static struct mfd_cell khadas_mcu_fan_cells[] = {
diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c
index c211183cecb2..0a2409d00b2e 100644
--- a/drivers/mfd/lm3533-core.c
+++ b/drivers/mfd/lm3533-core.c
@@ -11,7 +11,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/err.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/regmap.h>
@@ -225,14 +225,12 @@ static int lm3533_set_lvled_config(struct lm3533 *lm3533, u8 lvled, u8 led)
static void lm3533_enable(struct lm3533 *lm3533)
{
- if (gpio_is_valid(lm3533->gpio_hwen))
- gpio_set_value(lm3533->gpio_hwen, 1);
+ gpiod_set_value(lm3533->hwen, 1);
}
static void lm3533_disable(struct lm3533 *lm3533)
{
- if (gpio_is_valid(lm3533->gpio_hwen))
- gpio_set_value(lm3533->gpio_hwen, 0);
+ gpiod_set_value(lm3533->hwen, 0);
}
enum lm3533_attribute_type {
@@ -483,18 +481,10 @@ static int lm3533_device_init(struct lm3533 *lm3533)
return -EINVAL;
}
- lm3533->gpio_hwen = pdata->gpio_hwen;
-
- if (gpio_is_valid(lm3533->gpio_hwen)) {
- ret = devm_gpio_request_one(lm3533->dev, lm3533->gpio_hwen,
- GPIOF_OUT_INIT_LOW, "lm3533-hwen");
- if (ret < 0) {
- dev_err(lm3533->dev,
- "failed to request HWEN GPIO %d\n",
- lm3533->gpio_hwen);
- return ret;
- }
- }
+ lm3533->hwen = devm_gpiod_get(lm3533->dev, NULL, GPIOD_OUT_LOW);
+ if (IS_ERR(lm3533->hwen))
+ return dev_err_probe(lm3533->dev, PTR_ERR(lm3533->hwen), "failed to request HWEN GPIO\n");
+ gpiod_set_consumer_name(lm3533->hwen, "lm3533-hwen");
lm3533_enable(lm3533);
@@ -614,8 +604,8 @@ static void lm3533_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id lm3533_i2c_ids[] = {
- { "lm3533", 0 },
- { },
+ { "lm3533" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, lm3533_i2c_ids);
diff --git a/drivers/mfd/lochnagar-i2c.c b/drivers/mfd/lochnagar-i2c.c
index 3c8843117080..6c930c57f2e2 100644
--- a/drivers/mfd/lochnagar-i2c.c
+++ b/drivers/mfd/lochnagar-i2c.c
@@ -15,8 +15,8 @@
#include <linux/i2c.h>
#include <linux/lockdep.h>
#include <linux/mfd/core.h>
+#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
-#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
@@ -70,7 +70,7 @@ static const struct regmap_config lochnagar1_i2c_regmap = {
.use_single_read = true,
.use_single_write = true,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct reg_sequence lochnagar1_patch[] = {
@@ -163,7 +163,7 @@ static const struct regmap_config lochnagar2_i2c_regmap = {
.readable_reg = lochnagar2_readable_register,
.volatile_reg = lochnagar2_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct reg_sequence lochnagar2_patch[] = {
@@ -270,7 +270,6 @@ static int lochnagar_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
const struct lochnagar_config *config = NULL;
- const struct of_device_id *of_id;
struct lochnagar *lochnagar;
struct gpio_desc *reset, *present;
unsigned int val;
@@ -282,11 +281,7 @@ static int lochnagar_i2c_probe(struct i2c_client *i2c)
if (!lochnagar)
return -ENOMEM;
- of_id = of_match_device(lochnagar_of_match, dev);
- if (!of_id)
- return -EINVAL;
-
- config = of_id->data;
+ config = i2c_get_match_data(i2c);
lochnagar->dev = dev;
mutex_init(&lochnagar->analogue_config_lock);
@@ -379,7 +374,7 @@ static int lochnagar_i2c_probe(struct i2c_client *i2c)
static struct i2c_driver lochnagar_i2c_driver = {
.driver = {
.name = "lochnagar",
- .of_match_table = of_match_ptr(lochnagar_of_match),
+ .of_match_table = lochnagar_of_match,
.suppress_bind_attrs = true,
},
.probe = lochnagar_i2c_probe,
diff --git a/drivers/mfd/loongson-se.c b/drivers/mfd/loongson-se.c
new file mode 100644
index 000000000000..3902ba377d69
--- /dev/null
+++ b/drivers/mfd/loongson-se.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Loongson Technology Corporation Limited
+ *
+ * Author: Yinggang Gu <guyinggang@loongson.cn>
+ * Author: Qunqin Zhao <zhaoqunqin@loongson.cn>
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/loongson-se.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+struct loongson_se {
+ void __iomem *base;
+ spinlock_t dev_lock;
+ struct completion cmd_completion;
+
+ void *dmam_base;
+ int dmam_size;
+
+ struct mutex engine_init_lock;
+ struct loongson_se_engine engines[SE_ENGINE_MAX];
+};
+
+struct loongson_se_controller_cmd {
+ u32 command_id;
+ u32 info[7];
+};
+
+static int loongson_se_poll(struct loongson_se *se, u32 int_bit)
+{
+ u32 status;
+ int err;
+
+ spin_lock_irq(&se->dev_lock);
+
+ /* Notify the controller that the engine needs to be started */
+ writel(int_bit, se->base + SE_L2SINT_SET);
+
+ /* Polling until the controller has forwarded the engine command */
+ err = readl_relaxed_poll_timeout_atomic(se->base + SE_L2SINT_STAT, status,
+ !(status & int_bit),
+ 1, LOONGSON_ENGINE_CMD_TIMEOUT_US);
+
+ spin_unlock_irq(&se->dev_lock);
+
+ return err;
+}
+
+static int loongson_se_send_controller_cmd(struct loongson_se *se,
+ struct loongson_se_controller_cmd *cmd)
+{
+ u32 *send_cmd = (u32 *)cmd;
+ int err, i;
+
+ for (i = 0; i < SE_SEND_CMD_REG_LEN; i++)
+ writel(send_cmd[i], se->base + SE_SEND_CMD_REG + i * 4);
+
+ err = loongson_se_poll(se, SE_INT_CONTROLLER);
+ if (err)
+ return err;
+
+ return wait_for_completion_interruptible(&se->cmd_completion);
+}
+
+int loongson_se_send_engine_cmd(struct loongson_se_engine *engine)
+{
+ /*
+ * After engine initialization, the controller already knows
+ * where to obtain engine commands from. Now all we need to
+ * do is notify the controller that the engine needs to be started.
+ */
+ int err = loongson_se_poll(engine->se, BIT(engine->id));
+
+ if (err)
+ return err;
+
+ return wait_for_completion_interruptible(&engine->completion);
+}
+EXPORT_SYMBOL_GPL(loongson_se_send_engine_cmd);
+
+struct loongson_se_engine *loongson_se_init_engine(struct device *dev, int id)
+{
+ struct loongson_se *se = dev_get_drvdata(dev);
+ struct loongson_se_engine *engine = &se->engines[id];
+ struct loongson_se_controller_cmd cmd;
+
+ engine->se = se;
+ engine->id = id;
+ init_completion(&engine->completion);
+
+ /* Divide DMA memory equally among all engines */
+ engine->buffer_size = se->dmam_size / SE_ENGINE_MAX;
+ engine->buffer_off = (se->dmam_size / SE_ENGINE_MAX) * id;
+ engine->data_buffer = se->dmam_base + engine->buffer_off;
+
+ /*
+ * There has no engine0, use its data buffer as command buffer for other
+ * engines. The DMA memory size is obtained from the ACPI table, which
+ * ensures that the data buffer size of engine0 is larger than the
+ * command buffer size of all engines.
+ */
+ engine->command = se->dmam_base + id * (2 * SE_ENGINE_CMD_SIZE);
+ engine->command_ret = engine->command + SE_ENGINE_CMD_SIZE;
+
+ mutex_lock(&se->engine_init_lock);
+
+ /* Tell the controller where to find engine command */
+ cmd.command_id = SE_CMD_SET_ENGINE_CMDBUF;
+ cmd.info[0] = id;
+ cmd.info[1] = engine->command - se->dmam_base;
+ cmd.info[2] = 2 * SE_ENGINE_CMD_SIZE;
+
+ if (loongson_se_send_controller_cmd(se, &cmd))
+ engine = NULL;
+
+ mutex_unlock(&se->engine_init_lock);
+
+ return engine;
+}
+EXPORT_SYMBOL_GPL(loongson_se_init_engine);
+
+static irqreturn_t se_irq_handler(int irq, void *dev_id)
+{
+ struct loongson_se *se = dev_id;
+ u32 int_status;
+ int id;
+
+ spin_lock(&se->dev_lock);
+
+ int_status = readl(se->base + SE_S2LINT_STAT);
+
+ /* For controller */
+ if (int_status & SE_INT_CONTROLLER) {
+ complete(&se->cmd_completion);
+ int_status &= ~SE_INT_CONTROLLER;
+ writel(SE_INT_CONTROLLER, se->base + SE_S2LINT_CL);
+ }
+
+ /* For engines */
+ while (int_status) {
+ id = __ffs(int_status);
+ complete(&se->engines[id].completion);
+ int_status &= ~BIT(id);
+ writel(BIT(id), se->base + SE_S2LINT_CL);
+ }
+
+ spin_unlock(&se->dev_lock);
+
+ return IRQ_HANDLED;
+}
+
+static int loongson_se_init(struct loongson_se *se, dma_addr_t addr, int size)
+{
+ struct loongson_se_controller_cmd cmd;
+ int err;
+
+ cmd.command_id = SE_CMD_START;
+ err = loongson_se_send_controller_cmd(se, &cmd);
+ if (err)
+ return err;
+
+ cmd.command_id = SE_CMD_SET_DMA;
+ cmd.info[0] = lower_32_bits(addr);
+ cmd.info[1] = upper_32_bits(addr);
+ cmd.info[2] = size;
+
+ return loongson_se_send_controller_cmd(se, &cmd);
+}
+
+static const struct mfd_cell engines[] = {
+ { .name = "loongson-rng" },
+ { .name = "tpm_loongson" },
+};
+
+static int loongson_se_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct loongson_se *se;
+ int nr_irq, irq, err, i;
+ dma_addr_t paddr;
+
+ se = devm_kmalloc(dev, sizeof(*se), GFP_KERNEL);
+ if (!se)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, se);
+ init_completion(&se->cmd_completion);
+ spin_lock_init(&se->dev_lock);
+ mutex_init(&se->engine_init_lock);
+
+ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+ if (device_property_read_u32(dev, "dmam_size", &se->dmam_size))
+ return -ENODEV;
+
+ se->dmam_base = dmam_alloc_coherent(dev, se->dmam_size, &paddr, GFP_KERNEL);
+ if (!se->dmam_base)
+ return -ENOMEM;
+
+ se->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(se->base))
+ return PTR_ERR(se->base);
+
+ writel(SE_INT_ALL, se->base + SE_S2LINT_EN);
+
+ nr_irq = platform_irq_count(pdev);
+ if (nr_irq <= 0)
+ return -ENODEV;
+
+ for (i = 0; i < nr_irq; i++) {
+ irq = platform_get_irq(pdev, i);
+ err = devm_request_irq(dev, irq, se_irq_handler, 0, "loongson-se", se);
+ if (err)
+ dev_err(dev, "failed to request IRQ: %d\n", irq);
+ }
+
+ err = loongson_se_init(se, paddr, se->dmam_size);
+ if (err)
+ return err;
+
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, engines,
+ ARRAY_SIZE(engines), NULL, 0, NULL);
+}
+
+static const struct acpi_device_id loongson_se_acpi_match[] = {
+ { "LOON0011", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, loongson_se_acpi_match);
+
+static struct platform_driver loongson_se_driver = {
+ .probe = loongson_se_probe,
+ .driver = {
+ .name = "loongson-se",
+ .acpi_match_table = loongson_se_acpi_match,
+ },
+};
+module_platform_driver(loongson_se_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yinggang Gu <guyinggang@loongson.cn>");
+MODULE_AUTHOR("Qunqin Zhao <zhaoqunqin@loongson.cn>");
+MODULE_DESCRIPTION("Loongson Security Engine chip controller driver");
diff --git a/drivers/mfd/lp3943.c b/drivers/mfd/lp3943.c
index 7f749a23dca8..6764553147e4 100644
--- a/drivers/mfd/lp3943.c
+++ b/drivers/mfd/lp3943.c
@@ -126,7 +126,7 @@ static int lp3943_probe(struct i2c_client *cl)
}
static const struct i2c_device_id lp3943_ids[] = {
- { "lp3943", 0 },
+ { "lp3943" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lp3943_ids);
diff --git a/drivers/mfd/lp873x.c b/drivers/mfd/lp873x.c
index 6639f0fad4ea..e8c5c89c2a76 100644
--- a/drivers/mfd/lp873x.c
+++ b/drivers/mfd/lp873x.c
@@ -7,8 +7,8 @@
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/mfd/lp873x.h>
@@ -68,8 +68,8 @@ static const struct of_device_id of_lp873x_match_table[] = {
MODULE_DEVICE_TABLE(of, of_lp873x_match_table);
static const struct i2c_device_id lp873x_id_table[] = {
- { "lp873x", 0 },
- { },
+ { "lp873x" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, lp873x_id_table);
diff --git a/drivers/mfd/lp87565.c b/drivers/mfd/lp87565.c
index 88ce4d7c50a7..9488d3793c10 100644
--- a/drivers/mfd/lp87565.c
+++ b/drivers/mfd/lp87565.c
@@ -6,10 +6,11 @@
*/
#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/mfd/lp87565.h>
@@ -46,7 +47,6 @@ MODULE_DEVICE_TABLE(of, of_lp87565_match_table);
static int lp87565_probe(struct i2c_client *client)
{
struct lp87565 *lp87565;
- const struct of_device_id *of_id;
int ret;
unsigned int otpid;
@@ -89,10 +89,7 @@ static int lp87565_probe(struct i2c_client *client)
}
lp87565->rev = otpid & LP87565_OTP_REV_OTP_ID;
-
- of_id = of_match_device(of_lp87565_match_table, &client->dev);
- if (of_id)
- lp87565->dev_type = (enum lp87565_device_type)of_id->data;
+ lp87565->dev_type = (uintptr_t)i2c_get_match_data(client);
i2c_set_clientdata(client, lp87565);
@@ -109,8 +106,8 @@ static void lp87565_shutdown(struct i2c_client *client)
}
static const struct i2c_device_id lp87565_id_table[] = {
- { "lp87565-q1", 0 },
- { },
+ { "lp87565-q1" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, lp87565_id_table);
diff --git a/drivers/mfd/lp8788-irq.c b/drivers/mfd/lp8788-irq.c
index 39006297f3d2..f62fa2d7f010 100644
--- a/drivers/mfd/lp8788-irq.c
+++ b/drivers/mfd/lp8788-irq.c
@@ -161,7 +161,7 @@ int lp8788_irq_init(struct lp8788 *lp, int irq)
return -ENOMEM;
irqd->lp = lp;
- irqd->domain = irq_domain_add_linear(lp->dev->of_node, LP8788_INT_MAX,
+ irqd->domain = irq_domain_create_linear(dev_fwnode(lp->dev), LP8788_INT_MAX,
&lp8788_domain_ops, irqd);
if (!irqd->domain) {
dev_err(lp->dev, "failed to add irq domain err\n");
diff --git a/drivers/mfd/lp8788.c b/drivers/mfd/lp8788.c
index f371eeb042e0..32f255378f5a 100644
--- a/drivers/mfd/lp8788.c
+++ b/drivers/mfd/lp8788.c
@@ -216,7 +216,7 @@ static void lp8788_remove(struct i2c_client *cl)
}
static const struct i2c_device_id lp8788_ids[] = {
- {"lp8788", 0},
+ { "lp8788" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lp8788_ids);
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index 7b1c597b6879..4b7d0cb9340f 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -38,6 +38,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/align.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
@@ -85,19 +86,6 @@
#define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i)
#define wdt_res(b, i) (&wdt_ich_res[(b) + (i)])
-struct lpc_ich_priv {
- int chipset;
-
- int abase; /* ACPI base */
- int actrl_pbase; /* ACPI control or PMC base */
- int gbase; /* GPIO base */
- int gctrl; /* GPIO control */
-
- int abase_save; /* Cached ACPI base value */
- int actrl_pbase_save; /* Cached ACPI control or PMC base value */
- int gctrl_save; /* Cached GPIO control value */
-};
-
static struct resource wdt_ich_res[] = {
/* ACPI - TCO */
{
@@ -144,22 +132,33 @@ static struct mfd_cell lpc_ich_gpio_cell = {
.ignore_resource_conflicts = true,
};
+#define INTEL_GPIO_RESOURCE_SIZE 0x1000
+
+struct lpc_ich_gpio_info {
+ const char *hid;
+ const struct mfd_cell *devices;
+ size_t nr_devices;
+ struct resource **resources;
+ size_t nr_resources;
+ const resource_size_t *offsets;
+};
+
#define APL_GPIO_NORTH 0
#define APL_GPIO_NORTHWEST 1
#define APL_GPIO_WEST 2
#define APL_GPIO_SOUTHWEST 3
+
#define APL_GPIO_NR_DEVICES 4
+#define APL_GPIO_NR_RESOURCES 4
/* Offset data for Apollo Lake GPIO controllers */
-static resource_size_t apl_gpio_offsets[APL_GPIO_NR_DEVICES] = {
+static const resource_size_t apl_gpio_offsets[APL_GPIO_NR_RESOURCES] = {
[APL_GPIO_NORTH] = 0xc50000,
[APL_GPIO_NORTHWEST] = 0xc40000,
[APL_GPIO_WEST] = 0xc70000,
[APL_GPIO_SOUTHWEST] = 0xc00000,
};
-#define APL_GPIO_RESOURCE_SIZE 0x1000
-
#define APL_GPIO_IRQ 14
static struct resource apl_gpio_resources[APL_GPIO_NR_DEVICES][2] = {
@@ -181,6 +180,13 @@ static struct resource apl_gpio_resources[APL_GPIO_NR_DEVICES][2] = {
},
};
+static struct resource *apl_gpio_mem_resources[APL_GPIO_NR_RESOURCES] = {
+ [APL_GPIO_NORTH] = &apl_gpio_resources[APL_GPIO_NORTH][0],
+ [APL_GPIO_NORTHWEST] = &apl_gpio_resources[APL_GPIO_NORTHWEST][0],
+ [APL_GPIO_WEST] = &apl_gpio_resources[APL_GPIO_WEST][0],
+ [APL_GPIO_SOUTHWEST] = &apl_gpio_resources[APL_GPIO_SOUTHWEST][0],
+};
+
static const struct mfd_cell apl_gpio_devices[APL_GPIO_NR_DEVICES] = {
[APL_GPIO_NORTH] = {
.name = "apollolake-pinctrl",
@@ -212,6 +218,58 @@ static const struct mfd_cell apl_gpio_devices[APL_GPIO_NR_DEVICES] = {
},
};
+static const struct lpc_ich_gpio_info apl_gpio_info = {
+ .hid = "INT3452",
+ .devices = apl_gpio_devices,
+ .nr_devices = ARRAY_SIZE(apl_gpio_devices),
+ .resources = apl_gpio_mem_resources,
+ .nr_resources = ARRAY_SIZE(apl_gpio_mem_resources),
+ .offsets = apl_gpio_offsets,
+};
+
+#define DNV_GPIO_NORTH 0
+#define DNV_GPIO_SOUTH 1
+
+#define DNV_GPIO_NR_DEVICES 1
+#define DNV_GPIO_NR_RESOURCES 2
+
+/* Offset data for Denverton GPIO controllers */
+static const resource_size_t dnv_gpio_offsets[DNV_GPIO_NR_RESOURCES] = {
+ [DNV_GPIO_NORTH] = 0xc20000,
+ [DNV_GPIO_SOUTH] = 0xc50000,
+};
+
+#define DNV_GPIO_IRQ 14
+
+static struct resource dnv_gpio_resources[DNV_GPIO_NR_RESOURCES + 1] = {
+ [DNV_GPIO_NORTH] = DEFINE_RES_MEM(0, 0),
+ [DNV_GPIO_SOUTH] = DEFINE_RES_MEM(0, 0),
+ DEFINE_RES_IRQ(DNV_GPIO_IRQ),
+};
+
+static struct resource *dnv_gpio_mem_resources[DNV_GPIO_NR_RESOURCES] = {
+ [DNV_GPIO_NORTH] = &dnv_gpio_resources[DNV_GPIO_NORTH],
+ [DNV_GPIO_SOUTH] = &dnv_gpio_resources[DNV_GPIO_SOUTH],
+};
+
+static const struct mfd_cell dnv_gpio_devices[DNV_GPIO_NR_DEVICES] = {
+ {
+ .name = "denverton-pinctrl",
+ .num_resources = ARRAY_SIZE(dnv_gpio_resources),
+ .resources = dnv_gpio_resources,
+ .ignore_resource_conflicts = true,
+ },
+};
+
+static const struct lpc_ich_gpio_info dnv_gpio_info = {
+ .hid = "INTC3000",
+ .devices = dnv_gpio_devices,
+ .nr_devices = ARRAY_SIZE(dnv_gpio_devices),
+ .resources = dnv_gpio_mem_resources,
+ .nr_resources = ARRAY_SIZE(dnv_gpio_mem_resources),
+ .offsets = dnv_gpio_offsets,
+};
+
static struct mfd_cell lpc_ich_spi_cell = {
.name = "intel-spi",
.num_resources = ARRAY_SIZE(intel_spi_res),
@@ -289,10 +347,24 @@ enum lpc_chipsets {
LPC_LEWISBURG, /* Lewisburg */
LPC_9S, /* 9 Series */
LPC_APL, /* Apollo Lake SoC */
+ LPC_DNV, /* Denverton SoC */
LPC_GLK, /* Gemini Lake SoC */
LPC_COUGARMOUNTAIN,/* Cougar Mountain SoC*/
};
+struct lpc_ich_priv {
+ enum lpc_chipsets chipset;
+
+ int abase; /* ACPI base */
+ int actrl_pbase; /* ACPI control or PMC base */
+ int gbase; /* GPIO base */
+ int gctrl; /* GPIO control */
+
+ int abase_save; /* Cached ACPI base value */
+ int actrl_pbase_save; /* Cached ACPI control or PMC base value */
+ int gctrl_save; /* Cached GPIO control value */
+};
+
static struct lpc_ich_info lpc_chipset_info[] = {
[LPC_ICH] = {
.name = "ICH",
@@ -618,8 +690,13 @@ static struct lpc_ich_info lpc_chipset_info[] = {
[LPC_APL] = {
.name = "Apollo Lake SoC",
.iTCO_version = 5,
+ .gpio_info = &apl_gpio_info,
.spi_type = INTEL_SPI_BXT,
},
+ [LPC_DNV] = {
+ .name = "Denverton SoC",
+ .gpio_info = &dnv_gpio_info,
+ },
[LPC_GLK] = {
.name = "Gemini Lake SoC",
.spi_type = INTEL_SPI_BXT,
@@ -638,6 +715,7 @@ static struct lpc_ich_info lpc_chipset_info[] = {
*/
static const struct pci_device_id lpc_ich_ids[] = {
{ PCI_VDEVICE(INTEL, 0x0f1c), LPC_BAYTRAIL},
+ { PCI_VDEVICE(INTEL, 0x19dc), LPC_DNV},
{ PCI_VDEVICE(INTEL, 0x1c41), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c42), LPC_CPTD},
{ PCI_VDEVICE(INTEL, 0x1c43), LPC_CPTM},
@@ -756,8 +834,9 @@ static const struct pci_device_id lpc_ich_ids[] = {
{ PCI_VDEVICE(INTEL, 0x2917), LPC_ICH9ME},
{ PCI_VDEVICE(INTEL, 0x2918), LPC_ICH9},
{ PCI_VDEVICE(INTEL, 0x2919), LPC_ICH9M},
- { PCI_VDEVICE(INTEL, 0x3197), LPC_GLK},
{ PCI_VDEVICE(INTEL, 0x2b9c), LPC_COUGARMOUNTAIN},
+ { PCI_VDEVICE(INTEL, 0x3197), LPC_GLK},
+ { PCI_VDEVICE(INTEL, 0x31e8), LPC_GLK},
{ PCI_VDEVICE(INTEL, 0x3a14), LPC_ICH10DO},
{ PCI_VDEVICE(INTEL, 0x3a16), LPC_ICH10R},
{ PCI_VDEVICE(INTEL, 0x3a18), LPC_ICH10},
@@ -1156,30 +1235,32 @@ wdt_done:
static int lpc_ich_init_pinctrl(struct pci_dev *dev)
{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
+ const struct lpc_ich_gpio_info *info = lpc_chipset_info[priv->chipset].gpio_info;
struct resource base;
unsigned int i;
int ret;
/* Check, if GPIO has been exported as an ACPI device */
- if (acpi_dev_present("INT3452", NULL, -1))
+ if (acpi_dev_present(info->hid, NULL, -1))
return -EEXIST;
ret = p2sb_bar(dev->bus, 0, &base);
if (ret)
return ret;
- for (i = 0; i < ARRAY_SIZE(apl_gpio_devices); i++) {
- struct resource *mem = &apl_gpio_resources[i][0];
- resource_size_t offset = apl_gpio_offsets[i];
+ for (i = 0; i < info->nr_resources; i++) {
+ struct resource *mem = info->resources[i];
+ resource_size_t offset = info->offsets[i];
/* Fill MEM resource */
mem->start = base.start + offset;
- mem->end = base.start + offset + APL_GPIO_RESOURCE_SIZE - 1;
+ mem->end = base.start + offset + INTEL_GPIO_RESOURCE_SIZE - 1;
mem->flags = base.flags;
}
- return mfd_add_devices(&dev->dev, 0, apl_gpio_devices,
- ARRAY_SIZE(apl_gpio_devices), NULL, 0, NULL);
+ return mfd_add_devices(&dev->dev, 0, info->devices, info->nr_devices,
+ NULL, 0, NULL);
}
static bool lpc_ich_byt_set_writeable(void __iomem *base, void *data)
@@ -1242,7 +1323,7 @@ static int lpc_ich_init_spi(struct pci_dev *dev)
case INTEL_SPI_BYT:
pci_read_config_dword(dev, SPIBASE_BYT, &spi_base);
if (spi_base & SPIBASE_BYT_EN) {
- res->start = spi_base & ~(SPIBASE_BYT_SZ - 1);
+ res->start = ALIGN_DOWN(spi_base, SPIBASE_BYT_SZ);
res->end = res->start + SPIBASE_BYT_SZ - 1;
info->set_writeable = lpc_ich_byt_set_writeable;
@@ -1332,7 +1413,7 @@ static int lpc_ich_probe(struct pci_dev *dev,
cell_added = true;
}
- if (priv->chipset == LPC_APL) {
+ if (lpc_chipset_info[priv->chipset].gpio_info) {
ret = lpc_ich_init_pinctrl(dev);
if (!ret)
cell_added = true;
diff --git a/drivers/mfd/ls2k-bmc-core.c b/drivers/mfd/ls2k-bmc-core.c
new file mode 100644
index 000000000000..e42f1de9e641
--- /dev/null
+++ b/drivers/mfd/ls2k-bmc-core.c
@@ -0,0 +1,532 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Loongson-2K Board Management Controller (BMC) Core Driver.
+ *
+ * Copyright (C) 2024-2025 Loongson Technology Corporation Limited.
+ *
+ * Authors:
+ * Chong Qiao <qiaochong@loongson.cn>
+ * Binbin Zhou <zhoubinbin@loongson.cn>
+ */
+
+#include <linux/aperture.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/iopoll.h>
+#include <linux/kbd_kern.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/platform_device.h>
+#include <linux/stop_machine.h>
+#include <linux/vt_kern.h>
+
+/* LS2K BMC resources */
+#define LS2K_DISPLAY_RES_START (SZ_16M + SZ_2M)
+#define LS2K_IPMI_RES_SIZE 0x1C
+#define LS2K_IPMI0_RES_START (SZ_16M + 0xF00000)
+#define LS2K_IPMI1_RES_START (LS2K_IPMI0_RES_START + LS2K_IPMI_RES_SIZE)
+#define LS2K_IPMI2_RES_START (LS2K_IPMI1_RES_START + LS2K_IPMI_RES_SIZE)
+#define LS2K_IPMI3_RES_START (LS2K_IPMI2_RES_START + LS2K_IPMI_RES_SIZE)
+#define LS2K_IPMI4_RES_START (LS2K_IPMI3_RES_START + LS2K_IPMI_RES_SIZE)
+
+#define LS7A_PCI_CFG_SIZE 0x100
+
+/* LS7A bridge registers */
+#define LS7A_PCIE_PORT_CTL0 0x0
+#define LS7A_PCIE_PORT_STS1 0xC
+#define LS7A_GEN2_CTL 0x80C
+#define LS7A_SYMBOL_TIMER 0x71C
+
+/* Bits of LS7A_PCIE_PORT_CTL0 */
+#define LS2K_BMC_PCIE_LTSSM_ENABLE BIT(3)
+
+/* Bits of LS7A_PCIE_PORT_STS1 */
+#define LS2K_BMC_PCIE_LTSSM_STS GENMASK(5, 0)
+#define LS2K_BMC_PCIE_CONNECTED 0x11
+
+#define LS2K_BMC_PCIE_DELAY_US 1000
+#define LS2K_BMC_PCIE_TIMEOUT_US 1000000
+
+/* Bits of LS7A_GEN2_CTL */
+#define LS7A_GEN2_SPEED_CHANG BIT(17)
+#define LS7A_CONF_PHY_TX BIT(18)
+
+/* Bits of LS7A_SYMBOL_TIMER */
+#define LS7A_MASK_LEN_MATCH BIT(26)
+
+/* Interval between interruptions */
+#define LS2K_BMC_INT_INTERVAL (60 * HZ)
+
+/* Maximum time to wait for U-Boot and DDR to be ready with ms. */
+#define LS2K_BMC_RESET_WAIT_TIME 10000
+
+/* It's an experience value */
+#define LS7A_BAR0_CHECK_MAX_TIMES 2000
+
+#define PCI_REG_STRIDE 0x4
+
+#define LS2K_BMC_RESET_GPIO 14
+#define LOONGSON_GPIO_REG_BASE 0x1FE00500
+#define LOONGSON_GPIO_REG_SIZE 0x18
+#define LOONGSON_GPIO_OEN 0x0
+#define LOONGSON_GPIO_FUNC 0x4
+#define LOONGSON_GPIO_INTPOL 0x10
+#define LOONGSON_GPIO_INTEN 0x14
+
+#define LOONGSON_IO_INT_BASE 16
+#define LS2K_BMC_RESET_GPIO_INT_VEC (LS2K_BMC_RESET_GPIO % 8)
+#define LS2K_BMC_RESET_GPIO_GSI (LOONGSON_IO_INT_BASE + LS2K_BMC_RESET_GPIO_INT_VEC)
+
+enum {
+ LS2K_BMC_DISPLAY,
+ LS2K_BMC_IPMI0,
+ LS2K_BMC_IPMI1,
+ LS2K_BMC_IPMI2,
+ LS2K_BMC_IPMI3,
+ LS2K_BMC_IPMI4,
+};
+
+static struct resource ls2k_display_resources[] = {
+ DEFINE_RES_MEM_NAMED(LS2K_DISPLAY_RES_START, SZ_4M, "simpledrm-res"),
+};
+
+static struct resource ls2k_ipmi0_resources[] = {
+ DEFINE_RES_MEM_NAMED(LS2K_IPMI0_RES_START, LS2K_IPMI_RES_SIZE, "ipmi0-res"),
+};
+
+static struct resource ls2k_ipmi1_resources[] = {
+ DEFINE_RES_MEM_NAMED(LS2K_IPMI1_RES_START, LS2K_IPMI_RES_SIZE, "ipmi1-res"),
+};
+
+static struct resource ls2k_ipmi2_resources[] = {
+ DEFINE_RES_MEM_NAMED(LS2K_IPMI2_RES_START, LS2K_IPMI_RES_SIZE, "ipmi2-res"),
+};
+
+static struct resource ls2k_ipmi3_resources[] = {
+ DEFINE_RES_MEM_NAMED(LS2K_IPMI3_RES_START, LS2K_IPMI_RES_SIZE, "ipmi3-res"),
+};
+
+static struct resource ls2k_ipmi4_resources[] = {
+ DEFINE_RES_MEM_NAMED(LS2K_IPMI4_RES_START, LS2K_IPMI_RES_SIZE, "ipmi4-res"),
+};
+
+static struct mfd_cell ls2k_bmc_cells[] = {
+ [LS2K_BMC_DISPLAY] = {
+ .name = "simple-framebuffer",
+ .num_resources = ARRAY_SIZE(ls2k_display_resources),
+ .resources = ls2k_display_resources
+ },
+ [LS2K_BMC_IPMI0] = {
+ .name = "ls2k-ipmi-si",
+ .num_resources = ARRAY_SIZE(ls2k_ipmi0_resources),
+ .resources = ls2k_ipmi0_resources
+ },
+ [LS2K_BMC_IPMI1] = {
+ .name = "ls2k-ipmi-si",
+ .num_resources = ARRAY_SIZE(ls2k_ipmi1_resources),
+ .resources = ls2k_ipmi1_resources
+ },
+ [LS2K_BMC_IPMI2] = {
+ .name = "ls2k-ipmi-si",
+ .num_resources = ARRAY_SIZE(ls2k_ipmi2_resources),
+ .resources = ls2k_ipmi2_resources
+ },
+ [LS2K_BMC_IPMI3] = {
+ .name = "ls2k-ipmi-si",
+ .num_resources = ARRAY_SIZE(ls2k_ipmi3_resources),
+ .resources = ls2k_ipmi3_resources
+ },
+ [LS2K_BMC_IPMI4] = {
+ .name = "ls2k-ipmi-si",
+ .num_resources = ARRAY_SIZE(ls2k_ipmi4_resources),
+ .resources = ls2k_ipmi4_resources
+ },
+};
+
+/* Index of the BMC PCI configuration space to be restored at BMC reset. */
+struct ls2k_bmc_pci_data {
+ u32 pci_command;
+ u32 base_address0;
+ u32 interrupt_line;
+};
+
+/* Index of the parent PCI configuration space to be restored at BMC reset. */
+struct ls2k_bmc_bridge_pci_data {
+ u32 pci_command;
+ u32 base_address[6];
+ u32 rom_addreess;
+ u32 interrupt_line;
+ u32 msi_hi;
+ u32 msi_lo;
+ u32 devctl;
+ u32 linkcap;
+ u32 linkctl_sts;
+ u32 symbol_timer;
+ u32 gen2_ctrl;
+};
+
+struct ls2k_bmc_ddata {
+ struct device *dev;
+ struct work_struct bmc_reset_work;
+ struct ls2k_bmc_pci_data bmc_pci_data;
+ struct ls2k_bmc_bridge_pci_data bridge_pci_data;
+};
+
+static bool ls2k_bmc_bar0_addr_is_set(struct pci_dev *pdev)
+{
+ u32 addr;
+
+ pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &addr);
+
+ return addr & PCI_BASE_ADDRESS_MEM_MASK ? true : false;
+}
+
+static bool ls2k_bmc_pcie_is_connected(struct pci_dev *parent, struct ls2k_bmc_ddata *ddata)
+{
+ void __iomem *base;
+ int val, ret;
+
+ base = pci_iomap(parent, 0, LS7A_PCI_CFG_SIZE);
+ if (!base)
+ return false;
+
+ val = readl(base + LS7A_PCIE_PORT_CTL0);
+ writel(val | LS2K_BMC_PCIE_LTSSM_ENABLE, base + LS7A_PCIE_PORT_CTL0);
+
+ ret = readl_poll_timeout_atomic(base + LS7A_PCIE_PORT_STS1, val,
+ (val & LS2K_BMC_PCIE_LTSSM_STS) == LS2K_BMC_PCIE_CONNECTED,
+ LS2K_BMC_PCIE_DELAY_US, LS2K_BMC_PCIE_TIMEOUT_US);
+ if (ret) {
+ pci_iounmap(parent, base);
+ dev_err(ddata->dev, "PCI-E training failed status=0x%x\n", val);
+ return false;
+ }
+
+ pci_iounmap(parent, base);
+ return true;
+}
+
+static void ls2k_bmc_restore_bridge_pci_data(struct pci_dev *parent, struct ls2k_bmc_ddata *ddata)
+{
+ int base, i = 0;
+
+ pci_write_config_dword(parent, PCI_COMMAND, ddata->bridge_pci_data.pci_command);
+
+ for (base = PCI_BASE_ADDRESS_0; base <= PCI_BASE_ADDRESS_5; base += PCI_REG_STRIDE, i++)
+ pci_write_config_dword(parent, base, ddata->bridge_pci_data.base_address[i]);
+
+ pci_write_config_dword(parent, PCI_ROM_ADDRESS, ddata->bridge_pci_data.rom_addreess);
+ pci_write_config_dword(parent, PCI_INTERRUPT_LINE, ddata->bridge_pci_data.interrupt_line);
+
+ pci_write_config_dword(parent, parent->msi_cap + PCI_MSI_ADDRESS_LO,
+ ddata->bridge_pci_data.msi_lo);
+ pci_write_config_dword(parent, parent->msi_cap + PCI_MSI_ADDRESS_HI,
+ ddata->bridge_pci_data.msi_hi);
+ pci_write_config_dword(parent, parent->pcie_cap + PCI_EXP_DEVCTL,
+ ddata->bridge_pci_data.devctl);
+ pci_write_config_dword(parent, parent->pcie_cap + PCI_EXP_LNKCAP,
+ ddata->bridge_pci_data.linkcap);
+ pci_write_config_dword(parent, parent->pcie_cap + PCI_EXP_LNKCTL,
+ ddata->bridge_pci_data.linkctl_sts);
+
+ pci_write_config_dword(parent, LS7A_GEN2_CTL, ddata->bridge_pci_data.gen2_ctrl);
+ pci_write_config_dword(parent, LS7A_SYMBOL_TIMER, ddata->bridge_pci_data.symbol_timer);
+}
+
+static int ls2k_bmc_recover_pci_data(void *data)
+{
+ struct ls2k_bmc_ddata *ddata = data;
+ struct pci_dev *pdev = to_pci_dev(ddata->dev);
+ struct pci_dev *parent = pdev->bus->self;
+ u32 i;
+
+ /*
+ * Clear the bus, io and mem resources of the PCI-E bridge to zero, so that
+ * the processor can not access the LS2K PCI-E port, to avoid crashing due to
+ * the lack of return signal from accessing the LS2K PCI-E port.
+ */
+ pci_write_config_dword(parent, PCI_BASE_ADDRESS_2, 0);
+ pci_write_config_dword(parent, PCI_BASE_ADDRESS_3, 0);
+ pci_write_config_dword(parent, PCI_BASE_ADDRESS_4, 0);
+
+ /*
+ * When the LS2K BMC is reset, the LS7A PCI-E port is also reset, and its PCI
+ * BAR0 register is cleared. Due to the time gap between the GPIO interrupt
+ * generation and the LS2K BMC reset, the LS7A PCI BAR0 register is read to
+ * determine whether the reset has begun.
+ */
+ for (i = LS7A_BAR0_CHECK_MAX_TIMES; i > 0 ; i--) {
+ if (!ls2k_bmc_bar0_addr_is_set(parent))
+ break;
+ mdelay(1);
+ }
+
+ if (i == 0)
+ return false;
+
+ ls2k_bmc_restore_bridge_pci_data(parent, ddata);
+
+ /* Check if PCI-E is connected */
+ if (!ls2k_bmc_pcie_is_connected(parent, ddata))
+ return false;
+
+ /* Waiting for U-Boot and DDR ready */
+ mdelay(LS2K_BMC_RESET_WAIT_TIME);
+ if (!ls2k_bmc_bar0_addr_is_set(parent))
+ return false;
+
+ /* Restore LS2K BMC PCI-E config data */
+ pci_write_config_dword(pdev, PCI_COMMAND, ddata->bmc_pci_data.pci_command);
+ pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, ddata->bmc_pci_data.base_address0);
+ pci_write_config_dword(pdev, PCI_INTERRUPT_LINE, ddata->bmc_pci_data.interrupt_line);
+
+ return 0;
+}
+
+static void ls2k_bmc_events_fn(struct work_struct *work)
+{
+ struct ls2k_bmc_ddata *ddata = container_of(work, struct ls2k_bmc_ddata, bmc_reset_work);
+
+ /*
+ * The PCI-E is lost when the BMC resets, at which point access to the PCI-E
+ * from other CPUs is suspended to prevent a crash.
+ */
+ stop_machine(ls2k_bmc_recover_pci_data, ddata, NULL);
+
+ if (IS_ENABLED(CONFIG_VT)) {
+ /* Re-push the display due to previous PCI-E loss. */
+ set_console(vt_move_to_console(MAX_NR_CONSOLES - 1, 1));
+ }
+}
+
+static irqreturn_t ls2k_bmc_interrupt(int irq, void *arg)
+{
+ struct ls2k_bmc_ddata *ddata = arg;
+ static unsigned long last_jiffies;
+
+ if (system_state != SYSTEM_RUNNING)
+ return IRQ_HANDLED;
+
+ /* Skip interrupt in LS2K_BMC_INT_INTERVAL */
+ if (time_after(jiffies, last_jiffies + LS2K_BMC_INT_INTERVAL)) {
+ schedule_work(&ddata->bmc_reset_work);
+ last_jiffies = jiffies;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Saves the BMC parent device (LS7A) and its own PCI configuration space registers
+ * that need to be restored after BMC reset.
+ */
+static void ls2k_bmc_save_pci_data(struct pci_dev *pdev, struct ls2k_bmc_ddata *ddata)
+{
+ struct pci_dev *parent = pdev->bus->self;
+ int base, i = 0;
+
+ pci_read_config_dword(parent, PCI_COMMAND, &ddata->bridge_pci_data.pci_command);
+
+ for (base = PCI_BASE_ADDRESS_0; base <= PCI_BASE_ADDRESS_5; base += PCI_REG_STRIDE, i++)
+ pci_read_config_dword(parent, base, &ddata->bridge_pci_data.base_address[i]);
+
+ pci_read_config_dword(parent, PCI_ROM_ADDRESS, &ddata->bridge_pci_data.rom_addreess);
+ pci_read_config_dword(parent, PCI_INTERRUPT_LINE, &ddata->bridge_pci_data.interrupt_line);
+
+ pci_read_config_dword(parent, parent->msi_cap + PCI_MSI_ADDRESS_LO,
+ &ddata->bridge_pci_data.msi_lo);
+ pci_read_config_dword(parent, parent->msi_cap + PCI_MSI_ADDRESS_HI,
+ &ddata->bridge_pci_data.msi_hi);
+
+ pci_read_config_dword(parent, parent->pcie_cap + PCI_EXP_DEVCTL,
+ &ddata->bridge_pci_data.devctl);
+ pci_read_config_dword(parent, parent->pcie_cap + PCI_EXP_LNKCAP,
+ &ddata->bridge_pci_data.linkcap);
+ pci_read_config_dword(parent, parent->pcie_cap + PCI_EXP_LNKCTL,
+ &ddata->bridge_pci_data.linkctl_sts);
+
+ pci_read_config_dword(parent, LS7A_GEN2_CTL, &ddata->bridge_pci_data.gen2_ctrl);
+ ddata->bridge_pci_data.gen2_ctrl |= FIELD_PREP(LS7A_GEN2_SPEED_CHANG, 0x1) |
+ FIELD_PREP(LS7A_CONF_PHY_TX, 0x0);
+
+ pci_read_config_dword(parent, LS7A_SYMBOL_TIMER, &ddata->bridge_pci_data.symbol_timer);
+ ddata->bridge_pci_data.symbol_timer |= LS7A_MASK_LEN_MATCH;
+
+ pci_read_config_dword(pdev, PCI_COMMAND, &ddata->bmc_pci_data.pci_command);
+ pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &ddata->bmc_pci_data.base_address0);
+ pci_read_config_dword(pdev, PCI_INTERRUPT_LINE, &ddata->bmc_pci_data.interrupt_line);
+}
+
+static int ls2k_bmc_init(struct ls2k_bmc_ddata *ddata)
+{
+ struct pci_dev *pdev = to_pci_dev(ddata->dev);
+ void __iomem *gpio_base;
+ int gpio_irq, ret, val;
+
+ ls2k_bmc_save_pci_data(pdev, ddata);
+
+ INIT_WORK(&ddata->bmc_reset_work, ls2k_bmc_events_fn);
+
+ ret = devm_request_irq(&pdev->dev, pdev->irq, ls2k_bmc_interrupt,
+ IRQF_SHARED | IRQF_TRIGGER_FALLING, "ls2kbmc pcie", ddata);
+ if (ret) {
+ dev_err(ddata->dev, "Failed to request LS2KBMC PCI-E IRQ %d.\n", pdev->irq);
+ return ret;
+ }
+
+ gpio_base = ioremap(LOONGSON_GPIO_REG_BASE, LOONGSON_GPIO_REG_SIZE);
+ if (!gpio_base)
+ return -ENOMEM;
+
+ /* Disable GPIO output */
+ val = readl(gpio_base + LOONGSON_GPIO_OEN);
+ writel(val | BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_OEN);
+
+ /* Enable GPIO functionality */
+ val = readl(gpio_base + LOONGSON_GPIO_FUNC);
+ writel(val & ~BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_FUNC);
+
+ /* Set GPIO interrupts to low-level active */
+ val = readl(gpio_base + LOONGSON_GPIO_INTPOL);
+ writel(val & ~BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_INTPOL);
+
+ /* Enable GPIO interrupts */
+ val = readl(gpio_base + LOONGSON_GPIO_INTEN);
+ writel(val | BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_INTEN);
+
+ iounmap(gpio_base);
+
+ /*
+ * Since gpio_chip->to_irq is not implemented in the Loongson-3 GPIO driver,
+ * acpi_register_gsi() is used to obtain the GPIO IRQ. The GPIO interrupt is a
+ * watchdog interrupt that is triggered when the BMC resets.
+ */
+ gpio_irq = acpi_register_gsi(NULL, LS2K_BMC_RESET_GPIO_GSI, ACPI_EDGE_SENSITIVE,
+ ACPI_ACTIVE_LOW);
+ if (gpio_irq < 0)
+ return gpio_irq;
+
+ ret = devm_request_irq(ddata->dev, gpio_irq, ls2k_bmc_interrupt,
+ IRQF_SHARED | IRQF_TRIGGER_FALLING, "ls2kbmc gpio", ddata);
+ if (ret)
+ dev_err(ddata->dev, "Failed to request LS2KBMC GPIO IRQ %d.\n", gpio_irq);
+
+ acpi_unregister_gsi(LS2K_BMC_RESET_GPIO_GSI);
+ return ret;
+}
+
+/*
+ * Currently the Loongson-2K BMC hardware does not have an I2C interface to adapt to the
+ * resolution. We set the resolution by presetting "video=1280x1024-16@2M" to the BMC memory.
+ */
+static int ls2k_bmc_parse_mode(struct pci_dev *pdev, struct simplefb_platform_data *pd)
+{
+ char *mode;
+ int depth, ret;
+
+ /* The last 16M of PCI BAR0 is used to store the resolution string. */
+ mode = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0) + SZ_16M, SZ_16M);
+ if (!mode)
+ return -ENOMEM;
+
+ /* The resolution field starts with the flag "video=". */
+ if (!strncmp(mode, "video=", 6))
+ mode = mode + 6;
+
+ ret = kstrtoint(strsep(&mode, "x"), 10, &pd->width);
+ if (ret)
+ return ret;
+
+ ret = kstrtoint(strsep(&mode, "-"), 10, &pd->height);
+ if (ret)
+ return ret;
+
+ ret = kstrtoint(strsep(&mode, "@"), 10, &depth);
+ if (ret)
+ return ret;
+
+ pd->stride = pd->width * depth / 8;
+ pd->format = depth == 32 ? "a8r8g8b8" : "r5g6b5";
+
+ return 0;
+}
+
+static int ls2k_bmc_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ struct simplefb_platform_data pd;
+ struct ls2k_bmc_ddata *ddata;
+ resource_size_t base;
+ int ret;
+
+ ret = pci_enable_device(dev);
+ if (ret)
+ return ret;
+
+ ddata = devm_kzalloc(&dev->dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata) {
+ ret = -ENOMEM;
+ goto disable_pci;
+ }
+
+ ddata->dev = &dev->dev;
+
+ ret = ls2k_bmc_init(ddata);
+ if (ret)
+ goto disable_pci;
+
+ ret = ls2k_bmc_parse_mode(dev, &pd);
+ if (ret)
+ goto disable_pci;
+
+ ls2k_bmc_cells[LS2K_BMC_DISPLAY].platform_data = &pd;
+ ls2k_bmc_cells[LS2K_BMC_DISPLAY].pdata_size = sizeof(pd);
+ base = dev->resource[0].start + LS2K_DISPLAY_RES_START;
+
+ /* Remove conflicting efifb device */
+ ret = aperture_remove_conflicting_devices(base, SZ_4M, "simple-framebuffer");
+ if (ret) {
+ dev_err(&dev->dev, "Failed to removed firmware framebuffers: %d\n", ret);
+ goto disable_pci;
+ }
+
+ ret = devm_mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO,
+ ls2k_bmc_cells, ARRAY_SIZE(ls2k_bmc_cells),
+ &dev->resource[0], 0, NULL);
+ if (ret)
+ goto disable_pci;
+
+ return 0;
+
+disable_pci:
+ pci_disable_device(dev);
+ return ret;
+}
+
+static void ls2k_bmc_remove(struct pci_dev *dev)
+{
+ pci_disable_device(dev);
+}
+
+static struct pci_device_id ls2k_bmc_devices[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, 0x1a05) },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, ls2k_bmc_devices);
+
+static struct pci_driver ls2k_bmc_driver = {
+ .name = "ls2k-bmc",
+ .id_table = ls2k_bmc_devices,
+ .probe = ls2k_bmc_probe,
+ .remove = ls2k_bmc_remove,
+};
+module_pci_driver(ls2k_bmc_driver);
+
+MODULE_DESCRIPTION("Loongson-2K Board Management Controller (BMC) Core driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/macsmc.c b/drivers/mfd/macsmc.c
new file mode 100644
index 000000000000..e3893e255ce5
--- /dev/null
+++ b/drivers/mfd/macsmc.c
@@ -0,0 +1,499 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/*
+ * Apple SMC (System Management Controller) MFD driver
+ *
+ * Copyright The Asahi Linux Contributors
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/math.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/macsmc.h>
+#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/soc/apple/rtkit.h>
+#include <linux/unaligned.h>
+
+#define SMC_ENDPOINT 0x20
+
+/* We don't actually know the true size here but this seem reasonable */
+#define SMC_SHMEM_SIZE 0x1000
+#define SMC_MAX_SIZE 255
+
+#define SMC_MSG_READ_KEY 0x10
+#define SMC_MSG_WRITE_KEY 0x11
+#define SMC_MSG_GET_KEY_BY_INDEX 0x12
+#define SMC_MSG_GET_KEY_INFO 0x13
+#define SMC_MSG_INITIALIZE 0x17
+#define SMC_MSG_NOTIFICATION 0x18
+#define SMC_MSG_RW_KEY 0x20
+
+#define SMC_DATA GENMASK_ULL(63, 32)
+#define SMC_WSIZE GENMASK_ULL(31, 24)
+#define SMC_SIZE GENMASK_ULL(23, 16)
+#define SMC_ID GENMASK_ULL(15, 12)
+#define SMC_MSG GENMASK_ULL(7, 0)
+#define SMC_RESULT SMC_MSG
+
+#define SMC_TIMEOUT_MS 500
+
+static const struct mfd_cell apple_smc_devs[] = {
+ MFD_CELL_OF("macsmc-gpio", NULL, NULL, 0, 0, "apple,smc-gpio"),
+ MFD_CELL_OF("macsmc-reboot", NULL, NULL, 0, 0, "apple,smc-reboot"),
+};
+
+static int apple_smc_cmd_locked(struct apple_smc *smc, u64 cmd, u64 arg,
+ u64 size, u64 wsize, u32 *ret_data)
+{
+ u8 result;
+ int ret;
+ u64 msg;
+
+ lockdep_assert_held(&smc->mutex);
+
+ if (smc->boot_stage != APPLE_SMC_INITIALIZED)
+ return -EIO;
+ if (smc->atomic_mode)
+ return -EIO;
+
+ reinit_completion(&smc->cmd_done);
+
+ smc->msg_id = (smc->msg_id + 1) & 0xf;
+ msg = (FIELD_PREP(SMC_MSG, cmd) |
+ FIELD_PREP(SMC_SIZE, size) |
+ FIELD_PREP(SMC_WSIZE, wsize) |
+ FIELD_PREP(SMC_ID, smc->msg_id) |
+ FIELD_PREP(SMC_DATA, arg));
+
+ ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT, msg, NULL, false);
+ if (ret) {
+ dev_err(smc->dev, "Failed to send command\n");
+ return ret;
+ }
+
+ if (wait_for_completion_timeout(&smc->cmd_done, msecs_to_jiffies(SMC_TIMEOUT_MS)) <= 0) {
+ dev_err(smc->dev, "Command timed out (%llx)", msg);
+ return -ETIMEDOUT;
+ }
+
+ if (FIELD_GET(SMC_ID, smc->cmd_ret) != smc->msg_id) {
+ dev_err(smc->dev, "Command sequence mismatch (expected %d, got %d)\n",
+ smc->msg_id, (unsigned int)FIELD_GET(SMC_ID, smc->cmd_ret));
+ return -EIO;
+ }
+
+ result = FIELD_GET(SMC_RESULT, smc->cmd_ret);
+ if (result)
+ return -EIO;
+
+ if (ret_data)
+ *ret_data = FIELD_GET(SMC_DATA, smc->cmd_ret);
+
+ return FIELD_GET(SMC_SIZE, smc->cmd_ret);
+}
+
+static int apple_smc_cmd(struct apple_smc *smc, u64 cmd, u64 arg,
+ u64 size, u64 wsize, u32 *ret_data)
+{
+ guard(mutex)(&smc->mutex);
+
+ return apple_smc_cmd_locked(smc, cmd, arg, size, wsize, ret_data);
+}
+
+static int apple_smc_rw_locked(struct apple_smc *smc, smc_key key,
+ const void *wbuf, size_t wsize,
+ void *rbuf, size_t rsize)
+{
+ u64 smc_size, smc_wsize;
+ u32 rdata;
+ int ret;
+ u64 cmd;
+
+ lockdep_assert_held(&smc->mutex);
+
+ if (rsize > SMC_MAX_SIZE)
+ return -EINVAL;
+ if (wsize > SMC_MAX_SIZE)
+ return -EINVAL;
+
+ if (rsize && wsize) {
+ cmd = SMC_MSG_RW_KEY;
+ memcpy_toio(smc->shmem.iomem, wbuf, wsize);
+ smc_size = rsize;
+ smc_wsize = wsize;
+ } else if (wsize && !rsize) {
+ cmd = SMC_MSG_WRITE_KEY;
+ memcpy_toio(smc->shmem.iomem, wbuf, wsize);
+ /*
+ * Setting size to the length we want to write and wsize to 0
+ * looks silly but that's how the SMC protocol works ¯\_(ツ)_/¯
+ */
+ smc_size = wsize;
+ smc_wsize = 0;
+ } else if (!wsize && rsize) {
+ cmd = SMC_MSG_READ_KEY;
+ smc_size = rsize;
+ smc_wsize = 0;
+ } else {
+ return -EINVAL;
+ }
+
+ ret = apple_smc_cmd_locked(smc, cmd, key, smc_size, smc_wsize, &rdata);
+ if (ret < 0)
+ return ret;
+
+ if (rsize) {
+ /*
+ * Small data <= 4 bytes is returned as part of the reply
+ * message which is sent over the mailbox FIFO. Everything
+ * bigger has to be copied from SRAM which is mapped as
+ * Device memory.
+ */
+ if (rsize <= 4)
+ memcpy(rbuf, &rdata, rsize);
+ else
+ memcpy_fromio(rbuf, smc->shmem.iomem, rsize);
+ }
+
+ return ret;
+}
+
+int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size)
+{
+ guard(mutex)(&smc->mutex);
+
+ return apple_smc_rw_locked(smc, key, NULL, 0, buf, size);
+}
+EXPORT_SYMBOL(apple_smc_read);
+
+int apple_smc_write(struct apple_smc *smc, smc_key key, const void *buf, size_t size)
+{
+ guard(mutex)(&smc->mutex);
+
+ return apple_smc_rw_locked(smc, key, buf, size, NULL, 0);
+}
+EXPORT_SYMBOL(apple_smc_write);
+
+int apple_smc_rw(struct apple_smc *smc, smc_key key, const void *wbuf, size_t wsize,
+ void *rbuf, size_t rsize)
+{
+ guard(mutex)(&smc->mutex);
+
+ return apple_smc_rw_locked(smc, key, wbuf, wsize, rbuf, rsize);
+}
+EXPORT_SYMBOL(apple_smc_rw);
+
+int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key)
+{
+ int ret;
+
+ ret = apple_smc_cmd(smc, SMC_MSG_GET_KEY_BY_INDEX, index, 0, 0, key);
+
+ *key = swab32(*key);
+ return ret;
+}
+EXPORT_SYMBOL(apple_smc_get_key_by_index);
+
+int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_key_info *info)
+{
+ u8 key_info[6];
+ int ret;
+
+ ret = apple_smc_cmd(smc, SMC_MSG_GET_KEY_INFO, key, 0, 0, NULL);
+ if (ret >= 0 && info) {
+ memcpy_fromio(key_info, smc->shmem.iomem, sizeof(key_info));
+ info->size = key_info[0];
+ info->type_code = get_unaligned_be32(&key_info[1]);
+ info->flags = key_info[5];
+ }
+ return ret;
+}
+EXPORT_SYMBOL(apple_smc_get_key_info);
+
+int apple_smc_enter_atomic(struct apple_smc *smc)
+{
+ guard(mutex)(&smc->mutex);
+
+ /*
+ * Disable notifications since this is called before shutdown and no
+ * notification handler will be able to handle the notification
+ * using atomic operations only. Also ignore any failure here
+ * because we're about to shut down or reboot anyway.
+ * We can't use apple_smc_write_flag here since that would try to lock
+ * smc->mutex again.
+ */
+ const u8 flag = 0;
+
+ apple_smc_rw_locked(smc, SMC_KEY(NTAP), &flag, sizeof(flag), NULL, 0);
+
+ smc->atomic_mode = true;
+
+ return 0;
+}
+EXPORT_SYMBOL(apple_smc_enter_atomic);
+
+int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, const void *buf, size_t size)
+{
+ guard(spinlock_irqsave)(&smc->lock);
+ u8 result;
+ int ret;
+ u64 msg;
+
+ if (size > SMC_MAX_SIZE || size == 0)
+ return -EINVAL;
+
+ if (smc->boot_stage != APPLE_SMC_INITIALIZED)
+ return -EIO;
+ if (!smc->atomic_mode)
+ return -EIO;
+
+ memcpy_toio(smc->shmem.iomem, buf, size);
+ smc->msg_id = (smc->msg_id + 1) & 0xf;
+ msg = (FIELD_PREP(SMC_MSG, SMC_MSG_WRITE_KEY) |
+ FIELD_PREP(SMC_SIZE, size) |
+ FIELD_PREP(SMC_ID, smc->msg_id) |
+ FIELD_PREP(SMC_DATA, key));
+ smc->atomic_pending = true;
+
+ ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT, msg, NULL, true);
+ if (ret < 0) {
+ dev_err(smc->dev, "Failed to send command (%d)\n", ret);
+ return ret;
+ }
+
+ while (smc->atomic_pending) {
+ ret = apple_rtkit_poll(smc->rtk);
+ if (ret < 0) {
+ dev_err(smc->dev, "RTKit poll failed (%llx)", msg);
+ return ret;
+ }
+ udelay(100);
+ }
+
+ if (FIELD_GET(SMC_ID, smc->cmd_ret) != smc->msg_id) {
+ dev_err(smc->dev, "Command sequence mismatch (expected %d, got %d)\n",
+ smc->msg_id, (unsigned int)FIELD_GET(SMC_ID, smc->cmd_ret));
+ return -EIO;
+ }
+
+ result = FIELD_GET(SMC_RESULT, smc->cmd_ret);
+ if (result)
+ return -EIO;
+
+ return FIELD_GET(SMC_SIZE, smc->cmd_ret);
+}
+EXPORT_SYMBOL(apple_smc_write_atomic);
+
+static void apple_smc_rtkit_crashed(void *cookie, const void *bfr, size_t bfr_len)
+{
+ struct apple_smc *smc = cookie;
+
+ smc->boot_stage = APPLE_SMC_ERROR_CRASHED;
+ dev_err(smc->dev, "SMC crashed! Your system will reboot in a few seconds...\n");
+}
+
+static int apple_smc_rtkit_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr)
+{
+ struct apple_smc *smc = cookie;
+ size_t bfr_end;
+
+ if (!bfr->iova) {
+ dev_err(smc->dev, "RTKit wants a RAM buffer\n");
+ return -EIO;
+ }
+
+ if (check_add_overflow(bfr->iova, bfr->size - 1, &bfr_end))
+ return -EFAULT;
+
+ if (bfr->iova < smc->sram->start || bfr->iova > smc->sram->end ||
+ bfr_end > smc->sram->end) {
+ dev_err(smc->dev, "RTKit buffer request outside SRAM region: [0x%llx, 0x%llx]\n",
+ (unsigned long long)bfr->iova,
+ (unsigned long long)bfr_end);
+ return -EFAULT;
+ }
+
+ bfr->iomem = smc->sram_base + (bfr->iova - smc->sram->start);
+ bfr->is_mapped = true;
+
+ return 0;
+}
+
+static bool apple_smc_rtkit_recv_early(void *cookie, u8 endpoint, u64 message)
+{
+ struct apple_smc *smc = cookie;
+
+ if (endpoint != SMC_ENDPOINT) {
+ dev_warn(smc->dev, "Received message for unknown endpoint 0x%x\n", endpoint);
+ return false;
+ }
+
+ if (smc->boot_stage == APPLE_SMC_BOOTING) {
+ int ret;
+
+ smc->shmem.iova = message;
+ smc->shmem.size = SMC_SHMEM_SIZE;
+ ret = apple_smc_rtkit_shmem_setup(smc, &smc->shmem);
+ if (ret < 0) {
+ smc->boot_stage = APPLE_SMC_ERROR_NO_SHMEM;
+ dev_err(smc->dev, "Failed to initialize shared memory (%d)\n", ret);
+ } else {
+ smc->boot_stage = APPLE_SMC_INITIALIZED;
+ }
+ complete(&smc->init_done);
+ } else if (FIELD_GET(SMC_MSG, message) == SMC_MSG_NOTIFICATION) {
+ /* Handle these in the RTKit worker thread */
+ return false;
+ } else {
+ smc->cmd_ret = message;
+ if (smc->atomic_pending)
+ smc->atomic_pending = false;
+ else
+ complete(&smc->cmd_done);
+ }
+
+ return true;
+}
+
+static void apple_smc_rtkit_recv(void *cookie, u8 endpoint, u64 message)
+{
+ struct apple_smc *smc = cookie;
+
+ if (endpoint != SMC_ENDPOINT) {
+ dev_warn(smc->dev, "Received message for unknown endpoint 0x%x\n", endpoint);
+ return;
+ }
+
+ if (FIELD_GET(SMC_MSG, message) != SMC_MSG_NOTIFICATION) {
+ dev_warn(smc->dev, "Received unknown message from worker: 0x%llx\n", message);
+ return;
+ }
+
+ blocking_notifier_call_chain(&smc->event_handlers, FIELD_GET(SMC_DATA, message), NULL);
+}
+
+static const struct apple_rtkit_ops apple_smc_rtkit_ops = {
+ .crashed = apple_smc_rtkit_crashed,
+ .recv_message = apple_smc_rtkit_recv,
+ .recv_message_early = apple_smc_rtkit_recv_early,
+ .shmem_setup = apple_smc_rtkit_shmem_setup,
+};
+
+static void apple_smc_rtkit_shutdown(void *data)
+{
+ struct apple_smc *smc = data;
+
+ /* Shut down SMC firmware, if it's not completely wedged */
+ if (apple_rtkit_is_running(smc->rtk))
+ apple_rtkit_quiesce(smc->rtk);
+}
+
+static void apple_smc_disable_notifications(void *data)
+{
+ struct apple_smc *smc = data;
+
+ apple_smc_write_flag(smc, SMC_KEY(NTAP), false);
+}
+
+static int apple_smc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct apple_smc *smc;
+ u32 count;
+ int ret;
+
+ smc = devm_kzalloc(dev, sizeof(*smc), GFP_KERNEL);
+ if (!smc)
+ return -ENOMEM;
+
+ smc->dev = &pdev->dev;
+ smc->sram_base = devm_platform_get_and_ioremap_resource(pdev, 1, &smc->sram);
+ if (IS_ERR(smc->sram_base))
+ return dev_err_probe(dev, PTR_ERR(smc->sram_base), "Failed to map SRAM region");
+
+ smc->rtk = devm_apple_rtkit_init(dev, smc, NULL, 0, &apple_smc_rtkit_ops);
+ if (IS_ERR(smc->rtk))
+ return dev_err_probe(dev, PTR_ERR(smc->rtk), "Failed to initialize RTKit");
+
+ smc->boot_stage = APPLE_SMC_BOOTING;
+ ret = apple_rtkit_wake(smc->rtk);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to wake up SMC");
+
+ ret = devm_add_action_or_reset(dev, apple_smc_rtkit_shutdown, smc);
+ if (ret)
+ return ret;
+
+ ret = apple_rtkit_start_ep(smc->rtk, SMC_ENDPOINT);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to start SMC endpoint");
+
+ init_completion(&smc->init_done);
+ init_completion(&smc->cmd_done);
+
+ ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT,
+ FIELD_PREP(SMC_MSG, SMC_MSG_INITIALIZE), NULL, false);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to send init message");
+
+ if (wait_for_completion_timeout(&smc->init_done, msecs_to_jiffies(SMC_TIMEOUT_MS)) == 0) {
+ dev_err(dev, "Timed out initializing SMC");
+ return -ETIMEDOUT;
+ }
+
+ if (smc->boot_stage != APPLE_SMC_INITIALIZED) {
+ dev_err(dev, "SMC failed to boot successfully, boot stage=%d\n", smc->boot_stage);
+ return -EIO;
+ }
+
+ dev_set_drvdata(&pdev->dev, smc);
+ BLOCKING_INIT_NOTIFIER_HEAD(&smc->event_handlers);
+
+ ret = apple_smc_read_u32(smc, SMC_KEY(#KEY), &count);
+ if (ret)
+ return dev_err_probe(smc->dev, ret, "Failed to get key count");
+ smc->key_count = be32_to_cpu(count);
+
+ /* Enable notifications */
+ apple_smc_write_flag(smc, SMC_KEY(NTAP), true);
+ ret = devm_add_action_or_reset(dev, apple_smc_disable_notifications, smc);
+ if (ret)
+ return ret;
+
+ ret = devm_mfd_add_devices(smc->dev, PLATFORM_DEVID_NONE,
+ apple_smc_devs, ARRAY_SIZE(apple_smc_devs),
+ NULL, 0, NULL);
+ if (ret)
+ return dev_err_probe(smc->dev, ret, "Failed to register sub-devices");
+
+
+ return 0;
+}
+
+static const struct of_device_id apple_smc_of_match[] = {
+ { .compatible = "apple,t8103-smc" },
+ { .compatible = "apple,smc" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, apple_smc_of_match);
+
+static struct platform_driver apple_smc_driver = {
+ .driver = {
+ .name = "macsmc",
+ .of_match_table = apple_smc_of_match,
+ },
+ .probe = apple_smc_probe,
+};
+module_platform_driver(apple_smc_driver);
+
+MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
+MODULE_AUTHOR("Sven Peter <sven@kernel.org>");
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("Apple SMC driver");
diff --git a/drivers/mfd/madera-core.c b/drivers/mfd/madera-core.c
index bdbd5bfc9714..2f74a8c644a3 100644
--- a/drivers/mfd/madera-core.c
+++ b/drivers/mfd/madera-core.c
@@ -456,7 +456,7 @@ int madera_dev_init(struct madera *madera)
struct device *dev = madera->dev;
unsigned int hwid;
int (*patch_fn)(struct madera *) = NULL;
- const struct mfd_cell *mfd_devs;
+ const struct mfd_cell *mfd_devs = NULL;
int n_devs = 0;
int i, ret;
@@ -670,7 +670,7 @@ int madera_dev_init(struct madera *madera)
goto err_reset;
}
- if (!n_devs) {
+ if (!n_devs || !mfd_devs) {
dev_err(madera->dev, "Device ID 0x%x not a %s\n", hwid,
madera->type_name);
ret = -ENODEV;
diff --git a/drivers/mfd/madera-i2c.c b/drivers/mfd/madera-i2c.c
index 0968aa9733ac..0986e4a99f4a 100644
--- a/drivers/mfd/madera-i2c.c
+++ b/drivers/mfd/madera-i2c.c
@@ -10,7 +10,6 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/mfd/madera/core.h>
@@ -19,21 +18,14 @@
static int madera_i2c_probe(struct i2c_client *i2c)
{
- const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
struct madera *madera;
const struct regmap_config *regmap_16bit_config = NULL;
const struct regmap_config *regmap_32bit_config = NULL;
- const void *of_data;
unsigned long type;
const char *name;
int ret;
- of_data = of_device_get_match_data(&i2c->dev);
- if (of_data)
- type = (unsigned long)of_data;
- else
- type = id->driver_data;
-
+ type = (uintptr_t)i2c_get_match_data(i2c);
switch (type) {
case CS47L15:
if (IS_ENABLED(CONFIG_MFD_CS47L15)) {
diff --git a/drivers/mfd/madera-spi.c b/drivers/mfd/madera-spi.c
index da84eb50e53a..ce9e90322c9c 100644
--- a/drivers/mfd/madera-spi.c
+++ b/drivers/mfd/madera-spi.c
@@ -9,7 +9,6 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
@@ -19,21 +18,14 @@
static int madera_spi_probe(struct spi_device *spi)
{
- const struct spi_device_id *id = spi_get_device_id(spi);
struct madera *madera;
const struct regmap_config *regmap_16bit_config = NULL;
const struct regmap_config *regmap_32bit_config = NULL;
- const void *of_data;
unsigned long type;
const char *name;
int ret;
- of_data = of_device_get_match_data(&spi->dev);
- if (of_data)
- type = (unsigned long)of_data;
- else
- type = id->driver_data;
-
+ type = (unsigned long)spi_get_device_match_data(spi);
switch (type) {
case CS47L15:
if (IS_ENABLED(CONFIG_MFD_CS47L15)) {
diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c
index 25ed8846b7fb..7e7e8af9af22 100644
--- a/drivers/mfd/max14577.c
+++ b/drivers/mfd/max14577.c
@@ -9,9 +9,10 @@
// This driver is based on max8997.c
#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/interrupt.h>
-#include <linux/of_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max14577.h>
#include <linux/mfd/max14577-private.h>
@@ -142,6 +143,7 @@ static const struct of_device_id max14577_dt_match[] = {
},
{},
};
+MODULE_DEVICE_TABLE(of, max14577_dt_match);
static bool max14577_muic_volatile_reg(struct device *dev, unsigned int reg)
{
@@ -357,7 +359,6 @@ static void max77836_remove(struct max14577 *max14577)
static int max14577_i2c_probe(struct i2c_client *i2c)
{
- const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
struct max14577 *max14577;
struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct device_node *np = i2c->dev.of_node;
@@ -397,16 +398,7 @@ static int max14577_i2c_probe(struct i2c_client *i2c)
return ret;
}
- if (np) {
- const struct of_device_id *of_id;
-
- of_id = of_match_device(max14577_dt_match, &i2c->dev);
- if (of_id)
- max14577->dev_type =
- (enum maxim_device_type)of_id->data;
- } else {
- max14577->dev_type = id->driver_data;
- }
+ max14577->dev_type = (kernel_ulong_t)i2c_get_match_data(i2c);
max14577_print_dev_type(max14577);
@@ -464,6 +456,7 @@ static void max14577_i2c_remove(struct i2c_client *i2c)
{
struct max14577 *max14577 = i2c_get_clientdata(i2c);
+ device_init_wakeup(max14577->dev, false);
mfd_remove_devices(max14577->dev);
regmap_del_irq_chip(max14577->irq, max14577->irq_data);
if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836)
diff --git a/drivers/mfd/max7360.c b/drivers/mfd/max7360.c
new file mode 100644
index 000000000000..5ee459c490ec
--- /dev/null
+++ b/drivers/mfd/max7360.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Maxim MAX7360 Core Driver
+ *
+ * Copyright 2025 Bootlin
+ *
+ * Authors:
+ * Kamel Bouhara <kamel.bouhara@bootlin.com>
+ * Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device/devres.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max7360.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+static const struct mfd_cell max7360_cells[] = {
+ { .name = "max7360-pinctrl" },
+ { .name = "max7360-pwm" },
+ { .name = "max7360-keypad" },
+ { .name = "max7360-rotary" },
+ {
+ .name = "max7360-gpo",
+ .of_compatible = "maxim,max7360-gpo",
+ },
+ {
+ .name = "max7360-gpio",
+ .of_compatible = "maxim,max7360-gpio",
+ },
+};
+
+static const struct regmap_range max7360_volatile_ranges[] = {
+ regmap_reg_range(MAX7360_REG_KEYFIFO, MAX7360_REG_KEYFIFO),
+ regmap_reg_range(MAX7360_REG_I2C_TIMEOUT, MAX7360_REG_RTR_CNT),
+};
+
+static const struct regmap_access_table max7360_volatile_table = {
+ .yes_ranges = max7360_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(max7360_volatile_ranges),
+};
+
+static const struct regmap_config max7360_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX7360_REG_PWMCFG(MAX7360_PORT_PWM_COUNT - 1),
+ .volatile_table = &max7360_volatile_table,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int max7360_mask_irqs(struct regmap *regmap)
+{
+ struct device *dev = regmap_get_device(regmap);
+ unsigned int val;
+ int ret;
+
+ /*
+ * GPIO/PWM interrupts are not masked on reset: as the MAX7360 "INTI"
+ * interrupt line is shared between GPIOs and rotary encoder, this could
+ * result in repeated spurious interrupts on the rotary encoder driver
+ * if the GPIO driver is not loaded. Mask them now to avoid this
+ * situation.
+ */
+ for (unsigned int i = 0; i < MAX7360_PORT_PWM_COUNT; i++) {
+ ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i),
+ MAX7360_PORT_CFG_INTERRUPT_MASK,
+ MAX7360_PORT_CFG_INTERRUPT_MASK);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to write MAX7360 port configuration\n");
+ }
+
+ /* Read GPIO in register, to ACK any pending IRQ. */
+ ret = regmap_read(regmap, MAX7360_REG_GPIOIN, &val);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read GPIO values\n");
+
+ return 0;
+}
+
+static int max7360_reset(struct regmap *regmap)
+{
+ struct device *dev = regmap_get_device(regmap);
+ int ret;
+
+ ret = regmap_write(regmap, MAX7360_REG_GPIOCFG, MAX7360_GPIO_CFG_GPIO_RST);
+ if (ret) {
+ dev_err(dev, "Failed to reset GPIO configuration: %x\n", ret);
+ return ret;
+ }
+
+ ret = regcache_drop_region(regmap, MAX7360_REG_GPIOCFG, MAX7360_REG_GPIO_LAST);
+ if (ret) {
+ dev_err(dev, "Failed to drop regmap cache: %x\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(regmap, MAX7360_REG_SLEEP, 0);
+ if (ret) {
+ dev_err(dev, "Failed to reset autosleep configuration: %x\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(regmap, MAX7360_REG_DEBOUNCE, 0);
+ if (ret)
+ dev_err(dev, "Failed to reset GPO port count: %x\n", ret);
+
+ return ret;
+}
+
+static int max7360_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &max7360_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to initialise regmap\n");
+
+ ret = max7360_reset(regmap);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to reset device\n");
+
+ /* Get the device out of shutdown mode. */
+ ret = regmap_write_bits(regmap, MAX7360_REG_GPIOCFG,
+ MAX7360_GPIO_CFG_GPIO_EN,
+ MAX7360_GPIO_CFG_GPIO_EN);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable GPIO and PWM module\n");
+
+ ret = max7360_mask_irqs(regmap);
+ if (ret)
+ return dev_err_probe(dev, ret, "Could not mask interrupts\n");
+
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
+ max7360_cells, ARRAY_SIZE(max7360_cells),
+ NULL, 0, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register child devices\n");
+
+ return 0;
+}
+
+static const struct of_device_id max7360_dt_match[] = {
+ { .compatible = "maxim,max7360" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, max7360_dt_match);
+
+static struct i2c_driver max7360_driver = {
+ .driver = {
+ .name = "max7360",
+ .of_match_table = max7360_dt_match,
+ },
+ .probe = max7360_probe,
+};
+module_i2c_driver(max7360_driver);
+
+MODULE_DESCRIPTION("Maxim MAX7360 I2C IO Expander core driver");
+MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max77541.c b/drivers/mfd/max77541.c
index e147e949c2b3..f91b4f5373ce 100644
--- a/drivers/mfd/max77541.c
+++ b/drivers/mfd/max77541.c
@@ -152,7 +152,7 @@ static int max77541_pmic_setup(struct device *dev)
if (ret)
return dev_err_probe(dev, ret, "Failed to initialize IRQ\n");
- ret = device_init_wakeup(dev, true);
+ ret = devm_device_init_wakeup(dev);
if (ret)
return dev_err_probe(dev, ret, "Unable to init wakeup\n");
@@ -162,7 +162,6 @@ static int max77541_pmic_setup(struct device *dev)
static int max77541_probe(struct i2c_client *client)
{
- const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct device *dev = &client->dev;
struct max77541 *max77541;
@@ -173,10 +172,7 @@ static int max77541_probe(struct i2c_client *client)
i2c_set_clientdata(client, max77541);
max77541->i2c = client;
- max77541->id = (enum max7754x_ids)device_get_match_data(dev);
- if (!max77541->id)
- max77541->id = (enum max7754x_ids)id->driver_data;
-
+ max77541->id = (uintptr_t)i2c_get_match_data(client);
if (!max77541->id)
return -EINVAL;
diff --git a/drivers/mfd/max77620.c b/drivers/mfd/max77620.c
index 5811ed8f4840..3af2974b3023 100644
--- a/drivers/mfd/max77620.c
+++ b/drivers/mfd/max77620.c
@@ -29,8 +29,8 @@
#include <linux/mfd/core.h>
#include <linux/mfd/max77620.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -173,7 +173,7 @@ static const struct regmap_config max77620_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX77620_REG_DVSSD4 + 1,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.rd_table = &max77620_readable_table,
.wr_table = &max77620_writable_table,
.volatile_table = &max77620_volatile_table,
@@ -185,7 +185,7 @@ static const struct regmap_config max20024_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX20024_REG_MAX_ADD + 1,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.rd_table = &max20024_readable_table,
.wr_table = &max77620_writable_table,
.volatile_table = &max77620_volatile_table,
@@ -214,7 +214,7 @@ static const struct regmap_config max77663_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX77620_REG_CID5 + 1,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.rd_table = &max77663_readable_table,
.wr_table = &max77663_writable_table,
.volatile_table = &max77620_volatile_table,
@@ -254,7 +254,7 @@ static int max77620_irq_global_unmask(void *irq_drv_data)
return ret;
}
-static struct regmap_irq_chip max77620_top_irq_chip = {
+static const struct regmap_irq_chip max77620_top_irq_chip = {
.name = "max77620-top",
.irqs = max77620_top_irqs,
.num_irqs = ARRAY_SIZE(max77620_top_irqs),
@@ -401,7 +401,7 @@ static int max77620_config_fps(struct max77620_chip *chip,
static int max77620_initialise_fps(struct max77620_chip *chip)
{
struct device *dev = chip->dev;
- struct device_node *fps_np, *fps_child;
+ struct device_node *fps_np;
u8 config;
int fps_id;
int ret;
@@ -415,10 +415,9 @@ static int max77620_initialise_fps(struct max77620_chip *chip)
if (!fps_np)
goto skip_fps;
- for_each_child_of_node(fps_np, fps_child) {
+ for_each_child_of_node_scoped(fps_np, fps_child) {
ret = max77620_config_fps(chip, fps_child);
if (ret < 0) {
- of_node_put(fps_child);
of_node_put(fps_np);
return ret;
}
@@ -499,6 +498,7 @@ static int max77620_probe(struct i2c_client *client)
const struct i2c_device_id *id = i2c_client_get_device_id(client);
const struct regmap_config *rmap_config;
struct max77620_chip *chip;
+ struct regmap_irq_chip *chip_desc;
const struct mfd_cell *mfd_cells;
int n_mfd_cells;
bool pm_off;
@@ -509,6 +509,14 @@ static int max77620_probe(struct i2c_client *client)
return -ENOMEM;
i2c_set_clientdata(client, chip);
+
+ chip_desc = devm_kmemdup(&client->dev, &max77620_top_irq_chip,
+ sizeof(max77620_top_irq_chip),
+ GFP_KERNEL);
+ if (!chip_desc)
+ return -ENOMEM;
+ chip_desc->irq_drv_data = chip;
+
chip->dev = &client->dev;
chip->chip_irq = client->irq;
chip->chip_id = (enum max77620_chip_id)id->driver_data;
@@ -545,11 +553,9 @@ static int max77620_probe(struct i2c_client *client)
if (ret < 0)
return ret;
- max77620_top_irq_chip.irq_drv_data = chip;
ret = devm_regmap_add_irq_chip(chip->dev, chip->rmap, client->irq,
IRQF_ONESHOT | IRQF_SHARED, 0,
- &max77620_top_irq_chip,
- &chip->top_irq_data);
+ chip_desc, &chip->top_irq_data);
if (ret < 0) {
dev_err(chip->dev, "Failed to add regmap irq: %d\n", ret);
return ret;
@@ -702,3 +708,7 @@ static struct i2c_driver max77620_driver = {
.id_table = max77620_id,
};
builtin_i2c_driver(max77620_driver);
+
+MODULE_DESCRIPTION("Maxim Semiconductor MAX77620 and MAX20024 PMIC Support");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
index 01833086ca7d..0118a444a68b 100644
--- a/drivers/mfd/max77686.c
+++ b/drivers/mfd/max77686.c
@@ -20,7 +20,6 @@
#include <linux/mfd/max77686-private.h>
#include <linux/err.h>
#include <linux/of.h>
-#include <linux/of_device.h>
static const struct mfd_cell max77686_devs[] = {
{ .name = "max77686-pmic", },
@@ -109,7 +108,7 @@ static const struct regmap_config max77802_regmap_config = {
.precious_reg = max77802_is_precious_reg,
.volatile_reg = max77802_is_volatile_reg,
.name = "max77802-pmic",
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_irq max77686_irqs[] = {
diff --git a/drivers/mfd/max77705.c b/drivers/mfd/max77705.c
new file mode 100644
index 000000000000..e1a9bfd65856
--- /dev/null
+++ b/drivers/mfd/max77705.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Maxim MAX77705 PMIC core driver
+ *
+ * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com>
+ **/
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77705-private.h>
+#include <linux/mfd/max77693-common.h>
+#include <linux/pm.h>
+#include <linux/power/max17042_battery.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+
+static struct mfd_cell max77705_devs[] = {
+ MFD_CELL_OF("max77705-rgb", NULL, NULL, 0, 0, "maxim,max77705-rgb"),
+ MFD_CELL_OF("max77705-charger", NULL, NULL, 0, 0, "maxim,max77705-charger"),
+ MFD_CELL_OF("max77705-haptic", NULL, NULL, 0, 0, "maxim,max77705-haptic"),
+};
+
+static const struct regmap_range max77705_readable_ranges[] = {
+ regmap_reg_range(MAX77705_PMIC_REG_PMICID1, MAX77705_PMIC_REG_BSTOUT_MASK),
+ regmap_reg_range(MAX77705_PMIC_REG_INTSRC, MAX77705_PMIC_REG_RESERVED_29),
+ regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1),
+ regmap_reg_range(MAX77705_PMIC_REG_MCONFIG, MAX77705_PMIC_REG_MCONFIG2),
+ regmap_reg_range(MAX77705_PMIC_REG_FORCE_EN_MASK, MAX77705_PMIC_REG_FORCE_EN_MASK),
+ regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1),
+ regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL2, MAX77705_PMIC_REG_BOOSTCONTROL2),
+ regmap_reg_range(MAX77705_PMIC_REG_SW_RESET, MAX77705_PMIC_REG_USBC_RESET),
+};
+
+static const struct regmap_range max77705_writable_ranges[] = {
+ regmap_reg_range(MAX77705_PMIC_REG_MAINCTRL1, MAX77705_PMIC_REG_BSTOUT_MASK),
+ regmap_reg_range(MAX77705_PMIC_REG_INTSRC, MAX77705_PMIC_REG_RESERVED_29),
+ regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1),
+ regmap_reg_range(MAX77705_PMIC_REG_MCONFIG, MAX77705_PMIC_REG_MCONFIG2),
+ regmap_reg_range(MAX77705_PMIC_REG_FORCE_EN_MASK, MAX77705_PMIC_REG_FORCE_EN_MASK),
+ regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1, MAX77705_PMIC_REG_BOOSTCONTROL1),
+ regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL2, MAX77705_PMIC_REG_BOOSTCONTROL2),
+ regmap_reg_range(MAX77705_PMIC_REG_SW_RESET, MAX77705_PMIC_REG_USBC_RESET),
+};
+
+static const struct regmap_access_table max77705_readable_table = {
+ .yes_ranges = max77705_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(max77705_readable_ranges),
+};
+
+static const struct regmap_access_table max77705_writable_table = {
+ .yes_ranges = max77705_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(max77705_writable_ranges),
+};
+
+static const struct regmap_config max77705_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &max77705_readable_table,
+ .wr_table = &max77705_writable_table,
+ .max_register = MAX77705_PMIC_REG_USBC_RESET,
+};
+
+static const struct regmap_irq max77705_irqs[] = {
+ { .mask = MAX77705_SRC_IRQ_CHG, },
+ { .mask = MAX77705_SRC_IRQ_TOP, },
+ { .mask = MAX77705_SRC_IRQ_FG, },
+ { .mask = MAX77705_SRC_IRQ_USBC, },
+};
+
+static const struct regmap_irq_chip max77705_irq_chip = {
+ .name = "max77705",
+ .status_base = MAX77705_PMIC_REG_INTSRC,
+ .ack_base = MAX77705_PMIC_REG_INTSRC,
+ .mask_base = MAX77705_PMIC_REG_INTSRC_MASK,
+ .num_regs = 1,
+ .irqs = max77705_irqs,
+ .num_irqs = ARRAY_SIZE(max77705_irqs),
+};
+
+static int max77705_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct max77693_dev *max77705;
+ struct regmap_irq_chip_data *irq_data;
+ struct irq_domain *domain;
+ enum max77705_hw_rev pmic_rev;
+ unsigned int pmic_rev_value;
+ int ret;
+
+ max77705 = devm_kzalloc(dev, sizeof(*max77705), GFP_KERNEL);
+ if (!max77705)
+ return -ENOMEM;
+
+ max77705->i2c = i2c;
+ max77705->type = TYPE_MAX77705;
+ i2c_set_clientdata(i2c, max77705);
+
+ max77705->regmap = devm_regmap_init_i2c(i2c, &max77705_regmap_config);
+ if (IS_ERR(max77705->regmap))
+ return PTR_ERR(max77705->regmap);
+
+ ret = regmap_read(max77705->regmap, MAX77705_PMIC_REG_PMICREV, &pmic_rev_value);
+ if (ret < 0)
+ return -ENODEV;
+
+ pmic_rev = pmic_rev_value & MAX77705_REVISION_MASK;
+ if (pmic_rev != MAX77705_PASS3)
+ return dev_err_probe(dev, -ENODEV, "Rev.0x%x is not tested\n", pmic_rev);
+
+ /* Active Discharge Enable */
+ regmap_update_bits(max77705->regmap, MAX77705_PMIC_REG_MAINCTRL1, 1, 1);
+
+ ret = devm_regmap_add_irq_chip(dev, max77705->regmap,
+ i2c->irq,
+ IRQF_ONESHOT, 0,
+ &max77705_irq_chip,
+ &irq_data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add IRQ chip\n");
+
+ domain = regmap_irq_get_domain(irq_data);
+
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
+ max77705_devs, ARRAY_SIZE(max77705_devs),
+ NULL, 0, domain);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register child devices\n");
+
+ ret = devm_device_init_wakeup(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to init wakeup\n");
+
+ return 0;
+}
+
+static int max77705_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+
+ disable_irq(i2c->irq);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(i2c->irq);
+
+ return 0;
+}
+
+static int max77705_resume(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(i2c->irq);
+
+ enable_irq(i2c->irq);
+
+ return 0;
+}
+DEFINE_SIMPLE_DEV_PM_OPS(max77705_pm_ops, max77705_suspend, max77705_resume);
+
+static const struct of_device_id max77705_i2c_of_match[] = {
+ { .compatible = "maxim,max77705" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max77705_i2c_of_match);
+
+static struct i2c_driver max77705_i2c_driver = {
+ .driver = {
+ .name = "max77705",
+ .of_match_table = max77705_i2c_of_match,
+ .pm = pm_sleep_ptr(&max77705_pm_ops),
+ },
+ .probe = max77705_i2c_probe
+};
+module_i2c_driver(max77705_i2c_driver);
+
+MODULE_DESCRIPTION("Maxim MAX77705 PMIC core driver");
+MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max77759.c b/drivers/mfd/max77759.c
new file mode 100644
index 000000000000..6cf6306c4a3b
--- /dev/null
+++ b/drivers/mfd/max77759.c
@@ -0,0 +1,690 @@
+// 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");
diff --git a/drivers/mfd/max77843.c b/drivers/mfd/max77843.c
index b3689c13a14d..fcff0c498c0f 100644
--- a/drivers/mfd/max77843.c
+++ b/drivers/mfd/max77843.c
@@ -13,7 +13,7 @@
#include <linux/mfd/core.h>
#include <linux/mfd/max77693-common.h>
#include <linux/mfd/max77843-private.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
static const struct mfd_cell max77843_devs[] = {
diff --git a/drivers/mfd/max8907.c b/drivers/mfd/max8907.c
index 78b5ee688dec..7bac1d651771 100644
--- a/drivers/mfd/max8907.c
+++ b/drivers/mfd/max8907.c
@@ -15,7 +15,6 @@
#include <linux/mfd/max8907.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -64,7 +63,7 @@ static const struct regmap_config max8907_regmap_gen_config = {
.precious_reg = max8907_gen_is_precious_reg,
.writeable_reg = max8907_gen_is_writeable_reg,
.max_register = MAX8907_REG_LDO20VOUT,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static bool max8907_rtc_is_volatile_reg(struct device *dev, unsigned int reg)
@@ -109,7 +108,7 @@ static const struct regmap_config max8907_regmap_rtc_config = {
.precious_reg = max8907_rtc_is_precious_reg,
.writeable_reg = max8907_rtc_is_writeable_reg,
.max_register = MAX8907_REG_MPL_CNTL,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_irq max8907_chg_irqs[] = {
@@ -301,7 +300,7 @@ MODULE_DEVICE_TABLE(of, max8907_of_match);
#endif
static const struct i2c_device_id max8907_i2c_id[] = {
- {"max8907", 0},
+ { "max8907" },
{}
};
MODULE_DEVICE_TABLE(i2c, max8907_i2c_id);
diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c
index 0246bbe80354..25377dcce60e 100644
--- a/drivers/mfd/max8925-core.c
+++ b/drivers/mfd/max8925-core.c
@@ -17,7 +17,6 @@
#include <linux/mfd/core.h>
#include <linux/mfd/max8925.h>
#include <linux/of.h>
-#include <linux/of_platform.h>
static const struct resource bk_resources[] = {
{ 0x84, 0x84, "mode control", IORESOURCE_REG, },
@@ -657,7 +656,6 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq,
{
unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
int ret;
- struct device_node *node = chip->dev->of_node;
/* clear all interrupts */
max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ1);
@@ -683,8 +681,9 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq,
return -EBUSY;
}
- irq_domain_add_legacy(node, MAX8925_NR_IRQS, chip->irq_base, 0,
- &max8925_irq_domain_ops, chip);
+ irq_domain_create_legacy(dev_fwnode(chip->dev), MAX8925_NR_IRQS,
+ chip->irq_base, 0, &max8925_irq_domain_ops,
+ chip);
/* request irq handler for pmic main irq*/
chip->core_irq = irq;
diff --git a/drivers/mfd/max8925-i2c.c b/drivers/mfd/max8925-i2c.c
index 7608000488f9..ab19ff0c7867 100644
--- a/drivers/mfd/max8925-i2c.c
+++ b/drivers/mfd/max8925-i2c.c
@@ -127,8 +127,8 @@ EXPORT_SYMBOL(max8925_set_bits);
static const struct i2c_device_id max8925_id_table[] = {
- { "max8925", 0 },
- { },
+ { "max8925" },
+ { }
};
static int max8925_dt_init(struct device_node *np, struct device *dev,
@@ -201,6 +201,7 @@ static void max8925_remove(struct i2c_client *client)
struct max8925_chip *chip = i2c_get_clientdata(client);
max8925_device_exit(chip);
+ device_init_wakeup(&client->dev, false);
i2c_unregister_device(chip->adc);
i2c_unregister_device(chip->rtc);
}
diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c
index 93a3b1698d9c..cc87571c9af5 100644
--- a/drivers/mfd/max8997-irq.c
+++ b/drivers/mfd/max8997-irq.c
@@ -327,15 +327,16 @@ int max8997_irq_init(struct max8997_dev *max8997)
true : false;
}
- domain = irq_domain_add_linear(NULL, MAX8997_IRQ_NR,
- &max8997_irq_domain_ops, max8997);
+ domain = irq_domain_create_linear(NULL, MAX8997_IRQ_NR,
+ &max8997_irq_domain_ops, max8997);
if (!domain) {
dev_err(max8997->dev, "could not create irq domain\n");
return -ENODEV;
}
max8997->irq_domain = domain;
- ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread,
+ ret = devm_request_threaded_irq(max8997->dev, max8997->irq, NULL,
+ max8997_irq_thread,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"max8997-irq", max8997);
@@ -348,7 +349,8 @@ int max8997_irq_init(struct max8997_dev *max8997)
if (!max8997->ono)
return 0;
- ret = request_threaded_irq(max8997->ono, NULL, max8997_irq_thread,
+ ret = devm_request_threaded_irq(max8997->dev, max8997->ono, NULL,
+ max8997_irq_thread,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
IRQF_ONESHOT, "max8997-ono", max8997);
@@ -358,12 +360,3 @@ int max8997_irq_init(struct max8997_dev *max8997)
return 0;
}
-
-void max8997_irq_exit(struct max8997_dev *max8997)
-{
- if (max8997->ono)
- free_irq(max8997->ono, max8997);
-
- if (max8997->irq)
- free_irq(max8997->irq, max8997);
-}
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index 94c09a5eab32..7ba8ed1dfde3 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -11,7 +11,6 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
@@ -143,18 +142,8 @@ static struct max8997_platform_data *max8997_i2c_parse_dt_pdata(
return pd;
}
-static inline unsigned long max8997_i2c_get_driver_data(struct i2c_client *i2c,
- const struct i2c_device_id *id)
-{
- if (i2c->dev.of_node)
- return (unsigned long)of_device_get_match_data(&i2c->dev);
-
- return id->driver_data;
-}
-
static int max8997_i2c_probe(struct i2c_client *i2c)
{
- const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
struct max8997_dev *max8997;
struct max8997_platform_data *pdata = dev_get_platdata(&i2c->dev);
int ret = 0;
@@ -167,7 +156,7 @@ static int max8997_i2c_probe(struct i2c_client *i2c)
i2c_set_clientdata(i2c, max8997);
max8997->dev = &i2c->dev;
max8997->i2c = i2c;
- max8997->type = max8997_i2c_get_driver_data(i2c, id);
+ max8997->type = (uintptr_t)i2c_get_match_data(i2c);
max8997->irq = i2c->irq;
if (IS_ENABLED(CONFIG_OF) && max8997->dev->of_node) {
@@ -449,7 +438,7 @@ static int max8997_suspend(struct device *dev)
disable_irq(max8997->irq);
if (device_may_wakeup(dev))
- irq_set_irq_wake(max8997->irq, 1);
+ enable_irq_wake(max8997->irq);
return 0;
}
@@ -459,7 +448,7 @@ static int max8997_resume(struct device *dev)
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
- irq_set_irq_wake(max8997->irq, 0);
+ disable_irq_wake(max8997->irq);
enable_irq(max8997->irq);
return max8997_irq_resume(max8997);
}
diff --git a/drivers/mfd/max8998-irq.c b/drivers/mfd/max8998-irq.c
index 83b6f510bc05..b0773fa6e07f 100644
--- a/drivers/mfd/max8998-irq.c
+++ b/drivers/mfd/max8998-irq.c
@@ -230,7 +230,7 @@ int max8998_irq_init(struct max8998_dev *max8998)
max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM1, 0xff);
max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM2, 0xff);
- domain = irq_domain_add_simple(NULL, MAX8998_IRQ_NR,
+ domain = irq_domain_create_simple(NULL, MAX8998_IRQ_NR,
max8998->irq_base, &max8998_irq_domain_ops, max8998);
if (!domain) {
dev_err(max8998->dev, "could not create irq domain\n");
diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c
index 33a3ec5464fb..eb13bbaeda55 100644
--- a/drivers/mfd/max8998.c
+++ b/drivers/mfd/max8998.c
@@ -12,7 +12,6 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
#include <linux/mutex.h>
@@ -153,18 +152,8 @@ static struct max8998_platform_data *max8998_i2c_parse_dt_pdata(
return pd;
}
-static inline unsigned long max8998_i2c_get_driver_data(struct i2c_client *i2c,
- const struct i2c_device_id *id)
-{
- if (i2c->dev.of_node)
- return (unsigned long)of_device_get_match_data(&i2c->dev);
-
- return id->driver_data;
-}
-
static int max8998_i2c_probe(struct i2c_client *i2c)
{
- const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
struct max8998_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct max8998_dev *max8998;
int ret = 0;
@@ -184,7 +173,7 @@ static int max8998_i2c_probe(struct i2c_client *i2c)
max8998->dev = &i2c->dev;
max8998->i2c = i2c;
max8998->irq = i2c->irq;
- max8998->type = max8998_i2c_get_driver_data(i2c, id);
+ max8998->type = (uintptr_t)i2c_get_match_data(i2c);
max8998->pdata = pdata;
if (pdata) {
max8998->ono = pdata->ono;
@@ -245,7 +234,7 @@ static int max8998_suspend(struct device *dev)
struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
- irq_set_irq_wake(max8998->irq, 1);
+ enable_irq_wake(max8998->irq);
return 0;
}
@@ -255,7 +244,7 @@ static int max8998_resume(struct device *dev)
struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
- irq_set_irq_wake(max8998->irq, 0);
+ disable_irq_wake(max8998->irq);
/*
* In LP3974, if IRQ registers are not "read & clear"
* when it's set during sleep, the interrupt becomes
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index 1000572761a8..920797b806ce 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -7,6 +7,7 @@
* Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
*/
+#include <linux/bitfield.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -174,28 +175,27 @@ int mc13xxx_irq_free(struct mc13xxx *mc13xxx, int irq, void *dev)
}
EXPORT_SYMBOL(mc13xxx_irq_free);
-#define maskval(reg, mask) (((reg) & (mask)) >> __ffs(mask))
static void mc13xxx_print_revision(struct mc13xxx *mc13xxx, u32 revision)
{
dev_info(mc13xxx->dev, "%s: rev: %d.%d, "
"fin: %d, fab: %d, icid: %d/%d\n",
mc13xxx->variant->name,
- maskval(revision, MC13XXX_REVISION_REVFULL),
- maskval(revision, MC13XXX_REVISION_REVMETAL),
- maskval(revision, MC13XXX_REVISION_FIN),
- maskval(revision, MC13XXX_REVISION_FAB),
- maskval(revision, MC13XXX_REVISION_ICID),
- maskval(revision, MC13XXX_REVISION_ICIDCODE));
+ FIELD_GET(MC13XXX_REVISION_REVFULL, revision),
+ FIELD_GET(MC13XXX_REVISION_REVMETAL, revision),
+ FIELD_GET(MC13XXX_REVISION_FIN, revision),
+ FIELD_GET(MC13XXX_REVISION_FAB, revision),
+ FIELD_GET(MC13XXX_REVISION_ICID, revision),
+ FIELD_GET(MC13XXX_REVISION_ICIDCODE, revision));
}
static void mc34708_print_revision(struct mc13xxx *mc13xxx, u32 revision)
{
dev_info(mc13xxx->dev, "%s: rev %d.%d, fin: %d, fab: %d\n",
mc13xxx->variant->name,
- maskval(revision, MC34708_REVISION_REVFULL),
- maskval(revision, MC34708_REVISION_REVMETAL),
- maskval(revision, MC34708_REVISION_FIN),
- maskval(revision, MC34708_REVISION_FAB));
+ FIELD_GET(MC34708_REVISION_REVFULL, revision),
+ FIELD_GET(MC34708_REVISION_REVMETAL, revision),
+ FIELD_GET(MC34708_REVISION_FIN, revision),
+ FIELD_GET(MC34708_REVISION_FAB, revision));
}
/* These are only exported for mc13xxx-i2c and mc13xxx-spi */
diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c
index de59b498c925..6bc0e755ba34 100644
--- a/drivers/mfd/mc13xxx-i2c.c
+++ b/drivers/mfd/mc13xxx-i2c.c
@@ -53,7 +53,6 @@ static const struct regmap_config mc13xxx_regmap_i2c_config = {
static int mc13xxx_i2c_probe(struct i2c_client *client)
{
- const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct mc13xxx *mc13xxx;
int ret;
@@ -73,13 +72,7 @@ static int mc13xxx_i2c_probe(struct i2c_client *client)
return ret;
}
- if (client->dev.of_node) {
- const struct of_device_id *of_id =
- of_match_device(mc13xxx_dt_ids, &client->dev);
- mc13xxx->variant = of_id->data;
- } else {
- mc13xxx->variant = (void *)id->driver_data;
- }
+ mc13xxx->variant = i2c_get_match_data(client);
return mc13xxx_common_init(&client->dev);
}
diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c
index f70d79aa5a83..9f438d5d4326 100644
--- a/drivers/mfd/mc13xxx-spi.c
+++ b/drivers/mfd/mc13xxx-spi.c
@@ -8,13 +8,12 @@
*/
#include <linux/slab.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/mfd/mc13xxx.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/err.h>
#include <linux/spi/spi.h>
@@ -117,7 +116,7 @@ static int mc13xxx_spi_write(void *context, const void *data, size_t count)
* single transfer.
*/
-static struct regmap_bus regmap_mc13xxx_bus = {
+static const struct regmap_bus regmap_mc13xxx_bus = {
.write = mc13xxx_spi_write,
.read = mc13xxx_spi_read,
};
@@ -151,16 +150,7 @@ static int mc13xxx_spi_probe(struct spi_device *spi)
return ret;
}
- if (spi->dev.of_node) {
- const struct of_device_id *of_id =
- of_match_device(mc13xxx_dt_ids, &spi->dev);
-
- mc13xxx->variant = of_id->data;
- } else {
- const struct spi_device_id *id_entry = spi_get_device_id(spi);
-
- mc13xxx->variant = (void *)id_entry->driver_data;
- }
+ mc13xxx->variant = spi_get_device_match_data(spi);
return mc13xxx_common_init(&spi->dev);
}
diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c
index 2fa592c37c6f..be08eaee0a90 100644
--- a/drivers/mfd/mcp-core.c
+++ b/drivers/mfd/mcp-core.c
@@ -20,7 +20,7 @@
#define to_mcp(d) container_of(d, struct mcp, attached_device)
#define to_mcp_driver(d) container_of(d, struct mcp_driver, drv)
-static int mcp_bus_match(struct device *dev, struct device_driver *drv)
+static int mcp_bus_match(struct device *dev, const struct device_driver *drv)
{
return 1;
}
@@ -41,7 +41,7 @@ static void mcp_bus_remove(struct device *dev)
drv->remove(mcp);
}
-static struct bus_type mcp_bus_type = {
+static const struct bus_type mcp_bus_type = {
.name = "mcp",
.match = mcp_bus_match,
.probe = mcp_bus_probe,
diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c
index 1c9831b78cf9..228c4a2f4c1f 100644
--- a/drivers/mfd/mcp-sa11x0.c
+++ b/drivers/mfd/mcp-sa11x0.c
@@ -232,7 +232,7 @@ static int mcp_sa11x0_probe(struct platform_device *dev)
return ret;
}
-static int mcp_sa11x0_remove(struct platform_device *dev)
+static void mcp_sa11x0_remove(struct platform_device *dev)
{
struct mcp *mcp = platform_get_drvdata(dev);
struct mcp_sa11x0 *m = priv(mcp);
@@ -251,8 +251,6 @@ static int mcp_sa11x0_remove(struct platform_device *dev)
mcp_host_free(mcp);
release_mem_region(mem1->start, resource_size(mem1));
release_mem_region(mem0->start, resource_size(mem0));
-
- return 0;
}
static int mcp_sa11x0_suspend(struct device *dev)
diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c
index 662604ea97f2..a125d40fa121 100644
--- a/drivers/mfd/menelaus.c
+++ b/drivers/mfd/menelaus.c
@@ -29,7 +29,6 @@
#include <linux/bcd.h>
#include <linux/slab.h>
#include <linux/mfd/menelaus.h>
-#include <linux/gpio.h>
#include <asm/mach/irq.h>
@@ -1231,7 +1230,7 @@ static void menelaus_remove(struct i2c_client *client)
}
static const struct i2c_device_id menelaus_id[] = {
- { "menelaus", 0 },
+ { "menelaus" },
{ }
};
MODULE_DEVICE_TABLE(i2c, menelaus_id);
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 0ed7c0d7784e..7d14a1e7631e 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -29,7 +29,7 @@ struct mfd_of_node_entry {
struct device_node *np;
};
-static struct device_type mfd_dev_type = {
+static const struct device_type mfd_dev_type = {
.name = "mfd_device",
};
@@ -87,7 +87,7 @@ static void mfd_acpi_add_device(const struct mfd_cell *cell,
}
}
- ACPI_COMPANION_SET(&pdev->dev, adev ?: parent);
+ device_set_node(&pdev->dev, acpi_fwnode_handle(adev ?: parent));
}
#else
static inline void mfd_acpi_add_device(const struct mfd_cell *cell,
@@ -131,8 +131,8 @@ allocate_of_node:
of_entry->np = np;
list_add_tail(&of_entry->list, &mfd_of_node_list);
- pdev->dev.of_node = np;
- pdev->dev.fwnode = &np->fwnode;
+ of_node_get(np);
+ device_set_node(&pdev->dev, of_fwnode_handle(np));
#endif
return 0;
}
@@ -146,6 +146,7 @@ static int mfd_add_device(struct device *parent, int id,
struct platform_device *pdev;
struct device_node *np = NULL;
struct mfd_of_node_entry *of_entry, *tmp;
+ bool disabled = false;
int ret = -ENOMEM;
int platform_id;
int r;
@@ -183,11 +184,10 @@ static int mfd_add_device(struct device *parent, int id,
if (IS_ENABLED(CONFIG_OF) && parent->of_node && cell->of_compatible) {
for_each_child_of_node(parent->of_node, np) {
if (of_device_is_compatible(np, cell->of_compatible)) {
- /* Ignore 'disabled' devices error free */
+ /* Skip 'disabled' devices */
if (!of_device_is_available(np)) {
- of_node_put(np);
- ret = 0;
- goto fail_alias;
+ disabled = true;
+ continue;
}
ret = mfd_match_of_node_to_dev(pdev, np, cell);
@@ -197,10 +197,17 @@ static int mfd_add_device(struct device *parent, int id,
if (ret)
goto fail_alias;
- break;
+ goto match;
}
}
+ if (disabled) {
+ /* Ignore 'disabled' devices error free */
+ ret = 0;
+ goto fail_alias;
+ }
+
+match:
if (!pdev->dev.of_node)
pr_warn("%s: Failed to locate of_node [id: %d]\n",
cell->name, platform_id);
@@ -430,5 +437,6 @@ int devm_mfd_add_devices(struct device *dev, int id,
}
EXPORT_SYMBOL(devm_mfd_add_devices);
+MODULE_DESCRIPTION("Core MFD support");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov");
diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c
index a19691ba8d8b..d8243b956f87 100644
--- a/drivers/mfd/motorola-cpcap.c
+++ b/drivers/mfd/motorola-cpcap.c
@@ -11,7 +11,7 @@
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/regmap.h>
#include <linux/sysfs.h>
@@ -290,14 +290,9 @@ static const struct mfd_cell cpcap_mfd_devices[] = {
static int cpcap_probe(struct spi_device *spi)
{
- const struct of_device_id *match;
struct cpcap_ddata *cpcap;
int ret;
- match = of_match_device(cpcap_of_match, &spi->dev);
- if (!match)
- return -ENODEV;
-
cpcap = devm_kzalloc(&spi->dev, sizeof(*cpcap), GFP_KERNEL);
if (!cpcap)
return -ENOMEM;
diff --git a/drivers/mfd/mt6358-irq.c b/drivers/mfd/mt6358-irq.c
index 389756436af6..74cf20843044 100644
--- a/drivers/mfd/mt6358-irq.c
+++ b/drivers/mfd/mt6358-irq.c
@@ -3,6 +3,8 @@
// Copyright (c) 2020 MediaTek Inc.
#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/mfd/mt6357/core.h>
#include <linux/mfd/mt6357/registers.h>
#include <linux/mfd/mt6358/core.h>
@@ -11,9 +13,6 @@
#include <linux/mfd/mt6359/registers.h>
#include <linux/mfd/mt6397/core.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -273,9 +272,8 @@ int mt6358_irq_init(struct mt6397_chip *chip)
irqd->pmic_ints[i].en_reg_shift * j, 0);
}
- chip->irq_domain = irq_domain_add_linear(chip->dev->of_node,
- irqd->num_pmic_irqs,
- &mt6358_irq_domain_ops, chip);
+ chip->irq_domain = irq_domain_create_linear(dev_fwnode(chip->dev), irqd->num_pmic_irqs,
+ &mt6358_irq_domain_ops, chip);
if (!chip->irq_domain) {
dev_err(chip->dev, "Could not create IRQ domain\n");
return -ENODEV;
@@ -287,6 +285,7 @@ int mt6358_irq_init(struct mt6397_chip *chip)
if (ret) {
dev_err(chip->dev, "Failed to register IRQ=%d, ret=%d\n",
chip->irq, ret);
+ irq_domain_remove(chip->irq_domain);
return ret;
}
diff --git a/drivers/mfd/mt6360-core.c b/drivers/mfd/mt6360-core.c
index 2685efa5c9e2..b9b1036c8ff4 100644
--- a/drivers/mfd/mt6360-core.c
+++ b/drivers/mfd/mt6360-core.c
@@ -5,6 +5,7 @@
* Author: Gene Chen <gene_chen@richtek.com>
*/
+#include <linux/cleanup.h>
#include <linux/crc8.h>
#include <linux/i2c.h>
#include <linux/init.h>
@@ -404,7 +405,6 @@ static int mt6360_regmap_read(void *context, const void *reg, size_t reg_size,
u8 reg_addr = *(u8 *)(reg + 1);
struct i2c_client *i2c;
bool crc_needed = false;
- u8 *buf;
int buf_len = MT6360_ALLOC_READ_SIZE(val_size);
int read_size = val_size;
u8 crc;
@@ -423,7 +423,7 @@ static int mt6360_regmap_read(void *context, const void *reg, size_t reg_size,
read_size += MT6360_CRC_CRC8_SIZE;
}
- buf = kzalloc(buf_len, GFP_KERNEL);
+ u8 *buf __free(kfree) = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -433,24 +433,19 @@ static int mt6360_regmap_read(void *context, const void *reg, size_t reg_size,
ret = i2c_smbus_read_i2c_block_data(i2c, reg_addr, read_size,
buf + MT6360_CRC_PREDATA_OFFSET);
if (ret < 0)
- goto out;
- else if (ret != read_size) {
- ret = -EIO;
- goto out;
- }
+ return ret;
+ else if (ret != read_size)
+ return -EIO;
if (crc_needed) {
crc = crc8(ddata->crc8_tbl, buf, val_size + MT6360_CRC_PREDATA_OFFSET, 0);
- if (crc != buf[val_size + MT6360_CRC_PREDATA_OFFSET]) {
- ret = -EIO;
- goto out;
- }
+ if (crc != buf[val_size + MT6360_CRC_PREDATA_OFFSET])
+ return -EIO;
}
memcpy(val, buf + MT6360_CRC_PREDATA_OFFSET, val_size);
-out:
- kfree(buf);
- return (ret < 0) ? ret : 0;
+
+ return 0;
}
static int mt6360_regmap_write(void *context, const void *val, size_t val_size)
diff --git a/drivers/mfd/mt6370.c b/drivers/mfd/mt6370.c
index c126ccb25d66..c7c2efe3598c 100644
--- a/drivers/mfd/mt6370.c
+++ b/drivers/mfd/mt6370.c
@@ -95,7 +95,7 @@ static const struct regmap_irq mt6370_irqs[] = {
REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_SHORT, 8),
REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_STRB, 8),
REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_STRB, 8),
- REGMAP_IRQ_REG_LINE(mT6370_IRQ_FLED2_STRB_TO, 8),
+ REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_STRB_TO, 8),
REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_STRB_TO, 8),
REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_TOR, 8),
REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_TOR, 8),
diff --git a/drivers/mfd/mt6370.h b/drivers/mfd/mt6370.h
index 094e59e4af4e..dd9ccc0a53f1 100644
--- a/drivers/mfd/mt6370.h
+++ b/drivers/mfd/mt6370.h
@@ -69,7 +69,7 @@
#define MT6370_IRQ_FLED1_SHORT 79
#define MT6370_IRQ_FLED2_STRB 80
#define MT6370_IRQ_FLED1_STRB 81
-#define mT6370_IRQ_FLED2_STRB_TO 82
+#define MT6370_IRQ_FLED2_STRB_TO 82
#define MT6370_IRQ_FLED1_STRB_TO 83
#define MT6370_IRQ_FLED2_TOR 84
#define MT6370_IRQ_FLED1_TOR 85
diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
index f6c1f80f94a4..3e58d0764c7e 100644
--- a/drivers/mfd/mt6397-core.c
+++ b/drivers/mfd/mt6397-core.c
@@ -6,18 +6,21 @@
#include <linux/interrupt.h>
#include <linux/ioport.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/mfd/core.h>
#include <linux/mfd/mt6323/core.h>
+#include <linux/mfd/mt6328/core.h>
#include <linux/mfd/mt6331/core.h>
#include <linux/mfd/mt6357/core.h>
#include <linux/mfd/mt6358/core.h>
#include <linux/mfd/mt6359/core.h>
#include <linux/mfd/mt6397/core.h>
#include <linux/mfd/mt6323/registers.h>
+#include <linux/mfd/mt6328/registers.h>
#include <linux/mfd/mt6331/registers.h>
#include <linux/mfd/mt6357/registers.h>
#include <linux/mfd/mt6358/registers.h>
@@ -81,11 +84,24 @@ static const struct resource mt6359_keys_resources[] = {
DEFINE_RES_IRQ_NAMED(MT6359_IRQ_HOMEKEY_R, "homekey_r"),
};
+static const struct resource mt6359_accdet_resources[] = {
+ DEFINE_RES_IRQ_NAMED(MT6359_IRQ_ACCDET, "accdet_irq"),
+ DEFINE_RES_IRQ_NAMED(MT6359_IRQ_ACCDET_EINT0, "accdet_eint0"),
+ DEFINE_RES_IRQ_NAMED(MT6359_IRQ_ACCDET_EINT1, "accdet_eint1"),
+};
+
static const struct resource mt6323_keys_resources[] = {
DEFINE_RES_IRQ_NAMED(MT6323_IRQ_STATUS_PWRKEY, "powerkey"),
DEFINE_RES_IRQ_NAMED(MT6323_IRQ_STATUS_FCHRKEY, "homekey"),
};
+static const struct resource mt6328_keys_resources[] = {
+ DEFINE_RES_IRQ_NAMED(MT6328_IRQ_STATUS_PWRKEY, "powerkey"),
+ DEFINE_RES_IRQ_NAMED(MT6328_IRQ_STATUS_HOMEKEY, "homekey"),
+ DEFINE_RES_IRQ_NAMED(MT6328_IRQ_STATUS_PWRKEY_R, "powerkey_r"),
+ DEFINE_RES_IRQ_NAMED(MT6328_IRQ_STATUS_HOMEKEY_R, "homekey_r"),
+};
+
static const struct resource mt6357_keys_resources[] = {
DEFINE_RES_IRQ_NAMED(MT6357_IRQ_PWRKEY, "powerkey"),
DEFINE_RES_IRQ_NAMED(MT6357_IRQ_HOMEKEY, "homekey"),
@@ -120,7 +136,7 @@ static const struct mfd_cell mt6323_devs[] = {
.name = "mt6323-led",
.of_compatible = "mediatek,mt6323-led"
}, {
- .name = "mtk-pmic-keys",
+ .name = "mt6323-keys",
.num_resources = ARRAY_SIZE(mt6323_keys_resources),
.resources = mt6323_keys_resources,
.of_compatible = "mediatek,mt6323-keys"
@@ -132,8 +148,23 @@ static const struct mfd_cell mt6323_devs[] = {
},
};
+static const struct mfd_cell mt6328_devs[] = {
+ {
+ .name = "mt6328-regulator",
+ .of_compatible = "mediatek,mt6328-regulator"
+ }, {
+ .name = "mt6328-keys",
+ .num_resources = ARRAY_SIZE(mt6328_keys_resources),
+ .resources = mt6328_keys_resources,
+ .of_compatible = "mediatek,mt6328-keys"
+ },
+};
+
static const struct mfd_cell mt6357_devs[] = {
{
+ .name = "mt6359-auxadc",
+ .of_compatible = "mediatek,mt6357-auxadc"
+ }, {
.name = "mt6357-regulator",
}, {
.name = "mt6357-rtc",
@@ -141,7 +172,10 @@ static const struct mfd_cell mt6357_devs[] = {
.resources = mt6357_rtc_resources,
.of_compatible = "mediatek,mt6357-rtc",
}, {
- .name = "mtk-pmic-keys",
+ .name = "mt6357-sound",
+ .of_compatible = "mediatek,mt6357-sound"
+ }, {
+ .name = "mt6357-keys",
.num_resources = ARRAY_SIZE(mt6357_keys_resources),
.resources = mt6357_keys_resources,
.of_compatible = "mediatek,mt6357-keys"
@@ -162,7 +196,7 @@ static const struct mfd_cell mt6331_mt6332_devs[] = {
.name = "mt6332-regulator",
.of_compatible = "mediatek,mt6332-regulator"
}, {
- .name = "mtk-pmic-keys",
+ .name = "mt6331-keys",
.num_resources = ARRAY_SIZE(mt6331_keys_resources),
.resources = mt6331_keys_resources,
.of_compatible = "mediatek,mt6331-keys"
@@ -171,6 +205,9 @@ static const struct mfd_cell mt6331_mt6332_devs[] = {
static const struct mfd_cell mt6358_devs[] = {
{
+ .name = "mt6359-auxadc",
+ .of_compatible = "mediatek,mt6358-auxadc"
+ }, {
.name = "mt6358-regulator",
.of_compatible = "mediatek,mt6358-regulator"
}, {
@@ -190,6 +227,10 @@ static const struct mfd_cell mt6358_devs[] = {
};
static const struct mfd_cell mt6359_devs[] = {
+ {
+ .name = "mt6359-auxadc",
+ .of_compatible = "mediatek,mt6359-auxadc"
+ },
{ .name = "mt6359-regulator", },
{
.name = "mt6359-rtc",
@@ -199,11 +240,17 @@ static const struct mfd_cell mt6359_devs[] = {
},
{ .name = "mt6359-sound", },
{
- .name = "mtk-pmic-keys",
+ .name = "mt6359-keys",
.num_resources = ARRAY_SIZE(mt6359_keys_resources),
.resources = mt6359_keys_resources,
.of_compatible = "mediatek,mt6359-keys"
},
+ {
+ .name = "mt6359-accdet",
+ .of_compatible = "mediatek,mt6359-accdet",
+ .num_resources = ARRAY_SIZE(mt6359_accdet_resources),
+ .resources = mt6359_accdet_resources,
+ },
};
static const struct mfd_cell mt6397_devs[] = {
@@ -225,7 +272,7 @@ static const struct mfd_cell mt6397_devs[] = {
.name = "mt6397-pinctrl",
.of_compatible = "mediatek,mt6397-pinctrl",
}, {
- .name = "mtk-pmic-keys",
+ .name = "mt6397-keys",
.num_resources = ARRAY_SIZE(mt6397_keys_resources),
.resources = mt6397_keys_resources,
.of_compatible = "mediatek,mt6397-keys"
@@ -248,6 +295,14 @@ static const struct chip_data mt6323_core = {
.irq_init = mt6397_irq_init,
};
+static const struct chip_data mt6328_core = {
+ .cid_addr = MT6328_HWCID,
+ .cid_shift = 0,
+ .cells = mt6328_devs,
+ .cell_size = ARRAY_SIZE(mt6328_devs),
+ .irq_init = mt6397_irq_init,
+};
+
static const struct chip_data mt6357_core = {
.cid_addr = MT6357_SWCID,
.cid_shift = 8,
@@ -347,6 +402,9 @@ static const struct of_device_id mt6397_of_match[] = {
.compatible = "mediatek,mt6323",
.data = &mt6323_core,
}, {
+ .compatible = "mediatek,mt6328",
+ .data = &mt6328_core,
+ }, {
.compatible = "mediatek,mt6331",
.data = &mt6331_mt6332_core,
}, {
diff --git a/drivers/mfd/mt6397-irq.c b/drivers/mfd/mt6397-irq.c
index 72f923e47752..5d2e5459f744 100644
--- a/drivers/mfd/mt6397-irq.c
+++ b/drivers/mfd/mt6397-irq.c
@@ -3,15 +3,16 @@
// Copyright (c) 2019 MediaTek Inc.
#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/suspend.h>
#include <linux/mfd/mt6323/core.h>
#include <linux/mfd/mt6323/registers.h>
+#include <linux/mfd/mt6328/core.h>
+#include <linux/mfd/mt6328/registers.h>
#include <linux/mfd/mt6331/core.h>
#include <linux/mfd/mt6331/registers.h>
#include <linux/mfd/mt6397/core.h>
@@ -32,6 +33,9 @@ static void mt6397_irq_sync_unlock(struct irq_data *data)
mt6397->irq_masks_cur[0]);
regmap_write(mt6397->regmap, mt6397->int_con[1],
mt6397->irq_masks_cur[1]);
+ if (mt6397->int_con[2])
+ regmap_write(mt6397->regmap, mt6397->int_con[2],
+ mt6397->irq_masks_cur[2]);
mutex_unlock(&mt6397->irqlock);
}
@@ -106,6 +110,8 @@ static irqreturn_t mt6397_irq_thread(int irq, void *data)
mt6397_irq_handle_reg(mt6397, mt6397->int_status[0], 0);
mt6397_irq_handle_reg(mt6397, mt6397->int_status[1], 16);
+ if (mt6397->int_status[2])
+ mt6397_irq_handle_reg(mt6397, mt6397->int_status[2], 32);
return IRQ_HANDLED;
}
@@ -139,6 +145,9 @@ static int mt6397_irq_pm_notifier(struct notifier_block *notifier,
chip->int_con[0], chip->wake_mask[0]);
regmap_write(chip->regmap,
chip->int_con[1], chip->wake_mask[1]);
+ if (chip->int_con[2])
+ regmap_write(chip->regmap,
+ chip->int_con[2], chip->wake_mask[2]);
enable_irq_wake(chip->irq);
break;
@@ -147,6 +156,9 @@ static int mt6397_irq_pm_notifier(struct notifier_block *notifier,
chip->int_con[0], chip->irq_masks_cur[0]);
regmap_write(chip->regmap,
chip->int_con[1], chip->irq_masks_cur[1]);
+ if (chip->int_con[2])
+ regmap_write(chip->regmap,
+ chip->int_con[2], chip->irq_masks_cur[2]);
disable_irq_wake(chip->irq);
break;
@@ -170,6 +182,14 @@ int mt6397_irq_init(struct mt6397_chip *chip)
chip->int_status[0] = MT6323_INT_STATUS0;
chip->int_status[1] = MT6323_INT_STATUS1;
break;
+ case MT6328_CHIP_ID:
+ chip->int_con[0] = MT6328_INT_CON0;
+ chip->int_con[1] = MT6328_INT_CON1;
+ chip->int_con[2] = MT6328_INT_CON2;
+ chip->int_status[0] = MT6328_INT_STATUS0;
+ chip->int_status[1] = MT6328_INT_STATUS1;
+ chip->int_status[2] = MT6328_INT_STATUS2;
+ break;
case MT6331_CHIP_ID:
chip->int_con[0] = MT6331_INT_CON0;
chip->int_con[1] = MT6331_INT_CON1;
@@ -192,12 +212,12 @@ int mt6397_irq_init(struct mt6397_chip *chip)
/* Mask all interrupt sources */
regmap_write(chip->regmap, chip->int_con[0], 0x0);
regmap_write(chip->regmap, chip->int_con[1], 0x0);
+ if (chip->int_con[2])
+ regmap_write(chip->regmap, chip->int_con[2], 0x0);
chip->pm_nb.notifier_call = mt6397_irq_pm_notifier;
- chip->irq_domain = irq_domain_add_linear(chip->dev->of_node,
- MT6397_IRQ_NR,
- &mt6397_irq_domain_ops,
- chip);
+ chip->irq_domain = irq_domain_create_linear(dev_fwnode(chip->dev), MT6397_IRQ_NR,
+ &mt6397_irq_domain_ops, chip);
if (!chip->irq_domain) {
dev_err(chip->dev, "could not create irq domain\n");
return -ENOMEM;
@@ -209,6 +229,7 @@ int mt6397_irq_init(struct mt6397_chip *chip)
if (ret) {
dev_err(chip->dev, "failed to register irq=%d; err: %d\n",
chip->irq, ret);
+ irq_domain_remove(chip->irq_domain);
return ret;
}
diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c
index 111d11fd25aa..64afc7631790 100644
--- a/drivers/mfd/mxs-lradc.c
+++ b/drivers/mfd/mxs-lradc.c
@@ -16,8 +16,8 @@
#include <linux/mfd/mxs-lradc.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/slab.h>
#define ADC_CELL 0
@@ -125,7 +125,6 @@ MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
static int mxs_lradc_probe(struct platform_device *pdev)
{
- const struct of_device_id *of_id;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct mxs_lradc *lradc;
@@ -138,11 +137,7 @@ static int mxs_lradc_probe(struct platform_device *pdev)
if (!lradc)
return -ENOMEM;
- of_id = of_match_device(mxs_lradc_dt_ids, &pdev->dev);
- if (!of_id)
- return -EINVAL;
-
- lradc->soc = (enum mxs_lradc_id)of_id->data;
+ lradc->soc = (kernel_ulong_t)device_get_match_data(&pdev->dev);
lradc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(lradc->clk)) {
@@ -235,13 +230,11 @@ err_clk:
return ret;
}
-static int mxs_lradc_remove(struct platform_device *pdev)
+static void mxs_lradc_remove(struct platform_device *pdev)
{
struct mxs_lradc *lradc = platform_get_drvdata(pdev);
clk_disable_unprepare(lradc->clk);
-
- return 0;
}
static struct platform_driver mxs_lradc_driver = {
diff --git a/drivers/mfd/nct6694.c b/drivers/mfd/nct6694.c
new file mode 100644
index 000000000000..308b2fda3055
--- /dev/null
+++ b/drivers/mfd/nct6694.c
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Nuvoton Technology Corp.
+ *
+ * Nuvoton NCT6694 core driver using USB interface to provide
+ * access to the NCT6694 hardware monitoring and control features.
+ *
+ * The NCT6694 is an integrated controller that provides GPIO, I2C,
+ * CAN, WDT, HWMON and RTC management.
+ */
+
+#include <linux/bits.h>
+#include <linux/interrupt.h>
+#include <linux/idr.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/nct6694.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+
+static const struct mfd_cell nct6694_devs[] = {
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+ MFD_CELL_NAME("nct6694-gpio"),
+
+ MFD_CELL_NAME("nct6694-i2c"),
+ MFD_CELL_NAME("nct6694-i2c"),
+ MFD_CELL_NAME("nct6694-i2c"),
+ MFD_CELL_NAME("nct6694-i2c"),
+ MFD_CELL_NAME("nct6694-i2c"),
+ MFD_CELL_NAME("nct6694-i2c"),
+
+ MFD_CELL_NAME("nct6694-canfd"),
+ MFD_CELL_NAME("nct6694-canfd"),
+
+ MFD_CELL_NAME("nct6694-wdt"),
+ MFD_CELL_NAME("nct6694-wdt"),
+
+ MFD_CELL_NAME("nct6694-hwmon"),
+
+ MFD_CELL_NAME("nct6694-rtc"),
+};
+
+static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char err_status)
+{
+ switch (err_status) {
+ case NCT6694_NO_ERROR:
+ return 0;
+ case NCT6694_NOT_SUPPORT_ERROR:
+ dev_err(nct6694->dev, "Command is not supported!\n");
+ break;
+ case NCT6694_NO_RESPONSE_ERROR:
+ dev_warn(nct6694->dev, "Command received no response!\n");
+ break;
+ case NCT6694_TIMEOUT_ERROR:
+ dev_warn(nct6694->dev, "Command timed out!\n");
+ break;
+ case NCT6694_PENDING:
+ dev_err(nct6694->dev, "Command is pending!\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return -EIO;
+}
+
+/**
+ * nct6694_read_msg() - Read message from NCT6694 device
+ * @nct6694: NCT6694 device pointer
+ * @cmd_hd: command header structure
+ * @buf: buffer to store the response data
+ *
+ * Sends a command to the NCT6694 device and reads the response.
+ * The command header is specified in @cmd_hd, and the response
+ * data is stored in @buf.
+ *
+ * Return: Negative value on error or 0 on success.
+ */
+int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)
+{
+ union nct6694_usb_msg *msg = nct6694->usb_msg;
+ struct usb_device *udev = nct6694->udev;
+ int tx_len, rx_len, ret;
+
+ guard(mutex)(&nct6694->access_lock);
+
+ memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
+ msg->cmd_header.hctrl = NCT6694_HCTRL_GET;
+
+ /* Send command packet to USB device */
+ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), &msg->cmd_header,
+ sizeof(*msg), &tx_len, NCT6694_URB_TIMEOUT);
+ if (ret)
+ return ret;
+
+ /* Receive response packet from USB device */
+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), &msg->response_header,
+ sizeof(*msg), &rx_len, NCT6694_URB_TIMEOUT);
+ if (ret)
+ return ret;
+
+ /* Receive data packet from USB device */
+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf,
+ le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT);
+ if (ret)
+ return ret;
+
+ if (rx_len != le16_to_cpu(cmd_hd->len)) {
+ dev_err(nct6694->dev, "Expected received length %d, but got %d\n",
+ le16_to_cpu(cmd_hd->len), rx_len);
+ return -EIO;
+ }
+
+ return nct6694_response_err_handling(nct6694, msg->response_header.sts);
+}
+EXPORT_SYMBOL_GPL(nct6694_read_msg);
+
+/**
+ * nct6694_write_msg() - Write message to NCT6694 device
+ * @nct6694: NCT6694 device pointer
+ * @cmd_hd: command header structure
+ * @buf: buffer containing the data to be sent
+ *
+ * Sends a command to the NCT6694 device and writes the data
+ * from @buf. The command header is specified in @cmd_hd.
+ *
+ * Return: Negative value on error or 0 on success.
+ */
+int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)
+{
+ union nct6694_usb_msg *msg = nct6694->usb_msg;
+ struct usb_device *udev = nct6694->udev;
+ int tx_len, rx_len, ret;
+
+ guard(mutex)(&nct6694->access_lock);
+
+ memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
+ msg->cmd_header.hctrl = NCT6694_HCTRL_SET;
+
+ /* Send command packet to USB device */
+ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), &msg->cmd_header,
+ sizeof(*msg), &tx_len, NCT6694_URB_TIMEOUT);
+ if (ret)
+ return ret;
+
+ /* Send data packet to USB device */
+ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), buf,
+ le16_to_cpu(cmd_hd->len), &tx_len, NCT6694_URB_TIMEOUT);
+ if (ret)
+ return ret;
+
+ /* Receive response packet from USB device */
+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), &msg->response_header,
+ sizeof(*msg), &rx_len, NCT6694_URB_TIMEOUT);
+ if (ret)
+ return ret;
+
+ /* Receive data packet from USB device */
+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf,
+ le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT);
+ if (ret)
+ return ret;
+
+ if (rx_len != le16_to_cpu(cmd_hd->len)) {
+ dev_err(nct6694->dev, "Expected transmitted length %d, but got %d\n",
+ le16_to_cpu(cmd_hd->len), rx_len);
+ return -EIO;
+ }
+
+ return nct6694_response_err_handling(nct6694, msg->response_header.sts);
+}
+EXPORT_SYMBOL_GPL(nct6694_write_msg);
+
+static void usb_int_callback(struct urb *urb)
+{
+ struct nct6694 *nct6694 = urb->context;
+ __le32 *status_le = urb->transfer_buffer;
+ u32 int_status;
+ int ret;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default:
+ goto resubmit;
+ }
+
+ int_status = le32_to_cpu(*status_le);
+
+ while (int_status) {
+ int irq = __ffs(int_status);
+
+ generic_handle_irq_safe(irq_find_mapping(nct6694->domain, irq));
+ int_status &= ~BIT(irq);
+ }
+
+resubmit:
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ dev_warn(nct6694->dev, "Failed to resubmit urb, status %pe", ERR_PTR(ret));
+}
+
+static void nct6694_irq_enable(struct irq_data *data)
+{
+ struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
+
+ guard(spinlock_irqsave)(&nct6694->irq_lock);
+
+ nct6694->irq_enable |= BIT(hwirq);
+}
+
+static void nct6694_irq_disable(struct irq_data *data)
+{
+ struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
+
+ guard(spinlock_irqsave)(&nct6694->irq_lock);
+
+ nct6694->irq_enable &= ~BIT(hwirq);
+}
+
+static const struct irq_chip nct6694_irq_chip = {
+ .name = "nct6694-irq",
+ .flags = IRQCHIP_SKIP_SET_WAKE,
+ .irq_enable = nct6694_irq_enable,
+ .irq_disable = nct6694_irq_disable,
+};
+
+static int nct6694_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+{
+ struct nct6694 *nct6694 = d->host_data;
+
+ irq_set_chip_data(irq, nct6694);
+ irq_set_chip_and_handler(irq, &nct6694_irq_chip, handle_simple_irq);
+
+ return 0;
+}
+
+static void nct6694_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops nct6694_irq_domain_ops = {
+ .map = nct6694_irq_domain_map,
+ .unmap = nct6694_irq_domain_unmap,
+};
+
+static int nct6694_usb_probe(struct usb_interface *iface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(iface);
+ struct usb_endpoint_descriptor *int_endpoint;
+ struct usb_host_interface *interface;
+ struct device *dev = &iface->dev;
+ struct nct6694 *nct6694;
+ int ret;
+
+ nct6694 = devm_kzalloc(dev, sizeof(*nct6694), GFP_KERNEL);
+ if (!nct6694)
+ return -ENOMEM;
+
+ nct6694->usb_msg = devm_kzalloc(dev, sizeof(union nct6694_usb_msg), GFP_KERNEL);
+ if (!nct6694->usb_msg)
+ return -ENOMEM;
+
+ nct6694->int_buffer = devm_kzalloc(dev, sizeof(*nct6694->int_buffer), GFP_KERNEL);
+ if (!nct6694->int_buffer)
+ return -ENOMEM;
+
+ nct6694->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!nct6694->int_in_urb)
+ return -ENOMEM;
+
+ nct6694->domain = irq_domain_create_simple(NULL, NCT6694_NR_IRQS, 0,
+ &nct6694_irq_domain_ops,
+ nct6694);
+ if (!nct6694->domain) {
+ ret = -ENODEV;
+ goto err_urb;
+ }
+
+ nct6694->dev = dev;
+ nct6694->udev = udev;
+
+ ida_init(&nct6694->gpio_ida);
+ ida_init(&nct6694->i2c_ida);
+ ida_init(&nct6694->canfd_ida);
+ ida_init(&nct6694->wdt_ida);
+
+ spin_lock_init(&nct6694->irq_lock);
+
+ ret = devm_mutex_init(dev, &nct6694->access_lock);
+ if (ret)
+ goto err_ida;
+
+ interface = iface->cur_altsetting;
+
+ int_endpoint = &interface->endpoint[0].desc;
+ if (!usb_endpoint_is_int_in(int_endpoint)) {
+ ret = -ENODEV;
+ goto err_ida;
+ }
+
+ usb_fill_int_urb(nct6694->int_in_urb, udev, usb_rcvintpipe(udev, NCT6694_INT_IN_EP),
+ nct6694->int_buffer, sizeof(*nct6694->int_buffer), usb_int_callback,
+ nct6694, int_endpoint->bInterval);
+
+ ret = usb_submit_urb(nct6694->int_in_urb, GFP_KERNEL);
+ if (ret)
+ goto err_ida;
+
+ usb_set_intfdata(iface, nct6694);
+
+ ret = mfd_add_hotplug_devices(dev, nct6694_devs, ARRAY_SIZE(nct6694_devs));
+ if (ret)
+ goto err_mfd;
+
+ return 0;
+
+err_mfd:
+ usb_kill_urb(nct6694->int_in_urb);
+err_ida:
+ ida_destroy(&nct6694->wdt_ida);
+ ida_destroy(&nct6694->canfd_ida);
+ ida_destroy(&nct6694->i2c_ida);
+ ida_destroy(&nct6694->gpio_ida);
+ irq_domain_remove(nct6694->domain);
+err_urb:
+ usb_free_urb(nct6694->int_in_urb);
+ return ret;
+}
+
+static void nct6694_usb_disconnect(struct usb_interface *iface)
+{
+ struct nct6694 *nct6694 = usb_get_intfdata(iface);
+
+ mfd_remove_devices(nct6694->dev);
+ usb_kill_urb(nct6694->int_in_urb);
+ ida_destroy(&nct6694->wdt_ida);
+ ida_destroy(&nct6694->canfd_ida);
+ ida_destroy(&nct6694->i2c_ida);
+ ida_destroy(&nct6694->gpio_ida);
+ irq_domain_remove(nct6694->domain);
+ usb_free_urb(nct6694->int_in_urb);
+}
+
+static const struct usb_device_id nct6694_ids[] = {
+ { USB_DEVICE_AND_INTERFACE_INFO(NCT6694_VENDOR_ID, NCT6694_PRODUCT_ID, 0xFF, 0x00, 0x00) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, nct6694_ids);
+
+static struct usb_driver nct6694_usb_driver = {
+ .name = "nct6694",
+ .id_table = nct6694_ids,
+ .probe = nct6694_usb_probe,
+ .disconnect = nct6694_usb_disconnect,
+};
+module_usb_driver(nct6694_usb_driver);
+
+MODULE_DESCRIPTION("Nuvoton NCT6694 core driver");
+MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/ntxec.c b/drivers/mfd/ntxec.c
index 4416cd37e539..08c68de0f01b 100644
--- a/drivers/mfd/ntxec.c
+++ b/drivers/mfd/ntxec.c
@@ -21,7 +21,7 @@
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/types.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define NTXEC_REG_VERSION 0x00
#define NTXEC_REG_POWEROFF 0x50
diff --git a/drivers/mfd/ocelot-core.c b/drivers/mfd/ocelot-core.c
index 9cccf54fc9c8..41aff2708854 100644
--- a/drivers/mfd/ocelot-core.c
+++ b/drivers/mfd/ocelot-core.c
@@ -113,7 +113,7 @@ int ocelot_chip_reset(struct device *dev)
return readx_poll_timeout(ocelot_gcb_chip_rst_status, ddata, val, !val,
VSC7512_GCB_RST_SLEEP_US, VSC7512_GCB_RST_TIMEOUT_US);
}
-EXPORT_SYMBOL_NS(ocelot_chip_reset, MFD_OCELOT);
+EXPORT_SYMBOL_NS(ocelot_chip_reset, "MFD_OCELOT");
static const struct resource vsc7512_miim0_resources[] = {
DEFINE_RES_REG_NAMED(VSC7512_MIIM0_RES_START, VSC7512_MIIM_RES_SIZE, "gcb_miim0"),
@@ -226,9 +226,9 @@ int ocelot_core_init(struct device *dev)
return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, vsc7512_devs, ndevs, NULL, 0, NULL);
}
-EXPORT_SYMBOL_NS(ocelot_core_init, MFD_OCELOT);
+EXPORT_SYMBOL_NS(ocelot_core_init, "MFD_OCELOT");
MODULE_DESCRIPTION("Externally Controlled Ocelot Chip Driver");
MODULE_AUTHOR("Colin Foster <colin.foster@in-advantage.com>");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(MFD_OCELOT_SPI);
+MODULE_IMPORT_NS("MFD_OCELOT_SPI");
diff --git a/drivers/mfd/ocelot-spi.c b/drivers/mfd/ocelot-spi.c
index 94f82677675b..1fed9878c323 100644
--- a/drivers/mfd/ocelot-spi.c
+++ b/drivers/mfd/ocelot-spi.c
@@ -145,7 +145,6 @@ static int ocelot_spi_regmap_bus_read(void *context, const void *reg, size_t reg
struct device *dev = context;
struct ocelot_ddata *ddata;
struct spi_device *spi;
- struct spi_message msg;
unsigned int index = 0;
ddata = dev_get_drvdata(dev);
@@ -166,9 +165,7 @@ static int ocelot_spi_regmap_bus_read(void *context, const void *reg, size_t reg
xfers[index].len = val_size;
index++;
- spi_message_init_with_transfers(&msg, xfers, index);
-
- return spi_sync(spi, &msg);
+ return spi_sync_transfer(spi, xfers, index);
}
static int ocelot_spi_regmap_bus_write(void *context, const void *data, size_t count)
@@ -196,7 +193,7 @@ struct regmap *ocelot_spi_init_regmap(struct device *dev, const struct resource
return devm_regmap_init(dev, &ocelot_spi_regmap_bus, dev, &regmap_config);
}
-EXPORT_SYMBOL_NS(ocelot_spi_init_regmap, MFD_OCELOT_SPI);
+EXPORT_SYMBOL_NS(ocelot_spi_init_regmap, "MFD_OCELOT_SPI");
static int ocelot_spi_probe(struct spi_device *spi)
{
@@ -298,4 +295,4 @@ module_spi_driver(ocelot_spi_driver);
MODULE_DESCRIPTION("SPI Controlled Ocelot Chip Driver");
MODULE_AUTHOR("Colin Foster <colin.foster@in-advantage.com>");
MODULE_LICENSE("Dual MIT/GPL");
-MODULE_IMPORT_NS(MFD_OCELOT);
+MODULE_IMPORT_NS("MFD_OCELOT");
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 7f5775109593..a77b6fc790f2 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -13,7 +13,6 @@
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
-#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/platform_data/usb-omap.h>
#include <linux/pm_runtime.h>
@@ -534,7 +533,6 @@ static int usbhs_omap_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct usbhs_omap_platform_data *pdata = dev_get_platdata(dev);
struct usbhs_hcd_omap *omap;
- struct resource *res;
int ret = 0;
int i;
bool need_logic_fck;
@@ -569,8 +567,7 @@ static int usbhs_omap_probe(struct platform_device *pdev)
return -ENOMEM;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- omap->uhh_base = devm_ioremap_resource(dev, res);
+ omap->uhh_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(omap->uhh_base))
return PTR_ERR(omap->uhh_base);
@@ -701,7 +698,7 @@ static int usbhs_omap_probe(struct platform_device *pdev)
}
for (i = 0; i < omap->nports; i++) {
- char clkname[30];
+ char clkname[40];
/* clock names are indexed from 1*/
snprintf(clkname, sizeof(clkname),
@@ -818,13 +815,12 @@ static int usbhs_omap_remove_child(struct device *dev, void *data)
*
* Reverses the effect of usbhs_omap_probe().
*/
-static int usbhs_omap_remove(struct platform_device *pdev)
+static void usbhs_omap_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
/* remove children */
device_for_each_child(&pdev->dev, NULL, usbhs_omap_remove_child);
- return 0;
}
static const struct dev_pm_ops usbhsomap_dev_pm_ops = {
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index 69cbc2097911..0f7fdb99c809 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -98,8 +98,8 @@
struct usbtll_omap {
void __iomem *base;
- int nch; /* num. of channels */
- struct clk *ch_clk[]; /* must be the last member */
+ int nch;
+ struct clk *ch_clk[] __counted_by(nch);
};
/*-------------------------------------------------------------------------*/
@@ -200,15 +200,13 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode)
static int usbtll_omap_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct resource *res;
struct usbtll_omap *tll;
void __iomem *base;
int i, nch, ver;
dev_dbg(dev, "starting TI HSUSB TLL Controller\n");
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -232,8 +230,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)
break;
}
- tll = devm_kzalloc(dev, sizeof(*tll) + sizeof(tll->ch_clk[nch]),
- GFP_KERNEL);
+ tll = devm_kzalloc(dev, struct_size(tll, ch_clk, nch), GFP_KERNEL);
if (!tll) {
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
@@ -272,7 +269,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)
*
* Reverses the effect of usbtll_omap_probe().
*/
-static int usbtll_omap_remove(struct platform_device *pdev)
+static void usbtll_omap_remove(struct platform_device *pdev)
{
struct usbtll_omap *tll = platform_get_drvdata(pdev);
int i;
@@ -289,7 +286,6 @@ static int usbtll_omap_remove(struct platform_device *pdev)
}
pm_runtime_disable(&pdev->dev);
- return 0;
}
static const struct of_device_id usbtll_omap_dt_ids[] = {
diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c
index a36f12402987..7fc886f4f80e 100644
--- a/drivers/mfd/palmas.c
+++ b/drivers/mfd/palmas.c
@@ -18,7 +18,8 @@
#include <linux/err.h>
#include <linux/mfd/core.h>
#include <linux/mfd/palmas.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
static const struct regmap_config palmas_regmap_config[PALMAS_NUM_CLIENTS] = {
{
@@ -295,7 +296,7 @@ static const struct regmap_irq palmas_irqs[] = {
},
};
-static struct regmap_irq_chip palmas_irq_chip = {
+static const struct regmap_irq_chip palmas_irq_chip = {
.name = "palmas",
.irqs = palmas_irqs,
.num_irqs = ARRAY_SIZE(palmas_irqs),
@@ -308,7 +309,7 @@ static struct regmap_irq_chip palmas_irq_chip = {
PALMAS_INT1_MASK),
};
-static struct regmap_irq_chip tps65917_irq_chip = {
+static const struct regmap_irq_chip tps65917_irq_chip = {
.name = "tps65917",
.irqs = tps65917_irqs,
.num_irqs = ARRAY_SIZE(tps65917_irqs),
@@ -462,51 +463,29 @@ static void palmas_power_off(void)
__func__, ret);
}
-static unsigned int palmas_features = PALMAS_PMIC_FEATURE_SMPS10_BOOST;
-static unsigned int tps659038_features;
-
struct palmas_driver_data {
- unsigned int *features;
- struct regmap_irq_chip *irq_chip;
+ unsigned int features;
+ const struct regmap_irq_chip *irq_chip;
};
-static struct palmas_driver_data palmas_data = {
- .features = &palmas_features,
+static const struct palmas_driver_data palmas_data = {
+ .features = PALMAS_PMIC_FEATURE_SMPS10_BOOST,
.irq_chip = &palmas_irq_chip,
};
-static struct palmas_driver_data tps659038_data = {
- .features = &tps659038_features,
+static const struct palmas_driver_data tps659038_data = {
.irq_chip = &palmas_irq_chip,
};
-static struct palmas_driver_data tps65917_data = {
- .features = &tps659038_features,
+static const struct palmas_driver_data tps65917_data = {
.irq_chip = &tps65917_irq_chip,
};
-static const struct of_device_id of_palmas_match_tbl[] = {
- {
- .compatible = "ti,palmas",
- .data = &palmas_data,
- },
- {
- .compatible = "ti,tps659038",
- .data = &tps659038_data,
- },
- {
- .compatible = "ti,tps65917",
- .data = &tps65917_data,
- },
- { },
-};
-MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
-
static int palmas_i2c_probe(struct i2c_client *i2c)
{
struct palmas *palmas;
struct palmas_platform_data *pdata;
- struct palmas_driver_data *driver_data;
+ const struct palmas_driver_data *driver_data;
struct device_node *node = i2c->dev.of_node;
int ret = 0, i;
unsigned int reg, addr;
@@ -534,8 +513,8 @@ static int palmas_i2c_probe(struct i2c_client *i2c)
palmas->dev = &i2c->dev;
palmas->irq = i2c->irq;
- driver_data = (struct palmas_driver_data *) device_get_match_data(&i2c->dev);
- palmas->features = *driver_data->features;
+ driver_data = i2c_get_match_data(i2c);
+ palmas->features = driver_data->features;
for (i = 0; i < PALMAS_NUM_CLIENTS; i++) {
if (i == 0)
@@ -711,11 +690,19 @@ static void palmas_i2c_remove(struct i2c_client *i2c)
}
}
+static const struct of_device_id of_palmas_match_tbl[] = {
+ { .compatible = "ti,palmas", .data = &palmas_data },
+ { .compatible = "ti,tps659038", .data = &tps659038_data },
+ { .compatible = "ti,tps65917", .data = &tps65917_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
+
static const struct i2c_device_id palmas_i2c_id[] = {
- { "palmas", },
- { "twl6035", },
- { "twl6037", },
- { "tps65913", },
+ { "palmas", (kernel_ulong_t)&palmas_data },
+ { "twl6035", (kernel_ulong_t)&palmas_data },
+ { "twl6037", (kernel_ulong_t)&palmas_data },
+ { "tps65913", (kernel_ulong_t)&palmas_data },
{ /* end */ }
};
MODULE_DEVICE_TABLE(i2c, palmas_i2c_id);
diff --git a/drivers/mfd/pcf50633-adc.c b/drivers/mfd/pcf50633-adc.c
deleted file mode 100644
index 191b1bc6141c..000000000000
--- a/drivers/mfd/pcf50633-adc.c
+++ /dev/null
@@ -1,257 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* NXP PCF50633 ADC Driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- *
- * Broken down from monstrous PCF50633 driver mainly by
- * Harald Welte, Andy Green and Werner Almesberger
- *
- * NOTE: This driver does not yet support subtractive ADC mode, which means
- * you can do only one measurement per read request.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/completion.h>
-
-#include <linux/mfd/pcf50633/core.h>
-#include <linux/mfd/pcf50633/adc.h>
-
-struct pcf50633_adc_request {
- int mux;
- int avg;
- void (*callback)(struct pcf50633 *, void *, int);
- void *callback_param;
-};
-
-struct pcf50633_adc_sync_request {
- int result;
- struct completion completion;
-};
-
-#define PCF50633_MAX_ADC_FIFO_DEPTH 8
-
-struct pcf50633_adc {
- struct pcf50633 *pcf;
-
- /* Private stuff */
- struct pcf50633_adc_request *queue[PCF50633_MAX_ADC_FIFO_DEPTH];
- int queue_head;
- int queue_tail;
- struct mutex queue_mutex;
-};
-
-static inline struct pcf50633_adc *__to_adc(struct pcf50633 *pcf)
-{
- return platform_get_drvdata(pcf->adc_pdev);
-}
-
-static void adc_setup(struct pcf50633 *pcf, int channel, int avg)
-{
- channel &= PCF50633_ADCC1_ADCMUX_MASK;
-
- /* kill ratiometric, but enable ACCSW biasing */
- pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00);
- pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01);
-
- /* start ADC conversion on selected channel */
- pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg |
- PCF50633_ADCC1_ADCSTART | PCF50633_ADCC1_RES_10BIT);
-}
-
-static void trigger_next_adc_job_if_any(struct pcf50633 *pcf)
-{
- struct pcf50633_adc *adc = __to_adc(pcf);
- int head;
-
- head = adc->queue_head;
-
- if (!adc->queue[head])
- return;
-
- adc_setup(pcf, adc->queue[head]->mux, adc->queue[head]->avg);
-}
-
-static int
-adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req)
-{
- struct pcf50633_adc *adc = __to_adc(pcf);
- int head, tail;
-
- mutex_lock(&adc->queue_mutex);
-
- head = adc->queue_head;
- tail = adc->queue_tail;
-
- if (adc->queue[tail]) {
- mutex_unlock(&adc->queue_mutex);
- dev_err(pcf->dev, "ADC queue is full, dropping request\n");
- return -EBUSY;
- }
-
- adc->queue[tail] = req;
- if (head == tail)
- trigger_next_adc_job_if_any(pcf);
- adc->queue_tail = (tail + 1) & (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
-
- mutex_unlock(&adc->queue_mutex);
-
- return 0;
-}
-
-static void pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param,
- int result)
-{
- struct pcf50633_adc_sync_request *req = param;
-
- req->result = result;
- complete(&req->completion);
-}
-
-int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg)
-{
- struct pcf50633_adc_sync_request req;
- int ret;
-
- init_completion(&req.completion);
-
- ret = pcf50633_adc_async_read(pcf, mux, avg,
- pcf50633_adc_sync_read_callback, &req);
- if (ret)
- return ret;
-
- wait_for_completion(&req.completion);
-
- return req.result;
-}
-EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read);
-
-int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg,
- void (*callback)(struct pcf50633 *, void *, int),
- void *callback_param)
-{
- struct pcf50633_adc_request *req;
- int ret;
-
- /* req is freed when the result is ready, in interrupt handler */
- req = kmalloc(sizeof(*req), GFP_KERNEL);
- if (!req)
- return -ENOMEM;
-
- req->mux = mux;
- req->avg = avg;
- req->callback = callback;
- req->callback_param = callback_param;
-
- ret = adc_enqueue_request(pcf, req);
- if (ret)
- kfree(req);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(pcf50633_adc_async_read);
-
-static int adc_result(struct pcf50633 *pcf)
-{
- u8 adcs1, adcs3;
- u16 result;
-
- adcs1 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS1);
- adcs3 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS3);
- result = (adcs1 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT1L_MASK);
-
- dev_dbg(pcf->dev, "adc result = %d\n", result);
-
- return result;
-}
-
-static void pcf50633_adc_irq(int irq, void *data)
-{
- struct pcf50633_adc *adc = data;
- struct pcf50633 *pcf = adc->pcf;
- struct pcf50633_adc_request *req;
- int head, res;
-
- mutex_lock(&adc->queue_mutex);
- head = adc->queue_head;
-
- req = adc->queue[head];
- if (WARN_ON(!req)) {
- dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n");
- mutex_unlock(&adc->queue_mutex);
- return;
- }
- adc->queue[head] = NULL;
- adc->queue_head = (head + 1) &
- (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
-
- res = adc_result(pcf);
- trigger_next_adc_job_if_any(pcf);
-
- mutex_unlock(&adc->queue_mutex);
-
- req->callback(pcf, req->callback_param, res);
- kfree(req);
-}
-
-static int pcf50633_adc_probe(struct platform_device *pdev)
-{
- struct pcf50633_adc *adc;
-
- adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL);
- if (!adc)
- return -ENOMEM;
-
- adc->pcf = dev_to_pcf50633(pdev->dev.parent);
- platform_set_drvdata(pdev, adc);
-
- pcf50633_register_irq(adc->pcf, PCF50633_IRQ_ADCRDY,
- pcf50633_adc_irq, adc);
-
- mutex_init(&adc->queue_mutex);
-
- return 0;
-}
-
-static int pcf50633_adc_remove(struct platform_device *pdev)
-{
- struct pcf50633_adc *adc = platform_get_drvdata(pdev);
- int i, head;
-
- pcf50633_free_irq(adc->pcf, PCF50633_IRQ_ADCRDY);
-
- mutex_lock(&adc->queue_mutex);
- head = adc->queue_head;
-
- if (WARN_ON(adc->queue[head]))
- dev_err(adc->pcf->dev,
- "adc driver removed with request pending\n");
-
- for (i = 0; i < PCF50633_MAX_ADC_FIFO_DEPTH; i++)
- kfree(adc->queue[i]);
-
- mutex_unlock(&adc->queue_mutex);
-
- return 0;
-}
-
-static struct platform_driver pcf50633_adc_driver = {
- .driver = {
- .name = "pcf50633-adc",
- },
- .probe = pcf50633_adc_probe,
- .remove = pcf50633_adc_remove,
-};
-
-module_platform_driver(pcf50633_adc_driver);
-
-MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
-MODULE_DESCRIPTION("PCF50633 adc driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:pcf50633-adc");
-
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
deleted file mode 100644
index 014a68711b18..000000000000
--- a/drivers/mfd/pcf50633-core.c
+++ /dev/null
@@ -1,304 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* NXP PCF50633 Power Management Unit (PMU) driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Harald Welte <laforge@openmoko.org>
- * Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/sysfs.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
-#include <linux/pm.h>
-#include <linux/slab.h>
-#include <linux/regmap.h>
-#include <linux/err.h>
-
-#include <linux/mfd/pcf50633/core.h>
-
-/* Read a block of up to 32 regs */
-int pcf50633_read_block(struct pcf50633 *pcf, u8 reg,
- int nr_regs, u8 *data)
-{
- int ret;
-
- ret = regmap_raw_read(pcf->regmap, reg, data, nr_regs);
- if (ret != 0)
- return ret;
-
- return nr_regs;
-}
-EXPORT_SYMBOL_GPL(pcf50633_read_block);
-
-/* Write a block of up to 32 regs */
-int pcf50633_write_block(struct pcf50633 *pcf , u8 reg,
- int nr_regs, u8 *data)
-{
- return regmap_raw_write(pcf->regmap, reg, data, nr_regs);
-}
-EXPORT_SYMBOL_GPL(pcf50633_write_block);
-
-u8 pcf50633_reg_read(struct pcf50633 *pcf, u8 reg)
-{
- unsigned int val;
- int ret;
-
- ret = regmap_read(pcf->regmap, reg, &val);
- if (ret < 0)
- return -1;
-
- return val;
-}
-EXPORT_SYMBOL_GPL(pcf50633_reg_read);
-
-int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val)
-{
- return regmap_write(pcf->regmap, reg, val);
-}
-EXPORT_SYMBOL_GPL(pcf50633_reg_write);
-
-int pcf50633_reg_set_bit_mask(struct pcf50633 *pcf, u8 reg, u8 mask, u8 val)
-{
- return regmap_update_bits(pcf->regmap, reg, mask, val);
-}
-EXPORT_SYMBOL_GPL(pcf50633_reg_set_bit_mask);
-
-int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 val)
-{
- return regmap_update_bits(pcf->regmap, reg, val, 0);
-}
-EXPORT_SYMBOL_GPL(pcf50633_reg_clear_bits);
-
-/* sysfs attributes */
-static ssize_t dump_regs_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct pcf50633 *pcf = dev_get_drvdata(dev);
- u8 dump[16];
- int n, n1, idx = 0;
- char *buf1 = buf;
- static u8 address_no_read[] = { /* must be ascending */
- PCF50633_REG_INT1,
- PCF50633_REG_INT2,
- PCF50633_REG_INT3,
- PCF50633_REG_INT4,
- PCF50633_REG_INT5,
- 0 /* terminator */
- };
-
- for (n = 0; n < 256; n += sizeof(dump)) {
- for (n1 = 0; n1 < sizeof(dump); n1++)
- if (n == address_no_read[idx]) {
- idx++;
- dump[n1] = 0x00;
- } else
- dump[n1] = pcf50633_reg_read(pcf, n + n1);
-
- buf1 += sprintf(buf1, "%*ph\n", (int)sizeof(dump), dump);
- }
-
- return buf1 - buf;
-}
-static DEVICE_ATTR_ADMIN_RO(dump_regs);
-
-static ssize_t resume_reason_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct pcf50633 *pcf = dev_get_drvdata(dev);
- int n;
-
- n = sprintf(buf, "%02x%02x%02x%02x%02x\n",
- pcf->resume_reason[0],
- pcf->resume_reason[1],
- pcf->resume_reason[2],
- pcf->resume_reason[3],
- pcf->resume_reason[4]);
-
- return n;
-}
-static DEVICE_ATTR_ADMIN_RO(resume_reason);
-
-static struct attribute *pcf_sysfs_entries[] = {
- &dev_attr_dump_regs.attr,
- &dev_attr_resume_reason.attr,
- NULL,
-};
-
-static struct attribute_group pcf_attr_group = {
- .name = NULL, /* put in device directory */
- .attrs = pcf_sysfs_entries,
-};
-
-static void
-pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
- struct platform_device **pdev)
-{
- int ret;
-
- *pdev = platform_device_alloc(name, -1);
- if (!*pdev) {
- dev_err(pcf->dev, "Failed to allocate %s\n", name);
- return;
- }
-
- (*pdev)->dev.parent = pcf->dev;
-
- ret = platform_device_add(*pdev);
- if (ret) {
- dev_err(pcf->dev, "Failed to register %s: %d\n", name, ret);
- platform_device_put(*pdev);
- *pdev = NULL;
- }
-}
-
-static const struct regmap_config pcf50633_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
-};
-
-static int pcf50633_probe(struct i2c_client *client)
-{
- struct pcf50633 *pcf;
- struct platform_device *pdev;
- struct pcf50633_platform_data *pdata = dev_get_platdata(&client->dev);
- int i, j, ret;
- int version, variant;
-
- if (!client->irq) {
- dev_err(&client->dev, "Missing IRQ\n");
- return -ENOENT;
- }
-
- pcf = devm_kzalloc(&client->dev, sizeof(*pcf), GFP_KERNEL);
- if (!pcf)
- return -ENOMEM;
-
- i2c_set_clientdata(client, pcf);
- pcf->dev = &client->dev;
- pcf->pdata = pdata;
-
- mutex_init(&pcf->lock);
-
- pcf->regmap = devm_regmap_init_i2c(client, &pcf50633_regmap_config);
- if (IS_ERR(pcf->regmap)) {
- ret = PTR_ERR(pcf->regmap);
- dev_err(pcf->dev, "Failed to allocate register map: %d\n", ret);
- return ret;
- }
-
- version = pcf50633_reg_read(pcf, 0);
- variant = pcf50633_reg_read(pcf, 1);
- if (version < 0 || variant < 0) {
- dev_err(pcf->dev, "Unable to probe pcf50633\n");
- ret = -ENODEV;
- return ret;
- }
-
- dev_info(pcf->dev, "Probed device version %d variant %d\n",
- version, variant);
-
- pcf50633_irq_init(pcf, client->irq);
-
- /* Create sub devices */
- pcf50633_client_dev_register(pcf, "pcf50633-input", &pcf->input_pdev);
- pcf50633_client_dev_register(pcf, "pcf50633-rtc", &pcf->rtc_pdev);
- pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev);
- pcf50633_client_dev_register(pcf, "pcf50633-adc", &pcf->adc_pdev);
- pcf50633_client_dev_register(pcf, "pcf50633-backlight", &pcf->bl_pdev);
-
-
- for (i = 0; i < PCF50633_NUM_REGULATORS; i++) {
- pdev = platform_device_alloc("pcf50633-regulator", i);
- if (!pdev) {
- ret = -ENOMEM;
- goto err2;
- }
-
- pdev->dev.parent = pcf->dev;
- ret = platform_device_add_data(pdev, &pdata->reg_init_data[i],
- sizeof(pdata->reg_init_data[i]));
- if (ret)
- goto err;
-
- ret = platform_device_add(pdev);
- if (ret)
- goto err;
-
- pcf->regulator_pdev[i] = pdev;
- }
-
- ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group);
- if (ret)
- dev_warn(pcf->dev, "error creating sysfs entries\n");
-
- if (pdata->probe_done)
- pdata->probe_done(pcf);
-
- return 0;
-
-err:
- platform_device_put(pdev);
-err2:
- for (j = 0; j < i; j++)
- platform_device_put(pcf->regulator_pdev[j]);
-
- return ret;
-}
-
-static void pcf50633_remove(struct i2c_client *client)
-{
- struct pcf50633 *pcf = i2c_get_clientdata(client);
- int i;
-
- sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
- pcf50633_irq_free(pcf);
-
- platform_device_unregister(pcf->input_pdev);
- platform_device_unregister(pcf->rtc_pdev);
- platform_device_unregister(pcf->mbc_pdev);
- platform_device_unregister(pcf->adc_pdev);
- platform_device_unregister(pcf->bl_pdev);
-
- for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
- platform_device_unregister(pcf->regulator_pdev[i]);
-}
-
-static const struct i2c_device_id pcf50633_id_table[] = {
- {"pcf50633", 0x73},
- {/* end of list */}
-};
-MODULE_DEVICE_TABLE(i2c, pcf50633_id_table);
-
-static struct i2c_driver pcf50633_driver = {
- .driver = {
- .name = "pcf50633",
- .pm = pm_sleep_ptr(&pcf50633_pm),
- },
- .id_table = pcf50633_id_table,
- .probe = pcf50633_probe,
- .remove = pcf50633_remove,
-};
-
-static int __init pcf50633_init(void)
-{
- return i2c_add_driver(&pcf50633_driver);
-}
-
-static void __exit pcf50633_exit(void)
-{
- i2c_del_driver(&pcf50633_driver);
-}
-
-MODULE_DESCRIPTION("I2C chip driver for NXP PCF50633 PMU");
-MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>");
-MODULE_LICENSE("GPL");
-
-subsys_initcall(pcf50633_init);
-module_exit(pcf50633_exit);
diff --git a/drivers/mfd/pcf50633-gpio.c b/drivers/mfd/pcf50633-gpio.c
deleted file mode 100644
index 4d2b53b12eeb..000000000000
--- a/drivers/mfd/pcf50633-gpio.c
+++ /dev/null
@@ -1,91 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* NXP PCF50633 GPIO Driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- *
- * Broken down from monstrous PCF50633 driver mainly by
- * Harald Welte, Andy Green and Werner Almesberger
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-
-#include <linux/mfd/pcf50633/core.h>
-#include <linux/mfd/pcf50633/gpio.h>
-#include <linux/mfd/pcf50633/pmic.h>
-
-static const u8 pcf50633_regulator_registers[PCF50633_NUM_REGULATORS] = {
- [PCF50633_REGULATOR_AUTO] = PCF50633_REG_AUTOOUT,
- [PCF50633_REGULATOR_DOWN1] = PCF50633_REG_DOWN1OUT,
- [PCF50633_REGULATOR_DOWN2] = PCF50633_REG_DOWN2OUT,
- [PCF50633_REGULATOR_MEMLDO] = PCF50633_REG_MEMLDOOUT,
- [PCF50633_REGULATOR_LDO1] = PCF50633_REG_LDO1OUT,
- [PCF50633_REGULATOR_LDO2] = PCF50633_REG_LDO2OUT,
- [PCF50633_REGULATOR_LDO3] = PCF50633_REG_LDO3OUT,
- [PCF50633_REGULATOR_LDO4] = PCF50633_REG_LDO4OUT,
- [PCF50633_REGULATOR_LDO5] = PCF50633_REG_LDO5OUT,
- [PCF50633_REGULATOR_LDO6] = PCF50633_REG_LDO6OUT,
- [PCF50633_REGULATOR_HCLDO] = PCF50633_REG_HCLDOOUT,
-};
-
-int pcf50633_gpio_set(struct pcf50633 *pcf, int gpio, u8 val)
-{
- u8 reg;
-
- reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
-
- return pcf50633_reg_set_bit_mask(pcf, reg, 0x07, val);
-}
-EXPORT_SYMBOL_GPL(pcf50633_gpio_set);
-
-u8 pcf50633_gpio_get(struct pcf50633 *pcf, int gpio)
-{
- u8 reg, val;
-
- reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
- val = pcf50633_reg_read(pcf, reg) & 0x07;
-
- return val;
-}
-EXPORT_SYMBOL_GPL(pcf50633_gpio_get);
-
-int pcf50633_gpio_invert_set(struct pcf50633 *pcf, int gpio, int invert)
-{
- u8 val, reg;
-
- reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
- val = !!invert << 3;
-
- return pcf50633_reg_set_bit_mask(pcf, reg, 1 << 3, val);
-}
-EXPORT_SYMBOL_GPL(pcf50633_gpio_invert_set);
-
-int pcf50633_gpio_invert_get(struct pcf50633 *pcf, int gpio)
-{
- u8 reg, val;
-
- reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
- val = pcf50633_reg_read(pcf, reg);
-
- return val & (1 << 3);
-}
-EXPORT_SYMBOL_GPL(pcf50633_gpio_invert_get);
-
-int pcf50633_gpio_power_supply_set(struct pcf50633 *pcf,
- int gpio, int regulator, int on)
-{
- u8 reg, val, mask;
-
- /* the *ENA register is always one after the *OUT register */
- reg = pcf50633_regulator_registers[regulator] + 1;
-
- val = !!on << (gpio - PCF50633_GPIO1);
- mask = 1 << (gpio - PCF50633_GPIO1);
-
- return pcf50633_reg_set_bit_mask(pcf, reg, mask, val);
-}
-EXPORT_SYMBOL_GPL(pcf50633_gpio_power_supply_set);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/pcf50633-irq.c b/drivers/mfd/pcf50633-irq.c
deleted file mode 100644
index e85af7f1cb0b..000000000000
--- a/drivers/mfd/pcf50633-irq.c
+++ /dev/null
@@ -1,312 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* NXP PCF50633 Power Management Unit (PMU) driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Harald Welte <laforge@openmoko.org>
- * Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- */
-
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/mutex.h>
-#include <linux/export.h>
-#include <linux/slab.h>
-
-#include <linux/mfd/pcf50633/core.h>
-#include <linux/mfd/pcf50633/mbc.h>
-
-int pcf50633_register_irq(struct pcf50633 *pcf, int irq,
- void (*handler) (int, void *), void *data)
-{
- if (irq < 0 || irq >= PCF50633_NUM_IRQ || !handler)
- return -EINVAL;
-
- if (WARN_ON(pcf->irq_handler[irq].handler))
- return -EBUSY;
-
- mutex_lock(&pcf->lock);
- pcf->irq_handler[irq].handler = handler;
- pcf->irq_handler[irq].data = data;
- mutex_unlock(&pcf->lock);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(pcf50633_register_irq);
-
-int pcf50633_free_irq(struct pcf50633 *pcf, int irq)
-{
- if (irq < 0 || irq >= PCF50633_NUM_IRQ)
- return -EINVAL;
-
- mutex_lock(&pcf->lock);
- pcf->irq_handler[irq].handler = NULL;
- mutex_unlock(&pcf->lock);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(pcf50633_free_irq);
-
-static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask)
-{
- u8 reg, bit;
- int idx;
-
- idx = irq >> 3;
- reg = PCF50633_REG_INT1M + idx;
- bit = 1 << (irq & 0x07);
-
- pcf50633_reg_set_bit_mask(pcf, reg, bit, mask ? bit : 0);
-
- mutex_lock(&pcf->lock);
-
- if (mask)
- pcf->mask_regs[idx] |= bit;
- else
- pcf->mask_regs[idx] &= ~bit;
-
- mutex_unlock(&pcf->lock);
-
- return 0;
-}
-
-int pcf50633_irq_mask(struct pcf50633 *pcf, int irq)
-{
- dev_dbg(pcf->dev, "Masking IRQ %d\n", irq);
-
- return __pcf50633_irq_mask_set(pcf, irq, 1);
-}
-EXPORT_SYMBOL_GPL(pcf50633_irq_mask);
-
-int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq)
-{
- dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq);
-
- return __pcf50633_irq_mask_set(pcf, irq, 0);
-}
-EXPORT_SYMBOL_GPL(pcf50633_irq_unmask);
-
-int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq)
-{
- u8 reg, bits;
-
- reg = irq >> 3;
- bits = 1 << (irq & 0x07);
-
- return pcf->mask_regs[reg] & bits;
-}
-EXPORT_SYMBOL_GPL(pcf50633_irq_mask_get);
-
-static void pcf50633_irq_call_handler(struct pcf50633 *pcf, int irq)
-{
- if (pcf->irq_handler[irq].handler)
- pcf->irq_handler[irq].handler(irq, pcf->irq_handler[irq].data);
-}
-
-/* Maximum amount of time ONKEY is held before emergency action is taken */
-#define PCF50633_ONKEY1S_TIMEOUT 8
-
-static irqreturn_t pcf50633_irq(int irq, void *data)
-{
- struct pcf50633 *pcf = data;
- int ret, i, j;
- u8 pcf_int[5], chgstat;
-
- /* Read the 5 INT regs in one transaction */
- ret = pcf50633_read_block(pcf, PCF50633_REG_INT1,
- ARRAY_SIZE(pcf_int), pcf_int);
- if (ret != ARRAY_SIZE(pcf_int)) {
- dev_err(pcf->dev, "Error reading INT registers\n");
-
- /*
- * If this doesn't ACK the interrupt to the chip, we'll be
- * called once again as we're level triggered.
- */
- goto out;
- }
-
- /* defeat 8s death from lowsys on A5 */
- pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN, 0x04);
-
- /* We immediately read the usb and adapter status. We thus make sure
- * only of USBINS/USBREM IRQ handlers are called */
- if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) {
- chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
- if (chgstat & (0x3 << 4))
- pcf_int[0] &= ~PCF50633_INT1_USBREM;
- else
- pcf_int[0] &= ~PCF50633_INT1_USBINS;
- }
-
- /* Make sure only one of ADPINS or ADPREM is set */
- if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) {
- chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
- if (chgstat & (0x3 << 4))
- pcf_int[0] &= ~PCF50633_INT1_ADPREM;
- else
- pcf_int[0] &= ~PCF50633_INT1_ADPINS;
- }
-
- dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x "
- "INT4=0x%02x INT5=0x%02x\n", pcf_int[0],
- pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]);
-
- /* Some revisions of the chip don't have a 8s standby mode on
- * ONKEY1S press. We try to manually do it in such cases. */
- if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) {
- dev_info(pcf->dev, "ONKEY1S held for %d secs\n",
- pcf->onkey1s_held);
- if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT)
- if (pcf->pdata->force_shutdown)
- pcf->pdata->force_shutdown(pcf);
- }
-
- if (pcf_int[2] & PCF50633_INT3_ONKEY1S) {
- dev_info(pcf->dev, "ONKEY1S held\n");
- pcf->onkey1s_held = 1 ;
-
- /* Unmask IRQ_SECOND */
- pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M,
- PCF50633_INT1_SECOND);
-
- /* Unmask IRQ_ONKEYR */
- pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M,
- PCF50633_INT2_ONKEYR);
- }
-
- if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) {
- pcf->onkey1s_held = 0;
-
- /* Mask SECOND and ONKEYR interrupts */
- if (pcf->mask_regs[0] & PCF50633_INT1_SECOND)
- pcf50633_reg_set_bit_mask(pcf,
- PCF50633_REG_INT1M,
- PCF50633_INT1_SECOND,
- PCF50633_INT1_SECOND);
-
- if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR)
- pcf50633_reg_set_bit_mask(pcf,
- PCF50633_REG_INT2M,
- PCF50633_INT2_ONKEYR,
- PCF50633_INT2_ONKEYR);
- }
-
- /* Have we just resumed ? */
- if (pcf->is_suspended) {
- pcf->is_suspended = 0;
-
- /* Set the resume reason filtering out non resumers */
- for (i = 0; i < ARRAY_SIZE(pcf_int); i++)
- pcf->resume_reason[i] = pcf_int[i] &
- pcf->pdata->resumers[i];
-
- /* Make sure we don't pass on any ONKEY events to
- * userspace now */
- pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF);
- }
-
- for (i = 0; i < ARRAY_SIZE(pcf_int); i++) {
- /* Unset masked interrupts */
- pcf_int[i] &= ~pcf->mask_regs[i];
-
- for (j = 0; j < 8 ; j++)
- if (pcf_int[i] & (1 << j))
- pcf50633_irq_call_handler(pcf, (i * 8) + j);
- }
-
-out:
- return IRQ_HANDLED;
-}
-
-static int pcf50633_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct pcf50633 *pcf = i2c_get_clientdata(client);
- int ret;
- int i;
- u8 res[5];
-
-
- /* Make sure our interrupt handlers are not called
- * henceforth */
- disable_irq(pcf->irq);
-
- /* Save the masks */
- ret = pcf50633_read_block(pcf, PCF50633_REG_INT1M,
- ARRAY_SIZE(pcf->suspend_irq_masks),
- pcf->suspend_irq_masks);
- if (ret < 0) {
- dev_err(pcf->dev, "error saving irq masks\n");
- goto out;
- }
-
- /* Write wakeup irq masks */
- for (i = 0; i < ARRAY_SIZE(res); i++)
- res[i] = ~pcf->pdata->resumers[i];
-
- ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
- ARRAY_SIZE(res), &res[0]);
- if (ret < 0) {
- dev_err(pcf->dev, "error writing wakeup irq masks\n");
- goto out;
- }
-
- pcf->is_suspended = 1;
-
-out:
- return ret;
-}
-
-static int pcf50633_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct pcf50633 *pcf = i2c_get_clientdata(client);
- int ret;
-
- /* Write the saved mask registers */
- ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
- ARRAY_SIZE(pcf->suspend_irq_masks),
- pcf->suspend_irq_masks);
- if (ret < 0)
- dev_err(pcf->dev, "Error restoring saved suspend masks\n");
-
- enable_irq(pcf->irq);
-
- return ret;
-}
-
-EXPORT_GPL_SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume);
-
-int pcf50633_irq_init(struct pcf50633 *pcf, int irq)
-{
- int ret;
-
- pcf->irq = irq;
-
- /* Enable all interrupts except RTC SECOND */
- pcf->mask_regs[0] = 0x80;
- pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]);
- pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00);
- pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00);
- pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00);
- pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00);
-
- ret = request_threaded_irq(irq, NULL, pcf50633_irq,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- "pcf50633", pcf);
-
- if (ret)
- dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
-
- if (enable_irq_wake(irq) < 0)
- dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source"
- "in this hardware revision", irq);
-
- return ret;
-}
-
-void pcf50633_irq_free(struct pcf50633 *pcf)
-{
- free_irq(pcf->irq, pcf);
-}
diff --git a/drivers/mfd/pf1550.c b/drivers/mfd/pf1550.c
new file mode 100644
index 000000000000..c4f567c05564
--- /dev/null
+++ b/drivers/mfd/pf1550.c
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Core driver for the PF1550
+ *
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Robin Gong <yibin.gong@freescale.com>
+ *
+ * Portions Copyright (c) 2025 Savoir-faire Linux Inc.
+ * Samuel Kayode <samuel.kayode@savoirfairelinux.com>
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pf1550.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+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, &reg_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 <yibin.gong@freescale.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/qcom-pm8008.c b/drivers/mfd/qcom-pm8008.c
index 94a8cca1d955..60204cc9a2dc 100644
--- a/drivers/mfd/qcom-pm8008.c
+++ b/drivers/mfd/qcom-pm8008.c
@@ -4,19 +4,20 @@
*/
#include <linux/bitops.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
+#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
+#include <linux/mfd/core.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/regmap.h>
#include <linux/slab.h>
-#include <dt-bindings/mfd/qcom-pm8008.h>
-
#define I2C_INTR_STATUS_BASE 0x0550
#define INT_RT_STS_OFFSET 0x10
#define INT_SET_TYPE_OFFSET 0x11
@@ -37,34 +38,54 @@ enum {
#define PM8008_PERIPH_0_BASE 0x900
#define PM8008_PERIPH_1_BASE 0x2400
-#define PM8008_PERIPH_2_BASE 0xC000
-#define PM8008_PERIPH_3_BASE 0xC100
+#define PM8008_PERIPH_2_BASE 0xc000
+#define PM8008_PERIPH_3_BASE 0xc100
#define PM8008_TEMP_ALARM_ADDR PM8008_PERIPH_1_BASE
#define PM8008_GPIO1_ADDR PM8008_PERIPH_2_BASE
#define PM8008_GPIO2_ADDR PM8008_PERIPH_3_BASE
+/* PM8008 IRQ numbers */
+#define PM8008_IRQ_MISC_UVLO 0
+#define PM8008_IRQ_MISC_OVLO 1
+#define PM8008_IRQ_MISC_OTST2 2
+#define PM8008_IRQ_MISC_OTST3 3
+#define PM8008_IRQ_MISC_LDO_OCP 4
+#define PM8008_IRQ_TEMP_ALARM 5
+#define PM8008_IRQ_GPIO1 6
+#define PM8008_IRQ_GPIO2 7
+
enum {
SET_TYPE_INDEX,
POLARITY_HI_INDEX,
POLARITY_LO_INDEX,
};
-static unsigned int pm8008_config_regs[] = {
+static const unsigned int pm8008_config_regs[] = {
INT_SET_TYPE_OFFSET,
INT_POL_HIGH_OFFSET,
INT_POL_LOW_OFFSET,
};
-static struct regmap_irq pm8008_irqs[] = {
- REGMAP_IRQ_REG(PM8008_IRQ_MISC_UVLO, PM8008_MISC, BIT(0)),
- REGMAP_IRQ_REG(PM8008_IRQ_MISC_OVLO, PM8008_MISC, BIT(1)),
- REGMAP_IRQ_REG(PM8008_IRQ_MISC_OTST2, PM8008_MISC, BIT(2)),
- REGMAP_IRQ_REG(PM8008_IRQ_MISC_OTST3, PM8008_MISC, BIT(3)),
- REGMAP_IRQ_REG(PM8008_IRQ_MISC_LDO_OCP, PM8008_MISC, BIT(4)),
- REGMAP_IRQ_REG(PM8008_IRQ_TEMP_ALARM, PM8008_TEMP_ALARM, BIT(0)),
- REGMAP_IRQ_REG(PM8008_IRQ_GPIO1, PM8008_GPIO1, BIT(0)),
- REGMAP_IRQ_REG(PM8008_IRQ_GPIO2, PM8008_GPIO2, BIT(0)),
+#define _IRQ(_irq, _off, _mask, _types) \
+ [_irq] = { \
+ .reg_offset = (_off), \
+ .mask = (_mask), \
+ .type = { \
+ .type_reg_offset = (_off), \
+ .types_supported = (_types), \
+ }, \
+ }
+
+static const struct regmap_irq pm8008_irqs[] = {
+ _IRQ(PM8008_IRQ_MISC_UVLO, PM8008_MISC, BIT(0), IRQ_TYPE_EDGE_RISING),
+ _IRQ(PM8008_IRQ_MISC_OVLO, PM8008_MISC, BIT(1), IRQ_TYPE_EDGE_RISING),
+ _IRQ(PM8008_IRQ_MISC_OTST2, PM8008_MISC, BIT(2), IRQ_TYPE_EDGE_RISING),
+ _IRQ(PM8008_IRQ_MISC_OTST3, PM8008_MISC, BIT(3), IRQ_TYPE_EDGE_RISING),
+ _IRQ(PM8008_IRQ_MISC_LDO_OCP, PM8008_MISC, BIT(4), IRQ_TYPE_EDGE_RISING),
+ _IRQ(PM8008_IRQ_TEMP_ALARM, PM8008_TEMP_ALARM,BIT(0), IRQ_TYPE_SENSE_MASK),
+ _IRQ(PM8008_IRQ_GPIO1, PM8008_GPIO1, BIT(0), IRQ_TYPE_SENSE_MASK),
+ _IRQ(PM8008_IRQ_GPIO2, PM8008_GPIO2, BIT(0), IRQ_TYPE_SENSE_MASK),
};
static const unsigned int pm8008_periph_base[] = {
@@ -118,8 +139,8 @@ static int pm8008_set_type_config(unsigned int **buf, unsigned int type,
return 0;
}
-static struct regmap_irq_chip pm8008_irq_chip = {
- .name = "pm8008_irq",
+static const struct regmap_irq_chip pm8008_irq_chip = {
+ .name = "pm8008",
.main_status = I2C_INTR_STATUS_BASE,
.num_main_regs = 1,
.irqs = pm8008_irqs,
@@ -137,62 +158,106 @@ static struct regmap_irq_chip pm8008_irq_chip = {
.get_irq_reg = pm8008_get_irq_reg,
};
-static struct regmap_config qcom_mfd_regmap_cfg = {
+static const struct regmap_config qcom_mfd_regmap_cfg = {
+ .name = "primary",
.reg_bits = 16,
.val_bits = 8,
- .max_register = 0xFFFF,
+ .max_register = 0xffff,
};
-static int pm8008_probe_irq_peripherals(struct device *dev,
- struct regmap *regmap,
- int client_irq)
-{
- int rc, i;
- struct regmap_irq_type *type;
- struct regmap_irq_chip_data *irq_data;
-
- for (i = 0; i < ARRAY_SIZE(pm8008_irqs); i++) {
- type = &pm8008_irqs[i].type;
+static const struct regmap_config pm8008_regmap_cfg_2 = {
+ .name = "secondary",
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0xffff,
+};
- type->type_reg_offset = pm8008_irqs[i].reg_offset;
+static const struct resource pm8008_temp_res[] = {
+ DEFINE_RES_MEM(PM8008_TEMP_ALARM_ADDR, 0x100),
+ DEFINE_RES_IRQ(PM8008_IRQ_TEMP_ALARM),
+};
- if (type->type_reg_offset == PM8008_MISC)
- type->types_supported = IRQ_TYPE_EDGE_RISING;
- else
- type->types_supported = (IRQ_TYPE_EDGE_BOTH |
- IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW);
- }
+static const struct mfd_cell pm8008_cells[] = {
+ MFD_CELL_NAME("pm8008-regulator"),
+ MFD_CELL_RES("qpnp-temp-alarm", pm8008_temp_res),
+ MFD_CELL_NAME("pm8008-gpio"),
+};
- rc = devm_regmap_add_irq_chip(dev, regmap, client_irq,
- IRQF_SHARED, 0, &pm8008_irq_chip, &irq_data);
- if (rc) {
- dev_err(dev, "Failed to add IRQ chip: %d\n", rc);
- return rc;
- }
+static void devm_irq_domain_fwnode_release(void *data)
+{
+ struct fwnode_handle *fwnode = data;
- return 0;
+ irq_domain_free_fwnode(fwnode);
}
static int pm8008_probe(struct i2c_client *client)
{
- int rc;
- struct device *dev;
- struct regmap *regmap;
+ struct regmap_irq_chip_data *irq_data;
+ struct device *dev = &client->dev;
+ struct regmap *regmap, *regmap2;
+ struct fwnode_handle *fwnode;
+ struct i2c_client *dummy;
+ struct gpio_desc *reset;
+ char *name;
+ int ret;
+
+ dummy = devm_i2c_new_dummy_device(dev, client->adapter, client->addr + 1);
+ if (IS_ERR(dummy)) {
+ ret = PTR_ERR(dummy);
+ dev_err(dev, "failed to claim second address: %d\n", ret);
+ return ret;
+ }
+
+ regmap2 = devm_regmap_init_i2c(dummy, &qcom_mfd_regmap_cfg);
+ if (IS_ERR(regmap2))
+ return PTR_ERR(regmap2);
+
+ ret = regmap_attach_dev(dev, regmap2, &pm8008_regmap_cfg_2);
+ if (ret)
+ return ret;
- dev = &client->dev;
+ /* Default regmap must be attached last. */
regmap = devm_regmap_init_i2c(client, &qcom_mfd_regmap_cfg);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- i2c_set_clientdata(client, regmap);
+ reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(reset))
+ return PTR_ERR(reset);
- if (of_property_read_bool(dev->of_node, "interrupt-controller")) {
- rc = pm8008_probe_irq_peripherals(dev, regmap, client->irq);
- if (rc)
- dev_err(dev, "Failed to probe irq periphs: %d\n", rc);
+ /*
+ * The PMIC does not appear to require a post-reset delay, but wait
+ * for a millisecond for now anyway.
+ */
+ usleep_range(1000, 2000);
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "%pOF-internal", dev->of_node);
+ if (!name)
+ return -ENOMEM;
+
+ name = strreplace(name, '/', ':');
+
+ fwnode = irq_domain_alloc_named_fwnode(name);
+ if (!fwnode)
+ return -ENOMEM;
+
+ ret = devm_add_action_or_reset(dev, devm_irq_domain_fwnode_release, fwnode);
+ if (ret)
+ return ret;
+
+ ret = devm_regmap_add_irq_chip_fwnode(dev, fwnode, regmap, client->irq,
+ IRQF_SHARED, 0, &pm8008_irq_chip, &irq_data);
+ if (ret) {
+ dev_err(dev, "failed to add IRQ chip: %d\n", ret);
+ return ret;
}
- return devm_of_platform_populate(dev);
+ /* Needed by GPIO driver. */
+ dev_set_drvdata(dev, regmap_irq_get_domain(irq_data));
+
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, pm8008_cells,
+ ARRAY_SIZE(pm8008_cells), NULL, 0,
+ regmap_irq_get_domain(irq_data));
}
static const struct of_device_id pm8008_match[] = {
@@ -210,4 +275,5 @@ static struct i2c_driver pm8008_mfd_driver = {
};
module_i2c_driver(pm8008_mfd_driver);
+MODULE_DESCRIPTION("QCOM PM8008 Power Management IC driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c
index 9a948df8c28d..1149f7102a36 100644
--- a/drivers/mfd/qcom-pm8xxx.c
+++ b/drivers/mfd/qcom-pm8xxx.c
@@ -103,8 +103,9 @@ static int
pm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp)
{
int rc;
+ unsigned long flags;
- spin_lock(&chip->pm_irq_lock);
+ spin_lock_irqsave(&chip->pm_irq_lock, flags);
rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
if (rc) {
pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
@@ -116,7 +117,7 @@ pm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp)
if (rc)
pr_err("Failed Configuring IRQ rc=%d\n", rc);
bail:
- spin_unlock(&chip->pm_irq_lock);
+ spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
return rc;
}
@@ -321,6 +322,7 @@ static int pm8xxx_irq_get_irqchip_state(struct irq_data *d,
struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
unsigned int pmirq = irqd_to_hwirq(d);
unsigned int bits;
+ unsigned long flags;
int irq_bit;
u8 block;
int rc;
@@ -331,7 +333,7 @@ static int pm8xxx_irq_get_irqchip_state(struct irq_data *d,
block = pmirq / 8;
irq_bit = pmirq % 8;
- spin_lock(&chip->pm_irq_lock);
+ spin_lock_irqsave(&chip->pm_irq_lock, flags);
rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
if (rc) {
pr_err("Failed Selecting Block %d rc=%d\n", block, rc);
@@ -346,7 +348,7 @@ static int pm8xxx_irq_get_irqchip_state(struct irq_data *d,
*state = !!(bits & BIT(irq_bit));
bail:
- spin_unlock(&chip->pm_irq_lock);
+ spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
return rc;
}
@@ -557,10 +559,8 @@ static int pm8xxx_probe(struct platform_device *pdev)
chip->pm_irq_data = data;
spin_lock_init(&chip->pm_irq_lock);
- chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node,
- data->num_irqs,
- &pm8xxx_irq_domain_ops,
- chip);
+ chip->irqdomain = irq_domain_create_linear(dev_fwnode(&pdev->dev), data->num_irqs,
+ &pm8xxx_irq_domain_ops, chip);
if (!chip->irqdomain)
return -ENODEV;
@@ -583,14 +583,12 @@ static int pm8xxx_remove_child(struct device *dev, void *unused)
return 0;
}
-static int pm8xxx_remove(struct platform_device *pdev)
+static void pm8xxx_remove(struct platform_device *pdev)
{
struct pm_irq_chip *chip = platform_get_drvdata(pdev);
device_for_each_child(&pdev->dev, NULL, pm8xxx_remove_child);
irq_domain_remove(chip->irqdomain);
-
- return 0;
}
static struct platform_driver pm8xxx_driver = {
diff --git a/drivers/mfd/qcom-spmi-pmic.c b/drivers/mfd/qcom-spmi-pmic.c
index 7e2cd79d17eb..b4b178caf754 100644
--- a/drivers/mfd/qcom-spmi-pmic.c
+++ b/drivers/mfd/qcom-spmi-pmic.c
@@ -8,10 +8,12 @@
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
#include <linux/spmi.h>
#include <linux/types.h>
#include <linux/regmap.h>
-#include <linux/of_platform.h>
#include <soc/qcom/qcom-spmi-pmic.h>
#define PMIC_REV2 0x101
@@ -30,6 +32,8 @@ struct qcom_spmi_dev {
struct qcom_spmi_pmic pmic;
};
+static DEFINE_MUTEX(pmic_spmi_revid_lock);
+
#define N_USIDS(n) ((void *)n)
static const struct of_device_id pmic_spmi_id_table[] = {
@@ -49,6 +53,7 @@ static const struct of_device_id pmic_spmi_id_table[] = {
{ .compatible = "qcom,pm8901", .data = N_USIDS(2) },
{ .compatible = "qcom,pm8909", .data = N_USIDS(2) },
{ .compatible = "qcom,pm8916", .data = N_USIDS(2) },
+ { .compatible = "qcom,pm8937", .data = N_USIDS(2) },
{ .compatible = "qcom,pm8941", .data = N_USIDS(2) },
{ .compatible = "qcom,pm8950", .data = N_USIDS(2) },
{ .compatible = "qcom,pm8994", .data = N_USIDS(2) },
@@ -76,24 +81,20 @@ static const struct of_device_id pmic_spmi_id_table[] = {
*
* This only supports PMICs with 1 or 2 USIDs.
*/
-static struct spmi_device *qcom_pmic_get_base_usid(struct device *dev)
+static struct spmi_device *qcom_pmic_get_base_usid(struct spmi_device *sdev, struct qcom_spmi_dev *ctx)
{
- struct spmi_device *sdev;
- struct qcom_spmi_dev *ctx;
struct device_node *spmi_bus;
- struct device_node *other_usid = NULL;
int function_parent_usid, ret;
u32 pmic_addr;
- sdev = to_spmi_device(dev);
- ctx = dev_get_drvdata(&sdev->dev);
-
/*
* Quick return if the function device is already in the base
* USID. This will always be hit for PMICs with only 1 USID.
*/
- if (sdev->usid % ctx->num_usids == 0)
+ if (sdev->usid % ctx->num_usids == 0) {
+ get_device(&sdev->dev);
return sdev;
+ }
function_parent_usid = sdev->usid;
@@ -105,28 +106,59 @@ static struct spmi_device *qcom_pmic_get_base_usid(struct device *dev)
* device for USID 2.
*/
spmi_bus = of_get_parent(sdev->dev.of_node);
- do {
- other_usid = of_get_next_child(spmi_bus, other_usid);
-
- ret = of_property_read_u32_index(other_usid, "reg", 0, &pmic_addr);
- if (ret)
- return ERR_PTR(ret);
+ sdev = ERR_PTR(-ENODATA);
+ for_each_child_of_node_scoped(spmi_bus, child) {
+ ret = of_property_read_u32_index(child, "reg", 0, &pmic_addr);
+ if (ret) {
+ sdev = ERR_PTR(ret);
+ break;
+ }
- sdev = spmi_device_from_of(other_usid);
if (pmic_addr == function_parent_usid - (ctx->num_usids - 1)) {
- if (!sdev)
+ sdev = spmi_find_device_by_of_node(child);
+ if (!sdev) {
/*
- * If the base USID for this PMIC hasn't probed yet
- * but the secondary USID has, then we need to defer
- * the function driver so that it will attempt to
- * probe again when the base USID is ready.
+ * If the base USID for this PMIC hasn't been
+ * registered yet then we need to defer.
*/
- return ERR_PTR(-EPROBE_DEFER);
- return sdev;
+ sdev = ERR_PTR(-EPROBE_DEFER);
+ }
+ break;
}
- } while (other_usid->sibling);
+ }
- return ERR_PTR(-ENODATA);
+ of_node_put(spmi_bus);
+
+ return sdev;
+}
+
+static int pmic_spmi_get_base_revid(struct spmi_device *sdev, struct qcom_spmi_dev *ctx)
+{
+ struct qcom_spmi_dev *base_ctx;
+ struct spmi_device *base;
+ int ret = 0;
+
+ base = qcom_pmic_get_base_usid(sdev, ctx);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ /*
+ * Copy revid info from base device if it has probed and is still
+ * bound to its driver.
+ */
+ mutex_lock(&pmic_spmi_revid_lock);
+ base_ctx = spmi_device_get_drvdata(base);
+ if (!base_ctx) {
+ ret = -EPROBE_DEFER;
+ goto out_unlock;
+ }
+ memcpy(&ctx->pmic, &base_ctx->pmic, sizeof(ctx->pmic));
+out_unlock:
+ mutex_unlock(&pmic_spmi_revid_lock);
+
+ put_device(&base->dev);
+
+ return ret;
}
static int pmic_spmi_load_revid(struct regmap *map, struct device *dev,
@@ -204,16 +236,12 @@ const struct qcom_spmi_pmic *qcom_pmic_get(struct device *dev)
if (!of_match_device(pmic_spmi_id_table, dev->parent))
return ERR_PTR(-EINVAL);
- sdev = qcom_pmic_get_base_usid(dev->parent);
-
- if (IS_ERR(sdev))
- return ERR_CAST(sdev);
-
+ sdev = to_spmi_device(dev->parent);
spmi = dev_get_drvdata(&sdev->dev);
return &spmi->pmic;
}
-EXPORT_SYMBOL(qcom_pmic_get);
+EXPORT_SYMBOL_GPL(qcom_pmic_get);
static const struct regmap_config spmi_regmap_config = {
.reg_bits = 16,
@@ -236,23 +264,38 @@ static int pmic_spmi_probe(struct spmi_device *sdev)
if (!ctx)
return -ENOMEM;
- ctx->num_usids = (uintptr_t)of_device_get_match_data(&sdev->dev);
+ ctx->num_usids = (uintptr_t)device_get_match_data(&sdev->dev);
/* Only the first slave id for a PMIC contains this information */
if (sdev->usid % ctx->num_usids == 0) {
ret = pmic_spmi_load_revid(regmap, &sdev->dev, &ctx->pmic);
if (ret < 0)
return ret;
+ } else {
+ ret = pmic_spmi_get_base_revid(sdev, ctx);
+ if (ret)
+ return ret;
}
+
+ mutex_lock(&pmic_spmi_revid_lock);
spmi_device_set_drvdata(sdev, ctx);
+ mutex_unlock(&pmic_spmi_revid_lock);
return devm_of_platform_populate(&sdev->dev);
}
+static void pmic_spmi_remove(struct spmi_device *sdev)
+{
+ mutex_lock(&pmic_spmi_revid_lock);
+ spmi_device_set_drvdata(sdev, NULL);
+ mutex_unlock(&pmic_spmi_revid_lock);
+}
+
MODULE_DEVICE_TABLE(of, pmic_spmi_id_table);
static struct spmi_driver pmic_spmi_driver = {
.probe = pmic_spmi_probe,
+ .remove = pmic_spmi_remove,
.driver = {
.name = "pmic-spmi",
.of_match_table = pmic_spmi_id_table,
diff --git a/drivers/mfd/qcom_rpm.c b/drivers/mfd/qcom_rpm.c
index 086611322874..27446f43e3f3 100644
--- a/drivers/mfd/qcom_rpm.c
+++ b/drivers/mfd/qcom_rpm.c
@@ -7,6 +7,8 @@
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/io.h>
#include <linux/interrupt.h>
@@ -528,7 +530,6 @@ static irqreturn_t qcom_rpm_wakeup_interrupt(int irq, void *dev)
static int qcom_rpm_probe(struct platform_device *pdev)
{
- const struct of_device_id *match;
struct device_node *syscon_np;
struct qcom_rpm *rpm;
u32 fw_version[3];
@@ -570,10 +571,9 @@ static int qcom_rpm_probe(struct platform_device *pdev)
if (irq_wakeup < 0)
return irq_wakeup;
- match = of_match_device(qcom_rpm_of_match, &pdev->dev);
- if (!match)
+ rpm->data = device_get_match_data(&pdev->dev);
+ if (!rpm->data)
return -ENODEV;
- rpm->data = match->data;
rpm->status_regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(rpm->status_regs))
diff --git a/drivers/mfd/qnap-mcu.c b/drivers/mfd/qnap-mcu.c
new file mode 100644
index 000000000000..f81c69f22254
--- /dev/null
+++ b/drivers/mfd/qnap-mcu.c
@@ -0,0 +1,417 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Core driver for the microcontroller unit in QNAP NAS devices that is
+ * connected via a dedicated UART port.
+ *
+ * Copyright (C) 2024 Heiko Stuebner <heiko@sntech.de>
+ */
+
+#include <linux/cleanup.h>
+#include <linux/export.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/qnap-mcu.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/reboot.h>
+#include <linux/serdev.h>
+#include <linux/slab.h>
+
+/* The longest command found so far is 5 bytes long */
+#define QNAP_MCU_MAX_CMD_SIZE 5
+#define QNAP_MCU_MAX_DATA_SIZE 36
+#define QNAP_MCU_ERROR_SIZE 2
+#define QNAP_MCU_CHECKSUM_SIZE 1
+
+#define QNAP_MCU_RX_BUFFER_SIZE \
+ (QNAP_MCU_MAX_DATA_SIZE + QNAP_MCU_CHECKSUM_SIZE)
+
+#define QNAP_MCU_TX_BUFFER_SIZE \
+ (QNAP_MCU_MAX_CMD_SIZE + QNAP_MCU_CHECKSUM_SIZE)
+
+#define QNAP_MCU_ACK_LEN 2
+#define QNAP_MCU_VERSION_LEN 4
+
+#define QNAP_MCU_TIMEOUT_MS 500
+
+/**
+ * struct qnap_mcu_reply - Reply to a command
+ *
+ * @data: Buffer to store reply payload in
+ * @length: Expected reply length, including the checksum
+ * @received: Received number of bytes, so far
+ * @done: Triggered when the entire reply has been received
+ */
+struct qnap_mcu_reply {
+ u8 *data;
+ size_t length;
+ size_t received;
+ struct completion done;
+};
+
+/**
+ * struct qnap_mcu - QNAP NAS embedded controller
+ *
+ * @serdev: Pointer to underlying serdev
+ * @bus_lock: Lock to serialize access to the device
+ * @reply: Reply data structure
+ * @variant: Device variant specific information
+ * @version: MCU firmware version
+ */
+struct qnap_mcu {
+ struct serdev_device *serdev;
+ struct mutex bus_lock;
+ struct qnap_mcu_reply reply;
+ const struct qnap_mcu_variant *variant;
+ u8 version[QNAP_MCU_VERSION_LEN];
+};
+
+/*
+ * The QNAP-MCU uses a basic XOR checksum.
+ * It is always the last byte and XORs the whole previous message.
+ */
+static u8 qnap_mcu_csum(const u8 *buf, size_t size)
+{
+ u8 csum = 0;
+
+ while (size--)
+ csum ^= *buf++;
+
+ return csum;
+}
+
+static bool qnap_mcu_verify_checksum(const u8 *buf, size_t size)
+{
+ u8 crc = qnap_mcu_csum(buf, size - QNAP_MCU_CHECKSUM_SIZE);
+
+ return crc == buf[size - QNAP_MCU_CHECKSUM_SIZE];
+}
+
+static int qnap_mcu_write(struct qnap_mcu *mcu, const u8 *data, u8 data_size)
+{
+ unsigned char tx[QNAP_MCU_TX_BUFFER_SIZE];
+ size_t length = data_size + QNAP_MCU_CHECKSUM_SIZE;
+
+ if (length > sizeof(tx)) {
+ dev_err(&mcu->serdev->dev, "data too big for transmit buffer");
+ return -EINVAL;
+ }
+
+ memcpy(tx, data, data_size);
+ tx[data_size] = qnap_mcu_csum(data, data_size);
+
+ serdev_device_write_flush(mcu->serdev);
+
+ return serdev_device_write(mcu->serdev, tx, length, HZ);
+}
+
+static bool qnap_mcu_is_error_msg(size_t size)
+{
+ return (size == QNAP_MCU_ERROR_SIZE + QNAP_MCU_CHECKSUM_SIZE);
+}
+
+static bool qnap_mcu_reply_is_generic_error(unsigned char *buf, size_t size)
+{
+ if (!qnap_mcu_is_error_msg(size))
+ return false;
+
+ if (buf[0] == '@' && buf[1] == '9')
+ return true;
+
+ return false;
+}
+
+static bool qnap_mcu_reply_is_checksum_error(unsigned char *buf, size_t size)
+{
+ if (!qnap_mcu_is_error_msg(size))
+ return false;
+
+ if (buf[0] == '@' && buf[1] == '8')
+ return true;
+
+ return false;
+}
+
+static bool qnap_mcu_reply_is_any_error(struct qnap_mcu *mcu, unsigned char *buf, size_t size)
+{
+ if (qnap_mcu_reply_is_generic_error(buf, size)) {
+ dev_err(&mcu->serdev->dev, "Controller sent generic error response\n");
+ return true;
+ }
+
+ if (qnap_mcu_reply_is_checksum_error(buf, size)) {
+ dev_err(&mcu->serdev->dev, "Controller received invalid checksum for the command\n");
+ return true;
+ }
+
+ return false;
+}
+
+static size_t qnap_mcu_receive_buf(struct serdev_device *serdev, const u8 *buf, size_t size)
+{
+ struct device *dev = &serdev->dev;
+ struct qnap_mcu *mcu = dev_get_drvdata(dev);
+ struct qnap_mcu_reply *reply = &mcu->reply;
+ const u8 *src = buf;
+ const u8 *end = buf + size;
+
+ if (!reply->length) {
+ dev_warn(dev, "Received %zu bytes, we were not waiting for\n", size);
+ return size;
+ }
+
+ while (src < end) {
+ reply->data[reply->received] = *src++;
+ reply->received++;
+
+ if (reply->received == reply->length) {
+ /* We don't expect any characters from the device now */
+ reply->length = 0;
+
+ complete(&reply->done);
+
+ /*
+ * We report the consumed number of bytes. If there
+ * are still bytes remaining (though there shouldn't)
+ * the serdev layer will re-execute this handler with
+ * the remainder of the Rx bytes.
+ */
+ return src - buf;
+ }
+ }
+
+ /*
+ * We received everything the uart had to offer for now.
+ * This could mean that either the uart will send more in a 2nd
+ * receive run, or that the MCU cut the reply short because it
+ * sent an error code instead of the expected reply.
+ *
+ * So check if the received data has the correct size for an error
+ * reply and if it matches, is an actual error code.
+ */
+ if (qnap_mcu_is_error_msg(reply->received) &&
+ qnap_mcu_verify_checksum(reply->data, reply->received) &&
+ qnap_mcu_reply_is_any_error(mcu, reply->data, reply->received)) {
+ /* The reply was an error code, we're done */
+ reply->length = 0;
+
+ complete(&reply->done);
+ }
+
+ /*
+ * The only way to get out of the above loop and end up here
+ * is through consuming all of the supplied data, so here we
+ * report that we processed it all.
+ */
+ return size;
+}
+
+static const struct serdev_device_ops qnap_mcu_serdev_device_ops = {
+ .receive_buf = qnap_mcu_receive_buf,
+ .write_wakeup = serdev_device_write_wakeup,
+};
+
+int qnap_mcu_exec(struct qnap_mcu *mcu,
+ const u8 *cmd_data, size_t cmd_data_size,
+ u8 *reply_data, size_t reply_data_size)
+{
+ unsigned char rx[QNAP_MCU_RX_BUFFER_SIZE];
+ size_t length = reply_data_size + QNAP_MCU_CHECKSUM_SIZE;
+ struct qnap_mcu_reply *reply = &mcu->reply;
+ int ret = 0;
+
+ if (length > sizeof(rx)) {
+ dev_err(&mcu->serdev->dev, "expected data too big for receive buffer");
+ return -EINVAL;
+ }
+
+ guard(mutex)(&mcu->bus_lock);
+
+ reply->data = rx;
+ reply->length = length;
+ reply->received = 0;
+ reinit_completion(&reply->done);
+
+ ret = qnap_mcu_write(mcu, cmd_data, cmd_data_size);
+ if (ret < 0)
+ return ret;
+
+ serdev_device_wait_until_sent(mcu->serdev, msecs_to_jiffies(QNAP_MCU_TIMEOUT_MS));
+
+ if (!wait_for_completion_timeout(&reply->done, msecs_to_jiffies(QNAP_MCU_TIMEOUT_MS))) {
+ dev_err(&mcu->serdev->dev, "Command timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ if (!qnap_mcu_verify_checksum(rx, reply->received)) {
+ dev_err(&mcu->serdev->dev, "Invalid Checksum received from controller\n");
+ return -EPROTO;
+ }
+
+ if (qnap_mcu_reply_is_any_error(mcu, rx, reply->received))
+ return -EPROTO;
+
+ memcpy(reply_data, rx, reply_data_size);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qnap_mcu_exec);
+
+int qnap_mcu_exec_with_ack(struct qnap_mcu *mcu,
+ const u8 *cmd_data, size_t cmd_data_size)
+{
+ u8 ack[QNAP_MCU_ACK_LEN];
+ int ret;
+
+ ret = qnap_mcu_exec(mcu, cmd_data, cmd_data_size, ack, sizeof(ack));
+ if (ret)
+ return ret;
+
+ /* Should return @0 */
+ if (ack[0] != '@' || ack[1] != '0') {
+ dev_err(&mcu->serdev->dev, "Did not receive ack\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qnap_mcu_exec_with_ack);
+
+static int qnap_mcu_get_version(struct qnap_mcu *mcu)
+{
+ const u8 cmd[] = { '%', 'V' };
+ u8 rx[14];
+ int ret;
+
+ /* Reply is the 2 command-bytes + 4 bytes describing the version */
+ ret = qnap_mcu_exec(mcu, cmd, sizeof(cmd), rx, QNAP_MCU_VERSION_LEN + 2);
+ if (ret)
+ return ret;
+
+ memcpy(mcu->version, &rx[2], QNAP_MCU_VERSION_LEN);
+
+ return 0;
+}
+
+/*
+ * The MCU controls power to the peripherals but not the CPU.
+ *
+ * So using the PMIC to power off the system keeps the MCU and hard-drives
+ * running. This also then prevents the system from turning back on until
+ * the MCU is turned off by unplugging the power cable.
+ * Turning off the MCU alone on the other hand turns off the hard drives,
+ * LEDs, etc while the main SoC stays running - including its network ports.
+ */
+static int qnap_mcu_power_off(struct sys_off_data *data)
+{
+ const u8 cmd[] = { '@', 'C', '0' };
+ struct qnap_mcu *mcu = data->cb_data;
+ int ret;
+
+ ret = qnap_mcu_exec_with_ack(mcu, cmd, sizeof(cmd));
+ if (ret) {
+ dev_err(&mcu->serdev->dev, "MCU poweroff failed %d\n", ret);
+ return NOTIFY_STOP;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static const struct qnap_mcu_variant qnap_ts233_mcu = {
+ .baud_rate = 115200,
+ .num_drives = 2,
+ .fan_pwm_min = 51, /* Specified in original model.conf */
+ .fan_pwm_max = 255,
+ .usb_led = true,
+};
+
+static const struct qnap_mcu_variant qnap_ts433_mcu = {
+ .baud_rate = 115200,
+ .num_drives = 4,
+ .fan_pwm_min = 51, /* Specified in original model.conf */
+ .fan_pwm_max = 255,
+ .usb_led = true,
+};
+
+static struct mfd_cell qnap_mcu_cells[] = {
+ { .name = "qnap-mcu-eeprom", },
+ { .name = "qnap-mcu-input", },
+ { .name = "qnap-mcu-leds", },
+ { .name = "qnap-mcu-hwmon", }
+};
+
+static int qnap_mcu_probe(struct serdev_device *serdev)
+{
+ struct device *dev = &serdev->dev;
+ struct qnap_mcu *mcu;
+ int ret;
+
+ mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL);
+ if (!mcu)
+ return -ENOMEM;
+
+ mcu->serdev = serdev;
+ dev_set_drvdata(dev, mcu);
+
+ mcu->variant = of_device_get_match_data(dev);
+ if (!mcu->variant)
+ return -ENODEV;
+
+ mutex_init(&mcu->bus_lock);
+ init_completion(&mcu->reply.done);
+
+ serdev_device_set_client_ops(serdev, &qnap_mcu_serdev_device_ops);
+ ret = devm_serdev_device_open(dev, serdev);
+ if (ret)
+ return ret;
+
+ serdev_device_set_baudrate(serdev, mcu->variant->baud_rate);
+ serdev_device_set_flow_control(serdev, false);
+
+ ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set parity\n");
+
+ ret = qnap_mcu_get_version(mcu);
+ if (ret)
+ return ret;
+
+ ret = devm_register_sys_off_handler(dev,
+ SYS_OFF_MODE_POWER_OFF_PREPARE,
+ SYS_OFF_PRIO_DEFAULT,
+ &qnap_mcu_power_off, mcu);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to register poweroff handler\n");
+
+ for (int i = 0; i < ARRAY_SIZE(qnap_mcu_cells); i++) {
+ qnap_mcu_cells[i].platform_data = mcu->variant;
+ qnap_mcu_cells[i].pdata_size = sizeof(*mcu->variant);
+ }
+
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, qnap_mcu_cells,
+ ARRAY_SIZE(qnap_mcu_cells), NULL, 0, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add child devices\n");
+
+ return 0;
+}
+
+static const struct of_device_id qnap_mcu_dt_ids[] = {
+ { .compatible = "qnap,ts233-mcu", .data = &qnap_ts233_mcu },
+ { .compatible = "qnap,ts433-mcu", .data = &qnap_ts433_mcu },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, qnap_mcu_dt_ids);
+
+static struct serdev_device_driver qnap_mcu_drv = {
+ .probe = qnap_mcu_probe,
+ .driver = {
+ .name = "qnap-mcu",
+ .of_match_table = qnap_mcu_dt_ids,
+ },
+};
+module_serdev_device_driver(qnap_mcu_drv);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("QNAP MCU core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c
index 545196c85b5c..c1b78d127a26 100644
--- a/drivers/mfd/rave-sp.c
+++ b/drivers/mfd/rave-sp.c
@@ -9,7 +9,7 @@
*/
#include <linux/atomic.h>
-#include <linux/crc-ccitt.h>
+#include <linux/crc-itu-t.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/init.h>
@@ -18,10 +18,10 @@
#include <linux/mfd/rave-sp.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/of_platform.h>
#include <linux/sched.h>
#include <linux/serdev.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
/*
* UART protocol using following entities:
@@ -251,7 +251,7 @@ static void csum_8b2c(const u8 *buf, size_t size, u8 *crc)
static void csum_ccitt(const u8 *buf, size_t size, u8 *crc)
{
- const u16 calculated = crc_ccitt_false(0xffff, buf, size);
+ const u16 calculated = crc_itu_t(0xffff, buf, size);
/*
* While the rest of the wire protocol is little-endian,
@@ -358,7 +358,7 @@ int rave_sp_exec(struct rave_sp *sp,
ackid = atomic_inc_return(&sp->ackid);
reply.ackid = ackid;
- reply.code = rave_sp_reply_code((u8)command),
+ reply.code = rave_sp_reply_code((u8)command);
mutex_lock(&sp->bus_lock);
@@ -471,17 +471,17 @@ static void rave_sp_receive_frame(struct rave_sp *sp,
rave_sp_receive_reply(sp, data, length);
}
-static int rave_sp_receive_buf(struct serdev_device *serdev,
- const unsigned char *buf, size_t size)
+static size_t rave_sp_receive_buf(struct serdev_device *serdev,
+ const u8 *buf, size_t size)
{
struct device *dev = &serdev->dev;
struct rave_sp *sp = dev_get_drvdata(dev);
struct rave_sp_deframer *deframer = &sp->deframer;
- const unsigned char *src = buf;
- const unsigned char *end = buf + size;
+ const u8 *src = buf;
+ const u8 *end = buf + size;
while (src < end) {
- const unsigned char byte = *src++;
+ const u8 byte = *src++;
switch (deframer->state) {
case RAVE_SP_EXPECT_SOF:
diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c
index 5e81f011363f..2c0e8e9630f7 100644
--- a/drivers/mfd/rc5t583.c
+++ b/drivers/mfd/rc5t583.c
@@ -230,7 +230,7 @@ static const struct regmap_config rc5t583_regmap_config = {
.volatile_reg = volatile_reg,
.max_register = RC5T583_MAX_REG,
.num_reg_defaults_raw = RC5T583_NUM_REGS,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int rc5t583_i2c_probe(struct i2c_client *i2c)
diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c
index b50cfa7f4b8f..1d43458b4938 100644
--- a/drivers/mfd/retu-mfd.c
+++ b/drivers/mfd/retu-mfd.c
@@ -65,13 +65,13 @@ static const struct mfd_cell retu_devs[] = {
}
};
-static struct regmap_irq retu_irqs[] = {
+static const struct regmap_irq retu_irqs[] = {
[RETU_INT_PWR] = {
.mask = 1 << RETU_INT_PWR,
}
};
-static struct regmap_irq_chip retu_irq_chip = {
+static const struct regmap_irq_chip retu_irq_chip = {
.name = "RETU",
.irqs = retu_irqs,
.num_irqs = ARRAY_SIZE(retu_irqs),
@@ -101,13 +101,13 @@ static const struct mfd_cell tahvo_devs[] = {
},
};
-static struct regmap_irq tahvo_irqs[] = {
+static const struct regmap_irq tahvo_irqs[] = {
[TAHVO_INT_VBUS] = {
.mask = 1 << TAHVO_INT_VBUS,
}
};
-static struct regmap_irq_chip tahvo_irq_chip = {
+static const struct regmap_irq_chip tahvo_irq_chip = {
.name = "TAHVO",
.irqs = tahvo_irqs,
.num_irqs = ARRAY_SIZE(tahvo_irqs),
@@ -120,7 +120,7 @@ static struct regmap_irq_chip tahvo_irq_chip = {
static const struct retu_data {
char *chip_name;
char *companion_name;
- struct regmap_irq_chip *irq_chip;
+ const struct regmap_irq_chip *irq_chip;
const struct mfd_cell *children;
int nchildren;
} retu_data[] = {
@@ -216,7 +216,7 @@ static int retu_regmap_write(void *context, const void *data, size_t count)
return i2c_smbus_write_word_data(i2c, reg, val);
}
-static struct regmap_bus retu_bus = {
+static const struct regmap_bus retu_bus = {
.read = retu_regmap_read,
.write = retu_regmap_write,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
@@ -300,8 +300,8 @@ static void retu_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id retu_id[] = {
- { "retu", 0 },
- { "tahvo", 0 },
+ { "retu" },
+ { "tahvo" },
{ }
};
MODULE_DEVICE_TABLE(i2c, retu_id);
diff --git a/drivers/mfd/rk8xx-core.c b/drivers/mfd/rk8xx-core.c
index e8fc9e2ab1d0..def4587fdfb8 100644
--- a/drivers/mfd/rk8xx-core.c
+++ b/drivers/mfd/rk8xx-core.c
@@ -10,11 +10,12 @@
* Author: Wadim Egorov <w.egorov@phytec.de>
*/
+#include <linux/bitfield.h>
#include <linux/interrupt.h>
#include <linux/mfd/rk808.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/reboot.h>
@@ -28,6 +29,10 @@ static const struct resource rtc_resources[] = {
DEFINE_RES_IRQ(RK808_IRQ_RTC_ALARM),
};
+static const struct resource rk816_rtc_resources[] = {
+ DEFINE_RES_IRQ(RK816_IRQ_RTC_ALARM),
+};
+
static const struct resource rk817_rtc_resources[] = {
DEFINE_RES_IRQ(RK817_IRQ_RTC_ALARM),
};
@@ -43,8 +48,8 @@ static struct resource rk806_pwrkey_resources[] = {
};
static const struct resource rk817_pwrkey_resources[] = {
- DEFINE_RES_IRQ(RK817_IRQ_PWRON_RISE),
DEFINE_RES_IRQ(RK817_IRQ_PWRON_FALL),
+ DEFINE_RES_IRQ(RK817_IRQ_PWRON_RISE),
};
static const struct resource rk817_charger_resources[] = {
@@ -53,76 +58,84 @@ static const struct resource rk817_charger_resources[] = {
};
static const struct mfd_cell rk805s[] = {
- { .name = "rk808-clkout", .id = PLATFORM_DEVID_NONE, },
- { .name = "rk808-regulator", .id = PLATFORM_DEVID_NONE, },
- { .name = "rk805-pinctrl", .id = PLATFORM_DEVID_NONE, },
+ { .name = "rk808-clkout", },
+ { .name = "rk808-regulator", },
+ { .name = "rk805-pinctrl", },
{
.name = "rk808-rtc",
.num_resources = ARRAY_SIZE(rtc_resources),
.resources = &rtc_resources[0],
- .id = PLATFORM_DEVID_NONE,
},
{ .name = "rk805-pwrkey",
.num_resources = ARRAY_SIZE(rk805_key_resources),
.resources = &rk805_key_resources[0],
- .id = PLATFORM_DEVID_NONE,
},
};
static const struct mfd_cell rk806s[] = {
- { .name = "rk805-pinctrl", .id = PLATFORM_DEVID_AUTO, },
- { .name = "rk808-regulator", .id = PLATFORM_DEVID_AUTO, },
+ { .name = "rk805-pinctrl", },
+ { .name = "rk808-regulator", },
{
.name = "rk805-pwrkey",
.resources = rk806_pwrkey_resources,
.num_resources = ARRAY_SIZE(rk806_pwrkey_resources),
- .id = PLATFORM_DEVID_AUTO,
},
};
static const struct mfd_cell rk808s[] = {
- { .name = "rk808-clkout", .id = PLATFORM_DEVID_NONE, },
- { .name = "rk808-regulator", .id = PLATFORM_DEVID_NONE, },
+ { .name = "rk808-clkout", },
+ { .name = "rk808-regulator", },
{
.name = "rk808-rtc",
.num_resources = ARRAY_SIZE(rtc_resources),
.resources = rtc_resources,
- .id = PLATFORM_DEVID_NONE,
+ },
+};
+
+static const struct mfd_cell rk816s[] = {
+ { .name = "rk805-pinctrl", },
+ { .name = "rk808-clkout", },
+ { .name = "rk808-regulator", },
+ {
+ .name = "rk805-pwrkey",
+ .num_resources = ARRAY_SIZE(rk805_key_resources),
+ .resources = rk805_key_resources,
+ },
+ {
+ .name = "rk808-rtc",
+ .num_resources = ARRAY_SIZE(rk816_rtc_resources),
+ .resources = rk816_rtc_resources,
},
};
static const struct mfd_cell rk817s[] = {
- { .name = "rk808-clkout", .id = PLATFORM_DEVID_NONE, },
- { .name = "rk808-regulator", .id = PLATFORM_DEVID_NONE, },
+ { .name = "rk808-clkout", },
+ { .name = "rk808-regulator", },
{
.name = "rk805-pwrkey",
.num_resources = ARRAY_SIZE(rk817_pwrkey_resources),
.resources = &rk817_pwrkey_resources[0],
- .id = PLATFORM_DEVID_NONE,
},
{
.name = "rk808-rtc",
.num_resources = ARRAY_SIZE(rk817_rtc_resources),
.resources = &rk817_rtc_resources[0],
- .id = PLATFORM_DEVID_NONE,
},
- { .name = "rk817-codec", .id = PLATFORM_DEVID_NONE, },
+ { .name = "rk817-codec", },
{
.name = "rk817-charger",
.num_resources = ARRAY_SIZE(rk817_charger_resources),
.resources = &rk817_charger_resources[0],
- .id = PLATFORM_DEVID_NONE,
},
};
static const struct mfd_cell rk818s[] = {
- { .name = "rk808-clkout", .id = PLATFORM_DEVID_NONE, },
- { .name = "rk808-regulator", .id = PLATFORM_DEVID_NONE, },
+ { .name = "rk808-clkout", },
+ { .name = "rk808-regulator", },
{
.name = "rk808-rtc",
.num_resources = ARRAY_SIZE(rtc_resources),
.resources = rtc_resources,
- .id = PLATFORM_DEVID_NONE,
},
};
@@ -156,6 +169,17 @@ static const struct rk808_reg_data rk808_pre_init_reg[] = {
VB_LO_SEL_3500MV },
};
+static const struct rk808_reg_data rk816_pre_init_reg[] = {
+ { RK818_BUCK1_CONFIG_REG, RK817_RAMP_RATE_MASK,
+ RK817_RAMP_RATE_12_5MV_PER_US },
+ { RK818_BUCK2_CONFIG_REG, RK817_RAMP_RATE_MASK,
+ RK817_RAMP_RATE_12_5MV_PER_US },
+ { RK818_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_250MA },
+ { RK808_THERMAL_REG, TEMP_HOTDIE_MSK, TEMP105C},
+ { RK808_VB_MON_REG, VBAT_LOW_VOL_MASK | VBAT_LOW_ACT_MASK,
+ RK808_VBAT_LOW_3V0 | EN_VABT_LOW_SHUT_DOWN },
+};
+
static const struct rk808_reg_data rk817_pre_init_reg[] = {
{RK817_RTC_CTRL_REG, RTC_STOP, RTC_STOP},
/* Codec specific registers */
@@ -358,6 +382,59 @@ static const struct regmap_irq rk808_irqs[] = {
},
};
+static const unsigned int rk816_irq_status_offsets[] = {
+ RK816_IRQ_STS_OFFSET(RK816_INT_STS_REG1),
+ RK816_IRQ_STS_OFFSET(RK816_INT_STS_REG2),
+ RK816_IRQ_STS_OFFSET(RK816_INT_STS_REG3),
+};
+
+static const unsigned int rk816_irq_mask_offsets[] = {
+ RK816_IRQ_MSK_OFFSET(RK816_INT_STS_MSK_REG1),
+ RK816_IRQ_MSK_OFFSET(RK816_INT_STS_MSK_REG2),
+ RK816_IRQ_MSK_OFFSET(RK816_INT_STS_MSK_REG3),
+};
+
+static unsigned int rk816_get_irq_reg(struct regmap_irq_chip_data *data,
+ unsigned int base, int index)
+{
+ unsigned int irq_reg = base;
+
+ switch (base) {
+ case RK816_INT_STS_REG1:
+ irq_reg += rk816_irq_status_offsets[index];
+ break;
+ case RK816_INT_STS_MSK_REG1:
+ irq_reg += rk816_irq_mask_offsets[index];
+ break;
+ }
+
+ return irq_reg;
+};
+
+static const struct regmap_irq rk816_irqs[] = {
+ /* INT_STS_REG1 IRQs */
+ REGMAP_IRQ_REG(RK816_IRQ_PWRON_FALL, 0, RK816_INT_STS_PWRON_FALL),
+ REGMAP_IRQ_REG(RK816_IRQ_PWRON_RISE, 0, RK816_INT_STS_PWRON_RISE),
+
+ /* INT_STS_REG2 IRQs */
+ REGMAP_IRQ_REG(RK816_IRQ_VB_LOW, 1, RK816_INT_STS_VB_LOW),
+ REGMAP_IRQ_REG(RK816_IRQ_PWRON, 1, RK816_INT_STS_PWRON),
+ REGMAP_IRQ_REG(RK816_IRQ_PWRON_LP, 1, RK816_INT_STS_PWRON_LP),
+ REGMAP_IRQ_REG(RK816_IRQ_HOTDIE, 1, RK816_INT_STS_HOTDIE),
+ REGMAP_IRQ_REG(RK816_IRQ_RTC_ALARM, 1, RK816_INT_STS_RTC_ALARM),
+ REGMAP_IRQ_REG(RK816_IRQ_RTC_PERIOD, 1, RK816_INT_STS_RTC_PERIOD),
+ REGMAP_IRQ_REG(RK816_IRQ_USB_OV, 1, RK816_INT_STS_USB_OV),
+
+ /* INT_STS3 IRQs */
+ REGMAP_IRQ_REG(RK816_IRQ_PLUG_IN, 2, RK816_INT_STS_PLUG_IN),
+ REGMAP_IRQ_REG(RK816_IRQ_PLUG_OUT, 2, RK816_INT_STS_PLUG_OUT),
+ REGMAP_IRQ_REG(RK816_IRQ_CHG_OK, 2, RK816_INT_STS_CHG_OK),
+ REGMAP_IRQ_REG(RK816_IRQ_CHG_TE, 2, RK816_INT_STS_CHG_TE),
+ REGMAP_IRQ_REG(RK816_IRQ_CHG_TS, 2, RK816_INT_STS_CHG_TS),
+ REGMAP_IRQ_REG(RK816_IRQ_CHG_CVTLIM, 2, RK816_INT_STS_CHG_CVTLIM),
+ REGMAP_IRQ_REG(RK816_IRQ_DISCHG_ILIM, 2, RK816_INT_STS_DISCHG_ILIM),
+};
+
static const struct regmap_irq rk818_irqs[] = {
/* INT_STS */
[RK818_IRQ_VOUT_LO] = {
@@ -455,7 +532,7 @@ static const struct regmap_irq rk817_irqs[RK817_IRQ_END] = {
REGMAP_IRQ_REG_LINE(23, 8)
};
-static struct regmap_irq_chip rk805_irq_chip = {
+static const struct regmap_irq_chip rk805_irq_chip = {
.name = "rk805",
.irqs = rk805_irqs,
.num_irqs = ARRAY_SIZE(rk805_irqs),
@@ -466,7 +543,7 @@ static struct regmap_irq_chip rk805_irq_chip = {
.init_ack_masked = true,
};
-static struct regmap_irq_chip rk806_irq_chip = {
+static const struct regmap_irq_chip rk806_irq_chip = {
.name = "rk806",
.irqs = rk806_irqs,
.num_irqs = ARRAY_SIZE(rk806_irqs),
@@ -490,7 +567,19 @@ static const struct regmap_irq_chip rk808_irq_chip = {
.init_ack_masked = true,
};
-static struct regmap_irq_chip rk817_irq_chip = {
+static const struct regmap_irq_chip rk816_irq_chip = {
+ .name = "rk816",
+ .irqs = rk816_irqs,
+ .num_irqs = ARRAY_SIZE(rk816_irqs),
+ .num_regs = 3,
+ .get_irq_reg = rk816_get_irq_reg,
+ .status_base = RK816_INT_STS_REG1,
+ .mask_base = RK816_INT_STS_MSK_REG1,
+ .ack_base = RK816_INT_STS_REG1,
+ .init_ack_masked = true,
+};
+
+static const struct regmap_irq_chip rk817_irq_chip = {
.name = "rk817",
.irqs = rk817_irqs,
.num_irqs = ARRAY_SIZE(rk817_irqs),
@@ -525,8 +614,12 @@ static int rk808_power_off(struct sys_off_data *data)
reg = RK805_DEV_CTRL_REG;
bit = DEV_OFF;
break;
+ case RK806_ID:
+ reg = RK806_SYS_CFG3;
+ bit = DEV_OFF;
+ break;
case RK808_ID:
- reg = RK808_DEVCTRL_REG,
+ reg = RK808_DEVCTRL_REG;
bit = DEV_OFF_RST;
break;
case RK809_ID:
@@ -534,6 +627,7 @@ static int rk808_power_off(struct sys_off_data *data)
reg = RK817_SYS_CFG(3);
bit = DEV_OFF;
break;
+ case RK816_ID:
case RK818_ID:
reg = RK818_DEVCTRL_REG;
bit = DEV_OFF;
@@ -606,6 +700,7 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regmap
const struct mfd_cell *cells;
int dual_support = 0;
int nr_pre_init_regs;
+ u32 rst_fun = 0;
int nr_cells;
int ret;
int i;
@@ -633,6 +728,16 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regmap
cells = rk806s;
nr_cells = ARRAY_SIZE(rk806s);
dual_support = IRQF_SHARED;
+
+ ret = device_property_read_u32(dev, "rockchip,reset-mode", &rst_fun);
+ if (ret)
+ break;
+
+ ret = regmap_update_bits(rk808->regmap, RK806_SYS_CFG3, RK806_RST_FUN_MSK,
+ FIELD_PREP(RK806_RST_FUN_MSK, rst_fun));
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to configure requested restart/reset behavior\n");
break;
case RK808_ID:
rk808->regmap_irq_chip = &rk808_irq_chip;
@@ -641,6 +746,13 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regmap
cells = rk808s;
nr_cells = ARRAY_SIZE(rk808s);
break;
+ case RK816_ID:
+ rk808->regmap_irq_chip = &rk816_irq_chip;
+ pre_init_reg = rk816_pre_init_reg;
+ nr_pre_init_regs = ARRAY_SIZE(rk816_pre_init_reg);
+ cells = rk816s;
+ nr_cells = ARRAY_SIZE(rk816s);
+ break;
case RK818_ID:
rk808->regmap_irq_chip = &rk818_irq_chip;
pre_init_reg = rk818_pre_init_reg;
@@ -680,12 +792,13 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regmap
pre_init_reg[i].addr);
}
- ret = devm_mfd_add_devices(dev, 0, cells, nr_cells, NULL, 0,
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells, nr_cells, NULL, 0,
regmap_irq_get_domain(rk808->irq_data));
if (ret)
return dev_err_probe(dev, ret, "failed to add MFD devices\n");
- if (device_property_read_bool(dev, "rockchip,system-power-controller")) {
+ if (device_property_read_bool(dev, "system-power-controller") ||
+ device_property_read_bool(dev, "rockchip,system-power-controller")) {
ret = devm_register_sys_off_handler(dev,
SYS_OFF_MODE_POWER_OFF_PREPARE, SYS_OFF_PRIO_HIGH,
&rk808_power_off, rk808);
diff --git a/drivers/mfd/rk8xx-i2c.c b/drivers/mfd/rk8xx-i2c.c
index 1a98feea97e2..37287b06dab0 100644
--- a/drivers/mfd/rk8xx-i2c.c
+++ b/drivers/mfd/rk8xx-i2c.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Rockchip RK808/RK818 Core (I2C) driver
+ * Rockchip RK805/RK808/RK816/RK817/RK818 Core (I2C) driver
*
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
* Copyright (C) 2016 PHYTEC Messtechnik GmbH
@@ -21,6 +21,17 @@ struct rk8xx_i2c_platform_data {
int variant;
};
+static bool rk806_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RK806_POWER_EN0 ... RK806_POWER_EN5:
+ case RK806_DVS_START_CTRL ... RK806_INT_MSK1:
+ return true;
+ }
+
+ return false;
+}
+
static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg)
{
/*
@@ -49,6 +60,35 @@ static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg)
return false;
}
+static bool rk816_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /*
+ * Technically the ROUND_30s bit makes RTC_CTRL_REG volatile, but
+ * we don't use that feature. It's better to cache.
+ */
+
+ switch (reg) {
+ case RK808_SECONDS_REG ... RK808_WEEKS_REG:
+ case RK808_RTC_STATUS_REG:
+ case RK808_VB_MON_REG:
+ case RK808_THERMAL_REG:
+ case RK816_DCDC_EN_REG1:
+ case RK816_DCDC_EN_REG2:
+ case RK816_INT_STS_REG1:
+ case RK816_INT_STS_REG2:
+ case RK816_INT_STS_REG3:
+ case RK808_DEVCTRL_REG:
+ case RK816_SUP_STS_REG:
+ case RK816_GGSTS_REG:
+ case RK816_ZERO_CUR_ADC_REGH:
+ case RK816_ZERO_CUR_ADC_REGL:
+ case RK816_GASCNT_REG(0) ... RK816_BAT_VOL_REGL:
+ return true;
+ }
+
+ return false;
+}
+
static bool rk817_is_volatile_reg(struct device *dev, unsigned int reg)
{
/*
@@ -80,7 +120,7 @@ static const struct regmap_config rk818_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = RK818_USB_CTRL_REG,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = rk808_is_volatile_reg,
};
@@ -88,18 +128,34 @@ static const struct regmap_config rk805_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = RK805_OFF_SOURCE_REG,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = rk808_is_volatile_reg,
};
+static const struct regmap_config rk806_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = RK806_BUCK_RSERVE_REG5,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = rk806_is_volatile_reg,
+};
+
static const struct regmap_config rk808_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = RK808_IO_POL_REG,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = rk808_is_volatile_reg,
};
+static const struct regmap_config rk816_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = RK816_DATA_REG(18),
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = rk816_is_volatile_reg,
+};
+
static const struct regmap_config rk817_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -113,6 +169,11 @@ static const struct rk8xx_i2c_platform_data rk805_data = {
.variant = RK805_ID,
};
+static const struct rk8xx_i2c_platform_data rk806_data = {
+ .regmap_cfg = &rk806_regmap_config,
+ .variant = RK806_ID,
+};
+
static const struct rk8xx_i2c_platform_data rk808_data = {
.regmap_cfg = &rk808_regmap_config,
.variant = RK808_ID,
@@ -123,6 +184,11 @@ static const struct rk8xx_i2c_platform_data rk809_data = {
.variant = RK809_ID,
};
+static const struct rk8xx_i2c_platform_data rk816_data = {
+ .regmap_cfg = &rk816_regmap_config,
+ .variant = RK816_ID,
+};
+
static const struct rk8xx_i2c_platform_data rk817_data = {
.regmap_cfg = &rk817_regmap_config,
.variant = RK817_ID,
@@ -159,8 +225,10 @@ static SIMPLE_DEV_PM_OPS(rk8xx_i2c_pm_ops, rk8xx_suspend, rk8xx_resume);
static const struct of_device_id rk8xx_i2c_of_match[] = {
{ .compatible = "rockchip,rk805", .data = &rk805_data },
+ { .compatible = "rockchip,rk806", .data = &rk806_data },
{ .compatible = "rockchip,rk808", .data = &rk808_data },
{ .compatible = "rockchip,rk809", .data = &rk809_data },
+ { .compatible = "rockchip,rk816", .data = &rk816_data },
{ .compatible = "rockchip,rk817", .data = &rk817_data },
{ .compatible = "rockchip,rk818", .data = &rk818_data },
{ },
diff --git a/drivers/mfd/rk8xx-spi.c b/drivers/mfd/rk8xx-spi.c
index fd137f38c2c4..3405fb82ff9f 100644
--- a/drivers/mfd/rk8xx-spi.c
+++ b/drivers/mfd/rk8xx-spi.c
@@ -34,7 +34,7 @@ static const struct regmap_config rk806_regmap_config_spi = {
.reg_bits = 16,
.val_bits = 8,
.max_register = RK806_BUCK_RSERVE_REG5,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_table = &rk806_volatile_table,
};
diff --git a/drivers/mfd/rn5t618.c b/drivers/mfd/rn5t618.c
index 333fef8729a5..23ca00d2c624 100644
--- a/drivers/mfd/rn5t618.c
+++ b/drivers/mfd/rn5t618.c
@@ -13,7 +13,7 @@
#include <linux/mfd/core.h>
#include <linux/mfd/rn5t618.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
@@ -62,7 +62,7 @@ static const struct regmap_config rn5t618_regmap_config = {
.val_bits = 8,
.volatile_reg = rn5t618_volatile_reg,
.max_register = RN5T618_MAX_REG,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_irq rc5t619_irqs[] = {
@@ -179,22 +179,15 @@ MODULE_DEVICE_TABLE(of, rn5t618_of_match);
static int rn5t618_i2c_probe(struct i2c_client *i2c)
{
- const struct of_device_id *of_id;
struct rn5t618 *priv;
int ret;
- of_id = of_match_device(rn5t618_of_match, &i2c->dev);
- if (!of_id) {
- dev_err(&i2c->dev, "Failed to find matching DT ID\n");
- return -EINVAL;
- }
-
priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
i2c_set_clientdata(i2c, priv);
- priv->variant = (long)of_id->data;
+ priv->variant = (long)i2c_get_match_data(i2c);
priv->irq = i2c->irq;
priv->dev = &i2c->dev;
@@ -277,7 +270,7 @@ static SIMPLE_DEV_PM_OPS(rn5t618_i2c_dev_pm_ops,
static struct i2c_driver rn5t618_i2c_driver = {
.driver = {
.name = "rn5t618",
- .of_match_table = of_match_ptr(rn5t618_of_match),
+ .of_match_table = rn5t618_of_match,
.pm = &rn5t618_i2c_dev_pm_ops,
},
.probe = rn5t618_i2c_probe,
diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c
index 93d80a79b901..84a64c3b9c9f 100644
--- a/drivers/mfd/rohm-bd71828.c
+++ b/drivers/mfd/rohm-bd71828.c
@@ -15,7 +15,7 @@
#include <linux/mfd/rohm-bd71828.h>
#include <linux/mfd/rohm-generic.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/types.h>
@@ -25,28 +25,28 @@ static struct gpio_keys_button button = {
.type = EV_KEY,
};
-static struct gpio_keys_platform_data bd71828_powerkey_data = {
+static const struct gpio_keys_platform_data bd71828_powerkey_data = {
.buttons = &button,
.nbuttons = 1,
.name = "bd71828-pwrkey",
};
static const struct resource bd71815_rtc_irqs[] = {
- DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC0, "bd71815-rtc-alm-0"),
- DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC1, "bd71815-rtc-alm-1"),
- DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC2, "bd71815-rtc-alm-2"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC0, "bd70528-rtc-alm-0"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC1, "bd70528-rtc-alm-1"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC2, "bd70528-rtc-alm-2"),
};
static const struct resource bd71828_rtc_irqs[] = {
- DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC0, "bd71828-rtc-alm-0"),
- DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC1, "bd71828-rtc-alm-1"),
- DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC2, "bd71828-rtc-alm-2"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC0, "bd70528-rtc-alm-0"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC1, "bd70528-rtc-alm-1"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC2, "bd70528-rtc-alm-2"),
};
-static struct resource bd71815_power_irqs[] = {
+static const struct resource bd71815_power_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_RMV, "bd71815-dcin-rmv"),
- DEFINE_RES_IRQ_NAMED(BD71815_INT_CLPS_OUT, "bd71815-clps-out"),
- DEFINE_RES_IRQ_NAMED(BD71815_INT_CLPS_IN, "bd71815-clps-in"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_CLPS_OUT, "bd71815-dcin-clps-out"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_CLPS_IN, "bd71815-dcin-clps-in"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_OVP_RES, "bd71815-dcin-ovp-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_OVP_DET, "bd71815-dcin-ovp-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_MON_RES, "bd71815-dcin-mon-res"),
@@ -56,7 +56,7 @@ static struct resource bd71815_power_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_LOW_RES, "bd71815-vsys-low-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_LOW_DET, "bd71815-vsys-low-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_MON_RES, "bd71815-vsys-mon-res"),
- DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_MON_RES, "bd71815-vsys-mon-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_MON_DET, "bd71815-vsys-mon-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_WDG_TEMP, "bd71815-chg-wdg-temp"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_WDG_TIME, "bd71815-chg-wdg"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_RECHARGE_RES, "bd71815-rechg-res"),
@@ -87,13 +87,13 @@ static struct resource bd71815_power_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_2_DET, "bd71815-bat-oc2-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_3_RES, "bd71815-bat-oc3-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_3_DET, "bd71815-bat-oc3-det"),
- DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_LOW_RES, "bd71815-bat-low-res"),
- DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_LOW_DET, "bd71815-bat-low-det"),
- DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_HI_RES, "bd71815-bat-hi-res"),
- DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_HI_DET, "bd71815-bat-hi-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_LOW_RES, "bd71815-temp-bat-low-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_LOW_DET, "bd71815-temp-bat-low-det"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_HI_RES, "bd71815-temp-bat-hi-res"),
+ DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_HI_DET, "bd71815-temp-bat-hi-det"),
};
-static struct mfd_cell bd71815_mfd_cells[] = {
+static const struct mfd_cell bd71815_mfd_cells[] = {
{ .name = "bd71815-pmic", },
{ .name = "bd71815-clk", },
{ .name = "bd71815-gpo", },
@@ -109,6 +109,29 @@ static struct mfd_cell bd71815_mfd_cells[] = {
},
};
+static const struct resource bd71828_power_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_CHG_TOPOFF_TO_DONE,
+ "bd71828-chg-done"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_DCIN_DET, "bd71828-pwr-dcin-in"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_DCIN_RMV, "bd71828-pwr-dcin-out"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_BAT_LOW_VOLT_RES,
+ "bd71828-vbat-normal"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_BAT_LOW_VOLT_DET, "bd71828-vbat-low"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_TEMP_BAT_HI_DET, "bd71828-btemp-hi"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_TEMP_BAT_HI_RES, "bd71828-btemp-cool"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_TEMP_BAT_LOW_DET, "bd71828-btemp-lo"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_TEMP_BAT_LOW_RES,
+ "bd71828-btemp-warm"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_TEMP_CHIP_OVER_VF_DET,
+ "bd71828-temp-hi"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_TEMP_CHIP_OVER_VF_RES,
+ "bd71828-temp-norm"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_TEMP_CHIP_OVER_125_DET,
+ "bd71828-temp-125-over"),
+ DEFINE_RES_IRQ_NAMED(BD71828_INT_TEMP_CHIP_OVER_125_RES,
+ "bd71828-temp-125-under"),
+};
+
static struct mfd_cell bd71828_mfd_cells[] = {
{ .name = "bd71828-pmic", },
{ .name = "bd71828-gpio", },
@@ -118,8 +141,11 @@ static struct mfd_cell bd71828_mfd_cells[] = {
* BD70528 clock gate are the register address and mask.
*/
{ .name = "bd71828-clk", },
- { .name = "bd71827-power", },
{
+ .name = "bd71828-power",
+ .resources = bd71828_power_irqs,
+ .num_resources = ARRAY_SIZE(bd71828_power_irqs),
+ }, {
.name = "bd71828-rtc",
.resources = bd71828_rtc_irqs,
.num_resources = ARRAY_SIZE(bd71828_rtc_irqs),
@@ -197,7 +223,7 @@ static const struct regmap_config bd71815_regmap = {
.val_bits = 8,
.volatile_table = &bd71815_volatile_regs,
.max_register = BD71815_MAX_REGISTER - 1,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct regmap_config bd71828_regmap = {
@@ -205,7 +231,7 @@ static const struct regmap_config bd71828_regmap = {
.val_bits = 8,
.volatile_table = &bd71828_volatile_regs,
.max_register = BD71828_MAX_REGISTER,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
/*
@@ -223,7 +249,7 @@ static unsigned int bit5_offsets[] = {3}; /* VSYS IRQ */
static unsigned int bit6_offsets[] = {1, 2}; /* DCIN IRQ */
static unsigned int bit7_offsets[] = {0}; /* BUCK IRQ */
-static struct regmap_irq_sub_irq_map bd718xx_sub_irq_offsets[] = {
+static const struct regmap_irq_sub_irq_map bd718xx_sub_irq_offsets[] = {
REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets),
@@ -316,7 +342,7 @@ static const struct regmap_irq bd71815_irqs[] = {
REGMAP_IRQ_REG(BD71815_INT_RTC2, 11, BD71815_INT_RTC2_MASK),
};
-static struct regmap_irq bd71828_irqs[] = {
+static const struct regmap_irq bd71828_irqs[] = {
REGMAP_IRQ_REG(BD71828_INT_BUCK1_OCP, 0, BD71828_INT_BUCK1_OCP_MASK),
REGMAP_IRQ_REG(BD71828_INT_BUCK2_OCP, 0, BD71828_INT_BUCK2_OCP_MASK),
REGMAP_IRQ_REG(BD71828_INT_BUCK3_OCP, 0, BD71828_INT_BUCK3_OCP_MASK),
@@ -407,7 +433,7 @@ static struct regmap_irq bd71828_irqs[] = {
REGMAP_IRQ_REG(BD71828_INT_RTC2, 11, BD71828_INT_RTC2_MASK),
};
-static struct regmap_irq_chip bd71828_irq_chip = {
+static const struct regmap_irq_chip bd71828_irq_chip = {
.name = "bd71828_irq",
.main_status = BD71828_REG_INT_MAIN,
.irqs = &bd71828_irqs[0],
@@ -423,7 +449,7 @@ static struct regmap_irq_chip bd71828_irq_chip = {
.irq_reg_stride = 1,
};
-static struct regmap_irq_chip bd71815_irq_chip = {
+static const struct regmap_irq_chip bd71815_irq_chip = {
.name = "bd71815_irq",
.main_status = BD71815_REG_INT_STAT,
.irqs = &bd71815_irqs[0],
@@ -464,15 +490,36 @@ static int set_clk_mode(struct device *dev, struct regmap *regmap,
OUT32K_MODE_CMOS);
}
+static struct i2c_client *bd71828_dev;
+static void bd71828_power_off(void)
+{
+ while (true) {
+ s32 val;
+
+ /* We are not allowed to sleep, so do not use regmap involving mutexes here. */
+ val = i2c_smbus_read_byte_data(bd71828_dev, BD71828_REG_PS_CTRL_1);
+ if (val >= 0)
+ i2c_smbus_write_byte_data(bd71828_dev,
+ BD71828_REG_PS_CTRL_1,
+ BD71828_MASK_STATE_HBNT | (u8)val);
+ mdelay(500);
+ }
+}
+
+static void bd71828_remove_poweroff(void *data)
+{
+ pm_power_off = NULL;
+}
+
static int bd71828_i2c_probe(struct i2c_client *i2c)
{
struct regmap_irq_chip_data *irq_data;
int ret;
struct regmap *regmap;
const struct regmap_config *regmap_config;
- struct regmap_irq_chip *irqchip;
+ const struct regmap_irq_chip *irqchip;
unsigned int chip_type;
- struct mfd_cell *mfd;
+ const struct mfd_cell *mfd;
int cells;
int button_irq;
int clkmode_reg;
@@ -542,7 +589,20 @@ static int bd71828_i2c_probe(struct i2c_client *i2c)
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, mfd, cells,
NULL, 0, regmap_irq_get_domain(irq_data));
if (ret)
- dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n");
+ return dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n");
+
+ if (of_device_is_system_power_controller(i2c->dev.of_node) &&
+ chip_type == ROHM_CHIP_TYPE_BD71828) {
+ if (!pm_power_off) {
+ bd71828_dev = i2c;
+ pm_power_off = bd71828_power_off;
+ ret = devm_add_action_or_reset(&i2c->dev,
+ bd71828_remove_poweroff,
+ NULL);
+ } else {
+ dev_warn(&i2c->dev, "Poweroff callback already assigned\n");
+ }
+ }
return ret;
}
diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c
index 0b58ecc78334..ff714fd4f54d 100644
--- a/drivers/mfd/rohm-bd718x7.c
+++ b/drivers/mfd/rohm-bd718x7.c
@@ -14,7 +14,7 @@
#include <linux/mfd/rohm-bd718x7.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/types.h>
@@ -60,7 +60,7 @@ static const struct regmap_irq bd718xx_irqs[] = {
REGMAP_IRQ_REG(BD718XX_INT_STBY_REQ, 0, BD718XX_INT_STBY_REQ_MASK),
};
-static struct regmap_irq_chip bd718xx_irq_chip = {
+static const struct regmap_irq_chip bd718xx_irq_chip = {
.name = "bd718xx-irq",
.irqs = bd718xx_irqs,
.num_irqs = ARRAY_SIZE(bd718xx_irqs),
@@ -72,14 +72,13 @@ static struct regmap_irq_chip bd718xx_irq_chip = {
.init_ack_masked = true,
};
-static const struct regmap_range pmic_status_range = {
- .range_min = BD718XX_REG_IRQ,
- .range_max = BD718XX_REG_POW_STATE,
+static const struct regmap_range pmic_status_range[] = {
+ regmap_reg_range(BD718XX_REG_IRQ, BD718XX_REG_POW_STATE),
};
static const struct regmap_access_table volatile_regs = {
- .yes_ranges = &pmic_status_range,
- .n_yes_ranges = 1,
+ .yes_ranges = &pmic_status_range[0],
+ .n_yes_ranges = ARRAY_SIZE(pmic_status_range),
};
static const struct regmap_config bd718xx_regmap_config = {
@@ -87,7 +86,7 @@ static const struct regmap_config bd718xx_regmap_config = {
.val_bits = 8,
.volatile_table = &volatile_regs,
.max_register = BD718XX_MAX_REGISTER - 1,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int bd718xx_init_press_duration(struct regmap *regmap,
diff --git a/drivers/mfd/rohm-bd9576.c b/drivers/mfd/rohm-bd9576.c
index 645673322ec0..17323ae39803 100644
--- a/drivers/mfd/rohm-bd9576.c
+++ b/drivers/mfd/rohm-bd9576.c
@@ -13,7 +13,7 @@
#include <linux/mfd/rohm-bd957x.h>
#include <linux/mfd/rohm-generic.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/types.h>
@@ -57,15 +57,15 @@ static const struct regmap_access_table volatile_regs = {
.n_yes_ranges = ARRAY_SIZE(volatile_ranges),
};
-static struct regmap_config bd957x_regmap = {
+static const struct regmap_config bd957x_regmap = {
.reg_bits = 8,
.val_bits = 8,
.volatile_table = &volatile_regs,
.max_register = BD957X_MAX_REGISTER,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-static struct regmap_irq bd9576_irqs[] = {
+static const struct regmap_irq bd9576_irqs[] = {
REGMAP_IRQ_REG(BD9576_INT_THERM, 0, BD957X_MASK_INT_MAIN_THERM),
REGMAP_IRQ_REG(BD9576_INT_OVP, 0, BD957X_MASK_INT_MAIN_OVP),
REGMAP_IRQ_REG(BD9576_INT_SCP, 0, BD957X_MASK_INT_MAIN_SCP),
@@ -76,7 +76,7 @@ static struct regmap_irq bd9576_irqs[] = {
REGMAP_IRQ_REG(BD9576_INT_SYS, 0, BD957X_MASK_INT_MAIN_SYS),
};
-static struct regmap_irq_chip bd9576_irq_chip = {
+static const struct regmap_irq_chip bd9576_irq_chip = {
.name = "bd9576_irq",
.irqs = &bd9576_irqs[0],
.num_irqs = ARRAY_SIZE(bd9576_irqs),
diff --git a/drivers/mfd/rohm-bd96801.c b/drivers/mfd/rohm-bd96801.c
new file mode 100644
index 000000000000..66fa017ad568
--- /dev/null
+++ b/drivers/mfd/rohm-bd96801.c
@@ -0,0 +1,793 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024 ROHM Semiconductors
+ *
+ * ROHM BD96801 PMIC driver
+ *
+ * This version of the "BD86801 scalable PMIC"'s driver supports only very
+ * basic set of the PMIC features.
+ * Most notably, there is no support for the configurations which should
+ * be done when the PMIC is in STBY mode.
+ *
+ * Being able to reliably do the configurations like changing the
+ * regulator safety limits (like limits for the over/under -voltages, over
+ * current, thermal protection) would require the configuring driver to be
+ * synchronized with entity causing the PMIC state transitions. Eg, one
+ * should be able to ensure the PMIC is in STBY state when the
+ * configurations are applied to the hardware. How and when the PMIC state
+ * transitions are to be done is likely to be very system specific, as will
+ * be the need to configure these safety limits. Hence it's not simple to
+ * come up with a generic solution.
+ *
+ * Users who require the STBY state configurations can have a look at the
+ * original RFC:
+ * https://lore.kernel.org/all/cover.1712920132.git.mazziesaccount@gmail.com/
+ * which implements some of the safety limit configurations - but leaves the
+ * state change handling and synchronization to be implemented.
+ *
+ * It would be great to hear (and receive a patch!) if you implement the
+ * STBY configuration support or a proper fix in your downstream driver ;)
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include <linux/mfd/rohm-bd96801.h>
+#include <linux/mfd/rohm-bd96802.h>
+#include <linux/mfd/rohm-generic.h>
+
+struct bd968xx {
+ const struct resource *errb_irqs;
+ const struct resource *intb_irqs;
+ int num_errb_irqs;
+ int num_intb_irqs;
+ const struct regmap_irq_chip *errb_irq_chip;
+ const struct regmap_irq_chip *intb_irq_chip;
+ const struct regmap_config *regmap_config;
+ struct mfd_cell *cells;
+ int num_cells;
+ int unlock_reg;
+ int unlock_val;
+};
+
+static const struct resource bd96801_reg_errb_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(BD96801_OTP_ERR_STAT, "otp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_DBIST_ERR_STAT, "dbist-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_EEP_ERR_STAT, "eep-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_ABIST_ERR_STAT, "abist-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_PRSTB_ERR_STAT, "prstb-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_DRMOS1_ERR_STAT, "drmoserr1"),
+ DEFINE_RES_IRQ_NAMED(BD96801_DRMOS2_ERR_STAT, "drmoserr2"),
+ DEFINE_RES_IRQ_NAMED(BD96801_SLAVE_ERR_STAT, "slave-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_VREF_ERR_STAT, "vref-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_TSD_ERR_STAT, "tsd"),
+ DEFINE_RES_IRQ_NAMED(BD96801_UVLO_ERR_STAT, "uvlo-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_OVLO_ERR_STAT, "ovlo-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_OSC_ERR_STAT, "osc-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_PON_ERR_STAT, "pon-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_POFF_ERR_STAT, "poff-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_CMD_SHDN_ERR_STAT, "cmd-shdn-err"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_INT_PRSTB_WDT_ERR, "bd96801-prstb-wdt-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_INT_CHIP_IF_ERR, "bd96801-chip-if-err"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_INT_SHDN_ERR_STAT, "int-shdn-err"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_PVIN_ERR_STAT, "buck1-pvin-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OVP_ERR_STAT, "buck1-ovp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_UVP_ERR_STAT, "buck1-uvp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_SHDN_ERR_STAT, "buck1-shdn-err"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_PVIN_ERR_STAT, "buck2-pvin-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OVP_ERR_STAT, "buck2-ovp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_UVP_ERR_STAT, "buck2-uvp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_SHDN_ERR_STAT, "buck2-shdn-err"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_PVIN_ERR_STAT, "buck3-pvin-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OVP_ERR_STAT, "buck3-ovp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_UVP_ERR_STAT, "buck3-uvp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_SHDN_ERR_STAT, "buck3-shdn-err"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_PVIN_ERR_STAT, "buck4-pvin-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OVP_ERR_STAT, "buck4-ovp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_UVP_ERR_STAT, "buck4-uvp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_SHDN_ERR_STAT, "buck4-shdn-err"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO5_PVIN_ERR_STAT, "ldo5-pvin-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO5_OVP_ERR_STAT, "ldo5-ovp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO5_UVP_ERR_STAT, "ldo5-uvp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO5_SHDN_ERR_STAT, "ldo5-shdn-err"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO6_PVIN_ERR_STAT, "ldo6-pvin-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO6_OVP_ERR_STAT, "ldo6-ovp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO6_UVP_ERR_STAT, "ldo6-uvp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO6_SHDN_ERR_STAT, "ldo6-shdn-err"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO7_PVIN_ERR_STAT, "ldo7-pvin-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO7_OVP_ERR_STAT, "ldo7-ovp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO7_UVP_ERR_STAT, "ldo7-uvp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO7_SHDN_ERR_STAT, "ldo7-shdn-err"),
+};
+
+static const struct resource bd96802_reg_errb_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(BD96802_OTP_ERR_STAT, "otp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_DBIST_ERR_STAT, "dbist-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_EEP_ERR_STAT, "eep-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_ABIST_ERR_STAT, "abist-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_PRSTB_ERR_STAT, "prstb-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_DRMOS1_ERR_STAT, "drmoserr1"),
+ DEFINE_RES_IRQ_NAMED(BD96802_DRMOS1_ERR_STAT, "drmoserr2"),
+ DEFINE_RES_IRQ_NAMED(BD96802_SLAVE_ERR_STAT, "slave-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_VREF_ERR_STAT, "vref-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_TSD_ERR_STAT, "tsd"),
+ DEFINE_RES_IRQ_NAMED(BD96802_UVLO_ERR_STAT, "uvlo-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_OVLO_ERR_STAT, "ovlo-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_OSC_ERR_STAT, "osc-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_PON_ERR_STAT, "pon-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_POFF_ERR_STAT, "poff-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_CMD_SHDN_ERR_STAT, "cmd-shdn-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_INT_SHDN_ERR_STAT, "int-shdn-err"),
+
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_PVIN_ERR_STAT, "buck1-pvin-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_OVP_ERR_STAT, "buck1-ovp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_UVP_ERR_STAT, "buck1-uvp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_SHDN_ERR_STAT, "buck1-shdn-err"),
+
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_PVIN_ERR_STAT, "buck2-pvin-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_OVP_ERR_STAT, "buck2-ovp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_UVP_ERR_STAT, "buck2-uvp-err"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_SHDN_ERR_STAT, "buck2-shdn-err"),
+};
+
+static const struct resource bd96801_reg_intb_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(BD96801_TW_STAT, "core-thermal"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OCPH_STAT, "buck1-overcurr-h"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OCPL_STAT, "buck1-overcurr-l"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OCPN_STAT, "buck1-overcurr-n"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OVD_STAT, "buck1-overvolt"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_UVD_STAT, "buck1-undervolt"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_TW_CH_STAT, "buck1-thermal"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OCPH_STAT, "buck2-overcurr-h"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OCPL_STAT, "buck2-overcurr-l"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OCPN_STAT, "buck2-overcurr-n"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OVD_STAT, "buck2-overvolt"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_UVD_STAT, "buck2-undervolt"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_TW_CH_STAT, "buck2-thermal"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OCPH_STAT, "buck3-overcurr-h"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OCPL_STAT, "buck3-overcurr-l"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OCPN_STAT, "buck3-overcurr-n"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OVD_STAT, "buck3-overvolt"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_UVD_STAT, "buck3-undervolt"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_TW_CH_STAT, "buck3-thermal"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OCPH_STAT, "buck4-overcurr-h"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OCPL_STAT, "buck4-overcurr-l"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OCPN_STAT, "buck4-overcurr-n"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OVD_STAT, "buck4-overvolt"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_UVD_STAT, "buck4-undervolt"),
+ DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_TW_CH_STAT, "buck4-thermal"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO5_OCPH_STAT, "ldo5-overcurr"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO5_OVD_STAT, "ldo5-overvolt"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO5_UVD_STAT, "ldo5-undervolt"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO6_OCPH_STAT, "ldo6-overcurr"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO6_OVD_STAT, "ldo6-overvolt"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO6_UVD_STAT, "ldo6-undervolt"),
+
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO7_OCPH_STAT, "ldo7-overcurr"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO7_OVD_STAT, "ldo7-overvolt"),
+ DEFINE_RES_IRQ_NAMED(BD96801_LDO7_UVD_STAT, "ldo7-undervolt"),
+};
+
+static const struct resource bd96802_reg_intb_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(BD96802_TW_STAT, "core-thermal"),
+
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_OCPH_STAT, "buck1-overcurr-h"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_OCPL_STAT, "buck1-overcurr-l"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_OCPN_STAT, "buck1-overcurr-n"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_OVD_STAT, "buck1-overvolt"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_UVD_STAT, "buck1-undervolt"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK1_TW_CH_STAT, "buck1-thermal"),
+
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_OCPH_STAT, "buck2-overcurr-h"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_OCPL_STAT, "buck2-overcurr-l"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_OCPN_STAT, "buck2-overcurr-n"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_OVD_STAT, "buck2-overvolt"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_UVD_STAT, "buck2-undervolt"),
+ DEFINE_RES_IRQ_NAMED(BD96802_BUCK2_TW_CH_STAT, "buck2-thermal"),
+};
+
+enum {
+ WDG_CELL = 0,
+ REGULATOR_CELL,
+};
+
+static struct mfd_cell bd96801_cells[] = {
+ [WDG_CELL] = { .name = "bd96801-wdt", },
+ [REGULATOR_CELL] = { .name = "bd96801-regulator", },
+};
+
+static struct mfd_cell bd96802_cells[] = {
+ [WDG_CELL] = { .name = "bd96801-wdt", },
+ [REGULATOR_CELL] = { .name = "bd96802-regulator", },
+};
+static struct mfd_cell bd96805_cells[] = {
+ [WDG_CELL] = { .name = "bd96801-wdt", },
+ [REGULATOR_CELL] = { .name = "bd96805-regulator", },
+};
+
+static struct mfd_cell bd96806_cells[] = {
+ [WDG_CELL] = { .name = "bd96806-wdt", },
+ [REGULATOR_CELL] = { .name = "bd96806-regulator", },
+};
+
+static const struct regmap_range bd96801_volatile_ranges[] = {
+ /* Status registers */
+ regmap_reg_range(BD96801_REG_WD_FEED, BD96801_REG_WD_FAILCOUNT),
+ regmap_reg_range(BD96801_REG_WD_ASK, BD96801_REG_WD_ASK),
+ regmap_reg_range(BD96801_REG_WD_STATUS, BD96801_REG_WD_STATUS),
+ regmap_reg_range(BD96801_REG_PMIC_STATE, BD96801_REG_INT_LDO7_INTB),
+ /* Registers which do not update value unless PMIC is in STBY */
+ regmap_reg_range(BD96801_REG_SSCG_CTRL, BD96801_REG_SHD_INTB),
+ regmap_reg_range(BD96801_REG_BUCK_OVP, BD96801_REG_BOOT_OVERTIME),
+ /*
+ * LDO control registers have single bit (LDO MODE) which does not
+ * change when we write it unless PMIC is in STBY. It's safer to not
+ * cache it.
+ */
+ regmap_reg_range(BD96801_LDO5_VOL_LVL_REG, BD96801_LDO7_VOL_LVL_REG),
+};
+
+static const struct regmap_range bd96802_volatile_ranges[] = {
+ /* Status regs */
+ regmap_reg_range(BD96801_REG_WD_FEED, BD96801_REG_WD_FAILCOUNT),
+ regmap_reg_range(BD96801_REG_WD_ASK, BD96801_REG_WD_ASK),
+ regmap_reg_range(BD96801_REG_WD_STATUS, BD96801_REG_WD_STATUS),
+ regmap_reg_range(BD96801_REG_PMIC_STATE, BD96801_REG_INT_BUCK2_ERRB),
+ regmap_reg_range(BD96801_REG_INT_SYS_INTB, BD96801_REG_INT_BUCK2_INTB),
+ /* Registers which do not update value unless PMIC is in STBY */
+ regmap_reg_range(BD96801_REG_SSCG_CTRL, BD96801_REG_SHD_INTB),
+ regmap_reg_range(BD96801_REG_BUCK_OVP, BD96801_REG_BOOT_OVERTIME),
+};
+
+static const struct regmap_access_table bd96801_volatile_regs = {
+ .yes_ranges = bd96801_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(bd96801_volatile_ranges),
+};
+
+static const struct regmap_access_table bd96802_volatile_regs = {
+ .yes_ranges = bd96802_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(bd96802_volatile_ranges),
+};
+
+/*
+ * For ERRB we need main register bit mapping as bit(0) indicates active IRQ
+ * in one of the first 3 sub IRQ registers, For INTB we can use default 1 to 1
+ * mapping.
+ */
+static unsigned int bit0_offsets[] = {0, 1, 2}; /* System stat, 3 registers */
+static unsigned int bit1_offsets[] = {3}; /* Buck 1 stat */
+static unsigned int bit2_offsets[] = {4}; /* Buck 2 stat */
+static unsigned int bit3_offsets[] = {5}; /* Buck 3 stat */
+static unsigned int bit4_offsets[] = {6}; /* Buck 4 stat */
+static unsigned int bit5_offsets[] = {7}; /* LDO 5 stat */
+static unsigned int bit6_offsets[] = {8}; /* LDO 6 stat */
+static unsigned int bit7_offsets[] = {9}; /* LDO 7 stat */
+
+static const struct regmap_irq_sub_irq_map bd96801_errb_sub_irq_offsets[] = {
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit3_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit4_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit5_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit6_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets),
+};
+
+static const struct regmap_irq_sub_irq_map bd96802_errb_sub_irq_offsets[] = {
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets),
+};
+
+static const struct regmap_irq bd96801_errb_irqs[] = {
+ /* Reg 0x52 Fatal ERRB1 */
+ REGMAP_IRQ_REG(BD96801_OTP_ERR_STAT, 0, BD96801_OTP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_DBIST_ERR_STAT, 0, BD96801_DBIST_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_EEP_ERR_STAT, 0, BD96801_EEP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_ABIST_ERR_STAT, 0, BD96801_ABIST_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_PRSTB_ERR_STAT, 0, BD96801_PRSTB_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_DRMOS1_ERR_STAT, 0, BD96801_DRMOS1_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_DRMOS2_ERR_STAT, 0, BD96801_DRMOS2_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_SLAVE_ERR_STAT, 0, BD96801_SLAVE_ERR_MASK),
+ /* 0x53 Fatal ERRB2 */
+ REGMAP_IRQ_REG(BD96801_VREF_ERR_STAT, 1, BD96801_VREF_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_TSD_ERR_STAT, 1, BD96801_TSD_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_UVLO_ERR_STAT, 1, BD96801_UVLO_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_OVLO_ERR_STAT, 1, BD96801_OVLO_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_OSC_ERR_STAT, 1, BD96801_OSC_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_PON_ERR_STAT, 1, BD96801_PON_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_POFF_ERR_STAT, 1, BD96801_POFF_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_CMD_SHDN_ERR_STAT, 1, BD96801_CMD_SHDN_ERR_MASK),
+ /* 0x54 Fatal INTB shadowed to ERRB */
+ REGMAP_IRQ_REG(BD96801_INT_PRSTB_WDT_ERR, 2, BD96801_INT_PRSTB_WDT_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_INT_CHIP_IF_ERR, 2, BD96801_INT_CHIP_IF_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_INT_SHDN_ERR_STAT, 2, BD96801_INT_SHDN_ERR_MASK),
+ /* Reg 0x55 BUCK1 ERR IRQs */
+ REGMAP_IRQ_REG(BD96801_BUCK1_PVIN_ERR_STAT, 3, BD96801_OUT_PVIN_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK1_OVP_ERR_STAT, 3, BD96801_OUT_OVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK1_UVP_ERR_STAT, 3, BD96801_OUT_UVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK1_SHDN_ERR_STAT, 3, BD96801_OUT_SHDN_ERR_MASK),
+ /* Reg 0x56 BUCK2 ERR IRQs */
+ REGMAP_IRQ_REG(BD96801_BUCK2_PVIN_ERR_STAT, 4, BD96801_OUT_PVIN_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK2_OVP_ERR_STAT, 4, BD96801_OUT_OVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK2_UVP_ERR_STAT, 4, BD96801_OUT_UVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK2_SHDN_ERR_STAT, 4, BD96801_OUT_SHDN_ERR_MASK),
+ /* Reg 0x57 BUCK3 ERR IRQs */
+ REGMAP_IRQ_REG(BD96801_BUCK3_PVIN_ERR_STAT, 5, BD96801_OUT_PVIN_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK3_OVP_ERR_STAT, 5, BD96801_OUT_OVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK3_UVP_ERR_STAT, 5, BD96801_OUT_UVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK3_SHDN_ERR_STAT, 5, BD96801_OUT_SHDN_ERR_MASK),
+ /* Reg 0x58 BUCK4 ERR IRQs */
+ REGMAP_IRQ_REG(BD96801_BUCK4_PVIN_ERR_STAT, 6, BD96801_OUT_PVIN_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK4_OVP_ERR_STAT, 6, BD96801_OUT_OVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK4_UVP_ERR_STAT, 6, BD96801_OUT_UVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK4_SHDN_ERR_STAT, 6, BD96801_OUT_SHDN_ERR_MASK),
+ /* Reg 0x59 LDO5 ERR IRQs */
+ REGMAP_IRQ_REG(BD96801_LDO5_PVIN_ERR_STAT, 7, BD96801_OUT_PVIN_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO5_OVP_ERR_STAT, 7, BD96801_OUT_OVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO5_UVP_ERR_STAT, 7, BD96801_OUT_UVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO5_SHDN_ERR_STAT, 7, BD96801_OUT_SHDN_ERR_MASK),
+ /* Reg 0x5a LDO6 ERR IRQs */
+ REGMAP_IRQ_REG(BD96801_LDO6_PVIN_ERR_STAT, 8, BD96801_OUT_PVIN_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO6_OVP_ERR_STAT, 8, BD96801_OUT_OVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO6_UVP_ERR_STAT, 8, BD96801_OUT_UVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO6_SHDN_ERR_STAT, 8, BD96801_OUT_SHDN_ERR_MASK),
+ /* Reg 0x5b LDO7 ERR IRQs */
+ REGMAP_IRQ_REG(BD96801_LDO7_PVIN_ERR_STAT, 9, BD96801_OUT_PVIN_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO7_OVP_ERR_STAT, 9, BD96801_OUT_OVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO7_UVP_ERR_STAT, 9, BD96801_OUT_UVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO7_SHDN_ERR_STAT, 9, BD96801_OUT_SHDN_ERR_MASK),
+};
+
+static const struct regmap_irq bd96802_errb_irqs[] = {
+ /* Reg 0x52 Fatal ERRB1 */
+ REGMAP_IRQ_REG(BD96802_OTP_ERR_STAT, 0, BD96801_OTP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_DBIST_ERR_STAT, 0, BD96801_DBIST_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_EEP_ERR_STAT, 0, BD96801_EEP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_ABIST_ERR_STAT, 0, BD96801_ABIST_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_PRSTB_ERR_STAT, 0, BD96801_PRSTB_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_DRMOS1_ERR_STAT, 0, BD96801_DRMOS1_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_DRMOS2_ERR_STAT, 0, BD96801_DRMOS2_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_SLAVE_ERR_STAT, 0, BD96801_SLAVE_ERR_MASK),
+ /* 0x53 Fatal ERRB2 */
+ REGMAP_IRQ_REG(BD96802_VREF_ERR_STAT, 1, BD96801_VREF_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_TSD_ERR_STAT, 1, BD96801_TSD_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_UVLO_ERR_STAT, 1, BD96801_UVLO_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_OVLO_ERR_STAT, 1, BD96801_OVLO_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_OSC_ERR_STAT, 1, BD96801_OSC_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_PON_ERR_STAT, 1, BD96801_PON_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_POFF_ERR_STAT, 1, BD96801_POFF_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_CMD_SHDN_ERR_STAT, 1, BD96801_CMD_SHDN_ERR_MASK),
+ /* 0x54 Fatal INTB shadowed to ERRB */
+ REGMAP_IRQ_REG(BD96802_INT_SHDN_ERR_STAT, 2, BD96801_INT_SHDN_ERR_MASK),
+ /* Reg 0x55 BUCK1 ERR IRQs */
+ REGMAP_IRQ_REG(BD96802_BUCK1_PVIN_ERR_STAT, 3, BD96801_OUT_PVIN_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK1_OVP_ERR_STAT, 3, BD96801_OUT_OVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK1_UVP_ERR_STAT, 3, BD96801_OUT_UVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK1_SHDN_ERR_STAT, 3, BD96801_OUT_SHDN_ERR_MASK),
+ /* Reg 0x56 BUCK2 ERR IRQs */
+ REGMAP_IRQ_REG(BD96802_BUCK2_PVIN_ERR_STAT, 4, BD96801_OUT_PVIN_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK2_OVP_ERR_STAT, 4, BD96801_OUT_OVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK2_UVP_ERR_STAT, 4, BD96801_OUT_UVP_ERR_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK2_SHDN_ERR_STAT, 4, BD96801_OUT_SHDN_ERR_MASK),
+};
+
+static const struct regmap_irq bd96801_intb_irqs[] = {
+ /* STATUS SYSTEM INTB */
+ REGMAP_IRQ_REG(BD96801_TW_STAT, 0, BD96801_TW_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_WDT_ERR_STAT, 0, BD96801_WDT_ERR_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_I2C_ERR_STAT, 0, BD96801_I2C_ERR_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_CHIP_IF_ERR_STAT, 0, BD96801_CHIP_IF_ERR_STAT_MASK),
+ /* STATUS BUCK1 INTB */
+ REGMAP_IRQ_REG(BD96801_BUCK1_OCPH_STAT, 1, BD96801_BUCK_OCPH_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK1_OCPL_STAT, 1, BD96801_BUCK_OCPL_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK1_OCPN_STAT, 1, BD96801_BUCK_OCPN_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK1_OVD_STAT, 1, BD96801_BUCK_OVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK1_UVD_STAT, 1, BD96801_BUCK_UVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK1_TW_CH_STAT, 1, BD96801_BUCK_TW_CH_STAT_MASK),
+ /* BUCK 2 INTB */
+ REGMAP_IRQ_REG(BD96801_BUCK2_OCPH_STAT, 2, BD96801_BUCK_OCPH_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK2_OCPL_STAT, 2, BD96801_BUCK_OCPL_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK2_OCPN_STAT, 2, BD96801_BUCK_OCPN_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK2_OVD_STAT, 2, BD96801_BUCK_OVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK2_UVD_STAT, 2, BD96801_BUCK_UVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK2_TW_CH_STAT, 2, BD96801_BUCK_TW_CH_STAT_MASK),
+ /* BUCK 3 INTB */
+ REGMAP_IRQ_REG(BD96801_BUCK3_OCPH_STAT, 3, BD96801_BUCK_OCPH_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK3_OCPL_STAT, 3, BD96801_BUCK_OCPL_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK3_OCPN_STAT, 3, BD96801_BUCK_OCPN_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK3_OVD_STAT, 3, BD96801_BUCK_OVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK3_UVD_STAT, 3, BD96801_BUCK_UVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK3_TW_CH_STAT, 3, BD96801_BUCK_TW_CH_STAT_MASK),
+ /* BUCK 4 INTB */
+ REGMAP_IRQ_REG(BD96801_BUCK4_OCPH_STAT, 4, BD96801_BUCK_OCPH_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK4_OCPL_STAT, 4, BD96801_BUCK_OCPL_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK4_OCPN_STAT, 4, BD96801_BUCK_OCPN_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK4_OVD_STAT, 4, BD96801_BUCK_OVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK4_UVD_STAT, 4, BD96801_BUCK_UVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_BUCK4_TW_CH_STAT, 4, BD96801_BUCK_TW_CH_STAT_MASK),
+ /* LDO5 INTB */
+ REGMAP_IRQ_REG(BD96801_LDO5_OCPH_STAT, 5, BD96801_LDO_OCPH_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO5_OVD_STAT, 5, BD96801_LDO_OVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO5_UVD_STAT, 5, BD96801_LDO_UVD_STAT_MASK),
+ /* LDO6 INTB */
+ REGMAP_IRQ_REG(BD96801_LDO6_OCPH_STAT, 6, BD96801_LDO_OCPH_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO6_OVD_STAT, 6, BD96801_LDO_OVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO6_UVD_STAT, 6, BD96801_LDO_UVD_STAT_MASK),
+ /* LDO7 INTB */
+ REGMAP_IRQ_REG(BD96801_LDO7_OCPH_STAT, 7, BD96801_LDO_OCPH_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO7_OVD_STAT, 7, BD96801_LDO_OVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96801_LDO7_UVD_STAT, 7, BD96801_LDO_UVD_STAT_MASK),
+};
+
+static const struct regmap_irq bd96802_intb_irqs[] = {
+ /* STATUS SYSTEM INTB */
+ REGMAP_IRQ_REG(BD96802_TW_STAT, 0, BD96801_TW_STAT_MASK),
+ REGMAP_IRQ_REG(BD96802_WDT_ERR_STAT, 0, BD96801_WDT_ERR_STAT_MASK),
+ REGMAP_IRQ_REG(BD96802_I2C_ERR_STAT, 0, BD96801_I2C_ERR_STAT_MASK),
+ REGMAP_IRQ_REG(BD96802_CHIP_IF_ERR_STAT, 0, BD96801_CHIP_IF_ERR_STAT_MASK),
+ /* STATUS BUCK1 INTB */
+ REGMAP_IRQ_REG(BD96802_BUCK1_OCPH_STAT, 1, BD96801_BUCK_OCPH_STAT_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK1_OCPL_STAT, 1, BD96801_BUCK_OCPL_STAT_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK1_OCPN_STAT, 1, BD96801_BUCK_OCPN_STAT_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK1_OVD_STAT, 1, BD96801_BUCK_OVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK1_UVD_STAT, 1, BD96801_BUCK_UVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK1_TW_CH_STAT, 1, BD96801_BUCK_TW_CH_STAT_MASK),
+ /* BUCK 2 INTB */
+ REGMAP_IRQ_REG(BD96802_BUCK2_OCPH_STAT, 2, BD96801_BUCK_OCPH_STAT_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK2_OCPL_STAT, 2, BD96801_BUCK_OCPL_STAT_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK2_OCPN_STAT, 2, BD96801_BUCK_OCPN_STAT_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK2_OVD_STAT, 2, BD96801_BUCK_OVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK2_UVD_STAT, 2, BD96801_BUCK_UVD_STAT_MASK),
+ REGMAP_IRQ_REG(BD96802_BUCK2_TW_CH_STAT, 2, BD96801_BUCK_TW_CH_STAT_MASK),
+};
+
+/*
+ * The IRQ stuff is a bit hairy. The BD96801 / BD96802 provide two physical
+ * IRQ lines called INTB and ERRB. They share the same main status register.
+ *
+ * For ERRB, mapping from main status to sub-status is such that the
+ * 'global' faults are mapped to first 3 sub-status registers - and indicated
+ * by the first bit[0] in main status reg.
+ *
+ * Rest of the status registers are for indicating stuff for individual
+ * regulators, 1 sub register / regulator and 1 main status register bit /
+ * regulator, starting from bit[1].
+ *
+ * Eg, regulator specific stuff has 1 to 1 mapping from main-status to sub
+ * registers but 'global' ERRB IRQs require mapping from main status bit[0] to
+ * 3 status registers.
+ *
+ * Furthermore, the BD96801 has 7 regulators where the BD96802 has only 2.
+ *
+ * INTB has only 1 sub status register for 'global' events and then own sub
+ * status register for each of the regulators. So, for INTB we have direct
+ * 1 to 1 mapping - BD96801 just having 5 register and 5 main status bits
+ * more than the BD96802.
+ *
+ * Sharing the main status bits could be a problem if we had both INTB and
+ * ERRB IRQs asserted but for different sub-status offsets. This might lead
+ * IRQ controller code to go read a sub status register which indicates no
+ * active IRQs. I assume this occurring repeteadly might lead the IRQ to be
+ * disabled by core as a result of repeteadly returned IRQ_NONEs.
+ *
+ * I don't consider this as a fatal problem for now because:
+ * a) Having ERRB asserted leads to PMIC fault state which will kill
+ * the SoC powered by the PMIC. (So, relevant only for potential
+ * case of not powering the processor with this PMIC).
+ * b) Having ERRB set without having respective INTB is unlikely
+ * (haven't actually verified this).
+ *
+ * So, let's proceed with main status enabled for both INTB and ERRB. We can
+ * later disable main-status usage on systems where this ever proves to be
+ * a problem.
+ */
+
+static const struct regmap_irq_chip bd96801_irq_chip_errb = {
+ .name = "bd96801-irq-errb",
+ .domain_suffix = "errb",
+ .main_status = BD96801_REG_INT_MAIN,
+ .num_main_regs = 1,
+ .irqs = &bd96801_errb_irqs[0],
+ .num_irqs = ARRAY_SIZE(bd96801_errb_irqs),
+ .status_base = BD96801_REG_INT_SYS_ERRB1,
+ .mask_base = BD96801_REG_MASK_SYS_ERRB,
+ .ack_base = BD96801_REG_INT_SYS_ERRB1,
+ .init_ack_masked = true,
+ .num_regs = 10,
+ .irq_reg_stride = 1,
+ .sub_reg_offsets = &bd96801_errb_sub_irq_offsets[0],
+};
+
+static const struct regmap_irq_chip bd96802_irq_chip_errb = {
+ .name = "bd96802-irq-errb",
+ .domain_suffix = "errb",
+ .main_status = BD96801_REG_INT_MAIN,
+ .num_main_regs = 1,
+ .irqs = &bd96802_errb_irqs[0],
+ .num_irqs = ARRAY_SIZE(bd96802_errb_irqs),
+ .status_base = BD96801_REG_INT_SYS_ERRB1,
+ .mask_base = BD96801_REG_MASK_SYS_ERRB,
+ .ack_base = BD96801_REG_INT_SYS_ERRB1,
+ .init_ack_masked = true,
+ .num_regs = 5,
+ .irq_reg_stride = 1,
+ .sub_reg_offsets = &bd96802_errb_sub_irq_offsets[0],
+};
+
+static const struct regmap_irq_chip bd96801_irq_chip_intb = {
+ .name = "bd96801-irq-intb",
+ .domain_suffix = "intb",
+ .main_status = BD96801_REG_INT_MAIN,
+ .num_main_regs = 1,
+ .irqs = &bd96801_intb_irqs[0],
+ .num_irqs = ARRAY_SIZE(bd96801_intb_irqs),
+ .status_base = BD96801_REG_INT_SYS_INTB,
+ .mask_base = BD96801_REG_MASK_SYS_INTB,
+ .ack_base = BD96801_REG_INT_SYS_INTB,
+ .init_ack_masked = true,
+ .num_regs = 8,
+ .irq_reg_stride = 1,
+};
+
+static const struct regmap_irq_chip bd96802_irq_chip_intb = {
+ .name = "bd96802-irq-intb",
+ .domain_suffix = "intb",
+ .main_status = BD96801_REG_INT_MAIN,
+ .num_main_regs = 1,
+ .irqs = &bd96802_intb_irqs[0],
+ .num_irqs = ARRAY_SIZE(bd96802_intb_irqs),
+ .status_base = BD96801_REG_INT_SYS_INTB,
+ .mask_base = BD96801_REG_MASK_SYS_INTB,
+ .ack_base = BD96801_REG_INT_SYS_INTB,
+ .init_ack_masked = true,
+ .num_regs = 3,
+ .irq_reg_stride = 1,
+};
+
+static const struct regmap_config bd96801_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_table = &bd96801_volatile_regs,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct regmap_config bd96802_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_table = &bd96802_volatile_regs,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct bd968xx bd96801_data = {
+ .errb_irqs = bd96801_reg_errb_irqs,
+ .intb_irqs = bd96801_reg_intb_irqs,
+ .num_errb_irqs = ARRAY_SIZE(bd96801_reg_errb_irqs),
+ .num_intb_irqs = ARRAY_SIZE(bd96801_reg_intb_irqs),
+ .errb_irq_chip = &bd96801_irq_chip_errb,
+ .intb_irq_chip = &bd96801_irq_chip_intb,
+ .regmap_config = &bd96801_regmap_config,
+ .cells = bd96801_cells,
+ .num_cells = ARRAY_SIZE(bd96801_cells),
+ .unlock_reg = BD96801_LOCK_REG,
+ .unlock_val = BD96801_UNLOCK,
+};
+
+static const struct bd968xx bd96802_data = {
+ .errb_irqs = bd96802_reg_errb_irqs,
+ .intb_irqs = bd96802_reg_intb_irqs,
+ .num_errb_irqs = ARRAY_SIZE(bd96802_reg_errb_irqs),
+ .num_intb_irqs = ARRAY_SIZE(bd96802_reg_intb_irqs),
+ .errb_irq_chip = &bd96802_irq_chip_errb,
+ .intb_irq_chip = &bd96802_irq_chip_intb,
+ .regmap_config = &bd96802_regmap_config,
+ .cells = bd96802_cells,
+ .num_cells = ARRAY_SIZE(bd96802_cells),
+ .unlock_reg = BD96801_LOCK_REG,
+ .unlock_val = BD96801_UNLOCK,
+};
+
+static const struct bd968xx bd96805_data = {
+ .errb_irqs = bd96801_reg_errb_irqs,
+ .intb_irqs = bd96801_reg_intb_irqs,
+ .num_errb_irqs = ARRAY_SIZE(bd96801_reg_errb_irqs),
+ .num_intb_irqs = ARRAY_SIZE(bd96801_reg_intb_irqs),
+ .errb_irq_chip = &bd96801_irq_chip_errb,
+ .intb_irq_chip = &bd96801_irq_chip_intb,
+ .regmap_config = &bd96801_regmap_config,
+ .cells = bd96805_cells,
+ .num_cells = ARRAY_SIZE(bd96805_cells),
+ .unlock_reg = BD96801_LOCK_REG,
+ .unlock_val = BD96801_UNLOCK,
+};
+
+static struct bd968xx bd96806_data = {
+ .errb_irqs = bd96802_reg_errb_irqs,
+ .intb_irqs = bd96802_reg_intb_irqs,
+ .num_errb_irqs = ARRAY_SIZE(bd96802_reg_errb_irqs),
+ .num_intb_irqs = ARRAY_SIZE(bd96802_reg_intb_irqs),
+ .errb_irq_chip = &bd96802_irq_chip_errb,
+ .intb_irq_chip = &bd96802_irq_chip_intb,
+ .regmap_config = &bd96802_regmap_config,
+ .cells = bd96806_cells,
+ .num_cells = ARRAY_SIZE(bd96806_cells),
+ .unlock_reg = BD96801_LOCK_REG,
+ .unlock_val = BD96801_UNLOCK,
+};
+
+static int bd96801_i2c_probe(struct i2c_client *i2c)
+{
+ struct regmap_irq_chip_data *intb_irq_data, *errb_irq_data;
+ struct irq_domain *intb_domain, *errb_domain;
+ const struct bd968xx *ddata;
+ const struct fwnode_handle *fwnode;
+ struct resource *regulator_res;
+ struct resource wdg_irq;
+ struct regmap *regmap;
+ int intb_irq, errb_irq, num_errb = 0;
+ int num_regu_irqs, wdg_irq_no;
+ unsigned int chip_type;
+ int i, ret;
+
+ chip_type = (unsigned int)(uintptr_t)device_get_match_data(&i2c->dev);
+ switch (chip_type) {
+ case ROHM_CHIP_TYPE_BD96801:
+ ddata = &bd96801_data;
+ break;
+ case ROHM_CHIP_TYPE_BD96802:
+ ddata = &bd96802_data;
+ break;
+ case ROHM_CHIP_TYPE_BD96805:
+ ddata = &bd96805_data;
+ break;
+ case ROHM_CHIP_TYPE_BD96806:
+ ddata = &bd96806_data;
+ break;
+ default:
+ dev_err(&i2c->dev, "Unknown IC\n");
+ return -EINVAL;
+ }
+
+ fwnode = dev_fwnode(&i2c->dev);
+ if (!fwnode)
+ return dev_err_probe(&i2c->dev, -EINVAL, "Failed to find fwnode\n");
+
+ intb_irq = fwnode_irq_get_byname(fwnode, "intb");
+ if (intb_irq < 0)
+ return dev_err_probe(&i2c->dev, intb_irq, "INTB IRQ not configured\n");
+
+ /* ERRB may be omitted if processor is powered by the PMIC */
+ errb_irq = fwnode_irq_get_byname(fwnode, "errb");
+ if (errb_irq == -EPROBE_DEFER)
+ return errb_irq;
+
+ if (errb_irq > 0)
+ num_errb = ddata->num_errb_irqs;
+
+ num_regu_irqs = ddata->num_intb_irqs + num_errb;
+
+ regulator_res = devm_kcalloc(&i2c->dev, num_regu_irqs,
+ sizeof(*regulator_res), GFP_KERNEL);
+ if (!regulator_res)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(i2c, ddata->regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(regmap),
+ "Regmap initialization failed\n");
+
+ ret = regmap_write(regmap, ddata->unlock_reg, ddata->unlock_val);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Failed to unlock PMIC\n");
+
+ ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, intb_irq,
+ IRQF_ONESHOT, 0, ddata->intb_irq_chip,
+ &intb_irq_data);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Failed to add INTB IRQ chip\n");
+
+ intb_domain = regmap_irq_get_domain(intb_irq_data);
+
+ /*
+ * MFD core code is built to handle only one IRQ domain. BD96801
+ * has two domains so we do IRQ mapping here and provide the
+ * already mapped IRQ numbers to sub-devices.
+ */
+ for (i = 0; i < ddata->num_intb_irqs; i++) {
+ struct resource *res = &regulator_res[i];
+
+ *res = ddata->intb_irqs[i];
+ res->start = res->end = irq_create_mapping(intb_domain,
+ res->start);
+ }
+
+ wdg_irq_no = irq_create_mapping(intb_domain, BD96801_WDT_ERR_STAT);
+ wdg_irq = DEFINE_RES_IRQ_NAMED(wdg_irq_no, "bd96801-wdg");
+
+ ddata->cells[WDG_CELL].resources = &wdg_irq;
+ ddata->cells[WDG_CELL].num_resources = 1;
+
+ if (!num_errb)
+ goto skip_errb;
+
+ ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, errb_irq, IRQF_ONESHOT,
+ 0, ddata->errb_irq_chip, &errb_irq_data);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to add ERRB IRQ chip\n");
+
+ errb_domain = regmap_irq_get_domain(errb_irq_data);
+
+ for (i = 0; i < num_errb; i++) {
+ struct resource *res = &regulator_res[ddata->num_intb_irqs + i];
+
+ *res = ddata->errb_irqs[i];
+ res->start = res->end = irq_create_mapping(errb_domain, res->start);
+ }
+
+skip_errb:
+ ddata->cells[REGULATOR_CELL].resources = regulator_res;
+ ddata->cells[REGULATOR_CELL].num_resources = num_regu_irqs;
+ ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, ddata->cells,
+ ddata->num_cells, NULL, 0, NULL);
+ if (ret)
+ dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n");
+
+ return ret;
+}
+
+static const struct of_device_id bd96801_of_match[] = {
+ { .compatible = "rohm,bd96801", .data = (void *)ROHM_CHIP_TYPE_BD96801 },
+ { .compatible = "rohm,bd96802", .data = (void *)ROHM_CHIP_TYPE_BD96802 },
+ { .compatible = "rohm,bd96805", .data = (void *)ROHM_CHIP_TYPE_BD96805 },
+ { .compatible = "rohm,bd96806", .data = (void *)ROHM_CHIP_TYPE_BD96806 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bd96801_of_match);
+
+static struct i2c_driver bd96801_i2c_driver = {
+ .driver = {
+ .name = "rohm-bd96801",
+ .of_match_table = bd96801_of_match,
+ },
+ .probe = bd96801_i2c_probe,
+};
+
+static int __init bd96801_i2c_init(void)
+{
+ return i2c_add_driver(&bd96801_i2c_driver);
+}
+
+/* Initialise early so consumer devices can complete system boot */
+subsys_initcall(bd96801_i2c_init);
+
+static void __exit bd96801_i2c_exit(void)
+{
+ i2c_del_driver(&bd96801_i2c_driver);
+}
+module_exit(bd96801_i2c_exit);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("ROHM BD9680X Power Management IC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/rsmu_core.c b/drivers/mfd/rsmu_core.c
index 29437fd0bd5b..fd04a6e5dfa3 100644
--- a/drivers/mfd/rsmu_core.c
+++ b/drivers/mfd/rsmu_core.c
@@ -78,11 +78,13 @@ int rsmu_core_init(struct rsmu_ddata *rsmu)
return ret;
}
+EXPORT_SYMBOL_GPL(rsmu_core_init);
void rsmu_core_exit(struct rsmu_ddata *rsmu)
{
mutex_destroy(&rsmu->lock);
}
+EXPORT_SYMBOL_GPL(rsmu_core_exit);
MODULE_DESCRIPTION("Renesas SMU core driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/rsmu_i2c.c b/drivers/mfd/rsmu_i2c.c
index 26972a5aff45..cba64f107a2f 100644
--- a/drivers/mfd/rsmu_i2c.c
+++ b/drivers/mfd/rsmu_i2c.c
@@ -32,6 +32,8 @@
#define RSMU_SABRE_PAGE_ADDR 0x7F
#define RSMU_SABRE_PAGE_WINDOW 128
+typedef int (*rsmu_rw_device)(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u8 bytes);
+
static const struct regmap_range_cfg rsmu_sabre_range_cfg[] = {
{
.range_min = 0,
@@ -54,7 +56,28 @@ static bool rsmu_sabre_volatile_reg(struct device *dev, unsigned int reg)
}
}
-static int rsmu_read_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u16 bytes)
+static int rsmu_smbus_i2c_write_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u8 bytes)
+{
+ struct i2c_client *client = to_i2c_client(rsmu->dev);
+
+ return i2c_smbus_write_i2c_block_data(client, reg, bytes, buf);
+}
+
+static int rsmu_smbus_i2c_read_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u8 bytes)
+{
+ struct i2c_client *client = to_i2c_client(rsmu->dev);
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, bytes, buf);
+ if (ret == bytes)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int rsmu_i2c_read_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u8 bytes)
{
struct i2c_client *client = to_i2c_client(rsmu->dev);
struct i2c_msg msg[2];
@@ -84,10 +107,11 @@ static int rsmu_read_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u16 bytes)
return 0;
}
-static int rsmu_write_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u16 bytes)
+static int rsmu_i2c_write_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u8 bytes)
{
struct i2c_client *client = to_i2c_client(rsmu->dev);
- u8 msg[RSMU_MAX_WRITE_COUNT + 1]; /* 1 Byte added for the device register */
+ /* we add 1 byte for device register */
+ u8 msg[RSMU_MAX_WRITE_COUNT + 1];
int cnt;
if (bytes > RSMU_MAX_WRITE_COUNT)
@@ -107,7 +131,8 @@ static int rsmu_write_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u16 bytes
return 0;
}
-static int rsmu_write_page_register(struct rsmu_ddata *rsmu, u32 reg)
+static int rsmu_write_page_register(struct rsmu_ddata *rsmu, u32 reg,
+ rsmu_rw_device rsmu_write_device)
{
u32 page = reg & RSMU_CM_PAGE_MASK;
u8 buf[4];
@@ -136,35 +161,35 @@ static int rsmu_write_page_register(struct rsmu_ddata *rsmu, u32 reg)
return err;
}
-static int rsmu_reg_read(void *context, unsigned int reg, unsigned int *val)
+static int rsmu_i2c_reg_read(void *context, unsigned int reg, unsigned int *val)
{
struct rsmu_ddata *rsmu = i2c_get_clientdata((struct i2c_client *)context);
u8 addr = (u8)(reg & RSMU_CM_ADDRESS_MASK);
int err;
- err = rsmu_write_page_register(rsmu, reg);
+ err = rsmu_write_page_register(rsmu, reg, rsmu_i2c_write_device);
if (err)
return err;
- err = rsmu_read_device(rsmu, addr, (u8 *)val, 1);
+ err = rsmu_i2c_read_device(rsmu, addr, (u8 *)val, 1);
if (err)
dev_err(rsmu->dev, "Failed to read offset address 0x%x\n", addr);
return err;
}
-static int rsmu_reg_write(void *context, unsigned int reg, unsigned int val)
+static int rsmu_i2c_reg_write(void *context, unsigned int reg, unsigned int val)
{
struct rsmu_ddata *rsmu = i2c_get_clientdata((struct i2c_client *)context);
u8 addr = (u8)(reg & RSMU_CM_ADDRESS_MASK);
u8 data = (u8)val;
int err;
- err = rsmu_write_page_register(rsmu, reg);
+ err = rsmu_write_page_register(rsmu, reg, rsmu_i2c_write_device);
if (err)
return err;
- err = rsmu_write_device(rsmu, addr, &data, 1);
+ err = rsmu_i2c_write_device(rsmu, addr, &data, 1);
if (err)
dev_err(rsmu->dev,
"Failed to write offset address 0x%x\n", addr);
@@ -172,12 +197,57 @@ static int rsmu_reg_write(void *context, unsigned int reg, unsigned int val)
return err;
}
-static const struct regmap_config rsmu_cm_regmap_config = {
+static int rsmu_smbus_i2c_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct rsmu_ddata *rsmu = i2c_get_clientdata((struct i2c_client *)context);
+ u8 addr = (u8)(reg & RSMU_CM_ADDRESS_MASK);
+ int err;
+
+ err = rsmu_write_page_register(rsmu, reg, rsmu_smbus_i2c_write_device);
+ if (err)
+ return err;
+
+ err = rsmu_smbus_i2c_read_device(rsmu, addr, (u8 *)val, 1);
+ if (err)
+ dev_err(rsmu->dev, "Failed to read offset address 0x%x\n", addr);
+
+ return err;
+}
+
+static int rsmu_smbus_i2c_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct rsmu_ddata *rsmu = i2c_get_clientdata((struct i2c_client *)context);
+ u8 addr = (u8)(reg & RSMU_CM_ADDRESS_MASK);
+ u8 data = (u8)val;
+ int err;
+
+ err = rsmu_write_page_register(rsmu, reg, rsmu_smbus_i2c_write_device);
+ if (err)
+ return err;
+
+ err = rsmu_smbus_i2c_write_device(rsmu, addr, &data, 1);
+ if (err)
+ dev_err(rsmu->dev,
+ "Failed to write offset address 0x%x\n", addr);
+
+ return err;
+}
+
+static const struct regmap_config rsmu_i2c_cm_regmap_config = {
.reg_bits = 32,
.val_bits = 8,
.max_register = 0x20120000,
- .reg_read = rsmu_reg_read,
- .reg_write = rsmu_reg_write,
+ .reg_read = rsmu_i2c_reg_read,
+ .reg_write = rsmu_i2c_reg_write,
+ .cache_type = REGCACHE_NONE,
+};
+
+static const struct regmap_config rsmu_smbus_i2c_cm_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .max_register = 0x20120000,
+ .reg_read = rsmu_smbus_i2c_reg_read,
+ .reg_write = rsmu_smbus_i2c_reg_write,
.cache_type = REGCACHE_NONE,
};
@@ -188,7 +258,7 @@ static const struct regmap_config rsmu_sabre_regmap_config = {
.ranges = rsmu_sabre_range_cfg,
.num_ranges = ARRAY_SIZE(rsmu_sabre_range_cfg),
.volatile_reg = rsmu_sabre_volatile_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.can_multi_write = true,
};
@@ -219,7 +289,15 @@ static int rsmu_i2c_probe(struct i2c_client *client)
switch (rsmu->type) {
case RSMU_CM:
- cfg = &rsmu_cm_regmap_config;
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ cfg = &rsmu_i2c_cm_regmap_config;
+ } else if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ cfg = &rsmu_smbus_i2c_cm_regmap_config;
+ } else {
+ dev_err(rsmu->dev, "Unsupported i2c adapter\n");
+ return -ENOTSUPP;
+ }
break;
case RSMU_SABRE:
cfg = &rsmu_sabre_regmap_config;
@@ -236,6 +314,7 @@ static int rsmu_i2c_probe(struct i2c_client *client)
rsmu->regmap = devm_regmap_init(&client->dev, NULL, client, cfg);
else
rsmu->regmap = devm_regmap_init_i2c(client, cfg);
+
if (IS_ERR(rsmu->regmap)) {
ret = PTR_ERR(rsmu->regmap);
dev_err(rsmu->dev, "Failed to allocate register map: %d\n", ret);
@@ -277,7 +356,7 @@ MODULE_DEVICE_TABLE(of, rsmu_i2c_of_match);
static struct i2c_driver rsmu_i2c_driver = {
.driver = {
.name = "rsmu-i2c",
- .of_match_table = of_match_ptr(rsmu_i2c_of_match),
+ .of_match_table = rsmu_i2c_of_match,
},
.probe = rsmu_i2c_probe,
.remove = rsmu_i2c_remove,
diff --git a/drivers/mfd/rsmu_spi.c b/drivers/mfd/rsmu_spi.c
index a4a595bb8d0d..39d9be1e141f 100644
--- a/drivers/mfd/rsmu_spi.c
+++ b/drivers/mfd/rsmu_spi.c
@@ -106,10 +106,10 @@ static int rsmu_write_page_register(struct rsmu_ddata *rsmu, u32 reg)
return 0;
page_reg = RSMU_CM_PAGE_ADDR;
page = reg & RSMU_PAGE_MASK;
- buf[0] = (u8)(page & 0xff);
- buf[1] = (u8)((page >> 8) & 0xff);
- buf[2] = (u8)((page >> 16) & 0xff);
- buf[3] = (u8)((page >> 24) & 0xff);
+ buf[0] = (u8)(page & 0xFF);
+ buf[1] = (u8)((page >> 8) & 0xFF);
+ buf[2] = (u8)((page >> 16) & 0xFF);
+ buf[3] = (u8)((page >> 24) & 0xFF);
bytes = 4;
break;
case RSMU_SABRE:
@@ -262,7 +262,7 @@ MODULE_DEVICE_TABLE(of, rsmu_spi_of_match);
static struct spi_driver rsmu_spi_driver = {
.driver = {
.name = "rsmu-spi",
- .of_match_table = of_match_ptr(rsmu_spi_of_match),
+ .of_match_table = rsmu_spi_of_match,
},
.probe = rsmu_spi_probe,
.remove = rsmu_spi_remove,
diff --git a/drivers/mfd/rt4831.c b/drivers/mfd/rt4831.c
index f8d6dc55b558..1ab8870e4ebf 100644
--- a/drivers/mfd/rt4831.c
+++ b/drivers/mfd/rt4831.c
@@ -115,4 +115,5 @@ static struct i2c_driver rt4831_driver = {
module_i2c_driver(rt4831_driver);
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("Richtek RT4831 core driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/rt5033.c b/drivers/mfd/rt5033.c
index 67b0a228db24..2204bf1c5a51 100644
--- a/drivers/mfd/rt5033.c
+++ b/drivers/mfd/rt5033.c
@@ -10,9 +10,9 @@
*/
#include <linux/err.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/interrupt.h>
-#include <linux/of_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/rt5033.h>
#include <linux/mfd/rt5033-private.h>
@@ -81,8 +81,8 @@ static int rt5033_i2c_probe(struct i2c_client *i2c)
chip_rev = dev_id & RT5033_CHIP_REV_MASK;
dev_info(&i2c->dev, "Device found (rev. %d)\n", chip_rev);
- ret = regmap_add_irq_chip(rt5033->regmap, rt5033->irq,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ ret = devm_regmap_add_irq_chip(rt5033->dev, rt5033->regmap,
+ rt5033->irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
0, &rt5033_irq_chip, &rt5033->irq_data);
if (ret) {
dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n",
@@ -98,7 +98,11 @@ static int rt5033_i2c_probe(struct i2c_client *i2c)
return ret;
}
- device_init_wakeup(rt5033->dev, rt5033->wakeup);
+ if (rt5033->wakeup) {
+ ret = devm_device_init_wakeup(rt5033->dev);
+ if (ret)
+ return dev_err_probe(rt5033->dev, ret, "Failed to init wakeup\n");
+ }
return 0;
}
diff --git a/drivers/mfd/rz-mtu3.c b/drivers/mfd/rz-mtu3.c
index 04006f4aa702..9cdfef610398 100644
--- a/drivers/mfd/rz-mtu3.c
+++ b/drivers/mfd/rz-mtu3.c
@@ -11,7 +11,9 @@
#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/mfd/rz-mtu3.h>
-#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/spinlock.h>
@@ -20,7 +22,7 @@
struct rz_mtu3_priv {
void __iomem *mmio;
struct reset_control *rstc;
- raw_spinlock_t lock;
+ spinlock_t lock;
};
/******* MTU3 registers (original offset is +0x1200) *******/
@@ -30,7 +32,7 @@ static const unsigned long rz_mtu3_8bit_ch_reg_offs[][13] = {
[RZ_MTU3_CHAN_2] = MTU_8BIT_CH_1_2(0x204, 0x092, 0x205, 0x200, 0x20c, 0x201, 0x202),
[RZ_MTU3_CHAN_3] = MTU_8BIT_CH_3_4_6_7(0x008, 0x093, 0x02c, 0x000, 0x04c, 0x002, 0x004, 0x005, 0x038),
[RZ_MTU3_CHAN_4] = MTU_8BIT_CH_3_4_6_7(0x009, 0x094, 0x02d, 0x001, 0x04d, 0x003, 0x006, 0x007, 0x039),
- [RZ_MTU3_CHAN_5] = MTU_8BIT_CH_5(0xab2, 0x1eb, 0xab4, 0xab6, 0xa84, 0xa85, 0xa86, 0xa94, 0xa95, 0xa96, 0xaa4, 0xaa5, 0xaa6),
+ [RZ_MTU3_CHAN_5] = MTU_8BIT_CH_5(0xab2, 0x895, 0xab4, 0xab6, 0xa84, 0xa85, 0xa86, 0xa94, 0xa95, 0xa96, 0xaa4, 0xaa5, 0xaa6),
[RZ_MTU3_CHAN_6] = MTU_8BIT_CH_3_4_6_7(0x808, 0x893, 0x82c, 0x800, 0x84c, 0x802, 0x804, 0x805, 0x838),
[RZ_MTU3_CHAN_7] = MTU_8BIT_CH_3_4_6_7(0x809, 0x894, 0x82d, 0x801, 0x84d, 0x803, 0x806, 0x807, 0x839),
[RZ_MTU3_CHAN_8] = MTU_8BIT_CH_8(0x404, 0x098, 0x400, 0x406, 0x401, 0x402, 0x403)
@@ -174,11 +176,11 @@ void rz_mtu3_shared_reg_update_bit(struct rz_mtu3_channel *ch, u16 offset,
struct rz_mtu3_priv *priv = mtu->priv_data;
unsigned long tmdr, flags;
- raw_spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
tmdr = rz_mtu3_shared_reg_read(ch, offset);
__assign_bit(pos, &tmdr, !!val);
rz_mtu3_shared_reg_write(ch, offset, tmdr);
- raw_spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
}
EXPORT_SYMBOL_GPL(rz_mtu3_shared_reg_update_bit);
@@ -250,16 +252,17 @@ static void rz_mtu3_start_stop_ch(struct rz_mtu3_channel *ch, bool start)
u16 offset;
u8 bitpos;
- /* start stop register shared by multiple timer channels */
- raw_spin_lock_irqsave(&priv->lock, flags);
-
offset = rz_mtu3_get_tstr_offset(ch);
bitpos = rz_mtu3_get_tstr_bit_pos(ch);
+
+ /* start stop register shared by multiple timer channels */
+ spin_lock_irqsave(&priv->lock, flags);
+
tstr = rz_mtu3_shared_reg_read(ch, offset);
__assign_bit(bitpos, &tstr, start);
rz_mtu3_shared_reg_write(ch, offset, tstr);
- raw_spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
}
bool rz_mtu3_is_enabled(struct rz_mtu3_channel *ch)
@@ -267,21 +270,18 @@ bool rz_mtu3_is_enabled(struct rz_mtu3_channel *ch)
struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent);
struct rz_mtu3_priv *priv = mtu->priv_data;
unsigned long flags, tstr;
- bool ret = false;
u16 offset;
u8 bitpos;
- /* start stop register shared by multiple timer channels */
- raw_spin_lock_irqsave(&priv->lock, flags);
-
offset = rz_mtu3_get_tstr_offset(ch);
bitpos = rz_mtu3_get_tstr_bit_pos(ch);
- tstr = rz_mtu3_shared_reg_read(ch, offset);
- ret = tstr & BIT(bitpos);
- raw_spin_unlock_irqrestore(&priv->lock, flags);
+ /* start stop register shared by multiple timer channels */
+ spin_lock_irqsave(&priv->lock, flags);
+ tstr = rz_mtu3_shared_reg_read(ch, offset);
+ spin_unlock_irqrestore(&priv->lock, flags);
- return ret;
+ return tstr & BIT(bitpos);
}
EXPORT_SYMBOL_GPL(rz_mtu3_is_enabled);
@@ -349,7 +349,7 @@ static int rz_mtu3_probe(struct platform_device *pdev)
return PTR_ERR(ddata->clk);
reset_control_deassert(priv->rstc);
- raw_spin_lock_init(&priv->lock);
+ spin_lock_init(&priv->lock);
platform_set_drvdata(pdev, ddata);
for (i = 0; i < RZ_MTU_NUM_CHANNELS; i++) {
diff --git a/drivers/mfd/sec-acpm.c b/drivers/mfd/sec-acpm.c
new file mode 100644
index 000000000000..36622069a788
--- /dev/null
+++ b/drivers/mfd/sec-acpm.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2020 Google Inc
+ * Copyright 2025 Linaro Ltd.
+ *
+ * Samsung S2MPG1x ACPM driver
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/firmware/samsung/exynos-acpm-protocol.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/rtc.h>
+#include <linux/mfd/samsung/s2mpg10.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include "sec-core.h"
+
+#define ACPM_ADDR_BITS 8
+#define ACPM_MAX_BULK_DATA 8
+
+struct sec_pmic_acpm_platform_data {
+ int device_type;
+
+ unsigned int acpm_chan_id;
+ u8 speedy_channel;
+
+ const struct regmap_config *regmap_cfg_common;
+ const struct regmap_config *regmap_cfg_pmic;
+ const struct regmap_config *regmap_cfg_rtc;
+ const struct regmap_config *regmap_cfg_meter;
+};
+
+static const struct regmap_range s2mpg10_common_registers[] = {
+ regmap_reg_range(0x00, 0x02), /* CHIP_ID_M, INT, INT_MASK */
+ regmap_reg_range(0x0a, 0x0c), /* Speedy control */
+ regmap_reg_range(0x1a, 0x2a), /* Debug */
+};
+
+static const struct regmap_range s2mpg10_common_ro_registers[] = {
+ regmap_reg_range(0x00, 0x01), /* CHIP_ID_M, INT */
+ regmap_reg_range(0x28, 0x2a), /* Debug */
+};
+
+static const struct regmap_range s2mpg10_common_nonvolatile_registers[] = {
+ regmap_reg_range(0x00, 0x00), /* CHIP_ID_M */
+ regmap_reg_range(0x02, 0x02), /* INT_MASK */
+ regmap_reg_range(0x0a, 0x0c), /* Speedy control */
+};
+
+static const struct regmap_range s2mpg10_common_precious_registers[] = {
+ regmap_reg_range(0x01, 0x01), /* INT */
+};
+
+static const struct regmap_access_table s2mpg10_common_wr_table = {
+ .yes_ranges = s2mpg10_common_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg10_common_registers),
+ .no_ranges = s2mpg10_common_ro_registers,
+ .n_no_ranges = ARRAY_SIZE(s2mpg10_common_ro_registers),
+};
+
+static const struct regmap_access_table s2mpg10_common_rd_table = {
+ .yes_ranges = s2mpg10_common_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg10_common_registers),
+};
+
+static const struct regmap_access_table s2mpg10_common_volatile_table = {
+ .no_ranges = s2mpg10_common_nonvolatile_registers,
+ .n_no_ranges = ARRAY_SIZE(s2mpg10_common_nonvolatile_registers),
+};
+
+static const struct regmap_access_table s2mpg10_common_precious_table = {
+ .yes_ranges = s2mpg10_common_precious_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg10_common_precious_registers),
+};
+
+static const struct regmap_config s2mpg10_regmap_config_common = {
+ .name = "common",
+ .reg_bits = ACPM_ADDR_BITS,
+ .val_bits = 8,
+ .max_register = S2MPG10_COMMON_SPD_DEBUG4,
+ .wr_table = &s2mpg10_common_wr_table,
+ .rd_table = &s2mpg10_common_rd_table,
+ .volatile_table = &s2mpg10_common_volatile_table,
+ .precious_table = &s2mpg10_common_precious_table,
+ .num_reg_defaults_raw = S2MPG10_COMMON_SPD_DEBUG4 + 1,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_range s2mpg10_pmic_registers[] = {
+ regmap_reg_range(0x00, 0xf6), /* All PMIC registers */
+};
+
+static const struct regmap_range s2mpg10_pmic_ro_registers[] = {
+ regmap_reg_range(0x00, 0x05), /* INTx */
+ regmap_reg_range(0x0c, 0x0f), /* STATUSx PWRONSRC OFFSRC */
+ regmap_reg_range(0xc7, 0xc7), /* GPIO input */
+};
+
+static const struct regmap_range s2mpg10_pmic_nonvolatile_registers[] = {
+ regmap_reg_range(0x06, 0x0b), /* INTxM */
+};
+
+static const struct regmap_range s2mpg10_pmic_precious_registers[] = {
+ regmap_reg_range(0x00, 0x05), /* INTx */
+};
+
+static const struct regmap_access_table s2mpg10_pmic_wr_table = {
+ .yes_ranges = s2mpg10_pmic_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg10_pmic_registers),
+ .no_ranges = s2mpg10_pmic_ro_registers,
+ .n_no_ranges = ARRAY_SIZE(s2mpg10_pmic_ro_registers),
+};
+
+static const struct regmap_access_table s2mpg10_pmic_rd_table = {
+ .yes_ranges = s2mpg10_pmic_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg10_pmic_registers),
+};
+
+static const struct regmap_access_table s2mpg10_pmic_volatile_table = {
+ .no_ranges = s2mpg10_pmic_nonvolatile_registers,
+ .n_no_ranges = ARRAY_SIZE(s2mpg10_pmic_nonvolatile_registers),
+};
+
+static const struct regmap_access_table s2mpg10_pmic_precious_table = {
+ .yes_ranges = s2mpg10_pmic_precious_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg10_pmic_precious_registers),
+};
+
+static const struct regmap_config s2mpg10_regmap_config_pmic = {
+ .name = "pmic",
+ .reg_bits = ACPM_ADDR_BITS,
+ .val_bits = 8,
+ .max_register = S2MPG10_PMIC_LDO_SENSE4,
+ .wr_table = &s2mpg10_pmic_wr_table,
+ .rd_table = &s2mpg10_pmic_rd_table,
+ .volatile_table = &s2mpg10_pmic_volatile_table,
+ .precious_table = &s2mpg10_pmic_precious_table,
+ .num_reg_defaults_raw = S2MPG10_PMIC_LDO_SENSE4 + 1,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_range s2mpg10_rtc_registers[] = {
+ regmap_reg_range(0x00, 0x2b), /* All RTC registers */
+};
+
+static const struct regmap_range s2mpg10_rtc_volatile_registers[] = {
+ regmap_reg_range(0x01, 0x01), /* RTC_UPDATE */
+ regmap_reg_range(0x05, 0x0c), /* Time / date */
+};
+
+static const struct regmap_access_table s2mpg10_rtc_rd_table = {
+ .yes_ranges = s2mpg10_rtc_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg10_rtc_registers),
+};
+
+static const struct regmap_access_table s2mpg10_rtc_volatile_table = {
+ .yes_ranges = s2mpg10_rtc_volatile_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg10_rtc_volatile_registers),
+};
+
+static const struct regmap_config s2mpg10_regmap_config_rtc = {
+ .name = "rtc",
+ .reg_bits = ACPM_ADDR_BITS,
+ .val_bits = 8,
+ .max_register = S2MPG10_RTC_OSC_CTRL,
+ .rd_table = &s2mpg10_rtc_rd_table,
+ .volatile_table = &s2mpg10_rtc_volatile_table,
+ .num_reg_defaults_raw = S2MPG10_RTC_OSC_CTRL + 1,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_range s2mpg10_meter_registers[] = {
+ regmap_reg_range(0x00, 0x21), /* Meter config */
+ regmap_reg_range(0x40, 0x8a), /* Meter data */
+ regmap_reg_range(0xee, 0xee), /* Offset */
+ regmap_reg_range(0xf1, 0xf1), /* Trim */
+};
+
+static const struct regmap_range s2mpg10_meter_ro_registers[] = {
+ regmap_reg_range(0x40, 0x8a), /* Meter data */
+};
+
+static const struct regmap_access_table s2mpg10_meter_wr_table = {
+ .yes_ranges = s2mpg10_meter_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg10_meter_registers),
+ .no_ranges = s2mpg10_meter_ro_registers,
+ .n_no_ranges = ARRAY_SIZE(s2mpg10_meter_ro_registers),
+};
+
+static const struct regmap_access_table s2mpg10_meter_rd_table = {
+ .yes_ranges = s2mpg10_meter_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg10_meter_registers),
+};
+
+static const struct regmap_access_table s2mpg10_meter_volatile_table = {
+ .yes_ranges = s2mpg10_meter_ro_registers,
+ .n_yes_ranges = ARRAY_SIZE(s2mpg10_meter_ro_registers),
+};
+
+static const struct regmap_config s2mpg10_regmap_config_meter = {
+ .name = "meter",
+ .reg_bits = ACPM_ADDR_BITS,
+ .val_bits = 8,
+ .max_register = S2MPG10_METER_BUCK_METER_TRIM3,
+ .wr_table = &s2mpg10_meter_wr_table,
+ .rd_table = &s2mpg10_meter_rd_table,
+ .volatile_table = &s2mpg10_meter_volatile_table,
+ .num_reg_defaults_raw = S2MPG10_METER_BUCK_METER_TRIM3 + 1,
+ .cache_type = REGCACHE_FLAT,
+};
+
+struct sec_pmic_acpm_shared_bus_context {
+ const struct acpm_handle *acpm;
+ unsigned int acpm_chan_id;
+ u8 speedy_channel;
+};
+
+enum sec_pmic_acpm_accesstype {
+ SEC_PMIC_ACPM_ACCESSTYPE_COMMON = 0x00,
+ SEC_PMIC_ACPM_ACCESSTYPE_PMIC = 0x01,
+ SEC_PMIC_ACPM_ACCESSTYPE_RTC = 0x02,
+ SEC_PMIC_ACPM_ACCESSTYPE_METER = 0x0a,
+ SEC_PMIC_ACPM_ACCESSTYPE_WLWP = 0x0b,
+ SEC_PMIC_ACPM_ACCESSTYPE_TRIM = 0x0f,
+};
+
+struct sec_pmic_acpm_bus_context {
+ struct sec_pmic_acpm_shared_bus_context *shared;
+ enum sec_pmic_acpm_accesstype type;
+};
+
+static int sec_pmic_acpm_bus_write(void *context, const void *data,
+ size_t count)
+{
+ struct sec_pmic_acpm_bus_context *ctx = context;
+ const struct acpm_handle *acpm = ctx->shared->acpm;
+ const struct acpm_pmic_ops *pmic_ops = &acpm->ops.pmic_ops;
+ size_t val_count = count - BITS_TO_BYTES(ACPM_ADDR_BITS);
+ const u8 *d = data;
+ const u8 *vals = &d[BITS_TO_BYTES(ACPM_ADDR_BITS)];
+ u8 reg;
+
+ if (val_count < 1 || val_count > ACPM_MAX_BULK_DATA)
+ return -EINVAL;
+
+ reg = d[0];
+
+ return pmic_ops->bulk_write(acpm, ctx->shared->acpm_chan_id, ctx->type, reg,
+ ctx->shared->speedy_channel, val_count, vals);
+}
+
+static int sec_pmic_acpm_bus_read(void *context, const void *reg_buf, size_t reg_size,
+ void *val_buf, size_t val_size)
+{
+ struct sec_pmic_acpm_bus_context *ctx = context;
+ const struct acpm_handle *acpm = ctx->shared->acpm;
+ const struct acpm_pmic_ops *pmic_ops = &acpm->ops.pmic_ops;
+ const u8 *r = reg_buf;
+ u8 reg;
+
+ if (reg_size != BITS_TO_BYTES(ACPM_ADDR_BITS) || !val_size ||
+ val_size > ACPM_MAX_BULK_DATA)
+ return -EINVAL;
+
+ reg = r[0];
+
+ return pmic_ops->bulk_read(acpm, ctx->shared->acpm_chan_id, ctx->type, reg,
+ ctx->shared->speedy_channel, val_size, val_buf);
+}
+
+static int sec_pmic_acpm_bus_reg_update_bits(void *context, unsigned int reg, unsigned int mask,
+ unsigned int val)
+{
+ struct sec_pmic_acpm_bus_context *ctx = context;
+ const struct acpm_handle *acpm = ctx->shared->acpm;
+ const struct acpm_pmic_ops *pmic_ops = &acpm->ops.pmic_ops;
+
+ return pmic_ops->update_reg(acpm, ctx->shared->acpm_chan_id, ctx->type, reg & 0xff,
+ ctx->shared->speedy_channel, val, mask);
+}
+
+static const struct regmap_bus sec_pmic_acpm_regmap_bus = {
+ .write = sec_pmic_acpm_bus_write,
+ .read = sec_pmic_acpm_bus_read,
+ .reg_update_bits = sec_pmic_acpm_bus_reg_update_bits,
+ .max_raw_read = ACPM_MAX_BULK_DATA,
+ .max_raw_write = ACPM_MAX_BULK_DATA,
+};
+
+static struct regmap *sec_pmic_acpm_regmap_init(struct device *dev,
+ struct sec_pmic_acpm_shared_bus_context *shared_ctx,
+ enum sec_pmic_acpm_accesstype type,
+ const struct regmap_config *cfg, bool do_attach)
+{
+ struct sec_pmic_acpm_bus_context *ctx;
+ struct regmap *regmap;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ ctx->shared = shared_ctx;
+ ctx->type = type;
+
+ regmap = devm_regmap_init(dev, &sec_pmic_acpm_regmap_bus, ctx, cfg);
+ if (IS_ERR(regmap))
+ return dev_err_cast_probe(dev, regmap, "regmap init (%s) failed\n", cfg->name);
+
+ if (do_attach) {
+ int ret;
+
+ ret = regmap_attach_dev(dev, regmap, cfg);
+ if (ret)
+ return dev_err_ptr_probe(dev, ret, "regmap attach (%s) failed\n",
+ cfg->name);
+ }
+
+ return regmap;
+}
+
+static int sec_pmic_acpm_probe(struct platform_device *pdev)
+{
+ struct regmap *regmap_common, *regmap_pmic, *regmap;
+ const struct sec_pmic_acpm_platform_data *pdata;
+ struct sec_pmic_acpm_shared_bus_context *shared_ctx;
+ const struct acpm_handle *acpm;
+ struct device *dev = &pdev->dev;
+ int ret, irq;
+
+ pdata = device_get_match_data(dev);
+ if (!pdata)
+ return dev_err_probe(dev, -ENODEV, "unsupported device type\n");
+
+ acpm = devm_acpm_get_by_node(dev, dev->parent->of_node);
+ if (IS_ERR(acpm))
+ return dev_err_probe(dev, PTR_ERR(acpm), "failed to get acpm\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ shared_ctx = devm_kzalloc(dev, sizeof(*shared_ctx), GFP_KERNEL);
+ if (!shared_ctx)
+ return -ENOMEM;
+
+ shared_ctx->acpm = acpm;
+ shared_ctx->acpm_chan_id = pdata->acpm_chan_id;
+ shared_ctx->speedy_channel = pdata->speedy_channel;
+
+ regmap_common = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_COMMON,
+ pdata->regmap_cfg_common, true);
+ if (IS_ERR(regmap_common))
+ return PTR_ERR(regmap_common);
+
+ regmap_pmic = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_PMIC,
+ pdata->regmap_cfg_pmic, false);
+ if (IS_ERR(regmap_pmic))
+ return PTR_ERR(regmap_pmic);
+
+ regmap = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_RTC,
+ pdata->regmap_cfg_rtc, true);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ regmap = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_METER,
+ pdata->regmap_cfg_meter, true);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = sec_pmic_probe(dev, pdata->device_type, irq, regmap_pmic, NULL);
+ if (ret)
+ return ret;
+
+ if (device_property_read_bool(dev, "wakeup-source"))
+ devm_device_init_wakeup(dev);
+
+ return 0;
+}
+
+static void sec_pmic_acpm_shutdown(struct platform_device *pdev)
+{
+ sec_pmic_shutdown(&pdev->dev);
+}
+
+static const struct sec_pmic_acpm_platform_data s2mpg10_data = {
+ .device_type = S2MPG10,
+ .acpm_chan_id = 2,
+ .speedy_channel = 0,
+ .regmap_cfg_common = &s2mpg10_regmap_config_common,
+ .regmap_cfg_pmic = &s2mpg10_regmap_config_pmic,
+ .regmap_cfg_rtc = &s2mpg10_regmap_config_rtc,
+ .regmap_cfg_meter = &s2mpg10_regmap_config_meter,
+};
+
+static const struct of_device_id sec_pmic_acpm_of_match[] = {
+ { .compatible = "samsung,s2mpg10-pmic", .data = &s2mpg10_data, },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sec_pmic_acpm_of_match);
+
+static struct platform_driver sec_pmic_acpm_driver = {
+ .driver = {
+ .name = "sec-pmic-acpm",
+ .pm = pm_sleep_ptr(&sec_pmic_pm_ops),
+ .of_match_table = sec_pmic_acpm_of_match,
+ },
+ .probe = sec_pmic_acpm_probe,
+ .shutdown = sec_pmic_acpm_shutdown,
+};
+module_platform_driver(sec_pmic_acpm_driver);
+
+MODULE_AUTHOR("André Draszik <andre.draszik@linaro.org>");
+MODULE_DESCRIPTION("ACPM driver for the Samsung S2MPG1x");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/sec-common.c b/drivers/mfd/sec-common.c
new file mode 100644
index 000000000000..42d55e70e34c
--- /dev/null
+++ b/drivers/mfd/sec-common.c
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2012 Samsung Electronics Co., Ltd
+ * http://www.samsung.com
+ * Copyright 2025 Linaro Ltd.
+ *
+ * Samsung SxM core driver
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/irq.h>
+#include <linux/mfd/samsung/s2mps11.h>
+#include <linux/mfd/samsung/s2mps13.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include "sec-core.h"
+
+static const struct mfd_cell s5m8767_devs[] = {
+ MFD_CELL_NAME("s5m8767-pmic"),
+ MFD_CELL_NAME("s5m-rtc"),
+ MFD_CELL_OF("s5m8767-clk", NULL, NULL, 0, 0, "samsung,s5m8767-clk"),
+};
+
+static const struct mfd_cell s2dos05_devs[] = {
+ MFD_CELL_NAME("s2dos05-regulator"),
+};
+
+static const struct mfd_cell s2mpg10_devs[] = {
+ MFD_CELL_NAME("s2mpg10-meter"),
+ MFD_CELL_NAME("s2mpg10-regulator"),
+ MFD_CELL_NAME("s2mpg10-rtc"),
+ MFD_CELL_OF("s2mpg10-clk", NULL, NULL, 0, 0, "samsung,s2mpg10-clk"),
+ MFD_CELL_OF("s2mpg10-gpio", NULL, NULL, 0, 0, "samsung,s2mpg10-gpio"),
+};
+
+static const struct mfd_cell s2mps11_devs[] = {
+ MFD_CELL_NAME("s2mps11-regulator"),
+ MFD_CELL_NAME("s2mps14-rtc"),
+ MFD_CELL_OF("s2mps11-clk", NULL, NULL, 0, 0, "samsung,s2mps11-clk"),
+};
+
+static const struct mfd_cell s2mps13_devs[] = {
+ MFD_CELL_NAME("s2mps13-regulator"),
+ MFD_CELL_NAME("s2mps13-rtc"),
+ MFD_CELL_OF("s2mps13-clk", NULL, NULL, 0, 0, "samsung,s2mps13-clk"),
+};
+
+static const struct mfd_cell s2mps14_devs[] = {
+ MFD_CELL_NAME("s2mps14-regulator"),
+ MFD_CELL_NAME("s2mps14-rtc"),
+ MFD_CELL_OF("s2mps14-clk", NULL, NULL, 0, 0, "samsung,s2mps14-clk"),
+};
+
+static const struct mfd_cell s2mps15_devs[] = {
+ MFD_CELL_NAME("s2mps15-regulator"),
+ MFD_CELL_NAME("s2mps15-rtc"),
+ MFD_CELL_OF("s2mps13-clk", NULL, NULL, 0, 0, "samsung,s2mps13-clk"),
+};
+
+static const struct mfd_cell s2mpa01_devs[] = {
+ MFD_CELL_NAME("s2mpa01-pmic"),
+ MFD_CELL_NAME("s2mps14-rtc"),
+};
+
+static const struct mfd_cell s2mpu02_devs[] = {
+ MFD_CELL_NAME("s2mpu02-regulator"),
+};
+
+static const struct mfd_cell s2mpu05_devs[] = {
+ MFD_CELL_NAME("s2mpu05-regulator"),
+ MFD_CELL_NAME("s2mps15-rtc"),
+};
+
+static void sec_pmic_dump_rev(struct sec_pmic_dev *sec_pmic)
+{
+ unsigned int val;
+
+ /* For s2mpg1x, the revision is in a different regmap */
+ if (sec_pmic->device_type == S2MPG10)
+ return;
+
+ /* For each device type, the REG_ID is always the first register */
+ if (!regmap_read(sec_pmic->regmap_pmic, S2MPS11_REG_ID, &val))
+ dev_dbg(sec_pmic->dev, "Revision: 0x%x\n", val);
+}
+
+static void sec_pmic_configure(struct sec_pmic_dev *sec_pmic)
+{
+ int err;
+
+ if (sec_pmic->device_type != S2MPS13X)
+ return;
+
+ if (sec_pmic->pdata->disable_wrstbi) {
+ /*
+ * If WRSTBI pin is pulled down this feature must be disabled
+ * because each Suspend to RAM will trigger buck voltage reset
+ * to default values.
+ */
+ err = regmap_update_bits(sec_pmic->regmap_pmic,
+ S2MPS13_REG_WRSTBI,
+ S2MPS13_REG_WRSTBI_MASK, 0x0);
+ if (err)
+ dev_warn(sec_pmic->dev,
+ "Cannot initialize WRSTBI config: %d\n",
+ err);
+ }
+}
+
+/*
+ * Only the common platform data elements for s5m8767 are parsed here from the
+ * device tree. Other sub-modules of s5m8767 such as pmic, rtc , charger and
+ * others have to parse their own platform data elements from device tree.
+ *
+ * The s5m8767 platform data structure is instantiated here and the drivers for
+ * the sub-modules need not instantiate another instance while parsing their
+ * platform data.
+ */
+static struct sec_platform_data *
+sec_pmic_parse_dt_pdata(struct device *dev)
+{
+ struct sec_platform_data *pd;
+
+ pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return ERR_PTR(-ENOMEM);
+
+ pd->manual_poweroff = of_property_read_bool(dev->of_node,
+ "samsung,s2mps11-acokb-ground");
+ pd->disable_wrstbi = of_property_read_bool(dev->of_node,
+ "samsung,s2mps11-wrstbi-ground");
+ return pd;
+}
+
+int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
+ struct regmap *regmap, struct i2c_client *client)
+{
+ struct sec_platform_data *pdata;
+ const struct mfd_cell *sec_devs;
+ struct sec_pmic_dev *sec_pmic;
+ int ret, num_sec_devs;
+
+ sec_pmic = devm_kzalloc(dev, sizeof(*sec_pmic), GFP_KERNEL);
+ if (!sec_pmic)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, sec_pmic);
+ sec_pmic->dev = dev;
+ sec_pmic->device_type = device_type;
+ sec_pmic->i2c = client;
+ sec_pmic->irq = irq;
+ sec_pmic->regmap_pmic = regmap;
+
+ pdata = sec_pmic_parse_dt_pdata(sec_pmic->dev);
+ if (IS_ERR(pdata)) {
+ ret = PTR_ERR(pdata);
+ return ret;
+ }
+
+ sec_pmic->pdata = pdata;
+
+ ret = sec_irq_init(sec_pmic);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_active(sec_pmic->dev);
+
+ switch (sec_pmic->device_type) {
+ case S5M8767X:
+ sec_devs = s5m8767_devs;
+ num_sec_devs = ARRAY_SIZE(s5m8767_devs);
+ break;
+ case S2DOS05:
+ sec_devs = s2dos05_devs;
+ num_sec_devs = ARRAY_SIZE(s2dos05_devs);
+ break;
+ case S2MPA01:
+ sec_devs = s2mpa01_devs;
+ num_sec_devs = ARRAY_SIZE(s2mpa01_devs);
+ break;
+ case S2MPG10:
+ sec_devs = s2mpg10_devs;
+ num_sec_devs = ARRAY_SIZE(s2mpg10_devs);
+ break;
+ case S2MPS11X:
+ sec_devs = s2mps11_devs;
+ num_sec_devs = ARRAY_SIZE(s2mps11_devs);
+ break;
+ case S2MPS13X:
+ sec_devs = s2mps13_devs;
+ num_sec_devs = ARRAY_SIZE(s2mps13_devs);
+ break;
+ case S2MPS14X:
+ sec_devs = s2mps14_devs;
+ num_sec_devs = ARRAY_SIZE(s2mps14_devs);
+ break;
+ case S2MPS15X:
+ sec_devs = s2mps15_devs;
+ num_sec_devs = ARRAY_SIZE(s2mps15_devs);
+ break;
+ case S2MPU02:
+ sec_devs = s2mpu02_devs;
+ num_sec_devs = ARRAY_SIZE(s2mpu02_devs);
+ break;
+ case S2MPU05:
+ sec_devs = s2mpu05_devs;
+ num_sec_devs = ARRAY_SIZE(s2mpu05_devs);
+ break;
+ default:
+ return dev_err_probe(sec_pmic->dev, -EINVAL,
+ "Unsupported device type %d\n",
+ sec_pmic->device_type);
+ }
+ ret = devm_mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs,
+ NULL, 0, NULL);
+ if (ret)
+ return ret;
+
+ sec_pmic_configure(sec_pmic);
+ sec_pmic_dump_rev(sec_pmic);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sec_pmic_probe);
+
+void sec_pmic_shutdown(struct device *dev)
+{
+ struct sec_pmic_dev *sec_pmic = dev_get_drvdata(dev);
+ unsigned int reg, mask;
+
+ if (!sec_pmic->pdata->manual_poweroff)
+ return;
+
+ switch (sec_pmic->device_type) {
+ case S2MPS11X:
+ reg = S2MPS11_REG_CTRL1;
+ mask = S2MPS11_CTRL1_PWRHOLD_MASK;
+ break;
+ default:
+ /*
+ * Currently only one board with S2MPS11 needs this, so just
+ * ignore the rest.
+ */
+ dev_warn(sec_pmic->dev,
+ "Unsupported device %d for manual power off\n",
+ sec_pmic->device_type);
+ return;
+ }
+
+ regmap_update_bits(sec_pmic->regmap_pmic, reg, mask, 0);
+}
+EXPORT_SYMBOL_GPL(sec_pmic_shutdown);
+
+static int sec_pmic_suspend(struct device *dev)
+{
+ struct sec_pmic_dev *sec_pmic = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(sec_pmic->irq);
+ /*
+ * PMIC IRQ must be disabled during suspend for RTC alarm
+ * to work properly.
+ * When device is woken up from suspend, an
+ * interrupt occurs before resuming I2C bus controller.
+ * The interrupt is handled by regmap_irq_thread which tries
+ * to read RTC registers. This read fails (I2C is still
+ * suspended) and RTC Alarm interrupt is disabled.
+ */
+ disable_irq(sec_pmic->irq);
+
+ return 0;
+}
+
+static int sec_pmic_resume(struct device *dev)
+{
+ struct sec_pmic_dev *sec_pmic = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(sec_pmic->irq);
+ enable_irq(sec_pmic->irq);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_DEV_PM_OPS(sec_pmic_pm_ops, sec_pmic_suspend, sec_pmic_resume);
+EXPORT_SYMBOL_GPL(sec_pmic_pm_ops);
+
+MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
+MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
+MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
+MODULE_AUTHOR("André Draszik <andre.draszik@linaro.org>");
+MODULE_DESCRIPTION("Core driver for the Samsung S5M");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c
deleted file mode 100644
index d2f631901886..000000000000
--- a/drivers/mfd/sec-core.c
+++ /dev/null
@@ -1,460 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-//
-// Copyright (c) 2012 Samsung Electronics Co., Ltd
-// http://www.samsung.com
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
-#include <linux/interrupt.h>
-#include <linux/pm_runtime.h>
-#include <linux/mutex.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/samsung/core.h>
-#include <linux/mfd/samsung/irq.h>
-#include <linux/mfd/samsung/s2mpa01.h>
-#include <linux/mfd/samsung/s2mps11.h>
-#include <linux/mfd/samsung/s2mps13.h>
-#include <linux/mfd/samsung/s2mps14.h>
-#include <linux/mfd/samsung/s2mps15.h>
-#include <linux/mfd/samsung/s2mpu02.h>
-#include <linux/mfd/samsung/s5m8767.h>
-#include <linux/regmap.h>
-
-static const struct mfd_cell s5m8767_devs[] = {
- { .name = "s5m8767-pmic", },
- { .name = "s5m-rtc", },
- {
- .name = "s5m8767-clk",
- .of_compatible = "samsung,s5m8767-clk",
- },
-};
-
-static const struct mfd_cell s2mps11_devs[] = {
- { .name = "s2mps11-regulator", },
- { .name = "s2mps14-rtc", },
- {
- .name = "s2mps11-clk",
- .of_compatible = "samsung,s2mps11-clk",
- },
-};
-
-static const struct mfd_cell s2mps13_devs[] = {
- { .name = "s2mps13-regulator", },
- { .name = "s2mps13-rtc", },
- {
- .name = "s2mps13-clk",
- .of_compatible = "samsung,s2mps13-clk",
- },
-};
-
-static const struct mfd_cell s2mps14_devs[] = {
- { .name = "s2mps14-regulator", },
- { .name = "s2mps14-rtc", },
- {
- .name = "s2mps14-clk",
- .of_compatible = "samsung,s2mps14-clk",
- },
-};
-
-static const struct mfd_cell s2mps15_devs[] = {
- { .name = "s2mps15-regulator", },
- { .name = "s2mps15-rtc", },
- {
- .name = "s2mps13-clk",
- .of_compatible = "samsung,s2mps13-clk",
- },
-};
-
-static const struct mfd_cell s2mpa01_devs[] = {
- { .name = "s2mpa01-pmic", },
- { .name = "s2mps14-rtc", },
-};
-
-static const struct mfd_cell s2mpu02_devs[] = {
- { .name = "s2mpu02-regulator", },
-};
-
-static const struct of_device_id sec_dt_match[] = {
- {
- .compatible = "samsung,s5m8767-pmic",
- .data = (void *)S5M8767X,
- }, {
- .compatible = "samsung,s2mps11-pmic",
- .data = (void *)S2MPS11X,
- }, {
- .compatible = "samsung,s2mps13-pmic",
- .data = (void *)S2MPS13X,
- }, {
- .compatible = "samsung,s2mps14-pmic",
- .data = (void *)S2MPS14X,
- }, {
- .compatible = "samsung,s2mps15-pmic",
- .data = (void *)S2MPS15X,
- }, {
- .compatible = "samsung,s2mpa01-pmic",
- .data = (void *)S2MPA01,
- }, {
- .compatible = "samsung,s2mpu02-pmic",
- .data = (void *)S2MPU02,
- }, {
- /* Sentinel */
- },
-};
-MODULE_DEVICE_TABLE(of, sec_dt_match);
-
-static bool s2mpa01_volatile(struct device *dev, unsigned int reg)
-{
- switch (reg) {
- case S2MPA01_REG_INT1M:
- case S2MPA01_REG_INT2M:
- case S2MPA01_REG_INT3M:
- return false;
- default:
- return true;
- }
-}
-
-static bool s2mps11_volatile(struct device *dev, unsigned int reg)
-{
- switch (reg) {
- case S2MPS11_REG_INT1M:
- case S2MPS11_REG_INT2M:
- case S2MPS11_REG_INT3M:
- return false;
- default:
- return true;
- }
-}
-
-static bool s2mpu02_volatile(struct device *dev, unsigned int reg)
-{
- switch (reg) {
- case S2MPU02_REG_INT1M:
- case S2MPU02_REG_INT2M:
- case S2MPU02_REG_INT3M:
- return false;
- default:
- return true;
- }
-}
-
-static const struct regmap_config sec_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
-};
-
-static const struct regmap_config s2mpa01_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = S2MPA01_REG_LDO_OVCB4,
- .volatile_reg = s2mpa01_volatile,
- .cache_type = REGCACHE_FLAT,
-};
-
-static const struct regmap_config s2mps11_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = S2MPS11_REG_L38CTRL,
- .volatile_reg = s2mps11_volatile,
- .cache_type = REGCACHE_FLAT,
-};
-
-static const struct regmap_config s2mps13_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = S2MPS13_REG_LDODSCH5,
- .volatile_reg = s2mps11_volatile,
- .cache_type = REGCACHE_FLAT,
-};
-
-static const struct regmap_config s2mps14_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = S2MPS14_REG_LDODSCH3,
- .volatile_reg = s2mps11_volatile,
- .cache_type = REGCACHE_FLAT,
-};
-
-static const struct regmap_config s2mps15_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = S2MPS15_REG_LDODSCH4,
- .volatile_reg = s2mps11_volatile,
- .cache_type = REGCACHE_FLAT,
-};
-
-static const struct regmap_config s2mpu02_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = S2MPU02_REG_DVSDATA,
- .volatile_reg = s2mpu02_volatile,
- .cache_type = REGCACHE_FLAT,
-};
-
-static const struct regmap_config s5m8767_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = S5M8767_REG_LDO28CTRL,
- .volatile_reg = s2mps11_volatile,
- .cache_type = REGCACHE_FLAT,
-};
-
-static void sec_pmic_dump_rev(struct sec_pmic_dev *sec_pmic)
-{
- unsigned int val;
-
- /* For each device type, the REG_ID is always the first register */
- if (!regmap_read(sec_pmic->regmap_pmic, S2MPS11_REG_ID, &val))
- dev_dbg(sec_pmic->dev, "Revision: 0x%x\n", val);
-}
-
-static void sec_pmic_configure(struct sec_pmic_dev *sec_pmic)
-{
- int err;
-
- if (sec_pmic->device_type != S2MPS13X)
- return;
-
- if (sec_pmic->pdata->disable_wrstbi) {
- /*
- * If WRSTBI pin is pulled down this feature must be disabled
- * because each Suspend to RAM will trigger buck voltage reset
- * to default values.
- */
- err = regmap_update_bits(sec_pmic->regmap_pmic,
- S2MPS13_REG_WRSTBI,
- S2MPS13_REG_WRSTBI_MASK, 0x0);
- if (err)
- dev_warn(sec_pmic->dev,
- "Cannot initialize WRSTBI config: %d\n",
- err);
- }
-}
-
-/*
- * Only the common platform data elements for s5m8767 are parsed here from the
- * device tree. Other sub-modules of s5m8767 such as pmic, rtc , charger and
- * others have to parse their own platform data elements from device tree.
- *
- * The s5m8767 platform data structure is instantiated here and the drivers for
- * the sub-modules need not instantiate another instance while parsing their
- * platform data.
- */
-static struct sec_platform_data *
-sec_pmic_i2c_parse_dt_pdata(struct device *dev)
-{
- struct sec_platform_data *pd;
-
- pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
- if (!pd)
- return ERR_PTR(-ENOMEM);
-
- pd->manual_poweroff = of_property_read_bool(dev->of_node,
- "samsung,s2mps11-acokb-ground");
- pd->disable_wrstbi = of_property_read_bool(dev->of_node,
- "samsung,s2mps11-wrstbi-ground");
- return pd;
-}
-
-static int sec_pmic_probe(struct i2c_client *i2c)
-{
- const struct regmap_config *regmap;
- struct sec_platform_data *pdata;
- const struct mfd_cell *sec_devs;
- struct sec_pmic_dev *sec_pmic;
- int ret, num_sec_devs;
-
- sec_pmic = devm_kzalloc(&i2c->dev, sizeof(struct sec_pmic_dev),
- GFP_KERNEL);
- if (sec_pmic == NULL)
- return -ENOMEM;
-
- i2c_set_clientdata(i2c, sec_pmic);
- sec_pmic->dev = &i2c->dev;
- sec_pmic->i2c = i2c;
- sec_pmic->irq = i2c->irq;
-
- pdata = sec_pmic_i2c_parse_dt_pdata(sec_pmic->dev);
- if (IS_ERR(pdata)) {
- ret = PTR_ERR(pdata);
- return ret;
- }
-
- sec_pmic->device_type = (unsigned long)of_device_get_match_data(sec_pmic->dev);
- sec_pmic->pdata = pdata;
-
- switch (sec_pmic->device_type) {
- case S2MPA01:
- regmap = &s2mpa01_regmap_config;
- break;
- case S2MPS11X:
- regmap = &s2mps11_regmap_config;
- break;
- case S2MPS13X:
- regmap = &s2mps13_regmap_config;
- break;
- case S2MPS14X:
- regmap = &s2mps14_regmap_config;
- break;
- case S2MPS15X:
- regmap = &s2mps15_regmap_config;
- break;
- case S5M8767X:
- regmap = &s5m8767_regmap_config;
- break;
- case S2MPU02:
- regmap = &s2mpu02_regmap_config;
- break;
- default:
- regmap = &sec_regmap_config;
- break;
- }
-
- sec_pmic->regmap_pmic = devm_regmap_init_i2c(i2c, regmap);
- if (IS_ERR(sec_pmic->regmap_pmic)) {
- ret = PTR_ERR(sec_pmic->regmap_pmic);
- dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
- ret);
- return ret;
- }
-
- sec_irq_init(sec_pmic);
-
- pm_runtime_set_active(sec_pmic->dev);
-
- switch (sec_pmic->device_type) {
- case S5M8767X:
- sec_devs = s5m8767_devs;
- num_sec_devs = ARRAY_SIZE(s5m8767_devs);
- break;
- case S2MPA01:
- sec_devs = s2mpa01_devs;
- num_sec_devs = ARRAY_SIZE(s2mpa01_devs);
- break;
- case S2MPS11X:
- sec_devs = s2mps11_devs;
- num_sec_devs = ARRAY_SIZE(s2mps11_devs);
- break;
- case S2MPS13X:
- sec_devs = s2mps13_devs;
- num_sec_devs = ARRAY_SIZE(s2mps13_devs);
- break;
- case S2MPS14X:
- sec_devs = s2mps14_devs;
- num_sec_devs = ARRAY_SIZE(s2mps14_devs);
- break;
- case S2MPS15X:
- sec_devs = s2mps15_devs;
- num_sec_devs = ARRAY_SIZE(s2mps15_devs);
- break;
- case S2MPU02:
- sec_devs = s2mpu02_devs;
- num_sec_devs = ARRAY_SIZE(s2mpu02_devs);
- break;
- default:
- dev_err(&i2c->dev, "Unsupported device type (%lu)\n",
- sec_pmic->device_type);
- return -ENODEV;
- }
- ret = devm_mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs,
- NULL, 0, NULL);
- if (ret)
- return ret;
-
- sec_pmic_configure(sec_pmic);
- sec_pmic_dump_rev(sec_pmic);
-
- return ret;
-}
-
-static void sec_pmic_shutdown(struct i2c_client *i2c)
-{
- struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
- unsigned int reg, mask;
-
- if (!sec_pmic->pdata->manual_poweroff)
- return;
-
- switch (sec_pmic->device_type) {
- case S2MPS11X:
- reg = S2MPS11_REG_CTRL1;
- mask = S2MPS11_CTRL1_PWRHOLD_MASK;
- break;
- default:
- /*
- * Currently only one board with S2MPS11 needs this, so just
- * ignore the rest.
- */
- dev_warn(sec_pmic->dev,
- "Unsupported device %lu for manual power off\n",
- sec_pmic->device_type);
- return;
- }
-
- regmap_update_bits(sec_pmic->regmap_pmic, reg, mask, 0);
-}
-
-static int sec_pmic_suspend(struct device *dev)
-{
- struct i2c_client *i2c = to_i2c_client(dev);
- struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
-
- if (device_may_wakeup(dev))
- enable_irq_wake(sec_pmic->irq);
- /*
- * PMIC IRQ must be disabled during suspend for RTC alarm
- * to work properly.
- * When device is woken up from suspend, an
- * interrupt occurs before resuming I2C bus controller.
- * The interrupt is handled by regmap_irq_thread which tries
- * to read RTC registers. This read fails (I2C is still
- * suspended) and RTC Alarm interrupt is disabled.
- */
- disable_irq(sec_pmic->irq);
-
- return 0;
-}
-
-static int sec_pmic_resume(struct device *dev)
-{
- struct i2c_client *i2c = to_i2c_client(dev);
- struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
-
- if (device_may_wakeup(dev))
- disable_irq_wake(sec_pmic->irq);
- enable_irq(sec_pmic->irq);
-
- return 0;
-}
-
-static DEFINE_SIMPLE_DEV_PM_OPS(sec_pmic_pm_ops,
- sec_pmic_suspend, sec_pmic_resume);
-
-static struct i2c_driver sec_pmic_driver = {
- .driver = {
- .name = "sec_pmic",
- .pm = pm_sleep_ptr(&sec_pmic_pm_ops),
- .of_match_table = sec_dt_match,
- },
- .probe = sec_pmic_probe,
- .shutdown = sec_pmic_shutdown,
-};
-module_i2c_driver(sec_pmic_driver);
-
-MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
-MODULE_DESCRIPTION("Core support for the S5M MFD");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/sec-core.h b/drivers/mfd/sec-core.h
new file mode 100644
index 000000000000..92c7558ab8b0
--- /dev/null
+++ b/drivers/mfd/sec-core.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2012 Samsung Electronics Co., Ltd
+ * http://www.samsung.com
+ * Copyright 2025 Linaro Ltd.
+ *
+ * Samsung SxM core driver internal data
+ */
+
+#ifndef __SEC_CORE_INT_H
+#define __SEC_CORE_INT_H
+
+struct i2c_client;
+
+extern const struct dev_pm_ops sec_pmic_pm_ops;
+
+int sec_pmic_probe(struct device *dev, int device_type, unsigned int irq,
+ struct regmap *regmap, struct i2c_client *client);
+void sec_pmic_shutdown(struct device *dev);
+
+int sec_irq_init(struct sec_pmic_dev *sec_pmic);
+
+#endif /* __SEC_CORE_INT_H */
diff --git a/drivers/mfd/sec-i2c.c b/drivers/mfd/sec-i2c.c
new file mode 100644
index 000000000000..3132b849b4bc
--- /dev/null
+++ b/drivers/mfd/sec-i2c.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2012 Samsung Electronics Co., Ltd
+ * http://www.samsung.com
+ * Copyright 2025 Linaro Ltd.
+ *
+ * Samsung SxM I2C driver
+ */
+
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/s2mpa01.h>
+#include <linux/mfd/samsung/s2mps11.h>
+#include <linux/mfd/samsung/s2mps13.h>
+#include <linux/mfd/samsung/s2mps14.h>
+#include <linux/mfd/samsung/s2mps15.h>
+#include <linux/mfd/samsung/s2mpu02.h>
+#include <linux/mfd/samsung/s5m8767.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include "sec-core.h"
+
+struct sec_pmic_i2c_platform_data {
+ const struct regmap_config *regmap_cfg;
+ int device_type;
+};
+
+static bool s2mpa01_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case S2MPA01_REG_INT1M:
+ case S2MPA01_REG_INT2M:
+ case S2MPA01_REG_INT3M:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool s2mps11_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case S2MPS11_REG_INT1M:
+ case S2MPS11_REG_INT2M:
+ case S2MPS11_REG_INT3M:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool s2mpu02_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case S2MPU02_REG_INT1M:
+ case S2MPU02_REG_INT2M:
+ case S2MPU02_REG_INT3M:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config s2dos05_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct regmap_config s2mpa01_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S2MPA01_REG_LDO_OVCB4,
+ .volatile_reg = s2mpa01_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_config s2mps11_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S2MPS11_REG_L38CTRL,
+ .volatile_reg = s2mps11_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_config s2mps13_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S2MPS13_REG_LDODSCH5,
+ .volatile_reg = s2mps11_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_config s2mps14_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S2MPS14_REG_LDODSCH3,
+ .volatile_reg = s2mps11_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_config s2mps15_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S2MPS15_REG_LDODSCH4,
+ .volatile_reg = s2mps11_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_config s2mpu02_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S2MPU02_REG_DVSDATA,
+ .volatile_reg = s2mpu02_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct regmap_config s2mpu05_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct regmap_config s5m8767_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S5M8767_REG_LDO28CTRL,
+ .volatile_reg = s2mps11_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int sec_pmic_i2c_probe(struct i2c_client *client)
+{
+ const struct sec_pmic_i2c_platform_data *pdata;
+ struct regmap *regmap_pmic;
+
+ pdata = device_get_match_data(&client->dev);
+ if (!pdata)
+ return dev_err_probe(&client->dev, -ENODEV,
+ "Unsupported device type\n");
+
+ regmap_pmic = devm_regmap_init_i2c(client, pdata->regmap_cfg);
+ if (IS_ERR(regmap_pmic))
+ return dev_err_probe(&client->dev, PTR_ERR(regmap_pmic),
+ "regmap init failed\n");
+
+ return sec_pmic_probe(&client->dev, pdata->device_type, client->irq,
+ regmap_pmic, client);
+}
+
+static void sec_pmic_i2c_shutdown(struct i2c_client *i2c)
+{
+ sec_pmic_shutdown(&i2c->dev);
+}
+
+static const struct sec_pmic_i2c_platform_data s2dos05_data = {
+ .regmap_cfg = &s2dos05_regmap_config,
+ .device_type = S2DOS05
+};
+
+static const struct sec_pmic_i2c_platform_data s2mpa01_data = {
+ .regmap_cfg = &s2mpa01_regmap_config,
+ .device_type = S2MPA01,
+};
+
+static const struct sec_pmic_i2c_platform_data s2mps11_data = {
+ .regmap_cfg = &s2mps11_regmap_config,
+ .device_type = S2MPS11X,
+};
+
+static const struct sec_pmic_i2c_platform_data s2mps13_data = {
+ .regmap_cfg = &s2mps13_regmap_config,
+ .device_type = S2MPS13X,
+};
+
+static const struct sec_pmic_i2c_platform_data s2mps14_data = {
+ .regmap_cfg = &s2mps14_regmap_config,
+ .device_type = S2MPS14X,
+};
+
+static const struct sec_pmic_i2c_platform_data s2mps15_data = {
+ .regmap_cfg = &s2mps15_regmap_config,
+ .device_type = S2MPS15X,
+};
+
+static const struct sec_pmic_i2c_platform_data s2mpu02_data = {
+ .regmap_cfg = &s2mpu02_regmap_config,
+ .device_type = S2MPU02,
+};
+
+static const struct sec_pmic_i2c_platform_data s2mpu05_data = {
+ .regmap_cfg = &s2mpu05_regmap_config,
+ .device_type = S2MPU05,
+};
+
+static const struct sec_pmic_i2c_platform_data s5m8767_data = {
+ .regmap_cfg = &s5m8767_regmap_config,
+ .device_type = S5M8767X,
+};
+
+static const struct of_device_id sec_pmic_i2c_of_match[] = {
+ { .compatible = "samsung,s2dos05", .data = &s2dos05_data, },
+ { .compatible = "samsung,s2mpa01-pmic", .data = &s2mpa01_data, },
+ { .compatible = "samsung,s2mps11-pmic", .data = &s2mps11_data, },
+ { .compatible = "samsung,s2mps13-pmic", .data = &s2mps13_data, },
+ { .compatible = "samsung,s2mps14-pmic", .data = &s2mps14_data, },
+ { .compatible = "samsung,s2mps15-pmic", .data = &s2mps15_data, },
+ { .compatible = "samsung,s2mpu02-pmic", .data = &s2mpu02_data, },
+ { .compatible = "samsung,s2mpu05-pmic", .data = &s2mpu05_data, },
+ { .compatible = "samsung,s5m8767-pmic", .data = &s5m8767_data, },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sec_pmic_i2c_of_match);
+
+static struct i2c_driver sec_pmic_i2c_driver = {
+ .driver = {
+ .name = "sec-pmic-i2c",
+ .pm = pm_sleep_ptr(&sec_pmic_pm_ops),
+ .of_match_table = sec_pmic_i2c_of_match,
+ },
+ .probe = sec_pmic_i2c_probe,
+ .shutdown = sec_pmic_i2c_shutdown,
+};
+module_i2c_driver(sec_pmic_i2c_driver);
+
+MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
+MODULE_AUTHOR("André Draszik <andre.draszik@linaro.org>");
+MODULE_DESCRIPTION("I2C driver for the Samsung S5M");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c
index e191aeb0c07c..74ac70002d1f 100644
--- a/drivers/mfd/sec-irq.c
+++ b/drivers/mfd/sec-irq.c
@@ -3,297 +3,206 @@
// Copyright (c) 2011-2014 Samsung Electronics Co., Ltd
// http://www.samsung.com
-#include <linux/device.h>
+#include <linux/array_size.h>
+#include <linux/build_bug.h>
+#include <linux/dev_printk.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/regmap.h>
-
#include <linux/mfd/samsung/core.h>
#include <linux/mfd/samsung/irq.h>
+#include <linux/mfd/samsung/s2mpg10.h>
#include <linux/mfd/samsung/s2mps11.h>
#include <linux/mfd/samsung/s2mps14.h>
#include <linux/mfd/samsung/s2mpu02.h>
+#include <linux/mfd/samsung/s2mpu05.h>
#include <linux/mfd/samsung/s5m8767.h>
+#include <linux/regmap.h>
+#include "sec-core.h"
+
+static const struct regmap_irq s2mpg10_irqs[] = {
+ REGMAP_IRQ_REG(S2MPG10_COMMON_IRQ_PMIC, 0, S2MPG10_COMMON_INT_SRC_PMIC),
+ /* No documentation or other reference for remaining bits */
+ REGMAP_IRQ_REG(S2MPG10_COMMON_IRQ_UNUSED, 0, GENMASK(7, 1)),
+};
+
+static const struct regmap_irq s2mpg10_pmic_irqs[] = {
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PWRONF, 0, S2MPG10_IRQ_PWRONF_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PWRONR, 0, S2MPG10_IRQ_PWRONR_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_JIGONBF, 0, S2MPG10_IRQ_JIGONBF_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_JIGONBR, 0, S2MPG10_IRQ_JIGONBR_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_ACOKBF, 0, S2MPG10_IRQ_ACOKBF_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_ACOKBR, 0, S2MPG10_IRQ_ACOKBR_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PWRON1S, 0, S2MPG10_IRQ_PWRON1S_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_MRB, 0, S2MPG10_IRQ_MRB_MASK),
+
+ REGMAP_IRQ_REG(S2MPG10_IRQ_RTC60S, 1, S2MPG10_IRQ_RTC60S_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_RTCA1, 1, S2MPG10_IRQ_RTCA1_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_RTCA0, 1, S2MPG10_IRQ_RTCA0_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_RTC1S, 1, S2MPG10_IRQ_RTC1S_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_WTSR_COLDRST, 1, S2MPG10_IRQ_WTSR_COLDRST_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_WTSR, 1, S2MPG10_IRQ_WTSR_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_WRST, 1, S2MPG10_IRQ_WRST_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_SMPL, 1, S2MPG10_IRQ_SMPL_MASK),
+
+ REGMAP_IRQ_REG(S2MPG10_IRQ_120C, 2, S2MPG10_IRQ_INT120C_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_140C, 2, S2MPG10_IRQ_INT140C_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_TSD, 2, S2MPG10_IRQ_TSD_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PIF_TIMEOUT1, 2, S2MPG10_IRQ_PIF_TIMEOUT1_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PIF_TIMEOUT2, 2, S2MPG10_IRQ_PIF_TIMEOUT2_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_SPD_PARITY_ERR, 2, S2MPG10_IRQ_SPD_PARITY_ERR_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_SPD_ABNORMAL_STOP, 2, S2MPG10_IRQ_SPD_ABNORMAL_STOP_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PMETER_OVERF, 2, S2MPG10_IRQ_PMETER_OVERF_MASK),
+
+ REGMAP_IRQ_REG(S2MPG10_IRQ_OCP_B1M, 3, S2MPG10_IRQ_OCP_B1M_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_OCP_B2M, 3, S2MPG10_IRQ_OCP_B2M_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_OCP_B3M, 3, S2MPG10_IRQ_OCP_B3M_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_OCP_B4M, 3, S2MPG10_IRQ_OCP_B4M_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_OCP_B5M, 3, S2MPG10_IRQ_OCP_B5M_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_OCP_B6M, 3, S2MPG10_IRQ_OCP_B6M_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_OCP_B7M, 3, S2MPG10_IRQ_OCP_B7M_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_OCP_B8M, 3, S2MPG10_IRQ_OCP_B8M_MASK),
+
+ REGMAP_IRQ_REG(S2MPG10_IRQ_OCP_B9M, 4, S2MPG10_IRQ_OCP_B9M_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_OCP_B10M, 4, S2MPG10_IRQ_OCP_B10M_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_WLWP_ACC, 4, S2MPG10_IRQ_WLWP_ACC_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_SMPL_TIMEOUT, 4, S2MPG10_IRQ_SMPL_TIMEOUT_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_WTSR_TIMEOUT, 4, S2MPG10_IRQ_WTSR_TIMEOUT_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_SPD_SRP_PKT_RST, 4, S2MPG10_IRQ_SPD_SRP_PKT_RST_MASK),
+
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PWR_WARN_CH0, 5, S2MPG10_IRQ_PWR_WARN_CH0_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PWR_WARN_CH1, 5, S2MPG10_IRQ_PWR_WARN_CH1_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PWR_WARN_CH2, 5, S2MPG10_IRQ_PWR_WARN_CH2_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PWR_WARN_CH3, 5, S2MPG10_IRQ_PWR_WARN_CH3_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PWR_WARN_CH4, 5, S2MPG10_IRQ_PWR_WARN_CH4_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PWR_WARN_CH5, 5, S2MPG10_IRQ_PWR_WARN_CH5_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PWR_WARN_CH6, 5, S2MPG10_IRQ_PWR_WARN_CH6_MASK),
+ REGMAP_IRQ_REG(S2MPG10_IRQ_PWR_WARN_CH7, 5, S2MPG10_IRQ_PWR_WARN_CH7_MASK),
+};
static const struct regmap_irq s2mps11_irqs[] = {
- [S2MPS11_IRQ_PWRONF] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_PWRONF_MASK,
- },
- [S2MPS11_IRQ_PWRONR] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_PWRONR_MASK,
- },
- [S2MPS11_IRQ_JIGONBF] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_JIGONBF_MASK,
- },
- [S2MPS11_IRQ_JIGONBR] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_JIGONBR_MASK,
- },
- [S2MPS11_IRQ_ACOKBF] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_ACOKBF_MASK,
- },
- [S2MPS11_IRQ_ACOKBR] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_ACOKBR_MASK,
- },
- [S2MPS11_IRQ_PWRON1S] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_PWRON1S_MASK,
- },
- [S2MPS11_IRQ_MRB] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_MRB_MASK,
- },
- [S2MPS11_IRQ_RTC60S] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_RTC60S_MASK,
- },
- [S2MPS11_IRQ_RTCA1] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_RTCA1_MASK,
- },
- [S2MPS11_IRQ_RTCA0] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_RTCA0_MASK,
- },
- [S2MPS11_IRQ_SMPL] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_SMPL_MASK,
- },
- [S2MPS11_IRQ_RTC1S] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_RTC1S_MASK,
- },
- [S2MPS11_IRQ_WTSR] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_WTSR_MASK,
- },
- [S2MPS11_IRQ_INT120C] = {
- .reg_offset = 2,
- .mask = S2MPS11_IRQ_INT120C_MASK,
- },
- [S2MPS11_IRQ_INT140C] = {
- .reg_offset = 2,
- .mask = S2MPS11_IRQ_INT140C_MASK,
- },
+ REGMAP_IRQ_REG(S2MPS11_IRQ_PWRONF, 0, S2MPS11_IRQ_PWRONF_MASK),
+ REGMAP_IRQ_REG(S2MPS11_IRQ_PWRONR, 0, S2MPS11_IRQ_PWRONR_MASK),
+ REGMAP_IRQ_REG(S2MPS11_IRQ_JIGONBF, 0, S2MPS11_IRQ_JIGONBF_MASK),
+ REGMAP_IRQ_REG(S2MPS11_IRQ_JIGONBR, 0, S2MPS11_IRQ_JIGONBR_MASK),
+ REGMAP_IRQ_REG(S2MPS11_IRQ_ACOKBF, 0, S2MPS11_IRQ_ACOKBF_MASK),
+ REGMAP_IRQ_REG(S2MPS11_IRQ_ACOKBR, 0, S2MPS11_IRQ_ACOKBR_MASK),
+ REGMAP_IRQ_REG(S2MPS11_IRQ_PWRON1S, 0, S2MPS11_IRQ_PWRON1S_MASK),
+ REGMAP_IRQ_REG(S2MPS11_IRQ_MRB, 0, S2MPS11_IRQ_MRB_MASK),
+
+ REGMAP_IRQ_REG(S2MPS11_IRQ_RTC60S, 1, S2MPS11_IRQ_RTC60S_MASK),
+ REGMAP_IRQ_REG(S2MPS11_IRQ_RTCA1, 1, S2MPS11_IRQ_RTCA1_MASK),
+ REGMAP_IRQ_REG(S2MPS11_IRQ_RTCA0, 1, S2MPS11_IRQ_RTCA0_MASK),
+ REGMAP_IRQ_REG(S2MPS11_IRQ_SMPL, 1, S2MPS11_IRQ_SMPL_MASK),
+ REGMAP_IRQ_REG(S2MPS11_IRQ_RTC1S, 1, S2MPS11_IRQ_RTC1S_MASK),
+ REGMAP_IRQ_REG(S2MPS11_IRQ_WTSR, 1, S2MPS11_IRQ_WTSR_MASK),
+
+ REGMAP_IRQ_REG(S2MPS11_IRQ_INT120C, 2, S2MPS11_IRQ_INT120C_MASK),
+ REGMAP_IRQ_REG(S2MPS11_IRQ_INT140C, 2, S2MPS11_IRQ_INT140C_MASK),
};
static const struct regmap_irq s2mps14_irqs[] = {
- [S2MPS14_IRQ_PWRONF] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_PWRONF_MASK,
- },
- [S2MPS14_IRQ_PWRONR] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_PWRONR_MASK,
- },
- [S2MPS14_IRQ_JIGONBF] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_JIGONBF_MASK,
- },
- [S2MPS14_IRQ_JIGONBR] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_JIGONBR_MASK,
- },
- [S2MPS14_IRQ_ACOKBF] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_ACOKBF_MASK,
- },
- [S2MPS14_IRQ_ACOKBR] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_ACOKBR_MASK,
- },
- [S2MPS14_IRQ_PWRON1S] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_PWRON1S_MASK,
- },
- [S2MPS14_IRQ_MRB] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_MRB_MASK,
- },
- [S2MPS14_IRQ_RTC60S] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_RTC60S_MASK,
- },
- [S2MPS14_IRQ_RTCA1] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_RTCA1_MASK,
- },
- [S2MPS14_IRQ_RTCA0] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_RTCA0_MASK,
- },
- [S2MPS14_IRQ_SMPL] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_SMPL_MASK,
- },
- [S2MPS14_IRQ_RTC1S] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_RTC1S_MASK,
- },
- [S2MPS14_IRQ_WTSR] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_WTSR_MASK,
- },
- [S2MPS14_IRQ_INT120C] = {
- .reg_offset = 2,
- .mask = S2MPS11_IRQ_INT120C_MASK,
- },
- [S2MPS14_IRQ_INT140C] = {
- .reg_offset = 2,
- .mask = S2MPS11_IRQ_INT140C_MASK,
- },
- [S2MPS14_IRQ_TSD] = {
- .reg_offset = 2,
- .mask = S2MPS14_IRQ_TSD_MASK,
- },
+ REGMAP_IRQ_REG(S2MPS14_IRQ_PWRONF, 0, S2MPS11_IRQ_PWRONF_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_PWRONR, 0, S2MPS11_IRQ_PWRONR_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_JIGONBF, 0, S2MPS11_IRQ_JIGONBF_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_JIGONBR, 0, S2MPS11_IRQ_JIGONBR_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_ACOKBF, 0, S2MPS11_IRQ_ACOKBF_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_ACOKBR, 0, S2MPS11_IRQ_ACOKBR_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_PWRON1S, 0, S2MPS11_IRQ_PWRON1S_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_MRB, 0, S2MPS11_IRQ_MRB_MASK),
+
+ REGMAP_IRQ_REG(S2MPS14_IRQ_RTC60S, 1, S2MPS11_IRQ_RTC60S_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_RTCA1, 1, S2MPS11_IRQ_RTCA1_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_RTCA0, 1, S2MPS11_IRQ_RTCA0_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_SMPL, 1, S2MPS11_IRQ_SMPL_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_RTC1S, 1, S2MPS11_IRQ_RTC1S_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_WTSR, 1, S2MPS11_IRQ_WTSR_MASK),
+
+ REGMAP_IRQ_REG(S2MPS14_IRQ_INT120C, 2, S2MPS11_IRQ_INT120C_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_INT140C, 2, S2MPS11_IRQ_INT140C_MASK),
+ REGMAP_IRQ_REG(S2MPS14_IRQ_TSD, 2, S2MPS14_IRQ_TSD_MASK),
};
static const struct regmap_irq s2mpu02_irqs[] = {
- [S2MPU02_IRQ_PWRONF] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_PWRONF_MASK,
- },
- [S2MPU02_IRQ_PWRONR] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_PWRONR_MASK,
- },
- [S2MPU02_IRQ_JIGONBF] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_JIGONBF_MASK,
- },
- [S2MPU02_IRQ_JIGONBR] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_JIGONBR_MASK,
- },
- [S2MPU02_IRQ_ACOKBF] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_ACOKBF_MASK,
- },
- [S2MPU02_IRQ_ACOKBR] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_ACOKBR_MASK,
- },
- [S2MPU02_IRQ_PWRON1S] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_PWRON1S_MASK,
- },
- [S2MPU02_IRQ_MRB] = {
- .reg_offset = 0,
- .mask = S2MPS11_IRQ_MRB_MASK,
- },
- [S2MPU02_IRQ_RTC60S] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_RTC60S_MASK,
- },
- [S2MPU02_IRQ_RTCA1] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_RTCA1_MASK,
- },
- [S2MPU02_IRQ_RTCA0] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_RTCA0_MASK,
- },
- [S2MPU02_IRQ_SMPL] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_SMPL_MASK,
- },
- [S2MPU02_IRQ_RTC1S] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_RTC1S_MASK,
- },
- [S2MPU02_IRQ_WTSR] = {
- .reg_offset = 1,
- .mask = S2MPS11_IRQ_WTSR_MASK,
- },
- [S2MPU02_IRQ_INT120C] = {
- .reg_offset = 2,
- .mask = S2MPS11_IRQ_INT120C_MASK,
- },
- [S2MPU02_IRQ_INT140C] = {
- .reg_offset = 2,
- .mask = S2MPS11_IRQ_INT140C_MASK,
- },
- [S2MPU02_IRQ_TSD] = {
- .reg_offset = 2,
- .mask = S2MPS14_IRQ_TSD_MASK,
- },
+ REGMAP_IRQ_REG(S2MPU02_IRQ_PWRONF, 0, S2MPS11_IRQ_PWRONF_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_PWRONR, 0, S2MPS11_IRQ_PWRONR_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_JIGONBF, 0, S2MPS11_IRQ_JIGONBF_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_JIGONBR, 0, S2MPS11_IRQ_JIGONBR_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_ACOKBF, 0, S2MPS11_IRQ_ACOKBF_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_ACOKBR, 0, S2MPS11_IRQ_ACOKBR_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_PWRON1S, 0, S2MPS11_IRQ_PWRON1S_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_MRB, 0, S2MPS11_IRQ_MRB_MASK),
+
+ REGMAP_IRQ_REG(S2MPU02_IRQ_RTC60S, 1, S2MPS11_IRQ_RTC60S_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_RTCA1, 1, S2MPS11_IRQ_RTCA1_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_RTCA0, 1, S2MPS11_IRQ_RTCA0_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_SMPL, 1, S2MPS11_IRQ_SMPL_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_RTC1S, 1, S2MPS11_IRQ_RTC1S_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_WTSR, 1, S2MPS11_IRQ_WTSR_MASK),
+
+ REGMAP_IRQ_REG(S2MPU02_IRQ_INT120C, 2, S2MPS11_IRQ_INT120C_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_INT140C, 2, S2MPS11_IRQ_INT140C_MASK),
+ REGMAP_IRQ_REG(S2MPU02_IRQ_TSD, 2, S2MPS14_IRQ_TSD_MASK),
+};
+
+static const struct regmap_irq s2mpu05_irqs[] = {
+ REGMAP_IRQ_REG(S2MPU05_IRQ_PWRONF, 0, S2MPU05_IRQ_PWRONF_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_PWRONR, 0, S2MPU05_IRQ_PWRONR_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_JIGONBF, 0, S2MPU05_IRQ_JIGONBF_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_JIGONBR, 0, S2MPU05_IRQ_JIGONBR_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_ACOKF, 0, S2MPU05_IRQ_ACOKF_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_ACOKR, 0, S2MPU05_IRQ_ACOKR_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_PWRON1S, 0, S2MPU05_IRQ_PWRON1S_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_MRB, 0, S2MPU05_IRQ_MRB_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_RTC60S, 1, S2MPU05_IRQ_RTC60S_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_RTCA1, 1, S2MPU05_IRQ_RTCA1_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_RTCA0, 1, S2MPU05_IRQ_RTCA0_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_SMPL, 1, S2MPU05_IRQ_SMPL_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_RTC1S, 1, S2MPU05_IRQ_RTC1S_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_WTSR, 1, S2MPU05_IRQ_WTSR_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_INT120C, 2, S2MPU05_IRQ_INT120C_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_INT140C, 2, S2MPU05_IRQ_INT140C_MASK),
+ REGMAP_IRQ_REG(S2MPU05_IRQ_TSD, 2, S2MPU05_IRQ_TSD_MASK),
};
static const struct regmap_irq s5m8767_irqs[] = {
- [S5M8767_IRQ_PWRR] = {
- .reg_offset = 0,
- .mask = S5M8767_IRQ_PWRR_MASK,
- },
- [S5M8767_IRQ_PWRF] = {
- .reg_offset = 0,
- .mask = S5M8767_IRQ_PWRF_MASK,
- },
- [S5M8767_IRQ_PWR1S] = {
- .reg_offset = 0,
- .mask = S5M8767_IRQ_PWR1S_MASK,
- },
- [S5M8767_IRQ_JIGR] = {
- .reg_offset = 0,
- .mask = S5M8767_IRQ_JIGR_MASK,
- },
- [S5M8767_IRQ_JIGF] = {
- .reg_offset = 0,
- .mask = S5M8767_IRQ_JIGF_MASK,
- },
- [S5M8767_IRQ_LOWBAT2] = {
- .reg_offset = 0,
- .mask = S5M8767_IRQ_LOWBAT2_MASK,
- },
- [S5M8767_IRQ_LOWBAT1] = {
- .reg_offset = 0,
- .mask = S5M8767_IRQ_LOWBAT1_MASK,
- },
- [S5M8767_IRQ_MRB] = {
- .reg_offset = 1,
- .mask = S5M8767_IRQ_MRB_MASK,
- },
- [S5M8767_IRQ_DVSOK2] = {
- .reg_offset = 1,
- .mask = S5M8767_IRQ_DVSOK2_MASK,
- },
- [S5M8767_IRQ_DVSOK3] = {
- .reg_offset = 1,
- .mask = S5M8767_IRQ_DVSOK3_MASK,
- },
- [S5M8767_IRQ_DVSOK4] = {
- .reg_offset = 1,
- .mask = S5M8767_IRQ_DVSOK4_MASK,
- },
- [S5M8767_IRQ_RTC60S] = {
- .reg_offset = 2,
- .mask = S5M8767_IRQ_RTC60S_MASK,
- },
- [S5M8767_IRQ_RTCA1] = {
- .reg_offset = 2,
- .mask = S5M8767_IRQ_RTCA1_MASK,
- },
- [S5M8767_IRQ_RTCA2] = {
- .reg_offset = 2,
- .mask = S5M8767_IRQ_RTCA2_MASK,
- },
- [S5M8767_IRQ_SMPL] = {
- .reg_offset = 2,
- .mask = S5M8767_IRQ_SMPL_MASK,
- },
- [S5M8767_IRQ_RTC1S] = {
- .reg_offset = 2,
- .mask = S5M8767_IRQ_RTC1S_MASK,
- },
- [S5M8767_IRQ_WTSR] = {
- .reg_offset = 2,
- .mask = S5M8767_IRQ_WTSR_MASK,
- },
+ REGMAP_IRQ_REG(S5M8767_IRQ_PWRR, 0, S5M8767_IRQ_PWRR_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_PWRF, 0, S5M8767_IRQ_PWRF_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_PWR1S, 0, S5M8767_IRQ_PWR1S_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_JIGR, 0, S5M8767_IRQ_JIGR_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_JIGF, 0, S5M8767_IRQ_JIGF_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_LOWBAT2, 0, S5M8767_IRQ_LOWBAT2_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_LOWBAT1, 0, S5M8767_IRQ_LOWBAT1_MASK),
+
+ REGMAP_IRQ_REG(S5M8767_IRQ_MRB, 1, S5M8767_IRQ_MRB_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_DVSOK2, 1, S5M8767_IRQ_DVSOK2_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_DVSOK3, 1, S5M8767_IRQ_DVSOK3_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_DVSOK4, 1, S5M8767_IRQ_DVSOK4_MASK),
+
+ REGMAP_IRQ_REG(S5M8767_IRQ_RTC60S, 2, S5M8767_IRQ_RTC60S_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_RTCA1, 2, S5M8767_IRQ_RTCA1_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_RTCA2, 2, S5M8767_IRQ_RTCA2_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_SMPL, 2, S5M8767_IRQ_SMPL_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_RTC1S, 2, S5M8767_IRQ_RTC1S_MASK),
+ REGMAP_IRQ_REG(S5M8767_IRQ_WTSR, 2, S5M8767_IRQ_WTSR_MASK),
+};
+
+/* All S2MPG10 interrupt sources are read-only and don't require clearing */
+static const struct regmap_irq_chip s2mpg10_irq_chip = {
+ .name = "s2mpg10",
+ .status_base = S2MPG10_COMMON_INT,
+ .mask_base = S2MPG10_COMMON_INT_MASK,
+ .num_regs = 1,
+ .irqs = s2mpg10_irqs,
+ .num_irqs = ARRAY_SIZE(s2mpg10_irqs),
+};
+
+static const struct regmap_irq_chip s2mpg10_irq_chip_pmic = {
+ .name = "s2mpg10-pmic",
+ .status_base = S2MPG10_PMIC_INT1,
+ .mask_base = S2MPG10_PMIC_INT1M,
+ .num_regs = 6,
+ .irqs = s2mpg10_pmic_irqs,
+ .num_irqs = ARRAY_SIZE(s2mpg10_pmic_irqs),
};
static const struct regmap_irq_chip s2mps11_irq_chip = {
@@ -339,6 +248,16 @@ static const struct regmap_irq_chip s2mpu02_irq_chip = {
.ack_base = S2MPU02_REG_INT1,
};
+static const struct regmap_irq_chip s2mpu05_irq_chip = {
+ .name = "s2mpu05",
+ .irqs = s2mpu05_irqs,
+ .num_irqs = ARRAY_SIZE(s2mpu05_irqs),
+ .num_regs = 3,
+ .status_base = S2MPU05_REG_INT1,
+ .mask_base = S2MPU05_REG_INT1M,
+ .ack_base = S2MPU05_REG_INT1,
+};
+
static const struct regmap_irq_chip s5m8767_irq_chip = {
.name = "s5m8767",
.irqs = s5m8767_irqs,
@@ -349,25 +268,75 @@ static const struct regmap_irq_chip s5m8767_irq_chip = {
.ack_base = S5M8767_REG_INT1,
};
-int sec_irq_init(struct sec_pmic_dev *sec_pmic)
+static int s2mpg1x_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 ret = 0;
- int type = sec_pmic->device_type;
- const struct regmap_irq_chip *sec_irq_chip;
+ int irq, ret;
- if (!sec_pmic->irq) {
- dev_warn(sec_pmic->dev,
- "No interrupt specified, no interrupts\n");
- return 0;
+ 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 sec_irq_init_s2mpg1x(struct sec_pmic_dev *sec_pmic)
+{
+ const struct regmap_irq_chip *irq_chip, *chained_irq_chip;
+ struct regmap_irq_chip_data *irq_data;
+ struct regmap *regmap_common;
+ int chained_pirq;
+ int ret;
+
+ switch (sec_pmic->device_type) {
+ case S2MPG10:
+ irq_chip = &s2mpg10_irq_chip;
+ chained_irq_chip = &s2mpg10_irq_chip_pmic;
+ chained_pirq = S2MPG10_COMMON_IRQ_PMIC;
+ break;
+ default:
+ return dev_err_probe(sec_pmic->dev, -EINVAL, "Unsupported device type %d\n",
+ sec_pmic->device_type);
}
- switch (type) {
+ regmap_common = dev_get_regmap(sec_pmic->dev, "common");
+ if (!regmap_common)
+ return dev_err_probe(sec_pmic->dev, -EINVAL, "No 'common' regmap %d\n",
+ sec_pmic->device_type);
+
+ ret = devm_regmap_add_irq_chip(sec_pmic->dev, regmap_common, sec_pmic->irq, IRQF_ONESHOT, 0,
+ irq_chip, &irq_data);
+ if (ret)
+ return dev_err_probe(sec_pmic->dev, ret, "Failed to add %s IRQ chip\n",
+ irq_chip->name);
+
+ return s2mpg1x_add_chained_irq_chip(sec_pmic->dev, sec_pmic->regmap_pmic, chained_pirq,
+ irq_data, chained_irq_chip, &sec_pmic->irq_data);
+}
+
+int sec_irq_init(struct sec_pmic_dev *sec_pmic)
+{
+ const struct regmap_irq_chip *sec_irq_chip;
+ int ret;
+
+ switch (sec_pmic->device_type) {
case S5M8767X:
sec_irq_chip = &s5m8767_irq_chip;
break;
+ case S2DOS05:
+ return 0;
case S2MPA01:
sec_irq_chip = &s2mps14_irq_chip;
break;
+ case S2MPG10:
+ return sec_irq_init_s2mpg1x(sec_pmic);
case S2MPS11X:
sec_irq_chip = &s2mps11_irq_chip;
break;
@@ -383,19 +352,28 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
case S2MPU02:
sec_irq_chip = &s2mpu02_irq_chip;
break;
+ case S2MPU05:
+ sec_irq_chip = &s2mpu05_irq_chip;
+ break;
default:
- dev_err(sec_pmic->dev, "Unknown device type %lu\n",
- sec_pmic->device_type);
- return -EINVAL;
+ return dev_err_probe(sec_pmic->dev, -EINVAL,
+ "Unsupported device type %d\n",
+ sec_pmic->device_type);
+ }
+
+ if (!sec_pmic->irq) {
+ dev_warn(sec_pmic->dev,
+ "No interrupt specified, no interrupts\n");
+ return 0;
}
ret = devm_regmap_add_irq_chip(sec_pmic->dev, sec_pmic->regmap_pmic,
sec_pmic->irq, IRQF_ONESHOT,
0, sec_irq_chip, &sec_pmic->irq_data);
- if (ret != 0) {
- dev_err(sec_pmic->dev, "Failed to register IRQ chip: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(sec_pmic->dev, ret,
+ "Failed to add %s IRQ chip\n",
+ sec_irq_chip->name);
/*
* The rtc-s5m driver requests S2MPS14_IRQ_RTCA0 also for S2MPS11
@@ -405,10 +383,3 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
return 0;
}
-EXPORT_SYMBOL_GPL(sec_irq_init);
-
-MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
-MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
-MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
-MODULE_DESCRIPTION("Interrupt support for the S5M MFD");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/si476x-cmd.c b/drivers/mfd/si476x-cmd.c
index c9a0ec084aa8..3bb2decfebd3 100644
--- a/drivers/mfd/si476x-cmd.c
+++ b/drivers/mfd/si476x-cmd.c
@@ -20,7 +20,7 @@
#include <linux/mfd/si476x-core.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define msb(x) ((u8)((u16) x >> 8))
#define lsb(x) ((u8)((u16) x & 0x00FF))
diff --git a/drivers/mfd/si476x-prop.c b/drivers/mfd/si476x-prop.c
index f0608d138f02..3d5c118888b2 100644
--- a/drivers/mfd/si476x-prop.c
+++ b/drivers/mfd/si476x-prop.c
@@ -222,7 +222,7 @@ static const struct regmap_config si476x_regmap_config = {
.reg_read = si476x_core_regmap_read,
.reg_write = si476x_core_regmap_write,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
struct regmap *devm_regmap_init_si476x(struct si476x_core *core)
diff --git a/drivers/mfd/simple-mfd-i2c.c b/drivers/mfd/simple-mfd-i2c.c
index 6eda79533208..8b751d8e3b5a 100644
--- a/drivers/mfd/simple-mfd-i2c.c
+++ b/drivers/mfd/simple-mfd-i2c.c
@@ -15,12 +15,18 @@
* will be subsequently registered.
*/
+#include <linux/array_size.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
#include <linux/i2c.h>
-#include <linux/kernel.h>
#include <linux/mfd/core.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
+#include <linux/stddef.h>
#include "simple-mfd-i2c.h"
@@ -83,11 +89,42 @@ static const struct simple_mfd_data maxim_max5970 = {
.mfd_cell_size = ARRAY_SIZE(max5970_cells),
};
+static const struct mfd_cell max77705_sensor_cells[] = {
+ { .name = "max77705-battery" },
+ { .name = "max77705-hwmon", },
+};
+
+static const struct simple_mfd_data maxim_mon_max77705 = {
+ .mfd_cell = max77705_sensor_cells,
+ .mfd_cell_size = ARRAY_SIZE(max77705_sensor_cells),
+};
+
+static const struct regmap_config spacemit_p1_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct mfd_cell spacemit_p1_cells[] = {
+ { .name = "spacemit-p1-regulator", },
+ { .name = "spacemit-p1-rtc", },
+};
+
+static const struct simple_mfd_data spacemit_p1 = {
+ .regmap_config = &spacemit_p1_regmap_config,
+ .mfd_cell = spacemit_p1_cells,
+ .mfd_cell_size = ARRAY_SIZE(spacemit_p1_cells),
+};
+
static const struct of_device_id simple_mfd_i2c_of_match[] = {
+ { .compatible = "fsl,ls1028aqds-fpga" },
+ { .compatible = "fsl,lx2160aqds-fpga" },
+ { .compatible = "fsl,lx2160ardb-fpga" },
{ .compatible = "kontron,sl28cpld" },
- { .compatible = "silergy,sy7636a", .data = &silergy_sy7636a},
- { .compatible = "maxim,max5970", .data = &maxim_max5970},
- { .compatible = "maxim,max5978", .data = &maxim_max5970},
+ { .compatible = "maxim,max5970", .data = &maxim_max5970 },
+ { .compatible = "maxim,max5978", .data = &maxim_max5970 },
+ { .compatible = "maxim,max77705-battery", .data = &maxim_mon_max77705 },
+ { .compatible = "silergy,sy7636a", .data = &silergy_sy7636a },
+ { .compatible = "spacemit,p1", .data = &spacemit_p1 },
{}
};
MODULE_DEVICE_TABLE(of, simple_mfd_i2c_of_match);
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
index 28027982cf69..50bf3260f65d 100644
--- a/drivers/mfd/sm501.c
+++ b/drivers/mfd/sm501.c
@@ -631,49 +631,6 @@ unsigned long sm501_set_clock(struct device *dev,
EXPORT_SYMBOL_GPL(sm501_set_clock);
-/* sm501_find_clock
- *
- * finds the closest available frequency for a given clock
-*/
-
-unsigned long sm501_find_clock(struct device *dev,
- int clksrc,
- unsigned long req_freq)
-{
- struct sm501_devdata *sm = dev_get_drvdata(dev);
- unsigned long sm501_freq; /* the frequency achieveable by the 501 */
- struct sm501_clock to;
-
- switch (clksrc) {
- case SM501_CLOCK_P2XCLK:
- if (sm->rev >= 0xC0) {
- /* SM502 -> use the programmable PLL */
- sm501_freq = (sm501_calc_pll(2 * req_freq,
- &to, 5) / 2);
- } else {
- sm501_freq = (sm501_select_clock(2 * req_freq,
- &to, 5) / 2);
- }
- break;
-
- case SM501_CLOCK_V2XCLK:
- sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2);
- break;
-
- case SM501_CLOCK_MCLK:
- case SM501_CLOCK_M1XCLK:
- sm501_freq = sm501_select_clock(req_freq, &to, 3);
- break;
-
- default:
- sm501_freq = 0; /* error */
- }
-
- return sm501_freq;
-}
-
-EXPORT_SYMBOL_GPL(sm501_find_clock);
-
static struct sm501_device *to_sm_device(struct platform_device *pdev)
{
return container_of(pdev, struct sm501_device, pdev);
@@ -915,12 +872,13 @@ static void sm501_gpio_ensure_gpio(struct sm501_gpio_chip *smchip,
}
}
-static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+static int sm501_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct sm501_gpio_chip *smchip = gpiochip_get_data(chip);
struct sm501_gpio *smgpio = smchip->ourgpio;
- unsigned long bit = 1 << offset;
+ unsigned long bit = BIT(offset);
void __iomem *regs = smchip->regbase;
unsigned long save;
unsigned long val;
@@ -939,6 +897,8 @@ static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
sm501_gpio_ensure_gpio(smchip, bit);
spin_unlock_irqrestore(&smgpio->lock, save);
+
+ return 0;
}
static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset)
@@ -946,7 +906,7 @@ static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset)
struct sm501_gpio_chip *smchip = gpiochip_get_data(chip);
struct sm501_gpio *smgpio = smchip->ourgpio;
void __iomem *regs = smchip->regbase;
- unsigned long bit = 1 << offset;
+ unsigned long bit = BIT(offset);
unsigned long save;
unsigned long ddr;
@@ -971,7 +931,7 @@ static int sm501_gpio_output(struct gpio_chip *chip,
{
struct sm501_gpio_chip *smchip = gpiochip_get_data(chip);
struct sm501_gpio *smgpio = smchip->ourgpio;
- unsigned long bit = 1 << offset;
+ unsigned long bit = BIT(offset);
void __iomem *regs = smchip->regbase;
unsigned long save;
unsigned long val;
@@ -1667,7 +1627,7 @@ static void sm501_pci_remove(struct pci_dev *dev)
pci_disable_device(dev);
}
-static int sm501_plat_remove(struct platform_device *dev)
+static void sm501_plat_remove(struct platform_device *dev)
{
struct sm501_devdata *sm = platform_get_drvdata(dev);
@@ -1675,8 +1635,6 @@ static int sm501_plat_remove(struct platform_device *dev)
iounmap(sm->regs);
release_mem_region(sm->io_res->start, 0x100);
-
- return 0;
}
static const struct pci_device_id sm501_pci_tbl[] = {
diff --git a/drivers/mfd/sprd-sc27xx-spi.c b/drivers/mfd/sprd-sc27xx-spi.c
index d21f32cc784d..d6b4350779e6 100644
--- a/drivers/mfd/sprd-sc27xx-spi.c
+++ b/drivers/mfd/sprd-sc27xx-spi.c
@@ -8,7 +8,7 @@
#include <linux/module.h>
#include <linux/mfd/core.h>
#include <linux/mfd/sc27xx-pmic.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
@@ -135,7 +135,7 @@ static int sprd_pmic_spi_read(void *context,
return 0;
}
-static struct regmap_bus sprd_pmic_regmap = {
+static const struct regmap_bus sprd_pmic_regmap = {
.write = sprd_pmic_spi_write,
.read = sprd_pmic_spi_read,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
@@ -210,7 +210,10 @@ static int sprd_pmic_probe(struct spi_device *spi)
return ret;
}
- device_init_wakeup(&spi->dev, true);
+ ret = devm_device_init_wakeup(&spi->dev);
+ if (ret)
+ return dev_err_probe(&spi->dev, ret, "Failed to init wakeup\n");
+
return 0;
}
diff --git a/drivers/mfd/ssbi.c b/drivers/mfd/ssbi.c
index dee89db3471d..6e7aff6e2746 100644
--- a/drivers/mfd/ssbi.c
+++ b/drivers/mfd/ssbi.c
@@ -14,12 +14,12 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/ssbi.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
/* SSBI 2.0 controller registers */
#define SSBI2_CMD 0x0008
@@ -64,7 +64,6 @@ enum ssbi_controller_type {
};
struct ssbi {
- struct device *slave;
void __iomem *base;
spinlock_t lock;
enum ssbi_controller_type controller_type;
@@ -320,6 +319,7 @@ static struct platform_driver ssbi_driver = {
};
module_platform_driver(ssbi_driver);
+MODULE_DESCRIPTION("Qualcomm Single-wire Serial Bus Interface (SSBI) driver");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.0");
MODULE_ALIAS("platform:ssbi");
diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c
deleted file mode 100644
index 02cc49daf2e3..000000000000
--- a/drivers/mfd/sta2x11-mfd.c
+++ /dev/null
@@ -1,645 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * STA2x11 mfd for GPIO, SCTL and APBREG
- *
- * Copyright (c) 2009-2011 Wind River Systems, Inc.
- * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini, Davide Ciminaghi)
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/export.h>
-#include <linux/spinlock.h>
-#include <linux/errno.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/io.h>
-#include <linux/ioport.h>
-#include <linux/pci.h>
-#include <linux/seq_file.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/sta2x11-mfd.h>
-#include <linux/regmap.h>
-
-#include <asm/sta2x11.h>
-
-static inline int __reg_within_range(unsigned int r,
- unsigned int start,
- unsigned int end)
-{
- return ((r >= start) && (r <= end));
-}
-
-/* This describes STA2X11 MFD chip for us, we may have several */
-struct sta2x11_mfd {
- struct sta2x11_instance *instance;
- struct regmap *regmap[sta2x11_n_mfd_plat_devs];
- spinlock_t lock[sta2x11_n_mfd_plat_devs];
- struct list_head list;
- void __iomem *regs[sta2x11_n_mfd_plat_devs];
-};
-
-static LIST_HEAD(sta2x11_mfd_list);
-
-/* Three functions to act on the list */
-static struct sta2x11_mfd *sta2x11_mfd_find(struct pci_dev *pdev)
-{
- struct sta2x11_instance *instance;
- struct sta2x11_mfd *mfd;
-
- if (!pdev && !list_empty(&sta2x11_mfd_list)) {
- pr_warn("%s: Unspecified device, using first instance\n",
- __func__);
- return list_entry(sta2x11_mfd_list.next,
- struct sta2x11_mfd, list);
- }
-
- instance = sta2x11_get_instance(pdev);
- if (!instance)
- return NULL;
- list_for_each_entry(mfd, &sta2x11_mfd_list, list) {
- if (mfd->instance == instance)
- return mfd;
- }
- return NULL;
-}
-
-static int sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags)
-{
- int i;
- struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
- struct sta2x11_instance *instance;
-
- if (mfd)
- return -EBUSY;
- instance = sta2x11_get_instance(pdev);
- if (!instance)
- return -EINVAL;
- mfd = kzalloc(sizeof(*mfd), flags);
- if (!mfd)
- return -ENOMEM;
- INIT_LIST_HEAD(&mfd->list);
- for (i = 0; i < ARRAY_SIZE(mfd->lock); i++)
- spin_lock_init(&mfd->lock[i]);
- mfd->instance = instance;
- list_add(&mfd->list, &sta2x11_mfd_list);
- return 0;
-}
-
-/* This function is exported and is not expected to fail */
-u32 __sta2x11_mfd_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val,
- enum sta2x11_mfd_plat_dev index)
-{
- struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
- u32 r;
- unsigned long flags;
- void __iomem *regs;
-
- if (!mfd) {
- dev_warn(&pdev->dev, ": can't access sctl regs\n");
- return 0;
- }
-
- regs = mfd->regs[index];
- if (!regs) {
- dev_warn(&pdev->dev, ": system ctl not initialized\n");
- return 0;
- }
- spin_lock_irqsave(&mfd->lock[index], flags);
- r = readl(regs + reg);
- r &= ~mask;
- r |= val;
- if (mask)
- writel(r, regs + reg);
- spin_unlock_irqrestore(&mfd->lock[index], flags);
- return r;
-}
-EXPORT_SYMBOL(__sta2x11_mfd_mask);
-
-int sta2x11_mfd_get_regs_data(struct platform_device *dev,
- enum sta2x11_mfd_plat_dev index,
- void __iomem **regs,
- spinlock_t **lock)
-{
- struct pci_dev *pdev = *(struct pci_dev **)dev_get_platdata(&dev->dev);
- struct sta2x11_mfd *mfd;
-
- if (!pdev)
- return -ENODEV;
- mfd = sta2x11_mfd_find(pdev);
- if (!mfd)
- return -ENODEV;
- if (index >= sta2x11_n_mfd_plat_devs)
- return -ENODEV;
- *regs = mfd->regs[index];
- *lock = &mfd->lock[index];
- pr_debug("%s %d *regs = %p\n", __func__, __LINE__, *regs);
- return *regs ? 0 : -ENODEV;
-}
-EXPORT_SYMBOL(sta2x11_mfd_get_regs_data);
-
-/*
- * Special sta2x11-mfd regmap lock/unlock functions
- */
-
-static void sta2x11_regmap_lock(void *__lock)
-{
- spinlock_t *lock = __lock;
- spin_lock(lock);
-}
-
-static void sta2x11_regmap_unlock(void *__lock)
-{
- spinlock_t *lock = __lock;
- spin_unlock(lock);
-}
-
-/* OTP (one time programmable registers do not require locking */
-static void sta2x11_regmap_nolock(void *__lock)
-{
-}
-
-static const char *sta2x11_mfd_names[sta2x11_n_mfd_plat_devs] = {
- [sta2x11_sctl] = STA2X11_MFD_SCTL_NAME,
- [sta2x11_apbreg] = STA2X11_MFD_APBREG_NAME,
- [sta2x11_apb_soc_regs] = STA2X11_MFD_APB_SOC_REGS_NAME,
- [sta2x11_scr] = STA2X11_MFD_SCR_NAME,
-};
-
-static bool sta2x11_sctl_writeable_reg(struct device *dev, unsigned int reg)
-{
- return !__reg_within_range(reg, SCTL_SCPCIECSBRST, SCTL_SCRSTSTA);
-}
-
-static struct regmap_config sta2x11_sctl_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .lock = sta2x11_regmap_lock,
- .unlock = sta2x11_regmap_unlock,
- .max_register = SCTL_SCRSTSTA,
- .writeable_reg = sta2x11_sctl_writeable_reg,
-};
-
-static bool sta2x11_scr_readable_reg(struct device *dev, unsigned int reg)
-{
- return (reg == STA2X11_SECR_CR) ||
- __reg_within_range(reg, STA2X11_SECR_FVR0, STA2X11_SECR_FVR1);
-}
-
-static bool sta2x11_scr_writeable_reg(struct device *dev, unsigned int reg)
-{
- return false;
-}
-
-static struct regmap_config sta2x11_scr_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .lock = sta2x11_regmap_nolock,
- .unlock = sta2x11_regmap_nolock,
- .max_register = STA2X11_SECR_FVR1,
- .readable_reg = sta2x11_scr_readable_reg,
- .writeable_reg = sta2x11_scr_writeable_reg,
-};
-
-static bool sta2x11_apbreg_readable_reg(struct device *dev, unsigned int reg)
-{
- /* Two blocks (CAN and MLB, SARAC) 0x100 bytes apart */
- if (reg >= APBREG_BSR_SARAC)
- reg -= APBREG_BSR_SARAC;
- switch (reg) {
- case APBREG_BSR:
- case APBREG_PAER:
- case APBREG_PWAC:
- case APBREG_PRAC:
- case APBREG_PCG:
- case APBREG_PUR:
- case APBREG_EMU_PCG:
- return true;
- default:
- return false;
- }
-}
-
-static bool sta2x11_apbreg_writeable_reg(struct device *dev, unsigned int reg)
-{
- if (reg >= APBREG_BSR_SARAC)
- reg -= APBREG_BSR_SARAC;
- if (!sta2x11_apbreg_readable_reg(dev, reg))
- return false;
- return reg != APBREG_PAER;
-}
-
-static struct regmap_config sta2x11_apbreg_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .lock = sta2x11_regmap_lock,
- .unlock = sta2x11_regmap_unlock,
- .max_register = APBREG_EMU_PCG_SARAC,
- .readable_reg = sta2x11_apbreg_readable_reg,
- .writeable_reg = sta2x11_apbreg_writeable_reg,
-};
-
-static bool sta2x11_apb_soc_regs_readable_reg(struct device *dev,
- unsigned int reg)
-{
- return reg <= PCIE_SoC_INT_ROUTER_STATUS3_REG ||
- __reg_within_range(reg, DMA_IP_CTRL_REG, SPARE3_RESERVED) ||
- __reg_within_range(reg, MASTER_LOCK_REG,
- SYSTEM_CONFIG_STATUS_REG) ||
- reg == MSP_CLK_CTRL_REG ||
- __reg_within_range(reg, COMPENSATION_REG1, TEST_CTL_REG);
-}
-
-static bool sta2x11_apb_soc_regs_writeable_reg(struct device *dev,
- unsigned int reg)
-{
- if (!sta2x11_apb_soc_regs_readable_reg(dev, reg))
- return false;
- switch (reg) {
- case PCIE_COMMON_CLOCK_CONFIG_0_4_0:
- case SYSTEM_CONFIG_STATUS_REG:
- case COMPENSATION_REG1:
- case PCIE_SoC_INT_ROUTER_STATUS0_REG...PCIE_SoC_INT_ROUTER_STATUS3_REG:
- case PCIE_PM_STATUS_0_PORT_0_4...PCIE_PM_STATUS_7_0_EP4:
- return false;
- default:
- return true;
- }
-}
-
-static struct regmap_config sta2x11_apb_soc_regs_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .lock = sta2x11_regmap_lock,
- .unlock = sta2x11_regmap_unlock,
- .max_register = TEST_CTL_REG,
- .readable_reg = sta2x11_apb_soc_regs_readable_reg,
- .writeable_reg = sta2x11_apb_soc_regs_writeable_reg,
-};
-
-static struct regmap_config *
-sta2x11_mfd_regmap_configs[sta2x11_n_mfd_plat_devs] = {
- [sta2x11_sctl] = &sta2x11_sctl_regmap_config,
- [sta2x11_apbreg] = &sta2x11_apbreg_regmap_config,
- [sta2x11_apb_soc_regs] = &sta2x11_apb_soc_regs_regmap_config,
- [sta2x11_scr] = &sta2x11_scr_regmap_config,
-};
-
-/* Probe for the four platform devices */
-
-static int sta2x11_mfd_platform_probe(struct platform_device *dev,
- enum sta2x11_mfd_plat_dev index)
-{
- struct pci_dev **pdev;
- struct sta2x11_mfd *mfd;
- struct resource *res;
- const char *name = sta2x11_mfd_names[index];
- struct regmap_config *regmap_config = sta2x11_mfd_regmap_configs[index];
-
- pdev = dev_get_platdata(&dev->dev);
- mfd = sta2x11_mfd_find(*pdev);
- if (!mfd)
- return -ENODEV;
- if (!regmap_config)
- return -ENODEV;
-
- res = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENOMEM;
-
- if (!request_mem_region(res->start, resource_size(res), name))
- return -EBUSY;
-
- mfd->regs[index] = ioremap(res->start, resource_size(res));
- if (!mfd->regs[index]) {
- release_mem_region(res->start, resource_size(res));
- return -ENOMEM;
- }
- regmap_config->lock_arg = &mfd->lock;
- /*
- No caching, registers could be reached both via regmap and via
- void __iomem *
- */
- regmap_config->cache_type = REGCACHE_NONE;
- mfd->regmap[index] = devm_regmap_init_mmio(&dev->dev, mfd->regs[index],
- regmap_config);
- WARN_ON(IS_ERR(mfd->regmap[index]));
-
- return 0;
-}
-
-static int sta2x11_sctl_probe(struct platform_device *dev)
-{
- return sta2x11_mfd_platform_probe(dev, sta2x11_sctl);
-}
-
-static int sta2x11_apbreg_probe(struct platform_device *dev)
-{
- return sta2x11_mfd_platform_probe(dev, sta2x11_apbreg);
-}
-
-static int sta2x11_apb_soc_regs_probe(struct platform_device *dev)
-{
- return sta2x11_mfd_platform_probe(dev, sta2x11_apb_soc_regs);
-}
-
-static int sta2x11_scr_probe(struct platform_device *dev)
-{
- return sta2x11_mfd_platform_probe(dev, sta2x11_scr);
-}
-
-/* The three platform drivers */
-static struct platform_driver sta2x11_sctl_platform_driver = {
- .driver = {
- .name = STA2X11_MFD_SCTL_NAME,
- },
- .probe = sta2x11_sctl_probe,
-};
-
-static struct platform_driver sta2x11_platform_driver = {
- .driver = {
- .name = STA2X11_MFD_APBREG_NAME,
- },
- .probe = sta2x11_apbreg_probe,
-};
-
-static struct platform_driver sta2x11_apb_soc_regs_platform_driver = {
- .driver = {
- .name = STA2X11_MFD_APB_SOC_REGS_NAME,
- },
- .probe = sta2x11_apb_soc_regs_probe,
-};
-
-static struct platform_driver sta2x11_scr_platform_driver = {
- .driver = {
- .name = STA2X11_MFD_SCR_NAME,
- },
- .probe = sta2x11_scr_probe,
-};
-
-static struct platform_driver * const drivers[] = {
- &sta2x11_platform_driver,
- &sta2x11_sctl_platform_driver,
- &sta2x11_apb_soc_regs_platform_driver,
- &sta2x11_scr_platform_driver,
-};
-
-static int __init sta2x11_drivers_init(void)
-{
- return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
-}
-
-/*
- * What follows are the PCI devices that host the above pdevs.
- * Each logic block is 4kB and they are all consecutive: we use this info.
- */
-
-/* Mfd 0 device */
-
-/* Mfd 0, Bar 0 */
-enum mfd0_bar0_cells {
- STA2X11_GPIO_0 = 0,
- STA2X11_GPIO_1,
- STA2X11_GPIO_2,
- STA2X11_GPIO_3,
- STA2X11_SCTL,
- STA2X11_SCR,
- STA2X11_TIME,
-};
-/* Mfd 0 , Bar 1 */
-enum mfd0_bar1_cells {
- STA2X11_APBREG = 0,
-};
-#define CELL_4K(_name, _cell) { \
- .name = _name, \
- .start = _cell * 4096, .end = _cell * 4096 + 4095, \
- .flags = IORESOURCE_MEM, \
- }
-
-static const struct resource gpio_resources[] = {
- {
- /* 4 consecutive cells, 1 driver */
- .name = STA2X11_MFD_GPIO_NAME,
- .start = 0,
- .end = (4 * 4096) - 1,
- .flags = IORESOURCE_MEM,
- }
-};
-static const struct resource sctl_resources[] = {
- CELL_4K(STA2X11_MFD_SCTL_NAME, STA2X11_SCTL),
-};
-static const struct resource scr_resources[] = {
- CELL_4K(STA2X11_MFD_SCR_NAME, STA2X11_SCR),
-};
-static const struct resource time_resources[] = {
- CELL_4K(STA2X11_MFD_TIME_NAME, STA2X11_TIME),
-};
-
-static const struct resource apbreg_resources[] = {
- CELL_4K(STA2X11_MFD_APBREG_NAME, STA2X11_APBREG),
-};
-
-#define DEV(_name, _r) \
- { .name = _name, .num_resources = ARRAY_SIZE(_r), .resources = _r, }
-
-static struct mfd_cell sta2x11_mfd0_bar0[] = {
- /* offset 0: we add pdata later */
- DEV(STA2X11_MFD_GPIO_NAME, gpio_resources),
- DEV(STA2X11_MFD_SCTL_NAME, sctl_resources),
- DEV(STA2X11_MFD_SCR_NAME, scr_resources),
- DEV(STA2X11_MFD_TIME_NAME, time_resources),
-};
-
-static struct mfd_cell sta2x11_mfd0_bar1[] = {
- DEV(STA2X11_MFD_APBREG_NAME, apbreg_resources),
-};
-
-/* Mfd 1 devices */
-
-/* Mfd 1, Bar 0 */
-enum mfd1_bar0_cells {
- STA2X11_VIC = 0,
-};
-
-/* Mfd 1, Bar 1 */
-enum mfd1_bar1_cells {
- STA2X11_APB_SOC_REGS = 0,
-};
-
-static const struct resource vic_resources[] = {
- CELL_4K(STA2X11_MFD_VIC_NAME, STA2X11_VIC),
-};
-
-static const struct resource apb_soc_regs_resources[] = {
- CELL_4K(STA2X11_MFD_APB_SOC_REGS_NAME, STA2X11_APB_SOC_REGS),
-};
-
-static struct mfd_cell sta2x11_mfd1_bar0[] = {
- DEV(STA2X11_MFD_VIC_NAME, vic_resources),
-};
-
-static struct mfd_cell sta2x11_mfd1_bar1[] = {
- DEV(STA2X11_MFD_APB_SOC_REGS_NAME, apb_soc_regs_resources),
-};
-
-
-static int sta2x11_mfd_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
-
- return 0;
-}
-
-static int sta2x11_mfd_resume(struct pci_dev *pdev)
-{
- int err;
-
- pci_set_power_state(pdev, PCI_D0);
- err = pci_enable_device(pdev);
- if (err)
- return err;
- pci_restore_state(pdev);
-
- return 0;
-}
-
-struct sta2x11_mfd_bar_setup_data {
- struct mfd_cell *cells;
- int ncells;
-};
-
-struct sta2x11_mfd_setup_data {
- struct sta2x11_mfd_bar_setup_data bars[2];
-};
-
-#define STA2X11_MFD0 0
-#define STA2X11_MFD1 1
-
-static struct sta2x11_mfd_setup_data mfd_setup_data[] = {
- /* Mfd 0: gpio, sctl, scr, timers / apbregs */
- [STA2X11_MFD0] = {
- .bars = {
- [0] = {
- .cells = sta2x11_mfd0_bar0,
- .ncells = ARRAY_SIZE(sta2x11_mfd0_bar0),
- },
- [1] = {
- .cells = sta2x11_mfd0_bar1,
- .ncells = ARRAY_SIZE(sta2x11_mfd0_bar1),
- },
- },
- },
- /* Mfd 1: vic / apb-soc-regs */
- [STA2X11_MFD1] = {
- .bars = {
- [0] = {
- .cells = sta2x11_mfd1_bar0,
- .ncells = ARRAY_SIZE(sta2x11_mfd1_bar0),
- },
- [1] = {
- .cells = sta2x11_mfd1_bar1,
- .ncells = ARRAY_SIZE(sta2x11_mfd1_bar1),
- },
- },
- },
-};
-
-static void sta2x11_mfd_setup(struct pci_dev *pdev,
- struct sta2x11_mfd_setup_data *sd)
-{
- int i, j;
- for (i = 0; i < ARRAY_SIZE(sd->bars); i++)
- for (j = 0; j < sd->bars[i].ncells; j++) {
- sd->bars[i].cells[j].pdata_size = sizeof(pdev);
- sd->bars[i].cells[j].platform_data = &pdev;
- }
-}
-
-static int sta2x11_mfd_probe(struct pci_dev *pdev,
- const struct pci_device_id *pci_id)
-{
- int err, i;
- struct sta2x11_mfd_setup_data *setup_data;
-
- dev_info(&pdev->dev, "%s\n", __func__);
-
- err = pci_enable_device(pdev);
- if (err) {
- dev_err(&pdev->dev, "Can't enable device.\n");
- return err;
- }
-
- err = pci_enable_msi(pdev);
- if (err)
- dev_info(&pdev->dev, "Enable msi failed\n");
-
- setup_data = pci_id->device == PCI_DEVICE_ID_STMICRO_GPIO ?
- &mfd_setup_data[STA2X11_MFD0] :
- &mfd_setup_data[STA2X11_MFD1];
-
- /* platform data is the pci device for all of them */
- sta2x11_mfd_setup(pdev, setup_data);
-
- /* Record this pdev before mfd_add_devices: their probe looks for it */
- if (!sta2x11_mfd_find(pdev))
- sta2x11_mfd_add(pdev, GFP_KERNEL);
-
- /* Just 2 bars for all mfd's at present */
- for (i = 0; i < 2; i++) {
- err = mfd_add_devices(&pdev->dev, -1,
- setup_data->bars[i].cells,
- setup_data->bars[i].ncells,
- &pdev->resource[i],
- 0, NULL);
- if (err) {
- dev_err(&pdev->dev,
- "mfd_add_devices[%d] failed: %d\n", i, err);
- goto err_disable;
- }
- }
-
- return 0;
-
-err_disable:
- mfd_remove_devices(&pdev->dev);
- pci_disable_device(pdev);
- pci_disable_msi(pdev);
- return err;
-}
-
-static const struct pci_device_id sta2x11_mfd_tbl[] = {
- {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_GPIO)},
- {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIC)},
- {0,},
-};
-
-static struct pci_driver sta2x11_mfd_driver = {
- .name = "sta2x11-mfd",
- .id_table = sta2x11_mfd_tbl,
- .probe = sta2x11_mfd_probe,
- .suspend = sta2x11_mfd_suspend,
- .resume = sta2x11_mfd_resume,
-};
-
-static int __init sta2x11_mfd_init(void)
-{
- pr_info("%s\n", __func__);
- return pci_register_driver(&sta2x11_mfd_driver);
-}
-
-/*
- * All of this must be ready before "normal" devices like MMCI appear.
- * But MFD (the pci device) can't be too early. The following choice
- * prepares platform drivers very early and probe the PCI device later,
- * but before other PCI devices.
- */
-subsys_initcall(sta2x11_drivers_init);
-rootfs_initcall(sta2x11_mfd_init);
diff --git a/drivers/mfd/stm32-lptimer.c b/drivers/mfd/stm32-lptimer.c
index fa322f4412c8..123659178cc2 100644
--- a/drivers/mfd/stm32-lptimer.c
+++ b/drivers/mfd/stm32-lptimer.c
@@ -6,9 +6,11 @@
* Inspired by Benjamin Gaignard's stm32-timers driver
*/
+#include <linux/bitfield.h>
#include <linux/mfd/stm32-lptimer.h>
#include <linux/module.h>
#include <linux/of_platform.h>
+#include <linux/platform_device.h>
#define STM32_LPTIM_MAX_REGISTER 0x3fc
@@ -17,7 +19,6 @@ static const struct regmap_config stm32_lptimer_regmap_cfg = {
.val_bits = 32,
.reg_stride = sizeof(u32),
.max_register = STM32_LPTIM_MAX_REGISTER,
- .fast_io = true,
};
static int stm32_lptimer_detect_encoder(struct stm32_lptimer *ddata)
@@ -48,6 +49,36 @@ static int stm32_lptimer_detect_encoder(struct stm32_lptimer *ddata)
return 0;
}
+static int stm32_lptimer_detect_hwcfgr(struct stm32_lptimer *ddata)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_read(ddata->regmap, STM32_LPTIM_VERR, &ddata->version);
+ if (ret)
+ return ret;
+
+ /* Try to guess parameters from HWCFGR: e.g. encoder mode (STM32MP15) */
+ ret = regmap_read(ddata->regmap, STM32_LPTIM_HWCFGR1, &val);
+ if (ret)
+ return ret;
+
+ /* Fallback to legacy init if HWCFGR isn't present */
+ if (!val)
+ return stm32_lptimer_detect_encoder(ddata);
+
+ ddata->has_encoder = FIELD_GET(STM32_LPTIM_HWCFGR1_ENCODER, val);
+
+ ret = regmap_read(ddata->regmap, STM32_LPTIM_HWCFGR2, &val);
+ if (ret)
+ return ret;
+
+ /* Number of capture/compare channels */
+ ddata->num_cc_chans = FIELD_GET(STM32_LPTIM_HWCFGR2_CHAN_NUM, val);
+
+ return 0;
+}
+
static int stm32_lptimer_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -72,7 +103,7 @@ static int stm32_lptimer_probe(struct platform_device *pdev)
if (IS_ERR(ddata->clk))
return PTR_ERR(ddata->clk);
- ret = stm32_lptimer_detect_encoder(ddata);
+ ret = stm32_lptimer_detect_hwcfgr(ddata);
if (ret)
return ret;
diff --git a/drivers/mfd/stm32-timers.c b/drivers/mfd/stm32-timers.c
index 44ed2fce0319..b3dbc02aaf79 100644
--- a/drivers/mfd/stm32-timers.c
+++ b/drivers/mfd/stm32-timers.c
@@ -5,9 +5,12 @@
*/
#include <linux/bitfield.h>
+#include <linux/export.h>
#include <linux/mfd/stm32-timers.h>
#include <linux/module.h>
#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/reset.h>
#define STM32_TIMERS_MAX_REGISTERS 0x3fc
@@ -172,6 +175,31 @@ static void stm32_timers_get_arr_size(struct stm32_timers *ddata)
regmap_write(ddata->regmap, TIM_ARR, arr);
}
+static int stm32_timers_probe_hwcfgr(struct device *dev, struct stm32_timers *ddata)
+{
+ u32 val;
+
+ ddata->ipidr = (uintptr_t)device_get_match_data(dev);
+ if (!ddata->ipidr) {
+ /* Fallback to legacy method for probing counter width */
+ stm32_timers_get_arr_size(ddata);
+ return 0;
+ }
+
+ regmap_read(ddata->regmap, TIM_IPIDR, &val);
+ if (val != ddata->ipidr) {
+ dev_err(dev, "Unsupported device detected: %u\n", val);
+ return -EINVAL;
+ }
+
+ regmap_read(ddata->regmap, TIM_HWCFGR2, &val);
+
+ /* Counter width in bits, max reload value is BIT(width) - 1 */
+ ddata->max_arr = BIT(FIELD_GET(TIM_HWCFGR2_CNT_WIDTH, val)) - 1;
+
+ return 0;
+}
+
static int stm32_timers_dma_probe(struct device *dev,
struct stm32_timers *ddata)
{
@@ -214,6 +242,48 @@ static void stm32_timers_dma_remove(struct device *dev,
dma_release_channel(ddata->dma.chans[i]);
}
+static const char * const stm32_timers_irq_name[STM32_TIMERS_MAX_IRQS] = {
+ "brk", "up", "trg-com", "cc"
+};
+
+static int stm32_timers_irq_probe(struct platform_device *pdev,
+ struct stm32_timers *ddata)
+{
+ int i, ret;
+
+ /*
+ * STM32 Timer may have either:
+ * - a unique global interrupt line
+ * - four dedicated interrupt lines that may be handled separately.
+ * Optionally get them here, to be used by child devices.
+ */
+ ret = platform_get_irq_byname_optional(pdev, "global");
+ if (ret < 0 && ret != -ENXIO) {
+ return ret;
+ } else if (ret != -ENXIO) {
+ ddata->irq[STM32_TIMERS_IRQ_GLOBAL_BRK] = ret;
+ ddata->nr_irqs = 1;
+ return 0;
+ }
+
+ for (i = 0; i < STM32_TIMERS_MAX_IRQS; i++) {
+ ret = platform_get_irq_byname_optional(pdev, stm32_timers_irq_name[i]);
+ if (ret < 0 && ret != -ENXIO) {
+ return ret;
+ } else if (ret != -ENXIO) {
+ ddata->irq[i] = ret;
+ ddata->nr_irqs++;
+ }
+ }
+
+ if (ddata->nr_irqs && ddata->nr_irqs != STM32_TIMERS_MAX_IRQS) {
+ dev_err(&pdev->dev, "Invalid number of IRQs %d\n", ddata->nr_irqs);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int stm32_timers_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -226,8 +296,7 @@ static int stm32_timers_probe(struct platform_device *pdev)
if (!ddata)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- mmio = devm_ioremap_resource(dev, res);
+ mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(mmio))
return PTR_ERR(mmio);
@@ -243,7 +312,13 @@ static int stm32_timers_probe(struct platform_device *pdev)
if (IS_ERR(ddata->clk))
return PTR_ERR(ddata->clk);
- stm32_timers_get_arr_size(ddata);
+ ret = stm32_timers_probe_hwcfgr(dev, ddata);
+ if (ret)
+ return ret;
+
+ ret = stm32_timers_irq_probe(pdev, ddata);
+ if (ret)
+ return ret;
ret = stm32_timers_dma_probe(dev, ddata);
if (ret) {
@@ -260,7 +335,7 @@ static int stm32_timers_probe(struct platform_device *pdev)
return ret;
}
-static int stm32_timers_remove(struct platform_device *pdev)
+static void stm32_timers_remove(struct platform_device *pdev)
{
struct stm32_timers *ddata = platform_get_drvdata(pdev);
@@ -270,12 +345,11 @@ static int stm32_timers_remove(struct platform_device *pdev)
*/
of_platform_depopulate(&pdev->dev);
stm32_timers_dma_remove(&pdev->dev, ddata);
-
- return 0;
}
static const struct of_device_id stm32_timers_of_match[] = {
{ .compatible = "st,stm32-timers", },
+ { .compatible = "st,stm32mp25-timers", .data = (void *)STM32MP25_TIM_IPIDR },
{ /* end node */ },
};
MODULE_DEVICE_TABLE(of, stm32_timers_of_match);
diff --git a/drivers/mfd/stmfx.c b/drivers/mfd/stmfx.c
index c02cbd9c2f5d..f683fdb6ece6 100644
--- a/drivers/mfd/stmfx.c
+++ b/drivers/mfd/stmfx.c
@@ -53,7 +53,7 @@ static const struct regmap_config stmfx_regmap_config = {
.max_register = STMFX_REG_MAX,
.volatile_reg = stmfx_reg_volatile,
.writeable_reg = stmfx_reg_writeable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct resource stmfx_pinctrl_resources[] = {
@@ -269,9 +269,8 @@ static int stmfx_irq_init(struct i2c_client *client)
u32 irqoutpin = 0, irqtrigger;
int ret;
- stmfx->irq_domain = irq_domain_add_simple(stmfx->dev->of_node,
- STMFX_REG_IRQ_SRC_MAX, 0,
- &stmfx_irq_ops, stmfx);
+ stmfx->irq_domain = irq_domain_create_simple(dev_fwnode(stmfx->dev), STMFX_REG_IRQ_SRC_MAX,
+ 0, &stmfx_irq_ops, stmfx);
if (!stmfx->irq_domain) {
dev_err(stmfx->dev, "Failed to create IRQ domain\n");
return -EINVAL;
diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c
index 1d7b401776d1..943fa363efc3 100644
--- a/drivers/mfd/stmpe-i2c.c
+++ b/drivers/mfd/stmpe-i2c.c
@@ -87,7 +87,7 @@ stmpe_i2c_probe(struct i2c_client *i2c)
dev_info(&i2c->dev, "matching on node name, compatible is preferred\n");
partnum = id->driver_data;
} else
- partnum = (enum stmpe_partnum)of_id->data;
+ partnum = (uintptr_t)of_id->data;
return stmpe_probe(&i2c_ci, partnum);
}
@@ -122,18 +122,8 @@ static struct i2c_driver stmpe_i2c_driver = {
.remove = stmpe_i2c_remove,
.id_table = stmpe_i2c_id,
};
-
-static int __init stmpe_init(void)
-{
- return i2c_add_driver(&stmpe_i2c_driver);
-}
-subsys_initcall(stmpe_init);
-
-static void __exit stmpe_exit(void)
-{
- i2c_del_driver(&stmpe_i2c_driver);
-}
-module_exit(stmpe_exit);
+module_i2c_driver(stmpe_i2c_driver);
MODULE_DESCRIPTION("STMPE MFD I2C Interface Driver");
MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/stmpe-spi.c b/drivers/mfd/stmpe-spi.c
index 792236f56399..dea31efface6 100644
--- a/drivers/mfd/stmpe-spi.c
+++ b/drivers/mfd/stmpe-spi.c
@@ -129,7 +129,7 @@ static const struct spi_device_id stmpe_spi_id[] = {
{ "stmpe2403", STMPE2403 },
{ }
};
-MODULE_DEVICE_TABLE(spi, stmpe_id);
+MODULE_DEVICE_TABLE(spi, stmpe_spi_id);
static struct spi_driver stmpe_spi_driver = {
.driver = {
@@ -141,18 +141,8 @@ static struct spi_driver stmpe_spi_driver = {
.remove = stmpe_spi_remove,
.id_table = stmpe_spi_id,
};
-
-static int __init stmpe_init(void)
-{
- return spi_register_driver(&stmpe_spi_driver);
-}
-subsys_initcall(stmpe_init);
-
-static void __exit stmpe_exit(void)
-{
- spi_unregister_driver(&stmpe_spi_driver);
-}
-module_exit(stmpe_exit);
+module_spi_driver(stmpe_spi_driver);
MODULE_DESCRIPTION("STMPE MFD SPI Interface Driver");
MODULE_AUTHOR("Viresh Kumar <vireshk@kernel.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index 9c3cf58457a7..3c5c2f157f52 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -1219,8 +1219,8 @@ static int stmpe_irq_init(struct stmpe *stmpe, struct device_node *np)
int base = 0;
int num_irqs = stmpe->variant->num_irqs;
- stmpe->domain = irq_domain_add_simple(np, num_irqs, base,
- &stmpe_irq_ops, stmpe);
+ stmpe->domain = irq_domain_create_simple(of_fwnode_handle(np), num_irqs,
+ base, &stmpe_irq_ops, stmpe);
if (!stmpe->domain) {
dev_err(stmpe->dev, "Failed to create irqdomain\n");
return -ENOSYS;
@@ -1482,9 +1482,13 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum)
return ret;
}
+EXPORT_SYMBOL_GPL(stmpe_probe);
void stmpe_remove(struct stmpe *stmpe)
{
+ if (stmpe->domain)
+ irq_domain_remove(stmpe->domain);
+
if (!IS_ERR(stmpe->vio) && regulator_is_enabled(stmpe->vio))
regulator_disable(stmpe->vio);
if (!IS_ERR(stmpe->vcc) && regulator_is_enabled(stmpe->vcc))
@@ -1494,6 +1498,7 @@ void stmpe_remove(struct stmpe *stmpe)
mfd_remove_devices(stmpe->dev);
}
+EXPORT_SYMBOL_GPL(stmpe_remove);
static int stmpe_suspend(struct device *dev)
{
@@ -1517,3 +1522,7 @@ static int stmpe_resume(struct device *dev)
EXPORT_GPL_SIMPLE_DEV_PM_OPS(stmpe_dev_pm_ops,
stmpe_suspend, stmpe_resume);
+
+MODULE_DESCRIPTION("STMPE Core driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c
index 3cc7492f828f..081827bc0596 100644
--- a/drivers/mfd/stpmic1.c
+++ b/drivers/mfd/stpmic1.c
@@ -63,7 +63,7 @@ static const struct regmap_access_table stpmic1_volatile_table = {
static const struct regmap_config stpmic1_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = PMIC_MAX_REGISTER_ADDRESS,
.rd_table = &stpmic1_readable_table,
.wr_table = &stpmic1_writeable_table,
@@ -170,11 +170,7 @@ static int stpmic1_probe(struct i2c_client *i2c)
return ret;
}
- ret = devm_register_sys_off_handler(ddata->dev,
- SYS_OFF_MODE_POWER_OFF,
- SYS_OFF_PRIO_DEFAULT,
- stpmic1_power_off,
- ddata);
+ ret = devm_register_power_off_handler(ddata->dev, stpmic1_power_off, ddata);
if (ret) {
dev_err(ddata->dev, "failed to register sys-off handler: %d\n", ret);
return ret;
@@ -219,7 +215,7 @@ MODULE_DEVICE_TABLE(of, stpmic1_of_match);
static struct i2c_driver stpmic1_driver = {
.driver = {
.name = "stpmic1",
- .of_match_table = of_match_ptr(stpmic1_of_match),
+ .of_match_table = stpmic1_of_match,
.pm = pm_sleep_ptr(&stpmic1_pm),
},
.probe = stpmic1_probe,
diff --git a/drivers/mfd/stw481x.c b/drivers/mfd/stw481x.c
index f35c3af680dd..5ed64d53c23d 100644
--- a/drivers/mfd/stw481x.c
+++ b/drivers/mfd/stw481x.c
@@ -222,8 +222,8 @@ static int stw481x_probe(struct i2c_client *client)
* the structure of the I2C core.
*/
static const struct i2c_device_id stw481x_id[] = {
- { "stw481x", 0 },
- { },
+ { "stw481x" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, stw481x_id);
diff --git a/drivers/mfd/sun4i-gpadc.c b/drivers/mfd/sun4i-gpadc.c
index d1cbea27b136..bf2f6fdaf8bf 100644
--- a/drivers/mfd/sun4i-gpadc.c
+++ b/drivers/mfd/sun4i-gpadc.c
@@ -8,8 +8,8 @@
#include <linux/kernel.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/mfd/sun4i-gpadc.h>
@@ -72,7 +72,6 @@ static const struct regmap_config sun4i_gpadc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
- .fast_io = true,
};
static const struct of_device_id sun4i_gpadc_of_match[] = {
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index 57b29c325131..e5d5def594f6 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -8,25 +8,20 @@
* Author: Dong Aisheng <dong.aisheng@linaro.org>
*/
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/hwspinlock.h>
-#include <linux/io.h>
-#include <linux/init.h>
#include <linux/list.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/platform_data/syscon.h>
-#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/mfd/syscon.h>
#include <linux/slab.h>
-static struct platform_driver syscon_driver;
-
-static DEFINE_SPINLOCK(syscon_list_slock);
+static DEFINE_MUTEX(syscon_list_lock);
static LIST_HEAD(syscon_list);
struct syscon {
@@ -45,7 +40,6 @@ static const struct regmap_config syscon_regmap_config = {
static struct syscon *of_syscon_register(struct device_node *np, bool check_res)
{
struct clk *clk;
- struct syscon *syscon;
struct regmap *regmap;
void __iomem *base;
u32 reg_io_width;
@@ -53,21 +47,20 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_res)
struct regmap_config syscon_config = syscon_regmap_config;
struct resource res;
struct reset_control *reset;
+ resource_size_t res_size;
- syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
+ WARN_ON(!mutex_is_locked(&syscon_list_lock));
+
+ struct syscon *syscon __free(kfree) = kzalloc(sizeof(*syscon), GFP_KERNEL);
if (!syscon)
return ERR_PTR(-ENOMEM);
- if (of_address_to_resource(np, 0, &res)) {
- ret = -ENOMEM;
- goto err_map;
- }
+ if (of_address_to_resource(np, 0, &res))
+ return ERR_PTR(-ENOMEM);
base = of_iomap(np, 0);
- if (!base) {
- ret = -ENOMEM;
- goto err_map;
- }
+ if (!base)
+ return ERR_PTR(-ENOMEM);
/* Parse the device's DT node for an endianness specification */
if (of_property_read_bool(np, "big-endian"))
@@ -104,10 +97,22 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_res)
}
}
+ res_size = resource_size(&res);
+ if (res_size < reg_io_width) {
+ ret = -EFAULT;
+ goto err_regmap;
+ }
+
syscon_config.name = kasprintf(GFP_KERNEL, "%pOFn@%pa", np, &res.start);
+ if (!syscon_config.name) {
+ ret = -ENOMEM;
+ goto err_regmap;
+ }
syscon_config.reg_stride = reg_io_width;
syscon_config.val_bits = reg_io_width * 8;
- syscon_config.max_register = resource_size(&res) - reg_io_width;
+ syscon_config.max_register = res_size - reg_io_width;
+ if (!syscon_config.max_register)
+ syscon_config.max_register_is_0 = true;
regmap = regmap_init_mmio(NULL, base, &syscon_config);
kfree(syscon_config.name);
@@ -144,11 +149,9 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_res)
syscon->regmap = regmap;
syscon->np = np;
- spin_lock(&syscon_list_slock);
list_add_tail(&syscon->list, &syscon_list);
- spin_unlock(&syscon_list_slock);
- return syscon;
+ return_ptr(syscon);
err_reset:
reset_control_put(reset);
@@ -159,17 +162,16 @@ err_clk:
regmap_exit(regmap);
err_regmap:
iounmap(base);
-err_map:
- kfree(syscon);
return ERR_PTR(ret);
}
static struct regmap *device_node_get_regmap(struct device_node *np,
+ bool create_regmap,
bool check_res)
{
struct syscon *entry, *syscon = NULL;
- spin_lock(&syscon_list_slock);
+ mutex_lock(&syscon_list_lock);
list_for_each_entry(entry, &syscon_list, list)
if (entry->np == np) {
@@ -177,10 +179,13 @@ static struct regmap *device_node_get_regmap(struct device_node *np,
break;
}
- spin_unlock(&syscon_list_slock);
-
- if (!syscon)
- syscon = of_syscon_register(np, check_res);
+ if (!syscon) {
+ if (create_regmap)
+ syscon = of_syscon_register(np, check_res);
+ else
+ syscon = ERR_PTR(-EPROBE_DEFER);
+ }
+ mutex_unlock(&syscon_list_lock);
if (IS_ERR(syscon))
return ERR_CAST(syscon);
@@ -188,18 +193,85 @@ static struct regmap *device_node_get_regmap(struct device_node *np,
return syscon->regmap;
}
+/**
+ * of_syscon_register_regmap() - Register regmap for specified device node
+ * @np: Device tree node
+ * @regmap: Pointer to regmap object
+ *
+ * Register an externally created regmap object with syscon for the specified
+ * device tree node. This regmap will then be returned to client drivers using
+ * the syscon_regmap_lookup_by_phandle() API.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int of_syscon_register_regmap(struct device_node *np, struct regmap *regmap)
+{
+ struct syscon *entry, *syscon = NULL;
+ int ret;
+
+ if (!np || !regmap)
+ return -EINVAL;
+
+ syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
+ if (!syscon)
+ return -ENOMEM;
+
+ /* check if syscon entry already exists */
+ mutex_lock(&syscon_list_lock);
+
+ list_for_each_entry(entry, &syscon_list, list)
+ if (entry->np == np) {
+ ret = -EEXIST;
+ goto err_unlock;
+ }
+
+ syscon->regmap = regmap;
+ syscon->np = np;
+
+ /* register the regmap in syscon list */
+ list_add_tail(&syscon->list, &syscon_list);
+ mutex_unlock(&syscon_list_lock);
+
+ return 0;
+
+err_unlock:
+ mutex_unlock(&syscon_list_lock);
+ kfree(syscon);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_syscon_register_regmap);
+
+/**
+ * device_node_to_regmap() - Get or create a regmap for specified device node
+ * @np: Device tree node
+ *
+ * Get a regmap for the specified device node. If there's not an existing
+ * regmap, then one is instantiated. This function should not be used if the
+ * device node has a custom regmap driver or has resources (clocks, resets) to
+ * be managed. Use syscon_node_to_regmap() instead for those cases.
+ *
+ * Return: regmap ptr on success, negative error code on failure.
+ */
struct regmap *device_node_to_regmap(struct device_node *np)
{
- return device_node_get_regmap(np, false);
+ return device_node_get_regmap(np, true, false);
}
EXPORT_SYMBOL_GPL(device_node_to_regmap);
+/**
+ * syscon_node_to_regmap() - Get or create a regmap for specified syscon device node
+ * @np: Device tree node
+ *
+ * Get a regmap for the specified device node. If there's not an existing
+ * regmap, then one is instantiated if the node is a generic "syscon". This
+ * function is safe to use for a syscon registered with
+ * of_syscon_register_regmap().
+ *
+ * Return: regmap ptr on success, negative error code on failure.
+ */
struct regmap *syscon_node_to_regmap(struct device_node *np)
{
- if (!of_device_is_compatible(np, "syscon"))
- return ERR_PTR(-EINVAL);
-
- return device_node_get_regmap(np, true);
+ return device_node_get_regmap(np, of_device_is_compatible(np, "syscon"), true);
}
EXPORT_SYMBOL_GPL(syscon_node_to_regmap);
@@ -234,7 +306,9 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
return ERR_PTR(-ENODEV);
regmap = syscon_node_to_regmap(syscon_np);
- of_node_put(syscon_np);
+
+ if (property)
+ of_node_put(syscon_np);
return regmap;
}
@@ -286,59 +360,3 @@ struct regmap *syscon_regmap_lookup_by_phandle_optional(struct device_node *np,
return regmap;
}
EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle_optional);
-
-static int syscon_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct syscon_platform_data *pdata = dev_get_platdata(dev);
- struct syscon *syscon;
- struct regmap_config syscon_config = syscon_regmap_config;
- struct resource *res;
- void __iomem *base;
-
- syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL);
- if (!syscon)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENOENT;
-
- base = devm_ioremap(dev, res->start, resource_size(res));
- if (!base)
- return -ENOMEM;
-
- syscon_config.max_register = resource_size(res) - 4;
- if (pdata)
- syscon_config.name = pdata->label;
- syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_config);
- if (IS_ERR(syscon->regmap)) {
- dev_err(dev, "regmap init failed\n");
- return PTR_ERR(syscon->regmap);
- }
-
- platform_set_drvdata(pdev, syscon);
-
- dev_dbg(dev, "regmap %pR registered\n", res);
-
- return 0;
-}
-
-static const struct platform_device_id syscon_ids[] = {
- { "syscon", },
- { }
-};
-
-static struct platform_driver syscon_driver = {
- .driver = {
- .name = "syscon",
- },
- .probe = syscon_probe,
- .id_table = syscon_ids,
-};
-
-static int __init syscon_init(void)
-{
- return platform_driver_register(&syscon_driver);
-}
-postcore_initcall(syscon_init);
diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c
index 16df64e3c0be..2d4eb771e230 100644
--- a/drivers/mfd/tc3589x.c
+++ b/drivers/mfd/tc3589x.c
@@ -234,9 +234,9 @@ static const struct irq_domain_ops tc3589x_irq_ops = {
static int tc3589x_irq_init(struct tc3589x *tc3589x, struct device_node *np)
{
- tc3589x->domain = irq_domain_add_simple(
- np, TC3589x_NR_INTERNAL_IRQS, 0,
- &tc3589x_irq_ops, tc3589x);
+ tc3589x->domain = irq_domain_create_simple(of_fwnode_handle(np),
+ TC3589x_NR_INTERNAL_IRQS, 0,
+ &tc3589x_irq_ops, tc3589x);
if (!tc3589x->domain) {
dev_err(tc3589x->dev, "Failed to create irqdomain\n");
@@ -312,8 +312,6 @@ static int tc3589x_device_init(struct tc3589x *tc3589x)
}
static const struct of_device_id tc3589x_match[] = {
- /* Legacy compatible string */
- { .compatible = "tc3589x", .data = (void *) TC3589X_UNKNOWN },
{ .compatible = "toshiba,tc35890", .data = (void *) TC3589X_TC35890 },
{ .compatible = "toshiba,tc35892", .data = (void *) TC3589X_TC35892 },
{ .compatible = "toshiba,tc35893", .data = (void *) TC3589X_TC35893 },
@@ -340,7 +338,7 @@ tc3589x_of_probe(struct device *dev, enum tc3589x_version *version)
of_id = of_match_device(tc3589x_match, dev);
if (!of_id)
return ERR_PTR(-ENODEV);
- *version = (enum tc3589x_version) of_id->data;
+ *version = (uintptr_t) of_id->data;
for_each_child_of_node(np, child) {
if (of_device_is_compatible(child, "toshiba,tc3589x-gpio"))
@@ -483,7 +481,7 @@ static struct i2c_driver tc3589x_driver = {
.driver = {
.name = "tc3589x",
.pm = pm_sleep_ptr(&tc3589x_dev_pm_ops),
- .of_match_table = of_match_ptr(tc3589x_match),
+ .of_match_table = tc3589x_match,
},
.probe = tc3589x_probe,
.remove = tc3589x_remove,
diff --git a/drivers/mfd/ti-lmu.c b/drivers/mfd/ti-lmu.c
index 4f06adad7b5e..cfc9f88b9842 100644
--- a/drivers/mfd/ti-lmu.c
+++ b/drivers/mfd/ti-lmu.c
@@ -17,7 +17,6 @@
#include <linux/mfd/ti-lmu-register.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/slab.h>
struct ti_lmu_data {
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index 07825cfd8aa8..068c25401c6c 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -14,7 +14,7 @@
#include <linux/mfd/core.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/mfd/ti_am335x_tscadc.h>
@@ -119,8 +119,6 @@ static int ti_tscadc_probe(struct platform_device *pdev)
struct clk *clk;
struct device_node *node;
struct mfd_cell *cell;
- struct property *prop;
- const __be32 *cur;
bool use_tsc = false, use_mag = false;
u32 val;
int err;
@@ -167,7 +165,7 @@ static int ti_tscadc_probe(struct platform_device *pdev)
}
node = of_get_child_by_name(pdev->dev.of_node, "adc");
- of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
+ of_property_for_each_u32(node, "ti,adc-channels", val) {
adc_channels++;
if (val > 7) {
dev_err(&pdev->dev, " PIN numbers are 0..7 (not %d)\n",
@@ -201,8 +199,7 @@ static int ti_tscadc_probe(struct platform_device *pdev)
else
tscadc->irq = err;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res);
+ tscadc->tscadc_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(tscadc->tscadc_base))
return PTR_ERR(tscadc->tscadc_base);
@@ -299,7 +296,7 @@ err_disable_clk:
return err;
}
-static int ti_tscadc_remove(struct platform_device *pdev)
+static void ti_tscadc_remove(struct platform_device *pdev)
{
struct ti_tscadc_dev *tscadc = platform_get_drvdata(pdev);
@@ -309,8 +306,6 @@ static int ti_tscadc_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
mfd_remove_devices(tscadc->dev);
-
- return 0;
}
static int __maybe_unused ti_tscadc_can_wakeup(struct device *dev, void *data)
@@ -382,7 +377,7 @@ static struct platform_driver ti_tscadc_driver = {
.of_match_table = ti_tscadc_dt_ids,
},
.probe = ti_tscadc_probe,
- .remove = ti_tscadc_remove,
+ .remove = ti_tscadc_remove,
};
diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c
index 07e5aa10a146..b059713db875 100644
--- a/drivers/mfd/timberdale.c
+++ b/drivers/mfd/timberdale.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/mfd/core.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/timb_gpio.h>
@@ -25,7 +26,6 @@
#include <linux/spi/max7301.h>
#include <linux/spi/mc33880.h>
-#include <linux/platform_data/tsc2007.h>
#include <linux/platform_data/media/timb_radio.h>
#include <linux/platform_data/media/timb_video.h>
@@ -49,16 +49,21 @@ struct timberdale_device {
/*--------------------------------------------------------------------------*/
-static struct tsc2007_platform_data timberdale_tsc2007_platform_data = {
- .model = 2003,
- .x_plate_ohms = 100
+static const struct property_entry timberdale_tsc2007_properties[] = {
+ PROPERTY_ENTRY_U32("ti,x-plate-ohms", 100),
+ { }
+};
+
+static const struct software_node timberdale_tsc2007_node = {
+ .name = "tsc2007",
+ .properties = timberdale_tsc2007_properties,
};
static struct i2c_board_info timberdale_i2c_board_info[] = {
{
I2C_BOARD_INFO("tsc2007", 0x48),
- .platform_data = &timberdale_tsc2007_platform_data,
- .irq = IRQ_TIMBERDALE_TSC_INT
+ .irq = IRQ_TIMBERDALE_TSC_INT,
+ .swnode = &timberdale_tsc2007_node,
},
};
@@ -765,7 +770,6 @@ static int timb_probe(struct pci_dev *dev,
default:
dev_err(&dev->dev, "Unknown IP setup: %d.%d.%d\n",
priv->fw.major, priv->fw.minor, ip_setup);
- err = -ENODEV;
goto err_mfd;
}
@@ -854,4 +858,5 @@ module_pci_driver(timberdale_pci_driver);
MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
MODULE_VERSION(DRV_VERSION);
+MODULE_DESCRIPTION("Timberdale FPGA MFD driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/tps6105x.c b/drivers/mfd/tps6105x.c
index 5601f6d0d874..e2f6858d101e 100644
--- a/drivers/mfd/tps6105x.c
+++ b/drivers/mfd/tps6105x.c
@@ -23,7 +23,7 @@
#include <linux/mfd/core.h>
#include <linux/mfd/tps6105x.h>
-static struct regmap_config tps6105x_regmap_config = {
+static const struct regmap_config tps6105x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = TPS6105X_REG_3,
@@ -191,8 +191,8 @@ static void tps6105x_remove(struct i2c_client *client)
}
static const struct i2c_device_id tps6105x_id[] = {
- { "tps61050", 0 },
- { "tps61052", 0 },
+ { "tps61050" },
+ { "tps61052" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tps6105x_id);
diff --git a/drivers/mfd/tps65010.c b/drivers/mfd/tps65010.c
index 2b9105295f30..8a144ec52201 100644
--- a/drivers/mfd/tps65010.c
+++ b/drivers/mfd/tps65010.c
@@ -16,6 +16,7 @@
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/string_choices.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
@@ -250,7 +251,7 @@ static int dbg_show(struct seq_file *s, void *_)
v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED1_PER);
seq_printf(s, "led1 %s, on=%02x, per=%02x, %d/%d msec\n",
(value & 0x80)
- ? ((v2 & 0x80) ? "on" : "off")
+ ? str_on_off(v2 & 0x80)
: ((v2 & 0x80) ? "blink" : "(nPG)"),
value, v2,
(value & 0x7f) * 10, (v2 & 0x7f) * 100);
@@ -259,7 +260,7 @@ static int dbg_show(struct seq_file *s, void *_)
v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED2_PER);
seq_printf(s, "led2 %s, on=%02x, per=%02x, %d/%d msec\n",
(value & 0x80)
- ? ((v2 & 0x80) ? "on" : "off")
+ ? str_on_off(v2 & 0x80)
: ((v2 & 0x80) ? "blink" : "off"),
value, v2,
(value & 0x7f) * 10, (v2 & 0x7f) * 100);
@@ -445,7 +446,7 @@ static irqreturn_t tps65010_irq(int irq, void *_tps)
* offsets 4..5 == LED1/nPG, LED2 (we set one of the non-BLINK modes)
* offset 6 == vibrator motor driver
*/
-static void
+static int
tps65010_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
if (offset < 4)
@@ -454,6 +455,8 @@ tps65010_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
tps65010_set_led(offset - 3, value ? ON : OFF);
else
tps65010_set_vib(value);
+
+ return 0;
}
static int
@@ -511,7 +514,6 @@ static void tps65010_remove(struct i2c_client *client)
if (client->irq > 0)
free_irq(client->irq, tps);
cancel_delayed_work_sync(&tps->work);
- debugfs_remove(tps->file);
the_tps = NULL;
}
@@ -544,17 +546,13 @@ static int tps65010_probe(struct i2c_client *client)
*/
if (client->irq > 0) {
status = request_irq(client->irq, tps65010_irq,
- IRQF_TRIGGER_FALLING, DRIVER_NAME, tps);
+ IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN,
+ DRIVER_NAME, tps);
if (status < 0) {
dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
client->irq, status);
return status;
}
- /* annoying race here, ideally we'd have an option
- * to claim the irq now and enable it later.
- * FIXME genirq IRQF_NOAUTOEN now solves that ...
- */
- disable_irq(client->irq);
set_bit(FLAG_IRQ_ENABLE, &tps->flags);
} else
dev_warn(&client->dev, "IRQ not configured!\n");
@@ -611,7 +609,7 @@ static int tps65010_probe(struct i2c_client *client)
tps65010_work(&tps->work.work);
- tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL,
+ tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, client->debugfs,
tps, DEBUG_FOPS);
/* optionally register GPIOs */
@@ -742,7 +740,7 @@ int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
TPS_DEFGPIO, defgpio);
pr_debug("%s: gpio%dout = %s, defgpio 0x%02x\n", DRIVER_NAME,
- gpio, value ? "high" : "low",
+ gpio, str_high_low(value),
i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO));
mutex_unlock(&the_tps->lock);
@@ -854,7 +852,7 @@ int tps65010_set_vib(unsigned value)
status = i2c_smbus_write_byte_data(the_tps->client,
TPS_VDCDC2, vdcdc2);
- pr_debug("%s: vibrator %s\n", DRIVER_NAME, value ? "on" : "off");
+ pr_debug("%s: vibrator %s\n", DRIVER_NAME, str_on_off(value));
mutex_unlock(&the_tps->lock);
return status;
@@ -876,7 +874,7 @@ int tps65010_set_low_pwr(unsigned mode)
mutex_lock(&the_tps->lock);
pr_debug("%s: %s low_pwr, vdcdc1 0x%02x\n", DRIVER_NAME,
- mode ? "enable" : "disable",
+ str_enable_disable(mode),
i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1);
@@ -988,7 +986,7 @@ int tps65013_set_low_pwr(unsigned mode)
pr_debug("%s: %s low_pwr, chgconfig 0x%02x vdcdc1 0x%02x\n",
DRIVER_NAME,
- mode ? "enable" : "disable",
+ str_enable_disable(mode),
i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG),
i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c
index 9716bf703c7a..9865512dc7cc 100644
--- a/drivers/mfd/tps6507x.c
+++ b/drivers/mfd/tps6507x.c
@@ -20,7 +20,6 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps6507x.h>
@@ -104,7 +103,7 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id tps6507x_i2c_id[] = {
- { "tps6507x", 0 },
+ { "tps6507x" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id);
diff --git a/drivers/mfd/tps65086.c b/drivers/mfd/tps65086.c
index 6a21000aad4a..54832e9321b9 100644
--- a/drivers/mfd/tps65086.c
+++ b/drivers/mfd/tps65086.c
@@ -34,8 +34,9 @@ static const struct regmap_access_table tps65086_volatile_table = {
static const struct regmap_config tps65086_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_table = &tps65086_volatile_table,
+ .max_register = TPS65086_OC_STATUS,
};
static const struct regmap_irq tps65086_irqs[] = {
@@ -44,7 +45,7 @@ static const struct regmap_irq tps65086_irqs[] = {
REGMAP_IRQ_REG(TPS65086_IRQ_FAULT, 0, TPS65086_IRQ_FAULT_MASK),
};
-static struct regmap_irq_chip tps65086_irq_chip = {
+static const struct regmap_irq_chip tps65086_irq_chip = {
.name = "tps65086",
.status_base = TPS65086_IRQ,
.mask_base = TPS65086_IRQ_MASK,
@@ -81,16 +82,23 @@ static int tps65086_probe(struct i2c_client *client)
return PTR_ERR(tps->regmap);
}
- ret = regmap_read(tps->regmap, TPS65086_DEVICEID, &version);
+ /* Store device ID to load regulator configuration that fit to IC variant */
+ ret = regmap_read(tps->regmap, TPS65086_DEVICEID1, &tps->chip_id);
if (ret) {
- dev_err(tps->dev, "Failed to read revision register\n");
+ dev_err(tps->dev, "Failed to read revision register 1\n");
+ return ret;
+ }
+
+ ret = regmap_read(tps->regmap, TPS65086_DEVICEID2, &version);
+ if (ret) {
+ dev_err(tps->dev, "Failed to read revision register 2\n");
return ret;
}
dev_info(tps->dev, "Device: TPS65086%01lX, OTP: %c, Rev: %ld\n",
- (version & TPS65086_DEVICEID_PART_MASK),
- (char)((version & TPS65086_DEVICEID_OTP_MASK) >> 4) + 'A',
- (version & TPS65086_DEVICEID_REV_MASK) >> 6);
+ (version & TPS65086_DEVICEID2_PART_MASK),
+ (char)((version & TPS65086_DEVICEID2_OTP_MASK) >> 4) + 'A',
+ (version & TPS65086_DEVICEID2_REV_MASK) >> 6);
if (tps->irq > 0) {
ret = regmap_add_irq_chip(tps->regmap, tps->irq, IRQF_ONESHOT, 0,
@@ -119,7 +127,7 @@ static void tps65086_remove(struct i2c_client *client)
}
static const struct i2c_device_id tps65086_id_table[] = {
- { "tps65086", 0 },
+ { "tps65086" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, tps65086_id_table);
diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c
index a35ad70755fb..24f42175a9b4 100644
--- a/drivers/mfd/tps65090.c
+++ b/drivers/mfd/tps65090.c
@@ -17,7 +17,6 @@
#include <linux/mfd/core.h>
#include <linux/mfd/tps65090.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/err.h>
#define NUM_INT_REG 2
@@ -121,7 +120,7 @@ static const struct regmap_irq tps65090_irqs[] = {
},
};
-static struct regmap_irq_chip tps65090_irq_chip = {
+static const struct regmap_irq_chip tps65090_irq_chip = {
.name = "tps65090",
.irqs = tps65090_irqs,
.num_irqs = ARRAY_SIZE(tps65090_irqs),
@@ -152,7 +151,7 @@ static const struct regmap_config tps65090_regmap_config = {
.val_bits = 8,
.max_register = TPS65090_MAX_REG,
.num_reg_defaults_raw = TPS65090_NUM_REGS,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = is_volatile_reg,
};
@@ -226,8 +225,8 @@ err_irq_exit:
static const struct i2c_device_id tps65090_id_table[] = {
- { "tps65090", 0 },
- { },
+ { "tps65090" },
+ { }
};
static struct i2c_driver tps65090_driver = {
diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c
index 60599291b315..c240fac0ede7 100644
--- a/drivers/mfd/tps65217.c
+++ b/drivers/mfd/tps65217.c
@@ -17,7 +17,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -159,8 +158,8 @@ static int tps65217_irq_init(struct tps65217 *tps, int irq)
tps65217_set_bits(tps, TPS65217_REG_INT, TPS65217_INT_MASK,
TPS65217_INT_MASK, TPS65217_PROTECT_NONE);
- tps->irq_domain = irq_domain_add_linear(tps->dev->of_node,
- TPS65217_NUM_IRQ, &tps65217_irq_domain_ops, tps);
+ tps->irq_domain = irq_domain_create_linear(dev_fwnode(tps->dev), TPS65217_NUM_IRQ,
+ &tps65217_irq_domain_ops, tps);
if (!tps->irq_domain) {
dev_err(tps->dev, "Could not create IRQ domain\n");
return -ENOMEM;
diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c
index 619bf7adb20c..4f3e632f726f 100644
--- a/drivers/mfd/tps65218.c
+++ b/drivers/mfd/tps65218.c
@@ -15,7 +15,6 @@
#include <linux/regmap.h>
#include <linux/err.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
@@ -128,7 +127,7 @@ static const struct regmap_access_table tps65218_volatile_table = {
static const struct regmap_config tps65218_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_table = &tps65218_volatile_table,
};
@@ -187,7 +186,7 @@ static const struct regmap_irq tps65218_irqs[] = {
},
};
-static struct regmap_irq_chip tps65218_irq_chip = {
+static const struct regmap_irq_chip tps65218_irq_chip = {
.name = "tps65218",
.irqs = tps65218_irqs,
.num_irqs = ARRAY_SIZE(tps65218_irqs),
diff --git a/drivers/mfd/tps65219.c b/drivers/mfd/tps65219.c
index 0e0c42e4fdfc..65a952555218 100644
--- a/drivers/mfd/tps65219.c
+++ b/drivers/mfd/tps65219.c
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
//
-// Driver for TPS65219 Integrated Power Management Integrated Chips (PMIC)
+// Driver for TPS65214/TPS65215/TPS65219 Power Management Integrated Chips
//
// Copyright (C) 2022 BayLibre Incorporated - https://www.baylibre.com/
+// Copyright (C) 2024 Texas Instruments Incorporated - https://www.ti.com/
#include <linux/i2c.h>
#include <linux/reboot.h>
@@ -59,6 +60,84 @@ static const struct resource tps65219_pwrbutton_resources[] = {
DEFINE_RES_IRQ_NAMED(TPS65219_INT_PB_RISING_EDGE_DETECT, "rising"),
};
+static const struct resource tps65214_regulator_resources[] = {
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_SCG, "LDO1_SCG"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_OC, "LDO1_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_UV, "LDO1_UV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO2_SCG, "LDO2_SCG"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO2_OC, "LDO2_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO2_UV, "LDO2_UV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_SCG, "BUCK3_SCG"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_OC, "BUCK3_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_NEG_OC, "BUCK3_NEG_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_UV, "BUCK3_UV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_SCG, "BUCK1_SCG"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_OC, "BUCK1_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_NEG_OC, "BUCK1_NEG_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_UV, "BUCK1_UV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_SCG, "BUCK2_SCG"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_OC, "BUCK2_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_NEG_OC, "BUCK2_NEG_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_UV, "BUCK2_UV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_RV, "BUCK1_RV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_RV, "BUCK2_RV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_RV, "BUCK3_RV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_RV, "LDO1_RV"),
+ DEFINE_RES_IRQ_NAMED(TPS65214_INT_LDO2_RV, "LDO2_RV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_RV_SD, "BUCK1_RV_SD"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_RV_SD, "BUCK2_RV_SD"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_RV_SD, "BUCK3_RV_SD"),
+ DEFINE_RES_IRQ_NAMED(TPS65214_INT_LDO1_RV_SD, "LDO1_RV_SD"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO2_RV_SD, "LDO2_RV_SD"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_TIMEOUT, "TIMEOUT"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_2_WARM, "SENSOR_2_WARM"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_1_WARM, "SENSOR_1_WARM"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_0_WARM, "SENSOR_0_WARM"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_2_HOT, "SENSOR_2_HOT"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_1_HOT, "SENSOR_1_HOT"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_0_HOT, "SENSOR_0_HOT"),
+};
+
+static const struct resource tps65215_regulator_resources[] = {
+ DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO1_SCG, "LDO1_SCG"),
+ DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO1_OC, "LDO1_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO1_UV, "LDO1_UV"),
+ DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO2_SCG, "LDO2_SCG"),
+ DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO2_OC, "LDO2_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO2_UV, "LDO2_UV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_SCG, "BUCK3_SCG"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_OC, "BUCK3_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_NEG_OC, "BUCK3_NEG_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_UV, "BUCK3_UV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_SCG, "BUCK1_SCG"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_OC, "BUCK1_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_NEG_OC, "BUCK1_NEG_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_UV, "BUCK1_UV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_SCG, "BUCK2_SCG"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_OC, "BUCK2_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_NEG_OC, "BUCK2_NEG_OC"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_UV, "BUCK2_UV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_RV, "BUCK1_RV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_RV, "BUCK2_RV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_RV, "BUCK3_RV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_RV, "LDO1_RV"),
+ DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO2_RV, "LDO2_RV"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_RV_SD, "BUCK1_RV_SD"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_RV_SD, "BUCK2_RV_SD"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_RV_SD, "BUCK3_RV_SD"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_RV_SD, "LDO1_RV_SD"),
+ DEFINE_RES_IRQ_NAMED(TPS65215_INT_LDO2_RV_SD, "LDO2_RV_SD"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_TIMEOUT, "TIMEOUT"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_3_WARM, "SENSOR_3_WARM"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_2_WARM, "SENSOR_2_WARM"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_1_WARM, "SENSOR_1_WARM"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_0_WARM, "SENSOR_0_WARM"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_3_HOT, "SENSOR_3_HOT"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_2_HOT, "SENSOR_2_HOT"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_1_HOT, "SENSOR_1_HOT"),
+ DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_0_HOT, "SENSOR_0_HOT"),
+};
+
static const struct resource tps65219_regulator_resources[] = {
DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO3_SCG, "LDO3_SCG"),
DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO3_OC, "LDO3_OC"),
@@ -109,21 +188,24 @@ static const struct resource tps65219_regulator_resources[] = {
DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_0_HOT, "SENSOR_0_HOT"),
};
-static const struct mfd_cell tps65219_cells[] = {
- {
- .name = "tps65219-regulator",
- .resources = tps65219_regulator_resources,
- .num_resources = ARRAY_SIZE(tps65219_regulator_resources),
- },
- { .name = "tps65219-gpio", },
+static const struct mfd_cell tps65214_cells[] = {
+ MFD_CELL_RES("tps65214-regulator", tps65214_regulator_resources),
+ MFD_CELL_NAME("tps65214-gpio"),
};
-static const struct mfd_cell tps65219_pwrbutton_cell = {
- .name = "tps65219-pwrbutton",
- .resources = tps65219_pwrbutton_resources,
- .num_resources = ARRAY_SIZE(tps65219_pwrbutton_resources),
+static const struct mfd_cell tps65215_cells[] = {
+ MFD_CELL_RES("tps65215-regulator", tps65215_regulator_resources),
+ MFD_CELL_NAME("tps65215-gpio"),
};
+static const struct mfd_cell tps65219_cells[] = {
+ MFD_CELL_RES("tps65219-regulator", tps65219_regulator_resources),
+ MFD_CELL_NAME("tps65219-gpio"),
+};
+
+static const struct mfd_cell tps65219_pwrbutton_cell =
+ MFD_CELL_RES("tps65219-pwrbutton", tps65219_pwrbutton_resources);
+
static const struct regmap_config tps65219_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -143,9 +225,20 @@ static unsigned int bit3_offsets[] = { TPS65219_REG_INT_BUCK_1_2_POS }; /* Buck
static unsigned int bit4_offsets[] = { TPS65219_REG_INT_BUCK_3_POS }; /* Buck 3 */
static unsigned int bit5_offsets[] = { TPS65219_REG_INT_LDO_1_2_POS }; /* LDO 1-2 */
static unsigned int bit6_offsets[] = { TPS65219_REG_INT_LDO_3_4_POS }; /* LDO 3-4 */
+static unsigned int tps65215_bit5_offsets[] = { TPS65215_REG_INT_LDO_1_POS };
+static unsigned int tps65215_bit6_offsets[] = { TPS65215_REG_INT_LDO_2_POS };
static unsigned int bit7_offsets[] = { TPS65219_REG_INT_PB_POS }; /* Power Button */
-static struct regmap_irq_sub_irq_map tps65219_sub_irq_offsets[] = {
+/* TPS65214 INT_SOURCE bit 6 is 'RESERVED'*/
+static unsigned int tps65214_bit0_offsets[] = { TPS65214_REG_INT_TO_RV_POS };
+static unsigned int tps65214_bit1_offsets[] = { TPS65214_REG_INT_RV_POS };
+static unsigned int tps65214_bit2_offsets[] = { TPS65214_REG_INT_SYS_POS };
+static unsigned int tps65214_bit3_offsets[] = { TPS65214_REG_INT_BUCK_1_2_POS };
+static unsigned int tps65214_bit4_offsets[] = { TPS65214_REG_INT_BUCK_3_POS };
+static unsigned int tps65214_bit5_offsets[] = { TPS65214_REG_INT_LDO_1_2_POS };
+static unsigned int tps65214_bit7_offsets[] = { TPS65214_REG_INT_PB_POS };
+
+static const struct regmap_irq_sub_irq_map tps65219_sub_irq_offsets[] = {
REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets),
@@ -156,10 +249,113 @@ static struct regmap_irq_sub_irq_map tps65219_sub_irq_offsets[] = {
REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets),
};
+static const struct regmap_irq_sub_irq_map tps65215_sub_irq_offsets[] = {
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit3_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit4_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(tps65215_bit5_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(tps65215_bit6_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets),
+};
+
+static const struct regmap_irq_sub_irq_map tps65214_sub_irq_offsets[] = {
+ REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit0_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit1_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit2_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit3_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit4_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit5_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(tps65214_bit7_offsets),
+};
+
#define TPS65219_REGMAP_IRQ_REG(int_name, register_position) \
REGMAP_IRQ_REG(int_name, register_position, int_name##_MASK)
-static struct regmap_irq tps65219_irqs[] = {
+static const struct regmap_irq tps65214_irqs[] = {
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_SCG, TPS65214_REG_INT_LDO_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_OC, TPS65214_REG_INT_LDO_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_UV, TPS65214_REG_INT_LDO_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_SCG, TPS65214_REG_INT_LDO_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_OC, TPS65214_REG_INT_LDO_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_UV, TPS65214_REG_INT_LDO_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_SCG, TPS65214_REG_INT_BUCK_3_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_OC, TPS65214_REG_INT_BUCK_3_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_NEG_OC, TPS65214_REG_INT_BUCK_3_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_UV, TPS65214_REG_INT_BUCK_3_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_SCG, TPS65214_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_OC, TPS65214_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_NEG_OC, TPS65214_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_UV, TPS65214_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_SCG, TPS65214_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_OC, TPS65214_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_NEG_OC, TPS65214_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_UV, TPS65214_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_2_WARM, TPS65214_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_1_WARM, TPS65214_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_0_WARM, TPS65214_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_2_HOT, TPS65214_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_1_HOT, TPS65214_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_0_HOT, TPS65214_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_RV, TPS65214_REG_INT_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_RV, TPS65214_REG_INT_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_RV, TPS65214_REG_INT_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_RV, TPS65214_REG_INT_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65214_INT_LDO2_RV, TPS65214_REG_INT_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_RV_SD, TPS65214_REG_INT_TO_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_RV_SD, TPS65214_REG_INT_TO_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_RV_SD, TPS65214_REG_INT_TO_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65214_INT_LDO1_RV_SD, TPS65214_REG_INT_TO_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_RV_SD, TPS65214_REG_INT_TO_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_TIMEOUT, TPS65214_REG_INT_TO_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_PB_FALLING_EDGE_DETECT, TPS65214_REG_INT_PB_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_PB_RISING_EDGE_DETECT, TPS65214_REG_INT_PB_POS),
+};
+
+static const struct regmap_irq tps65215_irqs[] = {
+ TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO1_SCG, TPS65215_REG_INT_LDO_1_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO1_OC, TPS65215_REG_INT_LDO_1_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO1_UV, TPS65215_REG_INT_LDO_1_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO2_SCG, TPS65215_REG_INT_LDO_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO2_OC, TPS65215_REG_INT_LDO_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO2_UV, TPS65215_REG_INT_LDO_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_SCG, TPS65219_REG_INT_BUCK_3_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_OC, TPS65219_REG_INT_BUCK_3_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_NEG_OC, TPS65219_REG_INT_BUCK_3_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_UV, TPS65219_REG_INT_BUCK_3_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_SCG, TPS65219_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_OC, TPS65219_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_NEG_OC, TPS65219_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_UV, TPS65219_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_SCG, TPS65219_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_OC, TPS65219_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_NEG_OC, TPS65219_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_UV, TPS65219_REG_INT_BUCK_1_2_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_3_WARM, TPS65219_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_2_WARM, TPS65219_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_1_WARM, TPS65219_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_0_WARM, TPS65219_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_3_HOT, TPS65219_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_2_HOT, TPS65219_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_1_HOT, TPS65219_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_0_HOT, TPS65219_REG_INT_SYS_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_RV, TPS65219_REG_INT_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_RV, TPS65219_REG_INT_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_RV, TPS65219_REG_INT_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_RV, TPS65219_REG_INT_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO2_RV, TPS65219_REG_INT_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_RV_SD, TPS65219_REG_INT_TO_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_RV_SD, TPS65219_REG_INT_TO_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_RV_SD, TPS65219_REG_INT_TO_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_RV_SD, TPS65219_REG_INT_TO_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65215_INT_LDO2_RV_SD, TPS65219_REG_INT_TO_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_TIMEOUT, TPS65219_REG_INT_TO_RV_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_PB_FALLING_EDGE_DETECT, TPS65219_REG_INT_PB_POS),
+ TPS65219_REGMAP_IRQ_REG(TPS65219_INT_PB_RISING_EDGE_DETECT, TPS65219_REG_INT_PB_POS),
+};
+
+static const struct regmap_irq tps65219_irqs[] = {
TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO3_SCG, TPS65219_REG_INT_LDO_3_4_POS),
TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO3_OC, TPS65219_REG_INT_LDO_3_4_POS),
TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO3_UV, TPS65219_REG_INT_LDO_3_4_POS),
@@ -211,7 +407,35 @@ static struct regmap_irq tps65219_irqs[] = {
TPS65219_REGMAP_IRQ_REG(TPS65219_INT_PB_RISING_EDGE_DETECT, TPS65219_REG_INT_PB_POS),
};
-static struct regmap_irq_chip tps65219_irq_chip = {
+static const struct regmap_irq_chip tps65214_irq_chip = {
+ .name = "tps65214_irq",
+ .main_status = TPS65219_REG_INT_SOURCE,
+ .num_main_regs = 1,
+ .num_main_status_bits = 8,
+ .irqs = tps65214_irqs,
+ .num_irqs = ARRAY_SIZE(tps65214_irqs),
+ .status_base = TPS65214_REG_INT_LDO_1_2,
+ .ack_base = TPS65214_REG_INT_LDO_1_2,
+ .clear_ack = 1,
+ .num_regs = 8,
+ .sub_reg_offsets = tps65214_sub_irq_offsets,
+};
+
+static const struct regmap_irq_chip tps65215_irq_chip = {
+ .name = "tps65215_irq",
+ .main_status = TPS65219_REG_INT_SOURCE,
+ .num_main_regs = 1,
+ .num_main_status_bits = 8,
+ .irqs = tps65215_irqs,
+ .num_irqs = ARRAY_SIZE(tps65215_irqs),
+ .status_base = TPS65215_REG_INT_LDO_2,
+ .ack_base = TPS65215_REG_INT_LDO_2,
+ .clear_ack = 1,
+ .num_regs = 8,
+ .sub_reg_offsets = tps65215_sub_irq_offsets,
+};
+
+static const struct regmap_irq_chip tps65219_irq_chip = {
.name = "tps65219_irq",
.main_status = TPS65219_REG_INT_SOURCE,
.num_main_regs = 1,
@@ -225,10 +449,35 @@ static struct regmap_irq_chip tps65219_irq_chip = {
.sub_reg_offsets = tps65219_sub_irq_offsets,
};
+struct tps65219_chip_data {
+ const struct regmap_irq_chip *irq_chip;
+ const struct mfd_cell *cells;
+ int n_cells;
+};
+
+static const struct tps65219_chip_data chip_info_table[] = {
+ [TPS65214] = {
+ .irq_chip = &tps65214_irq_chip,
+ .cells = tps65214_cells,
+ .n_cells = ARRAY_SIZE(tps65214_cells),
+ },
+ [TPS65215] = {
+ .irq_chip = &tps65215_irq_chip,
+ .cells = tps65215_cells,
+ .n_cells = ARRAY_SIZE(tps65215_cells),
+ },
+ [TPS65219] = {
+ .irq_chip = &tps65219_irq_chip,
+ .cells = tps65219_cells,
+ .n_cells = ARRAY_SIZE(tps65219_cells),
+ },
+};
+
static int tps65219_probe(struct i2c_client *client)
{
struct tps65219 *tps;
- unsigned int chipid;
+ const struct tps65219_chip_data *pmic;
+ unsigned int chip_id;
bool pwr_button;
int ret;
@@ -239,6 +488,8 @@ static int tps65219_probe(struct i2c_client *client)
i2c_set_clientdata(client, tps);
tps->dev = &client->dev;
+ chip_id = (uintptr_t)i2c_get_match_data(client);
+ pmic = &chip_info_table[chip_id];
tps->regmap = devm_regmap_init_i2c(client, &tps65219_regmap_config);
if (IS_ERR(tps->regmap)) {
@@ -247,20 +498,14 @@ static int tps65219_probe(struct i2c_client *client)
return ret;
}
- ret = devm_regmap_add_irq_chip(&client->dev, tps->regmap, client->irq,
- IRQF_ONESHOT, 0, &tps65219_irq_chip,
+ ret = devm_regmap_add_irq_chip(tps->dev, tps->regmap, client->irq,
+ IRQF_ONESHOT, 0, pmic->irq_chip,
&tps->irq_data);
if (ret)
return ret;
- ret = regmap_read(tps->regmap, TPS65219_REG_TI_DEV_ID, &chipid);
- if (ret) {
- dev_err(tps->dev, "Failed to read device ID: %d\n", ret);
- return ret;
- }
-
ret = devm_mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO,
- tps65219_cells, ARRAY_SIZE(tps65219_cells),
+ pmic->cells, pmic->n_cells,
NULL, 0, regmap_irq_get_domain(tps->irq_data));
if (ret) {
dev_err(tps->dev, "Failed to add child devices: %d\n", ret);
@@ -298,7 +543,9 @@ static int tps65219_probe(struct i2c_client *client)
}
static const struct of_device_id of_tps65219_match_table[] = {
- { .compatible = "ti,tps65219", },
+ { .compatible = "ti,tps65214", .data = (void *)TPS65214, },
+ { .compatible = "ti,tps65215", .data = (void *)TPS65215, },
+ { .compatible = "ti,tps65219", .data = (void *)TPS65219, },
{}
};
MODULE_DEVICE_TABLE(of, of_tps65219_match_table);
@@ -313,5 +560,5 @@ static struct i2c_driver tps65219_driver = {
module_i2c_driver(tps65219_driver);
MODULE_AUTHOR("Jerome Neanne <jneanne@baylibre.com>");
-MODULE_DESCRIPTION("TPS65219 power management IC driver");
+MODULE_DESCRIPTION("TPS65214/TPS65215/TPS65219 PMIC driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index 55675ceedcd3..8d5fe2b60bfa 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -22,6 +22,7 @@
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/of.h>
@@ -29,6 +30,7 @@
#include <linux/mfd/tps6586x.h>
#define TPS6586X_SUPPLYENE 0x14
+#define SOFT_RST_BIT BIT(0)
#define EXITSLREQ_BIT BIT(1)
#define SLEEP_MODE_BIT BIT(3)
@@ -361,9 +363,9 @@ static int tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
new_irq_base = 0;
}
- tps6586x->irq_domain = irq_domain_add_simple(tps6586x->dev->of_node,
- irq_num, new_irq_base, &tps6586x_domain_ops,
- tps6586x);
+ tps6586x->irq_domain = irq_domain_create_simple(dev_fwnode(tps6586x->dev), irq_num,
+ new_irq_base, &tps6586x_domain_ops,
+ tps6586x);
if (!tps6586x->irq_domain) {
dev_err(tps6586x->dev, "Failed to create IRQ domain\n");
return -ENOMEM;
@@ -454,16 +456,37 @@ static const struct regmap_config tps6586x_regmap_config = {
.val_bits = 8,
.max_register = TPS6586X_MAX_REGISTER,
.volatile_reg = is_volatile_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-static struct device *tps6586x_dev;
-static void tps6586x_power_off(void)
+static int tps6586x_power_off_handler(struct sys_off_data *data)
{
- if (tps6586x_clr_bits(tps6586x_dev, TPS6586X_SUPPLYENE, EXITSLREQ_BIT))
- return;
+ int ret;
+
+ /* Put the PMIC into sleep state. This takes at least 20ms. */
+ ret = tps6586x_clr_bits(data->dev, TPS6586X_SUPPLYENE, EXITSLREQ_BIT);
+ if (ret)
+ return notifier_from_errno(ret);
- tps6586x_set_bits(tps6586x_dev, TPS6586X_SUPPLYENE, SLEEP_MODE_BIT);
+ ret = tps6586x_set_bits(data->dev, TPS6586X_SUPPLYENE, SLEEP_MODE_BIT);
+ if (ret)
+ return notifier_from_errno(ret);
+
+ mdelay(50);
+ return notifier_from_errno(-ETIME);
+}
+
+static int tps6586x_restart_handler(struct sys_off_data *data)
+{
+ int ret;
+
+ /* Put the PMIC into hard reboot state. This takes at least 20ms. */
+ ret = tps6586x_set_bits(data->dev, TPS6586X_SUPPLYENE, SOFT_RST_BIT);
+ if (ret)
+ return notifier_from_errno(ret);
+
+ mdelay(50);
+ return notifier_from_errno(-ETIME);
}
static void tps6586x_print_version(struct i2c_client *client, int version)
@@ -559,9 +582,20 @@ static int tps6586x_i2c_probe(struct i2c_client *client)
goto err_add_devs;
}
- if (pdata->pm_off && !pm_power_off) {
- tps6586x_dev = &client->dev;
- pm_power_off = tps6586x_power_off;
+ if (pdata->pm_off) {
+ ret = devm_register_power_off_handler(&client->dev, &tps6586x_power_off_handler,
+ NULL);
+ if (ret) {
+ dev_err(&client->dev, "register power off handler failed: %d\n", ret);
+ goto err_add_devs;
+ }
+
+ ret = devm_register_restart_handler(&client->dev, &tps6586x_restart_handler,
+ NULL);
+ if (ret) {
+ dev_err(&client->dev, "register restart handler failed: %d\n", ret);
+ goto err_add_devs;
+ }
}
return 0;
@@ -608,8 +642,8 @@ static SIMPLE_DEV_PM_OPS(tps6586x_pm_ops, tps6586x_i2c_suspend,
tps6586x_i2c_resume);
static const struct i2c_device_id tps6586x_id_table[] = {
- { "tps6586x", 0 },
- { },
+ { "tps6586x" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tps6586x_id_table);
diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c
index 41408df1712f..6a7b7a697fb7 100644
--- a/drivers/mfd/tps65910.c
+++ b/drivers/mfd/tps65910.c
@@ -19,7 +19,7 @@
#include <linux/regmap.h>
#include <linux/mfd/tps65910.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/property.h>
static const struct resource rtc_resources[] = {
{
@@ -197,7 +197,7 @@ static const struct regmap_irq tps65910_irqs[] = {
},
};
-static struct regmap_irq_chip tps65911_irq_chip = {
+static const struct regmap_irq_chip tps65911_irq_chip = {
.name = "tps65910",
.irqs = tps65911_irqs,
.num_irqs = ARRAY_SIZE(tps65911_irqs),
@@ -208,7 +208,7 @@ static struct regmap_irq_chip tps65911_irq_chip = {
.ack_base = TPS65910_INT_STS,
};
-static struct regmap_irq_chip tps65910_irq_chip = {
+static const struct regmap_irq_chip tps65910_irq_chip = {
.name = "tps65910",
.irqs = tps65910_irqs,
.num_irqs = ARRAY_SIZE(tps65910_irqs),
@@ -223,7 +223,7 @@ static int tps65910_irq_init(struct tps65910 *tps65910, int irq,
struct tps65910_platform_data *pdata)
{
int ret;
- static struct regmap_irq_chip *tps6591x_irqs_chip;
+ static const struct regmap_irq_chip *tps6591x_irqs_chip;
if (!irq) {
dev_warn(tps65910->dev, "No interrupt support, no core IRQ\n");
@@ -281,7 +281,7 @@ static const struct regmap_config tps65910_regmap_config = {
.val_bits = 8,
.volatile_reg = is_volatile_reg,
.max_register = TPS65910_MAX_REGISTER - 1,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int tps65910_ck32k_init(struct tps65910 *tps65910,
@@ -374,16 +374,9 @@ static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
struct device_node *np = client->dev.of_node;
struct tps65910_board *board_info;
unsigned int prop;
- const struct of_device_id *match;
int ret;
- match = of_match_device(tps65910_of_match, &client->dev);
- if (!match) {
- dev_err(&client->dev, "Failed to find matching dt id\n");
- return NULL;
- }
-
- *chip_id = (unsigned long)match->data;
+ *chip_id = (unsigned long)device_get_match_data(&client->dev);
board_info = devm_kzalloc(&client->dev, sizeof(*board_info),
GFP_KERNEL);
diff --git a/drivers/mfd/tps65911-comparator.c b/drivers/mfd/tps65911-comparator.c
index 8f4210075913..7098712ea008 100644
--- a/drivers/mfd/tps65911-comparator.c
+++ b/drivers/mfd/tps65911-comparator.c
@@ -140,15 +140,13 @@ static int tps65911_comparator_probe(struct platform_device *pdev)
return ret;
}
-static int tps65911_comparator_remove(struct platform_device *pdev)
+static void tps65911_comparator_remove(struct platform_device *pdev)
{
struct tps65910 *tps65910;
tps65910 = dev_get_drvdata(pdev->dev.parent);
device_remove_file(&pdev->dev, &dev_attr_comp2_threshold);
device_remove_file(&pdev->dev, &dev_attr_comp1_threshold);
-
- return 0;
}
static struct platform_driver tps65911_comparator_driver = {
diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c
index 7d994b8a5965..a9dcd7f0d9e3 100644
--- a/drivers/mfd/tps65912-core.c
+++ b/drivers/mfd/tps65912-core.c
@@ -57,7 +57,7 @@ static const struct regmap_irq tps65912_irqs[] = {
REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_LDO10, 3, TPS65912_INT_STS4_PGOOD_LDO10),
};
-static struct regmap_irq_chip tps65912_irq_chip = {
+static const struct regmap_irq_chip tps65912_irq_chip = {
.name = "tps65912",
.irqs = tps65912_irqs,
.num_irqs = ARRAY_SIZE(tps65912_irqs),
@@ -81,7 +81,7 @@ static const struct regmap_access_table tps65912_volatile_table = {
const struct regmap_config tps65912_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_table = &tps65912_volatile_table,
};
EXPORT_SYMBOL_GPL(tps65912_regmap_config);
@@ -90,29 +90,22 @@ int tps65912_device_init(struct tps65912 *tps)
{
int ret;
- ret = regmap_add_irq_chip(tps->regmap, tps->irq, IRQF_ONESHOT, 0,
- &tps65912_irq_chip, &tps->irq_data);
+ ret = devm_regmap_add_irq_chip(tps->dev, tps->regmap, tps->irq,
+ IRQF_ONESHOT, 0, &tps65912_irq_chip,
+ &tps->irq_data);
if (ret)
return ret;
- ret = mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO, tps65912_cells,
- ARRAY_SIZE(tps65912_cells), NULL, 0,
- regmap_irq_get_domain(tps->irq_data));
- if (ret) {
- regmap_del_irq_chip(tps->irq, tps->irq_data);
+ ret = devm_mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO, tps65912_cells,
+ ARRAY_SIZE(tps65912_cells), NULL, 0,
+ regmap_irq_get_domain(tps->irq_data));
+ if (ret)
return ret;
- }
return 0;
}
EXPORT_SYMBOL_GPL(tps65912_device_init);
-void tps65912_device_exit(struct tps65912 *tps)
-{
- regmap_del_irq_chip(tps->irq, tps->irq_data);
-}
-EXPORT_SYMBOL_GPL(tps65912_device_exit);
-
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
MODULE_DESCRIPTION("TPS65912x MFD Driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/tps65912-i2c.c b/drivers/mfd/tps65912-i2c.c
index 3c5ac781b6c1..138e50497b51 100644
--- a/drivers/mfd/tps65912-i2c.c
+++ b/drivers/mfd/tps65912-i2c.c
@@ -42,15 +42,8 @@ static int tps65912_i2c_probe(struct i2c_client *client)
return tps65912_device_init(tps);
}
-static void tps65912_i2c_remove(struct i2c_client *client)
-{
- struct tps65912 *tps = i2c_get_clientdata(client);
-
- tps65912_device_exit(tps);
-}
-
static const struct i2c_device_id tps65912_i2c_id_table[] = {
- { "tps65912", 0 },
+ { "tps65912" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, tps65912_i2c_id_table);
@@ -61,7 +54,6 @@ static struct i2c_driver tps65912_i2c_driver = {
.of_match_table = tps65912_i2c_of_match_table,
},
.probe = tps65912_i2c_probe,
- .remove = tps65912_i2c_remove,
.id_table = tps65912_i2c_id_table,
};
module_i2c_driver(tps65912_i2c_driver);
diff --git a/drivers/mfd/tps65912-spi.c b/drivers/mfd/tps65912-spi.c
index 9e976f9c6bbe..2a77dccd6059 100644
--- a/drivers/mfd/tps65912-spi.c
+++ b/drivers/mfd/tps65912-spi.c
@@ -42,13 +42,6 @@ static int tps65912_spi_probe(struct spi_device *spi)
return tps65912_device_init(tps);
}
-static void tps65912_spi_remove(struct spi_device *spi)
-{
- struct tps65912 *tps = spi_get_drvdata(spi);
-
- tps65912_device_exit(tps);
-}
-
static const struct spi_device_id tps65912_spi_id_table[] = {
{ "tps65912", 0 },
{ /* sentinel */ }
@@ -61,7 +54,6 @@ static struct spi_driver tps65912_spi_driver = {
.of_match_table = tps65912_spi_of_match_table,
},
.probe = tps65912_spi_probe,
- .remove = tps65912_spi_remove,
.id_table = tps65912_spi_id_table,
};
module_spi_driver(tps65912_spi_driver);
diff --git a/drivers/mfd/tps6594-core.c b/drivers/mfd/tps6594-core.c
index 15f314833207..8b26c4127472 100644
--- a/drivers/mfd/tps6594-core.c
+++ b/drivers/mfd/tps6594-core.c
@@ -1,20 +1,29 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Core functions for TI TPS6594/TPS6593/LP8764 PMICs
+ * Core functions for following TI PMICs:
+ * - LP8764
+ * - TPS65224
+ * - TPS652G1
+ * - TPS6593
+ * - TPS6594
*
* Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
*/
+#include <linux/bitfield.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/reboot.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps6594.h>
#define TPS6594_CRC_SYNC_TIMEOUT_MS 150
+#define TPS65224_EN_SEL_PB 1
+#define TPS65224_GPIO3_SEL_PB 3
/* Completion to synchronize CRC feature enabling on all PMICs */
static DECLARE_COMPLETION(tps6594_crc_comp);
@@ -123,6 +132,12 @@ static const struct resource tps6594_rtc_resources[] = {
DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_POWER_UP, TPS6594_IRQ_NAME_POWERUP),
};
+static const struct resource tps6594_pwrbutton_resources[] = {
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_FALL, TPS65224_IRQ_NAME_PB_FALL),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_RISE, TPS65224_IRQ_NAME_PB_RISE),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_SHORT, TPS65224_IRQ_NAME_PB_SHORT),
+};
+
static const struct mfd_cell tps6594_common_cells[] = {
MFD_CELL_RES("tps6594-regulator", tps6594_regulator_resources),
MFD_CELL_RES("tps6594-pinctrl", tps6594_pinctrl_resources),
@@ -278,16 +293,218 @@ static const unsigned int tps6594_irq_reg[] = {
TPS6594_REG_RTC_STATUS,
};
+/* TPS65224 Resources */
+
+static const struct resource tps65224_regulator_resources[] = {
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_BUCK1_UVOV, TPS65224_IRQ_NAME_BUCK1_UVOV),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_BUCK2_UVOV, TPS65224_IRQ_NAME_BUCK2_UVOV),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_BUCK3_UVOV, TPS65224_IRQ_NAME_BUCK3_UVOV),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_BUCK4_UVOV, TPS65224_IRQ_NAME_BUCK4_UVOV),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_LDO1_UVOV, TPS65224_IRQ_NAME_LDO1_UVOV),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_LDO2_UVOV, TPS65224_IRQ_NAME_LDO2_UVOV),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_LDO3_UVOV, TPS65224_IRQ_NAME_LDO3_UVOV),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_VCCA_UVOV, TPS65224_IRQ_NAME_VCCA_UVOV),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_VMON1_UVOV, TPS65224_IRQ_NAME_VMON1_UVOV),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_VMON2_UVOV, TPS65224_IRQ_NAME_VMON2_UVOV),
+};
+
+static const struct resource tps65224_pinctrl_resources[] = {
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_GPIO1, TPS65224_IRQ_NAME_GPIO1),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_GPIO2, TPS65224_IRQ_NAME_GPIO2),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_GPIO3, TPS65224_IRQ_NAME_GPIO3),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_GPIO4, TPS65224_IRQ_NAME_GPIO4),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_GPIO5, TPS65224_IRQ_NAME_GPIO5),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_GPIO6, TPS65224_IRQ_NAME_GPIO6),
+};
+
+static const struct resource tps65224_pfsm_resources[] = {
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_VSENSE, TPS65224_IRQ_NAME_VSENSE),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_ENABLE, TPS65224_IRQ_NAME_ENABLE),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_SHORT, TPS65224_IRQ_NAME_PB_SHORT),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_FSD, TPS65224_IRQ_NAME_FSD),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_SOFT_REBOOT, TPS65224_IRQ_NAME_SOFT_REBOOT),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_BIST_PASS, TPS65224_IRQ_NAME_BIST_PASS),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_EXT_CLK, TPS65224_IRQ_NAME_EXT_CLK),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_REG_UNLOCK, TPS65224_IRQ_NAME_REG_UNLOCK),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_TWARN, TPS65224_IRQ_NAME_TWARN),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_LONG, TPS65224_IRQ_NAME_PB_LONG),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_TSD_ORD, TPS65224_IRQ_NAME_TSD_ORD),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_BIST_FAIL, TPS65224_IRQ_NAME_BIST_FAIL),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_REG_CRC_ERR, TPS65224_IRQ_NAME_REG_CRC_ERR),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_RECOV_CNT, TPS65224_IRQ_NAME_RECOV_CNT),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_TSD_IMM, TPS65224_IRQ_NAME_TSD_IMM),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_VCCA_OVP, TPS65224_IRQ_NAME_VCCA_OVP),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PFSM_ERR, TPS65224_IRQ_NAME_PFSM_ERR),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_BG_XMON, TPS65224_IRQ_NAME_BG_XMON),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_IMM_SHUTDOWN, TPS65224_IRQ_NAME_IMM_SHUTDOWN),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_ORD_SHUTDOWN, TPS65224_IRQ_NAME_ORD_SHUTDOWN),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_MCU_PWR_ERR, TPS65224_IRQ_NAME_MCU_PWR_ERR),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_SOC_PWR_ERR, TPS65224_IRQ_NAME_SOC_PWR_ERR),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_COMM_ERR, TPS65224_IRQ_NAME_COMM_ERR),
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_I2C2_ERR, TPS65224_IRQ_NAME_I2C2_ERR),
+};
+
+static const struct resource tps65224_adc_resources[] = {
+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_ADC_CONV_READY, TPS65224_IRQ_NAME_ADC_CONV_READY),
+};
+
+static const struct mfd_cell tps65224_common_cells[] = {
+ MFD_CELL_RES("tps65224-adc", tps65224_adc_resources),
+ MFD_CELL_RES("tps6594-pfsm", tps65224_pfsm_resources),
+ MFD_CELL_RES("tps6594-pinctrl", tps65224_pinctrl_resources),
+ MFD_CELL_RES("tps6594-regulator", tps65224_regulator_resources),
+};
+
+static const struct mfd_cell tps6594_pwrbutton_cell = {
+ .name = "tps6594-pwrbutton",
+ .resources = tps6594_pwrbutton_resources,
+ .num_resources = ARRAY_SIZE(tps6594_pwrbutton_resources),
+};
+
+static const struct regmap_irq tps65224_irqs[] = {
+ /* INT_BUCK register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_BUCK1_UVOV, 0, TPS65224_BIT_BUCK1_UVOV_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_BUCK2_UVOV, 0, TPS65224_BIT_BUCK2_UVOV_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_BUCK3_UVOV, 0, TPS65224_BIT_BUCK3_UVOV_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_BUCK4_UVOV, 0, TPS65224_BIT_BUCK4_UVOV_INT),
+
+ /* INT_VMON_LDO register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_LDO1_UVOV, 1, TPS65224_BIT_LDO1_UVOV_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_LDO2_UVOV, 1, TPS65224_BIT_LDO2_UVOV_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_LDO3_UVOV, 1, TPS65224_BIT_LDO3_UVOV_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_VCCA_UVOV, 1, TPS65224_BIT_VCCA_UVOV_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_VMON1_UVOV, 1, TPS65224_BIT_VMON1_UVOV_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_VMON2_UVOV, 1, TPS65224_BIT_VMON2_UVOV_INT),
+
+ /* INT_GPIO register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_GPIO1, 2, TPS65224_BIT_GPIO1_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_GPIO2, 2, TPS65224_BIT_GPIO2_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_GPIO3, 2, TPS65224_BIT_GPIO3_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_GPIO4, 2, TPS65224_BIT_GPIO4_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_GPIO5, 2, TPS65224_BIT_GPIO5_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_GPIO6, 2, TPS65224_BIT_GPIO6_INT),
+
+ /* INT_STARTUP register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_VSENSE, 3, TPS65224_BIT_VSENSE_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_ENABLE, 3, TPS6594_BIT_ENABLE_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_PB_SHORT, 3, TPS65224_BIT_PB_SHORT_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_FSD, 3, TPS6594_BIT_FSD_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_SOFT_REBOOT, 3, TPS6594_BIT_SOFT_REBOOT_INT),
+
+ /* INT_MISC register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_BIST_PASS, 4, TPS6594_BIT_BIST_PASS_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_EXT_CLK, 4, TPS6594_BIT_EXT_CLK_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_REG_UNLOCK, 4, TPS65224_BIT_REG_UNLOCK_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_TWARN, 4, TPS6594_BIT_TWARN_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_PB_LONG, 4, TPS65224_BIT_PB_LONG_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_PB_FALL, 4, TPS65224_BIT_PB_FALL_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_PB_RISE, 4, TPS65224_BIT_PB_RISE_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_ADC_CONV_READY, 4, TPS65224_BIT_ADC_CONV_READY_INT),
+
+ /* INT_MODERATE_ERR register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_TSD_ORD, 5, TPS6594_BIT_TSD_ORD_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_BIST_FAIL, 5, TPS6594_BIT_BIST_FAIL_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_REG_CRC_ERR, 5, TPS6594_BIT_REG_CRC_ERR_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_RECOV_CNT, 5, TPS6594_BIT_RECOV_CNT_INT),
+
+ /* INT_SEVERE_ERR register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_TSD_IMM, 6, TPS6594_BIT_TSD_IMM_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_VCCA_OVP, 6, TPS6594_BIT_VCCA_OVP_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_PFSM_ERR, 6, TPS6594_BIT_PFSM_ERR_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_BG_XMON, 6, TPS65224_BIT_BG_XMON_INT),
+
+ /* INT_FSM_ERR register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_IMM_SHUTDOWN, 7, TPS6594_BIT_IMM_SHUTDOWN_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_ORD_SHUTDOWN, 7, TPS6594_BIT_ORD_SHUTDOWN_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_MCU_PWR_ERR, 7, TPS6594_BIT_MCU_PWR_ERR_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_SOC_PWR_ERR, 7, TPS6594_BIT_SOC_PWR_ERR_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_COMM_ERR, 7, TPS6594_BIT_COMM_ERR_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_I2C2_ERR, 7, TPS65224_BIT_I2C2_ERR_INT),
+};
+
+static const unsigned int tps65224_irq_reg[] = {
+ TPS6594_REG_INT_BUCK,
+ TPS6594_REG_INT_LDO_VMON,
+ TPS6594_REG_INT_GPIO,
+ TPS6594_REG_INT_STARTUP,
+ TPS6594_REG_INT_MISC,
+ TPS6594_REG_INT_MODERATE_ERR,
+ TPS6594_REG_INT_SEVERE_ERR,
+ TPS6594_REG_INT_FSM_ERR,
+};
+
+/* TPS652G1 Resources */
+
+static const struct mfd_cell tps652g1_common_cells[] = {
+ MFD_CELL_RES("tps6594-pfsm", tps65224_pfsm_resources),
+ MFD_CELL_RES("tps6594-pinctrl", tps65224_pinctrl_resources),
+ MFD_CELL_NAME("tps6594-regulator"),
+};
+
+static const struct regmap_irq tps652g1_irqs[] = {
+ /* INT_GPIO register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_GPIO1, 2, TPS65224_BIT_GPIO1_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_GPIO2, 2, TPS65224_BIT_GPIO2_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_GPIO3, 2, TPS65224_BIT_GPIO3_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_GPIO4, 2, TPS65224_BIT_GPIO4_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_GPIO5, 2, TPS65224_BIT_GPIO5_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_GPIO6, 2, TPS65224_BIT_GPIO6_INT),
+
+ /* INT_STARTUP register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_VSENSE, 3, TPS65224_BIT_VSENSE_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_ENABLE, 3, TPS6594_BIT_ENABLE_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_PB_SHORT, 3, TPS65224_BIT_PB_SHORT_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_FSD, 3, TPS6594_BIT_FSD_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_SOFT_REBOOT, 3, TPS6594_BIT_SOFT_REBOOT_INT),
+
+ /* INT_MISC register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_BIST_PASS, 4, TPS6594_BIT_BIST_PASS_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_EXT_CLK, 4, TPS6594_BIT_EXT_CLK_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_REG_UNLOCK, 4, TPS65224_BIT_REG_UNLOCK_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_TWARN, 4, TPS6594_BIT_TWARN_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_PB_LONG, 4, TPS65224_BIT_PB_LONG_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_PB_FALL, 4, TPS65224_BIT_PB_FALL_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_PB_RISE, 4, TPS65224_BIT_PB_RISE_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_ADC_CONV_READY, 4, TPS65224_BIT_ADC_CONV_READY_INT),
+
+ /* INT_MODERATE_ERR register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_TSD_ORD, 5, TPS6594_BIT_TSD_ORD_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_BIST_FAIL, 5, TPS6594_BIT_BIST_FAIL_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_REG_CRC_ERR, 5, TPS6594_BIT_REG_CRC_ERR_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_RECOV_CNT, 5, TPS6594_BIT_RECOV_CNT_INT),
+
+ /* INT_SEVERE_ERR register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_TSD_IMM, 6, TPS6594_BIT_TSD_IMM_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_VCCA_OVP, 6, TPS6594_BIT_VCCA_OVP_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_PFSM_ERR, 6, TPS6594_BIT_PFSM_ERR_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_BG_XMON, 6, TPS65224_BIT_BG_XMON_INT),
+
+ /* INT_FSM_ERR register */
+ REGMAP_IRQ_REG(TPS65224_IRQ_IMM_SHUTDOWN, 7, TPS6594_BIT_IMM_SHUTDOWN_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_ORD_SHUTDOWN, 7, TPS6594_BIT_ORD_SHUTDOWN_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_MCU_PWR_ERR, 7, TPS6594_BIT_MCU_PWR_ERR_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_SOC_PWR_ERR, 7, TPS6594_BIT_SOC_PWR_ERR_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_COMM_ERR, 7, TPS6594_BIT_COMM_ERR_INT),
+ REGMAP_IRQ_REG(TPS65224_IRQ_I2C2_ERR, 7, TPS65224_BIT_I2C2_ERR_INT),
+};
+
static inline unsigned int tps6594_get_irq_reg(struct regmap_irq_chip_data *data,
unsigned int base, int index)
{
return tps6594_irq_reg[index];
};
+static inline unsigned int tps65224_get_irq_reg(struct regmap_irq_chip_data *data,
+ unsigned int base, int index)
+{
+ return tps65224_irq_reg[index];
+};
+
static int tps6594_handle_post_irq(void *irq_drv_data)
{
struct tps6594 *tps = irq_drv_data;
int ret = 0;
+ unsigned int regmap_reg, mask_val;
/*
* When CRC is enabled, writing to a read-only bit triggers an error,
@@ -299,10 +516,17 @@ static int tps6594_handle_post_irq(void *irq_drv_data)
* COMM_ADR_ERR_INT bit set. Clear immediately this bit to avoid raising
* a new interrupt.
*/
- if (tps->use_crc)
- ret = regmap_write_bits(tps->regmap, TPS6594_REG_INT_COMM_ERR,
- TPS6594_BIT_COMM_ADR_ERR_INT,
- TPS6594_BIT_COMM_ADR_ERR_INT);
+ if (tps->use_crc) {
+ if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) {
+ regmap_reg = TPS6594_REG_INT_FSM_ERR;
+ mask_val = TPS6594_BIT_COMM_ERR_INT;
+ } else {
+ regmap_reg = TPS6594_REG_INT_COMM_ERR;
+ mask_val = TPS6594_BIT_COMM_ADR_ERR_INT;
+ }
+
+ ret = regmap_write_bits(tps->regmap, regmap_reg, mask_val, mask_val);
+ }
return ret;
};
@@ -319,24 +543,70 @@ static struct regmap_irq_chip tps6594_irq_chip = {
.handle_post_irq = tps6594_handle_post_irq,
};
-bool tps6594_is_volatile_reg(struct device *dev, unsigned int reg)
-{
- return (reg >= TPS6594_REG_INT_TOP && reg <= TPS6594_REG_STAT_READBACK_ERR) ||
- reg == TPS6594_REG_RTC_STATUS;
-}
-EXPORT_SYMBOL_GPL(tps6594_is_volatile_reg);
+static struct regmap_irq_chip tps65224_irq_chip = {
+ .ack_base = TPS6594_REG_INT_BUCK,
+ .ack_invert = 1,
+ .clear_ack = 1,
+ .init_ack_masked = 1,
+ .num_regs = ARRAY_SIZE(tps65224_irq_reg),
+ .irqs = tps65224_irqs,
+ .num_irqs = ARRAY_SIZE(tps65224_irqs),
+ .get_irq_reg = tps65224_get_irq_reg,
+ .handle_post_irq = tps6594_handle_post_irq,
+};
+
+static struct regmap_irq_chip tps652g1_irq_chip = {
+ .ack_base = TPS6594_REG_INT_BUCK,
+ .ack_invert = 1,
+ .clear_ack = 1,
+ .init_ack_masked = 1,
+ .num_regs = ARRAY_SIZE(tps65224_irq_reg),
+ .irqs = tps652g1_irqs,
+ .num_irqs = ARRAY_SIZE(tps652g1_irqs),
+ .get_irq_reg = tps65224_get_irq_reg,
+ .handle_post_irq = tps6594_handle_post_irq,
+};
+
+static const struct regmap_range tps6594_volatile_ranges[] = {
+ regmap_reg_range(TPS6594_REG_INT_TOP, TPS6594_REG_STAT_READBACK_ERR),
+ regmap_reg_range(TPS6594_REG_RTC_STATUS, TPS6594_REG_RTC_STATUS),
+};
+
+const struct regmap_access_table tps6594_volatile_table = {
+ .yes_ranges = tps6594_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(tps6594_volatile_ranges),
+};
+EXPORT_SYMBOL_GPL(tps6594_volatile_table);
+
+static const struct regmap_range tps65224_volatile_ranges[] = {
+ regmap_reg_range(TPS6594_REG_INT_TOP, TPS6594_REG_STAT_SEVERE_ERR),
+};
+
+const struct regmap_access_table tps65224_volatile_table = {
+ .yes_ranges = tps65224_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(tps65224_volatile_ranges),
+};
+EXPORT_SYMBOL_GPL(tps65224_volatile_table);
static int tps6594_check_crc_mode(struct tps6594 *tps, bool primary_pmic)
{
int ret;
+ unsigned int regmap_reg, mask_val;
+
+ if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) {
+ regmap_reg = TPS6594_REG_CONFIG_2;
+ mask_val = TPS65224_BIT_I2C1_SPI_CRC_EN;
+ } else {
+ regmap_reg = TPS6594_REG_SERIAL_IF_CONFIG;
+ mask_val = TPS6594_BIT_I2C1_SPI_CRC_EN;
+ }
/*
* Check if CRC is enabled.
* Once CRC is enabled, it can't be disabled until next power cycle.
*/
tps->use_crc = true;
- ret = regmap_test_bits(tps->regmap, TPS6594_REG_SERIAL_IF_CONFIG,
- TPS6594_BIT_I2C1_SPI_CRC_EN);
+ ret = regmap_test_bits(tps->regmap, regmap_reg, mask_val);
if (ret == 0) {
ret = -EIO;
} else if (ret > 0) {
@@ -351,6 +621,15 @@ static int tps6594_check_crc_mode(struct tps6594 *tps, bool primary_pmic)
static int tps6594_set_crc_feature(struct tps6594 *tps)
{
int ret;
+ unsigned int regmap_reg, mask_val;
+
+ if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) {
+ regmap_reg = TPS6594_REG_CONFIG_2;
+ mask_val = TPS65224_BIT_I2C1_SPI_CRC_EN;
+ } else {
+ regmap_reg = TPS6594_REG_FSM_I2C_TRIGGERS;
+ mask_val = TPS6594_BIT_TRIGGER_I2C(2);
+ }
ret = tps6594_check_crc_mode(tps, true);
if (ret) {
@@ -359,8 +638,7 @@ static int tps6594_set_crc_feature(struct tps6594 *tps)
* on primary PMIC.
*/
tps->use_crc = false;
- ret = regmap_write_bits(tps->regmap, TPS6594_REG_FSM_I2C_TRIGGERS,
- TPS6594_BIT_TRIGGER_I2C(2), TPS6594_BIT_TRIGGER_I2C(2));
+ ret = regmap_write_bits(tps->regmap, regmap_reg, mask_val, mask_val);
if (ret)
return ret;
@@ -412,10 +690,27 @@ static int tps6594_enable_crc(struct tps6594 *tps)
return ret;
}
+static int tps6594_power_off_handler(struct sys_off_data *data)
+{
+ struct tps6594 *tps = data->cb_data;
+ int ret;
+
+ ret = regmap_update_bits(tps->regmap, TPS6594_REG_FSM_I2C_TRIGGERS,
+ TPS6594_BIT_TRIGGER_I2C(0), TPS6594_BIT_TRIGGER_I2C(0));
+ if (ret)
+ return notifier_from_errno(ret);
+
+ return NOTIFY_DONE;
+}
+
int tps6594_device_init(struct tps6594 *tps, bool enable_crc)
{
struct device *dev = tps->dev;
int ret;
+ struct regmap_irq_chip *irq_chip;
+ unsigned int pwr_on, gpio3_cfg;
+ const struct mfd_cell *cells;
+ int n_cells;
if (enable_crc) {
ret = tps6594_enable_crc(tps);
@@ -429,23 +724,60 @@ int tps6594_device_init(struct tps6594 *tps, bool enable_crc)
if (ret)
return dev_err_probe(dev, ret, "Failed to set PMIC state\n");
- tps6594_irq_chip.irq_drv_data = tps;
- tps6594_irq_chip.name = devm_kasprintf(dev, GFP_KERNEL, "%s-%ld-0x%02x",
- dev->driver->name, tps->chip_id, tps->reg);
+ if (tps->chip_id == TPS65224) {
+ irq_chip = &tps65224_irq_chip;
+ n_cells = ARRAY_SIZE(tps65224_common_cells);
+ cells = tps65224_common_cells;
+ } else if (tps->chip_id == TPS652G1) {
+ irq_chip = &tps652g1_irq_chip;
+ n_cells = ARRAY_SIZE(tps652g1_common_cells);
+ cells = tps652g1_common_cells;
+ } else {
+ irq_chip = &tps6594_irq_chip;
+ n_cells = ARRAY_SIZE(tps6594_common_cells);
+ cells = tps6594_common_cells;
+ }
+
+ irq_chip->irq_drv_data = tps;
+ irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-%ld-0x%02x",
+ dev->driver->name, tps->chip_id, tps->reg);
+
+ if (!irq_chip->name)
+ return -ENOMEM;
ret = devm_regmap_add_irq_chip(dev, tps->regmap, tps->irq, IRQF_SHARED | IRQF_ONESHOT,
- 0, &tps6594_irq_chip, &tps->irq_data);
+ 0, irq_chip, &tps->irq_data);
if (ret)
return dev_err_probe(dev, ret, "Failed to add regmap IRQ\n");
- ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, tps6594_common_cells,
- ARRAY_SIZE(tps6594_common_cells), NULL, 0,
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells, n_cells, NULL, 0,
regmap_irq_get_domain(tps->irq_data));
if (ret)
return dev_err_probe(dev, ret, "Failed to add common child devices\n");
- /* No RTC for LP8764 */
- if (tps->chip_id != LP8764) {
+ /* If either the PB/EN/VSENSE or GPIO3 is configured as PB, register a driver for it */
+ if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) {
+ ret = regmap_read(tps->regmap, TPS6594_REG_NPWRON_CONF, &pwr_on);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read PB/EN/VSENSE config\n");
+
+ ret = regmap_read(tps->regmap, TPS6594_REG_GPIOX_CONF(2), &gpio3_cfg);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read GPIO3 config\n");
+
+ if (FIELD_GET(TPS65224_MASK_EN_PB_VSENSE_CONFIG, pwr_on) == TPS65224_EN_SEL_PB ||
+ FIELD_GET(TPS65224_MASK_GPIO_SEL, gpio3_cfg) == TPS65224_GPIO3_SEL_PB) {
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
+ &tps6594_pwrbutton_cell, 1, NULL, 0,
+ regmap_irq_get_domain(tps->irq_data));
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to add power button device.\n");
+ }
+ }
+
+ /* No RTC for LP8764, TPS65224 and TPS652G1 */
+ if (tps->chip_id != LP8764 && tps->chip_id != TPS65224 && tps->chip_id != TPS652G1) {
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, tps6594_rtc_cells,
ARRAY_SIZE(tps6594_rtc_cells), NULL, 0,
regmap_irq_get_domain(tps->irq_data));
@@ -453,10 +785,17 @@ int tps6594_device_init(struct tps6594 *tps, bool enable_crc)
return dev_err_probe(dev, ret, "Failed to add RTC child device\n");
}
+ if (of_device_is_system_power_controller(dev->of_node)) {
+ ret = devm_register_power_off_handler(tps->dev, tps6594_power_off_handler, tps);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register power-off handler\n");
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(tps6594_device_init);
MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>");
+MODULE_AUTHOR("Bhargav Raviprakash <bhargav.r@ltts.com");
MODULE_DESCRIPTION("TPS6594 Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tps6594-i2c.c b/drivers/mfd/tps6594-i2c.c
index 899c88c0fe77..7ff7516286fd 100644
--- a/drivers/mfd/tps6594-i2c.c
+++ b/drivers/mfd/tps6594-i2c.c
@@ -1,6 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * I2C access driver for TI TPS6594/TPS6593/LP8764 PMICs
+ * I2C access driver for the following TI PMICs:
+ * - LP8764
+ * - TPS65224
+ * - TPS652G1
+ * - TPS6593
+ * - TPS6594
*
* Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
*/
@@ -183,11 +188,11 @@ static int tps6594_i2c_write(void *context, const void *data, size_t count)
return ret;
}
-static const struct regmap_config tps6594_i2c_regmap_config = {
+static struct regmap_config tps6594_i2c_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.max_register = TPS6594_REG_DWD_FAIL_CNT_REG,
- .volatile_reg = tps6594_is_volatile_reg,
+ .volatile_table = &tps6594_volatile_table,
.read = tps6594_i2c_read,
.write = tps6594_i2c_write,
};
@@ -196,6 +201,8 @@ static const struct of_device_id tps6594_i2c_of_match_table[] = {
{ .compatible = "ti,tps6594-q1", .data = (void *)TPS6594, },
{ .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, },
{ .compatible = "ti,lp8764-q1", .data = (void *)LP8764, },
+ { .compatible = "ti,tps65224-q1", .data = (void *)TPS65224, },
+ { .compatible = "ti,tps652g1", .data = (void *)TPS652G1, },
{}
};
MODULE_DEVICE_TABLE(of, tps6594_i2c_of_match_table);
@@ -216,15 +223,18 @@ static int tps6594_i2c_probe(struct i2c_client *client)
tps->reg = client->addr;
tps->irq = client->irq;
- tps->regmap = devm_regmap_init(dev, NULL, client, &tps6594_i2c_regmap_config);
- if (IS_ERR(tps->regmap))
- return dev_err_probe(dev, PTR_ERR(tps->regmap), "Failed to init regmap\n");
-
match = of_match_device(tps6594_i2c_of_match_table, dev);
if (!match)
return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n");
tps->chip_id = (unsigned long)match->data;
+ if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1)
+ tps6594_i2c_regmap_config.volatile_table = &tps65224_volatile_table;
+
+ tps->regmap = devm_regmap_init(dev, NULL, client, &tps6594_i2c_regmap_config);
+ if (IS_ERR(tps->regmap))
+ return dev_err_probe(dev, PTR_ERR(tps->regmap), "Failed to init regmap\n");
+
crc8_populate_msb(tps6594_i2c_crc_table, TPS6594_CRC8_POLYNOMIAL);
return tps6594_device_init(tps, enable_crc);
@@ -240,5 +250,5 @@ static struct i2c_driver tps6594_i2c_driver = {
module_i2c_driver(tps6594_i2c_driver);
MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>");
-MODULE_DESCRIPTION("TPS6594 I2C Interface Driver");
+MODULE_DESCRIPTION("I2C Interface Driver for TPS65224, TPS6594/3, and LP8764");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tps6594-spi.c b/drivers/mfd/tps6594-spi.c
index f4b4f37f957f..944b7313a1d9 100644
--- a/drivers/mfd/tps6594-spi.c
+++ b/drivers/mfd/tps6594-spi.c
@@ -1,6 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * SPI access driver for TI TPS6594/TPS6593/LP8764 PMICs
+ * SPI access driver for the following TI PMICs:
+ * - LP8764
+ * - TPS65224
+ * - TPS652G1
+ * - TPS6593
+ * - TPS6594
*
* Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
*/
@@ -66,11 +71,11 @@ static int tps6594_spi_reg_write(void *context, unsigned int reg, unsigned int v
return spi_write(spi, buf, count);
}
-static const struct regmap_config tps6594_spi_regmap_config = {
+static struct regmap_config tps6594_spi_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.max_register = TPS6594_REG_DWD_FAIL_CNT_REG,
- .volatile_reg = tps6594_is_volatile_reg,
+ .volatile_table = &tps6594_volatile_table,
.reg_read = tps6594_spi_reg_read,
.reg_write = tps6594_spi_reg_write,
.use_single_read = true,
@@ -81,6 +86,8 @@ static const struct of_device_id tps6594_spi_of_match_table[] = {
{ .compatible = "ti,tps6594-q1", .data = (void *)TPS6594, },
{ .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, },
{ .compatible = "ti,lp8764-q1", .data = (void *)LP8764, },
+ { .compatible = "ti,tps65224-q1", .data = (void *)TPS65224, },
+ { .compatible = "ti,tps652g1", .data = (void *)TPS652G1, },
{}
};
MODULE_DEVICE_TABLE(of, tps6594_spi_of_match_table);
@@ -98,18 +105,21 @@ static int tps6594_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, tps);
tps->dev = dev;
- tps->reg = spi->chip_select;
+ tps->reg = spi_get_chipselect(spi, 0);
tps->irq = spi->irq;
- tps->regmap = devm_regmap_init(dev, NULL, spi, &tps6594_spi_regmap_config);
- if (IS_ERR(tps->regmap))
- return dev_err_probe(dev, PTR_ERR(tps->regmap), "Failed to init regmap\n");
-
match = of_match_device(tps6594_spi_of_match_table, dev);
if (!match)
return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n");
tps->chip_id = (unsigned long)match->data;
+ if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1)
+ tps6594_spi_regmap_config.volatile_table = &tps65224_volatile_table;
+
+ tps->regmap = devm_regmap_init(dev, NULL, spi, &tps6594_spi_regmap_config);
+ if (IS_ERR(tps->regmap))
+ return dev_err_probe(dev, PTR_ERR(tps->regmap), "Failed to init regmap\n");
+
crc8_populate_msb(tps6594_spi_crc_table, TPS6594_CRC8_POLYNOMIAL);
return tps6594_device_init(tps, enable_crc);
@@ -125,5 +135,5 @@ static struct spi_driver tps6594_spi_driver = {
module_spi_driver(tps6594_spi_driver);
MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>");
-MODULE_DESCRIPTION("TPS6594 SPI Interface Driver");
+MODULE_DESCRIPTION("SPI Interface Driver for TPS65224, TPS6594/3, and LP8764");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tqmx86.c b/drivers/mfd/tqmx86.c
index fac02875fe7d..1c2fe3f91238 100644
--- a/drivers/mfd/tqmx86.c
+++ b/drivers/mfd/tqmx86.c
@@ -35,11 +35,16 @@
#define TQMX86_REG_BOARD_ID_E39C2 7
#define TQMX86_REG_BOARD_ID_70EB 8
#define TQMX86_REG_BOARD_ID_80UC 9
+#define TQMX86_REG_BOARD_ID_120UC 10
#define TQMX86_REG_BOARD_ID_110EB 11
#define TQMX86_REG_BOARD_ID_E40M 12
#define TQMX86_REG_BOARD_ID_E40S 13
#define TQMX86_REG_BOARD_ID_E40C1 14
#define TQMX86_REG_BOARD_ID_E40C2 15
+#define TQMX86_REG_BOARD_ID_130UC 16
+#define TQMX86_REG_BOARD_ID_E41S 19
+#define TQMX86_REG_BOARD_ID_CU1_HPCM 24
+#define TQMX86_REG_BOARD_ID_CU2_HPCM 25
#define TQMX86_REG_BOARD_REV 0x01
#define TQMX86_REG_IO_EXT_INT 0x06
#define TQMX86_REG_IO_EXT_INT_NONE 0
@@ -47,6 +52,7 @@
#define TQMX86_REG_IO_EXT_INT_9 2
#define TQMX86_REG_IO_EXT_INT_12 3
#define TQMX86_REG_IO_EXT_INT_MASK 0x3
+#define TQMX86_REG_IO_EXT_INT_I2C1_SHIFT 0
#define TQMX86_REG_IO_EXT_INT_GPIO_SHIFT 4
#define TQMX86_REG_SAUC 0x17
@@ -55,23 +61,36 @@
static uint gpio_irq;
module_param(gpio_irq, uint, 0);
-MODULE_PARM_DESC(gpio_irq, "GPIO IRQ number (7, 9, 12)");
+MODULE_PARM_DESC(gpio_irq, "GPIO IRQ number (valid parameters: 7, 9, 12)");
-static const struct resource tqmx_i2c_soft_resources[] = {
- DEFINE_RES_IO(TQMX86_IOBASE_I2C, TQMX86_IOSIZE_I2C),
+static uint i2c1_irq;
+module_param(i2c1_irq, uint, 0);
+MODULE_PARM_DESC(i2c1_irq, "I2C1 IRQ number (valid parameters: 7, 9, 12)");
+
+enum tqmx86_i2c1_resource_type {
+ TQMX86_I2C1_IO,
+ TQMX86_I2C1_IRQ,
+};
+
+static struct resource tqmx_i2c_soft_resources[] = {
+ [TQMX86_I2C1_IO] = DEFINE_RES_IO(TQMX86_IOBASE_I2C, TQMX86_IOSIZE_I2C),
+ /* Placeholder for IRQ resource */
+ [TQMX86_I2C1_IRQ] = {},
};
static const struct resource tqmx_watchdog_resources[] = {
DEFINE_RES_IO(TQMX86_IOBASE_WATCHDOG, TQMX86_IOSIZE_WATCHDOG),
};
-/*
- * The IRQ resource must be first, since it is updated with the
- * configured IRQ in the probe function.
- */
+enum tqmx86_gpio_resource_type {
+ TQMX86_GPIO_IO,
+ TQMX86_GPIO_IRQ,
+};
+
static struct resource tqmx_gpio_resources[] = {
- DEFINE_RES_IRQ(0),
- DEFINE_RES_IO(TQMX86_IOBASE_GPIO, TQMX86_IOSIZE_GPIO),
+ [TQMX86_GPIO_IO] = DEFINE_RES_IO(TQMX86_IOBASE_GPIO, TQMX86_IOSIZE_GPIO),
+ /* Placeholder for IRQ resource */
+ [TQMX86_GPIO_IRQ] = {},
};
static struct i2c_board_info tqmx86_i2c_devices[] = {
@@ -132,6 +151,8 @@ static const char *tqmx86_board_id_to_name(u8 board_id, u8 sauc)
return "TQMx70EB";
case TQMX86_REG_BOARD_ID_80UC:
return "TQMx80UC";
+ case TQMX86_REG_BOARD_ID_120UC:
+ return "TQMx120UC";
case TQMX86_REG_BOARD_ID_110EB:
return "TQMx110EB";
case TQMX86_REG_BOARD_ID_E40M:
@@ -142,6 +163,14 @@ static const char *tqmx86_board_id_to_name(u8 board_id, u8 sauc)
return "TQMxE40C1";
case TQMX86_REG_BOARD_ID_E40C2:
return "TQMxE40C2";
+ case TQMX86_REG_BOARD_ID_130UC:
+ return "TQMx130UC";
+ case TQMX86_REG_BOARD_ID_E41S:
+ return "TQMxE41S";
+ case TQMX86_REG_BOARD_ID_CU1_HPCM:
+ return "TQMxCU1-HPCM";
+ case TQMX86_REG_BOARD_ID_CU2_HPCM:
+ return "TQMxCU2-HPCM";
default:
return "Unknown";
}
@@ -154,11 +183,16 @@ static int tqmx86_board_id_to_clk_rate(struct device *dev, u8 board_id)
case TQMX86_REG_BOARD_ID_60EB:
case TQMX86_REG_BOARD_ID_70EB:
case TQMX86_REG_BOARD_ID_80UC:
+ case TQMX86_REG_BOARD_ID_120UC:
case TQMX86_REG_BOARD_ID_110EB:
case TQMX86_REG_BOARD_ID_E40M:
case TQMX86_REG_BOARD_ID_E40S:
case TQMX86_REG_BOARD_ID_E40C1:
case TQMX86_REG_BOARD_ID_E40C2:
+ case TQMX86_REG_BOARD_ID_130UC:
+ case TQMX86_REG_BOARD_ID_E41S:
+ case TQMX86_REG_BOARD_ID_CU1_HPCM:
+ case TQMX86_REG_BOARD_ID_CU2_HPCM:
return 24000;
case TQMX86_REG_BOARD_ID_E39MS:
case TQMX86_REG_BOARD_ID_E39C1:
@@ -174,33 +208,52 @@ static int tqmx86_board_id_to_clk_rate(struct device *dev, u8 board_id)
}
}
-static int tqmx86_probe(struct platform_device *pdev)
+static int tqmx86_setup_irq(struct device *dev, const char *label, u8 irq,
+ void __iomem *io_base, u8 reg_shift)
{
- u8 board_id, sauc, rev, i2c_det, io_ext_int_val;
- struct device *dev = &pdev->dev;
- u8 gpio_irq_cfg, readback;
- const char *board_name;
- void __iomem *io_base;
- int err;
+ u8 val, readback;
+ int irq_cfg;
- switch (gpio_irq) {
+ switch (irq) {
case 0:
- gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_NONE;
+ irq_cfg = TQMX86_REG_IO_EXT_INT_NONE;
break;
case 7:
- gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_7;
+ irq_cfg = TQMX86_REG_IO_EXT_INT_7;
break;
case 9:
- gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_9;
+ irq_cfg = TQMX86_REG_IO_EXT_INT_9;
break;
case 12:
- gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_12;
+ irq_cfg = TQMX86_REG_IO_EXT_INT_12;
break;
default:
- pr_err("tqmx86: Invalid GPIO IRQ (%d)\n", gpio_irq);
+ dev_err(dev, "invalid %s IRQ (%d)\n", label, irq);
return -EINVAL;
}
+ val = ioread8(io_base + TQMX86_REG_IO_EXT_INT);
+ val &= ~(TQMX86_REG_IO_EXT_INT_MASK << reg_shift);
+ val |= (irq_cfg & TQMX86_REG_IO_EXT_INT_MASK) << reg_shift;
+
+ iowrite8(val, io_base + TQMX86_REG_IO_EXT_INT);
+ readback = ioread8(io_base + TQMX86_REG_IO_EXT_INT);
+ if (readback != val) {
+ dev_warn(dev, "%s interrupts not supported\n", label);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tqmx86_probe(struct platform_device *pdev)
+{
+ u8 board_id, sauc, rev, i2c_det;
+ struct device *dev = &pdev->dev;
+ const char *board_name;
+ void __iomem *io_base;
+ int err;
+
io_base = devm_ioport_map(dev, TQMX86_IOBASE, TQMX86_IOSIZE);
if (!io_base)
return -ENOMEM;
@@ -221,25 +274,23 @@ static int tqmx86_probe(struct platform_device *pdev)
*/
i2c_det = inb(TQMX86_REG_I2C_DETECT);
- if (gpio_irq_cfg) {
- io_ext_int_val =
- gpio_irq_cfg << TQMX86_REG_IO_EXT_INT_GPIO_SHIFT;
- iowrite8(io_ext_int_val, io_base + TQMX86_REG_IO_EXT_INT);
- readback = ioread8(io_base + TQMX86_REG_IO_EXT_INT);
- if (readback != io_ext_int_val) {
- dev_warn(dev, "GPIO interrupts not supported.\n");
- return -EINVAL;
- }
-
- /* Assumes the IRQ resource is first. */
- tqmx_gpio_resources[0].start = gpio_irq;
- } else {
- tqmx_gpio_resources[0].flags = 0;
+ if (gpio_irq) {
+ err = tqmx86_setup_irq(dev, "GPIO", gpio_irq, io_base,
+ TQMX86_REG_IO_EXT_INT_GPIO_SHIFT);
+ if (!err)
+ tqmx_gpio_resources[TQMX86_GPIO_IRQ] = DEFINE_RES_IRQ(gpio_irq);
}
ocores_platform_data.clock_khz = tqmx86_board_id_to_clk_rate(dev, board_id);
if (i2c_det == TQMX86_REG_I2C_DETECT_SOFT) {
+ if (i2c1_irq) {
+ err = tqmx86_setup_irq(dev, "I2C1", i2c1_irq, io_base,
+ TQMX86_REG_IO_EXT_INT_I2C1_SHIFT);
+ if (!err)
+ tqmx_i2c_soft_resources[TQMX86_I2C1_IRQ] = DEFINE_RES_IRQ(i2c1_irq);
+ }
+
err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
tqmx86_i2c_soft_dev,
ARRAY_SIZE(tqmx86_i2c_soft_dev),
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index ce01a87f8dc3..f89eda4a17fe 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -31,6 +31,8 @@
#include <linux/regulator/machine.h>
#include <linux/i2c.h>
+
+#include <linux/mfd/core.h>
#include <linux/mfd/twl.h>
/* Register descriptions for audio */
@@ -122,6 +124,11 @@
#define TWL6030_BASEADD_RSV 0x0000
#define TWL6030_BASEADD_ZERO 0x0000
+/* Some fields in TWL6030_PHOENIX_DEV_ON */
+#define TWL6030_APP_DEVOFF BIT(0)
+#define TWL6030_CON_DEVOFF BIT(1)
+#define TWL6030_MOD_DEVOFF BIT(2)
+
/* Few power values */
#define R_CFG_BOOT 0x05
@@ -312,7 +319,7 @@ static const struct regmap_config twl4030_regmap_config[4] = {
.reg_defaults = twl4030_49_defaults,
.num_reg_defaults = ARRAY_SIZE(twl4030_49_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
},
{
/* Address 0x4a */
@@ -685,11 +692,33 @@ static void twl_remove(struct i2c_client *client)
twl_priv->ready = false;
}
+static void twl6030_power_off(void)
+{
+ int err;
+ u8 val;
+
+ err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &val, TWL6030_PHOENIX_DEV_ON);
+ if (err)
+ return;
+
+ val |= TWL6030_APP_DEVOFF | TWL6030_CON_DEVOFF | TWL6030_MOD_DEVOFF;
+ twl_i2c_write_u8(TWL_MODULE_PM_MASTER, val, TWL6030_PHOENIX_DEV_ON);
+}
+
+
static struct of_dev_auxdata twl_auxdata_lookup[] = {
OF_DEV_AUXDATA("ti,twl4030-gpio", 0, "twl4030-gpio", NULL),
{ /* sentinel */ },
};
+static const struct mfd_cell twl6030_cells[] = {
+ { .name = "twl6030-clk" },
+};
+
+static const struct mfd_cell twl6032_cells[] = {
+ { .name = "twl6032-clk" },
+};
+
/* NOTE: This driver only handles a single twl4030/tps659x0 chip */
static int
twl_probe(struct i2c_client *client)
@@ -836,6 +865,31 @@ twl_probe(struct i2c_client *client)
TWL4030_DCDC_GLOBAL_CFG);
}
+ if (twl_class_is_6030()) {
+ const struct mfd_cell *cells;
+ int num_cells;
+
+ if (id->driver_data & TWL6032_SUBCLASS) {
+ cells = twl6032_cells;
+ num_cells = ARRAY_SIZE(twl6032_cells);
+ } else {
+ cells = twl6030_cells;
+ num_cells = ARRAY_SIZE(twl6030_cells);
+ }
+
+ status = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
+ cells, num_cells, NULL, 0, NULL);
+ if (status < 0)
+ goto free;
+
+ if (of_device_is_system_power_controller(node)) {
+ if (!pm_power_off)
+ pm_power_off = twl6030_power_off;
+ else
+ dev_warn(&client->dev, "Poweroff callback already assigned\n");
+ }
+ }
+
status = of_platform_populate(node, NULL, twl_auxdata_lookup,
&client->dev);
diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c
index 88002f8941e5..a4a550bafb3c 100644
--- a/drivers/mfd/twl4030-audio.c
+++ b/drivers/mfd/twl4030-audio.c
@@ -258,12 +258,10 @@ static int twl4030_audio_probe(struct platform_device *pdev)
return ret;
}
-static int twl4030_audio_remove(struct platform_device *pdev)
+static void twl4030_audio_remove(struct platform_device *pdev)
{
mfd_remove_devices(&pdev->dev);
twl4030_audio_dev = NULL;
-
- return 0;
}
static const struct of_device_id twl4030_audio_of_match[] = {
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index 87496c1cb8bc..d3ab40651307 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -676,7 +676,6 @@ int twl4030_init_irq(struct device *dev, int irq_num)
static struct irq_chip twl4030_irq_chip;
int status, i;
int irq_base, irq_end, nr_irqs;
- struct device_node *node = dev->of_node;
/*
* TWL core and pwr interrupts must be contiguous because
@@ -691,8 +690,8 @@ int twl4030_init_irq(struct device *dev, int irq_num)
return irq_base;
}
- irq_domain_add_legacy(node, nr_irqs, irq_base, 0,
- &irq_domain_simple_ops, NULL);
+ irq_domain_create_legacy(dev_fwnode(dev), nr_irqs, irq_base, 0,
+ &irq_domain_simple_ops, NULL);
irq_end = irq_base + TWL4030_CORE_NR_IRQS;
diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c
index e35b0f788c50..0bca948ab6ba 100644
--- a/drivers/mfd/twl4030-power.c
+++ b/drivers/mfd/twl4030-power.c
@@ -27,8 +27,8 @@
#include <linux/pm.h>
#include <linux/mfd/twl.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <asm/mach-types.h>
@@ -686,6 +686,9 @@ static bool twl4030_power_use_poweroff(const struct twl4030_power_data *pdata,
if (of_property_read_bool(node, "ti,use_poweroff"))
return true;
+ if (of_device_is_system_power_controller(node->parent))
+ return true;
+
return false;
}
@@ -883,7 +886,6 @@ static int twl4030_power_probe(struct platform_device *pdev)
{
const struct twl4030_power_data *pdata = dev_get_platdata(&pdev->dev);
struct device_node *node = pdev->dev.of_node;
- const struct of_device_id *match;
int err = 0;
int err2 = 0;
u8 val;
@@ -904,10 +906,8 @@ static int twl4030_power_probe(struct platform_device *pdev)
return err;
}
- match = of_match_device(of_match_ptr(twl4030_power_of_match),
- &pdev->dev);
- if (match && match->data)
- pdata = match->data;
+ if (node)
+ pdata = device_get_match_data(&pdev->dev);
if (pdata) {
err = twl4030_power_configure_scripts(pdata);
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index 3c03681c124c..0ca00f618d4d 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -256,80 +256,6 @@ int twl6030_interrupt_mask(u8 bit_mask, u8 offset)
}
EXPORT_SYMBOL(twl6030_interrupt_mask);
-int twl6030_mmc_card_detect_config(void)
-{
- int ret;
- u8 reg_val = 0;
-
- /* Unmasking the Card detect Interrupt line for MMC1 from Phoenix */
- twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
- REG_INT_MSK_LINE_B);
- twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
- REG_INT_MSK_STS_B);
- /*
- * Initially Configuring MMC_CTRL for receiving interrupts &
- * Card status on TWL6030 for MMC1
- */
- ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val, TWL6030_MMCCTRL);
- if (ret < 0) {
- pr_err("twl6030: Failed to read MMCCTRL, error %d\n", ret);
- return ret;
- }
- reg_val &= ~VMMC_AUTO_OFF;
- reg_val |= SW_FC;
- ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, TWL6030_MMCCTRL);
- if (ret < 0) {
- pr_err("twl6030: Failed to write MMCCTRL, error %d\n", ret);
- return ret;
- }
-
- /* Configuring PullUp-PullDown register */
- ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val,
- TWL6030_CFG_INPUT_PUPD3);
- if (ret < 0) {
- pr_err("twl6030: Failed to read CFG_INPUT_PUPD3, error %d\n",
- ret);
- return ret;
- }
- reg_val &= ~(MMC_PU | MMC_PD);
- ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val,
- TWL6030_CFG_INPUT_PUPD3);
- if (ret < 0) {
- pr_err("twl6030: Failed to write CFG_INPUT_PUPD3, error %d\n",
- ret);
- return ret;
- }
-
- return irq_find_mapping(twl6030_irq->irq_domain,
- MMCDETECT_INTR_OFFSET);
-}
-EXPORT_SYMBOL(twl6030_mmc_card_detect_config);
-
-int twl6030_mmc_card_detect(struct device *dev, int slot)
-{
- int ret = -EIO;
- u8 read_reg = 0;
- struct platform_device *pdev = to_platform_device(dev);
-
- if (pdev->id) {
- /* TWL6030 provide's Card detect support for
- * only MMC1 controller.
- */
- pr_err("Unknown MMC controller %d in %s\n", pdev->id, __func__);
- return ret;
- }
- /*
- * BIT0 of MMC_CTRL on TWL6030 provides card status for MMC1
- * 0 - Card not present ,1 - Card present
- */
- ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &read_reg,
- TWL6030_MMCCTRL);
- if (ret >= 0)
- ret = read_reg & STS_MMC;
- return ret;
-}
-EXPORT_SYMBOL(twl6030_mmc_card_detect);
-
static int twl6030_irq_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hwirq)
{
@@ -364,7 +290,6 @@ static const struct of_device_id twl6030_of_match[] __maybe_unused = {
int twl6030_init_irq(struct device *dev, int irq_num)
{
- struct device_node *node = dev->of_node;
int nr_irqs;
int status;
u8 mask[3];
@@ -411,9 +336,8 @@ int twl6030_init_irq(struct device *dev, int irq_num)
atomic_set(&twl6030_irq->wakeirqs, 0);
twl6030_irq->irq_mapping_tbl = of_id->data;
- twl6030_irq->irq_domain =
- irq_domain_add_linear(node, nr_irqs,
- &twl6030_irq_domain_ops, twl6030_irq);
+ twl6030_irq->irq_domain = irq_domain_create_linear(dev_fwnode(dev), nr_irqs,
+ &twl6030_irq_domain_ops, twl6030_irq);
if (!twl6030_irq->irq_domain) {
dev_err(dev, "Can't add irq_domain\n");
return -ENOMEM;
diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c
index d85675a4d9a8..562a0f939f6e 100644
--- a/drivers/mfd/twl6040.c
+++ b/drivers/mfd/twl6040.c
@@ -16,8 +16,6 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/of.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/i2c.h>
@@ -71,7 +69,7 @@ static const struct reg_default twl6040_defaults[] = {
{ 0x2E, 0x00 }, /* REG_STATUS (ro) */
};
-static struct reg_sequence twl6040_patch[] = {
+static const struct reg_sequence twl6040_patch[] = {
/*
* Select I2C bus access to dual access registers
* Interrupt register is cleared on read
@@ -622,7 +620,7 @@ static const struct regmap_irq twl6040_irqs[] = {
{ .reg_offset = 0, .mask = TWL6040_READYINT, },
};
-static struct regmap_irq_chip twl6040_irq_chip = {
+static const struct regmap_irq_chip twl6040_irq_chip = {
.name = "twl6040",
.irqs = twl6040_irqs,
.num_irqs = ARRAY_SIZE(twl6040_irqs),
@@ -819,9 +817,9 @@ static void twl6040_remove(struct i2c_client *client)
}
static const struct i2c_device_id twl6040_i2c_id[] = {
- { "twl6040", 0, },
- { "twl6041", 0, },
- { },
+ { "twl6040" },
+ { "twl6041" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id);
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c
index fc4d4c844a81..4b450d78a65f 100644
--- a/drivers/mfd/ucb1x00-core.c
+++ b/drivers/mfd/ucb1x00-core.c
@@ -104,7 +104,8 @@ unsigned int ucb1x00_io_read(struct ucb1x00 *ucb)
return ucb1x00_reg_read(ucb, UCB_IO_DATA);
}
-static void ucb1x00_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+static int ucb1x00_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct ucb1x00 *ucb = gpiochip_get_data(chip);
unsigned long flags;
@@ -119,6 +120,8 @@ static void ucb1x00_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
ucb1x00_disable(ucb);
spin_unlock_irqrestore(&ucb->io_lock, flags);
+
+ return 0;
}
static int ucb1x00_gpio_get(struct gpio_chip *chip, unsigned offset)
diff --git a/drivers/mfd/upboard-fpga.c b/drivers/mfd/upboard-fpga.c
new file mode 100644
index 000000000000..afce623bbba5
--- /dev/null
+++ b/drivers/mfd/upboard-fpga.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UP Board FPGA driver.
+ *
+ * FPGA provides more GPIO driving power, LEDS and pin mux function.
+ *
+ * Copyright (c) AAEON. All rights reserved.
+ * Copyright (C) 2024 Bootlin
+ *
+ * Author: Gary Wang <garywang@aaeon.com.tw>
+ * Author: Thomas Richard <thomas.richard@bootlin.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/upboard-fpga.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+
+#define UPBOARD_AAEON_MANUFACTURER_ID 0x01
+#define UPBOARD_MANUFACTURER_ID_MASK GENMASK(7, 0)
+
+#define UPBOARD_ADDRESS_SIZE 7
+#define UPBOARD_REGISTER_SIZE 16
+
+#define UPBOARD_READ_FLAG BIT(UPBOARD_ADDRESS_SIZE)
+
+#define UPBOARD_FW_ID_MAJOR_SUPPORTED 0x0
+
+#define UPBOARD_FW_ID_BUILD_MASK GENMASK(15, 12)
+#define UPBOARD_FW_ID_MAJOR_MASK GENMASK(11, 8)
+#define UPBOARD_FW_ID_MINOR_MASK GENMASK(7, 4)
+#define UPBOARD_FW_ID_PATCH_MASK GENMASK(3, 0)
+
+static int upboard_fpga_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct upboard_fpga *fpga = context;
+ int i;
+
+ /* Clear to start new transaction */
+ gpiod_set_value(fpga->clear_gpio, 0);
+ gpiod_set_value(fpga->clear_gpio, 1);
+
+ reg |= UPBOARD_READ_FLAG;
+
+ /* Send clock and addr from strobe & datain pins */
+ for (i = UPBOARD_ADDRESS_SIZE; i >= 0; i--) {
+ gpiod_set_value(fpga->strobe_gpio, 0);
+ gpiod_set_value(fpga->datain_gpio, !!(reg & BIT(i)));
+ gpiod_set_value(fpga->strobe_gpio, 1);
+ }
+
+ gpiod_set_value(fpga->strobe_gpio, 0);
+ *val = 0;
+
+ /* Read data from dataout pin */
+ for (i = UPBOARD_REGISTER_SIZE - 1; i >= 0; i--) {
+ gpiod_set_value(fpga->strobe_gpio, 1);
+ gpiod_set_value(fpga->strobe_gpio, 0);
+ *val |= gpiod_get_value(fpga->dataout_gpio) << i;
+ }
+
+ gpiod_set_value(fpga->strobe_gpio, 1);
+
+ return 0;
+}
+
+static int upboard_fpga_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct upboard_fpga *fpga = context;
+ int i;
+
+ /* Clear to start new transcation */
+ gpiod_set_value(fpga->clear_gpio, 0);
+ gpiod_set_value(fpga->clear_gpio, 1);
+
+ /* Send clock and addr from strobe & datain pins */
+ for (i = UPBOARD_ADDRESS_SIZE; i >= 0; i--) {
+ gpiod_set_value(fpga->strobe_gpio, 0);
+ gpiod_set_value(fpga->datain_gpio, !!(reg & BIT(i)));
+ gpiod_set_value(fpga->strobe_gpio, 1);
+ }
+
+ gpiod_set_value(fpga->strobe_gpio, 0);
+
+ /* Write data to datain pin */
+ for (i = UPBOARD_REGISTER_SIZE - 1; i >= 0; i--) {
+ gpiod_set_value(fpga->datain_gpio, !!(val & BIT(i)));
+ gpiod_set_value(fpga->strobe_gpio, 1);
+ gpiod_set_value(fpga->strobe_gpio, 0);
+ }
+
+ gpiod_set_value(fpga->strobe_gpio, 1);
+
+ return 0;
+}
+
+static const struct regmap_range upboard_up_readable_ranges[] = {
+ regmap_reg_range(UPBOARD_REG_PLATFORM_ID, UPBOARD_REG_FIRMWARE_ID),
+ regmap_reg_range(UPBOARD_REG_FUNC_EN0, UPBOARD_REG_FUNC_EN0),
+ regmap_reg_range(UPBOARD_REG_GPIO_EN0, UPBOARD_REG_GPIO_EN1),
+ regmap_reg_range(UPBOARD_REG_GPIO_DIR0, UPBOARD_REG_GPIO_DIR1),
+};
+
+static const struct regmap_range upboard_up_writable_ranges[] = {
+ regmap_reg_range(UPBOARD_REG_FUNC_EN0, UPBOARD_REG_FUNC_EN0),
+ regmap_reg_range(UPBOARD_REG_GPIO_EN0, UPBOARD_REG_GPIO_EN1),
+ regmap_reg_range(UPBOARD_REG_GPIO_DIR0, UPBOARD_REG_GPIO_DIR1),
+};
+
+static const struct regmap_access_table upboard_up_readable_table = {
+ .yes_ranges = upboard_up_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(upboard_up_readable_ranges),
+};
+
+static const struct regmap_access_table upboard_up_writable_table = {
+ .yes_ranges = upboard_up_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(upboard_up_writable_ranges),
+};
+
+static const struct regmap_config upboard_up_regmap_config = {
+ .reg_bits = UPBOARD_ADDRESS_SIZE,
+ .val_bits = UPBOARD_REGISTER_SIZE,
+ .max_register = UPBOARD_REG_MAX,
+ .reg_read = upboard_fpga_read,
+ .reg_write = upboard_fpga_write,
+ .fast_io = false,
+ .cache_type = REGCACHE_NONE,
+ .rd_table = &upboard_up_readable_table,
+ .wr_table = &upboard_up_writable_table,
+};
+
+static const struct regmap_range upboard_up2_readable_ranges[] = {
+ regmap_reg_range(UPBOARD_REG_PLATFORM_ID, UPBOARD_REG_FIRMWARE_ID),
+ regmap_reg_range(UPBOARD_REG_FUNC_EN0, UPBOARD_REG_FUNC_EN1),
+ regmap_reg_range(UPBOARD_REG_GPIO_EN0, UPBOARD_REG_GPIO_EN2),
+ regmap_reg_range(UPBOARD_REG_GPIO_DIR0, UPBOARD_REG_GPIO_DIR2),
+};
+
+static const struct regmap_range upboard_up2_writable_ranges[] = {
+ regmap_reg_range(UPBOARD_REG_FUNC_EN0, UPBOARD_REG_FUNC_EN1),
+ regmap_reg_range(UPBOARD_REG_GPIO_EN0, UPBOARD_REG_GPIO_EN2),
+ regmap_reg_range(UPBOARD_REG_GPIO_DIR0, UPBOARD_REG_GPIO_DIR2),
+};
+
+static const struct regmap_access_table upboard_up2_readable_table = {
+ .yes_ranges = upboard_up2_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(upboard_up2_readable_ranges),
+};
+
+static const struct regmap_access_table upboard_up2_writable_table = {
+ .yes_ranges = upboard_up2_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(upboard_up2_writable_ranges),
+};
+
+static const struct regmap_config upboard_up2_regmap_config = {
+ .reg_bits = UPBOARD_ADDRESS_SIZE,
+ .val_bits = UPBOARD_REGISTER_SIZE,
+ .max_register = UPBOARD_REG_MAX,
+ .reg_read = upboard_fpga_read,
+ .reg_write = upboard_fpga_write,
+ .fast_io = false,
+ .cache_type = REGCACHE_NONE,
+ .rd_table = &upboard_up2_readable_table,
+ .wr_table = &upboard_up2_writable_table,
+};
+
+static const struct mfd_cell upboard_up_mfd_cells[] = {
+ { .name = "upboard-pinctrl" },
+ { .name = "upboard-leds" },
+};
+
+static const struct upboard_fpga_data upboard_up_fpga_data = {
+ .type = UPBOARD_UP_FPGA,
+ .regmap_config = &upboard_up_regmap_config,
+};
+
+static const struct upboard_fpga_data upboard_up2_fpga_data = {
+ .type = UPBOARD_UP2_FPGA,
+ .regmap_config = &upboard_up2_regmap_config,
+};
+
+static int upboard_fpga_gpio_init(struct upboard_fpga *fpga)
+{
+ fpga->enable_gpio = devm_gpiod_get(fpga->dev, "enable", GPIOD_ASIS);
+ if (IS_ERR(fpga->enable_gpio))
+ return PTR_ERR(fpga->enable_gpio);
+
+ fpga->clear_gpio = devm_gpiod_get(fpga->dev, "clear", GPIOD_OUT_LOW);
+ if (IS_ERR(fpga->clear_gpio))
+ return PTR_ERR(fpga->clear_gpio);
+
+ fpga->strobe_gpio = devm_gpiod_get(fpga->dev, "strobe", GPIOD_OUT_LOW);
+ if (IS_ERR(fpga->strobe_gpio))
+ return PTR_ERR(fpga->strobe_gpio);
+
+ fpga->datain_gpio = devm_gpiod_get(fpga->dev, "datain", GPIOD_OUT_LOW);
+ if (IS_ERR(fpga->datain_gpio))
+ return PTR_ERR(fpga->datain_gpio);
+
+ fpga->dataout_gpio = devm_gpiod_get(fpga->dev, "dataout", GPIOD_IN);
+ if (IS_ERR(fpga->dataout_gpio))
+ return PTR_ERR(fpga->dataout_gpio);
+
+ gpiod_set_value(fpga->enable_gpio, 1);
+
+ return 0;
+}
+
+static int upboard_fpga_get_firmware_version(struct upboard_fpga *fpga)
+{
+ unsigned int platform_id, manufacturer_id;
+ int ret;
+
+ if (!fpga)
+ return -ENOMEM;
+
+ ret = regmap_read(fpga->regmap, UPBOARD_REG_PLATFORM_ID, &platform_id);
+ if (ret)
+ return ret;
+
+ manufacturer_id = platform_id & UPBOARD_MANUFACTURER_ID_MASK;
+ if (manufacturer_id != UPBOARD_AAEON_MANUFACTURER_ID)
+ return dev_err_probe(fpga->dev, -ENODEV,
+ "driver not compatible with custom FPGA FW from manufacturer id %#02x.",
+ manufacturer_id);
+
+ ret = regmap_read(fpga->regmap, UPBOARD_REG_FIRMWARE_ID, &fpga->firmware_version);
+ if (ret)
+ return ret;
+
+ if (FIELD_GET(UPBOARD_FW_ID_MAJOR_MASK, fpga->firmware_version) !=
+ UPBOARD_FW_ID_MAJOR_SUPPORTED)
+ return dev_err_probe(fpga->dev, -ENODEV,
+ "unsupported FPGA FW v%lu.%lu.%lu build %#02lx",
+ FIELD_GET(UPBOARD_FW_ID_MAJOR_MASK, fpga->firmware_version),
+ FIELD_GET(UPBOARD_FW_ID_MINOR_MASK, fpga->firmware_version),
+ FIELD_GET(UPBOARD_FW_ID_PATCH_MASK, fpga->firmware_version),
+ FIELD_GET(UPBOARD_FW_ID_BUILD_MASK, fpga->firmware_version));
+ return 0;
+}
+
+static ssize_t upboard_fpga_version_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct upboard_fpga *fpga = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "FPGA FW v%lu.%lu.%lu build %#02lx\n",
+ FIELD_GET(UPBOARD_FW_ID_MAJOR_MASK, fpga->firmware_version),
+ FIELD_GET(UPBOARD_FW_ID_MINOR_MASK, fpga->firmware_version),
+ FIELD_GET(UPBOARD_FW_ID_PATCH_MASK, fpga->firmware_version),
+ FIELD_GET(UPBOARD_FW_ID_BUILD_MASK, fpga->firmware_version));
+}
+
+static DEVICE_ATTR_RO(upboard_fpga_version);
+
+static struct attribute *upboard_fpga_attrs[] = {
+ &dev_attr_upboard_fpga_version.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(upboard_fpga);
+
+static int upboard_fpga_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct upboard_fpga *fpga;
+ int ret;
+
+ fpga = devm_kzalloc(dev, sizeof(*fpga), GFP_KERNEL);
+ if (!fpga)
+ return -ENOMEM;
+
+ fpga->fpga_data = device_get_match_data(dev);
+
+ fpga->dev = dev;
+
+ platform_set_drvdata(pdev, fpga);
+
+ fpga->regmap = devm_regmap_init(dev, NULL, fpga, fpga->fpga_data->regmap_config);
+ if (IS_ERR(fpga->regmap))
+ return PTR_ERR(fpga->regmap);
+
+ ret = upboard_fpga_gpio_init(fpga);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize FPGA common GPIOs");
+
+ ret = upboard_fpga_get_firmware_version(fpga);
+ if (ret)
+ return ret;
+
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, upboard_up_mfd_cells,
+ ARRAY_SIZE(upboard_up_mfd_cells), NULL, 0, NULL);
+}
+
+static const struct acpi_device_id upboard_fpga_acpi_match[] = {
+ { "AANT0F01", (kernel_ulong_t)&upboard_up2_fpga_data },
+ { "AANT0F04", (kernel_ulong_t)&upboard_up_fpga_data },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, upboard_fpga_acpi_match);
+
+static struct platform_driver upboard_fpga_driver = {
+ .driver = {
+ .name = "upboard-fpga",
+ .acpi_match_table = upboard_fpga_acpi_match,
+ .dev_groups = upboard_fpga_groups,
+ },
+ .probe = upboard_fpga_probe,
+};
+
+module_platform_driver(upboard_fpga_driver);
+
+MODULE_AUTHOR("Gary Wang <garywang@aaeon.com.tw>");
+MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
+MODULE_DESCRIPTION("UP Board FPGA driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c
index eab82619ec31..f49cee91f71c 100644
--- a/drivers/mfd/vexpress-sysreg.c
+++ b/drivers/mfd/vexpress-sysreg.c
@@ -5,13 +5,14 @@
*/
#include <linux/gpio/driver.h>
+#include <linux/gpio/generic.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of_platform.h>
-#include <linux/platform_data/syscon.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/stat.h>
@@ -38,22 +39,34 @@
/* The sysreg block is just a random collection of various functions... */
-static struct bgpio_pdata vexpress_sysreg_sys_led_pdata = {
- .label = "sys_led",
- .base = -1,
- .ngpio = 8,
+static const struct property_entry vexpress_sysreg_sys_led_props[] = {
+ PROPERTY_ENTRY_STRING("label", "sys_led"),
+ PROPERTY_ENTRY_U32("ngpios", 8),
+ { }
};
-static struct bgpio_pdata vexpress_sysreg_sys_mci_pdata = {
- .label = "sys_mci",
- .base = -1,
- .ngpio = 2,
+static const struct software_node vexpress_sysreg_sys_led_swnode = {
+ .properties = vexpress_sysreg_sys_led_props,
};
-static struct bgpio_pdata vexpress_sysreg_sys_flash_pdata = {
- .label = "sys_flash",
- .base = -1,
- .ngpio = 1,
+static const struct property_entry vexpress_sysreg_sys_mci_props[] = {
+ PROPERTY_ENTRY_STRING("label", "sys_mci"),
+ PROPERTY_ENTRY_U32("ngpios", 2),
+ { }
+};
+
+static const struct software_node vexpress_sysreg_sys_mci_swnode = {
+ .properties = vexpress_sysreg_sys_mci_props,
+};
+
+static const struct property_entry vexpress_sysreg_sys_flash_props[] = {
+ PROPERTY_ENTRY_STRING("label", "sys_flash"),
+ PROPERTY_ENTRY_U32("ngpios", 1),
+ { }
+};
+
+static const struct software_node vexpress_sysreg_sys_flash_swnode = {
+ .properties = vexpress_sysreg_sys_flash_props,
};
static struct mfd_cell vexpress_sysreg_cells[] = {
@@ -62,22 +75,19 @@ static struct mfd_cell vexpress_sysreg_cells[] = {
.of_compatible = "arm,vexpress-sysreg,sys_led",
.num_resources = 1,
.resources = &DEFINE_RES_MEM_NAMED(SYS_LED, 0x4, "dat"),
- .platform_data = &vexpress_sysreg_sys_led_pdata,
- .pdata_size = sizeof(vexpress_sysreg_sys_led_pdata),
+ .swnode = &vexpress_sysreg_sys_led_swnode,
}, {
.name = "basic-mmio-gpio",
.of_compatible = "arm,vexpress-sysreg,sys_mci",
.num_resources = 1,
.resources = &DEFINE_RES_MEM_NAMED(SYS_MCI, 0x4, "dat"),
- .platform_data = &vexpress_sysreg_sys_mci_pdata,
- .pdata_size = sizeof(vexpress_sysreg_sys_mci_pdata),
+ .swnode = &vexpress_sysreg_sys_mci_swnode,
}, {
.name = "basic-mmio-gpio",
.of_compatible = "arm,vexpress-sysreg,sys_flash",
.num_resources = 1,
.resources = &DEFINE_RES_MEM_NAMED(SYS_FLASH, 0x4, "dat"),
- .platform_data = &vexpress_sysreg_sys_flash_pdata,
- .pdata_size = sizeof(vexpress_sysreg_sys_flash_pdata),
+ .swnode = &vexpress_sysreg_sys_flash_swnode,
}, {
.name = "vexpress-syscfg",
.num_resources = 1,
@@ -87,9 +97,11 @@ static struct mfd_cell vexpress_sysreg_cells[] = {
static int vexpress_sysreg_probe(struct platform_device *pdev)
{
+ struct gpio_generic_chip *mmc_gpio_chip;
+ struct gpio_generic_chip_config config;
struct resource *mem;
void __iomem *base;
- struct gpio_chip *mmc_gpio_chip;
+ int ret;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem)
@@ -107,10 +119,22 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!mmc_gpio_chip)
return -ENOMEM;
- bgpio_init(mmc_gpio_chip, &pdev->dev, 0x4, base + SYS_MCI,
- NULL, NULL, NULL, NULL, 0);
- mmc_gpio_chip->ngpio = 2;
- devm_gpiochip_add_data(&pdev->dev, mmc_gpio_chip, NULL);
+
+ config = (struct gpio_generic_chip_config) {
+ .dev = &pdev->dev,
+ .sz = 4,
+ .dat = base + SYS_MCI,
+ };
+
+ ret = gpio_generic_chip_init(mmc_gpio_chip, &config);
+ if (ret)
+ return ret;
+
+ mmc_gpio_chip->gc.ngpio = 2;
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &mmc_gpio_chip->gc, NULL);
+ if (ret)
+ return ret;
return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
vexpress_sysreg_cells,
@@ -132,4 +156,5 @@ static struct platform_driver vexpress_sysreg_driver = {
};
module_platform_driver(vexpress_sysreg_driver);
+MODULE_DESCRIPTION("Versatile Express system registers driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/wcd934x.c b/drivers/mfd/wcd934x.c
index 6b942d5270c1..3c3080e8c8cf 100644
--- a/drivers/mfd/wcd934x.c
+++ b/drivers/mfd/wcd934x.c
@@ -109,10 +109,10 @@ static const struct regmap_range_cfg wcd934x_ranges[] = {
},
};
-static struct regmap_config wcd934x_regmap_config = {
+static const struct regmap_config wcd934x_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = 0xffff,
.can_multi_write = true,
.ranges = wcd934x_ranges,
@@ -284,6 +284,7 @@ static const struct slim_device_id wcd934x_slim_id[] = {
SLIM_DEV_IDX_WCD9340, SLIM_DEV_INSTANCE_ID_WCD9340 },
{}
};
+MODULE_DEVICE_TABLE(slim, wcd934x_slim_id);
static struct slim_driver wcd934x_slim_driver = {
.driver = {
@@ -298,5 +299,4 @@ static struct slim_driver wcd934x_slim_driver = {
module_slim_driver(wcd934x_slim_driver);
MODULE_DESCRIPTION("WCD934X slim driver");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("slim:217:250:*");
MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>");
diff --git a/drivers/mfd/wl1273-core.c b/drivers/mfd/wl1273-core.c
deleted file mode 100644
index e2a7fccaed01..000000000000
--- a/drivers/mfd/wl1273-core.c
+++ /dev/null
@@ -1,262 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * MFD driver for wl1273 FM radio and audio codec submodules.
- *
- * Copyright (C) 2011 Nokia Corporation
- * Author: Matti Aaltonen <matti.j.aaltonen@nokia.com>
- */
-
-#include <linux/mfd/wl1273-core.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-
-#define DRIVER_DESC "WL1273 FM Radio Core"
-
-static const struct i2c_device_id wl1273_driver_id_table[] = {
- { WL1273_FM_DRIVER_NAME, 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wl1273_driver_id_table);
-
-static int wl1273_fm_read_reg(struct wl1273_core *core, u8 reg, u16 *value)
-{
- struct i2c_client *client = core->client;
- u8 b[2];
- int r;
-
- r = i2c_smbus_read_i2c_block_data(client, reg, sizeof(b), b);
- if (r != 2) {
- dev_err(&client->dev, "%s: Read: %d fails.\n", __func__, reg);
- return -EREMOTEIO;
- }
-
- *value = (u16)b[0] << 8 | b[1];
-
- return 0;
-}
-
-static int wl1273_fm_write_cmd(struct wl1273_core *core, u8 cmd, u16 param)
-{
- struct i2c_client *client = core->client;
- u8 buf[] = { (param >> 8) & 0xff, param & 0xff };
- int r;
-
- r = i2c_smbus_write_i2c_block_data(client, cmd, sizeof(buf), buf);
- if (r) {
- dev_err(&client->dev, "%s: Cmd: %d fails.\n", __func__, cmd);
- return r;
- }
-
- return 0;
-}
-
-static int wl1273_fm_write_data(struct wl1273_core *core, u8 *data, u16 len)
-{
- struct i2c_client *client = core->client;
- struct i2c_msg msg;
- int r;
-
- msg.addr = client->addr;
- msg.flags = 0;
- msg.buf = data;
- msg.len = len;
-
- r = i2c_transfer(client->adapter, &msg, 1);
- if (r != 1) {
- dev_err(&client->dev, "%s: write error.\n", __func__);
- return -EREMOTEIO;
- }
-
- return 0;
-}
-
-/**
- * wl1273_fm_set_audio() - Set audio mode.
- * @core: A pointer to the device struct.
- * @new_mode: The new audio mode.
- *
- * Audio modes are WL1273_AUDIO_DIGITAL and WL1273_AUDIO_ANALOG.
- */
-static int wl1273_fm_set_audio(struct wl1273_core *core, unsigned int new_mode)
-{
- int r = 0;
-
- if (core->mode == WL1273_MODE_OFF ||
- core->mode == WL1273_MODE_SUSPENDED)
- return -EPERM;
-
- if (core->mode == WL1273_MODE_RX && new_mode == WL1273_AUDIO_DIGITAL) {
- r = wl1273_fm_write_cmd(core, WL1273_PCM_MODE_SET,
- WL1273_PCM_DEF_MODE);
- if (r)
- goto out;
-
- r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,
- core->i2s_mode);
- if (r)
- goto out;
-
- r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,
- WL1273_AUDIO_ENABLE_I2S);
- if (r)
- goto out;
-
- } else if (core->mode == WL1273_MODE_RX &&
- new_mode == WL1273_AUDIO_ANALOG) {
- r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,
- WL1273_AUDIO_ENABLE_ANALOG);
- if (r)
- goto out;
-
- } else if (core->mode == WL1273_MODE_TX &&
- new_mode == WL1273_AUDIO_DIGITAL) {
- r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,
- core->i2s_mode);
- if (r)
- goto out;
-
- r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,
- WL1273_AUDIO_IO_SET_I2S);
- if (r)
- goto out;
-
- } else if (core->mode == WL1273_MODE_TX &&
- new_mode == WL1273_AUDIO_ANALOG) {
- r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,
- WL1273_AUDIO_IO_SET_ANALOG);
- if (r)
- goto out;
- }
-
- core->audio_mode = new_mode;
-out:
- return r;
-}
-
-/**
- * wl1273_fm_set_volume() - Set volume.
- * @core: A pointer to the device struct.
- * @volume: The new volume value.
- */
-static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume)
-{
- int r;
-
- if (volume > WL1273_MAX_VOLUME)
- return -EINVAL;
-
- if (core->volume == volume)
- return 0;
-
- r = wl1273_fm_write_cmd(core, WL1273_VOLUME_SET, volume);
- if (r)
- return r;
-
- core->volume = volume;
- return 0;
-}
-
-static int wl1273_core_probe(struct i2c_client *client)
-{
- struct wl1273_fm_platform_data *pdata = dev_get_platdata(&client->dev);
- struct wl1273_core *core;
- struct mfd_cell *cell;
- int children = 0;
- int r = 0;
-
- dev_dbg(&client->dev, "%s\n", __func__);
-
- if (!pdata) {
- dev_err(&client->dev, "No platform data.\n");
- return -EINVAL;
- }
-
- if (!(pdata->children & WL1273_RADIO_CHILD)) {
- dev_err(&client->dev, "Cannot function without radio child.\n");
- return -EINVAL;
- }
-
- core = devm_kzalloc(&client->dev, sizeof(*core), GFP_KERNEL);
- if (!core)
- return -ENOMEM;
-
- core->pdata = pdata;
- core->client = client;
- mutex_init(&core->lock);
-
- i2c_set_clientdata(client, core);
-
- dev_dbg(&client->dev, "%s: Have V4L2.\n", __func__);
-
- cell = &core->cells[children];
- cell->name = "wl1273_fm_radio";
- cell->platform_data = &core;
- cell->pdata_size = sizeof(core);
- children++;
-
- core->read = wl1273_fm_read_reg;
- core->write = wl1273_fm_write_cmd;
- core->write_data = wl1273_fm_write_data;
- core->set_audio = wl1273_fm_set_audio;
- core->set_volume = wl1273_fm_set_volume;
-
- if (pdata->children & WL1273_CODEC_CHILD) {
- cell = &core->cells[children];
-
- dev_dbg(&client->dev, "%s: Have codec.\n", __func__);
- cell->name = "wl1273-codec";
- cell->platform_data = &core;
- cell->pdata_size = sizeof(core);
- children++;
- }
-
- dev_dbg(&client->dev, "%s: number of children: %d.\n",
- __func__, children);
-
- r = devm_mfd_add_devices(&client->dev, -1, core->cells,
- children, NULL, 0, NULL);
- if (r)
- goto err;
-
- return 0;
-
-err:
- pdata->free_resources();
-
- dev_dbg(&client->dev, "%s\n", __func__);
-
- return r;
-}
-
-static struct i2c_driver wl1273_core_driver = {
- .driver = {
- .name = WL1273_FM_DRIVER_NAME,
- },
- .probe = wl1273_core_probe,
- .id_table = wl1273_driver_id_table,
-};
-
-static int __init wl1273_core_init(void)
-{
- int r;
-
- r = i2c_add_driver(&wl1273_core_driver);
- if (r) {
- pr_err(WL1273_FM_DRIVER_NAME
- ": driver registration failed\n");
- return r;
- }
-
- return r;
-}
-
-static void __exit wl1273_core_exit(void)
-{
- i2c_del_driver(&wl1273_core_driver);
-}
-late_initcall(wl1273_core_init);
-module_exit(wl1273_core_exit);
-
-MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>");
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c
index 6bba39657991..6a8602c1c4ee 100644
--- a/drivers/mfd/wm5102-tables.c
+++ b/drivers/mfd/wm5102-tables.c
@@ -1922,7 +1922,7 @@ const struct regmap_config wm5102_spi_regmap = {
.readable_reg = wm5102_readable_register,
.volatile_reg = wm5102_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm5102_reg_default,
.num_reg_defaults = ARRAY_SIZE(wm5102_reg_default),
};
@@ -1938,7 +1938,7 @@ const struct regmap_config wm5102_i2c_regmap = {
.readable_reg = wm5102_readable_register,
.volatile_reg = wm5102_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm5102_reg_default,
.num_reg_defaults = ARRAY_SIZE(wm5102_reg_default),
};
diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c
index 65b9b1d6daec..6ff33a54a068 100644
--- a/drivers/mfd/wm5110-tables.c
+++ b/drivers/mfd/wm5110-tables.c
@@ -3202,7 +3202,7 @@ const struct regmap_config wm5110_spi_regmap = {
.readable_reg = wm5110_readable_register,
.volatile_reg = wm5110_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm5110_reg_default,
.num_reg_defaults = ARRAY_SIZE(wm5110_reg_default),
};
@@ -3218,7 +3218,7 @@ const struct regmap_config wm5110_i2c_regmap = {
.readable_reg = wm5110_readable_register,
.volatile_reg = wm5110_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm5110_reg_default,
.num_reg_defaults = ARRAY_SIZE(wm5110_reg_default),
};
diff --git a/drivers/mfd/wm831x-auxadc.c b/drivers/mfd/wm831x-auxadc.c
index 65b98f3fbd92..18618a8f9206 100644
--- a/drivers/mfd/wm831x-auxadc.c
+++ b/drivers/mfd/wm831x-auxadc.c
@@ -152,7 +152,7 @@ static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
static int wm831x_auxadc_read_polled(struct wm831x *wm831x,
enum wm831x_auxadc input)
{
- int ret, src, timeout;
+ int ret, src;
mutex_lock(&wm831x->auxadc_lock);
@@ -179,32 +179,25 @@ static int wm831x_auxadc_read_polled(struct wm831x *wm831x,
goto disable;
}
- /* If we're not using interrupts then poll the
- * interrupt status register */
- timeout = 5;
- while (timeout) {
- msleep(1);
+ /* If we're not using interrupts then read the interrupt status register */
+ msleep(20);
- ret = wm831x_reg_read(wm831x,
- WM831X_INTERRUPT_STATUS_1);
- if (ret < 0) {
- dev_err(wm831x->dev,
- "ISR 1 read failed: %d\n", ret);
- goto disable;
- }
+ ret = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1);
+ if (ret < 0) {
+ dev_err(wm831x->dev,
+ "ISR 1 read failed: %d\n", ret);
+ goto disable;
+ }
- /* Did it complete? */
- if (ret & WM831X_AUXADC_DATA_EINT) {
- wm831x_reg_write(wm831x,
- WM831X_INTERRUPT_STATUS_1,
- WM831X_AUXADC_DATA_EINT);
- break;
- } else {
- dev_err(wm831x->dev,
- "AUXADC conversion timeout\n");
- ret = -EBUSY;
- goto disable;
- }
+ /* Did it complete? */
+ if (ret & WM831X_AUXADC_DATA_EINT) {
+ wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1,
+ WM831X_AUXADC_DATA_EINT);
+ } else {
+ dev_err(wm831x->dev,
+ "AUXADC conversion timeout\n");
+ ret = -EBUSY;
+ goto disable;
}
ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index e86b6a4896a6..e7e68929275e 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -15,8 +15,7 @@
#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/err.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c
index 997837f13180..9bee007f9c99 100644
--- a/drivers/mfd/wm831x-i2c.c
+++ b/drivers/mfd/wm831x-i2c.c
@@ -15,7 +15,6 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/mfd/wm831x/core.h>
@@ -23,22 +22,15 @@
static int wm831x_i2c_probe(struct i2c_client *i2c)
{
- const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
struct wm831x_pdata *pdata = dev_get_platdata(&i2c->dev);
- const struct of_device_id *of_id;
struct wm831x *wm831x;
enum wm831x_parent type;
int ret;
- if (i2c->dev.of_node) {
- of_id = of_match_device(wm831x_of_match, &i2c->dev);
- if (!of_id) {
- dev_err(&i2c->dev, "Failed to match device\n");
- return -ENODEV;
- }
- type = (enum wm831x_parent)of_id->data;
- } else {
- type = (enum wm831x_parent)id->driver_data;
+ type = (uintptr_t)i2c_get_match_data(i2c);
+ if (!type) {
+ dev_err(&i2c->dev, "Failed to match device\n");
+ return -ENODEV;
}
wm831x = devm_kzalloc(&i2c->dev, sizeof(struct wm831x), GFP_KERNEL);
diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c
index f1f58e3149ae..defd5f173eb6 100644
--- a/drivers/mfd/wm831x-irq.c
+++ b/drivers/mfd/wm831x-irq.c
@@ -587,16 +587,11 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
}
if (irq_base)
- domain = irq_domain_add_legacy(wm831x->dev->of_node,
- ARRAY_SIZE(wm831x_irqs),
- irq_base, 0,
- &wm831x_irq_domain_ops,
- wm831x);
+ domain = irq_domain_create_legacy(dev_fwnode(wm831x->dev), ARRAY_SIZE(wm831x_irqs),
+ irq_base, 0, &wm831x_irq_domain_ops, wm831x);
else
- domain = irq_domain_add_linear(wm831x->dev->of_node,
- ARRAY_SIZE(wm831x_irqs),
- &wm831x_irq_domain_ops,
- wm831x);
+ domain = irq_domain_create_linear(dev_fwnode(wm831x->dev), ARRAY_SIZE(wm831x_irqs),
+ &wm831x_irq_domain_ops, wm831x);
if (!domain) {
dev_warn(wm831x->dev, "Failed to allocate IRQ domain\n");
diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c
index 7bcddccbf155..54c87267917b 100644
--- a/drivers/mfd/wm831x-spi.c
+++ b/drivers/mfd/wm831x-spi.c
@@ -10,7 +10,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/pm.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
@@ -21,21 +20,14 @@
static int wm831x_spi_probe(struct spi_device *spi)
{
struct wm831x_pdata *pdata = dev_get_platdata(&spi->dev);
- const struct spi_device_id *id = spi_get_device_id(spi);
- const struct of_device_id *of_id;
struct wm831x *wm831x;
enum wm831x_parent type;
int ret;
- if (spi->dev.of_node) {
- of_id = of_match_device(wm831x_of_match, &spi->dev);
- if (!of_id) {
- dev_err(&spi->dev, "Failed to match device\n");
- return -ENODEV;
- }
- type = (enum wm831x_parent)of_id->data;
- } else {
- type = (enum wm831x_parent)id->driver_data;
+ type = (uintptr_t)spi_get_device_match_data(spi);
+ if (!type) {
+ dev_err(&spi->dev, "Failed to match device\n");
+ return -ENODEV;
}
wm831x = devm_kzalloc(&spi->dev, sizeof(struct wm831x), GFP_KERNEL);
diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c
index c2a7d7069975..767c176b12a7 100644
--- a/drivers/mfd/wm8350-i2c.c
+++ b/drivers/mfd/wm8350-i2c.c
@@ -41,9 +41,9 @@ static int wm8350_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8350_i2c_id[] = {
- { "wm8350", 0 },
- { "wm8351", 0 },
- { "wm8352", 0 },
+ { "wm8350" },
+ { "wm8351" },
+ { "wm8352" },
{ }
};
diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c
index 5663b8b0b3ad..3d0ebb004dbf 100644
--- a/drivers/mfd/wm8350-regmap.c
+++ b/drivers/mfd/wm8350-regmap.c
@@ -325,7 +325,7 @@ const struct regmap_config wm8350_regmap = {
.reg_bits = 8,
.val_bits = 16,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = WM8350_MAX_REGISTER,
.readable_reg = wm8350_readable,
diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c
index 75483c9be0c4..8ecfe878a5ba 100644
--- a/drivers/mfd/wm8400-core.c
+++ b/drivers/mfd/wm8400-core.c
@@ -100,7 +100,7 @@ static const struct regmap_config wm8400_regmap_config = {
.volatile_reg = wm8400_volatile,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
/**
@@ -135,7 +135,7 @@ static int wm8400_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8400_i2c_id[] = {
- { "wm8400", 0 },
+ { "wm8400" },
{ }
};
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index 1e4f1694f065..094c0b3dbd97 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -15,7 +15,6 @@
#include <linux/delay.h>
#include <linux/mfd/core.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -612,8 +611,6 @@ MODULE_DEVICE_TABLE(of, wm8994_of_match);
static int wm8994_i2c_probe(struct i2c_client *i2c)
{
- const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
- const struct of_device_id *of_id;
struct wm8994 *wm8994;
int ret;
@@ -625,13 +622,7 @@ static int wm8994_i2c_probe(struct i2c_client *i2c)
wm8994->dev = &i2c->dev;
wm8994->irq = i2c->irq;
- if (i2c->dev.of_node) {
- of_id = of_match_device(wm8994_of_match, &i2c->dev);
- if (of_id)
- wm8994->type = (enum wm8994_type)of_id->data;
- } else {
- wm8994->type = id->driver_data;
- }
+ wm8994->type = (kernel_ulong_t)i2c_get_match_data(i2c);
wm8994->regmap = devm_regmap_init_i2c(i2c, &wm8994_base_regmap_config);
if (IS_ERR(wm8994->regmap)) {
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c
index 651a028bc519..1475b1ac6983 100644
--- a/drivers/mfd/wm8994-irq.c
+++ b/drivers/mfd/wm8994-irq.c
@@ -213,9 +213,7 @@ int wm8994_irq_init(struct wm8994 *wm8994)
return ret;
}
- wm8994->edge_irq = irq_domain_add_linear(NULL, 1,
- &wm8994_edge_irq_ops,
- wm8994);
+ wm8994->edge_irq = irq_domain_create_linear(NULL, 1, &wm8994_edge_irq_ops, wm8994);
ret = regmap_add_irq_chip(wm8994->regmap,
irq_create_mapping(wm8994->edge_irq,
diff --git a/drivers/mfd/wm8994-regmap.c b/drivers/mfd/wm8994-regmap.c
index cd4fef7df336..ee2ed6773afd 100644
--- a/drivers/mfd/wm8994-regmap.c
+++ b/drivers/mfd/wm8994-regmap.c
@@ -1238,7 +1238,7 @@ struct regmap_config wm1811_regmap_config = {
.reg_bits = 16,
.val_bits = 16,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm1811_defaults,
.num_reg_defaults = ARRAY_SIZE(wm1811_defaults),
@@ -1253,7 +1253,7 @@ struct regmap_config wm8994_regmap_config = {
.reg_bits = 16,
.val_bits = 16,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8994_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8994_defaults),
@@ -1268,7 +1268,7 @@ struct regmap_config wm8958_regmap_config = {
.reg_bits = 16,
.val_bits = 16,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8958_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8958_defaults),
diff --git a/drivers/mfd/wm8997-tables.c b/drivers/mfd/wm8997-tables.c
index 3476787c485e..288c57b2d21e 100644
--- a/drivers/mfd/wm8997-tables.c
+++ b/drivers/mfd/wm8997-tables.c
@@ -1523,7 +1523,7 @@ const struct regmap_config wm8997_i2c_regmap = {
.readable_reg = wm8997_readable_register,
.volatile_reg = wm8997_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8997_reg_default,
.num_reg_defaults = ARRAY_SIZE(wm8997_reg_default),
};
diff --git a/drivers/mfd/wm8998-tables.c b/drivers/mfd/wm8998-tables.c
index 9b34a6d76094..b3e6e85bee89 100644
--- a/drivers/mfd/wm8998-tables.c
+++ b/drivers/mfd/wm8998-tables.c
@@ -1556,7 +1556,7 @@ const struct regmap_config wm8998_i2c_regmap = {
.readable_reg = wm8998_readable_register,
.volatile_reg = wm8998_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8998_reg_default,
.num_reg_defaults = ARRAY_SIZE(wm8998_reg_default),
};
diff --git a/drivers/mfd/wm97xx-core.c b/drivers/mfd/wm97xx-core.c
index 663acbb1854c..1566a9b04b6a 100644
--- a/drivers/mfd/wm97xx-core.c
+++ b/drivers/mfd/wm97xx-core.c
@@ -95,7 +95,7 @@ static const struct regmap_config wm9705_regmap_config = {
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm9705_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm9705_reg_defaults),
@@ -163,7 +163,7 @@ static const struct regmap_config wm9712_regmap_config = {
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm9712_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm9712_reg_defaults),
@@ -234,7 +234,7 @@ static const struct regmap_config wm9713_regmap_config = {
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm9713_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults),