diff options
Diffstat (limited to 'drivers/mfd')
276 files changed, 21550 insertions, 15027 deletions
diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c index eaf9845633b4..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,16 +391,15 @@ 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), .num_regs = 4, .status_base = PM800_INT_STATUS1, - .mask_base = PM800_INT_ENA_1, + .unmask_base = PM800_INT_ENA_1, .ack_base = PM800_INT_STATUS1, - .mask_invert = 1, }; static int pm800_pages_init(struct pm80x_chip *chip) @@ -528,8 +527,7 @@ out: return ret; } -static int pm800_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pm800_probe(struct i2c_client *client) { int ret = 0; struct pm80x_chip *chip; @@ -583,7 +581,7 @@ out_init: return ret; } -static int pm800_remove(struct i2c_client *client) +static void pm800_remove(struct i2c_client *client) { struct pm80x_chip *chip = i2c_get_clientdata(client); @@ -592,14 +590,12 @@ static int pm800_remove(struct i2c_client *client) pm800_pages_exit(chip); pm80x_deinit(); - - return 0; } static struct i2c_driver pm800_driver = { .driver = { .name = "88PM800", - .pm = &pm80x_pm_ops, + .pm = pm_sleep_ptr(&pm80x_pm_ops), }, .probe = pm800_probe, .remove = pm800_remove, diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c index ada6c513302b..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), @@ -209,8 +209,7 @@ out_irq_init: return ret; } -static int pm805_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pm805_probe(struct i2c_client *client) { int ret = 0; struct pm80x_chip *chip; @@ -239,7 +238,7 @@ out_init: return ret; } -static int pm805_remove(struct i2c_client *client) +static void pm805_remove(struct i2c_client *client) { struct pm80x_chip *chip = i2c_get_clientdata(client); @@ -247,14 +246,12 @@ static int pm805_remove(struct i2c_client *client) device_irq_exit_805(chip); pm80x_deinit(); - - return 0; } static struct i2c_driver pm805_driver = { .driver = { .name = "88PM805", - .pm = &pm80x_pm_ops, + .pm = pm_sleep_ptr(&pm80x_pm_ops), }, .probe = pm805_probe, .remove = pm805_remove, diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c index be036e7e787b..bbc1a87f0c8f 100644 --- a/drivers/mfd/88pm80x.c +++ b/drivers/mfd/88pm80x.c @@ -74,7 +74,6 @@ int pm80x_init(struct i2c_client *client) chip->irq = client->irq; chip->dev = &client->dev; - dev_set_drvdata(chip->dev, chip); i2c_set_clientdata(chip->client, chip); ret = regmap_read(chip->regmap, PM80X_CHIP_ID, &val); @@ -129,7 +128,6 @@ int pm80x_deinit(void) } EXPORT_SYMBOL_GPL(pm80x_deinit); -#ifdef CONFIG_PM_SLEEP static int pm80x_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -153,10 +151,8 @@ static int pm80x_resume(struct device *dev) return 0; } -#endif -SIMPLE_DEV_PM_OPS(pm80x_pm_ops, pm80x_suspend, pm80x_resume); -EXPORT_SYMBOL_GPL(pm80x_pm_ops); +EXPORT_GPL_SIMPLE_DEV_PM_OPS(pm80x_pm_ops, pm80x_suspend, pm80x_resume); MODULE_DESCRIPTION("I2C Driver for Marvell 88PM80x"); MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>"); diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index b1e829ea909b..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); @@ -1117,8 +1116,7 @@ static int pm860x_dt_init(struct device_node *np, { int ret; - if (of_get_property(np, "marvell,88pm860x-irq-read-clr", NULL)) - pdata->irq_mode = 1; + pdata->irq_mode = of_property_read_bool(np, "marvell,88pm860x-irq-read-clr"); ret = of_property_read_u32(np, "marvell,88pm860x-slave-addr", &pdata->companion_addr); if (ret) { @@ -1167,7 +1165,6 @@ static int pm860x_probe(struct i2c_client *client) chip->client = client; i2c_set_clientdata(client, chip); chip->dev = &client->dev; - dev_set_drvdata(chip->dev, chip); /* * Both client and companion client shares same platform driver. @@ -1201,7 +1198,7 @@ static int pm860x_probe(struct i2c_client *client) return 0; } -static int pm860x_remove(struct i2c_client *client) +static void pm860x_remove(struct i2c_client *client) { struct pm860x_chip *chip = i2c_get_clientdata(client); @@ -1210,10 +1207,8 @@ static int pm860x_remove(struct i2c_client *client) regmap_exit(chip->regmap_companion); i2c_unregister_device(chip->companion); } - return 0; } -#ifdef CONFIG_PM_SLEEP static int pm860x_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -1233,12 +1228,11 @@ static int pm860x_resume(struct device *dev) disable_irq_wake(chip->core_irq); return 0; } -#endif -static SIMPLE_DEV_PM_OPS(pm860x_pm_ops, pm860x_suspend, pm860x_resume); +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); @@ -1252,10 +1246,10 @@ MODULE_DEVICE_TABLE(of, pm860x_dt_ids); static struct i2c_driver pm860x_driver = { .driver = { .name = "88PM860x", - .pm = &pm860x_pm_ops, + .pm = pm_sleep_ptr(&pm860x_pm_ops), .of_match_table = pm860x_dt_ids, }, - .probe_new = pm860x_probe, + .probe = pm860x_probe, .remove = pm860x_remove, .id_table = pm860x_id_table, }; @@ -1279,4 +1273,3 @@ module_exit(pm860x_i2c_exit); MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x"); MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); -MODULE_LICENSE("GPL"); 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 6a3fd2d75f96..aace5766b38a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -15,10 +15,23 @@ config MFD_CS5535 tristate "AMD CS5535 and CS5536 southbridge core functions" select MFD_CORE depends on PCI && (X86_32 || (X86 && COMPILE_TEST)) + depends on !UML help 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 @@ -32,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 @@ -77,6 +91,18 @@ config MFD_AS3711 help Support for the AS3711 PMIC from AMS +config MFD_SMPRO + tristate "Ampere Computing SMpro core driver" + depends on I2C + select MFD_CORE + select REGMAP_I2C + help + Say yes here to enable SMpro driver support for Ampere's Altra + processor family. + + Ampere's Altra SMpro exposes an I2C regmap interface that can + be accessed by child devices. + config MFD_AS3722 tristate "ams AS3722 Power Management IC" select MFD_CORE @@ -93,7 +119,7 @@ config PMIC_ADP5520 bool "Analog Devices ADP5520/01 MFD PMIC Core Support" depends on I2C=y help - Say yes here to add support for Analog Devices AD5520 and ADP5501, + Say yes here to add support for Analog Devices ADP5520 and ADP5501, Multifunction Power Management IC. This includes the I2C driver and the core APIs _only_, you have to select individual components like LCD backlight, LEDs, GPIOs and Kepad @@ -104,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, @@ -113,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 @@ -211,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 @@ -224,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 @@ -288,15 +407,6 @@ config MFD_CS47L92 help Support for Cirrus Logic CS42L92, CS47L92 and CS47L93 Smart Codecs -config MFD_ASIC3 - bool "Compaq ASIC3" - depends on GPIOLIB - depends on ARM || COMPILE_TEST - select MFD_CORE - help - This driver supports the ASIC3 multifunction chip found on many - PDAs (mainly iPAQ and HTC based ones) - config PMIC_DA903X bool "Dialog Semiconductor DA9030/DA9034 PMIC Support" depends on I2C=y @@ -349,9 +459,6 @@ config MFD_DA9055 Additional drivers must be enabled in order to use the functionality of the device. - This driver can be built as a module. If built as a module it will be - called "da9055" - config MFD_DA9062 tristate "Dialog Semiconductor DA9062/61 PMIC Support" select MFD_CORE @@ -417,11 +524,13 @@ config MFD_EXYNOS_LPASS select REGMAP_MMIO help Select this option to enable support for Samsung Exynos Low Power - Audio Subsystem. + Audio Subsystem present on some of Samsung Exynos + SoCs (e.g. Exynos5433). + Choose Y here only if you build for such Samsung SoC. 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 @@ -436,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 @@ -497,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 @@ -510,6 +635,22 @@ config MFD_HI6421_PMIC menus in order to enable them. We communicate with the Hi6421 via memory-mapped I/O. +config MFD_HI6421_SPMI + tristate "HiSilicon Hi6421v600 SPMI PMU/Codec IC" + depends on OF + depends on SPMI + select MFD_CORE + select REGMAP_SPMI + help + Add support for HiSilicon Hi6421v600 SPMI PMIC. Hi6421 includes + multi-functions, such as regulators, RTC, codec, Coulomb counter, + etc. + + This driver includes core APIs _only_. You have to select + individual components like voltage regulators under corresponding + menus in order to enable them. + We communicate with the Hi6421v600 via a SPMI bus. + config MFD_HI655X_PMIC tristate "HiSilicon Hi655X series PMU/Codec IC" depends on ARCH_HISI || COMPILE_TEST @@ -520,24 +661,6 @@ config MFD_HI655X_PMIC help Select this option to enable Hisilicon hi655x series pmic driver. -config HTC_PASIC3 - tristate "HTC PASIC3 LED/DS1WM chip support" - select MFD_CORE - help - This core driver provides register access for the LED/DS1WM - chips labeled "AIC2" and "AIC3", found on HTC Blueangel and - HTC Magician devices, respectively. Actual functionality is - handled by the leds-pasic3 and ds1wm drivers. - -config HTC_I2CPLD - bool "HTC I2C PLD chip support" - depends on I2C=y && GPIOLIB - help - If you say yes here you get support for the supposed CPLD - found on omap850 HTC devices like the HTC Wizard and HTC Herald. - This device provides input and output GPIOs through an I2C - interface to one or more sub-chips. - config MFD_INTEL_QUARK_I2C_GPIO tristate "Intel Quark MFD I2C GPIO" depends on PCI @@ -554,6 +677,7 @@ config LPC_ICH tristate "Intel ICH LPC" depends on PCI select MFD_CORE + select P2SB if X86 help The LPC bridge function of the Intel ICH provides support for many functional units. This driver provides needed support for @@ -570,9 +694,9 @@ config LPC_SCH config INTEL_SOC_PMIC bool "Support for Crystal Cove PMIC" - depends on ACPI && HAS_IOMEM && I2C=y && GPIOLIB && COMMON_CLK - depends on X86 || COMPILE_TEST - depends on I2C_DESIGNWARE_PLATFORM=y + depends on HAS_IOMEM && I2C=y && GPIOLIB && COMMON_CLK + depends on (X86 && ACPI) || COMPILE_TEST + depends on I2C_DESIGNWARE_PLATFORM=y || COMPILE_TEST select MFD_CORE select REGMAP_I2C select REGMAP_IRQ @@ -616,7 +740,7 @@ config INTEL_SOC_PMIC_CHTWC config INTEL_SOC_PMIC_CHTDC_TI tristate "Support for Intel Cherry Trail Dollar Cove TI PMIC" depends on GPIOLIB - depends on I2C + depends on I2C=y && I2C_DESIGNWARE_PLATFORM=y depends on ACPI depends on X86 select MFD_CORE @@ -626,6 +750,10 @@ config INTEL_SOC_PMIC_CHTDC_TI Select this option for supporting Dollar Cove (TI version) PMIC device that is found on some Intel Cherry Trail systems. + This option is a bool as it provides an ACPI OpRegion which must be + available before any devices using it are probed. This option also + needs the designware-i2c driver to be builtin for the same reason. + config INTEL_SOC_PMIC_MRFLD tristate "Support for Intel Merrifield Basin Cove PMIC" depends on GPIOLIB @@ -674,19 +802,9 @@ config MFD_INTEL_PMC_BXT Register and P-unit access. In addition this creates devices for iTCO watchdog and telemetry that are part of the PMC. -config MFD_INTEL_PMT - tristate "Intel Platform Monitoring Technology (PMT) support" - depends on PCI - select MFD_CORE - help - The Intel Platform Monitoring Technology (PMT) is an interface that - provides access to hardware monitor registers. This driver supports - Telemetry, Watcher, and Crashlog PMT capabilities/devices for - platforms starting from Tiger Lake. - config MFD_IPAQ_MICRO bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support" - depends on SA1100_H3100 || SA1100_H3600 + depends on SA1100_H3600 select MFD_CORE help Select this to get support for the Microcontroller found in @@ -764,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 @@ -778,10 +918,23 @@ config MFD_MAX14577 additional drivers must be enabled in order to use the functionality of the device. +config MFD_MAX77541 + tristate "Analog Devices MAX77541/77540 PMIC Support" + depends on I2C=y + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + Say yes here to add support for Analog Devices MAX77541 and + MAX77540 Power Management ICs. This driver provides + common support for accessing the device; additional drivers + must be enabled in order to use the functionality of the device. + 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 || COMPILE_TEST + depends on OF select MFD_CORE select REGMAP_I2C select REGMAP_IRQ @@ -796,7 +949,7 @@ config MFD_MAX77620 config MFD_MAX77650 tristate "Maxim MAX77650/77651 PMIC Support" depends on I2C - depends on OF || COMPILE_TEST + depends on OF select MFD_CORE select REGMAP_I2C select REGMAP_IRQ @@ -811,7 +964,7 @@ config MFD_MAX77650 config MFD_MAX77686 tristate "Maxim Semiconductor MAX77686/802 PMIC Support" depends on I2C - depends on OF || COMPILE_TEST + depends on OF select MFD_CORE select REGMAP_I2C select REGMAP_IRQ @@ -837,6 +990,53 @@ 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 + depends on OF + select MFD_CORE + select REGMAP_I2C + help + Say yes here to add support for Maxim Semiconductor MAX77714. + This is a Power Management IC with 4 buck regulators, 9 + low-dropout regulators, 8 GPIOs, RTC, watchdog etc. This driver + provides common support for accessing the device; additional + 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 @@ -911,6 +1111,22 @@ config MFD_MT6360 PMIC part includes 2-channel BUCKs and 2-channel LDOs LDO part includes 4-channel LDOs +config MFD_MT6370 + tristate "MediaTek MT6370 SubPMIC" + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + depends on I2C + help + Say Y here to enable MT6370 SubPMIC functional support. + It consists of a single cell battery charger with ADC monitoring, RGB + LEDs, dual channel flashlight, WLED backlight driver, display bias + voltage supply, one general purpose LDO, and the USB Type-C & PD + controller complies with the latest USB Type-C and PD standards. + + This driver can also be built as a module. If so, the module + will be called "mt6370". + config MFD_MT6397 tristate "MediaTek MT6397 PMIC Support" select MFD_CORE @@ -936,6 +1152,42 @@ 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 + select MFD_CORE + select REGMAP_SPI + help + Ocelot is a family of networking chips that support multiple ethernet + and fibre interfaces. In addition to networking, they contain several + other functions, including pinctrl, MDIO, and communication with + external chips. While some chips have an internal processor capable of + running an OS, others don't. All chips can be controlled externally + through different interfaces, including SPI, I2C, and PCIe. + + Say yes here to add support for Ocelot chips (VSC7511, VSC7512, + VSC7513, VSC7514) controlled externally. + + To compile this driver as a module, choose M here: the module will be + called ocelot-soc. + + If unsure, say N. + config EZX_PCAP bool "Motorola EZXPCAP Support" depends on SPI_MASTER @@ -946,7 +1198,7 @@ config EZX_PCAP config MFD_CPCAP tristate "Support for Motorola CPCAP" depends on SPI - depends on OF || COMPILE_TEST + depends on OF select MFD_CORE select REGMAP_SPI select REGMAP_IRQ @@ -971,7 +1223,7 @@ config MFD_VIPERBOARD config MFD_NTXEC tristate "Netronix embedded controller (EC)" - depends on OF || COMPILE_TEST + depends on OF depends on I2C select REGMAP_I2C select MFD_CORE @@ -989,44 +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 UCB1400_CORE - tristate "Philips UCB1400 Core driver" - depends on AC97_BUS - depends on GPIOLIB - help - This enables support for the Philips UCB1400 core functions. - The UCB1400 is an AC97 audio codec. - - To compile this driver as a module, choose M here: the - module will be called ucb1400_core. - 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 @@ -1054,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 @@ -1069,6 +1298,16 @@ config MFD_SPMI_PMIC Say M here if you want to include support for the SPMI PMIC series as a module. The module will be called "qcom-spmi-pmic". +config MFD_SY7636A + tristate "Silergy SY7636A voltage regulator" + depends on I2C + select MFD_SIMPLE_MFD_I2C + help + Enable support for Silergy SY7636A voltage regulator. + + To enable support for building sub-devices as modules, + choose M here. + config MFD_RDC321X tristate "RDC R-321x southbridge" select MFD_CORE @@ -1101,6 +1340,18 @@ config MFD_RT5033 sub-devices like charger, fuel gauge, flash LED, current source, LDO and Buck. +config MFD_RT5120 + tristate "Richtek RT5120 Power Management IC" + depends on I2C + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + The enables support for Richtek RT5120 PMIC. It includes four high + efficiency buck converters and one LDO voltage regulator. The device + is targeted at providing the CPU voltage, memory, I/O and peripheral + power rails in home entertainment devices. + config MFD_RC5T583 bool "Ricoh RC5T583 Power Management system device" depends on I2C=y @@ -1115,19 +1366,38 @@ config MFD_RC5T583 Additional drivers must be enabled in order to use the different functionality of the device. -config MFD_RK808 - tristate "Rockchip RK805/RK808/RK809/RK817/RK818 Power Management Chip" +config MFD_RK8XX + tristate + select MFD_CORE + +config MFD_RK8XX_I2C + tristate "Rockchip RK805/RK808/RK809/RK816/RK817/RK818 Power Management Chip" depends on I2C && OF select MFD_CORE select REGMAP_I2C select REGMAP_IRQ + 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. +config MFD_RK8XX_SPI + tristate "Rockchip RK806 Power Management Chip" + depends on SPI && OF + select MFD_CORE + select REGMAP_SPI + select REGMAP_IRQ + select MFD_RK8XX + help + If you say yes here you get support for the RK806 Power Management + chip. + This driver provides common support for accessing the device + through an SPI interface. The device supports multiple sub-devices + including interrupts, LDO & DCDC regulators, and power on-key. + config MFD_RN5T618 tristate "Ricoh RN5T567/618 PMIC" depends on I2C @@ -1143,21 +1413,42 @@ config MFD_RN5T618 functionality of the device. config MFD_SEC_CORE - tristate "Samsung Electronics PMIC Series Support" - depends on I2C=y - depends on OF || COMPILE_TEST + tristate select MFD_CORE - select REGMAP_I2C 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 PMIC devices coming - usually along with Samsung Exynos SoC chipset. + 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_SEC_CORE + select REGMAP_I2C + help + 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. @@ -1165,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 @@ -1178,6 +1470,7 @@ config MFD_SI476X_CORE config MFD_SIMPLE_MFD_I2C tristate depends on I2C + select MFD_CORE select REGMAP_I2C help This driver creates a single register map with the intention for it @@ -1246,6 +1539,17 @@ config MFD_SC27XX_PMIC This driver provides common support for accessing the SC27xx PMICs, and it also adds the irq_chip parts for handling the PMIC chip events. +config RZ_MTU3 + 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 + on SoCs from Renesas. The core driver shares the clk and channel + register access for the other child devices like Counter, PWM, + Clock Source, and Clock event. + config ABX500_CORE bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions" depends on ARCH_U8500 || COMPILE_TEST @@ -1270,14 +1574,6 @@ config AB8500_CORE the irq_chip parts for handling the Mixed Signal chip events. This chip embeds various other multimedia functionalities as well. -config AB8500_DEBUG - bool "Enable debug info via debugfs" - depends on AB8500_GPADC && DEBUG_FS - default y if DEBUG_FS - help - Select this option if you want debug information using the debug - filesystem, debugfs. - config MFD_DB8500_PRCMU bool "ST-Ericsson DB8500 Power Reset Control Management Unit" depends on UX500_SOC_DB8500 @@ -1289,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 @@ -1318,32 +1614,27 @@ 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 PRCM controller" + bool "Allwinner A31/A23/A33 PRCM controller" depends on ARCH_SUNXI || COMPILE_TEST select MFD_CORE help Support for the PRCM (Power/Reset/Clock Management) unit available - in A31 SoC. + in the A31, A23, and A33 SoCs. Other Allwinner SoCs contain similar + hardware, but they do not use this driver. config MFD_SYSCON bool "System Controller Register R/W Based on Regmap" @@ -1352,13 +1643,9 @@ config MFD_SYSCON Select this option to enable accessing system control registers via regmap. -config MFD_DAVINCI_VOICECODEC - tristate - select MFD_CORE - select REGMAP_MMIO - 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 @@ -1368,14 +1655,6 @@ config MFD_TI_AM335X_TSCADC To compile this driver as a module, choose M here: the module will be called ti_am335x_tscadc. -config MFD_DM355EVM_MSP - bool "TI DaVinci DM355 EVM microcontroller" - depends on I2C=y && MACH_DAVINCI_DM355_EVM - help - This driver supports the MSP430 microcontroller used on these - boards. MSP430 firmware manages resets and power sequencing, - inputs from buttons and the IR remote, LEDs, an RTC, and more. - config MFD_LP3943 tristate "TI/National Semiconductor LP3943 MFD Driver" depends on I2C @@ -1408,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 @@ -1419,7 +1709,7 @@ config MFD_OMAP_USB_HOST OMAP USB Host drivers. config MFD_PALMAS - bool "TI Palmas series chips" + tristate "TI Palmas series chips" select MFD_CORE select REGMAP_I2C select REGMAP_IRQ @@ -1444,7 +1734,7 @@ config TPS6105X config TPS65010 tristate "TI TPS6501x Power Management chips" depends on I2C && GPIOLIB - default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK + default MACH_OMAP_OSK help If you say yes here you get support for the TPS6501x series of Power Management chips. These include voltage regulators, @@ -1555,6 +1845,20 @@ config MFD_TPS65218 This driver can also be built as a module. If so, the module will be called tps65218. +config MFD_TPS65219 + tristate "TI TPS65219 Power Management IC" + depends on I2C && OF + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + If you say yes here you get support for the TPS65219 series of Power + Management ICs. These include voltage regulators, GPIOs and + push/power button that is often used in portable devices. + + This driver can also be built as a module. If so, the module + will be called tps65219. + config MFD_TPS6586X bool "TI TPS6586x Power Management chips" depends on I2C=y @@ -1606,24 +1910,43 @@ config MFD_TPS65912_SPI If you say yes here you get support for the TPS65912 series of PM chips with SPI interface. -config MFD_TPS80031 - bool "TI TPS80031/TPS80032 Power Management chips" - depends on I2C=y +config MFD_TPS6594 + tristate select MFD_CORE - select REGMAP_I2C + select REGMAP select REGMAP_IRQ + +config MFD_TPS6594_I2C + tristate "TI TPS6594 Power Management chip with I2C" + select MFD_TPS6594 + select REGMAP_I2C + select CRC8 + depends on I2C help - If you say yes here you get support for the Texas Instruments - TPS80031/ TPS80032 Fully Integrated Power Management with Power - Path and Battery Charger. The device provides five configurable - step-down converters, 11 general purpose LDOs, USB OTG Module, - ADC, RTC, 2 PWM, System Voltage Regulator/Battery Charger with - Power Path from USB, 32K clock generator. + If you say yes here you get support for the TPS6594 series of + PM chips with I2C interface. + + This driver can also be built as a module. If so, the module + will be called tps6594-i2c. + +config MFD_TPS6594_SPI + tristate "TI TPS6594 Power Management chip with SPI" + select MFD_TPS6594 + select REGMAP_SPI + select CRC8 + depends on SPI_MASTER + help + If you say yes here you get support for the TPS6594 series of + PM chips with SPI interface. + + This driver can also be built as a module. If so, the module + will be called tps6594-spi. 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. @@ -1679,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 @@ -1711,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" @@ -1725,35 +2038,6 @@ config MFD_TC3589X additional drivers must be enabled in order to use the functionality of the device. -config MFD_TMIO - bool - default n - -config MFD_T7L66XB - bool "Toshiba T7L66XB" - depends on ARM && HAVE_CLK - select MFD_CORE - select MFD_TMIO - help - Support for Toshiba Mobile IO Controller T7L66XB - -config MFD_TC6387XB - bool "Toshiba TC6387XB" - depends on ARM && HAVE_CLK - select MFD_CORE - select MFD_TMIO - help - Support for Toshiba Mobile IO Controller TC6387XB - -config MFD_TC6393XB - bool "Toshiba TC6393XB" - depends on ARM && HAVE_CLK - select GPIOLIB - select MFD_CORE - select MFD_TMIO - help - Support for Toshiba Mobile IO Controller TC6393XB - config MFD_TQMX86 tristate "TQ-Systems IO controller TQMX86" select MFD_CORE @@ -1771,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 @@ -1940,23 +2216,6 @@ config MFD_ROHM_BD718XX NXP i.MX8. It contains 8 BUCK outputs and 7 LDOs, voltage monitoring and emergency shut down as well as 32,768KHz clock output. -config MFD_ROHM_BD70528 - tristate "ROHM BD70528 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 BD70528 Power - Management IC. BD71837 is general purpose single-chip power - management IC for battery-powered portable devices. It contains - 3 ultra-low current consumption buck converters, 3 LDOs and 2 LED - drivers. Also included are 4 GPIOs, a real-time clock (RTC), a 32kHz - crystal oscillator, high-accuracy VREF for use with an external ADC, - 10 bits SAR ADC for battery temperature monitor and 1S battery - charger. - config MFD_ROHM_BD71828 tristate "ROHM BD71828 and BD71815 Power Management IC" depends on I2C=y @@ -1978,12 +2237,26 @@ config MFD_ROHM_BD957XMUF 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 BD9576MUF and 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 @@ -2011,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 @@ -2028,7 +2301,7 @@ config MFD_STPMIC1 config MFD_STMFX tristate "Support for STMicroelectronics Multi-Function eXpander (STMFX)" depends on I2C - depends on OF || COMPILE_TEST + depends on OF select MFD_CORE select REGMAP_I2C help @@ -2103,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 @@ -2152,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. @@ -2170,18 +2444,127 @@ config SGI_MFD_IOC3 If you have an SGI Origin, Octane, or a PCI IOC3 card, then say Y. Otherwise say N. -config MFD_INTEL_M10_BMC - tristate "Intel MAX 10 Board Management Controller" - depends on SPI_MASTER - select REGMAP_SPI_AVMM - select MFD_CORE +config MFD_INTEL_M10_BMC_CORE + tristate + select MFD_CORE + select REGMAP + default n + +config MFD_INTEL_M10_BMC_SPI + tristate "Intel MAX 10 Board Management Controller with SPI" + depends on SPI_MASTER + select MFD_INTEL_M10_BMC_CORE + select REGMAP_SPI_AVMM + help + Support for the Intel MAX 10 board management controller using the + SPI interface. + + 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_INTEL_M10_BMC_PMCI + tristate "Intel MAX 10 Board Management Controller with PMCI" + depends on FPGA_DFL + select MFD_INTEL_M10_BMC_CORE + select REGMAP help - Support for the Intel MAX 10 board management controller using the - SPI interface. + Support for the Intel MAX 10 board management controller via PMCI. 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_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 + select MFD_CORE + select REGMAP_I2C + help + Support for the Renesas Synchronization Management Unit, such as + Clockmatrix and 82P33XXX series. This option supports I2C as + the control interface. + + 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_RSMU_SPI + tristate "Renesas Synchronization Management Unit with SPI" + depends on SPI && OF + select MFD_CORE + select REGMAP_SPI + help + Support for the Renesas Synchronization Management Unit, such as + Clockmatrix and 82P33XXX series. This option supports SPI as + the control interface. + + 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_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 8116c19d5fd4..e75e8045c28a 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -7,36 +7,32 @@ 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_MFD_ASIC3) += asic3.o tmio_core.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_HTC_PASIC3) += htc-pasic3.o -obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o +obj-$(CONFIG_MFD_MACSMC) += macsmc.o obj-$(CONFIG_MFD_TI_LP873X) += lp873x.o obj-$(CONFIG_MFD_TI_LP87565) += lp87565.o - -obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o -obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.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 obj-$(CONFIG_MFD_SUN6I_PRCM) += sun6i-prcm.o obj-$(CONFIG_MFD_TC3589X) += tc3589x.o -obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o -obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o -obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o obj-$(CONFIG_MFD_TQMX86) += tqmx86.o obj-$(CONFIG_MFD_LOCHNAGAR) += lochnagar-i2c.o @@ -95,17 +91,24 @@ 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 obj-$(CONFIG_MFD_TPS65086) += tps65086.o obj-$(CONFIG_MFD_TPS65217) += tps65217.o obj-$(CONFIG_MFD_TPS65218) += tps65218.o +obj-$(CONFIG_MFD_TPS65219) += tps65219.o obj-$(CONFIG_MFD_TPS65910) += tps65910.o obj-$(CONFIG_MFD_TPS65912) += tps65912-core.o obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o -obj-$(CONFIG_MFD_TPS80031) += tps80031.o +obj-$(CONFIG_MFD_TPS6594) += tps6594-core.o +obj-$(CONFIG_MFD_TPS6594_I2C) += tps6594-i2c.o +obj-$(CONFIG_MFD_TPS6594_SPI) += tps6594-spi.o obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o @@ -119,8 +122,15 @@ 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 +obj-$(CONFIG_MFD_OCELOT) += ocelot-soc.o + obj-$(CONFIG_EZX_PCAP) += ezx-pcap.o obj-$(CONFIG_MFD_CPCAP) += motorola-cpcap.o @@ -132,7 +142,6 @@ obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o ifeq ($(CONFIG_SA1100_ASSABET),y) obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o endif -obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o obj-$(CONFIG_PMIC_DA903X) += da903x.o @@ -159,10 +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 @@ -172,17 +186,19 @@ obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o obj-$(CONFIG_MFD_MP2629) += mp2629.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_MFD_MT6360) += mt6360-core.o +obj-$(CONFIG_MFD_MT6370) += mt6370.o +mt6397-objs := mt6397-core.o mt6397-irq.o mt6358-irq.o +obj-$(CONFIG_MFD_MT6397) += mt6397.o + +obj-$(CONFIG_RZ_MTU3) += rz-mtu3.o obj-$(CONFIG_ABX500_CORE) += abx500-core.o -obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o # ab8500-core need to come after db8500-prcmu (which provides the channel) 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 @@ -191,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 @@ -212,14 +227,18 @@ obj-$(CONFIG_MFD_INTEL_LPSS) += intel-lpss.o obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o -obj-$(CONFIG_MFD_INTEL_PMT) += intel_pmt.o obj-$(CONFIG_MFD_PALMAS) += palmas.o obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o obj-$(CONFIG_MFD_NTXEC) += ntxec.o obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o -obj-$(CONFIG_MFD_RK808) += rk808.o +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 @@ -231,20 +250,18 @@ obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o obj-$(CONFIG_MFD_IQS62X) += iqs62x.o obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o +obj-$(CONFIG_MFD_HI6421_SPMI) += hi6421-spmi-pmic.o obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o obj-$(CONFIG_MFD_DLN2) += dln2.o obj-$(CONFIG_MFD_RT4831) += rt4831.o obj-$(CONFIG_MFD_RT5033) += rt5033.o +obj-$(CONFIG_MFD_RT5120) += rt5120.o obj-$(CONFIG_MFD_SKY81452) += sky81452.o -intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o -obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o +obj-$(CONFIG_INTEL_SOC_PMIC) += intel_soc_pmic_crc.o obj-$(CONFIG_INTEL_SOC_PMIC_BXTWC) += intel_soc_pmic_bxtwc.o obj-$(CONFIG_INTEL_SOC_PMIC_CHTWC) += intel_soc_pmic_chtwc.o obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI) += intel_soc_pmic_chtdc_ti.o -obj-$(CONFIG_MFD_MT6360) += mt6360-core.o -mt6397-objs := mt6397-core.o mt6397-irq.o mt6358-irq.o -obj-$(CONFIG_MFD_MT6397) += mt6397.o obj-$(CONFIG_INTEL_SOC_PMIC_MRFLD) += intel_soc_pmic_mrfld.o obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o @@ -257,10 +274,10 @@ obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o obj-$(CONFIG_MFD_SC27XX_PMIC) += sprd-sc27xx-spi.o obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o -obj-$(CONFIG_MFD_ROHM_BD70528) += rohm-bd70528.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 @@ -268,7 +285,22 @@ obj-$(CONFIG_MFD_QCOM_PM8008) += qcom-pm8008.o obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o obj-$(CONFIG_MFD_SIMPLE_MFD_I2C) += simple-mfd-i2c.o -obj-$(CONFIG_MFD_INTEL_M10_BMC) += intel-m10-bmc.o +obj-$(CONFIG_MFD_SMPRO) += smpro-core.o + +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 + +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 a17cf759739d..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); } @@ -332,8 +330,7 @@ static inline void aat2870_init_debugfs(struct aat2870_data *aat2870) } #endif /* CONFIG_DEBUG_FS */ -static int aat2870_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int aat2870_i2c_probe(struct i2c_client *client) { struct aat2870_platform_data *pdata = dev_get_platdata(&client->dev); struct aat2870_data *aat2870; @@ -346,8 +343,6 @@ static int aat2870_i2c_probe(struct i2c_client *client, return -ENOMEM; aat2870->dev = &client->dev; - dev_set_drvdata(aat2870->dev, aat2870); - aat2870->client = client; i2c_set_clientdata(client, aat2870); @@ -409,7 +404,6 @@ out_disable: return ret; } -#ifdef CONFIG_PM_SLEEP static int aat2870_i2c_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -438,20 +432,19 @@ static int aat2870_i2c_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(aat2870_pm_ops, aat2870_i2c_suspend, - aat2870_i2c_resume); +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" }, { } }; static struct i2c_driver aat2870_i2c_driver = { .driver = { .name = "aat2870", - .pm = &aat2870_pm_ops, + .pm = pm_sleep_ptr(&aat2870_pm_ops), .suppress_bind_attrs = true, }, .probe = aat2870_i2c_probe, diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 30489670ea52..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 @@ -485,7 +484,7 @@ static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500, if (line == AB8540_INT_GPIO43F || line == AB8540_INT_GPIO44F) line += 1; - handle_nested_irq(irq_create_mapping(ab8500->domain, line)); + handle_nested_irq(irq_find_mapping(ab8500->domain, line)); } return 0; @@ -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"); @@ -613,10 +611,6 @@ static const struct mfd_cell ab8500_bm_devs[] = { }; static const struct mfd_cell ab8500_devs[] = { -#ifdef CONFIG_DEBUG_FS - MFD_CELL_OF("ab8500-debug", - NULL, NULL, 0, 0, "stericsson,ab8500-debug"), -#endif MFD_CELL_OF("ab8500-sysctrl", NULL, NULL, 0, 0, "stericsson,ab8500-sysctrl"), MFD_CELL_OF("ab8500-ext-regulator", @@ -652,11 +646,6 @@ static const struct mfd_cell ab8500_devs[] = { }; static const struct mfd_cell ab9540_devs[] = { -#ifdef CONFIG_DEBUG_FS - { - .name = "ab8500-debug", - }, -#endif { .name = "ab8500-sysctrl", }, @@ -707,12 +696,6 @@ static const struct mfd_cell ab9540_devs[] = { /* Device list for ab8505 */ static const struct mfd_cell ab8505_devs[] = { -#ifdef CONFIG_DEBUG_FS - { - .name = "ab8500-debug", - .of_compatible = "stericsson,ab8500-debug", - }, -#endif { .name = "ab8500-sysctrl", .of_compatible = "stericsson,ab8500-sysctrl", @@ -764,11 +747,6 @@ static const struct mfd_cell ab8505_devs[] = { }; static const struct mfd_cell ab8540_devs[] = { -#ifdef CONFIG_DEBUG_FS - { - .name = "ab8500-debug", - }, -#endif { .name = "ab8500-sysctrl", }, @@ -1042,9 +1020,9 @@ static int ab8500_probe(struct platform_device *pdev) enum ab8500_version version = AB8500_VERSION_UNDEFINED; struct device_node *np = pdev->dev.of_node; struct ab8500 *ab8500; - struct resource *resource; int ret; int i; + int irq; u8 value; ab8500 = devm_kzalloc(&pdev->dev, sizeof(*ab8500), GFP_KERNEL); @@ -1053,13 +1031,11 @@ static int ab8500_probe(struct platform_device *pdev) ab8500->dev = &pdev->dev; - resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!resource) { - dev_err(&pdev->dev, "no IRQ resource\n"); - return -ENODEV; - } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; - ab8500->irq = resource->start; + ab8500->irq = irq; ab8500->read = ab8500_prcmu_read; ab8500->write = ab8500_prcmu_write; diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c deleted file mode 100644 index e43dea89b094..000000000000 --- a/drivers/mfd/ab8500-debugfs.c +++ /dev/null @@ -1,2096 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson. - */ -/* - * AB8500 register access - * ====================== - * - * read: - * # echo BANK > <debugfs>/ab8500/register-bank - * # echo ADDR > <debugfs>/ab8500/register-address - * # cat <debugfs>/ab8500/register-value - * - * write: - * # echo BANK > <debugfs>/ab8500/register-bank - * # echo ADDR > <debugfs>/ab8500/register-address - * # echo VALUE > <debugfs>/ab8500/register-value - * - * read all registers from a bank: - * # echo BANK > <debugfs>/ab8500/register-bank - * # cat <debugfs>/ab8500/all-bank-register - * - * BANK target AB8500 register bank - * ADDR target AB8500 register address - * VALUE decimal or 0x-prefixed hexadecimal - * - * - * User Space notification on AB8500 IRQ - * ===================================== - * - * Allows user space entity to be notified when target AB8500 IRQ occurs. - * When subscribed, a sysfs entry is created in ab8500.i2c platform device. - * One can pool this file to get target IRQ occurence information. - * - * subscribe to an AB8500 IRQ: - * # echo IRQ > <debugfs>/ab8500/irq-subscribe - * - * unsubscribe from an AB8500 IRQ: - * # echo IRQ > <debugfs>/ab8500/irq-unsubscribe - * - * - * AB8500 register formated read/write access - * ========================================== - * - * Read: read data, data>>SHIFT, data&=MASK, output data - * [0xABCDEF98] shift=12 mask=0xFFF => 0x00000CDE - * Write: read data, data &= ~(MASK<<SHIFT), data |= (VALUE<<SHIFT), write data - * [0xABCDEF98] shift=12 mask=0xFFF value=0x123 => [0xAB123F98] - * - * Usage: - * # echo "CMD [OPTIONS] BANK ADRESS [VALUE]" > $debugfs/ab8500/hwreg - * - * CMD read read access - * write write access - * - * BANK target reg bank - * ADDRESS target reg address - * VALUE (write) value to be updated - * - * OPTIONS - * -d|-dec (read) output in decimal - * -h|-hexa (read) output in 0x-hexa (default) - * -l|-w|-b 32bit (default), 16bit or 8bit reg access - * -m|-mask MASK 0x-hexa mask (default 0xFFFFFFFF) - * -s|-shift SHIFT bit shift value (read:left, write:right) - * -o|-offset OFFSET address offset to add to ADDRESS value - * - * Warning: bit shift operation is applied to bit-mask. - * Warning: bit shift direction depends on read or right command. - */ - -#include <linux/seq_file.h> -#include <linux/uaccess.h> -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/debugfs.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/kobject.h> -#include <linux/slab.h> -#include <linux/irq.h> - -#include <linux/mfd/abx500.h> -#include <linux/mfd/abx500/ab8500.h> - -#ifdef CONFIG_DEBUG_FS -#include <linux/string.h> -#include <linux/ctype.h> -#endif - -static u32 debug_bank; -static u32 debug_address; - -static int irq_ab8500; -static int irq_first; -static int irq_last; -static u32 *irq_count; -static int num_irqs; - -static struct device_attribute **dev_attr; -static char **event_name; - -/** - * struct ab8500_reg_range - * @first: the first address of the range - * @last: the last address of the range - * @perm: access permissions for the range - */ -struct ab8500_reg_range { - u8 first; - u8 last; - u8 perm; -}; - -/** - * struct ab8500_prcmu_ranges - * @num_ranges: the number of ranges in the list - * @bankid: bank identifier - * @range: the list of register ranges - */ -struct ab8500_prcmu_ranges { - u8 num_ranges; - u8 bankid; - const struct ab8500_reg_range *range; -}; - -/* hwreg- "mask" and "shift" entries ressources */ -struct hwreg_cfg { - u32 bank; /* target bank */ - unsigned long addr; /* target address */ - uint fmt; /* format */ - unsigned long mask; /* read/write mask, applied before any bit shift */ - long shift; /* bit shift (read:right shift, write:left shift */ -}; -/* fmt bit #0: 0=hexa, 1=dec */ -#define REG_FMT_DEC(c) ((c)->fmt & 0x1) -#define REG_FMT_HEX(c) (!REG_FMT_DEC(c)) - -static struct hwreg_cfg hwreg_cfg = { - .addr = 0, /* default: invalid phys addr */ - .fmt = 0, /* default: 32bit access, hex output */ - .mask = 0xFFFFFFFF, /* default: no mask */ - .shift = 0, /* default: no bit shift */ -}; - -#define AB8500_NAME_STRING "ab8500" -#define AB8500_NUM_BANKS AB8500_DEBUG_FIELD_LAST - -#define AB8500_REV_REG 0x80 - -static struct ab8500_prcmu_ranges *debug_ranges; - -static struct ab8500_prcmu_ranges ab8500_debug_ranges[AB8500_NUM_BANKS] = { - [AB8500_M_FSM_RANK] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_SYS_CTRL1_BLOCK] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x02, - }, - { - .first = 0x42, - .last = 0x42, - }, - { - .first = 0x80, - .last = 0x81, - }, - }, - }, - [AB8500_SYS_CTRL2_BLOCK] = { - .num_ranges = 4, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x0D, - }, - { - .first = 0x0F, - .last = 0x17, - }, - { - .first = 0x30, - .last = 0x30, - }, - { - .first = 0x32, - .last = 0x33, - }, - }, - }, - [AB8500_REGU_CTRL1] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x03, - .last = 0x10, - }, - { - .first = 0x80, - .last = 0x84, - }, - }, - }, - [AB8500_REGU_CTRL2] = { - .num_ranges = 5, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x15, - }, - { - .first = 0x17, - .last = 0x19, - }, - { - .first = 0x1B, - .last = 0x1D, - }, - { - .first = 0x1F, - .last = 0x22, - }, - { - .first = 0x40, - .last = 0x44, - }, - /* - * 0x80-0x8B are SIM registers and should - * not be accessed from here - */ - }, - }, - [AB8500_USB] = { - .num_ranges = 2, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x80, - .last = 0x83, - }, - { - .first = 0x87, - .last = 0x8A, - }, - }, - }, - [AB8500_TVOUT] = { - .num_ranges = 9, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x12, - }, - { - .first = 0x15, - .last = 0x17, - }, - { - .first = 0x19, - .last = 0x21, - }, - { - .first = 0x27, - .last = 0x2C, - }, - { - .first = 0x41, - .last = 0x41, - }, - { - .first = 0x45, - .last = 0x5B, - }, - { - .first = 0x5D, - .last = 0x5D, - }, - { - .first = 0x69, - .last = 0x69, - }, - { - .first = 0x80, - .last = 0x81, - }, - }, - }, - [AB8500_DBI] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_ECI_AV_ACC] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x80, - .last = 0x82, - }, - }, - }, - [AB8500_RESERVED] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_GPADC] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x08, - }, - }, - }, - [AB8500_CHARGER] = { - .num_ranges = 9, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x03, - }, - { - .first = 0x05, - .last = 0x05, - }, - { - .first = 0x40, - .last = 0x40, - }, - { - .first = 0x42, - .last = 0x42, - }, - { - .first = 0x44, - .last = 0x44, - }, - { - .first = 0x50, - .last = 0x55, - }, - { - .first = 0x80, - .last = 0x82, - }, - { - .first = 0xC0, - .last = 0xC2, - }, - { - .first = 0xf5, - .last = 0xf6, - }, - }, - }, - [AB8500_GAS_GAUGE] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x07, - .last = 0x0A, - }, - { - .first = 0x10, - .last = 0x14, - }, - }, - }, - [AB8500_AUDIO] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x6F, - }, - }, - }, - [AB8500_INTERRUPT] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_RTC] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x0F, - }, - }, - }, - [AB8500_MISC] = { - .num_ranges = 8, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x05, - }, - { - .first = 0x10, - .last = 0x15, - }, - { - .first = 0x20, - .last = 0x25, - }, - { - .first = 0x30, - .last = 0x35, - }, - { - .first = 0x40, - .last = 0x45, - }, - { - .first = 0x50, - .last = 0x50, - }, - { - .first = 0x60, - .last = 0x67, - }, - { - .first = 0x80, - .last = 0x80, - }, - }, - }, - [AB8500_DEVELOPMENT] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - }, - }, - [AB8500_DEBUG] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x05, - .last = 0x07, - }, - }, - }, - [AB8500_PROD_TEST] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_STE_TEST] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_OTP_EMUL] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x01, - .last = 0x0F, - }, - }, - }, -}; - -static struct ab8500_prcmu_ranges ab8505_debug_ranges[AB8500_NUM_BANKS] = { - [0x0] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_SYS_CTRL1_BLOCK] = { - .num_ranges = 5, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x04, - }, - { - .first = 0x42, - .last = 0x42, - }, - { - .first = 0x52, - .last = 0x52, - }, - { - .first = 0x54, - .last = 0x57, - }, - { - .first = 0x80, - .last = 0x83, - }, - }, - }, - [AB8500_SYS_CTRL2_BLOCK] = { - .num_ranges = 5, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x0D, - }, - { - .first = 0x0F, - .last = 0x17, - }, - { - .first = 0x20, - .last = 0x20, - }, - { - .first = 0x30, - .last = 0x30, - }, - { - .first = 0x32, - .last = 0x3A, - }, - }, - }, - [AB8500_REGU_CTRL1] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x03, - .last = 0x11, - }, - { - .first = 0x80, - .last = 0x86, - }, - }, - }, - [AB8500_REGU_CTRL2] = { - .num_ranges = 6, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x06, - }, - { - .first = 0x08, - .last = 0x15, - }, - { - .first = 0x17, - .last = 0x19, - }, - { - .first = 0x1B, - .last = 0x1D, - }, - { - .first = 0x1F, - .last = 0x30, - }, - { - .first = 0x40, - .last = 0x48, - }, - /* - * 0x80-0x8B are SIM registers and should - * not be accessed from here - */ - }, - }, - [AB8500_USB] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x80, - .last = 0x83, - }, - { - .first = 0x87, - .last = 0x8A, - }, - { - .first = 0x91, - .last = 0x94, - }, - }, - }, - [AB8500_TVOUT] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_DBI] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_ECI_AV_ACC] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x80, - .last = 0x82, - }, - }, - }, - [AB8500_RESERVED] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_GPADC] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x08, - }, - }, - }, - [AB8500_CHARGER] = { - .num_ranges = 9, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x02, - .last = 0x03, - }, - { - .first = 0x05, - .last = 0x05, - }, - { - .first = 0x40, - .last = 0x44, - }, - { - .first = 0x50, - .last = 0x57, - }, - { - .first = 0x60, - .last = 0x60, - }, - { - .first = 0xA0, - .last = 0xA7, - }, - { - .first = 0xAF, - .last = 0xB2, - }, - { - .first = 0xC0, - .last = 0xC2, - }, - { - .first = 0xF5, - .last = 0xF5, - }, - }, - }, - [AB8500_GAS_GAUGE] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x07, - .last = 0x0A, - }, - { - .first = 0x10, - .last = 0x14, - }, - }, - }, - [AB8500_AUDIO] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x83, - }, - }, - }, - [AB8500_INTERRUPT] = { - .num_ranges = 11, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x04, - }, - { - .first = 0x06, - .last = 0x07, - }, - { - .first = 0x09, - .last = 0x09, - }, - { - .first = 0x0B, - .last = 0x0C, - }, - { - .first = 0x12, - .last = 0x15, - }, - { - .first = 0x18, - .last = 0x18, - }, - /* Latch registers should not be read here */ - { - .first = 0x40, - .last = 0x44, - }, - { - .first = 0x46, - .last = 0x49, - }, - { - .first = 0x4B, - .last = 0x4D, - }, - { - .first = 0x52, - .last = 0x55, - }, - { - .first = 0x58, - .last = 0x58, - }, - /* LatchHier registers should not be read here */ - }, - }, - [AB8500_RTC] = { - .num_ranges = 2, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x14, - }, - { - .first = 0x16, - .last = 0x17, - }, - }, - }, - [AB8500_MISC] = { - .num_ranges = 8, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x06, - }, - { - .first = 0x10, - .last = 0x16, - }, - { - .first = 0x20, - .last = 0x26, - }, - { - .first = 0x30, - .last = 0x36, - }, - { - .first = 0x40, - .last = 0x46, - }, - { - .first = 0x50, - .last = 0x50, - }, - { - .first = 0x60, - .last = 0x6B, - }, - { - .first = 0x80, - .last = 0x82, - }, - }, - }, - [AB8500_DEVELOPMENT] = { - .num_ranges = 2, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x05, - .last = 0x05, - }, - }, - }, - [AB8500_DEBUG] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x05, - .last = 0x07, - }, - }, - }, - [AB8500_PROD_TEST] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_STE_TEST] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_OTP_EMUL] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x01, - .last = 0x15, - }, - }, - }, -}; - -static struct ab8500_prcmu_ranges ab8540_debug_ranges[AB8500_NUM_BANKS] = { - [AB8500_M_FSM_RANK] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x0B, - }, - }, - }, - [AB8500_SYS_CTRL1_BLOCK] = { - .num_ranges = 6, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x04, - }, - { - .first = 0x42, - .last = 0x42, - }, - { - .first = 0x50, - .last = 0x54, - }, - { - .first = 0x57, - .last = 0x57, - }, - { - .first = 0x80, - .last = 0x83, - }, - { - .first = 0x90, - .last = 0x90, - }, - }, - }, - [AB8500_SYS_CTRL2_BLOCK] = { - .num_ranges = 5, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x0D, - }, - { - .first = 0x0F, - .last = 0x10, - }, - { - .first = 0x20, - .last = 0x21, - }, - { - .first = 0x32, - .last = 0x3C, - }, - { - .first = 0x40, - .last = 0x42, - }, - }, - }, - [AB8500_REGU_CTRL1] = { - .num_ranges = 4, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x03, - .last = 0x15, - }, - { - .first = 0x20, - .last = 0x20, - }, - { - .first = 0x80, - .last = 0x85, - }, - { - .first = 0x87, - .last = 0x88, - }, - }, - }, - [AB8500_REGU_CTRL2] = { - .num_ranges = 8, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x06, - }, - { - .first = 0x08, - .last = 0x15, - }, - { - .first = 0x17, - .last = 0x19, - }, - { - .first = 0x1B, - .last = 0x1D, - }, - { - .first = 0x1F, - .last = 0x2F, - }, - { - .first = 0x31, - .last = 0x3A, - }, - { - .first = 0x43, - .last = 0x44, - }, - { - .first = 0x48, - .last = 0x49, - }, - }, - }, - [AB8500_USB] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x80, - .last = 0x83, - }, - { - .first = 0x87, - .last = 0x8A, - }, - { - .first = 0x91, - .last = 0x94, - }, - }, - }, - [AB8500_TVOUT] = { - .num_ranges = 0, - .range = NULL - }, - [AB8500_DBI] = { - .num_ranges = 4, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x07, - }, - { - .first = 0x10, - .last = 0x11, - }, - { - .first = 0x20, - .last = 0x21, - }, - { - .first = 0x30, - .last = 0x43, - }, - }, - }, - [AB8500_ECI_AV_ACC] = { - .num_ranges = 2, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x03, - }, - { - .first = 0x80, - .last = 0x82, - }, - }, - }, - [AB8500_RESERVED] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_GPADC] = { - .num_ranges = 4, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x01, - }, - { - .first = 0x04, - .last = 0x06, - }, - { - .first = 0x09, - .last = 0x0A, - }, - { - .first = 0x10, - .last = 0x14, - }, - }, - }, - [AB8500_CHARGER] = { - .num_ranges = 10, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x02, - .last = 0x05, - }, - { - .first = 0x40, - .last = 0x44, - }, - { - .first = 0x50, - .last = 0x57, - }, - { - .first = 0x60, - .last = 0x60, - }, - { - .first = 0x70, - .last = 0x70, - }, - { - .first = 0xA0, - .last = 0xA9, - }, - { - .first = 0xAF, - .last = 0xB2, - }, - { - .first = 0xC0, - .last = 0xC6, - }, - { - .first = 0xF5, - .last = 0xF5, - }, - }, - }, - [AB8500_GAS_GAUGE] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x07, - .last = 0x0A, - }, - { - .first = 0x10, - .last = 0x14, - }, - }, - }, - [AB8500_AUDIO] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x9f, - }, - }, - }, - [AB8500_INTERRUPT] = { - .num_ranges = 6, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x05, - }, - { - .first = 0x0B, - .last = 0x0D, - }, - { - .first = 0x12, - .last = 0x20, - }, - /* Latch registers should not be read here */ - { - .first = 0x40, - .last = 0x45, - }, - { - .first = 0x4B, - .last = 0x4D, - }, - { - .first = 0x52, - .last = 0x60, - }, - /* LatchHier registers should not be read here */ - }, - }, - [AB8500_RTC] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x07, - }, - { - .first = 0x0B, - .last = 0x18, - }, - { - .first = 0x20, - .last = 0x25, - }, - }, - }, - [AB8500_MISC] = { - .num_ranges = 9, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x06, - }, - { - .first = 0x10, - .last = 0x16, - }, - { - .first = 0x20, - .last = 0x26, - }, - { - .first = 0x30, - .last = 0x36, - }, - { - .first = 0x40, - .last = 0x49, - }, - { - .first = 0x50, - .last = 0x50, - }, - { - .first = 0x60, - .last = 0x6B, - }, - { - .first = 0x70, - .last = 0x74, - }, - { - .first = 0x80, - .last = 0x82, - }, - }, - }, - [AB8500_DEVELOPMENT] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x01, - }, - { - .first = 0x06, - .last = 0x06, - }, - { - .first = 0x10, - .last = 0x21, - }, - }, - }, - [AB8500_DEBUG] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x01, - .last = 0x0C, - }, - { - .first = 0x0E, - .last = 0x11, - }, - { - .first = 0x80, - .last = 0x81, - }, - }, - }, - [AB8500_PROD_TEST] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_STE_TEST] = { - .num_ranges = 0, - .range = NULL, - }, - [AB8500_OTP_EMUL] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x3F, - }, - }, - }, -}; - -static irqreturn_t ab8500_debug_handler(int irq, void *data) -{ - char buf[16]; - struct kobject *kobj = (struct kobject *)data; - unsigned int irq_abb = irq - irq_first; - - if (irq_abb < num_irqs) - irq_count[irq_abb]++; - /* - * This makes it possible to use poll for events (EPOLLPRI | EPOLLERR) - * from userspace on sysfs file named <irq-nr> - */ - sprintf(buf, "%d", irq); - sysfs_notify(kobj, NULL, buf); - - return IRQ_HANDLED; -} - -/* Prints to seq_file or log_buf */ -static int ab8500_registers_print(struct device *dev, u32 bank, - struct seq_file *s) -{ - unsigned int i; - - for (i = 0; i < debug_ranges[bank].num_ranges; i++) { - u32 reg; - - for (reg = debug_ranges[bank].range[i].first; - reg <= debug_ranges[bank].range[i].last; - reg++) { - u8 value; - int err; - - err = abx500_get_register_interruptible(dev, - (u8)bank, (u8)reg, &value); - if (err < 0) { - dev_err(dev, "ab->read fail %d\n", err); - return err; - } - - if (s) { - seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n", - bank, reg, value); - /* - * Error is not returned here since - * the output is wanted in any case - */ - if (seq_has_overflowed(s)) - return 0; - } else { - dev_info(dev, " [0x%02X/0x%02X]: 0x%02X\n", - bank, reg, value); - } - } - } - - return 0; -} - -static int ab8500_bank_registers_show(struct seq_file *s, void *p) -{ - struct device *dev = s->private; - u32 bank = debug_bank; - - seq_puts(s, AB8500_NAME_STRING " register values:\n"); - - seq_printf(s, " bank 0x%02X:\n", bank); - - return ab8500_registers_print(dev, bank, s); -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_bank_registers); - -static int ab8500_print_all_banks(struct seq_file *s, void *p) -{ - struct device *dev = s->private; - unsigned int i; - - seq_puts(s, AB8500_NAME_STRING " register values:\n"); - - for (i = 0; i < AB8500_NUM_BANKS; i++) { - int err; - - seq_printf(s, " bank 0x%02X:\n", i); - err = ab8500_registers_print(dev, i, s); - if (err) - return err; - } - return 0; -} - -/* Dump registers to kernel log */ -void ab8500_dump_all_banks(struct device *dev) -{ - unsigned int i; - - dev_info(dev, "ab8500 register values:\n"); - - for (i = 1; i < AB8500_NUM_BANKS; i++) { - dev_info(dev, " bank 0x%02X:\n", i); - ab8500_registers_print(dev, i, NULL); - } -} - -static int ab8500_all_banks_open(struct inode *inode, struct file *file) -{ - struct seq_file *s; - int err; - - err = single_open(file, ab8500_print_all_banks, inode->i_private); - if (!err) { - /* Default buf size in seq_read is not enough */ - s = (struct seq_file *)file->private_data; - s->size = (PAGE_SIZE * 2); - s->buf = kmalloc(s->size, GFP_KERNEL); - if (!s->buf) { - single_release(inode, file); - err = -ENOMEM; - } - } - return err; -} - -static const struct file_operations ab8500_all_banks_fops = { - .open = ab8500_all_banks_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_bank_print(struct seq_file *s, void *p) -{ - seq_printf(s, "0x%02X\n", debug_bank); - return 0; -} - -static int ab8500_bank_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_bank_print, inode->i_private); -} - -static ssize_t ab8500_bank_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_bank; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_bank); - if (err) - return err; - - if (user_bank >= AB8500_NUM_BANKS) { - dev_err(dev, "debugfs error input > number of banks\n"); - return -EINVAL; - } - - debug_bank = user_bank; - - return count; -} - -static int ab8500_address_print(struct seq_file *s, void *p) -{ - seq_printf(s, "0x%02X\n", debug_address); - return 0; -} - -static int ab8500_address_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_address_print, inode->i_private); -} - -static ssize_t ab8500_address_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_address; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_address); - if (err) - return err; - - if (user_address > 0xff) { - dev_err(dev, "debugfs error input > 0xff\n"); - return -EINVAL; - } - debug_address = user_address; - - return count; -} - -static int ab8500_val_print(struct seq_file *s, void *p) -{ - struct device *dev = s->private; - int ret; - u8 regvalue; - - ret = abx500_get_register_interruptible(dev, - (u8)debug_bank, (u8)debug_address, ®value); - if (ret < 0) { - dev_err(dev, "abx500_get_reg fail %d, %d\n", - ret, __LINE__); - return -EINVAL; - } - seq_printf(s, "0x%02X\n", regvalue); - - return 0; -} - -static int ab8500_val_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_val_print, inode->i_private); -} - -static ssize_t ab8500_val_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_val; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_val); - if (err) - return err; - - if (user_val > 0xff) { - dev_err(dev, "debugfs error input > 0xff\n"); - return -EINVAL; - } - err = abx500_set_register_interruptible(dev, - (u8)debug_bank, debug_address, (u8)user_val); - if (err < 0) { - pr_err("abx500_set_reg failed %d, %d", err, __LINE__); - return -EINVAL; - } - - return count; -} - -/* - * Interrupt status - */ -static u32 num_interrupts[AB8500_MAX_NR_IRQS]; -static u32 num_wake_interrupts[AB8500_MAX_NR_IRQS]; -static int num_interrupt_lines; - -void ab8500_debug_register_interrupt(int line) -{ - if (line < num_interrupt_lines) - num_interrupts[line]++; -} - -static int ab8500_interrupts_show(struct seq_file *s, void *p) -{ - int line; - - seq_puts(s, "name: number: irq: number of: wake:\n"); - - for (line = 0; line < num_interrupt_lines; line++) { - seq_printf(s, "%3i: %4i %6i %4i\n", - line, - line + irq_first, - num_interrupts[line], - num_wake_interrupts[line]); - } - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_interrupts); - -/* - * - HWREG DB8500 formated routines - */ -static int ab8500_hwreg_print(struct seq_file *s, void *d) -{ - struct device *dev = s->private; - int ret; - u8 regvalue; - - ret = abx500_get_register_interruptible(dev, - (u8)hwreg_cfg.bank, (u8)hwreg_cfg.addr, ®value); - if (ret < 0) { - dev_err(dev, "abx500_get_reg fail %d, %d\n", - ret, __LINE__); - return -EINVAL; - } - - if (hwreg_cfg.shift >= 0) - regvalue >>= hwreg_cfg.shift; - else - regvalue <<= -hwreg_cfg.shift; - regvalue &= hwreg_cfg.mask; - - if (REG_FMT_DEC(&hwreg_cfg)) - seq_printf(s, "%d\n", regvalue); - else - seq_printf(s, "0x%02X\n", regvalue); - return 0; -} - -static int ab8500_hwreg_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_hwreg_print, inode->i_private); -} - -#define AB8500_SUPPLY_CONTROL_CONFIG_1 0x01 -#define AB8500_SUPPLY_CONTROL_REG 0x00 -#define AB8500_FIRST_SIM_REG 0x80 -#define AB8500_LAST_SIM_REG 0x8B -#define AB8505_LAST_SIM_REG 0x8C - -static int ab8500_modem_show(struct seq_file *s, void *p) -{ - struct device *dev = s->private; - struct ab8500 *ab8500; - int err; - u8 value; - u8 orig_value; - u32 bank = AB8500_REGU_CTRL2; - u32 last_sim_reg = AB8500_LAST_SIM_REG; - u32 reg; - - ab8500 = dev_get_drvdata(dev->parent); - dev_warn(dev, "WARNING! This operation can interfer with modem side\n" - "and should only be done with care\n"); - - err = abx500_get_register_interruptible(dev, - AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, &orig_value); - if (err < 0) - goto report_read_failure; - - /* Config 1 will allow APE side to read SIM registers */ - err = abx500_set_register_interruptible(dev, - AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, - AB8500_SUPPLY_CONTROL_CONFIG_1); - if (err < 0) - goto report_write_failure; - - seq_printf(s, " bank 0x%02X:\n", bank); - - if (is_ab9540(ab8500) || is_ab8505(ab8500)) - last_sim_reg = AB8505_LAST_SIM_REG; - - for (reg = AB8500_FIRST_SIM_REG; reg <= last_sim_reg; reg++) { - err = abx500_get_register_interruptible(dev, - bank, reg, &value); - if (err < 0) - goto report_read_failure; - - seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n", bank, reg, value); - } - err = abx500_set_register_interruptible(dev, - AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, orig_value); - if (err < 0) - goto report_write_failure; - - return 0; - -report_read_failure: - dev_err(dev, "ab->read fail %d\n", err); - return err; - -report_write_failure: - dev_err(dev, "ab->write fail %d\n", err); - return err; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_modem); - -/* - * return length of an ASCII numerical value, 0 is string is not a - * numerical value. - * string shall start at value 1st char. - * string can be tailed with \0 or space or newline chars only. - * value can be decimal or hexadecimal (prefixed 0x or 0X). - */ -static int strval_len(char *b) -{ - char *s = b; - - if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) { - s += 2; - for (; *s && (*s != ' ') && (*s != '\n'); s++) { - if (!isxdigit(*s)) - return 0; - } - } else { - if (*s == '-') - s++; - for (; *s && (*s != ' ') && (*s != '\n'); s++) { - if (!isdigit(*s)) - return 0; - } - } - return (int) (s-b); -} - -/* - * parse hwreg input data. - * update global hwreg_cfg only if input data syntax is ok. - */ -static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg, - struct device *dev) -{ - uint write, val = 0; - u8 regvalue; - int ret; - struct hwreg_cfg loc = { - .bank = 0, /* default: invalid phys addr */ - .addr = 0, /* default: invalid phys addr */ - .fmt = 0, /* default: 32bit access, hex output */ - .mask = 0xFFFFFFFF, /* default: no mask */ - .shift = 0, /* default: no bit shift */ - }; - - /* read or write ? */ - if (!strncmp(b, "read ", 5)) { - write = 0; - b += 5; - } else if (!strncmp(b, "write ", 6)) { - write = 1; - b += 6; - } else - return -EINVAL; - - /* OPTIONS -l|-w|-b -s -m -o */ - while ((*b == ' ') || (*b == '-')) { - if (*(b-1) != ' ') { - b++; - continue; - } - if ((!strncmp(b, "-d ", 3)) || - (!strncmp(b, "-dec ", 5))) { - b += (*(b+2) == ' ') ? 3 : 5; - loc.fmt |= (1<<0); - } else if ((!strncmp(b, "-h ", 3)) || - (!strncmp(b, "-hex ", 5))) { - b += (*(b+2) == ' ') ? 3 : 5; - loc.fmt &= ~(1<<0); - } else if ((!strncmp(b, "-m ", 3)) || - (!strncmp(b, "-mask ", 6))) { - b += (*(b+2) == ' ') ? 3 : 6; - if (strval_len(b) == 0) - return -EINVAL; - ret = kstrtoul(b, 0, &loc.mask); - if (ret) - return ret; - } else if ((!strncmp(b, "-s ", 3)) || - (!strncmp(b, "-shift ", 7))) { - b += (*(b+2) == ' ') ? 3 : 7; - if (strval_len(b) == 0) - return -EINVAL; - ret = kstrtol(b, 0, &loc.shift); - if (ret) - return ret; - } else { - return -EINVAL; - } - } - /* get arg BANK and ADDRESS */ - if (strval_len(b) == 0) - return -EINVAL; - ret = kstrtouint(b, 0, &loc.bank); - if (ret) - return ret; - while (*b == ' ') - b++; - if (strval_len(b) == 0) - return -EINVAL; - ret = kstrtoul(b, 0, &loc.addr); - if (ret) - return ret; - - if (write) { - while (*b == ' ') - b++; - if (strval_len(b) == 0) - return -EINVAL; - ret = kstrtouint(b, 0, &val); - if (ret) - return ret; - } - - /* args are ok, update target cfg (mainly for read) */ - *cfg = loc; - -#ifdef ABB_HWREG_DEBUG - pr_warn("HWREG request: %s, %s,\n", (write) ? "write" : "read", - REG_FMT_DEC(cfg) ? "decimal" : "hexa"); - pr_warn(" addr=0x%08X, mask=0x%X, shift=%d" "value=0x%X\n", - cfg->addr, cfg->mask, cfg->shift, val); -#endif - - if (!write) - return 0; - - ret = abx500_get_register_interruptible(dev, - (u8)cfg->bank, (u8)cfg->addr, ®value); - if (ret < 0) { - dev_err(dev, "abx500_get_reg fail %d, %d\n", - ret, __LINE__); - return -EINVAL; - } - - if (cfg->shift >= 0) { - regvalue &= ~(cfg->mask << (cfg->shift)); - val = (val & cfg->mask) << (cfg->shift); - } else { - regvalue &= ~(cfg->mask >> (-cfg->shift)); - val = (val & cfg->mask) >> (-cfg->shift); - } - val = val | regvalue; - - ret = abx500_set_register_interruptible(dev, - (u8)cfg->bank, (u8)cfg->addr, (u8)val); - if (ret < 0) { - pr_err("abx500_set_reg failed %d, %d", ret, __LINE__); - return -EINVAL; - } - - return 0; -} - -static ssize_t ab8500_hwreg_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - char buf[128]; - int buf_size, ret; - - /* Get userspace string and assure termination */ - buf_size = min((int)count, (int)(sizeof(buf)-1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - /* get args and process */ - ret = hwreg_common_write(buf, &hwreg_cfg, dev); - return (ret) ? ret : buf_size; -} - -/* - * - irq subscribe/unsubscribe stuff - */ -static int ab8500_subscribe_unsubscribe_print(struct seq_file *s, void *p) -{ - seq_printf(s, "%d\n", irq_first); - - return 0; -} - -static int ab8500_subscribe_unsubscribe_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8500_subscribe_unsubscribe_print, - inode->i_private); -} - -/* - * Userspace should use poll() on this file. When an event occur - * the blocking poll will be released. - */ -static ssize_t show_irq(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long name; - unsigned int irq_index; - int err; - - err = kstrtoul(attr->attr.name, 0, &name); - if (err) - return err; - - irq_index = name - irq_first; - if (irq_index >= num_irqs) - return -EINVAL; - - return sprintf(buf, "%u\n", irq_count[irq_index]); -} - -static ssize_t ab8500_subscribe_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_val; - int err; - unsigned int irq_index; - - err = kstrtoul_from_user(user_buf, count, 0, &user_val); - if (err) - return err; - - if (user_val < irq_first) { - dev_err(dev, "debugfs error input < %d\n", irq_first); - return -EINVAL; - } - if (user_val > irq_last) { - dev_err(dev, "debugfs error input > %d\n", irq_last); - return -EINVAL; - } - - irq_index = user_val - irq_first; - if (irq_index >= num_irqs) - return -EINVAL; - - /* - * This will create a sysfs file named <irq-nr> which userspace can - * use to select or poll and get the AB8500 events - */ - dev_attr[irq_index] = kmalloc(sizeof(struct device_attribute), - GFP_KERNEL); - if (!dev_attr[irq_index]) - return -ENOMEM; - - event_name[irq_index] = kasprintf(GFP_KERNEL, "%lu", user_val); - if (!event_name[irq_index]) - return -ENOMEM; - - dev_attr[irq_index]->show = show_irq; - dev_attr[irq_index]->store = NULL; - dev_attr[irq_index]->attr.name = event_name[irq_index]; - dev_attr[irq_index]->attr.mode = S_IRUGO; - err = sysfs_create_file(&dev->kobj, &dev_attr[irq_index]->attr); - if (err < 0) { - pr_info("sysfs_create_file failed %d\n", err); - return err; - } - - err = request_threaded_irq(user_val, NULL, ab8500_debug_handler, - IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, - "ab8500-debug", &dev->kobj); - if (err < 0) { - pr_info("request_threaded_irq failed %d, %lu\n", - err, user_val); - sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr); - return err; - } - - return count; -} - -static ssize_t ab8500_unsubscribe_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_val; - int err; - unsigned int irq_index; - - err = kstrtoul_from_user(user_buf, count, 0, &user_val); - if (err) - return err; - - if (user_val < irq_first) { - dev_err(dev, "debugfs error input < %d\n", irq_first); - return -EINVAL; - } - if (user_val > irq_last) { - dev_err(dev, "debugfs error input > %d\n", irq_last); - return -EINVAL; - } - - irq_index = user_val - irq_first; - if (irq_index >= num_irqs) - return -EINVAL; - - /* Set irq count to 0 when unsubscribe */ - irq_count[irq_index] = 0; - - if (dev_attr[irq_index]) - sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr); - - - free_irq(user_val, &dev->kobj); - kfree(event_name[irq_index]); - kfree(dev_attr[irq_index]); - - return count; -} - -/* - * - several debugfs nodes fops - */ - -static const struct file_operations ab8500_bank_fops = { - .open = ab8500_bank_open, - .write = ab8500_bank_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab8500_address_fops = { - .open = ab8500_address_open, - .write = ab8500_address_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab8500_val_fops = { - .open = ab8500_val_open, - .write = ab8500_val_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab8500_subscribe_fops = { - .open = ab8500_subscribe_unsubscribe_open, - .write = ab8500_subscribe_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab8500_unsubscribe_fops = { - .open = ab8500_subscribe_unsubscribe_open, - .write = ab8500_unsubscribe_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab8500_hwreg_fops = { - .open = ab8500_hwreg_open, - .write = ab8500_hwreg_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_debug_probe(struct platform_device *plf) -{ - struct dentry *ab8500_dir; - struct ab8500 *ab8500; - struct resource *res; - - debug_bank = AB8500_MISC; - debug_address = AB8500_REV_REG & 0x00FF; - - ab8500 = dev_get_drvdata(plf->dev.parent); - num_irqs = ab8500->mask_size; - - irq_count = devm_kcalloc(&plf->dev, - num_irqs, sizeof(*irq_count), GFP_KERNEL); - if (!irq_count) - return -ENOMEM; - - dev_attr = devm_kcalloc(&plf->dev, - num_irqs, sizeof(*dev_attr), GFP_KERNEL); - if (!dev_attr) - return -ENOMEM; - - event_name = devm_kcalloc(&plf->dev, - num_irqs, sizeof(*event_name), GFP_KERNEL); - if (!event_name) - return -ENOMEM; - - res = platform_get_resource_byname(plf, 0, "IRQ_AB8500"); - if (!res) { - dev_err(&plf->dev, "AB8500 irq not found, err %d\n", irq_first); - return -ENXIO; - } - irq_ab8500 = res->start; - - irq_first = platform_get_irq_byname(plf, "IRQ_FIRST"); - if (irq_first < 0) - return irq_first; - - irq_last = platform_get_irq_byname(plf, "IRQ_LAST"); - if (irq_last < 0) - return irq_last; - - ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL); - - debugfs_create_file("all-bank-registers", S_IRUGO, ab8500_dir, - &plf->dev, &ab8500_bank_registers_fops); - debugfs_create_file("all-banks", S_IRUGO, ab8500_dir, - &plf->dev, &ab8500_all_banks_fops); - debugfs_create_file("register-bank", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_bank_fops); - debugfs_create_file("register-address", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_address_fops); - debugfs_create_file("register-value", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_val_fops); - debugfs_create_file("irq-subscribe", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_subscribe_fops); - - if (is_ab8500(ab8500)) { - debug_ranges = ab8500_debug_ranges; - num_interrupt_lines = AB8500_NR_IRQS; - } else if (is_ab8505(ab8500)) { - debug_ranges = ab8505_debug_ranges; - num_interrupt_lines = AB8505_NR_IRQS; - } else if (is_ab9540(ab8500)) { - debug_ranges = ab8505_debug_ranges; - num_interrupt_lines = AB9540_NR_IRQS; - } else if (is_ab8540(ab8500)) { - debug_ranges = ab8540_debug_ranges; - num_interrupt_lines = AB8540_NR_IRQS; - } - - debugfs_create_file("interrupts", (S_IRUGO), ab8500_dir, &plf->dev, - &ab8500_interrupts_fops); - debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops); - debugfs_create_file("hwreg", (S_IRUGO | S_IWUSR | S_IWGRP), ab8500_dir, - &plf->dev, &ab8500_hwreg_fops); - debugfs_create_file("all-modem-registers", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_modem_fops); - - return 0; -} - -static struct platform_driver ab8500_debug_driver = { - .driver = { - .name = "ab8500-debug", - .suppress_bind_attrs = true, - }, - .probe = ab8500_debug_probe, -}; - -static int __init ab8500_debug_init(void) -{ - return platform_driver_register(&ab8500_debug_driver); -} -subsys_initcall(ab8500_debug_init); 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 80c2fdd14fc4..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> @@ -169,7 +169,7 @@ static int a500_ec_probe(struct i2c_client *client) return 0; } -static int a500_ec_remove(struct i2c_client *client) +static void a500_ec_remove(struct i2c_client *client) { if (of_device_is_system_power_controller(client->dev.of_node)) { if (pm_power_off == a500_ec_poweroff) @@ -177,8 +177,6 @@ static int a500_ec_remove(struct i2c_client *client) unregister_restart_handler(&a500_ec_restart_handler); } - - return 0; } static const struct of_device_id a500_ec_match[] = { @@ -192,7 +190,7 @@ static struct i2c_driver a500_ec_driver = { .name = "acer-a500-embedded-controller", .of_match_table = a500_ec_match, }, - .probe_new = a500_ec_probe, + .probe = a500_ec_probe, .remove = a500_ec_remove, }; module_i2c_driver(a500_ec_driver); diff --git a/drivers/mfd/act8945a.c b/drivers/mfd/act8945a.c index d3520430997c..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[] = { @@ -28,8 +28,7 @@ static const struct regmap_config act8945a_regmap_config = { .val_bits = 8, }; -static int act8945a_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int act8945a_i2c_probe(struct i2c_client *i2c) { int ret; struct regmap *regmap; @@ -55,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); @@ -69,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/adp5520.c b/drivers/mfd/adp5520.c index 8db15f5a7179..bd6f4965ebc8 100644 --- a/drivers/mfd/adp5520.c +++ b/drivers/mfd/adp5520.c @@ -204,9 +204,9 @@ static int adp5520_remove_subdevs(struct adp5520_chip *chip) return device_for_each_child(chip->dev, NULL, __remove_subdev); } -static int adp5520_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adp5520_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); struct adp5520_platform_data *pdata = dev_get_platdata(&client->dev); struct platform_device *pdev; struct adp5520_chip *chip; @@ -305,7 +305,6 @@ out_free_irq: return ret; } -#ifdef CONFIG_PM_SLEEP static int adp5520_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -326,9 +325,8 @@ static int adp5520_resume(struct device *dev) adp5520_write(chip->dev, ADP5520_MODE_STATUS, chip->mode); return 0; } -#endif -static SIMPLE_DEV_PM_OPS(adp5520_pm, adp5520_suspend, adp5520_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(adp5520_pm, adp5520_suspend, adp5520_resume); static const struct i2c_device_id adp5520_id[] = { { "pmic-adp5520", ID_ADP5520 }, @@ -339,7 +337,7 @@ static const struct i2c_device_id adp5520_id[] = { static struct i2c_driver adp5520_driver = { .driver = { .name = "adp5520", - .pm = &adp5520_pm, + .pm = pm_sleep_ptr(&adp5520_pm), .suppress_bind_attrs = true, }, .probe = adp5520_probe, 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, ®_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 a3bf64f9afd1..d53e433ab5c1 100644 --- a/drivers/mfd/altera-a10sr.c +++ b/drivers/mfd/altera-a10sr.c @@ -14,6 +14,7 @@ #include <linux/mfd/altera-a10sr.h> #include <linux/mfd/core.h> #include <linux/init.h> +#include <linux/module.h> #include <linux/of.h> #include <linux/spi/spi.h> @@ -150,12 +151,20 @@ static const struct of_device_id altr_a10sr_spi_of_match[] = { { .compatible = "altr,a10sr" }, { }, }; +MODULE_DEVICE_TABLE(of, altr_a10sr_spi_of_match); + +static const struct spi_device_id altr_a10sr_spi_ids[] = { + { .name = "a10sr" }, + { }, +}; +MODULE_DEVICE_TABLE(spi, altr_a10sr_spi_ids); 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, }; builtin_driver(altr_a10sr_spi_driver, spi_register_driver) diff --git a/drivers/mfd/altera-sysmgr.c b/drivers/mfd/altera-sysmgr.c index 20cb294c7512..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); @@ -153,7 +156,7 @@ static int sysmgr_probe(struct platform_device *pdev) if (!base) return -ENOMEM; - sysmgr_config.max_register = resource_size(res) - 3; + sysmgr_config.max_register = resource_size(res) - 4; regmap = devm_regmap_init_mmio(dev, base, &sysmgr_config); } @@ -198,4 +201,3 @@ module_exit(altr_sysmgr_exit); MODULE_AUTHOR("Thor Thayer <>"); MODULE_DESCRIPTION("SOCFPGA System Manager driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 9323b1e3a69e..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> @@ -45,7 +44,7 @@ int arizona_clk32k_enable(struct arizona *arizona) if (arizona->clk32k_ref == 1) { switch (arizona->pdata.clk32k_src) { case ARIZONA_32KZ_MCLK1: - ret = pm_runtime_get_sync(arizona->dev); + ret = pm_runtime_resume_and_get(arizona->dev); if (ret != 0) goto err_ref; ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK1]); @@ -480,7 +479,6 @@ static int wm5102_clear_write_sequencer(struct arizona *arizona) return 0; } -#ifdef CONFIG_PM static int arizona_isolate_dcvdd(struct arizona *arizona) { int ret; @@ -742,9 +740,7 @@ static int arizona_runtime_suspend(struct device *dev) return 0; } -#endif -#ifdef CONFIG_PM_SLEEP static int arizona_suspend(struct device *dev) { struct arizona *arizona = dev_get_drvdata(dev); @@ -784,17 +780,15 @@ static int arizona_resume(struct device *dev) return 0; } -#endif -const struct dev_pm_ops arizona_pm_ops = { - SET_RUNTIME_PM_OPS(arizona_runtime_suspend, - arizona_runtime_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume) - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(arizona_suspend_noirq, - arizona_resume_noirq) +EXPORT_GPL_DEV_PM_OPS(arizona_pm_ops) = { + RUNTIME_PM_OPS(arizona_runtime_suspend, + arizona_runtime_resume, + NULL) + SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(arizona_suspend_noirq, + arizona_resume_noirq) }; -EXPORT_SYMBOL_GPL(arizona_pm_ops); #ifdef CONFIG_OF static int arizona_of_get_core_pdata(struct arizona *arizona) @@ -845,19 +839,6 @@ static int arizona_of_get_core_pdata(struct arizona *arizona) return 0; } - -const struct of_device_id arizona_of_match[] = { - { .compatible = "wlf,wm5102", .data = (void *)WM5102 }, - { .compatible = "wlf,wm5110", .data = (void *)WM5110 }, - { .compatible = "wlf,wm8280", .data = (void *)WM8280 }, - { .compatible = "wlf,wm8997", .data = (void *)WM8997 }, - { .compatible = "wlf,wm8998", .data = (void *)WM8998 }, - { .compatible = "wlf,wm1814", .data = (void *)WM1814 }, - { .compatible = "wlf,wm1831", .data = (void *)WM1831 }, - { .compatible = "cirrus,cs47l24", .data = (void *)CS47L24 }, - {}, -}; -EXPORT_SYMBOL_GPL(arizona_of_match); #else static inline int arizona_of_get_core_pdata(struct arizona *arizona) { @@ -1448,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 5e83b730c4ce..10e76fc8f12e 100644 --- a/drivers/mfd/arizona-i2c.c +++ b/drivers/mfd/arizona-i2c.c @@ -20,21 +20,14 @@ #include "arizona.h" -static int arizona_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int arizona_i2c_probe(struct i2c_client *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)) @@ -84,13 +77,11 @@ static int arizona_i2c_probe(struct i2c_client *i2c, return arizona_dev_init(arizona); } -static int arizona_i2c_remove(struct i2c_client *i2c) +static void arizona_i2c_remove(struct i2c_client *i2c) { struct arizona *arizona = dev_get_drvdata(&i2c->dev); arizona_dev_exit(arizona); - - return 0; } static const struct i2c_device_id arizona_i2c_id[] = { @@ -104,11 +95,24 @@ static const struct i2c_device_id arizona_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, arizona_i2c_id); +#ifdef CONFIG_OF +static const struct of_device_id arizona_i2c_of_match[] = { + { .compatible = "wlf,wm5102", .data = (void *)WM5102 }, + { .compatible = "wlf,wm5110", .data = (void *)WM5110 }, + { .compatible = "wlf,wm8280", .data = (void *)WM8280 }, + { .compatible = "wlf,wm8997", .data = (void *)WM8997 }, + { .compatible = "wlf,wm8998", .data = (void *)WM8998 }, + { .compatible = "wlf,wm1814", .data = (void *)WM1814 }, + {}, +}; +MODULE_DEVICE_TABLE(of, arizona_i2c_of_match); +#endif + static struct i2c_driver arizona_i2c_driver = { .driver = { .name = "arizona", - .pm = &arizona_pm_ops, - .of_match_table = of_match_ptr(arizona_of_match), + .pm = pm_ptr(&arizona_pm_ops), + .of_match_table = of_match_ptr(arizona_i2c_of_match), }, .probe = arizona_i2c_probe, .remove = arizona_i2c_remove, 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 aa1d6f94ae53..eaa2b2bc5dd0 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -43,31 +43,13 @@ static const struct gpiod_lookup arizona_soc_gpios[] = { { "arizona", 4, "wlf,micd-pol", 0, GPIO_ACTIVE_LOW }, }; -/* - * The AOSP 3.5 mm Headset: Accessory Specification gives the following values: - * Function A Play/Pause: 0 ohm - * Function D Voice assistant: 135 ohm - * Function B Volume Up 240 ohm - * Function C Volume Down 470 ohm - * Minimum Mic DC resistance 1000 ohm - * Minimum Ear speaker impedance 16 ohm - * Note the first max value below must be less then the min. speaker impedance, - * to allow CTIA/OMTP detection to work. The other max values are the closest - * value from extcon-arizona.c:arizona_micd_levels halfway 2 button resistances. - */ -static const struct arizona_micd_range arizona_micd_aosp_ranges[] = { - { .max = 11, .key = KEY_PLAYPAUSE }, - { .max = 186, .key = KEY_VOICECOMMAND }, - { .max = 348, .key = KEY_VOLUMEUP }, - { .max = 752, .key = KEY_VOLUMEDOWN }, -}; - static void arizona_spi_acpi_remove_lookup(void *lookup) { gpiod_remove_lookup_table(lookup); } -static int arizona_spi_acpi_probe(struct arizona *arizona) +/* For ACPI tables from boards which ship with Windows as factory OS */ +static int arizona_spi_acpi_windows_probe(struct arizona *arizona) { struct gpiod_lookup_table *lookup; acpi_status status; @@ -96,6 +78,65 @@ static int arizona_spi_acpi_probe(struct arizona *arizona) if (ACPI_FAILURE(status)) dev_warn(arizona->dev, "Failed to enable 32KHz clk ACPI error %d\n", status); + return 0; +} + +/* For ACPI tables from boards which ship with Android as factory OS */ +static int arizona_spi_acpi_android_probe(struct arizona *arizona) +{ + int ret; + + /* + * Get the reset GPIO, treating -ENOENT as -EPROBE_DEFER to wait for + * the x86-android-tablets module to register the board specific GPIO + * lookup table. + */ + arizona->pdata.reset = devm_gpiod_get(arizona->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(arizona->pdata.reset)) { + ret = PTR_ERR(arizona->pdata.reset); + if (ret == -ENOENT) { + dev_info_once(arizona->dev, + "Deferring probe till GPIO lookup is registered\n"); + ret = -EPROBE_DEFER; + } + return dev_err_probe(arizona->dev, ret, "getting reset GPIO\n"); + } + + return 0; +} + +/* + * The AOSP 3.5 mm Headset: Accessory Specification gives the following values: + * Function A Play/Pause: 0 ohm + * Function D Voice assistant: 135 ohm + * Function B Volume Up 240 ohm + * Function C Volume Down 470 ohm + * Minimum Mic DC resistance 1000 ohm + * Minimum Ear speaker impedance 16 ohm + * Note the first max value below must be less then the min. speaker impedance, + * to allow CTIA/OMTP detection to work. The other max values are the closest + * value from extcon-arizona.c:arizona_micd_levels halfway 2 button resistances. + */ +static const struct arizona_micd_range arizona_micd_aosp_ranges[] = { + { .max = 11, .key = KEY_PLAYPAUSE }, + { .max = 186, .key = KEY_VOICECOMMAND }, + { .max = 348, .key = KEY_VOLUMEUP }, + { .max = 752, .key = KEY_VOLUMEDOWN }, +}; + +static int arizona_spi_acpi_probe(struct arizona *arizona) +{ + struct acpi_device *adev = ACPI_COMPANION(arizona->dev); + int ret; + + if (acpi_dev_hid_uid_match(adev, "10WM5102", NULL)) + ret = arizona_spi_acpi_android_probe(arizona); + else + ret = arizona_spi_acpi_windows_probe(arizona); + + if (ret) + return ret; + /* * Some DSDTs wrongly declare the IRQ trigger-type as IRQF_TRIGGER_FALLING * The IRQ line will stay low when a new IRQ event happens between reading @@ -118,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; } @@ -130,6 +174,10 @@ static const struct acpi_device_id arizona_acpi_match[] = { .id = "WM510205", .driver_data = WM5102, }, + { + .id = "10WM5102", + .driver_data = WM5102, + }, { } }; MODULE_DEVICE_TABLE(acpi, arizona_acpi_match); @@ -142,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)) @@ -206,13 +247,11 @@ static int arizona_spi_probe(struct spi_device *spi) return arizona_dev_init(arizona); } -static int arizona_spi_remove(struct spi_device *spi) +static void arizona_spi_remove(struct spi_device *spi) { struct arizona *arizona = spi_get_drvdata(spi); arizona_dev_exit(arizona); - - return 0; } static const struct spi_device_id arizona_spi_ids[] = { @@ -225,11 +264,23 @@ static const struct spi_device_id arizona_spi_ids[] = { }; MODULE_DEVICE_TABLE(spi, arizona_spi_ids); +#ifdef CONFIG_OF +static const struct of_device_id arizona_spi_of_match[] = { + { .compatible = "wlf,wm5102", .data = (void *)WM5102 }, + { .compatible = "wlf,wm5110", .data = (void *)WM5110 }, + { .compatible = "wlf,wm8280", .data = (void *)WM8280 }, + { .compatible = "wlf,wm1831", .data = (void *)WM1831 }, + { .compatible = "cirrus,cs47l24", .data = (void *)CS47L24 }, + {}, +}; +MODULE_DEVICE_TABLE(of, arizona_spi_of_match); +#endif + static struct spi_driver arizona_spi_driver = { .driver = { .name = "arizona", - .pm = &arizona_pm_ops, - .of_match_table = of_match_ptr(arizona_of_match), + .pm = pm_ptr(&arizona_pm_ops), + .of_match_table = of_match_ptr(arizona_spi_of_match), .acpi_match_table = ACPI_PTR(arizona_acpi_match), }, .probe = arizona_spi_probe, diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h index 801cbbcd71cb..66d6092d0851 100644 --- a/drivers/mfd/arizona.h +++ b/drivers/mfd/arizona.h @@ -28,8 +28,6 @@ extern const struct regmap_config wm8998_i2c_regmap; extern const struct dev_pm_ops arizona_pm_ops; -extern const struct of_device_id arizona_of_match[]; - extern const struct regmap_irq_chip wm5102_aod; extern const struct regmap_irq_chip wm5102_irq; diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c index 3adaec6c37df..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 @@ -116,8 +116,7 @@ static const struct of_device_id as3711_of_match[] = { }; #endif -static int as3711_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int as3711_i2c_probe(struct i2c_client *client) { struct as3711 *as3711; struct as3711_platform_data *pdata; diff --git a/drivers/mfd/as3722.c b/drivers/mfd/as3722.c index 38665efae4f0..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, @@ -333,8 +333,7 @@ static int as3722_i2c_of_probe(struct i2c_client *i2c, return 0; } -static int as3722_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int as3722_i2c_probe(struct i2c_client *i2c) { struct as3722 *as3722; unsigned long irq_flags; @@ -395,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; @@ -431,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/asic3.c b/drivers/mfd/asic3.c deleted file mode 100644 index 8d58c8df46cf..000000000000 --- a/drivers/mfd/asic3.c +++ /dev/null @@ -1,1070 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * driver/mfd/asic3.c - * - * Compaq ASIC3 support. - * - * Copyright 2001 Compaq Computer Corporation. - * Copyright 2004-2005 Phil Blundell - * Copyright 2007-2008 OpenedHand Ltd. - * - * Authors: Phil Blundell <pb@handhelds.org>, - * Samuel Ortiz <sameo@openedhand.com> - */ - -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/irq.h> -#include <linux/gpio/driver.h> -#include <linux/export.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/platform_device.h> - -#include <linux/mfd/asic3.h> -#include <linux/mfd/core.h> -#include <linux/mfd/ds1wm.h> -#include <linux/mfd/tmio.h> - -#include <linux/mmc/host.h> - -enum { - ASIC3_CLOCK_SPI, - ASIC3_CLOCK_OWM, - ASIC3_CLOCK_PWM0, - ASIC3_CLOCK_PWM1, - ASIC3_CLOCK_LED0, - ASIC3_CLOCK_LED1, - ASIC3_CLOCK_LED2, - ASIC3_CLOCK_SD_HOST, - ASIC3_CLOCK_SD_BUS, - ASIC3_CLOCK_SMBUS, - ASIC3_CLOCK_EX0, - ASIC3_CLOCK_EX1, -}; - -struct asic3_clk { - int enabled; - unsigned int cdex; - unsigned long rate; -}; - -#define INIT_CDEX(_name, _rate) \ - [ASIC3_CLOCK_##_name] = { \ - .cdex = CLOCK_CDEX_##_name, \ - .rate = _rate, \ - } - -static struct asic3_clk asic3_clk_init[] __initdata = { - INIT_CDEX(SPI, 0), - INIT_CDEX(OWM, 5000000), - INIT_CDEX(PWM0, 0), - INIT_CDEX(PWM1, 0), - INIT_CDEX(LED0, 0), - INIT_CDEX(LED1, 0), - INIT_CDEX(LED2, 0), - INIT_CDEX(SD_HOST, 24576000), - INIT_CDEX(SD_BUS, 12288000), - INIT_CDEX(SMBUS, 0), - INIT_CDEX(EX0, 32768), - INIT_CDEX(EX1, 24576000), -}; - -struct asic3 { - void __iomem *mapping; - unsigned int bus_shift; - unsigned int irq_nr; - unsigned int irq_base; - raw_spinlock_t lock; - u16 irq_bothedge[4]; - struct gpio_chip gpio; - struct device *dev; - void __iomem *tmio_cnf; - - struct asic3_clk clocks[ARRAY_SIZE(asic3_clk_init)]; -}; - -static int asic3_gpio_get(struct gpio_chip *chip, unsigned offset); - -void asic3_write_register(struct asic3 *asic, unsigned int reg, u32 value) -{ - iowrite16(value, asic->mapping + - (reg >> asic->bus_shift)); -} -EXPORT_SYMBOL_GPL(asic3_write_register); - -u32 asic3_read_register(struct asic3 *asic, unsigned int reg) -{ - return ioread16(asic->mapping + - (reg >> asic->bus_shift)); -} -EXPORT_SYMBOL_GPL(asic3_read_register); - -static void asic3_set_register(struct asic3 *asic, u32 reg, u32 bits, bool set) -{ - unsigned long flags; - u32 val; - - raw_spin_lock_irqsave(&asic->lock, flags); - val = asic3_read_register(asic, reg); - if (set) - val |= bits; - else - val &= ~bits; - asic3_write_register(asic, reg, val); - raw_spin_unlock_irqrestore(&asic->lock, flags); -} - -/* IRQs */ -#define MAX_ASIC_ISR_LOOPS 20 -#define ASIC3_GPIO_BASE_INCR \ - (ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE) - -static void asic3_irq_flip_edge(struct asic3 *asic, - u32 base, int bit) -{ - u16 edge; - unsigned long flags; - - raw_spin_lock_irqsave(&asic->lock, flags); - edge = asic3_read_register(asic, - base + ASIC3_GPIO_EDGE_TRIGGER); - edge ^= bit; - asic3_write_register(asic, - base + ASIC3_GPIO_EDGE_TRIGGER, edge); - raw_spin_unlock_irqrestore(&asic->lock, flags); -} - -static void asic3_irq_demux(struct irq_desc *desc) -{ - struct asic3 *asic = irq_desc_get_handler_data(desc); - struct irq_data *data = irq_desc_get_irq_data(desc); - int iter, i; - unsigned long flags; - - data->chip->irq_ack(data); - - for (iter = 0 ; iter < MAX_ASIC_ISR_LOOPS; iter++) { - u32 status; - int bank; - - raw_spin_lock_irqsave(&asic->lock, flags); - status = asic3_read_register(asic, - ASIC3_OFFSET(INTR, P_INT_STAT)); - raw_spin_unlock_irqrestore(&asic->lock, flags); - - /* Check all ten register bits */ - if ((status & 0x3ff) == 0) - break; - - /* Handle GPIO IRQs */ - for (bank = 0; bank < ASIC3_NUM_GPIO_BANKS; bank++) { - if (status & (1 << bank)) { - unsigned long base, istat; - - base = ASIC3_GPIO_A_BASE - + bank * ASIC3_GPIO_BASE_INCR; - raw_spin_lock_irqsave(&asic->lock, flags); - istat = asic3_read_register(asic, - base + - ASIC3_GPIO_INT_STATUS); - /* Clearing IntStatus */ - asic3_write_register(asic, - base + - ASIC3_GPIO_INT_STATUS, 0); - raw_spin_unlock_irqrestore(&asic->lock, flags); - - for (i = 0; i < ASIC3_GPIOS_PER_BANK; i++) { - int bit = (1 << i); - unsigned int irqnr; - - if (!(istat & bit)) - continue; - - irqnr = asic->irq_base + - (ASIC3_GPIOS_PER_BANK * bank) - + i; - generic_handle_irq(irqnr); - if (asic->irq_bothedge[bank] & bit) - asic3_irq_flip_edge(asic, base, - bit); - } - } - } - - /* Handle remaining IRQs in the status register */ - for (i = ASIC3_NUM_GPIOS; i < ASIC3_NR_IRQS; i++) { - /* They start at bit 4 and go up */ - if (status & (1 << (i - ASIC3_NUM_GPIOS + 4))) - generic_handle_irq(asic->irq_base + i); - } - } - - if (iter >= MAX_ASIC_ISR_LOOPS) - dev_err(asic->dev, "interrupt processing overrun\n"); -} - -static inline int asic3_irq_to_bank(struct asic3 *asic, int irq) -{ - int n; - - n = (irq - asic->irq_base) >> 4; - - return (n * (ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE)); -} - -static inline int asic3_irq_to_index(struct asic3 *asic, int irq) -{ - return (irq - asic->irq_base) & 0xf; -} - -static void asic3_mask_gpio_irq(struct irq_data *data) -{ - struct asic3 *asic = irq_data_get_irq_chip_data(data); - u32 val, bank, index; - unsigned long flags; - - bank = asic3_irq_to_bank(asic, data->irq); - index = asic3_irq_to_index(asic, data->irq); - - raw_spin_lock_irqsave(&asic->lock, flags); - val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK); - val |= 1 << index; - asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val); - raw_spin_unlock_irqrestore(&asic->lock, flags); -} - -static void asic3_mask_irq(struct irq_data *data) -{ - struct asic3 *asic = irq_data_get_irq_chip_data(data); - int regval; - unsigned long flags; - - raw_spin_lock_irqsave(&asic->lock, flags); - regval = asic3_read_register(asic, - ASIC3_INTR_BASE + - ASIC3_INTR_INT_MASK); - - regval &= ~(ASIC3_INTMASK_MASK0 << - (data->irq - (asic->irq_base + ASIC3_NUM_GPIOS))); - - asic3_write_register(asic, - ASIC3_INTR_BASE + - ASIC3_INTR_INT_MASK, - regval); - raw_spin_unlock_irqrestore(&asic->lock, flags); -} - -static void asic3_unmask_gpio_irq(struct irq_data *data) -{ - struct asic3 *asic = irq_data_get_irq_chip_data(data); - u32 val, bank, index; - unsigned long flags; - - bank = asic3_irq_to_bank(asic, data->irq); - index = asic3_irq_to_index(asic, data->irq); - - raw_spin_lock_irqsave(&asic->lock, flags); - val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK); - val &= ~(1 << index); - asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val); - raw_spin_unlock_irqrestore(&asic->lock, flags); -} - -static void asic3_unmask_irq(struct irq_data *data) -{ - struct asic3 *asic = irq_data_get_irq_chip_data(data); - int regval; - unsigned long flags; - - raw_spin_lock_irqsave(&asic->lock, flags); - regval = asic3_read_register(asic, - ASIC3_INTR_BASE + - ASIC3_INTR_INT_MASK); - - regval |= (ASIC3_INTMASK_MASK0 << - (data->irq - (asic->irq_base + ASIC3_NUM_GPIOS))); - - asic3_write_register(asic, - ASIC3_INTR_BASE + - ASIC3_INTR_INT_MASK, - regval); - raw_spin_unlock_irqrestore(&asic->lock, flags); -} - -static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type) -{ - struct asic3 *asic = irq_data_get_irq_chip_data(data); - u32 bank, index; - u16 trigger, level, edge, bit; - unsigned long flags; - - bank = asic3_irq_to_bank(asic, data->irq); - index = asic3_irq_to_index(asic, data->irq); - bit = 1<<index; - - raw_spin_lock_irqsave(&asic->lock, flags); - level = asic3_read_register(asic, - bank + ASIC3_GPIO_LEVEL_TRIGGER); - edge = asic3_read_register(asic, - bank + ASIC3_GPIO_EDGE_TRIGGER); - trigger = asic3_read_register(asic, - bank + ASIC3_GPIO_TRIGGER_TYPE); - asic->irq_bothedge[(data->irq - asic->irq_base) >> 4] &= ~bit; - - if (type == IRQ_TYPE_EDGE_RISING) { - trigger |= bit; - edge |= bit; - } else if (type == IRQ_TYPE_EDGE_FALLING) { - trigger |= bit; - edge &= ~bit; - } else if (type == IRQ_TYPE_EDGE_BOTH) { - trigger |= bit; - if (asic3_gpio_get(&asic->gpio, data->irq - asic->irq_base)) - edge &= ~bit; - else - edge |= bit; - asic->irq_bothedge[(data->irq - asic->irq_base) >> 4] |= bit; - } else if (type == IRQ_TYPE_LEVEL_LOW) { - trigger &= ~bit; - level &= ~bit; - } else if (type == IRQ_TYPE_LEVEL_HIGH) { - trigger &= ~bit; - level |= bit; - } else { - /* - * if type == IRQ_TYPE_NONE, we should mask interrupts, but - * be careful to not unmask them if mask was also called. - * Probably need internal state for mask. - */ - dev_notice(asic->dev, "irq type not changed\n"); - } - asic3_write_register(asic, bank + ASIC3_GPIO_LEVEL_TRIGGER, - level); - asic3_write_register(asic, bank + ASIC3_GPIO_EDGE_TRIGGER, - edge); - asic3_write_register(asic, bank + ASIC3_GPIO_TRIGGER_TYPE, - trigger); - raw_spin_unlock_irqrestore(&asic->lock, flags); - return 0; -} - -static int asic3_gpio_irq_set_wake(struct irq_data *data, unsigned int on) -{ - struct asic3 *asic = irq_data_get_irq_chip_data(data); - u32 bank, index; - u16 bit; - - bank = asic3_irq_to_bank(asic, data->irq); - index = asic3_irq_to_index(asic, data->irq); - bit = 1<<index; - - asic3_set_register(asic, bank + ASIC3_GPIO_SLEEP_MASK, bit, !on); - - return 0; -} - -static struct irq_chip asic3_gpio_irq_chip = { - .name = "ASIC3-GPIO", - .irq_ack = asic3_mask_gpio_irq, - .irq_mask = asic3_mask_gpio_irq, - .irq_unmask = asic3_unmask_gpio_irq, - .irq_set_type = asic3_gpio_irq_type, - .irq_set_wake = asic3_gpio_irq_set_wake, -}; - -static struct irq_chip asic3_irq_chip = { - .name = "ASIC3", - .irq_ack = asic3_mask_irq, - .irq_mask = asic3_mask_irq, - .irq_unmask = asic3_unmask_irq, -}; - -static int __init asic3_irq_probe(struct platform_device *pdev) -{ - struct asic3 *asic = platform_get_drvdata(pdev); - unsigned long clksel = 0; - unsigned int irq, irq_base; - int ret; - - ret = platform_get_irq(pdev, 0); - if (ret < 0) - return ret; - asic->irq_nr = ret; - - /* turn on clock to IRQ controller */ - clksel |= CLOCK_SEL_CX; - asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), - clksel); - - irq_base = asic->irq_base; - - for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) { - if (irq < asic->irq_base + ASIC3_NUM_GPIOS) - irq_set_chip(irq, &asic3_gpio_irq_chip); - else - irq_set_chip(irq, &asic3_irq_chip); - - irq_set_chip_data(irq, asic); - irq_set_handler(irq, handle_level_irq); - irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); - } - - asic3_write_register(asic, ASIC3_OFFSET(INTR, INT_MASK), - ASIC3_INTMASK_GINTMASK); - - irq_set_chained_handler_and_data(asic->irq_nr, asic3_irq_demux, asic); - irq_set_irq_type(asic->irq_nr, IRQ_TYPE_EDGE_RISING); - - return 0; -} - -static void asic3_irq_remove(struct platform_device *pdev) -{ - struct asic3 *asic = platform_get_drvdata(pdev); - unsigned int irq, irq_base; - - irq_base = asic->irq_base; - - for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) { - irq_set_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); - irq_set_chip_and_handler(irq, NULL, NULL); - irq_set_chip_data(irq, NULL); - } - irq_set_chained_handler(asic->irq_nr, NULL); -} - -/* GPIOs */ -static int asic3_gpio_direction(struct gpio_chip *chip, - unsigned offset, int out) -{ - u32 mask = ASIC3_GPIO_TO_MASK(offset), out_reg; - unsigned int gpio_base; - unsigned long flags; - struct asic3 *asic; - - asic = gpiochip_get_data(chip); - gpio_base = ASIC3_GPIO_TO_BASE(offset); - - if (gpio_base > ASIC3_GPIO_D_BASE) { - dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n", - gpio_base, offset); - return -EINVAL; - } - - raw_spin_lock_irqsave(&asic->lock, flags); - - out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_DIRECTION); - - /* Input is 0, Output is 1 */ - if (out) - out_reg |= mask; - else - out_reg &= ~mask; - - asic3_write_register(asic, gpio_base + ASIC3_GPIO_DIRECTION, out_reg); - - raw_spin_unlock_irqrestore(&asic->lock, flags); - - return 0; - -} - -static int asic3_gpio_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - return asic3_gpio_direction(chip, offset, 0); -} - -static int asic3_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - return asic3_gpio_direction(chip, offset, 1); -} - -static int asic3_gpio_get(struct gpio_chip *chip, - unsigned offset) -{ - unsigned int gpio_base; - u32 mask = ASIC3_GPIO_TO_MASK(offset); - struct asic3 *asic; - - asic = gpiochip_get_data(chip); - gpio_base = ASIC3_GPIO_TO_BASE(offset); - - if (gpio_base > ASIC3_GPIO_D_BASE) { - dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n", - gpio_base, offset); - return -EINVAL; - } - - return !!(asic3_read_register(asic, - gpio_base + ASIC3_GPIO_STATUS) & mask); -} - -static void asic3_gpio_set(struct gpio_chip *chip, - unsigned offset, int value) -{ - u32 mask, out_reg; - unsigned int gpio_base; - unsigned long flags; - struct asic3 *asic; - - asic = gpiochip_get_data(chip); - gpio_base = ASIC3_GPIO_TO_BASE(offset); - - if (gpio_base > ASIC3_GPIO_D_BASE) { - dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n", - gpio_base, offset); - return; - } - - mask = ASIC3_GPIO_TO_MASK(offset); - - raw_spin_lock_irqsave(&asic->lock, flags); - - out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_OUT); - - if (value) - out_reg |= mask; - else - out_reg &= ~mask; - - asic3_write_register(asic, gpio_base + ASIC3_GPIO_OUT, out_reg); - - raw_spin_unlock_irqrestore(&asic->lock, flags); -} - -static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct asic3 *asic = gpiochip_get_data(chip); - - return asic->irq_base + offset; -} - -static __init int asic3_gpio_probe(struct platform_device *pdev, - u16 *gpio_config, int num) -{ - struct asic3 *asic = platform_get_drvdata(pdev); - u16 alt_reg[ASIC3_NUM_GPIO_BANKS]; - u16 out_reg[ASIC3_NUM_GPIO_BANKS]; - u16 dir_reg[ASIC3_NUM_GPIO_BANKS]; - int i; - - memset(alt_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16)); - memset(out_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16)); - memset(dir_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16)); - - /* Enable all GPIOs */ - asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, MASK), 0xffff); - asic3_write_register(asic, ASIC3_GPIO_OFFSET(B, MASK), 0xffff); - asic3_write_register(asic, ASIC3_GPIO_OFFSET(C, MASK), 0xffff); - asic3_write_register(asic, ASIC3_GPIO_OFFSET(D, MASK), 0xffff); - - for (i = 0; i < num; i++) { - u8 alt, pin, dir, init, bank_num, bit_num; - u16 config = gpio_config[i]; - - pin = ASIC3_CONFIG_GPIO_PIN(config); - alt = ASIC3_CONFIG_GPIO_ALT(config); - dir = ASIC3_CONFIG_GPIO_DIR(config); - init = ASIC3_CONFIG_GPIO_INIT(config); - - bank_num = ASIC3_GPIO_TO_BANK(pin); - bit_num = ASIC3_GPIO_TO_BIT(pin); - - alt_reg[bank_num] |= (alt << bit_num); - out_reg[bank_num] |= (init << bit_num); - dir_reg[bank_num] |= (dir << bit_num); - } - - for (i = 0; i < ASIC3_NUM_GPIO_BANKS; i++) { - asic3_write_register(asic, - ASIC3_BANK_TO_BASE(i) + - ASIC3_GPIO_DIRECTION, - dir_reg[i]); - asic3_write_register(asic, - ASIC3_BANK_TO_BASE(i) + ASIC3_GPIO_OUT, - out_reg[i]); - asic3_write_register(asic, - ASIC3_BANK_TO_BASE(i) + - ASIC3_GPIO_ALT_FUNCTION, - alt_reg[i]); - } - - return gpiochip_add_data(&asic->gpio, asic); -} - -static int asic3_gpio_remove(struct platform_device *pdev) -{ - struct asic3 *asic = platform_get_drvdata(pdev); - - gpiochip_remove(&asic->gpio); - return 0; -} - -static void asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk) -{ - unsigned long flags; - u32 cdex; - - raw_spin_lock_irqsave(&asic->lock, flags); - if (clk->enabled++ == 0) { - cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX)); - cdex |= clk->cdex; - asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex); - } - raw_spin_unlock_irqrestore(&asic->lock, flags); -} - -static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk) -{ - unsigned long flags; - u32 cdex; - - WARN_ON(clk->enabled == 0); - - raw_spin_lock_irqsave(&asic->lock, flags); - if (--clk->enabled == 0) { - cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX)); - cdex &= ~clk->cdex; - asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex); - } - raw_spin_unlock_irqrestore(&asic->lock, flags); -} - -/* MFD cells (SPI, PWM, LED, DS1WM, MMC) */ -static struct ds1wm_driver_data ds1wm_pdata = { - .active_high = 1, - .reset_recover_delay = 1, -}; - -static struct resource ds1wm_resources[] = { - { - .start = ASIC3_OWM_BASE, - .end = ASIC3_OWM_BASE + 0x13, - .flags = IORESOURCE_MEM, - }, - { - .start = ASIC3_IRQ_OWM, - .end = ASIC3_IRQ_OWM, - .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, - }, -}; - -static int ds1wm_enable(struct platform_device *pdev) -{ - struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); - - /* Turn on external clocks and the OWM clock */ - asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX0]); - asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]); - asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_OWM]); - usleep_range(1000, 5000); - - /* Reset and enable DS1WM */ - asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET), - ASIC3_EXTCF_OWM_RESET, 1); - usleep_range(1000, 5000); - asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET), - ASIC3_EXTCF_OWM_RESET, 0); - usleep_range(1000, 5000); - asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), - ASIC3_EXTCF_OWM_EN, 1); - usleep_range(1000, 5000); - - return 0; -} - -static int ds1wm_disable(struct platform_device *pdev) -{ - struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); - - asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), - ASIC3_EXTCF_OWM_EN, 0); - - asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_OWM]); - asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX0]); - asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX1]); - - return 0; -} - -static const struct mfd_cell asic3_cell_ds1wm = { - .name = "ds1wm", - .enable = ds1wm_enable, - .disable = ds1wm_disable, - .platform_data = &ds1wm_pdata, - .pdata_size = sizeof(ds1wm_pdata), - .num_resources = ARRAY_SIZE(ds1wm_resources), - .resources = ds1wm_resources, -}; - -static void asic3_mmc_pwr(struct platform_device *pdev, int state) -{ - struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); - - tmio_core_mmc_pwr(asic->tmio_cnf, 1 - asic->bus_shift, state); -} - -static void asic3_mmc_clk_div(struct platform_device *pdev, int state) -{ - struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); - - tmio_core_mmc_clk_div(asic->tmio_cnf, 1 - asic->bus_shift, state); -} - -static struct tmio_mmc_data asic3_mmc_data = { - .hclk = 24576000, - .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, - .set_pwr = asic3_mmc_pwr, - .set_clk_div = asic3_mmc_clk_div, -}; - -static struct resource asic3_mmc_resources[] = { - DEFINE_RES_MEM(ASIC3_SD_CTRL_BASE, 0x400), - DEFINE_RES_IRQ(0) -}; - -static int asic3_mmc_enable(struct platform_device *pdev) -{ - struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); - - /* Not sure if it must be done bit by bit, but leaving as-is */ - asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF), - ASIC3_SDHWCTRL_LEVCD, 1); - asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF), - ASIC3_SDHWCTRL_LEVWP, 1); - asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF), - ASIC3_SDHWCTRL_SUSPEND, 0); - asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF), - ASIC3_SDHWCTRL_PCLR, 0); - - asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX0]); - /* CLK32 used for card detection and for interruption detection - * when HCLK is stopped. - */ - asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]); - usleep_range(1000, 5000); - - /* HCLK 24.576 MHz, BCLK 12.288 MHz: */ - asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), - CLOCK_SEL_CX | CLOCK_SEL_SD_HCLK_SEL); - - asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_SD_HOST]); - asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_SD_BUS]); - usleep_range(1000, 5000); - - asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), - ASIC3_EXTCF_SD_MEM_ENABLE, 1); - - /* Enable SD card slot 3.3V power supply */ - asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF), - ASIC3_SDHWCTRL_SDPWR, 1); - - /* ASIC3_SD_CTRL_BASE assumes 32-bit addressing, TMIO is 16-bit */ - tmio_core_mmc_enable(asic->tmio_cnf, 1 - asic->bus_shift, - ASIC3_SD_CTRL_BASE >> 1); - - return 0; -} - -static int asic3_mmc_disable(struct platform_device *pdev) -{ - struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); - - /* Put in suspend mode */ - asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF), - ASIC3_SDHWCTRL_SUSPEND, 1); - - /* Disable clocks */ - asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_SD_HOST]); - asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_SD_BUS]); - asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX0]); - asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX1]); - return 0; -} - -static const struct mfd_cell asic3_cell_mmc = { - .name = "tmio-mmc", - .enable = asic3_mmc_enable, - .disable = asic3_mmc_disable, - .suspend = asic3_mmc_disable, - .resume = asic3_mmc_enable, - .platform_data = &asic3_mmc_data, - .pdata_size = sizeof(asic3_mmc_data), - .num_resources = ARRAY_SIZE(asic3_mmc_resources), - .resources = asic3_mmc_resources, -}; - -static const int clock_ledn[ASIC3_NUM_LEDS] = { - [0] = ASIC3_CLOCK_LED0, - [1] = ASIC3_CLOCK_LED1, - [2] = ASIC3_CLOCK_LED2, -}; - -static int asic3_leds_enable(struct platform_device *pdev) -{ - const struct mfd_cell *cell = mfd_get_cell(pdev); - struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); - - asic3_clk_enable(asic, &asic->clocks[clock_ledn[cell->id]]); - - return 0; -} - -static int asic3_leds_disable(struct platform_device *pdev) -{ - const struct mfd_cell *cell = mfd_get_cell(pdev); - struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); - - asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]); - - return 0; -} - -static int asic3_leds_suspend(struct platform_device *pdev) -{ - const struct mfd_cell *cell = mfd_get_cell(pdev); - struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); - - while (asic3_gpio_get(&asic->gpio, ASIC3_GPIO(C, cell->id)) != 0) - usleep_range(1000, 5000); - - asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]); - - return 0; -} - -static struct mfd_cell asic3_cell_leds[ASIC3_NUM_LEDS] = { - [0] = { - .name = "leds-asic3", - .id = 0, - .enable = asic3_leds_enable, - .disable = asic3_leds_disable, - .suspend = asic3_leds_suspend, - .resume = asic3_leds_enable, - }, - [1] = { - .name = "leds-asic3", - .id = 1, - .enable = asic3_leds_enable, - .disable = asic3_leds_disable, - .suspend = asic3_leds_suspend, - .resume = asic3_leds_enable, - }, - [2] = { - .name = "leds-asic3", - .id = 2, - .enable = asic3_leds_enable, - .disable = asic3_leds_disable, - .suspend = asic3_leds_suspend, - .resume = asic3_leds_enable, - }, -}; - -static int __init asic3_mfd_probe(struct platform_device *pdev, - struct asic3_platform_data *pdata, - struct resource *mem) -{ - struct asic3 *asic = platform_get_drvdata(pdev); - struct resource *mem_sdio; - int irq, ret; - - mem_sdio = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!mem_sdio) - dev_dbg(asic->dev, "no SDIO MEM resource\n"); - - irq = platform_get_irq(pdev, 1); - if (irq < 0) - dev_dbg(asic->dev, "no SDIO IRQ resource\n"); - - /* DS1WM */ - asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), - ASIC3_EXTCF_OWM_SMB, 0); - - ds1wm_resources[0].start >>= asic->bus_shift; - ds1wm_resources[0].end >>= asic->bus_shift; - - /* MMC */ - if (mem_sdio) { - asic->tmio_cnf = ioremap((ASIC3_SD_CONFIG_BASE >> - asic->bus_shift) + mem_sdio->start, - ASIC3_SD_CONFIG_SIZE >> asic->bus_shift); - if (!asic->tmio_cnf) { - ret = -ENOMEM; - dev_dbg(asic->dev, "Couldn't ioremap SD_CONFIG\n"); - goto out; - } - } - asic3_mmc_resources[0].start >>= asic->bus_shift; - asic3_mmc_resources[0].end >>= asic->bus_shift; - - if (pdata->clock_rate) { - ds1wm_pdata.clock_rate = pdata->clock_rate; - ret = mfd_add_devices(&pdev->dev, pdev->id, - &asic3_cell_ds1wm, 1, mem, asic->irq_base, NULL); - if (ret < 0) - goto out; - } - - if (mem_sdio && (irq >= 0)) { - ret = mfd_add_devices(&pdev->dev, pdev->id, - &asic3_cell_mmc, 1, mem_sdio, irq, NULL); - if (ret < 0) - goto out; - } - - ret = 0; - if (pdata->leds) { - int i; - - for (i = 0; i < ASIC3_NUM_LEDS; ++i) { - asic3_cell_leds[i].platform_data = &pdata->leds[i]; - asic3_cell_leds[i].pdata_size = sizeof(pdata->leds[i]); - } - ret = mfd_add_devices(&pdev->dev, 0, - asic3_cell_leds, ASIC3_NUM_LEDS, NULL, 0, NULL); - } - - out: - return ret; -} - -static void asic3_mfd_remove(struct platform_device *pdev) -{ - struct asic3 *asic = platform_get_drvdata(pdev); - - mfd_remove_devices(&pdev->dev); - iounmap(asic->tmio_cnf); -} - -/* Core */ -static int __init asic3_probe(struct platform_device *pdev) -{ - struct asic3_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct asic3 *asic; - struct resource *mem; - unsigned long clksel; - int ret = 0; - - asic = devm_kzalloc(&pdev->dev, - sizeof(struct asic3), GFP_KERNEL); - if (!asic) - return -ENOMEM; - - raw_spin_lock_init(&asic->lock); - platform_set_drvdata(pdev, asic); - asic->dev = &pdev->dev; - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(asic->dev, "no MEM resource\n"); - return -ENOMEM; - } - - asic->mapping = ioremap(mem->start, resource_size(mem)); - if (!asic->mapping) { - dev_err(asic->dev, "Couldn't ioremap\n"); - return -ENOMEM; - } - - asic->irq_base = pdata->irq_base; - - /* calculate bus shift from mem resource */ - asic->bus_shift = 2 - (resource_size(mem) >> 12); - - clksel = 0; - asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), clksel); - - ret = asic3_irq_probe(pdev); - if (ret < 0) { - dev_err(asic->dev, "Couldn't probe IRQs\n"); - goto out_unmap; - } - - asic->gpio.label = "asic3"; - asic->gpio.base = pdata->gpio_base; - asic->gpio.ngpio = ASIC3_NUM_GPIOS; - asic->gpio.get = asic3_gpio_get; - asic->gpio.set = asic3_gpio_set; - asic->gpio.direction_input = asic3_gpio_direction_input; - asic->gpio.direction_output = asic3_gpio_direction_output; - asic->gpio.to_irq = asic3_gpio_to_irq; - - ret = asic3_gpio_probe(pdev, - pdata->gpio_config, - pdata->gpio_config_num); - if (ret < 0) { - dev_err(asic->dev, "GPIO probe failed\n"); - goto out_irq; - } - - /* Making a per-device copy is only needed for the - * theoretical case of multiple ASIC3s on one board: - */ - memcpy(asic->clocks, asic3_clk_init, sizeof(asic3_clk_init)); - - asic3_mfd_probe(pdev, pdata, mem); - - asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), - (ASIC3_EXTCF_CF0_BUF_EN|ASIC3_EXTCF_CF0_PWAIT_EN), 1); - - dev_info(asic->dev, "ASIC3 Core driver\n"); - - return 0; - - out_irq: - asic3_irq_remove(pdev); - - out_unmap: - iounmap(asic->mapping); - - return ret; -} - -static int asic3_remove(struct platform_device *pdev) -{ - int ret; - struct asic3 *asic = platform_get_drvdata(pdev); - - asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), - (ASIC3_EXTCF_CF0_BUF_EN|ASIC3_EXTCF_CF0_PWAIT_EN), 0); - - asic3_mfd_remove(pdev); - - ret = asic3_gpio_remove(pdev); - if (ret < 0) - return ret; - asic3_irq_remove(pdev); - - asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), 0); - - iounmap(asic->mapping); - - return 0; -} - -static void asic3_shutdown(struct platform_device *pdev) -{ -} - -static struct platform_driver asic3_device_driver = { - .driver = { - .name = "asic3", - }, - .remove = asic3_remove, - .shutdown = asic3_shutdown, -}; - -static int __init asic3_init(void) -{ - int retval = 0; - - retval = platform_driver_probe(&asic3_device_driver, asic3_probe); - - return retval; -} - -subsys_initcall(asic3_init); diff --git a/drivers/mfd/atc260x-core.c b/drivers/mfd/atc260x-core.c index 7148ff5b05b1..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 @@ -100,8 +99,7 @@ static const struct regmap_irq_chip atc2603c_regmap_irq_chip = { .num_irqs = ARRAY_SIZE(atc2603c_regmap_irqs), .num_regs = 1, .status_base = ATC2603C_INTS_PD, - .mask_base = ATC2603C_INTS_MSK, - .mask_invert = true, + .unmask_base = ATC2603C_INTS_MSK, }; static const struct regmap_irq_chip atc2609a_regmap_irq_chip = { @@ -110,8 +108,7 @@ static const struct regmap_irq_chip atc2609a_regmap_irq_chip = { .num_irqs = ARRAY_SIZE(atc2609a_regmap_irqs), .num_regs = 1, .status_base = ATC2609A_INTS_PD, - .mask_base = ATC2609A_INTS_MSK, - .mask_invert = true, + .unmask_base = ATC2609A_INTS_MSK, }; static const struct resource atc2603c_onkey_resources[] = { @@ -238,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/atc260x-i2c.c b/drivers/mfd/atc260x-i2c.c index 5855efd09efc..fb53b0432a31 100644 --- a/drivers/mfd/atc260x-i2c.c +++ b/drivers/mfd/atc260x-i2c.c @@ -12,8 +12,7 @@ #include <linux/of.h> #include <linux/regmap.h> -static int atc260x_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int atc260x_i2c_probe(struct i2c_client *client) { struct atc260x *atc260x; struct regmap_config regmap_cfg; @@ -52,7 +51,7 @@ MODULE_DEVICE_TABLE(of, atc260x_i2c_of_match); static struct i2c_driver atc260x_i2c_driver = { .driver = { .name = "atc260x", - .of_match_table = of_match_ptr(atc260x_i2c_of_match), + .of_match_table = atc260x_i2c_of_match, }, .probe = atc260x_i2c_probe, }; diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c index d2f5c073fdf3..d5df5466eaf5 100644 --- a/drivers/mfd/atmel-flexcom.c +++ b/drivers/mfd/atmel-flexcom.c @@ -37,7 +37,6 @@ struct atmel_flexcom { static int atmel_flexcom_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct resource *res; struct atmel_flexcom *ddata; int err; @@ -55,8 +54,7 @@ static int atmel_flexcom_probe(struct platform_device *pdev) ddata->opmode > ATMEL_FLEXCOM_MODE_TWI) return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ddata->base = devm_ioremap_resource(&pdev->dev, res); + ddata->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(ddata->base)) return PTR_ERR(ddata->base); @@ -87,8 +85,7 @@ static const struct of_device_id atmel_flexcom_of_match[] = { }; MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match); -#ifdef CONFIG_PM_SLEEP -static int atmel_flexcom_resume(struct device *dev) +static int __maybe_unused atmel_flexcom_resume_noirq(struct device *dev) { struct atmel_flexcom *ddata = dev_get_drvdata(dev); int err; @@ -98,23 +95,23 @@ static int atmel_flexcom_resume(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); return 0; } -#endif -static SIMPLE_DEV_PM_OPS(atmel_flexcom_pm_ops, NULL, - atmel_flexcom_resume); +static const struct dev_pm_ops __maybe_unused atmel_flexcom_pm_ops = { + .resume_noirq = atmel_flexcom_resume_noirq, +}; static struct platform_driver atmel_flexcom_driver = { .probe = atmel_flexcom_probe, .driver = { .name = "atmel_flexcom", - .pm = &atmel_flexcom_pm_ops, + .pm = pm_ptr(&atmel_flexcom_pm_ops), .of_match_table = atmel_flexcom_of_match, }, }; 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 d96f1d689e7f..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 @@ -240,7 +247,7 @@ EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_cycle); * @conf: the SMC CS conf to apply * * Applies an SMC CS configuration. - * Only valid on at91sam9/avr32 SoCs. + * Only valid on at91sam9 SoCs. */ void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs, const struct atmel_smc_cs_conf *conf) @@ -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. @@ -281,7 +288,7 @@ EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_apply); * @conf: the SMC CS conf object to store the current conf * * Retrieve the SMC CS configuration. - * Only valid on at91sam9/avr32 SoCs. + * Only valid on at91sam9 SoCs. */ void atmel_smc_cs_conf_get(struct regmap *regmap, int cs, struct atmel_smc_cs_conf *conf) @@ -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. @@ -323,7 +330,7 @@ static const struct atmel_hsmc_reg_layout sama5d2_reg_layout = { .timing_regs_offset = 0x700, }; -static const struct of_device_id atmel_smc_ids[] = { +static const struct of_device_id atmel_smc_ids[] __maybe_unused = { { .compatible = "atmel,at91sam9260-smc", .data = NULL }, { .compatible = "atmel,sama5d3-smc", .data = &sama5d3_reg_layout }, { .compatible = "atmel,sama5d2-smc", .data = &sama5d2_reg_layout }, diff --git a/drivers/mfd/axp20x-i2c.c b/drivers/mfd/axp20x-i2c.c index 00ab48018d8d..5c93136f977e 100644 --- a/drivers/mfd/axp20x-i2c.c +++ b/drivers/mfd/axp20x-i2c.c @@ -22,8 +22,7 @@ #include <linux/regmap.h> #include <linux/slab.h> -static int axp20x_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int axp20x_i2c_probe(struct i2c_client *i2c) { struct axp20x_dev *axp20x; int ret; @@ -50,38 +49,45 @@ static int axp20x_i2c_probe(struct i2c_client *i2c, return axp20x_device_probe(axp20x); } -static int axp20x_i2c_remove(struct i2c_client *i2c) +static void axp20x_i2c_remove(struct i2c_client *i2c) { struct axp20x_dev *axp20x = i2c_get_clientdata(i2c); axp20x_device_remove(axp20x); - - return 0; } #ifdef CONFIG_OF static const struct of_device_id axp20x_i2c_of_match[] = { { .compatible = "x-powers,axp152", .data = (void *)AXP152_ID }, + { .compatible = "x-powers,axp192", .data = (void *)AXP192_ID }, { .compatible = "x-powers,axp202", .data = (void *)AXP202_ID }, { .compatible = "x-powers,axp209", .data = (void *)AXP209_ID }, { .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 }, { }, }; MODULE_DEVICE_TABLE(of, axp20x_i2c_of_match); #endif static const struct i2c_device_id axp20x_i2c_id[] = { - { "axp152", 0 }, - { "axp202", 0 }, - { "axp209", 0 }, - { "axp221", 0 }, - { "axp223", 0 }, - { "axp803", 0 }, - { "axp806", 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 4145a38b3890..c5f0ebae327f 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -22,8 +22,9 @@ #include <linux/mfd/axp20x.h> #include <linux/mfd/core.h> #include <linux/module.h> -#include <linux/of_device.h> -#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/property.h> +#include <linux/reboot.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -33,16 +34,21 @@ #define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4) static const char * const axp20x_model_names[] = { - "AXP152", - "AXP202", - "AXP209", - "AXP221", - "AXP223", - "AXP288", - "AXP803", - "AXP806", - "AXP809", - "AXP813", + [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[] = { @@ -92,6 +98,35 @@ static const struct regmap_access_table axp20x_volatile_table = { .n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges), }; +static const struct regmap_range axp192_writeable_ranges[] = { + regmap_reg_range(AXP192_DATACACHE(0), AXP192_DATACACHE(5)), + regmap_reg_range(AXP192_PWR_OUT_CTRL, AXP192_IRQ5_STATE), + regmap_reg_range(AXP20X_DCDC_MODE, AXP192_N_RSTO_CTRL), + regmap_reg_range(AXP20X_CC_CTRL, AXP20X_CC_CTRL), +}; + +static const struct regmap_range axp192_volatile_ranges[] = { + regmap_reg_range(AXP20X_PWR_INPUT_STATUS, AXP192_USB_OTG_STATUS), + regmap_reg_range(AXP192_IRQ1_STATE, AXP192_IRQ4_STATE), + regmap_reg_range(AXP192_IRQ5_STATE, AXP192_IRQ5_STATE), + regmap_reg_range(AXP20X_ACIN_V_ADC_H, AXP20X_IPSOUT_V_HIGH_L), + regmap_reg_range(AXP20X_TIMER_CTRL, AXP20X_TIMER_CTRL), + regmap_reg_range(AXP192_GPIO2_0_STATE, AXP192_GPIO2_0_STATE), + regmap_reg_range(AXP192_GPIO4_3_STATE, AXP192_GPIO4_3_STATE), + regmap_reg_range(AXP192_N_RSTO_CTRL, AXP192_N_RSTO_CTRL), + regmap_reg_range(AXP20X_CHRG_CC_31_24, AXP20X_CC_CTRL), +}; + +static const struct regmap_access_table axp192_writeable_table = { + .yes_ranges = axp192_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(axp192_writeable_ranges), +}; + +static const struct regmap_access_table axp192_volatile_table = { + .yes_ranges = axp192_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(axp192_volatile_ranges), +}; + /* AXP22x ranges are shared with the AXP809, as they cover the same range */ static const struct regmap_range axp22x_writeable_ranges[] = { regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE), @@ -119,18 +154,20 @@ static const struct regmap_access_table axp22x_volatile_table = { /* AXP288 ranges are shared with the AXP803, as they cover the same range */ static const struct regmap_range axp288_writeable_ranges[] = { + regmap_reg_range(AXP288_POWER_REASON, AXP288_POWER_REASON), regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE), regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5), }; static const struct regmap_range axp288_volatile_ranges[] = { regmap_reg_range(AXP20X_PWR_INPUT_STATUS, AXP288_POWER_REASON), + regmap_reg_range(AXP22X_PWR_OUT_CTRL1, AXP22X_ALDO3_V_OUT), regmap_reg_range(AXP288_BC_GLOBAL, AXP288_BC_GLOBAL), regmap_reg_range(AXP288_BC_DET_STAT, AXP20X_VBUS_IPSOUT_MGMT), regmap_reg_range(AXP20X_CHRG_BAK_CTRL, AXP20X_CHRG_BAK_CTRL), regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IPSOUT_V_HIGH_L), regmap_reg_range(AXP20X_TIMER_CTRL, AXP20X_TIMER_CTRL), - regmap_reg_range(AXP22X_GPIO_STATE, AXP22X_GPIO_STATE), + regmap_reg_range(AXP20X_GPIO1_CTRL, AXP22X_GPIO_STATE), regmap_reg_range(AXP288_RT_BATT_V_H, AXP288_RT_BATT_V_L), regmap_reg_range(AXP20X_FG_RES, AXP288_FG_CC_CAP_REG), }; @@ -153,6 +190,65 @@ static const struct regmap_range axp806_writeable_ranges[] = { regmap_reg_range(AXP806_REG_ADDR_EXT, AXP806_REG_ADDR_EXT), }; +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), +}; + +static const struct regmap_access_table axp313a_writeable_table = { + .yes_ranges = axp313a_writeable_ranges, + .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), }; @@ -167,11 +263,49 @@ static const struct regmap_access_table axp806_volatile_table = { .n_yes_ranges = ARRAY_SIZE(axp806_volatile_ranges), }; +static const struct regmap_range axp15060_writeable_ranges[] = { + regmap_reg_range(AXP15060_PWR_OUT_CTRL1, AXP15060_DCDC_MODE_CTRL2), + regmap_reg_range(AXP15060_OUTPUT_MONITOR_DISCHARGE, AXP15060_CPUSLDO_V_CTRL), + regmap_reg_range(AXP15060_PWR_WAKEUP_CTRL, AXP15060_PWR_DISABLE_DOWN_SEQ), + regmap_reg_range(AXP15060_PEK_KEY, AXP15060_PEK_KEY), + regmap_reg_range(AXP15060_IRQ1_EN, AXP15060_IRQ2_EN), + regmap_reg_range(AXP15060_IRQ1_STATE, AXP15060_IRQ2_STATE), +}; + +static const struct regmap_range axp15060_volatile_ranges[] = { + regmap_reg_range(AXP15060_STARTUP_SRC, AXP15060_STARTUP_SRC), + regmap_reg_range(AXP15060_PWR_WAKEUP_CTRL, AXP15060_PWR_DISABLE_DOWN_SEQ), + regmap_reg_range(AXP15060_IRQ1_STATE, AXP15060_IRQ2_STATE), +}; + +static const struct regmap_access_table axp15060_writeable_table = { + .yes_ranges = axp15060_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(axp15060_writeable_ranges), +}; + +static const struct regmap_access_table axp15060_volatile_table = { + .yes_ranges = axp15060_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(axp15060_volatile_ranges), +}; + static const struct resource axp152_pek_resources[] = { DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_RIS_EDGE, "PEK_DBR"), DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_FAL_EDGE, "PEK_DBF"), }; +static const struct resource axp192_ac_power_supply_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_ACIN_PLUGIN, "ACIN_PLUGIN"), + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_ACIN_REMOVAL, "ACIN_REMOVAL"), + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_ACIN_OVER_V, "ACIN_OVER_V"), +}; + +static const struct resource axp192_usb_power_supply_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"), + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"), + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_VBUS_VALID, "VBUS_VALID"), + DEFINE_RES_IRQ_NAMED(AXP192_IRQ_VBUS_NOT_VALID, "VBUS_NOT_VALID"), +}; + static const struct resource axp20x_ac_power_supply_resources[] = { DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_ACIN_PLUGIN, "ACIN_PLUGIN"), DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_ACIN_REMOVAL, "ACIN_REMOVAL"), @@ -195,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"), @@ -220,6 +360,16 @@ static const struct resource axp288_fuel_gauge_resources[] = { DEFINE_RES_IRQ(AXP288_IRQ_WL1), }; +static const struct resource axp313a_pek_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP313A_IRQ_PEK_RIS_EDGE, "PEK_DBR"), + 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"), @@ -235,13 +385,27 @@ static const struct resource axp809_pek_resources[] = { DEFINE_RES_IRQ_NAMED(AXP809_IRQ_PEK_FAL_EDGE, "PEK_DBF"), }; +static const struct resource axp15060_pek_resources[] = { + DEFINE_RES_IRQ_NAMED(AXP15060_IRQ_PEK_RIS_EDGE, "PEK_DBR"), + DEFINE_RES_IRQ_NAMED(AXP15060_IRQ_PEK_FAL_EDGE, "PEK_DBF"), +}; + static const struct regmap_config axp152_regmap_config = { .reg_bits = 8, .val_bits = 8, .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 = { + .reg_bits = 8, + .val_bits = 8, + .wr_table = &axp192_writeable_table, + .volatile_table = &axp192_volatile_table, + .max_register = AXP20X_CC_CTRL, + .cache_type = REGCACHE_MAPLE, }; static const struct regmap_config axp20x_regmap_config = { @@ -250,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 = { @@ -259,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 = { @@ -268,7 +432,34 @@ 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 = { + .reg_bits = 8, + .val_bits = 8, + .wr_table = &axp313a_writeable_table, + .volatile_table = &axp313a_volatile_table, + .max_register = AXP313A_IRQ_STATE, + .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 = { @@ -277,7 +468,16 @@ 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 = { + .reg_bits = 8, + .val_bits = 8, + .wr_table = &axp15060_writeable_table, + .volatile_table = &axp15060_volatile_table, + .max_register = AXP15060_IRQ2_STATE, + .cache_type = REGCACHE_MAPLE, }; #define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \ @@ -303,6 +503,42 @@ static const struct regmap_irq axp152_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP152, GPIO0_INPUT, 2, 0), }; +static const struct regmap_irq axp192_regmap_irqs[] = { + INIT_REGMAP_IRQ(AXP192, ACIN_OVER_V, 0, 7), + INIT_REGMAP_IRQ(AXP192, ACIN_PLUGIN, 0, 6), + INIT_REGMAP_IRQ(AXP192, ACIN_REMOVAL, 0, 5), + INIT_REGMAP_IRQ(AXP192, VBUS_OVER_V, 0, 4), + INIT_REGMAP_IRQ(AXP192, VBUS_PLUGIN, 0, 3), + INIT_REGMAP_IRQ(AXP192, VBUS_REMOVAL, 0, 2), + INIT_REGMAP_IRQ(AXP192, VBUS_V_LOW, 0, 1), + INIT_REGMAP_IRQ(AXP192, BATT_PLUGIN, 1, 7), + INIT_REGMAP_IRQ(AXP192, BATT_REMOVAL, 1, 6), + INIT_REGMAP_IRQ(AXP192, BATT_ENT_ACT_MODE, 1, 5), + INIT_REGMAP_IRQ(AXP192, BATT_EXIT_ACT_MODE, 1, 4), + INIT_REGMAP_IRQ(AXP192, CHARG, 1, 3), + INIT_REGMAP_IRQ(AXP192, CHARG_DONE, 1, 2), + INIT_REGMAP_IRQ(AXP192, BATT_TEMP_HIGH, 1, 1), + INIT_REGMAP_IRQ(AXP192, BATT_TEMP_LOW, 1, 0), + INIT_REGMAP_IRQ(AXP192, DIE_TEMP_HIGH, 2, 7), + INIT_REGMAP_IRQ(AXP192, CHARG_I_LOW, 2, 6), + INIT_REGMAP_IRQ(AXP192, DCDC1_V_LONG, 2, 5), + INIT_REGMAP_IRQ(AXP192, DCDC2_V_LONG, 2, 4), + INIT_REGMAP_IRQ(AXP192, DCDC3_V_LONG, 2, 3), + INIT_REGMAP_IRQ(AXP192, PEK_SHORT, 2, 1), + INIT_REGMAP_IRQ(AXP192, PEK_LONG, 2, 0), + INIT_REGMAP_IRQ(AXP192, N_OE_PWR_ON, 3, 7), + INIT_REGMAP_IRQ(AXP192, N_OE_PWR_OFF, 3, 6), + INIT_REGMAP_IRQ(AXP192, VBUS_VALID, 3, 5), + INIT_REGMAP_IRQ(AXP192, VBUS_NOT_VALID, 3, 4), + INIT_REGMAP_IRQ(AXP192, VBUS_SESS_VALID, 3, 3), + INIT_REGMAP_IRQ(AXP192, VBUS_SESS_END, 3, 2), + INIT_REGMAP_IRQ(AXP192, LOW_PWR_LVL, 3, 0), + INIT_REGMAP_IRQ(AXP192, TIMER, 4, 7), + INIT_REGMAP_IRQ(AXP192, GPIO2_INPUT, 4, 2), + INIT_REGMAP_IRQ(AXP192, GPIO1_INPUT, 4, 1), + INIT_REGMAP_IRQ(AXP192, GPIO0_INPUT, 4, 0), +}; + static const struct regmap_irq axp20x_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP20X, ACIN_OVER_V, 0, 7), INIT_REGMAP_IRQ(AXP20X, ACIN_PLUGIN, 0, 6), @@ -414,6 +650,50 @@ static const struct regmap_irq axp288_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1), }; +static const struct regmap_irq axp313a_regmap_irqs[] = { + INIT_REGMAP_IRQ(AXP313A, PEK_RIS_EDGE, 0, 7), + INIT_REGMAP_IRQ(AXP313A, PEK_FAL_EDGE, 0, 6), + INIT_REGMAP_IRQ(AXP313A, PEK_SHORT, 0, 5), + INIT_REGMAP_IRQ(AXP313A, PEK_LONG, 0, 4), + INIT_REGMAP_IRQ(AXP313A, DCDC3_V_LOW, 0, 3), + INIT_REGMAP_IRQ(AXP313A, DCDC2_V_LOW, 0, 2), + 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), @@ -501,24 +781,65 @@ static const struct regmap_irq axp809_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP809, GPIO0_INPUT, 4, 0), }; +static const struct regmap_irq axp15060_regmap_irqs[] = { + INIT_REGMAP_IRQ(AXP15060, DIE_TEMP_HIGH_LV1, 0, 0), + INIT_REGMAP_IRQ(AXP15060, DIE_TEMP_HIGH_LV2, 0, 1), + INIT_REGMAP_IRQ(AXP15060, DCDC1_V_LOW, 0, 2), + INIT_REGMAP_IRQ(AXP15060, DCDC2_V_LOW, 0, 3), + INIT_REGMAP_IRQ(AXP15060, DCDC3_V_LOW, 0, 4), + INIT_REGMAP_IRQ(AXP15060, DCDC4_V_LOW, 0, 5), + INIT_REGMAP_IRQ(AXP15060, DCDC5_V_LOW, 0, 6), + INIT_REGMAP_IRQ(AXP15060, DCDC6_V_LOW, 0, 7), + INIT_REGMAP_IRQ(AXP15060, PEK_LONG, 1, 0), + INIT_REGMAP_IRQ(AXP15060, PEK_SHORT, 1, 1), + INIT_REGMAP_IRQ(AXP15060, GPIO1_INPUT, 1, 2), + INIT_REGMAP_IRQ(AXP15060, PEK_FAL_EDGE, 1, 3), + INIT_REGMAP_IRQ(AXP15060, PEK_RIS_EDGE, 1, 4), + INIT_REGMAP_IRQ(AXP15060, GPIO2_INPUT, 1, 5), +}; + static const struct regmap_irq_chip axp152_regmap_irq_chip = { .name = "axp152_irq_chip", .status_base = AXP152_IRQ1_STATE, .ack_base = AXP152_IRQ1_STATE, - .mask_base = AXP152_IRQ1_EN, - .mask_invert = true, + .unmask_base = AXP152_IRQ1_EN, .init_ack_masked = true, .irqs = axp152_regmap_irqs, .num_irqs = ARRAY_SIZE(axp152_regmap_irqs), .num_regs = 3, }; +static unsigned int axp192_get_irq_reg(struct regmap_irq_chip_data *data, + unsigned int base, int index) +{ + /* linear mapping for IRQ1 to IRQ4 */ + if (index < 4) + return base + index; + + /* handle IRQ5 separately */ + if (base == AXP192_IRQ1_EN) + return AXP192_IRQ5_EN; + + return AXP192_IRQ5_STATE; +} + +static const struct regmap_irq_chip axp192_regmap_irq_chip = { + .name = "axp192_irq_chip", + .status_base = AXP192_IRQ1_STATE, + .ack_base = AXP192_IRQ1_STATE, + .unmask_base = AXP192_IRQ1_EN, + .init_ack_masked = true, + .irqs = axp192_regmap_irqs, + .num_irqs = ARRAY_SIZE(axp192_regmap_irqs), + .num_regs = 5, + .get_irq_reg = axp192_get_irq_reg, +}; + static const struct regmap_irq_chip axp20x_regmap_irq_chip = { .name = "axp20x_irq_chip", .status_base = AXP20X_IRQ1_STATE, .ack_base = AXP20X_IRQ1_STATE, - .mask_base = AXP20X_IRQ1_EN, - .mask_invert = true, + .unmask_base = AXP20X_IRQ1_EN, .init_ack_masked = true, .irqs = axp20x_regmap_irqs, .num_irqs = ARRAY_SIZE(axp20x_regmap_irqs), @@ -530,8 +851,7 @@ static const struct regmap_irq_chip axp22x_regmap_irq_chip = { .name = "axp22x_irq_chip", .status_base = AXP20X_IRQ1_STATE, .ack_base = AXP20X_IRQ1_STATE, - .mask_base = AXP20X_IRQ1_EN, - .mask_invert = true, + .unmask_base = AXP20X_IRQ1_EN, .init_ack_masked = true, .irqs = axp22x_regmap_irqs, .num_irqs = ARRAY_SIZE(axp22x_regmap_irqs), @@ -542,8 +862,7 @@ static const struct regmap_irq_chip axp288_regmap_irq_chip = { .name = "axp288_irq_chip", .status_base = AXP20X_IRQ1_STATE, .ack_base = AXP20X_IRQ1_STATE, - .mask_base = AXP20X_IRQ1_EN, - .mask_invert = true, + .unmask_base = AXP20X_IRQ1_EN, .init_ack_masked = true, .irqs = axp288_regmap_irqs, .num_irqs = ARRAY_SIZE(axp288_regmap_irqs), @@ -551,12 +870,33 @@ static const struct regmap_irq_chip axp288_regmap_irq_chip = { }; +static const struct regmap_irq_chip axp313a_regmap_irq_chip = { + .name = "axp313a_irq_chip", + .status_base = AXP313A_IRQ_STATE, + .ack_base = AXP313A_IRQ_STATE, + .unmask_base = AXP313A_IRQ_EN, + .init_ack_masked = true, + .irqs = axp313a_regmap_irqs, + .num_irqs = ARRAY_SIZE(axp313a_regmap_irqs), + .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, .ack_base = AXP20X_IRQ1_STATE, - .mask_base = AXP20X_IRQ1_EN, - .mask_invert = true, + .unmask_base = AXP20X_IRQ1_EN, .init_ack_masked = true, .irqs = axp803_regmap_irqs, .num_irqs = ARRAY_SIZE(axp803_regmap_irqs), @@ -567,8 +907,7 @@ static const struct regmap_irq_chip axp806_regmap_irq_chip = { .name = "axp806", .status_base = AXP20X_IRQ1_STATE, .ack_base = AXP20X_IRQ1_STATE, - .mask_base = AXP20X_IRQ1_EN, - .mask_invert = true, + .unmask_base = AXP20X_IRQ1_EN, .init_ack_masked = true, .irqs = axp806_regmap_irqs, .num_irqs = ARRAY_SIZE(axp806_regmap_irqs), @@ -579,14 +918,45 @@ static const struct regmap_irq_chip axp809_regmap_irq_chip = { .name = "axp809", .status_base = AXP20X_IRQ1_STATE, .ack_base = AXP20X_IRQ1_STATE, - .mask_base = AXP20X_IRQ1_EN, - .mask_invert = true, + .unmask_base = AXP20X_IRQ1_EN, .init_ack_masked = true, .irqs = axp809_regmap_irqs, .num_irqs = ARRAY_SIZE(axp809_regmap_irqs), .num_regs = 5, }; +static const struct regmap_irq_chip axp15060_regmap_irq_chip = { + .name = "axp15060", + .status_base = AXP15060_IRQ1_STATE, + .ack_base = AXP15060_IRQ1_STATE, + .unmask_base = AXP15060_IRQ1_EN, + .init_ack_masked = true, + .irqs = axp15060_regmap_irqs, + .num_irqs = ARRAY_SIZE(axp15060_regmap_irqs), + .num_regs = 2, +}; + +static const struct mfd_cell axp192_cells[] = { + { + .name = "axp192-adc", + .of_compatible = "x-powers,axp192-adc", + }, { + .name = "axp20x-battery-power-supply", + .of_compatible = "x-powers,axp192-battery-power-supply", + }, { + .name = "axp20x-ac-power-supply", + .of_compatible = "x-powers,axp202-ac-power-supply", + .num_resources = ARRAY_SIZE(axp192_ac_power_supply_resources), + .resources = axp192_ac_power_supply_resources, + }, { + .name = "axp20x-usb-power-supply", + .of_compatible = "x-powers,axp192-usb-power-supply", + .num_resources = ARRAY_SIZE(axp192_usb_power_supply_resources), + .resources = axp192_usb_power_supply_resources, + }, + { .name = "axp20x-regulator" }, +}; + static const struct mfd_cell axp20x_cells[] = { { .name = "axp20x-gpio", @@ -618,6 +988,9 @@ static const struct mfd_cell axp20x_cells[] = { static const struct mfd_cell axp221_cells[] = { { + .name = "axp20x-gpio", + .of_compatible = "x-powers,axp221-gpio", + }, { .name = "axp221-pek", .num_resources = ARRAY_SIZE(axp22x_pek_resources), .resources = axp22x_pek_resources, @@ -644,6 +1017,9 @@ static const struct mfd_cell axp221_cells[] = { static const struct mfd_cell axp223_cells[] = { { + .name = "axp20x-gpio", + .of_compatible = "x-powers,axp221-gpio", + }, { .name = "axp221-pek", .num_resources = ARRAY_SIZE(axp22x_pek_resources), .resources = axp22x_pek_resources, @@ -676,6 +1052,24 @@ static const struct mfd_cell axp152_cells[] = { }, }; +static struct mfd_cell axp313a_cells[] = { + /* 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"), }; @@ -699,6 +1093,18 @@ static const struct resource axp288_charger_resources[] = { DEFINE_RES_IRQ(AXP288_IRQ_CBTO), }; +static const char * const axp288_fuel_gauge_suppliers[] = { "axp288_charger" }; + +static const struct property_entry axp288_fuel_gauge_properties[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", axp288_fuel_gauge_suppliers), + { } +}; + +static const struct software_node axp288_fuel_gauge_sw_node = { + .name = "axp288_fuel_gauge", + .properties = axp288_fuel_gauge_properties, +}; + static const struct mfd_cell axp288_cells[] = { { .name = "axp288_adc", @@ -716,6 +1122,7 @@ static const struct mfd_cell axp288_cells[] = { .name = "axp288_fuel_gauge", .num_resources = ARRAY_SIZE(axp288_fuel_gauge_resources), .resources = axp288_fuel_gauge_resources, + .swnode = &axp288_fuel_gauge_sw_node, }, { .name = "axp221-pek", .num_resources = ARRAY_SIZE(axp288_power_button_resources), @@ -771,6 +1178,9 @@ static const struct mfd_cell axp806_cells[] = { static const struct mfd_cell axp809_cells[] = { { + .name = "axp20x-gpio", + .of_compatible = "x-powers,axp221-gpio", + }, { .name = "axp221-pek", .num_resources = ARRAY_SIZE(axp809_pek_resources), .resources = axp809_pek_resources, @@ -809,41 +1219,52 @@ static const struct mfd_cell axp813_cells[] = { }, }; -static struct axp20x_dev *axp20x_pm_power_off; -static void axp20x_power_off(void) +static const struct mfd_cell axp15060_cells[] = { + { + .name = "axp221-pek", + .num_resources = ARRAY_SIZE(axp15060_pek_resources), + .resources = axp15060_pek_resources, + }, { + .name = "axp20x-regulator", + }, +}; + +/* For boards that don't have IRQ line connected to SOC. */ +static const struct mfd_cell axp_regulator_only_cells[] = { + /* 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) { - if (axp20x_pm_power_off->variant == AXP288_ID) - return; + struct axp20x_dev *axp20x = data->cb_data; + unsigned int shutdown_reg; + + switch (axp20x->variant) { + case AXP323_ID: + case AXP313A_ID: + shutdown_reg = AXP313A_SHUTDOWN_CTRL; + break; + default: + shutdown_reg = AXP20X_OFF_CTRL; + break; + } - regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL, - AXP20X_OFF); + regmap_write(axp20x->regmap, shutdown_reg, AXP20X_OFF); /* Give capacitors etc. time to drain to avoid kernel panic msg. */ - msleep(500); + mdelay(500); + + return NOTIFY_DONE; } 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); @@ -851,6 +1272,12 @@ int axp20x_match_device(struct axp20x_dev *axp20x) axp20x->regmap_cfg = &axp152_regmap_config; axp20x->regmap_irq_chip = &axp152_regmap_irq_chip; break; + case AXP192_ID: + axp20x->nr_cells = ARRAY_SIZE(axp192_cells); + axp20x->cells = axp192_cells; + axp20x->regmap_cfg = &axp192_regmap_config; + axp20x->regmap_irq_chip = &axp192_regmap_irq_chip; + break; case AXP202_ID: case AXP209_ID: axp20x->nr_cells = ARRAY_SIZE(axp20x_cells); @@ -877,6 +1304,24 @@ int axp20x_match_device(struct axp20x_dev *axp20x) axp20x->regmap_irq_chip = &axp288_regmap_irq_chip; axp20x->irq_flags = IRQF_TRIGGER_LOW; break; + case AXP313A_ID: + axp20x->nr_cells = ARRAY_SIZE(axp313a_cells); + axp20x->cells = axp313a_cells; + 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; @@ -889,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; @@ -919,10 +1365,33 @@ int axp20x_match_device(struct axp20x_dev *axp20x) */ axp20x->regmap_irq_chip = &axp803_regmap_irq_chip; break; + case AXP15060_ID: + 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]); @@ -977,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) { @@ -986,10 +1455,8 @@ int axp20x_device_probe(struct axp20x_dev *axp20x) return ret; } - if (!pm_power_off) { - axp20x_pm_power_off = axp20x; - pm_power_off = axp20x_power_off; - } + if (axp20x->variant != AXP288_ID) + devm_register_power_off_handler(axp20x->dev, axp20x_power_off, axp20x); dev_info(axp20x->dev, "AXP20X driver loaded\n"); @@ -999,11 +1466,6 @@ EXPORT_SYMBOL(axp20x_device_probe); void axp20x_device_remove(struct axp20x_dev *axp20x) { - if (axp20x == axp20x_pm_power_off) { - axp20x_pm_power_off = NULL; - pm_power_off = NULL; - } - mfd_remove_devices(axp20x->dev); regmap_del_irq_chip(axp20x->irq, axp20x->regmap_irqc); } diff --git a/drivers/mfd/bcm2835-pm.c b/drivers/mfd/bcm2835-pm.c index 42fe67f1538e..8bed59816e82 100644 --- a/drivers/mfd/bcm2835-pm.c +++ b/drivers/mfd/bcm2835-pm.c @@ -25,9 +25,52 @@ static const struct mfd_cell bcm2835_power_devs[] = { { .name = "bcm2835-power" }, }; +static int bcm2835_pm_get_pdata(struct platform_device *pdev, + struct bcm2835_pm *pm) +{ + if (of_property_present(pm->dev->of_node, "reg-names")) { + struct resource *res; + + pm->base = devm_platform_ioremap_resource_byname(pdev, "pm"); + if (IS_ERR(pm->base)) + return PTR_ERR(pm->base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "asb"); + if (res) { + pm->asb = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pm->asb)) + pm->asb = NULL; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "rpivid_asb"); + if (res) { + pm->rpivid_asb = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pm->rpivid_asb)) + pm->rpivid_asb = NULL; + } + + return 0; + } + + /* If no 'reg-names' property is found we can assume we're using old DTB. */ + pm->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pm->base)) + return PTR_ERR(pm->base); + + pm->asb = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(pm->asb)) + pm->asb = NULL; + + pm->rpivid_asb = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(pm->rpivid_asb)) + pm->rpivid_asb = NULL; + + return 0; +} + static int bcm2835_pm_probe(struct platform_device *pdev) { - struct resource *res; struct device *dev = &pdev->dev; struct bcm2835_pm *pm; int ret; @@ -39,10 +82,9 @@ static int bcm2835_pm_probe(struct platform_device *pdev) pm->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pm->base = devm_ioremap_resource(dev, res); - if (IS_ERR(pm->base)) - return PTR_ERR(pm->base); + ret = bcm2835_pm_get_pdata(pdev, pm); + if (ret) + return ret; ret = devm_mfd_add_devices(dev, -1, bcm2835_pm_devs, ARRAY_SIZE(bcm2835_pm_devs), @@ -50,30 +92,23 @@ static int bcm2835_pm_probe(struct platform_device *pdev) if (ret) return ret; - /* We'll use the presence of the AXI ASB regs in the + /* + * We'll use the presence of the AXI ASB regs in the * bcm2835-pm binding as the key for whether we can reference * the full PM register range and support power domains. */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res) { - pm->asb = devm_ioremap_resource(dev, res); - if (IS_ERR(pm->asb)) - return PTR_ERR(pm->asb); - - ret = devm_mfd_add_devices(dev, -1, - bcm2835_power_devs, - ARRAY_SIZE(bcm2835_power_devs), - NULL, 0, NULL); - if (ret) - return ret; - } - + if (pm->asb) + return devm_mfd_add_devices(dev, -1, bcm2835_power_devs, + ARRAY_SIZE(bcm2835_power_devs), + NULL, 0, NULL); return 0; } 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); @@ -89,4 +124,3 @@ module_platform_driver(bcm2835_pm_driver); MODULE_AUTHOR("Eric Anholt <eric@anholt.net>"); MODULE_DESCRIPTION("Driver for Broadcom BCM2835 PM MFD"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/bcm590xx.c b/drivers/mfd/bcm590xx.c index 6ca337cde84c..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,18 +36,58 @@ 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_i2c_probe(struct i2c_client *i2c_pri, - const struct i2c_device_id *id) +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; int ret; @@ -52,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)) { @@ -78,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) { @@ -93,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 e15b1acfb063..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, @@ -204,8 +204,7 @@ static int bd957x_identify(struct device *dev, struct regmap *regmap) return 0; } -static int bd9571mwv_probe(struct i2c_client *client, - const struct i2c_device_id *ids) +static int bd9571mwv_probe(struct i2c_client *client) { const struct regmap_config *regmap_config; const struct regmap_irq_chip *irq_chip; @@ -269,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 8c08d1c55726..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> @@ -20,7 +20,6 @@ #define DRV_NAME "cros-ec-dev" static struct class cros_class = { - .owner = THIS_MODULE, .name = "chromeos", }; @@ -75,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", }, }; @@ -92,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, @@ -99,21 +122,50 @@ 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", }, +}; + +static const struct mfd_cell cros_ec_pchg_cells[] = { { .name = "cros-ec-pchg", }, }; @@ -137,6 +189,7 @@ static int ec_device_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct cros_ec_platform *ec_platform = dev_get_platdata(dev); struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL); + struct ec_response_pchg_count pchg_count; int i; if (!ec) @@ -146,8 +199,8 @@ static int ec_device_probe(struct platform_device *pdev) ec->ec_dev = dev_get_drvdata(dev->parent); ec->dev = dev; ec->cmd_offset = ec_platform->cmd_offset; - ec->features[0] = -1U; /* Not cached yet */ - ec->features[1] = -1U; /* Not cached yet */ + ec->features.flags[0] = -1U; /* Not cached yet */ + ec->features.flags[1] = -1U; /* Not cached yet */ device_initialize(&ec->class_dev); for (i = 0; i < ARRAY_SIZE(cros_mcu_devices); i++) { @@ -212,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. */ @@ -243,6 +311,21 @@ static int ec_device_probe(struct platform_device *pdev) } /* + * The PCHG device cannot be detected by sending EC_FEATURE_GET_CMD, but + * it can be detected by querying the number of peripheral chargers. + */ + retval = cros_ec_cmd(ec->ec_dev, 0, EC_CMD_PCHG_COUNT, NULL, 0, + &pchg_count, sizeof(pchg_count)); + if (retval >= 0 && pchg_count.port_count) { + retval = mfd_add_hotplug_devices(ec->dev, + cros_ec_pchg_cells, + ARRAY_SIZE(cros_ec_pchg_cells)); + if (retval) + dev_warn(ec->dev, "failed to add pchg: %d\n", + retval); + } + + /* * The following subdevices cannot be detected by sending the * EC_FEATURE_GET_CMD to the Embedded Controller device. */ @@ -270,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[] = { @@ -298,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; } @@ -326,8 +403,7 @@ static void __exit cros_ec_dev_exit(void) module_init(cros_ec_dev_init); module_exit(cros_ec_dev_exit); -MODULE_ALIAS("platform:" DRV_NAME); 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/da903x.c b/drivers/mfd/da903x.c index a818fbb55988..e86b39de3303 100644 --- a/drivers/mfd/da903x.c +++ b/drivers/mfd/da903x.c @@ -488,9 +488,9 @@ failed: return ret; } -static int da903x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int da903x_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); struct da903x_platform_data *pdata = dev_get_platdata(&client->dev); struct da903x_chip *chip; unsigned int tmp; @@ -532,12 +532,11 @@ static int da903x_probe(struct i2c_client *client, return da903x_add_subdevs(chip, pdata); } -static int da903x_remove(struct i2c_client *client) +static void da903x_remove(struct i2c_client *client) { struct da903x_chip *chip = i2c_get_clientdata(client); da903x_remove_subdevs(chip); - return 0; } static struct i2c_driver da903x_driver = { @@ -564,4 +563,3 @@ module_exit(da903x_exit); MODULE_DESCRIPTION("PMIC Driver for Dialog Semiconductor DA9034"); MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index 8b42d2f7024f..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; } @@ -653,4 +654,3 @@ void da9052_device_exit(struct da9052 *da9052) MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); MODULE_DESCRIPTION("DA9052 MFD Core"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c index 8de93db35f3a..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) @@ -126,9 +123,9 @@ static const struct of_device_id dialog_dt_ids[] = { }; #endif -static int da9052_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int da9052_i2c_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); struct da9052 *da9052; int ret; @@ -168,12 +165,11 @@ static int da9052_i2c_probe(struct i2c_client *client, return da9052_device_init(da9052, id->driver_data); } -static int da9052_i2c_remove(struct i2c_client *client) +static void da9052_i2c_remove(struct i2c_client *client) { struct da9052 *da9052 = i2c_get_clientdata(client); da9052_device_exit(da9052); - return 0; } static struct i2c_driver da9052_i2c_driver = { @@ -210,4 +206,3 @@ module_exit(da9052_i2c_exit); MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); MODULE_DESCRIPTION("I2C driver for Dialog DA9052 PMIC"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c index 5faf3766a5e2..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; @@ -55,12 +55,11 @@ static int da9052_spi_probe(struct spi_device *spi) return da9052_device_init(da9052, id->driver_data); } -static int da9052_spi_remove(struct spi_device *spi) +static void da9052_spi_remove(struct spi_device *spi) { struct da9052 *da9052 = spi_get_drvdata(spi); da9052_device_exit(da9052); - return 0; } static const struct spi_device_id da9052_spi_id[] = { @@ -103,4 +102,3 @@ module_exit(da9052_spi_exit); MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); MODULE_DESCRIPTION("SPI driver for Dialog DA9052 PMIC"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/da9055-core.c b/drivers/mfd/da9055-core.c index c3bcbd8905c6..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; } @@ -398,5 +398,4 @@ void da9055_device_exit(struct da9055 *da9055) } MODULE_DESCRIPTION("Core support for the DA9055 PMIC"); -MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); diff --git a/drivers/mfd/da9055-i2c.c b/drivers/mfd/da9055-i2c.c index bc60433b68db..6c1981832aaf 100644 --- a/drivers/mfd/da9055-i2c.c +++ b/drivers/mfd/da9055-i2c.c @@ -11,12 +11,10 @@ #include <linux/i2c.h> #include <linux/err.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/mfd/da9055/core.h> -static int da9055_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int da9055_i2c_probe(struct i2c_client *i2c) { struct da9055 *da9055; int ret; @@ -41,13 +39,11 @@ static int da9055_i2c_probe(struct i2c_client *i2c, return da9055_device_init(da9055); } -static int da9055_i2c_remove(struct i2c_client *i2c) +static void da9055_i2c_remove(struct i2c_client *i2c) { struct da9055 *da9055 = i2c_get_clientdata(i2c); da9055_device_exit(da9055); - - return 0; } /* @@ -58,7 +54,7 @@ static int 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); @@ -100,4 +96,3 @@ module_exit(da9055_i2c_exit); MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); MODULE_DESCRIPTION("I2C driver for Dialog DA9055 PMIC"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/da9062-core.c b/drivers/mfd/da9062-core.c index 01f8e10dfa55..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, @@ -181,35 +181,25 @@ static const struct resource da9061_onkey_resources[] = { DEFINE_RES_IRQ_NAMED(DA9061_IRQ_ONKEY, "ONKEY"), }; -static const struct mfd_cell da9061_devs[] = { - { - .name = "da9061-core", - .num_resources = ARRAY_SIZE(da9061_core_resources), - .resources = da9061_core_resources, - }, - { - .name = "da9062-regulators", - .num_resources = ARRAY_SIZE(da9061_regulators_resources), - .resources = da9061_regulators_resources, - }, - { - .name = "da9061-watchdog", - .num_resources = ARRAY_SIZE(da9061_wdt_resources), - .resources = da9061_wdt_resources, - .of_compatible = "dlg,da9061-watchdog", - }, - { - .name = "da9061-thermal", - .num_resources = ARRAY_SIZE(da9061_thermal_resources), - .resources = da9061_thermal_resources, - .of_compatible = "dlg,da9061-thermal", - }, - { - .name = "da9061-onkey", - .num_resources = ARRAY_SIZE(da9061_onkey_resources), - .resources = da9061_onkey_resources, - .of_compatible = "dlg,da9061-onkey", - }, +static const struct mfd_cell da9061_devs_irq[] = { + MFD_CELL_OF("da9061-core", da9061_core_resources, NULL, 0, 0, + NULL), + MFD_CELL_OF("da9062-regulators", da9061_regulators_resources, NULL, 0, 0, + NULL), + MFD_CELL_OF("da9061-watchdog", da9061_wdt_resources, NULL, 0, 0, + "dlg,da9061-watchdog"), + MFD_CELL_OF("da9061-thermal", da9061_thermal_resources, NULL, 0, 0, + "dlg,da9061-thermal"), + MFD_CELL_OF("da9061-onkey", da9061_onkey_resources, NULL, 0, 0, + "dlg,da9061-onkey"), +}; + +static const struct mfd_cell da9061_devs_noirq[] = { + MFD_CELL_OF("da9061-core", NULL, NULL, 0, 0, NULL), + MFD_CELL_OF("da9062-regulators", NULL, NULL, 0, 0, NULL), + MFD_CELL_OF("da9061-watchdog", NULL, NULL, 0, 0, "dlg,da9061-watchdog"), + MFD_CELL_OF("da9061-thermal", NULL, NULL, 0, 0, "dlg,da9061-thermal"), + MFD_CELL_OF("da9061-onkey", NULL, NULL, 0, 0, "dlg,da9061-onkey"), }; static const struct resource da9062_core_resources[] = { @@ -245,47 +235,31 @@ static const struct resource da9062_gpio_resources[] = { DEFINE_RES_NAMED(DA9062_IRQ_GPI4, 1, "GPI4", IORESOURCE_IRQ), }; -static const struct mfd_cell da9062_devs[] = { - { - .name = "da9062-core", - .num_resources = ARRAY_SIZE(da9062_core_resources), - .resources = da9062_core_resources, - }, - { - .name = "da9062-regulators", - .num_resources = ARRAY_SIZE(da9062_regulators_resources), - .resources = da9062_regulators_resources, - }, - { - .name = "da9062-watchdog", - .num_resources = ARRAY_SIZE(da9062_wdt_resources), - .resources = da9062_wdt_resources, - .of_compatible = "dlg,da9062-watchdog", - }, - { - .name = "da9062-thermal", - .num_resources = ARRAY_SIZE(da9062_thermal_resources), - .resources = da9062_thermal_resources, - .of_compatible = "dlg,da9062-thermal", - }, - { - .name = "da9062-rtc", - .num_resources = ARRAY_SIZE(da9062_rtc_resources), - .resources = da9062_rtc_resources, - .of_compatible = "dlg,da9062-rtc", - }, - { - .name = "da9062-onkey", - .num_resources = ARRAY_SIZE(da9062_onkey_resources), - .resources = da9062_onkey_resources, - .of_compatible = "dlg,da9062-onkey", - }, - { - .name = "da9062-gpio", - .num_resources = ARRAY_SIZE(da9062_gpio_resources), - .resources = da9062_gpio_resources, - .of_compatible = "dlg,da9062-gpio", - }, +static const struct mfd_cell da9062_devs_irq[] = { + MFD_CELL_OF("da9062-core", da9062_core_resources, NULL, 0, 0, + NULL), + MFD_CELL_OF("da9062-regulators", da9062_regulators_resources, NULL, 0, 0, + NULL), + MFD_CELL_OF("da9062-watchdog", da9062_wdt_resources, NULL, 0, 0, + "dlg,da9062-watchdog"), + MFD_CELL_OF("da9062-thermal", da9062_thermal_resources, NULL, 0, 0, + "dlg,da9062-thermal"), + MFD_CELL_OF("da9062-rtc", da9062_rtc_resources, NULL, 0, 0, + "dlg,da9062-rtc"), + MFD_CELL_OF("da9062-onkey", da9062_onkey_resources, NULL, 0, 0, + "dlg,da9062-onkey"), + MFD_CELL_OF("da9062-gpio", da9062_gpio_resources, NULL, 0, 0, + "dlg,da9062-gpio"), +}; + +static const struct mfd_cell da9062_devs_noirq[] = { + MFD_CELL_OF("da9062-core", NULL, NULL, 0, 0, NULL), + MFD_CELL_OF("da9062-regulators", NULL, NULL, 0, 0, NULL), + MFD_CELL_OF("da9062-watchdog", NULL, NULL, 0, 0, "dlg,da9062-watchdog"), + MFD_CELL_OF("da9062-thermal", NULL, NULL, 0, 0, "dlg,da9062-thermal"), + MFD_CELL_OF("da9062-rtc", NULL, NULL, 0, 0, "dlg,da9062-rtc"), + MFD_CELL_OF("da9062-onkey", NULL, NULL, 0, 0, "dlg,da9062-onkey"), + MFD_CELL_OF("da9062-gpio", NULL, NULL, 0, 0, "dlg,da9062-gpio"), }; static int da9062_clear_fault_log(struct da9062 *chip) @@ -453,6 +427,7 @@ static const struct regmap_range da9061_aa_writeable_ranges[] = { regmap_reg_range(DA9062AA_VBUCK1_B, DA9062AA_VBUCK4_B), regmap_reg_range(DA9062AA_VBUCK3_B, DA9062AA_VBUCK3_B), regmap_reg_range(DA9062AA_VLDO1_B, DA9062AA_VLDO4_B), + regmap_reg_range(DA9062AA_CONFIG_J, DA9062AA_CONFIG_J), regmap_reg_range(DA9062AA_GP_ID_0, DA9062AA_GP_ID_19), }; @@ -495,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, @@ -556,6 +531,7 @@ static const struct regmap_range da9062_aa_writeable_ranges[] = { regmap_reg_range(DA9062AA_VBUCK3_B, DA9062AA_VBUCK3_B), regmap_reg_range(DA9062AA_VLDO1_B, DA9062AA_VLDO4_B), regmap_reg_range(DA9062AA_BBAT_CONT, DA9062AA_BBAT_CONT), + regmap_reg_range(DA9062AA_CONFIG_J, DA9062AA_CONFIG_J), regmap_reg_range(DA9062AA_GP_ID_0, DA9062AA_GP_ID_19), }; @@ -600,30 +576,22 @@ 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) +static int da9062_i2c_probe(struct i2c_client *i2c) { struct da9062 *chip; - unsigned int irq_base; + unsigned int irq_base = 0; const struct mfd_cell *cell; const struct regmap_irq_chip *irq_chip; const struct regmap_config *config; @@ -635,30 +603,21 @@ 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; - if (!i2c->irq) { - dev_err(chip->dev, "No IRQ configured\n"); - return -EINVAL; - } - + /* Start with a base configuration without IRQ */ switch (chip->chip_type) { case COMPAT_TYPE_DA9061: - cell = da9061_devs; - cell_num = ARRAY_SIZE(da9061_devs); - irq_chip = &da9061_irq_chip; + cell = da9061_devs_noirq; + cell_num = ARRAY_SIZE(da9061_devs_noirq); config = &da9061_regmap_config; break; case COMPAT_TYPE_DA9062: - cell = da9062_devs; - cell_num = ARRAY_SIZE(da9062_devs); - irq_chip = &da9062_irq_chip; + cell = da9062_devs_noirq; + cell_num = ARRAY_SIZE(da9062_devs_noirq); config = &da9062_regmap_config; break; default: @@ -674,6 +633,17 @@ static int da9062_i2c_probe(struct i2c_client *i2c, return ret; } + /* If SMBus is not available and only I2C is possible, enter I2C mode */ + if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_info(chip->dev, "Entering I2C mode!\n"); + ret = regmap_clear_bits(chip->regmap, DA9062AA_CONFIG_J, + DA9062AA_TWOWIRE_TO_MASK); + if (ret < 0) { + dev_err(chip->dev, "Failed to set Two-Wire Bus Mode.\n"); + return ret; + } + } + ret = da9062_clear_fault_log(chip); if (ret < 0) dev_warn(chip->dev, "Cannot clear fault log\n"); @@ -682,49 +652,68 @@ static int da9062_i2c_probe(struct i2c_client *i2c, if (ret) return ret; - ret = da9062_configure_irq_type(chip, i2c->irq, &trigger_type); - if (ret < 0) { - dev_err(chip->dev, "Failed to configure IRQ type\n"); - return ret; + /* If IRQ is available, reconfigure it accordingly */ + if (i2c->irq) { + if (chip->chip_type == COMPAT_TYPE_DA9061) { + cell = da9061_devs_irq; + cell_num = ARRAY_SIZE(da9061_devs_irq); + irq_chip = &da9061_irq_chip; + } else { + cell = da9062_devs_irq; + cell_num = ARRAY_SIZE(da9062_devs_irq); + irq_chip = &da9062_irq_chip; + } + + ret = da9062_configure_irq_type(chip, i2c->irq, &trigger_type); + if (ret < 0) { + dev_err(chip->dev, "Failed to configure IRQ type\n"); + return ret; + } + + ret = regmap_add_irq_chip(chip->regmap, i2c->irq, + trigger_type | IRQF_SHARED | IRQF_ONESHOT, + -1, irq_chip, &chip->regmap_irq); + if (ret) { + dev_err(chip->dev, "Failed to request IRQ %d: %d\n", + i2c->irq, ret); + return ret; + } + + irq_base = regmap_irq_chip_get_base(chip->regmap_irq); } - ret = regmap_add_irq_chip(chip->regmap, i2c->irq, - trigger_type | IRQF_SHARED | IRQF_ONESHOT, - -1, irq_chip, &chip->regmap_irq); - if (ret) { - dev_err(chip->dev, "Failed to request IRQ %d: %d\n", - i2c->irq, ret); - return ret; - } - - irq_base = regmap_irq_chip_get_base(chip->regmap_irq); - ret = mfd_add_devices(chip->dev, PLATFORM_DEVID_NONE, cell, cell_num, NULL, irq_base, NULL); if (ret) { dev_err(chip->dev, "Cannot register child devices\n"); - regmap_del_irq_chip(i2c->irq, chip->regmap_irq); + if (i2c->irq) + regmap_del_irq_chip(i2c->irq, chip->regmap_irq); return ret; } return ret; } -static int da9062_i2c_remove(struct i2c_client *i2c) +static void da9062_i2c_remove(struct i2c_client *i2c) { struct da9062 *chip = i2c_get_clientdata(i2c); mfd_remove_devices(chip->dev); regmap_del_irq_chip(i2c->irq, chip->regmap_irq); - - return 0; } +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); @@ -733,7 +722,7 @@ static struct i2c_driver da9062_i2c_driver = { .name = "da9062", .of_match_table = da9062_dt_ids, }, - .probe = da9062_i2c_probe, + .probe = da9062_i2c_probe, .remove = da9062_i2c_remove, .id_table = da9062_i2c_id, }; diff --git a/drivers/mfd/da9063-i2c.c b/drivers/mfd/da9063-i2c.c index 4b7f707b7952..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[] = { @@ -351,9 +366,9 @@ static const struct of_device_id da9063_dt_ids[] = { { } }; MODULE_DEVICE_TABLE(of, da9063_dt_ids); -static int da9063_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int da9063_i2c_probe(struct i2c_client *i2c) { + const struct i2c_device_id *id = i2c_client_get_device_id(i2c); struct da9063 *da9063; int ret; @@ -391,6 +406,7 @@ static int da9063_i2c_probe(struct i2c_client *i2c, &da9063_bb_da_volatile_table; break; case PMIC_DA9063_DA: + case PMIC_DA9063_EA: da9063_regmap_config.rd_table = &da9063_da_readable_table; da9063_regmap_config.wr_table = @@ -416,6 +432,7 @@ static int da9063_i2c_probe(struct i2c_client *i2c, &da9063l_bb_da_volatile_table; break; case PMIC_DA9063_DA: + case PMIC_DA9063_EA: da9063_regmap_config.rd_table = &da9063l_da_readable_table; da9063_regmap_config.wr_table = @@ -452,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); } @@ -467,7 +487,7 @@ static struct i2c_driver da9063_i2c_driver = { .name = "da9063", .of_match_table = da9063_dt_ids, }, - .probe = da9063_i2c_probe, + .probe = da9063_i2c_probe, .id_table = da9063_i2c_id, }; diff --git a/drivers/mfd/da9150-core.c b/drivers/mfd/da9150-core.c index 58009c8cb870..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, }; @@ -392,8 +392,7 @@ static struct mfd_cell da9150_devs[] = { }, }; -static int da9150_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int da9150_probe(struct i2c_client *client) { struct da9150 *da9150; struct da9150_pdata *pdata = dev_get_platdata(&client->dev); @@ -471,15 +470,13 @@ regmap_irq_fail: return ret; } -static int da9150_remove(struct i2c_client *client) +static void da9150_remove(struct i2c_client *client) { struct da9150 *da9150 = i2c_get_clientdata(client); regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data); mfd_remove_devices(da9150->dev); i2c_unregister_device(da9150->core_qif); - - return 0; } static void da9150_shutdown(struct i2c_client *client) diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c deleted file mode 100644 index e5c8bc998eb4..000000000000 --- a/drivers/mfd/davinci_voicecodec.c +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * DaVinci Voice Codec Core Interface for TI platforms - * - * Copyright (C) 2010 Texas Instruments, Inc - * - * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com> - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/clk.h> -#include <linux/regmap.h> - -#include <sound/pcm.h> - -#include <linux/mfd/davinci_voicecodec.h> - -static const struct regmap_config davinci_vc_regmap = { - .reg_bits = 32, - .val_bits = 32, -}; - -static int __init davinci_vc_probe(struct platform_device *pdev) -{ - struct davinci_vc *davinci_vc; - struct resource *res; - struct mfd_cell *cell = NULL; - dma_addr_t fifo_base; - int ret; - - davinci_vc = devm_kzalloc(&pdev->dev, - sizeof(struct davinci_vc), GFP_KERNEL); - if (!davinci_vc) - return -ENOMEM; - - davinci_vc->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(davinci_vc->clk)) { - dev_dbg(&pdev->dev, - "could not get the clock for voice codec\n"); - return -ENODEV; - } - clk_enable(davinci_vc->clk); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - fifo_base = (dma_addr_t)res->start; - davinci_vc->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(davinci_vc->base)) { - ret = PTR_ERR(davinci_vc->base); - goto fail; - } - - davinci_vc->regmap = devm_regmap_init_mmio(&pdev->dev, - davinci_vc->base, - &davinci_vc_regmap); - if (IS_ERR(davinci_vc->regmap)) { - ret = PTR_ERR(davinci_vc->regmap); - goto fail; - } - - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(&pdev->dev, "no DMA resource\n"); - ret = -ENXIO; - goto fail; - } - - davinci_vc->davinci_vcif.dma_tx_channel = res->start; - davinci_vc->davinci_vcif.dma_tx_addr = fifo_base + DAVINCI_VC_WFIFO; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) { - dev_err(&pdev->dev, "no DMA resource\n"); - ret = -ENXIO; - goto fail; - } - - davinci_vc->davinci_vcif.dma_rx_channel = res->start; - davinci_vc->davinci_vcif.dma_rx_addr = fifo_base + DAVINCI_VC_RFIFO; - - davinci_vc->dev = &pdev->dev; - davinci_vc->pdev = pdev; - - /* Voice codec interface client */ - cell = &davinci_vc->cells[DAVINCI_VC_VCIF_CELL]; - cell->name = "davinci-vcif"; - cell->platform_data = davinci_vc; - cell->pdata_size = sizeof(*davinci_vc); - - /* Voice codec CQ93VC client */ - cell = &davinci_vc->cells[DAVINCI_VC_CQ93VC_CELL]; - cell->name = "cq93vc-codec"; - cell->platform_data = davinci_vc; - cell->pdata_size = sizeof(*davinci_vc); - - ret = mfd_add_devices(&pdev->dev, pdev->id, davinci_vc->cells, - DAVINCI_VC_CELLS, NULL, 0, NULL); - if (ret != 0) { - dev_err(&pdev->dev, "fail to register client devices\n"); - goto fail; - } - - return 0; - -fail: - clk_disable(davinci_vc->clk); - - return ret; -} - -static int davinci_vc_remove(struct platform_device *pdev) -{ - struct davinci_vc *davinci_vc = platform_get_drvdata(pdev); - - mfd_remove_devices(&pdev->dev); - - clk_disable(davinci_vc->clk); - - return 0; -} - -static struct platform_driver davinci_vc_driver = { - .driver = { - .name = "davinci_voicecodec", - }, - .remove = davinci_vc_remove, -}; - -module_platform_driver_probe(davinci_vc_driver, davinci_vc_probe); - -MODULE_AUTHOR("Miguel Aguilar"); -MODULE_DESCRIPTION("Texas Instruments DaVinci Voice Codec Core Interface"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/db8500-prcmu-regs.h index 75fd1069372c..75fd1069372c 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/db8500-prcmu-regs.h diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 3bde7fda755f..21e68a382b11 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -36,8 +36,7 @@ #include <linux/mfd/abx500/ab8500.h> #include <linux/regulator/db8500-prcmu.h> #include <linux/regulator/machine.h> -#include <linux/platform_data/ux500_wdt.h> -#include "dbx500-prcmu-regs.h" +#include "db8500-prcmu-regs.h" /* Index of different voltages to be used when accessing AVSData */ #define PRCM_AVS_BASE 0x2FC @@ -799,7 +798,7 @@ void db8500_prcmu_get_abb_event_buffer(void __iomem **buf) * @opp: The new ARM operating point to which transition is to be made * Returns: 0 on success, non-zero on failure * - * This function sets the the operating point of the ARM. + * This function sets the operating point of the ARM. */ int db8500_prcmu_set_arm_opp(u8 opp) { @@ -1622,22 +1621,20 @@ static long round_clock_rate(u8 clock, unsigned long rate) } static const unsigned long db8500_armss_freqs[] = { - 200000000, - 400000000, - 800000000, + 199680000, + 399360000, + 798720000, 998400000 }; /* The DB8520 has slightly higher ARMSS max frequency */ static const unsigned long db8520_armss_freqs[] = { - 200000000, - 400000000, - 800000000, + 199680000, + 399360000, + 798720000, 1152000000 }; - - static long round_armss_rate(unsigned long rate) { unsigned long freq = 0; @@ -2364,7 +2361,7 @@ static bool read_mailbox_0(void) for (n = 0; n < NUM_PRCMU_WAKEUPS; n++) { if (ev & prcmu_irq_bit[n]) - generic_handle_irq(irq_find_mapping(db8500_irq_domain, n)); + generic_handle_domain_irq(db8500_irq_domain, n); } r = true; break; @@ -2567,14 +2564,16 @@ static char *fw_project_name(u32 project) return "U8500 C4"; case PRCMU_FW_PROJECT_U9500_MBL: return "U9500 MBL"; - case PRCMU_FW_PROJECT_U8500_MBL: - return "U8500 MBL"; + case PRCMU_FW_PROJECT_U8500_SSG1: + return "U8500 Samsung 1"; case PRCMU_FW_PROJECT_U8500_MBL2: return "U8500 MBL2"; case PRCMU_FW_PROJECT_U8520: return "U8520 MBL"; case PRCMU_FW_PROJECT_U8420: return "U8420"; + case PRCMU_FW_PROJECT_U8500_SSG2: + return "U8500 Samsung 2"; case PRCMU_FW_PROJECT_U8420_SYSCLK: return "U8420-sysclk"; case PRCMU_FW_PROJECT_U9540: @@ -2608,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"); @@ -2640,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, @@ -2939,26 +2938,15 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { }, }; -static struct ux500_wdt_data db8500_wdt_pdata = { - .timeout = 600, /* 10 minutes */ - .has_28_bits_resolution = true, -}; - static const struct mfd_cell common_prcmu_devs[] = { - { - .name = "ux500_wdt", - .platform_data = &db8500_wdt_pdata, - .pdata_size = sizeof(db8500_wdt_pdata), - .id = -1, - }, + MFD_CELL_NAME("db8500_wdt"), + MFD_CELL_NAME("db8500-cpuidle"), }; static const struct mfd_cell db8500_prcmu_devs[] = { MFD_CELL_OF("db8500-prcmu-regulators", NULL, &db8500_regulators, sizeof(db8500_regulators), 0, "stericsson,db8500-prcmu-regulator"), - MFD_CELL_OF("cpuidle-dbx500", - NULL, NULL, 0, 0, "stericsson,cpuidle-dbx500"), MFD_CELL_OF("db8500-thermal", NULL, NULL, 0, 0, "stericsson,db8500-thermal"), }; diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c index 83e676a096dc..fbbe82c6e75b 100644 --- a/drivers/mfd/dln2.c +++ b/drivers/mfd/dln2.c @@ -14,7 +14,6 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/usb.h> -#include <linux/i2c.h> #include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/mfd/core.h> @@ -50,6 +49,7 @@ enum dln2_handle { DLN2_HANDLE_GPIO, DLN2_HANDLE_I2C, DLN2_HANDLE_SPI, + DLN2_HANDLE_ADC, DLN2_HANDLES }; @@ -90,11 +90,6 @@ struct dln2_mod_rx_slots { spinlock_t lock; }; -enum dln2_endpoint { - DLN2_EP_OUT = 0, - DLN2_EP_IN = 1, -}; - struct dln2_dev { struct usb_device *usb_dev; struct usb_interface *interface; @@ -653,6 +648,7 @@ enum { DLN2_ACPI_MATCH_GPIO = 0, DLN2_ACPI_MATCH_I2C = 1, DLN2_ACPI_MATCH_SPI = 2, + DLN2_ACPI_MATCH_ADC = 3, }; static struct dln2_platform_data dln2_pdata_gpio = { @@ -683,6 +679,16 @@ static struct mfd_cell_acpi_match dln2_acpi_match_spi = { .adr = DLN2_ACPI_MATCH_SPI, }; +/* Only one ADC port supported */ +static struct dln2_platform_data dln2_pdata_adc = { + .handle = DLN2_HANDLE_ADC, + .port = 0, +}; + +static struct mfd_cell_acpi_match dln2_acpi_match_adc = { + .adr = DLN2_ACPI_MATCH_ADC, +}; + static const struct mfd_cell dln2_devs[] = { { .name = "dln2-gpio", @@ -702,6 +708,12 @@ static const struct mfd_cell dln2_devs[] = { .platform_data = &dln2_pdata_spi, .pdata_size = sizeof(struct dln2_platform_data), }, + { + .name = "dln2-adc", + .acpi_match = &dln2_acpi_match_adc, + .platform_data = &dln2_pdata_adc, + .pdata_size = sizeof(struct dln2_platform_data), + }, }; static void dln2_stop(struct dln2_dev *dln2) @@ -759,16 +771,12 @@ static int dln2_probe(struct usb_interface *interface, int ret; int i, j; - if (hostif->desc.bInterfaceNumber != 0 || - hostif->desc.bNumEndpoints < 2) + if (hostif->desc.bInterfaceNumber != 0) return -ENODEV; - epout = &hostif->endpoint[DLN2_EP_OUT].desc; - if (!usb_endpoint_is_bulk_out(epout)) - return -ENODEV; - epin = &hostif->endpoint[DLN2_EP_IN].desc; - if (!usb_endpoint_is_bulk_in(epin)) - return -ENODEV; + ret = usb_find_common_endpoints(hostif, &epin, &epout, NULL, NULL); + if (ret) + return ret; dln2 = kzalloc(sizeof(*dln2), GFP_KERNEL); if (!dln2) diff --git a/drivers/mfd/dm355evm_msp.c b/drivers/mfd/dm355evm_msp.c deleted file mode 100644 index 54fb6cbd2aa0..000000000000 --- a/drivers/mfd/dm355evm_msp.c +++ /dev/null @@ -1,455 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * dm355evm_msp.c - driver for MSP430 firmware on DM355EVM board - * - * Copyright (C) 2008 David Brownell - */ - -#include <linux/init.h> -#include <linux/mutex.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/module.h> -#include <linux/err.h> -#include <linux/gpio.h> -#include <linux/gpio/machine.h> -#include <linux/leds.h> -#include <linux/i2c.h> -#include <linux/mfd/dm355evm_msp.h> - - -/* - * The DM355 is a DaVinci chip with video support but no C64+ DSP. Its - * EVM board has an MSP430 programmed with firmware for various board - * support functions. This driver exposes some of them directly, and - * supports other drivers (e.g. RTC, input) for more complex access. - * - * Because this firmware is entirely board-specific, this file embeds - * knowledge that would be passed as platform_data in a generic driver. - * - * This driver was tested with firmware revision A4. - */ - -#if IS_ENABLED(CONFIG_INPUT_DM355EVM) -#define msp_has_keyboard() true -#else -#define msp_has_keyboard() false -#endif - -#if IS_ENABLED(CONFIG_LEDS_GPIO) -#define msp_has_leds() true -#else -#define msp_has_leds() false -#endif - -#if IS_ENABLED(CONFIG_RTC_DRV_DM355EVM) -#define msp_has_rtc() true -#else -#define msp_has_rtc() false -#endif - -#if IS_ENABLED(CONFIG_VIDEO_TVP514X) -#define msp_has_tvp() true -#else -#define msp_has_tvp() false -#endif - - -/*----------------------------------------------------------------------*/ - -/* REVISIT for paranoia's sake, retry reads/writes on error */ - -static struct i2c_client *msp430; - -/** - * dm355evm_msp_write - Writes a register in dm355evm_msp - * @value: the value to be written - * @reg: register address - * - * Returns result of operation - 0 is success, else negative errno - */ -int dm355evm_msp_write(u8 value, u8 reg) -{ - return i2c_smbus_write_byte_data(msp430, reg, value); -} -EXPORT_SYMBOL(dm355evm_msp_write); - -/** - * dm355evm_msp_read - Reads a register from dm355evm_msp - * @reg: register address - * - * Returns result of operation - value, or negative errno - */ -int dm355evm_msp_read(u8 reg) -{ - return i2c_smbus_read_byte_data(msp430, reg); -} -EXPORT_SYMBOL(dm355evm_msp_read); - -/*----------------------------------------------------------------------*/ - -/* - * Many of the msp430 pins are just used as fixed-direction GPIOs. - * We could export a few more of them this way, if we wanted. - */ -#define MSP_GPIO(bit, reg) ((DM355EVM_MSP_ ## reg) << 3 | (bit)) - -static const u8 msp_gpios[] = { - /* eight leds */ - MSP_GPIO(0, LED), MSP_GPIO(1, LED), - MSP_GPIO(2, LED), MSP_GPIO(3, LED), - MSP_GPIO(4, LED), MSP_GPIO(5, LED), - MSP_GPIO(6, LED), MSP_GPIO(7, LED), - /* SW6 and the NTSC/nPAL jumper */ - MSP_GPIO(0, SWITCH1), MSP_GPIO(1, SWITCH1), - MSP_GPIO(2, SWITCH1), MSP_GPIO(3, SWITCH1), - MSP_GPIO(4, SWITCH1), - /* switches on MMC/SD sockets */ - /* - * Note: EVMDM355_ECP_VA4.pdf suggests that Bit 2 and 4 should be - * checked for card detection. However on the EVM bit 1 and 3 gives - * this status, for 0 and 1 instance respectively. The pdf also - * suggests that Bit 1 and 3 should be checked for write protection. - * However on the EVM bit 2 and 4 gives this status,for 0 and 1 - * instance respectively. - */ - MSP_GPIO(2, SDMMC), MSP_GPIO(1, SDMMC), /* mmc0 WP, nCD */ - MSP_GPIO(4, SDMMC), MSP_GPIO(3, SDMMC), /* mmc1 WP, nCD */ -}; - -static struct gpio_led evm_leds[] = { - { .name = "dm355evm::ds14", - .default_trigger = "heartbeat", }, - { .name = "dm355evm::ds15", - .default_trigger = "mmc0", }, - { .name = "dm355evm::ds16", - /* could also be a CE-ATA drive */ - .default_trigger = "mmc1", }, - { .name = "dm355evm::ds17", - .default_trigger = "nand-disk", }, - { .name = "dm355evm::ds18", }, - { .name = "dm355evm::ds19", }, - { .name = "dm355evm::ds20", }, - { .name = "dm355evm::ds21", }, -}; - -static struct gpio_led_platform_data evm_led_data = { - .num_leds = ARRAY_SIZE(evm_leds), - .leds = evm_leds, -}; - -static struct gpiod_lookup_table evm_leds_gpio_table = { - .dev_id = "leds-gpio", - .table = { - /* - * These GPIOs are on the dm355evm_msp - * GPIO chip at index 0..7 - */ - GPIO_LOOKUP_IDX("dm355evm_msp", 0, NULL, - 0, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 1, NULL, - 1, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 2, NULL, - 2, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 3, NULL, - 3, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 4, NULL, - 4, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 5, NULL, - 5, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 6, NULL, - 6, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 7, NULL, - 7, GPIO_ACTIVE_LOW), - { }, - }, -}; - -#define MSP_GPIO_REG(offset) (msp_gpios[(offset)] >> 3) -#define MSP_GPIO_MASK(offset) BIT(msp_gpios[(offset)] & 0x07) - -static int msp_gpio_in(struct gpio_chip *chip, unsigned offset) -{ - switch (MSP_GPIO_REG(offset)) { - case DM355EVM_MSP_SWITCH1: - case DM355EVM_MSP_SWITCH2: - case DM355EVM_MSP_SDMMC: - return 0; - default: - return -EINVAL; - } -} - -static u8 msp_led_cache; - -static int msp_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - int reg, status; - - reg = MSP_GPIO_REG(offset); - status = dm355evm_msp_read(reg); - if (status < 0) - return status; - if (reg == DM355EVM_MSP_LED) - msp_led_cache = status; - return !!(status & MSP_GPIO_MASK(offset)); -} - -static int msp_gpio_out(struct gpio_chip *chip, unsigned offset, int value) -{ - int mask, bits; - - /* NOTE: there are some other signals that could be - * packaged as output GPIOs, but they aren't as useful - * as the LEDs ... so for now we don't. - */ - if (MSP_GPIO_REG(offset) != DM355EVM_MSP_LED) - return -EINVAL; - - mask = MSP_GPIO_MASK(offset); - bits = msp_led_cache; - - bits &= ~mask; - if (value) - bits |= mask; - msp_led_cache = bits; - - return dm355evm_msp_write(bits, DM355EVM_MSP_LED); -} - -static void msp_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - msp_gpio_out(chip, offset, value); -} - -static struct gpio_chip dm355evm_msp_gpio = { - .label = "dm355evm_msp", - .owner = THIS_MODULE, - .direction_input = msp_gpio_in, - .get = msp_gpio_get, - .direction_output = msp_gpio_out, - .set = msp_gpio_set, - .base = -EINVAL, /* dynamic assignment */ - .ngpio = ARRAY_SIZE(msp_gpios), - .can_sleep = true, -}; - -/*----------------------------------------------------------------------*/ - -static struct device *add_child(struct i2c_client *client, const char *name, - void *pdata, unsigned pdata_len, - bool can_wakeup, int irq) -{ - struct platform_device *pdev; - int status; - - pdev = platform_device_alloc(name, -1); - if (!pdev) - return ERR_PTR(-ENOMEM); - - device_init_wakeup(&pdev->dev, can_wakeup); - pdev->dev.parent = &client->dev; - - if (pdata) { - status = platform_device_add_data(pdev, pdata, pdata_len); - if (status < 0) { - dev_dbg(&pdev->dev, "can't add platform_data\n"); - goto put_device; - } - } - - if (irq) { - struct resource r = { - .start = irq, - .flags = IORESOURCE_IRQ, - }; - - status = platform_device_add_resources(pdev, &r, 1); - if (status < 0) { - dev_dbg(&pdev->dev, "can't add irq\n"); - goto put_device; - } - } - - status = platform_device_add(pdev); - if (status) - goto put_device; - - return &pdev->dev; - -put_device: - platform_device_put(pdev); - dev_err(&client->dev, "failed to add device %s\n", name); - return ERR_PTR(status); -} - -static int add_children(struct i2c_client *client) -{ - static const struct { - int offset; - char *label; - } config_inputs[] = { - /* 8 == right after the LEDs */ - { 8 + 0, "sw6_1", }, - { 8 + 1, "sw6_2", }, - { 8 + 2, "sw6_3", }, - { 8 + 3, "sw6_4", }, - { 8 + 4, "NTSC/nPAL", }, - }; - - struct device *child; - int status; - int i; - - /* GPIO-ish stuff */ - dm355evm_msp_gpio.parent = &client->dev; - status = gpiochip_add_data(&dm355evm_msp_gpio, NULL); - if (status < 0) - return status; - - /* LED output */ - if (msp_has_leds()) { - gpiod_add_lookup_table(&evm_leds_gpio_table); - /* NOTE: these are the only fully programmable LEDs - * on the board, since GPIO-61/ds22 (and many signals - * going to DC7) must be used for AEMIF address lines - * unless the top 1 GB of NAND is unused... - */ - child = add_child(client, "leds-gpio", - &evm_led_data, sizeof(evm_led_data), - false, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - /* configuration inputs */ - for (i = 0; i < ARRAY_SIZE(config_inputs); i++) { - int gpio = dm355evm_msp_gpio.base + config_inputs[i].offset; - - gpio_request_one(gpio, GPIOF_IN, config_inputs[i].label); - - /* make it easy for userspace to see these */ - gpio_export(gpio, false); - } - - /* MMC/SD inputs -- right after the last config input */ - if (dev_get_platdata(&client->dev)) { - void (*mmcsd_setup)(unsigned) = dev_get_platdata(&client->dev); - - mmcsd_setup(dm355evm_msp_gpio.base + 8 + 5); - } - - /* RTC is a 32 bit counter, no alarm */ - if (msp_has_rtc()) { - child = add_child(client, "rtc-dm355evm", - NULL, 0, false, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - /* input from buttons and IR remote (uses the IRQ) */ - if (msp_has_keyboard()) { - child = add_child(client, "dm355evm_keys", - NULL, 0, true, client->irq); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - return 0; -} - -/*----------------------------------------------------------------------*/ - -static void dm355evm_command(unsigned command) -{ - int status; - - status = dm355evm_msp_write(command, DM355EVM_MSP_COMMAND); - if (status < 0) - dev_err(&msp430->dev, "command %d failure %d\n", - command, status); -} - -static void dm355evm_power_off(void) -{ - dm355evm_command(MSP_COMMAND_POWEROFF); -} - -static int dm355evm_msp_remove(struct i2c_client *client) -{ - pm_power_off = NULL; - msp430 = NULL; - return 0; -} - -static int -dm355evm_msp_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - int status; - const char *video = msp_has_tvp() ? "TVP5146" : "imager"; - - if (msp430) - return -EBUSY; - msp430 = client; - - /* display revision status; doubles as sanity check */ - status = dm355evm_msp_read(DM355EVM_MSP_FIRMREV); - if (status < 0) - goto fail; - dev_info(&client->dev, "firmware v.%02X, %s as video-in\n", - status, video); - - /* mux video input: either tvp5146 or some external imager */ - status = dm355evm_msp_write(msp_has_tvp() ? 0 : MSP_VIDEO_IMAGER, - DM355EVM_MSP_VIDEO_IN); - if (status < 0) - dev_warn(&client->dev, "error %d muxing %s as video-in\n", - status, video); - - /* init LED cache, and turn off the LEDs */ - msp_led_cache = 0xff; - dm355evm_msp_write(msp_led_cache, DM355EVM_MSP_LED); - - /* export capabilities we support */ - status = add_children(client); - if (status < 0) - goto fail; - - /* PM hookup */ - pm_power_off = dm355evm_power_off; - - return 0; - -fail: - /* FIXME remove children ... */ - dm355evm_msp_remove(client); - return status; -} - -static const struct i2c_device_id dm355evm_msp_ids[] = { - { "dm355evm_msp", 0 }, - { /* end of list */ }, -}; -MODULE_DEVICE_TABLE(i2c, dm355evm_msp_ids); - -static struct i2c_driver dm355evm_msp_driver = { - .driver.name = "dm355evm_msp", - .id_table = dm355evm_msp_ids, - .probe = dm355evm_msp_probe, - .remove = dm355evm_msp_remove, -}; - -static int __init dm355evm_msp_init(void) -{ - return i2c_add_driver(&dm355evm_msp_driver); -} -subsys_initcall(dm355evm_msp_init); - -static void __exit dm355evm_msp_exit(void) -{ - i2c_del_driver(&dm355evm_msp_driver); -} -module_exit(dm355evm_msp_exit); - -MODULE_DESCRIPTION("Interface to MSP430 firmware on DM355EVM"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/ene-kb3930.c b/drivers/mfd/ene-kb3930.c index 1b73318d1f1f..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; } @@ -177,7 +177,7 @@ static int kb3930_probe(struct i2c_client *client) return 0; } -static int kb3930_remove(struct i2c_client *client) +static void kb3930_remove(struct i2c_client *client) { struct kb3930 *ddata = i2c_get_clientdata(client); @@ -187,8 +187,6 @@ static int kb3930_remove(struct i2c_client *client) unregister_restart_handler(&kb3930_restart_nb); } kb3930_power_off = NULL; - - return 0; } static const struct of_device_id kb3930_dt_ids[] = { @@ -198,7 +196,7 @@ static const struct of_device_id kb3930_dt_ids[] = { MODULE_DEVICE_TABLE(of, kb3930_dt_ids); static struct i2c_driver kb3930_driver = { - .probe_new = kb3930_probe, + .probe = kb3930_probe, .remove = kb3930_remove, .driver = { .name = "ene-kb3930", diff --git a/drivers/mfd/exynos-lpass.c b/drivers/mfd/exynos-lpass.c index 99bd0e73c19c..9bb2687c2835 100644 --- a/drivers/mfd/exynos-lpass.c +++ b/drivers/mfd/exynos-lpass.c @@ -15,7 +15,6 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/mfd/syscon.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> @@ -102,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); @@ -125,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); @@ -137,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) @@ -184,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 70fa18b04ad2..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; @@ -193,13 +188,11 @@ static void pcap_isr_work(struct work_struct *work) ezx_pcap_write(pcap, PCAP_REG_MSR, isr | msr); ezx_pcap_write(pcap, PCAP_REG_ISR, isr); - local_irq_disable(); service = isr & ~msr; for (irq = pcap->irq_base; service; service >>= 1, irq++) { if (service & 1) - generic_handle_irq(irq); + generic_handle_irq_safe(irq); } - local_irq_enable(); ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr); } while (gpio_get_value(pdata->gpio)); } @@ -337,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) { @@ -392,7 +357,7 @@ static int pcap_add_subdev(struct pcap_chip *pcap, return ret; } -static int ezx_pcap_remove(struct spi_device *spi) +static void ezx_pcap_remove(struct spi_device *spi) { struct pcap_chip *pcap = spi_get_drvdata(spi); unsigned long flags; @@ -412,8 +377,6 @@ static int ezx_pcap_remove(struct spi_device *spi) irq_set_chip_and_handler(i, NULL, NULL); destroy_workqueue(pcap->workqueue); - - return 0; } static int ezx_pcap_probe(struct spi_device *spi) @@ -532,7 +495,6 @@ static void __exit ezx_pcap_exit(void) subsys_initcall(ezx_pcap_init); module_exit(ezx_pcap_exit); -MODULE_LICENSE("GPL"); MODULE_AUTHOR("Daniel Ribeiro / Harald Welte"); MODULE_DESCRIPTION("Motorola PCAP2 ASIC Driver"); MODULE_ALIAS("spi:ezx-pcap"); diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c index 5f6f0a83e1c5..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, @@ -35,10 +34,10 @@ static void mx25_tsadc_irq_handler(struct irq_desc *desc) regmap_read(tsadc->regs, MX25_TSC_TGSR, &status); if (status & MX25_TGSR_GCQ_INT) - generic_handle_irq(irq_find_mapping(tsadc->domain, 1)); + generic_handle_domain_irq(tsadc->domain, 1); if (status & MX25_TGSR_TCQ_INT) - generic_handle_irq(irq_find_mapping(tsadc->domain, 0)); + generic_handle_domain_irq(tsadc->domain, 0); chained_irq_exit(chip, desc); } @@ -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) + 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; @@ -84,6 +82,19 @@ static int mx25_tsadc_setup_irq(struct platform_device *pdev, return 0; } +static int mx25_tsadc_unset_irq(struct platform_device *pdev) +{ + struct mx25_tsadc *tsadc = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (irq >= 0) { + irq_set_chained_handler_and_data(irq, NULL, NULL); + irq_domain_remove(tsadc->domain); + } + + return 0; +} + static void mx25_tsadc_setup_clk(struct platform_device *pdev, struct mx25_tsadc *tsadc) { @@ -124,7 +135,6 @@ static int mx25_tsadc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mx25_tsadc *tsadc; - struct resource *res; int ret; void __iomem *iomem; @@ -132,8 +142,7 @@ static int mx25_tsadc_probe(struct platform_device *pdev) if (!tsadc) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - iomem = devm_ioremap_resource(dev, res); + iomem = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(iomem)) return PTR_ERR(iomem); @@ -171,20 +180,21 @@ static int mx25_tsadc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, tsadc); - return devm_of_platform_populate(dev); -} + ret = devm_of_platform_populate(dev); + if (ret) + goto err_irq; -static int mx25_tsadc_remove(struct platform_device *pdev) -{ - struct mx25_tsadc *tsadc = platform_get_drvdata(pdev); - int irq = platform_get_irq(pdev, 0); + return 0; - if (irq) { - irq_set_chained_handler_and_data(irq, NULL, NULL); - irq_domain_remove(tsadc->domain); - } +err_irq: + mx25_tsadc_unset_irq(pdev); - return 0; + return ret; +} + +static void mx25_tsadc_remove(struct platform_device *pdev) +{ + mx25_tsadc_unset_irq(pdev); } static const struct of_device_id mx25_tsadc_ids[] = { diff --git a/drivers/mfd/gateworks-gsc.c b/drivers/mfd/gateworks-gsc.c index d87876747b91..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, }; @@ -189,8 +189,7 @@ static const struct regmap_irq_chip gsc_irq_chip = { .num_irqs = ARRAY_SIZE(gsc_irqs), .num_regs = 1, .status_base = GSC_IRQ_STATUS, - .mask_base = GSC_IRQ_ENABLE, - .mask_invert = true, + .unmask_base = GSC_IRQ_ENABLE, .ack_base = GSC_IRQ_STATUS, .ack_invert = true, }; @@ -255,11 +254,9 @@ static int gsc_probe(struct i2c_client *client) return 0; } -static int gsc_remove(struct i2c_client *client) +static void gsc_remove(struct i2c_client *client) { sysfs_remove_group(&client->dev.kobj, &attr_group); - - return 0; } static struct i2c_driver gsc_driver = { @@ -267,7 +264,7 @@ static struct i2c_driver gsc_driver = { .name = "gateworks-gsc", .of_match_table = gsc_of_match, }, - .probe_new = gsc_probe, + .probe = gsc_probe, .remove = gsc_remove, }; module_i2c_driver(gsc_driver); diff --git a/drivers/mfd/hi6421-pmic-core.c b/drivers/mfd/hi6421-pmic-core.c index eba88b80d969..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,24 +51,18 @@ MODULE_DEVICE_TABLE(of, of_hi6421_pmic_match); static int hi6421_pmic_probe(struct platform_device *pdev) { struct hi6421_pmic *pmic; - struct resource *res; - 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) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/mfd/hi6421-spmi-pmic.c b/drivers/mfd/hi6421-spmi-pmic.c new file mode 100644 index 000000000000..c9c0c3d7011f --- /dev/null +++ b/drivers/mfd/hi6421-spmi-pmic.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device driver for regulators in HISI PMIC IC + * + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2011 Hisilicon. + * Copyright (c) 2020-2021 Huawei Technologies Co., Ltd. + */ + +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/spmi.h> + +static const struct mfd_cell hi6421v600_devs[] = { + { .name = "hi6421v600-irq", }, + { .name = "hi6421v600-regulator", }, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 16, + .val_bits = BITS_PER_BYTE, + .max_register = 0xffff, + .fast_io = true +}; + +static int hi6421_spmi_pmic_probe(struct spmi_device *sdev) +{ + struct device *dev = &sdev->dev; + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_spmi_ext(sdev, ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + dev_set_drvdata(&sdev->dev, regmap); + + ret = devm_mfd_add_devices(&sdev->dev, PLATFORM_DEVID_NONE, + hi6421v600_devs, ARRAY_SIZE(hi6421v600_devs), + NULL, 0, NULL); + if (ret < 0) + dev_err(dev, "Failed to add child devices: %d\n", ret); + + return ret; +} + +static const struct of_device_id pmic_spmi_id_table[] = { + { .compatible = "hisilicon,hi6421-spmi" }, + { } +}; +MODULE_DEVICE_TABLE(of, pmic_spmi_id_table); + +static struct spmi_driver hi6421_spmi_pmic_driver = { + .driver = { + .name = "hi6421-spmi-pmic", + .of_match_table = pmic_spmi_id_table, + }, + .probe = hi6421_spmi_pmic_probe, +}; +module_spmi_driver(hi6421_spmi_pmic_driver); + +MODULE_DESCRIPTION("HiSilicon Hi6421v600 SPMI PMIC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/hi655x-pmic.c b/drivers/mfd/hi655x-pmic.c index 6909d075d017..3b4ffcbbee20 100644 --- a/drivers/mfd/hi655x-pmic.c +++ b/drivers/mfd/hi655x-pmic.c @@ -9,15 +9,14 @@ * Fei Wang <w.f@huawei.com> */ -#include <linux/gpio.h> #include <linux/io.h> #include <linux/interrupt.h> #include <linux/init.h> #include <linux/mfd/core.h> #include <linux/mfd/hi655x-pmic.h> #include <linux/module.h> -#include <linux/of_gpio.h> -#include <linux/of_platform.h> +#include <linux/gpio/consumer.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/regmap.h> @@ -42,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, @@ -94,7 +93,6 @@ static int hi655x_pmic_probe(struct platform_device *pdev) int ret; struct hi655x_pmic *pmic; struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; void __iomem *base; pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); @@ -102,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); @@ -120,21 +117,12 @@ static int hi655x_pmic_probe(struct platform_device *pdev) hi655x_local_irq_clear(pmic->regmap); - pmic->gpio = of_get_named_gpio(np, "pmic-gpios", 0); - if (!gpio_is_valid(pmic->gpio)) { - dev_err(dev, "Failed to get the pmic-gpios\n"); - return -ENODEV; - } + pmic->gpio = devm_gpiod_get_optional(dev, "pmic", GPIOD_IN); + if (IS_ERR(pmic->gpio)) + return dev_err_probe(dev, PTR_ERR(pmic->gpio), + "Failed to request hi655x pmic-gpio"); - ret = devm_gpio_request_one(dev, pmic->gpio, GPIOF_IN, - "hi655x_pmic_irq"); - if (ret < 0) { - dev_err(dev, "Failed to request gpio %d ret = %d\n", - pmic->gpio, ret); - return ret; - } - - ret = regmap_add_irq_chip(pmic->regmap, gpio_to_irq(pmic->gpio), + ret = regmap_add_irq_chip(pmic->regmap, gpiod_to_irq(pmic->gpio), IRQF_TRIGGER_LOW | IRQF_NO_SUSPEND, 0, &hi655x_irq_chip, &pmic->irq_data); if (ret) { @@ -149,20 +137,19 @@ static int hi655x_pmic_probe(struct platform_device *pdev) regmap_irq_get_domain(pmic->irq_data)); if (ret) { dev_err(dev, "Failed to register device %d\n", ret); - regmap_del_irq_chip(gpio_to_irq(pmic->gpio), pmic->irq_data); + regmap_del_irq_chip(gpiod_to_irq(pmic->gpio), pmic->irq_data); return ret; } 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(gpio_to_irq(pmic->gpio), pmic->irq_data); + 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[] = { @@ -172,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/htc-i2cpld.c b/drivers/mfd/htc-i2cpld.c deleted file mode 100644 index 417b0355d904..000000000000 --- a/drivers/mfd/htc-i2cpld.c +++ /dev/null @@ -1,631 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * htc-i2cpld.c - * Chip driver for an unknown CPLD chip found on omap850 HTC devices like - * the HTC Wizard and HTC Herald. - * The cpld is located on the i2c bus and acts as an input/output GPIO - * extender. - * - * Copyright (C) 2009 Cory Maccarrone <darkstar6262@gmail.com> - * - * Based on work done in the linwizard project - * Copyright (C) 2008-2009 Angelo Arrifano <miknix@gmail.com> - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/i2c.h> -#include <linux/irq.h> -#include <linux/spinlock.h> -#include <linux/htcpld.h> -#include <linux/gpio.h> -#include <linux/slab.h> - -struct htcpld_chip { - spinlock_t lock; - - /* chip info */ - u8 reset; - u8 addr; - struct device *dev; - struct i2c_client *client; - - /* Output details */ - u8 cache_out; - struct gpio_chip chip_out; - - /* Input details */ - u8 cache_in; - struct gpio_chip chip_in; - - u16 irqs_enabled; - uint irq_start; - int nirqs; - - unsigned int flow_type; - /* - * Work structure to allow for setting values outside of any - * possible interrupt context - */ - struct work_struct set_val_work; -}; - -struct htcpld_data { - /* irq info */ - u16 irqs_enabled; - uint irq_start; - int nirqs; - uint chained_irq; - unsigned int int_reset_gpio_hi; - unsigned int int_reset_gpio_lo; - - /* htcpld info */ - struct htcpld_chip *chip; - unsigned int nchips; -}; - -/* There does not appear to be a way to proactively mask interrupts - * on the htcpld chip itself. So, we simply ignore interrupts that - * aren't desired. */ -static void htcpld_mask(struct irq_data *data) -{ - struct htcpld_chip *chip = irq_data_get_irq_chip_data(data); - chip->irqs_enabled &= ~(1 << (data->irq - chip->irq_start)); - pr_debug("HTCPLD mask %d %04x\n", data->irq, chip->irqs_enabled); -} -static void htcpld_unmask(struct irq_data *data) -{ - struct htcpld_chip *chip = irq_data_get_irq_chip_data(data); - chip->irqs_enabled |= 1 << (data->irq - chip->irq_start); - pr_debug("HTCPLD unmask %d %04x\n", data->irq, chip->irqs_enabled); -} - -static int htcpld_set_type(struct irq_data *data, unsigned int flags) -{ - struct htcpld_chip *chip = irq_data_get_irq_chip_data(data); - - if (flags & ~IRQ_TYPE_SENSE_MASK) - return -EINVAL; - - /* We only allow edge triggering */ - if (flags & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)) - return -EINVAL; - - chip->flow_type = flags; - return 0; -} - -static struct irq_chip htcpld_muxed_chip = { - .name = "htcpld", - .irq_mask = htcpld_mask, - .irq_unmask = htcpld_unmask, - .irq_set_type = htcpld_set_type, -}; - -/* To properly dispatch IRQ events, we need to read from the - * chip. This is an I2C action that could possibly sleep - * (which is bad in interrupt context) -- so we use a threaded - * interrupt handler to get around that. - */ -static irqreturn_t htcpld_handler(int irq, void *dev) -{ - struct htcpld_data *htcpld = dev; - unsigned int i; - unsigned long flags; - int irqpin; - - if (!htcpld) { - pr_debug("htcpld is null in ISR\n"); - return IRQ_HANDLED; - } - - /* - * For each chip, do a read of the chip and trigger any interrupts - * desired. The interrupts will be triggered from LSB to MSB (i.e. - * bit 0 first, then bit 1, etc.) - * - * For chips that have no interrupt range specified, just skip 'em. - */ - for (i = 0; i < htcpld->nchips; i++) { - struct htcpld_chip *chip = &htcpld->chip[i]; - struct i2c_client *client; - int val; - unsigned long uval, old_val; - - if (!chip) { - pr_debug("chip %d is null in ISR\n", i); - continue; - } - - if (chip->nirqs == 0) - continue; - - client = chip->client; - if (!client) { - pr_debug("client %d is null in ISR\n", i); - continue; - } - - /* Scan the chip */ - val = i2c_smbus_read_byte_data(client, chip->cache_out); - if (val < 0) { - /* Throw a warning and skip this chip */ - dev_warn(chip->dev, "Unable to read from chip: %d\n", - val); - continue; - } - - uval = (unsigned long)val; - - spin_lock_irqsave(&chip->lock, flags); - - /* Save away the old value so we can compare it */ - old_val = chip->cache_in; - - /* Write the new value */ - chip->cache_in = uval; - - spin_unlock_irqrestore(&chip->lock, flags); - - /* - * For each bit in the data (starting at bit 0), trigger - * associated interrupts. - */ - for (irqpin = 0; irqpin < chip->nirqs; irqpin++) { - unsigned oldb, newb, type = chip->flow_type; - - irq = chip->irq_start + irqpin; - - /* Run the IRQ handler, but only if the bit value - * changed, and the proper flags are set */ - oldb = (old_val >> irqpin) & 1; - newb = (uval >> irqpin) & 1; - - if ((!oldb && newb && (type & IRQ_TYPE_EDGE_RISING)) || - (oldb && !newb && (type & IRQ_TYPE_EDGE_FALLING))) { - pr_debug("fire IRQ %d\n", irqpin); - generic_handle_irq(irq); - } - } - } - - /* - * In order to continue receiving interrupts, the int_reset_gpio must - * be asserted. - */ - if (htcpld->int_reset_gpio_hi) - gpio_set_value(htcpld->int_reset_gpio_hi, 1); - if (htcpld->int_reset_gpio_lo) - gpio_set_value(htcpld->int_reset_gpio_lo, 0); - - return IRQ_HANDLED; -} - -/* - * The GPIO set routines can be called from interrupt context, especially if, - * for example they're attached to the led-gpio framework and a trigger is - * enabled. As such, we declared work above in the htcpld_chip structure, - * and that work is scheduled in the set routine. The kernel can then run - * the I2C functions, which will sleep, in process context. - */ -static void htcpld_chip_set(struct gpio_chip *chip, unsigned offset, int val) -{ - struct i2c_client *client; - struct htcpld_chip *chip_data = gpiochip_get_data(chip); - unsigned long flags; - - client = chip_data->client; - if (!client) - return; - - spin_lock_irqsave(&chip_data->lock, flags); - if (val) - chip_data->cache_out |= (1 << offset); - else - chip_data->cache_out &= ~(1 << offset); - spin_unlock_irqrestore(&chip_data->lock, flags); - - schedule_work(&(chip_data->set_val_work)); -} - -static void htcpld_chip_set_ni(struct work_struct *work) -{ - struct htcpld_chip *chip_data; - struct i2c_client *client; - - chip_data = container_of(work, struct htcpld_chip, set_val_work); - client = chip_data->client; - i2c_smbus_read_byte_data(client, chip_data->cache_out); -} - -static int htcpld_chip_get(struct gpio_chip *chip, unsigned offset) -{ - struct htcpld_chip *chip_data = gpiochip_get_data(chip); - u8 cache; - - if (!strncmp(chip->label, "htcpld-out", 10)) { - cache = chip_data->cache_out; - } else if (!strncmp(chip->label, "htcpld-in", 9)) { - cache = chip_data->cache_in; - } else - return -EINVAL; - - return (cache >> offset) & 1; -} - -static int htcpld_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - htcpld_chip_set(chip, offset, value); - return 0; -} - -static int htcpld_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - /* - * No-op: this function can only be called on the input chip. - * We do however make sure the offset is within range. - */ - return (offset < chip->ngpio) ? 0 : -EINVAL; -} - -static int htcpld_chip_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct htcpld_chip *chip_data = gpiochip_get_data(chip); - - if (offset < chip_data->nirqs) - return chip_data->irq_start + offset; - else - return -EINVAL; -} - -static void htcpld_chip_reset(struct i2c_client *client) -{ - struct htcpld_chip *chip_data = i2c_get_clientdata(client); - if (!chip_data) - return; - - i2c_smbus_read_byte_data( - client, (chip_data->cache_out = chip_data->reset)); -} - -static int htcpld_setup_chip_irq( - struct platform_device *pdev, - int chip_index) -{ - struct htcpld_data *htcpld; - struct htcpld_chip *chip; - unsigned int irq, irq_end; - - /* Get the platform and driver data */ - htcpld = platform_get_drvdata(pdev); - chip = &htcpld->chip[chip_index]; - - /* Setup irq handlers */ - irq_end = chip->irq_start + chip->nirqs; - for (irq = chip->irq_start; irq < irq_end; irq++) { - irq_set_chip_and_handler(irq, &htcpld_muxed_chip, - handle_simple_irq); - irq_set_chip_data(irq, chip); - irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); - } - - return 0; -} - -static int htcpld_register_chip_i2c( - struct platform_device *pdev, - int chip_index) -{ - struct htcpld_data *htcpld; - struct device *dev = &pdev->dev; - struct htcpld_core_platform_data *pdata; - struct htcpld_chip *chip; - struct htcpld_chip_platform_data *plat_chip_data; - struct i2c_adapter *adapter; - struct i2c_client *client; - struct i2c_board_info info; - - /* Get the platform and driver data */ - pdata = dev_get_platdata(dev); - htcpld = platform_get_drvdata(pdev); - chip = &htcpld->chip[chip_index]; - plat_chip_data = &pdata->chip[chip_index]; - - adapter = i2c_get_adapter(pdata->i2c_adapter_id); - if (!adapter) { - /* Eek, no such I2C adapter! Bail out. */ - dev_warn(dev, "Chip at i2c address 0x%x: Invalid i2c adapter %d\n", - plat_chip_data->addr, pdata->i2c_adapter_id); - return -ENODEV; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) { - dev_warn(dev, "i2c adapter %d non-functional\n", - pdata->i2c_adapter_id); - i2c_put_adapter(adapter); - return -EINVAL; - } - - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = plat_chip_data->addr; - strlcpy(info.type, "htcpld-chip", I2C_NAME_SIZE); - info.platform_data = chip; - - /* Add the I2C device. This calls the probe() function. */ - client = i2c_new_client_device(adapter, &info); - if (IS_ERR(client)) { - /* I2C device registration failed, contineu with the next */ - dev_warn(dev, "Unable to add I2C device for 0x%x\n", - plat_chip_data->addr); - i2c_put_adapter(adapter); - return PTR_ERR(client); - } - - i2c_set_clientdata(client, chip); - snprintf(client->name, I2C_NAME_SIZE, "Chip_0x%x", client->addr); - chip->client = client; - - /* Reset the chip */ - htcpld_chip_reset(client); - chip->cache_in = i2c_smbus_read_byte_data(client, chip->cache_out); - - return 0; -} - -static void htcpld_unregister_chip_i2c( - struct platform_device *pdev, - int chip_index) -{ - struct htcpld_data *htcpld; - struct htcpld_chip *chip; - - /* Get the platform and driver data */ - htcpld = platform_get_drvdata(pdev); - chip = &htcpld->chip[chip_index]; - - i2c_unregister_device(chip->client); -} - -static int htcpld_register_chip_gpio( - struct platform_device *pdev, - int chip_index) -{ - struct htcpld_data *htcpld; - struct device *dev = &pdev->dev; - struct htcpld_core_platform_data *pdata; - struct htcpld_chip *chip; - struct htcpld_chip_platform_data *plat_chip_data; - struct gpio_chip *gpio_chip; - int ret = 0; - - /* Get the platform and driver data */ - pdata = dev_get_platdata(dev); - htcpld = platform_get_drvdata(pdev); - chip = &htcpld->chip[chip_index]; - plat_chip_data = &pdata->chip[chip_index]; - - /* Setup the GPIO chips */ - gpio_chip = &(chip->chip_out); - gpio_chip->label = "htcpld-out"; - gpio_chip->parent = dev; - gpio_chip->owner = THIS_MODULE; - gpio_chip->get = htcpld_chip_get; - gpio_chip->set = htcpld_chip_set; - gpio_chip->direction_input = NULL; - gpio_chip->direction_output = htcpld_direction_output; - gpio_chip->base = plat_chip_data->gpio_out_base; - gpio_chip->ngpio = plat_chip_data->num_gpios; - - gpio_chip = &(chip->chip_in); - gpio_chip->label = "htcpld-in"; - gpio_chip->parent = dev; - gpio_chip->owner = THIS_MODULE; - gpio_chip->get = htcpld_chip_get; - gpio_chip->set = NULL; - gpio_chip->direction_input = htcpld_direction_input; - gpio_chip->direction_output = NULL; - gpio_chip->to_irq = htcpld_chip_to_irq; - gpio_chip->base = plat_chip_data->gpio_in_base; - gpio_chip->ngpio = plat_chip_data->num_gpios; - - /* Add the GPIO chips */ - ret = gpiochip_add_data(&(chip->chip_out), chip); - if (ret) { - dev_warn(dev, "Unable to register output GPIOs for 0x%x: %d\n", - plat_chip_data->addr, ret); - return ret; - } - - ret = gpiochip_add_data(&(chip->chip_in), chip); - if (ret) { - dev_warn(dev, "Unable to register input GPIOs for 0x%x: %d\n", - plat_chip_data->addr, ret); - gpiochip_remove(&(chip->chip_out)); - return ret; - } - - return 0; -} - -static int htcpld_setup_chips(struct platform_device *pdev) -{ - struct htcpld_data *htcpld; - struct device *dev = &pdev->dev; - struct htcpld_core_platform_data *pdata; - int i; - - /* Get the platform and driver data */ - pdata = dev_get_platdata(dev); - htcpld = platform_get_drvdata(pdev); - - /* Setup each chip's output GPIOs */ - htcpld->nchips = pdata->num_chip; - htcpld->chip = devm_kcalloc(dev, - htcpld->nchips, - sizeof(struct htcpld_chip), - GFP_KERNEL); - if (!htcpld->chip) - return -ENOMEM; - - /* Add the chips as best we can */ - for (i = 0; i < htcpld->nchips; i++) { - int ret; - - /* Setup the HTCPLD chips */ - htcpld->chip[i].reset = pdata->chip[i].reset; - htcpld->chip[i].cache_out = pdata->chip[i].reset; - htcpld->chip[i].cache_in = 0; - htcpld->chip[i].dev = dev; - htcpld->chip[i].irq_start = pdata->chip[i].irq_base; - htcpld->chip[i].nirqs = pdata->chip[i].num_irqs; - - INIT_WORK(&(htcpld->chip[i].set_val_work), &htcpld_chip_set_ni); - spin_lock_init(&(htcpld->chip[i].lock)); - - /* Setup the interrupts for the chip */ - if (htcpld->chained_irq) { - ret = htcpld_setup_chip_irq(pdev, i); - if (ret) - continue; - } - - /* Register the chip with I2C */ - ret = htcpld_register_chip_i2c(pdev, i); - if (ret) - continue; - - - /* Register the chips with the GPIO subsystem */ - ret = htcpld_register_chip_gpio(pdev, i); - if (ret) { - /* Unregister the chip from i2c and continue */ - htcpld_unregister_chip_i2c(pdev, i); - continue; - } - - dev_info(dev, "Registered chip at 0x%x\n", pdata->chip[i].addr); - } - - return 0; -} - -static int htcpld_core_probe(struct platform_device *pdev) -{ - struct htcpld_data *htcpld; - struct device *dev = &pdev->dev; - struct htcpld_core_platform_data *pdata; - struct resource *res; - int ret = 0; - - if (!dev) - return -ENODEV; - - pdata = dev_get_platdata(dev); - if (!pdata) { - dev_warn(dev, "Platform data not found for htcpld core!\n"); - return -ENXIO; - } - - htcpld = devm_kzalloc(dev, sizeof(struct htcpld_data), GFP_KERNEL); - if (!htcpld) - return -ENOMEM; - - /* Find chained irq */ - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res) { - int flags; - htcpld->chained_irq = res->start; - - /* Setup the chained interrupt handler */ - flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | - IRQF_ONESHOT; - ret = request_threaded_irq(htcpld->chained_irq, - NULL, htcpld_handler, - flags, pdev->name, htcpld); - if (ret) { - dev_warn(dev, "Unable to setup chained irq handler: %d\n", ret); - return ret; - } else - device_init_wakeup(dev, 0); - } - - /* Set the driver data */ - platform_set_drvdata(pdev, htcpld); - - /* Setup the htcpld chips */ - ret = htcpld_setup_chips(pdev); - if (ret) - return ret; - - /* Request the GPIO(s) for the int reset and set them up */ - if (pdata->int_reset_gpio_hi) { - ret = gpio_request(pdata->int_reset_gpio_hi, "htcpld-core"); - if (ret) { - /* - * If it failed, that sucks, but we can probably - * continue on without it. - */ - dev_warn(dev, "Unable to request int_reset_gpio_hi -- interrupts may not work\n"); - htcpld->int_reset_gpio_hi = 0; - } else { - htcpld->int_reset_gpio_hi = pdata->int_reset_gpio_hi; - gpio_set_value(htcpld->int_reset_gpio_hi, 1); - } - } - - if (pdata->int_reset_gpio_lo) { - ret = gpio_request(pdata->int_reset_gpio_lo, "htcpld-core"); - if (ret) { - /* - * If it failed, that sucks, but we can probably - * continue on without it. - */ - dev_warn(dev, "Unable to request int_reset_gpio_lo -- interrupts may not work\n"); - htcpld->int_reset_gpio_lo = 0; - } else { - htcpld->int_reset_gpio_lo = pdata->int_reset_gpio_lo; - gpio_set_value(htcpld->int_reset_gpio_lo, 0); - } - } - - dev_info(dev, "Initialized successfully\n"); - return 0; -} - -/* The I2C Driver -- used internally */ -static const struct i2c_device_id htcpld_chip_id[] = { - { "htcpld-chip", 0 }, - { } -}; - -static struct i2c_driver htcpld_chip_driver = { - .driver = { - .name = "htcpld-chip", - }, - .id_table = htcpld_chip_id, -}; - -/* The Core Driver */ -static struct platform_driver htcpld_core_driver = { - .driver = { - .name = "i2c-htcpld", - }, -}; - -static int __init htcpld_core_init(void) -{ - int ret; - - /* Register the I2C Chip driver */ - ret = i2c_add_driver(&htcpld_chip_driver); - if (ret) - return ret; - - /* Probe for our chips */ - return platform_driver_probe(&htcpld_core_driver, htcpld_core_probe); -} -device_initcall(htcpld_core_init); diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c deleted file mode 100644 index 0c46b7e64b2d..000000000000 --- a/drivers/mfd/htc-pasic3.c +++ /dev/null @@ -1,210 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Core driver for HTC PASIC3 LED/DS1WM chip. - * - * Copyright (C) 2006 Philipp Zabel <philipp.zabel@gmail.com> - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/platform_device.h> - -#include <linux/gpio.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/interrupt.h> -#include <linux/mfd/core.h> -#include <linux/mfd/ds1wm.h> -#include <linux/mfd/htc-pasic3.h> -#include <linux/slab.h> - -struct pasic3_data { - void __iomem *mapping; - unsigned int bus_shift; -}; - -#define REG_ADDR 5 -#define REG_DATA 6 - -#define READ_MODE 0x80 - -/* - * write to a secondary register on the PASIC3 - */ -void pasic3_write_register(struct device *dev, u32 reg, u8 val) -{ - struct pasic3_data *asic = dev_get_drvdata(dev); - int bus_shift = asic->bus_shift; - void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift); - void __iomem *data = asic->mapping + (REG_DATA << bus_shift); - - __raw_writeb(~READ_MODE & reg, addr); - __raw_writeb(val, data); -} -EXPORT_SYMBOL(pasic3_write_register); /* for leds-pasic3 */ - -/* - * read from a secondary register on the PASIC3 - */ -u8 pasic3_read_register(struct device *dev, u32 reg) -{ - struct pasic3_data *asic = dev_get_drvdata(dev); - int bus_shift = asic->bus_shift; - void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift); - void __iomem *data = asic->mapping + (REG_DATA << bus_shift); - - __raw_writeb(READ_MODE | reg, addr); - return __raw_readb(data); -} -EXPORT_SYMBOL(pasic3_read_register); /* for leds-pasic3 */ - -/* - * LEDs - */ - -static struct mfd_cell led_cell __initdata = { - .name = "leds-pasic3", -}; - -/* - * DS1WM - */ - -static int ds1wm_enable(struct platform_device *pdev) -{ - struct device *dev = pdev->dev.parent; - int c; - - c = pasic3_read_register(dev, 0x28); - pasic3_write_register(dev, 0x28, c & 0x7f); - - dev_dbg(dev, "DS1WM OWM_EN low (active) %02x\n", c & 0x7f); - return 0; -} - -static int ds1wm_disable(struct platform_device *pdev) -{ - struct device *dev = pdev->dev.parent; - int c; - - c = pasic3_read_register(dev, 0x28); - pasic3_write_register(dev, 0x28, c | 0x80); - - dev_dbg(dev, "DS1WM OWM_EN high (inactive) %02x\n", c | 0x80); - return 0; -} - -static struct ds1wm_driver_data ds1wm_pdata = { - .active_high = 0, - .reset_recover_delay = 1, -}; - -static struct resource ds1wm_resources[] __initdata = { - [0] = { - .start = 0, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = 0, - .end = 0, - .flags = IORESOURCE_IRQ, - }, -}; - -static const struct mfd_cell ds1wm_cell __initconst = { - .name = "ds1wm", - .enable = ds1wm_enable, - .disable = ds1wm_disable, - .platform_data = &ds1wm_pdata, - .pdata_size = sizeof(ds1wm_pdata), - .num_resources = 2, - .resources = ds1wm_resources, -}; - -static int __init pasic3_probe(struct platform_device *pdev) -{ - struct pasic3_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct device *dev = &pdev->dev; - struct pasic3_data *asic; - struct resource *r; - int ret; - int irq = 0; - - r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (r) { - ds1wm_resources[1].flags = IORESOURCE_IRQ | (r->flags & - (IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE)); - irq = r->start; - } - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) - return -ENXIO; - - if (!request_mem_region(r->start, resource_size(r), "pasic3")) - return -EBUSY; - - asic = devm_kzalloc(dev, sizeof(struct pasic3_data), GFP_KERNEL); - if (!asic) - return -ENOMEM; - - platform_set_drvdata(pdev, asic); - - asic->mapping = ioremap(r->start, resource_size(r)); - if (!asic->mapping) { - dev_err(dev, "couldn't ioremap PASIC3\n"); - return -ENOMEM; - } - - /* calculate bus shift from mem resource */ - asic->bus_shift = (resource_size(r) - 5) >> 3; - - if (pdata && pdata->clock_rate) { - ds1wm_pdata.clock_rate = pdata->clock_rate; - /* the first 5 PASIC3 registers control the DS1WM */ - ds1wm_resources[0].end = (5 << asic->bus_shift) - 1; - ret = mfd_add_devices(&pdev->dev, pdev->id, - &ds1wm_cell, 1, r, irq, NULL); - if (ret < 0) - dev_warn(dev, "failed to register DS1WM\n"); - } - - if (pdata && pdata->led_pdata) { - led_cell.platform_data = pdata->led_pdata; - led_cell.pdata_size = sizeof(struct pasic3_leds_machinfo); - ret = mfd_add_devices(&pdev->dev, pdev->id, &led_cell, 1, r, - 0, NULL); - if (ret < 0) - dev_warn(dev, "failed to register LED device\n"); - } - - return 0; -} - -static int pasic3_remove(struct platform_device *pdev) -{ - struct pasic3_data *asic = platform_get_drvdata(pdev); - struct resource *r; - - mfd_remove_devices(&pdev->dev); - - iounmap(asic->mapping); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, resource_size(r)); - return 0; -} - -MODULE_ALIAS("platform:pasic3"); - -static struct platform_driver pasic3_driver = { - .driver = { - .name = "pasic3", - }, - .remove = pasic3_remove, -}; - -module_platform_driver_probe(pasic3_driver, pasic3_probe); - -MODULE_AUTHOR("Philipp Zabel <philipp.zabel@gmail.com>"); -MODULE_DESCRIPTION("Core driver for HTC PASIC3"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c index 1f396039d58f..63406026d809 100644 --- a/drivers/mfd/intel-lpss-acpi.c +++ b/drivers/mfd/intel-lpss-acpi.c @@ -8,18 +8,34 @@ * 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[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_SPT_SSP), + { } +}; + +static const struct software_node spt_spi_node = { + .properties = spt_spi_properties, +}; + static const struct intel_lpss_platform_info spt_info = { .clk_rate = 120000000, + .swnode = &spt_spi_node, }; static const struct property_entry spt_i2c_properties[] = { @@ -53,8 +69,18 @@ static const struct intel_lpss_platform_info spt_uart_info = { .swnode = &uart_node, }; +static const struct property_entry bxt_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BXT_SSP), + { } +}; + +static const struct software_node bxt_spi_node = { + .properties = bxt_spi_properties, +}; + static const struct intel_lpss_platform_info bxt_info = { .clk_rate = 100000000, + .swnode = &bxt_spi_node, }; static const struct property_entry bxt_i2c_properties[] = { @@ -89,6 +115,25 @@ static const struct intel_lpss_platform_info apl_i2c_info = { .swnode = &apl_i2c_node, }; +static const struct property_entry cnl_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_CNL_SSP), + { } +}; + +static const struct software_node cnl_spi_node = { + .properties = cnl_spi_properties, +}; + +static const struct intel_lpss_platform_info cnl_info = { + .clk_rate = 120000000, + .swnode = &cnl_spi_node, +}; + +static const struct intel_lpss_platform_info cnl_i2c_info = { + .clk_rate = 216000000, + .swnode = &spt_i2c_node, +}; + static const struct acpi_device_id intel_lpss_acpi_ids[] = { /* SPT */ { "INT3440", (kernel_ulong_t)&spt_info }, @@ -102,6 +147,19 @@ static const struct acpi_device_id intel_lpss_acpi_ids[] = { { "INT3448", (kernel_ulong_t)&spt_uart_info }, { "INT3449", (kernel_ulong_t)&spt_uart_info }, { "INT344A", (kernel_ulong_t)&spt_uart_info }, + /* CNL */ + { "INT34B0", (kernel_ulong_t)&cnl_info }, + { "INT34B1", (kernel_ulong_t)&cnl_info }, + { "INT34B2", (kernel_ulong_t)&cnl_i2c_info }, + { "INT34B3", (kernel_ulong_t)&cnl_i2c_info }, + { "INT34B4", (kernel_ulong_t)&cnl_i2c_info }, + { "INT34B5", (kernel_ulong_t)&cnl_i2c_info }, + { "INT34B6", (kernel_ulong_t)&cnl_i2c_info }, + { "INT34B7", (kernel_ulong_t)&cnl_i2c_info }, + { "INT34B8", (kernel_ulong_t)&spt_uart_info }, + { "INT34B9", (kernel_ulong_t)&spt_uart_info }, + { "INT34BA", (kernel_ulong_t)&spt_uart_info }, + { "INT34BC", (kernel_ulong_t)&cnl_info }, /* BXT */ { "80860AAC", (kernel_ulong_t)&bxt_i2c_info }, { "80860ABC", (kernel_ulong_t)&bxt_info }, @@ -116,44 +174,45 @@ 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); info->irq = platform_get_irq(pdev, 0); + ret = intel_lpss_probe(&pdev->dev, info); + if (ret) + return ret; + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); - return intel_lpss_probe(&pdev->dev, info); + 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), }, }; @@ -163,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 c54d19fb184c..8d92c895d3ae 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -8,18 +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" +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; @@ -27,13 +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); + + quirk_pci_info = pci_match_id(quirk_ids, pdev); + if (quirk_pci_info) + info->quirks = quirk_pci_info->driver_data; pdev->d3cold_delay = 0; @@ -59,10 +94,18 @@ 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), + { } +}; -static const struct intel_lpss_platform_info spt_info = { +static const struct software_node spt_spi_node = { + .properties = spt_spi_properties, +}; + +static const struct intel_lpss_platform_info spt_spi_info = { .clk_rate = 120000000, + .swnode = &spt_spi_node, }; static const struct property_entry spt_i2c_properties[] = { @@ -96,8 +139,18 @@ static const struct intel_lpss_platform_info spt_uart_info = { .swnode = &uart_node, }; -static const struct intel_lpss_platform_info bxt_info = { +static const struct property_entry bxt_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BXT_SSP), + { } +}; + +static const struct software_node bxt_spi_node = { + .properties = bxt_spi_properties, +}; + +static const struct intel_lpss_platform_info bxt_spi_info = { .clk_rate = 100000000, + .swnode = &bxt_spi_node, }; static const struct intel_lpss_platform_info bxt_uart_info = { @@ -154,6 +207,20 @@ static const struct intel_lpss_platform_info glk_i2c_info = { .swnode = &glk_i2c_node, }; +static const struct property_entry cnl_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_CNL_SSP), + { } +}; + +static const struct software_node cnl_spi_node = { + .properties = cnl_spi_properties, +}; + +static const struct intel_lpss_platform_info cnl_spi_info = { + .clk_rate = 120000000, + .swnode = &cnl_spi_node, +}; + static const struct intel_lpss_platform_info cnl_i2c_info = { .clk_rate = 216000000, .swnode = &spt_i2c_node, @@ -164,12 +231,26 @@ static const struct intel_lpss_platform_info ehl_i2c_info = { .swnode = &bxt_i2c_node, }; +static const struct property_entry tgl_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_CNL_SSP), + { } +}; + +static const struct software_node tgl_spi_node = { + .properties = tgl_spi_properties, +}; + +static const struct intel_lpss_platform_info tgl_spi_info = { + .clk_rate = 100000000, + .swnode = &tgl_spi_node, +}; + 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)&spt_info }, - { PCI_VDEVICE(INTEL, 0x02ab), (kernel_ulong_t)&spt_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 }, @@ -177,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)&spt_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)&spt_info }, - { PCI_VDEVICE(INTEL, 0x06ab), (kernel_ulong_t)&spt_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)&spt_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 }, @@ -201,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 }, @@ -217,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 }, @@ -236,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)&spt_info }, - { PCI_VDEVICE(INTEL, 0x34ab), (kernel_ulong_t)&spt_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 }, @@ -252,13 +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)&spt_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)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x43ab), (kernel_ulong_t)&bxt_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 }, @@ -267,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)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x43fd), (kernel_ulong_t)&bxt_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 }, @@ -284,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)&spt_info }, - { PCI_VDEVICE(INTEL, 0x4dab), (kernel_ulong_t)&spt_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 }, @@ -296,25 +392,27 @@ 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)&spt_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)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x51ab), (kernel_ulong_t)&bxt_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 }, + { PCI_VDEVICE(INTEL, 0x51d8), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x51d9), (kernel_ulong_t)&bxt_i2c_info }, { PCI_VDEVICE(INTEL, 0x51e8), (kernel_ulong_t)&bxt_i2c_info }, { 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)&bxt_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)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x54ab), (kernel_ulong_t)&bxt_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 }, @@ -322,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)&bxt_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 }, @@ -335,34 +433,97 @@ 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_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_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)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x7aab), (kernel_ulong_t)&bxt_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)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x7afb), (kernel_ulong_t)&bxt_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_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 }, + { PCI_VDEVICE(INTEL, 0x7e78), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7e79), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7e7a), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x7e7b), (kernel_ulong_t)&bxt_i2c_info }, + /* 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_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_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_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 }, + { PCI_VDEVICE(INTEL, 0x98e8), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x98e9), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x98ea), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x98eb), (kernel_ulong_t)&bxt_i2c_info }, /* 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 }, @@ -373,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)&spt_info }, - { PCI_VDEVICE(INTEL, 0x9dab), (kernel_ulong_t)&spt_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 }, @@ -382,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)&spt_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)&spt_info }, - { PCI_VDEVICE(INTEL, 0xa0ab), (kernel_ulong_t)&spt_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 }, @@ -397,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)&spt_info }, - { PCI_VDEVICE(INTEL, 0xa0df), (kernel_ulong_t)&spt_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)&spt_info }, - { PCI_VDEVICE(INTEL, 0xa0fd), (kernel_ulong_t)&spt_info }, - { PCI_VDEVICE(INTEL, 0xa0fe), (kernel_ulong_t)&spt_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 }, @@ -418,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 }, @@ -428,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)&spt_info }, - { PCI_VDEVICE(INTEL, 0xa32b), (kernel_ulong_t)&spt_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)&spt_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); @@ -456,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), }, }; @@ -466,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 a9bf10bee796..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,12 +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, 0, + 0, lpss->priv, 1, 15, 16, 15, + CLK_FRAC_DIVIDER_POWER_OF_TWO_PS, 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); @@ -377,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; @@ -400,10 +419,11 @@ int intel_lpss_probe(struct device *dev, return ret; lpss->cell->swnode = info->swnode; + 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; @@ -440,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) { @@ -454,19 +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"); 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 @@ -475,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; @@ -496,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; @@ -511,7 +529,12 @@ int intel_lpss_resume(struct device *dev) return 0; } -EXPORT_SYMBOL_GPL(intel_lpss_resume); + +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 22dbc4aed793..6f8f668f4c6f 100644 --- a/drivers/mfd/intel-lpss.h +++ b/drivers/mfd/intel-lpss.h @@ -11,8 +11,20 @@ #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; @@ -20,6 +32,7 @@ struct software_node; struct intel_lpss_platform_info { struct resource *mem; int irq; + unsigned int quirks; unsigned long clk_rate; const char *clk_con_id; const struct software_node *swnode; @@ -29,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 new file mode 100644 index 000000000000..e930161bf65e --- /dev/null +++ b/drivers/mfd/intel-m10-bmc-core.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel MAX 10 Board Management Controller chip - common code + * + * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/dev_printk.h> +#include <linux/mfd/core.h> +#include <linux/mfd/intel-m10-bmc.h> +#include <linux/module.h> + +void m10bmc_fw_state_set(struct intel_m10bmc *m10bmc, enum m10bmc_fw_state new_state) +{ + /* bmcfw_state is only needed if handshake_sys_reg_nranges > 0 */ + if (!m10bmc->info->handshake_sys_reg_nranges) + return; + + down_write(&m10bmc->bmcfw_lock); + m10bmc->bmcfw_state = new_state; + up_write(&m10bmc->bmcfw_lock); +} +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 + * handshake registers during a secure update. + */ +static bool m10bmc_reg_always_available(struct intel_m10bmc *m10bmc, unsigned int offset) +{ + if (!m10bmc->info->handshake_sys_reg_nranges) + return true; + + return !regmap_reg_in_ranges(offset, m10bmc->info->handshake_sys_reg_ranges, + m10bmc->info->handshake_sys_reg_nranges); +} + +/* + * m10bmc_handshake_reg_unavailable - Checks if reg access collides with secure update state + * @m10bmc: M10 BMC structure + * + * For some Intel FPGA devices, the BMC firmware is not available to service + * handshake registers during a secure update erase and write phases. + * + * Context: @m10bmc->bmcfw_lock must be held. + */ +static bool m10bmc_handshake_reg_unavailable(struct intel_m10bmc *m10bmc) +{ + return m10bmc->bmcfw_state == M10BMC_FW_STATE_SEC_UPDATE_PREPARE || + m10bmc->bmcfw_state == M10BMC_FW_STATE_SEC_UPDATE_WRITE; +} + +/* + * This function helps to simplify the accessing of the system registers. + * + * The base of the system registers is configured through the struct + * csr_map. + */ +int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned int *val) +{ + const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map; + int ret; + + if (m10bmc_reg_always_available(m10bmc, offset)) + return m10bmc_raw_read(m10bmc, csr_map->base + offset, val); + + down_read(&m10bmc->bmcfw_lock); + if (m10bmc_handshake_reg_unavailable(m10bmc)) + ret = -EBUSY; /* Reg not available during secure update */ + else + ret = m10bmc_raw_read(m10bmc, csr_map->base + offset, val); + up_read(&m10bmc->bmcfw_lock); + + return ret; +} +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) +{ + const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map; + int ret; + + if (m10bmc_reg_always_available(m10bmc, offset)) + return regmap_update_bits(m10bmc->regmap, csr_map->base + offset, msk, val); + + down_read(&m10bmc->bmcfw_lock); + if (m10bmc_handshake_reg_unavailable(m10bmc)) + ret = -EBUSY; /* Reg not available during secure update */ + else + ret = regmap_update_bits(m10bmc->regmap, csr_map->base + offset, msk, val); + up_read(&m10bmc->bmcfw_lock); + + return ret; +} +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) +{ + struct intel_m10bmc *ddata = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = m10bmc_sys_read(ddata, ddata->info->csr_map->build_version, &val); + if (ret) + return ret; + + return sprintf(buf, "0x%x\n", val); +} +static DEVICE_ATTR_RO(bmc_version); + +static ssize_t bmcfw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_m10bmc *ddata = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = m10bmc_sys_read(ddata, ddata->info->csr_map->fw_version, &val); + if (ret) + return ret; + + return sprintf(buf, "0x%x\n", val); +} +static DEVICE_ATTR_RO(bmcfw_version); + +static ssize_t mac_address_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_m10bmc *ddata = dev_get_drvdata(dev); + unsigned int macaddr_low, macaddr_high; + int ret; + + ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_low, &macaddr_low); + if (ret) + return ret; + + ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_high, &macaddr_high); + if (ret) + return ret; + + return sysfs_emit(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", + (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE1, macaddr_low), + (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE2, macaddr_low), + (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE3, macaddr_low), + (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE4, macaddr_low), + (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE5, macaddr_high), + (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE6, macaddr_high)); +} +static DEVICE_ATTR_RO(mac_address); + +static ssize_t mac_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_m10bmc *ddata = dev_get_drvdata(dev); + unsigned int macaddr_high; + int ret; + + ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_high, &macaddr_high); + if (ret) + return ret; + + return sysfs_emit(buf, "%u\n", (u8)FIELD_GET(M10BMC_N3000_MAC_COUNT, macaddr_high)); +} +static DEVICE_ATTR_RO(mac_count); + +static struct attribute *m10bmc_attrs[] = { + &dev_attr_bmc_version.attr, + &dev_attr_bmcfw_version.attr, + &dev_attr_mac_address.attr, + &dev_attr_mac_count.attr, + NULL, +}; + +static const struct attribute_group m10bmc_group = { + .attrs = m10bmc_attrs, +}; + +const struct attribute_group *m10bmc_dev_groups[] = { + &m10bmc_group, + NULL, +}; +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) +{ + int ret; + + m10bmc->info = info; + dev_set_drvdata(m10bmc->dev, m10bmc); + init_rwsem(&m10bmc->bmcfw_lock); + + ret = devm_mfd_add_devices(m10bmc->dev, PLATFORM_DEVID_AUTO, + info->cells, info->n_cells, + NULL, 0, NULL); + if (ret) + dev_err(m10bmc->dev, "Failed to register sub-devices: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(m10bmc_dev_init, "INTEL_M10_BMC_CORE"); + +MODULE_DESCRIPTION("Intel MAX 10 BMC core driver"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/intel-m10-bmc-pmci.c b/drivers/mfd/intel-m10-bmc-pmci.c new file mode 100644 index 000000000000..d213c6ec04ba --- /dev/null +++ b/drivers/mfd/intel-m10-bmc-pmci.c @@ -0,0 +1,457 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MAX10 BMC Platform Management Component Interface (PMCI) based + * interface. + * + * Copyright (C) 2020-2023 Intel Corporation. + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/dfl.h> +#include <linux/mfd/core.h> +#include <linux/mfd/intel-m10-bmc.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/regmap.h> + +struct m10bmc_pmci_device { + void __iomem *base; + struct intel_m10bmc m10bmc; + struct mutex flash_mutex; /* protects flash_busy and serializes flash read/read */ + bool flash_busy; +}; + +/* + * Intel FGPA indirect register access via hardware controller/bridge. + */ +#define INDIRECT_CMD_OFF 0 +#define INDIRECT_CMD_CLR 0 +#define INDIRECT_CMD_RD BIT(0) +#define INDIRECT_CMD_WR BIT(1) +#define INDIRECT_CMD_ACK BIT(2) + +#define INDIRECT_ADDR_OFF 0x4 +#define INDIRECT_RD_OFF 0x8 +#define INDIRECT_WR_OFF 0xc + +#define INDIRECT_INT_US 1 +#define INDIRECT_TIMEOUT_US 10000 + +struct indirect_ctx { + void __iomem *base; + struct device *dev; +}; + +static int indirect_clear_cmd(struct indirect_ctx *ctx) +{ + unsigned int cmd; + int ret; + + writel(INDIRECT_CMD_CLR, ctx->base + INDIRECT_CMD_OFF); + + ret = readl_poll_timeout(ctx->base + INDIRECT_CMD_OFF, cmd, + cmd == INDIRECT_CMD_CLR, + INDIRECT_INT_US, INDIRECT_TIMEOUT_US); + if (ret) + dev_err(ctx->dev, "timed out waiting clear cmd (residual cmd=0x%x)\n", cmd); + + return ret; +} + +static int indirect_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct indirect_ctx *ctx = context; + unsigned int cmd, ack, tmpval; + int ret, ret2; + + cmd = readl(ctx->base + INDIRECT_CMD_OFF); + if (cmd != INDIRECT_CMD_CLR) + dev_warn(ctx->dev, "residual cmd 0x%x on read entry\n", cmd); + + writel(reg, ctx->base + INDIRECT_ADDR_OFF); + writel(INDIRECT_CMD_RD, ctx->base + INDIRECT_CMD_OFF); + + ret = readl_poll_timeout(ctx->base + INDIRECT_CMD_OFF, ack, + (ack & INDIRECT_CMD_ACK) == INDIRECT_CMD_ACK, + INDIRECT_INT_US, INDIRECT_TIMEOUT_US); + if (ret) + dev_err(ctx->dev, "read timed out on reg 0x%x ack 0x%x\n", reg, ack); + else + tmpval = readl(ctx->base + INDIRECT_RD_OFF); + + ret2 = indirect_clear_cmd(ctx); + + if (ret) + return ret; + if (ret2) + return ret2; + + *val = tmpval; + return 0; +} + +static int indirect_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct indirect_ctx *ctx = context; + unsigned int cmd, ack; + int ret, ret2; + + cmd = readl(ctx->base + INDIRECT_CMD_OFF); + if (cmd != INDIRECT_CMD_CLR) + dev_warn(ctx->dev, "residual cmd 0x%x on write entry\n", cmd); + + writel(val, ctx->base + INDIRECT_WR_OFF); + writel(reg, ctx->base + INDIRECT_ADDR_OFF); + writel(INDIRECT_CMD_WR, ctx->base + INDIRECT_CMD_OFF); + + ret = readl_poll_timeout(ctx->base + INDIRECT_CMD_OFF, ack, + (ack & INDIRECT_CMD_ACK) == INDIRECT_CMD_ACK, + INDIRECT_INT_US, INDIRECT_TIMEOUT_US); + if (ret) + dev_err(ctx->dev, "write timed out on reg 0x%x ack 0x%x\n", reg, ack); + + ret2 = indirect_clear_cmd(ctx); + + if (ret) + return ret; + return ret2; +} + +static void pmci_write_fifo(void __iomem *base, const u32 *buf, size_t count) +{ + while (count--) + writel(*buf++, base); +} + +static void pmci_read_fifo(void __iomem *base, u32 *buf, size_t count) +{ + while (count--) + *buf++ = readl(base); +} + +static u32 pmci_get_write_space(struct m10bmc_pmci_device *pmci) +{ + u32 val; + int ret; + + ret = read_poll_timeout(readl, val, + FIELD_GET(M10BMC_N6000_FLASH_FIFO_SPACE, val) == + M10BMC_N6000_FIFO_MAX_WORDS, + M10BMC_FLASH_INT_US, M10BMC_FLASH_TIMEOUT_US, + false, pmci->base + M10BMC_N6000_FLASH_CTRL); + if (ret == -ETIMEDOUT) + return 0; + + return FIELD_GET(M10BMC_N6000_FLASH_FIFO_SPACE, val) * M10BMC_N6000_FIFO_WORD_SIZE; +} + +static int pmci_flash_bulk_write(struct intel_m10bmc *m10bmc, const u8 *buf, u32 size) +{ + struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc); + u32 blk_size, offset = 0, write_count; + + while (size) { + blk_size = min(pmci_get_write_space(pmci), size); + if (blk_size == 0) { + dev_err(m10bmc->dev, "get FIFO available size fail\n"); + return -EIO; + } + + if (size < M10BMC_N6000_FIFO_WORD_SIZE) + break; + + write_count = blk_size / M10BMC_N6000_FIFO_WORD_SIZE; + pmci_write_fifo(pmci->base + M10BMC_N6000_FLASH_FIFO, + (u32 *)(buf + offset), write_count); + + size -= blk_size; + offset += blk_size; + } + + /* Handle remainder (less than M10BMC_N6000_FIFO_WORD_SIZE bytes) */ + if (size) { + u32 tmp = 0; + + memcpy(&tmp, buf + offset, size); + pmci_write_fifo(pmci->base + M10BMC_N6000_FLASH_FIFO, &tmp, 1); + } + + return 0; +} + +static int pmci_flash_bulk_read(struct intel_m10bmc *m10bmc, u8 *buf, u32 addr, u32 size) +{ + struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc); + u32 blk_size, offset = 0, val, full_read_count, read_count; + int ret; + + while (size) { + blk_size = min_t(u32, size, M10BMC_N6000_READ_BLOCK_SIZE); + full_read_count = blk_size / M10BMC_N6000_FIFO_WORD_SIZE; + + read_count = full_read_count; + if (full_read_count * M10BMC_N6000_FIFO_WORD_SIZE < blk_size) + read_count++; + + writel(addr + offset, pmci->base + M10BMC_N6000_FLASH_ADDR); + writel(FIELD_PREP(M10BMC_N6000_FLASH_READ_COUNT, read_count) | + M10BMC_N6000_FLASH_RD_MODE, + pmci->base + M10BMC_N6000_FLASH_CTRL); + + ret = readl_poll_timeout((pmci->base + M10BMC_N6000_FLASH_CTRL), val, + !(val & M10BMC_N6000_FLASH_BUSY), + M10BMC_FLASH_INT_US, M10BMC_FLASH_TIMEOUT_US); + if (ret) { + dev_err(m10bmc->dev, "read timed out on reading flash 0x%xn", val); + return ret; + } + + pmci_read_fifo(pmci->base + M10BMC_N6000_FLASH_FIFO, + (u32 *)(buf + offset), full_read_count); + + size -= blk_size; + offset += blk_size; + + if (full_read_count < read_count) + break; + + writel(0, pmci->base + M10BMC_N6000_FLASH_CTRL); + } + + /* Handle remainder (less than M10BMC_N6000_FIFO_WORD_SIZE bytes) */ + if (size) { + u32 tmp; + + pmci_read_fifo(pmci->base + M10BMC_N6000_FLASH_FIFO, &tmp, 1); + memcpy(buf + offset, &tmp, size); + + writel(0, pmci->base + M10BMC_N6000_FLASH_CTRL); + } + + return 0; +} + +static int m10bmc_pmci_set_flash_host_mux(struct intel_m10bmc *m10bmc, bool request) +{ + u32 ctrl; + int ret; + + ret = regmap_update_bits(m10bmc->regmap, M10BMC_N6000_FLASH_MUX_CTRL, + M10BMC_N6000_FLASH_HOST_REQUEST, + FIELD_PREP(M10BMC_N6000_FLASH_HOST_REQUEST, request)); + if (ret) + return ret; + + return regmap_read_poll_timeout(m10bmc->regmap, + M10BMC_N6000_FLASH_MUX_CTRL, ctrl, + request ? + (get_flash_mux(ctrl) == M10BMC_N6000_FLASH_MUX_HOST) : + (get_flash_mux(ctrl) != M10BMC_N6000_FLASH_MUX_HOST), + M10BMC_FLASH_INT_US, M10BMC_FLASH_TIMEOUT_US); +} + +static int m10bmc_pmci_flash_read(struct intel_m10bmc *m10bmc, u8 *buf, u32 addr, u32 size) +{ + struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc); + int ret, ret2; + + mutex_lock(&pmci->flash_mutex); + if (pmci->flash_busy) { + ret = -EBUSY; + goto unlock; + } + + ret = m10bmc_pmci_set_flash_host_mux(m10bmc, true); + if (ret) + goto mux_fail; + + ret = pmci_flash_bulk_read(m10bmc, buf, addr, size); + +mux_fail: + ret2 = m10bmc_pmci_set_flash_host_mux(m10bmc, false); + +unlock: + mutex_unlock(&pmci->flash_mutex); + if (ret) + return ret; + return ret2; +} + +static int m10bmc_pmci_flash_write(struct intel_m10bmc *m10bmc, const u8 *buf, u32 offset, u32 size) +{ + struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc); + int ret; + + mutex_lock(&pmci->flash_mutex); + WARN_ON_ONCE(!pmci->flash_busy); + /* On write, firmware manages flash MUX */ + ret = pmci_flash_bulk_write(m10bmc, buf + offset, size); + mutex_unlock(&pmci->flash_mutex); + + return ret; +} + +static int m10bmc_pmci_flash_lock(struct intel_m10bmc *m10bmc) +{ + struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc); + int ret = 0; + + mutex_lock(&pmci->flash_mutex); + if (pmci->flash_busy) { + ret = -EBUSY; + goto unlock; + } + + pmci->flash_busy = true; + +unlock: + mutex_unlock(&pmci->flash_mutex); + return ret; +} + +static void m10bmc_pmci_flash_unlock(struct intel_m10bmc *m10bmc) +{ + struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc); + + mutex_lock(&pmci->flash_mutex); + WARN_ON_ONCE(!pmci->flash_busy); + pmci->flash_busy = false; + mutex_unlock(&pmci->flash_mutex); +} + +static const struct intel_m10bmc_flash_bulk_ops m10bmc_pmci_flash_bulk_ops = { + .read = m10bmc_pmci_flash_read, + .write = m10bmc_pmci_flash_write, + .lock_write = m10bmc_pmci_flash_lock, + .unlock_write = m10bmc_pmci_flash_unlock, +}; + +static const struct regmap_range m10bmc_pmci_regmap_range[] = { + regmap_reg_range(M10BMC_N6000_SYS_BASE, M10BMC_N6000_SYS_END), +}; + +static const struct regmap_access_table m10bmc_pmci_access_table = { + .yes_ranges = m10bmc_pmci_regmap_range, + .n_yes_ranges = ARRAY_SIZE(m10bmc_pmci_regmap_range), +}; + +static const struct regmap_config m10bmc_pmci_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .wr_table = &m10bmc_pmci_access_table, + .rd_table = &m10bmc_pmci_access_table, + .reg_read = &indirect_reg_read, + .reg_write = &indirect_reg_write, + .max_register = M10BMC_N6000_SYS_END, +}; + +static struct mfd_cell m10bmc_pmci_n6000_bmc_subdevs[] = { + { .name = "n6000bmc-hwmon" }, + { .name = "n6000bmc-sec-update" }, +}; + +static const struct m10bmc_csr_map m10bmc_n6000_csr_map = { + .base = M10BMC_N6000_SYS_BASE, + .build_version = M10BMC_N6000_BUILD_VER, + .fw_version = NIOS2_N6000_FW_VERSION, + .mac_low = M10BMC_N6000_MAC_LOW, + .mac_high = M10BMC_N6000_MAC_HIGH, + .doorbell = M10BMC_N6000_DOORBELL, + .auth_result = M10BMC_N6000_AUTH_RESULT, + .bmc_prog_addr = M10BMC_N6000_BMC_PROG_ADDR, + .bmc_reh_addr = M10BMC_N6000_BMC_REH_ADDR, + .bmc_magic = M10BMC_N6000_BMC_PROG_MAGIC, + .sr_prog_addr = M10BMC_N6000_SR_PROG_ADDR, + .sr_reh_addr = M10BMC_N6000_SR_REH_ADDR, + .sr_magic = M10BMC_N6000_SR_PROG_MAGIC, + .pr_prog_addr = M10BMC_N6000_PR_PROG_ADDR, + .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 = { + .cells = m10bmc_pmci_n6000_bmc_subdevs, + .n_cells = ARRAY_SIZE(m10bmc_pmci_n6000_bmc_subdevs), + .csr_map = &m10bmc_n6000_csr_map, +}; + +static int m10bmc_pmci_probe(struct dfl_device *ddev) +{ + struct device *dev = &ddev->dev; + struct m10bmc_pmci_device *pmci; + struct indirect_ctx *ctx; + int ret; + + pmci = devm_kzalloc(dev, sizeof(*pmci), GFP_KERNEL); + if (!pmci) + return -ENOMEM; + + pmci->m10bmc.flash_bulk_ops = &m10bmc_pmci_flash_bulk_ops; + pmci->m10bmc.dev = dev; + + pmci->base = devm_ioremap_resource(dev, &ddev->mmio_res); + if (IS_ERR(pmci->base)) + return PTR_ERR(pmci->base); + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + mutex_init(&pmci->flash_mutex); + + ctx->base = pmci->base + M10BMC_N6000_INDIRECT_BASE; + ctx->dev = dev; + indirect_clear_cmd(ctx); + pmci->m10bmc.regmap = devm_regmap_init(dev, NULL, ctx, &m10bmc_pmci_regmap_config); + + if (IS_ERR(pmci->m10bmc.regmap)) { + ret = PTR_ERR(pmci->m10bmc.regmap); + goto destroy_mutex; + } + + ret = m10bmc_dev_init(&pmci->m10bmc, &m10bmc_pmci_n6000); + if (ret) + goto destroy_mutex; + return 0; + +destroy_mutex: + mutex_destroy(&pmci->flash_mutex); + return ret; +} + +static void m10bmc_pmci_remove(struct dfl_device *ddev) +{ + struct intel_m10bmc *m10bmc = dev_get_drvdata(&ddev->dev); + struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc); + + mutex_destroy(&pmci->flash_mutex); +} + +#define FME_FEATURE_ID_M10BMC_PMCI 0x12 + +static const struct dfl_device_id m10bmc_pmci_ids[] = { + { FME_ID, FME_FEATURE_ID_M10BMC_PMCI }, + { } +}; +MODULE_DEVICE_TABLE(dfl, m10bmc_pmci_ids); + +static struct dfl_driver m10bmc_pmci_driver = { + .drv = { + .name = "intel-m10-bmc", + .dev_groups = m10bmc_dev_groups, + }, + .id_table = m10bmc_pmci_ids, + .probe = m10bmc_pmci_probe, + .remove = m10bmc_pmci_remove, +}; + +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"); diff --git a/drivers/mfd/intel-m10-bmc-spi.c b/drivers/mfd/intel-m10-bmc-spi.c new file mode 100644 index 000000000000..cfa620f0c70e --- /dev/null +++ b/drivers/mfd/intel-m10-bmc-spi.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel MAX 10 Board Management Controller chip + * + * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. + */ +#include <linux/bitfield.h> +#include <linux/dev_printk.h> +#include <linux/init.h> +#include <linux/mfd/core.h> +#include <linux/mfd/intel-m10-bmc.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +static const struct regmap_range m10bmc_regmap_range[] = { + regmap_reg_range(M10BMC_N3000_LEGACY_BUILD_VER, M10BMC_N3000_LEGACY_BUILD_VER), + regmap_reg_range(M10BMC_N3000_SYS_BASE, M10BMC_N3000_SYS_END), + regmap_reg_range(M10BMC_N3000_FLASH_BASE, M10BMC_N3000_FLASH_END), +}; + +static const struct regmap_access_table m10bmc_access_table = { + .yes_ranges = m10bmc_regmap_range, + .n_yes_ranges = ARRAY_SIZE(m10bmc_regmap_range), +}; + +static const struct regmap_config intel_m10bmc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .wr_table = &m10bmc_access_table, + .rd_table = &m10bmc_access_table, + .max_register = M10BMC_N3000_MEM_END, +}; + +static int check_m10bmc_version(struct intel_m10bmc *ddata) +{ + unsigned int v; + int ret; + + /* + * This check is to filter out the very old legacy BMC versions. In the + * old BMC chips, the BMC version info is stored in the old version + * register (M10BMC_N3000_LEGACY_BUILD_VER), so its read out value would have + * not been M10BMC_N3000_VER_LEGACY_INVALID (0xffffffff). But in new BMC + * chips that the driver supports, the value of this register should be + * M10BMC_N3000_VER_LEGACY_INVALID. + */ + ret = m10bmc_raw_read(ddata, M10BMC_N3000_LEGACY_BUILD_VER, &v); + if (ret) + return -ENODEV; + + if (v != M10BMC_N3000_VER_LEGACY_INVALID) { + dev_err(ddata->dev, "bad version M10BMC detected\n"); + return -ENODEV; + } + + return 0; +} + +static int intel_m10_bmc_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + const struct intel_m10bmc_platform_info *info; + struct device *dev = &spi->dev; + struct intel_m10bmc *ddata; + int ret; + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + info = (struct intel_m10bmc_platform_info *)id->driver_data; + ddata->dev = dev; + + ddata->regmap = devm_regmap_init_spi_avmm(spi, &intel_m10bmc_regmap_config); + if (IS_ERR(ddata->regmap)) { + ret = PTR_ERR(ddata->regmap); + dev_err(dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + spi_set_drvdata(spi, ddata); + + ret = check_m10bmc_version(ddata); + if (ret) { + dev_err(dev, "Failed to identify m10bmc hardware\n"); + return ret; + } + + return m10bmc_dev_init(ddata, info); +} + +static const struct m10bmc_csr_map m10bmc_n3000_csr_map = { + .base = M10BMC_N3000_SYS_BASE, + .build_version = M10BMC_N3000_BUILD_VER, + .fw_version = NIOS2_N3000_FW_VERSION, + .mac_low = M10BMC_N3000_MAC_LOW, + .mac_high = M10BMC_N3000_MAC_HIGH, + .doorbell = M10BMC_N3000_DOORBELL, + .auth_result = M10BMC_N3000_AUTH_RESULT, + .bmc_prog_addr = M10BMC_N3000_BMC_PROG_ADDR, + .bmc_reh_addr = M10BMC_N3000_BMC_REH_ADDR, + .bmc_magic = M10BMC_N3000_BMC_PROG_MAGIC, + .sr_prog_addr = M10BMC_N3000_SR_PROG_ADDR, + .sr_reh_addr = M10BMC_N3000_SR_REH_ADDR, + .sr_magic = M10BMC_N3000_SR_PROG_MAGIC, + .pr_prog_addr = M10BMC_N3000_PR_PROG_ADDR, + .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[] = { + { .name = "d5005bmc-hwmon" }, + { .name = "d5005bmc-sec-update" }, +}; + +static const struct regmap_range m10bmc_d5005_fw_handshake_regs[] = { + regmap_reg_range(M10BMC_N3000_TELEM_START, M10BMC_D5005_TELEM_END), +}; + +static struct mfd_cell m10bmc_pacn3000_subdevs[] = { + { .name = "n3000bmc-hwmon" }, + { .name = "n3000bmc-retimer" }, + { .name = "n3000bmc-sec-update" }, +}; + +static const struct regmap_range m10bmc_n3000_fw_handshake_regs[] = { + regmap_reg_range(M10BMC_N3000_TELEM_START, M10BMC_N3000_TELEM_END), +}; + +static struct mfd_cell m10bmc_n5010_subdevs[] = { + { .name = "n5010bmc-hwmon" }, +}; + +static const struct intel_m10bmc_platform_info m10bmc_spi_n3000 = { + .cells = m10bmc_pacn3000_subdevs, + .n_cells = ARRAY_SIZE(m10bmc_pacn3000_subdevs), + .handshake_sys_reg_ranges = m10bmc_n3000_fw_handshake_regs, + .handshake_sys_reg_nranges = ARRAY_SIZE(m10bmc_n3000_fw_handshake_regs), + .csr_map = &m10bmc_n3000_csr_map, +}; + +static const struct intel_m10bmc_platform_info m10bmc_spi_d5005 = { + .cells = m10bmc_d5005_subdevs, + .n_cells = ARRAY_SIZE(m10bmc_d5005_subdevs), + .handshake_sys_reg_ranges = m10bmc_d5005_fw_handshake_regs, + .handshake_sys_reg_nranges = ARRAY_SIZE(m10bmc_d5005_fw_handshake_regs), + .csr_map = &m10bmc_n3000_csr_map, +}; + +static const struct intel_m10bmc_platform_info m10bmc_spi_n5010 = { + .cells = m10bmc_n5010_subdevs, + .n_cells = ARRAY_SIZE(m10bmc_n5010_subdevs), + .handshake_sys_reg_ranges = m10bmc_n3000_fw_handshake_regs, + .handshake_sys_reg_nranges = ARRAY_SIZE(m10bmc_n3000_fw_handshake_regs), + .csr_map = &m10bmc_n3000_csr_map, +}; + +static const struct spi_device_id m10bmc_spi_id[] = { + { "m10-n3000", (kernel_ulong_t)&m10bmc_spi_n3000 }, + { "m10-d5005", (kernel_ulong_t)&m10bmc_spi_d5005 }, + { "m10-n5010", (kernel_ulong_t)&m10bmc_spi_n5010 }, + { } +}; +MODULE_DEVICE_TABLE(spi, m10bmc_spi_id); + +static struct spi_driver intel_m10bmc_spi_driver = { + .driver = { + .name = "intel-m10-bmc", + .dev_groups = m10bmc_dev_groups, + }, + .probe = intel_m10_bmc_spi_probe, + .id_table = m10bmc_spi_id, +}; +module_spi_driver(intel_m10bmc_spi_driver); + +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"); diff --git a/drivers/mfd/intel-m10-bmc.c b/drivers/mfd/intel-m10-bmc.c deleted file mode 100644 index 1a9bfb7f48cd..000000000000 --- a/drivers/mfd/intel-m10-bmc.c +++ /dev/null @@ -1,227 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Intel MAX 10 Board Management Controller chip - * - * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. - */ -#include <linux/bitfield.h> -#include <linux/init.h> -#include <linux/mfd/core.h> -#include <linux/mfd/intel-m10-bmc.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/regmap.h> -#include <linux/spi/spi.h> - -enum m10bmc_type { - M10_N3000, - M10_D5005 -}; - -static struct mfd_cell m10bmc_d5005_subdevs[] = { - { .name = "d5005bmc-hwmon" }, -}; - -static struct mfd_cell m10bmc_pacn3000_subdevs[] = { - { .name = "n3000bmc-hwmon" }, - { .name = "n3000bmc-retimer" }, - { .name = "n3000bmc-secure" }, -}; - -static const struct regmap_range m10bmc_regmap_range[] = { - regmap_reg_range(M10BMC_LEGACY_BUILD_VER, M10BMC_LEGACY_BUILD_VER), - regmap_reg_range(M10BMC_SYS_BASE, M10BMC_SYS_END), - regmap_reg_range(M10BMC_FLASH_BASE, M10BMC_FLASH_END), -}; - -static const struct regmap_access_table m10bmc_access_table = { - .yes_ranges = m10bmc_regmap_range, - .n_yes_ranges = ARRAY_SIZE(m10bmc_regmap_range), -}; - -static struct regmap_config intel_m10bmc_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .wr_table = &m10bmc_access_table, - .rd_table = &m10bmc_access_table, - .max_register = M10BMC_MEM_END, -}; - -static ssize_t bmc_version_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct intel_m10bmc *ddata = dev_get_drvdata(dev); - unsigned int val; - int ret; - - ret = m10bmc_sys_read(ddata, M10BMC_BUILD_VER, &val); - if (ret) - return ret; - - return sprintf(buf, "0x%x\n", val); -} -static DEVICE_ATTR_RO(bmc_version); - -static ssize_t bmcfw_version_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct intel_m10bmc *ddata = dev_get_drvdata(dev); - unsigned int val; - int ret; - - ret = m10bmc_sys_read(ddata, NIOS2_FW_VERSION, &val); - if (ret) - return ret; - - return sprintf(buf, "0x%x\n", val); -} -static DEVICE_ATTR_RO(bmcfw_version); - -static ssize_t mac_address_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct intel_m10bmc *max10 = dev_get_drvdata(dev); - unsigned int macaddr_low, macaddr_high; - int ret; - - ret = m10bmc_sys_read(max10, M10BMC_MAC_LOW, &macaddr_low); - if (ret) - return ret; - - ret = m10bmc_sys_read(max10, M10BMC_MAC_HIGH, &macaddr_high); - if (ret) - return ret; - - return sysfs_emit(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", - (u8)FIELD_GET(M10BMC_MAC_BYTE1, macaddr_low), - (u8)FIELD_GET(M10BMC_MAC_BYTE2, macaddr_low), - (u8)FIELD_GET(M10BMC_MAC_BYTE3, macaddr_low), - (u8)FIELD_GET(M10BMC_MAC_BYTE4, macaddr_low), - (u8)FIELD_GET(M10BMC_MAC_BYTE5, macaddr_high), - (u8)FIELD_GET(M10BMC_MAC_BYTE6, macaddr_high)); -} -static DEVICE_ATTR_RO(mac_address); - -static ssize_t mac_count_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct intel_m10bmc *max10 = dev_get_drvdata(dev); - unsigned int macaddr_high; - int ret; - - ret = m10bmc_sys_read(max10, M10BMC_MAC_HIGH, &macaddr_high); - if (ret) - return ret; - - return sysfs_emit(buf, "%u\n", - (u8)FIELD_GET(M10BMC_MAC_COUNT, macaddr_high)); -} -static DEVICE_ATTR_RO(mac_count); - -static struct attribute *m10bmc_attrs[] = { - &dev_attr_bmc_version.attr, - &dev_attr_bmcfw_version.attr, - &dev_attr_mac_address.attr, - &dev_attr_mac_count.attr, - NULL, -}; -ATTRIBUTE_GROUPS(m10bmc); - -static int check_m10bmc_version(struct intel_m10bmc *ddata) -{ - unsigned int v; - int ret; - - /* - * This check is to filter out the very old legacy BMC versions. In the - * old BMC chips, the BMC version info is stored in the old version - * register (M10BMC_LEGACY_BUILD_VER), so its read out value would have - * not been M10BMC_VER_LEGACY_INVALID (0xffffffff). But in new BMC - * chips that the driver supports, the value of this register should be - * M10BMC_VER_LEGACY_INVALID. - */ - ret = m10bmc_raw_read(ddata, M10BMC_LEGACY_BUILD_VER, &v); - if (ret) - return -ENODEV; - - if (v != M10BMC_VER_LEGACY_INVALID) { - dev_err(ddata->dev, "bad version M10BMC detected\n"); - return -ENODEV; - } - - return 0; -} - -static int intel_m10_bmc_spi_probe(struct spi_device *spi) -{ - const struct spi_device_id *id = spi_get_device_id(spi); - struct device *dev = &spi->dev; - struct mfd_cell *cells; - struct intel_m10bmc *ddata; - int ret, n_cell; - - ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; - - ddata->dev = dev; - - ddata->regmap = - devm_regmap_init_spi_avmm(spi, &intel_m10bmc_regmap_config); - if (IS_ERR(ddata->regmap)) { - ret = PTR_ERR(ddata->regmap); - dev_err(dev, "Failed to allocate regmap: %d\n", ret); - return ret; - } - - spi_set_drvdata(spi, ddata); - - ret = check_m10bmc_version(ddata); - if (ret) { - dev_err(dev, "Failed to identify m10bmc hardware\n"); - return ret; - } - - switch (id->driver_data) { - case M10_N3000: - cells = m10bmc_pacn3000_subdevs; - n_cell = ARRAY_SIZE(m10bmc_pacn3000_subdevs); - break; - case M10_D5005: - cells = m10bmc_d5005_subdevs; - n_cell = ARRAY_SIZE(m10bmc_d5005_subdevs); - break; - default: - return -ENODEV; - } - - ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells, n_cell, - NULL, 0, NULL); - if (ret) - dev_err(dev, "Failed to register sub-devices: %d\n", ret); - - return ret; -} - -static const struct spi_device_id m10bmc_spi_id[] = { - { "m10-n3000", M10_N3000 }, - { "m10-d5005", M10_D5005 }, - { } -}; -MODULE_DEVICE_TABLE(spi, m10bmc_spi_id); - -static struct spi_driver intel_m10bmc_spi_driver = { - .driver = { - .name = "intel-m10-bmc", - .dev_groups = m10bmc_groups, - }, - .probe = intel_m10_bmc_spi_probe, - .id_table = m10bmc_spi_id, -}; -module_spi_driver(intel_m10bmc_spi_driver); - -MODULE_DESCRIPTION("Intel MAX 10 BMC Device Driver"); -MODULE_AUTHOR("Intel Corporation"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("spi:intel-m10-bmc"); 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_pmt.c b/drivers/mfd/intel_pmt.c deleted file mode 100644 index dd7eb614c28e..000000000000 --- a/drivers/mfd/intel_pmt.c +++ /dev/null @@ -1,261 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Intel Platform Monitoring Technology PMT driver - * - * Copyright (c) 2020, Intel Corporation. - * All Rights Reserved. - * - * Author: David E. Box <david.e.box@linux.intel.com> - */ - -#include <linux/bits.h> -#include <linux/kernel.h> -#include <linux/mfd/core.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/platform_device.h> -#include <linux/pm.h> -#include <linux/pm_runtime.h> -#include <linux/types.h> - -/* Intel DVSEC capability vendor space offsets */ -#define INTEL_DVSEC_ENTRIES 0xA -#define INTEL_DVSEC_SIZE 0xB -#define INTEL_DVSEC_TABLE 0xC -#define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) -#define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) -#define INTEL_DVSEC_ENTRY_SIZE 4 - -/* PMT capabilities */ -#define DVSEC_INTEL_ID_TELEMETRY 2 -#define DVSEC_INTEL_ID_WATCHER 3 -#define DVSEC_INTEL_ID_CRASHLOG 4 - -struct intel_dvsec_header { - u16 length; - u16 id; - u8 num_entries; - u8 entry_size; - u8 tbir; - u32 offset; -}; - -enum pmt_quirks { - /* Watcher capability not supported */ - PMT_QUIRK_NO_WATCHER = BIT(0), - - /* Crashlog capability not supported */ - PMT_QUIRK_NO_CRASHLOG = BIT(1), - - /* Use shift instead of mask to read discovery table offset */ - PMT_QUIRK_TABLE_SHIFT = BIT(2), - - /* DVSEC not present (provided in driver data) */ - PMT_QUIRK_NO_DVSEC = BIT(3), -}; - -struct pmt_platform_info { - unsigned long quirks; - struct intel_dvsec_header **capabilities; -}; - -static const struct pmt_platform_info tgl_info = { - .quirks = PMT_QUIRK_NO_WATCHER | PMT_QUIRK_NO_CRASHLOG | - PMT_QUIRK_TABLE_SHIFT, -}; - -/* DG1 Platform with DVSEC quirk*/ -static struct intel_dvsec_header dg1_telemetry = { - .length = 0x10, - .id = 2, - .num_entries = 1, - .entry_size = 3, - .tbir = 0, - .offset = 0x466000, -}; - -static struct intel_dvsec_header *dg1_capabilities[] = { - &dg1_telemetry, - NULL -}; - -static const struct pmt_platform_info dg1_info = { - .quirks = PMT_QUIRK_NO_DVSEC, - .capabilities = dg1_capabilities, -}; - -static int pmt_add_dev(struct pci_dev *pdev, struct intel_dvsec_header *header, - unsigned long quirks) -{ - struct device *dev = &pdev->dev; - struct resource *res, *tmp; - struct mfd_cell *cell; - const char *name; - int count = header->num_entries; - int size = header->entry_size; - int id = header->id; - int i; - - switch (id) { - case DVSEC_INTEL_ID_TELEMETRY: - name = "pmt_telemetry"; - break; - case DVSEC_INTEL_ID_WATCHER: - if (quirks & PMT_QUIRK_NO_WATCHER) { - dev_info(dev, "Watcher not supported\n"); - return -EINVAL; - } - name = "pmt_watcher"; - break; - case DVSEC_INTEL_ID_CRASHLOG: - if (quirks & PMT_QUIRK_NO_CRASHLOG) { - dev_info(dev, "Crashlog not supported\n"); - return -EINVAL; - } - name = "pmt_crashlog"; - break; - default: - return -EINVAL; - } - - if (!header->num_entries || !header->entry_size) { - dev_err(dev, "Invalid count or size for %s header\n", name); - return -EINVAL; - } - - cell = devm_kzalloc(dev, sizeof(*cell), GFP_KERNEL); - if (!cell) - return -ENOMEM; - - res = devm_kcalloc(dev, count, sizeof(*res), GFP_KERNEL); - if (!res) - return -ENOMEM; - - if (quirks & PMT_QUIRK_TABLE_SHIFT) - header->offset >>= 3; - - /* - * The PMT DVSEC contains the starting offset and count for a block of - * discovery tables, each providing access to monitoring facilities for - * a section of the device. Create a resource list of these tables to - * provide to the driver. - */ - for (i = 0, tmp = res; i < count; i++, tmp++) { - tmp->start = pdev->resource[header->tbir].start + - header->offset + i * (size << 2); - tmp->end = tmp->start + (size << 2) - 1; - tmp->flags = IORESOURCE_MEM; - } - - cell->resources = res; - cell->num_resources = count; - cell->name = name; - - return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cell, 1, NULL, 0, - NULL); -} - -static int pmt_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct pmt_platform_info *info; - unsigned long quirks = 0; - bool found_devices = false; - int ret, pos = 0; - - ret = pcim_enable_device(pdev); - if (ret) - return ret; - - info = (struct pmt_platform_info *)id->driver_data; - - if (info) - quirks = info->quirks; - - if (info && (info->quirks & PMT_QUIRK_NO_DVSEC)) { - struct intel_dvsec_header **header; - - header = info->capabilities; - while (*header) { - ret = pmt_add_dev(pdev, *header, quirks); - if (ret) - dev_warn(&pdev->dev, - "Failed to add device for DVSEC id %d\n", - (*header)->id); - else - found_devices = true; - - ++header; - } - } else { - do { - struct intel_dvsec_header header; - u32 table; - u16 vid; - - pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC); - if (!pos) - break; - - pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vid); - if (vid != PCI_VENDOR_ID_INTEL) - continue; - - pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, - &header.id); - pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, - &header.num_entries); - pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, - &header.entry_size); - pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, - &table); - - header.tbir = INTEL_DVSEC_TABLE_BAR(table); - header.offset = INTEL_DVSEC_TABLE_OFFSET(table); - - ret = pmt_add_dev(pdev, &header, quirks); - if (ret) - continue; - - found_devices = true; - } while (true); - } - - if (!found_devices) - return -ENODEV; - - pm_runtime_put(&pdev->dev); - pm_runtime_allow(&pdev->dev); - - return 0; -} - -static void pmt_pci_remove(struct pci_dev *pdev) -{ - pm_runtime_forbid(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); -} - -#define PCI_DEVICE_ID_INTEL_PMT_ADL 0x467d -#define PCI_DEVICE_ID_INTEL_PMT_DG1 0x490e -#define PCI_DEVICE_ID_INTEL_PMT_OOBMSM 0x09a7 -#define PCI_DEVICE_ID_INTEL_PMT_TGL 0x9a0d -static const struct pci_device_id pmt_pci_ids[] = { - { PCI_DEVICE_DATA(INTEL, PMT_ADL, &tgl_info) }, - { PCI_DEVICE_DATA(INTEL, PMT_DG1, &dg1_info) }, - { PCI_DEVICE_DATA(INTEL, PMT_OOBMSM, NULL) }, - { PCI_DEVICE_DATA(INTEL, PMT_TGL, &tgl_info) }, - { } -}; -MODULE_DEVICE_TABLE(pci, pmt_pci_ids); - -static struct pci_driver pmt_pci_driver = { - .name = "intel-pmt", - .id_table = pmt_pci_ids, - .probe = pmt_pci_probe, - .remove = pmt_pci_remove, -}; -module_pci_driver(pmt_pci_driver); - -MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); -MODULE_DESCRIPTION("Intel Platform Monitoring Technology PMT driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/intel_quark_i2c_gpio.c b/drivers/mfd/intel_quark_i2c_gpio.c index 01935ae4e9e1..9b9c76bd067b 100644 --- a/drivers/mfd/intel_quark_i2c_gpio.c +++ b/drivers/mfd/intel_quark_i2c_gpio.c @@ -17,7 +17,6 @@ #include <linux/clk-provider.h> #include <linux/dmi.h> #include <linux/i2c.h> -#include <linux/platform_data/gpio-dwapb.h> #include <linux/property.h> /* PCI BAR for register base address */ @@ -28,15 +27,6 @@ #define MFD_ACPI_MATCH_GPIO 0ULL #define MFD_ACPI_MATCH_I2C 1ULL -/* The base GPIO number under GPIOLIB framework */ -#define INTEL_QUARK_MFD_GPIO_BASE 8 - -/* The default number of South-Cluster GPIO on Quark. */ -#define INTEL_QUARK_MFD_NGPIO 8 - -/* The DesignWare GPIO ports on Quark. */ -#define INTEL_QUARK_GPIO_NPORTS 1 - #define INTEL_QUARK_IORES_MEM 0 #define INTEL_QUARK_IORES_IRQ 1 @@ -111,12 +101,38 @@ static struct resource intel_quark_gpio_res[] = { [INTEL_QUARK_IORES_MEM] = { .flags = IORESOURCE_MEM, }, + [INTEL_QUARK_IORES_IRQ] = { + .flags = IORESOURCE_IRQ, + }, }; static struct mfd_cell_acpi_match intel_quark_acpi_match_gpio = { .adr = MFD_ACPI_MATCH_GPIO, }; +static const struct software_node intel_quark_gpio_controller_node = { + .name = "intel-quark-gpio-controller", +}; + +static const struct property_entry intel_quark_gpio_portA_properties[] = { + PROPERTY_ENTRY_U32("reg", 0), + PROPERTY_ENTRY_U32("snps,nr-gpios", 8), + PROPERTY_ENTRY_U32("gpio-base", 8), + { } +}; + +static const struct software_node intel_quark_gpio_portA_node = { + .name = "portA", + .parent = &intel_quark_gpio_controller_node, + .properties = intel_quark_gpio_portA_properties, +}; + +static const struct software_node *intel_quark_gpio_node_group[] = { + &intel_quark_gpio_controller_node, + &intel_quark_gpio_portA_node, + NULL +}; + static struct mfd_cell intel_quark_mfd_cells[] = { [MFD_I2C_BAR] = { .id = MFD_I2C_BAR, @@ -203,35 +219,19 @@ static int intel_quark_gpio_setup(struct pci_dev *pdev) { struct mfd_cell *cell = &intel_quark_mfd_cells[MFD_GPIO_BAR]; struct resource *res = intel_quark_gpio_res; - struct dwapb_platform_data *pdata; - struct device *dev = &pdev->dev; + int ret; res[INTEL_QUARK_IORES_MEM].start = pci_resource_start(pdev, MFD_GPIO_BAR); res[INTEL_QUARK_IORES_MEM].end = pci_resource_end(pdev, MFD_GPIO_BAR); - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - /* For intel quark x1000, it has only one port: portA */ - pdata->nports = INTEL_QUARK_GPIO_NPORTS; - pdata->properties = devm_kcalloc(dev, pdata->nports, - sizeof(*pdata->properties), - GFP_KERNEL); - if (!pdata->properties) - return -ENOMEM; - - /* Set the properties for portA */ - pdata->properties->fwnode = NULL; - pdata->properties->idx = 0; - pdata->properties->ngpio = INTEL_QUARK_MFD_NGPIO; - pdata->properties->gpio_base = INTEL_QUARK_MFD_GPIO_BASE; - pdata->properties->irq[0] = pci_irq_vector(pdev, 0); - pdata->properties->irq_shared = true; + res[INTEL_QUARK_IORES_IRQ].start = pci_irq_vector(pdev, 0); + res[INTEL_QUARK_IORES_IRQ].end = pci_irq_vector(pdev, 0); - cell->platform_data = pdata; - cell->pdata_size = sizeof(*pdata); + ret = software_node_register_node_group(intel_quark_gpio_node_group); + if (ret) + return ret; + cell->swnode = &intel_quark_gpio_controller_node; return 0; } @@ -274,10 +274,12 @@ static int intel_quark_mfd_probe(struct pci_dev *pdev, ARRAY_SIZE(intel_quark_mfd_cells), NULL, 0, NULL); if (ret) - goto err_free_irq_vectors; + goto err_unregister_gpio_node_group; return 0; +err_unregister_gpio_node_group: + software_node_unregister_node_group(intel_quark_gpio_node_group); err_free_irq_vectors: pci_free_irq_vectors(pdev); err_unregister_i2c_clk: @@ -288,6 +290,7 @@ err_unregister_i2c_clk: static void intel_quark_mfd_remove(struct pci_dev *pdev) { mfd_remove_devices(&pdev->dev); + software_node_unregister_node_group(intel_quark_gpio_node_group); pci_free_irq_vectors(pdev); intel_quark_unregister_i2c_clk(&pdev->dev); } diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c index bc069c4daa60..9d89171d83f9 100644 --- a/drivers/mfd/intel_soc_pmic_bxtwc.c +++ b/drivers/mfd/intel_soc_pmic_bxtwc.c @@ -2,25 +2,36 @@ /* * MFD core driver for Intel Broxton Whiskey Cove PMIC * - * Copyright (C) 2015 Intel Corporation. All rights reserved. + * Copyright (C) 2015-2017, 2022 Intel Corporation. All rights reserved. */ #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 0xFF00 +#define REG_ADDR_MASK GENMASK(15, 8) #define REG_ADDR_SHIFT 8 -#define REG_OFFSET_MASK 0xFF +#define REG_OFFSET_MASK GENMASK(7, 0) /* Interrupt Status Registers */ #define BXTWC_IRQLVL1 0x4E02 @@ -112,32 +123,32 @@ static const struct regmap_irq bxtwc_regmap_irqs[] = { }; static const struct regmap_irq bxtwc_regmap_irqs_pwrbtn[] = { - REGMAP_IRQ_REG(BXTWC_PWRBTN_IRQ, 0, 0x01), + REGMAP_IRQ_REG(BXTWC_PWRBTN_IRQ, 0, BIT(0)), }; static const struct regmap_irq bxtwc_regmap_irqs_bcu[] = { - REGMAP_IRQ_REG(BXTWC_BCU_IRQ, 0, 0x1f), + REGMAP_IRQ_REG(BXTWC_BCU_IRQ, 0, GENMASK(4, 0)), }; static const struct regmap_irq bxtwc_regmap_irqs_adc[] = { - REGMAP_IRQ_REG(BXTWC_ADC_IRQ, 0, 0xff), + REGMAP_IRQ_REG(BXTWC_ADC_IRQ, 0, GENMASK(7, 0)), }; static const struct regmap_irq bxtwc_regmap_irqs_chgr[] = { - REGMAP_IRQ_REG(BXTWC_USBC_IRQ, 0, 0x20), - REGMAP_IRQ_REG(BXTWC_CHGR0_IRQ, 0, 0x1f), - REGMAP_IRQ_REG(BXTWC_CHGR1_IRQ, 1, 0x1f), + REGMAP_IRQ_REG(BXTWC_USBC_IRQ, 0, BIT(5)), + REGMAP_IRQ_REG(BXTWC_CHGR0_IRQ, 0, GENMASK(4, 0)), + REGMAP_IRQ_REG(BXTWC_CHGR1_IRQ, 1, GENMASK(4, 0)), }; static const struct regmap_irq bxtwc_regmap_irqs_tmu[] = { - REGMAP_IRQ_REG(BXTWC_TMU_IRQ, 0, 0x06), + REGMAP_IRQ_REG(BXTWC_TMU_IRQ, 0, GENMASK(2, 1)), }; static const struct regmap_irq bxtwc_regmap_irqs_crit[] = { - REGMAP_IRQ_REG(BXTWC_CRIT_IRQ, 0, 0x03), + 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, @@ -146,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, @@ -155,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, @@ -164,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, @@ -173,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, @@ -182,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, @@ -191,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, @@ -231,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, }, }; @@ -333,18 +361,21 @@ static unsigned long bxtwc_reg_addr; static ssize_t addr_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "0x%lx\n", bxtwc_reg_addr); + return sysfs_emit(buf, "0x%lx\n", bxtwc_reg_addr); } static ssize_t addr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - if (kstrtoul(buf, 0, &bxtwc_reg_addr)) { - dev_err(dev, "Invalid register address\n"); - return -EINVAL; - } - return (ssize_t)count; + int ret; + + ret = kstrtoul(buf, 0, &bxtwc_reg_addr); + if (ret) + return ret; + + return count; } +static DEVICE_ATTR_ADMIN_RW(addr); static ssize_t val_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -354,12 +385,12 @@ static ssize_t val_show(struct device *dev, struct intel_soc_pmic *pmic = dev_get_drvdata(dev); ret = regmap_read(pmic->regmap, bxtwc_reg_addr, &val); - if (ret < 0) { + if (ret) { dev_err(dev, "Failed to read 0x%lx\n", bxtwc_reg_addr); - return -EIO; + return ret; } - return sprintf(buf, "0x%02x\n", val); + return sysfs_emit(buf, "0x%02x\n", val); } static ssize_t val_store(struct device *dev, @@ -377,22 +408,18 @@ static ssize_t val_store(struct device *dev, if (ret) { dev_err(dev, "Failed to write value 0x%02x to address 0x%lx", val, bxtwc_reg_addr); - return -EIO; + return ret; } 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, -}; +ATTRIBUTE_GROUPS(bxtwc); static const struct regmap_config bxtwc_regmap_config = { .reg_bits = 16, @@ -407,41 +434,56 @@ 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) { - dev_err(pmic->dev, - "Failed to get parent vIRQ(%d) for chip %s, ret:%d\n", - pirq, chip->name, irq); - return irq; - } + if (irq < 0) + 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) { + struct device *dev = &pdev->dev; int ret; - acpi_handle handle; acpi_status status; unsigned long long hrv; struct intel_soc_pmic *pmic; - handle = ACPI_HANDLE(&pdev->dev); - status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv); - if (ACPI_FAILURE(status)) { - dev_err(&pdev->dev, "Failed to get PMIC hardware revision\n"); - return -ENODEV; - } - if (hrv != BROXTON_PMIC_WC_HRV) { - dev_err(&pdev->dev, "Invalid PMIC hardware revision: %llu\n", - hrv); - return -ENODEV; - } + status = acpi_evaluate_integer(ACPI_HANDLE(dev), "_HRV", NULL, &hrv); + if (ACPI_FAILURE(status)) + return dev_err_probe(dev, -ENODEV, "Failed to get PMIC hardware revision\n"); + if (hrv != BROXTON_PMIC_WC_HRV) + return dev_err_probe(dev, -ENODEV, "Invalid PMIC hardware revision: %llu\n", hrv); - pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); + pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); if (!pmic) return -ENOMEM; @@ -450,88 +492,67 @@ static int bxtwc_probe(struct platform_device *pdev) return ret; pmic->irq = ret; - dev_set_drvdata(&pdev->dev, pmic); - pmic->dev = &pdev->dev; + platform_set_drvdata(pdev, pmic); + pmic->dev = dev; - pmic->scu = devm_intel_scu_ipc_dev_get(&pdev->dev); + pmic->scu = devm_intel_scu_ipc_dev_get(dev); if (!pmic->scu) return -EPROBE_DEFER; - pmic->regmap = devm_regmap_init(&pdev->dev, NULL, pmic, - &bxtwc_regmap_config); - if (IS_ERR(pmic->regmap)) { - ret = PTR_ERR(pmic->regmap); - dev_err(&pdev->dev, "Failed to initialise regmap: %d\n", ret); - return ret; - } + pmic->regmap = devm_regmap_init(dev, NULL, pmic, &bxtwc_regmap_config); + if (IS_ERR(pmic->regmap)) + return dev_err_probe(dev, PTR_ERR(pmic->regmap), "Failed to initialise regmap\n"); - ret = devm_regmap_add_irq_chip(&pdev->dev, pmic->regmap, pmic->irq, + ret = devm_regmap_add_irq_chip(dev, pmic->regmap, pmic->irq, IRQF_ONESHOT | IRQF_SHARED, 0, &bxtwc_regmap_irq_chip, &pmic->irq_chip_data); - if (ret) { - dev_err(&pdev->dev, "Failed to add IRQ chip\n"); + 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) { - dev_err(&pdev->dev, "Failed to add PWRBTN IRQ chip\n"); - return ret; - } - - 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) { - dev_err(&pdev->dev, "Failed to add TMU IRQ chip\n"); + if (ret) 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); - - - if (ret) { - dev_err(&pdev->dev, "Failed to add BUC IRQ chip\n"); + 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 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); - - - if (ret) { - dev_err(&pdev->dev, "Failed to add ADC IRQ chip\n"); + 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 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); - - if (ret) { - dev_err(&pdev->dev, "Failed to add CHGR IRQ chip\n"); + 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 ret; - } /* Add chained IRQ handler for CRIT IRQs */ ret = bxtwc_add_chained_irq_chip(pmic, pmic->irq_chip_data, @@ -539,54 +560,33 @@ static int bxtwc_probe(struct platform_device *pdev) IRQF_ONESHOT, &bxtwc_regmap_irq_chip_crit, &pmic->irq_chip_data_crit); - - - if (ret) { - dev_err(&pdev->dev, "Failed to add CRIT IRQ chip\n"); - return ret; - } - - ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, bxt_wc_dev, - ARRAY_SIZE(bxt_wc_dev), NULL, 0, NULL); - if (ret) { - dev_err(&pdev->dev, "Failed to add devices\n"); + if (ret) return ret; - } - ret = sysfs_create_group(&pdev->dev.kobj, &bxtwc_group); - if (ret) { - dev_err(&pdev->dev, "Failed to create sysfs group %d\n", ret); - return ret; - } + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, bxt_wc_dev, ARRAY_SIZE(bxt_wc_dev), + NULL, 0, NULL); + if (ret) + return dev_err_probe(dev, ret, "Failed to add devices\n"); /* - * There is known hw bug. Upon reset BIT 5 of register + * There is a known H/W bug. Upon reset, BIT 5 of register * BXTWC_CHGR_LVL1_IRQ is 0 which is the expected value. However, * later it's set to 1(masked) automatically by hardware. So we - * have the software workaround here to unmaksed it in order to let - * charger interrutp work. + * place the software workaround here to unmask it again in order + * to re-enable the charger interrupt. */ - regmap_update_bits(pmic->regmap, BXTWC_MIRQLVL1, - BXTWC_MIRQLVL1_MCHGR, 0); - - return 0; -} - -static int bxtwc_remove(struct platform_device *pdev) -{ - sysfs_remove_group(&pdev->dev.kobj, &bxtwc_group); + regmap_update_bits(pmic->regmap, BXTWC_MIRQLVL1, BXTWC_MIRQLVL1_MCHGR, 0); return 0; } static void bxtwc_shutdown(struct platform_device *pdev) { - struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev); + struct intel_soc_pmic *pmic = platform_get_drvdata(pdev); disable_irq(pmic->irq); } -#ifdef CONFIG_PM_SLEEP static int bxtwc_suspend(struct device *dev) { struct intel_soc_pmic *pmic = dev_get_drvdata(dev); @@ -603,8 +603,8 @@ static int bxtwc_resume(struct device *dev) enable_irq(pmic->irq); return 0; } -#endif -static SIMPLE_DEV_PM_OPS(bxtwc_pm_ops, bxtwc_suspend, bxtwc_resume); + +static DEFINE_SIMPLE_DEV_PM_OPS(bxtwc_pm_ops, bxtwc_suspend, bxtwc_resume); static const struct acpi_device_id bxtwc_acpi_ids[] = { { "INT34D3", }, @@ -614,16 +614,17 @@ MODULE_DEVICE_TABLE(acpi, bxtwc_acpi_ids); static struct platform_driver bxtwc_driver = { .probe = bxtwc_probe, - .remove = bxtwc_remove, .shutdown = bxtwc_shutdown, .driver = { - .name = "BXTWC PMIC", - .pm = &bxtwc_pm_ops, - .acpi_match_table = ACPI_PTR(bxtwc_acpi_ids), + .name = "intel_soc_pmic_bxtwc", + .pm = pm_sleep_ptr(&bxtwc_pm_ops), + .acpi_match_table = bxtwc_acpi_ids, + .dev_groups = bxtwc_groups, }, }; 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>"); +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 1c7577b881ff..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[] = { @@ -140,7 +141,7 @@ static void chtdc_ti_shutdown(struct i2c_client *i2c) disable_irq(pmic->irq); } -static int __maybe_unused chtdc_ti_suspend(struct device *dev) +static int chtdc_ti_suspend(struct device *dev) { struct intel_soc_pmic *pmic = dev_get_drvdata(dev); @@ -149,7 +150,7 @@ static int __maybe_unused chtdc_ti_suspend(struct device *dev) return 0; } -static int __maybe_unused chtdc_ti_resume(struct device *dev) +static int chtdc_ti_resume(struct device *dev) { struct intel_soc_pmic *pmic = dev_get_drvdata(dev); @@ -158,7 +159,7 @@ static int __maybe_unused chtdc_ti_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(chtdc_ti_pm_ops, chtdc_ti_suspend, chtdc_ti_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(chtdc_ti_pm_ops, chtdc_ti_suspend, chtdc_ti_resume); static const struct acpi_device_id chtdc_ti_acpi_ids[] = { { "INT33F5" }, @@ -169,10 +170,10 @@ MODULE_DEVICE_TABLE(acpi, chtdc_ti_acpi_ids); static struct i2c_driver chtdc_ti_i2c_driver = { .driver = { .name = "intel_soc_pmic_chtdc_ti", - .pm = &chtdc_ti_pm_ops, + .pm = pm_sleep_ptr(&chtdc_ti_pm_ops), .acpi_match_table = chtdc_ti_acpi_ids, }, - .probe_new = chtdc_ti_probe, + .probe = chtdc_ti_probe, .shutdown = chtdc_ti_shutdown, }; module_i2c_driver(chtdc_ti_i2c_driver); diff --git a/drivers/mfd/intel_soc_pmic_chtwc.c b/drivers/mfd/intel_soc_pmic_chtwc.c index 49c5f71664bc..aa71a7d83fcd 100644 --- a/drivers/mfd/intel_soc_pmic_chtwc.c +++ b/drivers/mfd/intel_soc_pmic_chtwc.c @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/delay.h> +#include <linux/dmi.h> #include <linux/err.h> #include <linux/i2c.h> #include <linux/interrupt.h> @@ -134,32 +135,81 @@ static const struct regmap_irq_chip cht_wc_regmap_irq_chip = { .num_regs = 1, }; +static const struct dmi_system_id cht_wc_model_dmi_ids[] = { + { + /* GPD win / GPD pocket mini laptops */ + .driver_data = (void *)(long)INTEL_CHT_WC_GPD_WIN_POCKET, + /* + * This DMI match may not seem unique, but it is. In the 67000+ + * DMI decode dumps from linux-hardware.org only 116 have + * board_vendor set to "AMI Corporation" and of those 116 only + * the GPD win's and pocket's board_name is "Default string". + */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), + }, + }, { + /* Xiaomi Mi Pad 2 */ + .driver_data = (void *)(long)INTEL_CHT_WC_XIAOMI_MIPAD2, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"), + DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"), + }, + }, { + /* Lenovo Yoga Book X90F / X90L */ + .driver_data = (void *)(long)INTEL_CHT_WC_LENOVO_YOGABOOK1, + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"), + }, + }, { + /* Lenovo Yoga Book X91F / X91L */ + .driver_data = (void *)(long)INTEL_CHT_WC_LENOVO_YOGABOOK1, + .matches = { + /* Non exact match to match F + L versions */ + DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"), + }, + }, { + /* Lenovo Yoga Tab 3 Pro YT3-X90F */ + .driver_data = (void *)(long)INTEL_CHT_WC_LENOVO_YT3_X90, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"), + }, + }, + { } +}; + static int cht_wc_probe(struct i2c_client *client) { struct device *dev = &client->dev; + const struct dmi_system_id *id; struct intel_soc_pmic *pmic; acpi_status status; unsigned long long hrv; int ret; status = acpi_evaluate_integer(ACPI_HANDLE(dev), "_HRV", NULL, &hrv); - if (ACPI_FAILURE(status)) { - dev_err(dev, "Failed to get PMIC hardware revision\n"); - return -ENODEV; - } - if (hrv != CHT_WC_HRV) { - dev_err(dev, "Invalid PMIC hardware revision: %llu\n", hrv); - return -ENODEV; - } - if (client->irq < 0) { - dev_err(dev, "Invalid IRQ\n"); - return -EINVAL; - } + if (ACPI_FAILURE(status)) + return dev_err_probe(dev, -ENODEV, "Failed to get PMIC hardware revision\n"); + if (hrv != CHT_WC_HRV) + return dev_err_probe(dev, -ENODEV, "Invalid PMIC hardware revision: %llu\n", hrv); + + if (client->irq < 0) + return dev_err_probe(dev, -EINVAL, "Invalid IRQ\n"); pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); if (!pmic) return -ENOMEM; + id = dmi_first_match(cht_wc_model_dmi_ids); + if (id) + pmic->cht_wc_model = (long)id->driver_data; + pmic->irq = client->irq; pmic->dev = dev; i2c_set_clientdata(client, pmic); @@ -187,7 +237,7 @@ static void cht_wc_shutdown(struct i2c_client *client) disable_irq(pmic->irq); } -static int __maybe_unused cht_wc_suspend(struct device *dev) +static int cht_wc_suspend(struct device *dev) { struct intel_soc_pmic *pmic = dev_get_drvdata(dev); @@ -196,7 +246,7 @@ static int __maybe_unused cht_wc_suspend(struct device *dev) return 0; } -static int __maybe_unused cht_wc_resume(struct device *dev) +static int cht_wc_resume(struct device *dev) { struct intel_soc_pmic *pmic = dev_get_drvdata(dev); @@ -204,7 +254,7 @@ static int __maybe_unused cht_wc_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(cht_wc_pm_ops, cht_wc_suspend, cht_wc_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(cht_wc_pm_ops, cht_wc_suspend, cht_wc_resume); static const struct i2c_device_id cht_wc_i2c_id[] = { { } @@ -217,11 +267,11 @@ static const struct acpi_device_id cht_wc_acpi_ids[] = { static struct i2c_driver cht_wc_driver = { .driver = { - .name = "CHT Whiskey Cove PMIC", - .pm = &cht_wc_pm_ops, + .name = "intel_soc_pmic_chtwc", + .pm = pm_sleep_ptr(&cht_wc_pm_ops), .acpi_match_table = cht_wc_acpi_ids, }, - .probe_new = cht_wc_probe, + .probe = cht_wc_probe, .shutdown = cht_wc_shutdown, .id_table = cht_wc_i2c_id, }; diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c deleted file mode 100644 index ddd64f9e3341..000000000000 --- a/drivers/mfd/intel_soc_pmic_core.c +++ /dev/null @@ -1,178 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Intel SoC PMIC MFD Driver - * - * Copyright (C) 2013, 2014 Intel Corporation. All rights reserved. - * - * Author: Yang, Bin <bin.yang@intel.com> - * Author: Zhu, Lejun <lejun.zhu@linux.intel.com> - */ - -#include <linux/acpi.h> -#include <linux/i2c.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/mfd/core.h> -#include <linux/mfd/intel_soc_pmic.h> -#include <linux/pwm.h> -#include <linux/regmap.h> - -#include "intel_soc_pmic_core.h" - -/* Crystal Cove PMIC shares same ACPI ID between different platforms */ -#define BYT_CRC_HRV 2 -#define CHT_CRC_HRV 3 - -/* 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), -}; - -static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *i2c_id) -{ - struct device *dev = &i2c->dev; - struct intel_soc_pmic_config *config; - struct intel_soc_pmic *pmic; - unsigned long long hrv; - acpi_status status; - int ret; - - /* - * There are 2 different Crystal Cove PMICs a Bay Trail and Cherry - * Trail version, use _HRV to differentiate between the 2. - */ - status = acpi_evaluate_integer(ACPI_HANDLE(dev), "_HRV", NULL, &hrv); - if (ACPI_FAILURE(status)) { - dev_err(dev, "Failed to get PMIC hardware revision\n"); - return -ENODEV; - } - - switch (hrv) { - case BYT_CRC_HRV: - config = &intel_soc_pmic_config_byt_crc; - break; - case CHT_CRC_HRV: - config = &intel_soc_pmic_config_cht_crc; - break; - default: - dev_warn(dev, "Unknown hardware rev %llu, assuming BYT\n", hrv); - config = &intel_soc_pmic_config_byt_crc; - } - - pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); - if (!pmic) - return -ENOMEM; - - dev_set_drvdata(dev, pmic); - - pmic->regmap = devm_regmap_init_i2c(i2c, config->regmap_config); - if (IS_ERR(pmic->regmap)) - return PTR_ERR(pmic->regmap); - - pmic->irq = i2c->irq; - - ret = regmap_add_irq_chip(pmic->regmap, pmic->irq, - config->irq_flags | IRQF_ONESHOT, - 0, config->irq_chip, - &pmic->irq_chip_data); - if (ret) - return ret; - - ret = enable_irq_wake(pmic->irq); - if (ret) - dev_warn(dev, "Can't enable IRQ as wake source: %d\n", ret); - - /* Add lookup table for crc-pwm */ - pwm_add_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup)); - - ret = mfd_add_devices(dev, -1, config->cell_dev, - config->n_cell_devs, NULL, 0, - regmap_irq_get_domain(pmic->irq_chip_data)); - if (ret) - goto err_del_irq_chip; - - return 0; - -err_del_irq_chip: - regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data); - return ret; -} - -static int intel_soc_pmic_i2c_remove(struct i2c_client *i2c) -{ - struct intel_soc_pmic *pmic = dev_get_drvdata(&i2c->dev); - - regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data); - - /* remove crc-pwm lookup table */ - pwm_remove_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup)); - - mfd_remove_devices(&i2c->dev); - - return 0; -} - -static void intel_soc_pmic_shutdown(struct i2c_client *i2c) -{ - struct intel_soc_pmic *pmic = dev_get_drvdata(&i2c->dev); - - disable_irq(pmic->irq); - - return; -} - -#if defined(CONFIG_PM_SLEEP) -static int intel_soc_pmic_suspend(struct device *dev) -{ - struct intel_soc_pmic *pmic = dev_get_drvdata(dev); - - disable_irq(pmic->irq); - - return 0; -} - -static int intel_soc_pmic_resume(struct device *dev) -{ - struct intel_soc_pmic *pmic = dev_get_drvdata(dev); - - enable_irq(pmic->irq); - - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(intel_soc_pmic_pm_ops, intel_soc_pmic_suspend, - intel_soc_pmic_resume); - -static const struct i2c_device_id intel_soc_pmic_i2c_id[] = { - { } -}; -MODULE_DEVICE_TABLE(i2c, intel_soc_pmic_i2c_id); - -#if defined(CONFIG_ACPI) -static const struct acpi_device_id intel_soc_pmic_acpi_match[] = { - { "INT33FD" }, - { }, -}; -MODULE_DEVICE_TABLE(acpi, intel_soc_pmic_acpi_match); -#endif - -static struct i2c_driver intel_soc_pmic_i2c_driver = { - .driver = { - .name = "intel_soc_pmic_i2c", - .pm = &intel_soc_pmic_pm_ops, - .acpi_match_table = ACPI_PTR(intel_soc_pmic_acpi_match), - }, - .probe = intel_soc_pmic_i2c_probe, - .remove = intel_soc_pmic_i2c_remove, - .id_table = intel_soc_pmic_i2c_id, - .shutdown = intel_soc_pmic_shutdown, -}; - -module_i2c_driver(intel_soc_pmic_i2c_driver); - -MODULE_DESCRIPTION("I2C driver for Intel SoC PMIC"); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Yang, Bin <bin.yang@intel.com>"); -MODULE_AUTHOR("Zhu, Lejun <lejun.zhu@linux.intel.com>"); diff --git a/drivers/mfd/intel_soc_pmic_core.h b/drivers/mfd/intel_soc_pmic_core.h deleted file mode 100644 index d490685845eb..000000000000 --- a/drivers/mfd/intel_soc_pmic_core.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Intel SoC PMIC MFD Driver - * - * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. - * - * Author: Yang, Bin <bin.yang@intel.com> - * Author: Zhu, Lejun <lejun.zhu@linux.intel.com> - */ - -#ifndef __INTEL_SOC_PMIC_CORE_H__ -#define __INTEL_SOC_PMIC_CORE_H__ - -struct intel_soc_pmic_config { - unsigned long irq_flags; - struct mfd_cell *cell_dev; - int n_cell_devs; - const struct regmap_config *regmap_config; - const struct regmap_irq_chip *irq_chip; -}; - -extern struct intel_soc_pmic_config intel_soc_pmic_config_byt_crc; -extern struct intel_soc_pmic_config intel_soc_pmic_config_cht_crc; - -#endif /* __INTEL_SOC_PMIC_CORE_H__ */ diff --git a/drivers/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c index 38acb20e2d60..41429f9bcb69 100644 --- a/drivers/mfd/intel_soc_pmic_crc.c +++ b/drivers/mfd/intel_soc_pmic_crc.c @@ -2,18 +2,21 @@ /* * Device access for Crystal Cove PMIC * - * Copyright (C) 2013, 2014 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014, 2022 Intel Corporation. All rights reserved. * * Author: Yang, Bin <bin.yang@intel.com> * Author: Zhu, Lejun <lejun.zhu@linux.intel.com> */ +#include <linux/i2c.h> #include <linux/interrupt.h> -#include <linux/regmap.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> #include <linux/mfd/core.h> #include <linux/mfd/intel_soc_pmic.h> - -#include "intel_soc_pmic_core.h" +#include <linux/platform_data/x86/soc.h> +#include <linux/pwm.h> +#include <linux/regmap.h> #define CRYSTAL_COVE_MAX_REGISTER 0xC6 @@ -28,18 +31,10 @@ #define CRYSTAL_COVE_IRQ_GPIO 5 #define CRYSTAL_COVE_IRQ_VHDMIOCP 6 -static const struct resource gpio_resources[] = { - DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_GPIO, "GPIO"), -}; - static const struct resource pwrsrc_resources[] = { DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_PWRSRC, "PWRSRC"), }; -static const struct resource adc_resources[] = { - DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_ADC, "ADC"), -}; - static const struct resource thermal_resources[] = { DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_THRM, "THERMAL"), }; @@ -48,6 +43,18 @@ static const struct resource bcu_resources[] = { DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_BCU, "BCU"), }; +static const struct resource adc_resources[] = { + DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_ADC, "ADC"), +}; + +static const struct resource charger_resources[] = { + DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_CHGR, "CHGR"), +}; + +static const struct resource gpio_resources[] = { + DEFINE_RES_IRQ_NAMED(CRYSTAL_COVE_IRQ_GPIO, "GPIO"), +}; + static struct mfd_cell crystal_cove_byt_dev[] = { { .name = "crystal_cove_pwrsrc", @@ -55,11 +62,6 @@ static struct mfd_cell crystal_cove_byt_dev[] = { .resources = pwrsrc_resources, }, { - .name = "crystal_cove_adc", - .num_resources = ARRAY_SIZE(adc_resources), - .resources = adc_resources, - }, - { .name = "crystal_cove_thermal", .num_resources = ARRAY_SIZE(thermal_resources), .resources = thermal_resources, @@ -70,6 +72,16 @@ static struct mfd_cell crystal_cove_byt_dev[] = { .resources = bcu_resources, }, { + .name = "crystal_cove_adc", + .num_resources = ARRAY_SIZE(adc_resources), + .resources = adc_resources, + }, + { + .name = "crystal_cove_charger", + .num_resources = ARRAY_SIZE(charger_resources), + .resources = charger_resources, + }, + { .name = "crystal_cove_gpio", .num_resources = ARRAY_SIZE(gpio_resources), .resources = gpio_resources, @@ -101,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[] = { @@ -123,7 +134,22 @@ static const struct regmap_irq_chip crystal_cove_irq_chip = { .mask_base = CRYSTAL_COVE_REG_MIRQLVL1, }; -struct intel_soc_pmic_config intel_soc_pmic_config_byt_crc = { +/* PWM consumed by the Intel GFX */ +static struct pwm_lookup crc_pwm_lookup[] = { + 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 { + unsigned long irq_flags; + struct mfd_cell *cell_dev; + int n_cell_devs; + const struct regmap_config *regmap_config; + const struct regmap_irq_chip *irq_chip; +}; + +static const struct crystal_cove_config crystal_cove_config_byt_crc = { .irq_flags = IRQF_TRIGGER_RISING, .cell_dev = crystal_cove_byt_dev, .n_cell_devs = ARRAY_SIZE(crystal_cove_byt_dev), @@ -131,10 +157,127 @@ struct intel_soc_pmic_config intel_soc_pmic_config_byt_crc = { .irq_chip = &crystal_cove_irq_chip, }; -struct intel_soc_pmic_config intel_soc_pmic_config_cht_crc = { +static const struct crystal_cove_config crystal_cove_config_cht_crc = { .irq_flags = IRQF_TRIGGER_RISING, .cell_dev = crystal_cove_cht_dev, .n_cell_devs = ARRAY_SIZE(crystal_cove_cht_dev), .regmap_config = &crystal_cove_regmap_config, .irq_chip = &crystal_cove_irq_chip, }; + +static int crystal_cove_i2c_probe(struct i2c_client *i2c) +{ + const struct crystal_cove_config *config; + struct device *dev = &i2c->dev; + struct intel_soc_pmic *pmic; + int ret; + + if (soc_intel_is_byt()) + config = &crystal_cove_config_byt_crc; + else + config = &crystal_cove_config_cht_crc; + + pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); + if (!pmic) + return -ENOMEM; + + i2c_set_clientdata(i2c, pmic); + + pmic->regmap = devm_regmap_init_i2c(i2c, config->regmap_config); + if (IS_ERR(pmic->regmap)) + return PTR_ERR(pmic->regmap); + + pmic->irq = i2c->irq; + + ret = devm_regmap_add_irq_chip(dev, pmic->regmap, pmic->irq, + config->irq_flags | IRQF_ONESHOT, + 0, config->irq_chip, &pmic->irq_chip_data); + if (ret) + return ret; + + ret = enable_irq_wake(pmic->irq); + if (ret) + dev_warn(dev, "Can't enable IRQ as wake source: %d\n", ret); + + /* Add lookup table for crc-pwm */ + pwm_add_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup)); + + /* To distuingish this domain from the GPIO/charger's irqchip domains */ + irq_domain_update_bus_token(regmap_irq_get_domain(pmic->irq_chip_data), + DOMAIN_BUS_NEXUS); + + ret = mfd_add_devices(dev, PLATFORM_DEVID_NONE, config->cell_dev, + config->n_cell_devs, NULL, 0, + regmap_irq_get_domain(pmic->irq_chip_data)); + if (ret) + pwm_remove_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup)); + + return ret; +} + +static void crystal_cove_i2c_remove(struct i2c_client *i2c) +{ + /* remove crc-pwm lookup table */ + pwm_remove_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup)); + + mfd_remove_devices(&i2c->dev); +} + +static void crystal_cove_shutdown(struct i2c_client *i2c) +{ + struct intel_soc_pmic *pmic = i2c_get_clientdata(i2c); + + disable_irq(pmic->irq); + + return; +} + +static int crystal_cove_suspend(struct device *dev) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(dev); + + disable_irq(pmic->irq); + + return 0; +} + +static int crystal_cove_resume(struct device *dev) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(dev); + + enable_irq(pmic->irq); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(crystal_cove_pm_ops, crystal_cove_suspend, crystal_cove_resume); + +static const struct acpi_device_id crystal_cove_acpi_match[] = { + { "INT33FD" }, + { }, +}; +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 = "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, +}; + +module_i2c_driver(crystal_cove_i2c_driver); + +MODULE_DESCRIPTION("I2C driver for Intel SoC PMIC"); +MODULE_AUTHOR("Yang, Bin <bin.yang@intel.com>"); +MODULE_AUTHOR("Zhu, Lejun <lejun.zhu@linux.intel.com>"); 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 99b9c113f964..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 */ @@ -105,19 +105,15 @@ static void ioc3_irq_handler(struct irq_desc *desc) struct ioc3_priv_data *ipd = domain->host_data; struct ioc3 __iomem *regs = ipd->regs; u32 pending, mask; - unsigned int irq; pending = readl(®s->sio_ir); mask = readl(®s->sio_ies); pending &= mask; /* Mask off not enabled interrupts */ - if (pending) { - irq = irq_find_mapping(domain, __ffs(pending)); - if (irq) - generic_handle_irq(irq); - } else { + if (pending) + generic_handle_domain_irq(domain, __ffs(pending)); + else spurious_interrupt(); - } } /* diff --git a/drivers/mfd/ipaq-micro.c b/drivers/mfd/ipaq-micro.c index e92eeeb67a98..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(µ->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(µ->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", @@ -381,7 +378,6 @@ static int __maybe_unused micro_resume(struct device *dev) static int __init micro_probe(struct platform_device *pdev) { struct ipaq_micro *micro; - struct resource *res; int ret; int irq; @@ -391,8 +387,7 @@ static int __init micro_probe(struct platform_device *pdev) micro->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - micro->base = devm_ioremap_resource(&pdev->dev, res); + micro->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(micro->base)) return PTR_ERR(micro->base); @@ -403,7 +398,7 @@ static int __init micro_probe(struct platform_device *pdev) micro_reset_comm(micro); irq = platform_get_irq(pdev, 0); - if (!irq) + if (irq < 0) return -EINVAL; ret = devm_request_irq(&pdev->dev, irq, micro_serial_isr, IRQF_SHARED, "ipaq-micro", diff --git a/drivers/mfd/iqs62x.c b/drivers/mfd/iqs62x.c index 9805cf191245..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 { @@ -898,7 +898,6 @@ static int iqs62x_probe(struct i2c_client *client) struct iqs62x_info info; unsigned int val; int ret, i, j; - u8 sw_num = 0; const char *fw_name = NULL; iqs62x = devm_kzalloc(&client->dev, sizeof(*iqs62x), GFP_KERNEL); @@ -949,7 +948,8 @@ static int iqs62x_probe(struct i2c_client *client) if (info.sw_num < iqs62x->dev_desc->sw_num) continue; - sw_num = info.sw_num; + iqs62x->sw_num = info.sw_num; + iqs62x->hw_num = info.hw_num; /* * Read each of the device's designated calibration registers, @@ -985,7 +985,7 @@ static int iqs62x_probe(struct i2c_client *client) return -EINVAL; } - if (!sw_num) { + if (!iqs62x->sw_num) { dev_err(&client->dev, "Unrecognized software number: 0x%02X\n", info.sw_num); return -EINVAL; @@ -1008,13 +1008,11 @@ static int iqs62x_probe(struct i2c_client *client) return ret; } -static int iqs62x_remove(struct i2c_client *client) +static void iqs62x_remove(struct i2c_client *client) { struct iqs62x_core *iqs62x = i2c_get_clientdata(client); wait_for_completion(&iqs62x->fw_done); - - return 0; } static int __maybe_unused iqs62x_suspend(struct device *dev) @@ -1071,7 +1069,7 @@ static struct i2c_driver iqs62x_i2c_driver = { .of_match_table = iqs62x_of_match, .pm = &iqs62x_pm, }, - .probe_new = iqs62x_probe, + .probe = iqs62x_probe, .remove = iqs62x_remove, }; module_i2c_driver(iqs62x_i2c_driver); diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c index 70eba4ce496f..add3bc04185b 100644 --- a/drivers/mfd/janz-cmodio.c +++ b/drivers/mfd/janz-cmodio.c @@ -154,7 +154,7 @@ static ssize_t modulbus_number_show(struct device *dev, { struct cmodio_device *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%x\n", priv->hex); + return sysfs_emit(buf, "%x\n", priv->hex); } static DEVICE_ATTR_RO(modulbus_number); diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c index bb26241c73bd..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; } @@ -349,7 +338,7 @@ static ssize_t pld_version_show(struct device *dev, { struct kempld_device_data *pld = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%s\n", pld->info.version); + return sysfs_emit(buf, "%s\n", pld->info.version); } static ssize_t pld_specification_show(struct device *dev, @@ -357,8 +346,7 @@ static ssize_t pld_specification_show(struct device *dev, { struct kempld_device_data *pld = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d.%d\n", pld->info.spec_major, - pld->info.spec_minor); + return sysfs_emit(buf, "%d.%d\n", pld->info.spec_major, pld->info.spec_minor); } static ssize_t pld_type_show(struct device *dev, @@ -366,23 +354,20 @@ static ssize_t pld_type_show(struct device *dev, { struct kempld_device_data *pld = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%s\n", kempld_get_type_string(pld)); + return sysfs_emit(buf, "%s\n", kempld_get_type_string(pld)); } 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) { @@ -415,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) { @@ -491,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 @@ -510,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) @@ -536,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, @@ -574,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, }, {} }; @@ -952,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 f3d418810693..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[] = { @@ -84,8 +84,7 @@ static struct mfd_cell khadas_mcu_cells[] = { { .name = "khadas-mcu-user-mem", }, }; -static int khadas_mcu_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int khadas_mcu_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct khadas_mcu *ddata; @@ -113,7 +112,7 @@ static int khadas_mcu_probe(struct i2c_client *client, if (ret) return ret; - if (of_find_property(dev->of_node, "#cooling-cells", NULL)) + if (of_property_present(dev->of_node, "#cooling-cells")) return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, khadas_mcu_fan_cells, ARRAY_SIZE(khadas_mcu_fan_cells), diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c index 5690768f3e63..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 { @@ -286,7 +284,7 @@ static ssize_t show_output(struct device *dev, val = (val & mask) >> shift; - return scnprintf(buf, PAGE_SIZE, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t store_output(struct device *dev, @@ -483,20 +481,10 @@ static int lm3533_device_init(struct lm3533 *lm3533) return -EINVAL; } - lm3533->gpio_hwen = pdata->gpio_hwen; - - dev_set_drvdata(lm3533->dev, lm3533); - - 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); @@ -584,8 +572,7 @@ static const struct regmap_config regmap_config = { .precious_reg = lm3533_precious_register, }; -static int lm3533_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int lm3533_i2c_probe(struct i2c_client *i2c) { struct lm3533 *lm3533; @@ -607,20 +594,18 @@ static int lm3533_i2c_probe(struct i2c_client *i2c, return lm3533_device_init(lm3533); } -static int lm3533_i2c_remove(struct i2c_client *i2c) +static void lm3533_i2c_remove(struct i2c_client *i2c) { struct lm3533 *lm3533 = i2c_get_clientdata(i2c); dev_dbg(&i2c->dev, "%s\n", __func__); lm3533_device_exit(lm3533); - - return 0; } 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 3a65d9938902..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,10 +374,10 @@ 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_new = lochnagar_i2c_probe, + .probe = lochnagar_i2c_probe, }; static int __init lochnagar_i2c_init(void) 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 13cb89be3d66..6764553147e4 100644 --- a/drivers/mfd/lp3943.c +++ b/drivers/mfd/lp3943.c @@ -102,7 +102,7 @@ static const struct regmap_config lp3943_regmap_config = { .max_register = LP3943_MAX_REGISTERS, }; -static int lp3943_probe(struct i2c_client *cl, const struct i2c_device_id *id) +static int lp3943_probe(struct i2c_client *cl) { struct lp3943 *lp3943; struct device *dev = &cl->dev; @@ -126,7 +126,7 @@ static int lp3943_probe(struct i2c_client *cl, const struct i2c_device_id *id) } 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 858c9e0a49a4..e8c5c89c2a76 100644 --- a/drivers/mfd/lp873x.c +++ b/drivers/mfd/lp873x.c @@ -1,22 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com/ * * Author: Keerthy <j-keerthy@ti.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #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> @@ -32,8 +24,7 @@ static const struct mfd_cell lp873x_cells[] = { { .name = "lp873x-gpio", }, }; -static int lp873x_probe(struct i2c_client *client, - const struct i2c_device_id *ids) +static int lp873x_probe(struct i2c_client *client) { struct lp873x *lp873; int ret; @@ -77,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 a52ab76febb3..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> @@ -43,11 +44,9 @@ static const struct of_device_id of_lp87565_match_table[] = { }; MODULE_DEVICE_TABLE(of, of_lp87565_match_table); -static int lp87565_probe(struct i2c_client *client, - const struct i2c_device_id *ids) +static int lp87565_probe(struct i2c_client *client) { struct lp87565 *lp87565; - const struct of_device_id *of_id; int ret; unsigned int otpid; @@ -90,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); @@ -110,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 348439a3fbbd..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"); @@ -175,6 +175,7 @@ int lp8788_irq_init(struct lp8788 *lp, int irq) IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lp8788-irq", irqd); if (ret) { + irq_domain_remove(lp->irqdm); dev_err(lp->dev, "failed to create a thread for IRQ_N\n"); return ret; } @@ -188,4 +189,6 @@ void lp8788_irq_exit(struct lp8788 *lp) { if (lp->irq) free_irq(lp->irq, lp->irqdm); + if (lp->irqdm) + irq_domain_remove(lp->irqdm); } diff --git a/drivers/mfd/lp8788.c b/drivers/mfd/lp8788.c index c223d2c6a363..32f255378f5a 100644 --- a/drivers/mfd/lp8788.c +++ b/drivers/mfd/lp8788.c @@ -166,7 +166,7 @@ static const struct regmap_config lp8788_regmap_config = { .max_register = MAX_LP8788_REGISTERS, }; -static int lp8788_probe(struct i2c_client *cl, const struct i2c_device_id *id) +static int lp8788_probe(struct i2c_client *cl) { struct lp8788 *lp; struct lp8788_platform_data *pdata = dev_get_platdata(&cl->dev); @@ -195,21 +195,28 @@ static int lp8788_probe(struct i2c_client *cl, const struct i2c_device_id *id) if (ret) return ret; - return mfd_add_devices(lp->dev, -1, lp8788_devs, - ARRAY_SIZE(lp8788_devs), NULL, 0, NULL); + ret = mfd_add_devices(lp->dev, -1, lp8788_devs, + ARRAY_SIZE(lp8788_devs), NULL, 0, NULL); + if (ret) + goto err_exit_irq; + + return 0; + +err_exit_irq: + lp8788_irq_exit(lp); + return ret; } -static int lp8788_remove(struct i2c_client *cl) +static void lp8788_remove(struct i2c_client *cl) { struct lp8788 *lp = i2c_get_clientdata(cl); mfd_remove_devices(lp->dev); lp8788_irq_exit(lp); - return 0; } static const struct i2c_device_id lp8788_ids[] = { - {"lp8788", 0}, + { "lp8788" }, { } }; MODULE_DEVICE_TABLE(i2c, lp8788_ids); @@ -237,4 +244,3 @@ module_exit(lp8788_exit); MODULE_DESCRIPTION("TI LP8788 MFD Driver"); MODULE_AUTHOR("Milo Kim"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 3bbb29a7e7a5..4b7d0cb9340f 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -8,7 +8,8 @@ * Configuration Registers. * * This driver is derived from lpc_sch. - + * + * Copyright (c) 2017, 2021-2022 Intel Corporation * Copyright (c) 2011 Extreme Engineering Solution, Inc. * Author: Aaron Sierra <asierra@xes-inc.com> * @@ -37,14 +38,17 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/align.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/acpi.h> #include <linux/pci.h> +#include <linux/pinctrl/pinctrl.h> #include <linux/mfd/core.h> #include <linux/mfd/lpc_ich.h> #include <linux/platform_data/itco_wdt.h> +#include <linux/platform_data/x86/p2sb.h> #define ACPIBASE 0x40 #define ACPIBASE_GPE_OFF 0x28 @@ -63,14 +67,14 @@ #define SPIBASE_BYT 0x54 #define SPIBASE_BYT_SZ 512 #define SPIBASE_BYT_EN BIT(1) +#define BYT_BCR 0xfc +#define BYT_BCR_WPD BIT(0) #define SPIBASE_LPT 0x3800 #define SPIBASE_LPT_SZ 512 #define BCR 0xdc #define BCR_WPD BIT(0) -#define SPIBASE_APL_SZ 4096 - #define GPIOBASE_ICH0 0x58 #define GPIOCTRL_ICH0 0x5C #define GPIOBASE_ICH6 0x48 @@ -82,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 */ { @@ -141,6 +132,143 @@ 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 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_IRQ 14 + +static struct resource apl_gpio_resources[APL_GPIO_NR_DEVICES][2] = { + [APL_GPIO_NORTH] = { + DEFINE_RES_MEM(0, 0), + DEFINE_RES_IRQ(APL_GPIO_IRQ), + }, + [APL_GPIO_NORTHWEST] = { + DEFINE_RES_MEM(0, 0), + DEFINE_RES_IRQ(APL_GPIO_IRQ), + }, + [APL_GPIO_WEST] = { + DEFINE_RES_MEM(0, 0), + DEFINE_RES_IRQ(APL_GPIO_IRQ), + }, + [APL_GPIO_SOUTHWEST] = { + DEFINE_RES_MEM(0, 0), + DEFINE_RES_IRQ(APL_GPIO_IRQ), + }, +}; + +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", + .id = APL_GPIO_NORTH, + .num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_NORTH]), + .resources = apl_gpio_resources[APL_GPIO_NORTH], + .ignore_resource_conflicts = true, + }, + [APL_GPIO_NORTHWEST] = { + .name = "apollolake-pinctrl", + .id = APL_GPIO_NORTHWEST, + .num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_NORTHWEST]), + .resources = apl_gpio_resources[APL_GPIO_NORTHWEST], + .ignore_resource_conflicts = true, + }, + [APL_GPIO_WEST] = { + .name = "apollolake-pinctrl", + .id = APL_GPIO_WEST, + .num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_WEST]), + .resources = apl_gpio_resources[APL_GPIO_WEST], + .ignore_resource_conflicts = true, + }, + [APL_GPIO_SOUTHWEST] = { + .name = "apollolake-pinctrl", + .id = APL_GPIO_SOUTHWEST, + .num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_SOUTHWEST]), + .resources = apl_gpio_resources[APL_GPIO_SOUTHWEST], + .ignore_resource_conflicts = true, + }, +}; + +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", @@ -219,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", @@ -489,6 +631,7 @@ static struct lpc_ich_info lpc_chipset_info[] = { [LPC_DH89XXCC] = { .name = "DH89xxCC", .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, }, [LPC_PPT] = { .name = "Panther Point", @@ -547,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, @@ -567,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}, @@ -685,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}, @@ -888,7 +1038,7 @@ static int lpc_ich_finalize_wdt_cell(struct pci_dev *dev) info = &lpc_chipset_info[priv->chipset]; pdata->version = info->iTCO_version; - strlcpy(pdata->name, info->name, sizeof(pdata->name)); + strscpy(pdata->name, info->name, sizeof(pdata->name)); cell->platform_data = pdata; cell->pdata_size = sizeof(*pdata); @@ -1083,12 +1233,85 @@ wdt_done: return ret; } +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(info->hid, NULL, -1)) + return -EEXIST; + + ret = p2sb_bar(dev->bus, 0, &base); + if (ret) + return ret; + + 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 + INTEL_GPIO_RESOURCE_SIZE - 1; + mem->flags = base.flags; + } + + 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) +{ + u32 val; + + val = readl(base + BYT_BCR); + if (!(val & BYT_BCR_WPD)) { + val |= BYT_BCR_WPD; + writel(val, base + BYT_BCR); + val = readl(base + BYT_BCR); + } + + return val & BYT_BCR_WPD; +} + +static bool lpc_ich_set_writeable(struct pci_bus *bus, unsigned int devfn) +{ + u32 bcr; + + pci_bus_read_config_dword(bus, devfn, BCR, &bcr); + if (!(bcr & BCR_WPD)) { + bcr |= BCR_WPD; + pci_bus_write_config_dword(bus, devfn, BCR, bcr); + pci_bus_read_config_dword(bus, devfn, BCR, &bcr); + } + + return bcr & BCR_WPD; +} + +static bool lpc_ich_lpt_set_writeable(void __iomem *base, void *data) +{ + struct pci_dev *pdev = data; + + return lpc_ich_set_writeable(pdev->bus, pdev->devfn); +} + +static bool lpc_ich_bxt_set_writeable(void __iomem *base, void *data) +{ + struct pci_dev *pdev = data; + + return lpc_ich_set_writeable(pdev->bus, PCI_DEVFN(13, 2)); +} + static int lpc_ich_init_spi(struct pci_dev *dev) { struct lpc_ich_priv *priv = pci_get_drvdata(dev); struct resource *res = &intel_spi_res[0]; struct intel_spi_boardinfo *info; - u32 spi_base, rcba, bcr; + u32 spi_base, rcba; + int ret; info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -1100,8 +1323,10 @@ 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; } break; @@ -1112,35 +1337,24 @@ static int lpc_ich_init_spi(struct pci_dev *dev) res->start = spi_base + SPIBASE_LPT; res->end = res->start + SPIBASE_LPT_SZ - 1; - pci_read_config_dword(dev, BCR, &bcr); - info->writeable = !!(bcr & BCR_WPD); + info->set_writeable = lpc_ich_lpt_set_writeable; + info->data = dev; } break; - case INTEL_SPI_BXT: { - unsigned int p2sb = PCI_DEVFN(13, 0); - unsigned int spi = PCI_DEVFN(13, 2); - struct pci_bus *bus = dev->bus; - + case INTEL_SPI_BXT: /* * The P2SB is hidden by BIOS and we need to unhide it in * order to read BAR of the SPI flash device. Once that is * done we hide it again. */ - pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x0); - pci_bus_read_config_dword(bus, spi, PCI_BASE_ADDRESS_0, - &spi_base); - if (spi_base != ~0) { - res->start = spi_base & 0xfffffff0; - res->end = res->start + SPIBASE_APL_SZ - 1; - - pci_bus_read_config_dword(bus, spi, BCR, &bcr); - info->writeable = !!(bcr & BCR_WPD); - } + ret = p2sb_bar(dev->bus, PCI_DEVFN(13, 2), res); + if (ret) + return ret; - pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x1); + info->set_writeable = lpc_ich_bxt_set_writeable; + info->data = dev; break; - } default: return -EINVAL; @@ -1199,6 +1413,12 @@ static int lpc_ich_probe(struct pci_dev *dev, cell_added = true; } + if (lpc_chipset_info[priv->chipset].gpio_info) { + ret = lpc_ich_init_pinctrl(dev); + if (!ret) + cell_added = true; + } + if (lpc_chipset_info[priv->chipset].spi_type) { ret = lpc_ich_init_spi(dev); if (!ret) diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c index 428a526cbe86..9ab9adce06fd 100644 --- a/drivers/mfd/lpc_sch.c +++ b/drivers/mfd/lpc_sch.c @@ -22,7 +22,7 @@ #define SMBASE 0x40 #define SMBUS_IO_SIZE 64 -#define GPIOBASE 0x44 +#define GPIO_BASE 0x44 #define GPIO_IO_SIZE 64 #define GPIO_IO_SIZE_CENTERTON 128 @@ -145,7 +145,7 @@ static int lpc_sch_probe(struct pci_dev *dev, const struct pci_device_id *id) if (ret == 0) cells++; - ret = lpc_sch_populate_cell(dev, GPIOBASE, "sch_gpio", + ret = lpc_sch_populate_cell(dev, GPIO_BASE, "sch_gpio", info->io_size_gpio, id->device, &lpc_sch_cells[cells]); if (ret < 0) 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 a2abc0094def..2f74a8c644a3 100644 --- a/drivers/mfd/madera-core.c +++ b/drivers/mfd/madera-core.c @@ -8,13 +8,12 @@ #include <linux/device.h> #include <linux/delay.h> #include <linux/err.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/mfd/core.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/notifier.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> @@ -457,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; @@ -671,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 7df5b9ba5855..0986e4a99f4a 100644 --- a/drivers/mfd/madera-i2c.c +++ b/drivers/mfd/madera-i2c.c @@ -10,30 +10,22 @@ #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> #include "madera.h" -static int madera_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int madera_i2c_probe(struct i2c_client *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)) { @@ -112,13 +104,11 @@ static int madera_i2c_probe(struct i2c_client *i2c, return madera_dev_init(madera); } -static int madera_i2c_remove(struct i2c_client *i2c) +static void madera_i2c_remove(struct i2c_client *i2c) { struct madera *madera = dev_get_drvdata(&i2c->dev); madera_dev_exit(madera); - - return 0; } static const struct i2c_device_id madera_i2c_id[] = { diff --git a/drivers/mfd/madera-spi.c b/drivers/mfd/madera-spi.c index e860f5ff0933..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)) { @@ -112,13 +104,11 @@ static int madera_spi_probe(struct spi_device *spi) return madera_dev_init(madera); } -static int madera_spi_remove(struct spi_device *spi) +static void madera_spi_remove(struct spi_device *spi) { struct madera *madera = spi_get_drvdata(spi); madera_dev_exit(madera); - - return 0; } static const struct spi_device_id madera_spi_ids[] = { diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index be185e9d5f16..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) { @@ -209,8 +211,7 @@ static const struct regmap_irq max14577_irqs[] = { static const struct regmap_irq_chip max14577_irq_chip = { .name = "max14577", .status_base = MAX14577_REG_INT1, - .mask_base = MAX14577_REG_INTMASK1, - .mask_invert = true, + .unmask_base = MAX14577_REG_INTMASK1, .num_regs = 3, .irqs = max14577_irqs, .num_irqs = ARRAY_SIZE(max14577_irqs), @@ -239,8 +240,7 @@ static const struct regmap_irq max77836_muic_irqs[] = { static const struct regmap_irq_chip max77836_muic_irq_chip = { .name = "max77836-muic", .status_base = MAX14577_REG_INT1, - .mask_base = MAX14577_REG_INTMASK1, - .mask_invert = true, + .unmask_base = MAX14577_REG_INTMASK1, .num_regs = 3, .irqs = max77836_muic_irqs, .num_irqs = ARRAY_SIZE(max77836_muic_irqs), @@ -255,7 +255,6 @@ static const struct regmap_irq_chip max77836_pmic_irq_chip = { .name = "max77836-pmic", .status_base = MAX77836_PMIC_REG_TOPSYS_INT, .mask_base = MAX77836_PMIC_REG_TOPSYS_INT_MASK, - .mask_invert = false, .num_regs = 1, .irqs = max77836_pmic_irqs, .num_irqs = ARRAY_SIZE(max77836_pmic_irqs), @@ -332,7 +331,7 @@ static int max77836_init(struct max14577 *max14577) } ret = regmap_add_irq_chip(max14577->regmap_pmic, max14577->irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED, + IRQF_ONESHOT | IRQF_SHARED, 0, &max77836_pmic_irq_chip, &max14577->irq_data_pmic); if (ret != 0) { @@ -358,8 +357,7 @@ static void max77836_remove(struct max14577 *max14577) i2c_unregister_device(max14577->i2c_pmic); } -static int max14577_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max14577_i2c_probe(struct i2c_client *i2c) { struct max14577 *max14577; struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev); @@ -400,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); @@ -418,14 +407,14 @@ static int max14577_i2c_probe(struct i2c_client *i2c, irq_chip = &max77836_muic_irq_chip; mfd_devs = max77836_devs; mfd_devs_size = ARRAY_SIZE(max77836_devs); - irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED; + irq_flags = IRQF_ONESHOT | IRQF_SHARED; break; case MAXIM_DEVICE_TYPE_MAX14577: default: irq_chip = &max14577_irq_chip; mfd_devs = max14577_devs; mfd_devs_size = ARRAY_SIZE(max14577_devs); - irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + irq_flags = IRQF_ONESHOT; break; } @@ -463,16 +452,15 @@ err_max77836: return ret; } -static int max14577_i2c_remove(struct i2c_client *i2c) +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) max77836_remove(max14577); - - return 0; } static const struct i2c_device_id max14577_i2c_id[] = { @@ -482,7 +470,6 @@ static const struct i2c_device_id max14577_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, max14577_i2c_id); -#ifdef CONFIG_PM_SLEEP static int max14577_suspend(struct device *dev) { struct i2c_client *i2c = to_i2c_client(dev); @@ -515,14 +502,13 @@ static int max14577_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(max14577_pm, max14577_suspend, max14577_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(max14577_pm, max14577_suspend, max14577_resume); static struct i2c_driver max14577_i2c_driver = { .driver = { .name = "max14577", - .pm = &max14577_pm, + .pm = pm_sleep_ptr(&max14577_pm), .of_match_table = max14577_dt_match, }, .probe = max14577_i2c_probe, 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 new file mode 100644 index 000000000000..f91b4f5373ce --- /dev/null +++ b/drivers/mfd/max77541.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Analog Devices, Inc. + * Driver for the MAX77540 and MAX77541 + */ + +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max77541.h> +#include <linux/property.h> +#include <linux/regmap.h> + +static const struct regmap_config max77541_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static const struct regmap_irq max77541_src_irqs[] = { + { .mask = MAX77541_BIT_INT_SRC_TOPSYS }, + { .mask = MAX77541_BIT_INT_SRC_BUCK }, +}; + +static const struct regmap_irq_chip max77541_src_irq_chip = { + .name = "max77541-src", + .status_base = MAX77541_REG_INT_SRC, + .mask_base = MAX77541_REG_INT_SRC_M, + .num_regs = 1, + .irqs = max77541_src_irqs, + .num_irqs = ARRAY_SIZE(max77541_src_irqs), +}; + +static const struct regmap_irq max77541_topsys_irqs[] = { + { .mask = MAX77541_BIT_TOPSYS_INT_TJ_120C }, + { .mask = MAX77541_BIT_TOPSYS_INT_TJ_140C }, + { .mask = MAX77541_BIT_TOPSYS_INT_TSHDN }, + { .mask = MAX77541_BIT_TOPSYS_INT_UVLO }, + { .mask = MAX77541_BIT_TOPSYS_INT_ALT_SWO }, + { .mask = MAX77541_BIT_TOPSYS_INT_EXT_FREQ_DET }, +}; + +static const struct regmap_irq_chip max77541_topsys_irq_chip = { + .name = "max77541-topsys", + .status_base = MAX77541_REG_TOPSYS_INT, + .mask_base = MAX77541_REG_TOPSYS_INT_M, + .num_regs = 1, + .irqs = max77541_topsys_irqs, + .num_irqs = ARRAY_SIZE(max77541_topsys_irqs), +}; + +static const struct regmap_irq max77541_buck_irqs[] = { + { .mask = MAX77541_BIT_BUCK_INT_M1_POK_FLT }, + { .mask = MAX77541_BIT_BUCK_INT_M2_POK_FLT }, + { .mask = MAX77541_BIT_BUCK_INT_M1_SCFLT }, + { .mask = MAX77541_BIT_BUCK_INT_M2_SCFLT }, +}; + +static const struct regmap_irq_chip max77541_buck_irq_chip = { + .name = "max77541-buck", + .status_base = MAX77541_REG_BUCK_INT, + .mask_base = MAX77541_REG_BUCK_INT_M, + .num_regs = 1, + .irqs = max77541_buck_irqs, + .num_irqs = ARRAY_SIZE(max77541_buck_irqs), +}; + +static const struct regmap_irq max77541_adc_irqs[] = { + { .mask = MAX77541_BIT_ADC_INT_CH1_I }, + { .mask = MAX77541_BIT_ADC_INT_CH2_I }, + { .mask = MAX77541_BIT_ADC_INT_CH3_I }, + { .mask = MAX77541_BIT_ADC_INT_CH6_I }, +}; + +static const struct regmap_irq_chip max77541_adc_irq_chip = { + .name = "max77541-adc", + .status_base = MAX77541_REG_ADC_INT, + .mask_base = MAX77541_REG_ADC_INT_M, + .num_regs = 1, + .irqs = max77541_adc_irqs, + .num_irqs = ARRAY_SIZE(max77541_adc_irqs), +}; + +static const struct mfd_cell max77540_devs[] = { + MFD_CELL_OF("max77540-regulator", NULL, NULL, 0, 0, NULL), +}; + +static const struct mfd_cell max77541_devs[] = { + MFD_CELL_OF("max77541-regulator", NULL, NULL, 0, 0, NULL), + MFD_CELL_OF("max77541-adc", NULL, NULL, 0, 0, NULL), +}; + +static int max77541_pmic_irq_init(struct device *dev) +{ + struct max77541 *max77541 = dev_get_drvdata(dev); + int irq = max77541->i2c->irq; + int ret; + + ret = devm_regmap_add_irq_chip(dev, max77541->regmap, irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77541_src_irq_chip, + &max77541->irq_data); + if (ret) + return ret; + + ret = devm_regmap_add_irq_chip(dev, max77541->regmap, irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77541_topsys_irq_chip, + &max77541->irq_topsys); + if (ret) + return ret; + + ret = devm_regmap_add_irq_chip(dev, max77541->regmap, irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77541_buck_irq_chip, + &max77541->irq_buck); + if (ret) + return ret; + + if (max77541->id == MAX77541) { + ret = devm_regmap_add_irq_chip(dev, max77541->regmap, irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77541_adc_irq_chip, + &max77541->irq_adc); + if (ret) + return ret; + } + + return 0; +} + +static int max77541_pmic_setup(struct device *dev) +{ + struct max77541 *max77541 = dev_get_drvdata(dev); + const struct mfd_cell *cells; + int n_devs; + int ret; + + switch (max77541->id) { + case MAX77540: + cells = max77540_devs; + n_devs = ARRAY_SIZE(max77540_devs); + break; + case MAX77541: + cells = max77541_devs; + n_devs = ARRAY_SIZE(max77541_devs); + break; + default: + return -EINVAL; + } + + ret = max77541_pmic_irq_init(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize IRQ\n"); + + ret = devm_device_init_wakeup(dev); + if (ret) + return dev_err_probe(dev, ret, "Unable to init wakeup\n"); + + return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + cells, n_devs, NULL, 0, NULL); +} + +static int max77541_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct max77541 *max77541; + + max77541 = devm_kzalloc(dev, sizeof(*max77541), GFP_KERNEL); + if (!max77541) + return -ENOMEM; + + i2c_set_clientdata(client, max77541); + max77541->i2c = client; + + max77541->id = (uintptr_t)i2c_get_match_data(client); + if (!max77541->id) + return -EINVAL; + + max77541->regmap = devm_regmap_init_i2c(client, + &max77541_regmap_config); + if (IS_ERR(max77541->regmap)) + return dev_err_probe(dev, PTR_ERR(max77541->regmap), + "Failed to allocate register map\n"); + + return max77541_pmic_setup(dev); +} + +static const struct of_device_id max77541_of_id[] = { + { + .compatible = "adi,max77540", + .data = (void *)MAX77540, + }, + { + .compatible = "adi,max77541", + .data = (void *)MAX77541, + }, + { } +}; +MODULE_DEVICE_TABLE(of, max77541_of_id); + +static const struct i2c_device_id max77541_id[] = { + { "max77540", MAX77540 }, + { "max77541", MAX77541 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max77541_id); + +static struct i2c_driver max77541_driver = { + .driver = { + .name = "max77541", + .of_match_table = max77541_of_id, + }, + .probe = max77541_probe, + .id_table = max77541_id, +}; +module_i2c_driver(max77541_driver); + +MODULE_DESCRIPTION("MAX7740/MAX7741 Driver"); +MODULE_AUTHOR("Okan Sahin <okan.sahin@analog.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/max77620.c b/drivers/mfd/max77620.c index fec2096474ad..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,13 +415,14 @@ 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; } } + of_node_put(fps_np); config = chip->enable_global_lpm ? MAX77620_ONOFFCNFG2_SLP_LPM_MSK : 0; ret = regmap_update_bits(chip->rmap, MAX77620_REG_ONOFFCNFG2, @@ -492,11 +493,12 @@ static void max77620_pm_power_off(void) MAX77620_ONOFFCNFG1_SFT_RST); } -static int max77620_probe(struct i2c_client *client, - const struct i2c_device_id *id) +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; @@ -507,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; @@ -543,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; @@ -574,7 +582,6 @@ static int max77620_probe(struct i2c_client *client, return 0; } -#ifdef CONFIG_PM_SLEEP static int max77620_set_fps_period(struct max77620_chip *chip, int fps_id, int time_period) { @@ -681,7 +688,6 @@ out: return 0; } -#endif static const struct i2c_device_id max77620_id[] = { {"max77620", MAX77620}, @@ -690,16 +696,19 @@ static const struct i2c_device_id max77620_id[] = { {}, }; -static const struct dev_pm_ops max77620_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(max77620_i2c_suspend, max77620_i2c_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(max77620_pm_ops, + max77620_i2c_suspend, max77620_i2c_resume); static struct i2c_driver max77620_driver = { .driver = { .name = "max77620", - .pm = &max77620_pm_ops, + .pm = pm_sleep_ptr(&max77620_pm_ops), }, .probe = max77620_probe, .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/max77650.c b/drivers/mfd/max77650.c index 777485a33bc0..75b5dc44e38c 100644 --- a/drivers/mfd/max77650.c +++ b/drivers/mfd/max77650.c @@ -138,7 +138,6 @@ static const struct regmap_irq_chip max77650_irq_chip = { .status_base = MAX77650_REG_INT_GLBL, .mask_base = MAX77650_REG_INTM_GLBL, .type_in_mask = true, - .type_invert = true, .init_ack_masked = true, .clear_on_unmask = true, }; @@ -223,7 +222,7 @@ static struct i2c_driver max77650_i2c_driver = { .name = "max77650", .of_match_table = max77650_of_match, }, - .probe_new = max77650_i2c_probe, + .probe = max77650_i2c_probe, }; module_i2c_driver(max77650_i2c_driver); diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 2ad554b921d9..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", }, @@ -87,7 +86,7 @@ static bool max77802_rtc_is_volatile_reg(struct device *dev, unsigned int reg) reg == MAX77802_RTC_WEEKDAY || reg == MAX77802_RTC_MONTH || reg == MAX77802_RTC_YEAR || - reg == MAX77802_RTC_DATE); + reg == MAX77802_RTC_MONTHDAY); } static bool max77802_is_volatile_reg(struct device *dev, unsigned int reg) @@ -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[] = { @@ -209,8 +208,7 @@ static int max77686_i2c_probe(struct i2c_client *i2c) ret = devm_regmap_add_irq_chip(&i2c->dev, max77686->regmap, max77686->irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT | - IRQF_SHARED, 0, irq_chip, + IRQF_ONESHOT | IRQF_SHARED, 0, irq_chip, &max77686->irq_data); if (ret < 0) { dev_err(&i2c->dev, "failed to add PMIC irq chip: %d\n", ret); @@ -227,7 +225,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c) return 0; } -#ifdef CONFIG_PM_SLEEP static int max77686_suspend(struct device *dev) { struct i2c_client *i2c = to_i2c_client(dev); @@ -262,17 +259,16 @@ static int max77686_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(max77686_pm, max77686_suspend, max77686_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(max77686_pm, max77686_suspend, max77686_resume); static struct i2c_driver max77686_i2c_driver = { .driver = { .name = "max77686", - .pm = &max77686_pm, + .pm = pm_sleep_ptr(&max77686_pm), .of_match_table = max77686_pmic_dt_match, }, - .probe_new = max77686_i2c_probe, + .probe = max77686_i2c_probe, }; module_i2c_driver(max77686_i2c_driver); diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c index 596ed85cab3b..1c485a4c3dcf 100644 --- a/drivers/mfd/max77693.c +++ b/drivers/mfd/max77693.c @@ -66,7 +66,6 @@ static const struct regmap_irq_chip max77693_led_irq_chip = { .name = "max77693-led", .status_base = MAX77693_LED_REG_FLASH_INT, .mask_base = MAX77693_LED_REG_FLASH_INT_MASK, - .mask_invert = false, .num_regs = 1, .irqs = max77693_led_irqs, .num_irqs = ARRAY_SIZE(max77693_led_irqs), @@ -82,7 +81,6 @@ static const struct regmap_irq_chip max77693_topsys_irq_chip = { .name = "max77693-topsys", .status_base = MAX77693_PMIC_REG_TOPSYS_INT, .mask_base = MAX77693_PMIC_REG_TOPSYS_INT_MASK, - .mask_invert = false, .num_regs = 1, .irqs = max77693_topsys_irqs, .num_irqs = ARRAY_SIZE(max77693_topsys_irqs), @@ -100,7 +98,6 @@ static const struct regmap_irq_chip max77693_charger_irq_chip = { .name = "max77693-charger", .status_base = MAX77693_CHG_REG_CHG_INT, .mask_base = MAX77693_CHG_REG_CHG_INT_MASK, - .mask_invert = false, .num_regs = 1, .irqs = max77693_charger_irqs, .num_irqs = ARRAY_SIZE(max77693_charger_irqs), @@ -136,8 +133,7 @@ static const struct regmap_irq max77693_muic_irqs[] = { static const struct regmap_irq_chip max77693_muic_irq_chip = { .name = "max77693-muic", .status_base = MAX77693_MUIC_REG_INT1, - .mask_base = MAX77693_MUIC_REG_INTMASK1, - .mask_invert = true, + .unmask_base = MAX77693_MUIC_REG_INTMASK1, .num_regs = 3, .irqs = max77693_muic_irqs, .num_irqs = ARRAY_SIZE(max77693_muic_irqs), @@ -149,9 +145,9 @@ static const struct regmap_config max77693_regmap_haptic_config = { .max_register = MAX77693_HAPTIC_REG_END, }; -static int max77693_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max77693_i2c_probe(struct i2c_client *i2c) { + const struct i2c_device_id *id = i2c_client_get_device_id(i2c); struct max77693_dev *max77693; unsigned int reg_data; int ret = 0; @@ -222,8 +218,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, } ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, - IRQF_ONESHOT | IRQF_SHARED | - IRQF_TRIGGER_FALLING, 0, + IRQF_ONESHOT | IRQF_SHARED, 0, &max77693_led_irq_chip, &max77693->irq_data_led); if (ret) { @@ -232,8 +227,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, } ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, - IRQF_ONESHOT | IRQF_SHARED | - IRQF_TRIGGER_FALLING, 0, + IRQF_ONESHOT | IRQF_SHARED, 0, &max77693_topsys_irq_chip, &max77693->irq_data_topsys); if (ret) { @@ -242,8 +236,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, } ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, - IRQF_ONESHOT | IRQF_SHARED | - IRQF_TRIGGER_FALLING, 0, + IRQF_ONESHOT | IRQF_SHARED, 0, &max77693_charger_irq_chip, &max77693->irq_data_chg); if (ret) { @@ -252,8 +245,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, } ret = regmap_add_irq_chip(max77693->regmap_muic, max77693->irq, - IRQF_ONESHOT | IRQF_SHARED | - IRQF_TRIGGER_FALLING, 0, + IRQF_ONESHOT | IRQF_SHARED, 0, &max77693_muic_irq_chip, &max77693->irq_data_muic); if (ret) { @@ -298,7 +290,7 @@ err_i2c_haptic: return ret; } -static int max77693_i2c_remove(struct i2c_client *i2c) +static void max77693_i2c_remove(struct i2c_client *i2c) { struct max77693_dev *max77693 = i2c_get_clientdata(i2c); @@ -311,8 +303,6 @@ static int max77693_i2c_remove(struct i2c_client *i2c) i2c_unregister_device(max77693->i2c_muic); i2c_unregister_device(max77693->i2c_haptic); - - return 0; } static const struct i2c_device_id max77693_i2c_id[] = { 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/max77714.c b/drivers/mfd/max77714.c new file mode 100644 index 000000000000..c618605a3fb2 --- /dev/null +++ b/drivers/mfd/max77714.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Maxim MAX77714 Core Driver + * + * Copyright (C) 2022 Luca Ceresoli + * Author: Luca Ceresoli <luca.ceresoli@bootlin.com> + */ + +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max77714.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> + +static const struct mfd_cell max77714_cells[] = { + { .name = "max77714-watchdog" }, + { .name = "max77714-rtc" }, +}; + +static const struct regmap_range max77714_readable_ranges[] = { + regmap_reg_range(MAX77714_INT_TOP, MAX77714_INT_TOP), + regmap_reg_range(MAX77714_INT_TOPM, MAX77714_INT_TOPM), + regmap_reg_range(MAX77714_32K_STATUS, MAX77714_32K_CONFIG), + regmap_reg_range(MAX77714_CNFG_GLBL2, MAX77714_CNFG2_ONOFF), +}; + +static const struct regmap_range max77714_writable_ranges[] = { + regmap_reg_range(MAX77714_INT_TOPM, MAX77714_INT_TOPM), + regmap_reg_range(MAX77714_32K_CONFIG, MAX77714_32K_CONFIG), + regmap_reg_range(MAX77714_CNFG_GLBL2, MAX77714_CNFG2_ONOFF), +}; + +static const struct regmap_access_table max77714_readable_table = { + .yes_ranges = max77714_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(max77714_readable_ranges), +}; + +static const struct regmap_access_table max77714_writable_table = { + .yes_ranges = max77714_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(max77714_writable_ranges), +}; + +static const struct regmap_config max77714_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77714_CNFG2_ONOFF, + .rd_table = &max77714_readable_table, + .wr_table = &max77714_writable_table, +}; + +static const struct regmap_irq max77714_top_irqs[] = { + REGMAP_IRQ_REG(MAX77714_IRQ_TOP_ONOFF, 0, MAX77714_INT_TOP_ONOFF), + REGMAP_IRQ_REG(MAX77714_IRQ_TOP_RTC, 0, MAX77714_INT_TOP_RTC), + REGMAP_IRQ_REG(MAX77714_IRQ_TOP_GPIO, 0, MAX77714_INT_TOP_GPIO), + REGMAP_IRQ_REG(MAX77714_IRQ_TOP_LDO, 0, MAX77714_INT_TOP_LDO), + REGMAP_IRQ_REG(MAX77714_IRQ_TOP_SD, 0, MAX77714_INT_TOP_SD), + REGMAP_IRQ_REG(MAX77714_IRQ_TOP_GLBL, 0, MAX77714_INT_TOP_GLBL), +}; + +static const struct regmap_irq_chip max77714_irq_chip = { + .name = "max77714-pmic", + .status_base = MAX77714_INT_TOP, + .mask_base = MAX77714_INT_TOPM, + .num_regs = 1, + .irqs = max77714_top_irqs, + .num_irqs = ARRAY_SIZE(max77714_top_irqs), +}; + +/* + * MAX77714 initially uses the internal, low precision oscillator. Enable + * the external oscillator by setting the XOSC_RETRY bit. If the external + * oscillator is not OK (probably not installed) this has no effect. + */ +static int max77714_setup_xosc(struct device *dev, struct regmap *regmap) +{ + /* Internal Crystal Load Capacitance, indexed by value of 32KLOAD bits */ + static const unsigned int load_cap[4] = {0, 10, 12, 22}; /* pF */ + unsigned int load_cap_idx; + unsigned int status; + int err; + + err = regmap_update_bits(regmap, MAX77714_32K_CONFIG, + MAX77714_32K_CONFIG_XOSC_RETRY, + MAX77714_32K_CONFIG_XOSC_RETRY); + if (err) + return dev_err_probe(dev, err, "Failed to configure the external oscillator\n"); + + err = regmap_read(regmap, MAX77714_32K_STATUS, &status); + if (err) + return dev_err_probe(dev, err, "Failed to read external oscillator status\n"); + + load_cap_idx = (status >> MAX77714_32K_STATUS_32KLOAD_SHF) + & MAX77714_32K_STATUS_32KLOAD_MSK; + + dev_info(dev, "Using %s oscillator, %d pF load cap\n", + status & MAX77714_32K_STATUS_32KSOURCE ? "internal" : "external", + load_cap[load_cap_idx]); + + return 0; +} + +static int max77714_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct regmap *regmap; + struct regmap_irq_chip_data *irq_data; + int err; + + regmap = devm_regmap_init_i2c(client, &max77714_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to initialise regmap\n"); + + err = max77714_setup_xosc(dev, regmap); + if (err) + return err; + + err = devm_regmap_add_irq_chip(dev, regmap, client->irq, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77714_irq_chip, &irq_data); + if (err) + return dev_err_probe(dev, err, "Failed to add PMIC IRQ chip\n"); + + err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + max77714_cells, ARRAY_SIZE(max77714_cells), + NULL, 0, NULL); + if (err) + return dev_err_probe(dev, err, "Failed to register child devices\n"); + + return 0; +} + +static const struct of_device_id max77714_dt_match[] = { + { .compatible = "maxim,max77714" }, + {}, +}; +MODULE_DEVICE_TABLE(of, max77714_dt_match); + +static struct i2c_driver max77714_driver = { + .driver = { + .name = "max77714", + .of_match_table = max77714_dt_match, + }, + .probe = max77714_probe, +}; +module_i2c_driver(max77714_driver); + +MODULE_DESCRIPTION("Maxim MAX77714 MFD core driver"); +MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.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 209ee24d9ce1..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[] = { @@ -59,7 +59,6 @@ static const struct regmap_irq_chip max77843_irq_chip = { .name = "max77843", .status_base = MAX77843_SYS_REG_SYSINTSRC, .mask_base = MAX77843_SYS_REG_SYSINTMASK, - .mask_invert = false, .num_regs = 1, .irqs = max77843_irqs, .num_irqs = ARRAY_SIZE(max77843_irqs), @@ -93,9 +92,9 @@ err_chg_i2c: return ret; } -static int max77843_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max77843_probe(struct i2c_client *i2c) { + const struct i2c_device_id *id = i2c_client_get_device_id(i2c); struct max77693_dev *max77843; unsigned int reg_data; int ret; diff --git a/drivers/mfd/max8907.c b/drivers/mfd/max8907.c index 41f566e6a096..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[] = { @@ -181,8 +180,7 @@ static void max8907_power_off(void) MAX8907_MASK_POWER_OFF, MAX8907_MASK_POWER_OFF); } -static int max8907_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max8907_i2c_probe(struct i2c_client *i2c) { struct max8907 *max8907; int ret; @@ -202,8 +200,6 @@ static int max8907_i2c_probe(struct i2c_client *i2c, } max8907->dev = &i2c->dev; - dev_set_drvdata(max8907->dev, max8907); - max8907->i2c_gen = i2c; i2c_set_clientdata(i2c, max8907); max8907->regmap_gen = devm_regmap_init_i2c(i2c, @@ -282,7 +278,7 @@ err_alloc_drvdata: return ret; } -static int max8907_i2c_remove(struct i2c_client *i2c) +static void max8907_i2c_remove(struct i2c_client *i2c) { struct max8907 *max8907 = i2c_get_clientdata(i2c); @@ -293,8 +289,6 @@ static int max8907_i2c_remove(struct i2c_client *i2c) regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_chg); i2c_unregister_device(max8907->i2c_rtc); - - return 0; } #ifdef CONFIG_OF @@ -306,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 eb3f061c8ee6..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, }, @@ -469,12 +468,6 @@ static struct max8925_irq_data max8925_irqs[] = { }, }; -static inline struct max8925_irq_data *irq_to_max8925(struct max8925_chip *chip, - int irq) -{ - return &max8925_irqs[irq - chip->irq_base]; -} - static irqreturn_t max8925_irq(int irq, void *data) { struct max8925_chip *chip = data; @@ -663,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); @@ -689,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 114e905bef25..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, @@ -144,8 +144,7 @@ static int max8925_dt_init(struct device_node *np, struct device *dev, return 0; } -static int max8925_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max8925_probe(struct i2c_client *client) { struct max8925_platform_data *pdata = dev_get_platdata(&client->dev); struct max8925_chip *chip; @@ -173,7 +172,6 @@ static int max8925_probe(struct i2c_client *client, chip->i2c = client; chip->dev = &client->dev; i2c_set_clientdata(client, chip); - dev_set_drvdata(chip->dev, chip); mutex_init(&chip->io_lock); chip->rtc = i2c_new_dummy_device(chip->i2c->adapter, RTC_I2C_ADDR); @@ -198,17 +196,16 @@ static int max8925_probe(struct i2c_client *client, return 0; } -static int max8925_remove(struct i2c_client *client) +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); - return 0; } -#ifdef CONFIG_PM_SLEEP static int max8925_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -228,9 +225,9 @@ static int max8925_resume(struct device *dev) disable_irq_wake(chip->core_irq); return 0; } -#endif -static SIMPLE_DEV_PM_OPS(max8925_pm_ops, max8925_suspend, max8925_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(max8925_pm_ops, + max8925_suspend, max8925_resume); static const struct of_device_id max8925_dt_ids[] = { { .compatible = "maxim,max8925", }, @@ -240,7 +237,7 @@ static const struct of_device_id max8925_dt_ids[] = { static struct i2c_driver max8925_driver = { .driver = { .name = "max8925", - .pm = &max8925_pm_ops, + .pm = pm_sleep_ptr(&max8925_pm_ops), .of_match_table = max8925_dt_ids, }, .probe = max8925_probe, 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 2141de78115d..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,17 +142,7 @@ 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) +static int max8997_i2c_probe(struct i2c_client *i2c) { struct max8997_dev *max8997; struct max8997_platform_data *pdata = dev_get_platdata(&i2c->dev); @@ -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 0eb15e611b67..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,17 +152,7 @@ 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) +static int max8998_i2c_probe(struct i2c_client *i2c) { struct max8998_platform_data *pdata = dev_get_platdata(&i2c->dev); struct max8998_dev *max8998; @@ -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 1abe7432aad8..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 */ @@ -323,8 +323,10 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, adc1 |= MC13783_ADC1_ATOX; dev_dbg(mc13xxx->dev, "%s: request irq\n", __func__); - mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE, + ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE, mc13xxx_handler_adcdone, __func__, &adcdone_data); + if (ret) + goto out; mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, adc0); mc13xxx_reg_write(mc13xxx, MC13XXX_ADC1, adc1); @@ -496,15 +498,13 @@ int mc13xxx_common_init(struct device *dev) } EXPORT_SYMBOL_GPL(mc13xxx_common_init); -int mc13xxx_common_exit(struct device *dev) +void mc13xxx_common_exit(struct device *dev) { struct mc13xxx *mc13xxx = dev_get_drvdata(dev); mfd_remove_devices(dev); regmap_del_irq_chip(mc13xxx->irq, mc13xxx->irq_data); mutex_destroy(&mc13xxx->lock); - - return 0; } EXPORT_SYMBOL_GPL(mc13xxx_common_exit); diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c index 65b4dd8e5afb..6bc0e755ba34 100644 --- a/drivers/mfd/mc13xxx-i2c.c +++ b/drivers/mfd/mc13xxx-i2c.c @@ -11,7 +11,6 @@ #include <linux/mfd/mc13xxx.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> #include <linux/i2c.h> #include <linux/err.h> @@ -52,8 +51,7 @@ static const struct regmap_config mc13xxx_regmap_i2c_config = { .cache_type = REGCACHE_NONE, }; -static int mc13xxx_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mc13xxx_i2c_probe(struct i2c_client *client) { struct mc13xxx *mc13xxx; int ret; @@ -74,20 +72,14 @@ 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); } -static int mc13xxx_i2c_remove(struct i2c_client *client) +static void mc13xxx_i2c_remove(struct i2c_client *client) { - return mc13xxx_common_exit(&client->dev); + mc13xxx_common_exit(&client->dev); } static struct i2c_driver mc13xxx_i2c_driver = { diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c index 286ddcf5ddc6..9f438d5d4326 100644 --- a/drivers/mfd/mc13xxx-spi.c +++ b/drivers/mfd/mc13xxx-spi.c @@ -8,14 +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/of_gpio.h> #include <linux/err.h> #include <linux/spi/spi.h> @@ -115,10 +113,10 @@ static int mc13xxx_spi_write(void *context, const void *data, size_t count) * result, the SS will negate before all of the data has been * transferred to/from the peripheral." * We workaround this by accessing the SPI controller with a - * single transfert. + * 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, }; @@ -152,23 +150,14 @@ 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); } -static int mc13xxx_spi_remove(struct spi_device *spi) +static void mc13xxx_spi_remove(struct spi_device *spi) { - return mc13xxx_common_exit(&spi->dev); + mc13xxx_common_exit(&spi->dev); } static struct spi_driver mc13xxx_spi_driver = { diff --git a/drivers/mfd/mc13xxx.h b/drivers/mfd/mc13xxx.h index ce6eec52e8eb..bd5ba9a0e14f 100644 --- a/drivers/mfd/mc13xxx.h +++ b/drivers/mfd/mc13xxx.h @@ -44,6 +44,6 @@ struct mc13xxx { }; int mc13xxx_common_init(struct device *dev); -int mc13xxx_common_exit(struct device *dev); +void mc13xxx_common_exit(struct device *dev); #endif /* __DRIVERS_MFD_MC13XXX_H */ diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index eff9423e90f5..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; } @@ -33,16 +33,15 @@ static int mcp_bus_probe(struct device *dev) return drv->probe(mcp); } -static int mcp_bus_remove(struct device *dev) +static void mcp_bus_remove(struct device *dev) { struct mcp *mcp = to_mcp(dev); struct mcp_driver *drv = to_mcp_driver(dev->driver); drv->remove(mcp); - return 0; } -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 4629dff187cd..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,11 +251,8 @@ 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; } -#ifdef CONFIG_PM_SLEEP static int mcp_sa11x0_suspend(struct device *dev) { struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev)); @@ -277,17 +274,14 @@ static int mcp_sa11x0_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops mcp_sa11x0_pm_ops = { -#ifdef CONFIG_PM_SLEEP .suspend = mcp_sa11x0_suspend, .freeze = mcp_sa11x0_suspend, .poweroff = mcp_sa11x0_suspend, .resume_noirq = mcp_sa11x0_resume, .thaw_noirq = mcp_sa11x0_resume, .restore_noirq = mcp_sa11x0_resume, -#endif }; static struct platform_driver mcp_sa11x0_driver = { @@ -295,7 +289,7 @@ static struct platform_driver mcp_sa11x0_driver = { .remove = mcp_sa11x0_remove, .driver = { .name = DRIVER_NAME, - .pm = &mcp_sa11x0_pm_ops, + .pm = pm_sleep_ptr(&mcp_sa11x0_pm_ops), }, }; diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c index 07e0ca2e467c..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> @@ -1142,8 +1141,7 @@ static inline void menelaus_rtc_init(struct menelaus_chip *m) static struct i2c_driver menelaus_i2c_driver; -static int menelaus_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int menelaus_probe(struct i2c_client *client) { struct menelaus_chip *menelaus; int rev = 0; @@ -1222,18 +1220,17 @@ fail: return err; } -static int menelaus_remove(struct i2c_client *client) +static void menelaus_remove(struct i2c_client *client) { struct menelaus_chip *menelaus = i2c_get_clientdata(client); free_irq(client->irq, menelaus); flush_work(&menelaus->work); the_menelaus = NULL; - return 0; } static const struct i2c_device_id menelaus_id[] = { - { "menelaus", 0 }, + { "menelaus" }, { } }; MODULE_DEVICE_TABLE(i2c, menelaus_id); diff --git a/drivers/mfd/menf21bmc.c b/drivers/mfd/menf21bmc.c index 8f72b1cccbe5..1d36095155e0 100644 --- a/drivers/mfd/menf21bmc.c +++ b/drivers/mfd/menf21bmc.c @@ -49,7 +49,7 @@ static int menf21bmc_wdt_exit_prod_mode(struct i2c_client *client) } static int -menf21bmc_probe(struct i2c_client *client, const struct i2c_device_id *ids) +menf21bmc_probe(struct i2c_client *client) { int rev_major, rev_minor, rev_main; int ret; diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 79f5c6a18815..7d14a1e7631e 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -29,43 +29,34 @@ 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", }; -int mfd_cell_enable(struct platform_device *pdev) -{ - const struct mfd_cell *cell = mfd_get_cell(pdev); - - if (!cell->enable) { - dev_dbg(&pdev->dev, "No .enable() call-back registered\n"); - return 0; - } - - return cell->enable(pdev); -} -EXPORT_SYMBOL(mfd_cell_enable); +#if IS_ENABLED(CONFIG_ACPI) +struct match_ids_walk_data { + struct acpi_device_id *ids; + struct acpi_device *adev; +}; -int mfd_cell_disable(struct platform_device *pdev) +static int match_device_ids(struct acpi_device *adev, void *data) { - const struct mfd_cell *cell = mfd_get_cell(pdev); + struct match_ids_walk_data *wd = data; - if (!cell->disable) { - dev_dbg(&pdev->dev, "No .disable() call-back registered\n"); - return 0; + if (!acpi_match_device_ids(adev, wd->ids)) { + wd->adev = adev; + return 1; } - return cell->disable(pdev); + return 0; } -EXPORT_SYMBOL(mfd_cell_disable); -#if IS_ENABLED(CONFIG_ACPI) static void mfd_acpi_add_device(const struct mfd_cell *cell, struct platform_device *pdev) { const struct mfd_cell_acpi_match *match = cell->acpi_match; - struct acpi_device *parent, *child; struct acpi_device *adev = NULL; + struct acpi_device *parent; parent = ACPI_COMPANION(pdev->dev.parent); if (!parent) @@ -83,20 +74,20 @@ static void mfd_acpi_add_device(const struct mfd_cell *cell, if (match) { if (match->pnpid) { struct acpi_device_id ids[2] = {}; - - strlcpy(ids[0].id, match->pnpid, sizeof(ids[0].id)); - list_for_each_entry(child, &parent->children, node) { - if (!acpi_match_device_ids(child, ids)) { - adev = child; - break; - } - } + struct match_ids_walk_data wd = { + .adev = NULL, + .ids = ids, + }; + + strscpy(ids[0].id, match->pnpid, sizeof(ids[0].id)); + acpi_dev_for_each_child(parent, match_device_ids, &wd); + adev = wd.adev; } else { adev = acpi_find_child_device(parent, match->adr, false); } } - 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, @@ -111,7 +102,6 @@ static int mfd_match_of_node_to_dev(struct platform_device *pdev, { #if IS_ENABLED(CONFIG_OF) struct mfd_of_node_entry *of_entry; - const __be32 *reg; u64 of_node_addr; /* Skip if OF node has previously been allocated to a device */ @@ -124,13 +114,10 @@ static int mfd_match_of_node_to_dev(struct platform_device *pdev, goto allocate_of_node; /* We only care about each node's first defined address */ - reg = of_get_address(np, 0, NULL, NULL); - if (!reg) + if (of_property_read_reg(np, 0, &of_node_addr, NULL)) /* OF node does not contatin a 'reg' property to match to */ return -EAGAIN; - of_node_addr = of_read_number(reg, of_n_addr_cells(np)); - if (cell->of_reg != of_node_addr) /* No match */ return -EAGAIN; @@ -144,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; } @@ -159,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; @@ -196,22 +184,30 @@ 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)) { - ret = 0; - goto fail_alias; + disabled = true; + continue; } ret = mfd_match_of_node_to_dev(pdev, np, cell); if (ret == -EAGAIN) continue; + of_node_put(np); 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); @@ -349,6 +345,7 @@ static int mfd_remove_devices_fn(struct device *dev, void *data) { struct platform_device *pdev; const struct mfd_cell *cell; + struct mfd_of_node_entry *of_entry, *tmp; int *level = data; if (dev->type != &mfd_dev_type) @@ -363,6 +360,12 @@ static int mfd_remove_devices_fn(struct device *dev, void *data) if (cell->swnode) device_remove_software_node(&pdev->dev); + list_for_each_entry_safe(of_entry, tmp, &mfd_of_node_list, list) + if (of_entry->dev == &pdev->dev) { + list_del(&of_entry->list); + kfree(of_entry); + } + regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies, cell->num_parent_supplies); @@ -434,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 6fb206da2729..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> @@ -202,6 +202,13 @@ static const struct of_device_id cpcap_of_match[] = { }; MODULE_DEVICE_TABLE(of, cpcap_of_match); +static const struct spi_device_id cpcap_spi_ids[] = { + { .name = "cpcap", }, + { .name = "6556002", }, + {}, +}; +MODULE_DEVICE_TABLE(spi, cpcap_spi_ids); + static const struct regmap_config cpcap_regmap_config = { .reg_bits = 16, .reg_stride = 4, @@ -214,7 +221,6 @@ static const struct regmap_config cpcap_regmap_config = { .val_format_endian = REGMAP_ENDIAN_LITTLE, }; -#ifdef CONFIG_PM_SLEEP static int cpcap_suspend(struct device *dev) { struct spi_device *spi = to_spi_device(dev); @@ -232,9 +238,8 @@ static int cpcap_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(cpcap_pm, cpcap_suspend, cpcap_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(cpcap_pm, cpcap_suspend, cpcap_resume); static const struct mfd_cell cpcap_mfd_devices[] = { { @@ -285,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(of_match_ptr(cpcap_of_match), &spi->dev); - if (!match) - return -ENODEV; - cpcap = devm_kzalloc(&spi->dev, sizeof(*cpcap), GFP_KERNEL); if (!cpcap) return -ENOMEM; @@ -339,9 +339,10 @@ static struct spi_driver cpcap_driver = { .driver = { .name = "cpcap-core", .of_match_table = cpcap_of_match, - .pm = &cpcap_pm, + .pm = pm_sleep_ptr(&cpcap_pm), }, .probe = cpcap_probe, + .id_table = cpcap_spi_ids, }; module_spi_driver(cpcap_driver); diff --git a/drivers/mfd/mp2629.c b/drivers/mfd/mp2629.c index 16840ec5fd1c..717b704c3c6b 100644 --- a/drivers/mfd/mp2629.c +++ b/drivers/mfd/mp2629.c @@ -70,7 +70,7 @@ static struct i2c_driver mp2629_driver = { .name = "mp2629", .of_match_table = mp2629_of_match, }, - .probe_new = mp2629_probe, + .probe = mp2629_probe, }; module_i2c_driver(mp2629_driver); diff --git a/drivers/mfd/mt6358-irq.c b/drivers/mfd/mt6358-irq.c index 83f3ffbdbb4c..74cf20843044 100644 --- a/drivers/mfd/mt6358-irq.c +++ b/drivers/mfd/mt6358-irq.c @@ -3,20 +3,32 @@ // 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> #include <linux/mfd/mt6358/registers.h> #include <linux/mfd/mt6359/core.h> #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> #define MTK_PMIC_REG_WIDTH 16 +static const struct irq_top_t mt6357_ints[] = { + MT6357_TOP_GEN(BUCK), + MT6357_TOP_GEN(LDO), + MT6357_TOP_GEN(PSC), + MT6357_TOP_GEN(SCK), + MT6357_TOP_GEN(BM), + MT6357_TOP_GEN(HK), + MT6357_TOP_GEN(AUD), + MT6357_TOP_GEN(MISC), +}; + static const struct irq_top_t mt6358_ints[] = { MT6358_TOP_GEN(BUCK), MT6358_TOP_GEN(LDO), @@ -39,6 +51,13 @@ static const struct irq_top_t mt6359_ints[] = { MT6359_TOP_GEN(MISC), }; +static struct pmic_irq_data mt6357_irqd = { + .num_top = ARRAY_SIZE(mt6357_ints), + .num_pmic_irqs = MT6357_IRQ_NR, + .top_int_status_reg = MT6357_TOP_INT_STATUS0, + .pmic_ints = mt6357_ints, +}; + static struct pmic_irq_data mt6358_irqd = { .num_top = ARRAY_SIZE(mt6358_ints), .num_pmic_irqs = MT6358_IRQ_NR, @@ -211,7 +230,12 @@ int mt6358_irq_init(struct mt6397_chip *chip) struct pmic_irq_data *irqd; switch (chip->chip_id) { + case MT6357_CHIP_ID: + chip->irq_data = &mt6357_irqd; + break; + case MT6358_CHIP_ID: + case MT6366_CHIP_ID: chip->irq_data = &mt6358_irqd; break; @@ -248,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; @@ -262,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 e628953548ce..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> @@ -319,18 +320,18 @@ static const struct resource mt6360_regulator_resources[] = { DEFINE_RES_IRQ_NAMED(MT6360_BUCK2_OC_EVT, "buck2_oc_evt"), DEFINE_RES_IRQ_NAMED(MT6360_BUCK2_OV_EVT, "buck2_ov_evt"), DEFINE_RES_IRQ_NAMED(MT6360_BUCK2_UV_EVT, "buck2_uv_evt"), - DEFINE_RES_IRQ_NAMED(MT6360_LDO6_OC_EVT, "ldo6_oc_evt"), - DEFINE_RES_IRQ_NAMED(MT6360_LDO7_OC_EVT, "ldo7_oc_evt"), - DEFINE_RES_IRQ_NAMED(MT6360_LDO6_PGB_EVT, "ldo6_pgb_evt"), - DEFINE_RES_IRQ_NAMED(MT6360_LDO7_PGB_EVT, "ldo7_pgb_evt"), DEFINE_RES_IRQ_NAMED(MT6360_LDO1_OC_EVT, "ldo1_oc_evt"), DEFINE_RES_IRQ_NAMED(MT6360_LDO2_OC_EVT, "ldo2_oc_evt"), DEFINE_RES_IRQ_NAMED(MT6360_LDO3_OC_EVT, "ldo3_oc_evt"), DEFINE_RES_IRQ_NAMED(MT6360_LDO5_OC_EVT, "ldo5_oc_evt"), + DEFINE_RES_IRQ_NAMED(MT6360_LDO6_OC_EVT, "ldo6_oc_evt"), + DEFINE_RES_IRQ_NAMED(MT6360_LDO7_OC_EVT, "ldo7_oc_evt"), DEFINE_RES_IRQ_NAMED(MT6360_LDO1_PGB_EVT, "ldo1_pgb_evt"), DEFINE_RES_IRQ_NAMED(MT6360_LDO2_PGB_EVT, "ldo2_pgb_evt"), DEFINE_RES_IRQ_NAMED(MT6360_LDO3_PGB_EVT, "ldo3_pgb_evt"), DEFINE_RES_IRQ_NAMED(MT6360_LDO5_PGB_EVT, "ldo5_pgb_evt"), + DEFINE_RES_IRQ_NAMED(MT6360_LDO6_PGB_EVT, "ldo6_pgb_evt"), + DEFINE_RES_IRQ_NAMED(MT6360_LDO7_PGB_EVT, "ldo7_pgb_evt"), }; static const struct mfd_cell mt6360_devs[] = { @@ -402,14 +403,18 @@ static int mt6360_regmap_read(void *context, const void *reg, size_t reg_size, struct mt6360_ddata *ddata = context; u8 bank = *(u8 *)reg; u8 reg_addr = *(u8 *)(reg + 1); - struct i2c_client *i2c = ddata->i2c[bank]; + 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; int ret; + if (bank >= MT6360_SLAVE_MAX) + return -EINVAL; + + i2c = ddata->i2c[bank]; + if (bank == MT6360_SLAVE_PMIC || bank == MT6360_SLAVE_LDO) { crc_needed = true; ret = mt6360_xlate_pmicldo_addr(®_addr, val_size); @@ -418,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; @@ -428,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) @@ -453,13 +453,18 @@ static int mt6360_regmap_write(void *context, const void *val, size_t val_size) struct mt6360_ddata *ddata = context; u8 bank = *(u8 *)val; u8 reg_addr = *(u8 *)(val + 1); - struct i2c_client *i2c = ddata->i2c[bank]; + struct i2c_client *i2c; bool crc_needed = false; u8 *buf; int buf_len = MT6360_ALLOC_WRITE_SIZE(val_size); int write_size = val_size - MT6360_REGMAP_REG_BYTE_SIZE; int ret; + if (bank >= MT6360_SLAVE_MAX) + return -EINVAL; + + i2c = ddata->i2c[bank]; + if (bank == MT6360_SLAVE_PMIC || bank == MT6360_SLAVE_LDO) { crc_needed = true; ret = mt6360_xlate_pmicldo_addr(®_addr, val_size - MT6360_REGMAP_REG_BYTE_SIZE); @@ -613,7 +618,7 @@ static struct i2c_driver mt6360_driver = { .pm = &mt6360_pm_ops, .of_match_table = of_match_ptr(mt6360_of_id), }, - .probe_new = mt6360_probe, + .probe = mt6360_probe, }; module_i2c_driver(mt6360_driver); diff --git a/drivers/mfd/mt6370.c b/drivers/mfd/mt6370.c new file mode 100644 index 000000000000..c7c2efe3598c --- /dev/null +++ b/drivers/mfd/mt6370.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Richtek Technology Corp. + * + * Author: ChiYuan Huang <cy_huang@richtek.com> + */ + +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "mt6370.h" + +#define MT6370_REG_DEV_INFO 0x100 +#define MT6370_REG_CHG_IRQ1 0x1C0 +#define MT6370_REG_CHG_MASK1 0x1E0 +#define MT6370_REG_MAXADDR 0x1FF + +#define MT6370_VENID_MASK GENMASK(7, 4) + +#define MT6370_NUM_IRQREGS 16 +#define MT6370_USBC_I2CADDR 0x4E +#define MT6370_MAX_ADDRLEN 2 + +#define MT6370_VENID_RT5081 0x8 +#define MT6370_VENID_RT5081A 0xA +#define MT6370_VENID_MT6370 0xE +#define MT6370_VENID_MT6371 0xF +#define MT6370_VENID_MT6372P 0x9 +#define MT6370_VENID_MT6372CP 0xB + +static const struct regmap_irq mt6370_irqs[] = { + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHGON, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_TREG, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_AICR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_MIVR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_PWR_RDY, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FL_CHG_VINOVP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VSYSUV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VSYSOV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VBATOV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VINOVPCHG, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_COLD, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_COOL, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_WARM, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_HOT, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_STATC, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_FAULT, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_STATC, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_TMR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_BATABS, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_ADPBAD, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_RVP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_TSHUTDOWN, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_IINMEAS, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_ICCMEAS, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHGDET_DONE, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_WDTMR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_SSFINISH, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_RECHG, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_TERM, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_IEOC, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_ADC_DONE, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_PUMPX_DONE, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_BST_BATUV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_BST_MIDOV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_BST_OLP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_ATTACH, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DETACH, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_HVDCP_STPDONE, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_HVDCP_VBUSDET_DONE, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_HVDCP_DET, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHGDET, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DCDT, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_VGOK, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_WDTMR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_UC, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_OC, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_OV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_SWON, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_UVP_D, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_UVP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_OVP_D, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_OVP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_STRBPIN, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_TORPIN, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_TX, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_LVF, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_SHORT, 8), + 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_FLED1_STRB_TO, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_TOR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_TOR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_OTP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_VDDA_OVP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_VDDA_UV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_LDO_OC, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_BLED_OCP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_BLED_OVP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VNEG_OCP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VPOS_OCP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_BST_OCP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VNEG_SCP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VPOS_SCP, 8), +}; + +static const struct regmap_irq_chip mt6370_irq_chip = { + .name = "mt6370-irqs", + .status_base = MT6370_REG_CHG_IRQ1, + .mask_base = MT6370_REG_CHG_MASK1, + .num_regs = MT6370_NUM_IRQREGS, + .irqs = mt6370_irqs, + .num_irqs = ARRAY_SIZE(mt6370_irqs), +}; + +static const struct resource mt6370_regulator_irqs[] = { + DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VPOS_SCP, "db_vpos_scp"), + DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VNEG_SCP, "db_vneg_scp"), + DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_BST_OCP, "db_vbst_ocp"), + DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VPOS_OCP, "db_vpos_ocp"), + DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VNEG_OCP, "db_vneg_ocp"), + DEFINE_RES_IRQ_NAMED(MT6370_IRQ_LDO_OC, "ldo_oc"), +}; + +static const struct mfd_cell mt6370_devices[] = { + MFD_CELL_OF("mt6370-adc", + NULL, NULL, 0, 0, "mediatek,mt6370-adc"), + MFD_CELL_OF("mt6370-charger", + NULL, NULL, 0, 0, "mediatek,mt6370-charger"), + MFD_CELL_OF("mt6370-flashlight", + NULL, NULL, 0, 0, "mediatek,mt6370-flashlight"), + MFD_CELL_OF("mt6370-indicator", + NULL, NULL, 0, 0, "mediatek,mt6370-indicator"), + MFD_CELL_OF("mt6370-tcpc", + NULL, NULL, 0, 0, "mediatek,mt6370-tcpc"), + MFD_CELL_RES("mt6370-regulator", mt6370_regulator_irqs), +}; + +static const struct mfd_cell mt6370_exclusive_devices[] = { + MFD_CELL_OF("mt6370-backlight", + NULL, NULL, 0, 0, "mediatek,mt6370-backlight"), +}; + +static const struct mfd_cell mt6372_exclusive_devices[] = { + MFD_CELL_OF("mt6370-backlight", + NULL, NULL, 0, 0, "mediatek,mt6372-backlight"), +}; + +static int mt6370_check_vendor_info(struct device *dev, struct regmap *rmap, + int *vid) +{ + unsigned int devinfo; + int ret; + + ret = regmap_read(rmap, MT6370_REG_DEV_INFO, &devinfo); + if (ret) + return ret; + + *vid = FIELD_GET(MT6370_VENID_MASK, devinfo); + switch (*vid) { + case MT6370_VENID_RT5081: + case MT6370_VENID_RT5081A: + case MT6370_VENID_MT6370: + case MT6370_VENID_MT6371: + case MT6370_VENID_MT6372P: + case MT6370_VENID_MT6372CP: + return 0; + default: + dev_err(dev, "Unknown Vendor ID 0x%02x\n", devinfo); + return -ENODEV; + } +} + +static int mt6370_regmap_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, size_t val_size) +{ + struct mt6370_info *info = context; + const u8 *u8_buf = reg_buf; + u8 bank_idx, bank_addr; + int ret; + + bank_idx = u8_buf[0]; + bank_addr = u8_buf[1]; + + ret = i2c_smbus_read_i2c_block_data(info->i2c[bank_idx], bank_addr, + val_size, val_buf); + if (ret < 0) + return ret; + + if (ret != val_size) + return -EIO; + + return 0; +} + +static int mt6370_regmap_write(void *context, const void *data, size_t count) +{ + struct mt6370_info *info = context; + const u8 *u8_buf = data; + u8 bank_idx, bank_addr; + int len = count - MT6370_MAX_ADDRLEN; + + bank_idx = u8_buf[0]; + bank_addr = u8_buf[1]; + + return i2c_smbus_write_i2c_block_data(info->i2c[bank_idx], bank_addr, + len, data + MT6370_MAX_ADDRLEN); +} + +static const struct regmap_bus mt6370_regmap_bus = { + .read = mt6370_regmap_read, + .write = mt6370_regmap_write, +}; + +static const struct regmap_config mt6370_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .max_register = MT6370_REG_MAXADDR, +}; + +static int mt6370_probe(struct i2c_client *i2c) +{ + struct mt6370_info *info; + struct i2c_client *usbc_i2c; + struct regmap *regmap; + struct device *dev = &i2c->dev; + int ret, vid; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + usbc_i2c = devm_i2c_new_dummy_device(dev, i2c->adapter, + MT6370_USBC_I2CADDR); + if (IS_ERR(usbc_i2c)) + return dev_err_probe(dev, PTR_ERR(usbc_i2c), + "Failed to register USBC I2C client\n"); + + /* Assign I2C client for PMU and TypeC */ + info->i2c[MT6370_PMU_I2C] = i2c; + info->i2c[MT6370_USBC_I2C] = usbc_i2c; + + regmap = devm_regmap_init(dev, &mt6370_regmap_bus, + info, &mt6370_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to init regmap\n"); + + ret = mt6370_check_vendor_info(dev, regmap, &vid); + if (ret) + return dev_err_probe(dev, ret, "Failed to check vendor info\n"); + + ret = devm_regmap_add_irq_chip(dev, regmap, i2c->irq, + IRQF_ONESHOT, -1, &mt6370_irq_chip, + &info->irq_data); + if (ret) + return dev_err_probe(dev, ret, "Failed to add irq chip\n"); + + switch (vid) { + case MT6370_VENID_MT6372P: + case MT6370_VENID_MT6372CP: + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, + mt6372_exclusive_devices, + ARRAY_SIZE(mt6372_exclusive_devices), + NULL, 0, + regmap_irq_get_domain(info->irq_data)); + break; + default: + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, + mt6370_exclusive_devices, + ARRAY_SIZE(mt6370_exclusive_devices), + NULL, 0, + regmap_irq_get_domain(info->irq_data)); + break; + } + + if (ret) + return dev_err_probe(dev, ret, "Failed to add the exclusive devices\n"); + + return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, + mt6370_devices, ARRAY_SIZE(mt6370_devices), + NULL, 0, + regmap_irq_get_domain(info->irq_data)); +} + +static const struct of_device_id mt6370_match_table[] = { + { .compatible = "mediatek,mt6370" }, + {} +}; +MODULE_DEVICE_TABLE(of, mt6370_match_table); + +static struct i2c_driver mt6370_driver = { + .driver = { + .name = "mt6370", + .of_match_table = mt6370_match_table, + }, + .probe = mt6370_probe, +}; +module_i2c_driver(mt6370_driver); + +MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); +MODULE_DESCRIPTION("MediaTek MT6370 SubPMIC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/mt6370.h b/drivers/mfd/mt6370.h new file mode 100644 index 000000000000..dd9ccc0a53f1 --- /dev/null +++ b/drivers/mfd/mt6370.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2022 Richtek Technology Corp. + * + * Author: ChiYuan Huang <cy_huang@richtek.com> + */ + +#ifndef __MFD_MT6370_H__ +#define __MFD_MT6370_H__ + +/* IRQ definitions */ +#define MT6370_IRQ_DIRCHGON 0 +#define MT6370_IRQ_CHG_TREG 4 +#define MT6370_IRQ_CHG_AICR 5 +#define MT6370_IRQ_CHG_MIVR 6 +#define MT6370_IRQ_PWR_RDY 7 +#define MT6370_IRQ_FL_CHG_VINOVP 11 +#define MT6370_IRQ_CHG_VSYSUV 12 +#define MT6370_IRQ_CHG_VSYSOV 13 +#define MT6370_IRQ_CHG_VBATOV 14 +#define MT6370_IRQ_CHG_VINOVPCHG 15 +#define MT6370_IRQ_TS_BAT_COLD 20 +#define MT6370_IRQ_TS_BAT_COOL 21 +#define MT6370_IRQ_TS_BAT_WARM 22 +#define MT6370_IRQ_TS_BAT_HOT 23 +#define MT6370_IRQ_TS_STATC 24 +#define MT6370_IRQ_CHG_FAULT 25 +#define MT6370_IRQ_CHG_STATC 26 +#define MT6370_IRQ_CHG_TMR 27 +#define MT6370_IRQ_CHG_BATABS 28 +#define MT6370_IRQ_CHG_ADPBAD 29 +#define MT6370_IRQ_CHG_RVP 30 +#define MT6370_IRQ_TSHUTDOWN 31 +#define MT6370_IRQ_CHG_IINMEAS 32 +#define MT6370_IRQ_CHG_ICCMEAS 33 +#define MT6370_IRQ_CHGDET_DONE 34 +#define MT6370_IRQ_WDTMR 35 +#define MT6370_IRQ_SSFINISH 36 +#define MT6370_IRQ_CHG_RECHG 37 +#define MT6370_IRQ_CHG_TERM 38 +#define MT6370_IRQ_CHG_IEOC 39 +#define MT6370_IRQ_ADC_DONE 40 +#define MT6370_IRQ_PUMPX_DONE 41 +#define MT6370_IRQ_BST_BATUV 45 +#define MT6370_IRQ_BST_MIDOV 46 +#define MT6370_IRQ_BST_OLP 47 +#define MT6370_IRQ_ATTACH 48 +#define MT6370_IRQ_DETACH 49 +#define MT6370_IRQ_HVDCP_STPDONE 51 +#define MT6370_IRQ_HVDCP_VBUSDET_DONE 52 +#define MT6370_IRQ_HVDCP_DET 53 +#define MT6370_IRQ_CHGDET 54 +#define MT6370_IRQ_DCDT 55 +#define MT6370_IRQ_DIRCHG_VGOK 59 +#define MT6370_IRQ_DIRCHG_WDTMR 60 +#define MT6370_IRQ_DIRCHG_UC 61 +#define MT6370_IRQ_DIRCHG_OC 62 +#define MT6370_IRQ_DIRCHG_OV 63 +#define MT6370_IRQ_OVPCTRL_SWON 67 +#define MT6370_IRQ_OVPCTRL_UVP_D 68 +#define MT6370_IRQ_OVPCTRL_UVP 69 +#define MT6370_IRQ_OVPCTRL_OVP_D 70 +#define MT6370_IRQ_OVPCTRL_OVP 71 +#define MT6370_IRQ_FLED_STRBPIN 72 +#define MT6370_IRQ_FLED_TORPIN 73 +#define MT6370_IRQ_FLED_TX 74 +#define MT6370_IRQ_FLED_LVF 75 +#define MT6370_IRQ_FLED2_SHORT 78 +#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_FLED1_STRB_TO 83 +#define MT6370_IRQ_FLED2_TOR 84 +#define MT6370_IRQ_FLED1_TOR 85 +#define MT6370_IRQ_OTP 93 +#define MT6370_IRQ_VDDA_OVP 94 +#define MT6370_IRQ_VDDA_UV 95 +#define MT6370_IRQ_LDO_OC 103 +#define MT6370_IRQ_BLED_OCP 118 +#define MT6370_IRQ_BLED_OVP 119 +#define MT6370_IRQ_DSV_VNEG_OCP 123 +#define MT6370_IRQ_DSV_VPOS_OCP 124 +#define MT6370_IRQ_DSV_BST_OCP 125 +#define MT6370_IRQ_DSV_VNEG_SCP 126 +#define MT6370_IRQ_DSV_VPOS_SCP 127 + +enum { + MT6370_USBC_I2C = 0, + MT6370_PMU_I2C, + MT6370_MAX_I2C +}; + +struct mt6370_info { + struct i2c_client *i2c[MT6370_MAX_I2C]; + struct regmap_irq_chip_data *irq_data; +}; + +#endif /* __MFD_MT6375_H__ */ diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index bddb40054b9e..3e58d0764c7e 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -6,16 +6,23 @@ #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> #include <linux/mfd/mt6359/registers.h> #include <linux/mfd/mt6397/registers.h> @@ -23,6 +30,12 @@ #define MT6323_RTC_BASE 0x8000 #define MT6323_RTC_SIZE 0x40 +#define MT6357_RTC_BASE 0x0588 +#define MT6357_RTC_SIZE 0x3c + +#define MT6331_RTC_BASE 0x4000 +#define MT6331_RTC_SIZE 0x40 + #define MT6358_RTC_BASE 0x0588 #define MT6358_RTC_SIZE 0x3c @@ -37,6 +50,16 @@ static const struct resource mt6323_rtc_resources[] = { DEFINE_RES_IRQ(MT6323_IRQ_STATUS_RTC), }; +static const struct resource mt6357_rtc_resources[] = { + DEFINE_RES_MEM(MT6357_RTC_BASE, MT6357_RTC_SIZE), + DEFINE_RES_IRQ(MT6357_IRQ_RTC), +}; + +static const struct resource mt6331_rtc_resources[] = { + DEFINE_RES_MEM(MT6331_RTC_BASE, MT6331_RTC_SIZE), + DEFINE_RES_IRQ(MT6331_IRQ_STATUS_RTC), +}; + static const struct resource mt6358_rtc_resources[] = { DEFINE_RES_MEM(MT6358_RTC_BASE, MT6358_RTC_SIZE), DEFINE_RES_IRQ(MT6358_IRQ_RTC), @@ -54,11 +77,43 @@ static const struct resource mt6358_keys_resources[] = { DEFINE_RES_IRQ_NAMED(MT6358_IRQ_HOMEKEY_R, "homekey_r"), }; +static const struct resource mt6359_keys_resources[] = { + DEFINE_RES_IRQ_NAMED(MT6359_IRQ_PWRKEY, "powerkey"), + DEFINE_RES_IRQ_NAMED(MT6359_IRQ_HOMEKEY, "homekey"), + DEFINE_RES_IRQ_NAMED(MT6359_IRQ_PWRKEY_R, "powerkey_r"), + 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"), + DEFINE_RES_IRQ_NAMED(MT6357_IRQ_PWRKEY_R, "powerkey_r"), + DEFINE_RES_IRQ_NAMED(MT6357_IRQ_HOMEKEY_R, "homekey_r"), +}; + +static const struct resource mt6331_keys_resources[] = { + DEFINE_RES_IRQ_NAMED(MT6331_IRQ_STATUS_PWRKEY, "powerkey"), + DEFINE_RES_IRQ_NAMED(MT6331_IRQ_STATUS_HOMEKEY, "homekey"), +}; + static const struct resource mt6397_keys_resources[] = { DEFINE_RES_IRQ_NAMED(MT6397_IRQ_PWRKEY, "powerkey"), DEFINE_RES_IRQ_NAMED(MT6397_IRQ_HOMEKEY, "homekey"), @@ -81,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" @@ -93,8 +148,66 @@ 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", + .num_resources = ARRAY_SIZE(mt6357_rtc_resources), + .resources = mt6357_rtc_resources, + .of_compatible = "mediatek,mt6357-rtc", + }, { + .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" + }, +}; + +/* MT6331 is always used in combination with MT6332 */ +static const struct mfd_cell mt6331_mt6332_devs[] = { + { + .name = "mt6331-rtc", + .num_resources = ARRAY_SIZE(mt6331_rtc_resources), + .resources = mt6331_rtc_resources, + .of_compatible = "mediatek,mt6331-rtc", + }, { + .name = "mt6331-regulator", + .of_compatible = "mediatek,mt6331-regulator" + }, { + .name = "mt6332-regulator", + .of_compatible = "mediatek,mt6332-regulator" + }, { + .name = "mt6331-keys", + .num_resources = ARRAY_SIZE(mt6331_keys_resources), + .resources = mt6331_keys_resources, + .of_compatible = "mediatek,mt6331-keys" + }, +}; + static const struct mfd_cell mt6358_devs[] = { { + .name = "mt6359-auxadc", + .of_compatible = "mediatek,mt6358-auxadc" + }, { .name = "mt6358-regulator", .of_compatible = "mediatek,mt6358-regulator" }, { @@ -114,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", @@ -122,6 +239,18 @@ static const struct mfd_cell mt6359_devs[] = { .of_compatible = "mediatek,mt6358-rtc", }, { .name = "mt6359-sound", }, + { + .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[] = { @@ -143,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" @@ -166,6 +295,30 @@ 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, + .cells = mt6357_devs, + .cell_size = ARRAY_SIZE(mt6357_devs), + .irq_init = mt6358_irq_init, +}; + +static const struct chip_data mt6331_mt6332_core = { + .cid_addr = MT6331_HWCID, + .cid_shift = 0, + .cells = mt6331_mt6332_devs, + .cell_size = ARRAY_SIZE(mt6331_mt6332_devs), + .irq_init = mt6397_irq_init, +}; + static const struct chip_data mt6358_core = { .cid_addr = MT6358_SWCID, .cid_shift = 8, @@ -249,6 +402,15 @@ 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, + }, { + .compatible = "mediatek,mt6357", + .data = &mt6357_core, + }, { .compatible = "mediatek,mt6358", .data = &mt6358_core, }, { diff --git a/drivers/mfd/mt6397-irq.c b/drivers/mfd/mt6397-irq.c index 2924919da991..5d2e5459f744 100644 --- a/drivers/mfd/mt6397-irq.c +++ b/drivers/mfd/mt6397-irq.c @@ -3,15 +3,18 @@ // 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> #include <linux/mfd/mt6397/registers.h> @@ -30,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); } @@ -52,7 +58,6 @@ static void mt6397_irq_enable(struct irq_data *data) mt6397->irq_masks_cur[reg] |= BIT(shift); } -#ifdef CONFIG_PM_SLEEP static int mt6397_irq_set_wake(struct irq_data *irq_data, unsigned int on) { struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(irq_data); @@ -66,9 +71,6 @@ static int mt6397_irq_set_wake(struct irq_data *irq_data, unsigned int on) return 0; } -#else -#define mt6397_irq_set_wake NULL -#endif static struct irq_chip mt6397_irq_chip = { .name = "mt6397-irq", @@ -76,7 +78,7 @@ static struct irq_chip mt6397_irq_chip = { .irq_bus_sync_unlock = mt6397_irq_sync_unlock, .irq_enable = mt6397_irq_enable, .irq_disable = mt6397_irq_disable, - .irq_set_wake = mt6397_irq_set_wake, + .irq_set_wake = pm_sleep_ptr(mt6397_irq_set_wake), }; static void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg, @@ -108,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; } @@ -141,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; @@ -149,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; @@ -172,7 +182,20 @@ 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; + chip->int_status[0] = MT6331_INT_STATUS_CON0; + chip->int_status[1] = MT6331_INT_STATUS_CON1; + break; case MT6391_CHIP_ID: case MT6397_CHIP_ID: chip->int_con[0] = MT6397_INT_CON0; @@ -189,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; @@ -206,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 b711e73eedcb..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 @@ -175,6 +175,7 @@ static int ntxec_probe(struct i2c_client *client) /* Bail out if we encounter an unknown firmware version */ switch (version) { case NTXEC_VERSION_KOBO_AURA: + case NTXEC_VERSION_TOLINO_VISION: subdevs = ntxec_subdev; n_subdevs = ARRAY_SIZE(ntxec_subdev); break; @@ -239,15 +240,13 @@ static int ntxec_probe(struct i2c_client *client) return res; } -static int ntxec_remove(struct i2c_client *client) +static void ntxec_remove(struct i2c_client *client) { if (client == poweroff_restart_client) { poweroff_restart_client = NULL; pm_power_off = NULL; unregister_restart_handler(&ntxec_restart_handler); } - - return 0; } static const struct of_device_id of_ntxec_match_table[] = { @@ -261,7 +260,7 @@ static struct i2c_driver ntxec_driver = { .name = "ntxec", .of_match_table = of_ntxec_match_table, }, - .probe_new = ntxec_probe, + .probe = ntxec_probe, .remove = ntxec_remove, }; module_i2c_driver(ntxec_driver); diff --git a/drivers/mfd/ocelot-core.c b/drivers/mfd/ocelot-core.c new file mode 100644 index 000000000000..41aff2708854 --- /dev/null +++ b/drivers/mfd/ocelot-core.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Core driver for the Ocelot chip family. + * + * The VSC7511, 7512, 7513, and 7514 can be controlled internally via an + * on-chip MIPS processor, or externally via SPI, I2C, PCIe. This core driver is + * intended to be the bus-agnostic glue between, for example, the SPI bus and + * the child devices. + * + * Copyright 2021-2022 Innovative Advantage Inc. + * + * Author: Colin Foster <colin.foster@in-advantage.com> + */ + +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/export.h> +#include <linux/iopoll.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/mfd/core.h> +#include <linux/mfd/ocelot.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#include <soc/mscc/ocelot.h> + +#include "ocelot.h" + +#define REG_GCB_SOFT_RST 0x0008 + +#define BIT_SOFT_CHIP_RST BIT(0) + +#define VSC7512_MIIM0_RES_START 0x7107009c +#define VSC7512_MIIM1_RES_START 0x710700c0 +#define VSC7512_MIIM_RES_SIZE 0x00000024 + +#define VSC7512_PHY_RES_START 0x710700f0 +#define VSC7512_PHY_RES_SIZE 0x00000004 + +#define VSC7512_GPIO_RES_START 0x71070034 +#define VSC7512_GPIO_RES_SIZE 0x0000006c + +#define VSC7512_SIO_CTRL_RES_START 0x710700f8 +#define VSC7512_SIO_CTRL_RES_SIZE 0x00000100 + +#define VSC7512_HSIO_RES_START 0x710d0000 +#define VSC7512_HSIO_RES_SIZE 0x00000128 + +#define VSC7512_ANA_RES_START 0x71880000 +#define VSC7512_ANA_RES_SIZE 0x00010000 + +#define VSC7512_QS_RES_START 0x71080000 +#define VSC7512_QS_RES_SIZE 0x00000100 + +#define VSC7512_QSYS_RES_START 0x71800000 +#define VSC7512_QSYS_RES_SIZE 0x00200000 + +#define VSC7512_REW_RES_START 0x71030000 +#define VSC7512_REW_RES_SIZE 0x00010000 + +#define VSC7512_SYS_RES_START 0x71010000 +#define VSC7512_SYS_RES_SIZE 0x00010000 + +#define VSC7512_S0_RES_START 0x71040000 +#define VSC7512_S1_RES_START 0x71050000 +#define VSC7512_S2_RES_START 0x71060000 +#define VCAP_RES_SIZE 0x00000400 + +#define VSC7512_PORT_0_RES_START 0x711e0000 +#define VSC7512_PORT_1_RES_START 0x711f0000 +#define VSC7512_PORT_2_RES_START 0x71200000 +#define VSC7512_PORT_3_RES_START 0x71210000 +#define VSC7512_PORT_4_RES_START 0x71220000 +#define VSC7512_PORT_5_RES_START 0x71230000 +#define VSC7512_PORT_6_RES_START 0x71240000 +#define VSC7512_PORT_7_RES_START 0x71250000 +#define VSC7512_PORT_8_RES_START 0x71260000 +#define VSC7512_PORT_9_RES_START 0x71270000 +#define VSC7512_PORT_10_RES_START 0x71280000 +#define VSC7512_PORT_RES_SIZE 0x00010000 + +#define VSC7512_GCB_RST_SLEEP_US 100 +#define VSC7512_GCB_RST_TIMEOUT_US 100000 + +static int ocelot_gcb_chip_rst_status(struct ocelot_ddata *ddata) +{ + int val, err; + + err = regmap_read(ddata->gcb_regmap, REG_GCB_SOFT_RST, &val); + if (err) + return err; + + return val; +} + +int ocelot_chip_reset(struct device *dev) +{ + struct ocelot_ddata *ddata = dev_get_drvdata(dev); + int ret, val; + + /* + * Reset the entire chip here to put it into a completely known state. + * Other drivers may want to reset their own subsystems. The register + * self-clears, so one write is all that is needed and wait for it to + * clear. + */ + ret = regmap_write(ddata->gcb_regmap, REG_GCB_SOFT_RST, BIT_SOFT_CHIP_RST); + if (ret) + return ret; + + 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"); + +static const struct resource vsc7512_miim0_resources[] = { + DEFINE_RES_REG_NAMED(VSC7512_MIIM0_RES_START, VSC7512_MIIM_RES_SIZE, "gcb_miim0"), + DEFINE_RES_REG_NAMED(VSC7512_PHY_RES_START, VSC7512_PHY_RES_SIZE, "gcb_phy"), +}; + +static const struct resource vsc7512_miim1_resources[] = { + DEFINE_RES_REG_NAMED(VSC7512_MIIM1_RES_START, VSC7512_MIIM_RES_SIZE, "gcb_miim1"), +}; + +static const struct resource vsc7512_pinctrl_resources[] = { + DEFINE_RES_REG_NAMED(VSC7512_GPIO_RES_START, VSC7512_GPIO_RES_SIZE, "gcb_gpio"), +}; + +static const struct resource vsc7512_sgpio_resources[] = { + DEFINE_RES_REG_NAMED(VSC7512_SIO_CTRL_RES_START, VSC7512_SIO_CTRL_RES_SIZE, "gcb_sio"), +}; + +static const struct resource vsc7512_serdes_resources[] = { + DEFINE_RES_REG_NAMED(VSC7512_HSIO_RES_START, VSC7512_HSIO_RES_SIZE, "hsio"), +}; + +static const struct resource vsc7512_switch_resources[] = { + DEFINE_RES_REG_NAMED(VSC7512_ANA_RES_START, VSC7512_ANA_RES_SIZE, "ana"), + DEFINE_RES_REG_NAMED(VSC7512_HSIO_RES_START, VSC7512_HSIO_RES_SIZE, "hsio"), + DEFINE_RES_REG_NAMED(VSC7512_QS_RES_START, VSC7512_QS_RES_SIZE, "qs"), + DEFINE_RES_REG_NAMED(VSC7512_QSYS_RES_START, VSC7512_QSYS_RES_SIZE, "qsys"), + DEFINE_RES_REG_NAMED(VSC7512_REW_RES_START, VSC7512_REW_RES_SIZE, "rew"), + DEFINE_RES_REG_NAMED(VSC7512_SYS_RES_START, VSC7512_SYS_RES_SIZE, "sys"), + DEFINE_RES_REG_NAMED(VSC7512_S0_RES_START, VCAP_RES_SIZE, "s0"), + DEFINE_RES_REG_NAMED(VSC7512_S1_RES_START, VCAP_RES_SIZE, "s1"), + DEFINE_RES_REG_NAMED(VSC7512_S2_RES_START, VCAP_RES_SIZE, "s2"), + DEFINE_RES_REG_NAMED(VSC7512_PORT_0_RES_START, VSC7512_PORT_RES_SIZE, "port0"), + DEFINE_RES_REG_NAMED(VSC7512_PORT_1_RES_START, VSC7512_PORT_RES_SIZE, "port1"), + DEFINE_RES_REG_NAMED(VSC7512_PORT_2_RES_START, VSC7512_PORT_RES_SIZE, "port2"), + DEFINE_RES_REG_NAMED(VSC7512_PORT_3_RES_START, VSC7512_PORT_RES_SIZE, "port3"), + DEFINE_RES_REG_NAMED(VSC7512_PORT_4_RES_START, VSC7512_PORT_RES_SIZE, "port4"), + DEFINE_RES_REG_NAMED(VSC7512_PORT_5_RES_START, VSC7512_PORT_RES_SIZE, "port5"), + DEFINE_RES_REG_NAMED(VSC7512_PORT_6_RES_START, VSC7512_PORT_RES_SIZE, "port6"), + DEFINE_RES_REG_NAMED(VSC7512_PORT_7_RES_START, VSC7512_PORT_RES_SIZE, "port7"), + DEFINE_RES_REG_NAMED(VSC7512_PORT_8_RES_START, VSC7512_PORT_RES_SIZE, "port8"), + DEFINE_RES_REG_NAMED(VSC7512_PORT_9_RES_START, VSC7512_PORT_RES_SIZE, "port9"), + DEFINE_RES_REG_NAMED(VSC7512_PORT_10_RES_START, VSC7512_PORT_RES_SIZE, "port10") +}; + +static const struct mfd_cell vsc7512_devs[] = { + { + .name = "ocelot-pinctrl", + .of_compatible = "mscc,ocelot-pinctrl", + .num_resources = ARRAY_SIZE(vsc7512_pinctrl_resources), + .resources = vsc7512_pinctrl_resources, + }, { + .name = "ocelot-sgpio", + .of_compatible = "mscc,ocelot-sgpio", + .num_resources = ARRAY_SIZE(vsc7512_sgpio_resources), + .resources = vsc7512_sgpio_resources, + }, { + .name = "ocelot-miim0", + .of_compatible = "mscc,ocelot-miim", + .of_reg = VSC7512_MIIM0_RES_START, + .use_of_reg = true, + .num_resources = ARRAY_SIZE(vsc7512_miim0_resources), + .resources = vsc7512_miim0_resources, + }, { + .name = "ocelot-miim1", + .of_compatible = "mscc,ocelot-miim", + .of_reg = VSC7512_MIIM1_RES_START, + .use_of_reg = true, + .num_resources = ARRAY_SIZE(vsc7512_miim1_resources), + .resources = vsc7512_miim1_resources, + }, { + .name = "ocelot-serdes", + .of_compatible = "mscc,vsc7514-serdes", + .num_resources = ARRAY_SIZE(vsc7512_serdes_resources), + .resources = vsc7512_serdes_resources, + }, { + .name = "ocelot-ext-switch", + .of_compatible = "mscc,vsc7512-switch", + .num_resources = ARRAY_SIZE(vsc7512_switch_resources), + .resources = vsc7512_switch_resources, + }, +}; + +static void ocelot_core_try_add_regmap(struct device *dev, + const struct resource *res) +{ + if (dev_get_regmap(dev, res->name)) + return; + + ocelot_spi_init_regmap(dev, res); +} + +static void ocelot_core_try_add_regmaps(struct device *dev, + const struct mfd_cell *cell) +{ + int i; + + for (i = 0; i < cell->num_resources; i++) + ocelot_core_try_add_regmap(dev, &cell->resources[i]); +} + +int ocelot_core_init(struct device *dev) +{ + int i, ndevs; + + ndevs = ARRAY_SIZE(vsc7512_devs); + + for (i = 0; i < ndevs; i++) + ocelot_core_try_add_regmaps(dev, &vsc7512_devs[i]); + + return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, vsc7512_devs, ndevs, NULL, 0, NULL); +} +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"); diff --git a/drivers/mfd/ocelot-spi.c b/drivers/mfd/ocelot-spi.c new file mode 100644 index 000000000000..1fed9878c323 --- /dev/null +++ b/drivers/mfd/ocelot-spi.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * SPI core driver for the Ocelot chip family. + * + * This driver will handle everything necessary to allow for communication over + * SPI to the VSC7511, VSC7512, VSC7513 and VSC7514 chips. The main functions + * are to prepare the chip's SPI interface for a specific bus speed, and a host + * processor's endianness. This will create and distribute regmaps for any + * children. + * + * Copyright 2021-2022 Innovative Advantage Inc. + * + * Author: Colin Foster <colin.foster@in-advantage.com> + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/ioport.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <linux/types.h> +#include <linux/units.h> + +#include "ocelot.h" + +#define REG_DEV_CPUORG_IF_CTRL 0x0000 +#define REG_DEV_CPUORG_IF_CFGSTAT 0x0004 + +#define CFGSTAT_IF_NUM_VCORE (0 << 24) +#define CFGSTAT_IF_NUM_VRAP (1 << 24) +#define CFGSTAT_IF_NUM_SI (2 << 24) +#define CFGSTAT_IF_NUM_MIIM (3 << 24) + +#define VSC7512_DEVCPU_ORG_RES_START 0x71000000 +#define VSC7512_DEVCPU_ORG_RES_SIZE 0x38 + +#define VSC7512_CHIP_REGS_RES_START 0x71070000 +#define VSC7512_CHIP_REGS_RES_SIZE 0x14 + +static const struct resource vsc7512_dev_cpuorg_resource = + DEFINE_RES_REG_NAMED(VSC7512_DEVCPU_ORG_RES_START, + VSC7512_DEVCPU_ORG_RES_SIZE, + "devcpu_org"); + +static const struct resource vsc7512_gcb_resource = + DEFINE_RES_REG_NAMED(VSC7512_CHIP_REGS_RES_START, + VSC7512_CHIP_REGS_RES_SIZE, + "devcpu_gcb_chip_regs"); + +static int ocelot_spi_initialize(struct device *dev) +{ + struct ocelot_ddata *ddata = dev_get_drvdata(dev); + u32 val, check; + int err; + + val = OCELOT_SPI_BYTE_ORDER; + + /* + * The SPI address must be big-endian, but we want the payload to match + * our CPU. These are two bits (0 and 1) but they're repeated such that + * the write from any configuration will be valid. The four + * configurations are: + * + * 0b00: little-endian, MSB first + * | 111111 | 22221111 | 33222222 | + * | 76543210 | 54321098 | 32109876 | 10987654 | + * + * 0b01: big-endian, MSB first + * | 33222222 | 22221111 | 111111 | | + * | 10987654 | 32109876 | 54321098 | 76543210 | + * + * 0b10: little-endian, LSB first + * | 111111 | 11112222 | 22222233 | + * | 01234567 | 89012345 | 67890123 | 45678901 | + * + * 0b11: big-endian, LSB first + * | 22222233 | 11112222 | 111111 | | + * | 45678901 | 67890123 | 89012345 | 01234567 | + */ + err = regmap_write(ddata->cpuorg_regmap, REG_DEV_CPUORG_IF_CTRL, val); + if (err) + return err; + + /* + * Apply the number of padding bytes between a read request and the data + * payload. Some registers have access times of up to 1us, so if the + * first payload bit is shifted out too quickly, the read will fail. + */ + val = ddata->spi_padding_bytes; + err = regmap_write(ddata->cpuorg_regmap, REG_DEV_CPUORG_IF_CFGSTAT, val); + if (err) + return err; + + /* + * After we write the interface configuration, read it back here. This + * will verify several different things. The first is that the number of + * padding bytes actually got written correctly. These are found in bits + * 0:3. + * + * The second is that bit 16 is cleared. Bit 16 is IF_CFGSTAT:IF_STAT, + * and will be set if the register access is too fast. This would be in + * the condition that the number of padding bytes is insufficient for + * the SPI bus frequency. + * + * The last check is for bits 31:24, which define the interface by which + * the registers are being accessed. Since we're accessing them via the + * serial interface, it must return IF_NUM_SI. + */ + check = val | CFGSTAT_IF_NUM_SI; + + err = regmap_read(ddata->cpuorg_regmap, REG_DEV_CPUORG_IF_CFGSTAT, &val); + if (err) + return err; + + if (check != val) + return -ENODEV; + + return 0; +} + +static const struct regmap_config ocelot_spi_regmap_config = { + .reg_bits = 24, + .reg_stride = 4, + .reg_shift = REGMAP_DOWNSHIFT(2), + .val_bits = 32, + + .write_flag_mask = 0x80, + + .use_single_read = true, + .use_single_write = true, + .can_multi_write = false, + + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}; + +static int ocelot_spi_regmap_bus_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct spi_transfer xfers[3] = {0}; + struct device *dev = context; + struct ocelot_ddata *ddata; + struct spi_device *spi; + unsigned int index = 0; + + ddata = dev_get_drvdata(dev); + spi = to_spi_device(dev); + + xfers[index].tx_buf = reg; + xfers[index].len = reg_size; + index++; + + if (ddata->spi_padding_bytes) { + xfers[index].len = ddata->spi_padding_bytes; + xfers[index].tx_buf = ddata->dummy_buf; + xfers[index].dummy_data = 1; + index++; + } + + xfers[index].rx_buf = val; + xfers[index].len = val_size; + index++; + + return spi_sync_transfer(spi, xfers, index); +} + +static int ocelot_spi_regmap_bus_write(void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + + return spi_write(spi, data, count); +} + +static const struct regmap_bus ocelot_spi_regmap_bus = { + .write = ocelot_spi_regmap_bus_write, + .read = ocelot_spi_regmap_bus_read, +}; + +struct regmap *ocelot_spi_init_regmap(struct device *dev, const struct resource *res) +{ + struct regmap_config regmap_config; + + memcpy(®map_config, &ocelot_spi_regmap_config, sizeof(regmap_config)); + + regmap_config.name = res->name; + regmap_config.max_register = resource_size(res) - 1; + regmap_config.reg_base = res->start; + + return devm_regmap_init(dev, &ocelot_spi_regmap_bus, dev, ®map_config); +} +EXPORT_SYMBOL_NS(ocelot_spi_init_regmap, "MFD_OCELOT_SPI"); + +static int ocelot_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ocelot_ddata *ddata; + struct regmap *r; + int err; + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + spi_set_drvdata(spi, ddata); + + if (spi->max_speed_hz <= 500000) { + ddata->spi_padding_bytes = 0; + } else { + /* + * Calculation taken from the manual for IF_CFGSTAT:IF_CFG. + * Register access time is 1us, so we need to configure and send + * out enough padding bytes between the read request and data + * transmission that lasts at least 1 microsecond. + */ + ddata->spi_padding_bytes = 1 + (spi->max_speed_hz / HZ_PER_MHZ + 2) / 8; + + ddata->dummy_buf = devm_kzalloc(dev, ddata->spi_padding_bytes, GFP_KERNEL); + if (!ddata->dummy_buf) + return -ENOMEM; + } + + spi->bits_per_word = 8; + + err = spi_setup(spi); + if (err) + return dev_err_probe(&spi->dev, err, "Error performing SPI setup\n"); + + r = ocelot_spi_init_regmap(dev, &vsc7512_dev_cpuorg_resource); + if (IS_ERR(r)) + return PTR_ERR(r); + + ddata->cpuorg_regmap = r; + + r = ocelot_spi_init_regmap(dev, &vsc7512_gcb_resource); + if (IS_ERR(r)) + return PTR_ERR(r); + + ddata->gcb_regmap = r; + + /* + * The chip must be set up for SPI before it gets initialized and reset. + * This must be done before calling init, and after a chip reset is + * performed. + */ + err = ocelot_spi_initialize(dev); + if (err) + return dev_err_probe(dev, err, "Error initializing SPI bus\n"); + + err = ocelot_chip_reset(dev); + if (err) + return dev_err_probe(dev, err, "Error resetting device\n"); + + /* + * A chip reset will clear the SPI configuration, so it needs to be done + * again before we can access any registers. + */ + err = ocelot_spi_initialize(dev); + if (err) + return dev_err_probe(dev, err, "Error initializing SPI bus after reset\n"); + + err = ocelot_core_init(dev); + if (err) + return dev_err_probe(dev, err, "Error initializing Ocelot core\n"); + + return 0; +} + +static const struct spi_device_id ocelot_spi_ids[] = { + { "vsc7512", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ocelot_spi_ids); + +static const struct of_device_id ocelot_spi_of_match[] = { + { .compatible = "mscc,vsc7512" }, + { } +}; +MODULE_DEVICE_TABLE(of, ocelot_spi_of_match); + +static struct spi_driver ocelot_spi_driver = { + .driver = { + .name = "ocelot-soc", + .of_match_table = ocelot_spi_of_match, + }, + .id_table = ocelot_spi_ids, + .probe = ocelot_spi_probe, +}; +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"); diff --git a/drivers/mfd/ocelot.h b/drivers/mfd/ocelot.h new file mode 100644 index 000000000000..b8bc2f1486e2 --- /dev/null +++ b/drivers/mfd/ocelot.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* Copyright 2021, 2022 Innovative Advantage Inc. */ + +#ifndef _MFD_OCELOT_H +#define _MFD_OCELOT_H + +#include <linux/kconfig.h> + +struct device; +struct regmap; +struct resource; + +/** + * struct ocelot_ddata - Private data for an external Ocelot chip + * @gcb_regmap: General Configuration Block regmap. Used for + * operations like chip reset. + * @cpuorg_regmap: CPU Device Origin Block regmap. Used for operations + * like SPI bus configuration. + * @spi_padding_bytes: Number of padding bytes that must be thrown out before + * read data gets returned. This is calculated during + * initialization based on bus speed. + * @dummy_buf: Zero-filled buffer of spi_padding_bytes size. The dummy + * bytes that will be sent out between the address and + * data of a SPI read operation. + */ +struct ocelot_ddata { + struct regmap *gcb_regmap; + struct regmap *cpuorg_regmap; + int spi_padding_bytes; + void *dummy_buf; +}; + +int ocelot_chip_reset(struct device *dev); +int ocelot_core_init(struct device *dev); + +/* SPI-specific routines that won't be necessary for other interfaces */ +struct regmap *ocelot_spi_init_regmap(struct device *dev, + const struct resource *res); + +#define OCELOT_SPI_BYTE_ORDER_LE 0x00000000 +#define OCELOT_SPI_BYTE_ORDER_BE 0x81818181 + +#ifdef __LITTLE_ENDIAN +#define OCELOT_SPI_BYTE_ORDER OCELOT_SPI_BYTE_ORDER_LE +#else +#define OCELOT_SPI_BYTE_ORDER OCELOT_SPI_BYTE_ORDER_BE +#endif + +#endif diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 787d2ae86375..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 = { @@ -853,7 +849,6 @@ static struct platform_driver usbhs_omap_driver = { MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>"); MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>"); MODULE_ALIAS("platform:" USBHS_DRIVER_NAME); -MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("usb host common core driver for omap EHCI and OHCI"); static int omap_usbhs_drvinit(void) diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 080d7970a377..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); }; /*-------------------------------------------------------------------------*/ @@ -125,11 +125,6 @@ static inline void usbtll_writeb(void __iomem *base, u32 reg, u8 val) writeb_relaxed(val, base + reg); } -static inline u8 usbtll_readb(void __iomem *base, u32 reg) -{ - return readb_relaxed(base + reg); -} - /*-------------------------------------------------------------------------*/ static bool is_ohci_port(enum usbhs_omap_port_mode pmode) @@ -205,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); @@ -237,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); @@ -277,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; @@ -294,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[] = { @@ -450,7 +441,6 @@ EXPORT_SYMBOL_GPL(omap_tll_disable); MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>"); MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>"); -MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("usb tll driver for TI OMAP EHCI and OHCI controllers"); static int __init omap_usbtll_drvinit(void) diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index f5b3fa973b13..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,57 +463,33 @@ 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, - const struct i2c_device_id *id) +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; int slave; - const struct of_device_id *match; pdata = dev_get_platdata(&i2c->dev); @@ -536,13 +513,8 @@ static int palmas_i2c_probe(struct i2c_client *i2c, palmas->dev = &i2c->dev; palmas->irq = i2c->irq; - match = of_match_device(of_palmas_match_tbl, &i2c->dev); - - if (!match) - return -ENODATA; - - driver_data = (struct palmas_driver_data *)match->data; - 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) @@ -700,7 +672,7 @@ err_i2c: return ret; } -static int palmas_i2c_remove(struct i2c_client *i2c) +static void palmas_i2c_remove(struct i2c_client *i2c) { struct palmas *palmas = i2c_get_clientdata(i2c); int i; @@ -716,15 +688,21 @@ static int palmas_i2c_remove(struct i2c_client *i2c) pm_power_off = NULL; palmas_dev = NULL; } - - return 0; } +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 5cd653e61512..000000000000 --- a/drivers/mfd/pcf50633-adc.c +++ /dev/null @@ -1,252 +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; - - /* 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; - - return adc_enqueue_request(pcf, req); -} -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 e9c565cf0f54..000000000000 --- a/drivers/mfd/pcf50633-core.c +++ /dev/null @@ -1,327 +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; - } -} - -#ifdef CONFIG_PM_SLEEP -static int pcf50633_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pcf50633 *pcf = i2c_get_clientdata(client); - - return pcf50633_irq_suspend(pcf); -} - -static int pcf50633_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pcf50633 *pcf = i2c_get_clientdata(client); - - return pcf50633_irq_resume(pcf); -} -#endif - -static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume); - -static const struct regmap_config pcf50633_regmap_config = { - .reg_bits = 8, - .val_bits = 8, -}; - -static int pcf50633_probe(struct i2c_client *client, - const struct i2c_device_id *ids) -{ - 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 int 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]); - - return 0; -} - -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 = &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 2096afb0ce9b..000000000000 --- a/drivers/mfd/pcf50633-irq.c +++ /dev/null @@ -1,309 +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/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; -} - -#ifdef CONFIG_PM - -int pcf50633_irq_suspend(struct pcf50633 *pcf) -{ - 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; -} - -int pcf50633_irq_resume(struct pcf50633 *pcf) -{ - 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; -} - -#endif - -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, ®_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 c472d7f8103c..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,224 +38,242 @@ 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 -#define PM8008_STATUS_BASE (PM8008_PERIPH_0_BASE | INT_LATCHED_STS_OFFSET) -#define PM8008_MASK_BASE (PM8008_PERIPH_0_BASE | INT_EN_SET_OFFSET) -#define PM8008_UNMASK_BASE (PM8008_PERIPH_0_BASE | INT_EN_CLR_OFFSET) -#define PM8008_TYPE_BASE (PM8008_PERIPH_0_BASE | INT_SET_TYPE_OFFSET) -#define PM8008_ACK_BASE (PM8008_PERIPH_0_BASE | INT_LATCHED_CLR_OFFSET) -#define PM8008_POLARITY_HI_BASE (PM8008_PERIPH_0_BASE | INT_POL_HIGH_OFFSET) -#define PM8008_POLARITY_LO_BASE (PM8008_PERIPH_0_BASE | INT_POL_LOW_OFFSET) +/* 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 -#define PM8008_PERIPH_OFFSET(paddr) (paddr - PM8008_PERIPH_0_BASE) +enum { + SET_TYPE_INDEX, + POLARITY_HI_INDEX, + POLARITY_LO_INDEX, +}; -struct pm8008_data { - struct device *dev; - struct regmap *regmap; - int irq; - struct regmap_irq_chip_data *irq_data; +static const unsigned int pm8008_config_regs[] = { + INT_SET_TYPE_OFFSET, + INT_POL_HIGH_OFFSET, + INT_POL_LOW_OFFSET, }; -static unsigned int p0_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_0_BASE)}; -static unsigned int p1_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_1_BASE)}; -static unsigned int p2_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_2_BASE)}; -static unsigned int p3_offs[] = {PM8008_PERIPH_OFFSET(PM8008_PERIPH_3_BASE)}; +#define _IRQ(_irq, _off, _mask, _types) \ + [_irq] = { \ + .reg_offset = (_off), \ + .mask = (_mask), \ + .type = { \ + .type_reg_offset = (_off), \ + .types_supported = (_types), \ + }, \ + } -static struct regmap_irq_sub_irq_map pm8008_sub_reg_offsets[] = { - REGMAP_IRQ_MAIN_REG_OFFSET(p0_offs), - REGMAP_IRQ_MAIN_REG_OFFSET(p1_offs), - REGMAP_IRQ_MAIN_REG_OFFSET(p2_offs), - REGMAP_IRQ_MAIN_REG_OFFSET(p3_offs), +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 unsigned int pm8008_virt_regs[] = { - PM8008_POLARITY_HI_BASE, - PM8008_POLARITY_LO_BASE, +static const unsigned int pm8008_periph_base[] = { + PM8008_PERIPH_0_BASE, + PM8008_PERIPH_1_BASE, + PM8008_PERIPH_2_BASE, + PM8008_PERIPH_3_BASE, }; -enum { - POLARITY_HI_INDEX, - POLARITY_LO_INDEX, - PM8008_NUM_VIRT_REGS, -}; +static unsigned int pm8008_get_irq_reg(struct regmap_irq_chip_data *data, + unsigned int base, int index) +{ + /* Simple linear addressing for the main status register */ + if (base == I2C_INTR_STATUS_BASE) + return base + index; -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)), -}; + return pm8008_periph_base[index] + base; +} -static int pm8008_set_type_virt(unsigned int **virt_buf, - unsigned int type, unsigned long hwirq, - int reg) +static int pm8008_set_type_config(unsigned int **buf, unsigned int type, + const struct regmap_irq *irq_data, int idx, + void *irq_drv_data) { switch (type) { case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_LEVEL_LOW: - virt_buf[POLARITY_HI_INDEX][reg] &= ~pm8008_irqs[hwirq].mask; - virt_buf[POLARITY_LO_INDEX][reg] |= pm8008_irqs[hwirq].mask; + buf[POLARITY_HI_INDEX][idx] &= ~irq_data->mask; + buf[POLARITY_LO_INDEX][idx] |= irq_data->mask; break; case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_LEVEL_HIGH: - virt_buf[POLARITY_HI_INDEX][reg] |= pm8008_irqs[hwirq].mask; - virt_buf[POLARITY_LO_INDEX][reg] &= ~pm8008_irqs[hwirq].mask; + buf[POLARITY_HI_INDEX][idx] |= irq_data->mask; + buf[POLARITY_LO_INDEX][idx] &= ~irq_data->mask; break; case IRQ_TYPE_EDGE_BOTH: - virt_buf[POLARITY_HI_INDEX][reg] |= pm8008_irqs[hwirq].mask; - virt_buf[POLARITY_LO_INDEX][reg] |= pm8008_irqs[hwirq].mask; + buf[POLARITY_HI_INDEX][idx] |= irq_data->mask; + buf[POLARITY_LO_INDEX][idx] |= irq_data->mask; break; default: return -EINVAL; } + if (type & IRQ_TYPE_EDGE_BOTH) + buf[SET_TYPE_INDEX][idx] |= irq_data->mask; + else + buf[SET_TYPE_INDEX][idx] &= ~irq_data->mask; + 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, - .num_virt_regs = PM8008_NUM_VIRT_REGS, .irqs = pm8008_irqs, .num_irqs = ARRAY_SIZE(pm8008_irqs), .num_regs = PM8008_NUM_PERIPHS, - .not_fixed_stride = true, - .sub_reg_offsets = pm8008_sub_reg_offsets, - .set_type_virt = pm8008_set_type_virt, - .status_base = PM8008_STATUS_BASE, - .mask_base = PM8008_MASK_BASE, - .unmask_base = PM8008_UNMASK_BASE, - .type_base = PM8008_TYPE_BASE, - .ack_base = PM8008_ACK_BASE, - .virt_reg_base = pm8008_virt_regs, - .num_type_reg = PM8008_NUM_PERIPHS, + .status_base = INT_LATCHED_STS_OFFSET, + .mask_base = INT_EN_CLR_OFFSET, + .unmask_base = INT_EN_SET_OFFSET, + .mask_unmask_non_inverted = true, + .ack_base = INT_LATCHED_CLR_OFFSET, + .config_base = pm8008_config_regs, + .num_config_bases = ARRAY_SIZE(pm8008_config_regs), + .num_config_regs = PM8008_NUM_PERIPHS, + .set_type_config = pm8008_set_type_config, + .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_init(struct pm8008_data *chip) +static const struct regmap_config pm8008_regmap_cfg_2 = { + .name = "secondary", + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xffff, +}; + +static const struct resource pm8008_temp_res[] = { + DEFINE_RES_MEM(PM8008_TEMP_ALARM_ADDR, 0x100), + DEFINE_RES_IRQ(PM8008_IRQ_TEMP_ALARM), +}; + +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"), +}; + +static void devm_irq_domain_fwnode_release(void *data) { - int rc; + struct fwnode_handle *fwnode = data; - /* - * Set TEMP_ALARM peripheral's TYPE so that the regmap-irq framework - * reads this as the default value instead of zero, the HW default. - * This is required to enable the writing of TYPE registers in - * regmap_irq_sync_unlock(). - */ - rc = regmap_write(chip->regmap, - (PM8008_TEMP_ALARM_ADDR | INT_SET_TYPE_OFFSET), - BIT(0)); - if (rc) - return rc; - - /* Do the same for GPIO1 and GPIO2 peripherals */ - rc = regmap_write(chip->regmap, - (PM8008_GPIO1_ADDR | INT_SET_TYPE_OFFSET), BIT(0)); - if (rc) - return rc; - - rc = regmap_write(chip->regmap, - (PM8008_GPIO2_ADDR | INT_SET_TYPE_OFFSET), BIT(0)); - - return rc; + irq_domain_free_fwnode(fwnode); } -static int pm8008_probe_irq_peripherals(struct pm8008_data *chip, - int client_irq) +static int pm8008_probe(struct i2c_client *client) { - int rc, i; - struct regmap_irq_type *type; struct regmap_irq_chip_data *irq_data; - - rc = pm8008_init(chip); - if (rc) { - dev_err(chip->dev, "Init failed: %d\n", rc); - return rc; + 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; } - for (i = 0; i < ARRAY_SIZE(pm8008_irqs); i++) { - type = &pm8008_irqs[i].type; + regmap2 = devm_regmap_init_i2c(dummy, &qcom_mfd_regmap_cfg); + if (IS_ERR(regmap2)) + return PTR_ERR(regmap2); - type->type_reg_offset = pm8008_irqs[i].reg_offset; - type->type_rising_val = pm8008_irqs[i].mask; - type->type_falling_val = pm8008_irqs[i].mask; - type->type_level_high_val = 0; - type->type_level_low_val = 0; + ret = regmap_attach_dev(dev, regmap2, &pm8008_regmap_cfg_2); + if (ret) + return ret; - 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); - } + /* Default regmap must be attached last. */ + regmap = devm_regmap_init_i2c(client, &qcom_mfd_regmap_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - rc = devm_regmap_add_irq_chip(chip->dev, chip->regmap, client_irq, - IRQF_SHARED, 0, &pm8008_irq_chip, &irq_data); - if (rc) { - dev_err(chip->dev, "Failed to add IRQ chip: %d\n", rc); - return rc; - } + reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset)) + return PTR_ERR(reset); - return 0; -} - -static int pm8008_probe(struct i2c_client *client) -{ - int rc; - struct pm8008_data *chip; + /* + * The PMIC does not appear to require a post-reset delay, but wait + * for a millisecond for now anyway. + */ + usleep_range(1000, 2000); - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) + name = devm_kasprintf(dev, GFP_KERNEL, "%pOF-internal", dev->of_node); + if (!name) return -ENOMEM; - chip->dev = &client->dev; - chip->regmap = devm_regmap_init_i2c(client, &qcom_mfd_regmap_cfg); - if (!chip->regmap) - return -ENODEV; + name = strreplace(name, '/', ':'); + + fwnode = irq_domain_alloc_named_fwnode(name); + if (!fwnode) + return -ENOMEM; - i2c_set_clientdata(client, chip); + ret = devm_add_action_or_reset(dev, devm_irq_domain_fwnode_release, fwnode); + if (ret) + return ret; - if (of_property_read_bool(chip->dev->of_node, "interrupt-controller")) { - rc = pm8008_probe_irq_peripherals(chip, client->irq); - if (rc) - dev_err(chip->dev, "Failed to probe irq periphs: %d\n", rc); + 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(chip->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[] = { { .compatible = "qcom,pm8008", }, { }, }; +MODULE_DEVICE_TABLE(of, pm8008_match); static struct i2c_driver pm8008_mfd_driver = { .driver = { .name = "pm8008", .of_match_table = pm8008_match, }, - .probe_new = pm8008_probe, + .probe = pm8008_probe, }; module_i2c_driver(pm8008_mfd_driver); +MODULE_DESCRIPTION("QCOM PM8008 Power Management IC driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("i2c:qcom-pm8008"); diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c index acd172ddcbd6..1149f7102a36 100644 --- a/drivers/mfd/qcom-pm8xxx.c +++ b/drivers/mfd/qcom-pm8xxx.c @@ -65,7 +65,7 @@ struct pm_irq_data { int num_irqs; struct irq_chip *irq_chip; - void (*irq_handler)(struct irq_desc *desc); + irq_handler_t irq_handler; }; struct pm_irq_chip { @@ -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,13 +117,13 @@ 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; } static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block) { - int pmirq, irq, i, ret = 0; + int pmirq, i, ret = 0; unsigned int bits; ret = pm8xxx_read_block_irq(chip, block, &bits); @@ -139,8 +140,7 @@ static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block) for (i = 0; i < 8; i++) { if (bits & (1 << i)) { pmirq = block * 8 + i; - irq = irq_find_mapping(chip->irqdomain, pmirq); - generic_handle_irq(irq); + generic_handle_domain_irq(chip->irqdomain, pmirq); } } return 0; @@ -170,19 +170,16 @@ static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master) return ret; } -static void pm8xxx_irq_handler(struct irq_desc *desc) +static irqreturn_t pm8xxx_irq_handler(int irq, void *data) { - struct pm_irq_chip *chip = irq_desc_get_handler_data(desc); - struct irq_chip *irq_chip = irq_desc_get_chip(desc); + struct pm_irq_chip *chip = data; unsigned int root; int i, ret, masters = 0; - chained_irq_enter(irq_chip, desc); - ret = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_ROOT, &root); if (ret) { pr_err("Can't read root status ret=%d\n", ret); - return; + return IRQ_NONE; } /* on pm8xxx series masters start from bit 1 of the root */ @@ -193,13 +190,13 @@ static void pm8xxx_irq_handler(struct irq_desc *desc) if (masters & (1 << i)) pm8xxx_irq_master_handler(chip, i); - chained_irq_exit(irq_chip, desc); + return IRQ_HANDLED; } static void pm8821_irq_block_handler(struct pm_irq_chip *chip, int master, int block) { - int pmirq, irq, i, ret; + int pmirq, i, ret; unsigned int bits; ret = regmap_read(chip->regmap, @@ -216,8 +213,7 @@ static void pm8821_irq_block_handler(struct pm_irq_chip *chip, for (i = 0; i < 8; i++) { if (bits & BIT(i)) { pmirq = block * 8 + i; - irq = irq_find_mapping(chip->irqdomain, pmirq); - generic_handle_irq(irq); + generic_handle_domain_irq(chip->irqdomain, pmirq); } } } @@ -232,19 +228,17 @@ static inline void pm8821_irq_master_handler(struct pm_irq_chip *chip, pm8821_irq_block_handler(chip, master, block); } -static void pm8821_irq_handler(struct irq_desc *desc) +static irqreturn_t pm8821_irq_handler(int irq, void *data) { - struct pm_irq_chip *chip = irq_desc_get_handler_data(desc); - struct irq_chip *irq_chip = irq_desc_get_chip(desc); + struct pm_irq_chip *chip = data; unsigned int master; int ret; - chained_irq_enter(irq_chip, desc); ret = regmap_read(chip->regmap, PM8821_SSBI_REG_ADDR_IRQ_MASTER0, &master); if (ret) { pr_err("Failed to read master 0 ret=%d\n", ret); - goto done; + return IRQ_NONE; } /* bits 1 through 7 marks the first 7 blocks in master 0 */ @@ -253,19 +247,18 @@ static void pm8821_irq_handler(struct irq_desc *desc) /* bit 0 marks if master 1 contains any bits */ if (!(master & BIT(0))) - goto done; + return IRQ_NONE; ret = regmap_read(chip->regmap, PM8821_SSBI_REG_ADDR_IRQ_MASTER1, &master); if (ret) { pr_err("Failed to read master 1 ret=%d\n", ret); - goto done; + return IRQ_NONE; } pm8821_irq_master_handler(chip, 1, master); -done: - chained_irq_exit(irq_chip, desc); + return IRQ_HANDLED; } static void pm8xxx_irq_mask_ack(struct irq_data *d) @@ -329,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; @@ -339,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); @@ -354,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; } @@ -505,7 +499,6 @@ static const struct pm_irq_data pm8821_data = { }; static const struct of_device_id pm8xxx_id_table[] = { - { .compatible = "qcom,pm8018", .data = &pm8xxx_data}, { .compatible = "qcom,pm8058", .data = &pm8xxx_data}, { .compatible = "qcom,pm8821", .data = &pm8821_data}, { .compatible = "qcom,pm8921", .data = &pm8xxx_data}, @@ -519,7 +512,6 @@ static int pm8xxx_probe(struct platform_device *pdev) struct regmap *regmap; int irq, rc; unsigned int val; - u32 rev; struct pm_irq_chip *chip; data = of_device_get_match_data(&pdev->dev); @@ -544,7 +536,6 @@ static int pm8xxx_probe(struct platform_device *pdev) return rc; } pr_info("PMIC revision 1: %02X\n", val); - rev = val; /* Read PMIC chip revision 2 */ rc = regmap_read(regmap, REG_HWREV_2, &val); @@ -554,7 +545,6 @@ static int pm8xxx_probe(struct platform_device *pdev) return rc; } pr_info("PMIC revision 2: %02X\n", val); - rev |= val << BITS_PER_BYTE; chip = devm_kzalloc(&pdev->dev, struct_size(chip, config, data->num_irqs), @@ -569,21 +559,20 @@ 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; - irq_set_chained_handler_and_data(irq, data->irq_handler, chip); + rc = devm_request_irq(&pdev->dev, irq, data->irq_handler, 0, dev_name(&pdev->dev), chip); + if (rc) + return rc; + irq_set_irq_wake(irq, 1); rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); - if (rc) { - irq_set_chained_handler_and_data(irq, NULL, NULL); + if (rc) irq_domain_remove(chip->irqdomain); - } return rc; } @@ -594,16 +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) { - int irq = platform_get_irq(pdev, 0); struct pm_irq_chip *chip = platform_get_drvdata(pdev); device_for_each_child(&pdev->dev, NULL, pm8xxx_remove_child); - irq_set_chained_handler_and_data(irq, NULL, NULL); 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 a35d5cf16faa..b4b178caf754 100644 --- a/drivers/mfd/qcom-spmi-pmic.c +++ b/drivers/mfd/qcom-spmi-pmic.c @@ -3,103 +3,199 @@ * Copyright (c) 2014, The Linux Foundation. All rights reserved. */ +#include <linux/device.h> +#include <linux/errno.h> +#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 #define PMIC_REV3 0x102 #define PMIC_REV4 0x103 #define PMIC_TYPE 0x104 #define PMIC_SUBTYPE 0x105 +#define PMIC_FAB_ID 0x1f2 #define PMIC_TYPE_VALUE 0x51 -#define COMMON_SUBTYPE 0x00 -#define PM8941_SUBTYPE 0x01 -#define PM8841_SUBTYPE 0x02 -#define PM8019_SUBTYPE 0x03 -#define PM8226_SUBTYPE 0x04 -#define PM8110_SUBTYPE 0x05 -#define PMA8084_SUBTYPE 0x06 -#define PMI8962_SUBTYPE 0x07 -#define PMD9635_SUBTYPE 0x08 -#define PM8994_SUBTYPE 0x09 -#define PMI8994_SUBTYPE 0x0a -#define PM8916_SUBTYPE 0x0b -#define PM8004_SUBTYPE 0x0c -#define PM8909_SUBTYPE 0x0d -#define PM8950_SUBTYPE 0x10 -#define PMI8950_SUBTYPE 0x11 -#define PM8998_SUBTYPE 0x14 -#define PMI8998_SUBTYPE 0x15 -#define PM8005_SUBTYPE 0x18 -#define PM660L_SUBTYPE 0x1A -#define PM660_SUBTYPE 0x1B +#define PMIC_REV4_V2 0x02 + +struct qcom_spmi_dev { + int num_usids; + 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[] = { - { .compatible = "qcom,spmi-pmic", .data = (void *)COMMON_SUBTYPE }, - { .compatible = "qcom,pm8941", .data = (void *)PM8941_SUBTYPE }, - { .compatible = "qcom,pm8841", .data = (void *)PM8841_SUBTYPE }, - { .compatible = "qcom,pm8019", .data = (void *)PM8019_SUBTYPE }, - { .compatible = "qcom,pm8226", .data = (void *)PM8226_SUBTYPE }, - { .compatible = "qcom,pm8110", .data = (void *)PM8110_SUBTYPE }, - { .compatible = "qcom,pma8084", .data = (void *)PMA8084_SUBTYPE }, - { .compatible = "qcom,pmi8962", .data = (void *)PMI8962_SUBTYPE }, - { .compatible = "qcom,pmd9635", .data = (void *)PMD9635_SUBTYPE }, - { .compatible = "qcom,pm8994", .data = (void *)PM8994_SUBTYPE }, - { .compatible = "qcom,pmi8994", .data = (void *)PMI8994_SUBTYPE }, - { .compatible = "qcom,pm8916", .data = (void *)PM8916_SUBTYPE }, - { .compatible = "qcom,pm8004", .data = (void *)PM8004_SUBTYPE }, - { .compatible = "qcom,pm8909", .data = (void *)PM8909_SUBTYPE }, - { .compatible = "qcom,pm8950", .data = (void *)PM8950_SUBTYPE }, - { .compatible = "qcom,pmi8950", .data = (void *)PMI8950_SUBTYPE }, - { .compatible = "qcom,pm8998", .data = (void *)PM8998_SUBTYPE }, - { .compatible = "qcom,pmi8998", .data = (void *)PMI8998_SUBTYPE }, - { .compatible = "qcom,pm8005", .data = (void *)PM8005_SUBTYPE }, - { .compatible = "qcom,pm660l", .data = (void *)PM660L_SUBTYPE }, - { .compatible = "qcom,pm660", .data = (void *)PM660_SUBTYPE }, + { .compatible = "qcom,pm660", .data = N_USIDS(2) }, + { .compatible = "qcom,pm660l", .data = N_USIDS(2) }, + { .compatible = "qcom,pm8004", .data = N_USIDS(2) }, + { .compatible = "qcom,pm8005", .data = N_USIDS(2) }, + { .compatible = "qcom,pm8019", .data = N_USIDS(2) }, + { .compatible = "qcom,pm8028", .data = N_USIDS(2) }, + { .compatible = "qcom,pm8110", .data = N_USIDS(2) }, + { .compatible = "qcom,pm8150", .data = N_USIDS(2) }, + { .compatible = "qcom,pm8150b", .data = N_USIDS(2) }, + { .compatible = "qcom,pm8150c", .data = N_USIDS(2) }, + { .compatible = "qcom,pm8150l", .data = N_USIDS(2) }, + { .compatible = "qcom,pm8226", .data = N_USIDS(2) }, + { .compatible = "qcom,pm8841", .data = N_USIDS(2) }, + { .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) }, + { .compatible = "qcom,pm8998", .data = N_USIDS(2) }, + { .compatible = "qcom,pma8084", .data = N_USIDS(2) }, + { .compatible = "qcom,pmd9635", .data = N_USIDS(2) }, + { .compatible = "qcom,pmi8950", .data = N_USIDS(2) }, + { .compatible = "qcom,pmi8962", .data = N_USIDS(2) }, + { .compatible = "qcom,pmi8994", .data = N_USIDS(2) }, + { .compatible = "qcom,pmi8998", .data = N_USIDS(2) }, + { .compatible = "qcom,pmk8002", .data = N_USIDS(2) }, + { .compatible = "qcom,pmp8074", .data = N_USIDS(2) }, + { .compatible = "qcom,smb2351", .data = N_USIDS(2) }, + { .compatible = "qcom,spmi-pmic", .data = N_USIDS(1) }, { } }; -static void pmic_spmi_show_revid(struct regmap *map, struct device *dev) +/* + * A PMIC can be represented by multiple SPMI devices, but + * only the base PMIC device will contain a reference to + * the revision information. + * + * This function takes a pointer to a pmic device and + * returns a pointer to the base PMIC device. + * + * This only supports PMICs with 1 or 2 USIDs. + */ +static struct spmi_device *qcom_pmic_get_base_usid(struct spmi_device *sdev, struct qcom_spmi_dev *ctx) { - unsigned int rev2, minor, major, type, subtype; - const char *name = "unknown"; - int ret, i; + struct device_node *spmi_bus; + int function_parent_usid, ret; + u32 pmic_addr; - ret = regmap_read(map, PMIC_TYPE, &type); - if (ret < 0) - return; + /* + * 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) { + get_device(&sdev->dev); + return sdev; + } - if (type != PMIC_TYPE_VALUE) - return; + function_parent_usid = sdev->usid; - ret = regmap_read(map, PMIC_SUBTYPE, &subtype); - if (ret < 0) - return; + /* + * Walk through the list of PMICs until we find the sibling USID. + * The goal is to find the first USID which is less than the + * number of USIDs in the PMIC array, e.g. for a PMIC with 2 USIDs + * where the function device is under USID 3, we want to find the + * device for USID 2. + */ + spmi_bus = of_get_parent(sdev->dev.of_node); + 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; + } - for (i = 0; i < ARRAY_SIZE(pmic_spmi_id_table); i++) { - if (subtype == (unsigned long)pmic_spmi_id_table[i].data) + if (pmic_addr == function_parent_usid - (ctx->num_usids - 1)) { + sdev = spmi_find_device_by_of_node(child); + if (!sdev) { + /* + * If the base USID for this PMIC hasn't been + * registered yet then we need to defer. + */ + sdev = ERR_PTR(-EPROBE_DEFER); + } break; + } + } + + 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); - if (i != ARRAY_SIZE(pmic_spmi_id_table)) - name = pmic_spmi_id_table[i].compatible; + return ret; +} - ret = regmap_read(map, PMIC_REV2, &rev2); +static int pmic_spmi_load_revid(struct regmap *map, struct device *dev, + struct qcom_spmi_pmic *pmic) +{ + int ret; + + ret = regmap_read(map, PMIC_TYPE, &pmic->type); + if (ret < 0) + return ret; + + if (pmic->type != PMIC_TYPE_VALUE) + return ret; + + ret = regmap_read(map, PMIC_SUBTYPE, &pmic->subtype); + if (ret < 0) + return ret; + + pmic->name = of_match_device(pmic_spmi_id_table, dev)->compatible; + + ret = regmap_read(map, PMIC_REV2, &pmic->rev2); if (ret < 0) - return; + return ret; - ret = regmap_read(map, PMIC_REV3, &minor); + ret = regmap_read(map, PMIC_REV3, &pmic->minor); if (ret < 0) - return; + return ret; - ret = regmap_read(map, PMIC_REV4, &major); + ret = regmap_read(map, PMIC_REV4, &pmic->major); if (ret < 0) - return; + return ret; + + if (pmic->subtype == PMI8998_SUBTYPE || pmic->subtype == PM660_SUBTYPE) { + ret = regmap_read(map, PMIC_FAB_ID, &pmic->fab_id); + if (ret < 0) + return ret; + } /* * In early versions of PM8941 and PM8226, the major revision number @@ -107,16 +203,46 @@ static void pmic_spmi_show_revid(struct regmap *map, struct device *dev) * Increment the major revision number here if the chip is an early * version of PM8941 or PM8226. */ - if ((subtype == PM8941_SUBTYPE || subtype == PM8226_SUBTYPE) && - major < 0x02) - major++; + if ((pmic->subtype == PM8941_SUBTYPE || pmic->subtype == PM8226_SUBTYPE) && + pmic->major < PMIC_REV4_V2) + pmic->major++; + + if (pmic->subtype == PM8110_SUBTYPE) + pmic->minor = pmic->rev2; - if (subtype == PM8110_SUBTYPE) - minor = rev2; + dev_dbg(dev, "%x: %s v%d.%d\n", + pmic->subtype, pmic->name, pmic->major, pmic->minor); - dev_dbg(dev, "%x: %s v%d.%d\n", subtype, name, major, minor); + return 0; } +/** + * qcom_pmic_get() - Get a pointer to the base PMIC device + * + * This function takes a struct device for a driver which is a child of a PMIC. + * And locates the PMIC revision information for it. + * + * @dev: the pmic function device + * @return: the struct qcom_spmi_pmic* pointer associated with the function device + */ +const struct qcom_spmi_pmic *qcom_pmic_get(struct device *dev) +{ + struct spmi_device *sdev; + struct qcom_spmi_dev *spmi; + + /* + * Make sure the device is actually a child of a PMIC + */ + if (!of_match_device(pmic_spmi_id_table, dev->parent)) + return ERR_PTR(-EINVAL); + + sdev = to_spmi_device(dev->parent); + spmi = dev_get_drvdata(&sdev->dev); + + return &spmi->pmic; +} +EXPORT_SYMBOL_GPL(qcom_pmic_get); + static const struct regmap_config spmi_regmap_config = { .reg_bits = 16, .val_bits = 8, @@ -127,22 +253,49 @@ static const struct regmap_config spmi_regmap_config = { static int pmic_spmi_probe(struct spmi_device *sdev) { struct regmap *regmap; + struct qcom_spmi_dev *ctx; + int ret; regmap = devm_regmap_init_spmi_ext(sdev, &spmi_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); + ctx = devm_kzalloc(&sdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + 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 % 2 == 0) - pmic_spmi_show_revid(regmap, &sdev->dev); + 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 71bc34b74bc9..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,9 +530,7 @@ 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 resource *res; struct qcom_rpm *rpm; u32 fw_version[3]; int irq_wakeup; @@ -547,7 +547,7 @@ static int qcom_rpm_probe(struct platform_device *pdev) init_completion(&rpm->ack); /* Enable message RAM clock */ - rpm->ramclk = devm_clk_get(&pdev->dev, "ram"); + rpm->ramclk = devm_clk_get_enabled(&pdev->dev, "ram"); if (IS_ERR(rpm->ramclk)) { ret = PTR_ERR(rpm->ramclk); if (ret == -EPROBE_DEFER) @@ -558,7 +558,6 @@ static int qcom_rpm_probe(struct platform_device *pdev) */ rpm->ramclk = NULL; } - clk_prepare_enable(rpm->ramclk); /* Accepts NULL */ irq_ack = platform_get_irq_byname(pdev, "ack"); if (irq_ack < 0) @@ -572,13 +571,11 @@ 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; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - rpm->status_regs = devm_ioremap_resource(&pdev->dev, res); + rpm->status_regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(rpm->status_regs)) return PTR_ERR(rpm->status_regs); rpm->ctrl_regs = rpm->status_regs + 0x400; @@ -673,22 +670,11 @@ static int qcom_rpm_probe(struct platform_device *pdev) if (ret) dev_warn(&pdev->dev, "failed to mark wakeup irq as wakeup\n"); - return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); -} - -static int qcom_rpm_remove(struct platform_device *pdev) -{ - struct qcom_rpm *rpm = dev_get_drvdata(&pdev->dev); - - of_platform_depopulate(&pdev->dev); - clk_disable_unprepare(rpm->ramclk); - - return 0; + return devm_of_platform_populate(&pdev->dev); } static struct platform_driver qcom_rpm_driver = { .probe = qcom_rpm_probe, - .remove = qcom_rpm_remove, .driver = { .name = "qcom_rpm", .of_match_table = qcom_rpm_of_match, 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-irq.c b/drivers/mfd/rc5t583-irq.c index b374a3d34688..97f52b671520 100644 --- a/drivers/mfd/rc5t583-irq.c +++ b/drivers/mfd/rc5t583-irq.c @@ -8,9 +8,9 @@ * based on code * Copyright (C) 2011 RICOH COMPANY,LTD */ +#include <linux/device.h> #include <linux/interrupt.h> #include <linux/irq.h> -#include <linux/i2c.h> #include <linux/mfd/rc5t583.h> enum int_type { @@ -228,15 +228,12 @@ static void rc5t583_irq_sync_unlock(struct irq_data *irq_data) mutex_unlock(&rc5t583->irq_lock); } -#ifdef CONFIG_PM_SLEEP + static int rc5t583_irq_set_wake(struct irq_data *irq_data, unsigned int on) { struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data); return irq_set_irq_wake(rc5t583->chip_irq, on); } -#else -#define rc5t583_irq_set_wake NULL -#endif static irqreturn_t rc5t583_irq(int irq, void *data) { @@ -317,7 +314,7 @@ static struct irq_chip rc5t583_irq_chip = { .irq_bus_lock = rc5t583_irq_lock, .irq_bus_sync_unlock = rc5t583_irq_sync_unlock, .irq_set_type = rc5t583_irq_set_type, - .irq_set_wake = rc5t583_irq_set_wake, + .irq_set_wake = pm_sleep_ptr(rc5t583_irq_set_wake), }; int rc5t583_irq_init(struct rc5t583 *rc5t583, int irq, int irq_base) diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c index d0dc48f99096..2c0e8e9630f7 100644 --- a/drivers/mfd/rc5t583.c +++ b/drivers/mfd/rc5t583.c @@ -230,11 +230,10 @@ 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, - const struct i2c_device_id *id) +static int rc5t583_i2c_probe(struct i2c_client *i2c) { struct rc5t583 *rc5t583; struct rc5t583_platform_data *pdata = dev_get_platdata(&i2c->dev); diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c index c748fd29a220..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, @@ -227,7 +227,7 @@ static const struct regmap_config retu_config = { .val_bits = 16, }; -static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +static int retu_probe(struct i2c_client *i2c) { struct retu_data const *rdat; struct retu_dev *rdev; @@ -287,7 +287,7 @@ static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id) return 0; } -static int retu_remove(struct i2c_client *i2c) +static void retu_remove(struct i2c_client *i2c) { struct retu_dev *rdev = i2c_get_clientdata(i2c); @@ -297,13 +297,11 @@ static int retu_remove(struct i2c_client *i2c) } mfd_remove_devices(rdev->dev); regmap_del_irq_chip(i2c->irq, rdev->irq_data); - - return 0; } 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/rk808.c b/drivers/mfd/rk8xx-core.c index 77ccd31ca1d9..def4587fdfb8 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk8xx-core.c @@ -1,24 +1,23 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * MFD core driver for Rockchip RK808/RK818 + * MFD core driver for Rockchip RK8XX * * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * Copyright (C) 2016 PHYTEC Messtechnik GmbH * * Author: Chris Zhong <zyw@rock-chips.com> * Author: Zhang Qing <zhangqing@rock-chips.com> - * - * Copyright (C) 2016 PHYTEC Messtechnik GmbH - * * Author: Wadim Egorov <w.egorov@phytec.de> */ -#include <linux/i2c.h> +#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> struct rk808_reg_data { int addr; @@ -26,92 +25,14 @@ struct rk808_reg_data { int value; }; -static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg) -{ - /* - * Notes: - * - Technically the ROUND_30s bit makes RTC_CTRL_REG volatile, but - * we don't use that feature. It's better to cache. - * - It's unlikely we care that RK808_DEVCTRL_REG is volatile since - * bits are cleared in case when we shutoff anyway, but better safe. - */ - - switch (reg) { - case RK808_SECONDS_REG ... RK808_WEEKS_REG: - case RK808_RTC_STATUS_REG: - case RK808_VB_MON_REG: - case RK808_THERMAL_REG: - case RK808_DCDC_UV_STS_REG: - case RK808_LDO_UV_STS_REG: - case RK808_DCDC_PG_REG: - case RK808_LDO_PG_REG: - case RK808_DEVCTRL_REG: - case RK808_INT_STS_REG1: - case RK808_INT_STS_REG2: - return true; - } - - return false; -} - -static bool rk817_is_volatile_reg(struct device *dev, unsigned int reg) -{ - /* - * Notes: - * - 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 RK817_SECONDS_REG ... RK817_WEEKS_REG: - case RK817_RTC_STATUS_REG: - case RK817_CODEC_DTOP_LPT_SRST: - case RK817_INT_STS_REG0: - case RK817_INT_STS_REG1: - case RK817_INT_STS_REG2: - case RK817_SYS_STS: - return true; - } - - return true; -} - -static const struct regmap_config rk818_regmap_config = { - .reg_bits = 8, - .val_bits = 8, - .max_register = RK818_USB_CTRL_REG, - .cache_type = REGCACHE_RBTREE, - .volatile_reg = rk808_is_volatile_reg, -}; - -static const struct regmap_config rk805_regmap_config = { - .reg_bits = 8, - .val_bits = 8, - .max_register = RK805_OFF_SOURCE_REG, - .cache_type = REGCACHE_RBTREE, - .volatile_reg = rk808_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, - .volatile_reg = rk808_is_volatile_reg, -}; - -static const struct regmap_config rk817_regmap_config = { - .reg_bits = 8, - .val_bits = 8, - .max_register = RK817_GPIO_INT_CFG, - .cache_type = REGCACHE_NONE, - .volatile_reg = rk817_is_volatile_reg, -}; - 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), }; @@ -121,9 +42,19 @@ static const struct resource rk805_key_resources[] = { DEFINE_RES_IRQ(RK805_IRQ_PWRON_FALL), }; +static struct resource rk806_pwrkey_resources[] = { + DEFINE_RES_IRQ(RK806_IRQ_PWRON_FALL), + DEFINE_RES_IRQ(RK806_IRQ_PWRON_RISE), +}; + 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[] = { + DEFINE_RES_IRQ(RK817_IRQ_PLUG_IN), + DEFINE_RES_IRQ(RK817_IRQ_PLUG_OUT), }; static const struct mfd_cell rk805s[] = { @@ -141,6 +72,16 @@ static const struct mfd_cell rk805s[] = { }, }; +static const struct mfd_cell rk806s[] = { + { .name = "rk805-pinctrl", }, + { .name = "rk808-regulator", }, + { + .name = "rk805-pwrkey", + .resources = rk806_pwrkey_resources, + .num_resources = ARRAY_SIZE(rk806_pwrkey_resources), + }, +}; + static const struct mfd_cell rk808s[] = { { .name = "rk808-clkout", }, { .name = "rk808-regulator", }, @@ -151,9 +92,25 @@ static const struct mfd_cell rk808s[] = { }, }; +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",}, - { .name = "rk808-regulator",}, + { .name = "rk808-clkout", }, + { .name = "rk808-regulator", }, { .name = "rk805-pwrkey", .num_resources = ARRAY_SIZE(rk817_pwrkey_resources), @@ -164,7 +121,12 @@ static const struct mfd_cell rk817s[] = { .num_resources = ARRAY_SIZE(rk817_rtc_resources), .resources = &rk817_rtc_resources[0], }, - { .name = "rk817-codec",}, + { .name = "rk817-codec", }, + { + .name = "rk817-charger", + .num_resources = ARRAY_SIZE(rk817_charger_resources), + .resources = &rk817_charger_resources[0], + }, }; static const struct mfd_cell rk818s[] = { @@ -190,6 +152,12 @@ static const struct rk808_reg_data rk805_pre_init_reg[] = { {RK805_THERMAL_REG, TEMP_HOTDIE_MSK, TEMP115C}, }; +static const struct rk808_reg_data rk806_pre_init_reg[] = { + { RK806_GPIO_INT_CONFIG, RK806_INT_POL_MSK, RK806_INT_POL_L }, + { RK806_SYS_CFG3, RK806_SLAVE_RESTART_FUN_MSK, RK806_SLAVE_RESTART_FUN_EN }, + { RK806_SYS_OPTION, RK806_SYS_ENB2_2M_MSK, RK806_SYS_ENB2_2M_EN }, +}; + static const struct rk808_reg_data rk808_pre_init_reg[] = { { RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_150MA }, { RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_200MA }, @@ -201,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 */ @@ -340,6 +319,27 @@ static const struct regmap_irq rk805_irqs[] = { }, }; +static const struct regmap_irq rk806_irqs[] = { + /* INT_STS0 IRQs */ + REGMAP_IRQ_REG(RK806_IRQ_PWRON_FALL, 0, RK806_INT_STS_PWRON_FALL), + REGMAP_IRQ_REG(RK806_IRQ_PWRON_RISE, 0, RK806_INT_STS_PWRON_RISE), + REGMAP_IRQ_REG(RK806_IRQ_PWRON, 0, RK806_INT_STS_PWRON), + REGMAP_IRQ_REG(RK806_IRQ_PWRON_LP, 0, RK806_INT_STS_PWRON_LP), + REGMAP_IRQ_REG(RK806_IRQ_HOTDIE, 0, RK806_INT_STS_HOTDIE), + REGMAP_IRQ_REG(RK806_IRQ_VDC_RISE, 0, RK806_INT_STS_VDC_RISE), + REGMAP_IRQ_REG(RK806_IRQ_VDC_FALL, 0, RK806_INT_STS_VDC_FALL), + REGMAP_IRQ_REG(RK806_IRQ_VB_LO, 0, RK806_INT_STS_VB_LO), + /* INT_STS1 IRQs */ + REGMAP_IRQ_REG(RK806_IRQ_REV0, 1, RK806_INT_STS_REV0), + REGMAP_IRQ_REG(RK806_IRQ_REV1, 1, RK806_INT_STS_REV1), + REGMAP_IRQ_REG(RK806_IRQ_REV2, 1, RK806_INT_STS_REV2), + REGMAP_IRQ_REG(RK806_IRQ_CRC_ERROR, 1, RK806_INT_STS_CRC_ERROR), + REGMAP_IRQ_REG(RK806_IRQ_SLP3_GPIO, 1, RK806_INT_STS_SLP3_GPIO), + REGMAP_IRQ_REG(RK806_IRQ_SLP2_GPIO, 1, RK806_INT_STS_SLP2_GPIO), + REGMAP_IRQ_REG(RK806_IRQ_SLP1_GPIO, 1, RK806_INT_STS_SLP1_GPIO), + REGMAP_IRQ_REG(RK806_IRQ_WDT, 1, RK806_INT_STS_WDT), +}; + static const struct regmap_irq rk808_irqs[] = { /* INT_STS */ [RK808_IRQ_VOUT_LO] = { @@ -382,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] = { @@ -479,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), @@ -490,6 +543,18 @@ static struct regmap_irq_chip rk805_irq_chip = { .init_ack_masked = true, }; +static const struct regmap_irq_chip rk806_irq_chip = { + .name = "rk806", + .irqs = rk806_irqs, + .num_irqs = ARRAY_SIZE(rk806_irqs), + .num_regs = 2, + .irq_reg_stride = 2, + .mask_base = RK806_INT_MSK0, + .status_base = RK806_INT_STS0, + .ack_base = RK806_INT_STS0, + .init_ack_masked = true, +}; + static const struct regmap_irq_chip rk808_irq_chip = { .name = "rk808", .irqs = rk808_irqs, @@ -502,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), @@ -526,38 +603,71 @@ static const struct regmap_irq_chip rk818_irq_chip = { .init_ack_masked = true, }; -static struct i2c_client *rk808_i2c_client; - -static void rk808_pm_power_off(void) +static int rk808_power_off(struct sys_off_data *data) { + struct rk808 *rk808 = data->cb_data; int ret; unsigned int reg, bit; - struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); switch (rk808->variant) { case RK805_ID: 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: + case RK817_ID: + reg = RK817_SYS_CFG(3); + bit = DEV_OFF; + break; + case RK816_ID: case RK818_ID: reg = RK818_DEVCTRL_REG; bit = DEV_OFF; break; default: - return; + return NOTIFY_DONE; + } + ret = regmap_update_bits(rk808->regmap, reg, bit, bit); + if (ret) + dev_err(rk808->dev, "Failed to shutdown device!\n"); + + return NOTIFY_DONE; +} + +static int rk808_restart(struct sys_off_data *data) +{ + struct rk808 *rk808 = data->cb_data; + unsigned int reg, bit; + int ret; + + switch (rk808->variant) { + case RK809_ID: + case RK817_ID: + reg = RK817_SYS_CFG(3); + bit = DEV_RST; + break; + + default: + return NOTIFY_DONE; } ret = regmap_update_bits(rk808->regmap, reg, bit, bit); if (ret) - dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n"); + dev_err(rk808->dev, "Failed to restart device!\n"); + + return NOTIFY_DONE; } -static void rk8xx_shutdown(struct i2c_client *client) +void rk8xx_shutdown(struct device *dev) { - struct rk808 *rk808 = i2c_get_clientdata(client); + struct rk808 *rk808 = dev_get_drvdata(dev); int ret; switch (rk808->variant) { @@ -578,84 +688,72 @@ static void rk8xx_shutdown(struct i2c_client *client) return; } if (ret) - dev_warn(&client->dev, + dev_warn(dev, "Cannot switch to power down function\n"); } +EXPORT_SYMBOL_GPL(rk8xx_shutdown); -static const struct of_device_id rk808_of_match[] = { - { .compatible = "rockchip,rk805" }, - { .compatible = "rockchip,rk808" }, - { .compatible = "rockchip,rk809" }, - { .compatible = "rockchip,rk817" }, - { .compatible = "rockchip,rk818" }, - { }, -}; -MODULE_DEVICE_TABLE(of, rk808_of_match); - -static int rk808_probe(struct i2c_client *client, - const struct i2c_device_id *id) +int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regmap *regmap) { - struct device_node *np = client->dev.of_node; struct rk808 *rk808; const struct rk808_reg_data *pre_init_reg; const struct mfd_cell *cells; + int dual_support = 0; int nr_pre_init_regs; + u32 rst_fun = 0; int nr_cells; - int msb, lsb; - unsigned char pmic_id_msb, pmic_id_lsb; int ret; int i; - rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL); + rk808 = devm_kzalloc(dev, sizeof(*rk808), GFP_KERNEL); if (!rk808) return -ENOMEM; - - if (of_device_is_compatible(np, "rockchip,rk817") || - of_device_is_compatible(np, "rockchip,rk809")) { - pmic_id_msb = RK817_ID_MSB; - pmic_id_lsb = RK817_ID_LSB; - } else { - pmic_id_msb = RK808_ID_MSB; - pmic_id_lsb = RK808_ID_LSB; - } - - /* Read chip variant */ - msb = i2c_smbus_read_byte_data(client, pmic_id_msb); - if (msb < 0) { - dev_err(&client->dev, "failed to read the chip id at 0x%x\n", - RK808_ID_MSB); - return msb; - } - - lsb = i2c_smbus_read_byte_data(client, pmic_id_lsb); - if (lsb < 0) { - dev_err(&client->dev, "failed to read the chip id at 0x%x\n", - RK808_ID_LSB); - return lsb; - } - - rk808->variant = ((msb << 8) | lsb) & RK8XX_ID_MSK; - dev_info(&client->dev, "chip id: 0x%x\n", (unsigned int)rk808->variant); + rk808->dev = dev; + rk808->variant = variant; + rk808->regmap = regmap; + dev_set_drvdata(dev, rk808); switch (rk808->variant) { case RK805_ID: - rk808->regmap_cfg = &rk805_regmap_config; rk808->regmap_irq_chip = &rk805_irq_chip; pre_init_reg = rk805_pre_init_reg; nr_pre_init_regs = ARRAY_SIZE(rk805_pre_init_reg); cells = rk805s; nr_cells = ARRAY_SIZE(rk805s); break; + case RK806_ID: + rk808->regmap_irq_chip = &rk806_irq_chip; + pre_init_reg = rk806_pre_init_reg; + nr_pre_init_regs = ARRAY_SIZE(rk806_pre_init_reg); + 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_cfg = &rk808_regmap_config; rk808->regmap_irq_chip = &rk808_irq_chip; pre_init_reg = rk808_pre_init_reg; nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg); 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_cfg = &rk818_regmap_config; rk808->regmap_irq_chip = &rk818_irq_chip; pre_init_reg = rk818_pre_init_reg; nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg); @@ -664,7 +762,6 @@ static int rk808_probe(struct i2c_client *client, break; case RK809_ID: case RK817_ID: - rk808->regmap_cfg = &rk817_regmap_config; rk808->regmap_irq_chip = &rk817_irq_chip; pre_init_reg = rk817_pre_init_reg; nr_pre_init_regs = ARRAY_SIZE(rk817_pre_init_reg); @@ -672,85 +769,65 @@ static int rk808_probe(struct i2c_client *client, nr_cells = ARRAY_SIZE(rk817s); break; default: - dev_err(&client->dev, "Unsupported RK8XX ID %lu\n", - rk808->variant); + dev_err(dev, "Unsupported RK8XX ID %lu\n", rk808->variant); return -EINVAL; } - rk808->i2c = client; - i2c_set_clientdata(client, rk808); + if (!irq) + return dev_err_probe(dev, -EINVAL, "No interrupt support, no core IRQ\n"); - rk808->regmap = devm_regmap_init_i2c(client, rk808->regmap_cfg); - if (IS_ERR(rk808->regmap)) { - dev_err(&client->dev, "regmap initialization failed\n"); - return PTR_ERR(rk808->regmap); - } - - if (!client->irq) { - dev_err(&client->dev, "No interrupt support, no core IRQ\n"); - return -EINVAL; - } - - ret = regmap_add_irq_chip(rk808->regmap, client->irq, - IRQF_ONESHOT, -1, - rk808->regmap_irq_chip, &rk808->irq_data); - if (ret) { - dev_err(&client->dev, "Failed to add irq_chip %d\n", ret); - return ret; - } + ret = devm_regmap_add_irq_chip(dev, rk808->regmap, irq, + IRQF_ONESHOT | dual_support, -1, + rk808->regmap_irq_chip, &rk808->irq_data); + if (ret) + return dev_err_probe(dev, ret, "Failed to add irq_chip\n"); for (i = 0; i < nr_pre_init_regs; i++) { ret = regmap_update_bits(rk808->regmap, pre_init_reg[i].addr, pre_init_reg[i].mask, pre_init_reg[i].value); - if (ret) { - dev_err(&client->dev, - "0x%x write err\n", - pre_init_reg[i].addr); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "0x%x write err\n", + pre_init_reg[i].addr); } - ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, - 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) { - dev_err(&client->dev, "failed to add MFD devices %d\n", ret); - goto err_irq; - } - - if (of_property_read_bool(np, "rockchip,system-power-controller")) { - rk808_i2c_client = client; - pm_power_off = rk808_pm_power_off; + if (ret) + return dev_err_probe(dev, ret, "failed to add MFD devices\n"); + + 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); + if (ret) + return dev_err_probe(dev, ret, + "failed to register poweroff handler\n"); + + switch (rk808->variant) { + case RK809_ID: + case RK817_ID: + ret = devm_register_sys_off_handler(dev, + SYS_OFF_MODE_RESTART, SYS_OFF_PRIO_HIGH, + &rk808_restart, rk808); + if (ret) + dev_warn(dev, "failed to register rst handler, %d\n", ret); + break; + default: + dev_dbg(dev, "pmic controlled board reset not supported\n"); + break; + } } return 0; - -err_irq: - regmap_del_irq_chip(client->irq, rk808->irq_data); - return ret; -} - -static int rk808_remove(struct i2c_client *client) -{ - struct rk808 *rk808 = i2c_get_clientdata(client); - - regmap_del_irq_chip(client->irq, rk808->irq_data); - - /** - * pm_power_off may points to a function from another module. - * Check if the pointer is set by us and only then overwrite it. - */ - if (pm_power_off == rk808_pm_power_off) - pm_power_off = NULL; - - return 0; } +EXPORT_SYMBOL_GPL(rk8xx_probe); -static int __maybe_unused rk8xx_suspend(struct device *dev) +int rk8xx_suspend(struct device *dev) { - struct rk808 *rk808 = i2c_get_clientdata(to_i2c_client(dev)); + struct rk808 *rk808 = dev_get_drvdata(dev); int ret = 0; switch (rk808->variant) { @@ -773,10 +850,11 @@ static int __maybe_unused rk8xx_suspend(struct device *dev) return ret; } +EXPORT_SYMBOL_GPL(rk8xx_suspend); -static int __maybe_unused rk8xx_resume(struct device *dev) +int rk8xx_resume(struct device *dev) { - struct rk808 *rk808 = i2c_get_clientdata(to_i2c_client(dev)); + struct rk808 *rk808 = dev_get_drvdata(dev); int ret = 0; switch (rk808->variant) { @@ -793,23 +871,10 @@ static int __maybe_unused rk8xx_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(rk8xx_pm_ops, rk8xx_suspend, rk8xx_resume); - -static struct i2c_driver rk808_i2c_driver = { - .driver = { - .name = "rk808", - .of_match_table = rk808_of_match, - .pm = &rk8xx_pm_ops, - }, - .probe = rk808_probe, - .remove = rk808_remove, - .shutdown = rk8xx_shutdown, -}; - -module_i2c_driver(rk808_i2c_driver); +EXPORT_SYMBOL_GPL(rk8xx_resume); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>"); MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>"); MODULE_AUTHOR("Wadim Egorov <w.egorov@phytec.de>"); -MODULE_DESCRIPTION("RK808/RK818 PMIC driver"); +MODULE_DESCRIPTION("RK8xx PMIC core"); diff --git a/drivers/mfd/rk8xx-i2c.c b/drivers/mfd/rk8xx-i2c.c new file mode 100644 index 000000000000..37287b06dab0 --- /dev/null +++ b/drivers/mfd/rk8xx-i2c.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Rockchip RK805/RK808/RK816/RK817/RK818 Core (I2C) driver + * + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * Copyright (C) 2016 PHYTEC Messtechnik GmbH + * + * Author: Chris Zhong <zyw@rock-chips.com> + * Author: Zhang Qing <zhangqing@rock-chips.com> + * Author: Wadim Egorov <w.egorov@phytec.de> + */ + +#include <linux/i2c.h> +#include <linux/mfd/rk808.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> + +struct rk8xx_i2c_platform_data { + const struct regmap_config *regmap_cfg; + 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) +{ + /* + * Notes: + * - Technically the ROUND_30s bit makes RTC_CTRL_REG volatile, but + * we don't use that feature. It's better to cache. + * - It's unlikely we care that RK808_DEVCTRL_REG is volatile since + * bits are cleared in case when we shutoff anyway, but better safe. + */ + + switch (reg) { + case RK808_SECONDS_REG ... RK808_WEEKS_REG: + case RK808_RTC_STATUS_REG: + case RK808_VB_MON_REG: + case RK808_THERMAL_REG: + case RK808_DCDC_UV_STS_REG: + case RK808_LDO_UV_STS_REG: + case RK808_DCDC_PG_REG: + case RK808_LDO_PG_REG: + case RK808_DEVCTRL_REG: + case RK808_INT_STS_REG1: + case RK808_INT_STS_REG2: + return true; + } + + 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) +{ + /* + * Notes: + * - 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 RK817_SECONDS_REG ... RK817_WEEKS_REG: + case RK817_RTC_STATUS_REG: + case RK817_CODEC_DTOP_LPT_SRST: + case RK817_GAS_GAUGE_ADC_CONFIG0 ... RK817_GAS_GAUGE_CUR_ADC_K0: + case RK817_PMIC_CHRG_STS: + case RK817_PMIC_CHRG_OUT: + case RK817_PMIC_CHRG_IN: + case RK817_INT_STS_REG0: + case RK817_INT_STS_REG1: + case RK817_INT_STS_REG2: + case RK817_SYS_STS: + return true; + } + + return false; +} + + +static const struct regmap_config rk818_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RK818_USB_CTRL_REG, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = rk808_is_volatile_reg, +}; + +static const struct regmap_config rk805_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RK805_OFF_SOURCE_REG, + .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_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, + .max_register = RK817_GPIO_INT_CFG, + .cache_type = REGCACHE_NONE, + .volatile_reg = rk817_is_volatile_reg, +}; + +static const struct rk8xx_i2c_platform_data rk805_data = { + .regmap_cfg = &rk805_regmap_config, + .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, +}; + +static const struct rk8xx_i2c_platform_data rk809_data = { + .regmap_cfg = &rk817_regmap_config, + .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, +}; + +static const struct rk8xx_i2c_platform_data rk818_data = { + .regmap_cfg = &rk818_regmap_config, + .variant = RK818_ID, +}; + +static int rk8xx_i2c_probe(struct i2c_client *client) +{ + const struct rk8xx_i2c_platform_data *data; + struct regmap *regmap; + + data = device_get_match_data(&client->dev); + if (!data) + return -ENODEV; + + regmap = devm_regmap_init_i2c(client, data->regmap_cfg); + if (IS_ERR(regmap)) + return dev_err_probe(&client->dev, PTR_ERR(regmap), + "regmap initialization failed\n"); + + return rk8xx_probe(&client->dev, data->variant, client->irq, regmap); +} + +static void rk8xx_i2c_shutdown(struct i2c_client *client) +{ + rk8xx_shutdown(&client->dev); +} + +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 }, + { }, +}; +MODULE_DEVICE_TABLE(of, rk8xx_i2c_of_match); + +static struct i2c_driver rk8xx_i2c_driver = { + .driver = { + .name = "rk8xx-i2c", + .of_match_table = rk8xx_i2c_of_match, + .pm = &rk8xx_i2c_pm_ops, + }, + .probe = rk8xx_i2c_probe, + .shutdown = rk8xx_i2c_shutdown, +}; +module_i2c_driver(rk8xx_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>"); +MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>"); +MODULE_AUTHOR("Wadim Egorov <w.egorov@phytec.de>"); +MODULE_DESCRIPTION("RK8xx I2C PMIC driver"); diff --git a/drivers/mfd/rk8xx-spi.c b/drivers/mfd/rk8xx-spi.c new file mode 100644 index 000000000000..3405fb82ff9f --- /dev/null +++ b/drivers/mfd/rk8xx-spi.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip RK806 Core (SPI) driver + * + * Copyright (c) 2021 Rockchip Electronics Co., Ltd. + * Copyright (c) 2023 Collabora Ltd. + * + * Author: Xu Shengfei <xsf@rock-chips.com> + * Author: Sebastian Reichel <sebastian.reichel@collabora.com> + */ + +#include <linux/interrupt.h> +#include <linux/mfd/core.h> +#include <linux/mfd/rk808.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#define RK806_ADDR_SIZE 2 +#define RK806_CMD_WITH_SIZE(CMD, VALUE_BYTES) \ + (RK806_CMD_##CMD | RK806_CMD_CRC_DIS | (VALUE_BYTES - 1)) + +static const struct regmap_range rk806_volatile_ranges[] = { + regmap_reg_range(RK806_POWER_EN0, RK806_POWER_EN5), + regmap_reg_range(RK806_DVS_START_CTRL, RK806_INT_MSK1), +}; + +static const struct regmap_access_table rk806_volatile_table = { + .yes_ranges = rk806_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(rk806_volatile_ranges), +}; + +static const struct regmap_config rk806_regmap_config_spi = { + .reg_bits = 16, + .val_bits = 8, + .max_register = RK806_BUCK_RSERVE_REG5, + .cache_type = REGCACHE_MAPLE, + .volatile_table = &rk806_volatile_table, +}; + +static int rk806_spi_bus_write(void *context, const void *vdata, size_t count) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct spi_transfer xfer[2] = { 0 }; + /* data and thus count includes the register address */ + size_t val_size = count - RK806_ADDR_SIZE; + char cmd; + + if (val_size < 1 || val_size > (RK806_CMD_LEN_MSK + 1)) + return -EINVAL; + + cmd = RK806_CMD_WITH_SIZE(WRITE, val_size); + + xfer[0].tx_buf = &cmd; + xfer[0].len = sizeof(cmd); + xfer[1].tx_buf = vdata; + xfer[1].len = count; + + return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); +} + +static int rk806_spi_bus_read(void *context, const void *vreg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + char txbuf[3] = { 0 }; + + if (reg_size != RK806_ADDR_SIZE || + val_size < 1 || val_size > (RK806_CMD_LEN_MSK + 1)) + return -EINVAL; + + /* TX buffer contains command byte followed by two address bytes */ + txbuf[0] = RK806_CMD_WITH_SIZE(READ, val_size); + memcpy(txbuf+1, vreg, reg_size); + + return spi_write_then_read(spi, txbuf, sizeof(txbuf), val, val_size); +} + +static const struct regmap_bus rk806_regmap_bus_spi = { + .write = rk806_spi_bus_write, + .read = rk806_spi_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, +}; + +static int rk8xx_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + + regmap = devm_regmap_init(&spi->dev, &rk806_regmap_bus_spi, + &spi->dev, &rk806_regmap_config_spi); + if (IS_ERR(regmap)) + return dev_err_probe(&spi->dev, PTR_ERR(regmap), + "Failed to init regmap\n"); + + return rk8xx_probe(&spi->dev, RK806_ID, spi->irq, regmap); +} + +static const struct of_device_id rk8xx_spi_of_match[] = { + { .compatible = "rockchip,rk806", }, + { } +}; +MODULE_DEVICE_TABLE(of, rk8xx_spi_of_match); + +static const struct spi_device_id rk8xx_spi_id_table[] = { + { "rk806", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, rk8xx_spi_id_table); + +static struct spi_driver rk8xx_spi_driver = { + .driver = { + .name = "rk8xx-spi", + .of_match_table = rk8xx_spi_of_match, + }, + .probe = rk8xx_spi_probe, + .id_table = rk8xx_spi_id_table, +}; +module_spi_driver(rk8xx_spi_driver); + +MODULE_AUTHOR("Xu Shengfei <xsf@rock-chips.com>"); +MODULE_DESCRIPTION("RK8xx SPI PMIC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/rn5t618.c b/drivers/mfd/rn5t618.c index 384acb459427..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[] = { @@ -80,8 +80,7 @@ static const struct regmap_irq_chip rc5t619_irq_chip = { .num_irqs = ARRAY_SIZE(rc5t619_irqs), .num_regs = 1, .status_base = RN5T618_INTMON, - .mask_base = RN5T618_INTEN, - .mask_invert = true, + .unmask_base = RN5T618_INTEN, }; static struct i2c_client *rn5t618_pm_power_off; @@ -180,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; @@ -241,7 +233,7 @@ static int rn5t618_i2c_probe(struct i2c_client *i2c) return rn5t618_irq_init(priv); } -static int rn5t618_i2c_remove(struct i2c_client *i2c) +static void rn5t618_i2c_remove(struct i2c_client *i2c) { if (i2c == rn5t618_pm_power_off) { rn5t618_pm_power_off = NULL; @@ -249,8 +241,6 @@ static int rn5t618_i2c_remove(struct i2c_client *i2c) } unregister_restart_handler(&rn5t618_restart_handler); - - return 0; } static int __maybe_unused rn5t618_i2c_suspend(struct device *dev) @@ -280,10 +270,10 @@ 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_new = rn5t618_i2c_probe, + .probe = rn5t618_i2c_probe, .remove = rn5t618_i2c_remove, }; diff --git a/drivers/mfd/rohm-bd70528.c b/drivers/mfd/rohm-bd70528.c deleted file mode 100644 index 5c44d3b77b3e..000000000000 --- a/drivers/mfd/rohm-bd70528.c +++ /dev/null @@ -1,314 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// -// Copyright (C) 2019 ROHM Semiconductors -// -// ROHM BD70528 PMIC driver - -#include <linux/i2c.h> -#include <linux/interrupt.h> -#include <linux/ioport.h> -#include <linux/irq.h> -#include <linux/mfd/core.h> -#include <linux/mfd/rohm-bd70528.h> -#include <linux/module.h> -#include <linux/of_device.h> -#include <linux/regmap.h> -#include <linux/types.h> - -#define BD70528_NUM_OF_GPIOS 4 - -static const struct resource rtc_irqs[] = { - DEFINE_RES_IRQ_NAMED(BD70528_INT_RTC_ALARM, "bd70528-rtc-alm"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_ELPS_TIM, "bd70528-elapsed-timer"), -}; - -static const struct resource charger_irqs[] = { - DEFINE_RES_IRQ_NAMED(BD70528_INT_BAT_OV_RES, "bd70528-bat-ov-res"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_BAT_OV_DET, "bd70528-bat-ov-det"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_DBAT_DET, "bd70528-bat-dead"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_BATTSD_COLD_RES, "bd70528-bat-warmed"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_BATTSD_COLD_DET, "bd70528-bat-cold"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_BATTSD_HOT_RES, "bd70528-bat-cooled"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_BATTSD_HOT_DET, "bd70528-bat-hot"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_CHG_TSD, "bd70528-chg-tshd"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_BAT_RMV, "bd70528-bat-removed"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_BAT_DET, "bd70528-bat-detected"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_DCIN2_OV_RES, "bd70528-dcin2-ov-res"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_DCIN2_OV_DET, "bd70528-dcin2-ov-det"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_DCIN2_RMV, "bd70528-dcin2-removed"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_DCIN2_DET, "bd70528-dcin2-detected"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_DCIN1_RMV, "bd70528-dcin1-removed"), - DEFINE_RES_IRQ_NAMED(BD70528_INT_DCIN1_DET, "bd70528-dcin1-detected"), -}; - -static struct mfd_cell bd70528_mfd_cells[] = { - { .name = "bd70528-pmic", }, - { .name = "bd70528-gpio", }, - /* - * We use BD71837 driver to drive the clock block. Only differences to - * BD70528 clock gate are the register address and mask. - */ - { .name = "bd70528-clk", }, - { .name = "bd70528-wdt", }, - { - .name = "bd70528-power", - .resources = charger_irqs, - .num_resources = ARRAY_SIZE(charger_irqs), - }, { - .name = "bd70528-rtc", - .resources = rtc_irqs, - .num_resources = ARRAY_SIZE(rtc_irqs), - }, -}; - -static const struct regmap_range volatile_ranges[] = { - { - .range_min = BD70528_REG_INT_MAIN, - .range_max = BD70528_REG_INT_OP_FAIL, - }, { - .range_min = BD70528_REG_RTC_COUNT_H, - .range_max = BD70528_REG_RTC_ALM_REPEAT, - }, { - /* - * WDT control reg is special. Magic values must be written to - * it in order to change the control. Should not be cached. - */ - .range_min = BD70528_REG_WDT_CTRL, - .range_max = BD70528_REG_WDT_CTRL, - }, { - /* - * BD70528 also contains a few other registers which require - * magic sequences to be written in order to update the value. - * At least SHIPMODE, HWRESET, WARMRESET,and STANDBY - */ - .range_min = BD70528_REG_SHIPMODE, - .range_max = BD70528_REG_STANDBY, - }, -}; - -static const struct regmap_access_table volatile_regs = { - .yes_ranges = &volatile_ranges[0], - .n_yes_ranges = ARRAY_SIZE(volatile_ranges), -}; - -static struct regmap_config bd70528_regmap = { - .reg_bits = 8, - .val_bits = 8, - .volatile_table = &volatile_regs, - .max_register = BD70528_MAX_REGISTER, - .cache_type = REGCACHE_RBTREE, -}; - -/* - * Mapping of main IRQ register bits to sub-IRQ register offsets so that we can - * access corect sub-IRQ registers based on bits that are set in main IRQ - * register. - */ - -static unsigned int bit0_offsets[] = {0}; /* Shutdown */ -static unsigned int bit1_offsets[] = {1}; /* Power failure */ -static unsigned int bit2_offsets[] = {2}; /* VR FAULT */ -static unsigned int bit3_offsets[] = {3}; /* PMU interrupts */ -static unsigned int bit4_offsets[] = {4, 5}; /* Charger 1 and Charger 2 */ -static unsigned int bit5_offsets[] = {6}; /* RTC */ -static unsigned int bit6_offsets[] = {7}; /* GPIO */ -static unsigned int bit7_offsets[] = {8}; /* Invalid operation */ - -static struct regmap_irq_sub_irq_map bd70528_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 struct regmap_irq bd70528_irqs[] = { - REGMAP_IRQ_REG(BD70528_INT_LONGPUSH, 0, BD70528_INT_LONGPUSH_MASK), - REGMAP_IRQ_REG(BD70528_INT_WDT, 0, BD70528_INT_WDT_MASK), - REGMAP_IRQ_REG(BD70528_INT_HWRESET, 0, BD70528_INT_HWRESET_MASK), - REGMAP_IRQ_REG(BD70528_INT_RSTB_FAULT, 0, BD70528_INT_RSTB_FAULT_MASK), - REGMAP_IRQ_REG(BD70528_INT_VBAT_UVLO, 0, BD70528_INT_VBAT_UVLO_MASK), - REGMAP_IRQ_REG(BD70528_INT_TSD, 0, BD70528_INT_TSD_MASK), - REGMAP_IRQ_REG(BD70528_INT_RSTIN, 0, BD70528_INT_RSTIN_MASK), - REGMAP_IRQ_REG(BD70528_INT_BUCK1_FAULT, 1, - BD70528_INT_BUCK1_FAULT_MASK), - REGMAP_IRQ_REG(BD70528_INT_BUCK2_FAULT, 1, - BD70528_INT_BUCK2_FAULT_MASK), - REGMAP_IRQ_REG(BD70528_INT_BUCK3_FAULT, 1, - BD70528_INT_BUCK3_FAULT_MASK), - REGMAP_IRQ_REG(BD70528_INT_LDO1_FAULT, 1, BD70528_INT_LDO1_FAULT_MASK), - REGMAP_IRQ_REG(BD70528_INT_LDO2_FAULT, 1, BD70528_INT_LDO2_FAULT_MASK), - REGMAP_IRQ_REG(BD70528_INT_LDO3_FAULT, 1, BD70528_INT_LDO3_FAULT_MASK), - REGMAP_IRQ_REG(BD70528_INT_LED1_FAULT, 1, BD70528_INT_LED1_FAULT_MASK), - REGMAP_IRQ_REG(BD70528_INT_LED2_FAULT, 1, BD70528_INT_LED2_FAULT_MASK), - REGMAP_IRQ_REG(BD70528_INT_BUCK1_OCP, 2, BD70528_INT_BUCK1_OCP_MASK), - REGMAP_IRQ_REG(BD70528_INT_BUCK2_OCP, 2, BD70528_INT_BUCK2_OCP_MASK), - REGMAP_IRQ_REG(BD70528_INT_BUCK3_OCP, 2, BD70528_INT_BUCK3_OCP_MASK), - REGMAP_IRQ_REG(BD70528_INT_LED1_OCP, 2, BD70528_INT_LED1_OCP_MASK), - REGMAP_IRQ_REG(BD70528_INT_LED2_OCP, 2, BD70528_INT_LED2_OCP_MASK), - REGMAP_IRQ_REG(BD70528_INT_BUCK1_FULLON, 2, - BD70528_INT_BUCK1_FULLON_MASK), - REGMAP_IRQ_REG(BD70528_INT_BUCK2_FULLON, 2, - BD70528_INT_BUCK2_FULLON_MASK), - REGMAP_IRQ_REG(BD70528_INT_SHORTPUSH, 3, BD70528_INT_SHORTPUSH_MASK), - REGMAP_IRQ_REG(BD70528_INT_AUTO_WAKEUP, 3, - BD70528_INT_AUTO_WAKEUP_MASK), - REGMAP_IRQ_REG(BD70528_INT_STATE_CHANGE, 3, - BD70528_INT_STATE_CHANGE_MASK), - REGMAP_IRQ_REG(BD70528_INT_BAT_OV_RES, 4, BD70528_INT_BAT_OV_RES_MASK), - REGMAP_IRQ_REG(BD70528_INT_BAT_OV_DET, 4, BD70528_INT_BAT_OV_DET_MASK), - REGMAP_IRQ_REG(BD70528_INT_DBAT_DET, 4, BD70528_INT_DBAT_DET_MASK), - REGMAP_IRQ_REG(BD70528_INT_BATTSD_COLD_RES, 4, - BD70528_INT_BATTSD_COLD_RES_MASK), - REGMAP_IRQ_REG(BD70528_INT_BATTSD_COLD_DET, 4, - BD70528_INT_BATTSD_COLD_DET_MASK), - REGMAP_IRQ_REG(BD70528_INT_BATTSD_HOT_RES, 4, - BD70528_INT_BATTSD_HOT_RES_MASK), - REGMAP_IRQ_REG(BD70528_INT_BATTSD_HOT_DET, 4, - BD70528_INT_BATTSD_HOT_DET_MASK), - REGMAP_IRQ_REG(BD70528_INT_CHG_TSD, 4, BD70528_INT_CHG_TSD_MASK), - REGMAP_IRQ_REG(BD70528_INT_BAT_RMV, 5, BD70528_INT_BAT_RMV_MASK), - REGMAP_IRQ_REG(BD70528_INT_BAT_DET, 5, BD70528_INT_BAT_DET_MASK), - REGMAP_IRQ_REG(BD70528_INT_DCIN2_OV_RES, 5, - BD70528_INT_DCIN2_OV_RES_MASK), - REGMAP_IRQ_REG(BD70528_INT_DCIN2_OV_DET, 5, - BD70528_INT_DCIN2_OV_DET_MASK), - REGMAP_IRQ_REG(BD70528_INT_DCIN2_RMV, 5, BD70528_INT_DCIN2_RMV_MASK), - REGMAP_IRQ_REG(BD70528_INT_DCIN2_DET, 5, BD70528_INT_DCIN2_DET_MASK), - REGMAP_IRQ_REG(BD70528_INT_DCIN1_RMV, 5, BD70528_INT_DCIN1_RMV_MASK), - REGMAP_IRQ_REG(BD70528_INT_DCIN1_DET, 5, BD70528_INT_DCIN1_DET_MASK), - REGMAP_IRQ_REG(BD70528_INT_RTC_ALARM, 6, BD70528_INT_RTC_ALARM_MASK), - REGMAP_IRQ_REG(BD70528_INT_ELPS_TIM, 6, BD70528_INT_ELPS_TIM_MASK), - REGMAP_IRQ_REG(BD70528_INT_GPIO0, 7, BD70528_INT_GPIO0_MASK), - REGMAP_IRQ_REG(BD70528_INT_GPIO1, 7, BD70528_INT_GPIO1_MASK), - REGMAP_IRQ_REG(BD70528_INT_GPIO2, 7, BD70528_INT_GPIO2_MASK), - REGMAP_IRQ_REG(BD70528_INT_GPIO3, 7, BD70528_INT_GPIO3_MASK), - REGMAP_IRQ_REG(BD70528_INT_BUCK1_DVS_OPFAIL, 8, - BD70528_INT_BUCK1_DVS_OPFAIL_MASK), - REGMAP_IRQ_REG(BD70528_INT_BUCK2_DVS_OPFAIL, 8, - BD70528_INT_BUCK2_DVS_OPFAIL_MASK), - REGMAP_IRQ_REG(BD70528_INT_BUCK3_DVS_OPFAIL, 8, - BD70528_INT_BUCK3_DVS_OPFAIL_MASK), - REGMAP_IRQ_REG(BD70528_INT_LED1_VOLT_OPFAIL, 8, - BD70528_INT_LED1_VOLT_OPFAIL_MASK), - REGMAP_IRQ_REG(BD70528_INT_LED2_VOLT_OPFAIL, 8, - BD70528_INT_LED2_VOLT_OPFAIL_MASK), -}; - -static struct regmap_irq_chip bd70528_irq_chip = { - .name = "bd70528_irq", - .main_status = BD70528_REG_INT_MAIN, - .irqs = &bd70528_irqs[0], - .num_irqs = ARRAY_SIZE(bd70528_irqs), - .status_base = BD70528_REG_INT_SHDN, - .mask_base = BD70528_REG_INT_SHDN_MASK, - .ack_base = BD70528_REG_INT_SHDN, - .type_base = BD70528_REG_GPIO1_IN, - .init_ack_masked = true, - .num_regs = 9, - .num_main_regs = 1, - .num_type_reg = 4, - .sub_reg_offsets = &bd70528_sub_irq_offsets[0], - .num_main_status_bits = 8, - .irq_reg_stride = 1, -}; - -static int bd70528_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct bd70528_data *bd70528; - struct regmap_irq_chip_data *irq_data; - int ret, i; - - if (!i2c->irq) { - dev_err(&i2c->dev, "No IRQ configured\n"); - return -EINVAL; - } - - bd70528 = devm_kzalloc(&i2c->dev, sizeof(*bd70528), GFP_KERNEL); - if (!bd70528) - return -ENOMEM; - - mutex_init(&bd70528->rtc_timer_lock); - - dev_set_drvdata(&i2c->dev, &bd70528->chip); - - bd70528->chip.regmap = devm_regmap_init_i2c(i2c, &bd70528_regmap); - if (IS_ERR(bd70528->chip.regmap)) { - dev_err(&i2c->dev, "Failed to initialize Regmap\n"); - return PTR_ERR(bd70528->chip.regmap); - } - - /* - * Disallow type setting for all IRQs by default as most of them do not - * support setting type. - */ - for (i = 0; i < ARRAY_SIZE(bd70528_irqs); i++) - bd70528_irqs[i].type.types_supported = 0; - - /* Set IRQ typesetting information for GPIO pins 0 - 3 */ - for (i = 0; i < BD70528_NUM_OF_GPIOS; i++) { - struct regmap_irq_type *type; - - type = &bd70528_irqs[BD70528_INT_GPIO0 + i].type; - type->type_reg_offset = 2 * i; - type->type_rising_val = 0x20; - type->type_falling_val = 0x10; - type->type_level_high_val = 0x40; - type->type_level_low_val = 0x50; - type->types_supported = (IRQ_TYPE_EDGE_BOTH | - IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW); - } - - ret = devm_regmap_add_irq_chip(&i2c->dev, bd70528->chip.regmap, - i2c->irq, IRQF_ONESHOT, 0, - &bd70528_irq_chip, &irq_data); - if (ret) { - dev_err(&i2c->dev, "Failed to add IRQ chip\n"); - return ret; - } - dev_dbg(&i2c->dev, "Registered %d IRQs for chip\n", - bd70528_irq_chip.num_irqs); - - /* - * BD70528 IRQ controller is not touching the main mask register. - * So enable the GPIO block interrupts at main level. We can just leave - * them enabled as the IRQ controller should disable IRQs from - * sub-registers when IRQ is disabled or freed. - */ - ret = regmap_update_bits(bd70528->chip.regmap, - BD70528_REG_INT_MAIN_MASK, - BD70528_INT_GPIO_MASK, 0); - - ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, - bd70528_mfd_cells, - ARRAY_SIZE(bd70528_mfd_cells), NULL, 0, - regmap_irq_get_domain(irq_data)); - if (ret) - dev_err(&i2c->dev, "Failed to create subdevices\n"); - - return ret; -} - -static const struct of_device_id bd70528_of_match[] = { - { .compatible = "rohm,bd70528", }, - { }, -}; -MODULE_DEVICE_TABLE(of, bd70528_of_match); - -static struct i2c_driver bd70528_drv = { - .driver = { - .name = "rohm-bd70528", - .of_match_table = bd70528_of_match, - }, - .probe = &bd70528_i2c_probe, -}; - -module_i2c_driver(bd70528_drv); - -MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); -MODULE_DESCRIPTION("ROHM BD70528 Power Management IC driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c index 714d9fcbf07b..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,15 +433,14 @@ 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], .num_irqs = ARRAY_SIZE(bd71828_irqs), .status_base = BD71828_REG_INT_BUCK, - .mask_base = BD71828_REG_INT_MASK_BUCK, + .unmask_base = BD71828_REG_INT_MASK_BUCK, .ack_base = BD71828_REG_INT_BUCK, - .mask_invert = true, .init_ack_masked = true, .num_regs = 12, .num_main_regs = 1, @@ -424,15 +449,14 @@ 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], .num_irqs = ARRAY_SIZE(bd71815_irqs), .status_base = BD71815_REG_INT_STAT_01, - .mask_base = BD71815_REG_INT_EN_01, + .unmask_base = BD71815_REG_INT_EN_01, .ack_base = BD71815_REG_INT_STAT_01, - .mask_invert = true, .init_ack_masked = true, .num_regs = 12, .num_main_regs = 1, @@ -466,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; @@ -515,27 +560,24 @@ static int bd71828_i2c_probe(struct i2c_client *i2c) } regmap = devm_regmap_init_i2c(i2c, regmap_config); - if (IS_ERR(regmap)) { - dev_err(&i2c->dev, "Failed to initialize Regmap\n"); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&i2c->dev, PTR_ERR(regmap), + "Failed to initialize Regmap\n"); ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq, IRQF_ONESHOT, 0, irqchip, &irq_data); - if (ret) { - dev_err(&i2c->dev, "Failed to add IRQ chip\n"); - return ret; - } + if (ret) + return dev_err_probe(&i2c->dev, ret, + "Failed to add IRQ chip\n"); dev_dbg(&i2c->dev, "Registered %d IRQs for chip\n", irqchip->num_irqs); if (button_irq) { ret = regmap_irq_get_virq(irq_data, button_irq); - if (ret < 0) { - dev_err(&i2c->dev, "Failed to get the power-key IRQ\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&i2c->dev, ret, + "Failed to get the power-key IRQ\n"); button.irq = ret; } @@ -547,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(&i2c->dev, "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; } @@ -569,7 +624,7 @@ static struct i2c_driver bd71828_drv = { .name = "rohm-bd71828", .of_match_table = bd71828_of_match, }, - .probe_new = &bd71828_i2c_probe, + .probe = bd71828_i2c_probe, }; module_i2c_driver(bd71828_drv); diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c index bfd81f78beae..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), @@ -70,17 +70,15 @@ static struct regmap_irq_chip bd718xx_irq_chip = { .mask_base = BD718XX_REG_MIRQ, .ack_base = BD718XX_REG_IRQ, .init_ack_masked = true, - .mask_invert = false, }; -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 = { @@ -88,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, @@ -127,8 +125,7 @@ static int bd718xx_init_press_duration(struct regmap *regmap, return 0; } -static int bd718xx_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int bd718xx_i2c_probe(struct i2c_client *i2c) { struct regmap *regmap; struct regmap_irq_chip_data *irq_data; @@ -158,18 +155,15 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c, } regmap = devm_regmap_init_i2c(i2c, &bd718xx_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&i2c->dev, "regmap initialization failed\n"); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&i2c->dev, PTR_ERR(regmap), + "regmap initialization failed\n"); ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq, IRQF_ONESHOT, 0, &bd718xx_irq_chip, &irq_data); - if (ret) { - dev_err(&i2c->dev, "Failed to add irq_chip\n"); - return ret; - } + if (ret) + return dev_err_probe(&i2c->dev, ret, "Failed to add irq_chip\n"); ret = bd718xx_init_press_duration(regmap, &i2c->dev); if (ret) @@ -177,10 +171,8 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c, ret = regmap_irq_get_virq(irq_data, BD718XX_INT_PWRBTN_S); - if (ret < 0) { - dev_err(&i2c->dev, "Failed to get the IRQ\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&i2c->dev, ret, "Failed to get the IRQ\n"); button.irq = ret; @@ -188,7 +180,7 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c, mfd, cells, NULL, 0, regmap_irq_get_domain(irq_data)); if (ret) - dev_err(&i2c->dev, "Failed to create subdevices\n"); + dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n"); return ret; } diff --git a/drivers/mfd/rohm-bd9576.c b/drivers/mfd/rohm-bd9576.c index 6661a27d69a8..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> @@ -23,7 +23,7 @@ enum { }; /* - * Due to the BD9576MUF nasty IRQ behaiour we don't always populate IRQs. + * Due to the BD9576MUF nasty IRQ behaviour we don't always populate IRQs. * These will be added to regulator resources only if IRQ information for the * PMIC is populated in device-tree. */ @@ -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), @@ -88,8 +88,7 @@ static struct regmap_irq_chip bd9576_irq_chip = { .irq_reg_stride = 1, }; -static int bd957x_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int bd957x_i2c_probe(struct i2c_client *i2c) { int ret; struct regmap *regmap; @@ -122,10 +121,9 @@ static int bd957x_i2c_probe(struct i2c_client *i2c, } regmap = devm_regmap_init_i2c(i2c, &bd957x_regmap); - if (IS_ERR(regmap)) { - dev_err(&i2c->dev, "Failed to initialize Regmap\n"); - return PTR_ERR(regmap); - } + if (IS_ERR(regmap)) + return dev_err_probe(&i2c->dev, PTR_ERR(regmap), + "Failed to initialize Regmap\n"); /* * BD9576 behaves badly. It kepts IRQ line asserted for the whole @@ -146,10 +144,10 @@ static int bd957x_i2c_probe(struct i2c_client *i2c, ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq, IRQF_ONESHOT, 0, &bd9576_irq_chip, &irq_data); - if (ret) { - dev_err(&i2c->dev, "Failed to add IRQ chip\n"); - return ret; - } + if (ret) + return dev_err_probe(&i2c->dev, ret, + "Failed to add IRQ chip\n"); + domain = regmap_irq_get_domain(irq_data); } else { ret = regmap_update_bits(regmap, BD957X_REG_INT_MAIN_MASK, @@ -163,7 +161,7 @@ static int bd957x_i2c_probe(struct i2c_client *i2c, ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, cells, num_cells, NULL, 0, domain); if (ret) - dev_err(&i2c->dev, "Failed to create subdevices\n"); + dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n"); return ret; } @@ -180,7 +178,7 @@ static struct i2c_driver bd957x_drv = { .name = "rohm-bd957x", .of_match_table = bd957x_of_match, }, - .probe = &bd957x_i2c_probe, + .probe = bd957x_i2c_probe, }; module_i2c_driver(bd957x_drv); 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 = ®ulator_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 = ®ulator_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.h b/drivers/mfd/rsmu.h new file mode 100644 index 000000000000..1bb04cafa45d --- /dev/null +++ b/drivers/mfd/rsmu.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Renesas Synchronization Management Unit (SMU) devices. + * + * Copyright (C) 2021 Integrated Device Technology, Inc., a Renesas Company. + */ + +#ifndef __RSMU_MFD_H +#define __RSMU_MFD_H + +#include <linux/mfd/rsmu.h> + +#define RSMU_CM_SCSR_BASE 0x20100000 + +int rsmu_core_init(struct rsmu_ddata *rsmu); +void rsmu_core_exit(struct rsmu_ddata *rsmu); + +#endif /* __RSMU_MFD_H */ diff --git a/drivers/mfd/rsmu_core.c b/drivers/mfd/rsmu_core.c new file mode 100644 index 000000000000..fd04a6e5dfa3 --- /dev/null +++ b/drivers/mfd/rsmu_core.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Core driver for Renesas Synchronization Management Unit (SMU) devices. + * + * Copyright (C) 2021 Integrated Device Technology, Inc., a Renesas Company. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mfd/core.h> +#include <linux/mfd/rsmu.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include "rsmu.h" + +enum { + RSMU_PHC = 0, + RSMU_CDEV = 1, + RSMU_N_DEVS = 2, +}; + +static struct mfd_cell rsmu_cm_devs[] = { + [RSMU_PHC] = { + .name = "8a3400x-phc", + }, + [RSMU_CDEV] = { + .name = "8a3400x-cdev", + }, +}; + +static struct mfd_cell rsmu_sabre_devs[] = { + [RSMU_PHC] = { + .name = "82p33x1x-phc", + }, + [RSMU_CDEV] = { + .name = "82p33x1x-cdev", + }, +}; + +static struct mfd_cell rsmu_sl_devs[] = { + [RSMU_PHC] = { + .name = "8v19n85x-phc", + }, + [RSMU_CDEV] = { + .name = "8v19n85x-cdev", + }, +}; + +int rsmu_core_init(struct rsmu_ddata *rsmu) +{ + struct mfd_cell *cells; + int ret; + + switch (rsmu->type) { + case RSMU_CM: + cells = rsmu_cm_devs; + break; + case RSMU_SABRE: + cells = rsmu_sabre_devs; + break; + case RSMU_SL: + cells = rsmu_sl_devs; + break; + default: + dev_err(rsmu->dev, "Unsupported RSMU device type: %d\n", rsmu->type); + return -ENODEV; + } + + mutex_init(&rsmu->lock); + + ret = devm_mfd_add_devices(rsmu->dev, PLATFORM_DEVID_AUTO, cells, + RSMU_N_DEVS, NULL, 0, NULL); + if (ret < 0) + dev_err(rsmu->dev, "Failed to register sub-devices: %d\n", ret); + + 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 new file mode 100644 index 000000000000..cba64f107a2f --- /dev/null +++ b/drivers/mfd/rsmu_i2c.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * I2C driver for Renesas Synchronization Management Unit (SMU) devices. + * + * Copyright (C) 2021 Integrated Device Technology, Inc., a Renesas Company. + */ + +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mfd/core.h> +#include <linux/mfd/rsmu.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include "rsmu.h" + +/* + * 32-bit register address: the lower 8 bits of the register address come + * from the offset addr byte and the upper 24 bits come from the page register. + */ +#define RSMU_CM_PAGE_ADDR 0xFC +#define RSMU_CM_PAGE_MASK 0xFFFFFF00 +#define RSMU_CM_ADDRESS_MASK 0x000000FF + +/* + * 15-bit register address: the lower 7 bits of the register address come + * from the offset addr byte and the upper 8 bits come from the page register. + */ +#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, + .range_max = 0x400, + .selector_reg = RSMU_SABRE_PAGE_ADDR, + .selector_mask = 0xFF, + .selector_shift = 0, + .window_start = 0, + .window_len = RSMU_SABRE_PAGE_WINDOW, + } +}; + +static bool rsmu_sabre_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RSMU_SABRE_PAGE_ADDR: + return false; + default: + return true; + } +} + +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]; + int cnt; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = ® + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = bytes; + msg[1].buf = buf; + + cnt = i2c_transfer(client->adapter, msg, 2); + + if (cnt < 0) { + dev_err(rsmu->dev, "i2c_transfer failed at addr: %04x!", reg); + return cnt; + } else if (cnt != 2) { + dev_err(rsmu->dev, + "i2c_transfer sent only %d of 2 messages", cnt); + return -EIO; + } + + return 0; +} + +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); + /* we add 1 byte for device register */ + u8 msg[RSMU_MAX_WRITE_COUNT + 1]; + int cnt; + + if (bytes > RSMU_MAX_WRITE_COUNT) + return -EINVAL; + + msg[0] = reg; + memcpy(&msg[1], buf, bytes); + + cnt = i2c_master_send(client, msg, bytes + 1); + + if (cnt < 0) { + dev_err(&client->dev, + "i2c_master_send failed at addr: %04x!", reg); + return cnt; + } + + return 0; +} + +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]; + int err; + + /* Do not modify offset register for none-scsr registers */ + if (reg < RSMU_CM_SCSR_BASE) + return 0; + + /* Simply return if we are on the same page */ + if (rsmu->page == page) + return 0; + + buf[0] = 0x0; + buf[1] = (u8)((page >> 8) & 0xFF); + buf[2] = (u8)((page >> 16) & 0xFF); + buf[3] = (u8)((page >> 24) & 0xFF); + + err = rsmu_write_device(rsmu, RSMU_CM_PAGE_ADDR, buf, sizeof(buf)); + if (err) + dev_err(rsmu->dev, "Failed to set page offset 0x%x\n", page); + else + /* Remember the last page */ + rsmu->page = page; + + return err; +} + +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, rsmu_i2c_write_device); + if (err) + return err; + + 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_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_i2c_write_device); + if (err) + return err; + + err = rsmu_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 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_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, +}; + +static const struct regmap_config rsmu_sabre_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x400, + .ranges = rsmu_sabre_range_cfg, + .num_ranges = ARRAY_SIZE(rsmu_sabre_range_cfg), + .volatile_reg = rsmu_sabre_volatile_reg, + .cache_type = REGCACHE_MAPLE, + .can_multi_write = true, +}; + +static const struct regmap_config rsmu_sl_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .max_register = 0x340, + .cache_type = REGCACHE_NONE, + .can_multi_write = true, +}; + +static int rsmu_i2c_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + const struct regmap_config *cfg; + struct rsmu_ddata *rsmu; + int ret; + + rsmu = devm_kzalloc(&client->dev, sizeof(*rsmu), GFP_KERNEL); + if (!rsmu) + return -ENOMEM; + + i2c_set_clientdata(client, rsmu); + + rsmu->dev = &client->dev; + rsmu->type = (enum rsmu_type)id->driver_data; + + switch (rsmu->type) { + case RSMU_CM: + 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; + break; + case RSMU_SL: + cfg = &rsmu_sl_regmap_config; + break; + default: + dev_err(rsmu->dev, "Unsupported RSMU device type: %d\n", rsmu->type); + return -ENODEV; + } + + if (rsmu->type == RSMU_CM) + 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); + return ret; + } + + return rsmu_core_init(rsmu); +} + +static void rsmu_i2c_remove(struct i2c_client *client) +{ + struct rsmu_ddata *rsmu = i2c_get_clientdata(client); + + rsmu_core_exit(rsmu); +} + +static const struct i2c_device_id rsmu_i2c_id[] = { + { "8a34000", RSMU_CM }, + { "8a34001", RSMU_CM }, + { "82p33810", RSMU_SABRE }, + { "82p33811", RSMU_SABRE }, + { "8v19n850", RSMU_SL }, + { "8v19n851", RSMU_SL }, + {} +}; +MODULE_DEVICE_TABLE(i2c, rsmu_i2c_id); + +static const struct of_device_id rsmu_i2c_of_match[] = { + { .compatible = "idt,8a34000", .data = (void *)RSMU_CM }, + { .compatible = "idt,8a34001", .data = (void *)RSMU_CM }, + { .compatible = "idt,82p33810", .data = (void *)RSMU_SABRE }, + { .compatible = "idt,82p33811", .data = (void *)RSMU_SABRE }, + { .compatible = "idt,8v19n850", .data = (void *)RSMU_SL }, + { .compatible = "idt,8v19n851", .data = (void *)RSMU_SL }, + {} +}; +MODULE_DEVICE_TABLE(of, rsmu_i2c_of_match); + +static struct i2c_driver rsmu_i2c_driver = { + .driver = { + .name = "rsmu-i2c", + .of_match_table = rsmu_i2c_of_match, + }, + .probe = rsmu_i2c_probe, + .remove = rsmu_i2c_remove, + .id_table = rsmu_i2c_id, +}; + +static int __init rsmu_i2c_init(void) +{ + return i2c_add_driver(&rsmu_i2c_driver); +} +subsys_initcall(rsmu_i2c_init); + +static void __exit rsmu_i2c_exit(void) +{ + i2c_del_driver(&rsmu_i2c_driver); +} +module_exit(rsmu_i2c_exit); + +MODULE_DESCRIPTION("Renesas SMU I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/rsmu_spi.c b/drivers/mfd/rsmu_spi.c new file mode 100644 index 000000000000..39d9be1e141f --- /dev/null +++ b/drivers/mfd/rsmu_spi.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SPI driver for Renesas Synchronization Management Unit (SMU) devices. + * + * Copyright (C) 2021 Integrated Device Technology, Inc., a Renesas Company. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mfd/core.h> +#include <linux/mfd/rsmu.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> + +#include "rsmu.h" + +#define RSMU_CM_PAGE_ADDR 0x7C +#define RSMU_SABRE_PAGE_ADDR 0x7F +#define RSMU_PAGE_MASK 0xFFFFFF80 +#define RSMU_ADDR_MASK 0x7F + +static int rsmu_read_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u16 bytes) +{ + struct spi_device *client = to_spi_device(rsmu->dev); + struct spi_transfer xfer = {0}; + struct spi_message msg; + u8 cmd[RSMU_MAX_READ_COUNT + 1] = {0}; + u8 rsp[RSMU_MAX_READ_COUNT + 1] = {0}; + int ret; + + if (bytes > RSMU_MAX_READ_COUNT) + return -EINVAL; + + cmd[0] = reg | 0x80; + xfer.rx_buf = rsp; + xfer.len = bytes + 1; + xfer.tx_buf = cmd; + xfer.bits_per_word = client->bits_per_word; + xfer.speed_hz = client->max_speed_hz; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + /* + * 4-wire SPI is a shift register, so for every byte you send, + * you get one back at the same time. Example read from 0xC024, + * which has value of 0x2D + * + * MOSI: + * 7C 00 C0 #Set page register + * A4 00 #MSB is set, so this is read command + * MISO: + * XX 2D #XX is a dummy byte from sending A4 and we + * need to throw it away + */ + ret = spi_sync(client, &msg); + if (ret >= 0) + memcpy(buf, &rsp[1], xfer.len-1); + + return ret; +} + +static int rsmu_write_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u16 bytes) +{ + struct spi_device *client = to_spi_device(rsmu->dev); + struct spi_transfer xfer = {0}; + struct spi_message msg; + u8 cmd[RSMU_MAX_WRITE_COUNT + 1] = {0}; + + if (bytes > RSMU_MAX_WRITE_COUNT) + return -EINVAL; + + cmd[0] = reg; + memcpy(&cmd[1], buf, bytes); + + xfer.len = bytes + 1; + xfer.tx_buf = cmd; + xfer.bits_per_word = client->bits_per_word; + xfer.speed_hz = client->max_speed_hz; + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(client, &msg); +} + +/* + * 1-byte (1B) offset addressing: + * 16-bit register address: the lower 7 bits of the register address come + * from the offset addr byte and the upper 9 bits come from the page register. + */ +static int rsmu_write_page_register(struct rsmu_ddata *rsmu, u32 reg) +{ + u8 page_reg; + u8 buf[4]; + u16 bytes; + u32 page; + int err; + + switch (rsmu->type) { + case RSMU_CM: + /* Do not modify page register for none-scsr registers */ + if (reg < RSMU_CM_SCSR_BASE) + 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); + bytes = 4; + break; + case RSMU_SABRE: + /* Do not modify page register if reg is page register itself */ + if ((reg & RSMU_ADDR_MASK) == RSMU_ADDR_MASK) + return 0; + page_reg = RSMU_SABRE_PAGE_ADDR; + page = reg & RSMU_PAGE_MASK; + /* The three page bits are located in the single Page Register */ + buf[0] = (u8)((page >> 7) & 0x7); + bytes = 1; + break; + default: + dev_err(rsmu->dev, "Unsupported RSMU device type: %d\n", rsmu->type); + return -ENODEV; + } + + /* Simply return if we are on the same page */ + if (rsmu->page == page) + return 0; + + err = rsmu_write_device(rsmu, page_reg, buf, bytes); + if (err) + dev_err(rsmu->dev, "Failed to set page offset 0x%x\n", page); + else + /* Remember the last page */ + rsmu->page = page; + + return err; +} + +static int rsmu_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct rsmu_ddata *rsmu = spi_get_drvdata((struct spi_device *)context); + u8 addr = (u8)(reg & RSMU_ADDR_MASK); + int err; + + err = rsmu_write_page_register(rsmu, reg); + if (err) + return err; + + err = rsmu_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) +{ + struct rsmu_ddata *rsmu = spi_get_drvdata((struct spi_device *)context); + u8 addr = (u8)(reg & RSMU_ADDR_MASK); + u8 data = (u8)val; + int err; + + err = rsmu_write_page_register(rsmu, reg); + if (err) + return err; + + err = rsmu_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_cm_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .max_register = 0x20120000, + .reg_read = rsmu_reg_read, + .reg_write = rsmu_reg_write, + .cache_type = REGCACHE_NONE, +}; + +static const struct regmap_config rsmu_sabre_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0x400, + .reg_read = rsmu_reg_read, + .reg_write = rsmu_reg_write, + .cache_type = REGCACHE_NONE, +}; + +static int rsmu_spi_probe(struct spi_device *client) +{ + const struct spi_device_id *id = spi_get_device_id(client); + const struct regmap_config *cfg; + struct rsmu_ddata *rsmu; + int ret; + + rsmu = devm_kzalloc(&client->dev, sizeof(*rsmu), GFP_KERNEL); + if (!rsmu) + return -ENOMEM; + + spi_set_drvdata(client, rsmu); + + rsmu->dev = &client->dev; + rsmu->type = (enum rsmu_type)id->driver_data; + + /* Initialize regmap */ + switch (rsmu->type) { + case RSMU_CM: + cfg = &rsmu_cm_regmap_config; + break; + case RSMU_SABRE: + cfg = &rsmu_sabre_regmap_config; + break; + default: + dev_err(rsmu->dev, "Unsupported RSMU device type: %d\n", rsmu->type); + return -ENODEV; + } + + rsmu->regmap = devm_regmap_init(&client->dev, NULL, client, cfg); + if (IS_ERR(rsmu->regmap)) { + ret = PTR_ERR(rsmu->regmap); + dev_err(rsmu->dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + return rsmu_core_init(rsmu); +} + +static void rsmu_spi_remove(struct spi_device *client) +{ + struct rsmu_ddata *rsmu = spi_get_drvdata(client); + + rsmu_core_exit(rsmu); +} + +static const struct spi_device_id rsmu_spi_id[] = { + { "8a34000", RSMU_CM }, + { "8a34001", RSMU_CM }, + { "82p33810", RSMU_SABRE }, + { "82p33811", RSMU_SABRE }, + {} +}; +MODULE_DEVICE_TABLE(spi, rsmu_spi_id); + +static const struct of_device_id rsmu_spi_of_match[] = { + { .compatible = "idt,8a34000", .data = (void *)RSMU_CM }, + { .compatible = "idt,8a34001", .data = (void *)RSMU_CM }, + { .compatible = "idt,82p33810", .data = (void *)RSMU_SABRE }, + { .compatible = "idt,82p33811", .data = (void *)RSMU_SABRE }, + {} +}; +MODULE_DEVICE_TABLE(of, rsmu_spi_of_match); + +static struct spi_driver rsmu_spi_driver = { + .driver = { + .name = "rsmu-spi", + .of_match_table = rsmu_spi_of_match, + }, + .probe = rsmu_spi_probe, + .remove = rsmu_spi_remove, + .id_table = rsmu_spi_id, +}; + +static int __init rsmu_spi_init(void) +{ + return spi_register_driver(&rsmu_spi_driver); +} +subsys_initcall(rsmu_spi_init); + +static void __exit rsmu_spi_exit(void) +{ + spi_unregister_driver(&rsmu_spi_driver); +} +module_exit(rsmu_spi_exit); + +MODULE_DESCRIPTION("Renesas SMU SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/rt4831.c b/drivers/mfd/rt4831.c index b169781ac675..1ab8870e4ebf 100644 --- a/drivers/mfd/rt4831.c +++ b/drivers/mfd/rt4831.c @@ -87,12 +87,15 @@ static int rt4831_probe(struct i2c_client *client) ARRAY_SIZE(rt4831_subdevs), NULL, 0, NULL); } -static int rt4831_remove(struct i2c_client *client) +static void rt4831_remove(struct i2c_client *client) { struct regmap *regmap = dev_get_regmap(&client->dev, NULL); + int ret; /* Disable WLED and DSV outputs */ - return regmap_update_bits(regmap, RT4831_REG_ENABLE, RT4831_RESET_MASK, RT4831_RESET_MASK); + ret = regmap_update_bits(regmap, RT4831_REG_ENABLE, RT4831_RESET_MASK, RT4831_RESET_MASK); + if (ret) + dev_warn(&client->dev, "Failed to disable outputs (%pe)\n", ERR_PTR(ret)); } static const struct of_device_id __maybe_unused rt4831_of_match[] = { @@ -106,10 +109,11 @@ static struct i2c_driver rt4831_driver = { .name = "rt4831", .of_match_table = rt4831_of_match, }, - .probe_new = rt4831_probe, + .probe = rt4831_probe, .remove = rt4831_remove, }; 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 f1236a9acf30..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> @@ -29,8 +29,7 @@ static const struct regmap_irq rt5033_irqs[] = { static const struct regmap_irq_chip rt5033_irq_chip = { .name = "rt5033", .status_base = RT5033_REG_PMIC_IRQ_STAT, - .mask_base = RT5033_REG_PMIC_IRQ_CTRL, - .mask_invert = true, + .unmask_base = RT5033_REG_PMIC_IRQ_CTRL, .num_regs = 1, .irqs = rt5033_irqs, .num_irqs = ARRAY_SIZE(rt5033_irqs), @@ -42,9 +41,6 @@ static const struct mfd_cell rt5033_devs[] = { .name = "rt5033-charger", .of_compatible = "richtek,rt5033-charger", }, { - .name = "rt5033-battery", - .of_compatible = "richtek,rt5033-battery", - }, { .name = "rt5033-led", .of_compatible = "richtek,rt5033-led", }, @@ -56,11 +52,10 @@ static const struct regmap_config rt5033_regmap_config = { .max_register = RT5033_REG_END, }; -static int rt5033_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5033_i2c_probe(struct i2c_client *i2c) { struct rt5033_dev *rt5033; - unsigned int dev_id; + unsigned int dev_id, chip_rev; int ret; rt5033 = devm_kzalloc(&i2c->dev, sizeof(*rt5033), GFP_KERNEL); @@ -83,10 +78,11 @@ static int rt5033_i2c_probe(struct i2c_client *i2c, dev_err(&i2c->dev, "Device not found\n"); return -ENODEV; } - dev_info(&i2c->dev, "Device found Device ID: %04x\n", dev_id); + 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", @@ -102,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/rt5120.c b/drivers/mfd/rt5120.c new file mode 100644 index 000000000000..58d9a124d795 --- /dev/null +++ b/drivers/mfd/rt5120.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Richtek Technology Corp. + * Author: ChiYuan Huang <cy_huang@richtek.com> + */ + +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> + +#define RT5120_REG_INTENABLE 0x1D +#define RT5120_REG_INTSTAT 0x1E +#define RT5120_REG_FZCMODE 0x44 + +#define RT5120_INT_HOTDIE 0 +#define RT5120_INT_PWRKEY_REL 5 +#define RT5120_INT_PWRKEY_PRESS 6 + +static const struct regmap_range rt5120_rd_yes_ranges[] = { + regmap_reg_range(0x03, 0x13), + regmap_reg_range(0x1c, 0x20), + regmap_reg_range(0x44, 0x44), +}; + +static const struct regmap_range rt5120_wr_yes_ranges[] = { + regmap_reg_range(0x06, 0x13), + regmap_reg_range(0x1c, 0x20), + regmap_reg_range(0x44, 0x44), +}; + +static const struct regmap_access_table rt5120_rd_table = { + .yes_ranges = rt5120_rd_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(rt5120_rd_yes_ranges), +}; + +static const struct regmap_access_table rt5120_wr_table = { + .yes_ranges = rt5120_wr_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(rt5120_wr_yes_ranges), +}; + +static const struct regmap_config rt5120_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RT5120_REG_FZCMODE, + + .wr_table = &rt5120_wr_table, + .rd_table = &rt5120_rd_table, +}; + +static const struct regmap_irq rt5120_irqs[] = { + REGMAP_IRQ_REG_LINE(RT5120_INT_HOTDIE, 8), + REGMAP_IRQ_REG_LINE(RT5120_INT_PWRKEY_REL, 8), + REGMAP_IRQ_REG_LINE(RT5120_INT_PWRKEY_PRESS, 8), +}; + +static const struct regmap_irq_chip rt5120_irq_chip = { + .name = "rt5120-pmic", + .status_base = RT5120_REG_INTSTAT, + .unmask_base = RT5120_REG_INTENABLE, + .ack_base = RT5120_REG_INTSTAT, + .use_ack = true, + .num_regs = 1, + .irqs = rt5120_irqs, + .num_irqs = ARRAY_SIZE(rt5120_irqs), +}; + +static const struct resource rt5120_regulator_resources[] = { + DEFINE_RES_IRQ(RT5120_INT_HOTDIE), +}; + +static const struct resource rt5120_pwrkey_resources[] = { + DEFINE_RES_IRQ_NAMED(RT5120_INT_PWRKEY_PRESS, "pwrkey-press"), + DEFINE_RES_IRQ_NAMED(RT5120_INT_PWRKEY_REL, "pwrkey-release"), +}; + +static const struct mfd_cell rt5120_devs[] = { + MFD_CELL_RES("rt5120-regulator", rt5120_regulator_resources), + MFD_CELL_OF("rt5120-pwrkey", rt5120_pwrkey_resources, NULL, 0, 0, "richtek,rt5120-pwrkey"), +}; + +static int rt5120_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct regmap *regmap; + struct regmap_irq_chip_data *irq_data; + int ret; + + regmap = devm_regmap_init_i2c(i2c, &rt5120_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to init regmap\n"); + + ret = devm_regmap_add_irq_chip(dev, regmap, i2c->irq, IRQF_ONESHOT, 0, + &rt5120_irq_chip, &irq_data); + if (ret) + return dev_err_probe(dev, ret, "Failed to add IRQ chip\n"); + + return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, rt5120_devs, + ARRAY_SIZE(rt5120_devs), NULL, 0, + regmap_irq_get_domain(irq_data)); +} + +static const struct of_device_id rt5120_device_match_table[] = { + { .compatible = "richtek,rt5120" }, + {} +}; +MODULE_DEVICE_TABLE(of, rt5120_device_match_table); + +static struct i2c_driver rt5120_driver = { + .driver = { + .name = "rt5120", + .of_match_table = rt5120_device_match_table, + }, + .probe = rt5120_probe, +}; +module_i2c_driver(rt5120_driver); + +MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); +MODULE_DESCRIPTION("Richtek RT5120 I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/rz-mtu3.c b/drivers/mfd/rz-mtu3.c new file mode 100644 index 000000000000..9cdfef610398 --- /dev/null +++ b/drivers/mfd/rz-mtu3.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G2L Multi-Function Timer Pulse Unit 3(MTU3a) Core driver + * + * Copyright (C) 2023 Renesas Electronics Corporation + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mfd/core.h> +#include <linux/mfd/rz-mtu3.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/spinlock.h> + +#include "rz-mtu3.h" + +struct rz_mtu3_priv { + void __iomem *mmio; + struct reset_control *rstc; + spinlock_t lock; +}; + +/******* MTU3 registers (original offset is +0x1200) *******/ +static const unsigned long rz_mtu3_8bit_ch_reg_offs[][13] = { + [RZ_MTU3_CHAN_0] = MTU_8BIT_CH_0(0x104, 0x090, 0x100, 0x128, 0x101, 0x102, 0x103, 0x126), + [RZ_MTU3_CHAN_1] = MTU_8BIT_CH_1_2(0x184, 0x091, 0x185, 0x180, 0x194, 0x181, 0x182), + [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, 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) +}; + +static const unsigned long rz_mtu3_16bit_ch_reg_offs[][12] = { + [RZ_MTU3_CHAN_0] = MTU_16BIT_CH_0(0x106, 0x108, 0x10a, 0x10c, 0x10e, 0x120, 0x122), + [RZ_MTU3_CHAN_1] = MTU_16BIT_CH_1_2(0x186, 0x188, 0x18a), + [RZ_MTU3_CHAN_2] = MTU_16BIT_CH_1_2(0x206, 0x208, 0x20a), + [RZ_MTU3_CHAN_3] = MTU_16BIT_CH_3_6(0x010, 0x018, 0x01a, 0x024, 0x026, 0x072), + [RZ_MTU3_CHAN_4] = MTU_16BIT_CH_4_7(0x012, 0x01c, 0x01e, 0x028, 0x2a, 0x074, 0x076, 0x040, 0x044, 0x046, 0x048, 0x04a), + [RZ_MTU3_CHAN_5] = MTU_16BIT_CH_5(0xa80, 0xa82, 0xa90, 0xa92, 0xaa0, 0xaa2), + [RZ_MTU3_CHAN_6] = MTU_16BIT_CH_3_6(0x810, 0x818, 0x81a, 0x824, 0x826, 0x872), + [RZ_MTU3_CHAN_7] = MTU_16BIT_CH_4_7(0x812, 0x81c, 0x81e, 0x828, 0x82a, 0x874, 0x876, 0x840, 0x844, 0x846, 0x848, 0x84a) +}; + +static const unsigned long rz_mtu3_32bit_ch_reg_offs[][5] = { + [RZ_MTU3_CHAN_1] = MTU_32BIT_CH_1(0x1a0, 0x1a4, 0x1a8), + [RZ_MTU3_CHAN_8] = MTU_32BIT_CH_8(0x408, 0x40c, 0x410, 0x414, 0x418) +}; + +static bool rz_mtu3_is_16bit_shared_reg(u16 offset) +{ + return (offset == RZ_MTU3_TDDRA || offset == RZ_MTU3_TDDRB || + offset == RZ_MTU3_TCDRA || offset == RZ_MTU3_TCDRB || + offset == RZ_MTU3_TCBRA || offset == RZ_MTU3_TCBRB || + offset == RZ_MTU3_TCNTSA || offset == RZ_MTU3_TCNTSB); +} + +u16 rz_mtu3_shared_reg_read(struct rz_mtu3_channel *ch, u16 offset) +{ + struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); + struct rz_mtu3_priv *priv = mtu->priv_data; + + if (rz_mtu3_is_16bit_shared_reg(offset)) + return readw(priv->mmio + offset); + else + return readb(priv->mmio + offset); +} +EXPORT_SYMBOL_GPL(rz_mtu3_shared_reg_read); + +u8 rz_mtu3_8bit_ch_read(struct rz_mtu3_channel *ch, u16 offset) +{ + struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); + struct rz_mtu3_priv *priv = mtu->priv_data; + u16 ch_offs; + + ch_offs = rz_mtu3_8bit_ch_reg_offs[ch->channel_number][offset]; + + return readb(priv->mmio + ch_offs); +} +EXPORT_SYMBOL_GPL(rz_mtu3_8bit_ch_read); + +u16 rz_mtu3_16bit_ch_read(struct rz_mtu3_channel *ch, u16 offset) +{ + struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); + struct rz_mtu3_priv *priv = mtu->priv_data; + u16 ch_offs; + + /* MTU8 doesn't have 16-bit registers */ + if (ch->channel_number == RZ_MTU3_CHAN_8) + return 0; + + ch_offs = rz_mtu3_16bit_ch_reg_offs[ch->channel_number][offset]; + + return readw(priv->mmio + ch_offs); +} +EXPORT_SYMBOL_GPL(rz_mtu3_16bit_ch_read); + +u32 rz_mtu3_32bit_ch_read(struct rz_mtu3_channel *ch, u16 offset) +{ + struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); + struct rz_mtu3_priv *priv = mtu->priv_data; + u16 ch_offs; + + if (ch->channel_number != RZ_MTU3_CHAN_1 && ch->channel_number != RZ_MTU3_CHAN_8) + return 0; + + ch_offs = rz_mtu3_32bit_ch_reg_offs[ch->channel_number][offset]; + + return readl(priv->mmio + ch_offs); +} +EXPORT_SYMBOL_GPL(rz_mtu3_32bit_ch_read); + +void rz_mtu3_8bit_ch_write(struct rz_mtu3_channel *ch, u16 offset, u8 val) +{ + struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); + struct rz_mtu3_priv *priv = mtu->priv_data; + u16 ch_offs; + + ch_offs = rz_mtu3_8bit_ch_reg_offs[ch->channel_number][offset]; + writeb(val, priv->mmio + ch_offs); +} +EXPORT_SYMBOL_GPL(rz_mtu3_8bit_ch_write); + +void rz_mtu3_16bit_ch_write(struct rz_mtu3_channel *ch, u16 offset, u16 val) +{ + struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); + struct rz_mtu3_priv *priv = mtu->priv_data; + u16 ch_offs; + + /* MTU8 doesn't have 16-bit registers */ + if (ch->channel_number == RZ_MTU3_CHAN_8) + return; + + ch_offs = rz_mtu3_16bit_ch_reg_offs[ch->channel_number][offset]; + writew(val, priv->mmio + ch_offs); +} +EXPORT_SYMBOL_GPL(rz_mtu3_16bit_ch_write); + +void rz_mtu3_32bit_ch_write(struct rz_mtu3_channel *ch, u16 offset, u32 val) +{ + struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); + struct rz_mtu3_priv *priv = mtu->priv_data; + u16 ch_offs; + + if (ch->channel_number != RZ_MTU3_CHAN_1 && ch->channel_number != RZ_MTU3_CHAN_8) + return; + + ch_offs = rz_mtu3_32bit_ch_reg_offs[ch->channel_number][offset]; + writel(val, priv->mmio + ch_offs); +} +EXPORT_SYMBOL_GPL(rz_mtu3_32bit_ch_write); + +void rz_mtu3_shared_reg_write(struct rz_mtu3_channel *ch, u16 offset, u16 value) +{ + struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); + struct rz_mtu3_priv *priv = mtu->priv_data; + + if (rz_mtu3_is_16bit_shared_reg(offset)) + writew(value, priv->mmio + offset); + else + writeb((u8)value, priv->mmio + offset); +} +EXPORT_SYMBOL_GPL(rz_mtu3_shared_reg_write); + +void rz_mtu3_shared_reg_update_bit(struct rz_mtu3_channel *ch, u16 offset, + u16 pos, u8 val) +{ + struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); + struct rz_mtu3_priv *priv = mtu->priv_data; + unsigned long tmdr, 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); + spin_unlock_irqrestore(&priv->lock, flags); +} +EXPORT_SYMBOL_GPL(rz_mtu3_shared_reg_update_bit); + +static u16 rz_mtu3_get_tstr_offset(struct rz_mtu3_channel *ch) +{ + u16 offset; + + switch (ch->channel_number) { + case RZ_MTU3_CHAN_0: + case RZ_MTU3_CHAN_1: + case RZ_MTU3_CHAN_2: + case RZ_MTU3_CHAN_3: + case RZ_MTU3_CHAN_4: + case RZ_MTU3_CHAN_8: + offset = RZ_MTU3_TSTRA; + break; + case RZ_MTU3_CHAN_5: + offset = RZ_MTU3_TSTR; + break; + case RZ_MTU3_CHAN_6: + case RZ_MTU3_CHAN_7: + offset = RZ_MTU3_TSTRB; + break; + default: + offset = 0; + break; + } + + return offset; +} + +static u8 rz_mtu3_get_tstr_bit_pos(struct rz_mtu3_channel *ch) +{ + u8 bitpos; + + switch (ch->channel_number) { + case RZ_MTU3_CHAN_0: + case RZ_MTU3_CHAN_1: + case RZ_MTU3_CHAN_2: + case RZ_MTU3_CHAN_6: + case RZ_MTU3_CHAN_7: + bitpos = ch->channel_number; + break; + case RZ_MTU3_CHAN_3: + bitpos = 6; + break; + case RZ_MTU3_CHAN_4: + bitpos = 7; + break; + case RZ_MTU3_CHAN_5: + bitpos = 2; + break; + case RZ_MTU3_CHAN_8: + bitpos = 3; + break; + default: + bitpos = 0; + break; + } + + return bitpos; +} + +static void rz_mtu3_start_stop_ch(struct rz_mtu3_channel *ch, bool start) +{ + struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); + struct rz_mtu3_priv *priv = mtu->priv_data; + unsigned long flags, tstr; + u16 offset; + u8 bitpos; + + 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); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +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; + u16 offset; + u8 bitpos; + + 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); + spin_unlock_irqrestore(&priv->lock, flags); + + return tstr & BIT(bitpos); +} +EXPORT_SYMBOL_GPL(rz_mtu3_is_enabled); + +int rz_mtu3_enable(struct rz_mtu3_channel *ch) +{ + /* enable channel */ + rz_mtu3_start_stop_ch(ch, true); + + return 0; +} +EXPORT_SYMBOL_GPL(rz_mtu3_enable); + +void rz_mtu3_disable(struct rz_mtu3_channel *ch) +{ + /* disable channel */ + rz_mtu3_start_stop_ch(ch, false); +} +EXPORT_SYMBOL_GPL(rz_mtu3_disable); + +static void rz_mtu3_reset_assert(void *data) +{ + struct rz_mtu3 *mtu = dev_get_drvdata(data); + struct rz_mtu3_priv *priv = mtu->priv_data; + + mfd_remove_devices(data); + reset_control_assert(priv->rstc); +} + +static const struct mfd_cell rz_mtu3_devs[] = { + { + .name = "rz-mtu3-counter", + }, + { + .name = "pwm-rz-mtu3", + }, +}; + +static int rz_mtu3_probe(struct platform_device *pdev) +{ + struct rz_mtu3_priv *priv; + struct rz_mtu3 *ddata; + unsigned int i; + int ret; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + ddata->priv_data = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!ddata->priv_data) + return -ENOMEM; + + priv = ddata->priv_data; + + priv->mmio = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->mmio)) + return PTR_ERR(priv->mmio); + + priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(priv->rstc)) + return PTR_ERR(priv->rstc); + + ddata->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(ddata->clk)) + return PTR_ERR(ddata->clk); + + reset_control_deassert(priv->rstc); + spin_lock_init(&priv->lock); + platform_set_drvdata(pdev, ddata); + + for (i = 0; i < RZ_MTU_NUM_CHANNELS; i++) { + ddata->channels[i].channel_number = i; + ddata->channels[i].is_busy = false; + mutex_init(&ddata->channels[i].lock); + } + + ret = mfd_add_devices(&pdev->dev, 0, rz_mtu3_devs, + ARRAY_SIZE(rz_mtu3_devs), NULL, 0, NULL); + if (ret < 0) + goto err_assert; + + return devm_add_action_or_reset(&pdev->dev, rz_mtu3_reset_assert, + &pdev->dev); + +err_assert: + reset_control_assert(priv->rstc); + return ret; +} + +static const struct of_device_id rz_mtu3_of_match[] = { + { .compatible = "renesas,rz-mtu3", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rz_mtu3_of_match); + +static struct platform_driver rz_mtu3_driver = { + .probe = rz_mtu3_probe, + .driver = { + .name = "rz-mtu3", + .of_match_table = rz_mtu3_of_match, + }, +}; +module_platform_driver(rz_mtu3_driver); + +MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>"); +MODULE_DESCRIPTION("Renesas RZ/G2L MTU3a Core Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/rz-mtu3.h b/drivers/mfd/rz-mtu3.h new file mode 100644 index 000000000000..51a1298b0613 --- /dev/null +++ b/drivers/mfd/rz-mtu3.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * MFD internals for Renesas RZ/G2L MTU3 Core driver + * + * Copyright (C) 2023 Renesas Electronics Corporation + */ + +#ifndef RZ_MTU3_MFD_H +#define RZ_MTU3_MFD_H + +#define MTU_8BIT_CH_0(_tier, _nfcr, _tcr, _tcr2, _tmdr1, _tiorh, _tiorl, _tbtm) \ + { \ + [RZ_MTU3_TIER] = _tier, \ + [RZ_MTU3_NFCR] = _nfcr, \ + [RZ_MTU3_TCR] = _tcr, \ + [RZ_MTU3_TCR2] = _tcr2, \ + [RZ_MTU3_TMDR1] = _tmdr1, \ + [RZ_MTU3_TIORH] = _tiorh, \ + [RZ_MTU3_TIORL] = _tiorl, \ + [RZ_MTU3_TBTM] = _tbtm \ + } + +#define MTU_8BIT_CH_1_2(_tier, _nfcr, _tsr, _tcr, _tcr2, _tmdr1, _tior) \ + { \ + [RZ_MTU3_TIER] = _tier, \ + [RZ_MTU3_NFCR] = _nfcr, \ + [RZ_MTU3_TSR] = _tsr, \ + [RZ_MTU3_TCR] = _tcr, \ + [RZ_MTU3_TCR2] = _tcr2, \ + [RZ_MTU3_TMDR1] = _tmdr1, \ + [RZ_MTU3_TIOR] = _tior \ + } \ + +#define MTU_8BIT_CH_3_4_6_7(_tier, _nfcr, _tsr, _tcr, _tcr2, _tmdr1, _tiorh, _tiorl, _tbtm) \ + { \ + [RZ_MTU3_TIER] = _tier, \ + [RZ_MTU3_NFCR] = _nfcr, \ + [RZ_MTU3_TSR] = _tsr, \ + [RZ_MTU3_TCR] = _tcr, \ + [RZ_MTU3_TCR2] = _tcr2, \ + [RZ_MTU3_TMDR1] = _tmdr1, \ + [RZ_MTU3_TIORH] = _tiorh, \ + [RZ_MTU3_TIORL] = _tiorl, \ + [RZ_MTU3_TBTM] = _tbtm \ + } \ + +#define MTU_8BIT_CH_5(_tier, _nfcr, _tstr, _tcntcmpclr, _tcru, _tcr2u, _tioru, \ + _tcrv, _tcr2v, _tiorv, _tcrw, _tcr2w, _tiorw) \ + { \ + [RZ_MTU3_TIER] = _tier, \ + [RZ_MTU3_NFCR] = _nfcr, \ + [RZ_MTU3_TSTR] = _tstr, \ + [RZ_MTU3_TCNTCMPCLR] = _tcntcmpclr, \ + [RZ_MTU3_TCRU] = _tcru, \ + [RZ_MTU3_TCR2U] = _tcr2u, \ + [RZ_MTU3_TIORU] = _tioru, \ + [RZ_MTU3_TCRV] = _tcrv, \ + [RZ_MTU3_TCR2V] = _tcr2v, \ + [RZ_MTU3_TIORV] = _tiorv, \ + [RZ_MTU3_TCRW] = _tcrw, \ + [RZ_MTU3_TCR2W] = _tcr2w, \ + [RZ_MTU3_TIORW] = _tiorw \ + } \ + +#define MTU_8BIT_CH_8(_tier, _nfcr, _tcr, _tcr2, _tmdr1, _tiorh, _tiorl) \ + { \ + [RZ_MTU3_TIER] = _tier, \ + [RZ_MTU3_NFCR] = _nfcr, \ + [RZ_MTU3_TCR] = _tcr, \ + [RZ_MTU3_TCR2] = _tcr2, \ + [RZ_MTU3_TMDR1] = _tmdr1, \ + [RZ_MTU3_TIORH] = _tiorh, \ + [RZ_MTU3_TIORL] = _tiorl \ + } \ + +#define MTU_16BIT_CH_0(_tcnt, _tgra, _tgrb, _tgrc, _tgrd, _tgre, _tgrf) \ + { \ + [RZ_MTU3_TCNT] = _tcnt, \ + [RZ_MTU3_TGRA] = _tgra, \ + [RZ_MTU3_TGRB] = _tgrb, \ + [RZ_MTU3_TGRC] = _tgrc, \ + [RZ_MTU3_TGRD] = _tgrd, \ + [RZ_MTU3_TGRE] = _tgre, \ + [RZ_MTU3_TGRF] = _tgrf \ + } + +#define MTU_16BIT_CH_1_2(_tcnt, _tgra, _tgrb) \ + { \ + [RZ_MTU3_TCNT] = _tcnt, \ + [RZ_MTU3_TGRA] = _tgra, \ + [RZ_MTU3_TGRB] = _tgrb \ + } + +#define MTU_16BIT_CH_3_6(_tcnt, _tgra, _tgrb, _tgrc, _tgrd, _tgre) \ + { \ + [RZ_MTU3_TCNT] = _tcnt, \ + [RZ_MTU3_TGRA] = _tgra, \ + [RZ_MTU3_TGRB] = _tgrb, \ + [RZ_MTU3_TGRC] = _tgrc, \ + [RZ_MTU3_TGRD] = _tgrd, \ + [RZ_MTU3_TGRE] = _tgre \ + } + +#define MTU_16BIT_CH_4_7(_tcnt, _tgra, _tgrb, _tgrc, _tgrd, _tgre, _tgrf, \ + _tadcr, _tadcora, _tadcorb, _tadcobra, _tadcobrb) \ + { \ + [RZ_MTU3_TCNT] = _tcnt, \ + [RZ_MTU3_TGRA] = _tgra, \ + [RZ_MTU3_TGRB] = _tgrb, \ + [RZ_MTU3_TGRC] = _tgrc, \ + [RZ_MTU3_TGRD] = _tgrd, \ + [RZ_MTU3_TGRE] = _tgre, \ + [RZ_MTU3_TGRF] = _tgrf, \ + [RZ_MTU3_TADCR] = _tadcr, \ + [RZ_MTU3_TADCORA] = _tadcora, \ + [RZ_MTU3_TADCORB] = _tadcorb, \ + [RZ_MTU3_TADCOBRA] = _tadcobra, \ + [RZ_MTU3_TADCOBRB] = _tadcobrb \ + } + +#define MTU_16BIT_CH_5(_tcntu, _tgru, _tcntv, _tgrv, _tcntw, _tgrw) \ + { \ + [RZ_MTU3_TCNTU] = _tcntu, \ + [RZ_MTU3_TGRU] = _tgru, \ + [RZ_MTU3_TCNTV] = _tcntv, \ + [RZ_MTU3_TGRV] = _tgrv, \ + [RZ_MTU3_TCNTW] = _tcntw, \ + [RZ_MTU3_TGRW] = _tgrw \ + } + +#define MTU_32BIT_CH_1(_tcntlw, _tgralw, _tgrblw) \ + { \ + [RZ_MTU3_TCNTLW] = _tcntlw, \ + [RZ_MTU3_TGRALW] = _tgralw, \ + [RZ_MTU3_TGRBLW] = _tgrblw \ + } + +#define MTU_32BIT_CH_8(_tcnt, _tgra, _tgrb, _tgrc, _tgrd) \ + { \ + [RZ_MTU3_TCNT] = _tcnt, \ + [RZ_MTU3_TGRA] = _tgra, \ + [RZ_MTU3_TGRB] = _tgrb, \ + [RZ_MTU3_TGRC] = _tgrc, \ + [RZ_MTU3_TGRD] = _tgrd \ + } + +#endif 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 1fb29c45f5cf..000000000000 --- a/drivers/mfd/sec-core.c +++ /dev/null @@ -1,508 +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/s5m8763.h> -#include <linux/mfd/samsung/s5m8767.h> -#include <linux/regmap.h> - -static const struct mfd_cell s5m8751_devs[] = { - { .name = "s5m8751-pmic", }, - { .name = "s5m-charger", }, - { .name = "s5m8751-codec", }, -}; - -static const struct mfd_cell s5m8763_devs[] = { - { .name = "s5m8763-pmic", }, - { .name = "s5m-rtc", }, - { .name = "s5m-charger", }, -}; - -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 bool s5m8763_volatile(struct device *dev, unsigned int reg) -{ - switch (reg) { - case S5M8763_REG_IRQM1: - case S5M8763_REG_IRQM2: - case S5M8763_REG_IRQM3: - case S5M8763_REG_IRQM4: - 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 s5m8763_regmap_config = { - .reg_bits = 8, - .val_bits = 8, - - .max_register = S5M8763_REG_LBCNFG2, - .volatile_reg = s5m8763_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 i2c_device_id *id) -{ - 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 S5M8763X: - regmap = &s5m8763_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 S5M8751X: - sec_devs = s5m8751_devs; - num_sec_devs = ARRAY_SIZE(s5m8751_devs); - break; - case S5M8763X: - sec_devs = s5m8763_devs; - num_sec_devs = ARRAY_SIZE(s5m8763_devs); - break; - 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); -} - -#ifdef CONFIG_PM_SLEEP -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; -} -#endif /* CONFIG_PM_SLEEP */ - -static 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 = &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 e473c2fb42d5..74ac70002d1f 100644 --- a/drivers/mfd/sec-irq.c +++ b/drivers/mfd/sec-irq.c @@ -3,373 +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/s5m8763.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 s5m8763_irqs[] = { - [S5M8763_IRQ_DCINF] = { - .reg_offset = 0, - .mask = S5M8763_IRQ_DCINF_MASK, - }, - [S5M8763_IRQ_DCINR] = { - .reg_offset = 0, - .mask = S5M8763_IRQ_DCINR_MASK, - }, - [S5M8763_IRQ_JIGF] = { - .reg_offset = 0, - .mask = S5M8763_IRQ_JIGF_MASK, - }, - [S5M8763_IRQ_JIGR] = { - .reg_offset = 0, - .mask = S5M8763_IRQ_JIGR_MASK, - }, - [S5M8763_IRQ_PWRONF] = { - .reg_offset = 0, - .mask = S5M8763_IRQ_PWRONF_MASK, - }, - [S5M8763_IRQ_PWRONR] = { - .reg_offset = 0, - .mask = S5M8763_IRQ_PWRONR_MASK, - }, - [S5M8763_IRQ_WTSREVNT] = { - .reg_offset = 1, - .mask = S5M8763_IRQ_WTSREVNT_MASK, - }, - [S5M8763_IRQ_SMPLEVNT] = { - .reg_offset = 1, - .mask = S5M8763_IRQ_SMPLEVNT_MASK, - }, - [S5M8763_IRQ_ALARM1] = { - .reg_offset = 1, - .mask = S5M8763_IRQ_ALARM1_MASK, - }, - [S5M8763_IRQ_ALARM0] = { - .reg_offset = 1, - .mask = S5M8763_IRQ_ALARM0_MASK, - }, - [S5M8763_IRQ_ONKEY1S] = { - .reg_offset = 2, - .mask = S5M8763_IRQ_ONKEY1S_MASK, - }, - [S5M8763_IRQ_TOPOFFR] = { - .reg_offset = 2, - .mask = S5M8763_IRQ_TOPOFFR_MASK, - }, - [S5M8763_IRQ_DCINOVPR] = { - .reg_offset = 2, - .mask = S5M8763_IRQ_DCINOVPR_MASK, - }, - [S5M8763_IRQ_CHGRSTF] = { - .reg_offset = 2, - .mask = S5M8763_IRQ_CHGRSTF_MASK, - }, - [S5M8763_IRQ_DONER] = { - .reg_offset = 2, - .mask = S5M8763_IRQ_DONER_MASK, - }, - [S5M8763_IRQ_CHGFAULT] = { - .reg_offset = 2, - .mask = S5M8763_IRQ_CHGFAULT_MASK, - }, - [S5M8763_IRQ_LOBAT1] = { - .reg_offset = 3, - .mask = S5M8763_IRQ_LOBAT1_MASK, - }, - [S5M8763_IRQ_LOBAT2] = { - .reg_offset = 3, - .mask = S5M8763_IRQ_LOBAT2_MASK, - }, +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 = { @@ -415,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, @@ -425,38 +268,75 @@ static const struct regmap_irq_chip s5m8767_irq_chip = { .ack_base = S5M8767_REG_INT1, }; -static const struct regmap_irq_chip s5m8763_irq_chip = { - .name = "s5m8763", - .irqs = s5m8763_irqs, - .num_irqs = ARRAY_SIZE(s5m8763_irqs), - .num_regs = 4, - .status_base = S5M8763_REG_IRQ1, - .mask_base = S5M8763_REG_IRQM1, - .ack_base = S5M8763_REG_IRQ1, -}; +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 irq, ret; -int sec_irq_init(struct sec_pmic_dev *sec_pmic) + 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) { - int ret = 0; - int type = sec_pmic->device_type; - const struct regmap_irq_chip *sec_irq_chip; + 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; - if (!sec_pmic->irq) { - dev_warn(sec_pmic->dev, - "No interrupt specified, no interrupts\n"); - return 0; + 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) { - case S5M8763X: - sec_irq_chip = &s5m8763_irq_chip; - break; + 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; @@ -472,20 +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_TRIGGER_FALLING | IRQF_ONESHOT, + 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 @@ -495,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 f32f1fb93e37..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)) @@ -251,7 +251,7 @@ static int si476x_core_parse_and_nag_about_error(struct si476x_core *core) * @usecs: amount of time to wait before reading the response (in * usecs) * - * Function returns 0 on succsess and negative error code on + * Function returns 0 on success and negative error code on * failure */ static int si476x_core_send_command(struct si476x_core *core, @@ -398,7 +398,7 @@ static int si476x_cmd_tune_seek_freq(struct si476x_core *core, * The command requests the firmware and patch version for currently * loaded firmware (dependent on the function of the device FM/AM/WB) * - * Function returns 0 on succsess and negative error code on + * Function returns 0 on success and negative error code on * failure */ int si476x_core_cmd_func_info(struct si476x_core *core, @@ -429,7 +429,7 @@ EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info); * @property: property address * @value: property value * - * Function returns 0 on succsess and negative error code on + * Function returns 0 on success and negative error code on * failure */ int si476x_core_cmd_set_property(struct si476x_core *core, @@ -545,13 +545,13 @@ EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg); * SI476X_IQCLK_NOOP - do not modify the behaviour * SI476X_IQCLK_TRISTATE - put the pin in tristate condition, * enable 1MOhm pulldown - * SI476X_IQCLK_IQ - set pin to be a part of I/Q interace + * SI476X_IQCLK_IQ - set pin to be a part of I/Q interface * in master mode * @iqfs: - IQFS pin function configuration: * SI476X_IQFS_NOOP - do not modify the behaviour * SI476X_IQFS_TRISTATE - put the pin in tristate condition, * enable 1MOhm pulldown - * SI476X_IQFS_IQ - set pin to be a part of I/Q interace + * SI476X_IQFS_IQ - set pin to be a part of I/Q interface * in master mode * @iout: - IOUT pin function configuration: * SI476X_IOUT_NOOP - do not modify the behaviour @@ -589,7 +589,7 @@ EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg); /** * si476x_core_cmd_ic_link_gpo_ctl_pin_cfg - send - * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device + * 'IC_LINK_GPIO_CTL_PIN_CFG' command to the device * @core: - device to send the command to * @icin: - ICIN pin function configuration: * SI476X_ICIN_NOOP - do not modify the behaviour @@ -1014,7 +1014,7 @@ EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity); * NOTE caller must hold core lock * * Function returns the value of the status bit in case of success and - * negative error code in case of failre. + * negative error code in case of failure. */ int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core) { diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c index a2635c2d9d1a..899c0b5ea3aa 100644 --- a/drivers/mfd/si476x-i2c.c +++ b/drivers/mfd/si476x-i2c.c @@ -683,9 +683,9 @@ bool si476x_core_is_powered_up(struct si476x_core *core) } EXPORT_SYMBOL_GPL(si476x_core_is_powered_up); -static int si476x_core_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int si476x_core_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); int rval; struct si476x_core *core; struct si476x_platform_data *pdata; @@ -835,7 +835,7 @@ free_gpio: return rval; } -static int si476x_core_remove(struct i2c_client *client) +static void si476x_core_remove(struct i2c_client *client) { struct si476x_core *core = i2c_get_clientdata(client); @@ -851,8 +851,6 @@ static int si476x_core_remove(struct i2c_client *client) if (gpio_is_valid(core->gpio_reset)) gpio_free(core->gpio_reset); - - return 0; } 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 87f684cff9a1..8b751d8e3b5a 100644 --- a/drivers/mfd/simple-mfd-i2c.c +++ b/drivers/mfd/simple-mfd-i2c.c @@ -2,49 +2,135 @@ /* * Simple MFD - I2C * + * Author(s): + * Michael Walle <michael@walle.cc> + * Lee Jones <lee.jones@linaro.org> + * * This driver creates a single register map with the intention for it to be * shared by all sub-devices. Children can use their parent's device structure * (dev.parent) in order to reference it. * * Once the register map has been successfully initialised, any sub-devices - * represented by child nodes in Device Tree will be subsequently registered. + * represented by child nodes in Device Tree or via the MFD cells in this file + * 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" -static const struct regmap_config simple_regmap_config = { +static const struct regmap_config regmap_config_8r_8v = { .reg_bits = 8, .val_bits = 8, }; static int simple_mfd_i2c_probe(struct i2c_client *i2c) { - const struct regmap_config *config; + const struct simple_mfd_data *simple_mfd_data; + const struct regmap_config *regmap_config; struct regmap *regmap; + int ret; - config = device_get_match_data(&i2c->dev); - if (!config) - config = &simple_regmap_config; + simple_mfd_data = device_get_match_data(&i2c->dev); - regmap = devm_regmap_init_i2c(i2c, config); + /* If no regmap_config is specified, use the default 8reg and 8val bits */ + if (!simple_mfd_data || !simple_mfd_data->regmap_config) + regmap_config = ®map_config_8r_8v; + else + regmap_config = simple_mfd_data->regmap_config; + + regmap = devm_regmap_init_i2c(i2c, regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); - return devm_of_platform_populate(&i2c->dev); + /* If no MFD cells are specified, register using the DT child nodes instead */ + if (!simple_mfd_data || !simple_mfd_data->mfd_cell) + return devm_of_platform_populate(&i2c->dev); + + ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, + simple_mfd_data->mfd_cell, + simple_mfd_data->mfd_cell_size, + NULL, 0, NULL); + if (ret) + dev_err(&i2c->dev, "Failed to add child devices\n"); + + return ret; } +static const struct mfd_cell sy7636a_cells[] = { + { .name = "sy7636a-regulator", }, + { .name = "sy7636a-temperature", }, +}; + +static const struct simple_mfd_data silergy_sy7636a = { + .mfd_cell = sy7636a_cells, + .mfd_cell_size = ARRAY_SIZE(sy7636a_cells), +}; + +static const struct mfd_cell max5970_cells[] = { + { .name = "max5970-regulator", }, + { .name = "max5970-iio", }, + { .name = "max5970-led", }, +}; + +static const struct simple_mfd_data maxim_max5970 = { + .mfd_cell = max5970_cells, + .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 = "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); static struct i2c_driver simple_mfd_i2c_driver = { - .probe_new = simple_mfd_i2c_probe, + .probe = simple_mfd_i2c_probe, .driver = { .name = "simple-mfd-i2c", .of_match_table = simple_mfd_i2c_of_match, diff --git a/drivers/mfd/simple-mfd-i2c.h b/drivers/mfd/simple-mfd-i2c.h new file mode 100644 index 000000000000..7cb2bdd347d9 --- /dev/null +++ b/drivers/mfd/simple-mfd-i2c.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Simple MFD - I2C + * + * Author: Lee Jones <lee.jones@linaro.org> + * + * This driver creates a single register map with the intention for it to be + * shared by all sub-devices. Children can use their parent's device structure + * (dev.parent) in order to reference it. + * + * This driver creates a single register map with the intention for it to be + * shared by all sub-devices. Children can use their parent's device structure + * (dev.parent) in order to reference it. + * + * Once the register map has been successfully initialised, any sub-devices + * represented by child nodes in Device Tree or via the MFD cells in the + * associated C file will be subsequently registered. + */ + +#ifndef __MFD_SIMPLE_MFD_I2C_H +#define __MFD_SIMPLE_MFD_I2C_H + +#include <linux/mfd/core.h> +#include <linux/regmap.h> + +struct simple_mfd_data { + const struct regmap_config *regmap_config; + const struct mfd_cell *mfd_cell; + size_t mfd_cell_size; +}; + +#endif /* __MFD_SIMPLE_MFD_I2C_H */ diff --git a/drivers/mfd/sky81452.c b/drivers/mfd/sky81452.c index 3ad35bf0c015..771b62a5c70f 100644 --- a/drivers/mfd/sky81452.c +++ b/drivers/mfd/sky81452.c @@ -21,8 +21,7 @@ static const struct regmap_config sky81452_config = { .val_bits = 8, }; -static int sky81452_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sky81452_probe(struct i2c_client *client) { struct device *dev = &client->dev; const struct sky81452_platform_data *pdata = dev_get_platdata(dev); diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index bc0a2c38653e..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; @@ -1432,8 +1392,6 @@ static int sm501_plat_probe(struct platform_device *dev) } -#ifdef CONFIG_PM - /* power management support */ static void sm501_set_power(struct sm501_devdata *sm, int on) @@ -1509,10 +1467,6 @@ static int sm501_plat_resume(struct platform_device *pdev) return 0; } -#else -#define sm501_plat_suspend NULL -#define sm501_plat_resume NULL -#endif /* Initialisation data for PCI devices */ @@ -1673,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); @@ -1681,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[] = { @@ -1714,13 +1666,18 @@ static struct platform_driver sm501_plat_driver = { }, .probe = sm501_plat_probe, .remove = sm501_plat_remove, - .suspend = sm501_plat_suspend, - .resume = sm501_plat_resume, + .suspend = pm_sleep_ptr(sm501_plat_suspend), + .resume = pm_sleep_ptr(sm501_plat_resume), }; static int __init sm501_base_init(void) { - platform_driver_register(&sm501_plat_driver); + int ret; + + ret = platform_driver_register(&sm501_plat_driver); + if (ret < 0) + return ret; + return pci_register_driver(&sm501_pci_driver); } diff --git a/drivers/mfd/smpro-core.c b/drivers/mfd/smpro-core.c new file mode 100644 index 000000000000..59d31590c69b --- /dev/null +++ b/drivers/mfd/smpro-core.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Ampere Altra Family SMPro core driver + * Copyright (c) 2022, Ampere Computing LLC + */ + +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> + +/* Identification Registers */ +#define MANUFACTURER_ID_REG 0x02 +#define AMPERE_MANUFACTURER_ID 0xCD3A + +#define CORE_CE_ERR_DATA 0x82 +#define CORE_UE_ERR_DATA 0x85 +#define MEM_CE_ERR_DATA 0x92 +#define MEM_UE_ERR_DATA 0x95 +#define PCIE_CE_ERR_DATA 0xC2 +#define PCIE_UE_ERR_DATA 0xC5 +#define OTHER_CE_ERR_DATA 0xD2 +#define OTHER_UE_ERR_DATA 0xDA + +static int smpro_core_write(void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + int ret; + + ret = i2c_master_send(i2c, data, count); + if (unlikely(ret != count)) + return (ret < 0) ? ret : -EIO; + + return 0; +} + +static int smpro_core_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + struct i2c_msg xfer[2]; + unsigned char buf[2]; + int ret; + + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + + buf[0] = *(u8 *)reg; + buf[1] = val_size; + xfer[0].len = 2; + xfer[0].buf = buf; + + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = val_size; + xfer[1].buf = val; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (unlikely(ret != 2)) + return (ret < 0) ? ret : -EIO; + + return 0; +} + +static const struct regmap_bus smpro_regmap_bus = { + .read = smpro_core_read, + .write = smpro_core_write, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static bool smpro_core_readable_noinc_reg(struct device *dev, unsigned int reg) +{ + return (reg == CORE_CE_ERR_DATA || reg == CORE_UE_ERR_DATA || + reg == MEM_CE_ERR_DATA || reg == MEM_UE_ERR_DATA || + reg == PCIE_CE_ERR_DATA || reg == PCIE_UE_ERR_DATA || + reg == OTHER_CE_ERR_DATA || reg == OTHER_UE_ERR_DATA); +} + +static const struct regmap_config smpro_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .readable_noinc_reg = smpro_core_readable_noinc_reg, +}; + +static const struct mfd_cell smpro_devs[] = { + MFD_CELL_NAME("smpro-hwmon"), + MFD_CELL_NAME("smpro-errmon"), + MFD_CELL_NAME("smpro-misc"), +}; + +static int smpro_core_probe(struct i2c_client *i2c) +{ + const struct regmap_config *config; + struct regmap *regmap; + unsigned int val; + int ret; + + config = device_get_match_data(&i2c->dev); + if (!config) + return -EINVAL; + + regmap = devm_regmap_init(&i2c->dev, &smpro_regmap_bus, &i2c->dev, config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = regmap_read(regmap, MANUFACTURER_ID_REG, &val); + if (ret) + return ret; + + if (val != AMPERE_MANUFACTURER_ID) + return -ENODEV; + + return devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, + smpro_devs, ARRAY_SIZE(smpro_devs), NULL, 0, NULL); +} + +static const struct of_device_id smpro_core_of_match[] = { + { .compatible = "ampere,smpro", .data = &smpro_regmap_config }, + {} +}; +MODULE_DEVICE_TABLE(of, smpro_core_of_match); + +static struct i2c_driver smpro_core_driver = { + .probe = smpro_core_probe, + .driver = { + .name = "smpro-core", + .of_match_table = smpro_core_of_match, + }, +}; +module_i2c_driver(smpro_core_driver); + +MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>"); +MODULE_DESCRIPTION("SMPRO CORE - I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/sprd-sc27xx-spi.c b/drivers/mfd/sprd-sc27xx-spi.c index 6b7956604a0f..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> @@ -18,6 +18,9 @@ #define SPRD_PMIC_INT_RAW_STATUS 0x4 #define SPRD_PMIC_INT_EN 0x8 +#define SPRD_SC2730_IRQ_BASE 0x80 +#define SPRD_SC2730_IRQ_NUMS 10 +#define SPRD_SC2730_CHG_DET 0x1b9c #define SPRD_SC2731_IRQ_BASE 0x140 #define SPRD_SC2731_IRQ_NUMS 16 #define SPRD_SC2731_CHG_DET 0xedc @@ -52,6 +55,12 @@ struct sprd_pmic_data { * base address and irq number, we should save irq number and irq base * in the device data structure. */ +static const struct sprd_pmic_data sc2730_data = { + .irq_base = SPRD_SC2730_IRQ_BASE, + .num_irqs = SPRD_SC2730_IRQ_NUMS, + .charger_det = SPRD_SC2730_CHG_DET, +}; + static const struct sprd_pmic_data sc2731_data = { .irq_base = SPRD_SC2731_IRQ_BASE, .num_irqs = SPRD_SC2731_IRQ_NUMS, @@ -126,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, @@ -172,11 +181,10 @@ static int sprd_pmic_probe(struct spi_device *spi) ddata->irq_chip.name = dev_name(&spi->dev); ddata->irq_chip.status_base = pdata->irq_base + SPRD_PMIC_INT_MASK_STATUS; - ddata->irq_chip.mask_base = pdata->irq_base + SPRD_PMIC_INT_EN; + ddata->irq_chip.unmask_base = pdata->irq_base + SPRD_PMIC_INT_EN; ddata->irq_chip.ack_base = 0; ddata->irq_chip.num_regs = 1; ddata->irq_chip.num_irqs = pdata->num_irqs; - ddata->irq_chip.mask_invert = true; ddata->irqs = devm_kcalloc(&spi->dev, pdata->num_irqs, sizeof(struct regmap_irq), @@ -202,11 +210,13 @@ 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; } -#ifdef CONFIG_PM_SLEEP static int sprd_pmic_suspend(struct device *dev) { struct sprd_pmic *ddata = dev_get_drvdata(dev); @@ -226,23 +236,32 @@ static int sprd_pmic_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(sprd_pmic_pm_ops, sprd_pmic_suspend, sprd_pmic_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(sprd_pmic_pm_ops, + sprd_pmic_suspend, sprd_pmic_resume); static const struct of_device_id sprd_pmic_match[] = { + { .compatible = "sprd,sc2730", .data = &sc2730_data }, { .compatible = "sprd,sc2731", .data = &sc2731_data }, {}, }; MODULE_DEVICE_TABLE(of, sprd_pmic_match); +static const struct spi_device_id sprd_pmic_spi_ids[] = { + { .name = "sc2730", .driver_data = (unsigned long)&sc2730_data }, + { .name = "sc2731", .driver_data = (unsigned long)&sc2731_data }, + {}, +}; +MODULE_DEVICE_TABLE(spi, sprd_pmic_spi_ids); + static struct spi_driver sprd_pmic_driver = { .driver = { .name = "sc27xx-pmic", .of_match_table = sprd_pmic_match, - .pm = &sprd_pmic_pm_ops, + .pm = pm_sleep_ptr(&sprd_pmic_pm_ops), }, .probe = sprd_pmic_probe, + .id_table = sprd_pmic_spi_ids, }; static int __init sprd_pmic_init(void) diff --git a/drivers/mfd/ssbi.c b/drivers/mfd/ssbi.c index 94f60df0decd..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; @@ -262,7 +261,6 @@ EXPORT_SYMBOL_GPL(ssbi_write); static int ssbi_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct resource *mem_res; struct ssbi *ssbi; const char *type; @@ -270,8 +268,7 @@ static int ssbi_probe(struct platform_device *pdev) if (!ssbi) return -ENOMEM; - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ssbi->base = devm_ioremap_resource(&pdev->dev, mem_res); + ssbi->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(ssbi->base)) return PTR_ERR(ssbi->base); @@ -322,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 1819c8fe4d8f..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_ATOMIC); - - /* 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 746e51a17cc8..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,11 +49,40 @@ 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; struct stm32_lptimer *ddata; - struct resource *res; void __iomem *mmio; int ret; @@ -60,8 +90,7 @@ static int stm32_lptimer_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, NULL); if (IS_ERR(mmio)) return PTR_ERR(mmio); @@ -74,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 e095a3930142..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; @@ -330,9 +329,8 @@ static int stmfx_chip_init(struct i2c_client *client) stmfx->vdd = devm_regulator_get_optional(&client->dev, "vdd"); ret = PTR_ERR_OR_ZERO(stmfx->vdd); if (ret) { - if (ret == -ENODEV) - stmfx->vdd = NULL; - else + stmfx->vdd = NULL; + if (ret != -ENODEV) return dev_err_probe(&client->dev, ret, "Failed to get VDD regulator\n"); } @@ -387,26 +385,30 @@ static int stmfx_chip_init(struct i2c_client *client) err: if (stmfx->vdd) - return regulator_disable(stmfx->vdd); + regulator_disable(stmfx->vdd); return ret; } -static int stmfx_chip_exit(struct i2c_client *client) +static void stmfx_chip_exit(struct i2c_client *client) { struct stmfx *stmfx = i2c_get_clientdata(client); regmap_write(stmfx->map, STMFX_REG_IRQ_SRC_EN, 0); regmap_write(stmfx->map, STMFX_REG_SYS_CTRL, 0); - if (stmfx->vdd) - return regulator_disable(stmfx->vdd); + if (stmfx->vdd) { + int ret; - return 0; + ret = regulator_disable(stmfx->vdd); + if (ret) + dev_err(&client->dev, + "Failed to disable vdd regulator: %pe\n", + ERR_PTR(ret)); + } } -static int stmfx_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int stmfx_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct stmfx *stmfx; @@ -462,14 +464,13 @@ err_chip_exit: return ret; } -static int stmfx_remove(struct i2c_client *client) +static void stmfx_remove(struct i2c_client *client) { stmfx_irq_exit(client); - return stmfx_chip_exit(client); + stmfx_chip_exit(client); } -#ifdef CONFIG_PM_SLEEP static int stmfx_suspend(struct device *dev) { struct stmfx *stmfx = dev_get_drvdata(dev); @@ -535,9 +536,8 @@ static int stmfx_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(stmfx_dev_pm_ops, stmfx_suspend, stmfx_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(stmfx_dev_pm_ops, stmfx_suspend, stmfx_resume); static const struct of_device_id stmfx_of_match[] = { { .compatible = "st,stmfx-0300", }, @@ -549,7 +549,7 @@ static struct i2c_driver stmfx_driver = { .driver = { .name = "stmfx-core", .of_match_table = stmfx_of_match, - .pm = &stmfx_dev_pm_ops, + .pm = pm_sleep_ptr(&stmfx_dev_pm_ops), }, .probe = stmfx_probe, .remove = stmfx_remove, diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c index cd2f45257dc1..943fa363efc3 100644 --- a/drivers/mfd/stmpe-i2c.c +++ b/drivers/mfd/stmpe-i2c.c @@ -67,8 +67,9 @@ static const struct of_device_id stmpe_of_match[] = { MODULE_DEVICE_TABLE(of, stmpe_of_match); static int -stmpe_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +stmpe_i2c_probe(struct i2c_client *i2c) { + const struct i2c_device_id *id = i2c_client_get_device_id(i2c); enum stmpe_partnum partnum; const struct of_device_id *of_id; @@ -86,16 +87,16 @@ stmpe_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) 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); } -static int stmpe_i2c_remove(struct i2c_client *i2c) +static void stmpe_i2c_remove(struct i2c_client *i2c) { struct stmpe *stmpe = dev_get_drvdata(&i2c->dev); - return stmpe_remove(stmpe); + stmpe_remove(stmpe); } static const struct i2c_device_id stmpe_i2c_id[] = { @@ -114,28 +115,15 @@ MODULE_DEVICE_TABLE(i2c, stmpe_i2c_id); static struct i2c_driver stmpe_i2c_driver = { .driver = { .name = "stmpe-i2c", -#ifdef CONFIG_PM - .pm = &stmpe_dev_pm_ops, -#endif + .pm = pm_sleep_ptr(&stmpe_dev_pm_ops), .of_match_table = stmpe_of_match, }, .probe = stmpe_i2c_probe, .remove = stmpe_i2c_remove, .id_table = stmpe_i2c_id, }; +module_i2c_driver(stmpe_i2c_driver); -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_LICENSE("GPL v2"); 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 7351734f7593..dea31efface6 100644 --- a/drivers/mfd/stmpe-spi.c +++ b/drivers/mfd/stmpe-spi.c @@ -102,11 +102,11 @@ stmpe_spi_probe(struct spi_device *spi) return stmpe_probe(&spi_ci, id->driver_data); } -static int stmpe_spi_remove(struct spi_device *spi) +static void stmpe_spi_remove(struct spi_device *spi) { struct stmpe *stmpe = spi_get_drvdata(spi); - return stmpe_remove(stmpe); + stmpe_remove(stmpe); } static const struct of_device_id stmpe_spi_of_match[] = { @@ -129,33 +129,20 @@ 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 = { .name = "stmpe-spi", .of_match_table = of_match_ptr(stmpe_spi_of_match), -#ifdef CONFIG_PM - .pm = &stmpe_dev_pm_ops, -#endif + .pm = pm_sleep_ptr(&stmpe_dev_pm_ops), }, .probe = stmpe_spi_probe, .remove = stmpe_spi_remove, .id_table = stmpe_spi_id, }; +module_spi_driver(stmpe_spi_driver); -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_LICENSE("GPL v2"); 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 1dd39483e7c1..3c5c2f157f52 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -8,14 +8,13 @@ */ #include <linux/err.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/export.h> #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/pm.h> #include <linux/slab.h> #include <linux/mfd/core.h> @@ -30,17 +29,12 @@ * @irq_trigger: IRQ trigger to use for the interrupt to the host * @autosleep: bool to enable/disable stmpe autosleep * @autosleep_timeout: inactivity timeout in milliseconds for autosleep - * @irq_over_gpio: true if gpio is used to get irq - * @irq_gpio: gpio number over which irq will be requested (significant only if - * irq_over_gpio is true) */ struct stmpe_platform_data { int id; unsigned int blocks; unsigned int irq_trigger; bool autosleep; - bool irq_over_gpio; - int irq_gpio; int autosleep_timeout; }; @@ -1095,7 +1089,7 @@ static irqreturn_t stmpe_irq(int irq, void *data) if (variant->id_val == STMPE801_ID || variant->id_val == STMPE1600_ID) { - int base = irq_create_mapping(stmpe->domain, 0); + int base = irq_find_mapping(stmpe->domain, 0); handle_nested_irq(base); return IRQ_HANDLED; @@ -1123,7 +1117,7 @@ static irqreturn_t stmpe_irq(int irq, void *data) while (status) { int bit = __ffs(status); int line = bank * 8 + bit; - int nestedirq = irq_create_mapping(stmpe->domain, line); + int nestedirq = irq_find_mapping(stmpe->domain, line); handle_nested_irq(nestedirq); status &= ~(1 << bit); @@ -1225,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; @@ -1349,32 +1343,22 @@ static void stmpe_of_probe(struct stmpe_platform_data *pdata, if (pdata->id < 0) pdata->id = -1; - pdata->irq_gpio = of_get_named_gpio_flags(np, "irq-gpio", 0, - &pdata->irq_trigger); - if (gpio_is_valid(pdata->irq_gpio)) - pdata->irq_over_gpio = 1; - else - pdata->irq_trigger = IRQF_TRIGGER_NONE; - of_property_read_u32(np, "st,autosleep-timeout", &pdata->autosleep_timeout); pdata->autosleep = (pdata->autosleep_timeout) ? true : false; - for_each_child_of_node(np, child) { - if (of_node_name_eq(child, "stmpe_gpio")) { + for_each_available_child_of_node(np, child) { + if (of_device_is_compatible(child, stmpe_gpio_cell.of_compatible)) pdata->blocks |= STMPE_BLOCK_GPIO; - } else if (of_node_name_eq(child, "stmpe_keypad")) { + else if (of_device_is_compatible(child, stmpe_keypad_cell.of_compatible)) pdata->blocks |= STMPE_BLOCK_KEYPAD; - } else if (of_node_name_eq(child, "stmpe_touchscreen")) { + else if (of_device_is_compatible(child, stmpe_ts_cell.of_compatible)) pdata->blocks |= STMPE_BLOCK_TOUCHSCREEN; - } else if (of_node_name_eq(child, "stmpe_adc")) { + else if (of_device_is_compatible(child, stmpe_adc_cell.of_compatible)) pdata->blocks |= STMPE_BLOCK_ADC; - } else if (of_node_name_eq(child, "stmpe_pwm")) { + else if (of_device_is_compatible(child, stmpe_pwm_cell.of_compatible)) pdata->blocks |= STMPE_BLOCK_PWM; - } else if (of_node_name_eq(child, "stmpe_rotator")) { - pdata->blocks |= STMPE_BLOCK_ROTATOR; - } } } @@ -1384,6 +1368,7 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum) struct stmpe_platform_data *pdata; struct device_node *np = ci->dev->of_node; struct stmpe *stmpe; + struct gpio_desc *irq_gpio; int ret; u32 val; @@ -1393,7 +1378,7 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum) stmpe_of_probe(pdata, np); - if (of_find_property(np, "interrupts", NULL) == NULL) + if (!of_property_present(np, "interrupts")) ci->irq = -1; stmpe = devm_kzalloc(ci->dev, sizeof(struct stmpe), GFP_KERNEL); @@ -1437,18 +1422,20 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum) if (ci->init) ci->init(stmpe); - if (pdata->irq_over_gpio) { - ret = devm_gpio_request_one(ci->dev, pdata->irq_gpio, - GPIOF_DIR_IN, "stmpe"); - if (ret) { - dev_err(stmpe->dev, "failed to request IRQ GPIO: %d\n", - ret); - return ret; - } + irq_gpio = devm_gpiod_get_optional(ci->dev, "irq", GPIOD_ASIS); + ret = PTR_ERR_OR_ZERO(irq_gpio); + if (ret) { + dev_err(stmpe->dev, "failed to request IRQ GPIO: %d\n", ret); + return ret; + } - stmpe->irq = gpio_to_irq(pdata->irq_gpio); + if (irq_gpio) { + stmpe->irq = gpiod_to_irq(irq_gpio); + pdata->irq_trigger = gpiod_is_active_low(irq_gpio) ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH; } else { stmpe->irq = ci->irq; + pdata->irq_trigger = IRQF_TRIGGER_NONE; } if (stmpe->irq < 0) { @@ -1495,22 +1482,24 @@ int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum) return ret; } +EXPORT_SYMBOL_GPL(stmpe_probe); -int stmpe_remove(struct stmpe *stmpe) +void stmpe_remove(struct stmpe *stmpe) { - if (!IS_ERR(stmpe->vio)) + 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)) + if (!IS_ERR(stmpe->vcc) && regulator_is_enabled(stmpe->vcc)) regulator_disable(stmpe->vcc); __stmpe_disable(stmpe, STMPE_BLOCK_ADC); mfd_remove_devices(stmpe->dev); - - return 0; } +EXPORT_SYMBOL_GPL(stmpe_remove); -#ifdef CONFIG_PM static int stmpe_suspend(struct device *dev) { struct stmpe *stmpe = dev_get_drvdata(dev); @@ -1531,8 +1520,9 @@ static int stmpe_resume(struct device *dev) return 0; } -const struct dev_pm_ops stmpe_dev_pm_ops = { - .suspend = stmpe_suspend, - .resume = stmpe_resume, -}; -#endif +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/stmpe.h b/drivers/mfd/stmpe.h index 83491e99ba3c..1b4f91d03bbf 100644 --- a/drivers/mfd/stmpe.h +++ b/drivers/mfd/stmpe.h @@ -98,7 +98,7 @@ struct stmpe_client_info { }; int stmpe_probe(struct stmpe_client_info *ci, enum stmpe_partnum partnum); -int stmpe_remove(struct stmpe *stmpe); +void stmpe_remove(struct stmpe *stmpe); #define STMPE_ICR_LSB_HIGH (1 << 2) #define STMPE_ICR_LSB_EDGE (1 << 1) diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c index eb3da558c3fb..081827bc0596 100644 --- a/drivers/mfd/stpmic1.c +++ b/drivers/mfd/stpmic1.c @@ -7,6 +7,7 @@ #include <linux/mfd/core.h> #include <linux/mfd/stpmic1.h> #include <linux/module.h> +#include <linux/reboot.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/of_platform.h> @@ -19,7 +20,7 @@ static const struct regmap_range stpmic1_readable_ranges[] = { regmap_reg_range(TURN_ON_SR, VERSION_SR), - regmap_reg_range(SWOFF_PWRCTRL_CR, LDO6_STDBY_CR), + regmap_reg_range(MAIN_CR, LDO6_STDBY_CR), regmap_reg_range(BST_SW_CR, BST_SW_CR), regmap_reg_range(INT_PENDING_R1, INT_PENDING_R4), regmap_reg_range(INT_CLEAR_R1, INT_CLEAR_R4), @@ -30,7 +31,7 @@ static const struct regmap_range stpmic1_readable_ranges[] = { }; static const struct regmap_range stpmic1_writeable_ranges[] = { - regmap_reg_range(SWOFF_PWRCTRL_CR, LDO6_STDBY_CR), + regmap_reg_range(MAIN_CR, LDO6_STDBY_CR), regmap_reg_range(BST_SW_CR, BST_SW_CR), regmap_reg_range(INT_CLEAR_R1, INT_CLEAR_R4), regmap_reg_range(INT_SET_MASK_R1, INT_SET_MASK_R4), @@ -62,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, @@ -108,16 +109,26 @@ static const struct regmap_irq stpmic1_irqs[] = { static const struct regmap_irq_chip stpmic1_regmap_irq_chip = { .name = "pmic_irq", .status_base = INT_PENDING_R1, - .mask_base = INT_CLEAR_MASK_R1, - .unmask_base = INT_SET_MASK_R1, + .mask_base = INT_SET_MASK_R1, + .unmask_base = INT_CLEAR_MASK_R1, + .mask_unmask_non_inverted = true, .ack_base = INT_CLEAR_R1, .num_regs = STPMIC1_PMIC_NUM_IRQ_REGS, .irqs = stpmic1_irqs, .num_irqs = ARRAY_SIZE(stpmic1_irqs), }; -static int stpmic1_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int stpmic1_power_off(struct sys_off_data *data) +{ + struct stpmic1 *ddata = data->cb_data; + + regmap_update_bits(ddata->regmap, MAIN_CR, + SOFTWARE_SWITCH_OFF, SOFTWARE_SWITCH_OFF); + + return NOTIFY_DONE; +} + +static int stpmic1_probe(struct i2c_client *i2c) { struct stpmic1 *ddata; struct device *dev = &i2c->dev; @@ -159,10 +170,15 @@ static int stpmic1_probe(struct i2c_client *i2c, return ret; } + 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; + } + return devm_of_platform_populate(dev); } -#ifdef CONFIG_PM_SLEEP static int stpmic1_suspend(struct device *dev) { struct i2c_client *i2c = to_i2c_client(dev); @@ -187,9 +203,8 @@ static int stpmic1_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(stpmic1_pm, stpmic1_suspend, stpmic1_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(stpmic1_pm, stpmic1_suspend, stpmic1_resume); static const struct of_device_id stpmic1_of_match[] = { { .compatible = "st,stpmic1", }, @@ -200,8 +215,8 @@ 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), - .pm = &stpmic1_pm, + .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 7478f03ccbae..5ed64d53c23d 100644 --- a/drivers/mfd/stw481x.c +++ b/drivers/mfd/stw481x.c @@ -173,8 +173,7 @@ static const struct regmap_config stw481x_regmap_config = { .val_bits = 8, }; -static int stw481x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int stw481x_probe(struct i2c_client *client) { struct stw481x *stw481x; int ret; @@ -223,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 cfe14d9bf6dc..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> @@ -34,9 +34,8 @@ static const struct regmap_irq_chip sun4i_gpadc_regmap_irq_chip = { .name = "sun4i_gpadc_irq_chip", .status_base = SUN4I_GPADC_INT_FIFOS, .ack_base = SUN4I_GPADC_INT_FIFOS, - .mask_base = SUN4I_GPADC_INT_FIFOC, + .unmask_base = SUN4I_GPADC_INT_FIFOC, .init_ack_masked = true, - .mask_invert = true, .irqs = sun4i_gpadc_regmap_irq, .num_irqs = ARRAY_SIZE(sun4i_gpadc_regmap_irq), .num_regs = 1, @@ -73,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[] = { @@ -94,7 +92,6 @@ MODULE_DEVICE_TABLE(of, sun4i_gpadc_of_match); static int sun4i_gpadc_probe(struct platform_device *pdev) { struct sun4i_gpadc_dev *dev; - struct resource *mem; const struct of_device_id *of_id; const struct mfd_cell *cells; unsigned int irq, size; @@ -125,8 +122,7 @@ static int sun4i_gpadc_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->base = devm_ioremap_resource(&pdev->dev, mem); + dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(dev->base)) return PTR_ERR(dev->base); diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 765c0210cb52..e5d5def594f6 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -8,29 +8,26 @@ * 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 { struct device_node *np; struct regmap *regmap; + struct reset_control *reset; struct list_head list; }; @@ -40,31 +37,30 @@ static const struct regmap_config syscon_regmap_config = { .reg_stride = 4, }; -static struct syscon *of_syscon_register(struct device_node *np, bool check_clk) +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; int ret; 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 = ioremap(res.start, resource_size(&res)); - if (!base) { - ret = -ENOMEM; - goto err_map; - } + base = of_iomap(np, 0); + if (!base) + return ERR_PTR(-ENOMEM); /* Parse the device's DT node for an endianness specification */ if (of_property_read_bool(np, "big-endian")) @@ -101,11 +97,22 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk) } } - syscon_config.name = kasprintf(GFP_KERNEL, "%pOFn@%llx", np, - (u64)res.start); + 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); @@ -115,7 +122,7 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk) goto err_regmap; } - if (check_clk) { + if (check_res) { clk = of_clk_get(np, 0); if (IS_ERR(clk)) { ret = PTR_ERR(clk); @@ -125,37 +132,46 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk) } else { ret = regmap_mmio_attach_clk(regmap, clk); if (ret) - goto err_attach; + goto err_attach_clk; } + + reset = of_reset_control_get_optional_exclusive(np, NULL); + if (IS_ERR(reset)) { + ret = PTR_ERR(reset); + goto err_attach_clk; + } + + ret = reset_control_deassert(reset); + if (ret) + goto err_reset; } 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_attach: +err_reset: + reset_control_put(reset); +err_attach_clk: if (!IS_ERR(clk)) clk_put(clk); 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 check_clk) + 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) { @@ -163,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_clk); + 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); @@ -174,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); @@ -220,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; } @@ -272,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/t7l66xb.c b/drivers/mfd/t7l66xb.c deleted file mode 100644 index 5369c67e3280..000000000000 --- a/drivers/mfd/t7l66xb.c +++ /dev/null @@ -1,436 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Toshiba T7L66XB core mfd support - * - * Copyright (c) 2005, 2007, 2008 Ian Molton - * Copyright (c) 2008 Dmitry Baryshkov - * - * T7L66 features: - * - * Supported in this driver: - * SD/MMC - * SM/NAND flash controller - * - * As yet not supported - * GPIO interface (on NAND pins) - * Serial interface - * TFT 'interface converter' - * PCMCIA interface logic - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/irq.h> -#include <linux/clk.h> -#include <linux/platform_device.h> -#include <linux/mfd/core.h> -#include <linux/mfd/tmio.h> -#include <linux/mfd/t7l66xb.h> - -enum { - T7L66XB_CELL_NAND, - T7L66XB_CELL_MMC, -}; - -static const struct resource t7l66xb_mmc_resources[] = { - DEFINE_RES_MEM(0x800, 0x200), - DEFINE_RES_IRQ(IRQ_T7L66XB_MMC) -}; - -#define SCR_REVID 0x08 /* b Revision ID */ -#define SCR_IMR 0x42 /* b Interrupt Mask */ -#define SCR_DEV_CTL 0xe0 /* b Device control */ -#define SCR_ISR 0xe1 /* b Interrupt Status */ -#define SCR_GPO_OC 0xf0 /* b GPO output control */ -#define SCR_GPO_OS 0xf1 /* b GPO output enable */ -#define SCR_GPI_S 0xf2 /* w GPI status */ -#define SCR_APDC 0xf8 /* b Active pullup down ctrl */ - -#define SCR_DEV_CTL_USB BIT(0) /* USB enable */ -#define SCR_DEV_CTL_MMC BIT(1) /* MMC enable */ - -/*--------------------------------------------------------------------------*/ - -struct t7l66xb { - void __iomem *scr; - /* Lock to protect registers requiring read/modify/write ops. */ - raw_spinlock_t lock; - - struct resource rscr; - struct clk *clk48m; - struct clk *clk32k; - int irq; - int irq_base; -}; - -/*--------------------------------------------------------------------------*/ - -static int t7l66xb_mmc_enable(struct platform_device *mmc) -{ - struct t7l66xb *t7l66xb = dev_get_drvdata(mmc->dev.parent); - unsigned long flags; - u8 dev_ctl; - int ret; - - ret = clk_prepare_enable(t7l66xb->clk32k); - if (ret) - return ret; - - raw_spin_lock_irqsave(&t7l66xb->lock, flags); - - dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL); - dev_ctl |= SCR_DEV_CTL_MMC; - tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL); - - raw_spin_unlock_irqrestore(&t7l66xb->lock, flags); - - tmio_core_mmc_enable(t7l66xb->scr + 0x200, 0, - t7l66xb_mmc_resources[0].start & 0xfffe); - - return 0; -} - -static int t7l66xb_mmc_disable(struct platform_device *mmc) -{ - struct t7l66xb *t7l66xb = dev_get_drvdata(mmc->dev.parent); - unsigned long flags; - u8 dev_ctl; - - raw_spin_lock_irqsave(&t7l66xb->lock, flags); - - dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL); - dev_ctl &= ~SCR_DEV_CTL_MMC; - tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL); - - raw_spin_unlock_irqrestore(&t7l66xb->lock, flags); - - clk_disable_unprepare(t7l66xb->clk32k); - - return 0; -} - -static void t7l66xb_mmc_pwr(struct platform_device *mmc, int state) -{ - struct t7l66xb *t7l66xb = dev_get_drvdata(mmc->dev.parent); - - tmio_core_mmc_pwr(t7l66xb->scr + 0x200, 0, state); -} - -static void t7l66xb_mmc_clk_div(struct platform_device *mmc, int state) -{ - struct t7l66xb *t7l66xb = dev_get_drvdata(mmc->dev.parent); - - tmio_core_mmc_clk_div(t7l66xb->scr + 0x200, 0, state); -} - -/*--------------------------------------------------------------------------*/ - -static struct tmio_mmc_data t7166xb_mmc_data = { - .hclk = 24000000, - .set_pwr = t7l66xb_mmc_pwr, - .set_clk_div = t7l66xb_mmc_clk_div, -}; - -static const struct resource t7l66xb_nand_resources[] = { - { - .start = 0xc00, - .end = 0xc07, - .flags = IORESOURCE_MEM, - }, - { - .start = 0x0100, - .end = 0x01ff, - .flags = IORESOURCE_MEM, - }, - { - .start = IRQ_T7L66XB_NAND, - .end = IRQ_T7L66XB_NAND, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct mfd_cell t7l66xb_cells[] = { - [T7L66XB_CELL_MMC] = { - .name = "tmio-mmc", - .enable = t7l66xb_mmc_enable, - .disable = t7l66xb_mmc_disable, - .platform_data = &t7166xb_mmc_data, - .pdata_size = sizeof(t7166xb_mmc_data), - .num_resources = ARRAY_SIZE(t7l66xb_mmc_resources), - .resources = t7l66xb_mmc_resources, - }, - [T7L66XB_CELL_NAND] = { - .name = "tmio-nand", - .num_resources = ARRAY_SIZE(t7l66xb_nand_resources), - .resources = t7l66xb_nand_resources, - }, -}; - -/*--------------------------------------------------------------------------*/ - -/* Handle the T7L66XB interrupt mux */ -static void t7l66xb_irq(struct irq_desc *desc) -{ - struct t7l66xb *t7l66xb = irq_desc_get_handler_data(desc); - unsigned int isr; - unsigned int i, irq_base; - - irq_base = t7l66xb->irq_base; - - while ((isr = tmio_ioread8(t7l66xb->scr + SCR_ISR) & - ~tmio_ioread8(t7l66xb->scr + SCR_IMR))) - for (i = 0; i < T7L66XB_NR_IRQS; i++) - if (isr & (1 << i)) - generic_handle_irq(irq_base + i); -} - -static void t7l66xb_irq_mask(struct irq_data *data) -{ - struct t7l66xb *t7l66xb = irq_data_get_irq_chip_data(data); - unsigned long flags; - u8 imr; - - raw_spin_lock_irqsave(&t7l66xb->lock, flags); - imr = tmio_ioread8(t7l66xb->scr + SCR_IMR); - imr |= 1 << (data->irq - t7l66xb->irq_base); - tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR); - raw_spin_unlock_irqrestore(&t7l66xb->lock, flags); -} - -static void t7l66xb_irq_unmask(struct irq_data *data) -{ - struct t7l66xb *t7l66xb = irq_data_get_irq_chip_data(data); - unsigned long flags; - u8 imr; - - raw_spin_lock_irqsave(&t7l66xb->lock, flags); - imr = tmio_ioread8(t7l66xb->scr + SCR_IMR); - imr &= ~(1 << (data->irq - t7l66xb->irq_base)); - tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR); - raw_spin_unlock_irqrestore(&t7l66xb->lock, flags); -} - -static struct irq_chip t7l66xb_chip = { - .name = "t7l66xb", - .irq_ack = t7l66xb_irq_mask, - .irq_mask = t7l66xb_irq_mask, - .irq_unmask = t7l66xb_irq_unmask, -}; - -/*--------------------------------------------------------------------------*/ - -/* Install the IRQ handler */ -static void t7l66xb_attach_irq(struct platform_device *dev) -{ - struct t7l66xb *t7l66xb = platform_get_drvdata(dev); - unsigned int irq, irq_base; - - irq_base = t7l66xb->irq_base; - - for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) { - irq_set_chip_and_handler(irq, &t7l66xb_chip, handle_level_irq); - irq_set_chip_data(irq, t7l66xb); - } - - irq_set_irq_type(t7l66xb->irq, IRQ_TYPE_EDGE_FALLING); - irq_set_chained_handler_and_data(t7l66xb->irq, t7l66xb_irq, t7l66xb); -} - -static void t7l66xb_detach_irq(struct platform_device *dev) -{ - struct t7l66xb *t7l66xb = platform_get_drvdata(dev); - unsigned int irq, irq_base; - - irq_base = t7l66xb->irq_base; - - irq_set_chained_handler_and_data(t7l66xb->irq, NULL, NULL); - - for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) { - irq_set_chip(irq, NULL); - irq_set_chip_data(irq, NULL); - } -} - -/*--------------------------------------------------------------------------*/ - -#ifdef CONFIG_PM -static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state) -{ - struct t7l66xb *t7l66xb = platform_get_drvdata(dev); - struct t7l66xb_platform_data *pdata = dev_get_platdata(&dev->dev); - - if (pdata && pdata->suspend) - pdata->suspend(dev); - clk_disable_unprepare(t7l66xb->clk48m); - - return 0; -} - -static int t7l66xb_resume(struct platform_device *dev) -{ - struct t7l66xb *t7l66xb = platform_get_drvdata(dev); - struct t7l66xb_platform_data *pdata = dev_get_platdata(&dev->dev); - int ret; - - ret = clk_prepare_enable(t7l66xb->clk48m); - if (ret) - return ret; - - if (pdata && pdata->resume) - pdata->resume(dev); - - tmio_core_mmc_enable(t7l66xb->scr + 0x200, 0, - t7l66xb_mmc_resources[0].start & 0xfffe); - - return 0; -} -#else -#define t7l66xb_suspend NULL -#define t7l66xb_resume NULL -#endif - -/*--------------------------------------------------------------------------*/ - -static int t7l66xb_probe(struct platform_device *dev) -{ - struct t7l66xb_platform_data *pdata = dev_get_platdata(&dev->dev); - struct t7l66xb *t7l66xb; - struct resource *iomem, *rscr; - int ret; - - if (!pdata) - return -EINVAL; - - iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!iomem) - return -EINVAL; - - t7l66xb = kzalloc(sizeof *t7l66xb, GFP_KERNEL); - if (!t7l66xb) - return -ENOMEM; - - raw_spin_lock_init(&t7l66xb->lock); - - platform_set_drvdata(dev, t7l66xb); - - ret = platform_get_irq(dev, 0); - if (ret >= 0) - t7l66xb->irq = ret; - else - goto err_noirq; - - t7l66xb->irq_base = pdata->irq_base; - - t7l66xb->clk32k = clk_get(&dev->dev, "CLK_CK32K"); - if (IS_ERR(t7l66xb->clk32k)) { - ret = PTR_ERR(t7l66xb->clk32k); - goto err_clk32k_get; - } - - t7l66xb->clk48m = clk_get(&dev->dev, "CLK_CK48M"); - if (IS_ERR(t7l66xb->clk48m)) { - ret = PTR_ERR(t7l66xb->clk48m); - goto err_clk48m_get; - } - - rscr = &t7l66xb->rscr; - rscr->name = "t7l66xb-core"; - rscr->start = iomem->start; - rscr->end = iomem->start + 0xff; - rscr->flags = IORESOURCE_MEM; - - ret = request_resource(iomem, rscr); - if (ret) - goto err_request_scr; - - t7l66xb->scr = ioremap(rscr->start, resource_size(rscr)); - if (!t7l66xb->scr) { - ret = -ENOMEM; - goto err_ioremap; - } - - ret = clk_prepare_enable(t7l66xb->clk48m); - if (ret) - goto err_clk_enable; - - if (pdata->enable) - pdata->enable(dev); - - /* Mask all interrupts */ - tmio_iowrite8(0xbf, t7l66xb->scr + SCR_IMR); - - printk(KERN_INFO "%s rev %d @ 0x%08lx, irq %d\n", - dev->name, tmio_ioread8(t7l66xb->scr + SCR_REVID), - (unsigned long)iomem->start, t7l66xb->irq); - - t7l66xb_attach_irq(dev); - - t7l66xb_cells[T7L66XB_CELL_NAND].platform_data = pdata->nand_data; - t7l66xb_cells[T7L66XB_CELL_NAND].pdata_size = sizeof(*pdata->nand_data); - - ret = mfd_add_devices(&dev->dev, dev->id, - t7l66xb_cells, ARRAY_SIZE(t7l66xb_cells), - iomem, t7l66xb->irq_base, NULL); - - if (!ret) - return 0; - - t7l66xb_detach_irq(dev); - clk_disable_unprepare(t7l66xb->clk48m); -err_clk_enable: - iounmap(t7l66xb->scr); -err_ioremap: - release_resource(&t7l66xb->rscr); -err_request_scr: - clk_put(t7l66xb->clk48m); -err_clk48m_get: - clk_put(t7l66xb->clk32k); -err_clk32k_get: -err_noirq: - kfree(t7l66xb); - return ret; -} - -static int t7l66xb_remove(struct platform_device *dev) -{ - struct t7l66xb_platform_data *pdata = dev_get_platdata(&dev->dev); - struct t7l66xb *t7l66xb = platform_get_drvdata(dev); - int ret; - - ret = pdata->disable(dev); - clk_disable_unprepare(t7l66xb->clk48m); - clk_put(t7l66xb->clk48m); - clk_disable_unprepare(t7l66xb->clk32k); - clk_put(t7l66xb->clk32k); - t7l66xb_detach_irq(dev); - iounmap(t7l66xb->scr); - release_resource(&t7l66xb->rscr); - mfd_remove_devices(&dev->dev); - kfree(t7l66xb); - - return ret; - -} - -static struct platform_driver t7l66xb_platform_driver = { - .driver = { - .name = "t7l66xb", - }, - .suspend = t7l66xb_suspend, - .resume = t7l66xb_resume, - .probe = t7l66xb_probe, - .remove = t7l66xb_remove, -}; - -/*--------------------------------------------------------------------------*/ - -module_platform_driver(t7l66xb_platform_driver); - -MODULE_DESCRIPTION("Toshiba T7L66XB core driver"); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Ian Molton"); -MODULE_ALIAS("platform:t7l66xb"); diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index 7614f8fe0e91..2d4eb771e230 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -187,7 +187,7 @@ again: while (status) { int bit = __ffs(status); - int virq = irq_create_mapping(tc3589x->domain, bit); + int virq = irq_find_mapping(tc3589x->domain, bit); handle_nested_irq(virq); status &= ~(1 << bit); @@ -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")) @@ -352,9 +350,9 @@ tc3589x_of_probe(struct device *dev, enum tc3589x_version *version) return pdata; } -static int tc3589x_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int tc3589x_probe(struct i2c_client *i2c) { + const struct i2c_device_id *id = i2c_client_get_device_id(i2c); struct device_node *np = i2c->dev.of_node; struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev); struct tc3589x *tc3589x; @@ -429,16 +427,13 @@ static int tc3589x_probe(struct i2c_client *i2c, return 0; } -static int tc3589x_remove(struct i2c_client *client) +static void tc3589x_remove(struct i2c_client *client) { struct tc3589x *tc3589x = i2c_get_clientdata(client); mfd_remove_devices(tc3589x->dev); - - return 0; } -#ifdef CONFIG_PM_SLEEP static int tc3589x_suspend(struct device *dev) { struct tc3589x *tc3589x = dev_get_drvdata(dev); @@ -466,9 +461,9 @@ static int tc3589x_resume(struct device *dev) return ret; } -#endif -static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, + tc3589x_suspend, tc3589x_resume); static const struct i2c_device_id tc3589x_id[] = { { "tc35890", TC3589X_TC35890 }, @@ -485,8 +480,8 @@ MODULE_DEVICE_TABLE(i2c, tc3589x_id); static struct i2c_driver tc3589x_driver = { .driver = { .name = "tc3589x", - .pm = &tc3589x_dev_pm_ops, - .of_match_table = of_match_ptr(tc3589x_match), + .pm = pm_sleep_ptr(&tc3589x_dev_pm_ops), + .of_match_table = tc3589x_match, }, .probe = tc3589x_probe, .remove = tc3589x_remove, @@ -505,6 +500,5 @@ static void __exit tc3589x_exit(void) } module_exit(tc3589x_exit); -MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("TC3589x MFD core driver"); MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c deleted file mode 100644 index e846e4d26b6e..000000000000 --- a/drivers/mfd/tc6387xb.c +++ /dev/null @@ -1,233 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Toshiba TC6387XB support - * Copyright (c) 2005 Ian Molton - * - * This file contains TC6387XB base support. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/err.h> -#include <linux/mfd/core.h> -#include <linux/mfd/tmio.h> -#include <linux/mfd/tc6387xb.h> -#include <linux/slab.h> - -enum { - TC6387XB_CELL_MMC, -}; - -struct tc6387xb { - void __iomem *scr; - struct clk *clk32k; - struct resource rscr; -}; - -static const struct resource tc6387xb_mmc_resources[] = { - { - .start = 0x800, - .end = 0x9ff, - .flags = IORESOURCE_MEM, - }, - { - .start = 0, - .end = 0, - .flags = IORESOURCE_IRQ, - }, -}; - -/*--------------------------------------------------------------------------*/ - -#ifdef CONFIG_PM -static int tc6387xb_suspend(struct platform_device *dev, pm_message_t state) -{ - struct tc6387xb *tc6387xb = platform_get_drvdata(dev); - struct tc6387xb_platform_data *pdata = dev_get_platdata(&dev->dev); - - if (pdata && pdata->suspend) - pdata->suspend(dev); - clk_disable_unprepare(tc6387xb->clk32k); - - return 0; -} - -static int tc6387xb_resume(struct platform_device *dev) -{ - struct tc6387xb *tc6387xb = platform_get_drvdata(dev); - struct tc6387xb_platform_data *pdata = dev_get_platdata(&dev->dev); - - clk_prepare_enable(tc6387xb->clk32k); - if (pdata && pdata->resume) - pdata->resume(dev); - - tmio_core_mmc_resume(tc6387xb->scr + 0x200, 0, - tc6387xb_mmc_resources[0].start & 0xfffe); - - return 0; -} -#else -#define tc6387xb_suspend NULL -#define tc6387xb_resume NULL -#endif - -/*--------------------------------------------------------------------------*/ - -static void tc6387xb_mmc_pwr(struct platform_device *mmc, int state) -{ - struct tc6387xb *tc6387xb = dev_get_drvdata(mmc->dev.parent); - - tmio_core_mmc_pwr(tc6387xb->scr + 0x200, 0, state); -} - -static void tc6387xb_mmc_clk_div(struct platform_device *mmc, int state) -{ - struct tc6387xb *tc6387xb = dev_get_drvdata(mmc->dev.parent); - - tmio_core_mmc_clk_div(tc6387xb->scr + 0x200, 0, state); -} - - -static int tc6387xb_mmc_enable(struct platform_device *mmc) -{ - struct tc6387xb *tc6387xb = dev_get_drvdata(mmc->dev.parent); - - clk_prepare_enable(tc6387xb->clk32k); - - tmio_core_mmc_enable(tc6387xb->scr + 0x200, 0, - tc6387xb_mmc_resources[0].start & 0xfffe); - - return 0; -} - -static int tc6387xb_mmc_disable(struct platform_device *mmc) -{ - struct tc6387xb *tc6387xb = dev_get_drvdata(mmc->dev.parent); - - clk_disable_unprepare(tc6387xb->clk32k); - - return 0; -} - -static struct tmio_mmc_data tc6387xb_mmc_data = { - .hclk = 24000000, - .set_pwr = tc6387xb_mmc_pwr, - .set_clk_div = tc6387xb_mmc_clk_div, -}; - -/*--------------------------------------------------------------------------*/ - -static const struct mfd_cell tc6387xb_cells[] = { - [TC6387XB_CELL_MMC] = { - .name = "tmio-mmc", - .enable = tc6387xb_mmc_enable, - .disable = tc6387xb_mmc_disable, - .platform_data = &tc6387xb_mmc_data, - .pdata_size = sizeof(tc6387xb_mmc_data), - .num_resources = ARRAY_SIZE(tc6387xb_mmc_resources), - .resources = tc6387xb_mmc_resources, - }, -}; - -static int tc6387xb_probe(struct platform_device *dev) -{ - struct tc6387xb_platform_data *pdata = dev_get_platdata(&dev->dev); - struct resource *iomem, *rscr; - struct clk *clk32k; - struct tc6387xb *tc6387xb; - int irq, ret; - - iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!iomem) - return -EINVAL; - - tc6387xb = kzalloc(sizeof(*tc6387xb), GFP_KERNEL); - if (!tc6387xb) - return -ENOMEM; - - ret = platform_get_irq(dev, 0); - if (ret >= 0) - irq = ret; - else - goto err_no_irq; - - clk32k = clk_get(&dev->dev, "CLK_CK32K"); - if (IS_ERR(clk32k)) { - ret = PTR_ERR(clk32k); - goto err_no_clk; - } - - rscr = &tc6387xb->rscr; - rscr->name = "tc6387xb-core"; - rscr->start = iomem->start; - rscr->end = iomem->start + 0xff; - rscr->flags = IORESOURCE_MEM; - - ret = request_resource(iomem, rscr); - if (ret) - goto err_resource; - - tc6387xb->scr = ioremap(rscr->start, resource_size(rscr)); - if (!tc6387xb->scr) { - ret = -ENOMEM; - goto err_ioremap; - } - - tc6387xb->clk32k = clk32k; - platform_set_drvdata(dev, tc6387xb); - - if (pdata && pdata->enable) - pdata->enable(dev); - - dev_info(&dev->dev, "Toshiba tc6387xb initialised\n"); - - ret = mfd_add_devices(&dev->dev, dev->id, tc6387xb_cells, - ARRAY_SIZE(tc6387xb_cells), iomem, irq, NULL); - - if (!ret) - return 0; - - iounmap(tc6387xb->scr); -err_ioremap: - release_resource(&tc6387xb->rscr); -err_resource: - clk_put(clk32k); -err_no_clk: -err_no_irq: - kfree(tc6387xb); - return ret; -} - -static int tc6387xb_remove(struct platform_device *dev) -{ - struct tc6387xb *tc6387xb = platform_get_drvdata(dev); - - mfd_remove_devices(&dev->dev); - iounmap(tc6387xb->scr); - release_resource(&tc6387xb->rscr); - clk_disable_unprepare(tc6387xb->clk32k); - clk_put(tc6387xb->clk32k); - kfree(tc6387xb); - - return 0; -} - - -static struct platform_driver tc6387xb_platform_driver = { - .driver = { - .name = "tc6387xb", - }, - .probe = tc6387xb_probe, - .remove = tc6387xb_remove, - .suspend = tc6387xb_suspend, - .resume = tc6387xb_resume, -}; - -module_platform_driver(tc6387xb_platform_driver); - -MODULE_DESCRIPTION("Toshiba TC6387XB core driver"); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Ian Molton"); -MODULE_ALIAS("platform:tc6387xb"); - diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c deleted file mode 100644 index 3d5b14c60e20..000000000000 --- a/drivers/mfd/tc6393xb.c +++ /dev/null @@ -1,855 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Toshiba TC6393XB SoC support - * - * Copyright(c) 2005-2006 Chris Humbert - * Copyright(c) 2005 Dirk Opfer - * Copyright(c) 2005 Ian Molton <spyro@f2s.com> - * Copyright(c) 2007 Dmitry Baryshkov - * - * Based on code written by Sharp/Lineo for 2.4 kernels - * Based on locomo.c - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/err.h> -#include <linux/mfd/core.h> -#include <linux/mfd/tmio.h> -#include <linux/mfd/tc6393xb.h> -#include <linux/gpio/driver.h> -#include <linux/slab.h> - -#define SCR_REVID 0x08 /* b Revision ID */ -#define SCR_ISR 0x50 /* b Interrupt Status */ -#define SCR_IMR 0x52 /* b Interrupt Mask */ -#define SCR_IRR 0x54 /* b Interrupt Routing */ -#define SCR_GPER 0x60 /* w GP Enable */ -#define SCR_GPI_SR(i) (0x64 + (i)) /* b3 GPI Status */ -#define SCR_GPI_IMR(i) (0x68 + (i)) /* b3 GPI INT Mask */ -#define SCR_GPI_EDER(i) (0x6c + (i)) /* b3 GPI Edge Detect Enable */ -#define SCR_GPI_LIR(i) (0x70 + (i)) /* b3 GPI Level Invert */ -#define SCR_GPO_DSR(i) (0x78 + (i)) /* b3 GPO Data Set */ -#define SCR_GPO_DOECR(i) (0x7c + (i)) /* b3 GPO Data OE Control */ -#define SCR_GP_IARCR(i) (0x80 + (i)) /* b3 GP Internal Active Register Control */ -#define SCR_GP_IARLCR(i) (0x84 + (i)) /* b3 GP INTERNAL Active Register Level Control */ -#define SCR_GPI_BCR(i) (0x88 + (i)) /* b3 GPI Buffer Control */ -#define SCR_GPA_IARCR 0x8c /* w GPa Internal Active Register Control */ -#define SCR_GPA_IARLCR 0x90 /* w GPa Internal Active Register Level Control */ -#define SCR_GPA_BCR 0x94 /* w GPa Buffer Control */ -#define SCR_CCR 0x98 /* w Clock Control */ -#define SCR_PLL2CR 0x9a /* w PLL2 Control */ -#define SCR_PLL1CR 0x9c /* l PLL1 Control */ -#define SCR_DIARCR 0xa0 /* b Device Internal Active Register Control */ -#define SCR_DBOCR 0xa1 /* b Device Buffer Off Control */ -#define SCR_FER 0xe0 /* b Function Enable */ -#define SCR_MCR 0xe4 /* w Mode Control */ -#define SCR_CONFIG 0xfc /* b Configuration Control */ -#define SCR_DEBUG 0xff /* b Debug */ - -#define SCR_CCR_CK32K BIT(0) -#define SCR_CCR_USBCK BIT(1) -#define SCR_CCR_UNK1 BIT(4) -#define SCR_CCR_MCLK_MASK (7 << 8) -#define SCR_CCR_MCLK_OFF (0 << 8) -#define SCR_CCR_MCLK_12 (1 << 8) -#define SCR_CCR_MCLK_24 (2 << 8) -#define SCR_CCR_MCLK_48 (3 << 8) -#define SCR_CCR_HCLK_MASK (3 << 12) -#define SCR_CCR_HCLK_24 (0 << 12) -#define SCR_CCR_HCLK_48 (1 << 12) - -#define SCR_FER_USBEN BIT(0) /* USB host enable */ -#define SCR_FER_LCDCVEN BIT(1) /* polysilicon TFT enable */ -#define SCR_FER_SLCDEN BIT(2) /* SLCD enable */ - -#define SCR_MCR_RDY_MASK (3 << 0) -#define SCR_MCR_RDY_OPENDRAIN (0 << 0) -#define SCR_MCR_RDY_TRISTATE (1 << 0) -#define SCR_MCR_RDY_PUSHPULL (2 << 0) -#define SCR_MCR_RDY_UNK BIT(2) -#define SCR_MCR_RDY_EN BIT(3) -#define SCR_MCR_INT_MASK (3 << 4) -#define SCR_MCR_INT_OPENDRAIN (0 << 4) -#define SCR_MCR_INT_TRISTATE (1 << 4) -#define SCR_MCR_INT_PUSHPULL (2 << 4) -#define SCR_MCR_INT_UNK BIT(6) -#define SCR_MCR_INT_EN BIT(7) -/* bits 8 - 16 are unknown */ - -#define TC_GPIO_BIT(i) (1 << (i & 0x7)) - -/*--------------------------------------------------------------------------*/ - -struct tc6393xb { - void __iomem *scr; - - struct gpio_chip gpio; - - struct clk *clk; /* 3,6 Mhz */ - - raw_spinlock_t lock; /* protects RMW cycles */ - - struct { - u8 fer; - u16 ccr; - u8 gpi_bcr[3]; - u8 gpo_dsr[3]; - u8 gpo_doecr[3]; - } suspend_state; - - struct resource rscr; - struct resource *iomem; - int irq; - int irq_base; -}; - -enum { - TC6393XB_CELL_NAND, - TC6393XB_CELL_MMC, - TC6393XB_CELL_OHCI, - TC6393XB_CELL_FB, -}; - -/*--------------------------------------------------------------------------*/ - -static int tc6393xb_nand_enable(struct platform_device *nand) -{ - struct tc6393xb *tc6393xb = dev_get_drvdata(nand->dev.parent); - unsigned long flags; - - raw_spin_lock_irqsave(&tc6393xb->lock, flags); - - /* SMD buffer on */ - dev_dbg(nand->dev.parent, "SMD buffer on\n"); - tmio_iowrite8(0xff, tc6393xb->scr + SCR_GPI_BCR(1)); - - raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); - - return 0; -} - -static const struct resource tc6393xb_nand_resources[] = { - { - .start = 0x1000, - .end = 0x1007, - .flags = IORESOURCE_MEM, - }, - { - .start = 0x0100, - .end = 0x01ff, - .flags = IORESOURCE_MEM, - }, - { - .start = IRQ_TC6393_NAND, - .end = IRQ_TC6393_NAND, - .flags = IORESOURCE_IRQ, - }, -}; - -static const struct resource tc6393xb_mmc_resources[] = { - { - .start = 0x800, - .end = 0x9ff, - .flags = IORESOURCE_MEM, - }, - { - .start = IRQ_TC6393_MMC, - .end = IRQ_TC6393_MMC, - .flags = IORESOURCE_IRQ, - }, -}; - -static const struct resource tc6393xb_ohci_resources[] = { - { - .start = 0x3000, - .end = 0x31ff, - .flags = IORESOURCE_MEM, - }, - { - .start = 0x0300, - .end = 0x03ff, - .flags = IORESOURCE_MEM, - }, - { - .start = 0x010000, - .end = 0x017fff, - .flags = IORESOURCE_MEM, - }, - { - .start = 0x018000, - .end = 0x01ffff, - .flags = IORESOURCE_MEM, - }, - { - .start = IRQ_TC6393_OHCI, - .end = IRQ_TC6393_OHCI, - .flags = IORESOURCE_IRQ, - }, -}; - -static const struct resource tc6393xb_fb_resources[] = { - { - .start = 0x5000, - .end = 0x51ff, - .flags = IORESOURCE_MEM, - }, - { - .start = 0x0500, - .end = 0x05ff, - .flags = IORESOURCE_MEM, - }, - { - .start = 0x100000, - .end = 0x1fffff, - .flags = IORESOURCE_MEM, - }, - { - .start = IRQ_TC6393_FB, - .end = IRQ_TC6393_FB, - .flags = IORESOURCE_IRQ, - }, -}; - -static int tc6393xb_ohci_enable(struct platform_device *dev) -{ - struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent); - unsigned long flags; - u16 ccr; - u8 fer; - - raw_spin_lock_irqsave(&tc6393xb->lock, flags); - - ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR); - ccr |= SCR_CCR_USBCK; - tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR); - - fer = tmio_ioread8(tc6393xb->scr + SCR_FER); - fer |= SCR_FER_USBEN; - tmio_iowrite8(fer, tc6393xb->scr + SCR_FER); - - raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); - - return 0; -} - -static int tc6393xb_ohci_disable(struct platform_device *dev) -{ - struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent); - unsigned long flags; - u16 ccr; - u8 fer; - - raw_spin_lock_irqsave(&tc6393xb->lock, flags); - - fer = tmio_ioread8(tc6393xb->scr + SCR_FER); - fer &= ~SCR_FER_USBEN; - tmio_iowrite8(fer, tc6393xb->scr + SCR_FER); - - ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR); - ccr &= ~SCR_CCR_USBCK; - tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR); - - raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); - - return 0; -} - -static int tc6393xb_ohci_suspend(struct platform_device *dev) -{ - struct tc6393xb_platform_data *tcpd = dev_get_platdata(dev->dev.parent); - - /* We can't properly store/restore OHCI state, so fail here */ - if (tcpd->resume_restore) - return -EBUSY; - - return tc6393xb_ohci_disable(dev); -} - -static int tc6393xb_fb_enable(struct platform_device *dev) -{ - struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent); - unsigned long flags; - u16 ccr; - - raw_spin_lock_irqsave(&tc6393xb->lock, flags); - - ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR); - ccr &= ~SCR_CCR_MCLK_MASK; - ccr |= SCR_CCR_MCLK_48; - tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR); - - raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); - - return 0; -} - -static int tc6393xb_fb_disable(struct platform_device *dev) -{ - struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent); - unsigned long flags; - u16 ccr; - - raw_spin_lock_irqsave(&tc6393xb->lock, flags); - - ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR); - ccr &= ~SCR_CCR_MCLK_MASK; - ccr |= SCR_CCR_MCLK_OFF; - tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR); - - raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); - - return 0; -} - -int tc6393xb_lcd_set_power(struct platform_device *fb, bool on) -{ - struct tc6393xb *tc6393xb = dev_get_drvdata(fb->dev.parent); - u8 fer; - unsigned long flags; - - raw_spin_lock_irqsave(&tc6393xb->lock, flags); - - fer = ioread8(tc6393xb->scr + SCR_FER); - if (on) - fer |= SCR_FER_SLCDEN; - else - fer &= ~SCR_FER_SLCDEN; - iowrite8(fer, tc6393xb->scr + SCR_FER); - - raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); - - return 0; -} -EXPORT_SYMBOL(tc6393xb_lcd_set_power); - -int tc6393xb_lcd_mode(struct platform_device *fb, - const struct fb_videomode *mode) { - struct tc6393xb *tc6393xb = dev_get_drvdata(fb->dev.parent); - unsigned long flags; - - raw_spin_lock_irqsave(&tc6393xb->lock, flags); - - iowrite16(mode->pixclock, tc6393xb->scr + SCR_PLL1CR + 0); - iowrite16(mode->pixclock >> 16, tc6393xb->scr + SCR_PLL1CR + 2); - - raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); - - return 0; -} -EXPORT_SYMBOL(tc6393xb_lcd_mode); - -static int tc6393xb_mmc_enable(struct platform_device *mmc) -{ - struct tc6393xb *tc6393xb = dev_get_drvdata(mmc->dev.parent); - - tmio_core_mmc_enable(tc6393xb->scr + 0x200, 0, - tc6393xb_mmc_resources[0].start & 0xfffe); - - return 0; -} - -static int tc6393xb_mmc_resume(struct platform_device *mmc) -{ - struct tc6393xb *tc6393xb = dev_get_drvdata(mmc->dev.parent); - - tmio_core_mmc_resume(tc6393xb->scr + 0x200, 0, - tc6393xb_mmc_resources[0].start & 0xfffe); - - return 0; -} - -static void tc6393xb_mmc_pwr(struct platform_device *mmc, int state) -{ - struct tc6393xb *tc6393xb = dev_get_drvdata(mmc->dev.parent); - - tmio_core_mmc_pwr(tc6393xb->scr + 0x200, 0, state); -} - -static void tc6393xb_mmc_clk_div(struct platform_device *mmc, int state) -{ - struct tc6393xb *tc6393xb = dev_get_drvdata(mmc->dev.parent); - - tmio_core_mmc_clk_div(tc6393xb->scr + 0x200, 0, state); -} - -static struct tmio_mmc_data tc6393xb_mmc_data = { - .hclk = 24000000, - .set_pwr = tc6393xb_mmc_pwr, - .set_clk_div = tc6393xb_mmc_clk_div, -}; - -static struct mfd_cell tc6393xb_cells[] = { - [TC6393XB_CELL_NAND] = { - .name = "tmio-nand", - .enable = tc6393xb_nand_enable, - .num_resources = ARRAY_SIZE(tc6393xb_nand_resources), - .resources = tc6393xb_nand_resources, - }, - [TC6393XB_CELL_MMC] = { - .name = "tmio-mmc", - .enable = tc6393xb_mmc_enable, - .resume = tc6393xb_mmc_resume, - .platform_data = &tc6393xb_mmc_data, - .pdata_size = sizeof(tc6393xb_mmc_data), - .num_resources = ARRAY_SIZE(tc6393xb_mmc_resources), - .resources = tc6393xb_mmc_resources, - }, - [TC6393XB_CELL_OHCI] = { - .name = "tmio-ohci", - .num_resources = ARRAY_SIZE(tc6393xb_ohci_resources), - .resources = tc6393xb_ohci_resources, - .enable = tc6393xb_ohci_enable, - .suspend = tc6393xb_ohci_suspend, - .resume = tc6393xb_ohci_enable, - .disable = tc6393xb_ohci_disable, - }, - [TC6393XB_CELL_FB] = { - .name = "tmio-fb", - .num_resources = ARRAY_SIZE(tc6393xb_fb_resources), - .resources = tc6393xb_fb_resources, - .enable = tc6393xb_fb_enable, - .suspend = tc6393xb_fb_disable, - .resume = tc6393xb_fb_enable, - .disable = tc6393xb_fb_disable, - }, -}; - -/*--------------------------------------------------------------------------*/ - -static int tc6393xb_gpio_get(struct gpio_chip *chip, - unsigned offset) -{ - struct tc6393xb *tc6393xb = gpiochip_get_data(chip); - - /* XXX: does dsr also represent inputs? */ - return !!(tmio_ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8)) - & TC_GPIO_BIT(offset)); -} - -static void __tc6393xb_gpio_set(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct tc6393xb *tc6393xb = gpiochip_get_data(chip); - u8 dsr; - - dsr = tmio_ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8)); - if (value) - dsr |= TC_GPIO_BIT(offset); - else - dsr &= ~TC_GPIO_BIT(offset); - - tmio_iowrite8(dsr, tc6393xb->scr + SCR_GPO_DSR(offset / 8)); -} - -static void tc6393xb_gpio_set(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct tc6393xb *tc6393xb = gpiochip_get_data(chip); - unsigned long flags; - - raw_spin_lock_irqsave(&tc6393xb->lock, flags); - - __tc6393xb_gpio_set(chip, offset, value); - - raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); -} - -static int tc6393xb_gpio_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - struct tc6393xb *tc6393xb = gpiochip_get_data(chip); - unsigned long flags; - u8 doecr; - - raw_spin_lock_irqsave(&tc6393xb->lock, flags); - - doecr = tmio_ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); - doecr &= ~TC_GPIO_BIT(offset); - tmio_iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); - - raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); - - return 0; -} - -static int tc6393xb_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct tc6393xb *tc6393xb = gpiochip_get_data(chip); - unsigned long flags; - u8 doecr; - - raw_spin_lock_irqsave(&tc6393xb->lock, flags); - - __tc6393xb_gpio_set(chip, offset, value); - - doecr = tmio_ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); - doecr |= TC_GPIO_BIT(offset); - tmio_iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); - - raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); - - return 0; -} - -static int tc6393xb_register_gpio(struct tc6393xb *tc6393xb, int gpio_base) -{ - tc6393xb->gpio.label = "tc6393xb"; - tc6393xb->gpio.base = gpio_base; - tc6393xb->gpio.ngpio = 16; - tc6393xb->gpio.set = tc6393xb_gpio_set; - tc6393xb->gpio.get = tc6393xb_gpio_get; - tc6393xb->gpio.direction_input = tc6393xb_gpio_direction_input; - tc6393xb->gpio.direction_output = tc6393xb_gpio_direction_output; - - return gpiochip_add_data(&tc6393xb->gpio, tc6393xb); -} - -/*--------------------------------------------------------------------------*/ - -static void tc6393xb_irq(struct irq_desc *desc) -{ - struct tc6393xb *tc6393xb = irq_desc_get_handler_data(desc); - unsigned int isr; - unsigned int i, irq_base; - - irq_base = tc6393xb->irq_base; - - while ((isr = tmio_ioread8(tc6393xb->scr + SCR_ISR) & - ~tmio_ioread8(tc6393xb->scr + SCR_IMR))) - for (i = 0; i < TC6393XB_NR_IRQS; i++) { - if (isr & (1 << i)) - generic_handle_irq(irq_base + i); - } -} - -static void tc6393xb_irq_ack(struct irq_data *data) -{ -} - -static void tc6393xb_irq_mask(struct irq_data *data) -{ - struct tc6393xb *tc6393xb = irq_data_get_irq_chip_data(data); - unsigned long flags; - u8 imr; - - raw_spin_lock_irqsave(&tc6393xb->lock, flags); - imr = tmio_ioread8(tc6393xb->scr + SCR_IMR); - imr |= 1 << (data->irq - tc6393xb->irq_base); - tmio_iowrite8(imr, tc6393xb->scr + SCR_IMR); - raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); -} - -static void tc6393xb_irq_unmask(struct irq_data *data) -{ - struct tc6393xb *tc6393xb = irq_data_get_irq_chip_data(data); - unsigned long flags; - u8 imr; - - raw_spin_lock_irqsave(&tc6393xb->lock, flags); - imr = tmio_ioread8(tc6393xb->scr + SCR_IMR); - imr &= ~(1 << (data->irq - tc6393xb->irq_base)); - tmio_iowrite8(imr, tc6393xb->scr + SCR_IMR); - raw_spin_unlock_irqrestore(&tc6393xb->lock, flags); -} - -static struct irq_chip tc6393xb_chip = { - .name = "tc6393xb", - .irq_ack = tc6393xb_irq_ack, - .irq_mask = tc6393xb_irq_mask, - .irq_unmask = tc6393xb_irq_unmask, -}; - -static void tc6393xb_attach_irq(struct platform_device *dev) -{ - struct tc6393xb *tc6393xb = platform_get_drvdata(dev); - unsigned int irq, irq_base; - - irq_base = tc6393xb->irq_base; - - for (irq = irq_base; irq < irq_base + TC6393XB_NR_IRQS; irq++) { - irq_set_chip_and_handler(irq, &tc6393xb_chip, handle_edge_irq); - irq_set_chip_data(irq, tc6393xb); - irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); - } - - irq_set_irq_type(tc6393xb->irq, IRQ_TYPE_EDGE_FALLING); - irq_set_chained_handler_and_data(tc6393xb->irq, tc6393xb_irq, - tc6393xb); -} - -static void tc6393xb_detach_irq(struct platform_device *dev) -{ - struct tc6393xb *tc6393xb = platform_get_drvdata(dev); - unsigned int irq, irq_base; - - irq_set_chained_handler_and_data(tc6393xb->irq, NULL, NULL); - - irq_base = tc6393xb->irq_base; - - for (irq = irq_base; irq < irq_base + TC6393XB_NR_IRQS; irq++) { - irq_set_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); - irq_set_chip(irq, NULL); - irq_set_chip_data(irq, NULL); - } -} - -/*--------------------------------------------------------------------------*/ - -static int tc6393xb_probe(struct platform_device *dev) -{ - struct tc6393xb_platform_data *tcpd = dev_get_platdata(&dev->dev); - struct tc6393xb *tc6393xb; - struct resource *iomem, *rscr; - int ret; - - iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!iomem) - return -EINVAL; - - tc6393xb = kzalloc(sizeof *tc6393xb, GFP_KERNEL); - if (!tc6393xb) { - ret = -ENOMEM; - goto err_kzalloc; - } - - raw_spin_lock_init(&tc6393xb->lock); - - platform_set_drvdata(dev, tc6393xb); - - ret = platform_get_irq(dev, 0); - if (ret >= 0) - tc6393xb->irq = ret; - else - goto err_noirq; - - tc6393xb->iomem = iomem; - tc6393xb->irq_base = tcpd->irq_base; - - tc6393xb->clk = clk_get(&dev->dev, "CLK_CK3P6MI"); - if (IS_ERR(tc6393xb->clk)) { - ret = PTR_ERR(tc6393xb->clk); - goto err_clk_get; - } - - rscr = &tc6393xb->rscr; - rscr->name = "tc6393xb-core"; - rscr->start = iomem->start; - rscr->end = iomem->start + 0xff; - rscr->flags = IORESOURCE_MEM; - - ret = request_resource(iomem, rscr); - if (ret) - goto err_request_scr; - - tc6393xb->scr = ioremap(rscr->start, resource_size(rscr)); - if (!tc6393xb->scr) { - ret = -ENOMEM; - goto err_ioremap; - } - - ret = clk_prepare_enable(tc6393xb->clk); - if (ret) - goto err_clk_enable; - - ret = tcpd->enable(dev); - if (ret) - goto err_enable; - - iowrite8(0, tc6393xb->scr + SCR_FER); - iowrite16(tcpd->scr_pll2cr, tc6393xb->scr + SCR_PLL2CR); - iowrite16(SCR_CCR_UNK1 | SCR_CCR_HCLK_48, - tc6393xb->scr + SCR_CCR); - iowrite16(SCR_MCR_RDY_OPENDRAIN | SCR_MCR_RDY_UNK | SCR_MCR_RDY_EN | - SCR_MCR_INT_OPENDRAIN | SCR_MCR_INT_UNK | SCR_MCR_INT_EN | - BIT(15), tc6393xb->scr + SCR_MCR); - iowrite16(tcpd->scr_gper, tc6393xb->scr + SCR_GPER); - iowrite8(0, tc6393xb->scr + SCR_IRR); - iowrite8(0xbf, tc6393xb->scr + SCR_IMR); - - printk(KERN_INFO "Toshiba tc6393xb revision %d at 0x%08lx, irq %d\n", - tmio_ioread8(tc6393xb->scr + SCR_REVID), - (unsigned long) iomem->start, tc6393xb->irq); - - tc6393xb->gpio.base = -1; - - if (tcpd->gpio_base >= 0) { - ret = tc6393xb_register_gpio(tc6393xb, tcpd->gpio_base); - if (ret) - goto err_gpio_add; - } - - tc6393xb_attach_irq(dev); - - if (tcpd->setup) { - ret = tcpd->setup(dev); - if (ret) - goto err_setup; - } - - tc6393xb_cells[TC6393XB_CELL_NAND].platform_data = tcpd->nand_data; - tc6393xb_cells[TC6393XB_CELL_NAND].pdata_size = - sizeof(*tcpd->nand_data); - tc6393xb_cells[TC6393XB_CELL_FB].platform_data = tcpd->fb_data; - tc6393xb_cells[TC6393XB_CELL_FB].pdata_size = sizeof(*tcpd->fb_data); - - ret = mfd_add_devices(&dev->dev, dev->id, - tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), - iomem, tcpd->irq_base, NULL); - - if (!ret) - return 0; - - if (tcpd->teardown) - tcpd->teardown(dev); - -err_setup: - tc6393xb_detach_irq(dev); - -err_gpio_add: - if (tc6393xb->gpio.base != -1) - gpiochip_remove(&tc6393xb->gpio); - tcpd->disable(dev); -err_enable: - clk_disable_unprepare(tc6393xb->clk); -err_clk_enable: - iounmap(tc6393xb->scr); -err_ioremap: - release_resource(&tc6393xb->rscr); -err_request_scr: - clk_put(tc6393xb->clk); -err_noirq: -err_clk_get: - kfree(tc6393xb); -err_kzalloc: - return ret; -} - -static int tc6393xb_remove(struct platform_device *dev) -{ - struct tc6393xb_platform_data *tcpd = dev_get_platdata(&dev->dev); - struct tc6393xb *tc6393xb = platform_get_drvdata(dev); - int ret; - - mfd_remove_devices(&dev->dev); - - if (tcpd->teardown) - tcpd->teardown(dev); - - tc6393xb_detach_irq(dev); - - if (tc6393xb->gpio.base != -1) - gpiochip_remove(&tc6393xb->gpio); - - ret = tcpd->disable(dev); - clk_disable_unprepare(tc6393xb->clk); - iounmap(tc6393xb->scr); - release_resource(&tc6393xb->rscr); - clk_put(tc6393xb->clk); - kfree(tc6393xb); - - return ret; -} - -#ifdef CONFIG_PM -static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state) -{ - struct tc6393xb_platform_data *tcpd = dev_get_platdata(&dev->dev); - struct tc6393xb *tc6393xb = platform_get_drvdata(dev); - int i, ret; - - tc6393xb->suspend_state.ccr = ioread16(tc6393xb->scr + SCR_CCR); - tc6393xb->suspend_state.fer = ioread8(tc6393xb->scr + SCR_FER); - - for (i = 0; i < 3; i++) { - tc6393xb->suspend_state.gpo_dsr[i] = - ioread8(tc6393xb->scr + SCR_GPO_DSR(i)); - tc6393xb->suspend_state.gpo_doecr[i] = - ioread8(tc6393xb->scr + SCR_GPO_DOECR(i)); - tc6393xb->suspend_state.gpi_bcr[i] = - ioread8(tc6393xb->scr + SCR_GPI_BCR(i)); - } - ret = tcpd->suspend(dev); - clk_disable_unprepare(tc6393xb->clk); - - return ret; -} - -static int tc6393xb_resume(struct platform_device *dev) -{ - struct tc6393xb_platform_data *tcpd = dev_get_platdata(&dev->dev); - struct tc6393xb *tc6393xb = platform_get_drvdata(dev); - int ret; - int i; - - ret = clk_prepare_enable(tc6393xb->clk); - if (ret) - return ret; - - ret = tcpd->resume(dev); - if (ret) - return ret; - - if (!tcpd->resume_restore) - return 0; - - iowrite8(tc6393xb->suspend_state.fer, tc6393xb->scr + SCR_FER); - iowrite16(tcpd->scr_pll2cr, tc6393xb->scr + SCR_PLL2CR); - iowrite16(tc6393xb->suspend_state.ccr, tc6393xb->scr + SCR_CCR); - iowrite16(SCR_MCR_RDY_OPENDRAIN | SCR_MCR_RDY_UNK | SCR_MCR_RDY_EN | - SCR_MCR_INT_OPENDRAIN | SCR_MCR_INT_UNK | SCR_MCR_INT_EN | - BIT(15), tc6393xb->scr + SCR_MCR); - iowrite16(tcpd->scr_gper, tc6393xb->scr + SCR_GPER); - iowrite8(0, tc6393xb->scr + SCR_IRR); - iowrite8(0xbf, tc6393xb->scr + SCR_IMR); - - for (i = 0; i < 3; i++) { - iowrite8(tc6393xb->suspend_state.gpo_dsr[i], - tc6393xb->scr + SCR_GPO_DSR(i)); - iowrite8(tc6393xb->suspend_state.gpo_doecr[i], - tc6393xb->scr + SCR_GPO_DOECR(i)); - iowrite8(tc6393xb->suspend_state.gpi_bcr[i], - tc6393xb->scr + SCR_GPI_BCR(i)); - } - - return 0; -} -#else -#define tc6393xb_suspend NULL -#define tc6393xb_resume NULL -#endif - -static struct platform_driver tc6393xb_driver = { - .probe = tc6393xb_probe, - .remove = tc6393xb_remove, - .suspend = tc6393xb_suspend, - .resume = tc6393xb_resume, - - .driver = { - .name = "tc6393xb", - }, -}; - -static int __init tc6393xb_init(void) -{ - return platform_driver_register(&tc6393xb_driver); -} - -static void __exit tc6393xb_exit(void) -{ - platform_driver_unregister(&tc6393xb_driver); -} - -subsys_initcall(tc6393xb_init); -module_exit(tc6393xb_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov and Dirk Opfer"); -MODULE_DESCRIPTION("tc6393xb Toshiba Mobile IO Controller"); -MODULE_ALIAS("platform:tc6393xb"); - diff --git a/drivers/mfd/ti-lmu.c b/drivers/mfd/ti-lmu.c index fd6e8c417baa..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 { @@ -133,8 +132,9 @@ TI_LMU_DATA(lm3633, LM3633_MAX_REG); TI_LMU_DATA(lm3695, LM3695_MAX_REG); TI_LMU_DATA(lm36274, LM36274_MAX_REG); -static int ti_lmu_probe(struct i2c_client *cl, const struct i2c_device_id *id) +static int ti_lmu_probe(struct i2c_client *cl) { + const struct i2c_device_id *id = i2c_client_get_device_id(cl); struct device *dev = &cl->dev; const struct ti_lmu_data *data; struct regmap_config regmap_cfg; diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 0e6e25308190..068c25401c6c 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI Touch Screen / ADC MFD driver * * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> @@ -22,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> @@ -113,86 +105,111 @@ static void tscadc_idle_config(struct ti_tscadc_dev *tscadc) { unsigned int idleconfig; - idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM | - STEPCONFIG_INP_ADCREFM | STEPCONFIG_YPN; + idleconfig = STEPCONFIG_INM_ADCREFM | STEPCONFIG_INP_ADCREFM; + if (ti_adc_with_touchscreen(tscadc)) + idleconfig |= STEPCONFIG_YNN | STEPCONFIG_YPN; regmap_write(tscadc->regmap, REG_IDLECONFIG, idleconfig); } static int ti_tscadc_probe(struct platform_device *pdev) { - struct ti_tscadc_dev *tscadc; - struct resource *res; - struct clk *clk; - struct device_node *node; - struct mfd_cell *cell; - struct property *prop; - const __be32 *cur; - u32 val; - int err, ctrl; - int clock_rate; - int tsc_wires = 0, adc_channels = 0, total_channels; - int readouts = 0; + struct ti_tscadc_dev *tscadc; + struct resource *res; + struct clk *clk; + struct device_node *node; + struct mfd_cell *cell; + bool use_tsc = false, use_mag = false; + u32 val; + int err; + int tscmag_wires = 0, adc_channels = 0, cell_idx = 0, total_channels; + int readouts = 0, mag_tracks = 0; + + /* Allocate memory for device */ + tscadc = devm_kzalloc(&pdev->dev, sizeof(*tscadc), GFP_KERNEL); + if (!tscadc) + return -ENOMEM; + + tscadc->dev = &pdev->dev; if (!pdev->dev.of_node) { dev_err(&pdev->dev, "Could not find valid DT data.\n"); return -EINVAL; } - node = of_get_child_by_name(pdev->dev.of_node, "tsc"); - of_property_read_u32(node, "ti,wires", &tsc_wires); - of_property_read_u32(node, "ti,coordiante-readouts", &readouts); + tscadc->data = of_device_get_match_data(&pdev->dev); + + if (ti_adc_with_touchscreen(tscadc)) { + node = of_get_child_by_name(pdev->dev.of_node, "tsc"); + of_property_read_u32(node, "ti,wires", &tscmag_wires); + err = of_property_read_u32(node, "ti,coordinate-readouts", + &readouts); + if (err < 0) + of_property_read_u32(node, "ti,coordiante-readouts", + &readouts); + + of_node_put(node); + + if (tscmag_wires) + use_tsc = true; + } else { + /* + * When adding support for the magnetic stripe reader, here is + * the place to look for the number of tracks used from device + * tree. Let's default to 0 for now. + */ + mag_tracks = 0; + tscmag_wires = mag_tracks * 2; + if (tscmag_wires) + use_mag = true; + } 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", - val); + val); + of_node_put(node); return -EINVAL; } } - total_channels = tsc_wires + adc_channels; + + of_node_put(node); + + total_channels = tscmag_wires + adc_channels; if (total_channels > 8) { dev_err(&pdev->dev, "Number of i/p channels more than 8\n"); return -EINVAL; } + if (total_channels == 0) { - dev_err(&pdev->dev, "Need atleast one channel.\n"); + dev_err(&pdev->dev, "Need at least one channel.\n"); return -EINVAL; } - if (readouts * 2 + 2 + adc_channels > 16) { + if (use_tsc && (readouts * 2 + 2 + adc_channels > 16)) { dev_err(&pdev->dev, "Too many step configurations requested\n"); return -EINVAL; } - /* Allocate memory for device */ - tscadc = devm_kzalloc(&pdev->dev, sizeof(*tscadc), GFP_KERNEL); - if (!tscadc) - return -ENOMEM; - - tscadc->dev = &pdev->dev; - err = platform_get_irq(pdev, 0); - if (err < 0) { - dev_err(&pdev->dev, "no irq ID is specified.\n"); - goto ret; - } else + if (err < 0) + return err; + 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); tscadc->tscadc_phys_base = res->start; tscadc->regmap = devm_regmap_init_mmio(&pdev->dev, - tscadc->tscadc_base, &tscadc_regmap_config); + tscadc->tscadc_base, + &tscadc_regmap_config); if (IS_ERR(tscadc->regmap)) { dev_err(&pdev->dev, "regmap init failed\n"); - err = PTR_ERR(tscadc->regmap); - goto ret; + return PTR_ERR(tscadc->regmap); } spin_lock_init(&tscadc->reg_lock); @@ -202,71 +219,70 @@ static int ti_tscadc_probe(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); /* - * The TSC_ADC_Subsystem has 2 clock domains - * OCP_CLK and ADC_CLK. - * The ADC clock is expected to run at target of 3MHz, - * and expected to capture 12-bit data at a rate of 200 KSPS. - * The TSC_ADC_SS controller design assumes the OCP clock is - * at least 6x faster than the ADC clock. + * The TSC_ADC_Subsystem has 2 clock domains: OCP_CLK and ADC_CLK. + * ADCs produce a 12-bit sample every 15 ADC_CLK cycles. + * am33xx ADCs expect to capture 200ksps. + * am47xx ADCs expect to capture 867ksps. + * We need ADC clocks respectively running at 3MHz and 13MHz. + * These frequencies are valid since TSC_ADC_SS controller design + * assumes the OCP clock is at least 6x faster than the ADC clock. */ - clk = devm_clk_get(&pdev->dev, "adc_tsc_fck"); + clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { - dev_err(&pdev->dev, "failed to get TSC fck\n"); + dev_err(&pdev->dev, "failed to get fck\n"); err = PTR_ERR(clk); goto err_disable_clk; } - clock_rate = clk_get_rate(clk); - tscadc->clk_div = clock_rate / ADC_CLK; - /* TSCADC_CLKDIV needs to be configured to the value minus 1 */ - tscadc->clk_div--; + tscadc->clk_div = (clk_get_rate(clk) / tscadc->data->target_clk_rate) - 1; regmap_write(tscadc->regmap, REG_CLKDIV, tscadc->clk_div); - /* Set the control register bits */ - ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID; - regmap_write(tscadc->regmap, REG_CTRL, ctrl); - - /* Set register bits for Idle Config Mode */ - if (tsc_wires > 0) { - tscadc->tsc_wires = tsc_wires; - if (tsc_wires == 5) - ctrl |= CNTRLREG_5WIRE | CNTRLREG_TSCENB; - else - ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB; - tscadc_idle_config(tscadc); + /* + * Set the control register bits. tscadc->ctrl stores the configuration + * of the CTRL register but not the subsystem enable bit which must be + * added manually when timely. + */ + tscadc->ctrl = CNTRLREG_STEPID; + if (ti_adc_with_touchscreen(tscadc)) { + tscadc->ctrl |= CNTRLREG_TSC_STEPCONFIGWRT; + if (use_tsc) { + tscadc->ctrl |= CNTRLREG_TSC_ENB; + if (tscmag_wires == 5) + tscadc->ctrl |= CNTRLREG_TSC_5WIRE; + else + tscadc->ctrl |= CNTRLREG_TSC_4WIRE; + } + } else { + tscadc->ctrl |= CNTRLREG_MAG_PREAMP_PWRDOWN | + CNTRLREG_MAG_PREAMP_BYPASS; } + regmap_write(tscadc->regmap, REG_CTRL, tscadc->ctrl); + + tscadc_idle_config(tscadc); /* Enable the TSC module enable bit */ - ctrl |= CNTRLREG_TSCSSENB; - regmap_write(tscadc->regmap, REG_CTRL, ctrl); - - tscadc->used_cells = 0; - tscadc->tsc_cell = -1; - tscadc->adc_cell = -1; - - /* TSC Cell */ - if (tsc_wires > 0) { - tscadc->tsc_cell = tscadc->used_cells; - cell = &tscadc->cells[tscadc->used_cells++]; - cell->name = "TI-am335x-tsc"; - cell->of_compatible = "ti,am3359-tsc"; + regmap_write(tscadc->regmap, REG_CTRL, tscadc->ctrl | CNTRLREG_SSENB); + + /* TSC or MAG Cell */ + if (use_tsc || use_mag) { + cell = &tscadc->cells[cell_idx++]; + cell->name = tscadc->data->secondary_feature_name; + cell->of_compatible = tscadc->data->secondary_feature_compatible; cell->platform_data = &tscadc; cell->pdata_size = sizeof(tscadc); } /* ADC Cell */ if (adc_channels > 0) { - tscadc->adc_cell = tscadc->used_cells; - cell = &tscadc->cells[tscadc->used_cells++]; - cell->name = "TI-am335x-adc"; - cell->of_compatible = "ti,am3359-adc"; + cell = &tscadc->cells[cell_idx++]; + cell->name = tscadc->data->adc_feature_name; + cell->of_compatible = tscadc->data->adc_feature_compatible; cell->platform_data = &tscadc; cell->pdata_size = sizeof(tscadc); } err = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, - tscadc->cells, tscadc->used_cells, NULL, - 0, NULL); + tscadc->cells, cell_idx, NULL, 0, NULL); if (err < 0) goto err_disable_clk; @@ -276,13 +292,13 @@ static int ti_tscadc_probe(struct platform_device *pdev) err_disable_clk: pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -ret: + 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); + struct ti_tscadc_dev *tscadc = platform_get_drvdata(pdev); regmap_write(tscadc->regmap, REG_SE, 0x00); @@ -290,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) @@ -301,7 +315,7 @@ static int __maybe_unused ti_tscadc_can_wakeup(struct device *dev, void *data) static int __maybe_unused tscadc_suspend(struct device *dev) { - struct ti_tscadc_dev *tscadc = dev_get_drvdata(dev); + struct ti_tscadc_dev *tscadc = dev_get_drvdata(dev); regmap_write(tscadc->regmap, REG_SE, 0x00); if (device_for_each_child(dev, NULL, ti_tscadc_can_wakeup)) { @@ -309,7 +323,7 @@ static int __maybe_unused tscadc_suspend(struct device *dev) regmap_read(tscadc->regmap, REG_CTRL, &ctrl); ctrl &= ~(CNTRLREG_POWERDOWN); - ctrl |= CNTRLREG_TSCSSENB; + ctrl |= CNTRLREG_SSENB; regmap_write(tscadc->regmap, REG_CTRL, ctrl); } pm_runtime_put_sync(dev); @@ -319,34 +333,39 @@ static int __maybe_unused tscadc_suspend(struct device *dev) static int __maybe_unused tscadc_resume(struct device *dev) { - struct ti_tscadc_dev *tscadc = dev_get_drvdata(dev); - u32 ctrl; + struct ti_tscadc_dev *tscadc = dev_get_drvdata(dev); pm_runtime_get_sync(dev); - /* context restore */ - ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_STEPID; - regmap_write(tscadc->regmap, REG_CTRL, ctrl); - - if (tscadc->tsc_cell != -1) { - if (tscadc->tsc_wires == 5) - ctrl |= CNTRLREG_5WIRE | CNTRLREG_TSCENB; - else - ctrl |= CNTRLREG_4WIRE | CNTRLREG_TSCENB; - tscadc_idle_config(tscadc); - } - ctrl |= CNTRLREG_TSCSSENB; - regmap_write(tscadc->regmap, REG_CTRL, ctrl); - regmap_write(tscadc->regmap, REG_CLKDIV, tscadc->clk_div); + regmap_write(tscadc->regmap, REG_CTRL, tscadc->ctrl); + tscadc_idle_config(tscadc); + regmap_write(tscadc->regmap, REG_CTRL, tscadc->ctrl | CNTRLREG_SSENB); return 0; } static SIMPLE_DEV_PM_OPS(tscadc_pm_ops, tscadc_suspend, tscadc_resume); +static const struct ti_tscadc_data tscdata = { + .adc_feature_name = "TI-am335x-adc", + .adc_feature_compatible = "ti,am3359-adc", + .secondary_feature_name = "TI-am335x-tsc", + .secondary_feature_compatible = "ti,am3359-tsc", + .target_clk_rate = TSC_ADC_CLK, +}; + +static const struct ti_tscadc_data magdata = { + .adc_feature_name = "TI-am43xx-adc", + .adc_feature_compatible = "ti,am4372-adc", + .secondary_feature_name = "TI-am43xx-mag", + .secondary_feature_compatible = "ti,am4372-mag", + .target_clk_rate = MAG_ADC_CLK, +}; + static const struct of_device_id ti_tscadc_dt_ids[] = { - { .compatible = "ti,am3359-tscadc", }, + { .compatible = "ti,am3359-tscadc", .data = &tscdata }, + { .compatible = "ti,am4372-magadc", .data = &magdata }, { } }; MODULE_DEVICE_TABLE(of, ti_tscadc_dt_ids); @@ -358,12 +377,12 @@ 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, }; module_platform_driver(ti_tscadc_driver); -MODULE_DESCRIPTION("TI touchscreen / ADC MFD controller driver"); +MODULE_DESCRIPTION("TI touchscreen/magnetic stripe reader/ADC MFD controller driver"); MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 9393ee60a656..b059713db875 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -11,8 +11,8 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/msi.h> #include <linux/mfd/core.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/timb_gpio.h> @@ -26,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> @@ -50,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, }, }; @@ -766,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; } @@ -855,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/tmio_core.c b/drivers/mfd/tmio_core.c deleted file mode 100644 index 7ee873551482..000000000000 --- a/drivers/mfd/tmio_core.c +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright(c) 2009 Ian Molton <spyro@f2s.com> - */ - -#include <linux/export.h> -#include <linux/mfd/tmio.h> - -#define CNF_CMD 0x04 -#define CNF_CTL_BASE 0x10 -#define CNF_INT_PIN 0x3d -#define CNF_STOP_CLK_CTL 0x40 -#define CNF_GCLK_CTL 0x41 -#define CNF_SD_CLK_MODE 0x42 -#define CNF_PIN_STATUS 0x44 -#define CNF_PWR_CTL_1 0x48 -#define CNF_PWR_CTL_2 0x49 -#define CNF_PWR_CTL_3 0x4a -#define CNF_CARD_DETECT_MODE 0x4c -#define CNF_SD_SLOT 0x50 -#define CNF_EXT_GCLK_CTL_1 0xf0 -#define CNF_EXT_GCLK_CTL_2 0xf1 -#define CNF_EXT_GCLK_CTL_3 0xf9 -#define CNF_SD_LED_EN_1 0xfa -#define CNF_SD_LED_EN_2 0xfe - -#define SDCREN 0x2 /* Enable access to MMC CTL regs. (flag in COMMAND_REG)*/ - -int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base) -{ - /* Enable the MMC/SD Control registers */ - sd_config_write16(cnf, shift, CNF_CMD, SDCREN); - sd_config_write32(cnf, shift, CNF_CTL_BASE, base & 0xfffe); - - /* Disable SD power during suspend */ - sd_config_write8(cnf, shift, CNF_PWR_CTL_3, 0x01); - - /* The below is required but why? FIXME */ - sd_config_write8(cnf, shift, CNF_STOP_CLK_CTL, 0x1f); - - /* Power down SD bus */ - sd_config_write8(cnf, shift, CNF_PWR_CTL_2, 0x00); - - return 0; -} -EXPORT_SYMBOL(tmio_core_mmc_enable); - -int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base) -{ - - /* Enable the MMC/SD Control registers */ - sd_config_write16(cnf, shift, CNF_CMD, SDCREN); - sd_config_write32(cnf, shift, CNF_CTL_BASE, base & 0xfffe); - - return 0; -} -EXPORT_SYMBOL(tmio_core_mmc_resume); - -void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state) -{ - sd_config_write8(cnf, shift, CNF_PWR_CTL_2, state ? 0x02 : 0x00); -} -EXPORT_SYMBOL(tmio_core_mmc_pwr); - -void tmio_core_mmc_clk_div(void __iomem *cnf, int shift, int state) -{ - sd_config_write8(cnf, shift, CNF_SD_CLK_MODE, state ? 1 : 0); -} -EXPORT_SYMBOL(tmio_core_mmc_clk_div); - diff --git a/drivers/mfd/tps6105x.c b/drivers/mfd/tps6105x.c index c906324d293e..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, @@ -117,8 +117,7 @@ static struct tps6105x_platform_data *tps6105x_parse_dt(struct device *dev) return pdata; } -static int tps6105x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tps6105x_probe(struct i2c_client *client) { struct tps6105x *tps6105x; struct tps6105x_platform_data *pdata; @@ -179,7 +178,7 @@ static int tps6105x_probe(struct i2c_client *client, return ret; } -static int tps6105x_remove(struct i2c_client *client) +static void tps6105x_remove(struct i2c_client *client) { struct tps6105x *tps6105x = i2c_get_clientdata(client); @@ -189,13 +188,11 @@ static int tps6105x_remove(struct i2c_client *client) regmap_update_bits(tps6105x->regmap, TPS6105X_REG_0, TPS6105X_REG0_MODE_MASK, TPS6105X_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT); - - return 0; } 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 7e7dbee58ca9..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 @@ -501,28 +504,22 @@ static int tps65010_gpio_get(struct gpio_chip *chip, unsigned offset) static struct tps65010 *the_tps; -static int tps65010_remove(struct i2c_client *client) +static void tps65010_remove(struct i2c_client *client) { struct tps65010 *tps = i2c_get_clientdata(client); struct tps65010_board *board = dev_get_platdata(&client->dev); - if (board && board->teardown) { - int status = board->teardown(client, board->context); - if (status < 0) - dev_dbg(&client->dev, "board %s %s err %d\n", - "teardown", client->name, status); - } + if (board && board->teardown) + board->teardown(client, &tps->chip); if (client->irq > 0) free_irq(client->irq, tps); cancel_delayed_work_sync(&tps->work); - debugfs_remove(tps->file); the_tps = NULL; - return 0; } -static int tps65010_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tps65010_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); struct tps65010 *tps; int status; struct tps65010_board *board = dev_get_platdata(&client->dev); @@ -549,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"); @@ -616,11 +609,11 @@ 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 */ - if (board && board->base != 0) { + if (board) { tps->outmask = board->outmask; tps->chip.label = client->name; @@ -633,7 +626,7 @@ static int tps65010_probe(struct i2c_client *client, /* NOTE: only partial support for inputs; nyet IRQs */ tps->chip.get = tps65010_gpio_get; - tps->chip.base = board->base; + tps->chip.base = -1; tps->chip.ngpio = 7; tps->chip.can_sleep = 1; @@ -642,7 +635,7 @@ static int tps65010_probe(struct i2c_client *client, dev_err(&client->dev, "can't add gpiochip, err %d\n", status); else if (board->setup) { - status = board->setup(client, board->context); + status = board->setup(client, &tps->chip); if (status < 0) { dev_dbg(&client->dev, "board %s %s err %d\n", @@ -669,7 +662,7 @@ static struct i2c_driver tps65010_driver = { .driver = { .name = "tps65010", }, - .probe = tps65010_probe, + .probe = tps65010_probe, .remove = tps65010_remove, .id_table = tps65010_id, }; @@ -747,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); @@ -859,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; @@ -881,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); @@ -993,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 1f308c4e3694..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> @@ -84,8 +83,7 @@ static int tps6507x_i2c_write_device(struct tps6507x_dev *tps6507x, char reg, return 0; } -static int tps6507x_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int tps6507x_i2c_probe(struct i2c_client *i2c) { struct tps6507x_dev *tps6507x; @@ -105,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 341466ef20cc..54832e9321b9 100644 --- a/drivers/mfd/tps65086.c +++ b/drivers/mfd/tps65086.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis <afd@ti.com> * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65912 driver */ @@ -24,6 +16,7 @@ static const struct mfd_cell tps65086_cells[] = { { .name = "tps65086-regulator", }, { .name = "tps65086-gpio", }, + { .name = "tps65086-reset", }, }; static const struct regmap_range tps65086_yes_ranges[] = { @@ -41,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[] = { @@ -51,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, @@ -68,8 +62,7 @@ static const struct of_device_id tps65086_of_match_table[] = { }; MODULE_DEVICE_TABLE(of, tps65086_of_match_table); -static int tps65086_probe(struct i2c_client *client, - const struct i2c_device_id *ids) +static int tps65086_probe(struct i2c_client *client) { struct tps65086 *tps; unsigned int version; @@ -89,46 +82,52 @@ 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; } - 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); - - ret = regmap_add_irq_chip(tps->regmap, tps->irq, IRQF_ONESHOT, 0, - &tps65086_irq_chip, &tps->irq_data); + ret = regmap_read(tps->regmap, TPS65086_DEVICEID2, &version); if (ret) { - dev_err(tps->dev, "Failed to register IRQ chip\n"); + 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_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, + &tps65086_irq_chip, &tps->irq_data); + if (ret) { + dev_err(tps->dev, "Failed to register IRQ chip\n"); + return ret; + } + } + ret = mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO, tps65086_cells, ARRAY_SIZE(tps65086_cells), NULL, 0, regmap_irq_get_domain(tps->irq_data)); - if (ret) { + if (ret && tps->irq > 0) regmap_del_irq_chip(tps->irq, tps->irq_data); - return ret; - } - return 0; + return ret; } -static int tps65086_remove(struct i2c_client *client) +static void tps65086_remove(struct i2c_client *client) { struct tps65086 *tps = i2c_get_clientdata(client); - regmap_del_irq_chip(tps->irq, tps->irq_data); - - return 0; + if (tps->irq > 0) + regmap_del_irq_chip(tps->irq, tps->irq_data); } 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 bd6235308c6b..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,14 +120,13 @@ 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), .num_regs = NUM_INT_REG, .status_base = TPS65090_REG_INTR_STS, - .mask_base = TPS65090_REG_INTR_MASK, - .mask_invert = true, + .unmask_base = TPS65090_REG_INTR_MASK, }; static bool is_volatile_reg(struct device *dev, unsigned int reg) @@ -153,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, }; @@ -164,8 +162,7 @@ static const struct of_device_id tps65090_of_match[] = { }; #endif -static int tps65090_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tps65090_i2c_probe(struct i2c_client *client) { struct tps65090_platform_data *pdata = dev_get_platdata(&client->dev); int irq_base = 0; @@ -228,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 8027b0a9e14f..c240fac0ede7 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * tps65217.c * * TPS65217 chip family multi-function driver * * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/device.h> @@ -25,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> @@ -167,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; @@ -382,7 +373,7 @@ static int tps65217_probe(struct i2c_client *client) return 0; } -static int tps65217_remove(struct i2c_client *client) +static void tps65217_remove(struct i2c_client *client) { struct tps65217 *tps = i2c_get_clientdata(client); unsigned int virq; @@ -396,8 +387,6 @@ static int tps65217_remove(struct i2c_client *client) irq_domain_remove(tps->irq_domain); tps->irq_domain = NULL; - - return 0; } static const struct i2c_device_id tps65217_id_table[] = { @@ -412,7 +401,7 @@ static struct i2c_driver tps65217_driver = { .of_match_table = tps65217_of_match, }, .id_table = tps65217_id_table, - .probe_new = tps65217_probe, + .probe = tps65217_probe, .remove = tps65217_remove, }; diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c index 167e9fc308ef..4f3e632f726f 100644 --- a/drivers/mfd/tps65218.c +++ b/drivers/mfd/tps65218.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Driver for TPS65218 Integrated power management chipsets * * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. */ #include <linux/kernel.h> @@ -23,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> @@ -136,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, }; @@ -195,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), @@ -288,8 +279,7 @@ static int tps65218_voltage_set_uvlo(struct tps65218 *tps) return 0; } -static int tps65218_probe(struct i2c_client *client, - const struct i2c_device_id *ids) +static int tps65218_probe(struct i2c_client *client) { struct tps65218 *tps; int ret; diff --git a/drivers/mfd/tps65219.c b/drivers/mfd/tps65219.c new file mode 100644 index 000000000000..65a952555218 --- /dev/null +++ b/drivers/mfd/tps65219.c @@ -0,0 +1,564 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// 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> +#include <linux/regmap.h> + +#include <linux/mfd/core.h> +#include <linux/mfd/tps65219.h> + +static int tps65219_warm_reset(struct tps65219 *tps) +{ + return regmap_update_bits(tps->regmap, TPS65219_REG_MFP_CTRL, + TPS65219_MFP_WARM_RESET_I2C_CTRL_MASK, + TPS65219_MFP_WARM_RESET_I2C_CTRL_MASK); +} + +static int tps65219_cold_reset(struct tps65219 *tps) +{ + return regmap_update_bits(tps->regmap, TPS65219_REG_MFP_CTRL, + TPS65219_MFP_COLD_RESET_I2C_CTRL_MASK, + TPS65219_MFP_COLD_RESET_I2C_CTRL_MASK); +} + +static int tps65219_soft_shutdown(struct tps65219 *tps) +{ + return regmap_update_bits(tps->regmap, TPS65219_REG_MFP_CTRL, + TPS65219_MFP_I2C_OFF_REQ_MASK, + TPS65219_MFP_I2C_OFF_REQ_MASK); +} + +static int tps65219_power_off_handler(struct sys_off_data *data) +{ + tps65219_soft_shutdown(data->cb_data); + return NOTIFY_DONE; +} + +static int tps65219_restart(struct tps65219 *tps, unsigned long reboot_mode) +{ + if (reboot_mode == REBOOT_WARM) + tps65219_warm_reset(tps); + else + tps65219_cold_reset(tps); + + return NOTIFY_DONE; +} + +static int tps65219_restart_handler(struct sys_off_data *data) +{ + tps65219_restart(data->cb_data, data->mode); + return NOTIFY_DONE; +} + +static const struct resource tps65219_pwrbutton_resources[] = { + DEFINE_RES_IRQ_NAMED(TPS65219_INT_PB_FALLING_EDGE_DETECT, "falling"), + 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"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO3_UV, "LDO3_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO4_SCG, "LDO4_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO4_OC, "LDO4_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO4_UV, "LDO4_UV"), + 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(TPS65219_INT_LDO2_RV, "LDO2_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO3_RV, "LDO3_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO4_RV, "LDO4_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(TPS65219_INT_LDO2_RV_SD, "LDO2_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO3_RV_SD, "LDO3_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO4_RV_SD, "LDO4_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 mfd_cell tps65214_cells[] = { + MFD_CELL_RES("tps65214-regulator", tps65214_regulator_resources), + MFD_CELL_NAME("tps65214-gpio"), +}; + +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, + .max_register = TPS65219_REG_FACTORY_CONFIG_2, +}; + +/* + * Mapping of main IRQ register bits to sub-IRQ register offsets so that we can + * access corect sub-IRQ registers based on bits that are set in main IRQ + * register. + */ +/* Timeout Residual Voltage Shutdown */ +static unsigned int bit0_offsets[] = { TPS65219_REG_INT_TO_RV_POS }; +static unsigned int bit1_offsets[] = { TPS65219_REG_INT_RV_POS }; /* Residual Voltage */ +static unsigned int bit2_offsets[] = { TPS65219_REG_INT_SYS_POS }; /* System */ +static unsigned int bit3_offsets[] = { TPS65219_REG_INT_BUCK_1_2_POS }; /* Buck 1-2 */ +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 */ + +/* 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), + 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 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 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), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO4_SCG, TPS65219_REG_INT_LDO_3_4_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO4_OC, TPS65219_REG_INT_LDO_3_4_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO4_UV, TPS65219_REG_INT_LDO_3_4_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_SCG, TPS65219_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_OC, TPS65219_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_UV, TPS65219_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_SCG, TPS65219_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_OC, TPS65219_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_UV, TPS65219_REG_INT_LDO_1_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(TPS65219_INT_LDO2_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO3_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO4_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(TPS65219_INT_LDO2_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO3_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO4_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_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, + .num_main_status_bits = 8, + .irqs = tps65219_irqs, + .num_irqs = ARRAY_SIZE(tps65219_irqs), + .status_base = TPS65219_REG_INT_LDO_3_4, + .ack_base = TPS65219_REG_INT_LDO_3_4, + .clear_ack = 1, + .num_regs = 8, + .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; + const struct tps65219_chip_data *pmic; + unsigned int chip_id; + bool pwr_button; + int ret; + + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + 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)) { + ret = PTR_ERR(tps->regmap); + dev_err(tps->dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + 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 = devm_mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO, + 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); + return ret; + } + + pwr_button = of_property_read_bool(tps->dev->of_node, "ti,power-button"); + if (pwr_button) { + ret = devm_mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO, + &tps65219_pwrbutton_cell, 1, NULL, 0, + regmap_irq_get_domain(tps->irq_data)); + if (ret) { + dev_err(tps->dev, "Failed to add power-button: %d\n", ret); + return ret; + } + } + + ret = devm_register_restart_handler(tps->dev, + tps65219_restart_handler, + tps); + + if (ret) { + dev_err(tps->dev, "cannot register restart handler, %d\n", ret); + return ret; + } + + ret = devm_register_power_off_handler(tps->dev, + tps65219_power_off_handler, + tps); + if (ret) { + dev_err(tps->dev, "failed to register power-off handler: %d\n", ret); + return ret; + } + return 0; +} + +static const struct of_device_id of_tps65219_match_table[] = { + { .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); + +static struct i2c_driver tps65219_driver = { + .driver = { + .name = "tps65219", + .of_match_table = of_tps65219_match_table, + }, + .probe = tps65219_probe, +}; +module_i2c_driver(tps65219_driver); + +MODULE_AUTHOR("Jerome Neanne <jneanne@baylibre.com>"); +MODULE_DESCRIPTION("TPS65214/TPS65215/TPS65219 PMIC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index c9303d3d6602..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) @@ -269,15 +271,11 @@ static void tps6586x_irq_sync_unlock(struct irq_data *data) mutex_unlock(&tps6586x->irq_lock); } -#ifdef CONFIG_PM_SLEEP static int tps6586x_irq_set_wake(struct irq_data *irq_data, unsigned int on) { struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data); return irq_set_irq_wake(tps6586x->irq, on); } -#else -#define tps6586x_irq_set_wake NULL -#endif static struct irq_chip tps6586x_irq_chip = { .name = "tps6586x", @@ -285,7 +283,7 @@ static struct irq_chip tps6586x_irq_chip = { .irq_bus_sync_unlock = tps6586x_irq_sync_unlock, .irq_disable = tps6586x_irq_disable, .irq_enable = tps6586x_irq_enable, - .irq_set_wake = tps6586x_irq_set_wake, + .irq_set_wake = pm_sleep_ptr(tps6586x_irq_set_wake), }; static int tps6586x_irq_map(struct irq_domain *h, unsigned int virq, @@ -365,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; @@ -458,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) +{ + 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); + + 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) { - if (tps6586x_clr_bits(tps6586x_dev, TPS6586X_SUPPLYENE, EXITSLREQ_BIT)) - return; + 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); - tps6586x_set_bits(tps6586x_dev, TPS6586X_SUPPLYENE, SLEEP_MODE_BIT); + mdelay(50); + return notifier_from_errno(-ETIME); } static void tps6586x_print_version(struct i2c_client *client, int version) @@ -499,8 +518,7 @@ static void tps6586x_print_version(struct i2c_client *client, int version) dev_info(&client->dev, "Found %s, VERSIONCRC is %02x\n", name, version); } -static int tps6586x_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tps6586x_i2c_probe(struct i2c_client *client) { struct tps6586x_platform_data *pdata = dev_get_platdata(&client->dev); struct tps6586x *tps6586x; @@ -564,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; @@ -579,7 +608,7 @@ err_mfd_add: return ret; } -static int tps6586x_i2c_remove(struct i2c_client *client) +static void tps6586x_i2c_remove(struct i2c_client *client) { struct tps6586x *tps6586x = i2c_get_clientdata(client); @@ -587,7 +616,6 @@ static int tps6586x_i2c_remove(struct i2c_client *client) mfd_remove_devices(tps6586x->dev); if (client->irq) free_irq(client->irq, tps6586x); - return 0; } static int __maybe_unused tps6586x_i2c_suspend(struct device *dev) @@ -614,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); @@ -644,4 +672,3 @@ module_exit(tps6586x_exit); MODULE_DESCRIPTION("TPS6586X core driver"); MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index 6e105cca27d4..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); @@ -436,23 +429,14 @@ static void tps65910_power_off(void) tps65910 = dev_get_drvdata(&tps65910_i2c_client->dev); - /* - * The PWR_OFF bit needs to be set separately, before transitioning - * to the OFF state. It enables the "sequential" power-off mode on - * TPS65911, it's a NO-OP on TPS65910. - */ - if (regmap_set_bits(tps65910->regmap, TPS65910_DEVCTRL, - DEVCTRL_PWR_OFF_MASK) < 0) - return; - regmap_update_bits(tps65910->regmap, TPS65910_DEVCTRL, DEVCTRL_DEV_OFF_MASK | DEVCTRL_DEV_ON_MASK, DEVCTRL_DEV_OFF_MASK); } -static int tps65910_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int tps65910_i2c_probe(struct i2c_client *i2c) { + const struct i2c_device_id *id = i2c_client_get_device_id(i2c); struct tps65910 *tps65910; struct tps65910_board *pmic_plat_data; struct tps65910_board *of_pmic_plat_data = NULL; @@ -504,6 +488,19 @@ static int tps65910_i2c_probe(struct i2c_client *i2c, tps65910_sleepinit(tps65910, pmic_plat_data); if (pmic_plat_data->pm_off && !pm_power_off) { + /* + * The PWR_OFF bit needs to be set separately, before + * transitioning to the OFF state. It enables the "sequential" + * power-off mode on TPS65911, it's a NO-OP on TPS65910. + */ + ret = regmap_set_bits(tps65910->regmap, TPS65910_DEVCTRL, + DEVCTRL_PWR_OFF_MASK); + if (ret) { + dev_err(&i2c->dev, "failed to set power-off mode: %d\n", + ret); + return ret; + } + tps65910_i2c_client = i2c; pm_power_off = tps65910_power_off; } 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 b55b1d5d6955..a9dcd7f0d9e3 100644 --- a/drivers/mfd/tps65912-core.c +++ b/drivers/mfd/tps65912-core.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Core functions for TI TPS65912x PMICs * * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis <afd@ti.com> * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65218 driver and the previous TPS65912 driver by * Margarita Olaya Cabrera <magi@slimlogic.co.uk> */ @@ -65,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), @@ -89,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); @@ -98,31 +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); -int tps65912_device_exit(struct tps65912 *tps) -{ - regmap_del_irq_chip(tps->irq, tps->irq_data); - - return 0; -} -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 f7c22ea7b36c..138e50497b51 100644 --- a/drivers/mfd/tps65912-i2c.c +++ b/drivers/mfd/tps65912-i2c.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * I2C access driver for TI TPS65912x PMICs * * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis <afd@ti.com> * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65218 driver and the previous TPS65912 driver by * Margarita Olaya Cabrera <magi@slimlogic.co.uk> */ @@ -29,8 +21,7 @@ static const struct of_device_id tps65912_i2c_of_match_table[] = { }; MODULE_DEVICE_TABLE(of, tps65912_i2c_of_match_table); -static int tps65912_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *ids) +static int tps65912_i2c_probe(struct i2c_client *client) { struct tps65912 *tps; @@ -51,15 +42,8 @@ static int tps65912_i2c_probe(struct i2c_client *client, return tps65912_device_init(tps); } -static int tps65912_i2c_remove(struct i2c_client *client) -{ - struct tps65912 *tps = i2c_get_clientdata(client); - - return 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); @@ -70,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 21a8d6ac5c4a..2a77dccd6059 100644 --- a/drivers/mfd/tps65912-spi.c +++ b/drivers/mfd/tps65912-spi.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * SPI access driver for TI TPS65912x PMICs * * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis <afd@ti.com> * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65218 driver and the previous TPS65912 driver by * Margarita Olaya Cabrera <magi@slimlogic.co.uk> */ @@ -50,13 +42,6 @@ static int tps65912_spi_probe(struct spi_device *spi) return tps65912_device_init(tps); } -static int tps65912_spi_remove(struct spi_device *spi) -{ - struct tps65912 *tps = spi_get_drvdata(spi); - - return tps65912_device_exit(tps); -} - static const struct spi_device_id tps65912_spi_id_table[] = { { "tps65912", 0 }, { /* sentinel */ } @@ -69,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 new file mode 100644 index 000000000000..8b26c4127472 --- /dev/null +++ b/drivers/mfd/tps6594-core.c @@ -0,0 +1,801 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 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.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); + +static const struct resource tps6594_regulator_resources[] = { + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK1_OV, TPS6594_IRQ_NAME_BUCK1_OV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK1_UV, TPS6594_IRQ_NAME_BUCK1_UV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK1_SC, TPS6594_IRQ_NAME_BUCK1_SC), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK1_ILIM, TPS6594_IRQ_NAME_BUCK1_ILIM), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK2_OV, TPS6594_IRQ_NAME_BUCK2_OV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK2_UV, TPS6594_IRQ_NAME_BUCK2_UV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK2_SC, TPS6594_IRQ_NAME_BUCK2_SC), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK2_ILIM, TPS6594_IRQ_NAME_BUCK2_ILIM), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK3_OV, TPS6594_IRQ_NAME_BUCK3_OV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK3_UV, TPS6594_IRQ_NAME_BUCK3_UV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK3_SC, TPS6594_IRQ_NAME_BUCK3_SC), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK3_ILIM, TPS6594_IRQ_NAME_BUCK3_ILIM), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK4_OV, TPS6594_IRQ_NAME_BUCK4_OV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK4_UV, TPS6594_IRQ_NAME_BUCK4_UV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK4_SC, TPS6594_IRQ_NAME_BUCK4_SC), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK4_ILIM, TPS6594_IRQ_NAME_BUCK4_ILIM), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK5_OV, TPS6594_IRQ_NAME_BUCK5_OV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK5_UV, TPS6594_IRQ_NAME_BUCK5_UV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK5_SC, TPS6594_IRQ_NAME_BUCK5_SC), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BUCK5_ILIM, TPS6594_IRQ_NAME_BUCK5_ILIM), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO1_OV, TPS6594_IRQ_NAME_LDO1_OV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO1_UV, TPS6594_IRQ_NAME_LDO1_UV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO1_SC, TPS6594_IRQ_NAME_LDO1_SC), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO1_ILIM, TPS6594_IRQ_NAME_LDO1_ILIM), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO2_OV, TPS6594_IRQ_NAME_LDO2_OV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO2_UV, TPS6594_IRQ_NAME_LDO2_UV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO2_SC, TPS6594_IRQ_NAME_LDO2_SC), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO2_ILIM, TPS6594_IRQ_NAME_LDO2_ILIM), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO3_OV, TPS6594_IRQ_NAME_LDO3_OV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO3_UV, TPS6594_IRQ_NAME_LDO3_UV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO3_SC, TPS6594_IRQ_NAME_LDO3_SC), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO3_ILIM, TPS6594_IRQ_NAME_LDO3_ILIM), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO4_OV, TPS6594_IRQ_NAME_LDO4_OV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO4_UV, TPS6594_IRQ_NAME_LDO4_UV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO4_SC, TPS6594_IRQ_NAME_LDO4_SC), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_LDO4_ILIM, TPS6594_IRQ_NAME_LDO4_ILIM), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_VCCA_OV, TPS6594_IRQ_NAME_VCCA_OV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_VCCA_UV, TPS6594_IRQ_NAME_VCCA_UV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_VMON1_OV, TPS6594_IRQ_NAME_VMON1_OV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_VMON1_UV, TPS6594_IRQ_NAME_VMON1_UV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_VMON1_RV, TPS6594_IRQ_NAME_VMON1_RV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_VMON2_OV, TPS6594_IRQ_NAME_VMON2_OV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_VMON2_UV, TPS6594_IRQ_NAME_VMON2_UV), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_VMON2_RV, TPS6594_IRQ_NAME_VMON2_RV), +}; + +static const struct resource tps6594_pinctrl_resources[] = { + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_GPIO9, TPS6594_IRQ_NAME_GPIO9), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_GPIO10, TPS6594_IRQ_NAME_GPIO10), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_GPIO11, TPS6594_IRQ_NAME_GPIO11), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_GPIO1, TPS6594_IRQ_NAME_GPIO1), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_GPIO2, TPS6594_IRQ_NAME_GPIO2), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_GPIO3, TPS6594_IRQ_NAME_GPIO3), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_GPIO4, TPS6594_IRQ_NAME_GPIO4), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_GPIO5, TPS6594_IRQ_NAME_GPIO5), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_GPIO6, TPS6594_IRQ_NAME_GPIO6), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_GPIO7, TPS6594_IRQ_NAME_GPIO7), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_GPIO8, TPS6594_IRQ_NAME_GPIO8), +}; + +static const struct resource tps6594_pfsm_resources[] = { + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_NPWRON_START, TPS6594_IRQ_NAME_NPWRON_START), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_ENABLE, TPS6594_IRQ_NAME_ENABLE), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_FSD, TPS6594_IRQ_NAME_FSD), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_SOFT_REBOOT, TPS6594_IRQ_NAME_SOFT_REBOOT), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BIST_PASS, TPS6594_IRQ_NAME_BIST_PASS), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_EXT_CLK, TPS6594_IRQ_NAME_EXT_CLK), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_TWARN, TPS6594_IRQ_NAME_TWARN), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_TSD_ORD, TPS6594_IRQ_NAME_TSD_ORD), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_BIST_FAIL, TPS6594_IRQ_NAME_BIST_FAIL), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_REG_CRC_ERR, TPS6594_IRQ_NAME_REG_CRC_ERR), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_RECOV_CNT, TPS6594_IRQ_NAME_RECOV_CNT), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_SPMI_ERR, TPS6594_IRQ_NAME_SPMI_ERR), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_NPWRON_LONG, TPS6594_IRQ_NAME_NPWRON_LONG), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_NINT_READBACK, TPS6594_IRQ_NAME_NINT_READBACK), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_NRSTOUT_READBACK, TPS6594_IRQ_NAME_NRSTOUT_READBACK), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_TSD_IMM, TPS6594_IRQ_NAME_TSD_IMM), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_VCCA_OVP, TPS6594_IRQ_NAME_VCCA_OVP), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_PFSM_ERR, TPS6594_IRQ_NAME_PFSM_ERR), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_IMM_SHUTDOWN, TPS6594_IRQ_NAME_IMM_SHUTDOWN), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_ORD_SHUTDOWN, TPS6594_IRQ_NAME_ORD_SHUTDOWN), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_MCU_PWR_ERR, TPS6594_IRQ_NAME_MCU_PWR_ERR), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_SOC_PWR_ERR, TPS6594_IRQ_NAME_SOC_PWR_ERR), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_COMM_FRM_ERR, TPS6594_IRQ_NAME_COMM_FRM_ERR), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_COMM_CRC_ERR, TPS6594_IRQ_NAME_COMM_CRC_ERR), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_COMM_ADR_ERR, TPS6594_IRQ_NAME_COMM_ADR_ERR), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_EN_DRV_READBACK, TPS6594_IRQ_NAME_EN_DRV_READBACK), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_NRSTOUT_SOC_READBACK, + TPS6594_IRQ_NAME_NRSTOUT_SOC_READBACK), +}; + +static const struct resource tps6594_esm_resources[] = { + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_ESM_SOC_PIN, TPS6594_IRQ_NAME_ESM_SOC_PIN), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_ESM_SOC_FAIL, TPS6594_IRQ_NAME_ESM_SOC_FAIL), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_ESM_SOC_RST, TPS6594_IRQ_NAME_ESM_SOC_RST), +}; + +static const struct resource tps6594_rtc_resources[] = { + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_TIMER, TPS6594_IRQ_NAME_TIMER), + DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_ALARM, TPS6594_IRQ_NAME_ALARM), + 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), + MFD_CELL_RES("tps6594-pfsm", tps6594_pfsm_resources), + MFD_CELL_RES("tps6594-esm", tps6594_esm_resources), +}; + +static const struct mfd_cell tps6594_rtc_cells[] = { + MFD_CELL_RES("tps6594-rtc", tps6594_rtc_resources), +}; + +static const struct regmap_irq tps6594_irqs[] = { + /* INT_BUCK1_2 register */ + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK1_OV, 0, TPS6594_BIT_BUCKX_OV_INT(0)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK1_UV, 0, TPS6594_BIT_BUCKX_UV_INT(0)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK1_SC, 0, TPS6594_BIT_BUCKX_SC_INT(0)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK1_ILIM, 0, TPS6594_BIT_BUCKX_ILIM_INT(0)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK2_OV, 0, TPS6594_BIT_BUCKX_OV_INT(1)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK2_UV, 0, TPS6594_BIT_BUCKX_UV_INT(1)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK2_SC, 0, TPS6594_BIT_BUCKX_SC_INT(1)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK2_ILIM, 0, TPS6594_BIT_BUCKX_ILIM_INT(1)), + + /* INT_BUCK3_4 register */ + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK3_OV, 1, TPS6594_BIT_BUCKX_OV_INT(2)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK3_UV, 1, TPS6594_BIT_BUCKX_UV_INT(2)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK3_SC, 1, TPS6594_BIT_BUCKX_SC_INT(2)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK3_ILIM, 1, TPS6594_BIT_BUCKX_ILIM_INT(2)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK4_OV, 1, TPS6594_BIT_BUCKX_OV_INT(3)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK4_UV, 1, TPS6594_BIT_BUCKX_UV_INT(3)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK4_SC, 1, TPS6594_BIT_BUCKX_SC_INT(3)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK4_ILIM, 1, TPS6594_BIT_BUCKX_ILIM_INT(3)), + + /* INT_BUCK5 register */ + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK5_OV, 2, TPS6594_BIT_BUCKX_OV_INT(4)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK5_UV, 2, TPS6594_BIT_BUCKX_UV_INT(4)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK5_SC, 2, TPS6594_BIT_BUCKX_SC_INT(4)), + REGMAP_IRQ_REG(TPS6594_IRQ_BUCK5_ILIM, 2, TPS6594_BIT_BUCKX_ILIM_INT(4)), + + /* INT_LDO1_2 register */ + REGMAP_IRQ_REG(TPS6594_IRQ_LDO1_OV, 3, TPS6594_BIT_LDOX_OV_INT(0)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO1_UV, 3, TPS6594_BIT_LDOX_UV_INT(0)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO1_SC, 3, TPS6594_BIT_LDOX_SC_INT(0)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO1_ILIM, 3, TPS6594_BIT_LDOX_ILIM_INT(0)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO2_OV, 3, TPS6594_BIT_LDOX_OV_INT(1)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO2_UV, 3, TPS6594_BIT_LDOX_UV_INT(1)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO2_SC, 3, TPS6594_BIT_LDOX_SC_INT(1)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO2_ILIM, 3, TPS6594_BIT_LDOX_ILIM_INT(1)), + + /* INT_LDO3_4 register */ + REGMAP_IRQ_REG(TPS6594_IRQ_LDO3_OV, 4, TPS6594_BIT_LDOX_OV_INT(2)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO3_UV, 4, TPS6594_BIT_LDOX_UV_INT(2)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO3_SC, 4, TPS6594_BIT_LDOX_SC_INT(2)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO3_ILIM, 4, TPS6594_BIT_LDOX_ILIM_INT(2)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO4_OV, 4, TPS6594_BIT_LDOX_OV_INT(3)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO4_UV, 4, TPS6594_BIT_LDOX_UV_INT(3)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO4_SC, 4, TPS6594_BIT_LDOX_SC_INT(3)), + REGMAP_IRQ_REG(TPS6594_IRQ_LDO4_ILIM, 4, TPS6594_BIT_LDOX_ILIM_INT(3)), + + /* INT_VMON register */ + REGMAP_IRQ_REG(TPS6594_IRQ_VCCA_OV, 5, TPS6594_BIT_VCCA_OV_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_VCCA_UV, 5, TPS6594_BIT_VCCA_UV_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_VMON1_OV, 5, TPS6594_BIT_VMON1_OV_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_VMON1_UV, 5, TPS6594_BIT_VMON1_UV_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_VMON1_RV, 5, TPS6594_BIT_VMON1_RV_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_VMON2_OV, 5, TPS6594_BIT_VMON2_OV_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_VMON2_UV, 5, TPS6594_BIT_VMON2_UV_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_VMON2_RV, 5, TPS6594_BIT_VMON2_RV_INT), + + /* INT_GPIO register */ + REGMAP_IRQ_REG(TPS6594_IRQ_GPIO9, 6, TPS6594_BIT_GPIO9_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_GPIO10, 6, TPS6594_BIT_GPIO10_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_GPIO11, 6, TPS6594_BIT_GPIO11_INT), + + /* INT_GPIO1_8 register */ + REGMAP_IRQ_REG(TPS6594_IRQ_GPIO1, 7, TPS6594_BIT_GPIOX_INT(0)), + REGMAP_IRQ_REG(TPS6594_IRQ_GPIO2, 7, TPS6594_BIT_GPIOX_INT(1)), + REGMAP_IRQ_REG(TPS6594_IRQ_GPIO3, 7, TPS6594_BIT_GPIOX_INT(2)), + REGMAP_IRQ_REG(TPS6594_IRQ_GPIO4, 7, TPS6594_BIT_GPIOX_INT(3)), + REGMAP_IRQ_REG(TPS6594_IRQ_GPIO5, 7, TPS6594_BIT_GPIOX_INT(4)), + REGMAP_IRQ_REG(TPS6594_IRQ_GPIO6, 7, TPS6594_BIT_GPIOX_INT(5)), + REGMAP_IRQ_REG(TPS6594_IRQ_GPIO7, 7, TPS6594_BIT_GPIOX_INT(6)), + REGMAP_IRQ_REG(TPS6594_IRQ_GPIO8, 7, TPS6594_BIT_GPIOX_INT(7)), + + /* INT_STARTUP register */ + REGMAP_IRQ_REG(TPS6594_IRQ_NPWRON_START, 8, TPS6594_BIT_NPWRON_START_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_ENABLE, 8, TPS6594_BIT_ENABLE_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_FSD, 8, TPS6594_BIT_FSD_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_SOFT_REBOOT, 8, TPS6594_BIT_SOFT_REBOOT_INT), + + /* INT_MISC register */ + REGMAP_IRQ_REG(TPS6594_IRQ_BIST_PASS, 9, TPS6594_BIT_BIST_PASS_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_EXT_CLK, 9, TPS6594_BIT_EXT_CLK_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_TWARN, 9, TPS6594_BIT_TWARN_INT), + + /* INT_MODERATE_ERR register */ + REGMAP_IRQ_REG(TPS6594_IRQ_TSD_ORD, 10, TPS6594_BIT_TSD_ORD_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_BIST_FAIL, 10, TPS6594_BIT_BIST_FAIL_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_REG_CRC_ERR, 10, TPS6594_BIT_REG_CRC_ERR_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_RECOV_CNT, 10, TPS6594_BIT_RECOV_CNT_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_SPMI_ERR, 10, TPS6594_BIT_SPMI_ERR_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_NPWRON_LONG, 10, TPS6594_BIT_NPWRON_LONG_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_NINT_READBACK, 10, TPS6594_BIT_NINT_READBACK_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_NRSTOUT_READBACK, 10, TPS6594_BIT_NRSTOUT_READBACK_INT), + + /* INT_SEVERE_ERR register */ + REGMAP_IRQ_REG(TPS6594_IRQ_TSD_IMM, 11, TPS6594_BIT_TSD_IMM_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_VCCA_OVP, 11, TPS6594_BIT_VCCA_OVP_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_PFSM_ERR, 11, TPS6594_BIT_PFSM_ERR_INT), + + /* INT_FSM_ERR register */ + REGMAP_IRQ_REG(TPS6594_IRQ_IMM_SHUTDOWN, 12, TPS6594_BIT_IMM_SHUTDOWN_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_ORD_SHUTDOWN, 12, TPS6594_BIT_ORD_SHUTDOWN_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_MCU_PWR_ERR, 12, TPS6594_BIT_MCU_PWR_ERR_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_SOC_PWR_ERR, 12, TPS6594_BIT_SOC_PWR_ERR_INT), + + /* INT_COMM_ERR register */ + REGMAP_IRQ_REG(TPS6594_IRQ_COMM_FRM_ERR, 13, TPS6594_BIT_COMM_FRM_ERR_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_COMM_CRC_ERR, 13, TPS6594_BIT_COMM_CRC_ERR_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_COMM_ADR_ERR, 13, TPS6594_BIT_COMM_ADR_ERR_INT), + + /* INT_READBACK_ERR register */ + REGMAP_IRQ_REG(TPS6594_IRQ_EN_DRV_READBACK, 14, TPS6594_BIT_EN_DRV_READBACK_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_NRSTOUT_SOC_READBACK, 14, TPS6594_BIT_NRSTOUT_SOC_READBACK_INT), + + /* INT_ESM register */ + REGMAP_IRQ_REG(TPS6594_IRQ_ESM_SOC_PIN, 15, TPS6594_BIT_ESM_SOC_PIN_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_ESM_SOC_FAIL, 15, TPS6594_BIT_ESM_SOC_FAIL_INT), + REGMAP_IRQ_REG(TPS6594_IRQ_ESM_SOC_RST, 15, TPS6594_BIT_ESM_SOC_RST_INT), + + /* RTC_STATUS register */ + REGMAP_IRQ_REG(TPS6594_IRQ_TIMER, 16, TPS6594_BIT_TIMER), + REGMAP_IRQ_REG(TPS6594_IRQ_ALARM, 16, TPS6594_BIT_ALARM), + REGMAP_IRQ_REG(TPS6594_IRQ_POWER_UP, 16, TPS6594_BIT_POWER_UP), +}; + +static const unsigned int tps6594_irq_reg[] = { + TPS6594_REG_INT_BUCK1_2, + TPS6594_REG_INT_BUCK3_4, + TPS6594_REG_INT_BUCK5, + TPS6594_REG_INT_LDO1_2, + TPS6594_REG_INT_LDO3_4, + TPS6594_REG_INT_VMON, + TPS6594_REG_INT_GPIO, + TPS6594_REG_INT_GPIO1_8, + TPS6594_REG_INT_STARTUP, + TPS6594_REG_INT_MISC, + TPS6594_REG_INT_MODERATE_ERR, + TPS6594_REG_INT_SEVERE_ERR, + TPS6594_REG_INT_FSM_ERR, + TPS6594_REG_INT_COMM_ERR, + TPS6594_REG_INT_READBACK_ERR, + TPS6594_REG_INT_ESM, + 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, + * and COMM_ADR_ERR_INT bit is set. Besides, bits indicating interrupts + * (that must be cleared) and read-only bits are sometimes grouped in + * the same register. + * Since regmap clears interrupts by doing a write per register, clearing + * an interrupt bit in a register containing also a read-only bit makes + * COMM_ADR_ERR_INT bit set. Clear immediately this bit to avoid raising + * a new interrupt. + */ + 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; +}; + +static struct regmap_irq_chip tps6594_irq_chip = { + .ack_base = TPS6594_REG_INT_BUCK1_2, + .ack_invert = 1, + .clear_ack = 1, + .init_ack_masked = 1, + .num_regs = ARRAY_SIZE(tps6594_irq_reg), + .irqs = tps6594_irqs, + .num_irqs = ARRAY_SIZE(tps6594_irqs), + .get_irq_reg = tps6594_get_irq_reg, + .handle_post_irq = tps6594_handle_post_irq, +}; + +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, regmap_reg, mask_val); + if (ret == 0) { + ret = -EIO; + } else if (ret > 0) { + dev_info(tps->dev, "CRC feature enabled on %s PMIC", + primary_pmic ? "primary" : "secondary"); + ret = 0; + } + + return ret; +} + +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) { + /* + * If CRC is not already enabled, force PFSM I2C_2 trigger to enable it + * on primary PMIC. + */ + tps->use_crc = false; + ret = regmap_write_bits(tps->regmap, regmap_reg, mask_val, mask_val); + if (ret) + return ret; + + /* + * Wait for PFSM to process trigger. + * The datasheet indicates 2 ms, and clock specification is +/-5%. + * 4 ms should provide sufficient margin. + */ + usleep_range(4000, 5000); + + ret = tps6594_check_crc_mode(tps, true); + } + + return ret; +} + +static int tps6594_enable_crc(struct tps6594 *tps) +{ + struct device *dev = tps->dev; + unsigned int is_primary; + unsigned long timeout = msecs_to_jiffies(TPS6594_CRC_SYNC_TIMEOUT_MS); + int ret; + + /* + * CRC mode can be used with I2C or SPI protocols. + * If this mode is specified for primary PMIC, it will also be applied to secondary PMICs + * through SPMI serial interface. + * In this multi-PMIC synchronization scheme, the primary PMIC is the controller device + * on the SPMI bus, and the secondary PMICs are the target devices on the SPMI bus. + */ + is_primary = of_property_read_bool(dev->of_node, "ti,primary-pmic"); + if (is_primary) { + /* Enable CRC feature on primary PMIC */ + ret = tps6594_set_crc_feature(tps); + if (ret) + return ret; + + /* Notify secondary PMICs that CRC feature is enabled */ + complete_all(&tps6594_crc_comp); + } else { + /* Wait for CRC feature enabling event from primary PMIC */ + ret = wait_for_completion_interruptible_timeout(&tps6594_crc_comp, timeout); + if (ret == 0) + ret = -ETIMEDOUT; + else if (ret > 0) + ret = tps6594_check_crc_mode(tps, false); + } + + 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); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable CRC\n"); + } + + /* Keep PMIC in ACTIVE state */ + ret = regmap_set_bits(tps->regmap, TPS6594_REG_FSM_NSLEEP_TRIGGERS, + TPS6594_BIT_NSLEEP1B | TPS6594_BIT_NSLEEP2B); + if (ret) + return dev_err_probe(dev, ret, "Failed to set PMIC state\n"); + + 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, 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, 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"); + + /* 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)); + if (ret) + 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 new file mode 100644 index 000000000000..7ff7516286fd --- /dev/null +++ b/drivers/mfd/tps6594-i2c.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * I2C access driver for the following TI PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6593 + * - TPS6594 + * + * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ + */ + +#include <linux/crc8.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/of_device.h> +#include <linux/regmap.h> + +#include <linux/mfd/tps6594.h> + +static bool enable_crc; +module_param(enable_crc, bool, 0444); +MODULE_PARM_DESC(enable_crc, "Enable CRC feature for I2C interface"); + +DECLARE_CRC8_TABLE(tps6594_i2c_crc_table); + +static int tps6594_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + int ret = i2c_transfer(adap, msgs, num); + + if (ret == num) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static int tps6594_i2c_reg_read_with_crc(struct i2c_client *client, u8 page, u8 reg, u8 *val) +{ + struct i2c_msg msgs[2]; + u8 buf_rx[] = { 0, 0 }; + /* I2C address = I2C base address + Page index */ + const u8 addr = client->addr + page; + /* + * CRC is calculated from every bit included in the protocol + * except the ACK bits from the target. Byte stream is: + * - B0: (I2C_addr_7bits << 1) | WR_bit, with WR_bit = 0 + * - B1: reg + * - B2: (I2C_addr_7bits << 1) | RD_bit, with RD_bit = 1 + * - B3: val + * - B4: CRC from B0-B1-B2-B3 + */ + u8 crc_data[] = { addr << 1, reg, addr << 1 | 1, 0 }; + int ret; + + /* Write register */ + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = ® + + /* Read data and CRC */ + msgs[1].addr = msgs[0].addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 2; + msgs[1].buf = buf_rx; + + ret = tps6594_i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + return ret; + + crc_data[sizeof(crc_data) - 1] = *val = buf_rx[0]; + if (buf_rx[1] != crc8(tps6594_i2c_crc_table, crc_data, sizeof(crc_data), CRC8_INIT_VALUE)) + return -EIO; + + return ret; +} + +static int tps6594_i2c_reg_write_with_crc(struct i2c_client *client, u8 page, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[] = { reg, val, 0 }; + /* I2C address = I2C base address + Page index */ + const u8 addr = client->addr + page; + /* + * CRC is calculated from every bit included in the protocol + * except the ACK bits from the target. Byte stream is: + * - B0: (I2C_addr_7bits << 1) | WR_bit, with WR_bit = 0 + * - B1: reg + * - B2: val + * - B3: CRC from B0-B1-B2 + */ + const u8 crc_data[] = { addr << 1, reg, val }; + + /* Write register, data and CRC */ + msg.addr = addr; + msg.flags = client->flags & I2C_M_TEN; + msg.len = sizeof(buf); + msg.buf = buf; + + buf[msg.len - 1] = crc8(tps6594_i2c_crc_table, crc_data, sizeof(crc_data), CRC8_INIT_VALUE); + + return tps6594_i2c_transfer(client->adapter, &msg, 1); +} + +static int tps6594_i2c_read(void *context, const void *reg_buf, size_t reg_size, + void *val_buf, size_t val_size) +{ + struct i2c_client *client = context; + struct tps6594 *tps = i2c_get_clientdata(client); + struct i2c_msg msgs[2]; + const u8 *reg_bytes = reg_buf; + u8 *val_bytes = val_buf; + const u8 page = reg_bytes[1]; + u8 reg = reg_bytes[0]; + int ret = 0; + int i; + + if (tps->use_crc) { + /* + * Auto-increment feature does not support CRC protocol. + * Converts the bulk read operation into a series of single read operations. + */ + for (i = 0 ; ret == 0 && i < val_size ; i++) + ret = tps6594_i2c_reg_read_with_crc(client, page, reg + i, val_bytes + i); + + return ret; + } + + /* Write register: I2C address = I2C base address + Page index */ + msgs[0].addr = client->addr + page; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = ® + + /* Read data */ + msgs[1].addr = msgs[0].addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = val_size; + msgs[1].buf = val_bytes; + + return tps6594_i2c_transfer(client->adapter, msgs, 2); +} + +static int tps6594_i2c_write(void *context, const void *data, size_t count) +{ + struct i2c_client *client = context; + struct tps6594 *tps = i2c_get_clientdata(client); + struct i2c_msg msg; + const u8 *bytes = data; + u8 *buf; + const u8 page = bytes[1]; + const u8 reg = bytes[0]; + int ret = 0; + int i; + + if (tps->use_crc) { + /* + * Auto-increment feature does not support CRC protocol. + * Converts the bulk write operation into a series of single write operations. + */ + for (i = 0 ; ret == 0 && i < count - 2 ; i++) + ret = tps6594_i2c_reg_write_with_crc(client, page, reg + i, bytes[i + 2]); + + return ret; + } + + /* Setup buffer: page byte is not sent */ + buf = kzalloc(--count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = reg; + for (i = 0 ; i < count - 1 ; i++) + buf[i + 1] = bytes[i + 2]; + + /* Write register and data: I2C address = I2C base address + Page index */ + msg.addr = client->addr + page; + msg.flags = client->flags & I2C_M_TEN; + msg.len = count; + msg.buf = buf; + + ret = tps6594_i2c_transfer(client->adapter, &msg, 1); + + kfree(buf); + return ret; +} + +static struct regmap_config tps6594_i2c_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = TPS6594_REG_DWD_FAIL_CNT_REG, + .volatile_table = &tps6594_volatile_table, + .read = tps6594_i2c_read, + .write = tps6594_i2c_write, +}; + +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); + +static int tps6594_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct tps6594 *tps; + const struct of_device_id *match; + + tps = devm_kzalloc(dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + i2c_set_clientdata(client, tps); + + tps->dev = dev; + tps->reg = client->addr; + tps->irq = client->irq; + + 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); +} + +static struct i2c_driver tps6594_i2c_driver = { + .driver = { + .name = "tps6594", + .of_match_table = tps6594_i2c_of_match_table, + }, + .probe = tps6594_i2c_probe, +}; +module_i2c_driver(tps6594_i2c_driver); + +MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>"); +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 new file mode 100644 index 000000000000..944b7313a1d9 --- /dev/null +++ b/drivers/mfd/tps6594-spi.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SPI access driver for the following TI PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6593 + * - TPS6594 + * + * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ + */ + +#include <linux/crc8.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include <linux/mfd/tps6594.h> + +#define TPS6594_SPI_PAGE_SHIFT 5 +#define TPS6594_SPI_READ_BIT BIT(4) + +static bool enable_crc; +module_param(enable_crc, bool, 0444); +MODULE_PARM_DESC(enable_crc, "Enable CRC feature for SPI interface"); + +DECLARE_CRC8_TABLE(tps6594_spi_crc_table); + +static int tps6594_spi_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct spi_device *spi = context; + struct tps6594 *tps = spi_get_drvdata(spi); + u8 buf[4] = { 0 }; + size_t count_rx = 1; + int ret; + + buf[0] = reg; + buf[1] = TPS6594_REG_TO_PAGE(reg) << TPS6594_SPI_PAGE_SHIFT | TPS6594_SPI_READ_BIT; + + if (tps->use_crc) + count_rx++; + + ret = spi_write_then_read(spi, buf, 2, buf + 2, count_rx); + if (ret < 0) + return ret; + + if (tps->use_crc && buf[3] != crc8(tps6594_spi_crc_table, buf, 3, CRC8_INIT_VALUE)) + return -EIO; + + *val = buf[2]; + + return 0; +} + +static int tps6594_spi_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct spi_device *spi = context; + struct tps6594 *tps = spi_get_drvdata(spi); + u8 buf[4] = { 0 }; + size_t count = 3; + + buf[0] = reg; + buf[1] = TPS6594_REG_TO_PAGE(reg) << TPS6594_SPI_PAGE_SHIFT; + buf[2] = val; + + if (tps->use_crc) + buf[3] = crc8(tps6594_spi_crc_table, buf, count++, CRC8_INIT_VALUE); + + return spi_write(spi, buf, count); +} + +static struct regmap_config tps6594_spi_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = TPS6594_REG_DWD_FAIL_CNT_REG, + .volatile_table = &tps6594_volatile_table, + .reg_read = tps6594_spi_reg_read, + .reg_write = tps6594_spi_reg_write, + .use_single_read = true, + .use_single_write = true, +}; + +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); + +static int tps6594_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct tps6594 *tps; + const struct of_device_id *match; + + tps = devm_kzalloc(dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + spi_set_drvdata(spi, tps); + + tps->dev = dev; + tps->reg = spi_get_chipselect(spi, 0); + tps->irq = spi->irq; + + 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); +} + +static struct spi_driver tps6594_spi_driver = { + .driver = { + .name = "tps6594", + .of_match_table = tps6594_spi_of_match_table, + }, + .probe = tps6594_spi_probe, +}; +module_spi_driver(tps6594_spi_driver); + +MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>"); +MODULE_DESCRIPTION("SPI Interface Driver for TPS65224, TPS6594/3, and LP8764"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c deleted file mode 100644 index 3c4e62c3406a..000000000000 --- a/drivers/mfd/tps80031.c +++ /dev/null @@ -1,526 +0,0 @@ -/* - * tps80031.c -- TI TPS80031/TPS80032 mfd core driver. - * - * MFD core driver for TI TPS80031/TPS80032 Fully Integrated - * Power Management with Power Path and Battery Charger - * - * Copyright (c) 2012, NVIDIA Corporation. - * - * Author: Laxman Dewangan <ldewangan@nvidia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, - * whether express or implied; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA - */ - -#include <linux/err.h> -#include <linux/i2c.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/mfd/core.h> -#include <linux/mfd/tps80031.h> -#include <linux/pm.h> -#include <linux/regmap.h> -#include <linux/slab.h> - -static const struct resource tps80031_rtc_resources[] = { - DEFINE_RES_IRQ(TPS80031_INT_RTC_ALARM), -}; - -/* TPS80031 sub mfd devices */ -static const struct mfd_cell tps80031_cell[] = { - { - .name = "tps80031-pmic", - }, - { - .name = "tps80031-clock", - }, - { - .name = "tps80031-rtc", - .num_resources = ARRAY_SIZE(tps80031_rtc_resources), - .resources = tps80031_rtc_resources, - }, - { - .name = "tps80031-gpadc", - }, - { - .name = "tps80031-fuel-gauge", - }, - { - .name = "tps80031-charger", - }, -}; - -static int tps80031_slave_address[TPS80031_NUM_SLAVES] = { - TPS80031_I2C_ID0_ADDR, - TPS80031_I2C_ID1_ADDR, - TPS80031_I2C_ID2_ADDR, - TPS80031_I2C_ID3_ADDR, -}; - -struct tps80031_pupd_data { - u8 reg; - u8 pullup_bit; - u8 pulldown_bit; -}; - -#define TPS80031_IRQ(_reg, _mask) \ - { \ - .reg_offset = (TPS80031_INT_MSK_LINE_##_reg) - \ - TPS80031_INT_MSK_LINE_A, \ - .mask = BIT(_mask), \ - } - -static const struct regmap_irq tps80031_main_irqs[] = { - [TPS80031_INT_PWRON] = TPS80031_IRQ(A, 0), - [TPS80031_INT_RPWRON] = TPS80031_IRQ(A, 1), - [TPS80031_INT_SYS_VLOW] = TPS80031_IRQ(A, 2), - [TPS80031_INT_RTC_ALARM] = TPS80031_IRQ(A, 3), - [TPS80031_INT_RTC_PERIOD] = TPS80031_IRQ(A, 4), - [TPS80031_INT_HOT_DIE] = TPS80031_IRQ(A, 5), - [TPS80031_INT_VXX_SHORT] = TPS80031_IRQ(A, 6), - [TPS80031_INT_SPDURATION] = TPS80031_IRQ(A, 7), - [TPS80031_INT_WATCHDOG] = TPS80031_IRQ(B, 0), - [TPS80031_INT_BAT] = TPS80031_IRQ(B, 1), - [TPS80031_INT_SIM] = TPS80031_IRQ(B, 2), - [TPS80031_INT_MMC] = TPS80031_IRQ(B, 3), - [TPS80031_INT_RES] = TPS80031_IRQ(B, 4), - [TPS80031_INT_GPADC_RT] = TPS80031_IRQ(B, 5), - [TPS80031_INT_GPADC_SW2_EOC] = TPS80031_IRQ(B, 6), - [TPS80031_INT_CC_AUTOCAL] = TPS80031_IRQ(B, 7), - [TPS80031_INT_ID_WKUP] = TPS80031_IRQ(C, 0), - [TPS80031_INT_VBUSS_WKUP] = TPS80031_IRQ(C, 1), - [TPS80031_INT_ID] = TPS80031_IRQ(C, 2), - [TPS80031_INT_VBUS] = TPS80031_IRQ(C, 3), - [TPS80031_INT_CHRG_CTRL] = TPS80031_IRQ(C, 4), - [TPS80031_INT_EXT_CHRG] = TPS80031_IRQ(C, 5), - [TPS80031_INT_INT_CHRG] = TPS80031_IRQ(C, 6), - [TPS80031_INT_RES2] = TPS80031_IRQ(C, 7), -}; - -static struct regmap_irq_chip tps80031_irq_chip = { - .name = "tps80031", - .irqs = tps80031_main_irqs, - .num_irqs = ARRAY_SIZE(tps80031_main_irqs), - .num_regs = 3, - .status_base = TPS80031_INT_STS_A, - .mask_base = TPS80031_INT_MSK_LINE_A, -}; - -#define PUPD_DATA(_reg, _pulldown_bit, _pullup_bit) \ - { \ - .reg = TPS80031_CFG_INPUT_PUPD##_reg, \ - .pulldown_bit = _pulldown_bit, \ - .pullup_bit = _pullup_bit, \ - } - -static const struct tps80031_pupd_data tps80031_pupds[] = { - [TPS80031_PREQ1] = PUPD_DATA(1, BIT(0), BIT(1)), - [TPS80031_PREQ2A] = PUPD_DATA(1, BIT(2), BIT(3)), - [TPS80031_PREQ2B] = PUPD_DATA(1, BIT(4), BIT(5)), - [TPS80031_PREQ2C] = PUPD_DATA(1, BIT(6), BIT(7)), - [TPS80031_PREQ3] = PUPD_DATA(2, BIT(0), BIT(1)), - [TPS80031_NRES_WARM] = PUPD_DATA(2, 0, BIT(2)), - [TPS80031_PWM_FORCE] = PUPD_DATA(2, BIT(5), 0), - [TPS80031_CHRG_EXT_CHRG_STATZ] = PUPD_DATA(2, 0, BIT(6)), - [TPS80031_SIM] = PUPD_DATA(3, BIT(0), BIT(1)), - [TPS80031_MMC] = PUPD_DATA(3, BIT(2), BIT(3)), - [TPS80031_GPADC_START] = PUPD_DATA(3, BIT(4), 0), - [TPS80031_DVSI2C_SCL] = PUPD_DATA(4, 0, BIT(0)), - [TPS80031_DVSI2C_SDA] = PUPD_DATA(4, 0, BIT(1)), - [TPS80031_CTLI2C_SCL] = PUPD_DATA(4, 0, BIT(2)), - [TPS80031_CTLI2C_SDA] = PUPD_DATA(4, 0, BIT(3)), -}; -static struct tps80031 *tps80031_power_off_dev; - -int tps80031_ext_power_req_config(struct device *dev, - unsigned long ext_ctrl_flag, int preq_bit, - int state_reg_add, int trans_reg_add) -{ - u8 res_ass_reg = 0; - int preq_mask_bit = 0; - int ret; - - if (!(ext_ctrl_flag & TPS80031_EXT_PWR_REQ)) - return 0; - - if (ext_ctrl_flag & TPS80031_PWR_REQ_INPUT_PREQ1) { - res_ass_reg = TPS80031_PREQ1_RES_ASS_A + (preq_bit >> 3); - preq_mask_bit = 5; - } else if (ext_ctrl_flag & TPS80031_PWR_REQ_INPUT_PREQ2) { - res_ass_reg = TPS80031_PREQ2_RES_ASS_A + (preq_bit >> 3); - preq_mask_bit = 6; - } else if (ext_ctrl_flag & TPS80031_PWR_REQ_INPUT_PREQ3) { - res_ass_reg = TPS80031_PREQ3_RES_ASS_A + (preq_bit >> 3); - preq_mask_bit = 7; - } - - /* Configure REQ_ASS registers */ - ret = tps80031_set_bits(dev, TPS80031_SLAVE_ID1, res_ass_reg, - BIT(preq_bit & 0x7)); - if (ret < 0) { - dev_err(dev, "reg 0x%02x setbit failed, err = %d\n", - res_ass_reg, ret); - return ret; - } - - /* Unmask the PREQ */ - ret = tps80031_clr_bits(dev, TPS80031_SLAVE_ID1, - TPS80031_PHOENIX_MSK_TRANSITION, BIT(preq_mask_bit)); - if (ret < 0) { - dev_err(dev, "reg 0x%02x clrbit failed, err = %d\n", - TPS80031_PHOENIX_MSK_TRANSITION, ret); - return ret; - } - - /* Switch regulator control to resource now */ - if (ext_ctrl_flag & (TPS80031_PWR_REQ_INPUT_PREQ2 | - TPS80031_PWR_REQ_INPUT_PREQ3)) { - ret = tps80031_update(dev, TPS80031_SLAVE_ID1, state_reg_add, - 0x0, TPS80031_STATE_MASK); - if (ret < 0) - dev_err(dev, "reg 0x%02x update failed, err = %d\n", - state_reg_add, ret); - } else { - ret = tps80031_update(dev, TPS80031_SLAVE_ID1, trans_reg_add, - TPS80031_TRANS_SLEEP_OFF, - TPS80031_TRANS_SLEEP_MASK); - if (ret < 0) - dev_err(dev, "reg 0x%02x update failed, err = %d\n", - trans_reg_add, ret); - } - return ret; -} -EXPORT_SYMBOL_GPL(tps80031_ext_power_req_config); - -static void tps80031_power_off(void) -{ - dev_info(tps80031_power_off_dev->dev, "switching off PMU\n"); - tps80031_write(tps80031_power_off_dev->dev, TPS80031_SLAVE_ID1, - TPS80031_PHOENIX_DEV_ON, TPS80031_DEVOFF); -} - -static void tps80031_pupd_init(struct tps80031 *tps80031, - struct tps80031_platform_data *pdata) -{ - struct tps80031_pupd_init_data *pupd_init_data = pdata->pupd_init_data; - int data_size = pdata->pupd_init_data_size; - int i; - - for (i = 0; i < data_size; ++i) { - struct tps80031_pupd_init_data *pupd_init = &pupd_init_data[i]; - const struct tps80031_pupd_data *pupd = - &tps80031_pupds[pupd_init->input_pin]; - u8 update_value = 0; - u8 update_mask = pupd->pulldown_bit | pupd->pullup_bit; - - if (pupd_init->setting == TPS80031_PUPD_PULLDOWN) - update_value = pupd->pulldown_bit; - else if (pupd_init->setting == TPS80031_PUPD_PULLUP) - update_value = pupd->pullup_bit; - - tps80031_update(tps80031->dev, TPS80031_SLAVE_ID1, pupd->reg, - update_value, update_mask); - } -} - -static int tps80031_init_ext_control(struct tps80031 *tps80031, - struct tps80031_platform_data *pdata) -{ - struct device *dev = tps80031->dev; - int ret; - int i; - - /* Clear all external control for this rail */ - for (i = 0; i < 9; ++i) { - ret = tps80031_write(dev, TPS80031_SLAVE_ID1, - TPS80031_PREQ1_RES_ASS_A + i, 0); - if (ret < 0) { - dev_err(dev, "reg 0x%02x write failed, err = %d\n", - TPS80031_PREQ1_RES_ASS_A + i, ret); - return ret; - } - } - - /* Mask the PREQ */ - ret = tps80031_set_bits(dev, TPS80031_SLAVE_ID1, - TPS80031_PHOENIX_MSK_TRANSITION, 0x7 << 5); - if (ret < 0) { - dev_err(dev, "reg 0x%02x set_bits failed, err = %d\n", - TPS80031_PHOENIX_MSK_TRANSITION, ret); - return ret; - } - return ret; -} - -static int tps80031_irq_init(struct tps80031 *tps80031, int irq, int irq_base) -{ - struct device *dev = tps80031->dev; - int i, ret; - - /* - * The MASK register used for updating status register when - * interrupt occurs and LINE register used to pass the status - * to actual interrupt line. As per datasheet: - * When INT_MSK_LINE [i] is set to 1, the associated interrupt - * number i is INT line masked, which means that no interrupt is - * generated on the INT line. - * When INT_MSK_LINE [i] is set to 0, the associated interrupt - * number i is line enabled: An interrupt is generated on the - * INT line. - * In any case, the INT_STS [i] status bit may or may not be updated, - * only linked to the INT_MSK_STS [i] configuration register bit. - * - * When INT_MSK_STS [i] is set to 1, the associated interrupt number - * i is status masked, which means that no interrupt is stored in - * the INT_STS[i] status bit. Note that no interrupt number i is - * generated on the INT line, even if the INT_MSK_LINE [i] register - * bit is set to 0. - * When INT_MSK_STS [i] is set to 0, the associated interrupt number i - * is status enabled: An interrupt status is updated in the INT_STS [i] - * register. The interrupt may or may not be generated on the INT line, - * depending on the INT_MSK_LINE [i] configuration register bit. - */ - for (i = 0; i < 3; i++) - tps80031_write(dev, TPS80031_SLAVE_ID2, - TPS80031_INT_MSK_STS_A + i, 0x00); - - ret = regmap_add_irq_chip(tps80031->regmap[TPS80031_SLAVE_ID2], irq, - IRQF_ONESHOT, irq_base, - &tps80031_irq_chip, &tps80031->irq_data); - if (ret < 0) { - dev_err(dev, "add irq failed, err = %d\n", ret); - return ret; - } - return ret; -} - -static bool rd_wr_reg_id0(struct device *dev, unsigned int reg) -{ - switch (reg) { - case TPS80031_SMPS1_CFG_FORCE ... TPS80031_SMPS2_CFG_VOLTAGE: - return true; - default: - return false; - } -} - -static bool rd_wr_reg_id1(struct device *dev, unsigned int reg) -{ - switch (reg) { - case TPS80031_SECONDS_REG ... TPS80031_RTC_RESET_STATUS_REG: - case TPS80031_VALIDITY0 ... TPS80031_VALIDITY7: - case TPS80031_PHOENIX_START_CONDITION ... TPS80031_KEY_PRESS_DUR_CFG: - case TPS80031_SMPS4_CFG_TRANS ... TPS80031_SMPS3_CFG_VOLTAGE: - case TPS80031_BROADCAST_ADDR_ALL ... TPS80031_BROADCAST_ADDR_CLK_RST: - case TPS80031_VANA_CFG_TRANS ... TPS80031_LDO7_CFG_VOLTAGE: - case TPS80031_REGEN1_CFG_TRANS ... TPS80031_TMP_CFG_STATE: - case TPS80031_PREQ1_RES_ASS_A ... TPS80031_PREQ3_RES_ASS_C: - case TPS80031_SMPS_OFFSET ... TPS80031_BATDEBOUNCING: - case TPS80031_CFG_INPUT_PUPD1 ... TPS80031_CFG_SMPS_PD: - case TPS80031_BACKUP_REG: - return true; - default: - return false; - } -} - -static bool is_volatile_reg_id1(struct device *dev, unsigned int reg) -{ - switch (reg) { - case TPS80031_SMPS4_CFG_TRANS ... TPS80031_SMPS3_CFG_VOLTAGE: - case TPS80031_VANA_CFG_TRANS ... TPS80031_LDO7_CFG_VOLTAGE: - case TPS80031_REGEN1_CFG_TRANS ... TPS80031_TMP_CFG_STATE: - case TPS80031_PREQ1_RES_ASS_A ... TPS80031_PREQ3_RES_ASS_C: - case TPS80031_SMPS_OFFSET ... TPS80031_BATDEBOUNCING: - case TPS80031_CFG_INPUT_PUPD1 ... TPS80031_CFG_SMPS_PD: - return true; - default: - return false; - } -} - -static bool rd_wr_reg_id2(struct device *dev, unsigned int reg) -{ - switch (reg) { - case TPS80031_USB_VENDOR_ID_LSB ... TPS80031_USB_OTG_REVISION: - case TPS80031_GPADC_CTRL ... TPS80031_CTRL_P1: - case TPS80031_RTCH0_LSB ... TPS80031_GPCH0_MSB: - case TPS80031_TOGGLE1 ... TPS80031_VIBMODE: - case TPS80031_PWM1ON ... TPS80031_PWM2OFF: - case TPS80031_FG_REG_00 ... TPS80031_FG_REG_11: - case TPS80031_INT_STS_A ... TPS80031_INT_MSK_STS_C: - case TPS80031_CONTROLLER_CTRL2 ... TPS80031_LED_PWM_CTRL2: - return true; - default: - return false; - } -} - -static bool rd_wr_reg_id3(struct device *dev, unsigned int reg) -{ - switch (reg) { - case TPS80031_GPADC_TRIM0 ... TPS80031_GPADC_TRIM18: - return true; - default: - return false; - } -} - -static const struct regmap_config tps80031_regmap_configs[] = { - { - .reg_bits = 8, - .val_bits = 8, - .writeable_reg = rd_wr_reg_id0, - .readable_reg = rd_wr_reg_id0, - .max_register = TPS80031_MAX_REGISTER, - }, - { - .reg_bits = 8, - .val_bits = 8, - .writeable_reg = rd_wr_reg_id1, - .readable_reg = rd_wr_reg_id1, - .volatile_reg = is_volatile_reg_id1, - .max_register = TPS80031_MAX_REGISTER, - }, - { - .reg_bits = 8, - .val_bits = 8, - .writeable_reg = rd_wr_reg_id2, - .readable_reg = rd_wr_reg_id2, - .max_register = TPS80031_MAX_REGISTER, - }, - { - .reg_bits = 8, - .val_bits = 8, - .writeable_reg = rd_wr_reg_id3, - .readable_reg = rd_wr_reg_id3, - .max_register = TPS80031_MAX_REGISTER, - }, -}; - -static int tps80031_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct tps80031_platform_data *pdata = dev_get_platdata(&client->dev); - struct tps80031 *tps80031; - int ret; - uint8_t es_version; - uint8_t ep_ver; - int i; - - if (!pdata) { - dev_err(&client->dev, "tps80031 requires platform data\n"); - return -EINVAL; - } - - tps80031 = devm_kzalloc(&client->dev, sizeof(*tps80031), GFP_KERNEL); - if (!tps80031) - return -ENOMEM; - - for (i = 0; i < TPS80031_NUM_SLAVES; i++) { - if (tps80031_slave_address[i] == client->addr) - tps80031->clients[i] = client; - else - tps80031->clients[i] = devm_i2c_new_dummy_device(&client->dev, - client->adapter, tps80031_slave_address[i]); - if (IS_ERR(tps80031->clients[i])) { - dev_err(&client->dev, "can't attach client %d\n", i); - return PTR_ERR(tps80031->clients[i]); - } - - i2c_set_clientdata(tps80031->clients[i], tps80031); - tps80031->regmap[i] = devm_regmap_init_i2c(tps80031->clients[i], - &tps80031_regmap_configs[i]); - if (IS_ERR(tps80031->regmap[i])) { - ret = PTR_ERR(tps80031->regmap[i]); - dev_err(&client->dev, - "regmap %d init failed, err %d\n", i, ret); - return ret; - } - } - - ret = tps80031_read(&client->dev, TPS80031_SLAVE_ID3, - TPS80031_JTAGVERNUM, &es_version); - if (ret < 0) { - dev_err(&client->dev, - "Silicon version number read failed: %d\n", ret); - return ret; - } - - ret = tps80031_read(&client->dev, TPS80031_SLAVE_ID3, - TPS80031_EPROM_REV, &ep_ver); - if (ret < 0) { - dev_err(&client->dev, - "Silicon eeprom version read failed: %d\n", ret); - return ret; - } - - dev_info(&client->dev, "ES version 0x%02x and EPROM version 0x%02x\n", - es_version, ep_ver); - tps80031->es_version = es_version; - tps80031->dev = &client->dev; - i2c_set_clientdata(client, tps80031); - tps80031->chip_info = id->driver_data; - - ret = tps80031_irq_init(tps80031, client->irq, pdata->irq_base); - if (ret) { - dev_err(&client->dev, "IRQ init failed: %d\n", ret); - return ret; - } - - tps80031_pupd_init(tps80031, pdata); - - tps80031_init_ext_control(tps80031, pdata); - - ret = mfd_add_devices(tps80031->dev, -1, - tps80031_cell, ARRAY_SIZE(tps80031_cell), - NULL, 0, - regmap_irq_get_domain(tps80031->irq_data)); - if (ret < 0) { - dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret); - goto fail_mfd_add; - } - - if (pdata->use_power_off && !pm_power_off) { - tps80031_power_off_dev = tps80031; - pm_power_off = tps80031_power_off; - } - return 0; - -fail_mfd_add: - regmap_del_irq_chip(client->irq, tps80031->irq_data); - return ret; -} - -static const struct i2c_device_id tps80031_id_table[] = { - { "tps80031", TPS80031 }, - { "tps80032", TPS80032 }, - { } -}; - -static struct i2c_driver tps80031_driver = { - .driver = { - .name = "tps80031", - .suppress_bind_attrs = true, - }, - .probe = tps80031_probe, - .id_table = tps80031_id_table, -}; - -static int __init tps80031_init(void) -{ - return i2c_add_driver(&tps80031_driver); -} -subsys_initcall(tps80031_init); diff --git a/drivers/mfd/tqmx86.c b/drivers/mfd/tqmx86.c index ddddf08b6a4c..1c2fe3f91238 100644 --- a/drivers/mfd/tqmx86.c +++ b/drivers/mfd/tqmx86.c @@ -16,8 +16,8 @@ #include <linux/platform_data/i2c-ocores.h> #include <linux/platform_device.h> -#define TQMX86_IOBASE 0x160 -#define TQMX86_IOSIZE 0x3f +#define TQMX86_IOBASE 0x180 +#define TQMX86_IOSIZE 0x20 #define TQMX86_IOBASE_I2C 0x1a0 #define TQMX86_IOSIZE_I2C 0xa #define TQMX86_IOBASE_WATCHDOG 0x18b @@ -25,49 +25,72 @@ #define TQMX86_IOBASE_GPIO 0x18d #define TQMX86_IOSIZE_GPIO 0x4 -#define TQMX86_REG_BOARD_ID 0x20 +#define TQMX86_REG_BOARD_ID 0x00 #define TQMX86_REG_BOARD_ID_E38M 1 #define TQMX86_REG_BOARD_ID_50UC 2 #define TQMX86_REG_BOARD_ID_E38C 3 #define TQMX86_REG_BOARD_ID_60EB 4 -#define TQMX86_REG_BOARD_ID_E39M 5 -#define TQMX86_REG_BOARD_ID_E39C 6 -#define TQMX86_REG_BOARD_ID_E39x 7 +#define TQMX86_REG_BOARD_ID_E39MS 5 +#define TQMX86_REG_BOARD_ID_E39C1 6 +#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_90UC 10 -#define TQMX86_REG_BOARD_REV 0x21 -#define TQMX86_REG_IO_EXT_INT 0x26 +#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 #define TQMX86_REG_IO_EXT_INT_7 1 #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 -#define TQMX86_REG_I2C_DETECT 0x47 +#define TQMX86_REG_I2C_DETECT 0x1a7 #define TQMX86_REG_I2C_DETECT_SOFT 0xa5 -#define TQMX86_REG_I2C_INT_EN 0x49 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[] = { @@ -77,7 +100,7 @@ static struct i2c_board_info tqmx86_i2c_devices[] = { }, }; -static struct ocores_i2c_platform_data ocores_platfom_data = { +static struct ocores_i2c_platform_data ocores_platform_data = { .num_devices = ARRAY_SIZE(tqmx86_i2c_devices), .devices = tqmx86_i2c_devices, }; @@ -85,8 +108,8 @@ static struct ocores_i2c_platform_data ocores_platfom_data = { static const struct mfd_cell tqmx86_i2c_soft_dev[] = { { .name = "ocores-i2c", - .platform_data = &ocores_platfom_data, - .pdata_size = sizeof(ocores_platfom_data), + .platform_data = &ocores_platform_data, + .pdata_size = sizeof(ocores_platform_data), .resources = tqmx_i2c_soft_resources, .num_resources = ARRAY_SIZE(tqmx_i2c_soft_resources), }, @@ -107,7 +130,7 @@ static const struct mfd_cell tqmx86_devs[] = { }, }; -static const char *tqmx86_board_id_to_name(u8 board_id) +static const char *tqmx86_board_id_to_name(u8 board_id, u8 sauc) { switch (board_id) { case TQMX86_REG_BOARD_ID_E38M: @@ -118,102 +141,156 @@ static const char *tqmx86_board_id_to_name(u8 board_id) return "TQMxE38C"; case TQMX86_REG_BOARD_ID_60EB: return "TQMx60EB"; - case TQMX86_REG_BOARD_ID_E39M: - return "TQMxE39M"; - case TQMX86_REG_BOARD_ID_E39C: - return "TQMxE39C"; - case TQMX86_REG_BOARD_ID_E39x: - return "TQMxE39x"; + case TQMX86_REG_BOARD_ID_E39MS: + return (sauc == 0xff) ? "TQMxE39M" : "TQMxE39S"; + case TQMX86_REG_BOARD_ID_E39C1: + return "TQMxE39C1"; + case TQMX86_REG_BOARD_ID_E39C2: + return "TQMxE39C2"; case TQMX86_REG_BOARD_ID_70EB: return "TQMx70EB"; case TQMX86_REG_BOARD_ID_80UC: return "TQMx80UC"; - case TQMX86_REG_BOARD_ID_90UC: - return "TQMx90UC"; + case TQMX86_REG_BOARD_ID_120UC: + return "TQMx120UC"; + case TQMX86_REG_BOARD_ID_110EB: + return "TQMx110EB"; + case TQMX86_REG_BOARD_ID_E40M: + return "TQMxE40M"; + case TQMX86_REG_BOARD_ID_E40S: + return "TQMxE40S"; + case TQMX86_REG_BOARD_ID_E40C1: + 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"; } } -static int tqmx86_board_id_to_clk_rate(u8 board_id) +static int tqmx86_board_id_to_clk_rate(struct device *dev, u8 board_id) { switch (board_id) { case TQMX86_REG_BOARD_ID_50UC: case TQMX86_REG_BOARD_ID_60EB: case TQMX86_REG_BOARD_ID_70EB: case TQMX86_REG_BOARD_ID_80UC: - case TQMX86_REG_BOARD_ID_90UC: + 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_E39M: - case TQMX86_REG_BOARD_ID_E39C: - case TQMX86_REG_BOARD_ID_E39x: + case TQMX86_REG_BOARD_ID_E39MS: + case TQMX86_REG_BOARD_ID_E39C1: + case TQMX86_REG_BOARD_ID_E39C2: return 25000; case TQMX86_REG_BOARD_ID_E38M: case TQMX86_REG_BOARD_ID_E38C: return 33000; default: - return 0; + dev_warn(dev, "unknown board %d, assuming 24MHz LPC clock\n", + board_id); + return 24000; } } -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, 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; board_id = ioread8(io_base + TQMX86_REG_BOARD_ID); - board_name = tqmx86_board_id_to_name(board_id); + sauc = ioread8(io_base + TQMX86_REG_SAUC); + board_name = tqmx86_board_id_to_name(board_id, sauc); rev = ioread8(io_base + TQMX86_REG_BOARD_REV); dev_info(dev, "Found %s - Board ID %d, PCB Revision %d, PLD Revision %d\n", board_name, board_id, rev >> 4, rev & 0xf); - i2c_det = ioread8(io_base + 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; + /* + * The I2C_DETECT register is in the range assigned to the I2C driver + * later, so we don't extend TQMX86_IOSIZE. Use inb() for this one-off + * access instead of ioport_map + unmap. + */ + i2c_det = inb(TQMX86_REG_I2C_DETECT); + + 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_platfom_data.clock_khz = tqmx86_board_id_to_clk_rate(board_id); + 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), @@ -253,6 +330,14 @@ static const struct dmi_system_id tqmx86_dmi_table[] __initconst = { }, .callback = tqmx86_create_platform_device, }, + { + .ident = "TQMX86", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TQ-Systems"), + DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"), + }, + .callback = tqmx86_create_platform_device, + }, {} }; MODULE_DEVICE_TABLE(dmi, tqmx86_dmi_table); diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 289b556dede2..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 */ @@ -110,8 +112,8 @@ #define TWL6030_BASEADD_PWM 0x00BA #define TWL6030_BASEADD_GASGAUGE 0x00C0 #define TWL6030_BASEADD_PIH 0x00D0 -#define TWL6030_BASEADD_CHARGER 0x00E0 #define TWL6032_BASEADD_CHARGER 0x00DA +#define TWL6030_BASEADD_CHARGER 0x00E0 #define TWL6030_BASEADD_LED 0x00F4 /* subchip/slave 2 0x4A - DFT */ @@ -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 */ @@ -353,6 +360,9 @@ static struct twl_mapping twl6030_map[] = { { 2, TWL6030_BASEADD_ZERO }, { 1, TWL6030_BASEADD_GPADC_CTRL }, { 1, TWL6030_BASEADD_GASGAUGE }, + + /* TWL6032 specific charger registers */ + { 1, TWL6032_BASEADD_CHARGER }, }; static const struct regmap_config twl6030_regmap_config[3] = { @@ -591,374 +601,6 @@ int twl_get_hfclk_rate(void) } EXPORT_SYMBOL_GPL(twl_get_hfclk_rate); -static struct device * -add_numbered_child(unsigned mod_no, const char *name, int num, - void *pdata, unsigned pdata_len, - bool can_wakeup, int irq0, int irq1) -{ - struct platform_device *pdev; - struct twl_client *twl; - int status, sid; - - if (unlikely(mod_no >= twl_get_last_module())) { - pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); - return ERR_PTR(-EPERM); - } - sid = twl_priv->twl_map[mod_no].sid; - twl = &twl_priv->twl_modules[sid]; - - pdev = platform_device_alloc(name, num); - if (!pdev) - return ERR_PTR(-ENOMEM); - - pdev->dev.parent = &twl->client->dev; - - if (pdata) { - status = platform_device_add_data(pdev, pdata, pdata_len); - if (status < 0) { - dev_dbg(&pdev->dev, "can't add platform_data\n"); - goto put_device; - } - } - - if (irq0) { - struct resource r[2] = { - { .start = irq0, .flags = IORESOURCE_IRQ, }, - { .start = irq1, .flags = IORESOURCE_IRQ, }, - }; - - status = platform_device_add_resources(pdev, r, irq1 ? 2 : 1); - if (status < 0) { - dev_dbg(&pdev->dev, "can't add irqs\n"); - goto put_device; - } - } - - status = platform_device_add(pdev); - if (status) - goto put_device; - - device_init_wakeup(&pdev->dev, can_wakeup); - - return &pdev->dev; - -put_device: - platform_device_put(pdev); - dev_err(&twl->client->dev, "failed to add device %s\n", name); - return ERR_PTR(status); -} - -static inline struct device *add_child(unsigned mod_no, const char *name, - void *pdata, unsigned pdata_len, - bool can_wakeup, int irq0, int irq1) -{ - return add_numbered_child(mod_no, name, -1, pdata, pdata_len, - can_wakeup, irq0, irq1); -} - -static struct device * -add_regulator_linked(int num, struct regulator_init_data *pdata, - struct regulator_consumer_supply *consumers, - unsigned num_consumers, unsigned long features) -{ - struct twl_regulator_driver_data drv_data; - - /* regulator framework demands init_data ... */ - if (!pdata) - return NULL; - - if (consumers) { - pdata->consumer_supplies = consumers; - pdata->num_consumer_supplies = num_consumers; - } - - if (pdata->driver_data) { - /* If we have existing drv_data, just add the flags */ - struct twl_regulator_driver_data *tmp; - tmp = pdata->driver_data; - tmp->features |= features; - } else { - /* add new driver data struct, used only during init */ - drv_data.features = features; - drv_data.set_voltage = NULL; - drv_data.get_voltage = NULL; - drv_data.data = NULL; - pdata->driver_data = &drv_data; - } - - /* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */ - return add_numbered_child(TWL_MODULE_PM_MASTER, "twl_reg", num, - pdata, sizeof(*pdata), false, 0, 0); -} - -static struct device * -add_regulator(int num, struct regulator_init_data *pdata, - unsigned long features) -{ - return add_regulator_linked(num, pdata, NULL, 0, features); -} - -/* - * NOTE: We know the first 8 IRQs after pdata->base_irq are - * for the PIH, and the next are for the PWR_INT SIH, since - * that's how twl_init_irq() sets things up. - */ - -static int -add_children(struct twl4030_platform_data *pdata, unsigned irq_base, - unsigned long features) -{ - struct device *child; - - if (IS_ENABLED(CONFIG_GPIO_TWL4030) && pdata->gpio) { - child = add_child(TWL4030_MODULE_GPIO, "twl4030_gpio", - pdata->gpio, sizeof(*pdata->gpio), - false, irq_base + GPIO_INTR_OFFSET, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - if (IS_ENABLED(CONFIG_KEYBOARD_TWL4030) && pdata->keypad) { - child = add_child(TWL4030_MODULE_KEYPAD, "twl4030_keypad", - pdata->keypad, sizeof(*pdata->keypad), - true, irq_base + KEYPAD_INTR_OFFSET, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc && - twl_class_is_4030()) { - child = add_child(TWL4030_MODULE_MADC, "twl4030_madc", - pdata->madc, sizeof(*pdata->madc), - true, irq_base + MADC_INTR_OFFSET, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - if (IS_ENABLED(CONFIG_RTC_DRV_TWL4030)) { - /* - * REVISIT platform_data here currently might expose the - * "msecure" line ... but for now we just expect board - * setup to tell the chip "it's always ok to SET_TIME". - * Eventually, Linux might become more aware of such - * HW security concerns, and "least privilege". - */ - child = add_child(TWL_MODULE_RTC, "twl_rtc", NULL, 0, - true, irq_base + RTC_INTR_OFFSET, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - if (IS_ENABLED(CONFIG_PWM_TWL)) { - child = add_child(TWL_MODULE_PWM, "twl-pwm", NULL, 0, - false, 0, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - if (IS_ENABLED(CONFIG_PWM_TWL_LED)) { - child = add_child(TWL_MODULE_LED, "twl-pwmled", NULL, 0, - false, 0, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - if (IS_ENABLED(CONFIG_TWL4030_USB) && pdata->usb && - twl_class_is_4030()) { - - static struct regulator_consumer_supply usb1v5 = { - .supply = "usb1v5", - }; - static struct regulator_consumer_supply usb1v8 = { - .supply = "usb1v8", - }; - static struct regulator_consumer_supply usb3v1 = { - .supply = "usb3v1", - }; - - /* First add the regulators so that they can be used by transceiver */ - if (IS_ENABLED(CONFIG_REGULATOR_TWL4030)) { - /* this is a template that gets copied */ - struct regulator_init_data usb_fixed = { - .constraints.valid_modes_mask = - REGULATOR_MODE_NORMAL - | REGULATOR_MODE_STANDBY, - .constraints.valid_ops_mask = - REGULATOR_CHANGE_MODE - | REGULATOR_CHANGE_STATUS, - }; - - child = add_regulator_linked(TWL4030_REG_VUSB1V5, - &usb_fixed, &usb1v5, 1, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator_linked(TWL4030_REG_VUSB1V8, - &usb_fixed, &usb1v8, 1, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator_linked(TWL4030_REG_VUSB3V1, - &usb_fixed, &usb3v1, 1, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - } - - child = add_child(TWL_MODULE_USB, "twl4030_usb", - pdata->usb, sizeof(*pdata->usb), true, - /* irq0 = USB_PRES, irq1 = USB */ - irq_base + USB_PRES_INTR_OFFSET, - irq_base + USB_INTR_OFFSET); - - if (IS_ERR(child)) - return PTR_ERR(child); - - /* we need to connect regulators to this transceiver */ - if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && child) { - usb1v5.dev_name = dev_name(child); - usb1v8.dev_name = dev_name(child); - usb3v1.dev_name = dev_name(child); - } - } - - if (IS_ENABLED(CONFIG_TWL4030_WATCHDOG) && twl_class_is_4030()) { - child = add_child(TWL_MODULE_PM_RECEIVER, "twl4030_wdt", NULL, - 0, false, 0, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - if (IS_ENABLED(CONFIG_INPUT_TWL4030_PWRBUTTON) && twl_class_is_4030()) { - child = add_child(TWL_MODULE_PM_MASTER, "twl4030_pwrbutton", - NULL, 0, true, irq_base + 8 + 0, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - if (IS_ENABLED(CONFIG_MFD_TWL4030_AUDIO) && pdata->audio && - twl_class_is_4030()) { - child = add_child(TWL4030_MODULE_AUDIO_VOICE, "twl4030-audio", - pdata->audio, sizeof(*pdata->audio), - false, 0, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - /* twl4030 regulators */ - if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && twl_class_is_4030()) { - child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator(TWL4030_REG_VIO, pdata->vio, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator(TWL4030_REG_VDD1, pdata->vdd1, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator(TWL4030_REG_VDD2, pdata->vdd2, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator(TWL4030_REG_VMMC1, pdata->vmmc1, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator(TWL4030_REG_VDAC, pdata->vdac, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator((features & TWL4030_VAUX2) - ? TWL4030_REG_VAUX2_4030 - : TWL4030_REG_VAUX2, - pdata->vaux2, features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator(TWL4030_REG_VINTANA1, pdata->vintana1, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator(TWL4030_REG_VINTANA2, pdata->vintana2, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator(TWL4030_REG_VINTDIG, pdata->vintdig, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - /* maybe add LDOs that are omitted on cost-reduced parts */ - if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && !(features & TPS_SUBSET) - && twl_class_is_4030()) { - child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator(TWL4030_REG_VMMC2, pdata->vmmc2, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator(TWL4030_REG_VSIM, pdata->vsim, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator(TWL4030_REG_VAUX1, pdata->vaux1, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator(TWL4030_REG_VAUX3, pdata->vaux3, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator(TWL4030_REG_VAUX4, pdata->vaux4, - features); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - if (IS_ENABLED(CONFIG_CHARGER_TWL4030) && pdata->bci && - !(features & (TPS_SUBSET | TWL5031))) { - child = add_child(TWL_MODULE_MAIN_CHARGE, "twl4030_bci", - pdata->bci, sizeof(*pdata->bci), false, - /* irq0 = CHG_PRES, irq1 = BCI */ - irq_base + BCI_PRES_INTR_OFFSET, - irq_base + BCI_INTR_OFFSET); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata->power) { - child = add_child(TWL_MODULE_PM_MASTER, "twl4030_power", - pdata->power, sizeof(*pdata->power), false, - 0, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - return 0; -} - /*----------------------------------------------------------------------*/ /* @@ -987,8 +629,7 @@ static inline int unprotect_pm_master(void) return e; } -static void clocks_init(struct device *dev, - struct twl4030_clock_init_data *clock) +static void clocks_init(struct device *dev) { int e = 0; struct clk *osc; @@ -1018,8 +659,6 @@ static void clocks_init(struct device *dev, } ctrl |= HIGH_PERF_SQ; - if (clock && clock->ck32k_lowpwr_enable) - ctrl |= CK32K_LOWPWR_EN; e |= unprotect_pm_master(); /* effect->MADC+USB ck en */ @@ -1033,18 +672,14 @@ static void clocks_init(struct device *dev, /*----------------------------------------------------------------------*/ -static int twl_remove(struct i2c_client *client) +static void twl_remove(struct i2c_client *client) { unsigned i, num_slaves; - int status; if (twl_class_is_4030()) - status = twl4030_exit_irq(); + twl4030_exit_irq(); else - status = twl6030_exit_irq(); - - if (status < 0) - return status; + twl6030_exit_irq(); num_slaves = twl_get_num_slaves(); for (i = 0; i < num_slaves; i++) { @@ -1055,19 +690,40 @@ static int twl_remove(struct i2c_client *client) twl->client = NULL; } twl_priv->ready = false; - return 0; } +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, const struct i2c_device_id *id) +twl_probe(struct i2c_client *client) { - struct twl4030_platform_data *pdata = dev_get_platdata(&client->dev); + const struct i2c_device_id *id = i2c_client_get_device_id(client); struct device_node *node = client->dev.of_node; struct platform_device *pdev; const struct regmap_config *twl_regmap_config; @@ -1075,7 +731,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) int status; unsigned i, num_slaves; - if (!node && !pdata) { + if (!node) { dev_err(&client->dev, "no platform data\n"); return -EINVAL; } @@ -1114,10 +770,6 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) if ((id->driver_data) & TWL6030_CLASS) { twl_priv->twl_id = TWL6030_CLASS_ID; twl_priv->twl_map = &twl6030_map[0]; - /* The charger base address is different in twl6032 */ - if ((id->driver_data) & TWL6032_SUBCLASS) - twl_priv->twl_map[TWL_MODULE_MAIN_CHARGE].base = - TWL6032_BASEADD_CHARGER; twl_regmap_config = twl6030_regmap_config; } else { twl_priv->twl_id = TWL4030_CLASS_ID; @@ -1165,7 +817,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) twl_priv->ready = true; /* setup clock framework */ - clocks_init(&client->dev, pdata ? pdata->clock : NULL); + clocks_init(&client->dev); /* read TWL IDCODE Register */ if (twl_class_is_4030()) { @@ -1194,7 +846,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) * SR_I2C_SCL_CTRL_PU(bit 4)=0 and SR_I2C_SDA_CTRL_PU(bit 6)=0. * * Also, always enable SmartReflex bit as that's needed for omaps to - * to do anything over I2C4 for voltage scaling even if SmartReflex + * do anything over I2C4 for voltage scaling even if SmartReflex * is disabled. Without the SmartReflex bit omap sys_clkreq idle * signal will never trigger for retention idle. */ @@ -1213,15 +865,34 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) TWL4030_DCDC_GLOBAL_CFG); } - if (node) { - if (pdata) - twl_auxdata_lookup[0].platform_data = pdata->gpio; - status = of_platform_populate(node, NULL, twl_auxdata_lookup, - &client->dev); - } else { - status = add_children(pdata, irq_base, id->driver_data); + 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); + fail: if (status < 0) twl_remove(client); diff --git a/drivers/mfd/twl-core.h b/drivers/mfd/twl-core.h index 6f96c2009a9f..b4bf6a233bd0 100644 --- a/drivers/mfd/twl-core.h +++ b/drivers/mfd/twl-core.h @@ -3,9 +3,9 @@ #define __TWL_CORE_H__ extern int twl6030_init_irq(struct device *dev, int irq_num); -extern int twl6030_exit_irq(void); +extern void twl6030_exit_irq(void); extern int twl4030_init_irq(struct device *dev, int irq_num); -extern int twl4030_exit_irq(void); +extern void twl4030_exit_irq(void); extern int twl4030_init_chip_irq(const char *chip); #endif /* __TWL_CORE_H__ */ diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c index 4536d829b43e..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[] = { @@ -285,5 +283,4 @@ module_platform_driver(twl4030_audio_driver); MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); MODULE_DESCRIPTION("TWL4030 audio block MFD driver"); -MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:twl4030-audio"); diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index ab417438d1fa..d3ab40651307 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -14,6 +14,7 @@ * by syed khasim <x0khasim@ti.com> */ +#include <linux/device.h> #include <linux/export.h> #include <linux/interrupt.h> #include <linux/irq.h> @@ -675,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 @@ -690,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; @@ -753,14 +753,11 @@ fail: return status; } -int twl4030_exit_irq(void) +void twl4030_exit_irq(void) { /* FIXME undo twl_init_irq() */ - if (twl4030_irq_base) { + if (twl4030_irq_base) pr_err("twl4030: can't yet clean up IRQs?\n"); - return -ENOSYS; - } - return 0; } int twl4030_init_chip_irq(const char *chip) diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index 6b36932263ba..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); @@ -953,18 +953,12 @@ relock: return err; } -static int twl4030_power_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver twl4030_power_driver = { .driver = { .name = "twl4030_power", .of_match_table = of_match_ptr(twl4030_power_of_match), }, .probe = twl4030_power_probe, - .remove = twl4030_power_remove, }; module_platform_driver(twl4030_power_driver); diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index 97af6c2a6007..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, ®_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, ®_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; @@ -438,7 +362,7 @@ fail_irq: return status; } -int twl6030_exit_irq(void) +void twl6030_exit_irq(void) { if (twl6030_irq && twl6030_irq->twl_irq) { unregister_pm_notifier(&twl6030_irq->pm_nb); @@ -453,6 +377,5 @@ int twl6030_exit_irq(void) * in this module. */ } - return 0; } diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index b9c6d94b4002..562a0f939f6e 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -16,10 +16,7 @@ #include <linux/err.h> #include <linux/platform_device.h> #include <linux/of.h> -#include <linux/of_irq.h> -#include <linux/of_gpio.h> -#include <linux/of_platform.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/regmap.h> @@ -72,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 @@ -251,7 +248,7 @@ static int twl6040_power_up_automatic(struct twl6040 *twl6040) { int time_left; - gpio_set_value(twl6040->audpwron, 1); + gpiod_set_value_cansleep(twl6040->audpwron, 1); time_left = wait_for_completion_timeout(&twl6040->ready, msecs_to_jiffies(144)); @@ -262,7 +259,7 @@ static int twl6040_power_up_automatic(struct twl6040 *twl6040) intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); if (!(intid & TWL6040_READYINT)) { dev_err(twl6040->dev, "automatic power-up failed\n"); - gpio_set_value(twl6040->audpwron, 0); + gpiod_set_value_cansleep(twl6040->audpwron, 0); return -ETIMEDOUT; } } @@ -290,7 +287,7 @@ int twl6040_power(struct twl6040 *twl6040, int on) /* Allow writes to the chip */ regcache_cache_only(twl6040->regmap, false); - if (gpio_is_valid(twl6040->audpwron)) { + if (twl6040->audpwron) { /* use automatic power-up sequence */ ret = twl6040_power_up_automatic(twl6040); if (ret) { @@ -337,9 +334,9 @@ int twl6040_power(struct twl6040 *twl6040, int on) if (--twl6040->power_count) goto out; - if (gpio_is_valid(twl6040->audpwron)) { + if (twl6040->audpwron) { /* use AUDPWRON line */ - gpio_set_value(twl6040->audpwron, 0); + gpiod_set_value_cansleep(twl6040->audpwron, 0); /* power-down sequence latency */ usleep_range(500, 700); @@ -609,7 +606,7 @@ static const struct regmap_config twl6040_regmap_config = { .volatile_reg = twl6040_volatile_reg, .writeable_reg = twl6040_writeable_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -623,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), @@ -633,8 +630,7 @@ static struct regmap_irq_chip twl6040_irq_chip = { .mask_base = TWL6040_REG_INTMR, }; -static int twl6040_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int twl6040_probe(struct i2c_client *client) { struct device_node *node = client->dev.of_node; struct twl6040 *twl6040; @@ -712,18 +708,16 @@ static int twl6040_probe(struct i2c_client *client, } /* ERRATA: Automatic power-up is not possible in ES1.0 */ - if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) - twl6040->audpwron = of_get_named_gpio(node, - "ti,audpwron-gpio", 0); - else - twl6040->audpwron = -EINVAL; - - if (gpio_is_valid(twl6040->audpwron)) { - ret = devm_gpio_request_one(&client->dev, twl6040->audpwron, - GPIOF_OUT_INIT_LOW, "audpwron"); + if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) { + twl6040->audpwron = devm_gpiod_get_optional(&client->dev, + "ti,audpwron", + GPIOD_OUT_LOW); + ret = PTR_ERR_OR_ZERO(twl6040->audpwron); if (ret) goto gpio_err; + gpiod_set_consumer_name(twl6040->audpwron, "audpwron"); + /* Clear any pending interrupt */ twl6040_reg_read(twl6040, TWL6040_REG_INTID); } @@ -808,7 +802,7 @@ gpio_err: return ret; } -static int twl6040_remove(struct i2c_client *client) +static void twl6040_remove(struct i2c_client *client) { struct twl6040 *twl6040 = i2c_get_clientdata(client); @@ -820,14 +814,12 @@ static int twl6040_remove(struct i2c_client *client) mfd_remove_devices(&client->dev); regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies); - - return 0; } static const struct i2c_device_id twl6040_i2c_id[] = { - { "twl6040", 0, }, - { "twl6041", 0, }, - { }, + { "twl6040" }, + { "twl6041" }, + { } }; MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id); @@ -845,4 +837,3 @@ module_i2c_driver(twl6040_driver); MODULE_DESCRIPTION("TWL6040 MFD"); MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>"); MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c deleted file mode 100644 index 8c3832a58ef6..000000000000 --- a/drivers/mfd/ucb1400_core.c +++ /dev/null @@ -1,160 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Core functions for: - * Philips UCB1400 multifunction chip - * - * Based on ucb1400_ts.c: - * Author: Nicolas Pitre - * Created: September 25, 2006 - * Copyright: MontaVista Software, Inc. - * - * Spliting done by: Marek Vasut <marek.vasut@gmail.com> - * If something doesn't work and it worked before spliting, e-mail me, - * dont bother Nicolas please ;-) - * - * This code is heavily based on ucb1x00-*.c copyrighted by Russell King - * covering the UCB1100, UCB1200 and UCB1300.. Support for the UCB1400 has - * been made separate from ucb1x00-core/ucb1x00-ts on Russell's request. - */ - -#include <linux/module.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/ucb1400.h> - -unsigned int ucb1400_adc_read(struct snd_ac97 *ac97, u16 adc_channel, - int adcsync) -{ - unsigned int val; - - if (adcsync) - adc_channel |= UCB_ADC_SYNC_ENA; - - ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA | adc_channel); - ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA | adc_channel | - UCB_ADC_START); - - while (!((val = ucb1400_reg_read(ac97, UCB_ADC_DATA)) - & UCB_ADC_DAT_VALID)) - schedule_timeout_uninterruptible(1); - - return val & UCB_ADC_DAT_MASK; -} -EXPORT_SYMBOL_GPL(ucb1400_adc_read); - -static int ucb1400_core_probe(struct device *dev) -{ - int err; - struct ucb1400 *ucb; - struct ucb1400_ts ucb_ts; - struct ucb1400_gpio ucb_gpio; - struct snd_ac97 *ac97; - struct ucb1400_pdata *pdata = dev_get_platdata(dev); - - memset(&ucb_ts, 0, sizeof(ucb_ts)); - memset(&ucb_gpio, 0, sizeof(ucb_gpio)); - - ucb = kzalloc(sizeof(struct ucb1400), GFP_KERNEL); - if (!ucb) { - err = -ENOMEM; - goto err; - } - - dev_set_drvdata(dev, ucb); - - ac97 = to_ac97_t(dev); - - ucb_ts.id = ucb1400_reg_read(ac97, UCB_ID); - if (ucb_ts.id != UCB_ID_1400) { - err = -ENODEV; - goto err0; - } - - /* GPIO */ - ucb_gpio.ac97 = ac97; - if (pdata) { - ucb_gpio.gpio_setup = pdata->gpio_setup; - ucb_gpio.gpio_teardown = pdata->gpio_teardown; - ucb_gpio.gpio_offset = pdata->gpio_offset; - } - ucb->ucb1400_gpio = platform_device_alloc("ucb1400_gpio", -1); - if (!ucb->ucb1400_gpio) { - err = -ENOMEM; - goto err0; - } - err = platform_device_add_data(ucb->ucb1400_gpio, &ucb_gpio, - sizeof(ucb_gpio)); - if (err) - goto err1; - err = platform_device_add(ucb->ucb1400_gpio); - if (err) - goto err1; - - /* TOUCHSCREEN */ - ucb_ts.ac97 = ac97; - - if (pdata != NULL && pdata->irq >= 0) - ucb_ts.irq = pdata->irq; - else - ucb_ts.irq = -1; - - ucb->ucb1400_ts = platform_device_alloc("ucb1400_ts", -1); - if (!ucb->ucb1400_ts) { - err = -ENOMEM; - goto err2; - } - err = platform_device_add_data(ucb->ucb1400_ts, &ucb_ts, - sizeof(ucb_ts)); - if (err) - goto err3; - err = platform_device_add(ucb->ucb1400_ts); - if (err) - goto err3; - - return 0; - -err3: - platform_device_put(ucb->ucb1400_ts); -err2: - platform_device_del(ucb->ucb1400_gpio); -err1: - platform_device_put(ucb->ucb1400_gpio); -err0: - kfree(ucb); -err: - return err; -} - -static int ucb1400_core_remove(struct device *dev) -{ - struct ucb1400 *ucb = dev_get_drvdata(dev); - - platform_device_unregister(ucb->ucb1400_ts); - platform_device_unregister(ucb->ucb1400_gpio); - - kfree(ucb); - return 0; -} - -static struct device_driver ucb1400_core_driver = { - .name = "ucb1400_core", - .bus = &ac97_bus_type, - .probe = ucb1400_core_probe, - .remove = ucb1400_core_remove, -}; - -static int __init ucb1400_core_init(void) -{ - return driver_register(&ucb1400_core_driver); -} - -static void __exit ucb1400_core_exit(void) -{ - driver_unregister(&ucb1400_core_driver); -} - -module_init(ucb1400_core_init); -module_exit(ucb1400_core_exit); - -MODULE_DESCRIPTION("Philips UCB1400 driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index b690796d24d4..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) @@ -660,7 +663,6 @@ void ucb1x00_unregister_driver(struct ucb1x00_driver *drv) mutex_unlock(&ucb1x00_mutex); } -#ifdef CONFIG_PM_SLEEP static int ucb1x00_suspend(struct device *dev) { struct ucb1x00_plat_data *pdata = dev_get_platdata(dev); @@ -728,15 +730,15 @@ static int ucb1x00_resume(struct device *dev) mutex_unlock(&ucb1x00_mutex); return 0; } -#endif -static SIMPLE_DEV_PM_OPS(ucb1x00_pm_ops, ucb1x00_suspend, ucb1x00_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(ucb1x00_pm_ops, + ucb1x00_suspend, ucb1x00_resume); static struct mcp_driver ucb1x00_driver = { .drv = { .name = "ucb1x00", .owner = THIS_MODULE, - .pm = &ucb1x00_pm_ops, + .pm = pm_sleep_ptr(&ucb1x00_pm_ops), }, .probe = ucb1x00_probe, .remove = ucb1x00_remove, 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 aaf24af287dd..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[] = { @@ -61,43 +74,34 @@ static struct mfd_cell vexpress_sysreg_cells[] = { .name = "basic-mmio-gpio", .of_compatible = "arm,vexpress-sysreg,sys_led", .num_resources = 1, - .resources = (struct resource []) { - DEFINE_RES_MEM_NAMED(SYS_LED, 0x4, "dat"), - }, - .platform_data = &vexpress_sysreg_sys_led_pdata, - .pdata_size = sizeof(vexpress_sysreg_sys_led_pdata), + .resources = &DEFINE_RES_MEM_NAMED(SYS_LED, 0x4, "dat"), + .swnode = &vexpress_sysreg_sys_led_swnode, }, { .name = "basic-mmio-gpio", .of_compatible = "arm,vexpress-sysreg,sys_mci", .num_resources = 1, - .resources = (struct resource []) { - DEFINE_RES_MEM_NAMED(SYS_MCI, 0x4, "dat"), - }, - .platform_data = &vexpress_sysreg_sys_mci_pdata, - .pdata_size = sizeof(vexpress_sysreg_sys_mci_pdata), + .resources = &DEFINE_RES_MEM_NAMED(SYS_MCI, 0x4, "dat"), + .swnode = &vexpress_sysreg_sys_mci_swnode, }, { .name = "basic-mmio-gpio", .of_compatible = "arm,vexpress-sysreg,sys_flash", .num_resources = 1, - .resources = (struct resource []) { - DEFINE_RES_MEM_NAMED(SYS_FLASH, 0x4, "dat"), - }, - .platform_data = &vexpress_sysreg_sys_flash_pdata, - .pdata_size = sizeof(vexpress_sysreg_sys_flash_pdata), + .resources = &DEFINE_RES_MEM_NAMED(SYS_FLASH, 0x4, "dat"), + .swnode = &vexpress_sysreg_sys_flash_swnode, }, { .name = "vexpress-syscfg", .num_resources = 1, - .resources = (struct resource []) { - DEFINE_RES_MEM(SYS_MISC, 0x4c), - }, + .resources = &DEFINE_RES_MEM(SYS_MISC, 0x4c), } }; 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) @@ -115,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, @@ -140,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 aa19a6a4fdbf..3c3080e8c8cf 100644 --- a/drivers/mfd/wcd934x.c +++ b/drivers/mfd/wcd934x.c @@ -2,14 +2,13 @@ // Copyright (c) 2019, Linaro Limited #include <linux/clk.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/mfd/core.h> #include <linux/mfd/wcd934x/registers.h> #include <linux/mfd/wcd934x/wcd934x.h> #include <linux/module.h> -#include <linux/of_gpio.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/platform_device.h> @@ -56,17 +55,22 @@ static const struct regmap_irq wcd934x_irqs[] = { WCD934X_REGMAP_IRQ_REG(WCD934X_IRQ_SOUNDWIRE, 2, BIT(4)), }; +static const unsigned int wcd934x_config_regs[] = { + WCD934X_INTR_LEVEL0, +}; + static const struct regmap_irq_chip wcd934x_regmap_irq_chip = { .name = "wcd934x_irq", .status_base = WCD934X_INTR_PIN1_STATUS0, .mask_base = WCD934X_INTR_PIN1_MASK0, .ack_base = WCD934X_INTR_PIN1_CLEAR0, - .type_base = WCD934X_INTR_LEVEL0, - .num_type_reg = 4, - .type_in_mask = false, .num_regs = 4, .irqs = wcd934x_irqs, .num_irqs = ARRAY_SIZE(wcd934x_irqs), + .config_base = wcd934x_config_regs, + .num_config_bases = ARRAY_SIZE(wcd934x_config_regs), + .num_config_regs = 4, + .set_type_config = regmap_irq_set_type_config_simple, }; static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg) @@ -105,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, @@ -210,7 +214,8 @@ static int wcd934x_slim_probe(struct slim_device *sdev) struct device *dev = &sdev->dev; struct device_node *np = dev->of_node; struct wcd934x_ddata *ddata; - int reset_gpio, ret; + struct gpio_desc *reset_gpio; + int ret; ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) @@ -221,18 +226,10 @@ static int wcd934x_slim_probe(struct slim_device *sdev) return dev_err_probe(ddata->dev, ddata->irq, "Failed to get IRQ\n"); - reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); - if (reset_gpio < 0) { - dev_err(dev, "Failed to get reset gpio: err = %d\n", - reset_gpio); - return reset_gpio; - } - ddata->extclk = devm_clk_get(dev, "extclk"); - if (IS_ERR(ddata->extclk)) { - dev_err(dev, "Failed to get extclk"); - return PTR_ERR(ddata->extclk); - } + if (IS_ERR(ddata->extclk)) + return dev_err_probe(dev, PTR_ERR(ddata->extclk), + "Failed to get extclk"); ddata->supplies[0].supply = "vdd-buck"; ddata->supplies[1].supply = "vdd-buck-sido"; @@ -241,16 +238,12 @@ static int wcd934x_slim_probe(struct slim_device *sdev) ddata->supplies[4].supply = "vdd-io"; ret = regulator_bulk_get(dev, WCD934X_MAX_SUPPLY, ddata->supplies); - if (ret) { - dev_err(dev, "Failed to get supplies: err = %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to get supplies\n"); ret = regulator_bulk_enable(WCD934X_MAX_SUPPLY, ddata->supplies); - if (ret) { - dev_err(dev, "Failed to enable supplies: err = %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to enable supplies\n"); /* * For WCD934X, it takes about 600us for the Vout_A and @@ -258,15 +251,24 @@ static int wcd934x_slim_probe(struct slim_device *sdev) * SYS_RST_N shouldn't be pulled high during this time */ usleep_range(600, 650); - gpio_direction_output(reset_gpio, 0); + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) { + ret = dev_err_probe(dev, PTR_ERR(reset_gpio), + "Failed to get reset gpio\n"); + goto err_disable_regulators; + } msleep(20); - gpio_set_value(reset_gpio, 1); + gpiod_set_value(reset_gpio, 1); msleep(20); ddata->dev = dev; dev_set_drvdata(dev, ddata); return 0; + +err_disable_regulators: + regulator_bulk_disable(WCD934X_MAX_SUPPLY, ddata->supplies); + return ret; } static void wcd934x_slim_remove(struct slim_device *sdev) @@ -282,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 = { @@ -296,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 1ab5e15a65eb..000000000000 --- a/drivers/mfd/wl1273-core.c +++ /dev/null @@ -1,263 +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, - const struct i2c_device_id *id) -{ - 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 d2f444d2ae78..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> @@ -1430,7 +1429,7 @@ struct regmap_config wm831x_regmap_config = { .reg_bits = 16, .val_bits = 16, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .max_register = WM831X_DBE_CHECK_DATA, .readable_reg = wm831x_reg_readable, diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index daa1ad036595..9bee007f9c99 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -15,30 +15,22 @@ #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> #include <linux/mfd/wm831x/pdata.h> -static int wm831x_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm831x_i2c_probe(struct i2c_client *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 48fd46800c28..767c176b12a7 100644 --- a/drivers/mfd/wm8350-i2c.c +++ b/drivers/mfd/wm8350-i2c.c @@ -16,8 +16,7 @@ #include <linux/regmap.h> #include <linux/slab.h> -static int wm8350_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8350_i2c_probe(struct i2c_client *i2c) { struct wm8350 *wm8350; struct wm8350_platform_data *pdata = dev_get_platdata(&i2c->dev); @@ -42,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 0fe32a05421b..8ecfe878a5ba 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -54,8 +54,6 @@ static int wm8400_init(struct wm8400 *wm8400, unsigned int reg; int ret; - dev_set_drvdata(wm8400->dev, wm8400); - /* Check that this is actually a WM8400 */ ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, ®); if (ret != 0) { @@ -102,7 +100,7 @@ static const struct regmap_config wm8400_regmap_config = { .volatile_reg = wm8400_volatile, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; /** @@ -118,8 +116,7 @@ void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400) EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache); #if IS_ENABLED(CONFIG_I2C) -static int wm8400_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8400_i2c_probe(struct i2c_client *i2c) { struct wm8400 *wm8400; @@ -138,7 +135,7 @@ static int wm8400_i2c_probe(struct i2c_client *i2c, } static const struct i2c_device_id wm8400_i2c_id[] = { - { "wm8400", 0 }, + { "wm8400" }, { } }; @@ -146,7 +143,7 @@ static struct i2c_driver wm8400_i2c_driver = { .driver = { .name = "WM8400", }, - .probe = wm8400_i2c_probe, + .probe = wm8400_i2c_probe, .id_table = wm8400_i2c_id, }; #endif diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 7b1d270722ba..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> @@ -110,7 +109,6 @@ static const char *wm8958_main_supplies[] = { "SPKVDD2", }; -#ifdef CONFIG_PM static int wm8994_suspend(struct device *dev) { struct wm8994 *wm8994 = dev_get_drvdata(dev); @@ -213,7 +211,6 @@ err_enable: return ret; } -#endif #ifdef CONFIG_REGULATOR static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo) @@ -281,20 +278,11 @@ static int wm8994_set_pdata_from_of(struct wm8994 *wm8994) of_property_read_u32_array(np, "wlf,micbias-cfg", pdata->micbias, ARRAY_SIZE(pdata->micbias)); - pdata->lineout1_diff = true; - pdata->lineout2_diff = true; - if (of_find_property(np, "wlf,lineout1-se", NULL)) - pdata->lineout1_diff = false; - if (of_find_property(np, "wlf,lineout2-se", NULL)) - pdata->lineout2_diff = false; - - if (of_find_property(np, "wlf,lineout1-feedback", NULL)) - pdata->lineout1fb = true; - if (of_find_property(np, "wlf,lineout2-feedback", NULL)) - pdata->lineout2fb = true; - - if (of_find_property(np, "wlf,ldoena-always-driven", NULL)) - pdata->lineout2fb = true; + pdata->lineout1_diff = !of_property_read_bool(np, "wlf,lineout1-se"); + pdata->lineout2_diff = !of_property_read_bool(np, "wlf,lineout2-se"); + pdata->lineout1fb = of_property_read_bool(np, "wlf,lineout1-feedback"); + pdata->lineout2fb = of_property_read_bool(np, "wlf,lineout2-feedback") || + of_property_read_bool(np, "wlf,ldoena-always-driven"); pdata->spkmode_pu = of_property_read_bool(np, "wlf,spkmode-pu"); @@ -331,8 +319,6 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) if (ret != 0) return ret; - dev_set_drvdata(wm8994->dev, wm8994); - /* Add the on-chip regulators first for bootstrapping */ ret = mfd_add_devices(wm8994->dev, 0, wm8994_regulator_devs, @@ -623,10 +609,8 @@ static const struct of_device_id wm8994_of_match[] = { }; MODULE_DEVICE_TABLE(of, wm8994_of_match); -static int wm8994_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8994_i2c_probe(struct i2c_client *i2c) { - const struct of_device_id *of_id; struct wm8994 *wm8994; int ret; @@ -638,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)) { @@ -657,13 +635,11 @@ static int wm8994_i2c_probe(struct i2c_client *i2c, return wm8994_device_init(wm8994, i2c->irq); } -static int wm8994_i2c_remove(struct i2c_client *i2c) +static void wm8994_i2c_remove(struct i2c_client *i2c) { struct wm8994 *wm8994 = i2c_get_clientdata(i2c); wm8994_device_exit(wm8994); - - return 0; } static const struct i2c_device_id wm8994_i2c_id[] = { @@ -676,13 +652,13 @@ static const struct i2c_device_id wm8994_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, wm8994_i2c_id); static const struct dev_pm_ops wm8994_pm_ops = { - SET_RUNTIME_PM_OPS(wm8994_suspend, wm8994_resume, NULL) + RUNTIME_PM_OPS(wm8994_suspend, wm8994_resume, NULL) }; static struct i2c_driver wm8994_i2c_driver = { .driver = { .name = "wm8994", - .pm = &wm8994_pm_ops, + .pm = pm_ptr(&wm8994_pm_ops), .of_match_table = wm8994_of_match, }, .probe = wm8994_i2c_probe, diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c index 6c3a619e2628..1475b1ac6983 100644 --- a/drivers/mfd/wm8994-irq.c +++ b/drivers/mfd/wm8994-irq.c @@ -154,7 +154,7 @@ static irqreturn_t wm8994_edge_irq(int irq, void *data) struct wm8994 *wm8994 = data; while (gpio_get_value_cansleep(wm8994->pdata.irq_gpio)) - handle_nested_irq(irq_create_mapping(wm8994->edge_irq, 0)); + handle_nested_irq(irq_find_mapping(wm8994->edge_irq, 0)); return IRQ_HANDLED; } @@ -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 9a2331eb1bfa..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), @@ -319,13 +319,11 @@ err_free_compat: return ret; } -static int wm97xx_ac97_remove(struct ac97_codec_device *adev) +static void wm97xx_ac97_remove(struct ac97_codec_device *adev) { struct wm97xx_priv *wm97xx = ac97_get_drvdata(adev); snd_ac97_compat_release(wm97xx->ac97); - - return 0; } static const struct ac97_id wm97xx_ac97_ids[] = { |
