From c4726abce63bf9b1887ce68fdf012a823bd94ec3 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 8 Feb 2016 17:45:28 -0800 Subject: mfd: intel_quark_i2c_gpio: Use clkdev_create() Convert this driver to use clkdev_create() instead of clk_register_clkdevs(). The latter API is only used by this driver, although this driver only allocates one clk to add anyway. Furthermore, this driver allocates the clk_lookup structure with devm, but clkdev_drop() will free that structure when passed, leading to a double free when this driver is removed. Clean it all up and pave the way for the removal of clk_register_clkdevs(). Cc: Andy Shevchenko Cc: Russell King Signed-off-by: Stephen Boyd Signed-off-by: Lee Jones --- drivers/mfd/intel_quark_i2c_gpio.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/intel_quark_i2c_gpio.c b/drivers/mfd/intel_quark_i2c_gpio.c index 0421374..bdc5e27 100644 --- a/drivers/mfd/intel_quark_i2c_gpio.c +++ b/drivers/mfd/intel_quark_i2c_gpio.c @@ -52,8 +52,6 @@ /* The Quark I2C controller source clock */ #define INTEL_QUARK_I2C_CLK_HZ 33000000 -#define INTEL_QUARK_I2C_NCLK 1 - struct intel_quark_mfd { struct pci_dev *pdev; struct clk *i2c_clk; @@ -128,30 +126,24 @@ MODULE_DEVICE_TABLE(pci, intel_quark_mfd_ids); static int intel_quark_register_i2c_clk(struct intel_quark_mfd *quark_mfd) { struct pci_dev *pdev = quark_mfd->pdev; - struct clk_lookup *i2c_clk_lookup; struct clk *i2c_clk; - int ret; - - i2c_clk_lookup = devm_kcalloc(&pdev->dev, INTEL_QUARK_I2C_NCLK, - sizeof(*i2c_clk_lookup), GFP_KERNEL); - if (!i2c_clk_lookup) - return -ENOMEM; - - i2c_clk_lookup[0].dev_id = INTEL_QUARK_I2C_CONTROLLER_CLK; i2c_clk = clk_register_fixed_rate(&pdev->dev, INTEL_QUARK_I2C_CONTROLLER_CLK, NULL, CLK_IS_ROOT, INTEL_QUARK_I2C_CLK_HZ); + if (IS_ERR(i2c_clk)) + return PTR_ERR(i2c_clk); - quark_mfd->i2c_clk_lookup = i2c_clk_lookup; quark_mfd->i2c_clk = i2c_clk; + quark_mfd->i2c_clk_lookup = clkdev_create(i2c_clk, NULL, + INTEL_QUARK_I2C_CONTROLLER_CLK); - ret = clk_register_clkdevs(i2c_clk, i2c_clk_lookup, - INTEL_QUARK_I2C_NCLK); - if (ret) - dev_err(&pdev->dev, "Fixed clk register failed: %d\n", ret); + if (!quark_mfd->i2c_clk_lookup) { + dev_err(&pdev->dev, "Fixed clk register failed\n"); + return -ENOMEM; + } - return ret; + return 0; } static void intel_quark_unregister_i2c_clk(struct pci_dev *pdev) -- cgit v1.1 From e2fccf5c15157218f0b6488168f98183f82c1eda Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 14 Dec 2015 14:53:50 +0100 Subject: mfd: fsl-imx25-tsadc: Register touchscreen ADC driver This is the core driver for imx25 touchscreen/adc driver. The module has one shared ADC and two different conversion queues which use the ADC. The two queues are identical. Both can be used for general purpose ADC but one is meant to be used for touchscreens. This driver is the core which manages the central components and registers of the TSC/ADC unit. It manages the IRQs and forwards them to the correct components. Signed-off-by: Markus Pargmann Signed-off-by: Denis Carikli [ensure correct ADC clock depending on the IPG clock] Signed-off-by: Juergen Borleis Acked-by: Jonathan Cameron Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 9 ++ drivers/mfd/Makefile | 2 + drivers/mfd/fsl-imx25-tsadc.c | 203 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 drivers/mfd/fsl-imx25-tsadc.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9ca66de..6980e04 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -271,6 +271,15 @@ config MFD_MC13XXX_I2C help Select this if your MC13xxx is connected via an I2C bus. +config MFD_MX25_TSADC + tristate "Freescale i.MX25 integrated Touchscreen and ADC unit" + select REGMAP_MMIO + depends on (SOC_IMX25 && OF) || COMPILE_TEST + help + Enable support for the integrated Touchscreen and ADC unit of the + i.MX25 processors. They consist of a conversion queue for general + purpose ADC and a queue for Touchscreens. + config MFD_HI6421_PMIC tristate "HiSilicon Hi6421 PMU/Codec IC" depends on OF diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 0f230a6..8706bf9 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -85,6 +85,8 @@ obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o obj-$(CONFIG_TWL6040_CORE) += twl6040.o +obj-$(CONFIG_MFD_MX25_TSADC) += fsl-imx25-tsadc.o + obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c new file mode 100644 index 0000000..77b2675 --- /dev/null +++ b/drivers/mfd/fsl-imx25-tsadc.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct regmap_config mx25_tsadc_regmap_config = { + .fast_io = true, + .max_register = 8, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static void mx25_tsadc_irq_handler(struct irq_desc *desc) +{ + struct mx25_tsadc *tsadc = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + u32 status; + + chained_irq_enter(chip, desc); + + regmap_read(tsadc->regs, MX25_TSC_TGSR, &status); + + if (status & MX25_TGSR_GCQ_INT) + generic_handle_irq(irq_find_mapping(tsadc->domain, 1)); + + if (status & MX25_TGSR_TCQ_INT) + generic_handle_irq(irq_find_mapping(tsadc->domain, 0)); + + chained_irq_exit(chip, desc); +} + +static int mx25_tsadc_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct mx25_tsadc *tsadc = d->host_data; + + irq_set_chip_data(irq, tsadc); + irq_set_chip_and_handler(irq, &dummy_irq_chip, + handle_level_irq); + irq_modify_status(irq, IRQ_NOREQUEST, IRQ_NOPROBE); + + return 0; +} + +static struct irq_domain_ops mx25_tsadc_domain_ops = { + .map = mx25_tsadc_domain_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int mx25_tsadc_setup_irq(struct platform_device *pdev, + struct mx25_tsadc *tsadc) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int irq; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "Failed to get irq\n"); + return irq; + } + + tsadc->domain = irq_domain_add_simple(np, 2, 0, &mx25_tsadc_domain_ops, + tsadc); + if (!tsadc->domain) { + dev_err(dev, "Failed to add irq domain\n"); + return -ENOMEM; + } + + irq_set_chained_handler(irq, mx25_tsadc_irq_handler); + irq_set_handler_data(irq, tsadc); + + return 0; +} + +static void mx25_tsadc_setup_clk(struct platform_device *pdev, + struct mx25_tsadc *tsadc) +{ + unsigned clk_div; + + /* + * According to the datasheet the ADC clock should never + * exceed 1,75 MHz. Base clock is the IPG and the ADC unit uses + * a funny clock divider. To keep the ADC conversion time constant + * adapt the ADC internal clock divider to the IPG clock rate. + */ + + dev_dbg(&pdev->dev, "Found master clock at %lu Hz\n", + clk_get_rate(tsadc->clk)); + + clk_div = DIV_ROUND_UP(clk_get_rate(tsadc->clk), 1750000); + dev_dbg(&pdev->dev, "Setting up ADC clock divider to %u\n", clk_div); + + /* adc clock = IPG clock / (2 * div + 2) */ + clk_div -= 2; + clk_div /= 2; + + /* + * the ADC clock divider changes its behaviour when values below 4 + * are used: it is fixed to "/ 10" in this case + */ + clk_div = max_t(unsigned, 4, clk_div); + + dev_dbg(&pdev->dev, "Resulting ADC conversion clock at %lu Hz\n", + clk_get_rate(tsadc->clk) / (2 * clk_div + 2)); + + regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, + MX25_TGCR_ADCCLKCFG(0x1f), + MX25_TGCR_ADCCLKCFG(clk_div)); +} + +static int mx25_tsadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct mx25_tsadc *tsadc; + struct resource *res; + int ret; + void __iomem *iomem; + + tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL); + if (!tsadc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iomem = devm_ioremap_resource(dev, res); + if (IS_ERR(iomem)) + return PTR_ERR(iomem); + + tsadc->regs = devm_regmap_init_mmio(dev, iomem, + &mx25_tsadc_regmap_config); + if (IS_ERR(tsadc->regs)) { + dev_err(dev, "Failed to initialize regmap\n"); + return PTR_ERR(tsadc->regs); + } + + tsadc->clk = devm_clk_get(dev, "ipg"); + if (IS_ERR(tsadc->clk)) { + dev_err(dev, "Failed to get ipg clock\n"); + return PTR_ERR(tsadc->clk); + } + + /* setup clock according to the datasheet */ + mx25_tsadc_setup_clk(pdev, tsadc); + + /* Enable clock and reset the component */ + regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_CLK_EN, + MX25_TGCR_CLK_EN); + regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_TSC_RST, + MX25_TGCR_TSC_RST); + + /* Setup powersaving mode, but enable internal reference voltage */ + regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_POWERMODE_MASK, + MX25_TGCR_POWERMODE_SAVE); + regmap_update_bits(tsadc->regs, MX25_TSC_TGCR, MX25_TGCR_INTREFEN, + MX25_TGCR_INTREFEN); + + ret = mx25_tsadc_setup_irq(pdev, tsadc); + if (ret) + return ret; + + platform_set_drvdata(pdev, tsadc); + + of_platform_populate(np, NULL, NULL, dev); + + return 0; +} + +static const struct of_device_id mx25_tsadc_ids[] = { + { .compatible = "fsl,imx25-tsadc" }, + { /* Sentinel */ } +}; + +static struct platform_driver mx25_tsadc_driver = { + .driver = { + .name = "mx25-tsadc", + .of_match_table = of_match_ptr(mx25_tsadc_ids), + }, + .probe = mx25_tsadc_probe, +}; +module_platform_driver(mx25_tsadc_driver); + +MODULE_DESCRIPTION("MFD for ADC/TSC for Freescale mx25"); +MODULE_AUTHOR("Markus Pargmann "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mx25-tsadc"); -- cgit v1.1 From 6df2e98c3ea567bd15b46369180266d4c4df3447 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 14 Dec 2015 14:53:51 +0100 Subject: iio: adc: Add imx25-gcq ADC driver This is a conversion queue driver for the mx25 SoC. It uses the central ADC which is used by two seperate independent queues. This driver prepares different conversion configurations for each possible input. For a conversion it creates a conversionqueue of one item with the correct configuration for the chosen channel. It then executes the queue once and disables the conversion queue afterwards. The reference voltages are configurable through devicetree subnodes, depending on the connections of the ADC inputs. Signed-off-by: Markus Pargmann Signed-off-by: Denis Carikli Acked-by: Jonathan Cameron Signed-off-by: Lee Jones --- drivers/iio/adc/Kconfig | 7 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/fsl-imx25-gcq.c | 417 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 425 insertions(+) create mode 100644 drivers/iio/adc/fsl-imx25-gcq.c (limited to 'drivers') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 605ff42..a7ec74b 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -183,6 +183,13 @@ config EXYNOS_ADC To compile this driver as a module, choose M here: the module will be called exynos_adc. +config FSL_MX25_ADC + tristate "Freescale MX25 ADC driver" + depends on MFD_MX25_TSADC + help + Generic Conversion Queue driver used for general purpose ADC in the + MX25. This driver supports single measurements using the MX25 ADC. + config HI8435 tristate "Holt Integrated Circuits HI-8435 threshold detector" select IIO_TRIGGERED_EVENT diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 6435780..151088f 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o +obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o obj-$(CONFIG_HI8435) += hi8435.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c new file mode 100644 index 0000000..2fd1927 --- /dev/null +++ b/drivers/iio/adc/fsl-imx25-gcq.c @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann + * + * 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 is the driver for the imx25 GCQ (Generic Conversion Queue) + * connected to the imx25 ADC. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MX25_GCQ_TIMEOUT (msecs_to_jiffies(2000)) + +static const char * const driver_name = "mx25-gcq"; + +enum mx25_gcq_cfgs { + MX25_CFG_XP = 0, + MX25_CFG_YP, + MX25_CFG_XN, + MX25_CFG_YN, + MX25_CFG_WIPER, + MX25_CFG_INAUX0, + MX25_CFG_INAUX1, + MX25_CFG_INAUX2, + MX25_NUM_CFGS, +}; + +struct mx25_gcq_priv { + struct regmap *regs; + struct completion completed; + struct clk *clk; + int irq; + struct regulator *vref[4]; + u32 channel_vref_mv[MX25_NUM_CFGS]; +}; + +#define MX25_CQG_CHAN(chan, id) {\ + .type = IIO_VOLTAGE,\ + .indexed = 1,\ + .channel = chan,\ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE),\ + .datasheet_name = id,\ +} + +static const struct iio_chan_spec mx25_gcq_channels[MX25_NUM_CFGS] = { + MX25_CQG_CHAN(MX25_CFG_XP, "xp"), + MX25_CQG_CHAN(MX25_CFG_YP, "yp"), + MX25_CQG_CHAN(MX25_CFG_XN, "xn"), + MX25_CQG_CHAN(MX25_CFG_YN, "yn"), + MX25_CQG_CHAN(MX25_CFG_WIPER, "wiper"), + MX25_CQG_CHAN(MX25_CFG_INAUX0, "inaux0"), + MX25_CQG_CHAN(MX25_CFG_INAUX1, "inaux1"), + MX25_CQG_CHAN(MX25_CFG_INAUX2, "inaux2"), +}; + +static const char * const mx25_gcq_refp_names[] = { + [MX25_ADC_REFP_YP] = "yp", + [MX25_ADC_REFP_XP] = "xp", + [MX25_ADC_REFP_INT] = "int", + [MX25_ADC_REFP_EXT] = "ext", +}; + +static irqreturn_t mx25_gcq_irq(int irq, void *data) +{ + struct mx25_gcq_priv *priv = data; + u32 stats; + + regmap_read(priv->regs, MX25_ADCQ_SR, &stats); + + if (stats & MX25_ADCQ_SR_EOQ) { + regmap_update_bits(priv->regs, MX25_ADCQ_MR, + MX25_ADCQ_MR_EOQ_IRQ, MX25_ADCQ_MR_EOQ_IRQ); + complete(&priv->completed); + } + + /* Disable conversion queue run */ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, 0); + + /* Acknowledge all possible irqs */ + regmap_write(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR | + MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR | + MX25_ADCQ_SR_EOQ | MX25_ADCQ_SR_PD); + + return IRQ_HANDLED; +} + +static int mx25_gcq_get_raw_value(struct device *dev, + struct iio_chan_spec const *chan, + struct mx25_gcq_priv *priv, + int *val) +{ + long timeout; + u32 data; + + /* Setup the configuration we want to use */ + regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0, + MX25_ADCQ_ITEM(0, chan->channel)); + + regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_EOQ_IRQ, 0); + + /* Trigger queue for one run */ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, + MX25_ADCQ_CR_FQS); + + timeout = wait_for_completion_interruptible_timeout( + &priv->completed, MX25_GCQ_TIMEOUT); + if (timeout < 0) { + dev_err(dev, "ADC wait for measurement failed\n"); + return timeout; + } else if (timeout == 0) { + dev_err(dev, "ADC timed out\n"); + return -ETIMEDOUT; + } + + regmap_read(priv->regs, MX25_ADCQ_FIFO, &data); + + *val = MX25_ADCQ_FIFO_DATA(data); + + return IIO_VAL_INT; +} + +static int mx25_gcq_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct mx25_gcq_priv *priv = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + ret = mx25_gcq_get_raw_value(&indio_dev->dev, chan, priv, val); + mutex_unlock(&indio_dev->mlock); + return ret; + + case IIO_CHAN_INFO_SCALE: + *val = priv->channel_vref_mv[chan->channel]; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static const struct iio_info mx25_gcq_iio_info = { + .read_raw = mx25_gcq_read_raw, +}; + +static const struct regmap_config mx25_gcq_regconfig = { + .max_register = 0x5c, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static int mx25_gcq_setup_cfgs(struct platform_device *pdev, + struct mx25_gcq_priv *priv) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + struct device *dev = &pdev->dev; + unsigned int refp_used[4] = {}; + int ret, i; + + /* + * Setup all configurations registers with a default conversion + * configuration for each input + */ + for (i = 0; i < MX25_NUM_CFGS; ++i) + regmap_write(priv->regs, MX25_ADCQ_CFG(i), + MX25_ADCQ_CFG_YPLL_OFF | + MX25_ADCQ_CFG_XNUR_OFF | + MX25_ADCQ_CFG_XPUL_OFF | + MX25_ADCQ_CFG_REFP_INT | + MX25_ADCQ_CFG_IN(i) | + MX25_ADCQ_CFG_REFN_NGND2); + + /* + * First get all regulators to store them in channel_vref_mv if + * necessary. Later we use that information for proper IIO scale + * information. + */ + priv->vref[MX25_ADC_REFP_INT] = NULL; + priv->vref[MX25_ADC_REFP_EXT] = + devm_regulator_get_optional(&pdev->dev, "vref-ext"); + priv->vref[MX25_ADC_REFP_XP] = + devm_regulator_get_optional(&pdev->dev, "vref-xp"); + priv->vref[MX25_ADC_REFP_YP] = + devm_regulator_get_optional(&pdev->dev, "vref-yp"); + + for_each_child_of_node(np, child) { + u32 reg; + u32 refp = MX25_ADCQ_CFG_REFP_INT; + u32 refn = MX25_ADCQ_CFG_REFN_NGND2; + + ret = of_property_read_u32(child, "reg", ®); + if (ret) { + dev_err(dev, "Failed to get reg property\n"); + return ret; + } + + if (reg >= MX25_NUM_CFGS) { + dev_err(dev, + "reg value is greater than the number of available configuration registers\n"); + return -EINVAL; + } + + of_property_read_u32(child, "fsl,adc-refp", &refp); + of_property_read_u32(child, "fsl,adc-refn", &refn); + + switch (refp) { + case MX25_ADC_REFP_EXT: + case MX25_ADC_REFP_XP: + case MX25_ADC_REFP_YP: + if (IS_ERR(priv->vref[refp])) { + dev_err(dev, "Error, trying to use external voltage reference without a vref-%s regulator.", + mx25_gcq_refp_names[refp]); + return PTR_ERR(priv->vref[refp]); + } + priv->channel_vref_mv[reg] = + regulator_get_voltage(priv->vref[refp]); + /* Conversion from uV to mV */ + do_div(priv->channel_vref_mv[reg], 1000); + break; + case MX25_ADC_REFP_INT: + priv->channel_vref_mv[reg] = 2500; + break; + default: + dev_err(dev, "Invalid positive reference %d\n", refp); + return -EINVAL; + } + + ++refp_used[refp]; + + /* + * Shift the read values to the correct positions within the + * register. + */ + refp = MX25_ADCQ_CFG_REFP(refp); + refn = MX25_ADCQ_CFG_REFN(refn); + + if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) { + dev_err(dev, "Invalid fsl,adc-refp property value\n"); + return -EINVAL; + } + if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) { + dev_err(dev, "Invalid fsl,adc-refn property value\n"); + return -EINVAL; + } + + regmap_update_bits(priv->regs, MX25_ADCQ_CFG(reg), + MX25_ADCQ_CFG_REFP_MASK | + MX25_ADCQ_CFG_REFN_MASK, + refp | refn); + } + regmap_update_bits(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST, + MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST); + + regmap_write(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_PDMSK | MX25_ADCQ_CR_QSM_FQS); + + /* Remove unused regulators */ + for (i = 0; i != 4; ++i) { + if (!refp_used[i]) { + if (!IS_ERR_OR_NULL(priv->vref[i])) + devm_regulator_put(priv->vref[i]); + priv->vref[i] = NULL; + } + } + + return 0; +} + +static int mx25_gcq_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct mx25_gcq_priv *priv; + struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct resource *res; + void __iomem *mem; + int ret; + int i; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mem = devm_ioremap_resource(dev, res); + if (IS_ERR(mem)) + return PTR_ERR(mem); + + priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig); + if (IS_ERR(priv->regs)) { + dev_err(dev, "Failed to initialize regmap\n"); + return PTR_ERR(priv->regs); + } + + init_completion(&priv->completed); + + ret = mx25_gcq_setup_cfgs(pdev, priv); + if (ret) + return ret; + + for (i = 0; i != 4; ++i) { + if (!priv->vref[i]) + continue; + + ret = regulator_enable(priv->vref[i]); + if (ret) + goto err_regulator_disable; + } + + priv->clk = tsadc->clk; + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(dev, "Failed to enable clock\n"); + goto err_vref_disable; + } + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq <= 0) { + dev_err(dev, "Failed to get IRQ\n"); + ret = priv->irq; + if (!ret) + ret = -ENXIO; + goto err_clk_unprepare; + } + + ret = request_irq(priv->irq, mx25_gcq_irq, 0, pdev->name, priv); + if (ret) { + dev_err(dev, "Failed requesting IRQ\n"); + goto err_clk_unprepare; + } + + indio_dev->dev.parent = &pdev->dev; + indio_dev->channels = mx25_gcq_channels; + indio_dev->num_channels = ARRAY_SIZE(mx25_gcq_channels); + indio_dev->info = &mx25_gcq_iio_info; + indio_dev->name = driver_name; + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "Failed to register iio device\n"); + goto err_irq_free; + } + + platform_set_drvdata(pdev, indio_dev); + + return 0; + +err_irq_free: + free_irq(priv->irq, priv); +err_clk_unprepare: + clk_disable_unprepare(priv->clk); +err_vref_disable: + i = 4; +err_regulator_disable: + for (; i-- > 0;) { + if (priv->vref[i]) + regulator_disable(priv->vref[i]); + } + return ret; +} + +static int mx25_gcq_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct mx25_gcq_priv *priv = iio_priv(indio_dev); + int i; + + iio_device_unregister(indio_dev); + free_irq(priv->irq, priv); + clk_disable_unprepare(priv->clk); + for (i = 4; i-- > 0;) { + if (priv->vref[i]) + regulator_disable(priv->vref[i]); + } + + return 0; +} + +static const struct of_device_id mx25_gcq_ids[] = { + { .compatible = "fsl,imx25-gcq", }, + { /* Sentinel */ } +}; + +static struct platform_driver mx25_gcq_driver = { + .driver = { + .name = "mx25-gcq", + .of_match_table = mx25_gcq_ids, + }, + .probe = mx25_gcq_probe, + .remove = mx25_gcq_remove, +}; +module_platform_driver(mx25_gcq_driver); + +MODULE_DESCRIPTION("ADC driver for Freescale mx25"); +MODULE_AUTHOR("Markus Pargmann "); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 4f7ed234f2a5dcc26fc56a6e8324d34bbc9afe23 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 14 Dec 2015 14:53:52 +0100 Subject: Input: touchscreen: Add imx25 TCQ driver This is a driver for the imx25 ADC/TSC module. It controls the touchscreen conversion queue and creates a touchscreen input device. The driver currently only supports 4 wire touchscreens. The driver uses a simple conversion queue of precharge, touch detection, X measurement, Y measurement, precharge and another touch detection. This driver uses the regmap from the parent to setup some touch specific settings in the core driver and setup a idle configuration with touch detection. Signed-off-by: Markus Pargmann Signed-off-by: Denis Carikli [fix clock's period calculation] [fix calculation of the 'settling' value] Signed-off-by: Juergen Borleis Acked-by: Dmitry Torokhov Signed-off-by: Lee Jones --- drivers/input/touchscreen/Kconfig | 9 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/fsl-imx25-tcq.c | 596 ++++++++++++++++++++++++++++++ 3 files changed, 606 insertions(+) create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c (limited to 'drivers') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 66c6264..a768806 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -822,6 +822,15 @@ config TOUCHSCREEN_USB_COMPOSITE To compile this driver as a module, choose M here: the module will be called usbtouchscreen. +config TOUCHSCREEN_MX25 + tristate "Freescale i.MX25 touchscreen input driver" + depends on MFD_MX25_TSADC + help + Enable support for touchscreen connected to your i.MX25. + + To compile this driver as a module, choose M here: the + module will be called fsl-imx25-tcq. + config TOUCHSCREEN_MC13783 tristate "Freescale MC13783 touchscreen input driver" depends on MFD_MC13XXX diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 968ff12..49134ef 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o +obj-$(CONFIG_TOUCHSCREEN_MX25) += fsl-imx25-tcq.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c new file mode 100644 index 0000000..fe9877a --- /dev/null +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c @@ -0,0 +1,596 @@ +/* + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann + * + * 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. + * + * Based on driver from 2011: + * Juergen Beisert, Pengutronix + * + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue) + * connected to the imx25 ADC. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char mx25_tcq_name[] = "mx25-tcq"; + +enum mx25_tcq_mode { + MX25_TS_4WIRE, +}; + +struct mx25_tcq_priv { + struct regmap *regs; + struct regmap *core_regs; + struct input_dev *idev; + enum mx25_tcq_mode mode; + unsigned int pen_threshold; + unsigned int sample_count; + unsigned int expected_samples; + unsigned int pen_debounce; + unsigned int settling_time; + struct clk *clk; + int irq; + struct device *dev; +}; + +static struct regmap_config mx25_tcq_regconfig = { + .fast_io = true, + .max_register = 0x5c, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static const struct of_device_id mx25_tcq_ids[] = { + { .compatible = "fsl,imx25-tcq", }, + { /* Sentinel */ } +}; + +#define TSC_4WIRE_PRE_INDEX 0 +#define TSC_4WIRE_X_INDEX 1 +#define TSC_4WIRE_Y_INDEX 2 +#define TSC_4WIRE_POST_INDEX 3 +#define TSC_4WIRE_LEAVE 4 + +#define MX25_TSC_DEF_THRESHOLD 80 +#define TSC_MAX_SAMPLES 16 + +#define MX25_TSC_REPEAT_WAIT 14 + +enum mx25_adc_configurations { + MX25_CFG_PRECHARGE = 0, + MX25_CFG_TOUCH_DETECT, + MX25_CFG_X_MEASUREMENT, + MX25_CFG_Y_MEASUREMENT, +}; + +#define MX25_PRECHARGE_VALUE (\ + MX25_ADCQ_CFG_YPLL_OFF | \ + MX25_ADCQ_CFG_XNUR_OFF | \ + MX25_ADCQ_CFG_XPUL_HIGH | \ + MX25_ADCQ_CFG_REFP_INT | \ + MX25_ADCQ_CFG_IN_XP | \ + MX25_ADCQ_CFG_REFN_NGND2 | \ + MX25_ADCQ_CFG_IGS) + +#define MX25_TOUCH_DETECT_VALUE (\ + MX25_ADCQ_CFG_YNLR | \ + MX25_ADCQ_CFG_YPLL_OFF | \ + MX25_ADCQ_CFG_XNUR_OFF | \ + MX25_ADCQ_CFG_XPUL_OFF | \ + MX25_ADCQ_CFG_REFP_INT | \ + MX25_ADCQ_CFG_IN_XP | \ + MX25_ADCQ_CFG_REFN_NGND2 | \ + MX25_ADCQ_CFG_PENIACK) + +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv, + unsigned int settling_cnt) +{ + u32 precharge_cfg = + MX25_PRECHARGE_VALUE | + MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt); + u32 touch_detect_cfg = + MX25_TOUCH_DETECT_VALUE | + MX25_ADCQ_CFG_NOS(1) | + MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt); + + regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg); + + /* PRECHARGE */ + regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE), + precharge_cfg); + + /* TOUCH_DETECT */ + regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT), + touch_detect_cfg); + + /* X Measurement */ + regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT), + MX25_ADCQ_CFG_YPLL_OFF | + MX25_ADCQ_CFG_XNUR_LOW | + MX25_ADCQ_CFG_XPUL_HIGH | + MX25_ADCQ_CFG_REFP_XP | + MX25_ADCQ_CFG_IN_YP | + MX25_ADCQ_CFG_REFN_XN | + MX25_ADCQ_CFG_NOS(priv->sample_count) | + MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt)); + + /* Y Measurement */ + regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT), + MX25_ADCQ_CFG_YNLR | + MX25_ADCQ_CFG_YPLL_HIGH | + MX25_ADCQ_CFG_XNUR_OFF | + MX25_ADCQ_CFG_XPUL_OFF | + MX25_ADCQ_CFG_REFP_YP | + MX25_ADCQ_CFG_IN_XP | + MX25_ADCQ_CFG_REFN_YN | + MX25_ADCQ_CFG_NOS(priv->sample_count) | + MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt)); + + /* Enable the touch detection right now */ + regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg | + MX25_ADCQ_CFG_IGS); +} + +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv, + unsigned settling_cnt, int *items) +{ + imx25_setup_queue_cfgs(priv, settling_cnt); + + /* Setup the conversion queue */ + regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0, + MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) | + MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) | + MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) | + MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) | + MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) | + MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT)); + + /* + * We measure X/Y with 'sample_count' number of samples and execute a + * touch detection twice, with 1 sample each + */ + priv->expected_samples = priv->sample_count * 2 + 2; + *items = 6; + + return 0; +} + +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv) +{ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, + MX25_ADCQ_CR_PDMSK); +} + +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv) +{ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0); +} + +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv) +{ + regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, + MX25_ADCQ_MR_FDRY_IRQ); +} + +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv) +{ + regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0); +} + +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv) +{ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_FQS, + MX25_ADCQ_CR_FQS); +} + +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv) +{ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_FQS, 0); +} + +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv) +{ + u32 tcqcr; + + regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr); + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, + MX25_ADCQ_CR_FRST); + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0); + regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr); +} + +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv) +{ + /* stop the queue from looping */ + mx25_tcq_force_queue_stop(priv); + + /* for a clean touch detection, preload the X plane */ + regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE); + + /* waste some time now to pre-load the X plate to high voltage */ + mx25_tcq_fifo_reset(priv); + + /* re-enable the detection right now */ + regmap_write(priv->core_regs, MX25_TSC_TICR, + MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS); + + regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD, + MX25_ADCQ_SR_PD); + + /* enable the pen down event to be a source for the interrupt */ + regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0); + + /* lets fire the next IRQ if someone touches the touchscreen */ + mx25_tcq_enable_touch_irq(priv); +} + +static void mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv, + u32 *sample_buf, + unsigned int samples) +{ + unsigned int x_pos = 0; + unsigned int y_pos = 0; + unsigned int touch_pre = 0; + unsigned int touch_post = 0; + unsigned int i; + + for (i = 0; i < samples; i++) { + unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]); + unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]); + + switch (index) { + case 1: + touch_pre = val; + break; + case 2: + x_pos = val; + break; + case 3: + y_pos = val; + break; + case 5: + touch_post = val; + break; + default: + dev_dbg(priv->dev, "Dropped samples because of invalid index %d\n", + index); + return; + } + } + + if (samples != 0) { + /* + * only if both touch measures are below a threshold, + * the position is valid + */ + if (touch_pre < priv->pen_threshold && + touch_post < priv->pen_threshold) { + /* valid samples, generate a report */ + x_pos /= priv->sample_count; + y_pos /= priv->sample_count; + input_report_abs(priv->idev, ABS_X, x_pos); + input_report_abs(priv->idev, ABS_Y, y_pos); + input_report_key(priv->idev, BTN_TOUCH, 1); + input_sync(priv->idev); + + /* get next sample */ + mx25_tcq_enable_fifo_irq(priv); + } else if (touch_pre >= priv->pen_threshold && + touch_post >= priv->pen_threshold) { + /* + * if both samples are invalid, + * generate a release report + */ + input_report_key(priv->idev, BTN_TOUCH, 0); + input_sync(priv->idev); + mx25_tcq_re_enable_touch_detection(priv); + } else { + /* + * if only one of both touch measurements are + * below the threshold, still some bouncing + * happens. Take additional samples in this + * case to be sure + */ + mx25_tcq_enable_fifo_irq(priv); + } + } +} + +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id) +{ + struct mx25_tcq_priv *priv = dev_id; + u32 sample_buf[TSC_MAX_SAMPLES]; + unsigned int samples; + u32 stats; + unsigned int i; + + /* + * Check how many samples are available. We always have to read exactly + * sample_count samples from the fifo, or a multiple of sample_count. + * Otherwise we mixup samples into different touch events. + */ + regmap_read(priv->regs, MX25_ADCQ_SR, &stats); + samples = MX25_ADCQ_SR_FDN(stats); + samples -= samples % priv->sample_count; + + if (!samples) + return IRQ_HANDLED; + + for (i = 0; i != samples; ++i) + regmap_read(priv->regs, MX25_ADCQ_FIFO, &sample_buf[i]); + + mx25_tcq_create_event_for_4wire(priv, sample_buf, samples); + + return IRQ_HANDLED; +} + +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id) +{ + struct mx25_tcq_priv *priv = dev_id; + u32 stat; + int ret = IRQ_HANDLED; + + regmap_read(priv->regs, MX25_ADCQ_SR, &stat); + + if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR)) + mx25_tcq_re_enable_touch_detection(priv); + + if (stat & MX25_ADCQ_SR_PD) { + mx25_tcq_disable_touch_irq(priv); + mx25_tcq_force_queue_start(priv); + mx25_tcq_enable_fifo_irq(priv); + } + + if (stat & MX25_ADCQ_SR_FDRY) { + mx25_tcq_disable_fifo_irq(priv); + ret = IRQ_WAKE_THREAD; + } + + regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR | + MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR | + MX25_ADCQ_SR_PD, + MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | + MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD); + + return ret; +} + +/* configure the state machine for a 4-wire touchscreen */ +static int mx25_tcq_init(struct mx25_tcq_priv *priv) +{ + u32 tgcr; + unsigned int ipg_div; + unsigned int adc_period; + unsigned int debounce_cnt; + unsigned int settling_cnt; + int itemct; + int error; + + regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr); + ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr)); + adc_period = USEC_PER_SEC * ipg_div * 2 + 2; + adc_period /= clk_get_rate(priv->clk) / 1000 + 1; + debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1; + settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1; + + /* Reset */ + regmap_write(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST); + regmap_update_bits(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0); + + /* up to 128 * 8 ADC clocks are possible */ + if (debounce_cnt > 127) + debounce_cnt = 127; + + /* up to 255 * 8 ADC clocks are possible */ + if (settling_cnt > 255) + settling_cnt = 255; + + error = imx25_setup_queue_4wire(priv, settling_cnt, &itemct); + if (error) + return error; + + regmap_update_bits(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK, + MX25_ADCQ_CR_LITEMID(itemct - 1) | + MX25_ADCQ_CR_WMRK(priv->expected_samples - 1)); + + /* setup debounce count */ + regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, + MX25_TGCR_PDBTIME_MASK, + MX25_TGCR_PDBTIME(debounce_cnt)); + + /* enable debounce */ + regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN, + MX25_TGCR_PDBEN); + regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN, + MX25_TGCR_PDEN); + + /* enable the engine on demand */ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK, + MX25_ADCQ_CR_QSM_FQS); + + /* Enable repeat and repeat wait */ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK, + MX25_ADCQ_CR_RPT | + MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT)); + + return 0; +} + +static int mx25_tcq_parse_dt(struct platform_device *pdev, + struct mx25_tcq_priv *priv) +{ + struct device_node *np = pdev->dev.of_node; + u32 wires; + int error; + + /* Setup defaults */ + priv->pen_threshold = 500; + priv->sample_count = 3; + priv->pen_debounce = 1000000; + priv->settling_time = 250000; + + error = of_property_read_u32(np, "fsl,wires", &wires); + if (error) { + dev_err(&pdev->dev, "Failed to find fsl,wires properties\n"); + return error; + } + + if (wires == 4) { + priv->mode = MX25_TS_4WIRE; + } else { + dev_err(&pdev->dev, "%u-wire mode not supported\n", wires); + return -EINVAL; + } + + /* These are optional, we don't care about the return values */ + of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold); + of_property_read_u32(np, "fsl,settling-time-ns", &priv->settling_time); + of_property_read_u32(np, "fsl,pen-debounce-ns", &priv->pen_debounce); + + return 0; +} + +static int mx25_tcq_open(struct input_dev *idev) +{ + struct device *dev = &idev->dev; + struct mx25_tcq_priv *priv = dev_get_drvdata(dev); + int error; + + error = clk_prepare_enable(priv->clk); + if (error) { + dev_err(dev, "Failed to enable ipg clock\n"); + return error; + } + + error = mx25_tcq_init(priv); + if (error) { + dev_err(dev, "Failed to init tcq\n"); + clk_disable_unprepare(priv->clk); + return error; + } + + mx25_tcq_re_enable_touch_detection(priv); + + return 0; +} + +static void mx25_tcq_close(struct input_dev *idev) +{ + struct mx25_tcq_priv *priv = input_get_drvdata(idev); + + mx25_tcq_force_queue_stop(priv); + mx25_tcq_disable_touch_irq(priv); + mx25_tcq_disable_fifo_irq(priv); + clk_disable_unprepare(priv->clk); +} + +static int mx25_tcq_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct input_dev *idev; + struct mx25_tcq_priv *priv; + struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent); + struct resource *res; + void __iomem *mem; + int error; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mem = devm_ioremap_resource(dev, res); + if (IS_ERR(mem)) + return PTR_ERR(mem); + + error = mx25_tcq_parse_dt(pdev, priv); + if (error) + return error; + + priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig); + if (IS_ERR(priv->regs)) { + dev_err(dev, "Failed to initialize regmap\n"); + return PTR_ERR(priv->regs); + } + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq <= 0) { + dev_err(dev, "Failed to get IRQ\n"); + return priv->irq; + } + + idev = devm_input_allocate_device(dev); + if (!idev) { + dev_err(dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + idev->name = mx25_tcq_name; + input_set_capability(idev, EV_KEY, BTN_TOUCH); + input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0); + input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0); + + idev->id.bustype = BUS_HOST; + idev->open = mx25_tcq_open; + idev->close = mx25_tcq_close; + + priv->idev = idev; + input_set_drvdata(idev, priv); + + priv->core_regs = tsadc->regs; + if (!priv->core_regs) + return -EINVAL; + + priv->clk = tsadc->clk; + if (!priv->clk) + return -EINVAL; + + platform_set_drvdata(pdev, priv); + + error = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq, + mx25_tcq_irq_thread, 0, pdev->name, + priv); + if (error) { + dev_err(dev, "Failed requesting IRQ\n"); + return error; + } + + error = input_register_device(idev); + if (error) { + dev_err(dev, "Failed to register input device\n"); + return error; + } + + return 0; +} + +static struct platform_driver mx25_tcq_driver = { + .driver = { + .name = "mx25-tcq", + .of_match_table = mx25_tcq_ids, + }, + .probe = mx25_tcq_probe, +}; +module_platform_driver(mx25_tcq_driver); + +MODULE_DESCRIPTION("TS input driver for Freescale mx25"); +MODULE_AUTHOR("Markus Pargmann "); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From e47a3cf741e71b8c545a826df42432fec3ea60c6 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 12 Feb 2016 10:02:39 +0800 Subject: mfd: axp20x: Remove second struct device * parameter for axp20x_match_device() The first argument passed to axp20x_match_device(), struct axp20x_dev *, already contains a pointer to the device. By rearranging some code, moving the assignment of the pointer before axp20x_match_device() is called, we can eliminate the second parameter. Suggested-by: Andy Shevchenko Signed-off-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 9842199..685a786 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -606,8 +606,9 @@ static void axp20x_power_off(void) AXP20X_OFF); } -static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev) +static int axp20x_match_device(struct axp20x_dev *axp20x) { + struct device *dev = axp20x->dev; const struct acpi_device_id *acpi_id; const struct of_device_id *of_id; @@ -673,14 +674,14 @@ static int axp20x_i2c_probe(struct i2c_client *i2c, if (!axp20x) return -ENOMEM; - ret = axp20x_match_device(axp20x, &i2c->dev); - if (ret) - return ret; - axp20x->i2c_client = i2c; axp20x->dev = &i2c->dev; dev_set_drvdata(axp20x->dev, axp20x); + ret = axp20x_match_device(axp20x); + if (ret) + return ret; + axp20x->regmap = devm_regmap_init_i2c(i2c, axp20x->regmap_cfg); if (IS_ERR(axp20x->regmap)) { ret = PTR_ERR(axp20x->regmap); -- cgit v1.1 From af7acc3df7b1cad3956579c63a70a7b85b69161b Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 12 Feb 2016 10:02:40 +0800 Subject: mfd: axp20x: Use dev->driver->of_match_table in axp20x_match_device() In axp20x_match_device(), match the of_device_id table bound to the device driver instead of pointing to axp20x_of_match directly. This will allow us to keep axp20x_match_device() unmodified when we expand the axp20x driver into multiple ones covering different interface types. of_device_get_match_data() cannot be used here as we need to know if it failed to get a match, or if the match data value just happened to be 0, as it is for the AXP152. Signed-off-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 685a786..3e186f2 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -613,7 +613,7 @@ static int axp20x_match_device(struct axp20x_dev *axp20x) const struct of_device_id *of_id; if (dev->of_node) { - of_id = of_match_device(axp20x_of_match, dev); + of_id = of_match_device(dev->driver->of_match_table, dev); if (!of_id) { dev_err(dev, "Unable to match OF ID\n"); return -ENODEV; -- cgit v1.1 From e740235ddd84f4fe993af982b873578f3299b7d6 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 12 Feb 2016 10:02:41 +0800 Subject: mfd: axp20x: Add missing copyright notice Supply a backdated copyright notice. Signed-off-by: Chen-Yu Tsai Acked-by: Carlo Caione Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 3e186f2..cec51e6 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -5,6 +5,8 @@ * converters, LDOs, multiple 12-bit ADCs of voltage, current and temperature * as well as configurable GPIOs. * + * Copyright (C) 2014 Carlo Caione + * * Author: Carlo Caione * * This program is free software; you can redistribute it and/or modify -- cgit v1.1 From 4fd411514291ae75053003e33a6a4a56f97467d0 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 12 Feb 2016 10:02:42 +0800 Subject: mfd: axp20x: Split the driver into core and i2c bits The axp20x driver assumes the device is i2c based. This is not the case with later chips, which use a proprietary 2 wire serial bus by Allwinner called "Reduced Serial Bus". This patch follows the example of mfd/wm831x and splits it into an interface independent core, and an i2c specific glue layer. MFD_AXP20X and the new MFD_AXP20X_I2C are changed to tristate symbols, allowing the driver to be built as modules. Whitespace and other style errors in the moved i2c specific code have been fixed. Included but unused header files are removed as well. Signed-off-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 14 ++++--- drivers/mfd/Makefile | 1 + drivers/mfd/axp20x-i2c.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/axp20x.c | 90 +++++++--------------------------------- 4 files changed, 129 insertions(+), 80 deletions(-) create mode 100644 drivers/mfd/axp20x-i2c.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9ca66de..0037b9c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -91,14 +91,18 @@ config MFD_BCM590XX Support for the BCM590xx PMUs from Broadcom config MFD_AXP20X - bool "X-Powers AXP20X" + tristate select MFD_CORE - select REGMAP_I2C select REGMAP_IRQ - depends on I2C=y + +config MFD_AXP20X_I2C + tristate "X-Powers AXP series PMICs with I2C" + select MFD_AXP20X + select REGMAP_I2C + depends on I2C help - If you say Y here you get support for the X-Powers AXP202, AXP209 and - AXP288 power management IC (PMIC). + If you say Y here you get support for the X-Powers AXP series power + management ICs (PMICs) controlled with I2C. This driver include only the core APIs. You have to select individual components like regulators or the PEK (Power Enable Key) under the corresponding menus. diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 0f230a6..dba4f99 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -111,6 +111,7 @@ 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_AXP20X) += axp20x.o +obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o obj-$(CONFIG_MFD_LP3943) += lp3943.o obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o diff --git a/drivers/mfd/axp20x-i2c.c b/drivers/mfd/axp20x-i2c.c new file mode 100644 index 0000000..b1b8658 --- /dev/null +++ b/drivers/mfd/axp20x-i2c.c @@ -0,0 +1,104 @@ +/* + * I2C driver for the X-Powers' Power Management ICs + * + * AXP20x typically comprises an adaptive USB-Compatible PWM charger, BUCK DC-DC + * converters, LDOs, multiple 12-bit ADCs of voltage, current and temperature + * as well as configurable GPIOs. + * + * This driver supports the I2C variants. + * + * Copyright (C) 2014 Carlo Caione + * + * Author: Carlo Caione + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +static int axp20x_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct axp20x_dev *axp20x; + int ret; + + axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL); + if (!axp20x) + return -ENOMEM; + + axp20x->dev = &i2c->dev; + axp20x->irq = i2c->irq; + dev_set_drvdata(axp20x->dev, axp20x); + + ret = axp20x_match_device(axp20x); + if (ret) + return ret; + + axp20x->regmap = devm_regmap_init_i2c(i2c, axp20x->regmap_cfg); + if (IS_ERR(axp20x->regmap)) { + ret = PTR_ERR(axp20x->regmap); + dev_err(&i2c->dev, "regmap init failed: %d\n", ret); + return ret; + } + + return axp20x_device_probe(axp20x); +} + +static int axp20x_i2c_remove(struct i2c_client *i2c) +{ + struct axp20x_dev *axp20x = i2c_get_clientdata(i2c); + + return axp20x_device_remove(axp20x); +} + +static const struct of_device_id axp20x_i2c_of_match[] = { + { .compatible = "x-powers,axp152", .data = (void *)AXP152_ID }, + { .compatible = "x-powers,axp202", .data = (void *)AXP202_ID }, + { .compatible = "x-powers,axp209", .data = (void *)AXP209_ID }, + { .compatible = "x-powers,axp221", .data = (void *)AXP221_ID }, + { }, +}; +MODULE_DEVICE_TABLE(of, axp20x_i2c_of_match); + +/* + * This is useless for OF-enabled devices, but it is needed by I2C subsystem + */ +static const struct i2c_device_id axp20x_i2c_id[] = { + { }, +}; +MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id); + +static const struct acpi_device_id axp20x_i2c_acpi_match[] = { + { + .id = "INT33F4", + .driver_data = AXP288_ID, + }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, axp20x_i2c_acpi_match); + +static struct i2c_driver axp20x_i2c_driver = { + .driver = { + .name = "axp20x-i2c", + .of_match_table = of_match_ptr(axp20x_i2c_of_match), + .acpi_match_table = ACPI_PTR(axp20x_i2c_acpi_match), + }, + .probe = axp20x_i2c_probe, + .remove = axp20x_i2c_remove, + .id_table = axp20x_i2c_id, +}; + +module_i2c_driver(axp20x_i2c_driver); + +MODULE_DESCRIPTION("PMIC MFD I2C driver for AXP20X"); +MODULE_AUTHOR("Carlo Caione "); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index cec51e6..8e569bc 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -1,10 +1,12 @@ /* - * axp20x.c - MFD core driver for the X-Powers' Power Management ICs + * MFD core driver for the X-Powers' Power Management ICs * * AXP20x typically comprises an adaptive USB-Compatible PWM charger, BUCK DC-DC * converters, LDOs, multiple 12-bit ADCs of voltage, current and temperature * as well as configurable GPIOs. * + * This file contains the interface independent core functions. + * * Copyright (C) 2014 Carlo Caione * * Author: Carlo Caione @@ -15,18 +17,15 @@ */ #include -#include #include #include #include #include #include -#include #include #include #include #include -#include #include #define AXP20X_OFF 0x80 @@ -378,32 +377,6 @@ static const struct regmap_irq axp288_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1), }; -static const struct of_device_id axp20x_of_match[] = { - { .compatible = "x-powers,axp152", .data = (void *) AXP152_ID }, - { .compatible = "x-powers,axp202", .data = (void *) AXP202_ID }, - { .compatible = "x-powers,axp209", .data = (void *) AXP209_ID }, - { .compatible = "x-powers,axp221", .data = (void *) AXP221_ID }, - { }, -}; -MODULE_DEVICE_TABLE(of, axp20x_of_match); - -/* - * This is useless for OF-enabled devices, but it is needed by I2C subsystem - */ -static const struct i2c_device_id axp20x_i2c_id[] = { - { }, -}; -MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id); - -static const struct acpi_device_id axp20x_acpi_match[] = { - { - .id = "INT33F4", - .driver_data = AXP288_ID, - }, - { }, -}; -MODULE_DEVICE_TABLE(acpi, axp20x_acpi_match); - static const struct regmap_irq_chip axp152_regmap_irq_chip = { .name = "axp152_irq_chip", .status_base = AXP152_IRQ1_STATE, @@ -608,7 +581,7 @@ static void axp20x_power_off(void) AXP20X_OFF); } -static int axp20x_match_device(struct axp20x_dev *axp20x) +int axp20x_match_device(struct axp20x_dev *axp20x) { struct device *dev = axp20x->dev; const struct acpi_device_id *acpi_id; @@ -665,38 +638,18 @@ static int axp20x_match_device(struct axp20x_dev *axp20x) return 0; } +EXPORT_SYMBOL(axp20x_match_device); -static int axp20x_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +int axp20x_device_probe(struct axp20x_dev *axp20x) { - struct axp20x_dev *axp20x; int ret; - axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL); - if (!axp20x) - return -ENOMEM; - - axp20x->i2c_client = i2c; - axp20x->dev = &i2c->dev; - dev_set_drvdata(axp20x->dev, axp20x); - - ret = axp20x_match_device(axp20x); - if (ret) - return ret; - - axp20x->regmap = devm_regmap_init_i2c(i2c, axp20x->regmap_cfg); - if (IS_ERR(axp20x->regmap)) { - ret = PTR_ERR(axp20x->regmap); - dev_err(&i2c->dev, "regmap init failed: %d\n", ret); - return ret; - } - - ret = regmap_add_irq_chip(axp20x->regmap, i2c->irq, + ret = regmap_add_irq_chip(axp20x->regmap, axp20x->irq, IRQF_ONESHOT | IRQF_SHARED, -1, axp20x->regmap_irq_chip, &axp20x->regmap_irqc); if (ret) { - dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret); + dev_err(axp20x->dev, "failed to add irq chip: %d\n", ret); return ret; } @@ -704,8 +657,8 @@ static int axp20x_i2c_probe(struct i2c_client *i2c, axp20x->nr_cells, NULL, 0, NULL); if (ret) { - dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret); - regmap_del_irq_chip(i2c->irq, axp20x->regmap_irqc); + dev_err(axp20x->dev, "failed to add MFD devices: %d\n", ret); + regmap_del_irq_chip(axp20x->irq, axp20x->regmap_irqc); return ret; } @@ -714,38 +667,25 @@ static int axp20x_i2c_probe(struct i2c_client *i2c, pm_power_off = axp20x_power_off; } - dev_info(&i2c->dev, "AXP20X driver loaded\n"); + dev_info(axp20x->dev, "AXP20X driver loaded\n"); return 0; } +EXPORT_SYMBOL(axp20x_device_probe); -static int axp20x_i2c_remove(struct i2c_client *i2c) +int axp20x_device_remove(struct axp20x_dev *axp20x) { - struct axp20x_dev *axp20x = i2c_get_clientdata(i2c); - if (axp20x == axp20x_pm_power_off) { axp20x_pm_power_off = NULL; pm_power_off = NULL; } mfd_remove_devices(axp20x->dev); - regmap_del_irq_chip(axp20x->i2c_client->irq, axp20x->regmap_irqc); + regmap_del_irq_chip(axp20x->irq, axp20x->regmap_irqc); return 0; } - -static struct i2c_driver axp20x_i2c_driver = { - .driver = { - .name = "axp20x", - .of_match_table = of_match_ptr(axp20x_of_match), - .acpi_match_table = ACPI_PTR(axp20x_acpi_match), - }, - .probe = axp20x_i2c_probe, - .remove = axp20x_i2c_remove, - .id_table = axp20x_i2c_id, -}; - -module_i2c_driver(axp20x_i2c_driver); +EXPORT_SYMBOL(axp20x_device_remove); MODULE_DESCRIPTION("PMIC MFD core driver for AXP20X"); MODULE_AUTHOR("Carlo Caione "); -- cgit v1.1 From 2260a45356756285faa0b46f0afa53c7f251fb9c Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 12 Feb 2016 10:02:43 +0800 Subject: mfd: axp20x: Whitespace, open parenthesis alignment code style fixes This fixes some leftover code style issues in the axp20x core. Signed-off-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 8e569bc..3054ea4b 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -593,14 +593,14 @@ int axp20x_match_device(struct axp20x_dev *axp20x) dev_err(dev, "Unable to match OF ID\n"); return -ENODEV; } - axp20x->variant = (long) of_id->data; + axp20x->variant = (long)of_id->data; } else { acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev); if (!acpi_id || !acpi_id->driver_data) { dev_err(dev, "Unable to match ACPI ID and data\n"); return -ENODEV; } - axp20x->variant = (long) acpi_id->driver_data; + axp20x->variant = (long)acpi_id->driver_data; } switch (axp20x->variant) { @@ -634,7 +634,7 @@ int axp20x_match_device(struct axp20x_dev *axp20x) return -EINVAL; } dev_info(dev, "AXP20x variant %s found\n", - axp20x_model_names[axp20x->variant]); + axp20x_model_names[axp20x->variant]); return 0; } @@ -654,7 +654,7 @@ int axp20x_device_probe(struct axp20x_dev *axp20x) } ret = mfd_add_devices(axp20x->dev, -1, axp20x->cells, - axp20x->nr_cells, NULL, 0, NULL); + axp20x->nr_cells, NULL, 0, NULL); if (ret) { dev_err(axp20x->dev, "failed to add MFD devices: %d\n", ret); -- cgit v1.1 From 02071f0f797c989b342f46fbdf472ddb1c2cdee9 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 12 Feb 2016 10:02:44 +0800 Subject: mfd: axp20x: Add support for RSB based AXP223 PMIC The AXP223 is a new PMIC commonly paired with Allwinner A23/A33 SoCs. It is functionally identical to AXP221; only the regulator default voltage/status and the external host interface are different. Signed-off-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 11 +++++++ drivers/mfd/Makefile | 1 + drivers/mfd/axp20x-rsb.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/axp20x.c | 2 ++ 4 files changed, 94 insertions(+) create mode 100644 drivers/mfd/axp20x-rsb.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 0037b9c..ae3990b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -107,6 +107,17 @@ config MFD_AXP20X_I2C components like regulators or the PEK (Power Enable Key) under the corresponding menus. +config MFD_AXP20X_RSB + tristate "X-Powers AXP series PMICs with RSB" + select MFD_AXP20X + depends on SUNXI_RSB + help + If you say Y here you get support for the X-Powers AXP series power + management ICs (PMICs) controlled with RSB. + This driver include only the core APIs. You have to select individual + components like regulators or the PEK (Power Enable Key) under the + corresponding menus. + config MFD_CROS_EC tristate "ChromeOS Embedded Controller" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index dba4f99..c69ea74 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -112,6 +112,7 @@ obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o obj-$(CONFIG_MFD_AXP20X) += axp20x.o obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o +obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o obj-$(CONFIG_MFD_LP3943) += lp3943.o obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o diff --git a/drivers/mfd/axp20x-rsb.c b/drivers/mfd/axp20x-rsb.c new file mode 100644 index 0000000..28c2024 --- /dev/null +++ b/drivers/mfd/axp20x-rsb.c @@ -0,0 +1,80 @@ +/* + * RSB driver for the X-Powers' Power Management ICs + * + * AXP20x typically comprises an adaptive USB-Compatible PWM charger, BUCK DC-DC + * converters, LDOs, multiple 12-bit ADCs of voltage, current and temperature + * as well as configurable GPIOs. + * + * This driver supports the RSB variants. + * + * Copyright (C) 2015 Chen-Yu Tsai + * + * Author: Chen-Yu Tsai + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +static int axp20x_rsb_probe(struct sunxi_rsb_device *rdev) +{ + struct axp20x_dev *axp20x; + int ret; + + axp20x = devm_kzalloc(&rdev->dev, sizeof(*axp20x), GFP_KERNEL); + if (!axp20x) + return -ENOMEM; + + axp20x->dev = &rdev->dev; + axp20x->irq = rdev->irq; + dev_set_drvdata(&rdev->dev, axp20x); + + ret = axp20x_match_device(axp20x); + if (ret) + return ret; + + axp20x->regmap = devm_regmap_init_sunxi_rsb(rdev, axp20x->regmap_cfg); + if (IS_ERR(axp20x->regmap)) { + ret = PTR_ERR(axp20x->regmap); + dev_err(&rdev->dev, "regmap init failed: %d\n", ret); + return ret; + } + + return axp20x_device_probe(axp20x); +} + +static int axp20x_rsb_remove(struct sunxi_rsb_device *rdev) +{ + struct axp20x_dev *axp20x = sunxi_rsb_device_get_drvdata(rdev); + + return axp20x_device_remove(axp20x); +} + +static const struct of_device_id axp20x_rsb_of_match[] = { + { .compatible = "x-powers,axp223", .data = (void *)AXP223_ID }, + { }, +}; +MODULE_DEVICE_TABLE(of, axp20x_rsb_of_match); + +static struct sunxi_rsb_driver axp20x_rsb_driver = { + .driver = { + .name = "axp20x-rsb", + .of_match_table = of_match_ptr(axp20x_rsb_of_match), + }, + .probe = axp20x_rsb_probe, + .remove = axp20x_rsb_remove, +}; +module_sunxi_rsb_driver(axp20x_rsb_driver); + +MODULE_DESCRIPTION("PMIC MFD sunXi RSB driver for AXP20X"); +MODULE_AUTHOR("Chen-Yu Tsai "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 3054ea4b..a57d6e9 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -35,6 +35,7 @@ static const char * const axp20x_model_names[] = { "AXP202", "AXP209", "AXP221", + "AXP223", "AXP288", }; @@ -618,6 +619,7 @@ int axp20x_match_device(struct axp20x_dev *axp20x) axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip; break; case AXP221_ID: + case AXP223_ID: axp20x->nr_cells = ARRAY_SIZE(axp22x_cells); axp20x->cells = axp22x_cells; axp20x->regmap_cfg = &axp22x_regmap_config; -- cgit v1.1 From 04e0981c67e6a7ba8aacac731d925f4ff21b67fb Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 12 Feb 2016 10:02:45 +0800 Subject: regulator: axp20x: Support new AXP223 PMIC The AXP223 is a new PMIC commonly paired with Allwinner A23/A33 SoCs. It is functionally identical to AXP221; only the regulator default voltage/status and the external host interface are different. Signed-off-by: Chen-Yu Tsai Reviewed-by: Mark Brown Signed-off-by: Lee Jones --- drivers/regulator/axp20x-regulator.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index f2e1a39..e86d1fc 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -244,6 +244,7 @@ static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) step = 75; break; case AXP221_ID: + case AXP223_ID: min = 1800; max = 4050; def = 3000; @@ -322,6 +323,7 @@ static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 work break; case AXP221_ID: + case AXP223_ID: if (id < AXP22X_DCDC1 || id > AXP22X_DCDC5) return -EINVAL; @@ -360,6 +362,7 @@ static int axp20x_regulator_probe(struct platform_device *pdev) nregulators = AXP20X_REG_ID_MAX; break; case AXP221_ID: + case AXP223_ID: regulators = axp22x_regulators; nregulators = AXP22X_REG_ID_MAX; break; -- cgit v1.1 From 0708677558dd7d03e70292549433f0f6b04a5e00 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 12 Feb 2016 12:15:29 +0100 Subject: iio: adc/imx25-gcq: Move incorrect do_div The newly added driver uses do_div() to device a 32-bit number, which now provokes a warning: drivers/iio/adc/fsl-imx25-gcq.c: In function 'mx25_gcq_setup_cfgs': include/asm-generic/div64.h:207:28: warning: comparison of distinct pointer types lacks a cast (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \ This replaces the do_div() call with a straight division operator. Signed-off-by: Arnd Bergmann Fixes: 6df2e98c3ea5 ("iio: adc: Add imx25-gcq ADC driver") Signed-off-by: Lee Jones --- drivers/iio/adc/fsl-imx25-gcq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c index 2fd1927..72b32c1 100644 --- a/drivers/iio/adc/fsl-imx25-gcq.c +++ b/drivers/iio/adc/fsl-imx25-gcq.c @@ -233,7 +233,7 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, priv->channel_vref_mv[reg] = regulator_get_voltage(priv->vref[refp]); /* Conversion from uV to mV */ - do_div(priv->channel_vref_mv[reg], 1000); + priv->channel_vref_mv[reg] /= 1000; break; case MX25_ADC_REFP_INT: priv->channel_vref_mv[reg] = 2500; -- cgit v1.1 From dbc352b9f16de45277abf2e30d0317ce55fc1e57 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Mon, 11 Jan 2016 21:46:49 +0200 Subject: mfd: tps65010: Fix init when the driver is built-in tps65010 driver's initcall cannot succeed when the driver is built-in, because it expects that the I2C probe is completed at initcall time; this cannot happen as MFD is initialized before I2C. Also on systems where the chip is not present there is unnecessary 30 ms delay during the boot. Instead of waiting for probe to finish, just register the I2C device. If some boards need retry mechanism for startup glitches, that should be done in the actual probe function. Also delete the driver banner message. The patch allows to use tps65010 again with OMAP1 (where it's required to be built-in) and enables e.g. USB and LED functionality on OMAP5912 OSK. Signed-off-by: Aaro Koskinen Signed-off-by: Lee Jones --- drivers/mfd/tps65010.c | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/tps65010.c b/drivers/mfd/tps65010.c index 83e615e..495e451 100644 --- a/drivers/mfd/tps65010.c +++ b/drivers/mfd/tps65010.c @@ -1059,26 +1059,7 @@ EXPORT_SYMBOL(tps65013_set_low_pwr); static int __init tps_init(void) { - u32 tries = 3; - int status = -ENODEV; - - printk(KERN_INFO "%s: version %s\n", DRIVER_NAME, DRIVER_VERSION); - - /* some boards have startup glitches */ - while (tries--) { - status = i2c_add_driver(&tps65010_driver); - if (the_tps) - break; - i2c_del_driver(&tps65010_driver); - if (!tries) { - printk(KERN_ERR "%s: no chip?\n", DRIVER_NAME); - return -ENODEV; - } - pr_debug("%s: re-probe ...\n", DRIVER_NAME); - msleep(10); - } - - return status; + return i2c_add_driver(&tps65010_driver); } /* NOTE: this MUST be initialized before the other parts of the system * that rely on it ... but after the i2c bus on which this relies. -- cgit v1.1 From 84cb36cac581c915ef4e8b70abb73e084325df92 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 22 Jan 2016 16:48:46 +0200 Subject: mfd: intel-lpss: Remove clock tree on error path We forgot to remove the clock tree if something goes wrong in ->probe(). Add a call to intel_lpss_unregister_clock() on error path in ->probe() to fix the potential issue. Fixes: 4b45efe85263 (mfd: Add support for Intel Sunrisepoint LPSS devices) Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index 1743788..1bbbe87 100644 --- a/drivers/mfd/intel-lpss.c +++ b/drivers/mfd/intel-lpss.c @@ -453,6 +453,7 @@ int intel_lpss_probe(struct device *dev, err_remove_ltr: intel_lpss_debugfs_remove(lpss); intel_lpss_ltr_hide(lpss); + intel_lpss_unregister_clock(lpss); err_clk_register: ida_simple_remove(&intel_lpss_devid_ida, lpss->devid); -- cgit v1.1 From c2e04af072647465358095ce55c29cba7d5c18a6 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 21 Jan 2016 08:49:31 +0800 Subject: mfd: Allow i2c modular drivers to build with I2C=m These drivers can be built as module, so make them depend on I2C rather than I2C=y. Signed-off-by: Axel Lin Acked-by: Adam Thomson Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 19a5cae..f479658 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -218,7 +218,7 @@ config MFD_DA9062 select MFD_CORE select REGMAP_I2C select REGMAP_IRQ - depends on I2C=y + depends on I2C help Say yes here for support for the Dialog Semiconductor DA9062 PMIC. This includes the I2C driver and core APIs. @@ -230,7 +230,7 @@ config MFD_DA9063 select MFD_CORE select REGMAP_I2C select REGMAP_IRQ - depends on I2C=y + depends on I2C help Say yes here for support for the Dialog Semiconductor DA9063 PMIC. This includes the I2C driver and core APIs. @@ -239,7 +239,7 @@ config MFD_DA9063 config MFD_DA9150 tristate "Dialog Semiconductor DA9150 Charger Fuel-Gauge chip" - depends on I2C=y + depends on I2C select MFD_CORE select REGMAP_I2C select REGMAP_IRQ @@ -469,7 +469,7 @@ config MFD_KEMPLD config MFD_88PM800 tristate "Marvell 88PM800" - depends on I2C=y + depends on I2C select REGMAP_I2C select REGMAP_IRQ select MFD_CORE @@ -481,7 +481,7 @@ config MFD_88PM800 config MFD_88PM805 tristate "Marvell 88PM805" - depends on I2C=y + depends on I2C select REGMAP_I2C select REGMAP_IRQ select MFD_CORE @@ -562,7 +562,7 @@ config MFD_MAX77843 config MFD_MAX8907 tristate "Maxim Semiconductor MAX8907 PMIC Support" select MFD_CORE - depends on I2C=y + depends on I2C select REGMAP_I2C select REGMAP_IRQ help @@ -767,7 +767,7 @@ config MFD_RTSX_PCI config MFD_RT5033 tristate "Richtek RT5033 Power Management IC" - depends on I2C=y + depends on I2C select MFD_CORE select REGMAP_I2C select REGMAP_IRQ -- cgit v1.1 From 023269cc989bbae2c4c59dc5999f6db51c76f4d0 Mon Sep 17 00:00:00 2001 From: Huiquan Zhong Date: Fri, 15 Jan 2016 00:12:31 +0800 Subject: mfd: lpss: Add PCI IDs for Intel Broxton B-Step platform Add PCI IDs for Intel Broxton B-Step platform, which have same LPSS devices with A-Step. Signed-off-by: Huiquan Zhong Signed-off-by: Qipeng Zha cked-by: Mika Westerberg Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss-pci.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index a7136c7..afc7af9 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -112,7 +112,7 @@ static const struct intel_lpss_platform_info bxt_i2c_info = { }; static const struct pci_device_id intel_lpss_pci_ids[] = { - /* BXT */ + /* BXT A-Step */ { PCI_VDEVICE(INTEL, 0x0aac), (kernel_ulong_t)&bxt_i2c_info }, { PCI_VDEVICE(INTEL, 0x0aae), (kernel_ulong_t)&bxt_i2c_info }, { PCI_VDEVICE(INTEL, 0x0ab0), (kernel_ulong_t)&bxt_i2c_info }, @@ -128,6 +128,23 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x0ac4), (kernel_ulong_t)&bxt_info }, { PCI_VDEVICE(INTEL, 0x0ac6), (kernel_ulong_t)&bxt_info }, { PCI_VDEVICE(INTEL, 0x0aee), (kernel_ulong_t)&bxt_uart_info }, + /* BXT B-Step */ + { PCI_VDEVICE(INTEL, 0x1aac), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1aae), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1ab0), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1ab2), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1ab4), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1ab6), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1ab8), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1aba), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x1abc), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x1abe), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x1ac0), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x1ac2), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x1ac4), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x1ac6), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x1aee), (kernel_ulong_t)&bxt_uart_info }, + /* APL */ { PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&bxt_i2c_info }, { PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&bxt_i2c_info }, -- cgit v1.1 From b45b719ee03162eb54772c30a6474d57b41b6b54 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Mon, 25 Jan 2016 09:50:11 -0600 Subject: mfd: tps65086: Add driver for the TPS65086 PMIC Add support for the TPS65912 device. It provides communication through I2C and contains the following components: - Regulators - Load switches - GPO controller Signed-off-by: Andrew F. Davis Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 13 +++++ drivers/mfd/Makefile | 1 + drivers/mfd/tps65086.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 drivers/mfd/tps65086.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f479658..97d9406 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1130,6 +1130,19 @@ config TPS6507X This driver can also be built as a module. If so, the module will be called tps6507x. +config MFD_TPS65086 + tristate "TI TPS65086 Power Management Integrated Chips (PMICs)" + select REGMAP + select REGMAP_IRQ + select REGMAP_I2C + depends on I2C + help + If you say yes here you get support for the TPS65086 series of + Power Management chips. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. + config TPS65911_COMPARATOR tristate diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index b7a3cd9..d2ddcf4 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_MFD_WM8994) += wm8994.o obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS6507X) += tps6507x.o +obj-$(CONFIG_MFD_TPS65086) += tps65086.o obj-$(CONFIG_MFD_TPS65217) += tps65217.o obj-$(CONFIG_MFD_TPS65218) += tps65218.o obj-$(CONFIG_MFD_TPS65910) += tps65910.o diff --git a/drivers/mfd/tps65086.c b/drivers/mfd/tps65086.c new file mode 100644 index 0000000..43119a6 --- /dev/null +++ b/drivers/mfd/tps65086.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis + * + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether expressed or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2 for more details. + * + * Based on the TPS65912 driver + */ + +#include +#include +#include +#include + +#include + +static const struct mfd_cell tps65086_cells[] = { + { .name = "tps65086-regulator", }, + { .name = "tps65086-gpio", }, +}; + +static const struct regmap_range tps65086_yes_ranges[] = { + regmap_reg_range(TPS65086_IRQ, TPS65086_IRQ), + regmap_reg_range(TPS65086_PMICSTAT, TPS65086_SHUTDNSRC), + regmap_reg_range(TPS65086_GPOCTRL, TPS65086_GPOCTRL), + regmap_reg_range(TPS65086_PG_STATUS1, TPS65086_OC_STATUS), +}; + +static const struct regmap_access_table tps65086_volatile_table = { + .yes_ranges = tps65086_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(tps65086_yes_ranges), +}; + +static const struct regmap_config tps65086_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &tps65086_volatile_table, +}; + +static const struct regmap_irq tps65086_irqs[] = { + REGMAP_IRQ_REG(TPS65086_IRQ_DIETEMP, 0, TPS65086_IRQ_DIETEMP_MASK), + REGMAP_IRQ_REG(TPS65086_IRQ_SHUTDN, 0, TPS65086_IRQ_SHUTDN_MASK), + REGMAP_IRQ_REG(TPS65086_IRQ_FAULT, 0, TPS65086_IRQ_FAULT_MASK), +}; + +static struct regmap_irq_chip tps65086_irq_chip = { + .name = "tps65086", + .status_base = TPS65086_IRQ, + .mask_base = TPS65086_IRQ_MASK, + .ack_base = TPS65086_IRQ, + .init_ack_masked = true, + .num_regs = 1, + .irqs = tps65086_irqs, + .num_irqs = ARRAY_SIZE(tps65086_irqs), +}; + +static const struct of_device_id tps65086_of_match_table[] = { + { .compatible = "ti,tps65086", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tps65086_of_match_table); + +static int tps65086_probe(struct i2c_client *client, + const struct i2c_device_id *ids) +{ + struct tps65086 *tps; + unsigned int version; + int ret; + + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + i2c_set_clientdata(client, tps); + tps->dev = &client->dev; + tps->irq = client->irq; + + tps->regmap = devm_regmap_init_i2c(client, &tps65086_regmap_config); + if (IS_ERR(tps->regmap)) { + dev_err(tps->dev, "Failed to initialize register map\n"); + return PTR_ERR(tps->regmap); + } + + ret = regmap_read(tps->regmap, TPS65086_DEVICEID, &version); + if (ret) { + dev_err(tps->dev, "Failed to read revision register\n"); + return ret; + } + + dev_info(tps->dev, "Device: TPS65086%01lX, OTP: %c, Rev: %ld\n", + (version & TPS65086_DEVICEID_PART_MASK), + (char)((version & TPS65086_DEVICEID_OTP_MASK) >> 4) + 'A', + (version & TPS65086_DEVICEID_REV_MASK) >> 6); + + ret = regmap_add_irq_chip(tps->regmap, tps->irq, IRQF_ONESHOT, 0, + &tps65086_irq_chip, &tps->irq_data); + if (ret) { + dev_err(tps->dev, "Failed to register IRQ chip\n"); + return ret; + } + + ret = mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO, tps65086_cells, + ARRAY_SIZE(tps65086_cells), NULL, 0, + regmap_irq_get_domain(tps->irq_data)); + if (ret) { + regmap_del_irq_chip(tps->irq, tps->irq_data); + return ret; + } + + return 0; +} + +static int tps65086_remove(struct i2c_client *client) +{ + struct tps65086 *tps = i2c_get_clientdata(client); + + regmap_del_irq_chip(tps->irq, tps->irq_data); + + return 0; +} + +static const struct i2c_device_id tps65086_id_table[] = { + { "tps65086", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, tps65086_id_table); + +static struct i2c_driver tps65086_driver = { + .driver = { + .name = "tps65086", + .of_match_table = tps65086_of_match_table, + }, + .probe = tps65086_probe, + .remove = tps65086_remove, + .id_table = tps65086_id_table, +}; +module_i2c_driver(tps65086_driver); + +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("TPS65086 PMIC Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From a7e46317722ccdac6ae3bdb9476a1ec21b7aab6d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 25 Jan 2016 17:02:24 +0100 Subject: mfd: db8500: Avoid uninitialized variable reference The prcmu_config_clkout() function ensures that the 'clkout' argument can only be '0' or '1' using an appropriate BUG_ON(), so the compiler should know that the div_mask, mask, and bits variables are always initialized later on. However, it doesn't understand this in gcc-5.2 and produces a false positive warning instead: drivers/mfd/db8500-prcmu.c: In function 'prcmu_config_clkout': drivers/mfd/db8500-prcmu.c:762:10: error: 'div_mask' may be used uninitialized in this function [-Werror=maybe-uninitialized] if (val & div_mask) { ^ drivers/mfd/db8500-prcmu.c:769:13: error: 'mask' may be used uninitialized in this function [-Werror=maybe-uninitialized] if ((val & mask & ~div_mask) != bits) { ^ drivers/mfd/db8500-prcmu.c:757:7: error: 'bits' may be used uninitialized in this function [-Werror=maybe-uninitialized] Replacing the switch() statement with an equivalent if() lets gcc figure this out reliably and avoids the warnings. Signed-off-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/db8500-prcmu.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index e6e4bac..2f4a127 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -739,20 +739,17 @@ int prcmu_config_clkout(u8 clkout, u8 source, u8 div) if (!div && !requests[clkout]) return -EINVAL; - switch (clkout) { - case 0: + if (clkout == 0) { div_mask = PRCM_CLKOCR_CLKODIV0_MASK; mask = (PRCM_CLKOCR_CLKODIV0_MASK | PRCM_CLKOCR_CLKOSEL0_MASK); bits = ((source << PRCM_CLKOCR_CLKOSEL0_SHIFT) | (div << PRCM_CLKOCR_CLKODIV0_SHIFT)); - break; - case 1: + } else { div_mask = PRCM_CLKOCR_CLKODIV1_MASK; mask = (PRCM_CLKOCR_CLKODIV1_MASK | PRCM_CLKOCR_CLKOSEL1_MASK | PRCM_CLKOCR_CLK1TYPE); bits = ((source << PRCM_CLKOCR_CLKOSEL1_SHIFT) | (div << PRCM_CLKOCR_CLKODIV1_SHIFT)); - break; } bits &= mask; -- cgit v1.1 From ca668f0edfae65438c3f0a3ad5d3e59e3515915f Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 29 Jan 2016 10:35:51 +0100 Subject: mfd: syscon: Set regmap max_register in of_syscon_register Determine the regmap max_register configuration from the io resource size and the reg-io-width device tree property. Signed-off-by: Philipp Zabel Acked-by: Arnd Bergmann Signed-off-by: Lee Jones --- drivers/mfd/syscon.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index b7aabee..99e8f88 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -50,6 +50,7 @@ static struct syscon *of_syscon_register(struct device_node *np) u32 reg_io_width; int ret; struct regmap_config syscon_config = syscon_regmap_config; + struct resource res; if (!of_device_is_compatible(np, "syscon")) return ERR_PTR(-EINVAL); @@ -58,7 +59,12 @@ static struct syscon *of_syscon_register(struct device_node *np) if (!syscon) return ERR_PTR(-ENOMEM); - base = of_iomap(np, 0); + if (of_address_to_resource(np, 0, &res)) { + ret = -ENOMEM; + goto err_map; + } + + base = ioremap(res.start, resource_size(&res)); if (!base) { ret = -ENOMEM; goto err_map; @@ -81,6 +87,7 @@ static struct syscon *of_syscon_register(struct device_node *np) syscon_config.reg_stride = reg_io_width; syscon_config.val_bits = reg_io_width * 8; + syscon_config.max_register = resource_size(&res) - reg_io_width; regmap = regmap_init_mmio(NULL, base, &syscon_config); if (IS_ERR(regmap)) { -- cgit v1.1 From c131045d5b074bcb749421c4b3f11f0655bdc0b6 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 29 Jan 2016 10:30:16 +0100 Subject: mfd: syscon: Make syscon_regmap_config const syscon_regmap_config can be made const if syscon_probe() creates a local copy on the stack, just like syscon_register() does. Signed-off-by: Philipp Zabel Signed-off-by: Lee Jones --- drivers/mfd/syscon.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 99e8f88..2f2225e 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -36,7 +36,7 @@ struct syscon { struct list_head list; }; -static struct regmap_config syscon_regmap_config = { +static const struct regmap_config syscon_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, @@ -199,6 +199,7 @@ static int syscon_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct syscon_platform_data *pdata = dev_get_platdata(dev); struct syscon *syscon; + struct regmap_config syscon_config = syscon_regmap_config; struct resource *res; void __iomem *base; @@ -214,11 +215,10 @@ static int syscon_probe(struct platform_device *pdev) if (!base) return -ENOMEM; - syscon_regmap_config.max_register = res->end - res->start - 3; + syscon_config.max_register = res->end - res->start - 3; if (pdata) - syscon_regmap_config.name = pdata->label; - syscon->regmap = devm_regmap_init_mmio(dev, base, - &syscon_regmap_config); + syscon_config.name = pdata->label; + syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_config); if (IS_ERR(syscon->regmap)) { dev_err(dev, "regmap init failed\n"); return PTR_ERR(syscon->regmap); -- cgit v1.1 From 5dbd47840a1f4b31bf2acaacfb85d95a7038fe4c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 9 Feb 2016 10:52:02 +0000 Subject: mfd: arizona: Update small typo in Arizona SPI Kconfig The help text had a copy and paste error and refers to I2C in the SPI section. This patch corrects this typo. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 97d9406..87cee68 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1422,7 +1422,7 @@ config MFD_ARIZONA_SPI depends on SPI_MASTER help Support for the Cirrus Logic/Wolfson Microelectronics Arizona platform - audio SoC core functionality controlled via I2C. + audio SoC core functionality controlled via SPI. config MFD_CS47L24 bool "Cirrus Logic CS47L24 and WM1831" -- cgit v1.1 From 036fa859cd9b9195853eae81295689c515451c58 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 9 Feb 2016 10:51:57 +0000 Subject: mfd: wm5110: Fix defaults array based on testing My automated test is back and now can check defaults against the actual hardware. This patch updates the defaults array for the differences detected and removes a couple of completely unused registers. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm5110-tables.c | 82 ++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index c18e11f..8e74e71 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -676,8 +676,8 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000008, 0x0019 }, /* R8 - Ctrl IF SPI CFG 1 */ { 0x00000009, 0x0001 }, /* R9 - Ctrl IF I2C1 CFG 1 */ { 0x0000000A, 0x0001 }, /* R10 - Ctrl IF I2C2 CFG 1 */ - { 0x0000000B, 0x0036 }, /* R11 - Ctrl IF I2C1 CFG 2 */ - { 0x0000000C, 0x0036 }, /* R12 - Ctrl IF I2C2 CFG 2 */ + { 0x0000000B, 0x001A }, /* R11 - Ctrl IF I2C1 CFG 2 */ + { 0x0000000C, 0x001A }, /* R12 - Ctrl IF I2C2 CFG 2 */ { 0x00000020, 0x0000 }, /* R32 - Tone Generator 1 */ { 0x00000021, 0x1000 }, /* R33 - Tone Generator 2 */ { 0x00000022, 0x0000 }, /* R34 - Tone Generator 3 */ @@ -723,14 +723,12 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000154, 0x0000 }, /* R340 - Rate Estimator 3 */ { 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */ { 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */ - { 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */ + { 0x00000171, 0x0002 }, /* R369 - FLL1 Control 1 */ { 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */ { 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */ { 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */ { 0x00000175, 0x0006 }, /* R373 - FLL1 Control 5 */ { 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */ - { 0x00000177, 0x0281 }, /* R375 - FLL1 Loop Filter Test 1 */ - { 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */ { 0x00000179, 0x0000 }, /* R376 - FLL1 Control 7 */ { 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */ { 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */ @@ -740,15 +738,13 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000186, 0x0000 }, /* R390 - FLL1 Synchroniser 6 */ { 0x00000187, 0x0001 }, /* R390 - FLL1 Synchroniser 7 */ { 0x00000189, 0x0000 }, /* R393 - FLL1 Spread Spectrum */ - { 0x0000018A, 0x0004 }, /* R394 - FLL1 GPIO Clock */ - { 0x00000191, 0x0000 }, /* R401 - FLL2 Control 1 */ + { 0x0000018A, 0x000C }, /* R394 - FLL1 GPIO Clock */ + { 0x00000191, 0x0002 }, /* R401 - FLL2 Control 1 */ { 0x00000192, 0x0008 }, /* R402 - FLL2 Control 2 */ { 0x00000193, 0x0018 }, /* R403 - FLL2 Control 3 */ { 0x00000194, 0x007D }, /* R404 - FLL2 Control 4 */ { 0x00000195, 0x000C }, /* R405 - FLL2 Control 5 */ { 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */ - { 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */ - { 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */ { 0x00000199, 0x0000 }, /* R408 - FLL2 Control 7 */ { 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */ { 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */ @@ -758,7 +754,7 @@ static const struct reg_default wm5110_reg_default[] = { { 0x000001A6, 0x0000 }, /* R422 - FLL2 Synchroniser 6 */ { 0x000001A7, 0x0001 }, /* R422 - FLL2 Synchroniser 7 */ { 0x000001A9, 0x0000 }, /* R425 - FLL2 Spread Spectrum */ - { 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */ + { 0x000001AA, 0x000C }, /* R426 - FLL2 GPIO Clock */ { 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */ { 0x00000210, 0x0184 }, /* R528 - LDO1 Control 1 */ { 0x00000213, 0x03E4 }, /* R531 - LDO2 Control 1 */ @@ -771,9 +767,9 @@ static const struct reg_default wm5110_reg_default[] = { { 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */ { 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */ { 0x000002A6, 0x3737 }, /* R678 - Mic Detect Level 1 */ - { 0x000002A7, 0x372C }, /* R679 - Mic Detect Level 2 */ + { 0x000002A7, 0x2C37 }, /* R679 - Mic Detect Level 2 */ { 0x000002A8, 0x1422 }, /* R680 - Mic Detect Level 3 */ - { 0x000002A9, 0x300A }, /* R681 - Mic Detect Level 4 */ + { 0x000002A9, 0x030A }, /* R681 - Mic Detect Level 4 */ { 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */ { 0x000002CB, 0x0000 }, /* R715 - Isolation control */ { 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */ @@ -810,53 +806,53 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000409, 0x0022 }, /* R1033 - Output Volume Ramp */ { 0x00000410, 0x0080 }, /* R1040 - Output Path Config 1L */ { 0x00000411, 0x0180 }, /* R1041 - DAC Digital Volume 1L */ - { 0x00000412, 0x0080 }, /* R1042 - DAC Volume Limit 1L */ + { 0x00000412, 0x0081 }, /* R1042 - DAC Volume Limit 1L */ { 0x00000413, 0x0001 }, /* R1043 - Noise Gate Select 1L */ { 0x00000414, 0x0080 }, /* R1044 - Output Path Config 1R */ { 0x00000415, 0x0180 }, /* R1045 - DAC Digital Volume 1R */ - { 0x00000416, 0x0080 }, /* R1046 - DAC Volume Limit 1R */ + { 0x00000416, 0x0081 }, /* R1046 - DAC Volume Limit 1R */ { 0x00000417, 0x0002 }, /* R1047 - Noise Gate Select 1R */ { 0x00000418, 0x0080 }, /* R1048 - Output Path Config 2L */ { 0x00000419, 0x0180 }, /* R1049 - DAC Digital Volume 2L */ - { 0x0000041A, 0x0080 }, /* R1050 - DAC Volume Limit 2L */ + { 0x0000041A, 0x0081 }, /* R1050 - DAC Volume Limit 2L */ { 0x0000041B, 0x0004 }, /* R1051 - Noise Gate Select 2L */ { 0x0000041C, 0x0080 }, /* R1052 - Output Path Config 2R */ { 0x0000041D, 0x0180 }, /* R1053 - DAC Digital Volume 2R */ - { 0x0000041E, 0x0080 }, /* R1054 - DAC Volume Limit 2R */ + { 0x0000041E, 0x0081 }, /* R1054 - DAC Volume Limit 2R */ { 0x0000041F, 0x0008 }, /* R1055 - Noise Gate Select 2R */ { 0x00000420, 0x0080 }, /* R1056 - Output Path Config 3L */ { 0x00000421, 0x0180 }, /* R1057 - DAC Digital Volume 3L */ - { 0x00000422, 0x0080 }, /* R1058 - DAC Volume Limit 3L */ + { 0x00000422, 0x0081 }, /* R1058 - DAC Volume Limit 3L */ { 0x00000423, 0x0010 }, /* R1059 - Noise Gate Select 3L */ { 0x00000424, 0x0080 }, /* R1060 - Output Path Config 3R */ { 0x00000425, 0x0180 }, /* R1061 - DAC Digital Volume 3R */ - { 0x00000426, 0x0080 }, /* R1062 - DAC Volume Limit 3R */ + { 0x00000426, 0x0081 }, /* R1062 - DAC Volume Limit 3R */ { 0x00000427, 0x0020 }, /* R1063 - Noise Gate Select 3R */ { 0x00000428, 0x0000 }, /* R1064 - Output Path Config 4L */ { 0x00000429, 0x0180 }, /* R1065 - DAC Digital Volume 4L */ - { 0x0000042A, 0x0080 }, /* R1066 - Out Volume 4L */ + { 0x0000042A, 0x0081 }, /* R1066 - Out Volume 4L */ { 0x0000042B, 0x0040 }, /* R1067 - Noise Gate Select 4L */ { 0x0000042C, 0x0000 }, /* R1068 - Output Path Config 4R */ { 0x0000042D, 0x0180 }, /* R1069 - DAC Digital Volume 4R */ - { 0x0000042E, 0x0080 }, /* R1070 - Out Volume 4R */ + { 0x0000042E, 0x0081 }, /* R1070 - Out Volume 4R */ { 0x0000042F, 0x0080 }, /* R1071 - Noise Gate Select 4R */ { 0x00000430, 0x0000 }, /* R1072 - Output Path Config 5L */ { 0x00000431, 0x0180 }, /* R1073 - DAC Digital Volume 5L */ - { 0x00000432, 0x0080 }, /* R1074 - DAC Volume Limit 5L */ + { 0x00000432, 0x0081 }, /* R1074 - DAC Volume Limit 5L */ { 0x00000433, 0x0100 }, /* R1075 - Noise Gate Select 5L */ { 0x00000434, 0x0000 }, /* R1076 - Output Path Config 5R */ { 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */ - { 0x00000436, 0x0080 }, /* R1078 - DAC Volume Limit 5R */ + { 0x00000436, 0x0081 }, /* R1078 - DAC Volume Limit 5R */ { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */ { 0x00000438, 0x0000 }, /* R1080 - Output Path Config 6L */ { 0x00000439, 0x0180 }, /* R1081 - DAC Digital Volume 6L */ - { 0x0000043A, 0x0080 }, /* R1082 - DAC Volume Limit 6L */ + { 0x0000043A, 0x0081 }, /* R1082 - DAC Volume Limit 6L */ { 0x0000043B, 0x0400 }, /* R1083 - Noise Gate Select 6L */ { 0x0000043C, 0x0000 }, /* R1084 - Output Path Config 6R */ { 0x0000043D, 0x0180 }, /* R1085 - DAC Digital Volume 6R */ - { 0x0000043E, 0x0080 }, /* R1086 - DAC Volume Limit 6R */ + { 0x0000043E, 0x0081 }, /* R1086 - DAC Volume Limit 6R */ { 0x0000043F, 0x0800 }, /* R1087 - Noise Gate Select 6R */ - { 0x00000440, 0x8FFF }, /* R1088 - DRE Enable */ + { 0x00000440, 0x003F }, /* R1088 - DRE Enable */ { 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */ { 0x00000458, 0x0000 }, /* R1112 - Noise Gate Control */ { 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */ @@ -864,8 +860,8 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000492, 0x0069 }, /* R1170 - PDM SPK2 CTRL 1 */ { 0x00000493, 0x0000 }, /* R1171 - PDM SPK2 CTRL 2 */ { 0x000004A0, 0x3480 }, /* R1184 - HP1 Short Circuit Ctrl */ - { 0x000004A1, 0x3480 }, /* R1185 - HP2 Short Circuit Ctrl */ - { 0x000004A2, 0x3480 }, /* R1186 - HP3 Short Circuit Ctrl */ + { 0x000004A1, 0x3400 }, /* R1185 - HP2 Short Circuit Ctrl */ + { 0x000004A2, 0x3400 }, /* R1186 - HP3 Short Circuit Ctrl */ { 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */ { 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */ { 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */ @@ -1483,23 +1479,23 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000C10, 0x1000 }, /* R3088 - GPIO Debounce Config */ { 0x00000C18, 0x0000 }, /* R3096 - GP Switch 1 */ { 0x00000C20, 0x8002 }, /* R3104 - Misc Pad Ctrl 1 */ - { 0x00000C21, 0x8001 }, /* R3105 - Misc Pad Ctrl 2 */ + { 0x00000C21, 0x0001 }, /* R3105 - Misc Pad Ctrl 2 */ { 0x00000C22, 0x0000 }, /* R3106 - Misc Pad Ctrl 3 */ { 0x00000C23, 0x0000 }, /* R3107 - Misc Pad Ctrl 4 */ { 0x00000C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 5 */ { 0x00000C25, 0x0000 }, /* R3109 - Misc Pad Ctrl 6 */ - { 0x00000C30, 0x8282 }, /* R3120 - Misc Pad Ctrl 7 */ - { 0x00000C31, 0x0082 }, /* R3121 - Misc Pad Ctrl 8 */ - { 0x00000C32, 0x8282 }, /* R3122 - Misc Pad Ctrl 9 */ - { 0x00000C33, 0x8282 }, /* R3123 - Misc Pad Ctrl 10 */ - { 0x00000C34, 0x8282 }, /* R3124 - Misc Pad Ctrl 11 */ - { 0x00000C35, 0x8282 }, /* R3125 - Misc Pad Ctrl 12 */ - { 0x00000C36, 0x8282 }, /* R3126 - Misc Pad Ctrl 13 */ - { 0x00000C37, 0x8282 }, /* R3127 - Misc Pad Ctrl 14 */ - { 0x00000C38, 0x8282 }, /* R3128 - Misc Pad Ctrl 15 */ - { 0x00000C39, 0x8282 }, /* R3129 - Misc Pad Ctrl 16 */ - { 0x00000C3A, 0x8282 }, /* R3130 - Misc Pad Ctrl 17 */ - { 0x00000C3B, 0x8282 }, /* R3131 - Misc Pad Ctrl 18 */ + { 0x00000C30, 0x0404 }, /* R3120 - Misc Pad Ctrl 7 */ + { 0x00000C31, 0x0004 }, /* R3121 - Misc Pad Ctrl 8 */ + { 0x00000C32, 0x0404 }, /* R3122 - Misc Pad Ctrl 9 */ + { 0x00000C33, 0x0404 }, /* R3123 - Misc Pad Ctrl 10 */ + { 0x00000C34, 0x0404 }, /* R3124 - Misc Pad Ctrl 11 */ + { 0x00000C35, 0x0404 }, /* R3125 - Misc Pad Ctrl 12 */ + { 0x00000C36, 0x0404 }, /* R3126 - Misc Pad Ctrl 13 */ + { 0x00000C37, 0x0404 }, /* R3127 - Misc Pad Ctrl 14 */ + { 0x00000C38, 0x0004 }, /* R3128 - Misc Pad Ctrl 15 */ + { 0x00000C39, 0x0404 }, /* R3129 - Misc Pad Ctrl 16 */ + { 0x00000C3A, 0x0404 }, /* R3130 - Misc Pad Ctrl 17 */ + { 0x00000C3B, 0x0404 }, /* R3131 - Misc Pad Ctrl 18 */ { 0x00000D08, 0xFFFF }, /* R3336 - Interrupt Status 1 Mask */ { 0x00000D09, 0xFFFF }, /* R3337 - Interrupt Status 2 Mask */ { 0x00000D0A, 0xFFFF }, /* R3338 - Interrupt Status 3 Mask */ @@ -1641,7 +1637,7 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000F0D, 0x0000 }, /* R3853 - ANC Coefficient */ { 0x00000F0E, 0x0000 }, /* R3854 - ANC Coefficient */ { 0x00000F0F, 0x0000 }, /* R3855 - ANC Coefficient */ - { 0x00000F10, 0x0000 }, /* R3856 - ANC Coefficient */ + { 0x00000F10, 0x0001 }, /* R3856 - ANC Coefficient */ { 0x00000F11, 0x0000 }, /* R3857 - ANC Coefficient */ { 0x00000F12, 0x0000 }, /* R3858 - ANC Coefficient */ { 0x00000F15, 0x0000 }, /* R3861 - FCL Filter Control */ @@ -1947,8 +1943,6 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL1_CONTROL_5: case ARIZONA_FLL1_CONTROL_6: case ARIZONA_FLL1_CONTROL_7: - case ARIZONA_FLL1_LOOP_FILTER_TEST_1: - case ARIZONA_FLL1_NCO_TEST_0: case ARIZONA_FLL1_SYNCHRONISER_1: case ARIZONA_FLL1_SYNCHRONISER_2: case ARIZONA_FLL1_SYNCHRONISER_3: @@ -1965,8 +1959,6 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL2_CONTROL_5: case ARIZONA_FLL2_CONTROL_6: case ARIZONA_FLL2_CONTROL_7: - case ARIZONA_FLL2_LOOP_FILTER_TEST_1: - case ARIZONA_FLL2_NCO_TEST_0: case ARIZONA_FLL2_SYNCHRONISER_1: case ARIZONA_FLL2_SYNCHRONISER_2: case ARIZONA_FLL2_SYNCHRONISER_3: -- cgit v1.1 From 516c95f0b0a8814bf982bc71c7e083d9a8057596 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 9 Feb 2016 10:51:58 +0000 Subject: mfd: wm5102: Fix defaults array based on testing My automated test is back and now can check defaults against the actual hardware. This patch updates the defaults array for the differences detected and removes a couple of completely unused registers. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm5102-tables.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 0386eaf..ab8b23b 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -297,7 +297,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */ { 0x00000175, 0x0004 }, /* R373 - FLL1 Control 5 */ { 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */ - { 0x00000177, 0x0181 }, /* R375 - FLL1 Loop Filter Test 1 */ { 0x00000179, 0x0000 }, /* R377 - FLL1 Control 7 */ { 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */ { 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */ @@ -314,7 +313,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000194, 0x007D }, /* R404 - FLL2 Control 4 */ { 0x00000195, 0x0004 }, /* R405 - FLL2 Control 5 */ { 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */ - { 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */ { 0x00000199, 0x0000 }, /* R409 - FLL2 Control 7 */ { 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */ { 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */ @@ -338,7 +336,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */ { 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */ { 0x000002A6, 0x3737 }, /* R678 - Mic Detect Level 1 */ - { 0x000002A7, 0x372C }, /* R679 - Mic Detect Level 2 */ + { 0x000002A7, 0x2C37 }, /* R679 - Mic Detect Level 2 */ { 0x000002A8, 0x1422 }, /* R680 - Mic Detect Level 3 */ { 0x000002A9, 0x030A }, /* R681 - Mic Detect Level 4 */ { 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */ @@ -402,7 +400,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */ { 0x00000436, 0x0081 }, /* R1078 - DAC Volume Limit 5R */ { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */ - { 0x00000440, 0x8FFF }, /* R1088 - DRE Enable */ + { 0x00000440, 0x0FFF }, /* R1088 - DRE Enable */ { 0x00000442, 0x3F0A }, /* R1090 - DRE Control 2 */ { 0x00000443, 0xDC1F }, /* R1090 - DRE Control 3 */ { 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */ @@ -863,7 +861,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000C0F, 0x0400 }, /* R3087 - IRQ CTRL 1 */ { 0x00000C10, 0x1000 }, /* R3088 - GPIO Debounce Config */ { 0x00000C20, 0x8002 }, /* R3104 - Misc Pad Ctrl 1 */ - { 0x00000C21, 0x8001 }, /* R3105 - Misc Pad Ctrl 2 */ + { 0x00000C21, 0x0001 }, /* R3105 - Misc Pad Ctrl 2 */ { 0x00000C22, 0x0000 }, /* R3106 - Misc Pad Ctrl 3 */ { 0x00000C23, 0x0000 }, /* R3107 - Misc Pad Ctrl 4 */ { 0x00000C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 5 */ @@ -984,7 +982,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000ECD, 0x0000 }, /* R3789 - HPLPF4_2 */ { 0x00000EE0, 0x0000 }, /* R3808 - ASRC_ENABLE */ { 0x00000EE2, 0x0000 }, /* R3810 - ASRC_RATE1 */ - { 0x00000EE3, 0x0400 }, /* R3811 - ASRC_RATE2 */ + { 0x00000EE3, 0x4000 }, /* R3811 - ASRC_RATE2 */ { 0x00000EF0, 0x0000 }, /* R3824 - ISRC 1 CTRL 1 */ { 0x00000EF1, 0x0000 }, /* R3825 - ISRC 1 CTRL 2 */ { 0x00000EF2, 0x0000 }, /* R3826 - ISRC 1 CTRL 3 */ @@ -1062,8 +1060,6 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL1_CONTROL_4: case ARIZONA_FLL1_CONTROL_5: case ARIZONA_FLL1_CONTROL_6: - case ARIZONA_FLL1_LOOP_FILTER_TEST_1: - case ARIZONA_FLL1_NCO_TEST_0: case ARIZONA_FLL1_CONTROL_7: case ARIZONA_FLL1_SYNCHRONISER_1: case ARIZONA_FLL1_SYNCHRONISER_2: @@ -1080,8 +1076,6 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL2_CONTROL_4: case ARIZONA_FLL2_CONTROL_5: case ARIZONA_FLL2_CONTROL_6: - case ARIZONA_FLL2_LOOP_FILTER_TEST_1: - case ARIZONA_FLL2_NCO_TEST_0: case ARIZONA_FLL2_CONTROL_7: case ARIZONA_FLL2_SYNCHRONISER_1: case ARIZONA_FLL2_SYNCHRONISER_2: @@ -1849,8 +1843,6 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_HAPTICS_STATUS: case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS: case ARIZONA_ASYNC_SAMPLE_RATE_2_STATUS: - case ARIZONA_FLL1_NCO_TEST_0: - case ARIZONA_FLL2_NCO_TEST_0: case ARIZONA_DAC_COMP_1: case ARIZONA_DAC_COMP_2: case ARIZONA_DAC_COMP_3: -- cgit v1.1 From 9edd83c782257c4ad23f5e89102a9ea3f24b7a7f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 9 Feb 2016 10:51:59 +0000 Subject: mfd: wm8998: Fix defaults array based on testing My automated test is back and now can check defaults against the actual hardware. This patch updates the defaults array for the differences detected and removes a couple of completely unused registers. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm8998-tables.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/wm8998-tables.c b/drivers/mfd/wm8998-tables.c index 4c2dce7..a0de300 100644 --- a/drivers/mfd/wm8998-tables.c +++ b/drivers/mfd/wm8998-tables.c @@ -229,8 +229,6 @@ static const struct reg_default wm8998_reg_default[] = { { 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */ { 0x00000175, 0x0004 }, /* R373 - FLL1 Control 5 */ { 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */ - { 0x00000177, 0x0181 }, /* R375 - FLL1 Loop Filter Test 1 */ - { 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */ { 0x00000179, 0x0000 }, /* R377 - FLL1 Control 7 */ { 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */ { 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */ @@ -247,8 +245,6 @@ static const struct reg_default wm8998_reg_default[] = { { 0x00000194, 0x007D }, /* R404 - FLL2 Control 4 */ { 0x00000195, 0x0004 }, /* R405 - FLL2 Control 5 */ { 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */ - { 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */ - { 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */ { 0x00000199, 0x0000 }, /* R409 - FLL2 Control 7 */ { 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */ { 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */ @@ -320,7 +316,7 @@ static const struct reg_default wm8998_reg_default[] = { { 0x00000434, 0x0000 }, /* R1076 - Output Path Config 5R */ { 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */ { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */ - { 0x00000440, 0x8FFF }, /* R1088 - DRE Enable */ + { 0x00000440, 0x002F }, /* R1088 - DRE Enable */ { 0x00000441, 0xC759 }, /* R1089 - DRE Control 1 */ { 0x00000442, 0x2A08 }, /* R1089 - DRE Control 2 */ { 0x00000443, 0x5CFA }, /* R1089 - DRE Control 3 */ @@ -686,7 +682,7 @@ static const struct reg_default wm8998_reg_default[] = { { 0x00000C10, 0x1000 }, /* R3088 - GPIO Debounce Config */ { 0x00000C18, 0x0000 }, /* R3096 - GP Switch 1 */ { 0x00000C20, 0x8002 }, /* R3104 - Misc Pad Ctrl 1 */ - { 0x00000C21, 0x8001 }, /* R3105 - Misc Pad Ctrl 2 */ + { 0x00000C21, 0x0001 }, /* R3105 - Misc Pad Ctrl 2 */ { 0x00000C22, 0x0000 }, /* R3106 - Misc Pad Ctrl 3 */ { 0x00000C23, 0x0000 }, /* R3107 - Misc Pad Ctrl 4 */ { 0x00000C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 5 */ @@ -888,8 +884,6 @@ static bool wm8998_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL1_CONTROL_5: case ARIZONA_FLL1_CONTROL_6: case ARIZONA_FLL1_CONTROL_7: - case ARIZONA_FLL1_LOOP_FILTER_TEST_1: - case ARIZONA_FLL1_NCO_TEST_0: case ARIZONA_FLL1_SYNCHRONISER_1: case ARIZONA_FLL1_SYNCHRONISER_2: case ARIZONA_FLL1_SYNCHRONISER_3: @@ -906,8 +900,6 @@ static bool wm8998_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL2_CONTROL_5: case ARIZONA_FLL2_CONTROL_6: case ARIZONA_FLL2_CONTROL_7: - case ARIZONA_FLL2_LOOP_FILTER_TEST_1: - case ARIZONA_FLL2_NCO_TEST_0: case ARIZONA_FLL2_SYNCHRONISER_1: case ARIZONA_FLL2_SYNCHRONISER_2: case ARIZONA_FLL2_SYNCHRONISER_3: -- cgit v1.1 From 21aca3bf0d00465aa37a385098ab7ac2fe2412bb Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 9 Feb 2016 10:52:00 +0000 Subject: mfd: cs47l24: Fix defaults array based on testing My automated test is back and now can check defaults against the actual hardware. This patch updates the defaults array for the differences detected and removes a couple of completely unused registers. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/cs47l24-tables.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/cs47l24-tables.c b/drivers/mfd/cs47l24-tables.c index 8708006..f6b78aa 100644 --- a/drivers/mfd/cs47l24-tables.c +++ b/drivers/mfd/cs47l24-tables.c @@ -227,8 +227,6 @@ static const struct reg_default cs47l24_reg_default[] = { { 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */ { 0x00000175, 0x0006 }, /* R373 - FLL1 Control 5 */ { 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */ - { 0x00000177, 0x0281 }, /* R375 - FLL1 Loop Filter Test 1 */ - { 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */ { 0x00000179, 0x0000 }, /* R376 - FLL1 Control 7 */ { 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */ { 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */ @@ -245,8 +243,6 @@ static const struct reg_default cs47l24_reg_default[] = { { 0x00000194, 0x007D }, /* R404 - FLL2 Control 4 */ { 0x00000195, 0x000C }, /* R405 - FLL2 Control 5 */ { 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */ - { 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */ - { 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */ { 0x00000199, 0x0000 }, /* R408 - FLL2 Control 7 */ { 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */ { 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */ @@ -678,7 +674,7 @@ static const struct reg_default cs47l24_reg_default[] = { { 0x00000C0F, 0x0400 }, /* R3087 - IRQ CTRL 1 */ { 0x00000C10, 0x1000 }, /* R3088 - GPIO Debounce Config */ { 0x00000C20, 0x0002 }, /* R3104 - Misc Pad Ctrl 1 */ - { 0x00000C21, 0x8001 }, /* R3105 - Misc Pad Ctrl 2 */ + { 0x00000C21, 0x0000 }, /* R3105 - Misc Pad Ctrl 2 */ { 0x00000C22, 0x0000 }, /* R3106 - Misc Pad Ctrl 3 */ { 0x00000C23, 0x0000 }, /* R3107 - Misc Pad Ctrl 4 */ { 0x00000C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 5 */ @@ -858,8 +854,6 @@ static bool cs47l24_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL1_CONTROL_5: case ARIZONA_FLL1_CONTROL_6: case ARIZONA_FLL1_CONTROL_7: - case ARIZONA_FLL1_LOOP_FILTER_TEST_1: - case ARIZONA_FLL1_NCO_TEST_0: case ARIZONA_FLL1_SYNCHRONISER_1: case ARIZONA_FLL1_SYNCHRONISER_2: case ARIZONA_FLL1_SYNCHRONISER_3: @@ -876,8 +870,6 @@ static bool cs47l24_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL2_CONTROL_5: case ARIZONA_FLL2_CONTROL_6: case ARIZONA_FLL2_CONTROL_7: - case ARIZONA_FLL2_LOOP_FILTER_TEST_1: - case ARIZONA_FLL2_NCO_TEST_0: case ARIZONA_FLL2_SYNCHRONISER_1: case ARIZONA_FLL2_SYNCHRONISER_2: case ARIZONA_FLL2_SYNCHRONISER_3: -- cgit v1.1 From f4d34c94ff287f999c420de6facf1b7524b918c0 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 9 Feb 2016 10:52:01 +0000 Subject: mfd: arizona: Remove duplicate select of MFD_CORE MFD_ARIZONA_I2C and MFD_ARIZONA_SPI both select MFD_ARIZONA and all three of those select MFD_CORE, this makes the selects of MFD_CORE in MFD_ARIZONA_I2C and MFD_ARIZONA_SPI redundant, so we remove them. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 87cee68..3719cc8 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1407,7 +1407,6 @@ config MFD_ARIZONA config MFD_ARIZONA_I2C tristate "Cirrus Logic/Wolfson Microelectronics Arizona platform with I2C" select MFD_ARIZONA - select MFD_CORE select REGMAP_I2C depends on I2C help @@ -1417,7 +1416,6 @@ config MFD_ARIZONA_I2C config MFD_ARIZONA_SPI tristate "Cirrus Logic/Wolfson Microelectronics Arizona platform with SPI" select MFD_ARIZONA - select MFD_CORE select REGMAP_SPI depends on SPI_MASTER help -- cgit v1.1 From b25c6b7d2801f33b0fc2072d7b9bfbda3682bd86 Mon Sep 17 00:00:00 2001 From: Wenyou Yang Date: Fri, 5 Feb 2016 11:36:29 +0800 Subject: mfd: act8945a: Add Active-semi ACT8945A PMIC MFD driver This patch adds support for the Active-semi ACT8945A PMIC. It is a Multi Function Device with the following subdevices: - Regulator - Charger It is interfaced to the host controller using I2C interface, ACT8945A is a child device of the I2C. Signed-off-by: Wenyou Yang Reviewed-by: Krzysztof Kozlowski Acked-by: Peter Korsgaard Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 11 ++++++ drivers/mfd/Makefile | 1 + drivers/mfd/act8945a.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 drivers/mfd/act8945a.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3719cc8..aa21dc5 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -18,6 +18,17 @@ config MFD_CS5535 This is the core driver for CS5535/CS5536 MFD functions. This is necessary for using the board's GPIO and MFGPT functionality. +config MFD_ACT8945A + tristate "Active-semi ACT8945A" + select MFD_CORE + select REGMAP_I2C + depends on I2C && OF + help + Support for the ACT8945A PMIC from Active-semi. This device + features three step-down DC/DC converters and four low-dropout + linear regulators, along with a complete ActivePath battery + charger. + config MFD_AS3711 bool "AMS AS3711" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d2ddcf4..5eaa6465d 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o +obj-$(CONFIG_MFD_ACT8945A) += act8945a.o obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o diff --git a/drivers/mfd/act8945a.c b/drivers/mfd/act8945a.c new file mode 100644 index 0000000..525b546 --- /dev/null +++ b/drivers/mfd/act8945a.c @@ -0,0 +1,102 @@ +/* + * MFD driver for Active-semi ACT8945a PMIC + * + * Copyright (C) 2015 Atmel Corporation. + * + * Author: Wenyou Yang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include + +static const struct mfd_cell act8945a_devs[] = { + { + .name = "act8945a-regulator", + }, + { + .name = "act8945a-charger", + }, +}; + +static const struct regmap_config act8945a_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int act8945a_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &act8945a_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&i2c->dev, "regmap init failed: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, regmap); + + ret = mfd_add_devices(&i2c->dev, PLATFORM_DEVID_NONE, act8945a_devs, + ARRAY_SIZE(act8945a_devs), NULL, 0, NULL); + if (ret) { + dev_err(&i2c->dev, "Failed to add sub devices\n"); + return ret; + } + + return 0; +} + +static int act8945a_i2c_remove(struct i2c_client *i2c) +{ + mfd_remove_devices(&i2c->dev); + + return 0; +} + +static const struct i2c_device_id act8945a_i2c_id[] = { + { "act8945a", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, act8945a_i2c_id); + +static const struct of_device_id act8945a_of_match[] = { + { .compatible = "active-semi,act8945a", }, + {}, +}; +MODULE_DEVICE_TABLE(of, act8945a_of_match); + +static struct i2c_driver act8945a_i2c_driver = { + .driver = { + .name = "act8945a", + .of_match_table = of_match_ptr(act8945a_of_match), + }, + .probe = act8945a_i2c_probe, + .remove = act8945a_i2c_remove, + .id_table = act8945a_i2c_id, +}; + +static int __init act8945a_i2c_init(void) +{ + return i2c_add_driver(&act8945a_i2c_driver); +} +subsys_initcall(act8945a_i2c_init); + +static void __exit act8945a_i2c_exit(void) +{ + i2c_del_driver(&act8945a_i2c_driver); +} +module_exit(act8945a_i2c_exit); + +MODULE_DESCRIPTION("ACT8945A PMIC multi-function driver"); +MODULE_AUTHOR("Wenyou Yang "); +MODULE_LICENSE("GPL"); -- cgit v1.1 From bf742a53ce1eaca26849e22b013a9523621f8db5 Mon Sep 17 00:00:00 2001 From: Steve Twiss Date: Mon, 1 Feb 2016 16:09:38 +0000 Subject: mfd: da9062: Fix missing volatile registers in the core regmap_range volatile lists Add an updated set of registers listed in the core regmap_range volatile ranges defined for the DA9062. These new registers contain bits that cannot be considered under the full control of software. Under various conditions the hardware will set and/or automatically clear bit(s) contained in these registers. When using a cached version of regmap, the volatility of these registers must be identified otherwise the regmap operations may not ensure the registers are explicitly altered. As well as updating the list of volatile registers, this change will fix a corner case discovered in the DA9063 ONKEY which is used by the DA9062 core. In the ONKEY case, the CONTROL_B register is now listed as volatile in the regmap_range because it contains the bit field NONKEY_LOCK. This bit can be altered by hardware, in which case regmap must be notified of its ability to be manpiulated outside of software control. Signed-off-by: Steve Twiss Signed-off-by: Lee Jones --- drivers/mfd/da9062-core.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/da9062-core.c b/drivers/mfd/da9062-core.c index a9ad024..8f873866 100644 --- a/drivers/mfd/da9062-core.c +++ b/drivers/mfd/da9062-core.c @@ -388,11 +388,32 @@ static const struct regmap_range da9062_aa_volatile_ranges[] = { .range_min = DA9062AA_STATUS_D, .range_max = DA9062AA_EVENT_C, }, { - .range_min = DA9062AA_CONTROL_F, + .range_min = DA9062AA_CONTROL_A, + .range_max = DA9062AA_CONTROL_B, + }, { + .range_min = DA9062AA_CONTROL_E, .range_max = DA9062AA_CONTROL_F, }, { + .range_min = DA9062AA_BUCK2_CONT, + .range_max = DA9062AA_BUCK4_CONT, + }, { + .range_min = DA9062AA_BUCK3_CONT, + .range_max = DA9062AA_BUCK3_CONT, + }, { + .range_min = DA9062AA_LDO1_CONT, + .range_max = DA9062AA_LDO4_CONT, + }, { + .range_min = DA9062AA_DVC_1, + .range_max = DA9062AA_DVC_1, + }, { .range_min = DA9062AA_COUNT_S, .range_max = DA9062AA_SECOND_D, + }, { + .range_min = DA9062AA_SEQ, + .range_max = DA9062AA_SEQ, + }, { + .range_min = DA9062AA_EN_32K, + .range_max = DA9062AA_EN_32K, }, }; -- cgit v1.1 From feec4799ac4d214abd62e2bdfccb3ca9c5801d2f Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 27 Jan 2016 12:47:36 +0100 Subject: mfd: mt6397: int_con and int_status may vary in location MT6323 has the INT_CON and INT_STATUS located at a different position. Make the registers locations configurable. Signed-off-by: John Crispin Signed-off-by: Lee Jones --- drivers/mfd/mt6397-core.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index 1749c1c..75ad0fe 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -69,8 +69,10 @@ static void mt6397_irq_sync_unlock(struct irq_data *data) { struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data); - regmap_write(mt6397->regmap, MT6397_INT_CON0, mt6397->irq_masks_cur[0]); - regmap_write(mt6397->regmap, MT6397_INT_CON1, mt6397->irq_masks_cur[1]); + regmap_write(mt6397->regmap, mt6397->int_con[0], + mt6397->irq_masks_cur[0]); + regmap_write(mt6397->regmap, mt6397->int_con[1], + mt6397->irq_masks_cur[1]); mutex_unlock(&mt6397->irqlock); } @@ -147,8 +149,8 @@ static irqreturn_t mt6397_irq_thread(int irq, void *data) { struct mt6397_chip *mt6397 = data; - mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS0, 0); - mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS1, 16); + mt6397_irq_handle_reg(mt6397, mt6397->int_status[0], 0); + mt6397_irq_handle_reg(mt6397, mt6397->int_status[1], 16); return IRQ_HANDLED; } @@ -177,8 +179,8 @@ static int mt6397_irq_init(struct mt6397_chip *mt6397) mutex_init(&mt6397->irqlock); /* Mask all interrupt sources */ - regmap_write(mt6397->regmap, MT6397_INT_CON0, 0x0); - regmap_write(mt6397->regmap, MT6397_INT_CON1, 0x0); + regmap_write(mt6397->regmap, mt6397->int_con[0], 0x0); + regmap_write(mt6397->regmap, mt6397->int_con[1], 0x0); mt6397->irq_domain = irq_domain_add_linear(mt6397->dev->of_node, MT6397_IRQ_NR, &mt6397_irq_domain_ops, mt6397); @@ -203,8 +205,8 @@ static int mt6397_irq_suspend(struct device *dev) { struct mt6397_chip *chip = dev_get_drvdata(dev); - regmap_write(chip->regmap, MT6397_INT_CON0, chip->wake_mask[0]); - regmap_write(chip->regmap, MT6397_INT_CON1, chip->wake_mask[1]); + regmap_write(chip->regmap, chip->int_con[0], chip->wake_mask[0]); + regmap_write(chip->regmap, chip->int_con[1], chip->wake_mask[1]); enable_irq_wake(chip->irq); @@ -215,8 +217,8 @@ static int mt6397_irq_resume(struct device *dev) { struct mt6397_chip *chip = dev_get_drvdata(dev); - regmap_write(chip->regmap, MT6397_INT_CON0, chip->irq_masks_cur[0]); - regmap_write(chip->regmap, MT6397_INT_CON1, chip->irq_masks_cur[1]); + regmap_write(chip->regmap, chip->int_con[0], chip->irq_masks_cur[0]); + regmap_write(chip->regmap, chip->int_con[1], chip->irq_masks_cur[1]); disable_irq_wake(chip->irq); @@ -237,6 +239,11 @@ static int mt6397_probe(struct platform_device *pdev) return -ENOMEM; mt6397->dev = &pdev->dev; + mt6397->int_con[0] = MT6397_INT_CON0; + mt6397->int_con[1] = MT6397_INT_CON1; + mt6397->int_status[0] = MT6397_INT_STATUS0; + mt6397->int_status[1] = MT6397_INT_STATUS1; + /* * mt6397 MFD is child device of soc pmic wrapper. * Regmap is set from its parent. -- cgit v1.1 From 1d2c25ed4558cc591072e43e16118f08f7eeb206 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 27 Jan 2016 12:47:37 +0100 Subject: mfd: mt6397: Add support for different Slave types Signed-off-by: John Crispin Signed-off-by: Lee Jones --- drivers/mfd/mt6397-core.c | 58 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index 75ad0fe..aa91606 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -24,6 +24,9 @@ #define MT6397_RTC_BASE 0xe000 #define MT6397_RTC_SIZE 0x3e +#define MT6391_CID_CODE 0x91 +#define MT6397_CID_CODE 0x97 + static const struct resource mt6397_rtc_resources[] = { { .start = MT6397_RTC_BASE, @@ -232,39 +235,60 @@ static SIMPLE_DEV_PM_OPS(mt6397_pm_ops, mt6397_irq_suspend, static int mt6397_probe(struct platform_device *pdev) { int ret; - struct mt6397_chip *mt6397; + unsigned int id; + struct mt6397_chip *pmic; - mt6397 = devm_kzalloc(&pdev->dev, sizeof(*mt6397), GFP_KERNEL); - if (!mt6397) + pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); + if (!pmic) return -ENOMEM; - mt6397->dev = &pdev->dev; - mt6397->int_con[0] = MT6397_INT_CON0; - mt6397->int_con[1] = MT6397_INT_CON1; - mt6397->int_status[0] = MT6397_INT_STATUS0; - mt6397->int_status[1] = MT6397_INT_STATUS1; + pmic->dev = &pdev->dev; /* * mt6397 MFD is child device of soc pmic wrapper. * Regmap is set from its parent. */ - mt6397->regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!mt6397->regmap) + pmic->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!pmic->regmap) return -ENODEV; - platform_set_drvdata(pdev, mt6397); + platform_set_drvdata(pdev, pmic); + + ret = regmap_read(pmic->regmap, MT6397_CID, &id); + if (ret) { + dev_err(pmic->dev, "Failed to read chip id: %d\n", ret); + goto fail_irq; + } + + switch (id & 0xff) { + case MT6397_CID_CODE: + case MT6391_CID_CODE: + pmic->int_con[0] = MT6397_INT_CON0; + pmic->int_con[1] = MT6397_INT_CON1; + pmic->int_status[0] = MT6397_INT_STATUS0; + pmic->int_status[1] = MT6397_INT_STATUS1; + ret = mfd_add_devices(&pdev->dev, -1, mt6397_devs, + ARRAY_SIZE(mt6397_devs), NULL, 0, NULL); + break; + + default: + dev_err(&pdev->dev, "unsupported chip: %d\n", id); + ret = -ENODEV; + break; + } - mt6397->irq = platform_get_irq(pdev, 0); - if (mt6397->irq > 0) { - ret = mt6397_irq_init(mt6397); + pmic->irq = platform_get_irq(pdev, 0); + if (pmic->irq > 0) { + ret = mt6397_irq_init(pmic); if (ret) return ret; } - ret = mfd_add_devices(&pdev->dev, -1, mt6397_devs, - ARRAY_SIZE(mt6397_devs), NULL, 0, NULL); - if (ret) +fail_irq: + if (ret) { + irq_domain_remove(pmic->irq_domain); dev_err(&pdev->dev, "failed to add child devices: %d\n", ret); + } return ret; } -- cgit v1.1 From 44760cf3bf0a29da8f5cc271698c8772b8f79673 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 27 Jan 2016 12:47:38 +0100 Subject: mfd: mt6397: Add MT6323 support to MT6397 driver Signed-off-by: John Crispin Signed-off-by: Lee Jones --- drivers/mfd/mt6397-core.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index aa91606..8234cd3 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -19,11 +19,14 @@ #include #include #include +#include #include +#include #define MT6397_RTC_BASE 0xe000 #define MT6397_RTC_SIZE 0x3e +#define MT6323_CID_CODE 0x23 #define MT6391_CID_CODE 0x91 #define MT6397_CID_CODE 0x97 @@ -40,6 +43,13 @@ static const struct resource mt6397_rtc_resources[] = { }, }; +static const struct mfd_cell mt6323_devs[] = { + { + .name = "mt6323-regulator", + .of_compatible = "mediatek,mt6323-regulator" + }, +}; + static const struct mfd_cell mt6397_devs[] = { { .name = "mt6397-rtc", @@ -261,6 +271,15 @@ static int mt6397_probe(struct platform_device *pdev) } switch (id & 0xff) { + case MT6323_CID_CODE: + pmic->int_con[0] = MT6323_INT_CON0; + pmic->int_con[1] = MT6323_INT_CON1; + pmic->int_status[0] = MT6323_INT_STATUS0; + pmic->int_status[1] = MT6323_INT_STATUS1; + ret = mfd_add_devices(&pdev->dev, -1, mt6323_devs, + ARRAY_SIZE(mt6323_devs), NULL, 0, NULL); + break; + case MT6397_CID_CODE: case MT6391_CID_CODE: pmic->int_con[0] = MT6397_INT_CON0; @@ -302,6 +321,7 @@ static int mt6397_remove(struct platform_device *pdev) static const struct of_device_id mt6397_of_match[] = { { .compatible = "mediatek,mt6397" }, + { .compatible = "mediatek,mt6323" }, { } }; MODULE_DEVICE_TABLE(of, mt6397_of_match); -- cgit v1.1 From 0e5df61d26f2ad609eb5a699b9ab587564ccd65f Mon Sep 17 00:00:00 2001 From: Steve Twiss Date: Wed, 10 Feb 2016 17:35:07 +0000 Subject: mfd: da9063: Fix missing volatile registers in the core regmap_range volatile lists Add an updated set of registers listed in the core regmap_range volatile ranges defined for the DA9063. These new registers contain bits that cannot be considered under the full control of software. Under various conditions the hardware will set and/or automatically clear bit(s) contained in these registers. When using a cached version of regmap, the volatility of these registers must be identified otherwise the regmap operations may not ensure the registers are explicitly altered. As well as updating the list of volatile registers, this change will fix a corner case discovered in the DA9063 ONKEY which is used by the DA9063 core. In the ONKEY case, the CONTROL_B register is now listed as volatile in the regmap_range because it contains the bit field NONKEY_LOCK. This bit can be altered by hardware, in which case regmap must be notified of its ability to be manpiulated outside of software control. Signed-off-by: Steve Twiss Signed-off-by: Lee Jones --- drivers/mfd/da9063-i2c.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/da9063-i2c.c b/drivers/mfd/da9063-i2c.c index 2d4e3e0..7390108 100644 --- a/drivers/mfd/da9063-i2c.c +++ b/drivers/mfd/da9063-i2c.c @@ -74,18 +74,30 @@ static const struct regmap_range da9063_ad_writeable_ranges[] = { static const struct regmap_range da9063_ad_volatile_ranges[] = { { - .range_min = DA9063_REG_STATUS_A, + .range_min = DA9063_REG_PAGE_CON, .range_max = DA9063_REG_EVENT_D, }, { - .range_min = DA9063_REG_CONTROL_F, + .range_min = DA9063_REG_CONTROL_A, + .range_max = DA9063_REG_CONTROL_B, + }, { + .range_min = DA9063_REG_CONTROL_E, .range_max = DA9063_REG_CONTROL_F, }, { - .range_min = DA9063_REG_ADC_MAN, + .range_min = DA9063_REG_BCORE2_CONT, + .range_max = DA9063_REG_LDO11_CONT, + }, { + .range_min = DA9063_REG_DVC_1, .range_max = DA9063_REG_ADC_MAN, }, { .range_min = DA9063_REG_ADC_RES_L, .range_max = DA9063_AD_REG_SECOND_D, }, { + .range_min = DA9063_REG_SEQ, + .range_max = DA9063_REG_SEQ, + }, { + .range_min = DA9063_REG_EN_32K, + .range_max = DA9063_REG_EN_32K, + }, { .range_min = DA9063_AD_REG_MON_REG_5, .range_max = DA9063_AD_REG_MON_REG_6, }, @@ -152,18 +164,30 @@ static const struct regmap_range da9063_bb_writeable_ranges[] = { static const struct regmap_range da9063_bb_volatile_ranges[] = { { - .range_min = DA9063_REG_STATUS_A, + .range_min = DA9063_REG_PAGE_CON, .range_max = DA9063_REG_EVENT_D, }, { - .range_min = DA9063_REG_CONTROL_F, + .range_min = DA9063_REG_CONTROL_A, + .range_max = DA9063_REG_CONTROL_B, + }, { + .range_min = DA9063_REG_CONTROL_E, .range_max = DA9063_REG_CONTROL_F, }, { - .range_min = DA9063_REG_ADC_MAN, + .range_min = DA9063_REG_BCORE2_CONT, + .range_max = DA9063_REG_LDO11_CONT, + }, { + .range_min = DA9063_REG_DVC_1, .range_max = DA9063_REG_ADC_MAN, }, { .range_min = DA9063_REG_ADC_RES_L, .range_max = DA9063_BB_REG_SECOND_D, }, { + .range_min = DA9063_REG_SEQ, + .range_max = DA9063_REG_SEQ, + }, { + .range_min = DA9063_REG_EN_32K, + .range_max = DA9063_REG_EN_32K, + }, { .range_min = DA9063_BB_REG_MON_REG_5, .range_max = DA9063_BB_REG_MON_REG_6, }, -- cgit v1.1 From e1d9a10918396ae26dd5181d454c793ff17c9ded Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 10 Feb 2016 13:50:18 -0300 Subject: mfd: mt6397: Add platform device ID table The platform bus_type .match callback attempts to match the platform device name with an entry on the .id_table if provided and fallbacks to match with the driver's name if a table is not provided. Using a platform device ID to match is more explicit, allows the driver to support more than one device and also the MODULE_DEVICE_TABLE macro can be used to export the module aliases information instead of the MODULE_ALIAS. Signed-off-by: Javier Martinez Canillas Signed-off-by: Lee Jones --- drivers/mfd/mt6397-core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index 8234cd3..8e8d932 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -326,6 +326,12 @@ static const struct of_device_id mt6397_of_match[] = { }; MODULE_DEVICE_TABLE(of, mt6397_of_match); +static const struct platform_device_id mt6397_id[] = { + { "mt6397", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(platform, mt6397_id); + static struct platform_driver mt6397_driver = { .probe = mt6397_probe, .remove = mt6397_remove, @@ -334,6 +340,7 @@ static struct platform_driver mt6397_driver = { .of_match_table = of_match_ptr(mt6397_of_match), .pm = &mt6397_pm_ops, }, + .id_table = mt6397_id, }; module_platform_driver(mt6397_driver); @@ -341,4 +348,3 @@ module_platform_driver(mt6397_driver); MODULE_AUTHOR("Flora Fu, MediaTek"); MODULE_DESCRIPTION("Driver for MediaTek MT6397 PMIC"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:mt6397"); -- cgit v1.1 From 99f0fd540f53985443f36f96c0da209665e2dbfa Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sat, 6 Feb 2016 10:12:10 -0600 Subject: gpio: tps65086: Add GPO driver for the TPS65086 PMIC Add support for the TPS65086 PMIC GPOs. TPS65086 has four configurable GPOs that can be used for several purposes. These are output only. Signed-off-by: Andrew F. Davis Reviewed-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/gpio/Kconfig | 6 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-tps65086.c | 139 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 drivers/gpio/gpio-tps65086.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3d60b74..2b80903 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -844,6 +844,12 @@ config GPIO_TIMBERDALE ---help--- Add support for the GPIO IP in the timberdale FPGA. +config GPIO_TPS65086 + tristate "TI TPS65086 GPO" + depends on MFD_TPS65086 + help + This driver supports the GPO on TI TPS65086x PMICs. + config GPIO_TPS6586X bool "TPS6586X GPIO" depends on MFD_TPS6586X diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ece7d7c..c759190 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o +obj-$(CONFIG_GPIO_TPS65086) += gpio-tps65086.o obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c new file mode 100644 index 0000000..8e25f01 --- /dev/null +++ b/drivers/gpio/gpio-tps65086.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis + * + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether expressed or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2 for more details. + * + * Based on the TPS65912 driver + */ + +#include +#include +#include + +#include + +struct tps65086_gpio { + struct gpio_chip chip; + struct tps65086 *tps; +}; + +static int tps65086_gpio_get_direction(struct gpio_chip *chip, + unsigned offset) +{ + /* This device is output only */ + return 0; +} + +static int tps65086_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + /* This device is output only */ + return -EINVAL; +} + +static int tps65086_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct tps65086_gpio *gpio = gpiochip_get_data(chip); + + /* Set the initial value */ + regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, + BIT(4 + offset), value ? BIT(4 + offset) : 0); + + return 0; +} + +static int tps65086_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct tps65086_gpio *gpio = gpiochip_get_data(chip); + int ret, val; + + ret = regmap_read(gpio->tps->regmap, TPS65086_GPOCTRL, &val); + if (ret < 0) + return ret; + + return val & BIT(4 + offset); +} + +static void tps65086_gpio_set(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct tps65086_gpio *gpio = gpiochip_get_data(chip); + + regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, + BIT(4 + offset), value ? BIT(4 + offset) : 0); +} + +static struct gpio_chip template_chip = { + .label = "tps65086-gpio", + .owner = THIS_MODULE, + .get_direction = tps65086_gpio_get_direction, + .direction_input = tps65086_gpio_direction_input, + .direction_output = tps65086_gpio_direction_output, + .get = tps65086_gpio_get, + .set = tps65086_gpio_set, + .base = -1, + .ngpio = 4, + .can_sleep = true, +}; + +static int tps65086_gpio_probe(struct platform_device *pdev) +{ + struct tps65086_gpio *gpio; + int ret; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + platform_set_drvdata(pdev, gpio); + + gpio->tps = dev_get_drvdata(pdev->dev.parent); + gpio->chip = template_chip; + gpio->chip.parent = gpio->tps->dev; + + ret = gpiochip_add_data(&gpio->chip, gpio); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); + return ret; + } + + return 0; +} + +static int tps65086_gpio_remove(struct platform_device *pdev) +{ + struct tps65086_gpio *gpio = platform_get_drvdata(pdev); + + gpiochip_remove(&gpio->chip); + + return 0; +} + +static const struct platform_device_id tps65086_gpio_id_table[] = { + { "tps65086-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, tps65086_gpio_id_table); + +static struct platform_driver tps65086_gpio_driver = { + .driver = { + .name = "tps65086-gpio", + }, + .probe = tps65086_gpio_probe, + .remove = tps65086_gpio_remove, + .id_table = tps65086_gpio_id_table, +}; +module_platform_driver(tps65086_gpio_driver); + +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("TPS65086 GPIO driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From a862dc3ea793256a1364991f52e68198a2c5f27d Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sun, 31 Jan 2016 22:58:41 +0100 Subject: mfd: rc5t583: Set regmap config reg counts properly Regmap config max_register field should contain number of device last register, however num_reg_defaults_raw field should be set to register count instead (usually one register more than max_register). rc5t583 driver had both of these fields set to the same value, fix this by introducing separate defines for max register number and total count of registers. Signed-off-by: Maciej S. Szmigiero Signed-off-by: Lee Jones --- drivers/mfd/rc5t583.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c index e10f02f..fc2b2d9 100644 --- a/drivers/mfd/rc5t583.c +++ b/drivers/mfd/rc5t583.c @@ -241,8 +241,8 @@ static const struct regmap_config rc5t583_regmap_config = { .reg_bits = 8, .val_bits = 8, .volatile_reg = volatile_reg, - .max_register = RC5T583_MAX_REGS, - .num_reg_defaults_raw = RC5T583_MAX_REGS, + .max_register = RC5T583_MAX_REG, + .num_reg_defaults_raw = RC5T583_NUM_REGS, .cache_type = REGCACHE_RBTREE, }; -- cgit v1.1 From e9b7ba7954fa8df6e021ee4bef084ed10eba2c2b Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sun, 31 Jan 2016 22:56:59 +0100 Subject: mfd: as3711: Set regmap config reg counts properly Regmap config max_register field should contain number of device last register, however num_reg_defaults_raw field should be set to register count instead (usually one register more than max_register). as3711 driver had both of these fields set to the same value, fix this by introducing separate defines for max register number and total count of registers. Signed-off-by: Maciej S. Szmigiero Signed-off-by: Lee Jones --- drivers/mfd/as3711.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c index 94d67a6..09e1483 100644 --- a/drivers/mfd/as3711.c +++ b/drivers/mfd/as3711.c @@ -108,8 +108,8 @@ static const struct regmap_config as3711_regmap_config = { .volatile_reg = as3711_volatile_reg, .readable_reg = as3711_readable_reg, .precious_reg = as3711_precious_reg, - .max_register = AS3711_MAX_REGS, - .num_reg_defaults_raw = AS3711_MAX_REGS, + .max_register = AS3711_MAX_REG, + .num_reg_defaults_raw = AS3711_NUM_REGS, .cache_type = REGCACHE_RBTREE, }; -- cgit v1.1 From 5c1488906f852473b4d7837c3e33e411e0eb6910 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sun, 31 Jan 2016 23:00:06 +0100 Subject: mfd: tps65090: Set regmap config reg counts properly Regmap config max_register field should contain number of device last register, however num_reg_defaults_raw field should be set to register count instead (usually one register more than max_register). tps65090 driver had both of these fields set to the same value, fix this by introducing separate defines for max register number and total count of registers. Signed-off-by: Maciej S. Szmigiero Signed-off-by: Lee Jones --- drivers/mfd/tps65090.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index f88085a..d7ec318 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -30,7 +30,6 @@ #include #define NUM_INT_REG 2 -#define TOTAL_NUM_REG 0x18 #define TPS65090_INT1_MASK_VAC_STATUS_CHANGE 1 #define TPS65090_INT1_MASK_VSYS_STATUS_CHANGE 2 @@ -161,8 +160,8 @@ static bool is_volatile_reg(struct device *dev, unsigned int reg) static const struct regmap_config tps65090_regmap_config = { .reg_bits = 8, .val_bits = 8, - .max_register = TOTAL_NUM_REG, - .num_reg_defaults_raw = TOTAL_NUM_REG, + .max_register = TPS65090_MAX_REG, + .num_reg_defaults_raw = TPS65090_NUM_REGS, .cache_type = REGCACHE_RBTREE, .volatile_reg = is_volatile_reg, }; -- cgit v1.1 From b273c5e0b65a98003e6fde6bf2eeba15d1ce8cf4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 14 Feb 2016 14:07:07 +0100 Subject: mfd: stmpe: Add the proper PWM resources This adds the PWM resources to the STMPE MFD driver, so that it can properly grab and use them. Signed-off-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/mfd/stmpe.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 8222e37..fb8f9e8 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -334,6 +334,31 @@ static const struct mfd_cell stmpe_keypad_cell = { }; /* + * PWM (1601, 2401, 2403) + */ +static struct resource stmpe_pwm_resources[] = { + { + .name = "PWM0", + .flags = IORESOURCE_IRQ, + }, + { + .name = "PWM1", + .flags = IORESOURCE_IRQ, + }, + { + .name = "PWM2", + .flags = IORESOURCE_IRQ, + }, +}; + +static const struct mfd_cell stmpe_pwm_cell = { + .name = "stmpe-pwm", + .of_compatible = "st,stmpe-pwm", + .resources = stmpe_pwm_resources, + .num_resources = ARRAY_SIZE(stmpe_pwm_resources), +}; + +/* * STMPE801 */ static const u8 stmpe801_regs[] = { @@ -537,6 +562,11 @@ static struct stmpe_variant_block stmpe1601_blocks[] = { .irq = STMPE1601_IRQ_KEYPAD, .block = STMPE_BLOCK_KEYPAD, }, + { + .cell = &stmpe_pwm_cell, + .irq = STMPE1601_IRQ_PWM0, + .block = STMPE_BLOCK_PWM, + }, }; /* supported autosleep timeout delay (in msecs) */ @@ -771,6 +801,11 @@ static struct stmpe_variant_block stmpe24xx_blocks[] = { .irq = STMPE24XX_IRQ_KEYPAD, .block = STMPE_BLOCK_KEYPAD, }, + { + .cell = &stmpe_pwm_cell, + .irq = STMPE24XX_IRQ_PWM0, + .block = STMPE_BLOCK_PWM, + }, }; static int stmpe24xx_enable(struct stmpe *stmpe, unsigned int blocks, -- cgit v1.1 From a52e46ce9c3f847925707a601dc10e9af943bc26 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 12 Feb 2016 01:30:18 -0300 Subject: mfd: max77686: Allow driver to be built as a module The driver's Kconfig symbol is a boolean but nothing prevents the driver to be built as a module instead of built-in. It is true that most system integrators will choose the latter but the config should not restrict it. Signed-off-by: Javier Martinez Canillas Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index aa21dc5..eea61e3 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -528,8 +528,8 @@ config MFD_MAX14577 of the device. config MFD_MAX77686 - bool "Maxim Semiconductor MAX77686/802 PMIC Support" - depends on I2C=y + tristate "Maxim Semiconductor MAX77686/802 PMIC Support" + depends on I2C depends on OF select MFD_CORE select REGMAP_I2C -- cgit v1.1 From 7f8ada1a84313383eff97b357515a5a0df31b321 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 12 Feb 2016 01:30:19 -0300 Subject: mfd: max77686: Export OF module alias information When the device is registered via OF, the OF table is used to match the driver instead of the I2C device ID table but the entries in the latter are used as aliasses to load the module if the driver was not built-in. This is because the I2C core always reports an I2C module alias instead of an OF one but that could change so it is better to always export it. Signed-off-by: Javier Martinez Canillas Signed-off-by: Lee Jones --- drivers/mfd/max77686.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index d959ebb..7cc1dfd 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -204,6 +204,7 @@ static const struct of_device_id max77686_pmic_dt_match[] = { }, { }, }; +MODULE_DEVICE_TABLE(of, max77686_pmic_dt_match); static int max77686_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) -- cgit v1.1 From 95a6f715bcab610237530d266f8d8f752af6cd6f Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 12 Feb 2016 01:30:16 -0300 Subject: mfd: max77686: Add max77802 to I2C device ID table The max77686 MFD driver supports both the Maxim 77686 and Maxim 77802 PMICs but only the OF device table contains entries for both devices. The max77802 entry is missing in the I2C device ID table which isn't a problem currently since the driver only supports DT but it will be needed if the driver is changed to be built as a module since the I2C core always reports a I2C modalias uevent so auto-load will not work. Signed-off-by: Javier Martinez Canillas Signed-off-by: Lee Jones --- drivers/mfd/max77686.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 7cc1dfd..86458f5 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -346,6 +346,7 @@ static int max77686_i2c_remove(struct i2c_client *i2c) static const struct i2c_device_id max77686_i2c_id[] = { { "max77686", TYPE_MAX77686 }, + { "max77802", TYPE_MAX77802 }, { } }; MODULE_DEVICE_TABLE(i2c, max77686_i2c_id); -- cgit v1.1 From dcdf11739d768febbfb7ecf5388761cce1f48ffc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 2 Mar 2016 16:58:57 +0100 Subject: mfd: ipaq-micro: Use __maybe_unused to hide pm functions The ipaq-micro driver uses SET_SYSTEM_SLEEP_PM_OPS() to remove the reference to its resume function, but does not use an #ifdef around the definition, so we get a build warning: drivers/mfd/ipaq-micro.c:379:12: error: 'micro_resume' defined but not used [-Werror=unused-function] This adds a __maybe_unused annotation so the compiler knows it can silently drop it instead of warning. Signed-off-by: Arnd Bergmann Signed-off-by: Lee Jones --- drivers/mfd/ipaq-micro.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/ipaq-micro.c b/drivers/mfd/ipaq-micro.c index a41859c..df16fd1 100644 --- a/drivers/mfd/ipaq-micro.c +++ b/drivers/mfd/ipaq-micro.c @@ -376,7 +376,7 @@ static const struct mfd_cell micro_cells[] = { { .name = "ipaq-micro-leds", }, }; -static int micro_resume(struct device *dev) +static int __maybe_unused micro_resume(struct device *dev) { struct ipaq_micro *micro = dev_get_drvdata(dev); -- cgit v1.1 From 0343b2f4e4a52c907d7676ce3159e0b5e7f0301c Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 26 Jan 2016 14:17:49 +0200 Subject: mfd: intel-lpss: Pass I2C configuration via properties on BXT I2C host controller need to be configured properly in order to meet I2C timings specified in the I2C protocol specification. Some Intel Broxton based machines do not have this information in the ACPI namespace (or the boot firmware does not support ACPI at all) so we use build-in device properties instead. Signed-off-by: Mika Westerberg Signed-off-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/intel-lpss-acpi.c | 12 ++++++++++++ drivers/mfd/intel-lpss-pci.c | 12 ++++++++++++ 2 files changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c index 06f00d6..5a8d9c7 100644 --- a/drivers/mfd/intel-lpss-acpi.c +++ b/drivers/mfd/intel-lpss-acpi.c @@ -44,8 +44,20 @@ static const struct intel_lpss_platform_info bxt_info = { .clk_rate = 100000000, }; +static struct property_entry bxt_i2c_properties[] = { + PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 42), + PROPERTY_ENTRY_U32("i2c-sda-falling-time-ns", 171), + PROPERTY_ENTRY_U32("i2c-scl-falling-time-ns", 208), + { }, +}; + +static struct property_set bxt_i2c_pset = { + .properties = bxt_i2c_properties, +}; + static const struct intel_lpss_platform_info bxt_i2c_info = { .clk_rate = 133000000, + .pset = &bxt_i2c_pset, }; static const struct acpi_device_id intel_lpss_acpi_ids[] = { diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index afc7af9..a19e571 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -107,8 +107,20 @@ static const struct intel_lpss_platform_info bxt_uart_info = { .pset = &uart_pset, }; +static struct property_entry bxt_i2c_properties[] = { + PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 42), + PROPERTY_ENTRY_U32("i2c-sda-falling-time-ns", 171), + PROPERTY_ENTRY_U32("i2c-scl-falling-time-ns", 208), + { }, +}; + +static struct property_set bxt_i2c_pset = { + .properties = bxt_i2c_properties, +}; + static const struct intel_lpss_platform_info bxt_i2c_info = { .clk_rate = 133000000, + .pset = &bxt_i2c_pset, }; static const struct pci_device_id intel_lpss_pci_ids[] = { -- cgit v1.1