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 9ca66de0c1c1..6980e0463c8c 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 0f230a6103f8..8706bf9cf51b 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 000000000000..77b2675cf8f5 --- /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.2.3 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 605ff42c4631..a7ec74bffa5b 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 6435780e9b71..151088f03258 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 000000000000..2fd192735d5b --- /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.2.3 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 66c62641b59a..a768806d2dbd 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 968ff12e3132..49134efce85f 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 000000000000..fe9877a6af9e --- /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.2.3 From 65b6555971d0498b18a0b2fdc6dd7c822f2d69b7 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Mon, 25 Jan 2016 09:43:44 -0600 Subject: mfd: tps65912: Remove old driver in preparation for new driver The old tps65912 driver is being replaced, delete old driver. Signed-off-by: Andrew F. Davis Acked-by: Mark Brown Acked-by: Linus Walleij Signed-off-by: Lee Jones --- drivers/gpio/Kconfig | 6 - drivers/gpio/Makefile | 1 - drivers/gpio/gpio-tps65912.c | 151 --------- drivers/mfd/Kconfig | 26 -- drivers/mfd/Makefile | 4 - drivers/mfd/tps65912-core.c | 175 ----------- drivers/mfd/tps65912-i2c.c | 139 --------- drivers/mfd/tps65912-irq.c | 217 ------------- drivers/mfd/tps65912-spi.c | 140 --------- drivers/regulator/Kconfig | 6 - drivers/regulator/Makefile | 1 - drivers/regulator/tps65912-regulator.c | 541 --------------------------------- 12 files changed, 1407 deletions(-) delete mode 100644 drivers/gpio/gpio-tps65912.c delete mode 100644 drivers/mfd/tps65912-core.c delete mode 100644 drivers/mfd/tps65912-i2c.c delete mode 100644 drivers/mfd/tps65912-irq.c delete mode 100644 drivers/mfd/tps65912-spi.c delete mode 100644 drivers/regulator/tps65912-regulator.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c88dd24a4b1f..98eaeddc0ae3 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -858,12 +858,6 @@ config GPIO_TPS65910 Select this option to enable GPIO driver for the TPS65910 chip family. -config GPIO_TPS65912 - tristate "TI TPS65912 GPIO" - depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI) - help - This driver supports TPS65912 gpio chip - config GPIO_TWL4030 tristate "TWL4030, TWL5030, and TPS659x0 GPIOs" depends on TWL4030_CORE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ece7d7cbdc80..edc33f0a6788 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -97,7 +97,6 @@ obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o -obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c deleted file mode 100644 index 4f2029c7da3a..000000000000 --- a/drivers/gpio/gpio-tps65912.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2011 Texas Instruments Inc. - * - * Author: Margarita Olaya - * - * 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. - * - * This driver is based on wm8350 implementation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct tps65912_gpio_data { - struct tps65912 *tps65912; - struct gpio_chip gpio_chip; -}; - -static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset) -{ - struct tps65912_gpio_data *tps65912_gpio = gpiochip_get_data(gc); - struct tps65912 *tps65912 = tps65912_gpio->tps65912; - int val; - - val = tps65912_reg_read(tps65912, TPS65912_GPIO1 + offset); - - if (val & GPIO_STS_MASK) - return 1; - - return 0; -} - -static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset, - int value) -{ - struct tps65912_gpio_data *tps65912_gpio = gpiochip_get_data(gc); - struct tps65912 *tps65912 = tps65912_gpio->tps65912; - - if (value) - tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset, - GPIO_SET_MASK); - else - tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset, - GPIO_SET_MASK); -} - -static int tps65912_gpio_output(struct gpio_chip *gc, unsigned offset, - int value) -{ - struct tps65912_gpio_data *tps65912_gpio = gpiochip_get_data(gc); - struct tps65912 *tps65912 = tps65912_gpio->tps65912; - - /* Set the initial value */ - tps65912_gpio_set(gc, offset, value); - - return tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset, - GPIO_CFG_MASK); -} - -static int tps65912_gpio_input(struct gpio_chip *gc, unsigned offset) -{ - struct tps65912_gpio_data *tps65912_gpio = gpiochip_get_data(gc); - struct tps65912 *tps65912 = tps65912_gpio->tps65912; - - return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset, - GPIO_CFG_MASK); -} - -static struct gpio_chip template_chip = { - .label = "tps65912", - .owner = THIS_MODULE, - .direction_input = tps65912_gpio_input, - .direction_output = tps65912_gpio_output, - .get = tps65912_gpio_get, - .set = tps65912_gpio_set, - .can_sleep = true, - .ngpio = 5, - .base = -1, -}; - -static int tps65912_gpio_probe(struct platform_device *pdev) -{ - struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent); - struct tps65912_board *pdata = dev_get_platdata(tps65912->dev); - struct tps65912_gpio_data *tps65912_gpio; - int ret; - - tps65912_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps65912_gpio), - GFP_KERNEL); - if (tps65912_gpio == NULL) - return -ENOMEM; - - tps65912_gpio->tps65912 = tps65912; - tps65912_gpio->gpio_chip = template_chip; - tps65912_gpio->gpio_chip.parent = &pdev->dev; - if (pdata && pdata->gpio_base) - tps65912_gpio->gpio_chip.base = pdata->gpio_base; - - ret = gpiochip_add_data(&tps65912_gpio->gpio_chip, tps65912_gpio); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret); - return ret; - } - - platform_set_drvdata(pdev, tps65912_gpio); - - return ret; -} - -static int tps65912_gpio_remove(struct platform_device *pdev) -{ - struct tps65912_gpio_data *tps65912_gpio = platform_get_drvdata(pdev); - - gpiochip_remove(&tps65912_gpio->gpio_chip); - return 0; -} - -static struct platform_driver tps65912_gpio_driver = { - .driver = { - .name = "tps65912-gpio", - }, - .probe = tps65912_gpio_probe, - .remove = tps65912_gpio_remove, -}; - -static int __init tps65912_gpio_init(void) -{ - return platform_driver_register(&tps65912_gpio_driver); -} -subsys_initcall(tps65912_gpio_init); - -static void __exit tps65912_gpio_exit(void) -{ - platform_driver_unregister(&tps65912_gpio_driver); -} -module_exit(tps65912_gpio_exit); - -MODULE_AUTHOR("Margarita Olaya Cabrera "); -MODULE_DESCRIPTION("GPIO interface for TPS65912 PMICs"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:tps65912-gpio"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9ca66de0c1c1..dd2fc0158c7a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1180,32 +1180,6 @@ config MFD_TPS65910 if you say yes here you get support for the TPS65910 series of Power Management chips. -config MFD_TPS65912 - bool "TI TPS65912 Power Management chip" - depends on GPIOLIB - select MFD_CORE - help - If you say yes here you get support for the TPS65912 series of - PM chips. - -config MFD_TPS65912_I2C - bool "TI TPS65912 Power Management chip with I2C" - select MFD_CORE - select MFD_TPS65912 - depends on I2C=y && GPIOLIB - help - If you say yes here you get support for the TPS65912 series of - PM chips with I2C interface. - -config MFD_TPS65912_SPI - bool "TI TPS65912 Power Management chip with SPI" - select MFD_CORE - select MFD_TPS65912 - depends on SPI_MASTER && GPIOLIB - help - If you say yes here you get support for the TPS65912 series of - PM chips with SPI interface. - config MFD_TPS80031 bool "TI TPS80031/TPS80032 Power Management chips" depends on I2C=y diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 0f230a6103f8..e33e0b48f591 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -73,10 +73,6 @@ obj-$(CONFIG_TPS6507X) += tps6507x.o obj-$(CONFIG_MFD_TPS65217) += tps65217.o obj-$(CONFIG_MFD_TPS65218) += tps65218.o obj-$(CONFIG_MFD_TPS65910) += tps65910.o -tps65912-objs := tps65912-core.o tps65912-irq.o -obj-$(CONFIG_MFD_TPS65912) += tps65912.o -obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o -obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o obj-$(CONFIG_MFD_TPS80031) += tps80031.o obj-$(CONFIG_MENELAUS) += menelaus.o diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c deleted file mode 100644 index 1f82d60b1d0f..000000000000 --- a/drivers/mfd/tps65912-core.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * tps65912-core.c -- TI TPS65912x - * - * Copyright 2011 Texas Instruments Inc. - * - * Author: Margarita Olaya Cabrera - * - * 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. - * - * This driver is based on wm8350 implementation. - */ - -#include -#include -#include -#include -#include -#include - -static const struct mfd_cell tps65912s[] = { - { - .name = "tps65912-pmic", - }, -}; - -int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask) -{ - u8 data; - int err; - - mutex_lock(&tps65912->io_mutex); - - err = tps65912->read(tps65912, reg, 1, &data); - if (err) { - dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg); - goto out; - } - - data |= mask; - err = tps65912->write(tps65912, reg, 1, &data); - if (err) - dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg); - -out: - mutex_unlock(&tps65912->io_mutex); - return err; -} -EXPORT_SYMBOL_GPL(tps65912_set_bits); - -int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask) -{ - u8 data; - int err; - - mutex_lock(&tps65912->io_mutex); - err = tps65912->read(tps65912, reg, 1, &data); - if (err) { - dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg); - goto out; - } - - data &= ~mask; - err = tps65912->write(tps65912, reg, 1, &data); - if (err) - dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg); - -out: - mutex_unlock(&tps65912->io_mutex); - return err; -} -EXPORT_SYMBOL_GPL(tps65912_clear_bits); - -static inline int tps65912_read(struct tps65912 *tps65912, u8 reg) -{ - u8 val; - int err; - - err = tps65912->read(tps65912, reg, 1, &val); - if (err < 0) - return err; - - return val; -} - -static inline int tps65912_write(struct tps65912 *tps65912, u8 reg, u8 val) -{ - return tps65912->write(tps65912, reg, 1, &val); -} - -int tps65912_reg_read(struct tps65912 *tps65912, u8 reg) -{ - int data; - - mutex_lock(&tps65912->io_mutex); - - data = tps65912_read(tps65912, reg); - if (data < 0) - dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg); - - mutex_unlock(&tps65912->io_mutex); - return data; -} -EXPORT_SYMBOL_GPL(tps65912_reg_read); - -int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val) -{ - int err; - - mutex_lock(&tps65912->io_mutex); - - err = tps65912_write(tps65912, reg, val); - if (err < 0) - dev_err(tps65912->dev, "Write for reg 0x%x failed\n", reg); - - mutex_unlock(&tps65912->io_mutex); - return err; -} -EXPORT_SYMBOL_GPL(tps65912_reg_write); - -int tps65912_device_init(struct tps65912 *tps65912) -{ - struct tps65912_board *pmic_plat_data = dev_get_platdata(tps65912->dev); - struct tps65912_platform_data *init_data; - int ret, dcdc_avs, value; - - init_data = kzalloc(sizeof(struct tps65912_platform_data), GFP_KERNEL); - if (init_data == NULL) - return -ENOMEM; - - mutex_init(&tps65912->io_mutex); - dev_set_drvdata(tps65912->dev, tps65912); - - dcdc_avs = (pmic_plat_data->is_dcdc1_avs << 0 | - pmic_plat_data->is_dcdc2_avs << 1 | - pmic_plat_data->is_dcdc3_avs << 2 | - pmic_plat_data->is_dcdc4_avs << 3); - if (dcdc_avs) { - tps65912->read(tps65912, TPS65912_I2C_SPI_CFG, 1, &value); - dcdc_avs |= value; - tps65912->write(tps65912, TPS65912_I2C_SPI_CFG, 1, &dcdc_avs); - } - - ret = mfd_add_devices(tps65912->dev, -1, - tps65912s, ARRAY_SIZE(tps65912s), - NULL, 0, NULL); - if (ret < 0) - goto err; - - init_data->irq = pmic_plat_data->irq; - init_data->irq_base = pmic_plat_data->irq_base; - ret = tps65912_irq_init(tps65912, init_data->irq, init_data); - if (ret < 0) - goto err; - - kfree(init_data); - return ret; - -err: - kfree(init_data); - mfd_remove_devices(tps65912->dev); - return ret; -} - -void tps65912_device_exit(struct tps65912 *tps65912) -{ - mfd_remove_devices(tps65912->dev); - tps65912_irq_exit(tps65912); -} - -MODULE_AUTHOR("Margarita Olaya "); -MODULE_DESCRIPTION("TPS65912x chip family multi-function driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/tps65912-i2c.c b/drivers/mfd/tps65912-i2c.c deleted file mode 100644 index 7e55640b3ed5..000000000000 --- a/drivers/mfd/tps65912-i2c.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * tps65912-i2c.c -- I2C access for TI TPS65912x PMIC - * - * Copyright 2011 Texas Instruments Inc. - * - * Author: Margarita Olaya Cabrera - * - * 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. - * - * This driver is based on wm8350 implementation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static int tps65912_i2c_read(struct tps65912 *tps65912, u8 reg, - int bytes, void *dest) -{ - struct i2c_client *i2c = tps65912->control_data; - struct i2c_msg xfer[2]; - int ret; - - /* Write register */ - xfer[0].addr = i2c->addr; - xfer[0].flags = 0; - xfer[0].len = 1; - xfer[0].buf = ® - - /* Read data */ - xfer[1].addr = i2c->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = bytes; - xfer[1].buf = dest; - - ret = i2c_transfer(i2c->adapter, xfer, 2); - if (ret == 2) - ret = 0; - else if (ret >= 0) - ret = -EIO; - return ret; -} - -static int tps65912_i2c_write(struct tps65912 *tps65912, u8 reg, - int bytes, void *src) -{ - struct i2c_client *i2c = tps65912->control_data; - /* we add 1 byte for device register */ - u8 msg[TPS6591X_MAX_REGISTER + 1]; - int ret; - - if (bytes > TPS6591X_MAX_REGISTER) - return -EINVAL; - - msg[0] = reg; - memcpy(&msg[1], src, bytes); - - ret = i2c_master_send(i2c, msg, bytes + 1); - if (ret < 0) - return ret; - if (ret != bytes + 1) - return -EIO; - - return 0; -} - -static int tps65912_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct tps65912 *tps65912; - - tps65912 = devm_kzalloc(&i2c->dev, - sizeof(struct tps65912), GFP_KERNEL); - if (tps65912 == NULL) - return -ENOMEM; - - i2c_set_clientdata(i2c, tps65912); - tps65912->dev = &i2c->dev; - tps65912->control_data = i2c; - tps65912->read = tps65912_i2c_read; - tps65912->write = tps65912_i2c_write; - - return tps65912_device_init(tps65912); -} - -static int tps65912_i2c_remove(struct i2c_client *i2c) -{ - struct tps65912 *tps65912 = i2c_get_clientdata(i2c); - - tps65912_device_exit(tps65912); - - return 0; -} - -static const struct i2c_device_id tps65912_i2c_id[] = { - {"tps65912", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tps65912_i2c_id); - -static struct i2c_driver tps65912_i2c_driver = { - .driver = { - .name = "tps65912", - }, - .probe = tps65912_i2c_probe, - .remove = tps65912_i2c_remove, - .id_table = tps65912_i2c_id, -}; - -static int __init tps65912_i2c_init(void) -{ - int ret; - - ret = i2c_add_driver(&tps65912_i2c_driver); - if (ret != 0) - pr_err("Failed to register TPS65912 I2C driver: %d\n", ret); - - return ret; -} -/* init early so consumer devices can complete system boot */ -subsys_initcall(tps65912_i2c_init); - -static void __exit tps65912_i2c_exit(void) -{ - i2c_del_driver(&tps65912_i2c_driver); -} -module_exit(tps65912_i2c_exit); - -MODULE_AUTHOR("Margarita Olaya "); -MODULE_DESCRIPTION("TPS6591x chip family multi-function driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/tps65912-irq.c b/drivers/mfd/tps65912-irq.c deleted file mode 100644 index db2c29cb709b..000000000000 --- a/drivers/mfd/tps65912-irq.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * tps65912-irq.c -- TI TPS6591x - * - * Copyright 2011 Texas Instruments Inc. - * - * Author: Margarita Olaya - * - * 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. - * - * This driver is based on wm8350 implementation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static inline int irq_to_tps65912_irq(struct tps65912 *tps65912, - int irq) -{ - return irq - tps65912->irq_base; -} - -/* - * This is a threaded IRQ handler so can access I2C/SPI. Since the - * IRQ handler explicitly clears the IRQ it handles the IRQ line - * will be reasserted and the physical IRQ will be handled again if - * another interrupt is asserted while we run - in the normal course - * of events this is a rare occurrence so we save I2C/SPI reads. We're - * also assuming that it's rare to get lots of interrupts firing - * simultaneously so try to minimise I/O. - */ -static irqreturn_t tps65912_irq(int irq, void *irq_data) -{ - struct tps65912 *tps65912 = irq_data; - u32 irq_sts; - u32 irq_mask; - u8 reg; - int i; - - - tps65912->read(tps65912, TPS65912_INT_STS, 1, ®); - irq_sts = reg; - tps65912->read(tps65912, TPS65912_INT_STS2, 1, ®); - irq_sts |= reg << 8; - tps65912->read(tps65912, TPS65912_INT_STS3, 1, ®); - irq_sts |= reg << 16; - tps65912->read(tps65912, TPS65912_INT_STS4, 1, ®); - irq_sts |= reg << 24; - - tps65912->read(tps65912, TPS65912_INT_MSK, 1, ®); - irq_mask = reg; - tps65912->read(tps65912, TPS65912_INT_MSK2, 1, ®); - irq_mask |= reg << 8; - tps65912->read(tps65912, TPS65912_INT_MSK3, 1, ®); - irq_mask |= reg << 16; - tps65912->read(tps65912, TPS65912_INT_MSK4, 1, ®); - irq_mask |= reg << 24; - - irq_sts &= ~irq_mask; - if (!irq_sts) - return IRQ_NONE; - - for (i = 0; i < tps65912->irq_num; i++) { - if (!(irq_sts & (1 << i))) - continue; - - handle_nested_irq(tps65912->irq_base + i); - } - - /* Write the STS register back to clear IRQs we handled */ - reg = irq_sts & 0xFF; - irq_sts >>= 8; - if (reg) - tps65912->write(tps65912, TPS65912_INT_STS, 1, ®); - reg = irq_sts & 0xFF; - irq_sts >>= 8; - if (reg) - tps65912->write(tps65912, TPS65912_INT_STS2, 1, ®); - reg = irq_sts & 0xFF; - irq_sts >>= 8; - if (reg) - tps65912->write(tps65912, TPS65912_INT_STS3, 1, ®); - reg = irq_sts & 0xFF; - if (reg) - tps65912->write(tps65912, TPS65912_INT_STS4, 1, ®); - - return IRQ_HANDLED; -} - -static void tps65912_irq_lock(struct irq_data *data) -{ - struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data); - - mutex_lock(&tps65912->irq_lock); -} - -static void tps65912_irq_sync_unlock(struct irq_data *data) -{ - struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data); - u32 reg_mask; - u8 reg; - - tps65912->read(tps65912, TPS65912_INT_MSK, 1, ®); - reg_mask = reg; - tps65912->read(tps65912, TPS65912_INT_MSK2, 1, ®); - reg_mask |= reg << 8; - tps65912->read(tps65912, TPS65912_INT_MSK3, 1, ®); - reg_mask |= reg << 16; - tps65912->read(tps65912, TPS65912_INT_MSK4, 1, ®); - reg_mask |= reg << 24; - - if (tps65912->irq_mask != reg_mask) { - reg = tps65912->irq_mask & 0xFF; - tps65912->write(tps65912, TPS65912_INT_MSK, 1, ®); - reg = tps65912->irq_mask >> 8 & 0xFF; - tps65912->write(tps65912, TPS65912_INT_MSK2, 1, ®); - reg = tps65912->irq_mask >> 16 & 0xFF; - tps65912->write(tps65912, TPS65912_INT_MSK3, 1, ®); - reg = tps65912->irq_mask >> 24 & 0xFF; - tps65912->write(tps65912, TPS65912_INT_MSK4, 1, ®); - } - - mutex_unlock(&tps65912->irq_lock); -} - -static void tps65912_irq_enable(struct irq_data *data) -{ - struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data); - - tps65912->irq_mask &= ~(1 << irq_to_tps65912_irq(tps65912, data->irq)); -} - -static void tps65912_irq_disable(struct irq_data *data) -{ - struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data); - - tps65912->irq_mask |= (1 << irq_to_tps65912_irq(tps65912, data->irq)); -} - -static struct irq_chip tps65912_irq_chip = { - .name = "tps65912", - .irq_bus_lock = tps65912_irq_lock, - .irq_bus_sync_unlock = tps65912_irq_sync_unlock, - .irq_disable = tps65912_irq_disable, - .irq_enable = tps65912_irq_enable, -}; - -int tps65912_irq_init(struct tps65912 *tps65912, int irq, - struct tps65912_platform_data *pdata) -{ - int ret, cur_irq; - int flags = IRQF_ONESHOT; - u8 reg; - - if (!irq) { - dev_warn(tps65912->dev, "No interrupt support, no core IRQ\n"); - return 0; - } - - if (!pdata || !pdata->irq_base) { - dev_warn(tps65912->dev, "No interrupt support, no IRQ base\n"); - return 0; - } - - /* Clear unattended interrupts */ - tps65912->read(tps65912, TPS65912_INT_STS, 1, ®); - tps65912->write(tps65912, TPS65912_INT_STS, 1, ®); - tps65912->read(tps65912, TPS65912_INT_STS2, 1, ®); - tps65912->write(tps65912, TPS65912_INT_STS2, 1, ®); - tps65912->read(tps65912, TPS65912_INT_STS3, 1, ®); - tps65912->write(tps65912, TPS65912_INT_STS3, 1, ®); - tps65912->read(tps65912, TPS65912_INT_STS4, 1, ®); - tps65912->write(tps65912, TPS65912_INT_STS4, 1, ®); - - /* Mask top level interrupts */ - tps65912->irq_mask = 0xFFFFFFFF; - - mutex_init(&tps65912->irq_lock); - tps65912->chip_irq = irq; - tps65912->irq_base = pdata->irq_base; - - tps65912->irq_num = TPS65912_NUM_IRQ; - - /* Register with genirq */ - for (cur_irq = tps65912->irq_base; - cur_irq < tps65912->irq_num + tps65912->irq_base; - cur_irq++) { - irq_set_chip_data(cur_irq, tps65912); - irq_set_chip_and_handler(cur_irq, &tps65912_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 1); - irq_clear_status_flags(cur_irq, IRQ_NOREQUEST | IRQ_NOPROBE); - } - - ret = request_threaded_irq(irq, NULL, tps65912_irq, flags, - "tps65912", tps65912); - - irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); - if (ret != 0) - dev_err(tps65912->dev, "Failed to request IRQ: %d\n", ret); - - return ret; -} - -int tps65912_irq_exit(struct tps65912 *tps65912) -{ - free_irq(tps65912->chip_irq, tps65912); - return 0; -} diff --git a/drivers/mfd/tps65912-spi.c b/drivers/mfd/tps65912-spi.c deleted file mode 100644 index d59aa55b1495..000000000000 --- a/drivers/mfd/tps65912-spi.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * tps65912-spi.c -- SPI access for TI TPS65912x PMIC - * - * Copyright 2011 Texas Instruments Inc. - * - * Author: Margarita Olaya Cabrera - * - * 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. - * - * This driver is based on wm8350 implementation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static int tps65912_spi_write(struct tps65912 *tps65912, u8 addr, - int bytes, void *src) -{ - struct spi_device *spi = tps65912->control_data; - u8 *data = (u8 *) src; - int ret; - /* bit 23 is the read/write bit */ - unsigned long spi_data = 1 << 23 | addr << 15 | *data; - struct spi_transfer xfer; - struct spi_message msg; - u32 tx_buf; - - tx_buf = spi_data; - - xfer.tx_buf = &tx_buf; - xfer.rx_buf = NULL; - xfer.len = sizeof(unsigned long); - xfer.bits_per_word = 24; - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - ret = spi_sync(spi, &msg); - return ret; -} - -static int tps65912_spi_read(struct tps65912 *tps65912, u8 addr, - int bytes, void *dest) -{ - struct spi_device *spi = tps65912->control_data; - /* bit 23 is the read/write bit */ - unsigned long spi_data = 0 << 23 | addr << 15; - struct spi_transfer xfer; - struct spi_message msg; - int ret; - u8 *data = (u8 *) dest; - u32 tx_buf, rx_buf; - - tx_buf = spi_data; - rx_buf = 0; - - xfer.tx_buf = &tx_buf; - xfer.rx_buf = &rx_buf; - xfer.len = sizeof(unsigned long); - xfer.bits_per_word = 24; - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - if (spi == NULL) - return 0; - - ret = spi_sync(spi, &msg); - if (ret == 0) - *data = (u8) (rx_buf & 0xFF); - return ret; -} - -static int tps65912_spi_probe(struct spi_device *spi) -{ - struct tps65912 *tps65912; - - tps65912 = devm_kzalloc(&spi->dev, - sizeof(struct tps65912), GFP_KERNEL); - if (tps65912 == NULL) - return -ENOMEM; - - tps65912->dev = &spi->dev; - tps65912->control_data = spi; - tps65912->read = tps65912_spi_read; - tps65912->write = tps65912_spi_write; - - spi_set_drvdata(spi, tps65912); - - return tps65912_device_init(tps65912); -} - -static int tps65912_spi_remove(struct spi_device *spi) -{ - struct tps65912 *tps65912 = spi_get_drvdata(spi); - - tps65912_device_exit(tps65912); - - return 0; -} - -static struct spi_driver tps65912_spi_driver = { - .driver = { - .name = "tps65912", - }, - .probe = tps65912_spi_probe, - .remove = tps65912_spi_remove, -}; - -static int __init tps65912_spi_init(void) -{ - int ret; - - ret = spi_register_driver(&tps65912_spi_driver); - if (ret != 0) - pr_err("Failed to register TPS65912 SPI driver: %d\n", ret); - - return 0; -} -/* init early so consumer devices can complete system boot */ -subsys_initcall(tps65912_spi_init); - -static void __exit tps65912_spi_exit(void) -{ - spi_unregister_driver(&tps65912_spi_driver); -} -module_exit(tps65912_spi_exit); - -MODULE_AUTHOR("Margarita Olaya "); -MODULE_DESCRIPTION("SPI support for TPS65912 chip family mfd"); -MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 8155e80dd3f8..eb1b4a26cb3e 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -760,12 +760,6 @@ config REGULATOR_TPS65910 help This driver supports TPS65910/TPS65911 voltage regulator chips. -config REGULATOR_TPS65912 - tristate "TI TPS65912 Power regulator" - depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI) - help - This driver supports TPS65912 voltage regulator chip. - config REGULATOR_TPS80031 tristate "TI TPS80031/TPS80032 power regualtor driver" depends on MFD_TPS80031 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 980b1943fa81..85da3192701c 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -95,7 +95,6 @@ obj-$(CONFIG_REGULATOR_TPS65218) += tps65218-regulator.o obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o -obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c deleted file mode 100644 index 9503d5481a52..000000000000 --- a/drivers/regulator/tps65912-regulator.c +++ /dev/null @@ -1,541 +0,0 @@ -/* - * tps65912.c -- TI tps65912 - * - * Copyright 2011 Texas Instruments Inc. - * - * Author: Margarita Olaya Cabrera - * - * 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. - * - * This driver is based on wm8350 implementation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* DCDC's */ -#define TPS65912_REG_DCDC1 0 -#define TPS65912_REG_DCDC2 1 -#define TPS65912_REG_DCDC3 2 -#define TPS65912_REG_DCDC4 3 - -/* LDOs */ -#define TPS65912_REG_LDO1 4 -#define TPS65912_REG_LDO2 5 -#define TPS65912_REG_LDO3 6 -#define TPS65912_REG_LDO4 7 -#define TPS65912_REG_LDO5 8 -#define TPS65912_REG_LDO6 9 -#define TPS65912_REG_LDO7 10 -#define TPS65912_REG_LDO8 11 -#define TPS65912_REG_LDO9 12 -#define TPS65912_REG_LDO10 13 - -/* Number of step-down converters available */ -#define TPS65912_NUM_DCDC 4 - -/* Number of LDO voltage regulators available */ -#define TPS65912_NUM_LDO 10 - -/* Number of total regulators available */ -#define TPS65912_NUM_REGULATOR (TPS65912_NUM_DCDC + TPS65912_NUM_LDO) - -#define TPS65912_REG_ENABLED 0x80 -#define OP_SELREG_MASK 0x40 -#define OP_SELREG_SHIFT 6 - -struct tps_info { - const char *name; -}; - -static struct tps_info tps65912_regs[] = { - { - .name = "DCDC1", - }, - { - .name = "DCDC2", - }, - { - .name = "DCDC3", - }, - { - .name = "DCDC4", - }, - { - .name = "LDO1", - }, - { - .name = "LDO2", - }, - { - .name = "LDO3", - }, - { - .name = "LDO4", - }, - { - .name = "LDO5", - }, - { - .name = "LDO6", - }, - { - .name = "LDO7", - }, - { - .name = "LDO8", - }, - { - .name = "LDO9", - }, - { - .name = "LDO10", - }, -}; - -struct tps65912_reg { - struct regulator_desc desc[TPS65912_NUM_REGULATOR]; - struct tps65912 *mfd; - struct regulator_dev *rdev[TPS65912_NUM_REGULATOR]; - struct tps_info *info[TPS65912_NUM_REGULATOR]; - /* for read/write access */ - struct mutex io_lock; - int mode; - int (*get_ctrl_reg)(int); - int dcdc_range[TPS65912_NUM_DCDC]; - int pwm_mode_reg; - int eco_reg; -}; - -static const struct regulator_linear_range tps65912_ldo_ranges[] = { - REGULATOR_LINEAR_RANGE(800000, 0, 32, 25000), - REGULATOR_LINEAR_RANGE(1650000, 33, 60, 50000), - REGULATOR_LINEAR_RANGE(3100000, 61, 63, 100000), -}; - -static int tps65912_get_range(struct tps65912_reg *pmic, int id) -{ - struct tps65912 *mfd = pmic->mfd; - int range; - - switch (id) { - case TPS65912_REG_DCDC1: - range = tps65912_reg_read(mfd, TPS65912_DCDC1_LIMIT); - break; - case TPS65912_REG_DCDC2: - range = tps65912_reg_read(mfd, TPS65912_DCDC2_LIMIT); - break; - case TPS65912_REG_DCDC3: - range = tps65912_reg_read(mfd, TPS65912_DCDC3_LIMIT); - break; - case TPS65912_REG_DCDC4: - range = tps65912_reg_read(mfd, TPS65912_DCDC4_LIMIT); - break; - default: - return 0; - } - - if (range >= 0) - range = (range & DCDC_LIMIT_RANGE_MASK) - >> DCDC_LIMIT_RANGE_SHIFT; - - pmic->dcdc_range[id] = range; - return range; -} - -static unsigned long tps65912_vsel_to_uv_range0(u8 vsel) -{ - unsigned long uv; - - uv = ((vsel * 12500) + 500000); - return uv; -} - -static unsigned long tps65912_vsel_to_uv_range1(u8 vsel) -{ - unsigned long uv; - - uv = ((vsel * 12500) + 700000); - return uv; -} - -static unsigned long tps65912_vsel_to_uv_range2(u8 vsel) -{ - unsigned long uv; - - uv = ((vsel * 25000) + 500000); - return uv; -} - -static unsigned long tps65912_vsel_to_uv_range3(u8 vsel) -{ - unsigned long uv; - - if (vsel == 0x3f) - uv = 3800000; - else - uv = ((vsel * 50000) + 500000); - - return uv; -} - -static int tps65912_get_ctrl_register(int id) -{ - if (id >= TPS65912_REG_DCDC1 && id <= TPS65912_REG_LDO4) - return id * 3 + TPS65912_DCDC1_AVS; - else if (id >= TPS65912_REG_LDO5 && id <= TPS65912_REG_LDO10) - return id - TPS65912_REG_LDO5 + TPS65912_LDO5; - else - return -EINVAL; -} - -static int tps65912_get_sel_register(struct tps65912_reg *pmic, int id) -{ - struct tps65912 *mfd = pmic->mfd; - int opvsel; - u8 reg = 0; - - if (id >= TPS65912_REG_DCDC1 && id <= TPS65912_REG_LDO4) { - opvsel = tps65912_reg_read(mfd, id * 3 + TPS65912_DCDC1_OP); - if (opvsel & OP_SELREG_MASK) - reg = id * 3 + TPS65912_DCDC1_AVS; - else - reg = id * 3 + TPS65912_DCDC1_OP; - } else if (id >= TPS65912_REG_LDO5 && id <= TPS65912_REG_LDO10) { - reg = id - TPS65912_REG_LDO5 + TPS65912_LDO5; - } else { - return -EINVAL; - } - - return reg; -} - -static int tps65912_get_mode_regiters(struct tps65912_reg *pmic, int id) -{ - switch (id) { - case TPS65912_REG_DCDC1: - pmic->pwm_mode_reg = TPS65912_DCDC1_CTRL; - pmic->eco_reg = TPS65912_DCDC1_AVS; - break; - case TPS65912_REG_DCDC2: - pmic->pwm_mode_reg = TPS65912_DCDC2_CTRL; - pmic->eco_reg = TPS65912_DCDC2_AVS; - break; - case TPS65912_REG_DCDC3: - pmic->pwm_mode_reg = TPS65912_DCDC3_CTRL; - pmic->eco_reg = TPS65912_DCDC3_AVS; - break; - case TPS65912_REG_DCDC4: - pmic->pwm_mode_reg = TPS65912_DCDC4_CTRL; - pmic->eco_reg = TPS65912_DCDC4_AVS; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int tps65912_reg_is_enabled(struct regulator_dev *dev) -{ - struct tps65912_reg *pmic = rdev_get_drvdata(dev); - struct tps65912 *mfd = pmic->mfd; - int reg, value, id = rdev_get_id(dev); - - if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10) - return -EINVAL; - - reg = pmic->get_ctrl_reg(id); - if (reg < 0) - return reg; - - value = tps65912_reg_read(mfd, reg); - if (value < 0) - return value; - - return value & TPS65912_REG_ENABLED; -} - -static int tps65912_reg_enable(struct regulator_dev *dev) -{ - struct tps65912_reg *pmic = rdev_get_drvdata(dev); - struct tps65912 *mfd = pmic->mfd; - int id = rdev_get_id(dev); - int reg; - - if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10) - return -EINVAL; - - reg = pmic->get_ctrl_reg(id); - if (reg < 0) - return reg; - - return tps65912_set_bits(mfd, reg, TPS65912_REG_ENABLED); -} - -static int tps65912_reg_disable(struct regulator_dev *dev) -{ - struct tps65912_reg *pmic = rdev_get_drvdata(dev); - struct tps65912 *mfd = pmic->mfd; - int id = rdev_get_id(dev), reg; - - reg = pmic->get_ctrl_reg(id); - if (reg < 0) - return reg; - - return tps65912_clear_bits(mfd, reg, TPS65912_REG_ENABLED); -} - -static int tps65912_set_mode(struct regulator_dev *dev, unsigned int mode) -{ - struct tps65912_reg *pmic = rdev_get_drvdata(dev); - struct tps65912 *mfd = pmic->mfd; - int pwm_mode, eco, id = rdev_get_id(dev); - - tps65912_get_mode_regiters(pmic, id); - - pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg); - eco = tps65912_reg_read(mfd, pmic->eco_reg); - - pwm_mode &= DCDCCTRL_DCDC_MODE_MASK; - eco &= DCDC_AVS_ECO_MASK; - - switch (mode) { - case REGULATOR_MODE_FAST: - /* Verify if mode alredy set */ - if (pwm_mode && !eco) - break; - tps65912_set_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK); - tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK); - break; - case REGULATOR_MODE_NORMAL: - case REGULATOR_MODE_IDLE: - if (!pwm_mode && !eco) - break; - tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK); - tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK); - break; - case REGULATOR_MODE_STANDBY: - if (!pwm_mode && eco) - break; - tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK); - tps65912_set_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK); - break; - default: - return -EINVAL; - } - - return 0; -} - -static unsigned int tps65912_get_mode(struct regulator_dev *dev) -{ - struct tps65912_reg *pmic = rdev_get_drvdata(dev); - struct tps65912 *mfd = pmic->mfd; - int pwm_mode, eco, mode = 0, id = rdev_get_id(dev); - - tps65912_get_mode_regiters(pmic, id); - - pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg); - eco = tps65912_reg_read(mfd, pmic->eco_reg); - - pwm_mode &= DCDCCTRL_DCDC_MODE_MASK; - eco &= DCDC_AVS_ECO_MASK; - - if (pwm_mode && !eco) - mode = REGULATOR_MODE_FAST; - else if (!pwm_mode && !eco) - mode = REGULATOR_MODE_NORMAL; - else if (!pwm_mode && eco) - mode = REGULATOR_MODE_STANDBY; - - return mode; -} - -static int tps65912_list_voltage(struct regulator_dev *dev, unsigned selector) -{ - struct tps65912_reg *pmic = rdev_get_drvdata(dev); - int range, voltage = 0, id = rdev_get_id(dev); - - if (id > TPS65912_REG_DCDC4) - return -EINVAL; - - range = pmic->dcdc_range[id]; - - switch (range) { - case 0: - /* 0.5 - 1.2875V in 12.5mV steps */ - voltage = tps65912_vsel_to_uv_range0(selector); - break; - case 1: - /* 0.7 - 1.4875V in 12.5mV steps */ - voltage = tps65912_vsel_to_uv_range1(selector); - break; - case 2: - /* 0.5 - 2.075V in 25mV steps */ - voltage = tps65912_vsel_to_uv_range2(selector); - break; - case 3: - /* 0.5 - 3.8V in 50mV steps */ - voltage = tps65912_vsel_to_uv_range3(selector); - break; - } - return voltage; -} - -static int tps65912_get_voltage_sel(struct regulator_dev *dev) -{ - struct tps65912_reg *pmic = rdev_get_drvdata(dev); - struct tps65912 *mfd = pmic->mfd; - int id = rdev_get_id(dev); - int reg, vsel; - - reg = tps65912_get_sel_register(pmic, id); - if (reg < 0) - return reg; - - vsel = tps65912_reg_read(mfd, reg); - vsel &= 0x3F; - - return vsel; -} - -static int tps65912_set_voltage_sel(struct regulator_dev *dev, - unsigned selector) -{ - struct tps65912_reg *pmic = rdev_get_drvdata(dev); - struct tps65912 *mfd = pmic->mfd; - int id = rdev_get_id(dev); - int value; - u8 reg; - - reg = tps65912_get_sel_register(pmic, id); - value = tps65912_reg_read(mfd, reg); - value &= 0xC0; - return tps65912_reg_write(mfd, reg, selector | value); -} - -/* Operations permitted on DCDCx */ -static struct regulator_ops tps65912_ops_dcdc = { - .is_enabled = tps65912_reg_is_enabled, - .enable = tps65912_reg_enable, - .disable = tps65912_reg_disable, - .set_mode = tps65912_set_mode, - .get_mode = tps65912_get_mode, - .get_voltage_sel = tps65912_get_voltage_sel, - .set_voltage_sel = tps65912_set_voltage_sel, - .list_voltage = tps65912_list_voltage, -}; - -/* Operations permitted on LDOx */ -static struct regulator_ops tps65912_ops_ldo = { - .is_enabled = tps65912_reg_is_enabled, - .enable = tps65912_reg_enable, - .disable = tps65912_reg_disable, - .get_voltage_sel = tps65912_get_voltage_sel, - .set_voltage_sel = tps65912_set_voltage_sel, - .list_voltage = regulator_list_voltage_linear_range, - .map_voltage = regulator_map_voltage_linear_range, -}; - -static int tps65912_probe(struct platform_device *pdev) -{ - struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent); - struct regulator_config config = { }; - struct tps_info *info; - struct regulator_init_data *reg_data; - struct regulator_dev *rdev; - struct tps65912_reg *pmic; - struct tps65912_board *pmic_plat_data; - int i; - - pmic_plat_data = dev_get_platdata(tps65912->dev); - if (!pmic_plat_data) - return -EINVAL; - - reg_data = pmic_plat_data->tps65912_pmic_init_data; - - pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); - if (!pmic) - return -ENOMEM; - - mutex_init(&pmic->io_lock); - pmic->mfd = tps65912; - platform_set_drvdata(pdev, pmic); - - pmic->get_ctrl_reg = &tps65912_get_ctrl_register; - info = tps65912_regs; - - for (i = 0; i < TPS65912_NUM_REGULATOR; i++, info++, reg_data++) { - int range = 0; - /* Register the regulators */ - pmic->info[i] = info; - - pmic->desc[i].name = info->name; - pmic->desc[i].id = i; - pmic->desc[i].n_voltages = 64; - if (i > TPS65912_REG_DCDC4) { - pmic->desc[i].ops = &tps65912_ops_ldo; - pmic->desc[i].linear_ranges = tps65912_ldo_ranges; - pmic->desc[i].n_linear_ranges = - ARRAY_SIZE(tps65912_ldo_ranges); - } else { - pmic->desc[i].ops = &tps65912_ops_dcdc; - } - pmic->desc[i].type = REGULATOR_VOLTAGE; - pmic->desc[i].owner = THIS_MODULE; - range = tps65912_get_range(pmic, i); - - config.dev = tps65912->dev; - config.init_data = reg_data; - config.driver_data = pmic; - - rdev = devm_regulator_register(&pdev->dev, &pmic->desc[i], - &config); - if (IS_ERR(rdev)) { - dev_err(tps65912->dev, - "failed to register %s regulator\n", - pdev->name); - return PTR_ERR(rdev); - } - - /* Save regulator for cleanup */ - pmic->rdev[i] = rdev; - } - return 0; -} - -static struct platform_driver tps65912_driver = { - .driver = { - .name = "tps65912-pmic", - }, - .probe = tps65912_probe, -}; - -static int __init tps65912_init(void) -{ - return platform_driver_register(&tps65912_driver); -} -subsys_initcall(tps65912_init); - -static void __exit tps65912_cleanup(void) -{ - platform_driver_unregister(&tps65912_driver); -} -module_exit(tps65912_cleanup); - -MODULE_AUTHOR("Margarita Olaya Cabrera "); -MODULE_DESCRIPTION("TPS65912 voltage regulator driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:tps65912-pmic"); -- cgit v1.2.3 From 796f5692daac4ea47fa5252af742976cd1955f0b Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Mon, 25 Jan 2016 09:43:45 -0600 Subject: mfd: tps65912: Add driver for the TPS65912 PMIC This patch adds support for TPS65912 PMIC MFD core. It provides communication through the I2C and SPI interfaces. It contains the following components: - Regulators - GPIO controller Signed-off-by: Andrew F. Davis Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 24 ++++++++++ drivers/mfd/Makefile | 3 ++ drivers/mfd/tps65912-core.c | 111 ++++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/tps65912-i2c.c | 79 +++++++++++++++++++++++++++++++ drivers/mfd/tps65912-spi.c | 78 +++++++++++++++++++++++++++++++ 5 files changed, 295 insertions(+) create mode 100644 drivers/mfd/tps65912-core.c create mode 100644 drivers/mfd/tps65912-i2c.c create mode 100644 drivers/mfd/tps65912-spi.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index dd2fc0158c7a..1bc97c2761e4 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1180,6 +1180,30 @@ config MFD_TPS65910 if you say yes here you get support for the TPS65910 series of Power Management chips. +config MFD_TPS65912 + tristate + select MFD_CORE + select REGMAP + select REGMAP_IRQ + +config MFD_TPS65912_I2C + tristate "TI TPS65912 Power Management chip with I2C" + select MFD_TPS65912 + select REGMAP_I2C + depends on I2C + help + If you say yes here you get support for the TPS65912 series of + PM chips with I2C interface. + +config MFD_TPS65912_SPI + tristate "TI TPS65912 Power Management chip with SPI" + select MFD_TPS65912 + select REGMAP_SPI + depends on SPI_MASTER + help + If you say yes here you get support for the TPS65912 series of + PM chips with SPI interface. + config MFD_TPS80031 bool "TI TPS80031/TPS80032 Power Management chips" depends on I2C=y diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e33e0b48f591..1811202cee19 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -73,6 +73,9 @@ obj-$(CONFIG_TPS6507X) += tps6507x.o obj-$(CONFIG_MFD_TPS65217) += tps65217.o obj-$(CONFIG_MFD_TPS65218) += tps65218.o obj-$(CONFIG_MFD_TPS65910) += tps65910.o +obj-$(CONFIG_MFD_TPS65912) += tps65912-core.o +obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o +obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o obj-$(CONFIG_MFD_TPS80031) += tps80031.o obj-$(CONFIG_MENELAUS) += menelaus.o diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c new file mode 100644 index 000000000000..a88cfa80dbc4 --- /dev/null +++ b/drivers/mfd/tps65912-core.c @@ -0,0 +1,111 @@ +/* + * Core functions for TI TPS65912x PMICs + * + * 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 TPS65218 driver and the previous TPS65912 driver by + * Margarita Olaya Cabrera + */ + +#include +#include +#include + +#include + +static const struct mfd_cell tps65912_cells[] = { + { .name = "tps65912-regulator", }, + { .name = "tps65912-gpio", }, +}; + +static const struct regmap_irq tps65912_irqs[] = { + /* INT_STS IRQs */ + REGMAP_IRQ_REG(TPS65912_IRQ_PWRHOLD_F, 0, TPS65912_INT_STS_PWRHOLD_F), + REGMAP_IRQ_REG(TPS65912_IRQ_VMON, 0, TPS65912_INT_STS_VMON), + REGMAP_IRQ_REG(TPS65912_IRQ_PWRON, 0, TPS65912_INT_STS_PWRON), + REGMAP_IRQ_REG(TPS65912_IRQ_PWRON_LP, 0, TPS65912_INT_STS_PWRON_LP), + REGMAP_IRQ_REG(TPS65912_IRQ_PWRHOLD_R, 0, TPS65912_INT_STS_PWRHOLD_R), + REGMAP_IRQ_REG(TPS65912_IRQ_HOTDIE, 0, TPS65912_INT_STS_HOTDIE), + REGMAP_IRQ_REG(TPS65912_IRQ_GPIO1_R, 0, TPS65912_INT_STS_GPIO1_R), + REGMAP_IRQ_REG(TPS65912_IRQ_GPIO1_F, 0, TPS65912_INT_STS_GPIO1_F), + /* INT_STS2 IRQs */ + REGMAP_IRQ_REG(TPS65912_IRQ_GPIO2_R, 1, TPS65912_INT_STS2_GPIO2_R), + REGMAP_IRQ_REG(TPS65912_IRQ_GPIO2_F, 1, TPS65912_INT_STS2_GPIO2_F), + REGMAP_IRQ_REG(TPS65912_IRQ_GPIO3_R, 1, TPS65912_INT_STS2_GPIO3_R), + REGMAP_IRQ_REG(TPS65912_IRQ_GPIO3_F, 1, TPS65912_INT_STS2_GPIO3_F), + REGMAP_IRQ_REG(TPS65912_IRQ_GPIO4_R, 1, TPS65912_INT_STS2_GPIO4_R), + REGMAP_IRQ_REG(TPS65912_IRQ_GPIO4_F, 1, TPS65912_INT_STS2_GPIO4_F), + REGMAP_IRQ_REG(TPS65912_IRQ_GPIO5_R, 1, TPS65912_INT_STS2_GPIO5_R), + REGMAP_IRQ_REG(TPS65912_IRQ_GPIO5_F, 1, TPS65912_INT_STS2_GPIO5_F), + /* INT_STS3 IRQs */ + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_DCDC1, 2, TPS65912_INT_STS3_PGOOD_DCDC1), + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_DCDC2, 2, TPS65912_INT_STS3_PGOOD_DCDC2), + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_DCDC3, 2, TPS65912_INT_STS3_PGOOD_DCDC3), + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_DCDC4, 2, TPS65912_INT_STS3_PGOOD_DCDC4), + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_LDO1, 2, TPS65912_INT_STS3_PGOOD_LDO1), + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_LDO2, 2, TPS65912_INT_STS3_PGOOD_LDO2), + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_LDO3, 2, TPS65912_INT_STS3_PGOOD_LDO3), + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_LDO4, 2, TPS65912_INT_STS3_PGOOD_LDO4), + /* INT_STS4 IRQs */ + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_LDO5, 3, TPS65912_INT_STS4_PGOOD_LDO5), + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_LDO6, 3, TPS65912_INT_STS4_PGOOD_LDO6), + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_LDO7, 3, TPS65912_INT_STS4_PGOOD_LDO7), + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_LDO8, 3, TPS65912_INT_STS4_PGOOD_LDO8), + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_LDO9, 3, TPS65912_INT_STS4_PGOOD_LDO9), + REGMAP_IRQ_REG(TPS65912_IRQ_PGOOD_LDO10, 3, TPS65912_INT_STS4_PGOOD_LDO10), +}; + +static struct regmap_irq_chip tps65912_irq_chip = { + .name = "tps65912", + .irqs = tps65912_irqs, + .num_irqs = ARRAY_SIZE(tps65912_irqs), + .num_regs = 4, + .irq_reg_stride = 2, + .mask_base = TPS65912_INT_MSK, + .status_base = TPS65912_INT_STS, + .ack_base = TPS65912_INT_STS, + .init_ack_masked = true, +}; + +int tps65912_device_init(struct tps65912 *tps) +{ + int ret; + + ret = regmap_add_irq_chip(tps->regmap, tps->irq, IRQF_ONESHOT, 0, + &tps65912_irq_chip, &tps->irq_data); + if (ret) + return ret; + + ret = mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO, tps65912_cells, + ARRAY_SIZE(tps65912_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; +} +EXPORT_SYMBOL_GPL(tps65912_device_init); + +int tps65912_device_exit(struct tps65912 *tps) +{ + regmap_del_irq_chip(tps->irq, tps->irq_data); + + return 0; +} +EXPORT_SYMBOL_GPL(tps65912_device_exit); + +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("TPS65912x MFD Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/tps65912-i2c.c b/drivers/mfd/tps65912-i2c.c new file mode 100644 index 000000000000..45871403f995 --- /dev/null +++ b/drivers/mfd/tps65912-i2c.c @@ -0,0 +1,79 @@ +/* + * I2C access driver for TI TPS65912x PMICs + * + * 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 TPS65218 driver and the previous TPS65912 driver by + * Margarita Olaya Cabrera + */ + +#include +#include +#include + +#include + +static const struct of_device_id tps65912_i2c_of_match_table[] = { + { .compatible = "ti,tps65912", }, + { /* sentinel */ } +}; + +static int tps65912_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *ids) +{ + struct tps65912 *tps; + + 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, &tps65912_regmap_config); + if (IS_ERR(tps->regmap)) { + dev_err(tps->dev, "Failed to initialize register map\n"); + return PTR_ERR(tps->regmap); + } + + return tps65912_device_init(tps); +} + +static int tps65912_i2c_remove(struct i2c_client *client) +{ + struct tps65912 *tps = i2c_get_clientdata(client); + + return tps65912_device_exit(tps); +} + +static const struct i2c_device_id tps65912_i2c_id_table[] = { + { "tps65912", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, tps65912_i2c_id_table); + +static struct i2c_driver tps65912_i2c_driver = { + .driver = { + .name = "tps65912", + .of_match_table = tps65912_i2c_of_match_table, + }, + .probe = tps65912_i2c_probe, + .remove = tps65912_i2c_remove, + .id_table = tps65912_i2c_id_table, +}; +module_i2c_driver(tps65912_i2c_driver); + +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("TPS65912x I2C Interface Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/tps65912-spi.c b/drivers/mfd/tps65912-spi.c new file mode 100644 index 000000000000..4aeba9b6942a --- /dev/null +++ b/drivers/mfd/tps65912-spi.c @@ -0,0 +1,78 @@ +/* + * SPI access driver for TI TPS65912x PMICs + * + * 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 TPS65218 driver and the previous TPS65912 driver by + * Margarita Olaya Cabrera + */ + +#include +#include +#include + +#include + +static const struct of_device_id tps65912_spi_of_match_table[] = { + { .compatible = "ti,tps65912", }, + { /* sentinel */ } +}; + +static int tps65912_spi_probe(struct spi_device *spi) +{ + struct tps65912 *tps; + + tps = devm_kzalloc(&spi->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + spi_set_drvdata(spi, tps); + tps->dev = &spi->dev; + tps->irq = spi->irq; + + tps->regmap = devm_regmap_init_spi(spi, &tps65912_regmap_config); + if (IS_ERR(tps->regmap)) { + dev_err(tps->dev, "Failed to initialize register map\n"); + return PTR_ERR(tps->regmap); + } + + return tps65912_device_init(tps); +} + +static int tps65912_spi_remove(struct spi_device *client) +{ + struct tps65912 *tps = spi_get_drvdata(client); + + return tps65912_device_exit(tps); +} + +static const struct spi_device_id tps65912_spi_id_table[] = { + { "tps65912", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, tps65912_spi_id_table); + +static struct spi_driver tps65912_spi_driver = { + .driver = { + .name = "tps65912", + .of_match_table = tps65912_spi_of_match_table, + }, + .probe = tps65912_spi_probe, + .remove = tps65912_spi_remove, + .id_table = tps65912_spi_id_table, +}; +module_spi_driver(tps65912_spi_driver); + +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("TPS65912x SPI Interface Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 33f9d8c0b49c1e63d6efc6e60a52afcf85f5de65 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Mon, 25 Jan 2016 09:43:46 -0600 Subject: regulator: tps65912: Add regulator driver for the TPS65912 PMIC This patch adds support for TPS65912 PMIC regulators. The regulators set consists of 4 DCDCs and 10 LDOs. The output voltages are configurable and are meant to supply power to the main processor and other components. Signed-off-by: Andrew F. Davis Acked-by: Mark Brown Signed-off-by: Lee Jones --- drivers/regulator/Kconfig | 6 ++ drivers/regulator/Makefile | 1 + drivers/regulator/tps65912-regulator.c | 168 +++++++++++++++++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 drivers/regulator/tps65912-regulator.c (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index eb1b4a26cb3e..32f77a1e8305 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -760,6 +760,12 @@ config REGULATOR_TPS65910 help This driver supports TPS65910/TPS65911 voltage regulator chips. +config REGULATOR_TPS65912 + tristate "TI TPS65912 Power regulator" + depends on MFD_TPS65912 + help + This driver supports TPS65912 voltage regulator chip. + config REGULATOR_TPS80031 tristate "TI TPS80031/TPS80032 power regualtor driver" depends on MFD_TPS80031 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 85da3192701c..980b1943fa81 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_REGULATOR_TPS65218) += tps65218-regulator.o obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o +obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c new file mode 100644 index 000000000000..a4921a70da55 --- /dev/null +++ b/drivers/regulator/tps65912-regulator.c @@ -0,0 +1,168 @@ +/* + * Regulator driver for TI TPS65912x PMICs + * + * 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 TPS65218 driver and the previous TPS65912 driver by + * Margarita Olaya Cabrera + */ + +#include +#include +#include + +#include + +enum tps65912_regulators { DCDC1, DCDC2, DCDC3, DCDC4, LDO1, LDO2, LDO3, + LDO4, LDO5, LDO6, LDO7, LDO8, LDO9, LDO10 }; + +#define TPS65912_REGULATOR(_name, _id, _of_match, _ops, _vr, _er, _lr) \ + [_id] = { \ + .name = _name, \ + .of_match = _of_match, \ + .regulators_node = "regulators", \ + .id = _id, \ + .ops = &_ops, \ + .n_voltages = 64, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .vsel_reg = _vr, \ + .vsel_mask = 0x3f, \ + .enable_reg = _er, \ + .enable_mask = BIT(7), \ + .volt_table = NULL, \ + .linear_ranges = _lr, \ + .n_linear_ranges = ARRAY_SIZE(_lr), \ + } + +static const struct regulator_linear_range tps65912_dcdc_ranges[] = { + REGULATOR_LINEAR_RANGE(500000, 0x0, 0x3f, 50000), +}; + +static const struct regulator_linear_range tps65912_ldo_ranges[] = { + REGULATOR_LINEAR_RANGE(800000, 0x0, 0x20, 25000), + REGULATOR_LINEAR_RANGE(1650000, 0x21, 0x3c, 50000), + REGULATOR_LINEAR_RANGE(3100000, 0x3d, 0x3f, 100000), +}; + +/* Operations permitted on DCDCx */ +static struct regulator_ops tps65912_ops_dcdc = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear_range, +}; + +/* Operations permitted on LDOx */ +static struct regulator_ops tps65912_ops_ldo = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, +}; + +static const struct regulator_desc regulators[] = { + TPS65912_REGULATOR("DCDC1", DCDC1, "dcdc1", tps65912_ops_dcdc, + TPS65912_DCDC1_OP, TPS65912_DCDC1_CTRL, + tps65912_dcdc_ranges), + TPS65912_REGULATOR("DCDC2", DCDC2, "dcdc2", tps65912_ops_dcdc, + TPS65912_DCDC2_OP, TPS65912_DCDC2_CTRL, + tps65912_dcdc_ranges), + TPS65912_REGULATOR("DCDC3", DCDC3, "dcdc3", tps65912_ops_dcdc, + TPS65912_DCDC3_OP, TPS65912_DCDC3_CTRL, + tps65912_dcdc_ranges), + TPS65912_REGULATOR("DCDC4", DCDC4, "dcdc4", tps65912_ops_dcdc, + TPS65912_DCDC4_OP, TPS65912_DCDC4_CTRL, + tps65912_dcdc_ranges), + TPS65912_REGULATOR("LDO1", LDO1, "ldo1", tps65912_ops_ldo, + TPS65912_LDO1_OP, TPS65912_LDO1_AVS, + tps65912_ldo_ranges), + TPS65912_REGULATOR("LDO2", LDO2, "ldo2", tps65912_ops_ldo, + TPS65912_LDO2_OP, TPS65912_LDO2_AVS, + tps65912_ldo_ranges), + TPS65912_REGULATOR("LDO3", LDO3, "ldo3", tps65912_ops_ldo, + TPS65912_LDO3_OP, TPS65912_LDO3_AVS, + tps65912_ldo_ranges), + TPS65912_REGULATOR("LDO4", LDO4, "ldo4", tps65912_ops_ldo, + TPS65912_LDO4_OP, TPS65912_LDO4_AVS, + tps65912_ldo_ranges), + TPS65912_REGULATOR("LDO5", LDO5, "ldo5", tps65912_ops_ldo, + TPS65912_LDO5, TPS65912_LDO5, + tps65912_ldo_ranges), + TPS65912_REGULATOR("LDO6", LDO6, "ldo6", tps65912_ops_ldo, + TPS65912_LDO6, TPS65912_LDO6, + tps65912_ldo_ranges), + TPS65912_REGULATOR("LDO7", LDO7, "ldo7", tps65912_ops_ldo, + TPS65912_LDO7, TPS65912_LDO7, + tps65912_ldo_ranges), + TPS65912_REGULATOR("LDO8", LDO8, "ldo8", tps65912_ops_ldo, + TPS65912_LDO8, TPS65912_LDO8, + tps65912_ldo_ranges), + TPS65912_REGULATOR("LDO9", LDO9, "ldo9", tps65912_ops_ldo, + TPS65912_LDO9, TPS65912_LDO9, + tps65912_ldo_ranges), + TPS65912_REGULATOR("LDO10", LDO10, "ldo10", tps65912_ops_ldo, + TPS65912_LDO10, TPS65912_LDO10, + tps65912_ldo_ranges), +}; + +static int tps65912_regulator_probe(struct platform_device *pdev) +{ + struct tps65912 *tps = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; + struct regulator_dev *rdev; + int i; + + platform_set_drvdata(pdev, tps); + + config.dev = &pdev->dev; + config.driver_data = tps; + config.dev->of_node = tps->dev->of_node; + config.regmap = tps->regmap; + + for (i = 0; i < ARRAY_SIZE(regulators); i++) { + rdev = devm_regulator_register(&pdev->dev, ®ulators[i], + &config); + if (IS_ERR(rdev)) { + dev_err(tps->dev, "failed to register %s regulator\n", + pdev->name); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct platform_device_id tps65912_regulator_id_table[] = { + { "tps65912-regulator", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, tps65912_regulator_id_table); + +static struct platform_driver tps65912_regulator_driver = { + .driver = { + .name = "tps65912-regulator", + }, + .probe = tps65912_regulator_probe, + .id_table = tps65912_regulator_id_table, +}; +module_platform_driver(tps65912_regulator_driver); + +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("TPS65912 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From ca801a22f465eae39fadc770e15b5b7e82595f81 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Mon, 25 Jan 2016 09:43:47 -0600 Subject: gpio: tps65912: Add GPIO driver for the TPS65912 PMIC This patch adds support for the TPS65912 PMIC GPIOs. TPS65912 has five configurable GPIOs that can be used for several purposes. 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-tps65912.c | 158 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 drivers/gpio/gpio-tps65912.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 98eaeddc0ae3..3d60b742e42c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -858,6 +858,12 @@ config GPIO_TPS65910 Select this option to enable GPIO driver for the TPS65910 chip family. +config GPIO_TPS65912 + tristate "TI TPS65912 GPIO" + depends on MFD_TPS65912 + help + This driver supports TPS65912 gpio chip + config GPIO_TWL4030 tristate "TWL4030, TWL5030, and TPS659x0 GPIOs" depends on TWL4030_CORE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index edc33f0a6788..ece7d7cbdc80 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o +obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c new file mode 100644 index 000000000000..3da3840201de --- /dev/null +++ b/drivers/gpio/gpio-tps65912.c @@ -0,0 +1,158 @@ +/* + * GPIO driver for TI TPS65912x PMICs + * + * 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 Arizona GPIO driver and the previous TPS65912 driver by + * Margarita Olaya Cabrera + */ + +#include +#include +#include + +#include + +struct tps65912_gpio { + struct gpio_chip gpio_chip; + struct tps65912 *tps; +}; + +static int tps65912_gpio_get_direction(struct gpio_chip *gc, + unsigned offset) +{ + struct tps65912_gpio *gpio = gpiochip_get_data(gc); + + int ret, val; + + ret = regmap_read(gpio->tps->regmap, TPS65912_GPIO1 + offset, &val); + if (ret) + return ret; + + if (val & GPIO_CFG_MASK) + return GPIOF_DIR_OUT; + else + return GPIOF_DIR_IN; +} + +static int tps65912_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct tps65912_gpio *gpio = gpiochip_get_data(gc); + + return regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, + GPIO_CFG_MASK, 0); +} + +static int tps65912_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int value) +{ + struct tps65912_gpio *gpio = gpiochip_get_data(gc); + + /* Set the initial value */ + regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, + GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); + + return regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, + GPIO_CFG_MASK, GPIO_CFG_MASK); +} + +static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct tps65912_gpio *gpio = gpiochip_get_data(gc); + int ret, val; + + ret = regmap_read(gpio->tps->regmap, TPS65912_GPIO1 + offset, &val); + if (ret) + return ret; + + if (val & GPIO_STS_MASK) + return 1; + + return 0; +} + +static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct tps65912_gpio *gpio = gpiochip_get_data(gc); + + regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, + GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); +} + +static struct gpio_chip template_chip = { + .label = "tps65912-gpio", + .owner = THIS_MODULE, + .get_direction = tps65912_gpio_get_direction, + .direction_input = tps65912_gpio_direction_input, + .direction_output = tps65912_gpio_direction_output, + .get = tps65912_gpio_get, + .set = tps65912_gpio_set, + .base = -1, + .ngpio = 5, + .can_sleep = true, +}; + +static int tps65912_gpio_probe(struct platform_device *pdev) +{ + struct tps65912 *tps = dev_get_drvdata(pdev->dev.parent); + struct tps65912_gpio *gpio; + int ret; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->tps = dev_get_drvdata(pdev->dev.parent); + gpio->gpio_chip = template_chip; + gpio->gpio_chip.parent = tps->dev; + + ret = gpiochip_add_data(&gpio->gpio_chip, gpio); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, gpio); + + return 0; +} + +static int tps65912_gpio_remove(struct platform_device *pdev) +{ + struct tps65912_gpio *gpio = platform_get_drvdata(pdev); + + gpiochip_remove(&gpio->gpio_chip); + + return 0; +} + +static const struct platform_device_id tps65912_gpio_id_table[] = { + { "tps65912-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, tps65912_gpio_id_table); + +static struct platform_driver tps65912_gpio_driver = { + .driver = { + .name = "tps65912-gpio", + }, + .probe = tps65912_gpio_probe, + .remove = tps65912_gpio_remove, + .id_table = tps65912_gpio_id_table, +}; +module_platform_driver(tps65912_gpio_driver); + +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("TPS65912 GPIO driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 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 9842199e2e6c..685a78614f83 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.2.3 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 685a78614f83..3e186f2dcac3 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.2.3 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 3e186f2dcac3..cec51e689d1d 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.2.3 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 9ca66de0c1c1..0037b9c933d9 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 0f230a6103f8..dba4f99d9044 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 000000000000..b1b865822c07 --- /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 cec51e689d1d..8e569bcfe3bc 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.2.3 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 8e569bcfe3bc..3054ea4b95e8 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.2.3 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 0037b9c933d9..ae3990b5a2bf 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 dba4f99d9044..c69ea744fd1a 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 000000000000..28c20247c112 --- /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 3054ea4b95e8..a57d6e940610 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.2.3 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 f2e1a39ce0f3..e86d1fc2d80b 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.2.3 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 2fd192735d5b..72b32c1ab257 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.2.3