]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branches 'ib-from-asoc-3.16', 'ib-from-pm-3.16', 'ib-from-regulator-3.16',...
authorLee Jones <lee.jones@linaro.org>
Tue, 3 Jun 2014 07:08:40 +0000 (08:08 +0100)
committerLee Jones <lee.jones@linaro.org>
Tue, 3 Jun 2014 07:08:40 +0000 (08:08 +0100)
54 files changed:
Documentation/cpu-freq/cpu-drivers.txt
Documentation/devicetree/bindings/mfd/mc13xxx.txt
arch/arm/mach-davinci/da850.c
arch/arm/mach-omap2/omap_twl.c
drivers/cpufreq/acpi-cpufreq.c
drivers/cpufreq/arm_big_little.c
drivers/cpufreq/cpufreq.c
drivers/cpufreq/cpufreq_stats.c
drivers/cpufreq/dbx500-cpufreq.c
drivers/cpufreq/elanfreq.c
drivers/cpufreq/exynos-cpufreq.c
drivers/cpufreq/exynos5440-cpufreq.c
drivers/cpufreq/freq_table.c
drivers/cpufreq/longhaul.c
drivers/cpufreq/pasemi-cpufreq.c
drivers/cpufreq/powernow-k6.c
drivers/cpufreq/ppc_cbe_cpufreq.c
drivers/cpufreq/s3c2416-cpufreq.c
drivers/cpufreq/s3c64xx-cpufreq.c
drivers/extcon/Kconfig
drivers/extcon/extcon-max14577.c
drivers/gpio/gpio-stmpe.c
drivers/memstick/host/Kconfig
drivers/memstick/host/Makefile
drivers/memstick/host/rtsx_usb_ms.c [new file with mode: 0644]
drivers/mfd/Kconfig
drivers/mfd/arizona-core.c
drivers/mfd/db8500-prcmu.c
drivers/mfd/max14577.c
drivers/mfd/mc13xxx-core.c
drivers/mfd/rtsx_usb.c
drivers/mfd/stmpe-i2c.c
drivers/mfd/stmpe.c
drivers/mfd/stmpe.h
drivers/mfd/tps65090.c
drivers/mfd/tps6586x.c
drivers/mfd/twl-core.c
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/rtsx_usb_sdmmc.c [new file with mode: 0644]
drivers/power/tps65090-charger.c
drivers/regulator/Kconfig
drivers/regulator/arizona-ldo1.c
drivers/regulator/arizona-micsupp.c
drivers/regulator/max14577.c
drivers/regulator/tps6586x-regulator.c
include/linux/cpufreq.h
include/linux/mfd/arizona/core.h
include/linux/mfd/max14577-private.h
include/linux/mfd/max14577.h
include/linux/mfd/stmpe.h
include/linux/mfd/tps65090.h
include/linux/mfd/tps6586x.h
sound/soc/codecs/mc13783.c

index 48da5fdcb9f11b5671859a63e61ce2c9f40bab03..b045fe54986a077792c80e4ad2563b2e38118a86 100644 (file)
@@ -228,3 +228,22 @@ is the corresponding frequency table helper for the ->target
 stage. Just pass the values to this function, and the unsigned int
 index returns the number of the frequency table entry which contains
 the frequency the CPU shall be set to.
+
+The following macros can be used as iterators over cpufreq_frequency_table:
+
+cpufreq_for_each_entry(pos, table) - iterates over all entries of frequency
+table.
+
+cpufreq-for_each_valid_entry(pos, table) - iterates over all entries,
+excluding CPUFREQ_ENTRY_INVALID frequencies.
+Use arguments "pos" - a cpufreq_frequency_table * as a loop cursor and
+"table" - the cpufreq_frequency_table * you want to iterate over.
+
+For example:
+
+       struct cpufreq_frequency_table *pos, *driver_freq_table;
+
+       cpufreq_for_each_entry(pos, driver_freq_table) {
+               /* Do something with pos */
+               pos->frequency = ...
+       }
index 1413f39912d3acc3fbb8b5557909557e7500a1c0..8aba48821a85a3284ac92a31c2c401922fad3744 100644 (file)
@@ -10,6 +10,9 @@ Optional properties:
 - fsl,mc13xxx-uses-touch : Indicate the touchscreen controller is being used
 
 Sub-nodes:
+- codec: Contain the Audio Codec node.
+  - adc-port: Contain PMIC SSI port number used for ADC.
+  - dac-port: Contain PMIC SSI port number used for DAC.
 - leds : Contain the led nodes and initial register values in property
   "led-control". Number of register depends of used IC, for MC13783 is 6,
   for MC13892 is 4, for MC34708 is 1. See datasheet for bits definitions of
index 85399c98f84a7f30657ecc57beb267d404593560..45ce065e7170f802483ebef099621f20e12772e7 100644 (file)
@@ -1092,20 +1092,21 @@ int da850_register_cpufreq(char *async_clk)
 
 static int da850_round_armrate(struct clk *clk, unsigned long rate)
 {
-       int i, ret = 0, diff;
+       int ret = 0, diff;
        unsigned int best = (unsigned int) -1;
        struct cpufreq_frequency_table *table = cpufreq_info.freq_table;
+       struct cpufreq_frequency_table *pos;
 
        rate /= 1000; /* convert to kHz */
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               diff = table[i].frequency - rate;
+       cpufreq_for_each_entry(pos, table) {
+               diff = pos->frequency - rate;
                if (diff < 0)
                        diff = -diff;
 
                if (diff < best) {
                        best = diff;
-                       ret = table[i].frequency;
+                       ret = pos->frequency;
                }
        }
 
index 615e5b1fb025dddebadf72e21a7845c0a4306c14..6bf6267005571b5fadc03d4bab9d8e60489d2d0c 100644 (file)
 
 static bool is_offset_valid;
 static u8 smps_offset;
-/*
- * Flag to ensure Smartreflex bit in TWL
- * being cleared in board file is not overwritten.
- */
-static bool __initdata twl_sr_enable_autoinit;
 
-#define TWL4030_DCDC_GLOBAL_CFG        0x06
 #define REG_SMPS_OFFSET         0xE0
-#define SMARTREFLEX_ENABLE     BIT(3)
 
 static unsigned long twl4030_vsel_to_uv(const u8 vsel)
 {
@@ -251,18 +244,6 @@ int __init omap3_twl_init(void)
        if (!cpu_is_omap34xx())
                return -ENODEV;
 
-       /*
-        * The smartreflex bit on twl4030 specifies if the setting of voltage
-        * is done over the I2C_SR path. Since this setting is independent of
-        * the actual usage of smartreflex AVS module, we enable TWL SR bit
-        * by default irrespective of whether smartreflex AVS module is enabled
-        * on the OMAP side or not. This is because without this bit enabled,
-        * the voltage scaling through vp forceupdate/bypass mechanism of
-        * voltage scaling will not function on TWL over I2C_SR.
-        */
-       if (!twl_sr_enable_autoinit)
-               omap3_twl_set_sr_bit(true);
-
        voltdm = voltdm_lookup("mpu_iva");
        omap_voltage_register_pmic(voltdm, &omap3_mpu_pmic);
 
@@ -271,44 +252,3 @@ int __init omap3_twl_init(void)
 
        return 0;
 }
-
-/**
- * omap3_twl_set_sr_bit() - Set/Clear SR bit on TWL
- * @enable: enable SR mode in twl or not
- *
- * If 'enable' is true, enables Smartreflex bit on TWL 4030 to make sure
- * voltage scaling through OMAP SR works. Else, the smartreflex bit
- * on twl4030 is cleared as there are platforms which use OMAP3 and T2 but
- * use Synchronized Scaling Hardware Strategy (ENABLE_VMODE=1) and Direct
- * Strategy Software Scaling Mode (ENABLE_VMODE=0), for setting the voltages,
- * in those scenarios this bit is to be cleared (enable = false).
- *
- * Returns 0 on success, error is returned if I2C read/write fails.
- */
-int __init omap3_twl_set_sr_bit(bool enable)
-{
-       u8 temp;
-       int ret;
-       if (twl_sr_enable_autoinit)
-               pr_warning("%s: unexpected multiple calls\n", __func__);
-
-       ret = twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &temp,
-                             TWL4030_DCDC_GLOBAL_CFG);
-       if (ret)
-               goto err;
-
-       if (enable)
-               temp |= SMARTREFLEX_ENABLE;
-       else
-               temp &= ~SMARTREFLEX_ENABLE;
-
-       ret = twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, temp,
-                              TWL4030_DCDC_GLOBAL_CFG);
-       if (!ret) {
-               twl_sr_enable_autoinit = true;
-               return 0;
-       }
-err:
-       pr_err("%s: Error access to TWL4030 (%d)\n", __func__, ret);
-       return ret;
-}
index 000e4e0afd7e672abaac488d3096ae199f68a301..b0c18ed8d83f707d000213e458dba613e4ffaf96 100644 (file)
@@ -213,7 +213,7 @@ static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data)
 
 static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data)
 {
-       int i;
+       struct cpufreq_frequency_table *pos;
        struct acpi_processor_performance *perf;
 
        if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
@@ -223,10 +223,9 @@ static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data)
 
        perf = data->acpi_data;
 
-       for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               if (msr == perf->states[data->freq_table[i].driver_data].status)
-                       return data->freq_table[i].frequency;
-       }
+       cpufreq_for_each_entry(pos, data->freq_table)
+               if (msr == perf->states[pos->driver_data].status)
+                       return pos->frequency;
        return data->freq_table[0].frequency;
 }
 
index bad2ed317ba294462212bc00cc3cc94132e0aeb2..1f4d4e31505746db9e2cc5565710d4180349245b 100644 (file)
@@ -226,22 +226,22 @@ static inline u32 get_table_count(struct cpufreq_frequency_table *table)
 /* get the minimum frequency in the cpufreq_frequency_table */
 static inline u32 get_table_min(struct cpufreq_frequency_table *table)
 {
-       int i;
+       struct cpufreq_frequency_table *pos;
        uint32_t min_freq = ~0;
-       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++)
-               if (table[i].frequency < min_freq)
-                       min_freq = table[i].frequency;
+       cpufreq_for_each_entry(pos, table)
+               if (pos->frequency < min_freq)
+                       min_freq = pos->frequency;
        return min_freq;
 }
 
 /* get the maximum frequency in the cpufreq_frequency_table */
 static inline u32 get_table_max(struct cpufreq_frequency_table *table)
 {
-       int i;
+       struct cpufreq_frequency_table *pos;
        uint32_t max_freq = 0;
-       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++)
-               if (table[i].frequency > max_freq)
-                       max_freq = table[i].frequency;
+       cpufreq_for_each_entry(pos, table)
+               if (pos->frequency > max_freq)
+                       max_freq = pos->frequency;
        return max_freq;
 }
 
index abda6609d3e79f670c7143a01bbec53078e8bce1..a517da996aaffb229632c52bef7e17239729a641 100644 (file)
@@ -237,6 +237,17 @@ void cpufreq_cpu_put(struct cpufreq_policy *policy)
 }
 EXPORT_SYMBOL_GPL(cpufreq_cpu_put);
 
+bool cpufreq_next_valid(struct cpufreq_frequency_table **pos)
+{
+       while ((*pos)->frequency != CPUFREQ_TABLE_END)
+               if ((*pos)->frequency != CPUFREQ_ENTRY_INVALID)
+                       return true;
+               else
+                       (*pos)++;
+       return false;
+}
+EXPORT_SYMBOL_GPL(cpufreq_next_valid);
+
 /*********************************************************************
  *            EXTERNALLY AFFECTING FREQUENCY CHANGES                 *
  *********************************************************************/
index ecaaebf969fc4e5427464dfce53d0bb321e3c39b..0cd9b4dcef997d4814c5d2570092b46d26b4e143 100644 (file)
@@ -182,11 +182,11 @@ static void cpufreq_stats_free_table(unsigned int cpu)
 
 static int __cpufreq_stats_create_table(struct cpufreq_policy *policy)
 {
-       unsigned int i, j, count = 0, ret = 0;
+       unsigned int i, count = 0, ret = 0;
        struct cpufreq_stats *stat;
        unsigned int alloc_size;
        unsigned int cpu = policy->cpu;
-       struct cpufreq_frequency_table *table;
+       struct cpufreq_frequency_table *pos, *table;
 
        table = cpufreq_frequency_get_table(cpu);
        if (unlikely(!table))
@@ -205,12 +205,8 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy)
        stat->cpu = cpu;
        per_cpu(cpufreq_stats_table, cpu) = stat;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               unsigned int freq = table[i].frequency;
-               if (freq == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       cpufreq_for_each_valid_entry(pos, table)
                count++;
-       }
 
        alloc_size = count * sizeof(int) + count * sizeof(u64);
 
@@ -228,15 +224,11 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy)
 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
        stat->trans_table = stat->freq_table + count;
 #endif
-       j = 0;
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               unsigned int freq = table[i].frequency;
-               if (freq == CPUFREQ_ENTRY_INVALID)
-                       continue;
-               if (freq_table_get_index(stat, freq) == -1)
-                       stat->freq_table[j++] = freq;
-       }
-       stat->state_num = j;
+       i = 0;
+       cpufreq_for_each_valid_entry(pos, table)
+               if (freq_table_get_index(stat, pos->frequency) == -1)
+                       stat->freq_table[i++] = pos->frequency;
+       stat->state_num = i;
        spin_lock(&cpufreq_stats_lock);
        stat->last_time = get_jiffies_64();
        stat->last_index = freq_table_get_index(stat, policy->cur);
index 412a78bb0c9410b3fe592227d6b9b03e534616b7..4bebc1b5db48f389aa546083ea6ec68d80edfa67 100644 (file)
@@ -45,7 +45,7 @@ static struct cpufreq_driver dbx500_cpufreq_driver = {
 
 static int dbx500_cpufreq_probe(struct platform_device *pdev)
 {
-       int i = 0;
+       struct cpufreq_frequency_table *pos;
 
        freq_table = dev_get_platdata(&pdev->dev);
        if (!freq_table) {
@@ -60,10 +60,8 @@ static int dbx500_cpufreq_probe(struct platform_device *pdev)
        }
 
        pr_info("dbx500-cpufreq: Available frequencies:\n");
-       while (freq_table[i].frequency != CPUFREQ_TABLE_END) {
-               pr_info("  %d Mhz\n", freq_table[i].frequency/1000);
-               i++;
-       }
+       cpufreq_for_each_entry(pos, freq_table)
+               pr_info("  %d Mhz\n", pos->frequency / 1000);
 
        return cpufreq_register_driver(&dbx500_cpufreq_driver);
 }
index 7f5d2a68c3532880b37646ff0761bb73911e00e5..1c06e786c9baa53f587579ab932701e0a51d5a3f 100644 (file)
@@ -147,7 +147,7 @@ static int elanfreq_target(struct cpufreq_policy *policy,
 static int elanfreq_cpu_init(struct cpufreq_policy *policy)
 {
        struct cpuinfo_x86 *c = &cpu_data(0);
-       unsigned int i;
+       struct cpufreq_frequency_table *pos;
 
        /* capability check */
        if ((c->x86_vendor != X86_VENDOR_AMD) ||
@@ -159,10 +159,9 @@ static int elanfreq_cpu_init(struct cpufreq_policy *policy)
                max_freq = elanfreq_get_cpu_frequency(0);
 
        /* table init */
-       for (i = 0; (elanfreq_table[i].frequency != CPUFREQ_TABLE_END); i++) {
-               if (elanfreq_table[i].frequency > max_freq)
-                       elanfreq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
-       }
+       cpufreq_for_each_entry(pos, elanfreq_table)
+               if (pos->frequency > max_freq)
+                       pos->frequency = CPUFREQ_ENTRY_INVALID;
 
        /* cpuinfo and default policy values */
        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
index f99cfe24e7bca6489dcbf592372a38d1d019f4f1..9c132550d9106f1dbe13227db7aa39f604def58b 100644 (file)
@@ -29,17 +29,16 @@ static unsigned int locking_frequency;
 static int exynos_cpufreq_get_index(unsigned int freq)
 {
        struct cpufreq_frequency_table *freq_table = exynos_info->freq_table;
-       int index;
+       struct cpufreq_frequency_table *pos;
 
-       for (index = 0;
-               freq_table[index].frequency != CPUFREQ_TABLE_END; index++)
-               if (freq_table[index].frequency == freq)
+       cpufreq_for_each_entry(pos, freq_table)
+               if (pos->frequency == freq)
                        break;
 
-       if (freq_table[index].frequency == CPUFREQ_TABLE_END)
+       if (pos->frequency == CPUFREQ_TABLE_END)
                return -EINVAL;
 
-       return index;
+       return pos - freq_table;
 }
 
 static int exynos_cpufreq_scale(unsigned int target_freq)
index a6b8214d7b7712bb7d877d68d71e476e7bde26dc..f33f25b483ca6ce0ac87cfda264ba0f1a1eff8d6 100644 (file)
@@ -114,25 +114,23 @@ static struct cpufreq_freqs freqs;
 
 static int init_div_table(void)
 {
-       struct cpufreq_frequency_table *freq_tbl = dvfs_info->freq_table;
+       struct cpufreq_frequency_table *pos, *freq_tbl = dvfs_info->freq_table;
        unsigned int tmp, clk_div, ema_div, freq, volt_id;
-       int i = 0;
        struct dev_pm_opp *opp;
 
        rcu_read_lock();
-       for (i = 0; freq_tbl[i].frequency != CPUFREQ_TABLE_END; i++) {
-
+       cpufreq_for_each_entry(pos, freq_tbl) {
                opp = dev_pm_opp_find_freq_exact(dvfs_info->dev,
-                                       freq_tbl[i].frequency * 1000, true);
+                                       pos->frequency * 1000, true);
                if (IS_ERR(opp)) {
                        rcu_read_unlock();
                        dev_err(dvfs_info->dev,
                                "failed to find valid OPP for %u KHZ\n",
-                               freq_tbl[i].frequency);
+                               pos->frequency);
                        return PTR_ERR(opp);
                }
 
-               freq = freq_tbl[i].frequency / 1000; /* In MHZ */
+               freq = pos->frequency / 1000; /* In MHZ */
                clk_div = ((freq / CPU_DIV_FREQ_MAX) & P0_7_CPUCLKDEV_MASK)
                                        << P0_7_CPUCLKDEV_SHIFT;
                clk_div |= ((freq / CPU_ATB_FREQ_MAX) & P0_7_ATBCLKDEV_MASK)
@@ -157,7 +155,8 @@ static int init_div_table(void)
                tmp = (clk_div | ema_div | (volt_id << P0_7_VDD_SHIFT)
                        | ((freq / FREQ_UNIT) << P0_7_FREQ_SHIFT));
 
-               __raw_writel(tmp, dvfs_info->base + XMU_PMU_P0_7 + 4 * i);
+               __raw_writel(tmp, dvfs_info->base + XMU_PMU_P0_7 + 4 *
+                                               (pos - freq_tbl));
        }
 
        rcu_read_unlock();
@@ -166,8 +165,9 @@ static int init_div_table(void)
 
 static void exynos_enable_dvfs(unsigned int cur_frequency)
 {
-       unsigned int tmp, i, cpu;
+       unsigned int tmp, cpu;
        struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table;
+       struct cpufreq_frequency_table *pos;
        /* Disable DVFS */
        __raw_writel(0, dvfs_info->base + XMU_DVFS_CTRL);
 
@@ -182,15 +182,15 @@ static void exynos_enable_dvfs(unsigned int cur_frequency)
         __raw_writel(tmp, dvfs_info->base + XMU_PMUIRQEN);
 
        /* Set initial performance index */
-       for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
-               if (freq_table[i].frequency == cur_frequency)
+       cpufreq_for_each_entry(pos, freq_table)
+               if (pos->frequency == cur_frequency)
                        break;
 
-       if (freq_table[i].frequency == CPUFREQ_TABLE_END) {
+       if (pos->frequency == CPUFREQ_TABLE_END) {
                dev_crit(dvfs_info->dev, "Boot up frequency not supported\n");
                /* Assign the highest frequency */
-               i = 0;
-               cur_frequency = freq_table[i].frequency;
+               pos = freq_table;
+               cur_frequency = pos->frequency;
        }
 
        dev_info(dvfs_info->dev, "Setting dvfs initial frequency = %uKHZ",
@@ -199,7 +199,7 @@ static void exynos_enable_dvfs(unsigned int cur_frequency)
        for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++) {
                tmp = __raw_readl(dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4);
                tmp &= ~(P_VALUE_MASK << C0_3_PSTATE_NEW_SHIFT);
-               tmp |= (i << C0_3_PSTATE_NEW_SHIFT);
+               tmp |= ((pos - freq_table) << C0_3_PSTATE_NEW_SHIFT);
                __raw_writel(tmp, dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4);
        }
 
index 08e7bbcf6d7362d633311132e8b9fe21433360a0..8e518c6893935135e0bfff4d7c7b8735bd0fefa1 100644 (file)
 int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
                                    struct cpufreq_frequency_table *table)
 {
+       struct cpufreq_frequency_table *pos;
        unsigned int min_freq = ~0;
        unsigned int max_freq = 0;
-       unsigned int i;
+       unsigned int freq;
 
-       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
-               unsigned int freq = table[i].frequency;
-               if (freq == CPUFREQ_ENTRY_INVALID) {
-                       pr_debug("table entry %u is invalid, skipping\n", i);
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
-                       continue;
-               }
                if (!cpufreq_boost_enabled()
-                   && (table[i].flags & CPUFREQ_BOOST_FREQ))
+                   && (pos->flags & CPUFREQ_BOOST_FREQ))
                        continue;
 
-               pr_debug("table entry %u: %u kHz\n", i, freq);
+               pr_debug("table entry %u: %u kHz\n", (int)(pos - table), freq);
                if (freq < min_freq)
                        min_freq = freq;
                if (freq > max_freq)
@@ -57,7 +54,8 @@ EXPORT_SYMBOL_GPL(cpufreq_frequency_table_cpuinfo);
 int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
                                   struct cpufreq_frequency_table *table)
 {
-       unsigned int next_larger = ~0, freq, i = 0;
+       struct cpufreq_frequency_table *pos;
+       unsigned int freq, next_larger = ~0;
        bool found = false;
 
        pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n",
@@ -65,9 +63,9 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
 
        cpufreq_verify_within_cpu_limits(policy);
 
-       for (; freq = table[i].frequency, freq != CPUFREQ_TABLE_END; i++) {
-               if (freq == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
+
                if ((freq >= policy->min) && (freq <= policy->max)) {
                        found = true;
                        break;
@@ -118,7 +116,8 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
                .driver_data = ~0,
                .frequency = 0,
        };
-       unsigned int i;
+       struct cpufreq_frequency_table *pos;
+       unsigned int freq, i = 0;
 
        pr_debug("request for target %u kHz (relation: %u) for cpu %u\n",
                                        target_freq, relation, policy->cpu);
@@ -132,10 +131,10 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
                break;
        }
 
-       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
-               unsigned int freq = table[i].frequency;
-               if (freq == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
+
+               i = pos - table;
                if ((freq < policy->min) || (freq > policy->max))
                        continue;
                switch (relation) {
@@ -184,8 +183,7 @@ EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target);
 int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
                unsigned int freq)
 {
-       struct cpufreq_frequency_table *table;
-       int i;
+       struct cpufreq_frequency_table *pos, *table;
 
        table = cpufreq_frequency_get_table(policy->cpu);
        if (unlikely(!table)) {
@@ -193,10 +191,9 @@ int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
                return -ENOENT;
        }
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               if (table[i].frequency == freq)
-                       return i;
-       }
+       cpufreq_for_each_valid_entry(pos, table)
+               if (pos->frequency == freq)
+                       return pos - table;
 
        return -EINVAL;
 }
@@ -208,16 +205,13 @@ EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);
 static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
                                    bool show_boost)
 {
-       unsigned int i = 0;
        ssize_t count = 0;
-       struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *table = policy->freq_table;
 
        if (!table)
                return -ENODEV;
 
-       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
-               if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       cpufreq_for_each_valid_entry(pos, table) {
                /*
                 * show_boost = true and driver_data = BOOST freq
                 * display BOOST freqs
@@ -229,10 +223,10 @@ static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
                 * show_boost = false and driver_data != BOOST freq
                 * display NON BOOST freqs
                 */
-               if (show_boost ^ (table[i].flags & CPUFREQ_BOOST_FREQ))
+               if (show_boost ^ (pos->flags & CPUFREQ_BOOST_FREQ))
                        continue;
 
-               count += sprintf(&buf[count], "%d ", table[i].frequency);
+               count += sprintf(&buf[count], "%d ", pos->frequency);
        }
        count += sprintf(&buf[count], "\n");
 
index 5c4369b5d834d93f05095cf6052848ec2d2ddec4..c913906a719ee0f9b44598593b4900c201395596 100644 (file)
@@ -530,6 +530,7 @@ static int longhaul_get_ranges(void)
 
 static void longhaul_setup_voltagescaling(void)
 {
+       struct cpufreq_frequency_table *freq_pos;
        union msr_longhaul longhaul;
        struct mV_pos minvid, maxvid, vid;
        unsigned int j, speed, pos, kHz_step, numvscales;
@@ -608,18 +609,16 @@ static void longhaul_setup_voltagescaling(void)
        /* Calculate kHz for one voltage step */
        kHz_step = (highest_speed - min_vid_speed) / numvscales;
 
-       j = 0;
-       while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) {
-               speed = longhaul_table[j].frequency;
+       cpufreq_for_each_entry(freq_pos, longhaul_table) {
+               speed = freq_pos->frequency;
                if (speed > min_vid_speed)
                        pos = (speed - min_vid_speed) / kHz_step + minvid.pos;
                else
                        pos = minvid.pos;
-               longhaul_table[j].driver_data |= mV_vrm_table[pos] << 8;
+               freq_pos->driver_data |= mV_vrm_table[pos] << 8;
                vid = vrm_mV_table[mV_vrm_table[pos]];
                printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n",
-                               speed, j, vid.mV);
-               j++;
+                       speed, (int)(freq_pos - longhaul_table), vid.mV);
        }
 
        can_scale_voltage = 1;
index 84c84b5f0f3a426d70738730b8715defc18bec20..35dd4d7ffee0824be3bd1bb0ce55eca7074a8da4 100644 (file)
@@ -136,9 +136,10 @@ void restore_astate(int cpu)
 
 static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
+       struct cpufreq_frequency_table *pos;
        const u32 *max_freqp;
        u32 max_freq;
-       int i, cur_astate;
+       int cur_astate;
        struct resource res;
        struct device_node *cpu, *dn;
        int err = -ENODEV;
@@ -197,10 +198,9 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
        pr_debug("initializing frequency table\n");
 
        /* initialize frequency table */
-       for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
-               pas_freqs[i].frequency =
-                       get_astate_freq(pas_freqs[i].driver_data) * 100000;
-               pr_debug("%d: %d\n", i, pas_freqs[i].frequency);
+       cpufreq_for_each_entry(pos, pas_freqs) {
+               pos->frequency = get_astate_freq(pos->driver_data) * 100000;
+               pr_debug("%d: %d\n", (int)(pos - pas_freqs), pos->frequency);
        }
 
        cur_astate = get_cur_astate(policy->cpu);
index 78904e6ca4a020d53a60f4139711441a069ad447..c8012bc869107206ad1fd7024ab86a76a406890e 100644 (file)
@@ -151,6 +151,7 @@ static int powernow_k6_target(struct cpufreq_policy *policy,
 
 static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
 {
+       struct cpufreq_frequency_table *pos;
        unsigned int i, f;
        unsigned khz;
 
@@ -168,12 +169,11 @@ static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
                }
        }
        if (param_max_multiplier) {
-               for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
-                       if (clock_ratio[i].driver_data == param_max_multiplier) {
+               cpufreq_for_each_entry(pos, clock_ratio)
+                       if (pos->driver_data == param_max_multiplier) {
                                max_multiplier = param_max_multiplier;
                                goto have_max_multiplier;
                        }
-               }
                printk(KERN_ERR "powernow-k6: invalid max_multiplier parameter, valid parameters 20, 30, 35, 40, 45, 50, 55, 60\n");
                return -EINVAL;
        }
@@ -201,12 +201,12 @@ have_busfreq:
        param_busfreq = busfreq * 10;
 
        /* table init */
-       for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
-               f = clock_ratio[i].driver_data;
+       cpufreq_for_each_entry(pos, clock_ratio) {
+               f = pos->driver_data;
                if (f > max_multiplier)
-                       clock_ratio[i].frequency = CPUFREQ_ENTRY_INVALID;
+                       pos->frequency = CPUFREQ_ENTRY_INVALID;
                else
-                       clock_ratio[i].frequency = busfreq * f;
+                       pos->frequency = busfreq * f;
        }
 
        /* cpuinfo and default policy values */
index 5be8a48dba74fdca1d3d8b33ac02c93229b792c6..5a4c5a639f618f01074c5d1c73afa9b0e48352c3 100644 (file)
@@ -67,9 +67,10 @@ static int set_pmode(unsigned int cpu, unsigned int slow_mode)
 
 static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
+       struct cpufreq_frequency_table *pos;
        const u32 *max_freqp;
        u32 max_freq;
-       int i, cur_pmode;
+       int cur_pmode;
        struct device_node *cpu;
 
        cpu = of_get_cpu_node(policy->cpu, NULL);
@@ -102,9 +103,9 @@ static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy)
        pr_debug("initializing frequency table\n");
 
        /* initialize frequency table */
-       for (i=0; cbe_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
-               cbe_freqs[i].frequency = max_freq / cbe_freqs[i].driver_data;
-               pr_debug("%d: %d\n", i, cbe_freqs[i].frequency);
+       cpufreq_for_each_entry(pos, cbe_freqs) {
+               pos->frequency = max_freq / pos->driver_data;
+               pr_debug("%d: %d\n", (int)(pos - cbe_freqs), pos->frequency);
        }
 
        /* if DEBUG is enabled set_pmode() measures the latency
index 4626f90559b55167869b86aa6c0349cb45d29d46..2fd53eaaec20bf30ef00d6a19226fba13670501a 100644 (file)
@@ -266,7 +266,7 @@ out:
 static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq)
 {
        int count, v, i, found;
-       struct cpufreq_frequency_table *freq;
+       struct cpufreq_frequency_table *pos;
        struct s3c2416_dvfs *dvfs;
 
        count = regulator_count_voltages(s3c_freq->vddarm);
@@ -275,12 +275,11 @@ static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq)
                return;
        }
 
-       freq = s3c_freq->freq_table;
-       while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) {
-               if (freq->frequency == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       if (!count)
+               goto out;
 
-               dvfs = &s3c2416_dvfs_table[freq->driver_data];
+       cpufreq_for_each_valid_entry(pos, s3c_freq->freq_table) {
+               dvfs = &s3c2416_dvfs_table[pos->driver_data];
                found = 0;
 
                /* Check only the min-voltage, more is always ok on S3C2416 */
@@ -292,13 +291,12 @@ static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq)
 
                if (!found) {
                        pr_debug("cpufreq: %dkHz unsupported by regulator\n",
-                                freq->frequency);
-                       freq->frequency = CPUFREQ_ENTRY_INVALID;
+                                pos->frequency);
+                       pos->frequency = CPUFREQ_ENTRY_INVALID;
                }
-
-               freq++;
        }
 
+out:
        /* Guessed */
        s3c_freq->regulator_latency = 1 * 1000 * 1000;
 }
@@ -338,7 +336,7 @@ static struct notifier_block s3c2416_cpufreq_reboot_notifier = {
 static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy)
 {
        struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
-       struct cpufreq_frequency_table *freq;
+       struct cpufreq_frequency_table *pos;
        struct clk *msysclk;
        unsigned long rate;
        int ret;
@@ -427,31 +425,27 @@ static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy)
        s3c_freq->regulator_latency = 0;
 #endif
 
-       freq = s3c_freq->freq_table;
-       while (freq->frequency != CPUFREQ_TABLE_END) {
+       cpufreq_for_each_entry(pos, s3c_freq->freq_table) {
                /* special handling for dvs mode */
-               if (freq->driver_data == 0) {
+               if (pos->driver_data == 0) {
                        if (!s3c_freq->hclk) {
                                pr_debug("cpufreq: %dkHz unsupported as it would need unavailable dvs mode\n",
-                                        freq->frequency);
-                               freq->frequency = CPUFREQ_ENTRY_INVALID;
+                                        pos->frequency);
+                               pos->frequency = CPUFREQ_ENTRY_INVALID;
                        } else {
-                               freq++;
                                continue;
                        }
                }
 
                /* Check for frequencies we can generate */
                rate = clk_round_rate(s3c_freq->armdiv,
-                                     freq->frequency * 1000);
+                                     pos->frequency * 1000);
                rate /= 1000;
-               if (rate != freq->frequency) {
+               if (rate != pos->frequency) {
                        pr_debug("cpufreq: %dkHz unsupported by clock (clk_round_rate return %lu)\n",
-                                freq->frequency, rate);
-                       freq->frequency = CPUFREQ_ENTRY_INVALID;
+                               pos->frequency, rate);
+                       pos->frequency = CPUFREQ_ENTRY_INVALID;
                }
-
-               freq++;
        }
 
        /* Datasheet says PLL stabalisation time must be at least 300us,
index ff7d3ecb85f0f3c45430423942dc79a03f8837a6..176e84cc3991994871d6eee9a1b8927a3bdf95fa 100644 (file)
@@ -118,11 +118,10 @@ static void __init s3c64xx_cpufreq_config_regulator(void)
                pr_err("Unable to check supported voltages\n");
        }
 
-       freq = s3c64xx_freq_table;
-       while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) {
-               if (freq->frequency == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       if (!count)
+               goto out;
 
+       cpufreq_for_each_valid_entry(freq, s3c64xx_freq_table) {
                dvfs = &s3c64xx_dvfs_table[freq->driver_data];
                found = 0;
 
@@ -137,10 +136,9 @@ static void __init s3c64xx_cpufreq_config_regulator(void)
                                 freq->frequency);
                        freq->frequency = CPUFREQ_ENTRY_INVALID;
                }
-
-               freq++;
        }
 
+out:
        /* Guess based on having to do an I2C/SPI write; in future we
         * will be able to query the regulator performance here. */
        regulator_latency = 1 * 1000 * 1000;
@@ -179,8 +177,7 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
        }
 #endif
 
-       freq = s3c64xx_freq_table;
-       while (freq->frequency != CPUFREQ_TABLE_END) {
+       cpufreq_for_each_entry(freq, s3c64xx_freq_table) {
                unsigned long r;
 
                /* Check for frequencies we can generate */
@@ -196,8 +193,6 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
                 * frequency is the maximum we can support. */
                if (!vddarm && freq->frequency > clk_get_rate(policy->clk) / 1000)
                        freq->frequency = CPUFREQ_ENTRY_INVALID;
-
-               freq++;
        }
 
        /* Datasheet says PLL stabalisation time (if we were to use
index be56e8ac95e6e3d6240ec1039739f0efbaba74e1..aebde489c2914d07173558d0909203d9ffd04ee4 100644 (file)
@@ -28,13 +28,13 @@ config EXTCON_ADC_JACK
          Say Y here to enable extcon device driver based on ADC values.
 
 config EXTCON_MAX14577
-       tristate "MAX14577 EXTCON Support"
+       tristate "MAX14577/77836 EXTCON Support"
        depends on MFD_MAX14577
        select IRQ_DOMAIN
        select REGMAP_I2C
        help
          If you say yes here you get support for the MUIC device of
-         Maxim MAX14577 PMIC. The MAX14577 MUIC is a USB port accessory
+         Maxim MAX14577/77836. The MAX14577/77836 MUIC is a USB port accessory
          detector and switch.
 
 config EXTCON_MAX77693
index 3846941801b8fdc4eb5cc2e9adfd2024cf3e29d3..c76734a70171756a351167b58601712b0258c4fd 100644 (file)
@@ -1,8 +1,9 @@
 /*
- * extcon-max14577.c - MAX14577 extcon driver to support MAX14577 MUIC
+ * extcon-max14577.c - MAX14577/77836 extcon driver to support MUIC
  *
- * Copyright (C) 2013 Samsung Electrnoics
+ * Copyright (C) 2013,2014 Samsung Electrnoics
  * Chanwoo Choi <cw00.choi@samsung.com>
+ * Krzysztof Kozlowski <k.kozlowski@samsung.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
@@ -24,7 +25,6 @@
 #include <linux/mfd/max14577-private.h>
 #include <linux/extcon.h>
 
-#define        DEV_NAME                        "max14577-muic"
 #define        DELAY_MS_DEFAULT                17000           /* unit: millisecond */
 
 enum max14577_muic_adc_debounce_time {
@@ -40,6 +40,42 @@ enum max14577_muic_status {
        MAX14577_MUIC_STATUS_END,
 };
 
+/**
+ * struct max14577_muic_irq
+ * @irq: the index of irq list of MUIC device.
+ * @name: the name of irq.
+ * @virq: the virtual irq to use irq domain
+ */
+struct max14577_muic_irq {
+       unsigned int irq;
+       const char *name;
+       unsigned int virq;
+};
+
+static struct max14577_muic_irq max14577_muic_irqs[] = {
+       { MAX14577_IRQ_INT1_ADC,        "muic-ADC" },
+       { MAX14577_IRQ_INT1_ADCLOW,     "muic-ADCLOW" },
+       { MAX14577_IRQ_INT1_ADCERR,     "muic-ADCError" },
+       { MAX14577_IRQ_INT2_CHGTYP,     "muic-CHGTYP" },
+       { MAX14577_IRQ_INT2_CHGDETRUN,  "muic-CHGDETRUN" },
+       { MAX14577_IRQ_INT2_DCDTMR,     "muic-DCDTMR" },
+       { MAX14577_IRQ_INT2_DBCHG,      "muic-DBCHG" },
+       { MAX14577_IRQ_INT2_VBVOLT,     "muic-VBVOLT" },
+};
+
+static struct max14577_muic_irq max77836_muic_irqs[] = {
+       { MAX14577_IRQ_INT1_ADC,        "muic-ADC" },
+       { MAX14577_IRQ_INT1_ADCLOW,     "muic-ADCLOW" },
+       { MAX14577_IRQ_INT1_ADCERR,     "muic-ADCError" },
+       { MAX77836_IRQ_INT1_ADC1K,      "muic-ADC1K" },
+       { MAX14577_IRQ_INT2_CHGTYP,     "muic-CHGTYP" },
+       { MAX14577_IRQ_INT2_CHGDETRUN,  "muic-CHGDETRUN" },
+       { MAX14577_IRQ_INT2_DCDTMR,     "muic-DCDTMR" },
+       { MAX14577_IRQ_INT2_DBCHG,      "muic-DBCHG" },
+       { MAX14577_IRQ_INT2_VBVOLT,     "muic-VBVOLT" },
+       { MAX77836_IRQ_INT2_VIDRM,      "muic-VIDRM" },
+};
+
 struct max14577_muic_info {
        struct device *dev;
        struct max14577 *max14577;
@@ -48,6 +84,8 @@ struct max14577_muic_info {
        int prev_chg_type;
        u8 status[MAX14577_MUIC_STATUS_END];
 
+       struct max14577_muic_irq *muic_irqs;
+       unsigned int muic_irqs_num;
        bool irq_adc;
        bool irq_chg;
        struct work_struct irq_work;
@@ -74,29 +112,6 @@ enum max14577_muic_cable_group {
        MAX14577_CABLE_GROUP_CHG,
 };
 
-/**
- * struct max14577_muic_irq
- * @irq: the index of irq list of MUIC device.
- * @name: the name of irq.
- * @virq: the virtual irq to use irq domain
- */
-struct max14577_muic_irq {
-       unsigned int irq;
-       const char *name;
-       unsigned int virq;
-};
-
-static struct max14577_muic_irq muic_irqs[] = {
-       { MAX14577_IRQ_INT1_ADC,        "muic-ADC" },
-       { MAX14577_IRQ_INT1_ADCLOW,     "muic-ADCLOW" },
-       { MAX14577_IRQ_INT1_ADCERR,     "muic-ADCError" },
-       { MAX14577_IRQ_INT2_CHGTYP,     "muic-CHGTYP" },
-       { MAX14577_IRQ_INT2_CHGDETRUN,  "muic-CHGDETRUN" },
-       { MAX14577_IRQ_INT2_DCDTMR,     "muic-DCDTMR" },
-       { MAX14577_IRQ_INT2_DBCHG,      "muic-DBCHG" },
-       { MAX14577_IRQ_INT2_VBVOLT,     "muic-VBVOLT" },
-};
-
 /* Define supported accessory type */
 enum max14577_muic_acc_type {
        MAX14577_MUIC_ADC_GROUND = 0x0,
@@ -528,21 +543,12 @@ static void max14577_muic_irq_work(struct work_struct *work)
        return;
 }
 
-static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
+/*
+ * Sets irq_adc or irq_chg in max14577_muic_info and returns 1.
+ * Returns 0 if irq_type does not match registered IRQ for this device type.
+ */
+static int max14577_parse_irq(struct max14577_muic_info *info, int irq_type)
 {
-       struct max14577_muic_info *info = data;
-       int i, irq_type = -1;
-
-       /*
-        * We may be called multiple times for different nested IRQ-s.
-        * Including changes in INT1_ADC and INT2_CGHTYP at once.
-        * However we only need to know whether it was ADC, charger
-        * or both interrupts so decode IRQ and turn on proper flags.
-        */
-       for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
-               if (irq == muic_irqs[i].virq)
-                       irq_type = muic_irqs[i].irq;
-
        switch (irq_type) {
        case MAX14577_IRQ_INT1_ADC:
        case MAX14577_IRQ_INT1_ADCLOW:
@@ -550,7 +556,7 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
                /* Handle all of accessory except for
                   type of charger accessory */
                info->irq_adc = true;
-               break;
+               return 1;
        case MAX14577_IRQ_INT2_CHGTYP:
        case MAX14577_IRQ_INT2_CHGDETRUN:
        case MAX14577_IRQ_INT2_DCDTMR:
@@ -558,8 +564,62 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
        case MAX14577_IRQ_INT2_VBVOLT:
                /* Handle charger accessory */
                info->irq_chg = true;
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+/*
+ * Sets irq_adc or irq_chg in max14577_muic_info and returns 1.
+ * Returns 0 if irq_type does not match registered IRQ for this device type.
+ */
+static int max77836_parse_irq(struct max14577_muic_info *info, int irq_type)
+{
+       /* First check common max14577 interrupts */
+       if (max14577_parse_irq(info, irq_type))
+               return 1;
+
+       switch (irq_type) {
+       case MAX77836_IRQ_INT1_ADC1K:
+               info->irq_adc = true;
+               return 1;
+       case MAX77836_IRQ_INT2_VIDRM:
+               /* Handle charger accessory */
+               info->irq_chg = true;
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
+{
+       struct max14577_muic_info *info = data;
+       int i, irq_type = -1;
+       bool irq_parsed;
+
+       /*
+        * We may be called multiple times for different nested IRQ-s.
+        * Including changes in INT1_ADC and INT2_CGHTYP at once.
+        * However we only need to know whether it was ADC, charger
+        * or both interrupts so decode IRQ and turn on proper flags.
+        */
+       for (i = 0; i < info->muic_irqs_num; i++)
+               if (irq == info->muic_irqs[i].virq)
+                       irq_type = info->muic_irqs[i].irq;
+
+       switch (info->max14577->dev_type) {
+       case MAXIM_DEVICE_TYPE_MAX77836:
+               irq_parsed = max77836_parse_irq(info, irq_type);
                break;
+       case MAXIM_DEVICE_TYPE_MAX14577:
        default:
+               irq_parsed = max14577_parse_irq(info, irq_type);
+               break;
+       }
+
+       if (!irq_parsed) {
                dev_err(info->dev, "muic interrupt: irq %d occurred, skipped\n",
                                irq_type);
                return IRQ_HANDLED;
@@ -644,9 +704,20 @@ static int max14577_muic_probe(struct platform_device *pdev)
 
        INIT_WORK(&info->irq_work, max14577_muic_irq_work);
 
+       switch (max14577->dev_type) {
+       case MAXIM_DEVICE_TYPE_MAX77836:
+               info->muic_irqs = max77836_muic_irqs;
+               info->muic_irqs_num = ARRAY_SIZE(max77836_muic_irqs);
+               break;
+       case MAXIM_DEVICE_TYPE_MAX14577:
+       default:
+               info->muic_irqs = max14577_muic_irqs;
+               info->muic_irqs_num = ARRAY_SIZE(max14577_muic_irqs);
+       }
+
        /* Support irq domain for max14577 MUIC device */
-       for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
-               struct max14577_muic_irq *muic_irq = &muic_irqs[i];
+       for (i = 0; i < info->muic_irqs_num; i++) {
+               struct max14577_muic_irq *muic_irq = &info->muic_irqs[i];
                unsigned int virq = 0;
 
                virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq);
@@ -673,7 +744,8 @@ static int max14577_muic_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
                return -ENOMEM;
        }
-       info->edev->name = DEV_NAME;
+
+       info->edev->name = dev_name(&pdev->dev);
        info->edev->supported_cable = max14577_extcon_cable;
        ret = extcon_dev_register(info->edev);
        if (ret) {
@@ -735,18 +807,26 @@ static int max14577_muic_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct platform_device_id max14577_muic_id[] = {
+       { "max14577-muic", MAXIM_DEVICE_TYPE_MAX14577, },
+       { "max77836-muic", MAXIM_DEVICE_TYPE_MAX77836, },
+       { }
+};
+MODULE_DEVICE_TABLE(platform, max14577_muic_id);
+
 static struct platform_driver max14577_muic_driver = {
        .driver         = {
-               .name   = DEV_NAME,
+               .name   = "max14577-muic",
                .owner  = THIS_MODULE,
        },
        .probe          = max14577_muic_probe,
        .remove         = max14577_muic_remove,
+       .id_table       = max14577_muic_id,
 };
 
 module_platform_driver(max14577_muic_driver);
 
-MODULE_DESCRIPTION("MAXIM 14577 Extcon driver");
-MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
+MODULE_DESCRIPTION("Maxim 14577/77836 Extcon driver");
+MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:extcon-max14577");
index 2776a09bee58df0207968a74fa9fdc435fb5f402..628b58494294a97f2e05a18b59b8d3dea4777a25 100644 (file)
@@ -23,7 +23,8 @@
 enum { REG_RE, REG_FE, REG_IE };
 
 #define CACHE_NR_REGS  3
-#define CACHE_NR_BANKS (STMPE_NR_GPIOS / 8)
+/* No variant has more than 24 GPIOs */
+#define CACHE_NR_BANKS (24 / 8)
 
 struct stmpe_gpio {
        struct gpio_chip chip;
@@ -31,8 +32,6 @@ struct stmpe_gpio {
        struct device *dev;
        struct mutex irq_lock;
        struct irq_domain *domain;
-
-       int irq_base;
        unsigned norequest_mask;
 
        /* Caches of interrupt control registers for bus_lock */
@@ -311,13 +310,8 @@ static const struct irq_domain_ops stmpe_gpio_irq_simple_ops = {
 static int stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio,
                struct device_node *np)
 {
-       int base = 0;
-
-       if (!np)
-               base = stmpe_gpio->irq_base;
-
        stmpe_gpio->domain = irq_domain_add_simple(np,
-                               stmpe_gpio->chip.ngpio, base,
+                               stmpe_gpio->chip.ngpio, 0,
                                &stmpe_gpio_irq_simple_ops, stmpe_gpio);
        if (!stmpe_gpio->domain) {
                dev_err(stmpe_gpio->dev, "failed to create irqdomain\n");
@@ -354,7 +348,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
 #ifdef CONFIG_OF
        stmpe_gpio->chip.of_node = np;
 #endif
-       stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1;
+       stmpe_gpio->chip.base = -1;
 
        if (pdata)
                stmpe_gpio->norequest_mask = pdata->norequest_mask;
@@ -362,9 +356,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
                of_property_read_u32(np, "st,norequest-mask",
                                &stmpe_gpio->norequest_mask);
 
-       if (irq >= 0)
-               stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0);
-       else
+       if (irq < 0)
                dev_info(&pdev->dev,
                        "device configured in no-irq mode; "
                        "irqs are not available\n");
index 1b37cf8cd20478a4b9ba1539d3f9289eb5078c75..7310e32b5991af59276f0f3fb7a86d715191bb35 100644 (file)
@@ -52,3 +52,13 @@ config MEMSTICK_REALTEK_PCI
 
          To compile this driver as a module, choose M here: the module will
          be called rtsx_pci_ms.
+
+config MEMSTICK_REALTEK_USB
+       tristate "Realtek USB Memstick Card Interface Driver"
+       depends on MFD_RTSX_USB
+       help
+         Say Y here to include driver code to support Memstick card interface
+         of Realtek RTS5129/39 series USB card reader
+
+         To compile this driver as a module, choose M here: the module will
+         be called rts5139_ms.
index af3459d7686ef65ace7bfc1d91cc8623f7418a5d..491c9557441ddfa7a3135dd037ed8085c09d4fd4 100644 (file)
@@ -6,3 +6,4 @@ obj-$(CONFIG_MEMSTICK_TIFM_MS)          += tifm_ms.o
 obj-$(CONFIG_MEMSTICK_JMICRON_38X)     += jmb38x_ms.o
 obj-$(CONFIG_MEMSTICK_R592)            += r592.o
 obj-$(CONFIG_MEMSTICK_REALTEK_PCI)     += rtsx_pci_ms.o
+obj-$(CONFIG_MEMSTICK_REALTEK_USB)     += rtsx_usb_ms.o
diff --git a/drivers/memstick/host/rtsx_usb_ms.c b/drivers/memstick/host/rtsx_usb_ms.c
new file mode 100644 (file)
index 0000000..a7282b7
--- /dev/null
@@ -0,0 +1,839 @@
+/* Realtek USB Memstick Card Interface driver
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Roger Tseng <rogerable@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/memstick.h>
+#include <linux/kthread.h>
+#include <linux/mfd/rtsx_usb.h>
+#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <asm/unaligned.h>
+
+struct rtsx_usb_ms {
+       struct platform_device  *pdev;
+       struct rtsx_ucr *ucr;
+       struct memstick_host    *msh;
+       struct memstick_request *req;
+
+       struct mutex            host_mutex;
+       struct work_struct      handle_req;
+
+       struct task_struct      *detect_ms;
+       struct completion       detect_ms_exit;
+
+       u8                      ssc_depth;
+       unsigned int            clock;
+       int                     power_mode;
+       unsigned char           ifmode;
+       bool                    eject;
+};
+
+static inline struct device *ms_dev(struct rtsx_usb_ms *host)
+{
+       return &(host->pdev->dev);
+}
+
+static inline void ms_clear_error(struct rtsx_usb_ms *host)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       rtsx_usb_ep0_write_register(ucr, CARD_STOP,
+                                 MS_STOP | MS_CLR_ERR,
+                                 MS_STOP | MS_CLR_ERR);
+
+       rtsx_usb_clear_dma_err(ucr);
+       rtsx_usb_clear_fsm_err(ucr);
+}
+
+#ifdef DEBUG
+
+static void ms_print_debug_regs(struct rtsx_usb_ms *host)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       u16 i;
+       u8 *ptr;
+
+       /* Print MS host internal registers */
+       rtsx_usb_init_cmd(ucr);
+
+       /* MS_CFG to MS_INT_REG */
+       for (i = 0xFD40; i <= 0xFD44; i++)
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0);
+
+       /* CARD_SHARE_MODE to CARD_GPIO */
+       for (i = 0xFD51; i <= 0xFD56; i++)
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0);
+
+       /* CARD_PULL_CTLx */
+       for (i = 0xFD60; i <= 0xFD65; i++)
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0);
+
+       /* CARD_DATA_SOURCE, CARD_SELECT, CARD_CLK_EN, CARD_PWR_CTL */
+       rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_DATA_SOURCE, 0, 0);
+       rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_SELECT, 0, 0);
+       rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_CLK_EN, 0, 0);
+       rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_PWR_CTL, 0, 0);
+
+       rtsx_usb_send_cmd(ucr, MODE_CR, 100);
+       rtsx_usb_get_rsp(ucr, 21, 100);
+
+       ptr = ucr->rsp_buf;
+       for (i = 0xFD40; i <= 0xFD44; i++)
+               dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
+       for (i = 0xFD51; i <= 0xFD56; i++)
+               dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
+       for (i = 0xFD60; i <= 0xFD65; i++)
+               dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
+
+       dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_DATA_SOURCE, *(ptr++));
+       dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_SELECT, *(ptr++));
+       dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_CLK_EN, *(ptr++));
+       dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_PWR_CTL, *(ptr++));
+}
+
+#else
+
+static void ms_print_debug_regs(struct rtsx_usb_ms *host)
+{
+}
+
+#endif
+
+static int ms_pull_ctl_disable_lqfp48(struct rtsx_ucr *ucr)
+{
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5);
+
+       return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+
+static int ms_pull_ctl_disable_qfn24(struct rtsx_ucr *ucr)
+{
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x65);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x56);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x59);
+
+       return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+
+static int ms_pull_ctl_enable_lqfp48(struct rtsx_ucr *ucr)
+{
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5);
+
+       return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+
+static int ms_pull_ctl_enable_qfn24(struct rtsx_ucr *ucr)
+{
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x65);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x59);
+
+       return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+
+static int ms_power_on(struct rtsx_usb_ms *host)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       int err;
+
+       dev_dbg(ms_dev(host), "%s\n", __func__);
+
+       rtsx_usb_init_cmd(ucr);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SELECT, 0x07, MS_MOD_SEL);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SHARE_MODE,
+                       CARD_SHARE_MASK, CARD_SHARE_MS);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN,
+                       MS_CLK_EN, MS_CLK_EN);
+       err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
+       if (err < 0)
+               return err;
+
+       if (CHECK_PKG(ucr, LQFP48))
+               err = ms_pull_ctl_enable_lqfp48(ucr);
+       else
+               err = ms_pull_ctl_enable_qfn24(ucr);
+       if (err < 0)
+               return err;
+
+       err = rtsx_usb_write_register(ucr, CARD_PWR_CTL,
+                       POWER_MASK, PARTIAL_POWER_ON);
+       if (err)
+               return err;
+
+       usleep_range(800, 1000);
+
+       rtsx_usb_init_cmd(ucr);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
+                       POWER_MASK, POWER_ON);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE,
+                       MS_OUTPUT_EN, MS_OUTPUT_EN);
+
+       return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+
+static int ms_power_off(struct rtsx_usb_ms *host)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       int err;
+
+       dev_dbg(ms_dev(host), "%s\n", __func__);
+
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, 0);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0);
+
+       err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
+       if (err < 0)
+               return err;
+
+       if (CHECK_PKG(ucr, LQFP48))
+               return ms_pull_ctl_disable_lqfp48(ucr);
+
+       return ms_pull_ctl_disable_qfn24(ucr);
+}
+
+static int ms_transfer_data(struct rtsx_usb_ms *host, unsigned char data_dir,
+               u8 tpc, u8 cfg, struct scatterlist *sg)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       int err;
+       unsigned int length = sg->length;
+       u16 sec_cnt = (u16)(length / 512);
+       u8 trans_mode, dma_dir, flag;
+       unsigned int pipe;
+       struct memstick_dev *card = host->msh->card;
+
+       dev_dbg(ms_dev(host), "%s: tpc = 0x%02x, data_dir = %s, length = %d\n",
+                       __func__, tpc, (data_dir == READ) ? "READ" : "WRITE",
+                       length);
+
+       if (data_dir == READ) {
+               flag = MODE_CDIR;
+               dma_dir = DMA_DIR_FROM_CARD;
+               if (card->id.type != MEMSTICK_TYPE_PRO)
+                       trans_mode = MS_TM_NORMAL_READ;
+               else
+                       trans_mode = MS_TM_AUTO_READ;
+               pipe = usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN);
+       } else {
+               flag = MODE_CDOR;
+               dma_dir = DMA_DIR_TO_CARD;
+               if (card->id.type != MEMSTICK_TYPE_PRO)
+                       trans_mode = MS_TM_NORMAL_WRITE;
+               else
+                       trans_mode = MS_TM_AUTO_WRITE;
+               pipe = usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT);
+       }
+
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+       if (card->id.type == MEMSTICK_TYPE_PRO) {
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_SECTOR_CNT_H,
+                               0xFF, (u8)(sec_cnt >> 8));
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_SECTOR_CNT_L,
+                               0xFF, (u8)sec_cnt);
+       }
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC3,
+                       0xFF, (u8)(length >> 24));
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC2,
+                       0xFF, (u8)(length >> 16));
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC1,
+                       0xFF, (u8)(length >> 8));
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC0, 0xFF,
+                       (u8)length);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_CTL,
+                       0x03 | DMA_PACK_SIZE_MASK, dma_dir | DMA_EN | DMA_512);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, RING_BUFFER);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER,
+                       0xFF, MS_TRANSFER_START | trans_mode);
+       rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER,
+                       MS_TRANSFER_END, MS_TRANSFER_END);
+
+       err = rtsx_usb_send_cmd(ucr, flag | STAGE_MS_STATUS, 100);
+       if (err)
+               return err;
+
+       err = rtsx_usb_transfer_data(ucr, pipe, sg, length,
+                       1, NULL, 10000);
+       if (err)
+               goto err_out;
+
+       err = rtsx_usb_get_rsp(ucr, 3, 15000);
+       if (err)
+               goto err_out;
+
+       if (ucr->rsp_buf[0] & MS_TRANSFER_ERR ||
+           ucr->rsp_buf[1] & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) {
+               err = -EIO;
+               goto err_out;
+       }
+       return 0;
+err_out:
+       ms_clear_error(host);
+       return err;
+}
+
+static int ms_write_bytes(struct rtsx_usb_ms *host, u8 tpc,
+               u8 cfg, u8 cnt, u8 *data, u8 *int_reg)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       int err, i;
+
+       dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc);
+
+       rtsx_usb_init_cmd(ucr);
+
+       for (i = 0; i < cnt; i++)
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               PPBUF_BASE2 + i, 0xFF, data[i]);
+
+       if (cnt % 2)
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               PPBUF_BASE2 + i, 0xFF, 0xFF);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, PINGPONG_BUFFER);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER,
+                       0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES);
+       rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER,
+                       MS_TRANSFER_END, MS_TRANSFER_END);
+       rtsx_usb_add_cmd(ucr, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
+
+       err = rtsx_usb_send_cmd(ucr, MODE_CR, 100);
+       if (err)
+               return err;
+
+       err = rtsx_usb_get_rsp(ucr, 2, 5000);
+       if (err || (ucr->rsp_buf[0] & MS_TRANSFER_ERR)) {
+               u8 val;
+
+               rtsx_usb_ep0_read_register(ucr, MS_TRANS_CFG, &val);
+               dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val);
+
+               if (int_reg)
+                       *int_reg = val & 0x0F;
+
+               ms_print_debug_regs(host);
+
+               ms_clear_error(host);
+
+               if (!(tpc & 0x08)) {
+                       if (val & MS_CRC16_ERR)
+                               return -EIO;
+               } else {
+                       if (!(val & 0x80)) {
+                               if (val & (MS_INT_ERR | MS_INT_CMDNK))
+                                       return -EIO;
+                       }
+               }
+
+               return -ETIMEDOUT;
+       }
+
+       if (int_reg)
+               *int_reg = ucr->rsp_buf[1] & 0x0F;
+
+       return 0;
+}
+
+static int ms_read_bytes(struct rtsx_usb_ms *host, u8 tpc,
+               u8 cfg, u8 cnt, u8 *data, u8 *int_reg)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       int err, i;
+       u8 *ptr;
+
+       dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc);
+
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, PINGPONG_BUFFER);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER,
+                       0xFF, MS_TRANSFER_START | MS_TM_READ_BYTES);
+       rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER,
+                       MS_TRANSFER_END, MS_TRANSFER_END);
+       for (i = 0; i < cnt - 1; i++)
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0);
+       if (cnt % 2)
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD, PPBUF_BASE2 + cnt, 0, 0);
+       else
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD,
+                               PPBUF_BASE2 + cnt - 1, 0, 0);
+
+       rtsx_usb_add_cmd(ucr, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
+
+       err = rtsx_usb_send_cmd(ucr, MODE_CR, 100);
+       if (err)
+               return err;
+
+       err = rtsx_usb_get_rsp(ucr, cnt + 2, 5000);
+       if (err || (ucr->rsp_buf[0] & MS_TRANSFER_ERR)) {
+               u8 val;
+
+               rtsx_usb_ep0_read_register(ucr, MS_TRANS_CFG, &val);
+               dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val);
+
+               if (int_reg && (host->ifmode != MEMSTICK_SERIAL))
+                       *int_reg = val & 0x0F;
+
+               ms_print_debug_regs(host);
+
+               ms_clear_error(host);
+
+               if (!(tpc & 0x08)) {
+                       if (val & MS_CRC16_ERR)
+                               return -EIO;
+               } else {
+                       if (!(val & 0x80)) {
+                               if (val & (MS_INT_ERR | MS_INT_CMDNK))
+                                       return -EIO;
+                       }
+               }
+
+               return -ETIMEDOUT;
+       }
+
+       ptr = ucr->rsp_buf + 1;
+       for (i = 0; i < cnt; i++)
+               data[i] = *ptr++;
+
+
+       if (int_reg && (host->ifmode != MEMSTICK_SERIAL))
+               *int_reg = *ptr & 0x0F;
+
+       return 0;
+}
+
+static int rtsx_usb_ms_issue_cmd(struct rtsx_usb_ms *host)
+{
+       struct memstick_request *req = host->req;
+       int err = 0;
+       u8 cfg = 0, int_reg;
+
+       dev_dbg(ms_dev(host), "%s\n", __func__);
+
+       if (req->need_card_int) {
+               if (host->ifmode != MEMSTICK_SERIAL)
+                       cfg = WAIT_INT;
+       }
+
+       if (req->long_data) {
+               err = ms_transfer_data(host, req->data_dir,
+                               req->tpc, cfg, &(req->sg));
+       } else {
+               if (req->data_dir == READ)
+                       err = ms_read_bytes(host, req->tpc, cfg,
+                                       req->data_len, req->data, &int_reg);
+               else
+                       err = ms_write_bytes(host, req->tpc, cfg,
+                                       req->data_len, req->data, &int_reg);
+       }
+       if (err < 0)
+               return err;
+
+       if (req->need_card_int) {
+               if (host->ifmode == MEMSTICK_SERIAL) {
+                       err = ms_read_bytes(host, MS_TPC_GET_INT,
+                                       NO_WAIT_INT, 1, &req->int_reg, NULL);
+                       if (err < 0)
+                               return err;
+               } else {
+
+                       if (int_reg & MS_INT_CMDNK)
+                               req->int_reg |= MEMSTICK_INT_CMDNAK;
+                       if (int_reg & MS_INT_BREQ)
+                               req->int_reg |= MEMSTICK_INT_BREQ;
+                       if (int_reg & MS_INT_ERR)
+                               req->int_reg |= MEMSTICK_INT_ERR;
+                       if (int_reg & MS_INT_CED)
+                               req->int_reg |= MEMSTICK_INT_CED;
+               }
+               dev_dbg(ms_dev(host), "int_reg: 0x%02x\n", req->int_reg);
+       }
+
+       return 0;
+}
+
+static void rtsx_usb_ms_handle_req(struct work_struct *work)
+{
+       struct rtsx_usb_ms *host = container_of(work,
+                       struct rtsx_usb_ms, handle_req);
+       struct rtsx_ucr *ucr = host->ucr;
+       struct memstick_host *msh = host->msh;
+       int rc;
+
+       if (!host->req) {
+               do {
+                       rc = memstick_next_req(msh, &host->req);
+                       dev_dbg(ms_dev(host), "next req %d\n", rc);
+
+                       if (!rc) {
+                               mutex_lock(&ucr->dev_mutex);
+
+                               if (rtsx_usb_card_exclusive_check(ucr,
+                                                       RTSX_USB_MS_CARD))
+                                       host->req->error = -EIO;
+                               else
+                                       host->req->error =
+                                               rtsx_usb_ms_issue_cmd(host);
+
+                               mutex_unlock(&ucr->dev_mutex);
+
+                               dev_dbg(ms_dev(host), "req result %d\n",
+                                               host->req->error);
+                       }
+               } while (!rc);
+       }
+
+}
+
+static void rtsx_usb_ms_request(struct memstick_host *msh)
+{
+       struct rtsx_usb_ms *host = memstick_priv(msh);
+
+       dev_dbg(ms_dev(host), "--> %s\n", __func__);
+
+       if (!host->eject)
+               schedule_work(&host->handle_req);
+}
+
+static int rtsx_usb_ms_set_param(struct memstick_host *msh,
+               enum memstick_param param, int value)
+{
+       struct rtsx_usb_ms *host = memstick_priv(msh);
+       struct rtsx_ucr *ucr = host->ucr;
+       unsigned int clock = 0;
+       u8 ssc_depth = 0;
+       int err;
+
+       dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n",
+                       __func__, param, value);
+
+       mutex_lock(&ucr->dev_mutex);
+
+       err = rtsx_usb_card_exclusive_check(ucr, RTSX_USB_MS_CARD);
+       if (err)
+               goto out;
+
+       switch (param) {
+       case MEMSTICK_POWER:
+               if (value == host->power_mode)
+                       break;
+
+               if (value == MEMSTICK_POWER_ON) {
+                       pm_runtime_get_sync(ms_dev(host));
+                       err = ms_power_on(host);
+               } else if (value == MEMSTICK_POWER_OFF) {
+                       err = ms_power_off(host);
+                       if (host->msh->card)
+                               pm_runtime_put_noidle(ms_dev(host));
+                       else
+                               pm_runtime_put(ms_dev(host));
+               } else
+                       err = -EINVAL;
+               if (!err)
+                       host->power_mode = value;
+               break;
+
+       case MEMSTICK_INTERFACE:
+               if (value == MEMSTICK_SERIAL) {
+                       clock = 19000000;
+                       ssc_depth = SSC_DEPTH_512K;
+                       err = rtsx_usb_write_register(ucr, MS_CFG, 0x5A,
+                                      MS_BUS_WIDTH_1 | PUSH_TIME_DEFAULT);
+                       if (err < 0)
+                               break;
+               } else if (value == MEMSTICK_PAR4) {
+                       clock = 39000000;
+                       ssc_depth = SSC_DEPTH_1M;
+
+                       err = rtsx_usb_write_register(ucr, MS_CFG, 0x5A,
+                                       MS_BUS_WIDTH_4 | PUSH_TIME_ODD |
+                                       MS_NO_CHECK_INT);
+                       if (err < 0)
+                               break;
+               } else {
+                       err = -EINVAL;
+                       break;
+               }
+
+               err = rtsx_usb_switch_clock(ucr, clock,
+                               ssc_depth, false, true, false);
+               if (err < 0) {
+                       dev_dbg(ms_dev(host), "switch clock failed\n");
+                       break;
+               }
+
+               host->ssc_depth = ssc_depth;
+               host->clock = clock;
+               host->ifmode = value;
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+out:
+       mutex_unlock(&ucr->dev_mutex);
+
+       /* power-on delay */
+       if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON)
+               usleep_range(10000, 12000);
+
+       dev_dbg(ms_dev(host), "%s: return = %d\n", __func__, err);
+       return err;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rtsx_usb_ms_suspend(struct device *dev)
+{
+       struct rtsx_usb_ms *host = dev_get_drvdata(dev);
+       struct memstick_host *msh = host->msh;
+
+       dev_dbg(ms_dev(host), "--> %s\n", __func__);
+
+       memstick_suspend_host(msh);
+       return 0;
+}
+
+static int rtsx_usb_ms_resume(struct device *dev)
+{
+       struct rtsx_usb_ms *host = dev_get_drvdata(dev);
+       struct memstick_host *msh = host->msh;
+
+       dev_dbg(ms_dev(host), "--> %s\n", __func__);
+
+       memstick_resume_host(msh);
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+/*
+ * Thread function of ms card slot detection. The thread starts right after
+ * successful host addition. It stops while the driver removal function sets
+ * host->eject true.
+ */
+static int rtsx_usb_detect_ms_card(void *__host)
+{
+       struct rtsx_usb_ms *host = (struct rtsx_usb_ms *)__host;
+       struct rtsx_ucr *ucr = host->ucr;
+       u8 val = 0;
+       int err;
+
+       for (;;) {
+               mutex_lock(&ucr->dev_mutex);
+
+               /* Check pending MS card changes */
+               err = rtsx_usb_read_register(ucr, CARD_INT_PEND, &val);
+               if (err) {
+                       mutex_unlock(&ucr->dev_mutex);
+                       goto poll_again;
+               }
+
+               /* Clear the pending */
+               rtsx_usb_write_register(ucr, CARD_INT_PEND,
+                               XD_INT | MS_INT | SD_INT,
+                               XD_INT | MS_INT | SD_INT);
+
+               mutex_unlock(&ucr->dev_mutex);
+
+               if (val & MS_INT) {
+                       dev_dbg(ms_dev(host), "MS slot change detected\n");
+                       memstick_detect_change(host->msh);
+               }
+
+poll_again:
+               if (host->eject)
+                       break;
+
+               msleep(1000);
+       }
+
+       complete(&host->detect_ms_exit);
+       return 0;
+}
+
+static int rtsx_usb_ms_drv_probe(struct platform_device *pdev)
+{
+       struct memstick_host *msh;
+       struct rtsx_usb_ms *host;
+       struct rtsx_ucr *ucr;
+       int err;
+
+       ucr = usb_get_intfdata(to_usb_interface(pdev->dev.parent));
+       if (!ucr)
+               return -ENXIO;
+
+       dev_dbg(&(pdev->dev),
+                       "Realtek USB Memstick controller found\n");
+
+       msh = memstick_alloc_host(sizeof(*host), &pdev->dev);
+       if (!msh)
+               return -ENOMEM;
+
+       host = memstick_priv(msh);
+       host->ucr = ucr;
+       host->msh = msh;
+       host->pdev = pdev;
+       host->power_mode = MEMSTICK_POWER_OFF;
+       platform_set_drvdata(pdev, host);
+
+       mutex_init(&host->host_mutex);
+       INIT_WORK(&host->handle_req, rtsx_usb_ms_handle_req);
+
+       init_completion(&host->detect_ms_exit);
+       host->detect_ms = kthread_create(rtsx_usb_detect_ms_card, host,
+                       "rtsx_usb_ms_%d", pdev->id);
+       if (IS_ERR(host->detect_ms)) {
+               dev_dbg(&(pdev->dev),
+                               "Unable to create polling thread.\n");
+               err = PTR_ERR(host->detect_ms);
+               goto err_out;
+       }
+
+       msh->request = rtsx_usb_ms_request;
+       msh->set_param = rtsx_usb_ms_set_param;
+       msh->caps = MEMSTICK_CAP_PAR4;
+
+       pm_runtime_enable(&pdev->dev);
+       err = memstick_add_host(msh);
+       if (err)
+               goto err_out;
+
+       wake_up_process(host->detect_ms);
+       return 0;
+err_out:
+       memstick_free_host(msh);
+       return err;
+}
+
+static int rtsx_usb_ms_drv_remove(struct platform_device *pdev)
+{
+       struct rtsx_usb_ms *host = platform_get_drvdata(pdev);
+       struct memstick_host *msh;
+       int err;
+
+       msh = host->msh;
+       host->eject = true;
+       cancel_work_sync(&host->handle_req);
+
+       mutex_lock(&host->host_mutex);
+       if (host->req) {
+               dev_dbg(&(pdev->dev),
+                       "%s: Controller removed during transfer\n",
+                       dev_name(&msh->dev));
+               host->req->error = -ENOMEDIUM;
+               do {
+                       err = memstick_next_req(msh, &host->req);
+                       if (!err)
+                               host->req->error = -ENOMEDIUM;
+               } while (!err);
+       }
+       mutex_unlock(&host->host_mutex);
+
+       wait_for_completion(&host->detect_ms_exit);
+       memstick_remove_host(msh);
+       memstick_free_host(msh);
+
+       /* Balance possible unbalanced usage count
+        * e.g. unconditional module removal
+        */
+       if (pm_runtime_active(ms_dev(host)))
+               pm_runtime_put(ms_dev(host));
+
+       pm_runtime_disable(&pdev->dev);
+       platform_set_drvdata(pdev, NULL);
+
+       dev_dbg(&(pdev->dev),
+               ": Realtek USB Memstick controller has been removed\n");
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rtsx_usb_ms_pm_ops,
+               rtsx_usb_ms_suspend, rtsx_usb_ms_resume);
+
+static struct platform_device_id rtsx_usb_ms_ids[] = {
+       {
+               .name = "rtsx_usb_ms",
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(platform, rtsx_usb_ms_ids);
+
+static struct platform_driver rtsx_usb_ms_driver = {
+       .probe          = rtsx_usb_ms_drv_probe,
+       .remove         = rtsx_usb_ms_drv_remove,
+       .id_table       = rtsx_usb_ms_ids,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "rtsx_usb_ms",
+               .pm     = &rtsx_usb_ms_pm_ops,
+       },
+};
+module_platform_driver(rtsx_usb_ms_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>");
+MODULE_DESCRIPTION("Realtek USB Memstick Card Host Driver");
index 33834120d057117f5b98284227ee0e3ec59df371..e166d7176d7af34d56de4b4c28e9b635e0ad80c3 100644 (file)
@@ -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.
@@ -675,6 +675,7 @@ config MFD_DB8500_PRCMU
 config MFD_STMPE
        bool "STMicroelectronics STMPE"
        depends on (I2C=y || SPI_MASTER=y)
+       depends on OF
        select MFD_CORE
        help
          Support for the STMPE family of I/O Expanders from
index 1c3ae57082ed7bc1a2dcd8583a507875f3b7d92b..07e6e27be23cbc178254c26dcf77031128eebf42 100644 (file)
@@ -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);
index 7694e0700d340329c5696c4ccba0417f9f93486f..b11fdd63eecdaa68b58cdefd9af1204c2bf69887 100644 (file)
@@ -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)
index 5f13cefe8defcf73ed33e1c179de6b922f88b1fc..484d372a4892b642b0f064dd327ee7195e1ae0de 100644 (file)
@@ -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");
index 06e64b6fcb89d66aaa7299bb1384acc26b47884b..0c6c21c5b1a841250a0f1b457005ccff1482d19c 100644 (file)
@@ -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");
index b53b9d46cc4534671066ae03950fb44f25f36af9..141ea52c46e3affedc153b2c00e8640d111fb144 100644 (file)
@@ -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;
 }
 
index 0da02e11d58e30a6b935c0f2bf0d5173fb4c9440..a45f9c0a330a8e61e3046a55399adab011ceafd4 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/types.h>
+#include <linux/of_device.h>
 #include "stmpe.h"
 
 static int i2c_reg_read(struct stmpe *stmpe, u8 reg)
@@ -52,15 +53,41 @@ static struct stmpe_client_info i2c_ci = {
        .write_block = i2c_block_write,
 };
 
+static const struct of_device_id stmpe_of_match[] = {
+       { .compatible = "st,stmpe610", .data = (void *)STMPE610, },
+       { .compatible = "st,stmpe801", .data = (void *)STMPE801, },
+       { .compatible = "st,stmpe811", .data = (void *)STMPE811, },
+       { .compatible = "st,stmpe1601", .data = (void *)STMPE1601, },
+       { .compatible = "st,stmpe1801", .data = (void *)STMPE1801, },
+       { .compatible = "st,stmpe2401", .data = (void *)STMPE2401, },
+       { .compatible = "st,stmpe2403", .data = (void *)STMPE2403, },
+       {},
+};
+MODULE_DEVICE_TABLE(of, stmpe_of_match);
+
 static int
 stmpe_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
 {
+       int partnum;
+       const struct of_device_id *of_id;
+
        i2c_ci.data = (void *)id;
        i2c_ci.irq = i2c->irq;
        i2c_ci.client = i2c;
        i2c_ci.dev = &i2c->dev;
 
-       return stmpe_probe(&i2c_ci, id->driver_data);
+       of_id = of_match_device(stmpe_of_match, &i2c->dev);
+       if (!of_id) {
+               /*
+                * This happens when the I2C ID matches the node name
+                * but no real compatible string has been given.
+                */
+               dev_info(&i2c->dev, "matching on node name, compatible is preferred\n");
+               partnum = id->driver_data;
+       } else
+               partnum = (int)of_id->data;
+
+       return stmpe_probe(&i2c_ci, partnum);
 }
 
 static int stmpe_i2c_remove(struct i2c_client *i2c)
@@ -89,6 +116,7 @@ static struct i2c_driver stmpe_i2c_driver = {
 #ifdef CONFIG_PM
                .pm = &stmpe_dev_pm_ops,
 #endif
+               .of_match_table = stmpe_of_match,
        },
        .probe          = stmpe_i2c_probe,
        .remove         = stmpe_i2c_remove,
index 4a91f6771fb82da541868250a6f85ccd76d24abd..3b6bfa7184ad7167716bbea2c04f7b4f3375847b 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/slab.h>
 #include <linux/mfd/core.h>
 #include <linux/delay.h>
+#include <linux/regulator/consumer.h>
 #include "stmpe.h"
 
 static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
@@ -605,9 +606,18 @@ static int stmpe1601_enable(struct stmpe *stmpe, unsigned int blocks,
 
        if (blocks & STMPE_BLOCK_GPIO)
                mask |= STMPE1601_SYS_CTRL_ENABLE_GPIO;
+       else
+               mask &= ~STMPE1601_SYS_CTRL_ENABLE_GPIO;
 
        if (blocks & STMPE_BLOCK_KEYPAD)
                mask |= STMPE1601_SYS_CTRL_ENABLE_KPC;
+       else
+               mask &= ~STMPE1601_SYS_CTRL_ENABLE_KPC;
+
+       if (blocks & STMPE_BLOCK_PWM)
+               mask |= STMPE1601_SYS_CTRL_ENABLE_SPWM;
+       else
+               mask &= ~STMPE1601_SYS_CTRL_ENABLE_SPWM;
 
        return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL, mask,
                                enable ? mask : 0);
@@ -986,9 +996,6 @@ static int stmpe_irq_init(struct stmpe *stmpe, struct device_node *np)
        int base = 0;
        int num_irqs = stmpe->variant->num_irqs;
 
-       if (!np)
-               base = stmpe->irq_base;
-
        stmpe->domain = irq_domain_add_simple(np, num_irqs, base,
                                              &stmpe_irq_ops, stmpe);
        if (!stmpe->domain) {
@@ -1067,7 +1074,7 @@ static int stmpe_chip_init(struct stmpe *stmpe)
 static int stmpe_add_device(struct stmpe *stmpe, const struct mfd_cell *cell)
 {
        return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1,
-                              NULL, stmpe->irq_base, stmpe->domain);
+                              NULL, 0, stmpe->domain);
 }
 
 static int stmpe_devices_init(struct stmpe *stmpe)
@@ -1171,12 +1178,23 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum)
        stmpe->dev = ci->dev;
        stmpe->client = ci->client;
        stmpe->pdata = pdata;
-       stmpe->irq_base = pdata->irq_base;
        stmpe->ci = ci;
        stmpe->partnum = partnum;
        stmpe->variant = stmpe_variant_info[partnum];
        stmpe->regs = stmpe->variant->regs;
        stmpe->num_gpios = stmpe->variant->num_gpios;
+       stmpe->vcc = devm_regulator_get_optional(ci->dev, "vcc");
+       if (!IS_ERR(stmpe->vcc)) {
+               ret = regulator_enable(stmpe->vcc);
+               if (ret)
+                       dev_warn(ci->dev, "failed to enable VCC supply\n");
+       }
+       stmpe->vio = devm_regulator_get_optional(ci->dev, "vio");
+       if (!IS_ERR(stmpe->vio)) {
+               ret = regulator_enable(stmpe->vio);
+               if (ret)
+                       dev_warn(ci->dev, "failed to enable VIO supply\n");
+       }
        dev_set_drvdata(stmpe->dev, stmpe);
 
        if (ci->init)
@@ -1243,6 +1261,11 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum)
 
 int stmpe_remove(struct stmpe *stmpe)
 {
+       if (!IS_ERR(stmpe->vio))
+               regulator_disable(stmpe->vio);
+       if (!IS_ERR(stmpe->vcc))
+               regulator_disable(stmpe->vcc);
+
        mfd_remove_devices(stmpe->dev);
 
        return 0;
index 6639f1b0fef50525a6292a9cce9984dd7c05c836..9e4d21d37a1155378352be5807d2a18a2d7a807f 100644 (file)
@@ -192,7 +192,7 @@ int stmpe_remove(struct stmpe *stmpe);
 
 #define STMPE1601_SYS_CTRL_ENABLE_GPIO         (1 << 3)
 #define STMPE1601_SYS_CTRL_ENABLE_KPC          (1 << 1)
-#define STMPE1601_SYSCON_ENABLE_SPWM           (1 << 0)
+#define STMPE1601_SYS_CTRL_ENABLE_SPWM         (1 << 0)
 
 /* The 1601/2403 share the same masks */
 #define STMPE1601_AUTOSLEEP_TIMEOUT_MASK       (0x7)
index ba1a25d758c12ed61d82aaea82501272fc34081a..1c3e6e2efe410812d7fd0a58b9b940be47a5ecbc 100644 (file)
 #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,
index bbd54414a75da5f99edbc70375c717c4ecd41267..835e5549ecdd5796284f35c87ffef0b169b40e02 100644 (file)
@@ -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;
index e87140bef667212af51bb6e02fd0ee06744190dc..db11b4f406116124ca86d82b1cec6d2e358c99c2 100644 (file)
 #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) {
index 8aaf8c1f3f63a398003237828eef9bef8b753e81..b675882307e4f85e5fb8ebcfde84bf21c0363be2 100644 (file)
@@ -694,3 +694,10 @@ config MMC_REALTEK_PCI
        help
          Say Y here to include driver code to support SD/MMC card interface
          of Realtek PCI-E card reader
+
+config MMC_REALTEK_USB
+       tristate "Realtek USB SD/MMC Card Interface Driver"
+       depends on MFD_RTSX_USB
+       help
+         Say Y here to include driver code to support SD/MMC card interface
+         of Realtek RTS5129/39 series card reader
index 0c8aa5e1e304b55270613d2cb288448502467ff2..3eb48b656f2514541f25ae5dc8aa7de759988319 100644 (file)
@@ -52,6 +52,7 @@ obj-$(CONFIG_MMC_USHC)                += ushc.o
 obj-$(CONFIG_MMC_WMT)          += wmt-sdmmc.o
 
 obj-$(CONFIG_MMC_REALTEK_PCI)  += rtsx_pci_sdmmc.o
+obj-$(CONFIG_MMC_REALTEK_USB)  += rtsx_usb_sdmmc.o
 
 obj-$(CONFIG_MMC_SDHCI_PLTFM)          += sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_CNS3XXX)                += sdhci-cns3xxx.o
diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
new file mode 100644 (file)
index 0000000..e11fafa
--- /dev/null
@@ -0,0 +1,1455 @@
+/* Realtek USB SD/MMC Card Interface driver
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Roger Tseng <rogerable@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/card.h>
+#include <linux/scatterlist.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/mfd/rtsx_usb.h>
+#include <asm/unaligned.h>
+
+#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#define RTSX_USB_USE_LEDS_CLASS
+#endif
+
+struct rtsx_usb_sdmmc {
+       struct platform_device  *pdev;
+       struct rtsx_ucr *ucr;
+       struct mmc_host         *mmc;
+       struct mmc_request      *mrq;
+
+       struct mutex            host_mutex;
+
+       u8                      ssc_depth;
+       unsigned int            clock;
+       bool                    vpclk;
+       bool                    double_clk;
+       bool                    host_removal;
+       bool                    card_exist;
+       bool                    initial_mode;
+       bool                    ddr_mode;
+
+       unsigned char           power_mode;
+
+#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+       struct led_classdev     led;
+       char                    led_name[32];
+       struct work_struct      led_work;
+#endif
+};
+
+static inline struct device *sdmmc_dev(struct rtsx_usb_sdmmc *host)
+{
+       return &(host->pdev->dev);
+}
+
+static inline void sd_clear_error(struct rtsx_usb_sdmmc *host)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       rtsx_usb_ep0_write_register(ucr, CARD_STOP,
+                                 SD_STOP | SD_CLR_ERR,
+                                 SD_STOP | SD_CLR_ERR);
+
+       rtsx_usb_clear_dma_err(ucr);
+       rtsx_usb_clear_fsm_err(ucr);
+}
+
+#ifdef DEBUG
+static void sd_print_debug_regs(struct rtsx_usb_sdmmc *host)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       u8 val = 0;
+
+       rtsx_usb_ep0_read_register(ucr, SD_STAT1, &val);
+       dev_dbg(sdmmc_dev(host), "SD_STAT1: 0x%x\n", val);
+       rtsx_usb_ep0_read_register(ucr, SD_STAT2, &val);
+       dev_dbg(sdmmc_dev(host), "SD_STAT2: 0x%x\n", val);
+       rtsx_usb_ep0_read_register(ucr, SD_BUS_STAT, &val);
+       dev_dbg(sdmmc_dev(host), "SD_BUS_STAT: 0x%x\n", val);
+}
+#else
+#define sd_print_debug_regs(host)
+#endif /* DEBUG */
+
+static int sd_read_data(struct rtsx_usb_sdmmc *host, struct mmc_command *cmd,
+              u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       int err;
+       u8 trans_mode;
+
+       if (!buf)
+               buf_len = 0;
+
+       rtsx_usb_init_cmd(ucr);
+       if (cmd != NULL) {
+               dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD%d\n", __func__
+                               , cmd->opcode);
+               if (cmd->opcode == MMC_SEND_TUNING_BLOCK)
+                       trans_mode = SD_TM_AUTO_TUNING;
+               else
+                       trans_mode = SD_TM_NORMAL_READ;
+
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               SD_CMD0, 0xFF, (u8)(cmd->opcode) | 0x40);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               SD_CMD1, 0xFF, (u8)(cmd->arg >> 24));
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               SD_CMD2, 0xFF, (u8)(cmd->arg >> 16));
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               SD_CMD3, 0xFF, (u8)(cmd->arg >> 8));
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               SD_CMD4, 0xFF, (u8)cmd->arg);
+       } else {
+               trans_mode = SD_TM_AUTO_READ_3;
+       }
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BYTE_CNT_H,
+                       0xFF, (u8)(byte_cnt >> 8));
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG2, 0xFF,
+                       SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+                       SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6);
+       if (trans_mode != SD_TM_AUTO_TUNING)
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_TRANSFER,
+                       0xFF, trans_mode | SD_TRANSFER_START);
+       rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, SD_TRANSFER,
+                       SD_TRANSFER_END, SD_TRANSFER_END);
+
+       if (cmd != NULL) {
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD1, 0, 0);
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD2, 0, 0);
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD3, 0, 0);
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD4, 0, 0);
+       }
+
+       err = rtsx_usb_send_cmd(ucr, MODE_CR, timeout);
+       if (err) {
+               dev_dbg(sdmmc_dev(host),
+                       "rtsx_usb_send_cmd failed (err = %d)\n", err);
+               return err;
+       }
+
+       err = rtsx_usb_get_rsp(ucr, !cmd ? 1 : 5, timeout);
+       if (err || (ucr->rsp_buf[0] & SD_TRANSFER_ERR)) {
+               sd_print_debug_regs(host);
+
+               if (!err) {
+                       dev_dbg(sdmmc_dev(host),
+                               "Transfer failed (SD_TRANSFER = %02x)\n",
+                               ucr->rsp_buf[0]);
+                       err = -EIO;
+               } else {
+                       dev_dbg(sdmmc_dev(host),
+                               "rtsx_usb_get_rsp failed (err = %d)\n", err);
+               }
+
+               return err;
+       }
+
+       if (cmd != NULL) {
+               cmd->resp[0] = get_unaligned_be32(ucr->rsp_buf + 1);
+               dev_dbg(sdmmc_dev(host), "cmd->resp[0] = 0x%08x\n",
+                               cmd->resp[0]);
+       }
+
+       if (buf && buf_len) {
+               /* 2-byte aligned part */
+               err = rtsx_usb_read_ppbuf(ucr, buf, byte_cnt - (byte_cnt % 2));
+               if (err) {
+                       dev_dbg(sdmmc_dev(host),
+                               "rtsx_usb_read_ppbuf failed (err = %d)\n", err);
+                       return err;
+               }
+
+               /* unaligned byte */
+               if (byte_cnt % 2)
+                       return rtsx_usb_read_register(ucr,
+                                       PPBUF_BASE2 + byte_cnt,
+                                       buf + byte_cnt - 1);
+       }
+
+       return 0;
+}
+
+static int sd_write_data(struct rtsx_usb_sdmmc *host, struct mmc_command *cmd,
+               u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       int err;
+       u8 trans_mode;
+
+       if (!buf)
+               buf_len = 0;
+
+       if (buf && buf_len) {
+               err = rtsx_usb_write_ppbuf(ucr, buf, buf_len);
+               if (err) {
+                       dev_dbg(sdmmc_dev(host),
+                               "rtsx_usb_write_ppbuf failed (err = %d)\n",
+                               err);
+                       return err;
+               }
+       }
+
+       trans_mode = (cmd != NULL) ? SD_TM_AUTO_WRITE_2 : SD_TM_AUTO_WRITE_3;
+       rtsx_usb_init_cmd(ucr);
+
+       if (cmd != NULL) {
+               dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD%d\n", __func__,
+                               cmd->opcode);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               SD_CMD0, 0xFF, (u8)(cmd->opcode) | 0x40);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               SD_CMD1, 0xFF, (u8)(cmd->arg >> 24));
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               SD_CMD2, 0xFF, (u8)(cmd->arg >> 16));
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               SD_CMD3, 0xFF, (u8)(cmd->arg >> 8));
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               SD_CMD4, 0xFF, (u8)cmd->arg);
+       }
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BYTE_CNT_H,
+                       0xFF, (u8)(byte_cnt >> 8));
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG2, 0xFF,
+               SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+               SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                       CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
+                       trans_mode | SD_TRANSFER_START);
+       rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, SD_TRANSFER,
+                       SD_TRANSFER_END, SD_TRANSFER_END);
+
+       if (cmd != NULL) {
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD1, 0, 0);
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD2, 0, 0);
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD3, 0, 0);
+               rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_CMD4, 0, 0);
+       }
+
+       err = rtsx_usb_send_cmd(ucr, MODE_CR, timeout);
+       if (err) {
+               dev_dbg(sdmmc_dev(host),
+                       "rtsx_usb_send_cmd failed (err = %d)\n", err);
+               return err;
+       }
+
+       err = rtsx_usb_get_rsp(ucr, !cmd ? 1 : 5, timeout);
+       if (err) {
+               sd_print_debug_regs(host);
+               dev_dbg(sdmmc_dev(host),
+                       "rtsx_usb_get_rsp failed (err = %d)\n", err);
+               return err;
+       }
+
+       if (cmd != NULL) {
+               cmd->resp[0] = get_unaligned_be32(ucr->rsp_buf + 1);
+               dev_dbg(sdmmc_dev(host), "cmd->resp[0] = 0x%08x\n",
+                               cmd->resp[0]);
+       }
+
+       return 0;
+}
+
+static void sd_send_cmd_get_rsp(struct rtsx_usb_sdmmc *host,
+               struct mmc_command *cmd)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       u8 cmd_idx = (u8)cmd->opcode;
+       u32 arg = cmd->arg;
+       int err = 0;
+       int timeout = 100;
+       int i;
+       u8 *ptr;
+       int stat_idx = 0;
+       int len = 2;
+       u8 rsp_type;
+
+       dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
+                       __func__, cmd_idx, arg);
+
+       /* Response type:
+        * R0
+        * R1, R5, R6, R7
+        * R1b
+        * R2
+        * R3, R4
+        */
+       switch (mmc_resp_type(cmd)) {
+       case MMC_RSP_NONE:
+               rsp_type = SD_RSP_TYPE_R0;
+               break;
+       case MMC_RSP_R1:
+               rsp_type = SD_RSP_TYPE_R1;
+               break;
+       case MMC_RSP_R1 & ~MMC_RSP_CRC:
+               rsp_type = SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
+               break;
+       case MMC_RSP_R1B:
+               rsp_type = SD_RSP_TYPE_R1b;
+               break;
+       case MMC_RSP_R2:
+               rsp_type = SD_RSP_TYPE_R2;
+               break;
+       case MMC_RSP_R3:
+               rsp_type = SD_RSP_TYPE_R3;
+               break;
+       default:
+               dev_dbg(sdmmc_dev(host), "cmd->flag is not valid\n");
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (rsp_type == SD_RSP_TYPE_R1b)
+               timeout = 3000;
+
+       if (cmd->opcode == SD_SWITCH_VOLTAGE) {
+               err = rtsx_usb_write_register(ucr, SD_BUS_STAT,
+                               SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP,
+                               SD_CLK_TOGGLE_EN);
+               if (err)
+                       goto out;
+       }
+
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CMD0, 0xFF, 0x40 | cmd_idx);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CMD1, 0xFF, (u8)(arg >> 24));
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CMD2, 0xFF, (u8)(arg >> 16));
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CMD3, 0xFF, (u8)(arg >> 8));
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CMD4, 0xFF, (u8)arg);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, PINGPONG_BUFFER);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_TRANSFER,
+                       0xFF, SD_TM_CMD_RSP | SD_TRANSFER_START);
+       rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, SD_TRANSFER,
+                    SD_TRANSFER_END | SD_STAT_IDLE,
+                    SD_TRANSFER_END | SD_STAT_IDLE);
+
+       if (rsp_type == SD_RSP_TYPE_R2) {
+               /* Read data from ping-pong buffer */
+               for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++)
+                       rtsx_usb_add_cmd(ucr, READ_REG_CMD, (u16)i, 0, 0);
+               stat_idx = 16;
+       } else if (rsp_type != SD_RSP_TYPE_R0) {
+               /* Read data from SD_CMDx registers */
+               for (i = SD_CMD0; i <= SD_CMD4; i++)
+                       rtsx_usb_add_cmd(ucr, READ_REG_CMD, (u16)i, 0, 0);
+               stat_idx = 5;
+       }
+       len += stat_idx;
+
+       rtsx_usb_add_cmd(ucr, READ_REG_CMD, SD_STAT1, 0, 0);
+
+       err = rtsx_usb_send_cmd(ucr, MODE_CR, 100);
+       if (err) {
+               dev_dbg(sdmmc_dev(host),
+                       "rtsx_usb_send_cmd error (err = %d)\n", err);
+               goto out;
+       }
+
+       err = rtsx_usb_get_rsp(ucr, len, timeout);
+       if (err || (ucr->rsp_buf[0] & SD_TRANSFER_ERR)) {
+               sd_print_debug_regs(host);
+               sd_clear_error(host);
+
+               if (!err) {
+                       dev_dbg(sdmmc_dev(host),
+                               "Transfer failed (SD_TRANSFER = %02x)\n",
+                                       ucr->rsp_buf[0]);
+                       err = -EIO;
+               } else {
+                       dev_dbg(sdmmc_dev(host),
+                               "rtsx_usb_get_rsp failed (err = %d)\n", err);
+               }
+
+               goto out;
+       }
+
+       if (rsp_type == SD_RSP_TYPE_R0) {
+               err = 0;
+               goto out;
+       }
+
+       /* Skip result of CHECK_REG_CMD */
+       ptr = ucr->rsp_buf + 1;
+
+       /* Check (Start,Transmission) bit of Response */
+       if ((ptr[0] & 0xC0) != 0) {
+               err = -EILSEQ;
+               dev_dbg(sdmmc_dev(host), "Invalid response bit\n");
+               goto out;
+       }
+
+       /* Check CRC7 */
+       if (!(rsp_type & SD_NO_CHECK_CRC7)) {
+               if (ptr[stat_idx] & SD_CRC7_ERR) {
+                       err = -EILSEQ;
+                       dev_dbg(sdmmc_dev(host), "CRC7 error\n");
+                       goto out;
+               }
+       }
+
+       if (rsp_type == SD_RSP_TYPE_R2) {
+               for (i = 0; i < 4; i++) {
+                       cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4);
+                       dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n",
+                                       i, cmd->resp[i]);
+               }
+       } else {
+               cmd->resp[0] = get_unaligned_be32(ptr + 1);
+               dev_dbg(sdmmc_dev(host), "cmd->resp[0] = 0x%08x\n",
+                               cmd->resp[0]);
+       }
+
+out:
+       cmd->error = err;
+}
+
+static int sd_rw_multi(struct rtsx_usb_sdmmc *host, struct mmc_request *mrq)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       struct mmc_data *data = mrq->data;
+       int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
+       u8 cfg2, trans_mode;
+       int err;
+       u8 flag;
+       size_t data_len = data->blksz * data->blocks;
+       unsigned int pipe;
+
+       if (read) {
+               dev_dbg(sdmmc_dev(host), "%s: read %zu bytes\n",
+                               __func__, data_len);
+               cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+                       SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0;
+               trans_mode = SD_TM_AUTO_READ_3;
+       } else {
+               dev_dbg(sdmmc_dev(host), "%s: write %zu bytes\n",
+                               __func__, data_len);
+               cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+                       SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0;
+               trans_mode = SD_TM_AUTO_WRITE_3;
+       }
+
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, 0x02);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BLOCK_CNT_L,
+                       0xFF, (u8)data->blocks);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BLOCK_CNT_H,
+                       0xFF, (u8)(data->blocks >> 8));
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, RING_BUFFER);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC3,
+                       0xFF, (u8)(data_len >> 24));
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC2,
+                       0xFF, (u8)(data_len >> 16));
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC1,
+                       0xFF, (u8)(data_len >> 8));
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC0,
+                       0xFF, (u8)data_len);
+       if (read) {
+               flag = MODE_CDIR;
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_CTL,
+                               0x03 | DMA_PACK_SIZE_MASK,
+                               DMA_DIR_FROM_CARD | DMA_EN | DMA_512);
+       } else {
+               flag = MODE_CDOR;
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_CTL,
+                               0x03 | DMA_PACK_SIZE_MASK,
+                               DMA_DIR_TO_CARD | DMA_EN | DMA_512);
+       }
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG2, 0xFF, cfg2);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
+                       trans_mode | SD_TRANSFER_START);
+       rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, SD_TRANSFER,
+                       SD_TRANSFER_END, SD_TRANSFER_END);
+
+       err = rtsx_usb_send_cmd(ucr, flag, 100);
+       if (err)
+               return err;
+
+       if (read)
+               pipe = usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN);
+       else
+               pipe = usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT);
+
+       err = rtsx_usb_transfer_data(ucr, pipe, data->sg, data_len,
+                       data->sg_len,  NULL, 10000);
+       if (err) {
+               dev_dbg(sdmmc_dev(host), "rtsx_usb_transfer_data error %d\n"
+                               , err);
+               sd_clear_error(host);
+               return err;
+       }
+
+       return rtsx_usb_get_rsp(ucr, 1, 2000);
+}
+
+static inline void sd_enable_initial_mode(struct rtsx_usb_sdmmc *host)
+{
+       rtsx_usb_write_register(host->ucr, SD_CFG1,
+                       SD_CLK_DIVIDE_MASK, SD_CLK_DIVIDE_128);
+}
+
+static inline void sd_disable_initial_mode(struct rtsx_usb_sdmmc *host)
+{
+       rtsx_usb_write_register(host->ucr, SD_CFG1,
+                       SD_CLK_DIVIDE_MASK, SD_CLK_DIVIDE_0);
+}
+
+static void sd_normal_rw(struct rtsx_usb_sdmmc *host,
+               struct mmc_request *mrq)
+{
+       struct mmc_command *cmd = mrq->cmd;
+       struct mmc_data *data = mrq->data;
+       u8 *buf;
+
+       buf = kzalloc(data->blksz, GFP_NOIO);
+       if (!buf) {
+               cmd->error = -ENOMEM;
+               return;
+       }
+
+       if (data->flags & MMC_DATA_READ) {
+               if (host->initial_mode)
+                       sd_disable_initial_mode(host);
+
+               cmd->error = sd_read_data(host, cmd, (u16)data->blksz, buf,
+                               data->blksz, 200);
+
+               if (host->initial_mode)
+                       sd_enable_initial_mode(host);
+
+               sg_copy_from_buffer(data->sg, data->sg_len, buf, data->blksz);
+       } else {
+               sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz);
+
+               cmd->error = sd_write_data(host, cmd, (u16)data->blksz, buf,
+                               data->blksz, 200);
+       }
+
+       kfree(buf);
+}
+
+static int sd_change_phase(struct rtsx_usb_sdmmc *host, u8 sample_point, int tx)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       int err;
+
+       dev_dbg(sdmmc_dev(host), "%s: %s sample_point = %d\n",
+                       __func__, tx ? "TX" : "RX", sample_point);
+
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, CLK_CHANGE);
+
+       if (tx)
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+                               0x0F, sample_point);
+       else
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK1_CTL,
+                               0x0F, sample_point);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, PHASE_NOT_RESET, 0);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+                       PHASE_NOT_RESET, PHASE_NOT_RESET);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, 0);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG1, SD_ASYNC_FIFO_RST, 0);
+
+       err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static inline u32 get_phase_point(u32 phase_map, unsigned int idx)
+{
+       idx &= MAX_PHASE;
+       return phase_map & (1 << idx);
+}
+
+static int get_phase_len(u32 phase_map, unsigned int idx)
+{
+       int i;
+
+       for (i = 0; i < MAX_PHASE + 1; i++) {
+               if (get_phase_point(phase_map, idx + i) == 0)
+                       return i;
+       }
+       return MAX_PHASE + 1;
+}
+
+static u8 sd_search_final_phase(struct rtsx_usb_sdmmc *host, u32 phase_map)
+{
+       int start = 0, len = 0;
+       int start_final = 0, len_final = 0;
+       u8 final_phase = 0xFF;
+
+       if (phase_map == 0) {
+               dev_dbg(sdmmc_dev(host), "Phase: [map:%x]\n", phase_map);
+               return final_phase;
+       }
+
+       while (start < MAX_PHASE + 1) {
+               len = get_phase_len(phase_map, start);
+               if (len_final < len) {
+                       start_final = start;
+                       len_final = len;
+               }
+               start += len ? len : 1;
+       }
+
+       final_phase = (start_final + len_final / 2) & MAX_PHASE;
+       dev_dbg(sdmmc_dev(host), "Phase: [map:%x] [maxlen:%d] [final:%d]\n",
+               phase_map, len_final, final_phase);
+
+       return final_phase;
+}
+
+static void sd_wait_data_idle(struct rtsx_usb_sdmmc *host)
+{
+       int err, i;
+       u8 val = 0;
+
+       for (i = 0; i < 100; i++) {
+               err = rtsx_usb_ep0_read_register(host->ucr,
+                               SD_DATA_STATE, &val);
+               if (val & SD_DATA_IDLE)
+                       return;
+
+               usleep_range(100, 1000);
+       }
+}
+
+static int sd_tuning_rx_cmd(struct rtsx_usb_sdmmc *host,
+               u8 opcode, u8 sample_point)
+{
+       int err;
+       struct mmc_command cmd = {0};
+
+       err = sd_change_phase(host, sample_point, 0);
+       if (err)
+               return err;
+
+       cmd.opcode = MMC_SEND_TUNING_BLOCK;
+       err = sd_read_data(host, &cmd, 0x40, NULL, 0, 100);
+       if (err) {
+               /* Wait till SD DATA IDLE */
+               sd_wait_data_idle(host);
+               sd_clear_error(host);
+               return err;
+       }
+
+       return 0;
+}
+
+static void sd_tuning_phase(struct rtsx_usb_sdmmc *host,
+               u8 opcode, u16 *phase_map)
+{
+       int err, i;
+       u16 raw_phase_map = 0;
+
+       for (i = MAX_PHASE; i >= 0; i--) {
+               err = sd_tuning_rx_cmd(host, opcode, (u8)i);
+               if (!err)
+                       raw_phase_map |= 1 << i;
+       }
+
+       if (phase_map)
+               *phase_map = raw_phase_map;
+}
+
+static int sd_tuning_rx(struct rtsx_usb_sdmmc *host, u8 opcode)
+{
+       int err, i;
+       u16 raw_phase_map[RX_TUNING_CNT] = {0}, phase_map;
+       u8 final_phase;
+
+       /* setting fixed default TX phase */
+       err = sd_change_phase(host, 0x01, 1);
+       if (err) {
+               dev_dbg(sdmmc_dev(host), "TX phase setting failed\n");
+               return err;
+       }
+
+       /* tuning RX phase */
+       for (i = 0; i < RX_TUNING_CNT; i++) {
+               sd_tuning_phase(host, opcode, &(raw_phase_map[i]));
+
+               if (raw_phase_map[i] == 0)
+                       break;
+       }
+
+       phase_map = 0xFFFF;
+       for (i = 0; i < RX_TUNING_CNT; i++) {
+               dev_dbg(sdmmc_dev(host), "RX raw_phase_map[%d] = 0x%04x\n",
+                               i, raw_phase_map[i]);
+               phase_map &= raw_phase_map[i];
+       }
+       dev_dbg(sdmmc_dev(host), "RX phase_map = 0x%04x\n", phase_map);
+
+       if (phase_map) {
+               final_phase = sd_search_final_phase(host, phase_map);
+               if (final_phase == 0xFF)
+                       return -EINVAL;
+
+               err = sd_change_phase(host, final_phase, 0);
+               if (err)
+                       return err;
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int sdmmc_get_ro(struct mmc_host *mmc)
+{
+       struct rtsx_usb_sdmmc *host = mmc_priv(mmc);
+       struct rtsx_ucr *ucr = host->ucr;
+       int err;
+       u16 val;
+
+       if (host->host_removal)
+               return -ENOMEDIUM;
+
+       mutex_lock(&ucr->dev_mutex);
+
+       /* Check SD card detect */
+       err = rtsx_usb_get_card_status(ucr, &val);
+
+       mutex_unlock(&ucr->dev_mutex);
+
+
+       /* Treat failed detection as non-ro */
+       if (err)
+               return 0;
+
+       if (val & SD_WP)
+               return 1;
+
+       return 0;
+}
+
+static int sdmmc_get_cd(struct mmc_host *mmc)
+{
+       struct rtsx_usb_sdmmc *host = mmc_priv(mmc);
+       struct rtsx_ucr *ucr = host->ucr;
+       int err;
+       u16 val;
+
+       if (host->host_removal)
+               return -ENOMEDIUM;
+
+       mutex_lock(&ucr->dev_mutex);
+
+       /* Check SD card detect */
+       err = rtsx_usb_get_card_status(ucr, &val);
+
+       mutex_unlock(&ucr->dev_mutex);
+
+       /* Treat failed detection as non-exist */
+       if (err)
+               goto no_card;
+
+       if (val & SD_CD) {
+               host->card_exist = true;
+               return 1;
+       }
+
+no_card:
+       host->card_exist = false;
+       return 0;
+}
+
+static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+       struct rtsx_usb_sdmmc *host = mmc_priv(mmc);
+       struct rtsx_ucr *ucr = host->ucr;
+       struct mmc_command *cmd = mrq->cmd;
+       struct mmc_data *data = mrq->data;
+       unsigned int data_size = 0;
+
+       dev_dbg(sdmmc_dev(host), "%s\n", __func__);
+
+       if (host->host_removal) {
+               cmd->error = -ENOMEDIUM;
+               goto finish;
+       }
+
+       if ((!host->card_exist)) {
+               cmd->error = -ENOMEDIUM;
+               goto finish_detect_card;
+       }
+
+       /*
+        * Reject SDIO CMDs to speed up card identification
+        * since unsupported
+        */
+       if (cmd->opcode == SD_IO_SEND_OP_COND ||
+           cmd->opcode == SD_IO_RW_DIRECT ||
+           cmd->opcode == SD_IO_RW_EXTENDED) {
+               cmd->error = -EINVAL;
+               goto finish;
+       }
+
+       mutex_lock(&ucr->dev_mutex);
+
+       mutex_lock(&host->host_mutex);
+       host->mrq = mrq;
+       mutex_unlock(&host->host_mutex);
+
+       if (mrq->data)
+               data_size = data->blocks * data->blksz;
+
+       if (!data_size) {
+               sd_send_cmd_get_rsp(host, cmd);
+       } else if ((!(data_size % 512) && cmd->opcode != MMC_SEND_EXT_CSD) ||
+                  mmc_op_multi(cmd->opcode)) {
+               sd_send_cmd_get_rsp(host, cmd);
+
+               if (!cmd->error) {
+                       sd_rw_multi(host, mrq);
+
+                       if (mmc_op_multi(cmd->opcode) && mrq->stop) {
+                               sd_send_cmd_get_rsp(host, mrq->stop);
+                               rtsx_usb_write_register(ucr, MC_FIFO_CTL,
+                                               FIFO_FLUSH, FIFO_FLUSH);
+                       }
+               }
+       } else {
+               sd_normal_rw(host, mrq);
+       }
+
+       if (mrq->data) {
+               if (cmd->error || data->error)
+                       data->bytes_xfered = 0;
+               else
+                       data->bytes_xfered = data->blocks * data->blksz;
+       }
+
+       mutex_unlock(&ucr->dev_mutex);
+
+finish_detect_card:
+       if (cmd->error) {
+               /*
+                * detect card when fail to update card existence state and
+                * speed up card removal when retry
+                */
+               sdmmc_get_cd(mmc);
+               dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error);
+       }
+
+finish:
+       mutex_lock(&host->host_mutex);
+       host->mrq = NULL;
+       mutex_unlock(&host->host_mutex);
+
+       mmc_request_done(mmc, mrq);
+}
+
+static int sd_set_bus_width(struct rtsx_usb_sdmmc *host,
+               unsigned char bus_width)
+{
+       int err = 0;
+       u8 width[] = {
+               [MMC_BUS_WIDTH_1] = SD_BUS_WIDTH_1BIT,
+               [MMC_BUS_WIDTH_4] = SD_BUS_WIDTH_4BIT,
+               [MMC_BUS_WIDTH_8] = SD_BUS_WIDTH_8BIT,
+       };
+
+       if (bus_width <= MMC_BUS_WIDTH_8)
+               err = rtsx_usb_write_register(host->ucr, SD_CFG1,
+                               0x03, width[bus_width]);
+
+       return err;
+}
+
+static int sd_pull_ctl_disable_lqfp48(struct rtsx_ucr *ucr)
+{
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5);
+
+       return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+
+static int sd_pull_ctl_disable_qfn24(struct rtsx_ucr *ucr)
+{
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x65);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x56);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x59);
+
+       return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+
+static int sd_pull_ctl_enable_lqfp48(struct rtsx_ucr *ucr)
+{
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0xAA);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0xAA);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0xA9);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5);
+
+       return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+
+static int sd_pull_ctl_enable_qfn24(struct rtsx_ucr *ucr)
+{
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0xA5);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x9A);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0xA5);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x9A);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x65);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x5A);
+
+       return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+
+static int sd_power_on(struct rtsx_usb_sdmmc *host)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       int err;
+
+       dev_dbg(sdmmc_dev(host), "%s\n", __func__);
+       rtsx_usb_init_cmd(ucr);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SHARE_MODE,
+                       CARD_SHARE_MASK, CARD_SHARE_SD);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN,
+                       SD_CLK_EN, SD_CLK_EN);
+       err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
+       if (err)
+               return err;
+
+       if (CHECK_PKG(ucr, LQFP48))
+               err = sd_pull_ctl_enable_lqfp48(ucr);
+       else
+               err = sd_pull_ctl_enable_qfn24(ucr);
+       if (err)
+               return err;
+
+       err = rtsx_usb_write_register(ucr, CARD_PWR_CTL,
+                       POWER_MASK, PARTIAL_POWER_ON);
+       if (err)
+               return err;
+
+       usleep_range(800, 1000);
+
+       rtsx_usb_init_cmd(ucr);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
+                       POWER_MASK|LDO3318_PWR_MASK, POWER_ON|LDO_ON);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE,
+                       SD_OUTPUT_EN, SD_OUTPUT_EN);
+
+       return rtsx_usb_send_cmd(ucr, MODE_C, 100);
+}
+
+static int sd_power_off(struct rtsx_usb_sdmmc *host)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       int err;
+
+       dev_dbg(sdmmc_dev(host), "%s\n", __func__);
+       rtsx_usb_init_cmd(ucr);
+
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN, SD_CLK_EN, 0);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, SD_OUTPUT_EN, 0);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
+                       POWER_MASK, POWER_OFF);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
+                       POWER_MASK|LDO3318_PWR_MASK, POWER_OFF|LDO_SUSPEND);
+
+       err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
+       if (err)
+               return err;
+
+       if (CHECK_PKG(ucr, LQFP48))
+                       return sd_pull_ctl_disable_lqfp48(ucr);
+       return sd_pull_ctl_disable_qfn24(ucr);
+}
+
+static int sd_set_power_mode(struct rtsx_usb_sdmmc *host,
+               unsigned char power_mode)
+{
+       int err;
+
+       if (power_mode != MMC_POWER_OFF)
+               power_mode = MMC_POWER_ON;
+
+       if (power_mode == host->power_mode)
+               return 0;
+
+       if (power_mode == MMC_POWER_OFF) {
+               err = sd_power_off(host);
+               pm_runtime_put(sdmmc_dev(host));
+       } else {
+               pm_runtime_get_sync(sdmmc_dev(host));
+               err = sd_power_on(host);
+       }
+
+       if (!err)
+               host->power_mode = power_mode;
+
+       return err;
+}
+
+static int sd_set_timing(struct rtsx_usb_sdmmc *host,
+               unsigned char timing, bool *ddr_mode)
+{
+       struct rtsx_ucr *ucr = host->ucr;
+       int err;
+
+       *ddr_mode = false;
+
+       rtsx_usb_init_cmd(ucr);
+
+       switch (timing) {
+       case MMC_TIMING_UHS_SDR104:
+       case MMC_TIMING_UHS_SDR50:
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG1,
+                               0x0C | SD_ASYNC_FIFO_RST,
+                               SD_30_MODE | SD_ASYNC_FIFO_RST);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF,
+                               CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1);
+               break;
+
+       case MMC_TIMING_UHS_DDR50:
+               *ddr_mode = true;
+
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG1,
+                               0x0C | SD_ASYNC_FIFO_RST,
+                               SD_DDR_MODE | SD_ASYNC_FIFO_RST);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF,
+                               CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_PUSH_POINT_CTL,
+                               DDR_VAR_TX_CMD_DAT, DDR_VAR_TX_CMD_DAT);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL,
+                               DDR_VAR_RX_DAT | DDR_VAR_RX_CMD,
+                               DDR_VAR_RX_DAT | DDR_VAR_RX_CMD);
+               break;
+
+       case MMC_TIMING_MMC_HS:
+       case MMC_TIMING_SD_HS:
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG1,
+                               0x0C, SD_20_MODE);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF,
+                               CRC_FIX_CLK | SD30_VAR_CLK0 | SAMPLE_VAR_CLK1);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_PUSH_POINT_CTL,
+                               SD20_TX_SEL_MASK, SD20_TX_14_AHEAD);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL,
+                               SD20_RX_SEL_MASK, SD20_RX_14_DELAY);
+               break;
+
+       default:
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               SD_CFG1, 0x0C, SD_20_MODE);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF,
+                               CRC_FIX_CLK | SD30_VAR_CLK0 | SAMPLE_VAR_CLK1);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
+                               SD_PUSH_POINT_CTL, 0xFF, 0);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL,
+                               SD20_RX_SEL_MASK, SD20_RX_POS_EDGE);
+               break;
+       }
+
+       err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
+
+       return err;
+}
+
+static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct rtsx_usb_sdmmc *host = mmc_priv(mmc);
+       struct rtsx_ucr *ucr = host->ucr;
+
+       dev_dbg(sdmmc_dev(host), "%s\n", __func__);
+       mutex_lock(&ucr->dev_mutex);
+
+       if (rtsx_usb_card_exclusive_check(ucr, RTSX_USB_SD_CARD)) {
+               mutex_unlock(&ucr->dev_mutex);
+               return;
+       }
+
+       sd_set_power_mode(host, ios->power_mode);
+       sd_set_bus_width(host, ios->bus_width);
+       sd_set_timing(host, ios->timing, &host->ddr_mode);
+
+       host->vpclk = false;
+       host->double_clk = true;
+
+       switch (ios->timing) {
+       case MMC_TIMING_UHS_SDR104:
+       case MMC_TIMING_UHS_SDR50:
+               host->ssc_depth = SSC_DEPTH_2M;
+               host->vpclk = true;
+               host->double_clk = false;
+               break;
+       case MMC_TIMING_UHS_DDR50:
+       case MMC_TIMING_UHS_SDR25:
+               host->ssc_depth = SSC_DEPTH_1M;
+               break;
+       default:
+               host->ssc_depth = SSC_DEPTH_512K;
+               break;
+       }
+
+       host->initial_mode = (ios->clock <= 1000000) ? true : false;
+       host->clock = ios->clock;
+
+       rtsx_usb_switch_clock(host->ucr, host->clock, host->ssc_depth,
+                       host->initial_mode, host->double_clk, host->vpclk);
+
+       mutex_unlock(&ucr->dev_mutex);
+       dev_dbg(sdmmc_dev(host), "%s end\n", __func__);
+}
+
+static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct rtsx_usb_sdmmc *host = mmc_priv(mmc);
+       struct rtsx_ucr *ucr = host->ucr;
+       int err = 0;
+
+       dev_dbg(sdmmc_dev(host), "%s: signal_voltage = %d\n",
+                       __func__, ios->signal_voltage);
+
+       if (host->host_removal)
+               return -ENOMEDIUM;
+
+       if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_120)
+               return -EPERM;
+
+       mutex_lock(&ucr->dev_mutex);
+
+       err = rtsx_usb_card_exclusive_check(ucr, RTSX_USB_SD_CARD);
+       if (err) {
+               mutex_unlock(&ucr->dev_mutex);
+               return err;
+       }
+
+       /* Let mmc core do the busy checking, simply stop the forced-toggle
+        * clock(while issuing CMD11) and switch voltage.
+        */
+       rtsx_usb_init_cmd(ucr);
+
+       if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_PAD_CTL,
+                               SD_IO_USING_1V8, SD_IO_USING_3V3);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG,
+                               TUNE_SD18_MASK, TUNE_SD18_3V3);
+       } else {
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_BUS_STAT,
+                               SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP,
+                               SD_CLK_FORCE_STOP);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_PAD_CTL,
+                               SD_IO_USING_1V8, SD_IO_USING_1V8);
+               rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG,
+                               TUNE_SD18_MASK, TUNE_SD18_1V8);
+       }
+
+       err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
+       mutex_unlock(&ucr->dev_mutex);
+
+       return err;
+}
+
+static int sdmmc_card_busy(struct mmc_host *mmc)
+{
+       struct rtsx_usb_sdmmc *host = mmc_priv(mmc);
+       struct rtsx_ucr *ucr = host->ucr;
+       int err;
+       u8 stat;
+       u8 mask = SD_DAT3_STATUS | SD_DAT2_STATUS | SD_DAT1_STATUS
+               | SD_DAT0_STATUS;
+
+       dev_dbg(sdmmc_dev(host), "%s\n", __func__);
+
+       mutex_lock(&ucr->dev_mutex);
+
+       err = rtsx_usb_write_register(ucr, SD_BUS_STAT,
+                       SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP,
+                       SD_CLK_TOGGLE_EN);
+       if (err)
+               goto out;
+
+       mdelay(1);
+
+       err = rtsx_usb_read_register(ucr, SD_BUS_STAT, &stat);
+       if (err)
+               goto out;
+
+       err = rtsx_usb_write_register(ucr, SD_BUS_STAT,
+                       SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
+out:
+       mutex_unlock(&ucr->dev_mutex);
+
+       if (err)
+               return err;
+
+       /* check if any pin between dat[0:3] is low */
+       if ((stat & mask) != mask)
+               return 1;
+       else
+               return 0;
+}
+
+static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+       struct rtsx_usb_sdmmc *host = mmc_priv(mmc);
+       struct rtsx_ucr *ucr = host->ucr;
+       int err = 0;
+
+       if (host->host_removal)
+               return -ENOMEDIUM;
+
+       mutex_lock(&ucr->dev_mutex);
+
+       if (!host->ddr_mode)
+               err = sd_tuning_rx(host, MMC_SEND_TUNING_BLOCK);
+
+       mutex_unlock(&ucr->dev_mutex);
+
+       return err;
+}
+
+static const struct mmc_host_ops rtsx_usb_sdmmc_ops = {
+       .request = sdmmc_request,
+       .set_ios = sdmmc_set_ios,
+       .get_ro = sdmmc_get_ro,
+       .get_cd = sdmmc_get_cd,
+       .start_signal_voltage_switch = sdmmc_switch_voltage,
+       .card_busy = sdmmc_card_busy,
+       .execute_tuning = sdmmc_execute_tuning,
+};
+
+#ifdef RTSX_USB_USE_LEDS_CLASS
+static void rtsx_usb_led_control(struct led_classdev *led,
+       enum led_brightness brightness)
+{
+       struct rtsx_usb_sdmmc *host = container_of(led,
+                       struct rtsx_usb_sdmmc, led);
+
+       if (host->host_removal)
+               return;
+
+       host->led.brightness = brightness;
+       schedule_work(&host->led_work);
+}
+
+static void rtsx_usb_update_led(struct work_struct *work)
+{
+       struct rtsx_usb_sdmmc *host =
+               container_of(work, struct rtsx_usb_sdmmc, led_work);
+       struct rtsx_ucr *ucr = host->ucr;
+
+       mutex_lock(&ucr->dev_mutex);
+
+       if (host->led.brightness == LED_OFF)
+               rtsx_usb_turn_off_led(ucr);
+       else
+               rtsx_usb_turn_on_led(ucr);
+
+       mutex_unlock(&ucr->dev_mutex);
+}
+#endif
+
+static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host)
+{
+       struct mmc_host *mmc = host->mmc;
+
+       mmc->f_min = 250000;
+       mmc->f_max = 208000000;
+       mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+       mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
+               MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
+               MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
+               MMC_CAP_NEEDS_POLL;
+
+       mmc->max_current_330 = 400;
+       mmc->max_current_180 = 800;
+       mmc->ops = &rtsx_usb_sdmmc_ops;
+       mmc->max_segs = 256;
+       mmc->max_seg_size = 65536;
+       mmc->max_blk_size = 512;
+       mmc->max_blk_count = 65535;
+       mmc->max_req_size = 524288;
+
+       host->power_mode = MMC_POWER_OFF;
+}
+
+static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev)
+{
+       struct mmc_host *mmc;
+       struct rtsx_usb_sdmmc *host;
+       struct rtsx_ucr *ucr;
+#ifdef RTSX_USB_USE_LEDS_CLASS
+       int err;
+#endif
+
+       ucr = usb_get_intfdata(to_usb_interface(pdev->dev.parent));
+       if (!ucr)
+               return -ENXIO;
+
+       dev_dbg(&(pdev->dev), ": Realtek USB SD/MMC controller found\n");
+
+       mmc = mmc_alloc_host(sizeof(*host), &pdev->dev);
+       if (!mmc)
+               return -ENOMEM;
+
+       host = mmc_priv(mmc);
+       host->ucr = ucr;
+       host->mmc = mmc;
+       host->pdev = pdev;
+       platform_set_drvdata(pdev, host);
+
+       mutex_init(&host->host_mutex);
+       rtsx_usb_init_host(host);
+       pm_runtime_enable(&pdev->dev);
+
+#ifdef RTSX_USB_USE_LEDS_CLASS
+       snprintf(host->led_name, sizeof(host->led_name),
+               "%s::", mmc_hostname(mmc));
+       host->led.name = host->led_name;
+       host->led.brightness = LED_OFF;
+       host->led.default_trigger = mmc_hostname(mmc);
+       host->led.brightness_set = rtsx_usb_led_control;
+
+       err = led_classdev_register(mmc_dev(mmc), &host->led);
+       if (err)
+               dev_err(&(pdev->dev),
+                               "Failed to register LED device: %d\n", err);
+       INIT_WORK(&host->led_work, rtsx_usb_update_led);
+
+#endif
+       mmc_add_host(mmc);
+
+       return 0;
+}
+
+static int rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev)
+{
+       struct rtsx_usb_sdmmc *host = platform_get_drvdata(pdev);
+       struct mmc_host *mmc;
+
+       if (!host)
+               return 0;
+
+       mmc = host->mmc;
+       host->host_removal = true;
+
+       mutex_lock(&host->host_mutex);
+       if (host->mrq) {
+               dev_dbg(&(pdev->dev),
+                       "%s: Controller removed during transfer\n",
+                       mmc_hostname(mmc));
+               host->mrq->cmd->error = -ENOMEDIUM;
+               if (host->mrq->stop)
+                       host->mrq->stop->error = -ENOMEDIUM;
+               mmc_request_done(mmc, host->mrq);
+       }
+       mutex_unlock(&host->host_mutex);
+
+       mmc_remove_host(mmc);
+
+#ifdef RTSX_USB_USE_LEDS_CLASS
+       cancel_work_sync(&host->led_work);
+       led_classdev_unregister(&host->led);
+#endif
+
+       mmc_free_host(mmc);
+       pm_runtime_disable(&pdev->dev);
+       platform_set_drvdata(pdev, NULL);
+
+       dev_dbg(&(pdev->dev),
+               ": Realtek USB SD/MMC module has been removed\n");
+
+       return 0;
+}
+
+static struct platform_device_id rtsx_usb_sdmmc_ids[] = {
+       {
+               .name = "rtsx_usb_sdmmc",
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(platform, rtsx_usb_sdmmc_ids);
+
+static struct platform_driver rtsx_usb_sdmmc_driver = {
+       .probe          = rtsx_usb_sdmmc_drv_probe,
+       .remove         = rtsx_usb_sdmmc_drv_remove,
+       .id_table       = rtsx_usb_sdmmc_ids,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "rtsx_usb_sdmmc",
+       },
+};
+module_platform_driver(rtsx_usb_sdmmc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>");
+MODULE_DESCRIPTION("Realtek USB SD/MMC Card Host Driver");
index 8fc9d6df87f682610187a7ca665c393ef30c6e1d..1685f63b9e5d03c1084bc1436fdf6cce740013ea 100644 (file)
 
 #include <linux/mfd/tps65090.h>
 
-#define TPS65090_REG_INTR_STS  0x00
-#define TPS65090_REG_INTR_MASK 0x02
-#define TPS65090_REG_CG_CTRL0  0x04
-#define TPS65090_REG_CG_CTRL1  0x05
-#define TPS65090_REG_CG_CTRL2  0x06
-#define TPS65090_REG_CG_CTRL3  0x07
-#define TPS65090_REG_CG_CTRL4  0x08
-#define TPS65090_REG_CG_CTRL5  0x09
-#define TPS65090_REG_CG_STATUS1        0x0a
-#define TPS65090_REG_CG_STATUS2        0x0b
-
 #define TPS65090_CHARGER_ENABLE        BIT(0)
 #define TPS65090_VACG          BIT(1)
 #define TPS65090_NOITERM       BIT(5)
index 903eb37f047a916d510685b7e203a4ecdb680cbd..f0cc9e6dac3ac5a3ecd7cd78ea4dd38d5f72d41c 100644 (file)
@@ -266,11 +266,12 @@ config REGULATOR_LP8788
          This driver supports LP8788 voltage regulator chip.
 
 config REGULATOR_MAX14577
-       tristate "Maxim 14577 regulator"
+       tristate "Maxim 14577/77836 regulator"
        depends on MFD_MAX14577
        help
-         This driver controls a Maxim 14577 regulator via I2C bus.
-         The regulators include safeout LDO and current regulator 'CHARGER'.
+         This driver controls a Maxim MAX14577/77836 regulator via I2C bus.
+         The MAX14577 regulators include safeout LDO and charger current
+         regulator. The MAX77836 has two additional LDOs.
 
 config REGULATOR_MAX1586
        tristate "Maxim 1586/1587 voltage regulator"
index b1033d30b504c70cd50364bd5dcbdbca198474f6..d3787e11f535155e6479bc52c54053bffded71aa 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
 #include <linux/gpio.h>
 #include <linux/slab.h>
 
@@ -178,6 +179,42 @@ static const struct regulator_init_data arizona_ldo1_default = {
        .num_consumer_supplies = 1,
 };
 
+static int arizona_ldo1_of_get_pdata(struct arizona *arizona,
+                                    struct regulator_config *config)
+{
+       struct arizona_pdata *pdata = &arizona->pdata;
+       struct arizona_ldo1 *ldo1 = config->driver_data;
+       struct device_node *init_node, *dcvdd_node;
+       struct regulator_init_data *init_data;
+
+       pdata->ldoena = arizona_of_get_named_gpio(arizona, "wlf,ldoena", true);
+
+       init_node = of_get_child_by_name(arizona->dev->of_node, "ldo1");
+       dcvdd_node = of_parse_phandle(arizona->dev->of_node, "DCVDD-supply", 0);
+
+       if (init_node) {
+               config->of_node = init_node;
+
+               init_data = of_get_regulator_init_data(arizona->dev, init_node);
+
+               if (init_data) {
+                       init_data->consumer_supplies = &ldo1->supply;
+                       init_data->num_consumer_supplies = 1;
+
+                       if (dcvdd_node && dcvdd_node != init_node)
+                               arizona->external_dcvdd = true;
+
+                       pdata->ldo1 = init_data;
+               }
+       } else if (dcvdd_node) {
+               arizona->external_dcvdd = true;
+       }
+
+       of_node_put(dcvdd_node);
+
+       return 0;
+}
+
 static int arizona_ldo1_probe(struct platform_device *pdev)
 {
        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
@@ -186,6 +223,8 @@ static int arizona_ldo1_probe(struct platform_device *pdev)
        struct arizona_ldo1 *ldo1;
        int ret;
 
+       arizona->external_dcvdd = false;
+
        ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);
        if (!ldo1)
                return -ENOMEM;
@@ -216,6 +255,15 @@ static int arizona_ldo1_probe(struct platform_device *pdev)
        config.dev = arizona->dev;
        config.driver_data = ldo1;
        config.regmap = arizona->regmap;
+
+       if (IS_ENABLED(CONFIG_OF)) {
+               if (!dev_get_platdata(arizona->dev)) {
+                       ret = arizona_ldo1_of_get_pdata(arizona, &config);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
        config.ena_gpio = arizona->pdata.ldoena;
 
        if (arizona->pdata.ldo1)
@@ -223,6 +271,13 @@ static int arizona_ldo1_probe(struct platform_device *pdev)
        else
                config.init_data = &ldo1->init_data;
 
+       /*
+        * LDO1 can only be used to supply DCVDD so if it has no
+        * consumers then DCVDD is supplied externally.
+        */
+       if (config.init_data->num_consumer_supplies == 0)
+               arizona->external_dcvdd = true;
+
        ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config);
        if (IS_ERR(ldo1->regulator)) {
                ret = PTR_ERR(ldo1->regulator);
@@ -231,6 +286,8 @@ static int arizona_ldo1_probe(struct platform_device *pdev)
                return ret;
        }
 
+       of_node_put(config.of_node);
+
        platform_set_drvdata(pdev, ldo1);
 
        return 0;
index 6fdd9bf6927fcb39763156d34d2747c34ff2ebee..b80ebbe88bac558edcdebf94fc976fbbea192a60 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
 #include <linux/gpio.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
@@ -195,6 +196,32 @@ static const struct regulator_init_data arizona_micsupp_ext_default = {
        .num_consumer_supplies = 1,
 };
 
+static int arizona_micsupp_of_get_pdata(struct arizona *arizona,
+                                       struct regulator_config *config)
+{
+       struct arizona_pdata *pdata = &arizona->pdata;
+       struct arizona_micsupp *micsupp = config->driver_data;
+       struct device_node *np;
+       struct regulator_init_data *init_data;
+
+       np = of_get_child_by_name(arizona->dev->of_node, "micvdd");
+
+       if (np) {
+               config->of_node = np;
+
+               init_data = of_get_regulator_init_data(arizona->dev, np);
+
+               if (init_data) {
+                       init_data->consumer_supplies = &micsupp->supply;
+                       init_data->num_consumer_supplies = 1;
+
+                       pdata->micvdd = init_data;
+               }
+       }
+
+       return 0;
+}
+
 static int arizona_micsupp_probe(struct platform_device *pdev)
 {
        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
@@ -234,6 +261,14 @@ static int arizona_micsupp_probe(struct platform_device *pdev)
        config.driver_data = micsupp;
        config.regmap = arizona->regmap;
 
+       if (IS_ENABLED(CONFIG_OF)) {
+               if (!dev_get_platdata(arizona->dev)) {
+                       ret = arizona_micsupp_of_get_pdata(arizona, &config);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
        if (arizona->pdata.micvdd)
                config.init_data = arizona->pdata.micvdd;
        else
@@ -253,6 +288,8 @@ static int arizona_micsupp_probe(struct platform_device *pdev)
                return ret;
        }
 
+       of_node_put(config.of_node);
+
        platform_set_drvdata(pdev, micsupp);
 
        return 0;
index ed60baaeceeca79cc9ba2b9c770973d5039da3fa..5d9c605cf534d9dea23b99885b1a5babffc65869 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * max14577.c - Regulator driver for the Maxim 14577
+ * max14577.c - Regulator driver for the Maxim 14577/77836
  *
  * Copyright (C) 2013,2014 Samsung Electronics
  * Krzysztof Kozlowski <k.kozlowski@samsung.com>
 #include <linux/mfd/max14577-private.h>
 #include <linux/regulator/of_regulator.h>
 
+/*
+ * Valid limits of current for max14577 and max77836 chargers.
+ * They must correspond to MBCICHWRCL and MBCICHWRCH fields in CHGCTRL4
+ * register for given chipset.
+ */
+struct maxim_charger_current {
+       /* Minimal current, set in CHGCTRL4/MBCICHWRCL, uA */
+       unsigned int min;
+       /*
+        * Minimal current when high setting is active,
+        * set in CHGCTRL4/MBCICHWRCH, uA
+        */
+       unsigned int high_start;
+       /* Value of one step in high setting, uA */
+       unsigned int high_step;
+       /* Maximum current of high setting, uA */
+       unsigned int max;
+};
+
+/* Table of valid charger currents for different Maxim chipsets */
+static const struct maxim_charger_current maxim_charger_currents[] = {
+       [MAXIM_DEVICE_TYPE_UNKNOWN] = { 0, 0, 0, 0 },
+       [MAXIM_DEVICE_TYPE_MAX14577] = {
+               .min            = MAX14577_REGULATOR_CURRENT_LIMIT_MIN,
+               .high_start     = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START,
+               .high_step      = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
+               .max            = MAX14577_REGULATOR_CURRENT_LIMIT_MAX,
+       },
+       [MAXIM_DEVICE_TYPE_MAX77836] = {
+               .min            = MAX77836_REGULATOR_CURRENT_LIMIT_MIN,
+               .high_start     = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START,
+               .high_step      = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
+               .max            = MAX77836_REGULATOR_CURRENT_LIMIT_MAX,
+       },
+};
+
 static int max14577_reg_is_enabled(struct regulator_dev *rdev)
 {
        int rid = rdev_get_id(rdev);
@@ -47,6 +83,9 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev)
 {
        u8 reg_data;
        struct regmap *rmap = rdev->regmap;
+       struct max14577 *max14577 = rdev_get_drvdata(rdev);
+       const struct maxim_charger_current *limits =
+               &maxim_charger_currents[max14577->dev_type];
 
        if (rdev_get_id(rdev) != MAX14577_CHARGER)
                return -EINVAL;
@@ -54,12 +93,11 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev)
        max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL4, &reg_data);
 
        if ((reg_data & CHGCTRL4_MBCICHWRCL_MASK) == 0)
-               return MAX14577_REGULATOR_CURRENT_LIMIT_MIN;
+               return limits->min;
 
        reg_data = ((reg_data & CHGCTRL4_MBCICHWRCH_MASK) >>
                        CHGCTRL4_MBCICHWRCH_SHIFT);
-       return MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
-               reg_data * MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP;
+       return limits->high_start + reg_data * limits->high_step;
 }
 
 static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
@@ -67,33 +105,39 @@ static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
 {
        int i, current_bits = 0xf;
        u8 reg_data;
+       struct max14577 *max14577 = rdev_get_drvdata(rdev);
+       const struct maxim_charger_current *limits =
+               &maxim_charger_currents[max14577->dev_type];
 
        if (rdev_get_id(rdev) != MAX14577_CHARGER)
                return -EINVAL;
 
-       if (min_uA > MAX14577_REGULATOR_CURRENT_LIMIT_MAX ||
-                       max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_MIN)
+       if (min_uA > limits->max || max_uA < limits->min)
                return -EINVAL;
 
-       if (max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START) {
-               /* Less than 200 mA, so set 90mA (turn only Low Bit off) */
+       if (max_uA < limits->high_start) {
+               /*
+                * Less than high_start,
+                * so set the minimal current (turn only Low Bit off)
+                */
                u8 reg_data = 0x0 << CHGCTRL4_MBCICHWRCL_SHIFT;
                return max14577_update_reg(rdev->regmap,
                                MAX14577_CHG_REG_CHG_CTRL4,
                                CHGCTRL4_MBCICHWRCL_MASK, reg_data);
        }
 
-       /* max_uA is in range: <LIMIT_HIGH_START, inifinite>, so search for
-        * valid current starting from LIMIT_MAX. */
-       for (i = MAX14577_REGULATOR_CURRENT_LIMIT_MAX;
-                       i >= MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START;
-                       i -= MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP) {
+       /*
+        * max_uA is in range: <high_start, inifinite>, so search for
+        * valid current starting from maximum current.
+        */
+       for (i = limits->max; i >= limits->high_start; i -= limits->high_step) {
                if (i <= max_uA)
                        break;
                current_bits--;
        }
        BUG_ON(current_bits < 0); /* Cannot happen */
-       /* Turn Low Bit on (use range 200mA-950 mA) */
+
+       /* Turn Low Bit on (use range high_start-max)... */
        reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
        /* and set proper High Bits */
        reg_data |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT;
@@ -118,7 +162,7 @@ static struct regulator_ops max14577_charger_ops = {
        .set_current_limit      = max14577_reg_set_current_limit,
 };
 
-static const struct regulator_desc supported_regulators[] = {
+static const struct regulator_desc max14577_supported_regulators[] = {
        [MAX14577_SAFEOUT] = {
                .name           = "SAFEOUT",
                .id             = MAX14577_SAFEOUT,
@@ -141,16 +185,88 @@ static const struct regulator_desc supported_regulators[] = {
        },
 };
 
+static struct regulator_ops max77836_ldo_ops = {
+       .is_enabled             = regulator_is_enabled_regmap,
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .list_voltage           = regulator_list_voltage_linear,
+       .map_voltage            = regulator_map_voltage_linear,
+       .get_voltage_sel        = regulator_get_voltage_sel_regmap,
+       .set_voltage_sel        = regulator_set_voltage_sel_regmap,
+       /* TODO: add .set_suspend_mode */
+};
+
+static const struct regulator_desc max77836_supported_regulators[] = {
+       [MAX14577_SAFEOUT] = {
+               .name           = "SAFEOUT",
+               .id             = MAX14577_SAFEOUT,
+               .ops            = &max14577_safeout_ops,
+               .type           = REGULATOR_VOLTAGE,
+               .owner          = THIS_MODULE,
+               .n_voltages     = 1,
+               .min_uV         = MAX14577_REGULATOR_SAFEOUT_VOLTAGE,
+               .enable_reg     = MAX14577_REG_CONTROL2,
+               .enable_mask    = CTRL2_SFOUTORD_MASK,
+       },
+       [MAX14577_CHARGER] = {
+               .name           = "CHARGER",
+               .id             = MAX14577_CHARGER,
+               .ops            = &max14577_charger_ops,
+               .type           = REGULATOR_CURRENT,
+               .owner          = THIS_MODULE,
+               .enable_reg     = MAX14577_CHG_REG_CHG_CTRL2,
+               .enable_mask    = CHGCTRL2_MBCHOSTEN_MASK,
+       },
+       [MAX77836_LDO1] = {
+               .name           = "LDO1",
+               .id             = MAX77836_LDO1,
+               .ops            = &max77836_ldo_ops,
+               .type           = REGULATOR_VOLTAGE,
+               .owner          = THIS_MODULE,
+               .n_voltages     = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
+               .min_uV         = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
+               .uV_step        = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
+               .enable_reg     = MAX77836_LDO_REG_CNFG1_LDO1,
+               .enable_mask    = MAX77836_CNFG1_LDO_PWRMD_MASK,
+               .vsel_reg       = MAX77836_LDO_REG_CNFG1_LDO1,
+               .vsel_mask      = MAX77836_CNFG1_LDO_TV_MASK,
+       },
+       [MAX77836_LDO2] = {
+               .name           = "LDO2",
+               .id             = MAX77836_LDO2,
+               .ops            = &max77836_ldo_ops,
+               .type           = REGULATOR_VOLTAGE,
+               .owner          = THIS_MODULE,
+               .n_voltages     = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
+               .min_uV         = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
+               .uV_step        = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
+               .enable_reg     = MAX77836_LDO_REG_CNFG1_LDO2,
+               .enable_mask    = MAX77836_CNFG1_LDO_PWRMD_MASK,
+               .vsel_reg       = MAX77836_LDO_REG_CNFG1_LDO2,
+               .vsel_mask      = MAX77836_CNFG1_LDO_TV_MASK,
+       },
+};
+
 #ifdef CONFIG_OF
 static struct of_regulator_match max14577_regulator_matches[] = {
        { .name = "SAFEOUT", },
        { .name = "CHARGER", },
 };
 
-static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
+static struct of_regulator_match max77836_regulator_matches[] = {
+       { .name = "SAFEOUT", },
+       { .name = "CHARGER", },
+       { .name = "LDO1", },
+       { .name = "LDO2", },
+};
+
+static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev,
+               enum maxim_device_type dev_type)
 {
        int ret;
        struct device_node *np;
+       struct of_regulator_match *regulator_matches;
+       unsigned int regulator_matches_size;
 
        np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators");
        if (!np) {
@@ -158,8 +274,19 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       ret = of_regulator_match(&pdev->dev, np, max14577_regulator_matches,
-                       MAX14577_REG_MAX);
+       switch (dev_type) {
+       case MAXIM_DEVICE_TYPE_MAX77836:
+               regulator_matches = max77836_regulator_matches;
+               regulator_matches_size = ARRAY_SIZE(max77836_regulator_matches);
+               break;
+       case MAXIM_DEVICE_TYPE_MAX14577:
+       default:
+               regulator_matches = max14577_regulator_matches;
+               regulator_matches_size = ARRAY_SIZE(max14577_regulator_matches);
+       }
+
+       ret = of_regulator_match(&pdev->dev, np, regulator_matches,
+                       regulator_matches_size);
        if (ret < 0)
                dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret);
        else
@@ -170,31 +297,74 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
        return ret;
 }
 
-static inline struct regulator_init_data *match_init_data(int index)
+static inline struct regulator_init_data *match_init_data(int index,
+               enum maxim_device_type dev_type)
 {
-       return max14577_regulator_matches[index].init_data;
+       switch (dev_type) {
+       case MAXIM_DEVICE_TYPE_MAX77836:
+               return max77836_regulator_matches[index].init_data;
+
+       case MAXIM_DEVICE_TYPE_MAX14577:
+       default:
+               return max14577_regulator_matches[index].init_data;
+       }
 }
 
-static inline struct device_node *match_of_node(int index)
+static inline struct device_node *match_of_node(int index,
+               enum maxim_device_type dev_type)
 {
-       return max14577_regulator_matches[index].of_node;
+       switch (dev_type) {
+       case MAXIM_DEVICE_TYPE_MAX77836:
+               return max77836_regulator_matches[index].of_node;
+
+       case MAXIM_DEVICE_TYPE_MAX14577:
+       default:
+               return max14577_regulator_matches[index].of_node;
+       }
 }
 #else /* CONFIG_OF */
-static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
+static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev,
+               enum maxim_device_type dev_type)
 {
        return 0;
 }
-static inline struct regulator_init_data *match_init_data(int index)
+static inline struct regulator_init_data *match_init_data(int index,
+               enum maxim_device_type dev_type)
 {
        return NULL;
 }
 
-static inline struct device_node *match_of_node(int index)
+static inline struct device_node *match_of_node(int index,
+               enum maxim_device_type dev_type)
 {
        return NULL;
 }
 #endif /* CONFIG_OF */
 
+/**
+ * Registers for regulators of max77836 use different I2C slave addresses so
+ * different regmaps must be used for them.
+ *
+ * Returns proper regmap for accessing regulator passed by id.
+ */
+static struct regmap *max14577_get_regmap(struct max14577 *max14577,
+               int reg_id)
+{
+       switch (max14577->dev_type) {
+       case MAXIM_DEVICE_TYPE_MAX77836:
+               switch (reg_id) {
+               case MAX77836_SAFEOUT ... MAX77836_CHARGER:
+                       return max14577->regmap;
+               default:
+                       /* MAX77836_LDO1 ... MAX77836_LDO2 */
+                       return max14577->regmap_pmic;
+               }
+
+       case MAXIM_DEVICE_TYPE_MAX14577:
+       default:
+               return max14577->regmap;
+       }
+}
 
 static int max14577_regulator_probe(struct platform_device *pdev)
 {
@@ -202,15 +372,29 @@ static int max14577_regulator_probe(struct platform_device *pdev)
        struct max14577_platform_data *pdata = dev_get_platdata(max14577->dev);
        int i, ret;
        struct regulator_config config = {};
+       const struct regulator_desc *supported_regulators;
+       unsigned int supported_regulators_size;
+       enum maxim_device_type dev_type = max14577->dev_type;
 
-       ret = max14577_regulator_dt_parse_pdata(pdev);
+       ret = max14577_regulator_dt_parse_pdata(pdev, dev_type);
        if (ret)
                return ret;
 
+       switch (dev_type) {
+       case MAXIM_DEVICE_TYPE_MAX77836:
+               supported_regulators = max77836_supported_regulators;
+               supported_regulators_size = ARRAY_SIZE(max77836_supported_regulators);
+               break;
+       case MAXIM_DEVICE_TYPE_MAX14577:
+       default:
+               supported_regulators = max14577_supported_regulators;
+               supported_regulators_size = ARRAY_SIZE(max14577_supported_regulators);
+       }
+
        config.dev = &pdev->dev;
-       config.regmap = max14577->regmap;
+       config.driver_data = max14577;
 
-       for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) {
+       for (i = 0; i < supported_regulators_size; i++) {
                struct regulator_dev *regulator;
                /*
                 * Index of supported_regulators[] is also the id and must
@@ -220,17 +404,19 @@ static int max14577_regulator_probe(struct platform_device *pdev)
                        config.init_data = pdata->regulators[i].initdata;
                        config.of_node = pdata->regulators[i].of_node;
                } else {
-                       config.init_data = match_init_data(i);
-                       config.of_node = match_of_node(i);
+                       config.init_data = match_init_data(i, dev_type);
+                       config.of_node = match_of_node(i, dev_type);
                }
+               config.regmap = max14577_get_regmap(max14577,
+                               supported_regulators[i].id);
 
                regulator = devm_regulator_register(&pdev->dev,
                                &supported_regulators[i], &config);
                if (IS_ERR(regulator)) {
                        ret = PTR_ERR(regulator);
                        dev_err(&pdev->dev,
-                                       "Regulator init failed for ID %d with error: %d\n",
-                                       i, ret);
+                                       "Regulator init failed for %d/%s with error: %d\n",
+                                       i, supported_regulators[i].name, ret);
                        return ret;
                }
        }
@@ -238,20 +424,41 @@ static int max14577_regulator_probe(struct platform_device *pdev)
        return ret;
 }
 
+static const struct platform_device_id max14577_regulator_id[] = {
+       { "max14577-regulator", MAXIM_DEVICE_TYPE_MAX14577, },
+       { "max77836-regulator", MAXIM_DEVICE_TYPE_MAX77836, },
+       { }
+};
+MODULE_DEVICE_TABLE(platform, max14577_regulator_id);
+
 static struct platform_driver max14577_regulator_driver = {
        .driver = {
                   .owner = THIS_MODULE,
                   .name = "max14577-regulator",
                   },
-       .probe  = max14577_regulator_probe,
+       .probe          = max14577_regulator_probe,
+       .id_table       = max14577_regulator_id,
 };
 
 static int __init max14577_regulator_init(void)
 {
+       /* Check for valid values for charger */
        BUILD_BUG_ON(MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
                        MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
                        MAX14577_REGULATOR_CURRENT_LIMIT_MAX);
-       BUILD_BUG_ON(ARRAY_SIZE(supported_regulators) != MAX14577_REG_MAX);
+       BUILD_BUG_ON(MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START +
+                       MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
+                       MAX77836_REGULATOR_CURRENT_LIMIT_MAX);
+       /* Valid charger current values must be provided for each chipset */
+       BUILD_BUG_ON(ARRAY_SIZE(maxim_charger_currents) != MAXIM_DEVICE_TYPE_NUM);
+
+       BUILD_BUG_ON(ARRAY_SIZE(max14577_supported_regulators) != MAX14577_REGULATOR_NUM);
+       BUILD_BUG_ON(ARRAY_SIZE(max77836_supported_regulators) != MAX77836_REGULATOR_NUM);
+
+       BUILD_BUG_ON(MAX77836_REGULATOR_LDO_VOLTAGE_MIN +
+                       (MAX77836_REGULATOR_LDO_VOLTAGE_STEP *
+                         (MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM - 1)) !=
+                       MAX77836_REGULATOR_LDO_VOLTAGE_MAX);
 
        return platform_driver_register(&max14577_regulator_driver);
 }
@@ -264,6 +471,6 @@ static void __exit max14577_regulator_exit(void)
 module_exit(max14577_regulator_exit);
 
 MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
-MODULE_DESCRIPTION("MAXIM 14577 regulator driver");
+MODULE_DESCRIPTION("Maxim 14577/77836 regulator driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:max14577-regulator");
index 32f38a63d944eaaf148a5a1f7c4034ab372e7f0d..e045b7fe557278e68a747c84806274bf3178dd00 100644 (file)
@@ -68,7 +68,7 @@ static inline struct device *to_tps6586x_dev(struct regulator_dev *rdev)
        return rdev_get_dev(rdev)->parent;
 }
 
-static struct regulator_ops tps6586x_regulator_ops = {
+static struct regulator_ops tps6586x_rw_regulator_ops = {
        .list_voltage = regulator_list_voltage_table,
        .map_voltage = regulator_map_voltage_ascend,
        .get_voltage_sel = regulator_get_voltage_sel_regmap,
@@ -79,6 +79,16 @@ static struct regulator_ops tps6586x_regulator_ops = {
        .disable = regulator_disable_regmap,
 };
 
+static struct regulator_ops tps6586x_ro_regulator_ops = {
+       .list_voltage = regulator_list_voltage_table,
+       .map_voltage = regulator_map_voltage_ascend,
+       .get_voltage_sel = regulator_get_voltage_sel_regmap,
+
+       .is_enabled = regulator_is_enabled_regmap,
+       .enable = regulator_enable_regmap,
+       .disable = regulator_disable_regmap,
+};
+
 static struct regulator_ops tps6586x_sys_regulator_ops = {
 };
 
@@ -106,6 +116,13 @@ static const unsigned int tps6586x_sm2_voltages[] = {
        4200000, 4250000, 4300000, 4350000, 4400000, 4450000, 4500000, 4550000,
 };
 
+static int tps658640_sm2_voltages[] = {
+       2150000, 2200000, 2250000, 2300000, 2350000, 2400000, 2450000, 2500000,
+       2550000, 2600000, 2650000, 2700000, 2750000, 2800000, 2850000, 2900000,
+       2950000, 3000000, 3050000, 3100000, 3150000, 3200000, 3250000, 3300000,
+       3350000, 3400000, 3450000, 3500000, 3550000, 3600000, 3650000, 3700000,
+};
+
 static const unsigned int tps658643_sm2_voltages[] = {
        1025000, 1050000, 1075000, 1100000, 1125000, 1150000, 1175000, 1200000,
        1225000, 1250000, 1275000, 1300000, 1325000, 1350000, 1375000, 1400000,
@@ -120,12 +137,16 @@ static const unsigned int tps6586x_dvm_voltages[] = {
        1325000, 1350000, 1375000, 1400000, 1425000, 1450000, 1475000, 1500000,
 };
 
-#define TPS6586X_REGULATOR(_id, _pin_name, vdata, vreg, shift, nbits,  \
+static int tps658640_rtc_voltages[] = {
+       2500000, 2850000, 3100000, 3300000,
+};
+
+#define TPS6586X_REGULATOR(_id, _ops, _pin_name, vdata, vreg, shift, nbits, \
                           ereg0, ebit0, ereg1, ebit1, goreg, gobit)    \
        .desc   = {                                                     \
                .supply_name = _pin_name,                               \
                .name   = "REG-" #_id,                                  \
-               .ops    = &tps6586x_regulator_ops,                      \
+               .ops    = &tps6586x_## _ops ## _regulator_ops,          \
                .type   = REGULATOR_VOLTAGE,                            \
                .id     = TPS6586X_ID_##_id,                            \
                .n_voltages = ARRAY_SIZE(vdata##_voltages),             \
@@ -146,14 +167,21 @@ static const unsigned int tps6586x_dvm_voltages[] = {
 #define TPS6586X_LDO(_id, _pname, vdata, vreg, shift, nbits,           \
                     ereg0, ebit0, ereg1, ebit1)                        \
 {                                                                      \
-       TPS6586X_REGULATOR(_id, _pname, vdata, vreg, shift, nbits,      \
+       TPS6586X_REGULATOR(_id, rw, _pname, vdata, vreg, shift, nbits,  \
+                          ereg0, ebit0, ereg1, ebit1, 0, 0)            \
+}
+
+#define TPS6586X_FIXED_LDO(_id, _pname, vdata, vreg, shift, nbits,     \
+                         ereg0, ebit0, ereg1, ebit1)                   \
+{                                                                      \
+       TPS6586X_REGULATOR(_id, ro, _pname, vdata, vreg, shift, nbits,  \
                           ereg0, ebit0, ereg1, ebit1, 0, 0)            \
 }
 
 #define TPS6586X_DVM(_id, _pname, vdata, vreg, shift, nbits,           \
                     ereg0, ebit0, ereg1, ebit1, goreg, gobit)          \
 {                                                                      \
-       TPS6586X_REGULATOR(_id, _pname, vdata, vreg, shift, nbits,      \
+       TPS6586X_REGULATOR(_id, rw, _pname, vdata, vreg, shift, nbits,  \
                           ereg0, ebit0, ereg1, ebit1, goreg, gobit)    \
 }
 
@@ -207,6 +235,26 @@ static struct tps6586x_regulator tps658623_regulator[] = {
                                        END, 7),
 };
 
+static struct tps6586x_regulator tps658640_regulator[] = {
+       TPS6586X_LDO(LDO_3, "vinldo23", tps6586x_ldo0, SUPPLYV4, 0, 3,
+                                       ENC, 2, END, 2),
+       TPS6586X_LDO(LDO_5, "REG-SYS", tps6586x_ldo0, SUPPLYV6, 0, 3,
+                                       ENE, 6, ENE, 6),
+       TPS6586X_LDO(LDO_6, "vinldo678", tps6586x_ldo0, SUPPLYV3, 0, 3,
+                                       ENC, 4, END, 4),
+       TPS6586X_LDO(LDO_7, "vinldo678", tps6586x_ldo0, SUPPLYV3, 3, 3,
+                                       ENC, 5, END, 5),
+       TPS6586X_LDO(LDO_8, "vinldo678", tps6586x_ldo0, SUPPLYV2, 5, 3,
+                                       ENC, 6, END, 6),
+       TPS6586X_LDO(LDO_9, "vinldo9", tps6586x_ldo0, SUPPLYV6, 3, 3,
+                                       ENE, 7, ENE, 7),
+       TPS6586X_LDO(SM_2, "vin-sm2", tps658640_sm2, SUPPLYV2, 0, 5,
+                                       ENC, 7, END, 7),
+
+       TPS6586X_FIXED_LDO(LDO_RTC, "REG-SYS", tps658640_rtc, SUPPLYV4, 3, 2,
+                                       V4, 7, V4, 7),
+};
+
 static struct tps6586x_regulator tps658643_regulator[] = {
        TPS6586X_LDO(SM_2, "vin-sm2", tps658643_sm2, SUPPLYV2, 0, 5, ENC, 7,
                                        END, 7),
@@ -295,6 +343,11 @@ static struct tps6586x_regulator *find_regulator_info(int id, int version)
                table = tps658623_regulator;
                num = ARRAY_SIZE(tps658623_regulator);
                break;
+       case TPS658640:
+       case TPS658640v2:
+               table = tps658640_regulator;
+               num = ARRAY_SIZE(tps658640_regulator);
+               break;
        case TPS658643:
                table = tps658643_regulator;
                num = ARRAY_SIZE(tps658643_regulator);
index 5ae5100c1f2457ec9573870f2b90875031255a02..77a5fa191502fdf96528a3eccbf57f79cf3d9f6d 100644 (file)
@@ -468,6 +468,27 @@ struct cpufreq_frequency_table {
                                    * order */
 };
 
+bool cpufreq_next_valid(struct cpufreq_frequency_table **pos);
+
+/*
+ * cpufreq_for_each_entry -    iterate over a cpufreq_frequency_table
+ * @pos:       the cpufreq_frequency_table * to use as a loop cursor.
+ * @table:     the cpufreq_frequency_table * to iterate over.
+ */
+
+#define cpufreq_for_each_entry(pos, table)     \
+       for (pos = table; pos->frequency != CPUFREQ_TABLE_END; pos++)
+
+/*
+ * cpufreq_for_each_valid_entry -     iterate over a cpufreq_frequency_table
+ *     excluding CPUFREQ_ENTRY_INVALID frequencies.
+ * @pos:        the cpufreq_frequency_table * to use as a loop cursor.
+ * @table:      the cpufreq_frequency_table * to iterate over.
+ */
+
+#define cpufreq_for_each_valid_entry(pos, table)       \
+       for (pos = table; cpufreq_next_valid(&pos); pos++)
+
 int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
                                    struct cpufreq_frequency_table *table);
 
index 5cf8b91ce996c29652b4f5bdd5afad8cd9a62e6f..6d9371f88875d49c223f07f42401b1d1ed6f4bde 100644 (file)
@@ -124,4 +124,7 @@ int wm5102_patch(struct arizona *arizona);
 int wm5110_patch(struct arizona *arizona);
 int wm8997_patch(struct arizona *arizona);
 
+extern int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop,
+                                    bool mandatory);
+
 #endif
index c9b332fb0d5da1e11c0ac42c06bf62521541d55e..499253604026cf94044fe18709501b3b47e8fc3c 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * max14577-private.h - Common API for the Maxim 14577 internal sub chip
+ * max14577-private.h - Common API for the Maxim 14577/77836 internal sub chip
  *
- * Copyright (C) 2013 Samsung Electrnoics
+ * Copyright (C) 2014 Samsung Electrnoics
  * Chanwoo Choi <cw00.choi@samsung.com>
  * Krzysztof Kozlowski <k.kozlowski@samsung.com>
  *
 #include <linux/i2c.h>
 #include <linux/regmap.h>
 
-#define MAX14577_REG_INVALID           (0xff)
+#define I2C_ADDR_PMIC  (0x46 >> 1)
+#define I2C_ADDR_MUIC  (0x4A >> 1)
+#define I2C_ADDR_FG    (0x6C >> 1)
 
-/* Slave addr = 0x4A: Interrupt */
+enum maxim_device_type {
+       MAXIM_DEVICE_TYPE_UNKNOWN       = 0,
+       MAXIM_DEVICE_TYPE_MAX14577,
+       MAXIM_DEVICE_TYPE_MAX77836,
+
+       MAXIM_DEVICE_TYPE_NUM,
+};
+
+/* Slave addr = 0x4A: MUIC and Charger */
 enum max14577_reg {
        MAX14577_REG_DEVICEID           = 0x00,
        MAX14577_REG_INT1               = 0x01,
@@ -74,20 +84,22 @@ enum max14577_muic_charger_type {
 };
 
 /* MAX14577 interrupts */
-#define INT1_ADC_MASK                  (0x1 << 0)
-#define INT1_ADCLOW_MASK               (0x1 << 1)
-#define INT1_ADCERR_MASK               (0x1 << 2)
-
-#define INT2_CHGTYP_MASK               (0x1 << 0)
-#define INT2_CHGDETRUN_MASK            (0x1 << 1)
-#define INT2_DCDTMR_MASK               (0x1 << 2)
-#define INT2_DBCHG_MASK                        (0x1 << 3)
-#define INT2_VBVOLT_MASK               (0x1 << 4)
-
-#define INT3_EOC_MASK                  (0x1 << 0)
-#define INT3_CGMBC_MASK                        (0x1 << 1)
-#define INT3_OVP_MASK                  (0x1 << 2)
-#define INT3_MBCCHGERR_MASK            (0x1 << 3)
+#define MAX14577_INT1_ADC_MASK         BIT(0)
+#define MAX14577_INT1_ADCLOW_MASK      BIT(1)
+#define MAX14577_INT1_ADCERR_MASK      BIT(2)
+#define MAX77836_INT1_ADC1K_MASK       BIT(3)
+
+#define MAX14577_INT2_CHGTYP_MASK      BIT(0)
+#define MAX14577_INT2_CHGDETRUN_MASK   BIT(1)
+#define MAX14577_INT2_DCDTMR_MASK      BIT(2)
+#define MAX14577_INT2_DBCHG_MASK       BIT(3)
+#define MAX14577_INT2_VBVOLT_MASK      BIT(4)
+#define MAX77836_INT2_VIDRM_MASK       BIT(5)
+
+#define MAX14577_INT3_EOC_MASK         BIT(0)
+#define MAX14577_INT3_CGMBC_MASK       BIT(1)
+#define MAX14577_INT3_OVP_MASK         BIT(2)
+#define MAX14577_INT3_MBCCHGERR_MASK   BIT(3)
 
 /* MAX14577 DEVICE ID register */
 #define DEVID_VENDORID_SHIFT           0
@@ -99,9 +111,11 @@ enum max14577_muic_charger_type {
 #define STATUS1_ADC_SHIFT              0
 #define STATUS1_ADCLOW_SHIFT           5
 #define STATUS1_ADCERR_SHIFT           6
+#define MAX77836_STATUS1_ADC1K_SHIFT   7
 #define STATUS1_ADC_MASK               (0x1f << STATUS1_ADC_SHIFT)
-#define STATUS1_ADCLOW_MASK            (0x1 << STATUS1_ADCLOW_SHIFT)
-#define STATUS1_ADCERR_MASK            (0x1 << STATUS1_ADCERR_SHIFT)
+#define STATUS1_ADCLOW_MASK            BIT(STATUS1_ADCLOW_SHIFT)
+#define STATUS1_ADCERR_MASK            BIT(STATUS1_ADCERR_SHIFT)
+#define MAX77836_STATUS1_ADC1K_MASK    BIT(MAX77836_STATUS1_ADC1K_SHIFT)
 
 /* MAX14577 STATUS2 register */
 #define STATUS2_CHGTYP_SHIFT           0
@@ -109,11 +123,13 @@ enum max14577_muic_charger_type {
 #define STATUS2_DCDTMR_SHIFT           4
 #define STATUS2_DBCHG_SHIFT            5
 #define STATUS2_VBVOLT_SHIFT           6
+#define MAX77836_STATUS2_VIDRM_SHIFT   7
 #define STATUS2_CHGTYP_MASK            (0x7 << STATUS2_CHGTYP_SHIFT)
-#define STATUS2_CHGDETRUN_MASK         (0x1 << STATUS2_CHGDETRUN_SHIFT)
-#define STATUS2_DCDTMR_MASK            (0x1 << STATUS2_DCDTMR_SHIFT)
-#define STATUS2_DBCHG_MASK             (0x1 << STATUS2_DBCHG_SHIFT)
-#define STATUS2_VBVOLT_MASK            (0x1 << STATUS2_VBVOLT_SHIFT)
+#define STATUS2_CHGDETRUN_MASK         BIT(STATUS2_CHGDETRUN_SHIFT)
+#define STATUS2_DCDTMR_MASK            BIT(STATUS2_DCDTMR_SHIFT)
+#define STATUS2_DBCHG_MASK             BIT(STATUS2_DBCHG_SHIFT)
+#define STATUS2_VBVOLT_MASK            BIT(STATUS2_VBVOLT_SHIFT)
+#define MAX77836_STATUS2_VIDRM_MASK    BIT(MAX77836_STATUS2_VIDRM_SHIFT)
 
 /* MAX14577 CONTROL1 register */
 #define COMN1SW_SHIFT                  0
@@ -122,8 +138,8 @@ enum max14577_muic_charger_type {
 #define IDBEN_SHIFT                    7
 #define COMN1SW_MASK                   (0x7 << COMN1SW_SHIFT)
 #define COMP2SW_MASK                   (0x7 << COMP2SW_SHIFT)
-#define MICEN_MASK                     (0x1 << MICEN_SHIFT)
-#define IDBEN_MASK                     (0x1 << IDBEN_SHIFT)
+#define MICEN_MASK                     BIT(MICEN_SHIFT)
+#define IDBEN_MASK                     BIT(IDBEN_SHIFT)
 #define CLEAR_IDBEN_MICEN_MASK         (COMN1SW_MASK | COMP2SW_MASK)
 #define CTRL1_SW_USB                   ((1 << COMP2SW_SHIFT) \
                                                | (1 << COMN1SW_SHIFT))
@@ -143,14 +159,14 @@ enum max14577_muic_charger_type {
 #define CTRL2_ACCDET_SHIFT             (5)
 #define CTRL2_USBCPINT_SHIFT           (6)
 #define CTRL2_RCPS_SHIFT               (7)
-#define CTRL2_LOWPWR_MASK              (0x1 << CTRL2_LOWPWR_SHIFT)
-#define CTRL2_ADCEN_MASK               (0x1 << CTRL2_ADCEN_SHIFT)
-#define CTRL2_CPEN_MASK                        (0x1 << CTRL2_CPEN_SHIFT)
-#define CTRL2_SFOUTASRT_MASK           (0x1 << CTRL2_SFOUTASRT_SHIFT)
-#define CTRL2_SFOUTORD_MASK            (0x1 << CTRL2_SFOUTORD_SHIFT)
-#define CTRL2_ACCDET_MASK              (0x1 << CTRL2_ACCDET_SHIFT)
-#define CTRL2_USBCPINT_MASK            (0x1 << CTRL2_USBCPINT_SHIFT)
-#define CTRL2_RCPS_MASK                        (0x1 << CTR2_RCPS_SHIFT)
+#define CTRL2_LOWPWR_MASK              BIT(CTRL2_LOWPWR_SHIFT)
+#define CTRL2_ADCEN_MASK               BIT(CTRL2_ADCEN_SHIFT)
+#define CTRL2_CPEN_MASK                        BIT(CTRL2_CPEN_SHIFT)
+#define CTRL2_SFOUTASRT_MASK           BIT(CTRL2_SFOUTASRT_SHIFT)
+#define CTRL2_SFOUTORD_MASK            BIT(CTRL2_SFOUTORD_SHIFT)
+#define CTRL2_ACCDET_MASK              BIT(CTRL2_ACCDET_SHIFT)
+#define CTRL2_USBCPINT_MASK            BIT(CTRL2_USBCPINT_SHIFT)
+#define CTRL2_RCPS_MASK                        BIT(CTRL2_RCPS_SHIFT)
 
 #define CTRL2_CPEN1_LOWPWR0 ((1 << CTRL2_CPEN_SHIFT) | \
                                (0 << CTRL2_LOWPWR_SHIFT))
@@ -198,14 +214,14 @@ enum max14577_charger_reg {
 #define CDETCTRL1_DBEXIT_SHIFT         5
 #define CDETCTRL1_DBIDLE_SHIFT         6
 #define CDETCTRL1_CDPDET_SHIFT         7
-#define CDETCTRL1_CHGDETEN_MASK                (0x1 << CDETCTRL1_CHGDETEN_SHIFT)
-#define CDETCTRL1_CHGTYPMAN_MASK       (0x1 << CDETCTRL1_CHGTYPMAN_SHIFT)
-#define CDETCTRL1_DCDEN_MASK           (0x1 << CDETCTRL1_DCDEN_SHIFT)
-#define CDETCTRL1_DCD2SCT_MASK         (0x1 << CDETCTRL1_DCD2SCT_SHIFT)
-#define CDETCTRL1_DCHKTM_MASK          (0x1 << CDETCTRL1_DCHKTM_SHIFT)
-#define CDETCTRL1_DBEXIT_MASK          (0x1 << CDETCTRL1_DBEXIT_SHIFT)
-#define CDETCTRL1_DBIDLE_MASK          (0x1 << CDETCTRL1_DBIDLE_SHIFT)
-#define CDETCTRL1_CDPDET_MASK          (0x1 << CDETCTRL1_CDPDET_SHIFT)
+#define CDETCTRL1_CHGDETEN_MASK                BIT(CDETCTRL1_CHGDETEN_SHIFT)
+#define CDETCTRL1_CHGTYPMAN_MASK       BIT(CDETCTRL1_CHGTYPMAN_SHIFT)
+#define CDETCTRL1_DCDEN_MASK           BIT(CDETCTRL1_DCDEN_SHIFT)
+#define CDETCTRL1_DCD2SCT_MASK         BIT(CDETCTRL1_DCD2SCT_SHIFT)
+#define CDETCTRL1_DCHKTM_MASK          BIT(CDETCTRL1_DCHKTM_SHIFT)
+#define CDETCTRL1_DBEXIT_MASK          BIT(CDETCTRL1_DBEXIT_SHIFT)
+#define CDETCTRL1_DBIDLE_MASK          BIT(CDETCTRL1_DBIDLE_SHIFT)
+#define CDETCTRL1_CDPDET_MASK          BIT(CDETCTRL1_CDPDET_SHIFT)
 
 /* MAX14577 CHGCTRL1 register */
 #define CHGCTRL1_TCHW_SHIFT            4
@@ -213,9 +229,9 @@ enum max14577_charger_reg {
 
 /* MAX14577 CHGCTRL2 register */
 #define CHGCTRL2_MBCHOSTEN_SHIFT       6
-#define CHGCTRL2_MBCHOSTEN_MASK                (0x1 << CHGCTRL2_MBCHOSTEN_SHIFT)
+#define CHGCTRL2_MBCHOSTEN_MASK                BIT(CHGCTRL2_MBCHOSTEN_SHIFT)
 #define CHGCTRL2_VCHGR_RC_SHIFT                7
-#define CHGCTRL2_VCHGR_RC_MASK         (0x1 << CHGCTRL2_VCHGR_RC_SHIFT)
+#define CHGCTRL2_VCHGR_RC_MASK         BIT(CHGCTRL2_VCHGR_RC_SHIFT)
 
 /* MAX14577 CHGCTRL3 register */
 #define CHGCTRL3_MBCCVWRC_SHIFT                0
@@ -225,7 +241,7 @@ enum max14577_charger_reg {
 #define CHGCTRL4_MBCICHWRCH_SHIFT      0
 #define CHGCTRL4_MBCICHWRCH_MASK       (0xf << CHGCTRL4_MBCICHWRCH_SHIFT)
 #define CHGCTRL4_MBCICHWRCL_SHIFT      4
-#define CHGCTRL4_MBCICHWRCL_MASK       (0x1 << CHGCTRL4_MBCICHWRCL_SHIFT)
+#define CHGCTRL4_MBCICHWRCL_MASK       BIT(CHGCTRL4_MBCICHWRCL_SHIFT)
 
 /* MAX14577 CHGCTRL5 register */
 #define CHGCTRL5_EOCS_SHIFT            0
@@ -233,7 +249,7 @@ enum max14577_charger_reg {
 
 /* MAX14577 CHGCTRL6 register */
 #define CHGCTRL6_AUTOSTOP_SHIFT                5
-#define CHGCTRL6_AUTOSTOP_MASK         (0x1 << CHGCTRL6_AUTOSTOP_SHIFT)
+#define CHGCTRL6_AUTOSTOP_MASK         BIT(CHGCTRL6_AUTOSTOP_SHIFT)
 
 /* MAX14577 CHGCTRL7 register */
 #define CHGCTRL7_OTPCGHCVS_SHIFT       0
@@ -245,14 +261,111 @@ enum max14577_charger_reg {
 #define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP      50000
 #define MAX14577_REGULATOR_CURRENT_LIMIT_MAX           950000
 
+/* MAX77836 regulator current limits (as in CHGCTRL4 register), uA */
+#define MAX77836_REGULATOR_CURRENT_LIMIT_MIN            45000
+#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START    100000
+#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP      25000
+#define MAX77836_REGULATOR_CURRENT_LIMIT_MAX           475000
+
 /* MAX14577 regulator SFOUT LDO voltage, fixed, uV */
 #define MAX14577_REGULATOR_SAFEOUT_VOLTAGE             4900000
 
+/* MAX77836 regulator LDOx voltage, uV */
+#define MAX77836_REGULATOR_LDO_VOLTAGE_MIN             800000
+#define MAX77836_REGULATOR_LDO_VOLTAGE_MAX             3950000
+#define MAX77836_REGULATOR_LDO_VOLTAGE_STEP            50000
+#define MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM       64
+
+/* Slave addr = 0x46: PMIC */
+enum max77836_pmic_reg {
+       MAX77836_PMIC_REG_PMIC_ID               = 0x20,
+       MAX77836_PMIC_REG_PMIC_REV              = 0x21,
+       MAX77836_PMIC_REG_INTSRC                = 0x22,
+       MAX77836_PMIC_REG_INTSRC_MASK           = 0x23,
+       MAX77836_PMIC_REG_TOPSYS_INT            = 0x24,
+       MAX77836_PMIC_REG_TOPSYS_INT_MASK       = 0x26,
+       MAX77836_PMIC_REG_TOPSYS_STAT           = 0x28,
+       MAX77836_PMIC_REG_MRSTB_CNTL            = 0x2A,
+       MAX77836_PMIC_REG_LSCNFG                = 0x2B,
+
+       MAX77836_LDO_REG_CNFG1_LDO1             = 0x51,
+       MAX77836_LDO_REG_CNFG2_LDO1             = 0x52,
+       MAX77836_LDO_REG_CNFG1_LDO2             = 0x53,
+       MAX77836_LDO_REG_CNFG2_LDO2             = 0x54,
+       MAX77836_LDO_REG_CNFG_LDO_BIAS          = 0x55,
+
+       MAX77836_COMP_REG_COMP1                 = 0x60,
+
+       MAX77836_PMIC_REG_END,
+};
+
+#define MAX77836_INTSRC_MASK_TOP_INT_SHIFT     1
+#define MAX77836_INTSRC_MASK_MUIC_CHG_INT_SHIFT        3
+#define MAX77836_INTSRC_MASK_TOP_INT_MASK      BIT(MAX77836_INTSRC_MASK_TOP_INT_SHIFT)
+#define MAX77836_INTSRC_MASK_MUIC_CHG_INT_MASK BIT(MAX77836_INTSRC_MASK_MUIC_CHG_INT_SHIFT)
+
+/* MAX77836 PMIC interrupts */
+#define MAX77836_TOPSYS_INT_T120C_SHIFT                0
+#define MAX77836_TOPSYS_INT_T140C_SHIFT                1
+#define MAX77836_TOPSYS_INT_T120C_MASK         BIT(MAX77836_TOPSYS_INT_T120C_SHIFT)
+#define MAX77836_TOPSYS_INT_T140C_MASK         BIT(MAX77836_TOPSYS_INT_T140C_SHIFT)
+
+/* LDO1/LDO2 CONFIG1 register */
+#define MAX77836_CNFG1_LDO_PWRMD_SHIFT         6
+#define MAX77836_CNFG1_LDO_TV_SHIFT            0
+#define MAX77836_CNFG1_LDO_PWRMD_MASK          (0x3 << MAX77836_CNFG1_LDO_PWRMD_SHIFT)
+#define MAX77836_CNFG1_LDO_TV_MASK             (0x3f << MAX77836_CNFG1_LDO_TV_SHIFT)
+
+/* LDO1/LDO2 CONFIG2 register */
+#define MAX77836_CNFG2_LDO_OVCLMPEN_SHIFT      7
+#define MAX77836_CNFG2_LDO_ALPMEN_SHIFT                6
+#define MAX77836_CNFG2_LDO_COMP_SHIFT          4
+#define MAX77836_CNFG2_LDO_POK_SHIFT           3
+#define MAX77836_CNFG2_LDO_ADE_SHIFT           1
+#define MAX77836_CNFG2_LDO_SS_SHIFT            0
+#define MAX77836_CNFG2_LDO_OVCLMPEN_MASK       BIT(MAX77836_CNFG2_LDO_OVCLMPEN_SHIFT)
+#define MAX77836_CNFG2_LDO_ALPMEN_MASK         BIT(MAX77836_CNFG2_LDO_ALPMEN_SHIFT)
+#define MAX77836_CNFG2_LDO_COMP_MASK           (0x3 << MAX77836_CNFG2_LDO_COMP_SHIFT)
+#define MAX77836_CNFG2_LDO_POK_MASK            BIT(MAX77836_CNFG2_LDO_POK_SHIFT)
+#define MAX77836_CNFG2_LDO_ADE_MASK            BIT(MAX77836_CNFG2_LDO_ADE_SHIFT)
+#define MAX77836_CNFG2_LDO_SS_MASK             BIT(MAX77836_CNFG2_LDO_SS_SHIFT)
+
+/* Slave addr = 0x6C: Fuel-Gauge/Battery */
+enum max77836_fg_reg {
+       MAX77836_FG_REG_VCELL_MSB       = 0x02,
+       MAX77836_FG_REG_VCELL_LSB       = 0x03,
+       MAX77836_FG_REG_SOC_MSB         = 0x04,
+       MAX77836_FG_REG_SOC_LSB         = 0x05,
+       MAX77836_FG_REG_MODE_H          = 0x06,
+       MAX77836_FG_REG_MODE_L          = 0x07,
+       MAX77836_FG_REG_VERSION_MSB     = 0x08,
+       MAX77836_FG_REG_VERSION_LSB     = 0x09,
+       MAX77836_FG_REG_HIBRT_H         = 0x0A,
+       MAX77836_FG_REG_HIBRT_L         = 0x0B,
+       MAX77836_FG_REG_CONFIG_H        = 0x0C,
+       MAX77836_FG_REG_CONFIG_L        = 0x0D,
+       MAX77836_FG_REG_VALRT_MIN       = 0x14,
+       MAX77836_FG_REG_VALRT_MAX       = 0x15,
+       MAX77836_FG_REG_CRATE_MSB       = 0x16,
+       MAX77836_FG_REG_CRATE_LSB       = 0x17,
+       MAX77836_FG_REG_VRESET          = 0x18,
+       MAX77836_FG_REG_FGID            = 0x19,
+       MAX77836_FG_REG_STATUS_H        = 0x1A,
+       MAX77836_FG_REG_STATUS_L        = 0x1B,
+       /*
+        * TODO: TABLE registers
+        * TODO: CMD register
+        */
+
+       MAX77836_FG_REG_END,
+};
+
 enum max14577_irq {
        /* INT1 */
        MAX14577_IRQ_INT1_ADC,
        MAX14577_IRQ_INT1_ADCLOW,
        MAX14577_IRQ_INT1_ADCERR,
+       MAX77836_IRQ_INT1_ADC1K,
 
        /* INT2 */
        MAX14577_IRQ_INT2_CHGTYP,
@@ -260,6 +373,7 @@ enum max14577_irq {
        MAX14577_IRQ_INT2_DCDTMR,
        MAX14577_IRQ_INT2_DBCHG,
        MAX14577_IRQ_INT2_VBVOLT,
+       MAX77836_IRQ_INT2_VIDRM,
 
        /* INT3 */
        MAX14577_IRQ_INT3_EOC,
@@ -267,21 +381,25 @@ enum max14577_irq {
        MAX14577_IRQ_INT3_OVP,
        MAX14577_IRQ_INT3_MBCCHGERR,
 
+       /* TOPSYS_INT, only MAX77836 */
+       MAX77836_IRQ_TOPSYS_T140C,
+       MAX77836_IRQ_TOPSYS_T120C,
+
        MAX14577_IRQ_NUM,
 };
 
 struct max14577 {
        struct device *dev;
        struct i2c_client *i2c; /* Slave addr = 0x4A */
+       struct i2c_client *i2c_pmic; /* Slave addr = 0x46 */
+       enum maxim_device_type dev_type;
 
-       struct regmap *regmap;
+       struct regmap *regmap; /* For MUIC and Charger */
+       struct regmap *regmap_pmic;
 
-       struct regmap_irq_chip_data *irq_data;
+       struct regmap_irq_chip_data *irq_data; /* For MUIC and Charger */
+       struct regmap_irq_chip_data *irq_data_pmic;
        int irq;
-
-       /* Device ID */
-       u8 vendor_id;   /* Vendor Identification */
-       u8 device_id;   /* Chip Version */
 };
 
 /* MAX14577 shared regmap API function */
index 736d39c3ec0d7a4b115df93d97d60d84c4979a85..c83fbed1c7b64a7e59ab0ab36e8cfa8fb4d1b6af 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * max14577.h - Driver for the Maxim 14577
+ * max14577.h - 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>
  *
@@ -20,6 +20,9 @@
  * MAX14577 has MUIC, Charger devices.
  * The devices share the same I2C bus and interrupt line
  * included in this mfd driver.
+ *
+ * MAX77836 has additional PMIC and Fuel-Gauge on different I2C slave
+ * addresses.
  */
 
 #ifndef __MAX14577_H__
@@ -32,7 +35,17 @@ enum max14577_regulators {
        MAX14577_SAFEOUT = 0,
        MAX14577_CHARGER,
 
-       MAX14577_REG_MAX,
+       MAX14577_REGULATOR_NUM,
+};
+
+/* MAX77836 regulator IDs */
+enum max77836_regulators {
+       MAX77836_SAFEOUT = 0,
+       MAX77836_CHARGER,
+       MAX77836_LDO1,
+       MAX77836_LDO2,
+
+       MAX77836_REGULATOR_NUM,
 };
 
 struct max14577_regulator_platform_data {
index 48395a69a7e96464c8bd44ae6fdaaf3066c22ffa..575a86c7fcbd2d2b4168d25c3ee20c275e84fc0e 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/mutex.h>
 
 struct device;
+struct regulator;
 
 enum stmpe_block {
        STMPE_BLOCK_GPIO        = 1 << 0,
@@ -62,6 +63,8 @@ struct stmpe_client_info;
 
 /**
  * struct stmpe - STMPE MFD structure
+ * @vcc: optional VCC regulator
+ * @vio: optional VIO regulator
  * @lock: lock protecting I/O operations
  * @irq_lock: IRQ bus lock
  * @dev: device, mostly for dev_dbg()
@@ -73,13 +76,14 @@ struct stmpe_client_info;
  * @regs: list of addresses of registers which are at different addresses on
  *       different variants.  Indexed by one of STMPE_IDX_*.
  * @irq: irq number for stmpe
- * @irq_base: starting IRQ number for internal IRQs
  * @num_gpios: number of gpios, differs for variants
  * @ier: cache of IER registers for bus_lock
  * @oldier: cache of IER registers for bus_lock
  * @pdata: platform data
  */
 struct stmpe {
+       struct regulator *vcc;
+       struct regulator *vio;
        struct mutex lock;
        struct mutex irq_lock;
        struct device *dev;
@@ -91,7 +95,6 @@ struct stmpe {
        const u8 *regs;
 
        int irq;
-       int irq_base;
        int num_gpios;
        u8 ier[2];
        u8 oldier[2];
@@ -132,8 +135,6 @@ struct stmpe_keypad_platform_data {
 
 /**
  * struct stmpe_gpio_platform_data - STMPE GPIO platform data
- * @gpio_base: first gpio number assigned.  A maximum of
- *            %STMPE_NR_GPIOS GPIOs will be allocated.
  * @norequest_mask: bitmask specifying which GPIOs should _not_ be
  *                 requestable due to different usage (e.g. touch, keypad)
  *                 STMPE_GPIO_NOREQ_* macros can be used here.
@@ -141,7 +142,6 @@ struct stmpe_keypad_platform_data {
  * @remove: board specific remove callback
  */
 struct stmpe_gpio_platform_data {
-       int gpio_base;
        unsigned norequest_mask;
        void (*setup)(struct stmpe *stmpe, unsigned gpio_base);
        void (*remove)(struct stmpe *stmpe, unsigned gpio_base);
@@ -195,8 +195,6 @@ struct stmpe_ts_platform_data {
  * @irq_trigger: IRQ trigger to use for the interrupt to the host
  * @autosleep: bool to enable/disable stmpe autosleep
  * @autosleep_timeout: inactivity timeout in milliseconds for autosleep
- * @irq_base: base IRQ number.  %STMPE_NR_IRQS irqs will be used, or
- *           %STMPE_NR_INTERNAL_IRQS if the GPIO driver is not used.
  * @irq_over_gpio: true if gpio is used to get irq
  * @irq_gpio: gpio number over which irq will be requested (significant only if
  *           irq_over_gpio is true)
@@ -207,7 +205,6 @@ struct stmpe_ts_platform_data {
 struct stmpe_platform_data {
        int id;
        unsigned int blocks;
-       int irq_base;
        unsigned int irq_trigger;
        bool autosleep;
        bool irq_over_gpio;
@@ -219,10 +216,4 @@ struct stmpe_platform_data {
        struct stmpe_ts_platform_data *ts;
 };
 
-#define STMPE_NR_INTERNAL_IRQS 9
-#define STMPE_INT_GPIO(x)      (STMPE_NR_INTERNAL_IRQS + (x))
-
-#define STMPE_NR_GPIOS         24
-#define STMPE_NR_IRQS          STMPE_INT_GPIO(STMPE_NR_GPIOS)
-
 #endif
index 3f43069413e72dcc2975a14c1e9edf5677365627..45f0f9d2ed25f4eccf240443d0dcb76b0b3d0d41 100644 (file)
@@ -64,6 +64,20 @@ enum {
        TPS65090_REGULATOR_MAX,
 };
 
+/* Register addresses */
+#define TPS65090_REG_INTR_STS  0x00
+#define TPS65090_REG_INTR_STS2 0x01
+#define TPS65090_REG_INTR_MASK 0x02
+#define TPS65090_REG_INTR_MASK2        0x03
+#define TPS65090_REG_CG_CTRL0  0x04
+#define TPS65090_REG_CG_CTRL1  0x05
+#define TPS65090_REG_CG_CTRL2  0x06
+#define TPS65090_REG_CG_CTRL3  0x07
+#define TPS65090_REG_CG_CTRL4  0x08
+#define TPS65090_REG_CG_CTRL5  0x09
+#define TPS65090_REG_CG_STATUS1        0x0a
+#define TPS65090_REG_CG_STATUS2        0x0b
+
 struct tps65090 {
        struct device           *dev;
        struct regmap           *rmap;
index cbecec2e353a3df0d931da2c86d5c206c84ea3eb..96187ed9f9bbdf7941b08caa68ae5a20496232e8 100644 (file)
@@ -17,6 +17,8 @@
 #define TPS658621A     0x15
 #define TPS658621CD    0x2c
 #define TPS658623      0x1b
+#define TPS658640      0x01
+#define TPS658640v2    0x02
 #define TPS658643      0x03
 
 enum {
index 2c59b1fb69dc98368035e2c4d05a4db9f1cc167b..a7118052f32aa08a0250b3ff09a316c9db9ca4f5 100644 (file)
@@ -22,6 +22,7 @@
  */
 #include <linux/module.h>
 #include <linux/device.h>
+#include <linux/of.h>
 #include <linux/mfd/mc13xxx.h>
 #include <linux/slab.h>
 #include <sound/core.h>
@@ -750,6 +751,7 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
 {
        struct mc13783_priv *priv;
        struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data;
+       struct device_node *np;
        int ret;
 
        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
@@ -760,7 +762,17 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
                priv->adc_ssi_port = pdata->adc_ssi_port;
                priv->dac_ssi_port = pdata->dac_ssi_port;
        } else {
-               return -ENOSYS;
+               np = of_get_child_by_name(pdev->dev.parent->of_node, "codec");
+               if (!np)
+                       return -ENOSYS;
+
+               ret = of_property_read_u32(np, "adc-port", &priv->adc_ssi_port);
+               if (ret)
+                       return ret;
+
+               ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port);
+               if (ret)
+                       return ret;
        }
 
        dev_set_drvdata(&pdev->dev, priv);