diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-07 08:35:35 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-07 08:35:35 -0700 |
commit | d042380886fb2fc6c4b0fcfe1214ba473769a8e9 (patch) | |
tree | 61e29ff167e0f83f67930ee9911062415030e1ef /drivers/mfd | |
parent | 3477d168ba61c5b0ca42d3d4642f3463609a5417 (diff) | |
parent | b8d336ed90f541097a2ce583be430bb3e895dfbd (diff) | |
download | linux-d042380886fb2fc6c4b0fcfe1214ba473769a8e9.tar.bz2 |
Merge tag 'mfd-for-linus-4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
Pull MFD updates from Lee Jones:
"Core framework:
- Add the MFD bindings doc to MAINTAINERS
New drivers:
- X-Powers AC100 Audio CODEC and RTC
- TI LP873x PMIC
- Rockchip RK808 PMIC
- Samsung Exynos Low Power Audio
New device support:
- Add support for STMPE1600 variant to stmpe
- Add support for PM8018 PMIC to pm8921-core
- Add support for AXP806 PMIC in axp20x
- Add support for AXP209 GPIO in axp20x
New functionality:
- Add support for Reset to all STMPE variants
- Add support for MKBP event support to cros_ec
- Add support for USB to intel_soc_pmic_bxtwc
- Add support for IRQs and Power Button to tps65217
Fix-ups:
- Clean-up defunct author emails (da9063, max14577)
- Kconfig fixups (wm8350-i2c, as37220
- Constify (altera-a10sr, sm501)
- Supply PCI IDs (intel-lpss-pci)
- Improve clocking (qcom_rpm)
- Fix IRQ probing (ucb1x00-core)
- Ensure fault log is cleared (da9052)
- Remove NO_IRQ check (ucb1x00-core)
- Supply I2C properties (intel-lpss-acpi, intel-lpss-pci)
- Non standard declaration (tps65217, max8997-irq)
- Remove unused code (lp873x, db8500-prcmu, ab8500-debugfs,
cros_ec_spi)
- Make non-modular (altera-a10sr, intel_msic, smsc-ece1099,
sun6i-prcm, twl-core)
- OF bindings (ac100, stmpe, qcom-pm8xxx, qcom-rpm, rk808, axp20x,
lp873x, exynos5433-lpass, act8945a, aspeed-scu, twl6040, arizona)
Bugfixes:
- Release OF pointer (qcom_rpm)
- Avoid double shifting in suspend/resume (88pm80x)
- Fix 'defined but not used' error (exynos-lpass)
- Fix 'sleeping whilst attomic' (atmel-hlcdc)"
* tag 'mfd-for-linus-4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (69 commits)
mfd: arizona: Handle probe deferral for reset GPIO
mfd: arizona: Remove arizona_of_get_named_gpio helper function
mfd: arizona: Add DT options for max_channels_clocked and PDM speaker config
mfd: twl6040: Register child device for twl6040-pdmclk
mfd: cros_ec_spi: Remove unused variable 'request'
mfd: omap-usb-host: Return value is not 'const int'
mfd: ab8500-debugfs: Remove 'weak' function suspend_test_wake_cause_interrupt_is_mine()
mfd: ab8500-debugfs: Remove ab8500_dump_all_banks_to_mem()
mfd: db8500-prcmu: Remove unused *prcmu_set_ddr_opp() calls
mfd: ab8500-debugfs: Prevent initialised field from being over-written
mfd: max8997-irq: 'inline' should be at the beginning of the declaration
mfd: rk808: Fix RK818_IRQ_DISCHG_ILIM initializer
mfd: tps65217: Fix nonstandard declaration
mfd: lp873x: Remove unused mutex lock from struct lp873x
mfd: atmel-hlcdc: Do not sleep in atomic context
mfd: exynos-lpass: Mark PM functions as __maybe_unused
mfd: intel-lpss: Add default I2C device properties for Apollo Lake
mfd: twl-core: Make it explicitly non-modular
mfd: sun6i-prcm: Make it explicitly non-modular
mfd: smsc-ece1099: Make it explicitly non-modular
...
Diffstat (limited to 'drivers/mfd')
38 files changed, 1226 insertions, 271 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2caf7e967390..c6df6442ba2b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -50,7 +50,7 @@ config MFD_AS3711 Support for the AS3711 PMIC from AMS config MFD_AS3722 - bool "ams AS3722 Power Management IC" + tristate "ams AS3722 Power Management IC" select MFD_CORE select REGMAP_I2C select REGMAP_IRQ @@ -112,6 +112,16 @@ config MFD_BCM590XX help Support for the BCM590xx PMUs from Broadcom +config MFD_AC100 + tristate "X-Powers AC100" + select MFD_CORE + depends on SUNXI_RSB + help + If you say Y here you get support for the X-Powers AC100 audio codec + IC. + This driver include only the core APIs. You have to select individual + components like codecs or RTC under the corresponding menus. + config MFD_AXP20X tristate select MFD_CORE @@ -281,6 +291,14 @@ config MFD_DLN2 etc. must be enabled in order to use the functionality of the device. +config MFD_EXYNOS_LPASS + tristate "Samsung Exynos SoC Low Power Audio Subsystem" + select MFD_CORE + select REGMAP_MMIO + help + Select this option to enable support for Samsung Exynos Low Power + Audio Subsystem. + config MFD_MC13XXX tristate depends on (SPI_MASTER || I2C) @@ -844,13 +862,13 @@ config MFD_RC5T583 different functionality of the device. config MFD_RK808 - tristate "Rockchip RK808 Power Management chip" + tristate "Rockchip RK808/RK818 Power Management Chip" depends on I2C && OF select MFD_CORE select REGMAP_I2C select REGMAP_IRQ help - If you say yes here you get support for the RK808 + If you say yes here you get support for the RK808 and RK818 Power Management chips. This driver provides common support for accessing the device through I2C interface. The device supports multiple sub-devices @@ -1206,6 +1224,7 @@ config MFD_TPS65217 depends on I2C select MFD_CORE select REGMAP_I2C + select IRQ_DOMAIN help If you say yes here you get support for the TPS65217 series of Power Management / White LED chips. @@ -1555,6 +1574,7 @@ config MFD_WM8350 config MFD_WM8350_I2C bool "Wolfson Microelectronics WM8350 with I2C" select MFD_WM8350 + select REGMAP_I2C depends on I2C=y help The WM8350 is an integrated audio and power management diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 2bf6a1ac7428..9834e669d985 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o +obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o @@ -114,6 +115,8 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-irq.o obj-$(CONFIG_PMIC_DA9052) += da9052-core.o obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o + +obj-$(CONFIG_MFD_AC100) += ac100.o obj-$(CONFIG_MFD_AXP20X) += axp20x.o obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 0aecd7bd35f8..acf6c00b14b9 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -153,14 +153,14 @@ static struct hwreg_cfg hwreg_cfg = { #define AB8500_NAME_STRING "ab8500" #define AB8500_ADC_NAME_STRING "gpadc" -#define AB8500_NUM_BANKS 24 +#define AB8500_NUM_BANKS AB8500_DEBUG_FIELD_LAST #define AB8500_REV_REG 0x80 static struct ab8500_prcmu_ranges *debug_ranges; static struct ab8500_prcmu_ranges ab8500_debug_ranges[AB8500_NUM_BANKS] = { - [0x0] = { + [AB8500_M_FSM_RANK] = { .num_ranges = 0, .range = NULL, }, @@ -315,7 +315,7 @@ static struct ab8500_prcmu_ranges ab8500_debug_ranges[AB8500_NUM_BANKS] = { }, }, }, - [0x9] = { + [AB8500_RESERVED] = { .num_ranges = 0, .range = NULL, }, @@ -386,24 +386,6 @@ static struct ab8500_prcmu_ranges ab8500_debug_ranges[AB8500_NUM_BANKS] = { }, }, }, - [AB8500_DEVELOPMENT] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - }, - }, - [AB8500_DEBUG] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x05, - .last = 0x07, - }, - }, - }, [AB8500_AUDIO] = { .num_ranges = 1, .range = (struct ab8500_reg_range[]) { @@ -463,19 +445,29 @@ static struct ab8500_prcmu_ranges ab8500_debug_ranges[AB8500_NUM_BANKS] = { }, }, }, - [0x11] = { - .num_ranges = 0, - .range = NULL, + [AB8500_DEVELOPMENT] = { + .num_ranges = 1, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x00, + .last = 0x00, + }, + }, }, - [0x12] = { - .num_ranges = 0, - .range = NULL, + [AB8500_DEBUG] = { + .num_ranges = 1, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x05, + .last = 0x07, + }, + }, }, - [0x13] = { + [AB8500_PROD_TEST] = { .num_ranges = 0, .range = NULL, }, - [0x14] = { + [AB8500_STE_TEST] = { .num_ranges = 0, .range = NULL, }, @@ -1382,60 +1374,6 @@ void ab8500_dump_all_banks(struct device *dev) } } -/* Space for 500 registers. */ -#define DUMP_MAX_REGS 700 -static struct ab8500_register_dump -{ - u8 bank; - u8 reg; - u8 value; -} ab8500_complete_register_dump[DUMP_MAX_REGS]; - -/* This shall only be called upon kernel panic! */ -void ab8500_dump_all_banks_to_mem(void) -{ - int i, r = 0; - u8 bank; - int err = 0; - - pr_info("Saving all ABB registers for crash analysis.\n"); - - for (bank = 0; bank < AB8500_NUM_BANKS; bank++) { - for (i = 0; i < debug_ranges[bank].num_ranges; i++) { - u8 reg; - - for (reg = debug_ranges[bank].range[i].first; - reg <= debug_ranges[bank].range[i].last; - reg++) { - u8 value; - - err = prcmu_abb_read(bank, reg, &value, 1); - - if (err < 0) - goto out; - - ab8500_complete_register_dump[r].bank = bank; - ab8500_complete_register_dump[r].reg = reg; - ab8500_complete_register_dump[r].value = value; - - r++; - - if (r >= DUMP_MAX_REGS) { - pr_err("%s: too many register to dump!\n", - __func__); - err = -EINVAL; - goto out; - } - } - } - } -out: - if (err >= 0) - pr_info("Saved all ABB registers.\n"); - else - pr_info("Failed to save all ABB registers.\n"); -} - static int ab8500_all_banks_open(struct inode *inode, struct file *file) { struct seq_file *s; @@ -1584,18 +1522,10 @@ static u32 num_interrupts[AB8500_MAX_NR_IRQS]; static u32 num_wake_interrupts[AB8500_MAX_NR_IRQS]; static int num_interrupt_lines; -bool __attribute__((weak)) suspend_test_wake_cause_interrupt_is_mine(u32 my_int) -{ - return false; -} - void ab8500_debug_register_interrupt(int line) { - if (line < num_interrupt_lines) { + if (line < num_interrupt_lines) num_interrupts[line]++; - if (suspend_test_wake_cause_interrupt_is_mine(irq_ab8500)) - num_wake_interrupts[line]++; - } } static int ab8500_interrupts_print(struct seq_file *s, void *p) diff --git a/drivers/mfd/ac100.c b/drivers/mfd/ac100.c new file mode 100644 index 000000000000..9bc69cd7807d --- /dev/null +++ b/drivers/mfd/ac100.c @@ -0,0 +1,137 @@ +/* + * MFD core driver for X-Powers' AC100 Audio Codec IC + * + * The AC100 is a highly integrated audio codec and RTC subsystem designed + * for mobile applications. It has 3 I2S/PCM interfaces, a 2 channel DAC, + * a 2 channel ADC with 5 inputs and a builtin mixer. The RTC subsystem has + * 3 clock outputs. + * + * The audio codec and RTC parts are completely separate, sharing only the + * host interface for access to its registers. + * + * Copyright (2016) Chen-Yu Tsai + * + * Author: Chen-Yu Tsai <wens@csie.org> + * + * 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. + */ + +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/core.h> +#include <linux/mfd/ac100.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/sunxi-rsb.h> + +static const struct regmap_range ac100_writeable_ranges[] = { + regmap_reg_range(AC100_CHIP_AUDIO_RST, AC100_I2S_SR_CTRL), + regmap_reg_range(AC100_I2S1_CLK_CTRL, AC100_I2S1_MXR_GAIN), + regmap_reg_range(AC100_I2S2_CLK_CTRL, AC100_I2S2_MXR_GAIN), + regmap_reg_range(AC100_I2S3_CLK_CTRL, AC100_I2S3_SIG_PATH_CTRL), + regmap_reg_range(AC100_ADC_DIG_CTRL, AC100_ADC_VOL_CTRL), + regmap_reg_range(AC100_HMIC_CTRL1, AC100_HMIC_STATUS), + regmap_reg_range(AC100_DAC_DIG_CTRL, AC100_DAC_MXR_GAIN), + regmap_reg_range(AC100_ADC_APC_CTRL, AC100_LINEOUT_CTRL), + regmap_reg_range(AC100_ADC_DAP_L_CTRL, AC100_ADC_DAP_OPT), + regmap_reg_range(AC100_DAC_DAP_CTRL, AC100_DAC_DAP_OPT), + regmap_reg_range(AC100_ADC_DAP_ENA, AC100_DAC_DAP_ENA), + regmap_reg_range(AC100_SRC1_CTRL1, AC100_SRC1_CTRL2), + regmap_reg_range(AC100_SRC2_CTRL1, AC100_SRC2_CTRL2), + regmap_reg_range(AC100_CLK32K_ANALOG_CTRL, AC100_CLKOUT_CTRL3), + regmap_reg_range(AC100_RTC_RST, AC100_RTC_UPD), + regmap_reg_range(AC100_ALM_INT_ENA, AC100_ALM_INT_STA), + regmap_reg_range(AC100_ALM_SEC, AC100_RTC_GP(15)), +}; + +static const struct regmap_range ac100_volatile_ranges[] = { + regmap_reg_range(AC100_CHIP_AUDIO_RST, AC100_PLL_CTRL2), + regmap_reg_range(AC100_HMIC_STATUS, AC100_HMIC_STATUS), + regmap_reg_range(AC100_ADC_DAP_L_STA, AC100_ADC_DAP_L_STA), + regmap_reg_range(AC100_SRC1_CTRL1, AC100_SRC1_CTRL1), + regmap_reg_range(AC100_SRC1_CTRL3, AC100_SRC2_CTRL1), + regmap_reg_range(AC100_SRC2_CTRL3, AC100_SRC2_CTRL4), + regmap_reg_range(AC100_RTC_RST, AC100_RTC_RST), + regmap_reg_range(AC100_RTC_SEC, AC100_ALM_INT_STA), + regmap_reg_range(AC100_ALM_SEC, AC100_ALM_UPD), +}; + +static const struct regmap_access_table ac100_writeable_table = { + .yes_ranges = ac100_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(ac100_writeable_ranges), +}; + +static const struct regmap_access_table ac100_volatile_table = { + .yes_ranges = ac100_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(ac100_volatile_ranges), +}; + +static const struct regmap_config ac100_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .wr_table = &ac100_writeable_table, + .volatile_table = &ac100_volatile_table, + .max_register = AC100_RTC_GP(15), + .cache_type = REGCACHE_RBTREE, +}; + +static struct mfd_cell ac100_cells[] = { + { + .name = "ac100-codec", + .of_compatible = "x-powers,ac100-codec", + }, { + .name = "ac100-rtc", + .of_compatible = "x-powers,ac100-rtc", + }, +}; + +static int ac100_rsb_probe(struct sunxi_rsb_device *rdev) +{ + struct ac100_dev *ac100; + int ret; + + ac100 = devm_kzalloc(&rdev->dev, sizeof(*ac100), GFP_KERNEL); + if (!ac100) + return -ENOMEM; + + ac100->dev = &rdev->dev; + sunxi_rsb_device_set_drvdata(rdev, ac100); + + ac100->regmap = devm_regmap_init_sunxi_rsb(rdev, &ac100_regmap_config); + if (IS_ERR(ac100->regmap)) { + ret = PTR_ERR(ac100->regmap); + dev_err(ac100->dev, "regmap init failed: %d\n", ret); + return ret; + } + + ret = devm_mfd_add_devices(ac100->dev, PLATFORM_DEVID_NONE, ac100_cells, + ARRAY_SIZE(ac100_cells), NULL, 0, NULL); + if (ret) { + dev_err(ac100->dev, "failed to add MFD devices: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct of_device_id ac100_of_match[] = { + { .compatible = "x-powers,ac100" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ac100_of_match); + +static struct sunxi_rsb_driver ac100_rsb_driver = { + .driver = { + .name = "ac100", + .of_match_table = of_match_ptr(ac100_of_match), + }, + .probe = ac100_rsb_probe, +}; +module_sunxi_rsb_driver(ac100_rsb_driver); + +MODULE_DESCRIPTION("Audio codec MFD core driver for AC100"); +MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/act8945a.c b/drivers/mfd/act8945a.c index 10c6d2da8822..a4024d91da01 100644 --- a/drivers/mfd/act8945a.c +++ b/drivers/mfd/act8945a.c @@ -23,6 +23,7 @@ static const struct mfd_cell act8945a_devs[] = { }, { .name = "act8945a-charger", + .of_compatible = "active-semi,act8945a-charger", }, }; diff --git a/drivers/mfd/altera-a10sr.c b/drivers/mfd/altera-a10sr.c index c05aa4ff57fd..06e1f7fc5605 100644 --- a/drivers/mfd/altera-a10sr.c +++ b/drivers/mfd/altera-a10sr.c @@ -1,4 +1,8 @@ /* + * Altera Arria10 DevKit System Resource MFD Driver + * + * Author: Thor Thayer <tthayer@opensource.altera.com> + * * Copyright Intel Corporation (C) 2014-2016. All Rights Reserved * * This program is free software; you can redistribute it and/or modify it @@ -20,7 +24,7 @@ #include <linux/mfd/altera-a10sr.h> #include <linux/mfd/core.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/of.h> #include <linux/spi/spi.h> @@ -94,7 +98,7 @@ static bool altr_a10sr_reg_volatile(struct device *dev, unsigned int reg) } } -const struct regmap_config altr_a10sr_regmap_config = { +static const struct regmap_config altr_a10sr_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -152,7 +156,6 @@ static const struct of_device_id altr_a10sr_spi_of_match[] = { { .compatible = "altr,a10sr" }, { }, }; -MODULE_DEVICE_TABLE(of, altr_a10sr_spi_of_match); static struct spi_driver altr_a10sr_spi_driver = { .probe = altr_a10sr_spi_probe, @@ -161,9 +164,4 @@ static struct spi_driver altr_a10sr_spi_driver = { .of_match_table = of_match_ptr(altr_a10sr_spi_of_match), }, }; - -module_spi_driver(altr_a10sr_spi_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Thor Thayer <tthayer@opensource.altera.com>"); -MODULE_DESCRIPTION("Altera Arria10 DevKit System Resource MFD Driver"); +builtin_driver(altr_a10sr_spi_driver, spi_register_driver) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index e4f97b3c824b..41767f7239bb 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/gpio.h> @@ -49,7 +50,15 @@ int arizona_clk32k_enable(struct arizona *arizona) case ARIZONA_32KZ_MCLK1: ret = pm_runtime_get_sync(arizona->dev); if (ret != 0) - goto out; + goto err_ref; + ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK1]); + if (ret != 0) + goto err_pm; + break; + case ARIZONA_32KZ_MCLK2: + ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK2]); + if (ret != 0) + goto err_ref; break; } @@ -58,7 +67,9 @@ int arizona_clk32k_enable(struct arizona *arizona) ARIZONA_CLK_32K_ENA); } -out: +err_pm: + pm_runtime_put_sync(arizona->dev); +err_ref: if (ret != 0) arizona->clk32k_ref--; @@ -83,6 +94,10 @@ int arizona_clk32k_disable(struct arizona *arizona) switch (arizona->pdata.clk32k_src) { case ARIZONA_32KZ_MCLK1: pm_runtime_put_sync(arizona->dev); + clk_disable_unprepare(arizona->mclk[ARIZONA_MCLK1]); + break; + case ARIZONA_32KZ_MCLK2: + clk_disable_unprepare(arizona->mclk[ARIZONA_MCLK2]); break; } } @@ -735,7 +750,7 @@ static int arizona_suspend(struct device *dev) return 0; } -static int arizona_suspend_late(struct device *dev) +static int arizona_suspend_noirq(struct device *dev) { struct arizona *arizona = dev_get_drvdata(dev); @@ -759,7 +774,7 @@ static int arizona_resume(struct device *dev) { struct arizona *arizona = dev_get_drvdata(dev); - dev_dbg(arizona->dev, "Late resume, reenabling IRQ\n"); + dev_dbg(arizona->dev, "Resume, reenabling IRQ\n"); enable_irq(arizona->irq); return 0; @@ -771,10 +786,8 @@ const struct dev_pm_ops arizona_pm_ops = { arizona_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume) -#ifdef CONFIG_PM_SLEEP - .suspend_late = arizona_suspend_late, - .resume_noirq = arizona_resume_noirq, -#endif + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(arizona_suspend_noirq, + arizona_resume_noirq) }; EXPORT_SYMBOL_GPL(arizona_pm_ops); @@ -790,35 +803,25 @@ unsigned long 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; struct property *prop; const __be32 *cur; u32 val; + u32 pdm_val[ARIZONA_MAX_PDM_SPK]; int ret, i; int count = 0; - pdata->reset = arizona_of_get_named_gpio(arizona, "wlf,reset", true); + pdata->reset = of_get_named_gpio(arizona->dev->of_node, "wlf,reset", 0); + if (pdata->reset == -EPROBE_DEFER) { + return pdata->reset; + } else if (pdata->reset < 0) { + dev_err(arizona->dev, "Reset GPIO missing/malformed: %d\n", + pdata->reset); + + pdata->reset = 0; + } ret = of_property_read_u32_array(arizona->dev->of_node, "wlf,gpio-defaults", @@ -871,6 +874,35 @@ static int arizona_of_get_core_pdata(struct arizona *arizona) count++; } + count = 0; + of_property_for_each_u32(arizona->dev->of_node, + "wlf,max-channels-clocked", + prop, cur, val) { + if (count == ARRAY_SIZE(pdata->max_channels_clocked)) + break; + + pdata->max_channels_clocked[count] = val; + count++; + } + + ret = of_property_read_u32_array(arizona->dev->of_node, + "wlf,spk-fmt", + pdm_val, + ARRAY_SIZE(pdm_val)); + + if (ret >= 0) + for (count = 0; count < ARRAY_SIZE(pdata->spk_fmt); ++count) + pdata->spk_fmt[count] = pdm_val[count]; + + ret = of_property_read_u32_array(arizona->dev->of_node, + "wlf,spk-mute", + pdm_val, + ARRAY_SIZE(pdm_val)); + + if (ret >= 0) + for (count = 0; count < ARRAY_SIZE(pdata->spk_mute); ++count) + pdata->spk_mute[count] = pdm_val[count]; + return 0; } @@ -1000,6 +1032,7 @@ static const struct mfd_cell wm8998_devs[] = { int arizona_dev_init(struct arizona *arizona) { + const char * const mclk_name[] = { "mclk1", "mclk2" }; struct device *dev = arizona->dev; const char *type_name = NULL; unsigned int reg, val, mask; @@ -1010,11 +1043,24 @@ int arizona_dev_init(struct arizona *arizona) dev_set_drvdata(arizona->dev, arizona); mutex_init(&arizona->clk_lock); - if (dev_get_platdata(arizona->dev)) + if (dev_get_platdata(arizona->dev)) { memcpy(&arizona->pdata, dev_get_platdata(arizona->dev), sizeof(arizona->pdata)); - else - arizona_of_get_core_pdata(arizona); + } else { + ret = arizona_of_get_core_pdata(arizona); + if (ret < 0) + return ret; + } + + BUILD_BUG_ON(ARRAY_SIZE(arizona->mclk) != ARRAY_SIZE(mclk_name)); + for (i = 0; i < ARRAY_SIZE(arizona->mclk); i++) { + arizona->mclk[i] = devm_clk_get(arizona->dev, mclk_name[i]); + if (IS_ERR(arizona->mclk[i])) { + dev_info(arizona->dev, "Failed to get %s: %ld\n", + mclk_name[i], PTR_ERR(arizona->mclk[i])); + arizona->mclk[i] = NULL; + } + } regcache_cache_only(arizona->regmap, true); @@ -1035,7 +1081,7 @@ int arizona_dev_init(struct arizona *arizona) default: dev_err(arizona->dev, "Unknown device type %d\n", arizona->type); - return -EINVAL; + return -ENODEV; } /* Mark DCVDD as external, LDO1 driver will clear if internal */ @@ -1121,6 +1167,7 @@ int arizona_dev_init(struct arizona *arizona) break; default: dev_err(arizona->dev, "Unknown device ID: %x\n", reg); + ret = -ENODEV; goto err_reset; } @@ -1280,12 +1327,14 @@ int arizona_dev_init(struct arizona *arizona) break; default: dev_err(arizona->dev, "Unknown device ID %x\n", reg); + ret = -ENODEV; goto err_reset; } if (!subdevs) { dev_err(arizona->dev, "No kernel support for device ID %x\n", reg); + ret = -ENODEV; goto err_reset; } diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c index eca7ea69b81c..4b15b0840f16 100644 --- a/drivers/mfd/atmel-hlcdc.c +++ b/drivers/mfd/atmel-hlcdc.c @@ -50,8 +50,9 @@ static int regmap_atmel_hlcdc_reg_write(void *context, unsigned int reg, if (reg <= ATMEL_HLCDC_DIS) { u32 status; - readl_poll_timeout(hregmap->regs + ATMEL_HLCDC_SR, status, - !(status & ATMEL_HLCDC_SIP), 1, 100); + readl_poll_timeout_atomic(hregmap->regs + ATMEL_HLCDC_SR, + status, !(status & ATMEL_HLCDC_SIP), + 1, 100); } writel(val, hregmap->regs + reg); diff --git a/drivers/mfd/axp20x-rsb.c b/drivers/mfd/axp20x-rsb.c index a407527bcd09..a732cb50bcff 100644 --- a/drivers/mfd/axp20x-rsb.c +++ b/drivers/mfd/axp20x-rsb.c @@ -61,6 +61,7 @@ static int axp20x_rsb_remove(struct sunxi_rsb_device *rdev) static const struct of_device_id axp20x_rsb_of_match[] = { { .compatible = "x-powers,axp223", .data = (void *)AXP223_ID }, + { .compatible = "x-powers,axp806", .data = (void *)AXP806_ID }, { .compatible = "x-powers,axp809", .data = (void *)AXP809_ID }, { }, }; diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index fd80b0981f0f..ba130be32e61 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -38,6 +38,7 @@ static const char * const axp20x_model_names[] = { "AXP221", "AXP223", "AXP288", + "AXP806", "AXP809", }; @@ -129,6 +130,27 @@ static const struct regmap_access_table axp288_volatile_table = { .n_yes_ranges = ARRAY_SIZE(axp288_volatile_ranges), }; +static const struct regmap_range axp806_writeable_ranges[] = { + regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_DATACACHE(3)), + regmap_reg_range(AXP806_PWR_OUT_CTRL1, AXP806_CLDO3_V_CTRL), + regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ2_EN), + regmap_reg_range(AXP20X_IRQ1_STATE, AXP20X_IRQ2_STATE), +}; + +static const struct regmap_range axp806_volatile_ranges[] = { + regmap_reg_range(AXP20X_IRQ1_STATE, AXP20X_IRQ2_STATE), +}; + +static const struct regmap_access_table axp806_writeable_table = { + .yes_ranges = axp806_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(axp806_writeable_ranges), +}; + +static const struct regmap_access_table axp806_volatile_table = { + .yes_ranges = axp806_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(axp806_volatile_ranges), +}; + static struct resource axp152_pek_resources[] = { DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_RIS_EDGE, "PEK_DBR"), DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_FAL_EDGE, "PEK_DBF"), @@ -278,6 +300,15 @@ static const struct regmap_config axp288_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +static const struct regmap_config axp806_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .wr_table = &axp806_writeable_table, + .volatile_table = &axp806_volatile_table, + .max_register = AXP806_VREF_TEMP_WARN_L, + .cache_type = REGCACHE_RBTREE, +}; + #define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \ [_variant##_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) } @@ -409,6 +440,21 @@ static const struct regmap_irq axp288_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1), }; +static const struct regmap_irq axp806_regmap_irqs[] = { + INIT_REGMAP_IRQ(AXP806, DIE_TEMP_HIGH_LV1, 0, 0), + INIT_REGMAP_IRQ(AXP806, DIE_TEMP_HIGH_LV2, 0, 1), + INIT_REGMAP_IRQ(AXP806, DCDCA_V_LOW, 0, 3), + INIT_REGMAP_IRQ(AXP806, DCDCB_V_LOW, 0, 4), + INIT_REGMAP_IRQ(AXP806, DCDCC_V_LOW, 0, 5), + INIT_REGMAP_IRQ(AXP806, DCDCD_V_LOW, 0, 6), + INIT_REGMAP_IRQ(AXP806, DCDCE_V_LOW, 0, 7), + INIT_REGMAP_IRQ(AXP806, PWROK_LONG, 1, 0), + INIT_REGMAP_IRQ(AXP806, PWROK_SHORT, 1, 1), + INIT_REGMAP_IRQ(AXP806, WAKEUP, 1, 4), + INIT_REGMAP_IRQ(AXP806, PWROK_FALL, 1, 5), + INIT_REGMAP_IRQ(AXP806, PWROK_RISE, 1, 6), +}; + static const struct regmap_irq axp809_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP809, ACIN_OVER_V, 0, 7), INIT_REGMAP_IRQ(AXP809, ACIN_PLUGIN, 0, 6), @@ -494,6 +540,18 @@ static const struct regmap_irq_chip axp288_regmap_irq_chip = { }; +static const struct regmap_irq_chip axp806_regmap_irq_chip = { + .name = "axp806", + .status_base = AXP20X_IRQ1_STATE, + .ack_base = AXP20X_IRQ1_STATE, + .mask_base = AXP20X_IRQ1_EN, + .mask_invert = true, + .init_ack_masked = true, + .irqs = axp806_regmap_irqs, + .num_irqs = ARRAY_SIZE(axp806_regmap_irqs), + .num_regs = 2, +}; + static const struct regmap_irq_chip axp809_regmap_irq_chip = { .name = "axp809", .status_base = AXP20X_IRQ1_STATE, @@ -508,6 +566,9 @@ static const struct regmap_irq_chip axp809_regmap_irq_chip = { static struct mfd_cell axp20x_cells[] = { { + .name = "axp20x-gpio", + .of_compatible = "x-powers,axp209-gpio", + }, { .name = "axp20x-pek", .num_resources = ARRAY_SIZE(axp20x_pek_resources), .resources = axp20x_pek_resources, @@ -660,12 +721,20 @@ static struct mfd_cell axp288_cells[] = { }, }; +static struct mfd_cell axp806_cells[] = { + { + .id = 2, + .name = "axp20x-regulator", + }, +}; + static struct mfd_cell axp809_cells[] = { { .name = "axp20x-pek", .num_resources = ARRAY_SIZE(axp809_pek_resources), .resources = axp809_pek_resources, }, { + .id = 1, .name = "axp20x-regulator", }, }; @@ -732,6 +801,12 @@ int axp20x_match_device(struct axp20x_dev *axp20x) axp20x->regmap_cfg = &axp288_regmap_config; axp20x->regmap_irq_chip = &axp288_regmap_irq_chip; break; + case AXP806_ID: + axp20x->nr_cells = ARRAY_SIZE(axp806_cells); + axp20x->cells = axp806_cells; + axp20x->regmap_cfg = &axp806_regmap_config; + axp20x->regmap_irq_chip = &axp806_regmap_irq_chip; + break; case AXP809_ID: axp20x->nr_cells = ARRAY_SIZE(axp809_cells); axp20x->cells = axp809_cells; diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 0eee63542038..abd83424b498 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -23,6 +23,7 @@ #include <linux/module.h> #include <linux/mfd/core.h> #include <linux/mfd/cros_ec.h> +#include <asm/unaligned.h> #define CROS_EC_DEV_EC_INDEX 0 #define CROS_EC_DEV_PD_INDEX 1 @@ -49,11 +50,28 @@ static const struct mfd_cell ec_pd_cell = { .pdata_size = sizeof(pd_p), }; +static irqreturn_t ec_irq_thread(int irq, void *data) +{ + struct cros_ec_device *ec_dev = data; + int ret; + + if (device_may_wakeup(ec_dev->dev)) + pm_wakeup_event(ec_dev->dev, 0); + + ret = cros_ec_get_next_event(ec_dev); + if (ret > 0) + blocking_notifier_call_chain(&ec_dev->event_notifier, + 0, ec_dev); + return IRQ_HANDLED; +} + int cros_ec_register(struct cros_ec_device *ec_dev) { struct device *dev = ec_dev->dev; int err = 0; + BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier); + ec_dev->max_request = sizeof(struct ec_params_hello); ec_dev->max_response = sizeof(struct ec_response_get_protocol_info); ec_dev->max_passthru = 0; @@ -70,13 +88,24 @@ int cros_ec_register(struct cros_ec_device *ec_dev) cros_ec_query_all(ec_dev); + if (ec_dev->irq) { + err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "chromeos-ec", ec_dev); + if (err) { + dev_err(dev, "Failed to request IRQ %d: %d", + ec_dev->irq, err); + return err; + } + } + err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1, NULL, ec_dev->irq, NULL); if (err) { dev_err(dev, "Failed to register Embedded Controller subdevice %d\n", err); - return err; + goto fail_mfd; } if (ec_dev->max_passthru) { @@ -94,7 +123,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev) dev_err(dev, "Failed to register Power Delivery subdevice %d\n", err); - return err; + goto fail_mfd; } } @@ -103,13 +132,18 @@ int cros_ec_register(struct cros_ec_device *ec_dev) if (err) { mfd_remove_devices(dev); dev_err(dev, "Failed to register sub-devices\n"); - return err; + goto fail_mfd; } } dev_info(dev, "Chrome EC device registered\n"); return 0; + +fail_mfd: + if (ec_dev->irq) + free_irq(ec_dev->irq, ec_dev); + return err; } EXPORT_SYMBOL(cros_ec_register); @@ -136,13 +170,31 @@ int cros_ec_suspend(struct cros_ec_device *ec_dev) } EXPORT_SYMBOL(cros_ec_suspend); +static void cros_ec_drain_events(struct cros_ec_device *ec_dev) +{ + while (cros_ec_get_next_event(ec_dev) > 0) + blocking_notifier_call_chain(&ec_dev->event_notifier, + 1, ec_dev); +} + int cros_ec_resume(struct cros_ec_device *ec_dev) { enable_irq(ec_dev->irq); + /* + * In some cases, we need to distinguish between events that occur + * during suspend if the EC is not a wake source. For example, + * keypresses during suspend should be discarded if it does not wake + * the system. + * + * If the EC is not a wake source, drain the event queue and mark them + * as "queued during suspend". + */ if (ec_dev->wake_enabled) { disable_irq_wake(ec_dev->irq); ec_dev->wake_enabled = 0; + } else { + cros_ec_drain_events(ec_dev); } return 0; diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index ebe9b9477cb2..a518832ed5f5 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -366,7 +366,6 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev, struct cros_ec_command *ec_msg) { - struct ec_host_request *request; struct ec_host_response *response; struct cros_ec_spi *ec_spi = ec_dev->priv; struct spi_transfer trans, trans_delay; @@ -378,7 +377,6 @@ static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev, int ret = 0, final_ret; len = cros_ec_prepare_tx(ec_dev, ec_msg); - request = (struct ec_host_request *)ec_dev->dout; dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); /* If it's too soon to do another transaction, wait */ diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index c0bf68a3e614..a88c2065d8ab 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -167,6 +167,7 @@ static bool da9052_reg_writeable(struct device *dev, unsigned int reg) case DA9052_EVENT_B_REG: case DA9052_EVENT_C_REG: case DA9052_EVENT_D_REG: + case DA9052_FAULTLOG_REG: case DA9052_IRQ_MASK_A_REG: case DA9052_IRQ_MASK_B_REG: case DA9052_IRQ_MASK_C_REG: @@ -541,6 +542,52 @@ const struct regmap_config da9052_regmap_config = { }; EXPORT_SYMBOL_GPL(da9052_regmap_config); +static int da9052_clear_fault_log(struct da9052 *da9052) +{ + int ret = 0; + int fault_log = 0; + + fault_log = da9052_reg_read(da9052, DA9052_FAULTLOG_REG); + if (fault_log < 0) { + dev_err(da9052->dev, + "Cannot read FAULT_LOG %d\n", fault_log); + return fault_log; + } + + if (fault_log) { + if (fault_log & DA9052_FAULTLOG_TWDERROR) + dev_dbg(da9052->dev, + "Fault log entry detected: TWD_ERROR\n"); + if (fault_log & DA9052_FAULTLOG_VDDFAULT) + dev_dbg(da9052->dev, + "Fault log entry detected: VDD_FAULT\n"); + if (fault_log & DA9052_FAULTLOG_VDDSTART) + dev_dbg(da9052->dev, + "Fault log entry detected: VDD_START\n"); + if (fault_log & DA9052_FAULTLOG_TEMPOVER) + dev_dbg(da9052->dev, + "Fault log entry detected: TEMP_OVER\n"); + if (fault_log & DA9052_FAULTLOG_KEYSHUT) + dev_dbg(da9052->dev, + "Fault log entry detected: KEY_SHUT\n"); + if (fault_log & DA9052_FAULTLOG_NSDSET) + dev_dbg(da9052->dev, + "Fault log entry detected: nSD_SHUT\n"); + if (fault_log & DA9052_FAULTLOG_WAITSET) + dev_dbg(da9052->dev, + "Fault log entry detected: WAIT_SHUT\n"); + + ret = da9052_reg_write(da9052, + DA9052_FAULTLOG_REG, + 0xFF); + if (ret < 0) + dev_err(da9052->dev, + "Cannot reset FAULT_LOG values %d\n", ret); + } + + return ret; +} + int da9052_device_init(struct da9052 *da9052, u8 chip_id) { struct da9052_pdata *pdata = dev_get_platdata(da9052->dev); @@ -549,6 +596,10 @@ int da9052_device_init(struct da9052 *da9052, u8 chip_id) mutex_init(&da9052->auxadc_lock); init_completion(&da9052->done); + ret = da9052_clear_fault_log(da9052); + if (ret < 0) + dev_warn(da9052->dev, "Cannot clear FAULT_LOG\n"); + if (pdata && pdata->init != NULL) pdata->init(da9052); diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c index af841c165787..6c2870d4e754 100644 --- a/drivers/mfd/da9063-core.c +++ b/drivers/mfd/da9063-core.c @@ -4,8 +4,8 @@ * Copyright 2012 Dialog Semiconductors Ltd. * Copyright 2013 Philipp Zabel, Pengutronix * - * Author: Krystian Garbaciak <krystian.garbaciak@diasemi.com>, - * Michal Hajduk <michal.hajduk@diasemi.com> + * Author: Krystian Garbaciak, Dialog Semiconductor + * Author: Michal Hajduk, Dialog Semiconductor * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -242,5 +242,6 @@ void da9063_device_exit(struct da9063 *da9063) } MODULE_DESCRIPTION("PMIC driver for Dialog DA9063"); -MODULE_AUTHOR("Krystian Garbaciak <krystian.garbaciak@diasemi.com>, Michal Hajduk <michal.hajduk@diasemi.com>"); +MODULE_AUTHOR("Krystian Garbaciak"); +MODULE_AUTHOR("Michal Hajduk"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/da9063-i2c.c b/drivers/mfd/da9063-i2c.c index 73901084945f..981805a2c521 100644 --- a/drivers/mfd/da9063-i2c.c +++ b/drivers/mfd/da9063-i2c.c @@ -3,7 +3,7 @@ * Copyright 2012 Dialog Semiconductor Ltd. * Copyright 2013 Philipp Zabel, Pengutronix * - * Author: Krystian Garbaciak <krystian.garbaciak@diasemi.com> + * Author: Krystian Garbaciak, Dialog Semiconductor * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the diff --git a/drivers/mfd/da9063-irq.c b/drivers/mfd/da9063-irq.c index 7e903fcb8813..207bbfe55449 100644 --- a/drivers/mfd/da9063-irq.c +++ b/drivers/mfd/da9063-irq.c @@ -3,7 +3,7 @@ * Copyright 2012 Dialog Semiconductor Ltd. * Copyright 2013 Philipp Zabel, Pengutronix * - * Author: Michal Hajduk <michal.hajduk@diasemi.com> + * Author: Michal Hajduk, Dialog Semiconductor * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 388e268b9bcf..ca38a6a14110 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -938,25 +938,6 @@ int db8500_prcmu_get_ddr_opp(void) return readb(PRCM_DDR_SUBSYS_APE_MINBW); } -/** - * db8500_set_ddr_opp - set the appropriate DDR OPP - * @opp: The new DDR operating point to which transition is to be made - * Returns: 0 on success, non-zero on failure - * - * This function sets the operating point of the DDR. - */ -static bool enable_set_ddr_opp; -int db8500_prcmu_set_ddr_opp(u8 opp) -{ - if (opp < DDR_100_OPP || opp > DDR_25_OPP) - return -EINVAL; - /* Changing the DDR OPP can hang the hardware pre-v21 */ - if (enable_set_ddr_opp) - writeb(opp, PRCM_DDR_SUBSYS_APE_MINBW); - - return 0; -} - /* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */ static void request_even_slower_clocks(bool enable) { diff --git a/drivers/mfd/dm355evm_msp.c b/drivers/mfd/dm355evm_msp.c index 270e19c0bba1..86eca614507b 100644 --- a/drivers/mfd/dm355evm_msp.c +++ b/drivers/mfd/dm355evm_msp.c @@ -209,7 +209,7 @@ static struct device *add_child(struct i2c_client *client, const char *name, status = platform_device_add_data(pdev, pdata, pdata_len); if (status < 0) { dev_dbg(&pdev->dev, "can't add platform_data\n"); - goto err; + goto put_device; } } @@ -222,19 +222,20 @@ static struct device *add_child(struct i2c_client *client, const char *name, status = platform_device_add_resources(pdev, &r, 1); if (status < 0) { dev_dbg(&pdev->dev, "can't add irq\n"); - goto err; + goto put_device; } } status = platform_device_add(pdev); + if (status) + goto put_device; -err: - if (status < 0) { - platform_device_put(pdev); - dev_err(&client->dev, "can't add %s dev\n", name); - return ERR_PTR(status); - } return &pdev->dev; + +put_device: + platform_device_put(pdev); + dev_err(&client->dev, "failed to add device %s\n", name); + return ERR_PTR(status); } static int add_children(struct i2c_client *client) diff --git a/drivers/mfd/exynos-lpass.c b/drivers/mfd/exynos-lpass.c new file mode 100644 index 000000000000..2e064fb8826f --- /dev/null +++ b/drivers/mfd/exynos-lpass.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2015 - 2016 Samsung Electronics Co., Ltd. + * + * Authors: Inha Song <ideal.song@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * Samsung Exynos SoC series Low Power Audio Subsystem driver. + * + * This module provides regmap for the Top SFR region and instantiates + * devices for IP blocks like DMAC, I2S, UART. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/exynos5-pmu.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/types.h> + +/* LPASS Top register definitions */ +#define SFR_LPASS_CORE_SW_RESET 0x08 +#define LPASS_SB_SW_RESET BIT(11) +#define LPASS_UART_SW_RESET BIT(10) +#define LPASS_PCM_SW_RESET BIT(9) +#define LPASS_I2S_SW_RESET BIT(8) +#define LPASS_WDT1_SW_RESET BIT(4) +#define LPASS_WDT0_SW_RESET BIT(3) +#define LPASS_TIMER_SW_RESET BIT(2) +#define LPASS_MEM_SW_RESET BIT(1) +#define LPASS_DMA_SW_RESET BIT(0) + +#define SFR_LPASS_INTR_CA5_MASK 0x48 +#define SFR_LPASS_INTR_CPU_MASK 0x58 +#define LPASS_INTR_APM BIT(9) +#define LPASS_INTR_MIF BIT(8) +#define LPASS_INTR_TIMER BIT(7) +#define LPASS_INTR_DMA BIT(6) +#define LPASS_INTR_GPIO BIT(5) +#define LPASS_INTR_I2S BIT(4) +#define LPASS_INTR_PCM BIT(3) +#define LPASS_INTR_SLIMBUS BIT(2) +#define LPASS_INTR_UART BIT(1) +#define LPASS_INTR_SFR BIT(0) + +struct exynos_lpass { + /* pointer to the Power Management Unit regmap */ + struct regmap *pmu; + /* pointer to the LPASS TOP regmap */ + struct regmap *top; +}; + +static void exynos_lpass_core_sw_reset(struct exynos_lpass *lpass, int mask) +{ + unsigned int val = 0; + + regmap_read(lpass->top, SFR_LPASS_CORE_SW_RESET, &val); + + val &= ~mask; + regmap_write(lpass->top, SFR_LPASS_CORE_SW_RESET, val); + + usleep_range(100, 150); + + val |= mask; + regmap_write(lpass->top, SFR_LPASS_CORE_SW_RESET, val); +} + +static void exynos_lpass_enable(struct exynos_lpass *lpass) +{ + /* Unmask SFR, DMA and I2S interrupt */ + regmap_write(lpass->top, SFR_LPASS_INTR_CA5_MASK, + LPASS_INTR_SFR | LPASS_INTR_DMA | LPASS_INTR_I2S); + + regmap_write(lpass->top, SFR_LPASS_INTR_CPU_MASK, + LPASS_INTR_SFR | LPASS_INTR_DMA | LPASS_INTR_I2S); + + /* Activate related PADs from retention state */ + regmap_write(lpass->pmu, EXYNOS5433_PAD_RETENTION_AUD_OPTION, + EXYNOS5433_PAD_INITIATE_WAKEUP_FROM_LOWPWR); + + exynos_lpass_core_sw_reset(lpass, LPASS_I2S_SW_RESET); + exynos_lpass_core_sw_reset(lpass, LPASS_DMA_SW_RESET); + exynos_lpass_core_sw_reset(lpass, LPASS_MEM_SW_RESET); +} + +static void exynos_lpass_disable(struct exynos_lpass *lpass) +{ + /* Mask any unmasked IP interrupt sources */ + regmap_write(lpass->top, SFR_LPASS_INTR_CPU_MASK, 0); + regmap_write(lpass->top, SFR_LPASS_INTR_CA5_MASK, 0); + + /* Deactivate related PADs from retention state */ + regmap_write(lpass->pmu, EXYNOS5433_PAD_RETENTION_AUD_OPTION, 0); +} + +static const struct regmap_config exynos_lpass_reg_conf = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xfc, + .fast_io = true, +}; + +static int exynos_lpass_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct exynos_lpass *lpass; + void __iomem *base_top; + struct resource *res; + + lpass = devm_kzalloc(dev, sizeof(*lpass), GFP_KERNEL); + if (!lpass) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base_top = devm_ioremap_resource(dev, res); + if (IS_ERR(base_top)) + return PTR_ERR(base_top); + + lpass->top = regmap_init_mmio(dev, base_top, + &exynos_lpass_reg_conf); + if (IS_ERR(lpass->top)) { + dev_err(dev, "LPASS top regmap initialization failed\n"); + return PTR_ERR(lpass->top); + } + + lpass->pmu = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,pmu-syscon"); + if (IS_ERR(lpass->pmu)) { + dev_err(dev, "Failed to lookup PMU regmap\n"); + return PTR_ERR(lpass->pmu); + } + + platform_set_drvdata(pdev, lpass); + exynos_lpass_enable(lpass); + + return of_platform_populate(dev->of_node, NULL, NULL, dev); +} + +static int __maybe_unused exynos_lpass_suspend(struct device *dev) +{ + struct exynos_lpass *lpass = dev_get_drvdata(dev); + + exynos_lpass_disable(lpass); + + return 0; +} + +static int __maybe_unused exynos_lpass_resume(struct device *dev) +{ + struct exynos_lpass *lpass = dev_get_drvdata(dev); + + exynos_lpass_enable(lpass); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(lpass_pm_ops, exynos_lpass_suspend, + exynos_lpass_resume); + +static const struct of_device_id exynos_lpass_of_match[] = { + { .compatible = "samsung,exynos5433-lpass" }, + { }, +}; +MODULE_DEVICE_TABLE(of, exynos_lpass_of_match); + +static struct platform_driver exynos_lpass_driver = { + .driver = { + .name = "exynos-lpass", + .pm = &lpass_pm_ops, + .of_match_table = exynos_lpass_of_match, + }, + .probe = exynos_lpass_probe, +}; +module_platform_driver(exynos_lpass_driver); + +MODULE_DESCRIPTION("Samsung Low Power Audio Subsystem driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c index 7ddc4a9563ea..6bf8d643d942 100644 --- a/drivers/mfd/intel-lpss-acpi.c +++ b/drivers/mfd/intel-lpss-acpi.c @@ -52,6 +52,18 @@ static const struct intel_lpss_platform_info bxt_i2c_info = { .properties = bxt_i2c_properties, }; +static struct property_entry apl_i2c_properties[] = { + PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 207), + PROPERTY_ENTRY_U32("i2c-sda-falling-time-ns", 171), + PROPERTY_ENTRY_U32("i2c-scl-falling-time-ns", 208), + { }, +}; + +static const struct intel_lpss_platform_info apl_i2c_info = { + .clk_rate = 133000000, + .properties = apl_i2c_properties, +}; + static const struct acpi_device_id intel_lpss_acpi_ids[] = { /* SPT */ { "INT3446", (kernel_ulong_t)&spt_i2c_info }, @@ -61,7 +73,7 @@ static const struct acpi_device_id intel_lpss_acpi_ids[] = { { "80860ABC", (kernel_ulong_t)&bxt_info }, { "80860AC2", (kernel_ulong_t)&bxt_info }, /* APL */ - { "80865AAC", (kernel_ulong_t)&bxt_i2c_info }, + { "80865AAC", (kernel_ulong_t)&apl_i2c_info }, { "80865ABC", (kernel_ulong_t)&bxt_info }, { "80865AC2", (kernel_ulong_t)&bxt_info }, { } diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index 1d79a3c9370f..3228fd182a99 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -111,6 +111,31 @@ static const struct intel_lpss_platform_info bxt_i2c_info = { .properties = bxt_i2c_properties, }; +static struct property_entry apl_i2c_properties[] = { + PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 207), + PROPERTY_ENTRY_U32("i2c-sda-falling-time-ns", 171), + PROPERTY_ENTRY_U32("i2c-scl-falling-time-ns", 208), + { }, +}; + +static const struct intel_lpss_platform_info apl_i2c_info = { + .clk_rate = 133000000, + .properties = apl_i2c_properties, +}; + +static const struct intel_lpss_platform_info kbl_info = { + .clk_rate = 120000000, +}; + +static const struct intel_lpss_platform_info kbl_uart_info = { + .clk_rate = 120000000, + .clk_con_id = "baudclk", +}; + +static const struct intel_lpss_platform_info kbl_i2c_info = { + .clk_rate = 133000000, +}; + static const struct pci_device_id intel_lpss_pci_ids[] = { /* BXT A-Step */ { PCI_VDEVICE(INTEL, 0x0aac), (kernel_ulong_t)&bxt_i2c_info }, @@ -146,14 +171,14 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x1aee), (kernel_ulong_t)&bxt_uart_info }, /* APL */ - { PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5ab0), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5ab2), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5ab4), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5ab6), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5ab8), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x5aba), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5ab0), (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5ab2), (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5ab4), (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5ab6), (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5ab8), (kernel_ulong_t)&apl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x5aba), (kernel_ulong_t)&apl_i2c_info }, { PCI_VDEVICE(INTEL, 0x5abc), (kernel_ulong_t)&bxt_uart_info }, { PCI_VDEVICE(INTEL, 0x5abe), (kernel_ulong_t)&bxt_uart_info }, { PCI_VDEVICE(INTEL, 0x5ac0), (kernel_ulong_t)&bxt_uart_info }, @@ -181,6 +206,16 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0xa160), (kernel_ulong_t)&spt_i2c_info }, { PCI_VDEVICE(INTEL, 0xa161), (kernel_ulong_t)&spt_i2c_info }, { PCI_VDEVICE(INTEL, 0xa166), (kernel_ulong_t)&spt_uart_info }, + /* KBL-H */ + { PCI_VDEVICE(INTEL, 0xa2a7), (kernel_ulong_t)&kbl_uart_info }, + { PCI_VDEVICE(INTEL, 0xa2a8), (kernel_ulong_t)&kbl_uart_info }, + { PCI_VDEVICE(INTEL, 0xa2a9), (kernel_ulong_t)&kbl_info }, + { PCI_VDEVICE(INTEL, 0xa2aa), (kernel_ulong_t)&kbl_info }, + { PCI_VDEVICE(INTEL, 0xa2e0), (kernel_ulong_t)&kbl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa2e1), (kernel_ulong_t)&kbl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa2e2), (kernel_ulong_t)&kbl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa2e3), (kernel_ulong_t)&kbl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa2e6), (kernel_ulong_t)&kbl_uart_info }, { } }; MODULE_DEVICE_TABLE(pci, intel_lpss_pci_ids); diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c index 25d486c543cb..2017446c5b4b 100644 --- a/drivers/mfd/intel_msic.c +++ b/drivers/mfd/intel_msic.c @@ -12,7 +12,7 @@ #include <linux/err.h> #include <linux/gpio.h> #include <linux/io.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/mfd/core.h> #include <linux/mfd/intel_msic.h> #include <linux/platform_device.h> @@ -449,9 +449,4 @@ static struct platform_driver intel_msic_driver = { .name = "intel_msic", }, }; - -module_platform_driver(intel_msic_driver); - -MODULE_DESCRIPTION("Driver for Intel MSIC"); -MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); -MODULE_LICENSE("GPL"); +builtin_platform_driver(intel_msic_driver); diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c index b9428767e615..43e54b7e908f 100644 --- a/drivers/mfd/intel_soc_pmic_bxtwc.c +++ b/drivers/mfd/intel_soc_pmic_bxtwc.c @@ -47,6 +47,8 @@ #define BXTWC_MIRQLVL1 0x4E0E #define BXTWC_MPWRTNIRQ 0x4E0F +#define BXTWC_MIRQLVL1_MCHGR BIT(5) + #define BXTWC_MTHRM0IRQ 0x4E12 #define BXTWC_MTHRM1IRQ 0x4E13 #define BXTWC_MTHRM2IRQ 0x4E14 @@ -109,7 +111,7 @@ static const struct regmap_irq bxtwc_regmap_irqs_level2[] = { REGMAP_IRQ_REG(BXTWC_THRM2_IRQ, 2, 0xff), REGMAP_IRQ_REG(BXTWC_BCU_IRQ, 3, 0x1f), REGMAP_IRQ_REG(BXTWC_ADC_IRQ, 4, 0xff), - REGMAP_IRQ_REG(BXTWC_CHGR0_IRQ, 5, 0x1f), + REGMAP_IRQ_REG(BXTWC_CHGR0_IRQ, 5, 0x3f), REGMAP_IRQ_REG(BXTWC_CHGR1_IRQ, 6, 0x1f), REGMAP_IRQ_REG(BXTWC_GPIO0_IRQ, 7, 0xff), REGMAP_IRQ_REG(BXTWC_GPIO1_IRQ, 8, 0x3f), @@ -143,6 +145,10 @@ static struct resource adc_resources[] = { DEFINE_RES_IRQ_NAMED(BXTWC_ADC_IRQ, "ADC"), }; +static struct resource usbc_resources[] = { + DEFINE_RES_IRQ_NAMED(BXTWC_CHGR0_IRQ, "USBC"), +}; + static struct resource charger_resources[] = { DEFINE_RES_IRQ_NAMED(BXTWC_CHGR0_IRQ, "CHARGER"), DEFINE_RES_IRQ_NAMED(BXTWC_CHGR1_IRQ, "CHARGER1"), @@ -170,6 +176,11 @@ static struct mfd_cell bxt_wc_dev[] = { .resources = thermal_resources, }, { + .name = "bxt_wcove_usbc", + .num_resources = ARRAY_SIZE(usbc_resources), + .resources = usbc_resources, + }, + { .name = "bxt_wcove_ext_charger", .num_resources = ARRAY_SIZE(charger_resources), .resources = charger_resources, @@ -403,6 +414,16 @@ static int bxtwc_probe(struct platform_device *pdev) goto err_sysfs; } + /* + * There is known hw bug. Upon reset BIT 5 of register + * BXTWC_CHGR_LVL1_IRQ is 0 which is the expected value. However, + * later it's set to 1(masked) automatically by hardware. So we + * have the software workaround here to unmaksed it in order to let + * charger interrutp work. + */ + regmap_update_bits(pmic->regmap, BXTWC_MIRQLVL1, + BXTWC_MIRQLVL1_MCHGR, 0); + return 0; err_sysfs: diff --git a/drivers/mfd/lp873x.c b/drivers/mfd/lp873x.c index 9af064c940ee..873c608e6a5d 100644 --- a/drivers/mfd/lp873x.c +++ b/drivers/mfd/lp873x.c @@ -53,8 +53,6 @@ static int lp873x_probe(struct i2c_client *client, return ret; } - mutex_init(&lp873->lock); - ret = regmap_read(lp873->regmap, LP873X_REG_OTP_REV, &otpid); if (ret) { dev_err(lp873->dev, "Failed to read OTP ID\n"); diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index 6c245128ab2e..6cbe96b28f42 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -3,7 +3,7 @@ * * Copyright (C) 2014 Samsung Electronics * Chanwoo Choi <cw00.choi@samsung.com> - * Krzysztof Kozlowski <k.kozlowski@samsung.com> + * Krzysztof Kozlowski <krzk@kernel.org> * * 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 @@ -569,6 +569,6 @@ 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_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <krzk@kernel.org>"); MODULE_DESCRIPTION("Maxim 14577/77836 multi-function core driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c index b95a46d79b9d..326f17b632a7 100644 --- a/drivers/mfd/max8997-irq.c +++ b/drivers/mfd/max8997-irq.c @@ -139,7 +139,7 @@ static void max8997_irq_sync_unlock(struct irq_data *data) mutex_unlock(&max8997->irqlock); } -static const inline struct max8997_irq_data * +inline static const struct max8997_irq_data * irq_to_max8997_irq(struct max8997_dev *max8997, struct irq_data *data) { return &max8997_irqs[data->hwirq]; diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 1d924d1533c0..7aab376ecb84 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -162,7 +162,7 @@ static const char * const port_modes[] = { * provided port mode string as per the port_modes table. * If no match is found it returns -ENODEV */ -static const int omap_usbhs_get_dt_port_mode(const char *mode) +static int omap_usbhs_get_dt_port_mode(const char *mode) { int i; diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index 1b7ec0870c2a..0e3a2ea25942 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -309,6 +309,7 @@ static const struct regmap_config ssbi_regmap_config = { }; static const struct of_device_id pm8921_id_table[] = { + { .compatible = "qcom,pm8018", }, { .compatible = "qcom,pm8058", }, { .compatible = "qcom,pm8921", }, { } diff --git a/drivers/mfd/qcom_rpm.c b/drivers/mfd/qcom_rpm.c index 2e44323455dd..52fafea06067 100644 --- a/drivers/mfd/qcom_rpm.c +++ b/drivers/mfd/qcom_rpm.c @@ -21,6 +21,7 @@ #include <linux/mfd/qcom_rpm.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> +#include <linux/clk.h> #include <dt-bindings/mfd/qcom-rpm.h> @@ -48,6 +49,7 @@ struct qcom_rpm { struct regmap *ipc_regmap; unsigned ipc_offset; unsigned ipc_bit; + struct clk *ramclk; struct completion ack; struct mutex lock; @@ -388,11 +390,62 @@ static const struct qcom_rpm_data ipq806x_template = { .ack_sel_size = 7, }; +static const struct qcom_rpm_resource mdm9615_rpm_resource_table[] = { + [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 }, + [QCOM_RPM_SYS_FABRIC_CLK] = { 26, 10, 9, 1 }, + [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 27, 11, 11, 1 }, + [QCOM_RPM_SFPB_CLK] = { 28, 12, 12, 1 }, + [QCOM_RPM_CFPB_CLK] = { 29, 13, 13, 1 }, + [QCOM_RPM_EBI1_CLK] = { 30, 14, 16, 1 }, + [QCOM_RPM_APPS_FABRIC_HALT] = { 31, 15, 22, 2 }, + [QCOM_RPM_APPS_FABRIC_MODE] = { 33, 16, 23, 3 }, + [QCOM_RPM_APPS_FABRIC_IOCTL] = { 36, 17, 24, 1 }, + [QCOM_RPM_APPS_FABRIC_ARB] = { 37, 18, 25, 27 }, + [QCOM_RPM_PM8018_SMPS1] = { 64, 19, 30, 2 }, + [QCOM_RPM_PM8018_SMPS2] = { 66, 21, 31, 2 }, + [QCOM_RPM_PM8018_SMPS3] = { 68, 23, 32, 2 }, + [QCOM_RPM_PM8018_SMPS4] = { 70, 25, 33, 2 }, + [QCOM_RPM_PM8018_SMPS5] = { 72, 27, 34, 2 }, + [QCOM_RPM_PM8018_LDO1] = { 74, 29, 35, 2 }, + [QCOM_RPM_PM8018_LDO2] = { 76, 31, 36, 2 }, + [QCOM_RPM_PM8018_LDO3] = { 78, 33, 37, 2 }, + [QCOM_RPM_PM8018_LDO4] = { 80, 35, 38, 2 }, + [QCOM_RPM_PM8018_LDO5] = { 82, 37, 39, 2 }, + [QCOM_RPM_PM8018_LDO6] = { 84, 39, 40, 2 }, + [QCOM_RPM_PM8018_LDO7] = { 86, 41, 41, 2 }, + [QCOM_RPM_PM8018_LDO8] = { 88, 43, 42, 2 }, + [QCOM_RPM_PM8018_LDO9] = { 90, 45, 43, 2 }, + [QCOM_RPM_PM8018_LDO10] = { 92, 47, 44, 2 }, + [QCOM_RPM_PM8018_LDO11] = { 94, 49, 45, 2 }, + [QCOM_RPM_PM8018_LDO12] = { 96, 51, 46, 2 }, + [QCOM_RPM_PM8018_LDO13] = { 98, 53, 47, 2 }, + [QCOM_RPM_PM8018_LDO14] = { 100, 55, 48, 2 }, + [QCOM_RPM_PM8018_LVS1] = { 102, 57, 49, 1 }, + [QCOM_RPM_PM8018_NCP] = { 103, 58, 80, 2 }, + [QCOM_RPM_CXO_BUFFERS] = { 105, 60, 81, 1 }, + [QCOM_RPM_USB_OTG_SWITCH] = { 106, 61, 82, 1 }, + [QCOM_RPM_HDMI_SWITCH] = { 107, 62, 83, 1 }, + [QCOM_RPM_VOLTAGE_CORNER] = { 109, 64, 87, 1 }, +}; + +static const struct qcom_rpm_data mdm9615_template = { + .version = 3, + .resource_table = mdm9615_rpm_resource_table, + .n_resources = ARRAY_SIZE(mdm9615_rpm_resource_table), + .req_ctx_off = 3, + .req_sel_off = 11, + .ack_ctx_off = 15, + .ack_sel_off = 23, + .req_sel_size = 4, + .ack_sel_size = 7, +}; + static const struct of_device_id qcom_rpm_of_match[] = { { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template }, { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template }, { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template }, { .compatible = "qcom,rpm-ipq8064", .data = &ipq806x_template }, + { .compatible = "qcom,rpm-mdm9615", .data = &mdm9615_template }, { } }; MODULE_DEVICE_TABLE(of, qcom_rpm_of_match); @@ -501,6 +554,20 @@ static int qcom_rpm_probe(struct platform_device *pdev) mutex_init(&rpm->lock); init_completion(&rpm->ack); + /* Enable message RAM clock */ + rpm->ramclk = devm_clk_get(&pdev->dev, "ram"); + if (IS_ERR(rpm->ramclk)) { + ret = PTR_ERR(rpm->ramclk); + if (ret == -EPROBE_DEFER) + return ret; + /* + * Fall through in all other cases, as the clock is + * optional. (Does not exist on all platforms.) + */ + rpm->ramclk = NULL; + } + clk_prepare_enable(rpm->ramclk); /* Accepts NULL */ + irq_ack = platform_get_irq_byname(pdev, "ack"); if (irq_ack < 0) { dev_err(&pdev->dev, "required ack interrupt missing\n"); @@ -538,6 +605,7 @@ static int qcom_rpm_probe(struct platform_device *pdev) } rpm->ipc_regmap = syscon_node_to_regmap(syscon_np); + of_node_put(syscon_np); if (IS_ERR(rpm->ipc_regmap)) return PTR_ERR(rpm->ipc_regmap); @@ -620,7 +688,11 @@ static int qcom_rpm_probe(struct platform_device *pdev) static int qcom_rpm_remove(struct platform_device *pdev) { + struct qcom_rpm *rpm = dev_get_drvdata(&pdev->dev); + of_platform_depopulate(&pdev->dev); + clk_disable_unprepare(rpm->ramclk); + return 0; } diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index 49d7f624fc94..0f8acc5882a4 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -1,11 +1,15 @@ /* - * MFD core driver for Rockchip RK808 + * MFD core driver for Rockchip RK808/RK818 * * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd * * Author: Chris Zhong <zyw@rock-chips.com> * Author: Zhang Qing <zhangqing@rock-chips.com> * + * Copyright (C) 2016 PHYTEC Messtechnik GmbH + * + * Author: Wadim Egorov <w.egorov@phytec.de> + * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. @@ -21,6 +25,7 @@ #include <linux/mfd/rk808.h> #include <linux/mfd/core.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/regmap.h> struct rk808_reg_data { @@ -57,6 +62,14 @@ static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg) return false; } +static const struct regmap_config rk818_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RK818_USB_CTRL_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = rk808_is_volatile_reg, +}; + static const struct regmap_config rk808_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -79,11 +92,21 @@ static const struct mfd_cell rk808s[] = { { .name = "rk808-rtc", .num_resources = ARRAY_SIZE(rtc_resources), - .resources = &rtc_resources[0], + .resources = rtc_resources, }, }; -static const struct rk808_reg_data pre_init_reg[] = { +static const struct mfd_cell rk818s[] = { + { .name = "rk808-clkout", }, + { .name = "rk808-regulator", }, + { + .name = "rk808-rtc", + .num_resources = ARRAY_SIZE(rtc_resources), + .resources = rtc_resources, + }, +}; + +static const struct rk808_reg_data rk808_pre_init_reg[] = { { RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_150MA }, { RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_200MA }, { RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA }, @@ -94,6 +117,24 @@ static const struct rk808_reg_data pre_init_reg[] = { VB_LO_SEL_3500MV }, }; +static const struct rk808_reg_data rk818_pre_init_reg[] = { + /* improve efficiency */ + { RK818_BUCK2_CONFIG_REG, BUCK2_RATE_MASK, BUCK_ILMIN_250MA }, + { RK818_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_250MA }, + { RK818_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA }, + { RK818_USB_CTRL_REG, RK818_USB_ILIM_SEL_MASK, + RK818_USB_ILMIN_2000MA }, + /* close charger when usb lower then 3.4V */ + { RK818_USB_CTRL_REG, RK818_USB_CHG_SD_VSEL_MASK, + (0x7 << 4) }, + /* no action when vref */ + { RK818_H5V_EN_REG, BIT(1), RK818_REF_RDY_CTRL }, + /* enable HDMI 5V */ + { RK818_H5V_EN_REG, BIT(0), RK818_H5V_EN }, + { RK808_VB_MON_REG, MASK_ALL, VB_LO_ACT | + VB_LO_SEL_3500MV }, +}; + static const struct regmap_irq rk808_irqs[] = { /* INT_STS */ [RK808_IRQ_VOUT_LO] = { @@ -136,6 +177,76 @@ static const struct regmap_irq rk808_irqs[] = { }, }; +static const struct regmap_irq rk818_irqs[] = { + /* INT_STS */ + [RK818_IRQ_VOUT_LO] = { + .mask = RK818_IRQ_VOUT_LO_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_VB_LO] = { + .mask = RK818_IRQ_VB_LO_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_PWRON] = { + .mask = RK818_IRQ_PWRON_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_PWRON_LP] = { + .mask = RK818_IRQ_PWRON_LP_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_HOTDIE] = { + .mask = RK818_IRQ_HOTDIE_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_RTC_ALARM] = { + .mask = RK818_IRQ_RTC_ALARM_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_RTC_PERIOD] = { + .mask = RK818_IRQ_RTC_PERIOD_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_USB_OV] = { + .mask = RK818_IRQ_USB_OV_MSK, + .reg_offset = 0, + }, + + /* INT_STS2 */ + [RK818_IRQ_PLUG_IN] = { + .mask = RK818_IRQ_PLUG_IN_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_PLUG_OUT] = { + .mask = RK818_IRQ_PLUG_OUT_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_CHG_OK] = { + .mask = RK818_IRQ_CHG_OK_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_CHG_TE] = { + .mask = RK818_IRQ_CHG_TE_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_CHG_TS1] = { + .mask = RK818_IRQ_CHG_TS1_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_TS2] = { + .mask = RK818_IRQ_TS2_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_CHG_CVTLIM] = { + .mask = RK818_IRQ_CHG_CVTLIM_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_DISCHG_ILIM] = { + .mask = RK818_IRQ_DISCHG_ILIM_MSK, + .reg_offset = 1, + }, +}; + static struct regmap_irq_chip rk808_irq_chip = { .name = "rk808", .irqs = rk808_irqs, @@ -148,6 +259,18 @@ static struct regmap_irq_chip rk808_irq_chip = { .init_ack_masked = true, }; +static struct regmap_irq_chip rk818_irq_chip = { + .name = "rk818", + .irqs = rk818_irqs, + .num_irqs = ARRAY_SIZE(rk818_irqs), + .num_regs = 2, + .irq_reg_stride = 2, + .status_base = RK818_INT_STS_REG1, + .mask_base = RK818_INT_STS_MSK_REG1, + .ack_base = RK818_INT_STS_REG1, + .init_ack_masked = true, +}; + static struct i2c_client *rk808_i2c_client; static void rk808_device_shutdown(void) { @@ -167,55 +290,100 @@ static void rk808_device_shutdown(void) dev_err(&rk808_i2c_client->dev, "power off error!\n"); } +static const struct of_device_id rk808_of_match[] = { + { .compatible = "rockchip,rk808" }, + { .compatible = "rockchip,rk818" }, + { }, +}; +MODULE_DEVICE_TABLE(of, rk808_of_match); + static int rk808_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device_node *np = client->dev.of_node; struct rk808 *rk808; + const struct rk808_reg_data *pre_init_reg; + const struct mfd_cell *cells; + int nr_pre_init_regs; + int nr_cells; int pm_off = 0; int ret; int i; - if (!client->irq) { - dev_err(&client->dev, "No interrupt support, no core IRQ\n"); - return -EINVAL; - } - rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL); if (!rk808) return -ENOMEM; - rk808->regmap = devm_regmap_init_i2c(client, &rk808_regmap_config); + rk808->variant = i2c_smbus_read_word_data(client, RK808_ID_MSB); + if (rk808->variant < 0) { + dev_err(&client->dev, "Failed to read the chip id at 0x%02x\n", + RK808_ID_MSB); + return rk808->variant; + } + + dev_dbg(&client->dev, "Chip id: 0x%x\n", (unsigned int)rk808->variant); + + switch (rk808->variant) { + case RK808_ID: + rk808->regmap_cfg = &rk808_regmap_config; + rk808->regmap_irq_chip = &rk808_irq_chip; + pre_init_reg = rk808_pre_init_reg; + nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg); + cells = rk808s; + nr_cells = ARRAY_SIZE(rk808s); + break; + case RK818_ID: + rk808->regmap_cfg = &rk818_regmap_config; + rk808->regmap_irq_chip = &rk818_irq_chip; + pre_init_reg = rk818_pre_init_reg; + nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg); + cells = rk818s; + nr_cells = ARRAY_SIZE(rk818s); + break; + default: + dev_err(&client->dev, "Unsupported RK8XX ID %lu\n", + rk808->variant); + return -EINVAL; + } + + rk808->i2c = client; + i2c_set_clientdata(client, rk808); + + rk808->regmap = devm_regmap_init_i2c(client, rk808->regmap_cfg); if (IS_ERR(rk808->regmap)) { dev_err(&client->dev, "regmap initialization failed\n"); return PTR_ERR(rk808->regmap); } - for (i = 0; i < ARRAY_SIZE(pre_init_reg); i++) { - ret = regmap_update_bits(rk808->regmap, pre_init_reg[i].addr, - pre_init_reg[i].mask, - pre_init_reg[i].value); - if (ret) { - dev_err(&client->dev, - "0x%x write err\n", pre_init_reg[i].addr); - return ret; - } + if (!client->irq) { + dev_err(&client->dev, "No interrupt support, no core IRQ\n"); + return -EINVAL; } ret = regmap_add_irq_chip(rk808->regmap, client->irq, IRQF_ONESHOT, -1, - &rk808_irq_chip, &rk808->irq_data); + rk808->regmap_irq_chip, &rk808->irq_data); if (ret) { dev_err(&client->dev, "Failed to add irq_chip %d\n", ret); return ret; } - rk808->i2c = client; - i2c_set_clientdata(client, rk808); + for (i = 0; i < nr_pre_init_regs; i++) { + ret = regmap_update_bits(rk808->regmap, + pre_init_reg[i].addr, + pre_init_reg[i].mask, + pre_init_reg[i].value); + if (ret) { + dev_err(&client->dev, + "0x%x write err\n", + pre_init_reg[i].addr); + return ret; + } + } - ret = devm_mfd_add_devices(&client->dev, -1, - rk808s, ARRAY_SIZE(rk808s), NULL, 0, - regmap_irq_get_domain(rk808->irq_data)); + ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, + cells, nr_cells, NULL, 0, + regmap_irq_get_domain(rk808->irq_data)); if (ret) { dev_err(&client->dev, "failed to add MFD devices %d\n", ret); goto err_irq; @@ -245,14 +413,9 @@ static int rk808_remove(struct i2c_client *client) return 0; } -static const struct of_device_id rk808_of_match[] = { - { .compatible = "rockchip,rk808" }, - { }, -}; -MODULE_DEVICE_TABLE(of, rk808_of_match); - static const struct i2c_device_id rk808_ids[] = { { "rk808" }, + { "rk818" }, { }, }; MODULE_DEVICE_TABLE(i2c, rk808_ids); @@ -272,4 +435,5 @@ module_i2c_driver(rk808_i2c_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>"); MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>"); -MODULE_DESCRIPTION("RK808 PMIC driver"); +MODULE_AUTHOR("Wadim Egorov <w.egorov@phytec.de>"); +MODULE_DESCRIPTION("RK808/RK818 PMIC driver"); diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c index dbd907d7170e..691dab791f7a 100644 --- a/drivers/mfd/rtsx_usb.c +++ b/drivers/mfd/rtsx_usb.c @@ -46,9 +46,6 @@ static void rtsx_usb_sg_timed_out(unsigned long data) dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__); usb_sg_cancel(&ucr->current_sg); - - /* we know the cancellation is caused by time-out */ - ucr->current_sg.status = -ETIMEDOUT; } static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr, @@ -67,12 +64,15 @@ 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_sync(&ucr->sg_timer); + if (!del_timer_sync(&ucr->sg_timer)) + ret = -ETIMEDOUT; + else + ret = ucr->current_sg.status; if (act_len) *act_len = ucr->current_sg.bytes; - return ucr->current_sg.status; + return ret; } int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe, diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 65cd0d2a822a..40534352e574 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1001,7 +1001,7 @@ static int sm501_gpio_output(struct gpio_chip *chip, return 0; } -static struct gpio_chip gpio_chip_template = { +static const struct gpio_chip gpio_chip_template = { .ngpio = 32, .direction_input = sm501_gpio_input, .direction_output = sm501_gpio_output, diff --git a/drivers/mfd/smsc-ece1099.c b/drivers/mfd/smsc-ece1099.c index cd18c09827ef..1f40baf1234e 100644 --- a/drivers/mfd/smsc-ece1099.c +++ b/drivers/mfd/smsc-ece1099.c @@ -11,8 +11,7 @@ * */ -#include <linux/module.h> -#include <linux/moduleparam.h> +#include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/gpio.h> @@ -81,7 +80,6 @@ static const struct i2c_device_id smsc_i2c_id[] = { { "smscece1099", 0}, {}, }; -MODULE_DEVICE_TABLE(i2c, smsc_i2c_id); static struct i2c_driver smsc_i2c_driver = { .driver = { @@ -90,9 +88,4 @@ static struct i2c_driver smsc_i2c_driver = { .probe = smsc_i2c_probe, .id_table = smsc_i2c_id, }; - -module_i2c_driver(smsc_i2c_driver); - -MODULE_AUTHOR("Sourav Poddar <sourav.poddar@ti.com>"); -MODULE_DESCRIPTION("SMSC chip multi-function driver"); -MODULE_LICENSE("GPL v2"); +builtin_i2c_driver(smsc_i2c_driver); diff --git a/drivers/mfd/sun6i-prcm.c b/drivers/mfd/sun6i-prcm.c index 191173166d65..011fcc555945 100644 --- a/drivers/mfd/sun6i-prcm.c +++ b/drivers/mfd/sun6i-prcm.c @@ -9,7 +9,7 @@ */ #include <linux/mfd/core.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/of.h> struct prcm_data { @@ -170,8 +170,4 @@ static struct platform_driver sun6i_prcm_driver = { }, .probe = sun6i_prcm_probe, }; -module_platform_driver(sun6i_prcm_driver); - -MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>"); -MODULE_DESCRIPTION("Allwinner sun6i PRCM driver"); -MODULE_LICENSE("GPL v2"); +builtin_platform_driver(sun6i_prcm_driver); diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index 049a6fcac651..9a4d8684dd32 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -15,22 +15,103 @@ * GNU General Public License for more details. */ -#include <linux/kernel.h> #include <linux/device.h> -#include <linux/module.h> -#include <linux/platform_device.h> +#include <linux/err.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/i2c.h> -#include <linux/slab.h> -#include <linux/regmap.h> -#include <linux/err.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> #include <linux/mfd/core.h> #include <linux/mfd/tps65217.h> -static const struct mfd_cell tps65217s[] = { +static struct resource charger_resources[] = { + DEFINE_RES_IRQ_NAMED(TPS65217_IRQ_AC, "AC"), + DEFINE_RES_IRQ_NAMED(TPS65217_IRQ_USB, "USB"), +}; + +static struct resource pb_resources[] = { + DEFINE_RES_IRQ_NAMED(TPS65217_IRQ_PB, "PB"), +}; + +struct tps65217_irq { + int mask; + int interrupt; +}; + +static const struct tps65217_irq tps65217_irqs[] = { + [TPS65217_IRQ_PB] = { + .mask = TPS65217_INT_PBM, + .interrupt = TPS65217_INT_PBI, + }, + [TPS65217_IRQ_AC] = { + .mask = TPS65217_INT_ACM, + .interrupt = TPS65217_INT_ACI, + }, + [TPS65217_IRQ_USB] = { + .mask = TPS65217_INT_USBM, + .interrupt = TPS65217_INT_USBI, + }, +}; + +static void tps65217_irq_lock(struct irq_data *data) +{ + struct tps65217 *tps = irq_data_get_irq_chip_data(data); + + mutex_lock(&tps->irq_lock); +} + +static void tps65217_irq_sync_unlock(struct irq_data *data) +{ + struct tps65217 *tps = irq_data_get_irq_chip_data(data); + int ret; + + ret = tps65217_reg_write(tps, TPS65217_REG_INT, tps->irq_mask, + TPS65217_PROTECT_NONE); + if (ret != 0) + dev_err(tps->dev, "Failed to sync IRQ masks\n"); + + mutex_unlock(&tps->irq_lock); +} + +static inline const struct tps65217_irq * +irq_to_tps65217_irq(struct tps65217 *tps, struct irq_data *data) +{ + return &tps65217_irqs[data->hwirq]; +} + +static void tps65217_irq_enable(struct irq_data *data) +{ + struct tps65217 *tps = irq_data_get_irq_chip_data(data); + const struct tps65217_irq *irq_data = irq_to_tps65217_irq(tps, data); + + tps->irq_mask &= ~irq_data->mask; +} + +static void tps65217_irq_disable(struct irq_data *data) +{ + struct tps65217 *tps = irq_data_get_irq_chip_data(data); + const struct tps65217_irq *irq_data = irq_to_tps65217_irq(tps, data); + + tps->irq_mask |= irq_data->mask; +} + +static struct irq_chip tps65217_irq_chip = { + .irq_bus_lock = tps65217_irq_lock, + .irq_bus_sync_unlock = tps65217_irq_sync_unlock, + .irq_enable = tps65217_irq_enable, + .irq_disable = tps65217_irq_disable, +}; + +static struct mfd_cell tps65217s[] = { { .name = "tps65217-pmic", .of_compatible = "ti,tps65217-pmic", @@ -41,10 +122,96 @@ static const struct mfd_cell tps65217s[] = { }, { .name = "tps65217-charger", + .num_resources = ARRAY_SIZE(charger_resources), + .resources = charger_resources, .of_compatible = "ti,tps65217-charger", }, + { + .name = "tps65217-pwrbutton", + .num_resources = ARRAY_SIZE(pb_resources), + .resources = pb_resources, + .of_compatible = "ti,tps65217-pwrbutton", + }, +}; + +static irqreturn_t tps65217_irq_thread(int irq, void *data) +{ + struct tps65217 *tps = data; + unsigned int status; + bool handled = false; + int i; + int ret; + + ret = tps65217_reg_read(tps, TPS65217_REG_INT, &status); + if (ret < 0) { + dev_err(tps->dev, "Failed to read IRQ status: %d\n", + ret); + return IRQ_NONE; + } + + for (i = 0; i < ARRAY_SIZE(tps65217_irqs); i++) { + if (status & tps65217_irqs[i].interrupt) { + handle_nested_irq(irq_find_mapping(tps->irq_domain, i)); + handled = true; + } + } + + if (handled) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +static int tps65217_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct tps65217 *tps = h->host_data; + + irq_set_chip_data(virq, tps); + irq_set_chip_and_handler(virq, &tps65217_irq_chip, handle_edge_irq); + irq_set_nested_thread(virq, 1); + irq_set_parent(virq, tps->irq); + irq_set_noprobe(virq); + + return 0; +} + +static const struct irq_domain_ops tps65217_irq_domain_ops = { + .map = tps65217_irq_map, }; +static int tps65217_irq_init(struct tps65217 *tps, int irq) +{ + int ret; + + mutex_init(&tps->irq_lock); + tps->irq = irq; + + /* Mask all interrupt sources */ + tps->irq_mask = (TPS65217_INT_RESERVEDM | TPS65217_INT_PBM + | TPS65217_INT_ACM | TPS65217_INT_USBM); + tps65217_reg_write(tps, TPS65217_REG_INT, tps->irq_mask, + TPS65217_PROTECT_NONE); + + tps->irq_domain = irq_domain_add_linear(tps->dev->of_node, + TPS65217_NUM_IRQ, &tps65217_irq_domain_ops, tps); + if (!tps->irq_domain) { + dev_err(tps->dev, "Could not create IRQ domain\n"); + return -ENOMEM; + } + + ret = devm_request_threaded_irq(tps->dev, irq, NULL, + tps65217_irq_thread, IRQF_ONESHOT, + "tps65217-irq", tps); + if (ret) { + dev_err(tps->dev, "Failed to request IRQ %d: %d\n", + irq, ret); + return ret; + } + + return 0; +} + /** * tps65217_reg_read: Read a single tps65217 register. * @@ -149,11 +316,22 @@ int tps65217_clear_bits(struct tps65217 *tps, unsigned int reg, } EXPORT_SYMBOL_GPL(tps65217_clear_bits); +static bool tps65217_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS65217_REG_INT: + return true; + default: + return false; + } +} + static const struct regmap_config tps65217_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = TPS65217_REG_MAX, + .volatile_reg = tps65217_volatile_reg, }; static const struct of_device_id tps65217_of_match[] = { @@ -205,8 +383,19 @@ static int tps65217_probe(struct i2c_client *client, return ret; } + if (client->irq) { + tps65217_irq_init(tps, client->irq); + } else { + int i; + + /* Don't tell children about IRQ resources which won't fire */ + for (i = 0; i < ARRAY_SIZE(tps65217s); i++) + tps65217s[i].num_resources = 0; + } + ret = devm_mfd_add_devices(tps->dev, -1, tps65217s, - ARRAY_SIZE(tps65217s), NULL, 0, NULL); + ARRAY_SIZE(tps65217s), NULL, 0, + tps->irq_domain); if (ret < 0) { dev_err(tps->dev, "mfd_add_devices failed: %d\n", ret); return ret; diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index a49d3db6d936..c64615dca2bd 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -30,7 +30,6 @@ #include <linux/init.h> #include <linux/mutex.h> -#include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/clk.h> @@ -1258,7 +1257,6 @@ static const struct i2c_device_id twl_ids[] = { { "twl6032", TWL6030_CLASS | TWL6032_SUBCLASS }, /* "Phoenix lite" */ { /* end of list */ }, }; -MODULE_DEVICE_TABLE(i2c, twl_ids); /* One Client Driver , 4 Clients */ static struct i2c_driver twl_driver = { @@ -1267,9 +1265,4 @@ static struct i2c_driver twl_driver = { .probe = twl_probe, .remove = twl_remove, }; - -module_i2c_driver(twl_driver); - -MODULE_AUTHOR("Texas Instruments, Inc."); -MODULE_DESCRIPTION("I2C Core interface for TWL"); -MODULE_LICENSE("GPL"); +builtin_i2c_driver(twl_driver); diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index ab328ec49353..d66502d36ba0 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -609,6 +609,7 @@ static const struct regmap_config twl6040_regmap_config = { .writeable_reg = twl6040_writeable_reg, .cache_type = REGCACHE_RBTREE, + .use_single_rw = true, }; static const struct regmap_irq twl6040_irqs[] = { @@ -782,6 +783,11 @@ static int twl6040_probe(struct i2c_client *client, cell->name = "twl6040-gpo"; children++; + /* PDM clock support */ + cell = &twl6040->cells[children]; + cell->name = "twl6040-pdmclk"; + children++; + /* The chip is powered down so mark regmap to cache only and dirty */ regcache_cache_only(twl6040->regmap, true); regcache_mark_dirty(twl6040->regmap); diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 9ab9ec47ea75..d6fb2e1a759a 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -446,10 +446,6 @@ static int ucb1x00_detect_irq(struct ucb1x00 *ucb) unsigned long mask; mask = probe_irq_on(); - if (!mask) { - probe_irq_off(mask); - return NO_IRQ; - } /* * Enable the ADC interrupt. @@ -541,7 +537,7 @@ static int ucb1x00_probe(struct mcp *mcp) ucb1x00_enable(ucb); ucb->irq = ucb1x00_detect_irq(ucb); ucb1x00_disable(ucb); - if (ucb->irq == NO_IRQ) { + if (!ucb->irq) { dev_err(&ucb->dev, "IRQ probe failed\n"); ret = -ENODEV; goto err_no_irq; |