summaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig6
-rw-r--r--drivers/mfd/arizona-core.c41
-rw-r--r--drivers/mfd/db8500-prcmu.c19
-rw-r--r--drivers/mfd/max14577.c315
-rw-r--r--drivers/mfd/mc13xxx-core.c10
-rw-r--r--drivers/mfd/rtsx_pcr.c132
-rw-r--r--drivers/mfd/rtsx_usb.c14
-rw-r--r--drivers/mfd/tps65090.c41
-rw-r--r--drivers/mfd/tps6586x.c4
-rw-r--r--drivers/mfd/twl-core.c15
10 files changed, 409 insertions, 188 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 29be025d5835..e166d7176d7a 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -331,15 +331,15 @@ config MFD_88PM860X
battery-charger under the corresponding menus.
config MFD_MAX14577
- bool "Maxim Semiconductor MAX14577 MUIC + Charger Support"
+ bool "Maxim Semiconductor MAX14577/77836 MUIC + Charger Support"
depends on I2C=y
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
select IRQ_DOMAIN
help
- Say yes here to add support for Maxim Semiconductor MAX14577.
- This is a Micro-USB IC with Charger controls on chip.
+ Say yes here to add support for Maxim Semiconductor MAX14577 and
+ MAX77836 Micro-USB ICs with battery charger.
This driver provides common support for accessing the device;
additional drivers must be enabled in order to use the functionality
of the device.
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index 1c3ae57082ed..07e6e27be23c 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -508,19 +508,31 @@ int arizona_of_get_type(struct device *dev)
}
EXPORT_SYMBOL_GPL(arizona_of_get_type);
+int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop,
+ bool mandatory)
+{
+ int gpio;
+
+ gpio = of_get_named_gpio(arizona->dev->of_node, prop, 0);
+ if (gpio < 0) {
+ if (mandatory)
+ dev_err(arizona->dev,
+ "Mandatory DT gpio %s missing/malformed: %d\n",
+ prop, gpio);
+
+ gpio = 0;
+ }
+
+ return gpio;
+}
+EXPORT_SYMBOL_GPL(arizona_of_get_named_gpio);
+
static int arizona_of_get_core_pdata(struct arizona *arizona)
{
+ struct arizona_pdata *pdata = &arizona->pdata;
int ret, i;
- arizona->pdata.reset = of_get_named_gpio(arizona->dev->of_node,
- "wlf,reset", 0);
- if (arizona->pdata.reset < 0)
- arizona->pdata.reset = 0;
-
- arizona->pdata.ldoena = of_get_named_gpio(arizona->dev->of_node,
- "wlf,ldoena", 0);
- if (arizona->pdata.ldoena < 0)
- arizona->pdata.ldoena = 0;
+ pdata->reset = arizona_of_get_named_gpio(arizona, "wlf,reset", true);
ret = of_property_read_u32_array(arizona->dev->of_node,
"wlf,gpio-defaults",
@@ -652,6 +664,9 @@ int arizona_dev_init(struct arizona *arizona)
return -EINVAL;
}
+ /* Mark DCVDD as external, LDO1 driver will clear if internal */
+ arizona->external_dcvdd = true;
+
ret = mfd_add_devices(arizona->dev, -1, early_devs,
ARRAY_SIZE(early_devs), NULL, 0, NULL);
if (ret != 0) {
@@ -851,14 +866,6 @@ int arizona_dev_init(struct arizona *arizona)
arizona->pdata.gpio_defaults[i]);
}
- /*
- * LDO1 can only be used to supply DCVDD so if it has no
- * consumers then DCVDD is supplied externally.
- */
- if (arizona->pdata.ldo1 &&
- arizona->pdata.ldo1->num_consumer_supplies == 0)
- arizona->external_dcvdd = true;
-
pm_runtime_set_autosuspend_delay(arizona->dev, 100);
pm_runtime_use_autosuspend(arizona->dev);
pm_runtime_enable(arizona->dev);
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 7694e0700d34..b11fdd63eecd 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -1734,18 +1734,17 @@ static struct cpufreq_frequency_table db8500_cpufreq_table[] = {
static long round_armss_rate(unsigned long rate)
{
+ struct cpufreq_frequency_table *pos;
long freq = 0;
- int i = 0;
/* cpufreq table frequencies is in KHz. */
rate = rate / 1000;
/* Find the corresponding arm opp from the cpufreq table. */
- while (db8500_cpufreq_table[i].frequency != CPUFREQ_TABLE_END) {
- freq = db8500_cpufreq_table[i].frequency;
+ cpufreq_for_each_entry(pos, db8500_cpufreq_table) {
+ freq = pos->frequency;
if (freq == rate)
break;
- i++;
}
/* Return the last valid value, even if a match was not found. */
@@ -1886,23 +1885,21 @@ static void set_clock_rate(u8 clock, unsigned long rate)
static int set_armss_rate(unsigned long rate)
{
- int i = 0;
+ struct cpufreq_frequency_table *pos;
/* cpufreq table frequencies is in KHz. */
rate = rate / 1000;
/* Find the corresponding arm opp from the cpufreq table. */
- while (db8500_cpufreq_table[i].frequency != CPUFREQ_TABLE_END) {
- if (db8500_cpufreq_table[i].frequency == rate)
+ cpufreq_for_each_entry(pos, db8500_cpufreq_table)
+ if (pos->frequency == rate)
break;
- i++;
- }
- if (db8500_cpufreq_table[i].frequency != rate)
+ if (pos->frequency != rate)
return -EINVAL;
/* Set the new arm opp. */
- return db8500_prcmu_set_arm_opp(db8500_cpufreq_table[i].driver_data);
+ return db8500_prcmu_set_arm_opp(pos->driver_data);
}
static int set_plldsi_rate(unsigned long rate)
diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c
index 5f13cefe8def..484d372a4892 100644
--- a/drivers/mfd/max14577.c
+++ b/drivers/mfd/max14577.c
@@ -1,7 +1,7 @@
/*
- * max14577.c - mfd core driver for the Maxim 14577
+ * max14577.c - mfd core driver for the Maxim 14577/77836
*
- * Copyright (C) 2013 Samsung Electrnoics
+ * Copyright (C) 2014 Samsung Electrnoics
* Chanwoo Choi <cw00.choi@samsung.com>
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
*
@@ -21,6 +21,7 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/of_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max14577.h>
#include <linux/mfd/max14577-private.h>
@@ -37,7 +38,38 @@ static struct mfd_cell max14577_devs[] = {
{ .name = "max14577-charger", },
};
-static bool max14577_volatile_reg(struct device *dev, unsigned int reg)
+static struct mfd_cell max77836_devs[] = {
+ {
+ .name = "max77836-muic",
+ .of_compatible = "maxim,max77836-muic",
+ },
+ {
+ .name = "max77836-regulator",
+ .of_compatible = "maxim,max77836-regulator",
+ },
+ {
+ .name = "max77836-charger",
+ .of_compatible = "maxim,max77836-charger",
+ },
+ {
+ .name = "max77836-battery",
+ .of_compatible = "maxim,max77836-battery",
+ },
+};
+
+static struct of_device_id max14577_dt_match[] = {
+ {
+ .compatible = "maxim,max14577",
+ .data = (void *)MAXIM_DEVICE_TYPE_MAX14577,
+ },
+ {
+ .compatible = "maxim,max77836",
+ .data = (void *)MAXIM_DEVICE_TYPE_MAX77836,
+ },
+ {},
+};
+
+static bool max14577_muic_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX14577_REG_INT1 ... MAX14577_REG_STATUS3:
@@ -48,49 +80,221 @@ static bool max14577_volatile_reg(struct device *dev, unsigned int reg)
return false;
}
-static const struct regmap_config max14577_regmap_config = {
+static bool max77836_muic_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /* Any max14577 volatile registers are also max77836 volatile. */
+ if (max14577_muic_volatile_reg(dev, reg))
+ return true;
+
+ switch (reg) {
+ case MAX77836_FG_REG_VCELL_MSB ... MAX77836_FG_REG_SOC_LSB:
+ case MAX77836_FG_REG_CRATE_MSB ... MAX77836_FG_REG_CRATE_LSB:
+ case MAX77836_FG_REG_STATUS_H ... MAX77836_FG_REG_STATUS_L:
+ case MAX77836_PMIC_REG_INTSRC:
+ case MAX77836_PMIC_REG_TOPSYS_INT:
+ case MAX77836_PMIC_REG_TOPSYS_STAT:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static const struct regmap_config max14577_muic_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .volatile_reg = max14577_volatile_reg,
+ .volatile_reg = max14577_muic_volatile_reg,
.max_register = MAX14577_REG_END,
};
+static const struct regmap_config max77836_pmic_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = max77836_muic_volatile_reg,
+ .max_register = MAX77836_PMIC_REG_END,
+};
+
static const struct regmap_irq max14577_irqs[] = {
/* INT1 interrupts */
- { .reg_offset = 0, .mask = INT1_ADC_MASK, },
- { .reg_offset = 0, .mask = INT1_ADCLOW_MASK, },
- { .reg_offset = 0, .mask = INT1_ADCERR_MASK, },
+ { .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, },
+ { .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, },
+ { .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, },
/* INT2 interrupts */
- { .reg_offset = 1, .mask = INT2_CHGTYP_MASK, },
- { .reg_offset = 1, .mask = INT2_CHGDETRUN_MASK, },
- { .reg_offset = 1, .mask = INT2_DCDTMR_MASK, },
- { .reg_offset = 1, .mask = INT2_DBCHG_MASK, },
- { .reg_offset = 1, .mask = INT2_VBVOLT_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, },
/* INT3 interrupts */
- { .reg_offset = 2, .mask = INT3_EOC_MASK, },
- { .reg_offset = 2, .mask = INT3_CGMBC_MASK, },
- { .reg_offset = 2, .mask = INT3_OVP_MASK, },
- { .reg_offset = 2, .mask = INT3_MBCCHGERR_MASK, },
+ { .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, },
+ { .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, },
+ { .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, },
+ { .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, },
};
static const struct regmap_irq_chip max14577_irq_chip = {
.name = "max14577",
.status_base = MAX14577_REG_INT1,
.mask_base = MAX14577_REG_INTMASK1,
- .mask_invert = 1,
+ .mask_invert = true,
.num_regs = 3,
.irqs = max14577_irqs,
.num_irqs = ARRAY_SIZE(max14577_irqs),
};
+static const struct regmap_irq max77836_muic_irqs[] = {
+ /* INT1 interrupts */
+ { .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, },
+ { .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, },
+ { .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, },
+ { .reg_offset = 0, .mask = MAX77836_INT1_ADC1K_MASK, },
+ /* INT2 interrupts */
+ { .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, },
+ { .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, },
+ { .reg_offset = 1, .mask = MAX77836_INT2_VIDRM_MASK, },
+ /* INT3 interrupts */
+ { .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, },
+ { .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, },
+ { .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, },
+ { .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, },
+};
+
+static const struct regmap_irq_chip max77836_muic_irq_chip = {
+ .name = "max77836-muic",
+ .status_base = MAX14577_REG_INT1,
+ .mask_base = MAX14577_REG_INTMASK1,
+ .mask_invert = true,
+ .num_regs = 3,
+ .irqs = max77836_muic_irqs,
+ .num_irqs = ARRAY_SIZE(max77836_muic_irqs),
+};
+
+static const struct regmap_irq max77836_pmic_irqs[] = {
+ { .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T120C_MASK, },
+ { .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T140C_MASK, },
+};
+
+static const struct regmap_irq_chip max77836_pmic_irq_chip = {
+ .name = "max77836-pmic",
+ .status_base = MAX77836_PMIC_REG_TOPSYS_INT,
+ .mask_base = MAX77836_PMIC_REG_TOPSYS_INT_MASK,
+ .mask_invert = false,
+ .num_regs = 1,
+ .irqs = max77836_pmic_irqs,
+ .num_irqs = ARRAY_SIZE(max77836_pmic_irqs),
+};
+
+static void max14577_print_dev_type(struct max14577 *max14577)
+{
+ u8 reg_data, vendor_id, device_id;
+ int ret;
+
+ ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID,
+ &reg_data);
+ if (ret) {
+ dev_err(max14577->dev,
+ "Failed to read DEVICEID register: %d\n", ret);
+ return;
+ }
+
+ vendor_id = ((reg_data & DEVID_VENDORID_MASK) >>
+ DEVID_VENDORID_SHIFT);
+ device_id = ((reg_data & DEVID_DEVICEID_MASK) >>
+ DEVID_DEVICEID_SHIFT);
+
+ dev_info(max14577->dev, "Device type: %u (ID: 0x%x, vendor: 0x%x)\n",
+ max14577->dev_type, device_id, vendor_id);
+}
+
+/*
+ * Max77836 specific initialization code for driver probe.
+ * Adds new I2C dummy device, regmap and regmap IRQ chip.
+ * Unmasks Interrupt Source register.
+ *
+ * On success returns 0.
+ * On failure returns errno and reverts any changes done so far (e.g. remove
+ * I2C dummy device), except masking the INT SRC register.
+ */
+static int max77836_init(struct max14577 *max14577)
+{
+ int ret;
+ u8 intsrc_mask;
+
+ max14577->i2c_pmic = i2c_new_dummy(max14577->i2c->adapter,
+ I2C_ADDR_PMIC);
+ if (!max14577->i2c_pmic) {
+ dev_err(max14577->dev, "Failed to register PMIC I2C device\n");
+ return -ENODEV;
+ }
+ i2c_set_clientdata(max14577->i2c_pmic, max14577);
+
+ max14577->regmap_pmic = devm_regmap_init_i2c(max14577->i2c_pmic,
+ &max77836_pmic_regmap_config);
+ if (IS_ERR(max14577->regmap_pmic)) {
+ ret = PTR_ERR(max14577->regmap_pmic);
+ dev_err(max14577->dev, "Failed to allocate PMIC register map: %d\n",
+ ret);
+ goto err;
+ }
+
+ /* Un-mask MAX77836 Interrupt Source register */
+ ret = max14577_read_reg(max14577->regmap_pmic,
+ MAX77836_PMIC_REG_INTSRC_MASK, &intsrc_mask);
+ if (ret < 0) {
+ dev_err(max14577->dev, "Failed to read PMIC register\n");
+ goto err;
+ }
+
+ intsrc_mask &= ~(MAX77836_INTSRC_MASK_TOP_INT_MASK);
+ intsrc_mask &= ~(MAX77836_INTSRC_MASK_MUIC_CHG_INT_MASK);
+ ret = max14577_write_reg(max14577->regmap_pmic,
+ MAX77836_PMIC_REG_INTSRC_MASK, intsrc_mask);
+ if (ret < 0) {
+ dev_err(max14577->dev, "Failed to write PMIC register\n");
+ goto err;
+ }
+
+ ret = regmap_add_irq_chip(max14577->regmap_pmic, max14577->irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED,
+ 0, &max77836_pmic_irq_chip,
+ &max14577->irq_data_pmic);
+ if (ret != 0) {
+ dev_err(max14577->dev, "Failed to request PMIC IRQ %d: %d\n",
+ max14577->irq, ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ i2c_unregister_device(max14577->i2c_pmic);
+
+ return ret;
+}
+
+/*
+ * Max77836 specific de-initialization code for driver remove.
+ */
+static void max77836_remove(struct max14577 *max14577)
+{
+ regmap_del_irq_chip(max14577->irq, max14577->irq_data_pmic);
+ i2c_unregister_device(max14577->i2c_pmic);
+}
+
static int max14577_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max14577 *max14577;
struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct device_node *np = i2c->dev.of_node;
- u8 reg_data;
int ret = 0;
+ const struct regmap_irq_chip *irq_chip;
+ struct mfd_cell *mfd_devs;
+ unsigned int mfd_devs_size;
+ int irq_flags;
if (np) {
pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
@@ -113,7 +317,8 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
max14577->i2c = i2c;
max14577->irq = i2c->irq;
- max14577->regmap = devm_regmap_init_i2c(i2c, &max14577_regmap_config);
+ max14577->regmap = devm_regmap_init_i2c(i2c,
+ &max14577_muic_regmap_config);
if (IS_ERR(max14577->regmap)) {
ret = PTR_ERR(max14577->regmap);
dev_err(max14577->dev, "Failed to allocate register map: %d\n",
@@ -121,23 +326,36 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID,
- &reg_data);
- if (ret) {
- dev_err(max14577->dev, "Device not found on this channel: %d\n",
- ret);
- return ret;
+ if (np) {
+ const struct of_device_id *of_id;
+
+ of_id = of_match_device(max14577_dt_match, &i2c->dev);
+ if (of_id)
+ max14577->dev_type = (unsigned int)of_id->data;
+ } else {
+ max14577->dev_type = id->driver_data;
+ }
+
+ max14577_print_dev_type(max14577);
+
+ switch (max14577->dev_type) {
+ case MAXIM_DEVICE_TYPE_MAX77836:
+ irq_chip = &max77836_muic_irq_chip;
+ mfd_devs = max77836_devs;
+ mfd_devs_size = ARRAY_SIZE(max77836_devs);
+ irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED;
+ break;
+ case MAXIM_DEVICE_TYPE_MAX14577:
+ default:
+ irq_chip = &max14577_irq_chip;
+ mfd_devs = max14577_devs;
+ mfd_devs_size = ARRAY_SIZE(max14577_devs);
+ irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+ break;
}
- max14577->vendor_id = ((reg_data & DEVID_VENDORID_MASK) >>
- DEVID_VENDORID_SHIFT);
- max14577->device_id = ((reg_data & DEVID_DEVICEID_MASK) >>
- DEVID_DEVICEID_SHIFT);
- dev_info(max14577->dev, "Device ID: 0x%x, vendor: 0x%x\n",
- max14577->device_id, max14577->vendor_id);
ret = regmap_add_irq_chip(max14577->regmap, max14577->irq,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
- &max14577_irq_chip,
+ irq_flags, 0, irq_chip,
&max14577->irq_data);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n",
@@ -145,8 +363,15 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = mfd_add_devices(max14577->dev, -1, max14577_devs,
- ARRAY_SIZE(max14577_devs), NULL, 0,
+ /* Max77836 specific initialization code (additional regmap) */
+ if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836) {
+ ret = max77836_init(max14577);
+ if (ret < 0)
+ goto err_max77836;
+ }
+
+ ret = mfd_add_devices(max14577->dev, -1, mfd_devs,
+ mfd_devs_size, NULL, 0,
regmap_irq_get_domain(max14577->irq_data));
if (ret < 0)
goto err_mfd;
@@ -156,6 +381,9 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
return 0;
err_mfd:
+ if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836)
+ max77836_remove(max14577);
+err_max77836:
regmap_del_irq_chip(max14577->irq, max14577->irq_data);
return ret;
@@ -167,12 +395,15 @@ static int max14577_i2c_remove(struct i2c_client *i2c)
mfd_remove_devices(max14577->dev);
regmap_del_irq_chip(max14577->irq, max14577->irq_data);
+ if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836)
+ max77836_remove(max14577);
return 0;
}
static const struct i2c_device_id max14577_i2c_id[] = {
- { "max14577", 0 },
+ { "max14577", MAXIM_DEVICE_TYPE_MAX14577, },
+ { "max77836", MAXIM_DEVICE_TYPE_MAX77836, },
{ }
};
MODULE_DEVICE_TABLE(i2c, max14577_i2c_id);
@@ -215,11 +446,6 @@ static int max14577_resume(struct device *dev)
}
#endif /* CONFIG_PM_SLEEP */
-static struct of_device_id max14577_dt_match[] = {
- { .compatible = "maxim,max14577", },
- {},
-};
-
static SIMPLE_DEV_PM_OPS(max14577_pm, max14577_suspend, max14577_resume);
static struct i2c_driver max14577_i2c_driver = {
@@ -236,6 +462,9 @@ static struct i2c_driver max14577_i2c_driver = {
static int __init max14577_i2c_init(void)
{
+ BUILD_BUG_ON(ARRAY_SIZE(max14577_i2c_id) != MAXIM_DEVICE_TYPE_NUM);
+ BUILD_BUG_ON(ARRAY_SIZE(max14577_dt_match) != MAXIM_DEVICE_TYPE_NUM);
+
return i2c_add_driver(&max14577_i2c_driver);
}
subsys_initcall(max14577_i2c_init);
@@ -247,5 +476,5 @@ static void __exit max14577_i2c_exit(void)
module_exit(max14577_i2c_exit);
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
-MODULE_DESCRIPTION("MAXIM 14577 multi-function core driver");
+MODULE_DESCRIPTION("Maxim 14577/77836 multi-function core driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index 06e64b6fcb89..0c6c21c5b1a8 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -673,9 +673,13 @@ int mc13xxx_common_init(struct device *dev)
if (mc13xxx->flags & MC13XXX_USE_ADC)
mc13xxx_add_subdevice(mc13xxx, "%s-adc");
- if (mc13xxx->flags & MC13XXX_USE_CODEC)
- mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec",
- pdata->codec, sizeof(*pdata->codec));
+ if (mc13xxx->flags & MC13XXX_USE_CODEC) {
+ if (pdata)
+ mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec",
+ pdata->codec, sizeof(*pdata->codec));
+ else
+ mc13xxx_add_subdevice(mc13xxx, "%s-codec");
+ }
if (mc13xxx->flags & MC13XXX_USE_RTC)
mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c
index c9de3d598ea5..1d15735f9ef9 100644
--- a/drivers/mfd/rtsx_pcr.c
+++ b/drivers/mfd/rtsx_pcr.c
@@ -338,28 +338,58 @@ int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist,
int num_sg, bool read, int timeout)
{
struct completion trans_done;
- int err = 0, count;
+ u8 dir;
+ int err = 0, i, count;
long timeleft;
unsigned long flags;
+ struct scatterlist *sg;
+ enum dma_data_direction dma_dir;
+ u32 val;
+ dma_addr_t addr;
+ unsigned int len;
+
+ dev_dbg(&(pcr->pci->dev), "--> %s: num_sg = %d\n", __func__, num_sg);
+
+ /* don't transfer data during abort processing */
+ if (pcr->remove_pci)
+ return -EINVAL;
+
+ if ((sglist == NULL) || (num_sg <= 0))
+ return -EINVAL;
- count = rtsx_pci_dma_map_sg(pcr, sglist, num_sg, read);
+ if (read) {
+ dir = DEVICE_TO_HOST;
+ dma_dir = DMA_FROM_DEVICE;
+ } else {
+ dir = HOST_TO_DEVICE;
+ dma_dir = DMA_TO_DEVICE;
+ }
+
+ count = dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir);
if (count < 1) {
dev_err(&(pcr->pci->dev), "scatterlist map failed\n");
return -EINVAL;
}
dev_dbg(&(pcr->pci->dev), "DMA mapping count: %d\n", count);
+ val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE;
+ pcr->sgi = 0;
+ for_each_sg(sglist, sg, count, i) {
+ addr = sg_dma_address(sg);
+ len = sg_dma_len(sg);
+ rtsx_pci_add_sg_tbl(pcr, addr, len, i == count - 1);
+ }
spin_lock_irqsave(&pcr->lock, flags);
pcr->done = &trans_done;
pcr->trans_result = TRANS_NOT_READY;
init_completion(&trans_done);
+ rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr);
+ rtsx_pci_writel(pcr, RTSX_HDBCTLR, val);
spin_unlock_irqrestore(&pcr->lock, flags);
- rtsx_pci_dma_transfer(pcr, sglist, count, read);
-
timeleft = wait_for_completion_interruptible_timeout(
&trans_done, msecs_to_jiffies(timeout));
if (timeleft <= 0) {
@@ -383,7 +413,7 @@ out:
pcr->done = NULL;
spin_unlock_irqrestore(&pcr->lock, flags);
- rtsx_pci_dma_unmap_sg(pcr, sglist, num_sg, read);
+ dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir);
if ((err < 0) && (err != -ENODEV))
rtsx_pci_stop_cmd(pcr);
@@ -395,73 +425,6 @@ out:
}
EXPORT_SYMBOL_GPL(rtsx_pci_transfer_data);
-int rtsx_pci_dma_map_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist,
- int num_sg, bool read)
-{
- enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
-
- if (pcr->remove_pci)
- return -EINVAL;
-
- if ((sglist == NULL) || num_sg < 1)
- return -EINVAL;
-
- return dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dir);
-}
-EXPORT_SYMBOL_GPL(rtsx_pci_dma_map_sg);
-
-int rtsx_pci_dma_unmap_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist,
- int num_sg, bool read)
-{
- enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
-
- if (pcr->remove_pci)
- return -EINVAL;
-
- if (sglist == NULL || num_sg < 1)
- return -EINVAL;
-
- dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dir);
- return num_sg;
-}
-EXPORT_SYMBOL_GPL(rtsx_pci_dma_unmap_sg);
-
-int rtsx_pci_dma_transfer(struct rtsx_pcr *pcr, struct scatterlist *sglist,
- int sg_count, bool read)
-{
- struct scatterlist *sg;
- dma_addr_t addr;
- unsigned int len;
- int i;
- u32 val;
- u8 dir = read ? DEVICE_TO_HOST : HOST_TO_DEVICE;
- unsigned long flags;
-
- if (pcr->remove_pci)
- return -EINVAL;
-
- if ((sglist == NULL) || (sg_count < 1))
- return -EINVAL;
-
- val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE;
- pcr->sgi = 0;
- for_each_sg(sglist, sg, sg_count, i) {
- addr = sg_dma_address(sg);
- len = sg_dma_len(sg);
- rtsx_pci_add_sg_tbl(pcr, addr, len, i == sg_count - 1);
- }
-
- spin_lock_irqsave(&pcr->lock, flags);
-
- rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr);
- rtsx_pci_writel(pcr, RTSX_HDBCTLR, val);
-
- spin_unlock_irqrestore(&pcr->lock, flags);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(rtsx_pci_dma_transfer);
-
int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len)
{
int err;
@@ -873,8 +836,6 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
int_reg = rtsx_pci_readl(pcr, RTSX_BIPR);
/* Clear interrupt flag */
rtsx_pci_writel(pcr, RTSX_BIPR, int_reg);
- dev_dbg(&pcr->pci->dev, "=========== BIPR 0x%8x ==========\n", int_reg);
-
if ((int_reg & pcr->bier) == 0) {
spin_unlock(&pcr->lock);
return IRQ_NONE;
@@ -905,28 +866,17 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
}
if (int_reg & (NEED_COMPLETE_INT | DELINK_INT)) {
- if (int_reg & (TRANS_FAIL_INT | DELINK_INT))
+ if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) {
pcr->trans_result = TRANS_RESULT_FAIL;
- else if (int_reg & TRANS_OK_INT)
+ if (pcr->done)
+ complete(pcr->done);
+ } else if (int_reg & TRANS_OK_INT) {
pcr->trans_result = TRANS_RESULT_OK;
-
- if (pcr->done)
- complete(pcr->done);
-
- if (int_reg & SD_EXIST) {
- struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD];
- if (slot && slot->done_transfer)
- slot->done_transfer(slot->p_dev);
- }
-
- if (int_reg & MS_EXIST) {
- struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD];
- if (slot && slot->done_transfer)
- slot->done_transfer(slot->p_dev);
+ if (pcr->done)
+ complete(pcr->done);
}
}
-
if (pcr->card_inserted || pcr->card_removed)
schedule_delayed_work(&pcr->carddet_work,
msecs_to_jiffies(200));
diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c
index b53b9d46cc45..141ea52c46e3 100644
--- a/drivers/mfd/rtsx_usb.c
+++ b/drivers/mfd/rtsx_usb.c
@@ -67,7 +67,7 @@ static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr,
ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout);
add_timer(&ucr->sg_timer);
usb_sg_wait(&ucr->current_sg);
- del_timer(&ucr->sg_timer);
+ del_timer_sync(&ucr->sg_timer);
if (act_len)
*act_len = ucr->current_sg.bytes;
@@ -644,14 +644,14 @@ static int rtsx_usb_probe(struct usb_interface *intf,
if (ret)
goto out_init_fail;
+ /* initialize USB SG transfer timer */
+ setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr);
+
ret = mfd_add_devices(&intf->dev, usb_dev->devnum, rtsx_usb_cells,
ARRAY_SIZE(rtsx_usb_cells), NULL, 0, NULL);
if (ret)
goto out_init_fail;
- /* initialize USB SG transfer timer */
- init_timer(&ucr->sg_timer);
- setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr);
#ifdef CONFIG_PM
intf->needs_remote_wakeup = 1;
usb_enable_autosuspend(usb_dev);
@@ -687,9 +687,15 @@ static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message)
dev_dbg(&intf->dev, "%s called with pm message 0x%04u\n",
__func__, message.event);
+ /*
+ * Call to make sure LED is off during suspend to save more power.
+ * It is NOT a permanent state and could be turned on anytime later.
+ * Thus no need to call turn_on when resunming.
+ */
mutex_lock(&ucr->dev_mutex);
rtsx_usb_turn_off_led(ucr);
mutex_unlock(&ucr->dev_mutex);
+
return 0;
}
diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c
index ba1a25d758c1..1c3e6e2efe41 100644
--- a/drivers/mfd/tps65090.c
+++ b/drivers/mfd/tps65090.c
@@ -32,14 +32,6 @@
#define NUM_INT_REG 2
#define TOTAL_NUM_REG 0x18
-/* interrupt status registers */
-#define TPS65090_INT_STS 0x0
-#define TPS65090_INT_STS2 0x1
-
-/* interrupt mask registers */
-#define TPS65090_INT_MSK 0x2
-#define TPS65090_INT_MSK2 0x3
-
#define TPS65090_INT1_MASK_VAC_STATUS_CHANGE 1
#define TPS65090_INT1_MASK_VSYS_STATUS_CHANGE 2
#define TPS65090_INT1_MASK_BAT_STATUS_CHANGE 3
@@ -64,11 +56,16 @@ static struct resource charger_resources[] = {
}
};
-static const struct mfd_cell tps65090s[] = {
- {
+enum tps65090_cells {
+ PMIC = 0,
+ CHARGER = 1,
+};
+
+static struct mfd_cell tps65090s[] = {
+ [PMIC] = {
.name = "tps65090-pmic",
},
- {
+ [CHARGER] = {
.name = "tps65090-charger",
.num_resources = ARRAY_SIZE(charger_resources),
.resources = &charger_resources[0],
@@ -139,17 +136,26 @@ static struct regmap_irq_chip tps65090_irq_chip = {
.irqs = tps65090_irqs,
.num_irqs = ARRAY_SIZE(tps65090_irqs),
.num_regs = NUM_INT_REG,
- .status_base = TPS65090_INT_STS,
- .mask_base = TPS65090_INT_MSK,
+ .status_base = TPS65090_REG_INTR_STS,
+ .mask_base = TPS65090_REG_INTR_MASK,
.mask_invert = true,
};
static bool is_volatile_reg(struct device *dev, unsigned int reg)
{
- if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS2))
- return true;
- else
+ /* Nearly all registers have status bits mixed in, except a few */
+ switch (reg) {
+ case TPS65090_REG_INTR_MASK:
+ case TPS65090_REG_INTR_MASK2:
+ case TPS65090_REG_CG_CTRL0:
+ case TPS65090_REG_CG_CTRL1:
+ case TPS65090_REG_CG_CTRL2:
+ case TPS65090_REG_CG_CTRL3:
+ case TPS65090_REG_CG_CTRL4:
+ case TPS65090_REG_CG_CTRL5:
return false;
+ }
+ return true;
}
static const struct regmap_config tps65090_regmap_config = {
@@ -211,6 +217,9 @@ static int tps65090_i2c_probe(struct i2c_client *client,
"IRQ init failed with err: %d\n", ret);
return ret;
}
+ } else {
+ /* Don't tell children they have an IRQ that'll never fire */
+ tps65090s[CHARGER].num_resources = 0;
}
ret = mfd_add_devices(tps65090->dev, -1, tps65090s,
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index bbd54414a75d..835e5549ecdd 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -495,6 +495,10 @@ static void tps6586x_print_version(struct i2c_client *client, int version)
case TPS658623:
name = "TPS658623";
break;
+ case TPS658640:
+ case TPS658640v2:
+ name = "TPS658640";
+ break;
case TPS658643:
name = "TPS658643";
break;
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index e87140bef667..db11b4f40611 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -98,7 +98,11 @@
#define TWL4030_BASEADD_BACKUP 0x0014
#define TWL4030_BASEADD_INT 0x002E
#define TWL4030_BASEADD_PM_MASTER 0x0036
+
#define TWL4030_BASEADD_PM_RECEIVER 0x005B
+#define TWL4030_DCDC_GLOBAL_CFG 0x06
+#define SMARTREFLEX_ENABLE BIT(3)
+
#define TWL4030_BASEADD_RTC 0x001C
#define TWL4030_BASEADD_SECURED_REG 0x0000
@@ -1204,6 +1208,11 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
* Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface.
* Program I2C_SCL_CTRL_PU(bit 0)=0, I2C_SDA_CTRL_PU (bit 2)=0,
* SR_I2C_SCL_CTRL_PU(bit 4)=0 and SR_I2C_SDA_CTRL_PU(bit 6)=0.
+ *
+ * Also, always enable SmartReflex bit as that's needed for omaps to
+ * to do anything over I2C4 for voltage scaling even if SmartReflex
+ * is disabled. Without the SmartReflex bit omap sys_clkreq idle
+ * signal will never trigger for retention idle.
*/
if (twl_class_is_4030()) {
u8 temp;
@@ -1212,6 +1221,12 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
temp &= ~(SR_I2C_SDA_CTRL_PU | SR_I2C_SCL_CTRL_PU | \
I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU);
twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
+
+ twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &temp,
+ TWL4030_DCDC_GLOBAL_CFG);
+ temp |= SMARTREFLEX_ENABLE;
+ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, temp,
+ TWL4030_DCDC_GLOBAL_CFG);
}
if (node) {