summaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2020-10-14 15:56:58 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2020-10-14 15:56:58 -0700
commit1a31c12371556bfbe511edd268dab721b504d511 (patch)
tree862c9a1cf5fb3a95422c4f3db9650e84cd04b635 /drivers/mfd
parentf888bdf9823c85fe945c4eb3ba353f749dec3856 (diff)
parent6d81dc3c79d46b66b29712eb1ac5ad2cbe4231d2 (diff)
downloadlinux-rpi-1a31c12371556bfbe511edd268dab721b504d511.tar.gz
linux-rpi-1a31c12371556bfbe511edd268dab721b504d511.tar.bz2
linux-rpi-1a31c12371556bfbe511edd268dab721b504d511.zip
Merge tag 'mfd-next-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
Pull MFD updates from Lee Jones: "New Drivers: - Add support for initialising shared (between children) Regmaps - Add support for Kontron SL28CPLD - Add support for ENE KB3930 Embedded Controller - Add support for Intel FPGA PAC MAX 10 BMC New Device Support: - Add support for Power to Ricoh RN5T618 - Add support for UART to Intel Lakefield - Add support for LP87524_Q1 to Texas Instruments LP87565 New Functionality: - Device Tree; ene-kb3930, sl28cpld, syscon, lp87565, lp87524-q1 - Use new helper dev_err_probe(); madera-core, stmfx, wcd934x - Use new GPIOD API; dm355evm_msp - Add wake-up capability; sprd-sc27xx-spi - Add ACPI support; kempld-core Fix-ups: - Trivial (spelling/whitespace); Kconfig, ab8500 - Fix for unused variables; khadas-mcu, kempld-core - Remove unused header file(s); mt6360-core - Use correct IRQ flags in docs; act8945a, gateworks-gsc, rohm,bd70528-pmic - Add COMPILE_TEST support; asic3, tmio_core - Add dependency on I2C; SL28CPLD Bug Fixes: - Fix memory leak(s); sm501 - Do not free regmap_config's 'name' until exit; syscon" * tag 'mfd-next-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (34 commits) mfd: kempld-core: Fix unused variable 'kempld_acpi_table' when !ACPI mfd: sl28cpld: Depend on I2C mfd: asic3: Build if COMPILE_TEST=y dt-bindings: mfd: Correct interrupt flags in examples mfd: Add ACPI support to Kontron PLD driver mfd: intel-m10-bmc: Add Intel MAX 10 BMC chip support for Intel FPGA PAC mfd: lp87565: Add LP87524-Q1 variant dt-bindings: mfd: Add LP87524-Q1 dt-bindings: mfd: lp87565: Convert to yaml mfd: mt6360: Remove unused include <linux/version.h> mfd: sm501: Fix leaks in probe() mfd: syscon: Don't free allocated name for regmap_config dt-bindings: mfd: syscon: Document Exynos3 and Exynos5433 compatibles dt-bindings: mfd: syscon: Merge Samsung Exynos Sysreg bindings dt-bindings: mfd: ab8500: Remove weird Unicode characters mfd: sprd: Add wakeup capability for PMIC IRQ mfd: intel-lpss: Add device IDs for UART ports for Lakefield mfd: dm355evm_msp: Convert LEDs to GPIO descriptor table mfd: wcd934x: Simplify with dev_err_probe() mfd: stmfx: Simplify with dev_err_probe() ...
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig52
-rw-r--r--drivers/mfd/Makefile3
-rw-r--r--drivers/mfd/dm355evm_msp.c76
-rw-r--r--drivers/mfd/ene-kb3930.c212
-rw-r--r--drivers/mfd/intel-lpss-pci.c4
-rw-r--r--drivers/mfd/intel-m10-bmc.c164
-rw-r--r--drivers/mfd/kempld-core.c117
-rw-r--r--drivers/mfd/khadas-mcu.c2
-rw-r--r--drivers/mfd/lp87565.c4
-rw-r--r--drivers/mfd/madera-core.c11
-rw-r--r--drivers/mfd/mt6360-core.c1
-rw-r--r--drivers/mfd/rn5t618.c1
-rw-r--r--drivers/mfd/simple-mfd-i2c.c57
-rw-r--r--drivers/mfd/sm501.c8
-rw-r--r--drivers/mfd/sprd-sc27xx-spi.c28
-rw-r--r--drivers/mfd/stmfx.c8
-rw-r--r--drivers/mfd/syscon.c2
-rw-r--r--drivers/mfd/wcd934x.c9
18 files changed, 702 insertions, 57 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 33df0837ab41..8b99a13669bf 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -290,7 +290,8 @@ config MFD_CS47L92
config MFD_ASIC3
bool "Compaq ASIC3"
- depends on GPIOLIB && ARM
+ depends on GPIOLIB
+ depends on ARM || COMPILE_TEST
select MFD_CORE
help
This driver supports the ASIC3 multifunction chip found on many
@@ -398,6 +399,17 @@ config MFD_DLN2
etc. must be enabled in order to use the functionality of
the device.
+config MFD_ENE_KB3930
+ tristate "ENE KB3930 Embedded Controller support"
+ depends on I2C
+ depends on MACH_MMP3_DT || COMPILE_TEST
+ select MFD_CORE
+ help
+ This adds support for the power-off functionality and access to
+ the registers that control LEDS and USB port power on ENE KB3930
+ Embedded Controller. To use the LED functionality LEDS_ARIEL must
+ be enabled.
+
config MFD_EXYNOS_LPASS
tristate "Samsung Exynos SoC Low Power Audio Subsystem"
depends on ARCH_EXYNOS || COMPILE_TEST
@@ -493,7 +505,7 @@ config MFD_HI6421_PMIC
Add support for HiSilicon Hi6421 PMIC. Hi6421 includes multi-
functions, such as regulators, RTC, codec, Coulomb counter, etc.
This driver includes core APIs _only_. You have to select
- individul components like voltage regulators under corresponding
+ individual components like voltage regulators under corresponding
menus in order to enable them.
We communicate with the Hi6421 via memory-mapped I/O.
@@ -1162,6 +1174,29 @@ config MFD_SI476X_CORE
To compile this driver as a module, choose M here: the
module will be called si476x-core.
+config MFD_SIMPLE_MFD_I2C
+ tristate
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver creates a single register map with the intention for it
+ to be shared by all sub-devices.
+
+ Once the register map has been successfully initialised, any
+ sub-devices represented by child nodes in Device Tree will be
+ subsequently registered.
+
+config MFD_SL28CPLD
+ tristate "Kontron sl28cpld Board Management Controller"
+ depends on I2C
+ select MFD_SIMPLE_MFD_I2C
+ help
+ Say yes here to enable support for the Kontron sl28cpld board
+ management controller.
+
+ It can be found on the following boards:
+ * SMARC-sAL28
+
config MFD_SM501
tristate "Silicon Motion SM501"
depends on HAS_DMA
@@ -2118,5 +2153,18 @@ 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
+ 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.
+
endmenu
endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index a60e5f835283..1780019d2474 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o
obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o
obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.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
@@ -264,3 +265,5 @@ obj-$(CONFIG_MFD_STMFX) += stmfx.o
obj-$(CONFIG_MFD_KHADAS_MCU) += khadas-mcu.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
diff --git a/drivers/mfd/dm355evm_msp.c b/drivers/mfd/dm355evm_msp.c
index 151c36ce7343..54fb6cbd2aa0 100644
--- a/drivers/mfd/dm355evm_msp.c
+++ b/drivers/mfd/dm355evm_msp.c
@@ -12,6 +12,7 @@
#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>
@@ -116,6 +117,54 @@ static const u8 msp_gpios[] = {
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)
@@ -260,32 +309,7 @@ static int add_children(struct i2c_client *client)
/* LED output */
if (msp_has_leds()) {
-#define GPIO_LED(l) .name = l, .active_low = true
- static struct gpio_led evm_leds[] = {
- { GPIO_LED("dm355evm::ds14"),
- .default_trigger = "heartbeat", },
- { GPIO_LED("dm355evm::ds15"),
- .default_trigger = "mmc0", },
- { GPIO_LED("dm355evm::ds16"),
- /* could also be a CE-ATA drive */
- .default_trigger = "mmc1", },
- { GPIO_LED("dm355evm::ds17"),
- .default_trigger = "nand-disk", },
- { GPIO_LED("dm355evm::ds18"), },
- { GPIO_LED("dm355evm::ds19"), },
- { GPIO_LED("dm355evm::ds20"), },
- { GPIO_LED("dm355evm::ds21"), },
- };
-#undef GPIO_LED
-
- struct gpio_led_platform_data evm_led_data = {
- .num_leds = ARRAY_SIZE(evm_leds),
- .leds = evm_leds,
- };
-
- for (i = 0; i < ARRAY_SIZE(evm_leds); i++)
- evm_leds[i].gpio = i + dm355evm_msp_gpio.base;
-
+ 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
diff --git a/drivers/mfd/ene-kb3930.c b/drivers/mfd/ene-kb3930.c
new file mode 100644
index 000000000000..1c32ff586816
--- /dev/null
+++ b/drivers/mfd/ene-kb3930.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0-or-later
+/*
+ * ENE KB3930 Embedded Controller Driver
+ *
+ * Copyright (C) 2020 Lubomir Rintel
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+
+/* I2C registers that are multiplexing access to the EC RAM. */
+enum {
+ EC_DATA_IN = 0x00,
+ EC_RAM_OUT = 0x80,
+ EC_RAM_IN = 0x81,
+};
+
+/* EC RAM registers. */
+enum {
+ EC_MODEL = 0x30,
+ EC_VERSION_MAJ = 0x31,
+ EC_VERSION_MIN = 0x32,
+};
+
+struct kb3930 {
+ struct i2c_client *client;
+ struct regmap *ram_regmap;
+ struct gpio_descs *off_gpios;
+};
+
+struct kb3930 *kb3930_power_off;
+
+#define EC_GPIO_WAVE 0
+#define EC_GPIO_OFF_MODE 1
+
+#define EC_OFF_MODE_REBOOT 0
+#define EC_OFF_MODE_POWER 1
+
+static void kb3930_off(struct kb3930 *ddata, int off_mode)
+{
+ gpiod_direction_output(ddata->off_gpios->desc[EC_GPIO_OFF_MODE],
+ off_mode);
+
+ /*
+ * This creates a 10 Hz wave on EC_GPIO_WAVE that signals a
+ * shutdown request to the EC. Once the EC detects it, it will
+ * proceed to turn the power off or reset the board depending on
+ * the value of EC_GPIO_OFF_MODE.
+ */
+ while (1) {
+ mdelay(50);
+ gpiod_direction_output(ddata->off_gpios->desc[EC_GPIO_WAVE], 0);
+ mdelay(50);
+ gpiod_direction_output(ddata->off_gpios->desc[EC_GPIO_WAVE], 1);
+ }
+}
+
+static int kb3930_restart(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ kb3930_off(kb3930_power_off, EC_OFF_MODE_REBOOT);
+ return NOTIFY_DONE;
+}
+
+static void kb3930_pm_power_off(void)
+{
+ kb3930_off(kb3930_power_off, EC_OFF_MODE_POWER);
+}
+
+static struct notifier_block kb3930_restart_nb = {
+ .notifier_call = kb3930_restart,
+};
+
+static const struct mfd_cell ariel_ec_cells[] = {
+ { .name = "dell-wyse-ariel-led", },
+ { .name = "dell-wyse-ariel-power", },
+};
+
+static int kb3930_ec_ram_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct kb3930 *ddata = context;
+
+ return i2c_smbus_write_word_data(ddata->client, EC_RAM_OUT,
+ (val << 8) | reg);
+}
+
+static int kb3930_ec_ram_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct kb3930 *ddata = context;
+ int ret;
+
+ ret = i2c_smbus_write_word_data(ddata->client, EC_RAM_IN, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(ddata->client, EC_DATA_IN);
+ if (ret < 0)
+ return ret;
+
+ *val = ret >> 8;
+ return 0;
+}
+
+static const struct regmap_config kb3930_ram_regmap_config = {
+ .name = "ec_ram",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_stride = 1,
+ .max_register = 0xff,
+ .reg_write = kb3930_ec_ram_reg_write,
+ .reg_read = kb3930_ec_ram_reg_read,
+ .fast_io = false,
+};
+
+static int kb3930_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device_node *np = dev->of_node;
+ struct kb3930 *ddata;
+ unsigned int model;
+ int ret;
+
+ ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ kb3930_power_off = ddata;
+ ddata->client = client;
+ i2c_set_clientdata(client, ddata);
+
+ ddata->ram_regmap = devm_regmap_init(dev, NULL, ddata,
+ &kb3930_ram_regmap_config);
+ if (IS_ERR(ddata->ram_regmap))
+ return PTR_ERR(ddata->ram_regmap);
+
+ ret = regmap_read(ddata->ram_regmap, EC_MODEL, &model);
+ if (ret < 0)
+ return ret;
+
+ /* Currently we only support the cells present on Dell Ariel model. */
+ if (model != 'J') {
+ dev_err(dev, "unknown board model: %02x\n", model);
+ return -ENODEV;
+ }
+
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
+ ariel_ec_cells,
+ ARRAY_SIZE(ariel_ec_cells),
+ NULL, 0, NULL);
+ if (ret)
+ return ret;
+
+ if (of_property_read_bool(np, "system-power-controller")) {
+ ddata->off_gpios =
+ 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) {
+ dev_err(dev, "invalid off-gpios property\n");
+ return -EINVAL;
+ }
+ }
+
+ if (ddata->off_gpios) {
+ register_restart_handler(&kb3930_restart_nb);
+ if (!pm_power_off)
+ pm_power_off = kb3930_pm_power_off;
+ }
+
+ return 0;
+}
+
+static int kb3930_remove(struct i2c_client *client)
+{
+ struct kb3930 *ddata = i2c_get_clientdata(client);
+
+ if (ddata->off_gpios) {
+ if (pm_power_off == kb3930_pm_power_off)
+ pm_power_off = NULL;
+ unregister_restart_handler(&kb3930_restart_nb);
+ }
+ kb3930_power_off = NULL;
+
+ return 0;
+}
+
+static const struct of_device_id kb3930_dt_ids[] = {
+ { .compatible = "ene,kb3930" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, kb3930_dt_ids);
+
+static struct i2c_driver kb3930_driver = {
+ .probe_new = kb3930_probe,
+ .remove = kb3930_remove,
+ .driver = {
+ .name = "ene-kb3930",
+ .of_match_table = of_match_ptr(kb3930_dt_ids),
+ },
+};
+module_i2c_driver(kb3930_driver);
+
+MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
+MODULE_DESCRIPTION("ENE KB3930 Embedded Controller Driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c
index 9a58032f818a..2d7c588ef1ed 100644
--- a/drivers/mfd/intel-lpss-pci.c
+++ b/drivers/mfd/intel-lpss-pci.c
@@ -293,6 +293,10 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x5ac4), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x5ac6), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x5aee), (kernel_ulong_t)&bxt_uart_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, 0x98c7), (kernel_ulong_t)&bxt_uart_info },
/* SPT-LP */
{ PCI_VDEVICE(INTEL, 0x9d27), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x9d28), (kernel_ulong_t)&spt_uart_info },
diff --git a/drivers/mfd/intel-m10-bmc.c b/drivers/mfd/intel-m10-bmc.c
new file mode 100644
index 000000000000..b84579b7b4f0
--- /dev/null
+++ b/drivers/mfd/intel-m10-bmc.c
@@ -0,0 +1,164 @@
+// 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,
+};
+
+static struct mfd_cell m10bmc_pacn3000_subdevs[] = {
+ { .name = "n3000bmc-hwmon" },
+ { .name = "n3000bmc-retimer" },
+ { .name = "n3000bmc-secure" },
+};
+
+static struct regmap_config intel_m10bmc_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .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 struct attribute *m10bmc_attrs[] = {
+ &dev_attr_bmc_version.attr,
+ &dev_attr_bmcfw_version.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,
+ * M10BMC_LEGACY_SYS_BASE is the offset to this old block of mmio
+ * registers. In the old BMC chips, the BMC version info is stored
+ * in this old version register (M10BMC_LEGACY_SYS_BASE +
+ * M10BMC_BUILD_VER), so its read out value would have not been
+ * LEGACY_INVALID (0xffffffff). But in new BMC chips that the
+ * driver supports, the value of this register should be
+ * LEGACY_INVALID.
+ */
+ ret = m10bmc_raw_read(ddata,
+ M10BMC_LEGACY_SYS_BASE + M10BMC_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;
+ 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 },
+ { }
+};
+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/kempld-core.c b/drivers/mfd/kempld-core.c
index 52bec01149e5..2c9295953c11 100644
--- a/drivers/mfd/kempld-core.c
+++ b/drivers/mfd/kempld-core.c
@@ -13,6 +13,7 @@
#include <linux/dmi.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/acpi.h>
#define MAX_ID_LEN 4
static char force_device_id[MAX_ID_LEN + 1] = "";
@@ -124,6 +125,7 @@ static const struct kempld_platform_data kempld_platform_data_generic = {
};
static struct platform_device *kempld_pdev;
+static bool kempld_acpi_mode;
static int kempld_create_platform_device(const struct dmi_system_id *id)
{
@@ -426,13 +428,93 @@ static int kempld_detect_device(struct kempld_device_data *pld)
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;
+}
+#endif /* CONFIG_ACPI */
+
static int kempld_probe(struct platform_device *pdev)
{
- const struct kempld_platform_data *pdata =
- dev_get_platdata(&pdev->dev);
+ const struct kempld_platform_data *pdata;
struct device *dev = &pdev->dev;
struct kempld_device_data *pld;
struct resource *ioport;
+ int ret;
+
+ if (kempld_pdev == NULL) {
+ /*
+ * 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);
+ if (ret)
+ return ret;
+
+ kempld_acpi_mode = true;
+ } else if (kempld_pdev != pdev) {
+ /*
+ * The platform device we are probing is not the one we
+ * registered in kempld_init using the DMI table, so this one
+ * comes from ACPI.
+ * As we can only probe one - abort here and use the DMI
+ * based one instead.
+ */
+ 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)
@@ -471,9 +553,19 @@ static int kempld_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id kempld_acpi_table[] = {
+ { "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),
+ .probe_type = PROBE_FORCE_SYNCHRONOUS,
},
.probe = kempld_probe,
.remove = kempld_remove,
@@ -792,6 +884,7 @@ MODULE_DEVICE_TABLE(dmi, kempld_dmi_table);
static int __init kempld_init(void)
{
const struct dmi_system_id *id;
+ int ret;
if (force_device_id[0]) {
for (id = kempld_dmi_table;
@@ -801,12 +894,24 @@ static int __init kempld_init(void)
break;
if (id->matches[0].slot == DMI_NONE)
return -ENODEV;
- } else {
- if (!dmi_check_system(kempld_dmi_table))
- return -ENODEV;
}
- return platform_driver_register(&kempld_driver);
+ ret = platform_driver_register(&kempld_driver);
+ if (ret)
+ return ret;
+
+ /*
+ * With synchronous probing the device should already be probed now.
+ * If no device id is forced and also no ACPI definition for the
+ * device was found, scan DMI table as fallback.
+ *
+ * If drivers_autoprobing is disabled and the device is found here,
+ * only that device can be bound manually later.
+ */
+ if (!kempld_pdev && !kempld_acpi_mode)
+ dmi_check_system(kempld_dmi_table);
+
+ return 0;
}
static void __exit kempld_exit(void)
diff --git a/drivers/mfd/khadas-mcu.c b/drivers/mfd/khadas-mcu.c
index 44d5bb462dab..f3d418810693 100644
--- a/drivers/mfd/khadas-mcu.c
+++ b/drivers/mfd/khadas-mcu.c
@@ -122,11 +122,13 @@ static int khadas_mcu_probe(struct i2c_client *client,
return 0;
}
+#ifdef CONFIG_OF
static const struct of_device_id khadas_mcu_of_match[] = {
{ .compatible = "khadas,mcu", },
{},
};
MODULE_DEVICE_TABLE(of, khadas_mcu_of_match);
+#endif
static struct i2c_driver khadas_mcu_driver = {
.driver = {
diff --git a/drivers/mfd/lp87565.c b/drivers/mfd/lp87565.c
index 2268be9113f1..9c21483d9653 100644
--- a/drivers/mfd/lp87565.c
+++ b/drivers/mfd/lp87565.c
@@ -27,6 +27,10 @@ static const struct mfd_cell lp87565_cells[] = {
static const struct of_device_id of_lp87565_match_table[] = {
{ .compatible = "ti,lp87565", },
{
+ .compatible = "ti,lp87524-q1",
+ .data = (void *)LP87565_DEVICE_TYPE_LP87524_Q1,
+ },
+ {
.compatible = "ti,lp87565-q1",
.data = (void *)LP87565_DEVICE_TYPE_LP87565_Q1,
},
diff --git a/drivers/mfd/madera-core.c b/drivers/mfd/madera-core.c
index 8a8d733fdce5..4ed6ad8ce002 100644
--- a/drivers/mfd/madera-core.c
+++ b/drivers/mfd/madera-core.c
@@ -369,19 +369,14 @@ EXPORT_SYMBOL_GPL(madera_of_match);
static int madera_get_reset_gpio(struct madera *madera)
{
struct gpio_desc *reset;
- int ret;
if (madera->pdata.reset)
return 0;
reset = devm_gpiod_get_optional(madera->dev, "reset", GPIOD_OUT_LOW);
- if (IS_ERR(reset)) {
- ret = PTR_ERR(reset);
- if (ret != -EPROBE_DEFER)
- dev_err(madera->dev, "Failed to request /RESET: %d\n",
- ret);
- return ret;
- }
+ if (IS_ERR(reset))
+ return dev_err_probe(madera->dev, PTR_ERR(reset),
+ "Failed to request /RESET");
/*
* A hard reset is needed for full reset of the chip. We allow running
diff --git a/drivers/mfd/mt6360-core.c b/drivers/mfd/mt6360-core.c
index e9cacc27d980..4661c1b29a72 100644
--- a/drivers/mfd/mt6360-core.c
+++ b/drivers/mfd/mt6360-core.c
@@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
-#include <linux/version.h>
#include <linux/mfd/mt6360.h>
diff --git a/drivers/mfd/rn5t618.c b/drivers/mfd/rn5t618.c
index e25407ed3ad4..dc452df1f1bf 100644
--- a/drivers/mfd/rn5t618.c
+++ b/drivers/mfd/rn5t618.c
@@ -25,6 +25,7 @@ static const struct mfd_cell rn5t618_cells[] = {
static const struct mfd_cell rc5t619_cells[] = {
{ .name = "rn5t618-adc" },
+ { .name = "rn5t618-power" },
{ .name = "rn5t618-regulator" },
{ .name = "rc5t619-rtc" },
{ .name = "rn5t618-wdt" },
diff --git a/drivers/mfd/simple-mfd-i2c.c b/drivers/mfd/simple-mfd-i2c.c
new file mode 100644
index 000000000000..87f684cff9a1
--- /dev/null
+++ b/drivers/mfd/simple-mfd-i2c.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Simple MFD - I2C
+ *
+ * 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.
+ */
+
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+
+static const struct regmap_config simple_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int simple_mfd_i2c_probe(struct i2c_client *i2c)
+{
+ const struct regmap_config *config;
+ struct regmap *regmap;
+
+ config = device_get_match_data(&i2c->dev);
+ if (!config)
+ config = &simple_regmap_config;
+
+ regmap = devm_regmap_init_i2c(i2c, config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return devm_of_platform_populate(&i2c->dev);
+}
+
+static const struct of_device_id simple_mfd_i2c_of_match[] = {
+ { .compatible = "kontron,sl28cpld" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, simple_mfd_i2c_of_match);
+
+static struct i2c_driver simple_mfd_i2c_driver = {
+ .probe_new = simple_mfd_i2c_probe,
+ .driver = {
+ .name = "simple-mfd-i2c",
+ .of_match_table = simple_mfd_i2c_of_match,
+ },
+};
+module_i2c_driver(simple_mfd_i2c_driver);
+
+MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
+MODULE_DESCRIPTION("Simple MFD - I2C driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
index ccd62b963952..6d2f4a0a901d 100644
--- a/drivers/mfd/sm501.c
+++ b/drivers/mfd/sm501.c
@@ -1415,8 +1415,14 @@ static int sm501_plat_probe(struct platform_device *dev)
goto err_claim;
}
- return sm501_init_dev(sm);
+ ret = sm501_init_dev(sm);
+ if (ret)
+ goto err_unmap;
+
+ return 0;
+ err_unmap:
+ iounmap(sm->regs);
err_claim:
release_mem_region(sm->io_res->start, 0x100);
err_res:
diff --git a/drivers/mfd/sprd-sc27xx-spi.c b/drivers/mfd/sprd-sc27xx-spi.c
index f8a8b918c60d..6b7956604a0f 100644
--- a/drivers/mfd/sprd-sc27xx-spi.c
+++ b/drivers/mfd/sprd-sc27xx-spi.c
@@ -189,7 +189,7 @@ static int sprd_pmic_probe(struct spi_device *spi)
ddata->irqs[i].mask = BIT(i);
ret = devm_regmap_add_irq_chip(&spi->dev, ddata->regmap, ddata->irq,
- IRQF_ONESHOT | IRQF_NO_SUSPEND, 0,
+ IRQF_ONESHOT, 0,
&ddata->irq_chip, &ddata->irq_data);
if (ret) {
dev_err(&spi->dev, "Failed to add PMIC irq chip %d\n", ret);
@@ -202,9 +202,34 @@ static int sprd_pmic_probe(struct spi_device *spi)
return ret;
}
+ device_init_wakeup(&spi->dev, true);
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int sprd_pmic_suspend(struct device *dev)
+{
+ struct sprd_pmic *ddata = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(ddata->irq);
+
+ return 0;
+}
+
+static int sprd_pmic_resume(struct device *dev)
+{
+ struct sprd_pmic *ddata = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(ddata->irq);
+
+ return 0;
+}
+#endif
+
+static 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,sc2731", .data = &sc2731_data },
{},
@@ -215,6 +240,7 @@ static struct spi_driver sprd_pmic_driver = {
.driver = {
.name = "sc27xx-pmic",
.of_match_table = sprd_pmic_match,
+ .pm = &sprd_pmic_pm_ops,
},
.probe = sprd_pmic_probe,
};
diff --git a/drivers/mfd/stmfx.c b/drivers/mfd/stmfx.c
index 711979afd90a..5e680bfdf5c9 100644
--- a/drivers/mfd/stmfx.c
+++ b/drivers/mfd/stmfx.c
@@ -331,11 +331,9 @@ static int stmfx_chip_init(struct i2c_client *client)
ret = PTR_ERR_OR_ZERO(stmfx->vdd);
if (ret == -ENODEV) {
stmfx->vdd = NULL;
- } else if (ret == -EPROBE_DEFER) {
- return ret;
- } else if (ret) {
- dev_err(&client->dev, "Failed to get VDD regulator: %d\n", ret);
- return ret;
+ } else {
+ return dev_err_probe(&client->dev, ret,
+ "Failed to get VDD regulator\n");
}
if (stmfx->vdd) {
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index df5cebb372a5..ca465794ea9c 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -108,7 +108,6 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
syscon_config.max_register = resource_size(&res) - reg_io_width;
regmap = regmap_init_mmio(NULL, base, &syscon_config);
- kfree(syscon_config.name);
if (IS_ERR(regmap)) {
pr_err("regmap init failed\n");
ret = PTR_ERR(regmap);
@@ -145,6 +144,7 @@ err_clk:
regmap_exit(regmap);
err_regmap:
iounmap(base);
+ kfree(syscon_config.name);
err_map:
kfree(syscon);
return ERR_PTR(ret);
diff --git a/drivers/mfd/wcd934x.c b/drivers/mfd/wcd934x.c
index da910302d51a..c274d733b656 100644
--- a/drivers/mfd/wcd934x.c
+++ b/drivers/mfd/wcd934x.c
@@ -219,12 +219,9 @@ static int wcd934x_slim_probe(struct slim_device *sdev)
return -ENOMEM;
ddata->irq = of_irq_get(np, 0);
- if (ddata->irq < 0) {
- if (ddata->irq != -EPROBE_DEFER)
- dev_err(ddata->dev, "Failed to get IRQ: err = %d\n",
- ddata->irq);
- return ddata->irq;
- }
+ if (ddata->irq < 0)
+ 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) {