Merge branches 'ib-mfd-clk-4.6', 'ib-mfd-input-iio-4.6', 'ib-mfd-regulator-4.6' and 'ib-mfd-regulator-gpio-4.6' into ibs-for-mfd-merged
This commit is contained in:
commit
bab070620e
|
@ -0,0 +1,58 @@
|
|||
Freescale i.MX25 ADC GCQ device
|
||||
|
||||
This is a generic conversion queue device that can convert any of the
|
||||
analog inputs using the ADC unit of the i.MX25.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx25-gcq".
|
||||
- reg: Should be the register range of the module.
|
||||
- interrupts: Should be the interrupt number of the module.
|
||||
Typically this is <1>.
|
||||
- interrupt-parent: phandle to the tsadc module of the i.MX25.
|
||||
- #address-cells: Should be <1> (setting for the subnodes)
|
||||
- #size-cells: Should be <0> (setting for the subnodes)
|
||||
|
||||
Optional properties:
|
||||
- vref-ext-supply: The regulator supplying the ADC reference voltage.
|
||||
Required when at least one subnode uses the this reference.
|
||||
- vref-xp-supply: The regulator supplying the ADC reference voltage on pin XP.
|
||||
Required when at least one subnode uses this reference.
|
||||
- vref-yp-supply: The regulator supplying the ADC reference voltage on pin YP.
|
||||
Required when at least one subnode uses this reference.
|
||||
|
||||
Sub-nodes:
|
||||
Optionally you can define subnodes which define the reference voltage
|
||||
for the analog inputs.
|
||||
|
||||
Required properties for subnodes:
|
||||
- reg: Should be the number of the analog input.
|
||||
0: xp
|
||||
1: yp
|
||||
2: xn
|
||||
3: yn
|
||||
4: wiper
|
||||
5: inaux0
|
||||
6: inaux1
|
||||
7: inaux2
|
||||
Optional properties for subnodes:
|
||||
- fsl,adc-refp: specifies the positive reference input as defined in
|
||||
<dt-bindings/iio/adc/fsl-imx25-gcq.h>
|
||||
- fsl,adc-refn: specifies the negative reference input as defined in
|
||||
<dt-bindings/iio/adc/fsl-imx25-gcq.h>
|
||||
|
||||
Example:
|
||||
|
||||
adc: adc@50030800 {
|
||||
compatible = "fsl,imx25-gcq";
|
||||
reg = <0x50030800 0x60>;
|
||||
interrupt-parent = <&tscadc>;
|
||||
interrupts = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
inaux@5 {
|
||||
reg = <5>;
|
||||
fsl,adc-refp = <MX25_ADC_REFP_INT>;
|
||||
fsl,adc-refn = <MX25_ADC_REFN_NGND>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
Freescale mx25 TS conversion queue module
|
||||
|
||||
mx25 touchscreen conversion queue module which controls the ADC unit of the
|
||||
mx25 for attached touchscreens.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx25-tcq".
|
||||
- reg: Memory range of the device.
|
||||
- interrupts: Should be the interrupt number associated with this module within
|
||||
the tscadc unit (<0>).
|
||||
- interrupt-parent: Should be a phandle to the tscadc unit.
|
||||
- fsl,wires: Should be '<4>' or '<5>'
|
||||
|
||||
Optional properties:
|
||||
- fsl,pen-debounce-ns: Pen debounce time in nanoseconds.
|
||||
- fsl,pen-threshold: Pen-down threshold for the touchscreen. This is a value
|
||||
between 1 and 4096. It is the ratio between the internal reference voltage
|
||||
and the measured voltage after the plate was precharged. Resistence between
|
||||
plates and therefore the voltage decreases with pressure so that a smaller
|
||||
value is equivalent to a higher pressure.
|
||||
- fsl,settling-time-ns: Settling time in nanoseconds. The settling time is before
|
||||
the actual touch detection to wait for an even charge distribution in the
|
||||
plate.
|
||||
|
||||
This device includes two conversion queues which can be added as subnodes.
|
||||
The first queue is for the touchscreen, the second for general purpose ADC.
|
||||
|
||||
Example:
|
||||
tsc: tcq@50030400 {
|
||||
compatible = "fsl,imx25-tcq";
|
||||
reg = <0x50030400 0x60>;
|
||||
interrupt-parent = <&tscadc>;
|
||||
interrupts = <0>;
|
||||
fsl,wires = <4>;
|
||||
};
|
|
@ -5,11 +5,12 @@ axp152 (X-Powers)
|
|||
axp202 (X-Powers)
|
||||
axp209 (X-Powers)
|
||||
axp221 (X-Powers)
|
||||
axp223 (X-Powers)
|
||||
|
||||
Required properties:
|
||||
- compatible: "x-powers,axp152", "x-powers,axp202", "x-powers,axp209",
|
||||
"x-powers,axp221"
|
||||
- reg: The I2C slave address for the AXP chip
|
||||
"x-powers,axp221", "x-powers,axp223"
|
||||
- reg: The I2C slave address or RSB hardware address for the AXP chip
|
||||
- interrupt-parent: The parent interrupt controller
|
||||
- interrupts: SoC NMI / GPIO interrupt connected to the PMIC's IRQ pin
|
||||
- interrupt-controller: The PMIC has its own internal IRQs
|
||||
|
@ -51,7 +52,7 @@ LDO3 : LDO : ldo3in-supply
|
|||
LDO4 : LDO : ldo24in-supply : shared supply
|
||||
LDO5 : LDO : ldo5in-supply
|
||||
|
||||
AXP221 regulators, type, and corresponding input supply names:
|
||||
AXP221/AXP223 regulators, type, and corresponding input supply names:
|
||||
|
||||
Regulator Type Supply Name Notes
|
||||
--------- ---- ----------- -----
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
Freescale MX25 ADC/TSC MultiFunction Device (MFD)
|
||||
|
||||
This device combines two general purpose conversion queues one used for general
|
||||
ADC and the other used for touchscreens.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx25-tsadc".
|
||||
- reg: Start address and size of the memory area of
|
||||
the device
|
||||
- interrupts: Interrupt for this device
|
||||
(See: ../interrupt-controller/interrupts.txt)
|
||||
- clocks: An 'ipg' clock (See: ../clock/clock-bindings.txt)
|
||||
- interrupt-controller: This device is an interrupt controller. It
|
||||
controls the interrupts of both
|
||||
conversion queues.
|
||||
- #interrupt-cells: Should be '<1>'.
|
||||
- #address-cells: Should be '<1>'.
|
||||
- #size-cells: Should be '<1>'.
|
||||
|
||||
This device includes two conversion queues which can be added as subnodes.
|
||||
The first queue is for the touchscreen, the second for general purpose ADC.
|
||||
|
||||
Example:
|
||||
tscadc: tscadc@50030000 {
|
||||
compatible = "fsl,imx25-tsadc";
|
||||
reg = <0x50030000 0xc>;
|
||||
interrupts = <46>;
|
||||
clocks = <&clks 119>;
|
||||
clock-names = "ipg";
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
tsc: tcq@50030400 {
|
||||
compatible = "fsl,imx25-tcq";
|
||||
reg = <0x50030400 0x60>;
|
||||
...
|
||||
};
|
||||
|
||||
adc: gcq@50030800 {
|
||||
compatible = "fsl,imx25-gcq";
|
||||
reg = <0x50030800 0x60>;
|
||||
...
|
||||
};
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
* TPS65912 Power Management Integrated Circuit bindings
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "ti,tps65912".
|
||||
- reg : Slave address or chip select number (I2C / SPI).
|
||||
- interrupt-parent : The parent interrupt controller.
|
||||
- interrupts : The interrupt line the device is connected to.
|
||||
- interrupt-controller : Marks the device node as an interrupt controller.
|
||||
- #interrupt-cells : The number of cells to describe an IRQ, should be 2.
|
||||
The first cell is the IRQ number.
|
||||
The second cell is the flags, encoded as trigger
|
||||
masks from ../interrupt-controller/interrupts.txt.
|
||||
- gpio-controller : Marks the device node as a GPIO Controller.
|
||||
- #gpio-cells : Should be two. The first cell is the pin number and
|
||||
the second cell is used to specify flags.
|
||||
See ../gpio/gpio.txt for more information.
|
||||
- regulators: : List of child nodes that specify the regulator
|
||||
initialization data. Child nodes must be named
|
||||
after their hardware counterparts: dcdc[1-4] and
|
||||
ldo[1-10]. Each child nodes is defined using the
|
||||
standard binding for regulators.
|
||||
|
||||
Example:
|
||||
|
||||
pmic: tps65912@2d {
|
||||
compatible = "ti,tps65912";
|
||||
reg = <0x2d>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <28 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
||||
regulators {
|
||||
dcdc1 {
|
||||
regulator-name = "vdd_core";
|
||||
regulator-min-microvolt = <912000>;
|
||||
regulator-max-microvolt = <1144000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
ldo1 {
|
||||
regulator-name = "ldo1";
|
||||
regulator-min-microvolt = <1900000>;
|
||||
regulator-max-microvolt = <1900000>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -860,7 +860,7 @@ config GPIO_TPS65910
|
|||
|
||||
config GPIO_TPS65912
|
||||
tristate "TI TPS65912 GPIO"
|
||||
depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI)
|
||||
depends on MFD_TPS65912
|
||||
help
|
||||
This driver supports TPS65912 gpio chip
|
||||
|
||||
|
|
|
@ -1,38 +1,79 @@
|
|||
/*
|
||||
* Copyright 2011 Texas Instruments Inc.
|
||||
* GPIO driver for TI TPS65912x PMICs
|
||||
*
|
||||
* Author: Margarita Olaya <magi@slimlogic.co.uk>
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
* 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 driver is based on wm8350 implementation.
|
||||
* 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 <magi@slimlogic.co.uk>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mfd/tps65912.h>
|
||||
|
||||
struct tps65912_gpio_data {
|
||||
struct tps65912 *tps65912;
|
||||
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_data *tps65912_gpio = gpiochip_get_data(gc);
|
||||
struct tps65912 *tps65912 = tps65912_gpio->tps65912;
|
||||
int val;
|
||||
struct tps65912_gpio *gpio = gpiochip_get_data(gc);
|
||||
int ret, val;
|
||||
|
||||
val = tps65912_reg_read(tps65912, TPS65912_GPIO1 + offset);
|
||||
ret = regmap_read(gpio->tps->regmap, TPS65912_GPIO1 + offset, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val & GPIO_STS_MASK)
|
||||
return 1;
|
||||
|
@ -43,109 +84,75 @@ static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset)
|
|||
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;
|
||||
struct tps65912_gpio *gpio = gpiochip_get_data(gc);
|
||||
|
||||
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);
|
||||
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",
|
||||
.label = "tps65912-gpio",
|
||||
.owner = THIS_MODULE,
|
||||
.direction_input = tps65912_gpio_input,
|
||||
.direction_output = tps65912_gpio_output,
|
||||
.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,
|
||||
.can_sleep = true,
|
||||
.ngpio = 5,
|
||||
.base = -1,
|
||||
.ngpio = 5,
|
||||
.can_sleep = true,
|
||||
};
|
||||
|
||||
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;
|
||||
struct tps65912 *tps = dev_get_drvdata(pdev->dev.parent);
|
||||
struct tps65912_gpio *gpio;
|
||||
int ret;
|
||||
|
||||
tps65912_gpio = devm_kzalloc(&pdev->dev, sizeof(*tps65912_gpio),
|
||||
GFP_KERNEL);
|
||||
if (tps65912_gpio == NULL)
|
||||
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
|
||||
if (!gpio)
|
||||
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;
|
||||
gpio->tps = dev_get_drvdata(pdev->dev.parent);
|
||||
gpio->gpio_chip = template_chip;
|
||||
gpio->gpio_chip.parent = tps->dev;
|
||||
|
||||
ret = gpiochip_add_data(&tps65912_gpio->gpio_chip, tps65912_gpio);
|
||||
ret = gpiochip_add_data(&gpio->gpio_chip, gpio);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret);
|
||||
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tps65912_gpio);
|
||||
platform_set_drvdata(pdev, gpio);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tps65912_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tps65912_gpio_data *tps65912_gpio = platform_get_drvdata(pdev);
|
||||
struct tps65912_gpio *gpio = platform_get_drvdata(pdev);
|
||||
|
||||
gpiochip_remove(&gpio->gpio_chip);
|
||||
|
||||
gpiochip_remove(&tps65912_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);
|
||||
|
||||
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 <magi@slimlogic.co.uk>");
|
||||
MODULE_DESCRIPTION("GPIO interface for TPS65912 PMICs");
|
||||
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
|
||||
MODULE_DESCRIPTION("TPS65912 GPIO driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:tps65912-gpio");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
|
||||
*
|
||||
* 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 <dt-bindings/iio/adc/fsl-imx25-gcq.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/imx25-tsadc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#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 */
|
||||
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 <mpa@pengutronix.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,596 @@
|
|||
/*
|
||||
* Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
|
||||
*
|
||||
* 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 <kernel@pengutronix.de>
|
||||
*
|
||||
* This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
|
||||
* connected to the imx25 ADC.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/imx25-tsadc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
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 <mpa@pengutronix.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -91,14 +91,29 @@ 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.
|
||||
|
||||
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.
|
||||
|
@ -271,6 +286,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
|
||||
|
@ -1181,27 +1205,25 @@ config MFD_TPS65910
|
|||
Power Management chips.
|
||||
|
||||
config MFD_TPS65912
|
||||
bool "TI TPS65912 Power Management chip"
|
||||
depends on GPIOLIB
|
||||
tristate
|
||||
select MFD_CORE
|
||||
help
|
||||
If you say yes here you get support for the TPS65912 series of
|
||||
PM chips.
|
||||
select REGMAP
|
||||
select REGMAP_IRQ
|
||||
|
||||
config MFD_TPS65912_I2C
|
||||
bool "TI TPS65912 Power Management chip with I2C"
|
||||
select MFD_CORE
|
||||
tristate "TI TPS65912 Power Management chip with I2C"
|
||||
select MFD_TPS65912
|
||||
depends on I2C=y && GPIOLIB
|
||||
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
|
||||
bool "TI TPS65912 Power Management chip with SPI"
|
||||
select MFD_CORE
|
||||
tristate "TI TPS65912 Power Management chip with SPI"
|
||||
select MFD_TPS65912
|
||||
depends on SPI_MASTER && GPIOLIB
|
||||
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.
|
||||
|
|
|
@ -73,8 +73,7 @@ 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) += 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
|
||||
|
@ -85,6 +84,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
|
||||
|
@ -111,6 +112,8 @@ 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_AXP20X_RSB) += axp20x-rsb.o
|
||||
|
||||
obj-$(CONFIG_MFD_LP3943) += lp3943.o
|
||||
obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o
|
||||
|
|
|
@ -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 <carlo@caione.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/axp20x.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
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 <carlo@caione.org>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -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 <wens@csie.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mfd/axp20x.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sunxi-rsb.h>
|
||||
|
||||
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 <wens@csie.org>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -1,10 +1,14 @@
|
|||
/*
|
||||
* 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 <carlo@caione.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -13,18 +17,15 @@
|
|||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/mfd/axp20x.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#define AXP20X_OFF 0x80
|
||||
|
@ -34,6 +35,7 @@ static const char * const axp20x_model_names[] = {
|
|||
"AXP202",
|
||||
"AXP209",
|
||||
"AXP221",
|
||||
"AXP223",
|
||||
"AXP288",
|
||||
};
|
||||
|
||||
|
@ -376,32 +378,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,
|
||||
|
@ -606,25 +582,26 @@ static void axp20x_power_off(void)
|
|||
AXP20X_OFF);
|
||||
}
|
||||
|
||||
static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev)
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
|
@ -642,6 +619,7 @@ static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev)
|
|||
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;
|
||||
|
@ -658,51 +636,31 @@ static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev)
|
|||
return -EINVAL;
|
||||
}
|
||||
dev_info(dev, "AXP20x variant %s found\n",
|
||||
axp20x_model_names[axp20x->variant]);
|
||||
axp20x_model_names[axp20x->variant]);
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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(&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;
|
||||
}
|
||||
|
||||
|
@ -711,38 +669,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 <carlo@caione.org>");
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdesc.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mfd/imx25-tsadc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
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 <mpa@pengutronix.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:mx25-tsadc");
|
|
@ -1,175 +1,111 @@
|
|||
/*
|
||||
* tps65912-core.c -- TI TPS65912x
|
||||
* Core functions for TI TPS65912x PMICs
|
||||
*
|
||||
* Copyright 2011 Texas Instruments Inc.
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*
|
||||
* Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
|
||||
* 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 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 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.
|
||||
*
|
||||
* This driver is based on wm8350 implementation.
|
||||
* Based on the TPS65218 driver and the previous TPS65912 driver by
|
||||
* Margarita Olaya Cabrera <magi@slimlogic.co.uk>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/mfd/tps65912.h>
|
||||
|
||||
static const struct mfd_cell tps65912s[] = {
|
||||
{
|
||||
.name = "tps65912-pmic",
|
||||
},
|
||||
static const struct mfd_cell tps65912_cells[] = {
|
||||
{ .name = "tps65912-regulator", },
|
||||
{ .name = "tps65912-gpio", },
|
||||
};
|
||||
|
||||
int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
|
||||
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)
|
||||
{
|
||||
u8 data;
|
||||
int err;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&tps65912->io_mutex);
|
||||
ret = regmap_add_irq_chip(tps->regmap, tps->irq, IRQF_ONESHOT, 0,
|
||||
&tps65912_irq_chip, &tps->irq_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
err = tps65912->read(tps65912, reg, 1, &data);
|
||||
if (err) {
|
||||
dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
|
||||
goto out;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tps65912_set_bits);
|
||||
EXPORT_SYMBOL_GPL(tps65912_device_init);
|
||||
|
||||
int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
|
||||
int tps65912_device_exit(struct tps65912 *tps)
|
||||
{
|
||||
u8 data;
|
||||
int err;
|
||||
regmap_del_irq_chip(tps->irq, tps->irq_data);
|
||||
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tps65912_clear_bits);
|
||||
EXPORT_SYMBOL_GPL(tps65912_device_exit);
|
||||
|
||||
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 <magi@slimlogic.co.uk>");
|
||||
MODULE_DESCRIPTION("TPS65912x chip family multi-function driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
|
||||
MODULE_DESCRIPTION("TPS65912x MFD Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -1,139 +1,79 @@
|
|||
/*
|
||||
* tps65912-i2c.c -- I2C access for TI TPS65912x PMIC
|
||||
* I2C access driver for TI TPS65912x PMICs
|
||||
*
|
||||
* Copyright 2011 Texas Instruments Inc.
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*
|
||||
* Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
|
||||
* 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 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 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.
|
||||
*
|
||||
* This driver is based on wm8350 implementation.
|
||||
* Based on the TPS65218 driver and the previous TPS65912 driver by
|
||||
* Margarita Olaya Cabrera <magi@slimlogic.co.uk>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/mfd/tps65912.h>
|
||||
|
||||
static int tps65912_i2c_read(struct tps65912 *tps65912, u8 reg,
|
||||
int bytes, void *dest)
|
||||
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 i2c_client *i2c = tps65912->control_data;
|
||||
struct i2c_msg xfer[2];
|
||||
int ret;
|
||||
struct tps65912 *tps;
|
||||
|
||||
/* 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)
|
||||
tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
|
||||
if (!tps)
|
||||
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;
|
||||
i2c_set_clientdata(client, tps);
|
||||
tps->dev = &client->dev;
|
||||
tps->irq = client->irq;
|
||||
|
||||
return tps65912_device_init(tps65912);
|
||||
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 *i2c)
|
||||
static int tps65912_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tps65912 *tps65912 = i2c_get_clientdata(i2c);
|
||||
struct tps65912 *tps = i2c_get_clientdata(client);
|
||||
|
||||
tps65912_device_exit(tps65912);
|
||||
|
||||
return 0;
|
||||
return tps65912_device_exit(tps);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tps65912_i2c_id[] = {
|
||||
{"tps65912", 0 },
|
||||
{ }
|
||||
static const struct i2c_device_id tps65912_i2c_id_table[] = {
|
||||
{ "tps65912", 0 },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tps65912_i2c_id);
|
||||
MODULE_DEVICE_TABLE(i2c, tps65912_i2c_id_table);
|
||||
|
||||
static struct i2c_driver tps65912_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "tps65912",
|
||||
.driver = {
|
||||
.name = "tps65912",
|
||||
.of_match_table = tps65912_i2c_of_match_table,
|
||||
},
|
||||
.probe = tps65912_i2c_probe,
|
||||
.remove = tps65912_i2c_remove,
|
||||
.id_table = tps65912_i2c_id,
|
||||
.probe = tps65912_i2c_probe,
|
||||
.remove = tps65912_i2c_remove,
|
||||
.id_table = tps65912_i2c_id_table,
|
||||
};
|
||||
module_i2c_driver(tps65912_i2c_driver);
|
||||
|
||||
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 <magi@slimlogic.co.uk>");
|
||||
MODULE_DESCRIPTION("TPS6591x chip family multi-function driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
|
||||
MODULE_DESCRIPTION("TPS65912x I2C Interface Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -1,217 +0,0 @@
|
|||
/*
|
||||
* tps65912-irq.c -- TI TPS6591x
|
||||
*
|
||||
* Copyright 2011 Texas Instruments Inc.
|
||||
*
|
||||
* Author: Margarita Olaya <magi@slimlogic.co.uk>
|
||||
*
|
||||
* 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 <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mfd/tps65912.h>
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,140 +1,78 @@
|
|||
/*
|
||||
* tps65912-spi.c -- SPI access for TI TPS65912x PMIC
|
||||
* SPI access driver for TI TPS65912x PMICs
|
||||
*
|
||||
* Copyright 2011 Texas Instruments Inc.
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*
|
||||
* Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
|
||||
* 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 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 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.
|
||||
*
|
||||
* This driver is based on wm8350 implementation.
|
||||
* Based on the TPS65218 driver and the previous TPS65912 driver by
|
||||
* Margarita Olaya Cabrera <magi@slimlogic.co.uk>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/mfd/core.h>
|
||||
|
||||
#include <linux/mfd/tps65912.h>
|
||||
|
||||
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 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 *tps65912;
|
||||
struct tps65912 *tps;
|
||||
|
||||
tps65912 = devm_kzalloc(&spi->dev,
|
||||
sizeof(struct tps65912), GFP_KERNEL);
|
||||
if (tps65912 == NULL)
|
||||
tps = devm_kzalloc(&spi->dev, sizeof(*tps), GFP_KERNEL);
|
||||
if (!tps)
|
||||
return -ENOMEM;
|
||||
|
||||
tps65912->dev = &spi->dev;
|
||||
tps65912->control_data = spi;
|
||||
tps65912->read = tps65912_spi_read;
|
||||
tps65912->write = tps65912_spi_write;
|
||||
spi_set_drvdata(spi, tps);
|
||||
tps->dev = &spi->dev;
|
||||
tps->irq = spi->irq;
|
||||
|
||||
spi_set_drvdata(spi, tps65912);
|
||||
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(tps65912);
|
||||
return tps65912_device_init(tps);
|
||||
}
|
||||
|
||||
static int tps65912_spi_remove(struct spi_device *spi)
|
||||
static int tps65912_spi_remove(struct spi_device *client)
|
||||
{
|
||||
struct tps65912 *tps65912 = spi_get_drvdata(spi);
|
||||
struct tps65912 *tps = spi_get_drvdata(client);
|
||||
|
||||
tps65912_device_exit(tps65912);
|
||||
|
||||
return 0;
|
||||
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",
|
||||
.driver = {
|
||||
.name = "tps65912",
|
||||
.of_match_table = tps65912_spi_of_match_table,
|
||||
},
|
||||
.probe = tps65912_spi_probe,
|
||||
.remove = tps65912_spi_remove,
|
||||
.probe = tps65912_spi_probe,
|
||||
.remove = tps65912_spi_remove,
|
||||
.id_table = tps65912_spi_id_table,
|
||||
};
|
||||
module_spi_driver(tps65912_spi_driver);
|
||||
|
||||
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 <magi@slimlogic.co.uk>");
|
||||
MODULE_DESCRIPTION("SPI support for TPS65912 chip family mfd");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
|
||||
MODULE_DESCRIPTION("TPS65912x SPI Interface Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -762,7 +762,7 @@ config REGULATOR_TPS65910
|
|||
|
||||
config REGULATOR_TPS65912
|
||||
tristate "TI TPS65912 Power regulator"
|
||||
depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI)
|
||||
depends on MFD_TPS65912
|
||||
help
|
||||
This driver supports TPS65912 voltage regulator chip.
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,541 +1,168 @@
|
|||
/*
|
||||
* tps65912.c -- TI tps65912
|
||||
* Regulator driver for TI TPS65912x PMICs
|
||||
*
|
||||
* Copyright 2011 Texas Instruments Inc.
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*
|
||||
* Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
|
||||
* 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 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 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.
|
||||
*
|
||||
* This driver is based on wm8350 implementation.
|
||||
* Based on the TPS65218 driver and the previous TPS65912 driver by
|
||||
* Margarita Olaya Cabrera <magi@slimlogic.co.uk>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <linux/mfd/tps65912.h>
|
||||
|
||||
/* DCDC's */
|
||||
#define TPS65912_REG_DCDC1 0
|
||||
#define TPS65912_REG_DCDC2 1
|
||||
#define TPS65912_REG_DCDC3 2
|
||||
#define TPS65912_REG_DCDC4 3
|
||||
enum tps65912_regulators { DCDC1, DCDC2, DCDC3, DCDC4, LDO1, LDO2, LDO3,
|
||||
LDO4, LDO5, LDO6, LDO7, LDO8, LDO9, LDO10 };
|
||||
|
||||
/* 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
|
||||
#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), \
|
||||
}
|
||||
|
||||
/* 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_dcdc_ranges[] = {
|
||||
REGULATOR_LINEAR_RANGE(500000, 0x0, 0x3f, 50000),
|
||||
};
|
||||
|
||||
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),
|
||||
REGULATOR_LINEAR_RANGE(800000, 0x0, 0x20, 25000),
|
||||
REGULATOR_LINEAR_RANGE(1650000, 0x21, 0x3c, 50000),
|
||||
REGULATOR_LINEAR_RANGE(3100000, 0x3d, 0x3f, 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,
|
||||
.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 = 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,
|
||||
.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 int tps65912_probe(struct platform_device *pdev)
|
||||
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 *tps65912 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct tps65912 *tps = 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;
|
||||
platform_set_drvdata(pdev, tps);
|
||||
|
||||
reg_data = pmic_plat_data->tps65912_pmic_init_data;
|
||||
config.dev = &pdev->dev;
|
||||
config.driver_data = tps;
|
||||
config.dev->of_node = tps->dev->of_node;
|
||||
config.regmap = tps->regmap;
|
||||
|
||||
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],
|
||||
for (i = 0; i < ARRAY_SIZE(regulators); i++) {
|
||||
rdev = devm_regulator_register(&pdev->dev, ®ulators[i],
|
||||
&config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(tps65912->dev,
|
||||
"failed to register %s regulator\n",
|
||||
dev_err(tps->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 const struct platform_device_id tps65912_regulator_id_table[] = {
|
||||
{ "tps65912-regulator", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, tps65912_regulator_id_table);
|
||||
|
||||
static int __init tps65912_init(void)
|
||||
{
|
||||
return platform_driver_register(&tps65912_driver);
|
||||
}
|
||||
subsys_initcall(tps65912_init);
|
||||
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);
|
||||
|
||||
static void __exit tps65912_cleanup(void)
|
||||
{
|
||||
platform_driver_unregister(&tps65912_driver);
|
||||
}
|
||||
module_exit(tps65912_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Margarita Olaya Cabrera <magi@slimlogic.co.uk>");
|
||||
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
|
||||
MODULE_DESCRIPTION("TPS65912 voltage regulator driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:tps65912-pmic");
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* This header provides constants for configuring the I.MX25 ADC
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_IIO_ADC_FS_IMX25_GCQ_H
|
||||
#define _DT_BINDINGS_IIO_ADC_FS_IMX25_GCQ_H
|
||||
|
||||
#define MX25_ADC_REFP_YP 0 /* YP voltage reference */
|
||||
#define MX25_ADC_REFP_XP 1 /* XP voltage reference */
|
||||
#define MX25_ADC_REFP_EXT 2 /* External voltage reference */
|
||||
#define MX25_ADC_REFP_INT 3 /* Internal voltage reference */
|
||||
|
||||
#define MX25_ADC_REFN_XN 0 /* XN ground reference */
|
||||
#define MX25_ADC_REFN_YN 1 /* YN ground reference */
|
||||
#define MX25_ADC_REFN_NGND 2 /* Internal ground reference */
|
||||
#define MX25_ADC_REFN_NGND2 3 /* External ground reference */
|
||||
|
||||
#endif
|
|
@ -18,6 +18,7 @@ enum {
|
|||
AXP202_ID,
|
||||
AXP209_ID,
|
||||
AXP221_ID,
|
||||
AXP223_ID,
|
||||
AXP288_ID,
|
||||
NR_AXP20X_VARIANTS,
|
||||
};
|
||||
|
@ -396,7 +397,7 @@ enum axp288_irqs {
|
|||
|
||||
struct axp20x_dev {
|
||||
struct device *dev;
|
||||
struct i2c_client *i2c_client;
|
||||
int irq;
|
||||
struct regmap *regmap;
|
||||
struct regmap_irq_chip_data *regmap_irqc;
|
||||
long variant;
|
||||
|
@ -462,4 +463,35 @@ static inline int axp20x_read_variable_width(struct regmap *regmap,
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* axp20x_match_device(): Setup axp20x variant related fields
|
||||
*
|
||||
* @axp20x: axp20x device to setup (.dev field must be set)
|
||||
* @dev: device associated with this axp20x device
|
||||
*
|
||||
* This lets the axp20x core configure the mfd cells and register maps
|
||||
* for later use.
|
||||
*/
|
||||
int axp20x_match_device(struct axp20x_dev *axp20x);
|
||||
|
||||
/**
|
||||
* axp20x_device_probe(): Probe a configured axp20x device
|
||||
*
|
||||
* @axp20x: axp20x device to probe (must be configured)
|
||||
*
|
||||
* This function lets the axp20x core register the axp20x mfd devices
|
||||
* and irqchip. The axp20x device passed in must be fully configured
|
||||
* with axp20x_match_device, its irq set, and regmap created.
|
||||
*/
|
||||
int axp20x_device_probe(struct axp20x_dev *axp20x);
|
||||
|
||||
/**
|
||||
* axp20x_device_probe(): Remove a axp20x device
|
||||
*
|
||||
* @axp20x: axp20x device to remove
|
||||
*
|
||||
* This tells the axp20x core to remove the associated mfd devices
|
||||
*/
|
||||
int axp20x_device_remove(struct axp20x_dev *axp20x);
|
||||
|
||||
#endif /* __LINUX_MFD_AXP20X_H */
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
#ifndef _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
|
||||
#define _LINUX_INCLUDE_MFD_IMX25_TSADC_H_
|
||||
|
||||
struct regmap;
|
||||
struct clk;
|
||||
|
||||
struct mx25_tsadc {
|
||||
struct regmap *regs;
|
||||
struct irq_domain *domain;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
#define MX25_TSC_TGCR 0x00
|
||||
#define MX25_TSC_TGSR 0x04
|
||||
#define MX25_TSC_TICR 0x08
|
||||
|
||||
/* The same register layout for TC and GC queue */
|
||||
#define MX25_ADCQ_FIFO 0x00
|
||||
#define MX25_ADCQ_CR 0x04
|
||||
#define MX25_ADCQ_SR 0x08
|
||||
#define MX25_ADCQ_MR 0x0c
|
||||
#define MX25_ADCQ_ITEM_7_0 0x20
|
||||
#define MX25_ADCQ_ITEM_15_8 0x24
|
||||
#define MX25_ADCQ_CFG(n) (0x40 + ((n) * 0x4))
|
||||
|
||||
#define MX25_ADCQ_MR_MASK 0xffffffff
|
||||
|
||||
/* TGCR */
|
||||
#define MX25_TGCR_PDBTIME(x) ((x) << 25)
|
||||
#define MX25_TGCR_PDBTIME_MASK GENMASK(31, 25)
|
||||
#define MX25_TGCR_PDBEN BIT(24)
|
||||
#define MX25_TGCR_PDEN BIT(23)
|
||||
#define MX25_TGCR_ADCCLKCFG(x) ((x) << 16)
|
||||
#define MX25_TGCR_GET_ADCCLK(x) (((x) >> 16) & 0x1f)
|
||||
#define MX25_TGCR_INTREFEN BIT(10)
|
||||
#define MX25_TGCR_POWERMODE_MASK GENMASK(9, 8)
|
||||
#define MX25_TGCR_POWERMODE_SAVE (1 << 8)
|
||||
#define MX25_TGCR_POWERMODE_ON (2 << 8)
|
||||
#define MX25_TGCR_STLC BIT(5)
|
||||
#define MX25_TGCR_SLPC BIT(4)
|
||||
#define MX25_TGCR_FUNC_RST BIT(2)
|
||||
#define MX25_TGCR_TSC_RST BIT(1)
|
||||
#define MX25_TGCR_CLK_EN BIT(0)
|
||||
|
||||
/* TGSR */
|
||||
#define MX25_TGSR_SLP_INT BIT(2)
|
||||
#define MX25_TGSR_GCQ_INT BIT(1)
|
||||
#define MX25_TGSR_TCQ_INT BIT(0)
|
||||
|
||||
/* ADCQ_ITEM_* */
|
||||
#define _MX25_ADCQ_ITEM(item, x) ((x) << ((item) * 4))
|
||||
#define MX25_ADCQ_ITEM(item, x) ((item) >= 8 ? \
|
||||
_MX25_ADCQ_ITEM((item) - 8, (x)) : _MX25_ADCQ_ITEM((item), (x)))
|
||||
|
||||
/* ADCQ_FIFO (TCQFIFO and GCQFIFO) */
|
||||
#define MX25_ADCQ_FIFO_DATA(x) (((x) >> 4) & 0xfff)
|
||||
#define MX25_ADCQ_FIFO_ID(x) ((x) & 0xf)
|
||||
|
||||
/* ADCQ_CR (TCQR and GCQR) */
|
||||
#define MX25_ADCQ_CR_PDCFG_LEVEL BIT(19)
|
||||
#define MX25_ADCQ_CR_PDMSK BIT(18)
|
||||
#define MX25_ADCQ_CR_FRST BIT(17)
|
||||
#define MX25_ADCQ_CR_QRST BIT(16)
|
||||
#define MX25_ADCQ_CR_RWAIT_MASK GENMASK(15, 12)
|
||||
#define MX25_ADCQ_CR_RWAIT(x) ((x) << 12)
|
||||
#define MX25_ADCQ_CR_WMRK_MASK GENMASK(11, 8)
|
||||
#define MX25_ADCQ_CR_WMRK(x) ((x) << 8)
|
||||
#define MX25_ADCQ_CR_LITEMID_MASK (0xf << 4)
|
||||
#define MX25_ADCQ_CR_LITEMID(x) ((x) << 4)
|
||||
#define MX25_ADCQ_CR_RPT BIT(3)
|
||||
#define MX25_ADCQ_CR_FQS BIT(2)
|
||||
#define MX25_ADCQ_CR_QSM_MASK GENMASK(1, 0)
|
||||
#define MX25_ADCQ_CR_QSM_PD 0x1
|
||||
#define MX25_ADCQ_CR_QSM_FQS 0x2
|
||||
#define MX25_ADCQ_CR_QSM_FQS_PD 0x3
|
||||
|
||||
/* ADCQ_SR (TCQSR and GCQSR) */
|
||||
#define MX25_ADCQ_SR_FDRY BIT(15)
|
||||
#define MX25_ADCQ_SR_FULL BIT(14)
|
||||
#define MX25_ADCQ_SR_EMPT BIT(13)
|
||||
#define MX25_ADCQ_SR_FDN(x) (((x) >> 8) & 0x1f)
|
||||
#define MX25_ADCQ_SR_FRR BIT(6)
|
||||
#define MX25_ADCQ_SR_FUR BIT(5)
|
||||
#define MX25_ADCQ_SR_FOR BIT(4)
|
||||
#define MX25_ADCQ_SR_EOQ BIT(1)
|
||||
#define MX25_ADCQ_SR_PD BIT(0)
|
||||
|
||||
/* ADCQ_MR (TCQMR and GCQMR) */
|
||||
#define MX25_ADCQ_MR_FDRY_DMA BIT(31)
|
||||
#define MX25_ADCQ_MR_FER_DMA BIT(22)
|
||||
#define MX25_ADCQ_MR_FUR_DMA BIT(21)
|
||||
#define MX25_ADCQ_MR_FOR_DMA BIT(20)
|
||||
#define MX25_ADCQ_MR_EOQ_DMA BIT(17)
|
||||
#define MX25_ADCQ_MR_PD_DMA BIT(16)
|
||||
#define MX25_ADCQ_MR_FDRY_IRQ BIT(15)
|
||||
#define MX25_ADCQ_MR_FER_IRQ BIT(6)
|
||||
#define MX25_ADCQ_MR_FUR_IRQ BIT(5)
|
||||
#define MX25_ADCQ_MR_FOR_IRQ BIT(4)
|
||||
#define MX25_ADCQ_MR_EOQ_IRQ BIT(1)
|
||||
#define MX25_ADCQ_MR_PD_IRQ BIT(0)
|
||||
|
||||
/* ADCQ_CFG (TICR, TCC0-7,GCC0-7) */
|
||||
#define MX25_ADCQ_CFG_SETTLING_TIME(x) ((x) << 24)
|
||||
#define MX25_ADCQ_CFG_IGS (1 << 20)
|
||||
#define MX25_ADCQ_CFG_NOS_MASK GENMASK(19, 16)
|
||||
#define MX25_ADCQ_CFG_NOS(x) (((x) - 1) << 16)
|
||||
#define MX25_ADCQ_CFG_WIPER (1 << 15)
|
||||
#define MX25_ADCQ_CFG_YNLR (1 << 14)
|
||||
#define MX25_ADCQ_CFG_YPLL_HIGH (0 << 12)
|
||||
#define MX25_ADCQ_CFG_YPLL_OFF (1 << 12)
|
||||
#define MX25_ADCQ_CFG_YPLL_LOW (3 << 12)
|
||||
#define MX25_ADCQ_CFG_XNUR_HIGH (0 << 10)
|
||||
#define MX25_ADCQ_CFG_XNUR_OFF (1 << 10)
|
||||
#define MX25_ADCQ_CFG_XNUR_LOW (3 << 10)
|
||||
#define MX25_ADCQ_CFG_XPUL_HIGH (0 << 9)
|
||||
#define MX25_ADCQ_CFG_XPUL_OFF (1 << 9)
|
||||
#define MX25_ADCQ_CFG_REFP(sel) ((sel) << 7)
|
||||
#define MX25_ADCQ_CFG_REFP_YP MX25_ADCQ_CFG_REFP(0)
|
||||
#define MX25_ADCQ_CFG_REFP_XP MX25_ADCQ_CFG_REFP(1)
|
||||
#define MX25_ADCQ_CFG_REFP_EXT MX25_ADCQ_CFG_REFP(2)
|
||||
#define MX25_ADCQ_CFG_REFP_INT MX25_ADCQ_CFG_REFP(3)
|
||||
#define MX25_ADCQ_CFG_REFP_MASK GENMASK(8, 7)
|
||||
#define MX25_ADCQ_CFG_IN(sel) ((sel) << 4)
|
||||
#define MX25_ADCQ_CFG_IN_XP MX25_ADCQ_CFG_IN(0)
|
||||
#define MX25_ADCQ_CFG_IN_YP MX25_ADCQ_CFG_IN(1)
|
||||
#define MX25_ADCQ_CFG_IN_XN MX25_ADCQ_CFG_IN(2)
|
||||
#define MX25_ADCQ_CFG_IN_YN MX25_ADCQ_CFG_IN(3)
|
||||
#define MX25_ADCQ_CFG_IN_WIPER MX25_ADCQ_CFG_IN(4)
|
||||
#define MX25_ADCQ_CFG_IN_AUX0 MX25_ADCQ_CFG_IN(5)
|
||||
#define MX25_ADCQ_CFG_IN_AUX1 MX25_ADCQ_CFG_IN(6)
|
||||
#define MX25_ADCQ_CFG_IN_AUX2 MX25_ADCQ_CFG_IN(7)
|
||||
#define MX25_ADCQ_CFG_REFN(sel) ((sel) << 2)
|
||||
#define MX25_ADCQ_CFG_REFN_XN MX25_ADCQ_CFG_REFN(0)
|
||||
#define MX25_ADCQ_CFG_REFN_YN MX25_ADCQ_CFG_REFN(1)
|
||||
#define MX25_ADCQ_CFG_REFN_NGND MX25_ADCQ_CFG_REFN(2)
|
||||
#define MX25_ADCQ_CFG_REFN_NGND2 MX25_ADCQ_CFG_REFN(3)
|
||||
#define MX25_ADCQ_CFG_REFN_MASK GENMASK(3, 2)
|
||||
#define MX25_ADCQ_CFG_PENIACK (1 << 1)
|
||||
|
||||
#endif /* _LINUX_INCLUDE_MFD_IMX25_TSADC_H_ */
|
|
@ -1,28 +1,27 @@
|
|||
/*
|
||||
* tps65912.h -- TI TPS6591x
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*
|
||||
* Copyright 2011 Texas Instruments Inc.
|
||||
* 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.
|
||||
*
|
||||
* Author: Margarita Olaya <magi@slimlogic.co.uk>
|
||||
*
|
||||
* 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 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 <magi@slimlogic.co.uk>
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_TPS65912_H
|
||||
#define __LINUX_MFD_TPS65912_H
|
||||
|
||||
/* TPS regulator type list */
|
||||
#define REGULATOR_LDO 0
|
||||
#define REGULATOR_DCDC 1
|
||||
|
||||
/*
|
||||
* List of registers for TPS65912
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* List of registers for TPS65912 */
|
||||
#define TPS65912_DCDC1_CTRL 0x00
|
||||
#define TPS65912_DCDC2_CTRL 0x01
|
||||
#define TPS65912_DCDC3_CTRL 0x02
|
||||
|
@ -126,41 +125,45 @@
|
|||
#define TPS65912_VERNUM 0x64
|
||||
#define TPS6591X_MAX_REGISTER 0x64
|
||||
|
||||
/* IRQ Definitions */
|
||||
#define TPS65912_IRQ_PWRHOLD_F 0
|
||||
#define TPS65912_IRQ_VMON 1
|
||||
#define TPS65912_IRQ_PWRON 2
|
||||
#define TPS65912_IRQ_PWRON_LP 3
|
||||
#define TPS65912_IRQ_PWRHOLD_R 4
|
||||
#define TPS65912_IRQ_HOTDIE 5
|
||||
#define TPS65912_IRQ_GPIO1_R 6
|
||||
#define TPS65912_IRQ_GPIO1_F 7
|
||||
#define TPS65912_IRQ_GPIO2_R 8
|
||||
#define TPS65912_IRQ_GPIO2_F 9
|
||||
#define TPS65912_IRQ_GPIO3_R 10
|
||||
#define TPS65912_IRQ_GPIO3_F 11
|
||||
#define TPS65912_IRQ_GPIO4_R 12
|
||||
#define TPS65912_IRQ_GPIO4_F 13
|
||||
#define TPS65912_IRQ_GPIO5_R 14
|
||||
#define TPS65912_IRQ_GPIO5_F 15
|
||||
#define TPS65912_IRQ_PGOOD_DCDC1 16
|
||||
#define TPS65912_IRQ_PGOOD_DCDC2 17
|
||||
#define TPS65912_IRQ_PGOOD_DCDC3 18
|
||||
#define TPS65912_IRQ_PGOOD_DCDC4 19
|
||||
#define TPS65912_IRQ_PGOOD_LDO1 20
|
||||
#define TPS65912_IRQ_PGOOD_LDO2 21
|
||||
#define TPS65912_IRQ_PGOOD_LDO3 22
|
||||
#define TPS65912_IRQ_PGOOD_LDO4 23
|
||||
#define TPS65912_IRQ_PGOOD_LDO5 24
|
||||
#define TPS65912_IRQ_PGOOD_LDO6 25
|
||||
#define TPS65912_IRQ_PGOOD_LDO7 26
|
||||
#define TPS65912_IRQ_PGOOD_LD08 27
|
||||
#define TPS65912_IRQ_PGOOD_LDO9 28
|
||||
#define TPS65912_IRQ_PGOOD_LDO10 29
|
||||
/* INT_STS Register field definitions */
|
||||
#define TPS65912_INT_STS_PWRHOLD_F BIT(0)
|
||||
#define TPS65912_INT_STS_VMON BIT(1)
|
||||
#define TPS65912_INT_STS_PWRON BIT(2)
|
||||
#define TPS65912_INT_STS_PWRON_LP BIT(3)
|
||||
#define TPS65912_INT_STS_PWRHOLD_R BIT(4)
|
||||
#define TPS65912_INT_STS_HOTDIE BIT(5)
|
||||
#define TPS65912_INT_STS_GPIO1_R BIT(6)
|
||||
#define TPS65912_INT_STS_GPIO1_F BIT(7)
|
||||
|
||||
#define TPS65912_NUM_IRQ 30
|
||||
/* INT_STS Register field definitions */
|
||||
#define TPS65912_INT_STS2_GPIO2_R BIT(0)
|
||||
#define TPS65912_INT_STS2_GPIO2_F BIT(1)
|
||||
#define TPS65912_INT_STS2_GPIO3_R BIT(2)
|
||||
#define TPS65912_INT_STS2_GPIO3_F BIT(3)
|
||||
#define TPS65912_INT_STS2_GPIO4_R BIT(4)
|
||||
#define TPS65912_INT_STS2_GPIO4_F BIT(5)
|
||||
#define TPS65912_INT_STS2_GPIO5_R BIT(6)
|
||||
#define TPS65912_INT_STS2_GPIO5_F BIT(7)
|
||||
|
||||
/* GPIO 1 and 2 Register Definitions */
|
||||
/* INT_STS Register field definitions */
|
||||
#define TPS65912_INT_STS3_PGOOD_DCDC1 BIT(0)
|
||||
#define TPS65912_INT_STS3_PGOOD_DCDC2 BIT(1)
|
||||
#define TPS65912_INT_STS3_PGOOD_DCDC3 BIT(2)
|
||||
#define TPS65912_INT_STS3_PGOOD_DCDC4 BIT(3)
|
||||
#define TPS65912_INT_STS3_PGOOD_LDO1 BIT(4)
|
||||
#define TPS65912_INT_STS3_PGOOD_LDO2 BIT(5)
|
||||
#define TPS65912_INT_STS3_PGOOD_LDO3 BIT(6)
|
||||
#define TPS65912_INT_STS3_PGOOD_LDO4 BIT(7)
|
||||
|
||||
/* INT_STS Register field definitions */
|
||||
#define TPS65912_INT_STS4_PGOOD_LDO5 BIT(0)
|
||||
#define TPS65912_INT_STS4_PGOOD_LDO6 BIT(1)
|
||||
#define TPS65912_INT_STS4_PGOOD_LDO7 BIT(2)
|
||||
#define TPS65912_INT_STS4_PGOOD_LDO8 BIT(3)
|
||||
#define TPS65912_INT_STS4_PGOOD_LDO9 BIT(4)
|
||||
#define TPS65912_INT_STS4_PGOOD_LDO10 BIT(5)
|
||||
|
||||
/* GPIO 1 and 2 Register field definitions */
|
||||
#define GPIO_SLEEP_MASK 0x80
|
||||
#define GPIO_SLEEP_SHIFT 7
|
||||
#define GPIO_DEB_MASK 0x10
|
||||
|
@ -172,7 +175,7 @@
|
|||
#define GPIO_SET_MASK 0x01
|
||||
#define GPIO_SET_SHIFT 0
|
||||
|
||||
/* GPIO 3 Register Definitions */
|
||||
/* GPIO 3 Register field definitions */
|
||||
#define GPIO3_SLEEP_MASK 0x80
|
||||
#define GPIO3_SLEEP_SHIFT 7
|
||||
#define GPIO3_SEL_MASK 0x40
|
||||
|
@ -190,7 +193,7 @@
|
|||
#define GPIO3_SET_MASK 0x01
|
||||
#define GPIO3_SET_SHIFT 0
|
||||
|
||||
/* GPIO 4 Register Definitions */
|
||||
/* GPIO 4 Register field definitions */
|
||||
#define GPIO4_SLEEP_MASK 0x80
|
||||
#define GPIO4_SLEEP_SHIFT 7
|
||||
#define GPIO4_SEL_MASK 0x40
|
||||
|
@ -264,65 +267,75 @@
|
|||
#define DCDC_LIMIT_MAX_SEL_MASK 0x3F
|
||||
#define DCDC_LIMIT_MAX_SEL_SHIFT 0
|
||||
|
||||
/**
|
||||
* struct tps65912_board
|
||||
* Board platform dat may be used to initialize regulators.
|
||||
*/
|
||||
struct tps65912_board {
|
||||
int is_dcdc1_avs;
|
||||
int is_dcdc2_avs;
|
||||
int is_dcdc3_avs;
|
||||
int is_dcdc4_avs;
|
||||
int irq;
|
||||
int irq_base;
|
||||
int gpio_base;
|
||||
struct regulator_init_data *tps65912_pmic_init_data;
|
||||
/* Define the TPS65912 IRQ numbers */
|
||||
enum tps65912_irqs {
|
||||
/* INT_STS registers */
|
||||
TPS65912_IRQ_PWRHOLD_F,
|
||||
TPS65912_IRQ_VMON,
|
||||
TPS65912_IRQ_PWRON,
|
||||
TPS65912_IRQ_PWRON_LP,
|
||||
TPS65912_IRQ_PWRHOLD_R,
|
||||
TPS65912_IRQ_HOTDIE,
|
||||
TPS65912_IRQ_GPIO1_R,
|
||||
TPS65912_IRQ_GPIO1_F,
|
||||
/* INT_STS2 registers */
|
||||
TPS65912_IRQ_GPIO2_R,
|
||||
TPS65912_IRQ_GPIO2_F,
|
||||
TPS65912_IRQ_GPIO3_R,
|
||||
TPS65912_IRQ_GPIO3_F,
|
||||
TPS65912_IRQ_GPIO4_R,
|
||||
TPS65912_IRQ_GPIO4_F,
|
||||
TPS65912_IRQ_GPIO5_R,
|
||||
TPS65912_IRQ_GPIO5_F,
|
||||
/* INT_STS3 registers */
|
||||
TPS65912_IRQ_PGOOD_DCDC1,
|
||||
TPS65912_IRQ_PGOOD_DCDC2,
|
||||
TPS65912_IRQ_PGOOD_DCDC3,
|
||||
TPS65912_IRQ_PGOOD_DCDC4,
|
||||
TPS65912_IRQ_PGOOD_LDO1,
|
||||
TPS65912_IRQ_PGOOD_LDO2,
|
||||
TPS65912_IRQ_PGOOD_LDO3,
|
||||
TPS65912_IRQ_PGOOD_LDO4,
|
||||
/* INT_STS4 registers */
|
||||
TPS65912_IRQ_PGOOD_LDO5,
|
||||
TPS65912_IRQ_PGOOD_LDO6,
|
||||
TPS65912_IRQ_PGOOD_LDO7,
|
||||
TPS65912_IRQ_PGOOD_LDO8,
|
||||
TPS65912_IRQ_PGOOD_LDO9,
|
||||
TPS65912_IRQ_PGOOD_LDO10,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tps65912 - tps65912 sub-driver chip access routines
|
||||
/*
|
||||
* struct tps65912 - state holder for the tps65912 driver
|
||||
*
|
||||
* Device data may be used to access the TPS65912 chip
|
||||
*/
|
||||
|
||||
struct tps65912 {
|
||||
struct device *dev;
|
||||
/* for read/write acces */
|
||||
struct mutex io_mutex;
|
||||
struct regmap *regmap;
|
||||
|
||||
/* For device IO interfaces: I2C or SPI */
|
||||
void *control_data;
|
||||
|
||||
int (*read)(struct tps65912 *tps65912, u8 reg, int size, void *dest);
|
||||
int (*write)(struct tps65912 *tps65912, u8 reg, int size, void *src);
|
||||
|
||||
/* Client devices */
|
||||
struct tps65912_pmic *pmic;
|
||||
|
||||
/* GPIO Handling */
|
||||
struct gpio_chip gpio;
|
||||
|
||||
/* IRQ Handling */
|
||||
struct mutex irq_lock;
|
||||
int chip_irq;
|
||||
int irq_base;
|
||||
int irq_num;
|
||||
u32 irq_mask;
|
||||
};
|
||||
|
||||
struct tps65912_platform_data {
|
||||
/* IRQ Data */
|
||||
int irq;
|
||||
int irq_base;
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
};
|
||||
|
||||
unsigned int tps_chip(void);
|
||||
static const struct regmap_range tps65912_yes_ranges[] = {
|
||||
regmap_reg_range(TPS65912_INT_STS, TPS65912_GPIO5),
|
||||
};
|
||||
|
||||
int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask);
|
||||
int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask);
|
||||
int tps65912_reg_read(struct tps65912 *tps65912, u8 reg);
|
||||
int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val);
|
||||
int tps65912_device_init(struct tps65912 *tps65912);
|
||||
void tps65912_device_exit(struct tps65912 *tps65912);
|
||||
int tps65912_irq_init(struct tps65912 *tps65912, int irq,
|
||||
struct tps65912_platform_data *pdata);
|
||||
int tps65912_irq_exit(struct tps65912 *tps65912);
|
||||
static const struct regmap_access_table tps65912_volatile_table = {
|
||||
.yes_ranges = tps65912_yes_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(tps65912_yes_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_config tps65912_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.volatile_table = &tps65912_volatile_table,
|
||||
};
|
||||
|
||||
int tps65912_device_init(struct tps65912 *tps);
|
||||
int tps65912_device_exit(struct tps65912 *tps);
|
||||
|
||||
#endif /* __LINUX_MFD_TPS65912_H */
|
||||
|
|
Loading…
Reference in New Issue