summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clocksource/Kconfig4
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/timer-stm32-lp.c221
-rw-r--r--drivers/mfd/Kconfig33
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/ab3100-core.c2
-rw-r--r--drivers/mfd/ab3100-otp.c20
-rw-r--r--drivers/mfd/ab8500-debugfs.c2
-rw-r--r--drivers/mfd/altera-sysmgr.c19
-rw-r--r--drivers/mfd/arizona-core.c20
-rw-r--r--drivers/mfd/atmel-smc.c4
-rw-r--r--drivers/mfd/axp20x-i2c.c4
-rw-r--r--drivers/mfd/cros_ec_dev.c4
-rw-r--r--drivers/mfd/da9063-core.c31
-rw-r--r--drivers/mfd/da9063-i2c.c271
-rw-r--r--drivers/mfd/db8500-prcmu.c6
-rw-r--r--drivers/mfd/dln2.c4
-rw-r--r--drivers/mfd/hi6421-pmic-core.c2
-rw-r--r--drivers/mfd/intel-lpss-pci.c19
-rw-r--r--drivers/mfd/intel_soc_pmic_mrfld.c7
-rw-r--r--drivers/mfd/kempld-core.c30
-rw-r--r--drivers/mfd/khadas-mcu.c142
-rw-r--r--drivers/mfd/lm3533-ctrlbank.c94
-rw-r--r--drivers/mfd/lp873x.c2
-rw-r--r--drivers/mfd/lp87565.c2
-rw-r--r--drivers/mfd/madera-core.c33
-rw-r--r--drivers/mfd/madera-i2c.c1
-rw-r--r--drivers/mfd/max14577.c2
-rw-r--r--drivers/mfd/mfd-core.c121
-rw-r--r--drivers/mfd/motorola-cpcap.c23
-rw-r--r--drivers/mfd/omap-usb-host.c6
-rw-r--r--drivers/mfd/omap-usb-tll.c4
-rw-r--r--drivers/mfd/rave-sp.c2
-rw-r--r--drivers/mfd/rn5t618.c46
-rw-r--r--drivers/mfd/si476x-cmd.c74
-rw-r--r--drivers/mfd/si476x-i2c.c7
-rw-r--r--drivers/mfd/smsc-ece1099.c87
-rw-r--r--drivers/mfd/sprd-sc27xx-spi.c82
-rw-r--r--drivers/mfd/stm32-lptimer.c1
-rw-r--r--drivers/mfd/syscon.c4
-rw-r--r--drivers/mfd/tc3589x.c2
-rw-r--r--drivers/mfd/ti_am335x_tscadc.c2
-rw-r--r--drivers/mfd/tps65010.c5
-rw-r--r--drivers/mfd/tps65086.c2
-rw-r--r--drivers/mfd/tps65217.c6
-rw-r--r--drivers/mfd/tps65218.c6
-rw-r--r--drivers/mfd/tps6586x.c7
-rw-r--r--drivers/mfd/tps65912-core.c2
-rw-r--r--drivers/mfd/tps65912-i2c.c2
-rw-r--r--drivers/mfd/tps65912-spi.c2
-rw-r--r--drivers/mfd/twl4030-irq.c4
-rw-r--r--drivers/mfd/wm831x-core.c4
-rw-r--r--drivers/mfd/wm8350-core.c4
-rw-r--r--drivers/mfd/wm8400-core.c2
-rw-r--r--drivers/thermal/Kconfig11
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/khadas_mcu_fan.c162
57 files changed, 1232 insertions, 431 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 2ed8b4361d95..3576ad7bd380 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -291,6 +291,10 @@ config CLKSRC_STM32
select CLKSRC_MMIO
select TIMER_OF
+config CLKSRC_STM32_LP
+ bool "Low power clocksource for STM32 SoCs"
+ depends on MFD_STM32_LPTIMER || COMPILE_TEST
+
config CLKSRC_MPS2
bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
depends on GENERIC_SCHED_CLOCK
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 3994e221e262..eaedb7240ae7 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_BCM_KONA_TIMER) += bcm_kona_timer.o
obj-$(CONFIG_CADENCE_TTC_TIMER) += timer-cadence-ttc.o
obj-$(CONFIG_CLKSRC_EFM32) += timer-efm32.o
obj-$(CONFIG_CLKSRC_STM32) += timer-stm32.o
+obj-$(CONFIG_CLKSRC_STM32_LP) += timer-stm32-lp.o
obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
obj-$(CONFIG_CLKSRC_LPC32XX) += timer-lpc32xx.o
obj-$(CONFIG_CLKSRC_MPS2) += mps2-timer.o
diff --git a/drivers/clocksource/timer-stm32-lp.c b/drivers/clocksource/timer-stm32-lp.c
new file mode 100644
index 000000000000..db2841d0beb8
--- /dev/null
+++ b/drivers/clocksource/timer-stm32-lp.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2019 - All Rights Reserved
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
+ */
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/stm32-lptimer.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
+
+#define CFGR_PSC_OFFSET 9
+#define STM32_LP_RATING 1000
+#define STM32_TARGET_CLKRATE (32000 * HZ)
+#define STM32_LP_MAX_PSC 7
+
+struct stm32_lp_private {
+ struct regmap *reg;
+ struct clock_event_device clkevt;
+ unsigned long period;
+ struct device *dev;
+};
+
+static struct stm32_lp_private*
+to_priv(struct clock_event_device *clkevt)
+{
+ return container_of(clkevt, struct stm32_lp_private, clkevt);
+}
+
+static int stm32_clkevent_lp_shutdown(struct clock_event_device *clkevt)
+{
+ struct stm32_lp_private *priv = to_priv(clkevt);
+
+ regmap_write(priv->reg, STM32_LPTIM_CR, 0);
+ regmap_write(priv->reg, STM32_LPTIM_IER, 0);
+ /* clear pending flags */
+ regmap_write(priv->reg, STM32_LPTIM_ICR, STM32_LPTIM_ARRMCF);
+
+ return 0;
+}
+
+static int stm32_clkevent_lp_set_timer(unsigned long evt,
+ struct clock_event_device *clkevt,
+ int is_periodic)
+{
+ struct stm32_lp_private *priv = to_priv(clkevt);
+
+ /* disable LPTIMER to be able to write into IER register*/
+ regmap_write(priv->reg, STM32_LPTIM_CR, 0);
+ /* enable ARR interrupt */
+ regmap_write(priv->reg, STM32_LPTIM_IER, STM32_LPTIM_ARRMIE);
+ /* enable LPTIMER to be able to write into ARR register */
+ regmap_write(priv->reg, STM32_LPTIM_CR, STM32_LPTIM_ENABLE);
+ /* set next event counter */
+ regmap_write(priv->reg, STM32_LPTIM_ARR, evt);
+
+ /* start counter */
+ if (is_periodic)
+ regmap_write(priv->reg, STM32_LPTIM_CR,
+ STM32_LPTIM_CNTSTRT | STM32_LPTIM_ENABLE);
+ else
+ regmap_write(priv->reg, STM32_LPTIM_CR,
+ STM32_LPTIM_SNGSTRT | STM32_LPTIM_ENABLE);
+
+ return 0;
+}
+
+static int stm32_clkevent_lp_set_next_event(unsigned long evt,
+ struct clock_event_device *clkevt)
+{
+ return stm32_clkevent_lp_set_timer(evt, clkevt,
+ clockevent_state_periodic(clkevt));
+}
+
+static int stm32_clkevent_lp_set_periodic(struct clock_event_device *clkevt)
+{
+ struct stm32_lp_private *priv = to_priv(clkevt);
+
+ return stm32_clkevent_lp_set_timer(priv->period, clkevt, true);
+}
+
+static int stm32_clkevent_lp_set_oneshot(struct clock_event_device *clkevt)
+{
+ struct stm32_lp_private *priv = to_priv(clkevt);
+
+ return stm32_clkevent_lp_set_timer(priv->period, clkevt, false);
+}
+
+static irqreturn_t stm32_clkevent_lp_irq_handler(int irq, void *dev_id)
+{
+ struct clock_event_device *clkevt = (struct clock_event_device *)dev_id;
+ struct stm32_lp_private *priv = to_priv(clkevt);
+
+ regmap_write(priv->reg, STM32_LPTIM_ICR, STM32_LPTIM_ARRMCF);
+
+ if (clkevt->event_handler)
+ clkevt->event_handler(clkevt);
+
+ return IRQ_HANDLED;
+}
+
+static void stm32_clkevent_lp_set_prescaler(struct stm32_lp_private *priv,
+ unsigned long *rate)
+{
+ int i;
+
+ for (i = 0; i <= STM32_LP_MAX_PSC; i++) {
+ if (DIV_ROUND_CLOSEST(*rate, 1 << i) < STM32_TARGET_CLKRATE)
+ break;
+ }
+
+ regmap_write(priv->reg, STM32_LPTIM_CFGR, i << CFGR_PSC_OFFSET);
+
+ /* Adjust rate and period given the prescaler value */
+ *rate = DIV_ROUND_CLOSEST(*rate, (1 << i));
+ priv->period = DIV_ROUND_UP(*rate, HZ);
+}
+
+static void stm32_clkevent_lp_init(struct stm32_lp_private *priv,
+ struct device_node *np, unsigned long rate)
+{
+ priv->clkevt.name = np->full_name;
+ priv->clkevt.cpumask = cpu_possible_mask;
+ priv->clkevt.features = CLOCK_EVT_FEAT_PERIODIC |
+ CLOCK_EVT_FEAT_ONESHOT;
+ priv->clkevt.set_state_shutdown = stm32_clkevent_lp_shutdown;
+ priv->clkevt.set_state_periodic = stm32_clkevent_lp_set_periodic;
+ priv->clkevt.set_state_oneshot = stm32_clkevent_lp_set_oneshot;
+ priv->clkevt.set_next_event = stm32_clkevent_lp_set_next_event;
+ priv->clkevt.rating = STM32_LP_RATING;
+
+ clockevents_config_and_register(&priv->clkevt, rate, 0x1,
+ STM32_LPTIM_MAX_ARR);
+}
+
+static int stm32_clkevent_lp_probe(struct platform_device *pdev)
+{
+ struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
+ struct stm32_lp_private *priv;
+ unsigned long rate;
+ int ret, irq;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->reg = ddata->regmap;
+ ret = clk_prepare_enable(ddata->clk);
+ if (ret)
+ return -EINVAL;
+
+ rate = clk_get_rate(ddata->clk);
+ if (!rate) {
+ ret = -EINVAL;
+ goto out_clk_disable;
+ }
+
+ irq = platform_get_irq(to_platform_device(pdev->dev.parent), 0);
+ if (irq <= 0) {
+ ret = irq;
+ goto out_clk_disable;
+ }
+
+ if (of_property_read_bool(pdev->dev.parent->of_node, "wakeup-source")) {
+ ret = device_init_wakeup(&pdev->dev, true);
+ if (ret)
+ goto out_clk_disable;
+
+ ret = dev_pm_set_wake_irq(&pdev->dev, irq);
+ if (ret)
+ goto out_clk_disable;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, stm32_clkevent_lp_irq_handler,
+ IRQF_TIMER, pdev->name, &priv->clkevt);
+ if (ret)
+ goto out_clk_disable;
+
+ stm32_clkevent_lp_set_prescaler(priv, &rate);
+
+ stm32_clkevent_lp_init(priv, pdev->dev.parent->of_node, rate);
+
+ priv->dev = &pdev->dev;
+
+ return 0;
+
+out_clk_disable:
+ clk_disable_unprepare(ddata->clk);
+ return ret;
+}
+
+static int stm32_clkevent_lp_remove(struct platform_device *pdev)
+{
+ return -EBUSY; /* cannot unregister clockevent */
+}
+
+static const struct of_device_id stm32_clkevent_lp_of_match[] = {
+ { .compatible = "st,stm32-lptimer-timer", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_clkevent_lp_of_match);
+
+static struct platform_driver stm32_clkevent_lp_driver = {
+ .probe = stm32_clkevent_lp_probe,
+ .remove = stm32_clkevent_lp_remove,
+ .driver = {
+ .name = "stm32-lptimer-timer",
+ .of_match_table = of_match_ptr(stm32_clkevent_lp_of_match),
+ },
+};
+module_platform_driver(stm32_clkevent_lp_driver);
+
+MODULE_ALIAS("platform:stm32-lptimer-timer");
+MODULE_DESCRIPTION("STMicroelectronics STM32 clockevent low power driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index a37d7d171382..33df0837ab41 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1193,18 +1193,6 @@ config MFD_SKY81452
This driver can also be built as a module. If so, the module
will be called sky81452.
-config MFD_SMSC
- bool "SMSC ECE1099 series chips"
- depends on I2C=y
- select MFD_CORE
- select REGMAP_I2C
- help
- If you say yes here you get support for the
- ece1099 chips from SMSC.
-
- To compile this driver as a module, choose M here: the
- module will be called smsc.
-
config MFD_SC27XX_PMIC
tristate "Spreadtrum SC27xx PMICs"
depends on ARCH_SPRD || COMPILE_TEST
@@ -2053,6 +2041,27 @@ config MFD_WCD934X
This driver provides common support WCD934x audio codec and its
associated Pin Controller, Soundwire Controller and Audio codec.
+config MFD_KHADAS_MCU
+ tristate "Support for Khadas System control Microcontroller"
+ depends on I2C
+ depends on ARCH_MESON || ARCH_ROCKCHIP || COMPILE_TEST
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ Support for the Khadas System control Microcontroller interface
+ present on their VIM and Edge boards.
+
+ This Microcontroller is present on the Khadas VIM1, VIM2, VIM3 and
+ Edge boards.
+
+ It provides multiple boot control features like password check,
+ power-on options, power-off control and system FAN control on recent
+ boards.
+
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the functionality
+ of the device.
+
menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9367a92f795a..a60e5f835283 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -127,7 +127,6 @@ obj-$(CONFIG_MFD_CPCAP) += motorola-cpcap.o
obj-$(CONFIG_MCP) += mcp-core.o
obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
-obj-$(CONFIG_MFD_SMSC) += smsc-ece1099.o
obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o
ifeq ($(CONFIG_SA1100_ASSABET),y)
@@ -262,5 +261,6 @@ 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_STMFX) += stmfx.o
+obj-$(CONFIG_MFD_KHADAS_MCU) += khadas-mcu.o
obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o
diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c
index 57723f116bb5..ee71ae04b5e6 100644
--- a/drivers/mfd/ab3100-core.c
+++ b/drivers/mfd/ab3100-core.c
@@ -498,7 +498,7 @@ static ssize_t ab3100_get_set_reg(struct file *file,
int i = 0;
/* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf)-1));
+ buf_size = min((ssize_t)count, (ssize_t)(sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c
index c4751fb9bc22..c393102e3a39 100644
--- a/drivers/mfd/ab3100-otp.c
+++ b/drivers/mfd/ab3100-otp.c
@@ -29,22 +29,22 @@
/**
* struct ab3100_otp
- * @dev containing device
- * @locked whether the OTP is locked, after locking, no more bits
+ * @dev: containing device
+ * @locked: whether the OTP is locked, after locking, no more bits
* can be changed but before locking it is still possible
* to change bits from 1->0.
- * @freq clocking frequency for the OTP, this frequency is either
+ * @freq: clocking frequency for the OTP, this frequency is either
* 32768Hz or 1MHz/30
- * @paf product activation flag, indicates whether this is a real
+ * @paf: product activation flag, indicates whether this is a real
* product (paf true) or a lab board etc (paf false)
- * @imeich if this is set it is possible to override the
+ * @imeich: if this is set it is possible to override the
* IMEI number found in the tac, fac and svn fields with
* (secured) software
- * @cid customer ID
- * @tac type allocation code of the IMEI
- * @fac final assembly code of the IMEI
- * @svn software version number of the IMEI
- * @debugfs a debugfs file used when dumping to file
+ * @cid: customer ID
+ * @tac: type allocation code of the IMEI
+ * @fac: final assembly code of the IMEI
+ * @svn: software version number of the IMEI
+ * @debugfs: a debugfs file used when dumping to file
*/
struct ab3100_otp {
struct device *dev;
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index 1a9a3414d4fa..6d1bf7c3ca3b 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -1801,7 +1801,7 @@ static ssize_t ab8500_hwreg_write(struct file *file,
int buf_size, ret;
/* Get userspace string and assure termination */
- buf_size = min(count, (sizeof(buf)-1));
+ buf_size = min((int)count, (int)(sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
diff --git a/drivers/mfd/altera-sysmgr.c b/drivers/mfd/altera-sysmgr.c
index d2a13a547a3c..41076d121dd5 100644
--- a/drivers/mfd/altera-sysmgr.c
+++ b/drivers/mfd/altera-sysmgr.c
@@ -22,11 +22,9 @@
/**
* struct altr_sysmgr - Altera SOCFPGA System Manager
* @regmap: the regmap used for System Manager accesses.
- * @base : the base address for the System Manager
*/
struct altr_sysmgr {
struct regmap *regmap;
- resource_size_t *base;
};
static struct platform_driver altr_sysmgr_driver;
@@ -91,6 +89,9 @@ static struct regmap_config altr_sysmgr_regmap_cfg = {
* altr_sysmgr_regmap_lookup_by_phandle
* Find the sysmgr previous configured in probe() and return regmap property.
* Return: regmap if found or error if not found.
+ *
+ * @np: Pointer to device's Device Tree node
+ * @property: Device Tree property name which references the sysmgr
*/
struct regmap *altr_sysmgr_regmap_lookup_by_phandle(struct device_node *np,
const char *property)
@@ -127,6 +128,7 @@ static int sysmgr_probe(struct platform_device *pdev)
struct regmap_config sysmgr_config = altr_sysmgr_regmap_cfg;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
+ void __iomem *base;
sysmgr = devm_kzalloc(dev, sizeof(*sysmgr), GFP_KERNEL);
if (!sysmgr)
@@ -139,22 +141,19 @@ static int sysmgr_probe(struct platform_device *pdev)
sysmgr_config.max_register = resource_size(res) -
sysmgr_config.reg_stride;
if (of_device_is_compatible(np, "altr,sys-mgr-s10")) {
- /* Need physical address for SMCC call */
- sysmgr->base = (resource_size_t *)res->start;
sysmgr_config.reg_read = s10_protected_reg_read;
sysmgr_config.reg_write = s10_protected_reg_write;
- regmap = devm_regmap_init(dev, NULL, sysmgr->base,
+ /* Need physical address for SMCC call */
+ regmap = devm_regmap_init(dev, NULL, (void *)res->start,
&sysmgr_config);
} else {
- sysmgr->base = devm_ioremap(dev, res->start,
- resource_size(res));
- if (!sysmgr->base)
+ base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!base)
return -ENOMEM;
sysmgr_config.max_register = res->end - res->start - 3;
- regmap = devm_regmap_init_mmio(dev, sysmgr->base,
- &sysmgr_config);
+ regmap = devm_regmap_init_mmio(dev, base, &sysmgr_config);
}
if (IS_ERR(regmap)) {
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index f73cf76d1373..000cb82023e3 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -80,7 +80,7 @@ int arizona_clk32k_disable(struct arizona *arizona)
{
mutex_lock(&arizona->clk_lock);
- BUG_ON(arizona->clk32k_ref <= 0);
+ WARN_ON(arizona->clk32k_ref <= 0);
arizona->clk32k_ref--;
@@ -1426,6 +1426,15 @@ err_irq:
arizona_irq_exit(arizona);
err_pm:
pm_runtime_disable(arizona->dev);
+
+ switch (arizona->pdata.clk32k_src) {
+ case ARIZONA_32KZ_MCLK1:
+ case ARIZONA_32KZ_MCLK2:
+ arizona_clk32k_disable(arizona);
+ break;
+ default:
+ break;
+ }
err_reset:
arizona_enable_reset(arizona);
regulator_disable(arizona->dcvdd);
@@ -1448,6 +1457,15 @@ int arizona_dev_exit(struct arizona *arizona)
regulator_disable(arizona->dcvdd);
regulator_put(arizona->dcvdd);
+ switch (arizona->pdata.clk32k_src) {
+ case ARIZONA_32KZ_MCLK1:
+ case ARIZONA_32KZ_MCLK2:
+ arizona_clk32k_disable(arizona);
+ break;
+ default:
+ break;
+ }
+
mfd_remove_devices(arizona->dev);
arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona);
arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
diff --git a/drivers/mfd/atmel-smc.c b/drivers/mfd/atmel-smc.c
index 1fa2ec950e7d..d96f1d689e7f 100644
--- a/drivers/mfd/atmel-smc.c
+++ b/drivers/mfd/atmel-smc.c
@@ -237,7 +237,7 @@ EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_cycle);
* atmel_smc_cs_conf_apply - apply an SMC CS conf
* @regmap: the SMC regmap
* @cs: the CS id
- * @conf the SMC CS conf to apply
+ * @conf: the SMC CS conf to apply
*
* Applies an SMC CS configuration.
* Only valid on at91sam9/avr32 SoCs.
@@ -257,7 +257,7 @@ EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_apply);
* @regmap: the HSMC regmap
* @cs: the CS id
* @layout: the layout of registers
- * @conf the SMC CS conf to apply
+ * @conf: the SMC CS conf to apply
*
* Applies an SMC CS configuration.
* Only valid on post-sama5 SoCs.
diff --git a/drivers/mfd/axp20x-i2c.c b/drivers/mfd/axp20x-i2c.c
index 14f9df74f855..068e9c083f13 100644
--- a/drivers/mfd/axp20x-i2c.c
+++ b/drivers/mfd/axp20x-i2c.c
@@ -63,6 +63,7 @@ static const struct of_device_id axp20x_i2c_of_match[] = {
{ .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,axp803", .data = (void *)AXP803_ID },
{ .compatible = "x-powers,axp806", .data = (void *)AXP806_ID },
{ },
};
@@ -74,11 +75,13 @@ static const struct i2c_device_id axp20x_i2c_id[] = {
{ "axp209", 0 },
{ "axp221", 0 },
{ "axp223", 0 },
+ { "axp803", 0 },
{ "axp806", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id);
+#ifdef CONFIG_ACPI
static const struct acpi_device_id axp20x_i2c_acpi_match[] = {
{
.id = "INT33F4",
@@ -87,6 +90,7 @@ static const struct acpi_device_id axp20x_i2c_acpi_match[] = {
{ },
};
MODULE_DEVICE_TABLE(acpi, axp20x_i2c_acpi_match);
+#endif
static struct i2c_driver axp20x_i2c_driver = {
.driver = {
diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
index 32c2b912b58b..d07b43d7c761 100644
--- a/drivers/mfd/cros_ec_dev.c
+++ b/drivers/mfd/cros_ec_dev.c
@@ -24,7 +24,7 @@ static struct class cros_class = {
};
/**
- * cros_feature_to_name - CrOS feature id to name/short description.
+ * struct cros_feature_to_name - CrOS feature id to name/short description.
* @id: The feature identifier.
* @name: Device name associated with the feature id.
* @desc: Short name that will be displayed.
@@ -36,7 +36,7 @@ struct cros_feature_to_name {
};
/**
- * cros_feature_to_cells - CrOS feature id to mfd cells association.
+ * struct cros_feature_to_cells - CrOS feature id to mfd cells association.
* @id: The feature identifier.
* @mfd_cells: Pointer to the array of mfd cells that needs to be added.
* @num_cells: Number of mfd cells into the array.
diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c
index b125f90dd375..a353d52210a9 100644
--- a/drivers/mfd/da9063-core.c
+++ b/drivers/mfd/da9063-core.c
@@ -160,7 +160,6 @@ static int da9063_clear_fault_log(struct da9063 *da9063)
int da9063_device_init(struct da9063 *da9063, unsigned int irq)
{
- int model, variant_id, variant_code;
int ret;
ret = da9063_clear_fault_log(da9063);
@@ -171,36 +170,6 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq)
da9063->irq_base = -1;
da9063->chip_irq = irq;
- ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_ID, &model);
- if (ret < 0) {
- dev_err(da9063->dev, "Cannot read chip model id.\n");
- return -EIO;
- }
- if (model != PMIC_CHIP_ID_DA9063) {
- dev_err(da9063->dev, "Invalid chip model id: 0x%02x\n", model);
- return -ENODEV;
- }
-
- ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &variant_id);
- if (ret < 0) {
- dev_err(da9063->dev, "Cannot read chip variant id.\n");
- return -EIO;
- }
-
- variant_code = variant_id >> DA9063_CHIP_VARIANT_SHIFT;
-
- dev_info(da9063->dev,
- "Device detected (chip-ID: 0x%02X, var-ID: 0x%02X)\n",
- model, variant_id);
-
- if (variant_code < PMIC_DA9063_BB && variant_code != PMIC_DA9063_AD) {
- dev_err(da9063->dev,
- "Cannot support variant code: 0x%02X\n", variant_code);
- return -ENODEV;
- }
-
- da9063->variant_code = variant_code;
-
ret = da9063_irq_init(da9063);
if (ret) {
dev_err(da9063->dev, "Cannot initialize interrupts.\n");
diff --git a/drivers/mfd/da9063-i2c.c b/drivers/mfd/da9063-i2c.c
index 455de74c0dd2..b8217ad303ce 100644
--- a/drivers/mfd/da9063-i2c.c
+++ b/drivers/mfd/da9063-i2c.c
@@ -22,12 +22,124 @@
#include <linux/of.h>
#include <linux/regulator/of_regulator.h>
+/*
+ * Raw I2C access required for just accessing chip and variant info before we
+ * know which device is present. The info read from the device using this
+ * approach is then used to select the correct regmap tables.
+ */
+
+#define DA9063_REG_PAGE_SIZE 0x100
+#define DA9063_REG_PAGED_ADDR_MASK 0xFF
+
+enum da9063_page_sel_buf_fmt {
+ DA9063_PAGE_SEL_BUF_PAGE_REG = 0,
+ DA9063_PAGE_SEL_BUF_PAGE_VAL,
+ DA9063_PAGE_SEL_BUF_SIZE,
+};
+
+enum da9063_paged_read_msgs {
+ DA9063_PAGED_READ_MSG_PAGE_SEL = 0,
+ DA9063_PAGED_READ_MSG_REG_SEL,
+ DA9063_PAGED_READ_MSG_DATA,
+ DA9063_PAGED_READ_MSG_CNT,
+};
+
+static int da9063_i2c_blockreg_read(struct i2c_client *client, u16 addr,
+ u8 *buf, int count)
+{
+ struct i2c_msg xfer[DA9063_PAGED_READ_MSG_CNT];
+ u8 page_sel_buf[DA9063_PAGE_SEL_BUF_SIZE];
+ u8 page_num, paged_addr;
+ int ret;
+
+ /* Determine page info based on register address */
+ page_num = (addr / DA9063_REG_PAGE_SIZE);
+ if (page_num > 1) {
+ dev_err(&client->dev, "Invalid register address provided\n");
+ return -EINVAL;
+ }
+
+ paged_addr = (addr % DA9063_REG_PAGE_SIZE) & DA9063_REG_PAGED_ADDR_MASK;
+ page_sel_buf[DA9063_PAGE_SEL_BUF_PAGE_REG] = DA9063_REG_PAGE_CON;
+ page_sel_buf[DA9063_PAGE_SEL_BUF_PAGE_VAL] =
+ (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;
+
+ /* Select register address */
+ xfer[DA9063_PAGED_READ_MSG_REG_SEL].addr = client->addr;
+ xfer[DA9063_PAGED_READ_MSG_REG_SEL].flags = 0;
+ xfer[DA9063_PAGED_READ_MSG_REG_SEL].len = sizeof(paged_addr);
+ xfer[DA9063_PAGED_READ_MSG_REG_SEL].buf = &paged_addr;
+
+ /* Read data */
+ xfer[DA9063_PAGED_READ_MSG_DATA].addr = client->addr;
+ xfer[DA9063_PAGED_READ_MSG_DATA].flags = I2C_M_RD;
+ xfer[DA9063_PAGED_READ_MSG_DATA].len = count;
+ xfer[DA9063_PAGED_READ_MSG_DATA].buf = buf;
+
+ ret = i2c_transfer(client->adapter, xfer, DA9063_PAGED_READ_MSG_CNT);
+ if (ret < 0) {
+ dev_err(&client->dev, "Paged block read failed: %d\n", ret);
+ return ret;
+ }
+
+ if (ret != DA9063_PAGED_READ_MSG_CNT) {
+ dev_err(&client->dev, "Paged block read failed to complete\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+enum {
+ DA9063_DEV_ID_REG = 0,
+ DA9063_VAR_ID_REG,
+ DA9063_CHIP_ID_REGS,
+};
+
+static int da9063_get_device_type(struct i2c_client *i2c, struct da9063 *da9063)
+{
+ u8 buf[DA9063_CHIP_ID_REGS];
+ int ret;
+
+ ret = da9063_i2c_blockreg_read(i2c, DA9063_REG_DEVICE_ID, buf,
+ DA9063_CHIP_ID_REGS);
+ if (ret)
+ return ret;
+
+ if (buf[DA9063_DEV_ID_REG] != PMIC_CHIP_ID_DA9063) {
+ dev_err(da9063->dev,
+ "Invalid chip device ID: 0x%02x\n",
+ buf[DA9063_DEV_ID_REG]);
+ return -ENODEV;
+ }
+
+ dev_info(da9063->dev,
+ "Device detected (chip-ID: 0x%02X, var-ID: 0x%02X)\n",
+ buf[DA9063_DEV_ID_REG], buf[DA9063_VAR_ID_REG]);
+
+ da9063->variant_code =
+ (buf[DA9063_VAR_ID_REG] & DA9063_VARIANT_ID_MRC_MASK)
+ >> DA9063_VARIANT_ID_MRC_SHIFT;
+
+ return 0;
+}
+
+/*
+ * Variant specific regmap configs
+ */
+
static const struct regmap_range da9063_ad_readable_ranges[] = {
regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_AD_REG_SECOND_D),
regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_ID_32_31),
regmap_reg_range(DA9063_REG_SEQ_A, DA9063_REG_AUTO3_LOW),
regmap_reg_range(DA9063_REG_T_OFFSET, DA9063_AD_REG_GP_ID_19),
- regmap_reg_range(DA9063_REG_CHIP_ID, DA9063_REG_CHIP_VARIANT),
+ regmap_reg_range(DA9063_REG_DEVICE_ID, DA9063_REG_VARIANT_ID),
};
static const struct regmap_range da9063_ad_writeable_ranges[] = {
@@ -72,7 +184,7 @@ static const struct regmap_range da9063_bb_readable_ranges[] = {
regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_ID_32_31),
regmap_reg_range(DA9063_REG_SEQ_A, DA9063_REG_AUTO3_LOW),
regmap_reg_range(DA9063_REG_T_OFFSET, DA9063_BB_REG_GP_ID_19),
- regmap_reg_range(DA9063_REG_CHIP_ID, DA9063_REG_CHIP_VARIANT),
+ regmap_reg_range(DA9063_REG_DEVICE_ID, DA9063_REG_VARIANT_ID),
};
static const struct regmap_range da9063_bb_writeable_ranges[] = {
@@ -85,7 +197,7 @@ static const struct regmap_range da9063_bb_writeable_ranges[] = {
regmap_reg_range(DA9063_BB_REG_GP_ID_0, DA9063_BB_REG_GP_ID_19),
};
-static const struct regmap_range da9063_bb_volatile_ranges[] = {
+static const struct regmap_range da9063_bb_da_volatile_ranges[] = {
regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_REG_EVENT_D),
regmap_reg_range(DA9063_REG_CONTROL_A, DA9063_REG_CONTROL_B),
regmap_reg_range(DA9063_REG_CONTROL_E, DA9063_REG_CONTROL_F),
@@ -107,9 +219,9 @@ static const struct regmap_access_table da9063_bb_writeable_table = {
.n_yes_ranges = ARRAY_SIZE(da9063_bb_writeable_ranges),
};
-static const struct regmap_access_table da9063_bb_volatile_table = {
- .yes_ranges = da9063_bb_volatile_ranges,
- .n_yes_ranges = ARRAY_SIZE(da9063_bb_volatile_ranges),
+static const struct regmap_access_table da9063_bb_da_volatile_table = {
+ .yes_ranges = da9063_bb_da_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9063_bb_da_volatile_ranges),
};
static const struct regmap_range da9063l_bb_readable_ranges[] = {
@@ -117,7 +229,7 @@ static const struct regmap_range da9063l_bb_readable_ranges[] = {
regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_ID_32_31),
regmap_reg_range(DA9063_REG_SEQ_A, DA9063_REG_AUTO3_LOW),
regmap_reg_range(DA9063_REG_T_OFFSET, DA9063_BB_REG_GP_ID_19),
- regmap_reg_range(DA9063_REG_CHIP_ID, DA9063_REG_CHIP_VARIANT),
+ regmap_reg_range(DA9063_REG_DEVICE_ID, DA9063_REG_VARIANT_ID),
};
static const struct regmap_range da9063l_bb_writeable_ranges[] = {
@@ -129,7 +241,7 @@ static const struct regmap_range da9063l_bb_writeable_ranges[] = {
regmap_reg_range(DA9063_BB_REG_GP_ID_0, DA9063_BB_REG_GP_ID_19),
};
-static const struct regmap_range da9063l_bb_volatile_ranges[] = {
+static const struct regmap_range da9063l_bb_da_volatile_ranges[] = {
regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_REG_EVENT_D),
regmap_reg_range(DA9063_REG_CONTROL_A, DA9063_REG_CONTROL_B),
regmap_reg_range(DA9063_REG_CONTROL_E, DA9063_REG_CONTROL_F),
@@ -151,15 +263,70 @@ static const struct regmap_access_table da9063l_bb_writeable_table = {
.n_yes_ranges = ARRAY_SIZE(da9063l_bb_writeable_ranges),
};
-static const struct regmap_access_table da9063l_bb_volatile_table = {
- .yes_ranges = da9063l_bb_volatile_ranges,
- .n_yes_ranges = ARRAY_SIZE(da9063l_bb_volatile_ranges),
+static const struct regmap_access_table da9063l_bb_da_volatile_table = {
+ .yes_ranges = da9063l_bb_da_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9063l_bb_da_volatile_ranges),
+};
+
+static const struct regmap_range da9063_da_readable_ranges[] = {
+ regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_BB_REG_SECOND_D),
+ regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_ID_32_31),
+ regmap_reg_range(DA9063_REG_SEQ_A, DA9063_REG_AUTO3_LOW),
+ regmap_reg_range(DA9063_REG_T_OFFSET, DA9063_BB_REG_GP_ID_11),
+ regmap_reg_range(DA9063_REG_DEVICE_ID, DA9063_REG_VARIANT_ID),
+};
+
+static const struct regmap_range da9063_da_writeable_ranges[] = {
+ regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_REG_PAGE_CON),
+ regmap_reg_range(DA9063_REG_FAULT_LOG, DA9063_REG_VSYS_MON),
+ regmap_reg_range(DA9063_REG_COUNT_S, DA9063_BB_REG_ALARM_Y),
+ regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_ID_32_31),
+ regmap_reg_range(DA9063_REG_SEQ_A, DA9063_REG_AUTO3_LOW),
+ regmap_reg_range(DA9063_REG_CONFIG_I, DA9063_BB_REG_MON_REG_4),
+ regmap_reg_range(DA9063_BB_REG_GP_ID_0, DA9063_BB_REG_GP_ID_11),
+};
+
+static const struct regmap_access_table da9063_da_readable_table = {
+ .yes_ranges = da9063_da_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9063_da_readable_ranges),
+};
+
+static const struct regmap_access_table da9063_da_writeable_table = {
+ .yes_ranges = da9063_da_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9063_da_writeable_ranges),
+};
+
+static const struct regmap_range da9063l_da_readable_ranges[] = {
+ regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_REG_MON_A10_RES),
+ regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_ID_32_31),
+ regmap_reg_range(DA9063_REG_SEQ_A, DA9063_REG_AUTO3_LOW),
+ regmap_reg_range(DA9063_REG_T_OFFSET, DA9063_BB_REG_GP_ID_11),
+ regmap_reg_range(DA9063_REG_DEVICE_ID, DA9063_REG_VARIANT_ID),
+};
+
+static const struct regmap_range da9063l_da_writeable_ranges[] = {
+ regmap_reg_range(DA9063_REG_PAGE_CON, DA9063_REG_PAGE_CON),
+ regmap_reg_range(DA9063_REG_FAULT_LOG, DA9063_REG_VSYS_MON),
+ regmap_reg_range(DA9063_REG_SEQ, DA9063_REG_ID_32_31),
+ regmap_reg_range(DA9063_REG_SEQ_A, DA9063_REG_AUTO3_LOW),
+ regmap_reg_range(DA9063_REG_CONFIG_I, DA9063_BB_REG_MON_REG_4),
+ regmap_reg_range(DA9063_BB_REG_GP_ID_0, DA9063_BB_REG_GP_ID_11),
+};
+
+static const struct regmap_access_table da9063l_da_readable_table = {
+ .yes_ranges = da9063l_da_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9063l_da_readable_ranges),
+};
+
+static const struct regmap_access_table da9063l_da_writeable_table = {
+ .yes_ranges = da9063l_da_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(da9063l_da_writeable_ranges),
};
static const struct regmap_range_cfg da9063_range_cfg[] = {
{
.range_min = DA9063_REG_PAGE_CON,
- .range_max = DA9063_REG_CHIP_VARIANT,
+ .range_max = DA9063_REG_CONFIG_ID,
.selector_reg = DA9063_REG_PAGE_CON,
.selector_mask = 1 << DA9063_I2C_PAGE_SEL_SHIFT,
.selector_shift = DA9063_I2C_PAGE_SEL_SHIFT,
@@ -173,7 +340,7 @@ static struct regmap_config da9063_regmap_config = {
.val_bits = 8,
.ranges = da9063_range_cfg,
.num_ranges = ARRAY_SIZE(da9063_range_cfg),
- .max_register = DA9063_REG_CHIP_VARIANT,
+ .max_register = DA9063_REG_CONFIG_ID,
.cache_type = REGCACHE_RBTREE,
};
@@ -199,18 +366,72 @@ static int da9063_i2c_probe(struct i2c_client *i2c,
da9063->chip_irq = i2c->irq;
da9063->type = id->driver_data;
- if (da9063->variant_code == PMIC_DA9063_AD) {
- da9063_regmap_config.rd_table = &da9063_ad_readable_table;
- da9063_regmap_config.wr_table = &da9063_ad_writeable_table;
- da9063_regmap_config.volatile_table = &da9063_ad_volatile_table;
- } else if (da9063->type == PMIC_TYPE_DA9063L) {
- da9063_regmap_config.rd_table = &da9063l_bb_readable_table;
- da9063_regmap_config.wr_table = &da9063l_bb_writeable_table;
- da9063_regmap_config.volatile_table = &da9063l_bb_volatile_table;
- } else {
- da9063_regmap_config.rd_table = &da9063_bb_readable_table;
- da9063_regmap_config.wr_table = &da9063_bb_writeable_table;
- da9063_regmap_config.volatile_table = &da9063_bb_volatile_table;
+ ret = da9063_get_device_type(i2c, da9063);
+ if (ret)
+ return ret;
+
+ switch (da9063->type) {
+ case PMIC_TYPE_DA9063:
+ switch (da9063->variant_code) {
+ case PMIC_DA9063_AD:
+ da9063_regmap_config.rd_table =
+ &da9063_ad_readable_table;
+ da9063_regmap_config.wr_table =
+ &da9063_ad_writeable_table;
+ da9063_regmap_config.volatile_table =
+ &da9063_ad_volatile_table;
+ break;
+ case PMIC_DA9063_BB:
+ case PMIC_DA9063_CA:
+ da9063_regmap_config.rd_table =
+ &da9063_bb_readable_table;
+ da9063_regmap_config.wr_table =
+ &da9063_bb_writeable_table;
+ da9063_regmap_config.volatile_table =
+ &da9063_bb_da_volatile_table;
+ break;
+ case PMIC_DA9063_DA:
+ da9063_regmap_config.rd_table =
+ &da9063_da_readable_table;
+ da9063_regmap_config.wr_table =
+ &da9063_da_writeable_table;
+ da9063_regmap_config.volatile_table =
+ &da9063_bb_da_volatile_table;
+ break;
+ default:
+ dev_err(da9063->dev,
+ "Chip variant not supported for DA9063\n");
+ return -ENODEV;
+ }
+ break;
+ case PMIC_TYPE_DA9063L:
+ switch (da9063->variant_code) {
+ case PMIC_DA9063_BB:
+ case PMIC_DA9063_CA:
+ da9063_regmap_config.rd_table =
+ &da9063l_bb_readable_table;
+ da9063_regmap_config.wr_table =
+ &da9063l_bb_writeable_table;
+ da9063_regmap_config.volatile_table =
+ &da9063l_bb_da_volatile_table;
+ break;
+ case PMIC_DA9063_DA:
+ da9063_regmap_config.rd_table =
+ &da9063l_da_readable_table;
+ da9063_regmap_config.wr_table =
+ &da9063l_da_writeable_table;
+ da9063_regmap_config.volatile_table =
+ &da9063l_bb_da_volatile_table;
+ break;
+ default:
+ dev_err(da9063->dev,
+ "Chip variant not supported for DA9063L\n");
+ return -ENODEV;
+ }
+ break;
+ default:
+ dev_err(da9063->dev, "Chip type not supported\n");
+ return -ENODEV;
}
da9063->regmap = devm_regmap_init_i2c(i2c, &da9063_regmap_config);
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 0452b43b0423..a9d9c1cdf546 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -2276,6 +2276,8 @@ bool db8500_prcmu_is_ac_wake_requested(void)
*
* Saves the reset reason code and then sets the APE_SOFTRST register which
* fires interrupt to fw
+ *
+ * @reset_code: The reason for system reset
*/
void db8500_prcmu_system_reset(u16 reset_code)
{
@@ -3004,10 +3006,6 @@ static int db8500_prcmu_register_ab8500(struct device *parent)
return mfd_add_devices(parent, 0, ab850x_cell, 1, NULL, 0, NULL);
}
-/**
- * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
- *
- */
static int db8500_prcmu_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c
index 39276fa626d2..83e676a096dc 100644
--- a/drivers/mfd/dln2.c
+++ b/drivers/mfd/dln2.c
@@ -287,7 +287,11 @@ static void dln2_rx(struct urb *urb)
len = urb->actual_length - sizeof(struct dln2_header);
if (handle == DLN2_HANDLE_EVENT) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&dln2->event_cb_lock, flags);
dln2_run_event_callbacks(dln2, id, echo, data, len);
+ spin_unlock_irqrestore(&dln2->event_cb_lock, flags);
} else {
/* URB will be re-submitted in _dln2_transfer (free_rx_slot) */
if (dln2_transfer_complete(dln2, urb, handle, echo))
diff --git a/drivers/mfd/hi6421-pmic-core.c b/drivers/mfd/hi6421-pmic-core.c
index edfc172b8607..eba88b80d969 100644
--- a/drivers/mfd/hi6421-pmic-core.c
+++ b/drivers/mfd/hi6421-pmic-core.c
@@ -5,7 +5,7 @@
* Copyright (c) <2011-2014> HiSilicon Technologies Co., Ltd.
* http://www.hisilicon.com
* Copyright (c) <2013-2017> Linaro Ltd.
- * http://www.linaro.org
+ * https://www.linaro.org
*
* Author: Guodong Xu <guodong.xu@linaro.org>
*/
diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c
index 046222684b8b..9a58032f818a 100644
--- a/drivers/mfd/intel-lpss-pci.c
+++ b/drivers/mfd/intel-lpss-pci.c
@@ -201,6 +201,9 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x1ac4), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x1ac6), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x1aee), (kernel_ulong_t)&bxt_uart_info },
+ /* EBG */
+ { PCI_VDEVICE(INTEL, 0x1bad), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x1bae), (kernel_ulong_t)&bxt_uart_info },
/* GLK */
{ PCI_VDEVICE(INTEL, 0x31ac), (kernel_ulong_t)&glk_i2c_info },
{ PCI_VDEVICE(INTEL, 0x31ae), (kernel_ulong_t)&glk_i2c_info },
@@ -230,6 +233,22 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ 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 },
+ /* 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, 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 },
+ { PCI_VDEVICE(INTEL, 0x43da), (kernel_ulong_t)&bxt_uart_info },
+ { PCI_VDEVICE(INTEL, 0x43e8), (kernel_ulong_t)&bxt_i2c_info },
+ { 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 },
/* EHL */
{ PCI_VDEVICE(INTEL, 0x4b28), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x4b29), (kernel_ulong_t)&bxt_uart_info },
diff --git a/drivers/mfd/intel_soc_pmic_mrfld.c b/drivers/mfd/intel_soc_pmic_mrfld.c
index bd94c989d232..71da861e8c27 100644
--- a/drivers/mfd/intel_soc_pmic_mrfld.c
+++ b/drivers/mfd/intel_soc_pmic_mrfld.c
@@ -91,13 +91,8 @@ static int bcove_ipc_byte_reg_write(void *context, unsigned int reg,
{
struct intel_soc_pmic *pmic = context;
u8 ipc_in = val;
- int ret;
- ret = intel_scu_ipc_dev_iowrite8(pmic->scu, reg, ipc_in);
- if (ret)
- return ret;
-
- return 0;
+ return intel_scu_ipc_dev_iowrite8(pmic->scu, reg, ipc_in);
}
static const struct regmap_config bcove_regmap_config = {
diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c
index f48e21d8b97c..52bec01149e5 100644
--- a/drivers/mfd/kempld-core.c
+++ b/drivers/mfd/kempld-core.c
@@ -79,39 +79,31 @@ enum kempld_cells {
KEMPLD_UART,
};
-static const struct mfd_cell kempld_devs[] = {
- [KEMPLD_I2C] = {
- .name = "kempld-i2c",
- },
- [KEMPLD_WDT] = {
- .name = "kempld-wdt",
- },
- [KEMPLD_GPIO] = {
- .name = "kempld-gpio",
- },
- [KEMPLD_UART] = {
- .name = "kempld-uart",
- },
+static const char *kempld_dev_names[] = {
+ [KEMPLD_I2C] = "kempld-i2c",
+ [KEMPLD_WDT] = "kempld-wdt",
+ [KEMPLD_GPIO] = "kempld-gpio",
+ [KEMPLD_UART] = "kempld-uart",
};
-#define KEMPLD_MAX_DEVS ARRAY_SIZE(kempld_devs)
+#define KEMPLD_MAX_DEVS ARRAY_SIZE(kempld_dev_names)
static int kempld_register_cells_generic(struct kempld_device_data *pld)
{
- struct mfd_cell devs[KEMPLD_MAX_DEVS];
+ struct mfd_cell devs[KEMPLD_MAX_DEVS] = {};
int i = 0;
if (pld->feature_mask & KEMPLD_FEATURE_BIT_I2C)
- devs[i++] = kempld_devs[KEMPLD_I2C];
+ devs[i++].name = kempld_dev_names[KEMPLD_I2C];
if (pld->feature_mask & KEMPLD_FEATURE_BIT_WATCHDOG)
- devs[i++] = kempld_devs[KEMPLD_WDT];
+ devs[i++].name = kempld_dev_names[KEMPLD_WDT];
if (pld->feature_mask & KEMPLD_FEATURE_BIT_GPIO)
- devs[i++] = kempld_devs[KEMPLD_GPIO];
+ devs[i++].name = kempld_dev_names[KEMPLD_GPIO];
if (pld->feature_mask & KEMPLD_FEATURE_MASK_UART)
- devs[i++] = kempld_devs[KEMPLD_UART];
+ devs[i++].name = kempld_dev_names[KEMPLD_UART];
return mfd_add_devices(pld->dev, -1, devs, i, NULL, 0, NULL);
}
diff --git a/drivers/mfd/khadas-mcu.c b/drivers/mfd/khadas-mcu.c
new file mode 100644
index 000000000000..44d5bb462dab
--- /dev/null
+++ b/drivers/mfd/khadas-mcu.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Khadas System control Microcontroller
+ *
+ * Copyright (C) 2020 BayLibre SAS
+ *
+ * Author(s): Neil Armstrong <narmstrong@baylibre.com>
+ */
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/khadas-mcu.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+static bool khadas_mcu_reg_volatile(struct device *dev, unsigned int reg)
+{
+ if (reg >= KHADAS_MCU_USER_DATA_0_REG &&
+ reg < KHADAS_MCU_PWR_OFF_CMD_REG)
+ return true;
+
+ switch (reg) {
+ case KHADAS_MCU_PWR_OFF_CMD_REG:
+ case KHADAS_MCU_PASSWD_START_REG:
+ case KHADAS_MCU_CHECK_VEN_PASSWD_REG:
+ case KHADAS_MCU_CHECK_USER_PASSWD_REG:
+ case KHADAS_MCU_WOL_INIT_START_REG:
+ case KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool khadas_mcu_reg_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case KHADAS_MCU_PASSWD_VEN_0_REG:
+ case KHADAS_MCU_PASSWD_VEN_1_REG:
+ case KHADAS_MCU_PASSWD_VEN_2_REG:
+ case KHADAS_MCU_PASSWD_VEN_3_REG:
+ case KHADAS_MCU_PASSWD_VEN_4_REG:
+ case KHADAS_MCU_PASSWD_VEN_5_REG:
+ case KHADAS_MCU_MAC_0_REG:
+ case KHADAS_MCU_MAC_1_REG:
+ case KHADAS_MCU_MAC_2_REG:
+ case KHADAS_MCU_MAC_3_REG:
+ case KHADAS_MCU_MAC_4_REG:
+ case KHADAS_MCU_MAC_5_REG:
+ case KHADAS_MCU_USID_0_REG:
+ case KHADAS_MCU_USID_1_REG:
+ case KHADAS_MCU_USID_2_REG:
+ case KHADAS_MCU_USID_3_REG:
+ case KHADAS_MCU_USID_4_REG:
+ case KHADAS_MCU_USID_5_REG:
+ case KHADAS_MCU_VERSION_0_REG:
+ case KHADAS_MCU_VERSION_1_REG:
+ case KHADAS_MCU_DEVICE_NO_0_REG:
+ case KHADAS_MCU_DEVICE_NO_1_REG:
+ case KHADAS_MCU_FACTORY_TEST_REG:
+ case KHADAS_MCU_SHUTDOWN_NORMAL_STATUS_REG:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config khadas_mcu_regmap_config = {
+ .reg_bits = 8,
+ .reg_stride = 1,
+ .val_bits = 8,
+ .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,
+};
+
+static struct mfd_cell khadas_mcu_fan_cells[] = {
+ /* VIM1/2 Rev13+ and VIM3 only */
+ { .name = "khadas-mcu-fan-ctrl", },
+};
+
+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)
+{
+ struct device *dev = &client->dev;
+ struct khadas_mcu *ddata;
+ int ret;
+
+ ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ddata);
+
+ ddata->dev = dev;
+
+ ddata->regmap = devm_regmap_init_i2c(client, &khadas_mcu_regmap_config);
+ if (IS_ERR(ddata->regmap)) {
+ ret = PTR_ERR(ddata->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
+ khadas_mcu_cells,
+ ARRAY_SIZE(khadas_mcu_cells),
+ NULL, 0, NULL);
+ if (ret)
+ return ret;
+
+ if (of_find_property(dev->of_node, "#cooling-cells", NULL))
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
+ khadas_mcu_fan_cells,
+ ARRAY_SIZE(khadas_mcu_fan_cells),
+ NULL, 0, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id khadas_mcu_of_match[] = {
+ { .compatible = "khadas,mcu", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, khadas_mcu_of_match);
+
+static struct i2c_driver khadas_mcu_driver = {
+ .driver = {
+ .name = "khadas-mcu-core",
+ .of_match_table = of_match_ptr(khadas_mcu_of_match),
+ },
+ .probe = khadas_mcu_probe,
+};
+module_i2c_driver(khadas_mcu_driver);
+
+MODULE_DESCRIPTION("Khadas MCU core driver");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/lm3533-ctrlbank.c b/drivers/mfd/lm3533-ctrlbank.c
index 34fba06ec705..2537dfade51c 100644
--- a/drivers/mfd/lm3533-ctrlbank.c
+++ b/drivers/mfd/lm3533-ctrlbank.c
@@ -17,7 +17,6 @@
#define LM3533_MAX_CURRENT_MAX 29800
#define LM3533_MAX_CURRENT_STEP 800
-#define LM3533_BRIGHTNESS_MAX 255
#define LM3533_PWM_MAX 0x3f
#define LM3533_REG_PWM_BASE 0x14
@@ -89,41 +88,33 @@ int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u16 imax)
}
EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_max_current);
-#define lm3533_ctrlbank_set(_name, _NAME) \
-int lm3533_ctrlbank_set_##_name(struct lm3533_ctrlbank *cb, u8 val) \
-{ \
- u8 reg; \
- int ret; \
- \
- if (val > LM3533_##_NAME##_MAX) \
- return -EINVAL; \
- \
- reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE); \
- ret = lm3533_write(cb->lm3533, reg, val); \
- if (ret) \
- dev_err(cb->dev, "failed to set " #_name "\n"); \
- \
- return ret; \
-} \
-EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_##_name);
-
-#define lm3533_ctrlbank_get(_name, _NAME) \
-int lm3533_ctrlbank_get_##_name(struct lm3533_ctrlbank *cb, u8 *val) \
-{ \
- u8 reg; \
- int ret; \
- \
- reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE); \
- ret = lm3533_read(cb->lm3533, reg, val); \
- if (ret) \
- dev_err(cb->dev, "failed to get " #_name "\n"); \
- \
- return ret; \
-} \
-EXPORT_SYMBOL_GPL(lm3533_ctrlbank_get_##_name);
-
-lm3533_ctrlbank_set(brightness, BRIGHTNESS);
-lm3533_ctrlbank_get(brightness, BRIGHTNESS);
+int lm3533_ctrlbank_set_brightness(struct lm3533_ctrlbank *cb, u8 val)
+{
+ u8 reg;
+ int ret;
+
+ reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_BRIGHTNESS_BASE);
+ ret = lm3533_write(cb->lm3533, reg, val);
+ if (ret)
+ dev_err(cb->dev, "failed to set brightness\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_brightness);
+
+int lm3533_ctrlbank_get_brightness(struct lm3533_ctrlbank *cb, u8 *val)
+{
+ u8 reg;
+ int ret;
+
+ reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_BRIGHTNESS_BASE);
+ ret = lm3533_read(cb->lm3533, reg, val);
+ if (ret)
+ dev_err(cb->dev, "failed to get brightness\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_ctrlbank_get_brightness);
/*
* PWM-input control mask:
@@ -135,9 +126,36 @@ lm3533_ctrlbank_get(brightness, BRIGHTNESS);
* bit 1 - PWM-input enabled in Zone 0
* bit 0 - PWM-input enabled
*/
-lm3533_ctrlbank_set(pwm, PWM);
-lm3533_ctrlbank_get(pwm, PWM);
+int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u8 val)
+{
+ u8 reg;
+ int ret;
+
+ if (val > LM3533_PWM_MAX)
+ return -EINVAL;
+
+ reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_PWM_BASE);
+ ret = lm3533_write(cb->lm3533, reg, val);
+ if (ret)
+ dev_err(cb->dev, "failed to set PWM mask\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_pwm);
+
+int lm3533_ctrlbank_get_pwm(struct lm3533_ctrlbank *cb, u8 *val)
+{
+ u8 reg;
+ int ret;
+ reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_PWM_BASE);
+ ret = lm3533_read(cb->lm3533, reg, val);
+ if (ret)
+ dev_err(cb->dev, "failed to get PWM mask\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_ctrlbank_get_pwm);
MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
MODULE_DESCRIPTION("LM3533 Control Bank interface");
diff --git a/drivers/mfd/lp873x.c b/drivers/mfd/lp873x.c
index 873c608e6a5d..858c9e0a49a4 100644
--- a/drivers/mfd/lp873x.c
+++ b/drivers/mfd/lp873x.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com/
*
* Author: Keerthy <j-keerthy@ti.com>
*
diff --git a/drivers/mfd/lp87565.c b/drivers/mfd/lp87565.c
index 4a5c8ade4ae0..2268be9113f1 100644
--- a/drivers/mfd/lp87565.c
+++ b/drivers/mfd/lp87565.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/
*
* Author: Keerthy <j-keerthy@ti.com>
*/
diff --git a/drivers/mfd/madera-core.c b/drivers/mfd/madera-core.c
index 7e0835cb062b..8a8d733fdce5 100644
--- a/drivers/mfd/madera-core.c
+++ b/drivers/mfd/madera-core.c
@@ -44,7 +44,10 @@ static const char * const madera_core_supplies[] = {
};
static const struct mfd_cell madera_ldo1_devs[] = {
- { .name = "madera-ldo1" },
+ {
+ .name = "madera-ldo1",
+ .level = MFD_DEP_LEVEL_HIGH,
+ },
};
static const char * const cs47l15_supplies[] = {
@@ -55,8 +58,8 @@ static const char * const cs47l15_supplies[] = {
static const struct mfd_cell cs47l15_devs[] = {
{ .name = "madera-pinctrl", },
- { .name = "madera-irq" },
- { .name = "madera-gpio" },
+ { .name = "madera-irq", },
+ { .name = "madera-gpio", },
{
.name = "madera-extcon",
.parent_supplies = cs47l15_supplies,
@@ -108,7 +111,7 @@ static const char * const cs47l85_supplies[] = {
static const struct mfd_cell cs47l85_devs[] = {
{ .name = "madera-pinctrl", },
{ .name = "madera-irq", },
- { .name = "madera-micsupp" },
+ { .name = "madera-micsupp", },
{ .name = "madera-gpio", },
{
.name = "madera-extcon",
@@ -155,10 +158,10 @@ static const char * const cs47l92_supplies[] = {
};
static const struct mfd_cell cs47l92_devs[] = {
- { .name = "madera-pinctrl" },
+ { .name = "madera-pinctrl", },
{ .name = "madera-irq", },
{ .name = "madera-micsupp", },
- { .name = "madera-gpio" },
+ { .name = "madera-gpio", },
{
.name = "madera-extcon",
.parent_supplies = cs47l92_supplies,
@@ -743,18 +746,22 @@ int madera_dev_exit(struct madera *madera)
/* Prevent any IRQs being serviced while we clean up */
disable_irq(madera->irq);
- /*
- * DCVDD could be supplied by a child node, we must disable it before
- * removing the children, and prevent PM runtime from turning it back on
- */
- pm_runtime_disable(madera->dev);
+ pm_runtime_get_sync(madera->dev);
- clk_disable_unprepare(madera->mclk[MADERA_MCLK2].clk);
+ mfd_remove_devices(madera->dev);
+
+ pm_runtime_disable(madera->dev);
regulator_disable(madera->dcvdd);
regulator_put(madera->dcvdd);
- mfd_remove_devices(madera->dev);
+ mfd_remove_devices_late(madera->dev);
+
+ pm_runtime_set_suspended(madera->dev);
+ pm_runtime_put_noidle(madera->dev);
+
+ clk_disable_unprepare(madera->mclk[MADERA_MCLK2].clk);
+
madera_enable_hard_reset(madera);
regulator_bulk_disable(madera->num_core_supplies,
diff --git a/drivers/mfd/madera-i2c.c b/drivers/mfd/madera-i2c.c
index 6b965eb034b6..7df5b9ba5855 100644
--- a/drivers/mfd/madera-i2c.c
+++ b/drivers/mfd/madera-i2c.c
@@ -88,7 +88,6 @@ static int madera_i2c_probe(struct i2c_client *i2c,
if (!madera)
return -ENOMEM;
-
madera->regmap = devm_regmap_init_i2c(i2c, regmap_16bit_config);
if (IS_ERR(madera->regmap)) {
ret = PTR_ERR(madera->regmap);
diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c
index fd8864cafd25..be185e9d5f16 100644
--- a/drivers/mfd/max14577.c
+++ b/drivers/mfd/max14577.c
@@ -61,7 +61,7 @@ EXPORT_SYMBOL_GPL(maxim_charger_currents);
int maxim_charger_calc_reg_current(const struct maxim_charger_current *limits,
unsigned int min_ua, unsigned int max_ua, u8 *dst)
{
- unsigned int current_bits = 0xf;
+ unsigned int current_bits;
if (min_ua > max_ua)
return -EINVAL;
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index f5a73af60dd4..c3651f06684f 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
+#include <linux/list.h>
#include <linux/property.h>
#include <linux/mfd/core.h>
#include <linux/pm_runtime.h>
@@ -17,8 +18,17 @@
#include <linux/module.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/regulator/consumer.h>
+static LIST_HEAD(mfd_of_node_list);
+
+struct mfd_of_node_entry {
+ struct list_head list;
+ struct device *dev;
+ struct device_node *np;
+};
+
static struct device_type mfd_dev_type = {
.name = "mfd_device",
};
@@ -107,6 +117,55 @@ static inline void mfd_acpi_add_device(const struct mfd_cell *cell,
}
#endif
+static int mfd_match_of_node_to_dev(struct platform_device *pdev,
+ struct device_node *np,
+ const struct mfd_cell *cell)
+{
+#if IS_ENABLED(CONFIG_OF)
+ struct mfd_of_node_entry *of_entry;
+ const __be32 *reg;
+ u64 of_node_addr;
+
+ /* Skip devices 'disabled' by Device Tree */
+ if (!of_device_is_available(np))
+ return -ENODEV;
+
+ /* Skip if OF node has previously been allocated to a device */
+ list_for_each_entry(of_entry, &mfd_of_node_list, list)
+ if (of_entry->np == np)
+ return -EAGAIN;
+
+ if (!cell->use_of_reg)
+ /* No of_reg defined - allocate first free compatible match */
+ goto allocate_of_node;
+
+ /* We only care about each node's first defined address */
+ reg = of_get_address(np, 0, NULL, NULL);
+ if (!reg)
+ /* 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;
+
+allocate_of_node:
+ of_entry = kzalloc(sizeof(*of_entry), GFP_KERNEL);
+ if (!of_entry)
+ return -ENOMEM;
+
+ of_entry->dev = &pdev->dev;
+ of_entry->np = np;
+ list_add_tail(&of_entry->list, &mfd_of_node_list);
+
+ pdev->dev.of_node = np;
+ pdev->dev.fwnode = &np->fwnode;
+#endif
+ return 0;
+}
+
static int mfd_add_device(struct device *parent, int id,
const struct mfd_cell *cell,
struct resource *mem_base,
@@ -115,6 +174,7 @@ static int mfd_add_device(struct device *parent, int id,
struct resource *res;
struct platform_device *pdev;
struct device_node *np = NULL;
+ struct mfd_of_node_entry *of_entry, *tmp;
int ret = -ENOMEM;
int platform_id;
int r;
@@ -149,19 +209,22 @@ static int mfd_add_device(struct device *parent, int id,
if (ret < 0)
goto fail_res;
- if (parent->of_node && cell->of_compatible) {
+ 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)) {
- if (!of_device_is_available(np)) {
- /* Ignore disabled devices error free */
- ret = 0;
+ ret = mfd_match_of_node_to_dev(pdev, np, cell);
+ if (ret == -EAGAIN)
+ continue;
+ if (ret)
goto fail_alias;
- }
- pdev->dev.of_node = np;
- pdev->dev.fwnode = &np->fwnode;
+
break;
}
}
+
+ if (!pdev->dev.of_node)
+ pr_warn("%s: Failed to locate of_node [id: %d]\n",
+ cell->name, platform_id);
}
mfd_acpi_add_device(cell, pdev);
@@ -170,13 +233,13 @@ static int mfd_add_device(struct device *parent, int id,
ret = platform_device_add_data(pdev,
cell->platform_data, cell->pdata_size);
if (ret)
- goto fail_alias;
+ goto fail_of_entry;
}
if (cell->properties) {
ret = platform_device_add_properties(pdev, cell->properties);
if (ret)
- goto fail_alias;
+ goto fail_of_entry;
}
for (r = 0; r < cell->num_resources; r++) {
@@ -213,18 +276,18 @@ static int mfd_add_device(struct device *parent, int id,
if (has_acpi_companion(&pdev->dev)) {
ret = acpi_check_resource_conflict(&res[r]);
if (ret)
- goto fail_alias;
+ goto fail_of_entry;
}
}
}
ret = platform_device_add_resources(pdev, res, cell->num_resources);
if (ret)
- goto fail_alias;
+ goto fail_of_entry;
ret = platform_device_add(pdev);
if (ret)
- goto fail_alias;
+ goto fail_of_entry;
if (cell->pm_runtime_no_callbacks)
pm_runtime_no_callbacks(&pdev->dev);
@@ -233,6 +296,12 @@ static int mfd_add_device(struct device *parent, int id,
return 0;
+fail_of_entry:
+ 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);
+ }
fail_alias:
regulator_bulk_unregister_supply_alias(&pdev->dev,
cell->parent_supplies,
@@ -287,6 +356,7 @@ static int mfd_remove_devices_fn(struct device *dev, void *data)
{
struct platform_device *pdev;
const struct mfd_cell *cell;
+ int *level = data;
if (dev->type != &mfd_dev_type)
return 0;
@@ -294,16 +364,31 @@ static int mfd_remove_devices_fn(struct device *dev, void *data)
pdev = to_platform_device(dev);
cell = mfd_get_cell(pdev);
+ if (level && cell->level > *level)
+ return 0;
+
regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies,
cell->num_parent_supplies);
+ kfree(cell);
+
platform_device_unregister(pdev);
return 0;
}
+void mfd_remove_devices_late(struct device *parent)
+{
+ int level = MFD_DEP_LEVEL_HIGH;
+
+ device_for_each_child_reverse(parent, &level, mfd_remove_devices_fn);
+}
+EXPORT_SYMBOL(mfd_remove_devices_late);
+
void mfd_remove_devices(struct device *parent)
{
- device_for_each_child_reverse(parent, NULL, mfd_remove_devices_fn);
+ int level = MFD_DEP_LEVEL_NORMAL;
+
+ device_for_each_child_reverse(parent, &level, mfd_remove_devices_fn);
}
EXPORT_SYMBOL(mfd_remove_devices);
@@ -318,6 +403,16 @@ static void devm_mfd_dev_release(struct device *dev, void *res)
* Returns 0 on success or an appropriate negative error number on failure.
* All child-devices of the MFD will automatically be removed when it gets
* unbinded.
+ *
+ * @dev: Pointer to parent device.
+ * @id: Can be PLATFORM_DEVID_AUTO to let the Platform API take care
+ * of device numbering, or will be added to a device's cell_id.
+ * @cells: Array of (struct mfd_cell)s describing child devices.
+ * @n_devs: Number of child devices to register.
+ * @mem_base: Parent register range resource for child devices.
+ * @irq_base: Base of the range of virtual interrupt numbers allocated for
+ * this MFD device. Unused if @domain is specified.
+ * @domain: Interrupt domain to create mappings for hardware interrupts.
*/
int devm_mfd_add_devices(struct device *dev, int id,
const struct mfd_cell *cells, int n_devs,
diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c
index 52f38e57cdc1..2283d88adcc2 100644
--- a/drivers/mfd/motorola-cpcap.c
+++ b/drivers/mfd/motorola-cpcap.c
@@ -214,6 +214,28 @@ 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);
+
+ disable_irq(spi->irq);
+
+ return 0;
+}
+
+static int cpcap_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ enable_irq(spi->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cpcap_pm, cpcap_suspend, cpcap_resume);
+
static const struct mfd_cell cpcap_mfd_devices[] = {
{
.name = "cpcap_adc",
@@ -313,6 +335,7 @@ static struct spi_driver cpcap_driver = {
.driver = {
.name = "cpcap-core",
.of_match_table = cpcap_of_match,
+ .pm = &cpcap_pm,
},
.probe = cpcap_probe,
};
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 1f4f01b02d98..1e6431cb8536 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -2,7 +2,7 @@
/**
* omap-usb-host.c - The USBHS core driver for OMAP EHCI & OHCI
*
- * Copyright (C) 2011-2013 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2011-2013 Texas Instruments Incorporated - https://www.ti.com
* Author: Keshava Munegowda <keshava_mgowda@ti.com>
* Author: Roger Quadros <rogerq@ti.com>
*/
@@ -120,7 +120,7 @@ static inline u32 usbhs_read(void __iomem *base, u32 reg)
/*-------------------------------------------------------------------------*/
-/**
+/*
* Map 'enum usbhs_omap_port_mode' found in <linux/platform_data/usb-omap.h>
* to the device tree binding portN-mode found in
* 'Documentation/devicetree/bindings/mfd/omap-usb-host.txt'
@@ -526,6 +526,8 @@ static const struct of_device_id usbhs_child_match_table[] = {
* usbhs_omap_probe - initialize TI-based HCDs
*
* Allocates basic resources for this USB host controller.
+ *
+ * @pdev: Pointer to this device's platform device structure
*/
static int usbhs_omap_probe(struct platform_device *pdev)
{
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index 4b7f73c317e8..16fad79c73f1 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -2,7 +2,7 @@
/**
* omap-usb-tll.c - The USB TLL driver for OMAP EHCI & OHCI
*
- * Copyright (C) 2012-2013 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2012-2013 Texas Instruments Incorporated - https://www.ti.com
* Author: Keshava Munegowda <keshava_mgowda@ti.com>
* Author: Roger Quadros <rogerq@ti.com>
*/
@@ -199,6 +199,8 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode)
* usbtll_omap_probe - initialize TI-based HCDs
*
* Allocates basic resources for this USB host controller.
+ *
+ * @pdev: Pointer to this device's platform device structure
*/
static int usbtll_omap_probe(struct platform_device *pdev)
{
diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c
index 26c7b63e008a..abaab541df19 100644
--- a/drivers/mfd/rave-sp.c
+++ b/drivers/mfd/rave-sp.c
@@ -96,7 +96,7 @@ struct rave_sp_deframer {
* @data: Buffer to store reply payload in
* @code: Expected reply code
* @ackid: Expected reply ACK ID
- * @completion: Successful reply reception completion
+ * @received: Successful reply reception completion
*/
struct rave_sp_reply {
size_t length;
diff --git a/drivers/mfd/rn5t618.c b/drivers/mfd/rn5t618.c
index 232de50562f9..e25407ed3ad4 100644
--- a/drivers/mfd/rn5t618.c
+++ b/drivers/mfd/rn5t618.c
@@ -44,6 +44,9 @@ static bool rn5t618_volatile_reg(struct device *dev, unsigned int reg)
case RN5T618_INTMON:
case RN5T618_RTC_CTRL1 ... RN5T618_RTC_CTRL2:
case RN5T618_RTC_SECONDS ... RN5T618_RTC_YEAR:
+ case RN5T618_CHGSTATE:
+ case RN5T618_CHGCTRL_IRR ... RN5T618_CHGERR_MONI:
+ case RN5T618_CONTROL ... RN5T618_CC_AVEREG0:
return true;
default:
return false;
@@ -77,7 +80,7 @@ static const struct regmap_irq_chip rc5t619_irq_chip = {
.mask_invert = true,
};
-static struct rn5t618 *rn5t618_pm_power_off;
+static struct i2c_client *rn5t618_pm_power_off;
static struct notifier_block rn5t618_restart_handler;
static int rn5t618_irq_init(struct rn5t618 *rn5t618)
@@ -110,13 +113,38 @@ static int rn5t618_irq_init(struct rn5t618 *rn5t618)
static void rn5t618_trigger_poweroff_sequence(bool repower)
{
+ int ret;
+
/* disable automatic repower-on */
- regmap_update_bits(rn5t618_pm_power_off->regmap, RN5T618_REPCNT,
- RN5T618_REPCNT_REPWRON,
- repower ? RN5T618_REPCNT_REPWRON : 0);
+ ret = i2c_smbus_read_byte_data(rn5t618_pm_power_off, RN5T618_REPCNT);
+ if (ret < 0)
+ goto err;
+
+ ret &= ~RN5T618_REPCNT_REPWRON;
+ if (repower)
+ ret |= RN5T618_REPCNT_REPWRON;
+
+ ret = i2c_smbus_write_byte_data(rn5t618_pm_power_off,
+ RN5T618_REPCNT, (u8)ret);
+ if (ret < 0)
+ goto err;
+
/* start power-off sequence */
- regmap_update_bits(rn5t618_pm_power_off->regmap, RN5T618_SLPCNT,
- RN5T618_SLPCNT_SWPWROFF, RN5T618_SLPCNT_SWPWROFF);
+ ret = i2c_smbus_read_byte_data(rn5t618_pm_power_off, RN5T618_SLPCNT);
+ if (ret < 0)
+ goto err;
+
+ ret |= RN5T618_SLPCNT_SWPWROFF;
+
+ ret = i2c_smbus_write_byte_data(rn5t618_pm_power_off,
+ RN5T618_SLPCNT, (u8)ret);
+ if (ret < 0)
+ goto err;
+
+ return;
+
+err:
+ dev_alert(&rn5t618_pm_power_off->dev, "Failed to shutdown (err = %d)\n", ret);
}
static void rn5t618_power_off(void)
@@ -189,7 +217,7 @@ static int rn5t618_i2c_probe(struct i2c_client *i2c)
return ret;
}
- rn5t618_pm_power_off = priv;
+ rn5t618_pm_power_off = i2c;
if (of_device_is_system_power_controller(i2c->dev.of_node)) {
if (!pm_power_off)
pm_power_off = rn5t618_power_off;
@@ -211,9 +239,7 @@ static int rn5t618_i2c_probe(struct i2c_client *i2c)
static int rn5t618_i2c_remove(struct i2c_client *i2c)
{
- struct rn5t618 *priv = i2c_get_clientdata(i2c);
-
- if (priv == rn5t618_pm_power_off) {
+ if (i2c == rn5t618_pm_power_off) {
rn5t618_pm_power_off = NULL;
pm_power_off = NULL;
}
diff --git a/drivers/mfd/si476x-cmd.c b/drivers/mfd/si476x-cmd.c
index 4a09ce9609c9..d15b3e783369 100644
--- a/drivers/mfd/si476x-cmd.c
+++ b/drivers/mfd/si476x-cmd.c
@@ -241,13 +241,13 @@ static int si476x_core_parse_and_nag_about_error(struct si476x_core *core)
/**
* si476x_core_send_command() - sends a command to si476x and waits its
* response
- * @core: si476x_device structure for the device we are
+ * @core: si476x_device structure for the device we are
* communicating with
* @command: command id
* @args: command arguments we are sending
* @argn: actual size of @args
- * @response: buffer to place the expected response from the device
- * @respn: actual size of @response
+ * @resp: buffer to place the expected response from the device
+ * @respn: actual size of @resp
* @usecs: amount of time to wait before reading the response (in
* usecs)
*
@@ -496,7 +496,7 @@ EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
* enable 1MOhm pulldown
* SI476X_DFS_DAUDIO - set the pin to be a part of digital
* audio interface
- * @dout - DOUT pin function configuration:
+ * @dout: - DOUT pin function configuration:
* SI476X_DOUT_NOOP - do not modify the behaviour
* SI476X_DOUT_TRISTATE - put the pin in tristate condition,
* enable 1MOhm pulldown
@@ -504,7 +504,7 @@ EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
* port 1
* SI476X_DOUT_I2S_INPUT - set this pin to be digital in on I2S
* port 1
- * @xout - XOUT pin function configuration:
+ * @xout: - XOUT pin function configuration:
* SI476X_XOUT_NOOP - do not modify the behaviour
* SI476X_XOUT_TRISTATE - put the pin in tristate condition,
* enable 1MOhm pulldown
@@ -540,25 +540,25 @@ EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg);
/**
* si476x_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND'
- * @core - device to send the command to
- * @iqclk - IQCL pin function configuration:
+ * @core: - device to send the command to
+ * @iqclk: - IQCL pin function configuration:
* 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
* in master mode
- * @iqfs - IQFS pin function configuration:
+ * @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
* in master mode
- * @iout - IOUT pin function configuration:
+ * @iout: - IOUT pin function configuration:
* SI476X_IOUT_NOOP - do not modify the behaviour
* SI476X_IOUT_TRISTATE - put the pin in tristate condition,
* enable 1MOhm pulldown
* SI476X_IOUT_OUTPUT - set pin to be I out
- * @qout - QOUT pin function configuration:
+ * @qout: - QOUT pin function configuration:
* SI476X_QOUT_NOOP - do not modify the behaviour
* SI476X_QOUT_TRISTATE - put the pin in tristate condition,
* enable 1MOhm pulldown
@@ -590,29 +590,29 @@ EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg);
/**
* si476x_cmd_ic_link_gpo_ctl_pin_cfg - send
* 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device
- * @core - device to send the command to
- * @icin - ICIN pin function configuration:
+ * @core: - device to send the command to
+ * @icin: - ICIN pin function configuration:
* SI476X_ICIN_NOOP - do not modify the behaviour
* SI476X_ICIN_TRISTATE - put the pin in tristate condition,
* enable 1MOhm pulldown
* SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high
* SI476X_ICIN_GPO1_LOW - set pin to be an output, drive it low
* SI476X_ICIN_IC_LINK - set the pin to be a part of Inter-Chip link
- * @icip - ICIP pin function configuration:
+ * @icip: - ICIP pin function configuration:
* SI476X_ICIP_NOOP - do not modify the behaviour
* SI476X_ICIP_TRISTATE - put the pin in tristate condition,
* enable 1MOhm pulldown
* SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high
* SI476X_ICIP_GPO1_LOW - set pin to be an output, drive it low
* SI476X_ICIP_IC_LINK - set the pin to be a part of Inter-Chip link
- * @icon - ICON pin function configuration:
+ * @icon: - ICON pin function configuration:
* SI476X_ICON_NOOP - do not modify the behaviour
* SI476X_ICON_TRISTATE - put the pin in tristate condition,
* enable 1MOhm pulldown
* SI476X_ICON_I2S - set the pin to be a part of audio
* interface in slave mode (DCLK)
* SI476X_ICON_IC_LINK - set the pin to be a part of Inter-Chip link
- * @icop - ICOP pin function configuration:
+ * @icop: - ICOP pin function configuration:
* SI476X_ICOP_NOOP - do not modify the behaviour
* SI476X_ICOP_TRISTATE - put the pin in tristate condition,
* enable 1MOhm pulldown
@@ -647,8 +647,8 @@ EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg);
/**
* si476x_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the
* device
- * @core - device to send the command to
- * @lrout - LROUT pin function configuration:
+ * @core: - device to send the command to
+ * @lrout: - LROUT pin function configuration:
* SI476X_LROUT_NOOP - do not modify the behaviour
* SI476X_LROUT_TRISTATE - put the pin in tristate condition,
* enable 1MOhm pulldown
@@ -675,15 +675,15 @@ EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg);
/**
* si476x_cmd_intb_pin_cfg - send 'INTB_PIN_CFG' command to the device
- * @core - device to send the command to
- * @intb - INTB pin function configuration:
+ * @core: - device to send the command to
+ * @intb: - INTB pin function configuration:
* SI476X_INTB_NOOP - do not modify the behaviour
* SI476X_INTB_TRISTATE - put the pin in tristate condition,
* enable 1MOhm pulldown
* SI476X_INTB_DAUDIO - set pin to be a part of digital
* audio interface in slave mode
* SI476X_INTB_IRQ - set pin to be an interrupt request line
- * @a1 - A1 pin function configuration:
+ * @a1: - A1 pin function configuration:
* SI476X_A1_NOOP - do not modify the behaviour
* SI476X_A1_TRISTATE - put the pin in tristate condition,
* enable 1MOhm pulldown
@@ -728,14 +728,10 @@ static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core,
/**
* si476x_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the
* device
- * @core - device to send the command to
- * @rsqack - if set command clears RSQINT, SNRINT, SNRLINT, RSSIHINT,
- * RSSSILINT, BLENDINT, MULTHINT and MULTLINT
- * @attune - when set the values in the status report are the values
- * that were calculated at tune
- * @cancel - abort ongoing seek/tune opertation
- * @stcack - clear the STCINT bin in status register
- * @report - all signal quality information retured by the command
+ * @core: - device to send the command to
+ * @rsqargs: - pointer to a structure containing a group of sub-args
+ * relevant to sending the RSQ status command
+ * @report: - all signal quality information retured by the command
* (if NULL then the output of the command is ignored)
*
* Function returns 0 on success and negative error code on failure
@@ -862,9 +858,9 @@ EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
/**
* si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
* device
- * @core - device to send the command to
- * @seekup - if set the direction of the search is 'up'
- * @wrap - if set seek wraps when hitting band limit
+ * @core: - device to send the command to
+ * @seekup: - if set the direction of the search is 'up'
+ * @wrap: - if set seek wraps when hitting band limit
*
* This function begins search for a valid station. The station is
* considered valid when 'FM_VALID_SNR_THRESHOLD' and
@@ -890,12 +886,14 @@ EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
/**
* si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
* device
- * @core - device to send the command to
- * @status_only - if set the data is not removed from RDSFIFO,
+ * @core: - device to send the command to
+ * @status_only: - if set the data is not removed from RDSFIFO,
* RDSFIFOUSED is not decremented and data in all the
* rest RDS data contains the last valid info received
- * @mtfifo if set the command clears RDS receive FIFO
- * @intack if set the command clards the RDSINT bit.
+ * @mtfifo: if set the command clears RDS receive FIFO
+ * @intack: if set the command clards the RDSINT bit.
+ * @report: - all signal quality information retured by the command
+ * (if NULL then the output of the command is ignored)
*
* Function returns 0 on success and negative error code on failure
*/
@@ -1036,9 +1034,9 @@ EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
/**
* si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the
* device
- * @core - device to send the command to
- * @seekup - if set the direction of the search is 'up'
- * @wrap - if set seek wraps when hitting band limit
+ * @core: - device to send the command to
+ * @seekup: - if set the direction of the search is 'up'
+ * @wrap: - if set seek wraps when hitting band limit
*
* This function begins search for a valid station. The station is
* considered valid when 'FM_VALID_SNR_THRESHOLD' and
diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c
index c8d28b844def..c1d7b845244e 100644
--- a/drivers/mfd/si476x-i2c.c
+++ b/drivers/mfd/si476x-i2c.c
@@ -534,6 +534,11 @@ static irqreturn_t si476x_core_interrupt(int irq, void *dev)
/**
* si476x_firmware_version_to_revision()
* @core: Core device structure
+ * @func: Selects the boot function of the device:
+ * *_BOOTLOADER - Boot loader
+ * *_FM_RECEIVER - FM receiver
+ * *_AM_RECEIVER - AM receiver
+ * *_WB_RECEIVER - Weatherband receiver
* @major: Firmware major number
* @minor1: Firmware first minor number
* @minor2: Firmware second minor number
@@ -583,7 +588,7 @@ static int si476x_core_fwver_to_revision(struct si476x_core *core,
goto unknown_revision;
}
case SI476X_FUNC_BOOTLOADER:
- default: /* FALLTHROUG */
+ default: /* FALLTHROUGH */
BUG();
return -1;
}
diff --git a/drivers/mfd/smsc-ece1099.c b/drivers/mfd/smsc-ece1099.c
deleted file mode 100644
index 57b792eb58fd..000000000000
--- a/drivers/mfd/smsc-ece1099.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * TI SMSC MFD Driver
- *
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
- *
- * Author: Sourav Poddar <sourav.poddar@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; GPL v2.
- *
- */
-
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/gpio.h>
-#include <linux/workqueue.h>
-#include <linux/irq.h>
-#include <linux/regmap.h>
-#include <linux/err.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/smsc.h>
-#include <linux/of_platform.h>
-
-static const struct regmap_config smsc_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = SMSC_VEN_ID_H,
- .cache_type = REGCACHE_RBTREE,
-};
-
-static int smsc_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
-{
- struct smsc *smsc;
- int devid, rev, venid_l, venid_h;
- int ret;
-
- smsc = devm_kzalloc(&i2c->dev, sizeof(*smsc), GFP_KERNEL);
- if (!smsc)
- return -ENOMEM;
-
- smsc->regmap = devm_regmap_init_i2c(i2c, &smsc_regmap_config);
- if (IS_ERR(smsc->regmap))
- return PTR_ERR(smsc->regmap);
-
- i2c_set_clientdata(i2c, smsc);
- smsc->dev = &i2c->dev;
-
-#ifdef CONFIG_OF
- of_property_read_u32(i2c->dev.of_node, "clock", &smsc->clk);
-#endif
-
- regmap_read(smsc->regmap, SMSC_DEV_ID, &devid);
- regmap_read(smsc->regmap, SMSC_DEV_REV, &rev);
- regmap_read(smsc->regmap, SMSC_VEN_ID_L, &venid_l);
- regmap_read(smsc->regmap, SMSC_VEN_ID_H, &venid_h);
-
- dev_info(&i2c->dev, "SMSCxxx devid: %02x rev: %02x venid: %02x\n",
- devid, rev, (venid_h << 8) | venid_l);
-
- ret = regmap_write(smsc->regmap, SMSC_CLK_CTRL, smsc->clk);
- if (ret)
- return ret;
-
-#ifdef CONFIG_OF
- if (i2c->dev.of_node)
- ret = devm_of_platform_populate(&i2c->dev);
-#endif
-
- return ret;
-}
-
-static const struct i2c_device_id smsc_i2c_id[] = {
- { "smscece1099", 0},
- {},
-};
-
-static struct i2c_driver smsc_i2c_driver = {
- .driver = {
- .name = "smsc",
- },
- .probe = smsc_i2c_probe,
- .id_table = smsc_i2c_id,
-};
-builtin_i2c_driver(smsc_i2c_driver);
diff --git a/drivers/mfd/sprd-sc27xx-spi.c b/drivers/mfd/sprd-sc27xx-spi.c
index 33336cde4724..f8a8b918c60d 100644
--- a/drivers/mfd/sprd-sc27xx-spi.c
+++ b/drivers/mfd/sprd-sc27xx-spi.c
@@ -7,7 +7,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mfd/core.h>
+#include <linux/mfd/sc27xx-pmic.h>
#include <linux/of_device.h>
+#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <uapi/linux/usb/charger.h>
@@ -93,73 +95,6 @@ enum usb_charger_type sprd_pmic_detect_charger_type(struct device *dev)
}
EXPORT_SYMBOL_GPL(sprd_pmic_detect_charger_type);
-static const struct mfd_cell sprd_pmic_devs[] = {
- {
- .name = "sc27xx-wdt",
- .of_compatible = "sprd,sc2731-wdt",
- }, {
- .name = "sc27xx-rtc",
- .of_compatible = "sprd,sc2731-rtc",
- }, {
- .name = "sc27xx-charger",
- .of_compatible = "sprd,sc2731-charger",
- }, {
- .name = "sc27xx-chg-timer",
- .of_compatible = "sprd,sc2731-chg-timer",
- }, {
- .name = "sc27xx-fast-chg",
- .of_compatible = "sprd,sc2731-fast-chg",
- }, {
- .name = "sc27xx-chg-wdt",
- .of_compatible = "sprd,sc2731-chg-wdt",
- }, {
- .name = "sc27xx-typec",
- .of_compatible = "sprd,sc2731-typec",
- }, {
- .name = "sc27xx-flash",
- .of_compatible = "sprd,sc2731-flash",
- }, {
- .name = "sc27xx-eic",
- .of_compatible = "sprd,sc2731-eic",
- }, {
- .name = "sc27xx-efuse",
- .of_compatible = "sprd,sc2731-efuse",
- }, {
- .name = "sc27xx-thermal",
- .of_compatible = "sprd,sc2731-thermal",
- }, {
- .name = "sc27xx-adc",
- .of_compatible = "sprd,sc2731-adc",
- }, {
- .name = "sc27xx-audio-codec",
- .of_compatible = "sprd,sc2731-audio-codec",
- }, {
- .name = "sc27xx-regulator",
- .of_compatible = "sprd,sc2731-regulator",
- }, {
- .name = "sc27xx-vibrator",
- .of_compatible = "sprd,sc2731-vibrator",
- }, {
- .name = "sc27xx-keypad-led",
- .of_compatible = "sprd,sc2731-keypad-led",
- }, {
- .name = "sc27xx-bltc",
- .of_compatible = "sprd,sc2731-bltc",
- }, {
- .name = "sc27xx-fgu",
- .of_compatible = "sprd,sc2731-fgu",
- }, {
- .name = "sc27xx-7sreset",
- .of_compatible = "sprd,sc2731-7sreset",
- }, {
- .name = "sc27xx-poweroff",
- .of_compatible = "sprd,sc2731-poweroff",
- }, {
- .name = "sc27xx-syscon",
- .of_compatible = "sprd,sc2731-syscon",
- },
-};
-
static int sprd_pmic_spi_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
@@ -250,10 +185,8 @@ static int sprd_pmic_probe(struct spi_device *spi)
return -ENOMEM;
ddata->irq_chip.irqs = ddata->irqs;
- for (i = 0; i < pdata->num_irqs; i++) {
- ddata->irqs[i].reg_offset = i / pdata->num_irqs;
- ddata->irqs[i].mask = BIT(i % pdata->num_irqs);
- }
+ for (i = 0; i < pdata->num_irqs; i++)
+ ddata->irqs[i].mask = BIT(i);
ret = devm_regmap_add_irq_chip(&spi->dev, ddata->regmap, ddata->irq,
IRQF_ONESHOT | IRQF_NO_SUSPEND, 0,
@@ -263,12 +196,9 @@ static int sprd_pmic_probe(struct spi_device *spi)
return ret;
}
- ret = devm_mfd_add_devices(&spi->dev, PLATFORM_DEVID_AUTO,
- sprd_pmic_devs, ARRAY_SIZE(sprd_pmic_devs),
- NULL, 0,
- regmap_irq_get_domain(ddata->irq_data));
+ ret = devm_of_platform_populate(&spi->dev);
if (ret) {
- dev_err(&spi->dev, "Failed to register device %d\n", ret);
+ dev_err(&spi->dev, "Failed to populate sub-devices %d\n", ret);
return ret;
}
diff --git a/drivers/mfd/stm32-lptimer.c b/drivers/mfd/stm32-lptimer.c
index a00f99f36559..746e51a17cc8 100644
--- a/drivers/mfd/stm32-lptimer.c
+++ b/drivers/mfd/stm32-lptimer.c
@@ -17,6 +17,7 @@ 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)
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index 3a97816d0cba..75859e492984 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -101,12 +101,14 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
}
}
- syscon_config.name = of_node_full_name(np);
+ syscon_config.name = kasprintf(GFP_KERNEL, "%pOFn@%llx", np,
+ (u64)res.start);
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;
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);
diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c
index 67c9995bb1aa..7882a37ffc35 100644
--- a/drivers/mfd/tc3589x.c
+++ b/drivers/mfd/tc3589x.c
@@ -18,7 +18,7 @@
#include <linux/mfd/tc3589x.h>
#include <linux/err.h>
-/**
+/*
* enum tc3589x_version - indicates the TC3589x version
*/
enum tc3589x_version {
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index 926c289cb040..0e6e25308190 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -1,7 +1,7 @@
/*
* TI Touch Screen / ADC MFD driver
*
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * 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
diff --git a/drivers/mfd/tps65010.c b/drivers/mfd/tps65010.c
index 65fcc58c02da..7e7dbee58ca9 100644
--- a/drivers/mfd/tps65010.c
+++ b/drivers/mfd/tps65010.c
@@ -404,7 +404,6 @@ static void tps65010_work(struct work_struct *work)
tps65010_interrupt(tps);
if (test_and_clear_bit(FLAG_VBUS_CHANGED, &tps->flags)) {
- int status;
u8 chgconfig, tmp;
chgconfig = i2c_smbus_read_byte_data(tps->client,
@@ -415,8 +414,8 @@ static void tps65010_work(struct work_struct *work)
else if (tps->vbus >= 100)
chgconfig |= TPS_VBUS_CHARGING;
- status = i2c_smbus_write_byte_data(tps->client,
- TPS_CHGCONFIG, chgconfig);
+ i2c_smbus_write_byte_data(tps->client,
+ TPS_CHGCONFIG, chgconfig);
/* vbus update fails unless VBUS is connected! */
tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG);
diff --git a/drivers/mfd/tps65086.c b/drivers/mfd/tps65086.c
index 43119a6867fe..341466ef20cc 100644
--- a/drivers/mfd/tps65086.c
+++ b/drivers/mfd/tps65086.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * 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
diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c
index 7566ce4457a0..2d9c282ec917 100644
--- a/drivers/mfd/tps65217.c
+++ b/drivers/mfd/tps65217.c
@@ -3,7 +3,7 @@
*
* TPS65217 chip family multi-function driver
*
- * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ * 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
@@ -205,7 +205,7 @@ EXPORT_SYMBOL_GPL(tps65217_reg_read);
/**
* tps65217_reg_write: Write a single tps65217 register.
*
- * @tps65217: Device to write to.
+ * @tps: Device to write to.
* @reg: Register to write to.
* @val: Value to write.
* @level: Password protected level
@@ -250,7 +250,7 @@ EXPORT_SYMBOL_GPL(tps65217_reg_write);
/**
* tps65217_update_bits: Modify bits w.r.t mask, val and level.
*
- * @tps65217: Device to write to.
+ * @tps: Device to write to.
* @reg: Register to read-write to.
* @mask: Mask.
* @val: Value to write.
diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c
index a62ea4cb8be7..167e9fc308ef 100644
--- a/drivers/mfd/tps65218.c
+++ b/drivers/mfd/tps65218.c
@@ -1,7 +1,7 @@
/*
* Driver for TPS65218 Integrated power management chipsets
*
- * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
+ * 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
@@ -48,7 +48,7 @@ static const struct mfd_cell tps65218_cells[] = {
/**
* tps65218_reg_write: Write a single tps65218 register.
*
- * @tps65218: Device to write to.
+ * @tps: Device to write to.
* @reg: Register to write to.
* @val: Value to write.
* @level: Password protected level
@@ -79,7 +79,7 @@ EXPORT_SYMBOL_GPL(tps65218_reg_write);
/**
* tps65218_update_bits: Modify bits w.r.t mask, val and level.
*
- * @tps65218: Device to write to.
+ * @tps: Device to write to.
* @reg: Register to read-write to.
* @mask: Mask.
* @val: Value to write.
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index c8aadd39324e..c36597797ddd 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -309,18 +309,19 @@ static const struct irq_domain_ops tps6586x_domain_ops = {
static irqreturn_t tps6586x_irq(int irq, void *data)
{
struct tps6586x *tps6586x = data;
- u32 acks;
+ uint32_t acks;
+ __le32 val;
int ret = 0;
ret = tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1,
- sizeof(acks), (uint8_t *)&acks);
+ sizeof(acks), (uint8_t *)&val);
if (ret < 0) {
dev_err(tps6586x->dev, "failed to read interrupt status\n");
return IRQ_NONE;
}
- acks = le32_to_cpu(acks);
+ acks = le32_to_cpu(val);
while (acks) {
int i = __ffs(acks);
diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c
index f33567bc428d..b55b1d5d6955 100644
--- a/drivers/mfd/tps65912-core.c
+++ b/drivers/mfd/tps65912-core.c
@@ -1,7 +1,7 @@
/*
* Core functions for TI TPS65912x PMICs
*
- * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * 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
diff --git a/drivers/mfd/tps65912-i2c.c b/drivers/mfd/tps65912-i2c.c
index 785d19f6f7c9..f7c22ea7b36c 100644
--- a/drivers/mfd/tps65912-i2c.c
+++ b/drivers/mfd/tps65912-i2c.c
@@ -1,7 +1,7 @@
/*
* I2C access driver for TI TPS65912x PMICs
*
- * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * 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
diff --git a/drivers/mfd/tps65912-spi.c b/drivers/mfd/tps65912-spi.c
index f78be039e463..21a8d6ac5c4a 100644
--- a/drivers/mfd/tps65912-spi.c
+++ b/drivers/mfd/tps65912-spi.c
@@ -1,7 +1,7 @@
/*
* SPI access driver for TI TPS65912x PMICs
*
- * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * 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
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index 910a304b397c..ab417438d1fa 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -477,7 +477,7 @@ static void twl4030_sih_bus_sync_unlock(struct irq_data *data)
if (agent->imr_change_pending) {
union {
- u32 word;
+ __le32 word;
u8 bytes[4];
} imr;
@@ -561,7 +561,7 @@ static inline int sih_read_isr(const struct sih *sih)
int status;
union {
u8 bytes[4];
- u32 word;
+ __le32 word;
} isr;
/* FIXME need retry-on-error ... */
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index 02f879b23d9f..b0344e5353e4 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -114,6 +114,8 @@ static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
* The WM831x has a user key preventing writes to particularly
* critical registers. This function locks those registers,
* allowing writes to them.
+ *
+ * @wm831x: pointer to local driver data structure
*/
void wm831x_reg_lock(struct wm831x *wm831x)
{
@@ -140,6 +142,8 @@ EXPORT_SYMBOL_GPL(wm831x_reg_lock);
* The WM831x has a user key preventing writes to particularly
* critical registers. This function locks those registers,
* preventing spurious writes.
+ *
+ * @wm831x: pointer to local driver data structure
*/
int wm831x_reg_unlock(struct wm831x *wm831x)
{
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index 42b16503e6cd..fbc77b218215 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -131,6 +131,8 @@ EXPORT_SYMBOL_GPL(wm8350_block_write);
* The WM8350 has a hardware lock which can be used to prevent writes to
* some registers (generally those which can cause particularly serious
* problems if misused). This function enables that lock.
+ *
+ * @wm8350: pointer to local driver data structure
*/
int wm8350_reg_lock(struct wm8350 *wm8350)
{
@@ -160,6 +162,8 @@ EXPORT_SYMBOL_GPL(wm8350_reg_lock);
* problems if misused). This function disables that lock so updates
* can be performed. For maximum safety this should be done only when
* required.
+ *
+ * @wm8350: pointer to local driver data structure
*/
int wm8350_reg_unlock(struct wm8350 *wm8350)
{
diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c
index 3055d6f47afc..0fe32a05421b 100644
--- a/drivers/mfd/wm8400-core.c
+++ b/drivers/mfd/wm8400-core.c
@@ -108,6 +108,8 @@ static const struct regmap_config wm8400_regmap_config = {
/**
* wm8400_reset_codec_reg_cache - Reset cached codec registers to
* their default values.
+ *
+ * @wm8400: pointer to local driver data structure
*/
void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400)
{
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index a2e710d46432..b668224f906d 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -499,4 +499,15 @@ config SPRD_THERMAL
help
Support for the Spreadtrum thermal sensor driver in the Linux thermal
framework.
+
+config KHADAS_MCU_FAN_THERMAL
+ tristate "Khadas MCU controller FAN cooling support"
+ depends on OF || COMPILE_TEST
+ depends on MFD_KHADAS_MCU
+ select MFD_CORE
+ select REGMAP
+ help
+ If you say yes here you get support for the FAN controlled
+ by the Microcontroller found on the Khadas VIM boards.
+
endif
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index b8d96d26f9ec..b64dd50a6629 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -61,3 +61,4 @@ obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o
obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o
obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o
+obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o
diff --git a/drivers/thermal/khadas_mcu_fan.c b/drivers/thermal/khadas_mcu_fan.c
new file mode 100644
index 000000000000..9eadd2d6413e
--- /dev/null
+++ b/drivers/thermal/khadas_mcu_fan.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Khadas MCU Controlled FAN driver
+ *
+ * Copyright (C) 2020 BayLibre SAS
+ * Author(s): Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/khadas-mcu.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+
+#define MAX_LEVEL 3
+
+struct khadas_mcu_fan_ctx {
+ struct khadas_mcu *mcu;
+ unsigned int level;
+ struct thermal_cooling_device *cdev;
+};
+
+static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx,
+ unsigned int level)
+{
+ int ret;
+
+ ret = regmap_write(ctx->mcu->regmap, KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG,
+ level);
+ if (ret)
+ return ret;
+
+ ctx->level = level;
+
+ return 0;
+}
+
+static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = MAX_LEVEL;
+
+ return 0;
+}
+
+static int khadas_mcu_fan_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct khadas_mcu_fan_ctx *ctx = cdev->devdata;
+
+ *state = ctx->level;
+
+ return 0;
+}
+
+static int
+khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ struct khadas_mcu_fan_ctx *ctx = cdev->devdata;
+
+ if (state > MAX_LEVEL)
+ return -EINVAL;
+
+ if (state == ctx->level)
+ return 0;
+
+ return khadas_mcu_fan_set_level(ctx, state);
+}
+
+static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = {
+ .get_max_state = khadas_mcu_fan_get_max_state,
+ .get_cur_state = khadas_mcu_fan_get_cur_state,
+ .set_cur_state = khadas_mcu_fan_set_cur_state,
+};
+
+static int khadas_mcu_fan_probe(struct platform_device *pdev)
+{
+ struct khadas_mcu *mcu = dev_get_drvdata(pdev->dev.parent);
+ struct thermal_cooling_device *cdev;
+ struct device *dev = &pdev->dev;
+ struct khadas_mcu_fan_ctx *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->mcu = mcu;
+ platform_set_drvdata(pdev, ctx);
+
+ cdev = devm_thermal_of_cooling_device_register(dev->parent,
+ dev->parent->of_node, "khadas-mcu-fan", ctx,
+ &khadas_mcu_fan_cooling_ops);
+ if (IS_ERR(cdev)) {
+ ret = PTR_ERR(cdev);
+ dev_err(dev, "Failed to register khadas-mcu-fan as cooling device: %d\n",
+ ret);
+ return ret;
+ }
+ ctx->cdev = cdev;
+ thermal_cdev_update(cdev);
+
+ return 0;
+}
+
+static void khadas_mcu_fan_shutdown(struct platform_device *pdev)
+{
+ struct khadas_mcu_fan_ctx *ctx = platform_get_drvdata(pdev);
+
+ khadas_mcu_fan_set_level(ctx, 0);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int khadas_mcu_fan_suspend(struct device *dev)
+{
+ struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
+ unsigned int level_save = ctx->level;
+ int ret;
+
+ ret = khadas_mcu_fan_set_level(ctx, 0);
+ if (ret)
+ return ret;
+
+ ctx->level = level_save;
+
+ return 0;
+}
+
+static int khadas_mcu_fan_resume(struct device *dev)
+{
+ struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
+
+ return khadas_mcu_fan_set_level(ctx, ctx->level);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(khadas_mcu_fan_pm, khadas_mcu_fan_suspend,
+ khadas_mcu_fan_resume);
+
+static const struct platform_device_id khadas_mcu_fan_id_table[] = {
+ { .name = "khadas-mcu-fan-ctrl", },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, khadas_mcu_fan_id_table);
+
+static struct platform_driver khadas_mcu_fan_driver = {
+ .probe = khadas_mcu_fan_probe,
+ .shutdown = khadas_mcu_fan_shutdown,
+ .driver = {
+ .name = "khadas-mcu-fan-ctrl",
+ .pm = &khadas_mcu_fan_pm,
+ },
+ .id_table = khadas_mcu_fan_id_table,
+};
+
+module_platform_driver(khadas_mcu_fan_driver);
+
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_DESCRIPTION("Khadas MCU FAN driver");
+MODULE_LICENSE("GPL");