Devicetree
 help / color / mirror / Atom feed
* [PATCHv1 1/6] HSI: add Device Tree support for HSI clients
From: Sebastian Reichel @ 2014-02-23 23:49 UTC (permalink / raw)
  To: Sebastian Reichel, Linus Walleij, Shubhrajyoti Datta,
	Carlos Chinea
  Cc: Tony Lindgren, Grant Likely, Rob Herring, Pawel Moll,
	Mark Rutland, Stephen Warren, Ian Campbell, Rob Landley,
	devicetree, linux-kernel, linux-omap, Pali Rohár,
	Ивайло Димитров,
	Joni Lapilainen, Aaro Koskinen, Sebastian Reichel
In-Reply-To: <1393199401-27197-1-git-send-email-sre@debian.org>

Add new method hsi_add_clients_from_dt, which can be used
to initialize HSI clients from a device tree node.

The patch also documents the DT binding for trivial HSI
clients.

Signed-off-by: Sebastian Reichel <sre@debian.org>
---
 .../devicetree/bindings/hsi/trivial-devices.txt    | 36 +++++++++++
 drivers/hsi/hsi.c                                  | 70 +++++++++++++++++++++-
 include/dt-bindings/hsi/hsi.h                      | 17 ++++++
 include/linux/hsi/hsi.h                            |  2 +
 4 files changed, 124 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/hsi/trivial-devices.txt
 create mode 100644 include/dt-bindings/hsi/hsi.h

diff --git a/Documentation/devicetree/bindings/hsi/trivial-devices.txt b/Documentation/devicetree/bindings/hsi/trivial-devices.txt
new file mode 100644
index 0000000..1ace14a
--- /dev/null
+++ b/Documentation/devicetree/bindings/hsi/trivial-devices.txt
@@ -0,0 +1,36 @@
+This is a list of trivial hsi client devices that have simple
+device tree bindings, consisting only of a compatible field
+and the optional hsi configuration.
+
+If a device needs more specific bindings, such as properties to
+describe some aspect of it, there needs to be a specific binding
+document for it just like any other devices.
+
+Optional HSI configuration properties:
+
+- hsi,mode		Bit transmission mode (STREAM or FRAME)
+			The first value is used for RX and the second one for
+			TX configuration. If only one value is provided it will
+			be used for RX and TX.
+			The assignments may be found in header file
+			<dt-bindings/hsi/hsi.h>.
+- hsi,channels		Number of channels to use [1..16]
+			The first value is used for RX and the second one for
+			TX configuration. If only one value is provided it will
+			be used for RX and TX.
+- hsi,speed		Max bit transmission speed (Kbit/s)
+			The first value is used for RX and the second one for
+			TX configuration. If only one value is provided it will
+			be used for RX and TX.
+- hsi,flow		RX flow type (SYNCHRONIZED or PIPELINE)
+			The assignments may be found in header file
+			<dt-bindings/hsi/hsi.h>.
+- hsi,arb_mode		Arbitration mode for TX frame (Round robin, priority)
+			The assignments may be found in header file
+			<dt-bindings/hsi/hsi.h>.
+
+This is the list of trivial client devices:
+
+Compatible		Description
+==========		=============
+hsi-char		HSI character device
diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c
index 749f7b5..8bbc0f1 100644
--- a/drivers/hsi/hsi.c
+++ b/drivers/hsi/hsi.c
@@ -26,6 +26,8 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include "hsi_core.h"
 
 static ssize_t modalias_show(struct device *dev,
@@ -50,7 +52,10 @@ static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
 
 static int hsi_bus_match(struct device *dev, struct device_driver *driver)
 {
-	return strcmp(dev_name(dev), driver->name) == 0;
+	if (dev->of_node != NULL)
+		return of_driver_match_device(dev, driver);
+	else
+		return strcmp(dev_name(dev), driver->name) == 0;
 }
 
 static struct bus_type hsi_bus_type = {
@@ -75,6 +80,7 @@ static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info)
 	cl->tx_cfg = info->tx_cfg;
 	cl->rx_cfg = info->rx_cfg;
 	cl->device.bus = &hsi_bus_type;
+
 	cl->device.parent = &port->device;
 	cl->device.release = hsi_client_release;
 	dev_set_name(&cl->device, "%s", info->name);
@@ -101,6 +107,68 @@ static void hsi_scan_board_info(struct hsi_controller *hsi)
 		}
 }
 
+static void hsi_of_get_client_cfg_property(struct device_node *client,
+				char *name, unsigned int *rx, unsigned int *tx)
+{
+	int err;
+
+	err = of_property_read_u32_index(client, name, 0, rx);
+	if (err)
+		*rx = 0;
+
+	err = of_property_read_u32_index(client, name, 1, tx);
+	if (err)
+		*tx = *rx;
+}
+
+static void hsi_add_client_from_dt(struct hsi_port *port,
+						struct device_node *client)
+{
+	struct hsi_client *cl;
+	const char *name;
+	int err;
+
+	cl = kzalloc(sizeof(*cl), GFP_KERNEL);
+	if (!cl)
+		return;
+
+	err = of_property_read_string(client, "compatible", &name);
+	if (!err) {
+		dev_set_name(&cl->device, "%s", name);
+	} else {
+		kfree(cl);
+		return;
+	}
+
+	hsi_of_get_client_cfg_property(client, "hsi,mode", &cl->rx_cfg.mode,
+							&cl->tx_cfg.mode);
+	hsi_of_get_client_cfg_property(client, "hsi,speed", &cl->rx_cfg.speed,
+							&cl->tx_cfg.speed);
+	hsi_of_get_client_cfg_property(client, "hsi,channels",
+				&cl->rx_cfg.channels, &cl->tx_cfg.channels);
+	of_property_read_u32(client, "hsi,flow", &cl->rx_cfg.flow);
+	of_property_read_u32(client, "hsi,arb_mode", &cl->tx_cfg.arb_mode);
+
+	cl->device.bus = &hsi_bus_type;
+	cl->device.parent = &port->device;
+	cl->device.release = hsi_client_release;
+	cl->device.of_node = client;
+
+	if (device_register(&cl->device) < 0) {
+		pr_err("hsi: failed to register client: %s\n", name);
+		put_device(&cl->device);
+	}
+}
+
+void hsi_add_clients_from_dt(struct hsi_port *port, struct device_node *clients)
+{
+	struct device_node *child;
+
+	for_each_available_child_of_node(clients, child)
+		hsi_add_client_from_dt(port, child);
+}
+EXPORT_SYMBOL_GPL(hsi_add_clients_from_dt);
+
 static int hsi_remove_client(struct device *dev, void *data __maybe_unused)
 {
 	device_unregister(dev);
diff --git a/include/dt-bindings/hsi/hsi.h b/include/dt-bindings/hsi/hsi.h
new file mode 100644
index 0000000..2d6b181
--- /dev/null
+++ b/include/dt-bindings/hsi/hsi.h
@@ -0,0 +1,17 @@
+/*
+ * This header provides constants for hsi client bindings.
+ */
+
+#ifndef _DT_BINDINGS_HSI_H
+#define _DT_BINDINGS_HSI_H
+
+#define HSI_MODE_STREAM 1
+#define HSI_MODE_FRAME 2
+
+#define HSI_FLOW_SYNC 0
+#define HSI_FLOW_PIPE 1
+
+#define HSI_ARB_RR 0
+#define HSI_ARB_PRIO 1
+
+#endif /* _DT_BINDINGS_HSI_H */
diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h
index 0dca785..fb07339 100644
--- a/include/linux/hsi/hsi.h
+++ b/include/linux/hsi/hsi.h
@@ -282,6 +282,8 @@ struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags);
 void hsi_put_controller(struct hsi_controller *hsi);
 int hsi_register_controller(struct hsi_controller *hsi);
 void hsi_unregister_controller(struct hsi_controller *hsi);
+void hsi_add_clients_from_dt(struct hsi_port *port,
+						struct device_node *clients);
 
 static inline void hsi_controller_set_drvdata(struct hsi_controller *hsi,
 								void *data)
-- 
1.8.5.3

^ permalink raw reply related

* [PATCHv1 0/6] OMAP SSI driver
From: Sebastian Reichel @ 2014-02-23 23:49 UTC (permalink / raw)
  To: Sebastian Reichel, Linus Walleij, Shubhrajyoti Datta,
	Carlos Chinea
  Cc: Tony Lindgren, Grant Likely, Rob Herring, Pawel Moll,
	Mark Rutland, Stephen Warren, Ian Campbell, Rob Landley,
	devicetree, linux-kernel, linux-omap, Pali Rohár,
	Ивайло Димитров,
	Joni Lapilainen, Aaro Koskinen, Sebastian Reichel

Hi,

This is the fifth round of the OMAP SSI driver patches. I think the OMAP SSI
driver is ready for mainline and should be included in 3.15. I haven't heard
anything from DT binding maintainers since some months though and would like
to get some feedback about the binding from them.

Changes since RFCv4 [0]:
 * Removed patches for nokia-cmt and ssi-protocol, those are still WIP
 * tested with CONFIG_DEBUG_ATOMIC_SLEEP
 * tested with Russel's "OMAP dma engine rework" patchset
 * acquire clocks from DT
 * minor cleanups
  - removed unused include of omap specific dma header
 * updated patch subjects

TODO:
* Central Message Queue
  I did not yet implement a central message queue in the HSI framework.
  I will do this after Nokia N900 modem is working in the mainline kernel.
* Remove the hwmod DT hack
  This depends on some future work merging hwmod data into DT.

P.S.: It would be nice if I get some Reviewed-By/Acked-By.

[0] http://marc.info/?l=linux-kernel&m=138715030530931&w=2

-- Sebastian

Sebastian Reichel (6):
  HSI: add Device Tree support for HSI clients
  HSI: method to unregister clients from an hsi port
  HSI: hsi-char: add Device Tree support
  HSI: hsi-char: fix driver for multiport scenarios
  HSI: Introduce OMAP SSI driver
  Documentation: DT: omap-ssi binding documentation

 Documentation/devicetree/bindings/hsi/omap_ssi.txt |   82 ++
 .../devicetree/bindings/hsi/trivial-devices.txt    |   36 +
 drivers/hsi/Kconfig                                |    1 +
 drivers/hsi/Makefile                               |    1 +
 drivers/hsi/clients/hsi_char.c                     |   13 +-
 drivers/hsi/controllers/Kconfig                    |   19 +
 drivers/hsi/controllers/Makefile                   |    6 +
 drivers/hsi/controllers/omap_ssi.c                 |  618 +++++++++
 drivers/hsi/controllers/omap_ssi.h                 |  166 +++
 drivers/hsi/controllers/omap_ssi_port.c            | 1401 ++++++++++++++++++++
 drivers/hsi/controllers/omap_ssi_regs.h            |  171 +++
 drivers/hsi/hsi.c                                  |   80 +-
 include/dt-bindings/hsi/hsi.h                      |   17 +
 include/linux/hsi/hsi.h                            |    3 +
 14 files changed, 2612 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/hsi/omap_ssi.txt
 create mode 100644 Documentation/devicetree/bindings/hsi/trivial-devices.txt
 create mode 100644 drivers/hsi/controllers/Kconfig
 create mode 100644 drivers/hsi/controllers/Makefile
 create mode 100644 drivers/hsi/controllers/omap_ssi.c
 create mode 100644 drivers/hsi/controllers/omap_ssi.h
 create mode 100644 drivers/hsi/controllers/omap_ssi_port.c
 create mode 100644 drivers/hsi/controllers/omap_ssi_regs.h
 create mode 100644 include/dt-bindings/hsi/hsi.h

-- 
1.8.5.3

^ permalink raw reply

* Re: [RFCv2 5/5] mfd: twl4030-madc: Move driver to drivers/iio/adc
From: Joe Perches @ 2014-02-23 22:15 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Sebastian Reichel, Marek Belisko, Jonathan Cameron, Lee Jones,
	Samuel Ortiz, Lars-Peter Clausen, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
	linux-kernel, devicetree, linux-iio
In-Reply-To: <1393193271-16717-6-git-send-email-sre@debian.org>

On Sun, 2014-02-23 at 23:07 +0100, Sebastian Reichel wrote:
> This is a driver for an A/D converter, which belongs into
> drivers/iio/adc.

Please use git format-patch -M

> diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
[]
> +static int twl4030_madc_read(struct iio_dev *iio_dev,
> +			     const struct iio_chan_spec *chan,
> +			     int *val, int *val2, long mask)
> +{
[]
> +	req.do_avg = !!(mask & IIO_CHAN_INFO_AVERAGE_RAW);

It looks like do_avg should be bool not u16

^ permalink raw reply

* [RFCv2 5/5] mfd: twl4030-madc: Move driver to drivers/iio/adc
From: Sebastian Reichel @ 2014-02-23 22:07 UTC (permalink / raw)
  To: Sebastian Reichel, Marek Belisko, Jonathan Cameron
  Cc: Lee Jones, Samuel Ortiz, Lars-Peter Clausen, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Sebastian Reichel
In-Reply-To: <1393193271-16717-1-git-send-email-sre-8fiUuRrzOP0dnm+yROfE0A@public.gmane.org>

This is a driver for an A/D converter, which belongs into
drivers/iio/adc.

Signed-off-by: Sebastian Reichel <sre-8fiUuRrzOP0dnm+yROfE0A@public.gmane.org>
---
 drivers/iio/adc/Kconfig        |  10 +
 drivers/iio/adc/Makefile       |   1 +
 drivers/iio/adc/twl4030-madc.c | 914 +++++++++++++++++++++++++++++++++++++++++
 drivers/mfd/Kconfig            |  10 -
 drivers/mfd/Makefile           |   1 -
 drivers/mfd/twl4030-madc.c     | 914 -----------------------------------------
 6 files changed, 925 insertions(+), 925 deletions(-)
 create mode 100644 drivers/iio/adc/twl4030-madc.c
 delete mode 100644 drivers/mfd/twl4030-madc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 2209f28..427f75c 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -183,6 +183,16 @@ config TI_AM335X_ADC
 	  Say yes here to build support for Texas Instruments ADC
 	  driver which is also a MFD client.
 
+config TWL4030_MADC
+	tristate "TWL4030 MADC (Monitoring A/D Converter)"
+	depends on TWL4030_CORE
+	help
+	This driver provides support for Triton TWL4030-MADC. The
+	driver supports both RT and SW conversion methods.
+
+	This driver can also be built as a module. If so, the module will be
+	called twl4030-madc.
+
 config TWL6030_GPADC
 	tristate "TWL6030 GPADC (General Purpose A/D Converter) Support"
 	depends on TWL4030_CORE
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index ba9a10a..9acf2df 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -20,5 +20,6 @@ obj-$(CONFIG_MCP3422) += mcp3422.o
 obj-$(CONFIG_NAU7802) += nau7802.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
+obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
 obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
 obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
new file mode 100644
index 0000000..0bc79a7
--- /dev/null
+++ b/drivers/iio/adc/twl4030-madc.c
@@ -0,0 +1,914 @@
+/*
+ *
+ * TWL4030 MADC module driver-This driver monitors the real time
+ * conversion of analog signals like battery temperature,
+ * battery type, battery level etc.
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ * J Keerthy <j-keerthy-l0cyMroinI0@public.gmane.org>
+ *
+ * Based on twl4030-madc.c
+ * Copyright (C) 2008 Nokia Corporation
+ * Mikko Ylinen <mikko.k.ylinen-xNZwKgViW5gAvxtiuMwx3w@public.gmane.org>
+ *
+ * Amit Kucheria <amit.kucheria-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/i2c/twl.h>
+#include <linux/i2c/twl4030-madc.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/mutex.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+#include <linux/types.h>
+#include <linux/gfp.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+
+/*
+ * struct twl4030_madc_data - a container for madc info
+ * @dev - pointer to device structure for madc
+ * @lock - mutex protecting this data structure
+ * @requests - Array of request struct corresponding to SW1, SW2 and RT
+ * @imr - Interrupt mask register of MADC
+ * @isr - Interrupt status register of MADC
+ */
+struct twl4030_madc_data {
+	struct device *dev;
+	struct mutex lock;	/* mutex protecting this data structure */
+	struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
+	bool use_second_irq;
+	u8 imr;
+	u8 isr;
+};
+
+static int twl4030_madc_read(struct iio_dev *iio_dev,
+			     const struct iio_chan_spec *chan,
+			     int *val, int *val2, long mask)
+{
+	struct twl4030_madc_data *madc = iio_priv(iio_dev);
+	struct twl4030_madc_request req;
+	int channel = chan->channel;
+	int ret;
+
+	req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1;
+
+	req.channels = BIT(channel);
+	req.active = 0;
+	req.func_cb = NULL;
+	req.raw = !(mask & IIO_CHAN_INFO_PROCESSED);
+	req.do_avg = !!(mask & IIO_CHAN_INFO_AVERAGE_RAW);
+
+	ret = twl4030_madc_conversion(&req);
+	if (ret < 0)
+		return ret;
+
+	*val = req.rbuf[channel];
+
+	return IIO_VAL_INT;
+}
+
+static const struct iio_info twl4030_madc_iio_info = {
+	.read_raw = &twl4030_madc_read,
+	.driver_module = THIS_MODULE,
+};
+
+#define TWL4030_ADC_CHANNEL(_channel, _type, _name, _mask) {	\
+	.type = _type,					\
+	.channel = _channel,				\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
+			      BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
+			      _mask,			\
+	.datasheet_name = _name,			\
+	.indexed = 1,					\
+}
+
+static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
+	TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0", 0),
+	TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1",
+		BIT(IIO_CHAN_INFO_PROCESSED)),
+	TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2", 0),
+	TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3", 0),
+	TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4", 0),
+	TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5", 0),
+	TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6", 0),
+	TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7", 0),
+	TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8", 0),
+	TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9", 0),
+	TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10",
+		BIT(IIO_CHAN_INFO_PROCESSED)),
+	TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11", 0),
+	TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12", 0),
+	TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13", 0),
+	TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14", 0),
+	TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15", 0),
+};
+
+static struct twl4030_madc_data *twl4030_madc;
+
+struct twl4030_prescale_divider_ratios {
+	s16 numerator;
+	s16 denominator;
+};
+
+static const struct twl4030_prescale_divider_ratios
+twl4030_divider_ratios[16] = {
+	{1, 1},		/* CHANNEL 0 No Prescaler */
+	{1, 1},		/* CHANNEL 1 No Prescaler */
+	{6, 10},	/* CHANNEL 2 */
+	{6, 10},	/* CHANNEL 3 */
+	{6, 10},	/* CHANNEL 4 */
+	{6, 10},	/* CHANNEL 5 */
+	{6, 10},	/* CHANNEL 6 */
+	{6, 10},	/* CHANNEL 7 */
+	{3, 14},	/* CHANNEL 8 */
+	{1, 3},		/* CHANNEL 9 */
+	{1, 1},		/* CHANNEL 10 No Prescaler */
+	{15, 100},	/* CHANNEL 11 */
+	{1, 4},		/* CHANNEL 12 */
+	{1, 1},		/* CHANNEL 13 Reserved channels */
+	{1, 1},		/* CHANNEL 14 Reseved channels */
+	{5, 11},	/* CHANNEL 15 */
+};
+
+
+/*
+ * Conversion table from -3 to 55 degree Celcius
+ */
+static int therm_tbl[] = {
+30800,	29500,	28300,	27100,
+26000,	24900,	23900,	22900,	22000,	21100,	20300,	19400,	18700,	17900,
+17200,	16500,	15900,	15300,	14700,	14100,	13600,	13100,	12600,	12100,
+11600,	11200,	10800,	10400,	10000,	9630,	9280,	8950,	8620,	8310,
+8020,	7730,	7460,	7200,	6950,	6710,	6470,	6250,	6040,	5830,
+5640,	5450,	5260,	5090,	4920,	4760,	4600,	4450,	4310,	4170,
+4040,	3910,	3790,	3670,	3550
+};
+
+/*
+ * Structure containing the registers
+ * of different conversion methods supported by MADC.
+ * Hardware or RT real time conversion request initiated by external host
+ * processor for RT Signal conversions.
+ * External host processors can also request for non RT conversions
+ * SW1 and SW2 software conversions also called asynchronous or GPC request.
+ */
+static
+const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
+	[TWL4030_MADC_RT] = {
+			     .sel = TWL4030_MADC_RTSELECT_LSB,
+			     .avg = TWL4030_MADC_RTAVERAGE_LSB,
+			     .rbase = TWL4030_MADC_RTCH0_LSB,
+			     },
+	[TWL4030_MADC_SW1] = {
+			      .sel = TWL4030_MADC_SW1SELECT_LSB,
+			      .avg = TWL4030_MADC_SW1AVERAGE_LSB,
+			      .rbase = TWL4030_MADC_GPCH0_LSB,
+			      .ctrl = TWL4030_MADC_CTRL_SW1,
+			      },
+	[TWL4030_MADC_SW2] = {
+			      .sel = TWL4030_MADC_SW2SELECT_LSB,
+			      .avg = TWL4030_MADC_SW2AVERAGE_LSB,
+			      .rbase = TWL4030_MADC_GPCH0_LSB,
+			      .ctrl = TWL4030_MADC_CTRL_SW2,
+			      },
+};
+
+/*
+ * Function to read a particular channel value.
+ * @madc - pointer to struct twl4030_madc_data
+ * @reg - lsb of ADC Channel
+ * If the i2c read fails it returns an error else returns 0.
+ */
+static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
+{
+	u8 msb, lsb;
+	int ret;
+	/*
+	 * For each ADC channel, we have MSB and LSB register pair. MSB address
+	 * is always LSB address+1. reg parameter is the address of LSB register
+	 */
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1);
+	if (ret) {
+		dev_err(madc->dev, "unable to read MSB register 0x%X\n",
+			reg + 1);
+		return ret;
+	}
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg);
+	if (ret) {
+		dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg);
+		return ret;
+	}
+
+	return (int)(((msb << 8) | lsb) >> 6);
+}
+
+/*
+ * Return battery temperature
+ * Or < 0 on failure.
+ */
+static int twl4030battery_temperature(int raw_volt)
+{
+	u8 val;
+	int temp, curr, volt, res, ret;
+
+	volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R;
+	/* Getting and calculating the supply current in micro ampers */
+	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
+		REG_BCICTL2);
+	if (ret < 0)
+		return ret;
+
+	curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
+	/* Getting and calculating the thermistor resistance in ohms */
+	res = volt * 1000 / curr;
+	/* calculating temperature */
+	for (temp = 58; temp >= 0; temp--) {
+		int actual = therm_tbl[temp];
+		if ((actual - res) >= 0)
+			break;
+	}
+
+	return temp + 1;
+}
+
+static int twl4030battery_current(int raw_volt)
+{
+	int ret;
+	u8 val;
+
+	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
+		TWL4030_BCI_BCICTL1);
+	if (ret)
+		return ret;
+	if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */
+		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1;
+	else /* slope of 0.88 mV/mA */
+		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
+}
+
+/*
+ * Function to read channel values
+ * @madc - pointer to twl4030_madc_data struct
+ * @reg_base - Base address of the first channel
+ * @Channels - 16 bit bitmap. If the bit is set, channel value is read
+ * @buf - The channel values are stored here. if read fails error
+ * @raw - Return raw values without conversion
+ * value is stored
+ * Returns the number of successfully read channels.
+ */
+static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
+				      u8 reg_base, unsigned
+				      long channels, int *buf,
+				      bool raw)
+{
+	int count = 0, i;
+
+	for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
+		buf[i] = twl4030_madc_channel_raw_read(madc, reg_base + 2 * i);
+		if (buf[i] < 0) {
+			dev_err(madc->dev, "Unable to read register 0x%X\n",
+				reg_base + 2 * i);
+			return buf[i];
+		}
+		if (raw) {
+			count++;
+			continue;
+		}
+		switch (i) {
+		case 10:
+			buf[i] = twl4030battery_current(buf[i]);
+			if (buf[i] < 0) {
+				dev_err(madc->dev, "err reading current\n");
+				return buf[i];
+			} else {
+				count++;
+				buf[i] = buf[i] - 750;
+			}
+			break;
+		case 1:
+			buf[i] = twl4030battery_temperature(buf[i]);
+			if (buf[i] < 0) {
+				dev_err(madc->dev, "err reading temperature\n");
+				return buf[i];
+			} else {
+				buf[i] -= 3;
+				count++;
+			}
+			break;
+		default:
+			count++;
+			/* Analog Input (V) = conv_result * step_size / R
+			 * conv_result = decimal value of 10-bit conversion
+			 *		 result
+			 * step size = 1.5 / (2 ^ 10 -1)
+			 * R = Prescaler ratio for input channels.
+			 * Result given in mV hence multiplied by 1000.
+			 */
+			buf[i] = (buf[i] * 3 * 1000 *
+				 twl4030_divider_ratios[i].denominator)
+				/ (2 * 1023 *
+				twl4030_divider_ratios[i].numerator);
+		}
+	}
+
+	return count;
+}
+
+/*
+ * Enables irq.
+ * @madc - pointer to twl4030_madc_data struct
+ * @id - irq number to be enabled
+ * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
+ * corresponding to RT, SW1, SW2 conversion requests.
+ * If the i2c read fails it returns an error else returns 0.
+ */
+static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id)
+{
+	u8 val;
+	int ret;
+
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
+	if (ret) {
+		dev_err(madc->dev, "unable to read imr register 0x%X\n",
+			madc->imr);
+		return ret;
+	}
+
+	val &= ~(1 << id);
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
+	if (ret) {
+		dev_err(madc->dev,
+			"unable to write imr register 0x%X\n", madc->imr);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Disables irq.
+ * @madc - pointer to twl4030_madc_data struct
+ * @id - irq number to be disabled
+ * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
+ * corresponding to RT, SW1, SW2 conversion requests.
+ * Returns error if i2c read/write fails.
+ */
+static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id)
+{
+	u8 val;
+	int ret;
+
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
+	if (ret) {
+		dev_err(madc->dev, "unable to read imr register 0x%X\n",
+			madc->imr);
+		return ret;
+	}
+	val |= (1 << id);
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
+	if (ret) {
+		dev_err(madc->dev,
+			"unable to write imr register 0x%X\n", madc->imr);
+		return ret;
+	}
+
+	return 0;
+}
+
+static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
+{
+	struct twl4030_madc_data *madc = _madc;
+	const struct twl4030_madc_conversion_method *method;
+	u8 isr_val, imr_val;
+	int i, len, ret;
+	struct twl4030_madc_request *r;
+
+	mutex_lock(&madc->lock);
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr);
+	if (ret) {
+		dev_err(madc->dev, "unable to read isr register 0x%X\n",
+			madc->isr);
+		goto err_i2c;
+	}
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr);
+	if (ret) {
+		dev_err(madc->dev, "unable to read imr register 0x%X\n",
+			madc->imr);
+		goto err_i2c;
+	}
+	isr_val &= ~imr_val;
+	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
+		if (!(isr_val & (1 << i)))
+			continue;
+		ret = twl4030_madc_disable_irq(madc, i);
+		if (ret < 0)
+			dev_dbg(madc->dev, "Disable interrupt failed%d\n", i);
+		madc->requests[i].result_pending = 1;
+	}
+	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
+		r = &madc->requests[i];
+		/* No pending results for this method, move to next one */
+		if (!r->result_pending)
+			continue;
+		method = &twl4030_conversion_methods[r->method];
+		/* Read results */
+		len = twl4030_madc_read_channels(madc, method->rbase,
+						 r->channels, r->rbuf, r->raw);
+		/* Return results to caller */
+		if (r->func_cb != NULL) {
+			r->func_cb(len, r->channels, r->rbuf);
+			r->func_cb = NULL;
+		}
+		/* Free request */
+		r->result_pending = 0;
+		r->active = 0;
+	}
+	mutex_unlock(&madc->lock);
+
+	return IRQ_HANDLED;
+
+err_i2c:
+	/*
+	 * In case of error check whichever request is active
+	 * and service the same.
+	 */
+	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
+		r = &madc->requests[i];
+		if (r->active == 0)
+			continue;
+		method = &twl4030_conversion_methods[r->method];
+		/* Read results */
+		len = twl4030_madc_read_channels(madc, method->rbase,
+						 r->channels, r->rbuf, r->raw);
+		/* Return results to caller */
+		if (r->func_cb != NULL) {
+			r->func_cb(len, r->channels, r->rbuf);
+			r->func_cb = NULL;
+		}
+		/* Free request */
+		r->result_pending = 0;
+		r->active = 0;
+	}
+	mutex_unlock(&madc->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int twl4030_madc_set_irq(struct twl4030_madc_data *madc,
+				struct twl4030_madc_request *req)
+{
+	struct twl4030_madc_request *p;
+	int ret;
+
+	p = &madc->requests[req->method];
+	memcpy(p, req, sizeof(*req));
+	ret = twl4030_madc_enable_irq(madc, req->method);
+	if (ret < 0) {
+		dev_err(madc->dev, "enable irq failed!!\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Function which enables the madc conversion
+ * by writing to the control register.
+ * @madc - pointer to twl4030_madc_data struct
+ * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1
+ * corresponding to RT SW1 or SW2 conversion methods.
+ * Returns 0 if succeeds else a negative error value
+ */
+static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
+					 int conv_method)
+{
+	const struct twl4030_madc_conversion_method *method;
+	int ret = 0;
+
+	if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2)
+		return -ENOTSUPP;
+
+	method = &twl4030_conversion_methods[conv_method];
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START,
+			       method->ctrl);
+	if (ret) {
+		dev_err(madc->dev, "unable to write ctrl register 0x%X\n",
+			method->ctrl);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Function that waits for conversion to be ready
+ * @madc - pointer to twl4030_madc_data struct
+ * @timeout_ms - timeout value in milliseconds
+ * @status_reg - ctrl register
+ * returns 0 if succeeds else a negative error value
+ */
+static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc,
+					      unsigned int timeout_ms,
+					      u8 status_reg)
+{
+	unsigned long timeout;
+	int ret;
+
+	timeout = jiffies + msecs_to_jiffies(timeout_ms);
+	do {
+		u8 reg;
+
+		ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &reg, status_reg);
+		if (ret) {
+			dev_err(madc->dev,
+				"unable to read status register 0x%X\n",
+				status_reg);
+			return ret;
+		}
+		if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW))
+			return 0;
+		usleep_range(500, 2000);
+	} while (!time_after(jiffies, timeout));
+	dev_err(madc->dev, "conversion timeout!\n");
+
+	return -EAGAIN;
+}
+
+/*
+ * An exported function which can be called from other kernel drivers.
+ * @req twl4030_madc_request structure
+ * req->rbuf will be filled with read values of channels based on the
+ * channel index. If a particular channel reading fails there will
+ * be a negative error value in the corresponding array element.
+ * returns 0 if succeeds else error value
+ */
+int twl4030_madc_conversion(struct twl4030_madc_request *req)
+{
+	const struct twl4030_madc_conversion_method *method;
+	u8 ch_msb, ch_lsb;
+	int ret;
+
+	if (!req || !twl4030_madc)
+		return -EINVAL;
+
+	mutex_lock(&twl4030_madc->lock);
+	if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) {
+		ret = -EINVAL;
+		goto out;
+	}
+	/* Do we have a conversion request ongoing */
+	if (twl4030_madc->requests[req->method].active) {
+		ret = -EBUSY;
+		goto out;
+	}
+	ch_msb = (req->channels >> 8) & 0xff;
+	ch_lsb = req->channels & 0xff;
+	method = &twl4030_conversion_methods[req->method];
+	/* Select channels to be converted */
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1);
+	if (ret) {
+		dev_err(twl4030_madc->dev,
+			"unable to write sel register 0x%X\n", method->sel + 1);
+		goto out;
+	}
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel);
+	if (ret) {
+		dev_err(twl4030_madc->dev,
+			"unable to write sel register 0x%X\n", method->sel + 1);
+		goto out;
+	}
+	/* Select averaging for all channels if do_avg is set */
+	if (req->do_avg) {
+		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
+				       ch_msb, method->avg + 1);
+		if (ret) {
+			dev_err(twl4030_madc->dev,
+				"unable to write avg register 0x%X\n",
+				method->avg + 1);
+			goto out;
+		}
+		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
+				       ch_lsb, method->avg);
+		if (ret) {
+			dev_err(twl4030_madc->dev,
+				"unable to write sel reg 0x%X\n",
+				method->avg);
+			goto out;
+		}
+	}
+	if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) {
+		ret = twl4030_madc_set_irq(twl4030_madc, req);
+		if (ret < 0)
+			goto out;
+		ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
+		if (ret < 0)
+			goto out;
+		twl4030_madc->requests[req->method].active = 1;
+		ret = 0;
+		goto out;
+	}
+	/* With RT method we should not be here anymore */
+	if (req->method == TWL4030_MADC_RT) {
+		ret = -EINVAL;
+		goto out;
+	}
+	ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
+	if (ret < 0)
+		goto out;
+	twl4030_madc->requests[req->method].active = 1;
+	/* Wait until conversion is ready (ctrl register returns EOC) */
+	ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl);
+	if (ret) {
+		twl4030_madc->requests[req->method].active = 0;
+		goto out;
+	}
+	ret = twl4030_madc_read_channels(twl4030_madc, method->rbase,
+					 req->channels, req->rbuf, req->raw);
+	twl4030_madc->requests[req->method].active = 0;
+
+out:
+	mutex_unlock(&twl4030_madc->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(twl4030_madc_conversion);
+
+int twl4030_get_madc_conversion(int channel_no)
+{
+	struct twl4030_madc_request req;
+	int temp = 0;
+	int ret;
+
+	req.channels = (1 << channel_no);
+	req.method = TWL4030_MADC_SW2;
+	req.active = 0;
+	req.func_cb = NULL;
+	ret = twl4030_madc_conversion(&req);
+	if (ret < 0)
+		return ret;
+	if (req.rbuf[channel_no] > 0)
+		temp = req.rbuf[channel_no];
+
+	return temp;
+}
+EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
+
+/*
+ * Function to enable or disable bias current for
+ * main battery type reading or temperature sensing
+ * @madc - pointer to twl4030_madc_data struct
+ * @chan - can be one of the two values
+ * TWL4030_BCI_ITHEN - Enables bias current for main battery type reading
+ * TWL4030_BCI_TYPEN - Enables bias current for main battery temperature
+ * sensing
+ * @on - enable or disable chan.
+ */
+static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
+					      int chan, int on)
+{
+	int ret;
+	int regmask;
+	u8 regval;
+
+	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
+			      &regval, TWL4030_BCI_BCICTL1);
+	if (ret) {
+		dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X",
+			TWL4030_BCI_BCICTL1);
+		return ret;
+	}
+
+	regmask = (chan == 0) ? TWL4030_BCI_TYPEN : TWL4030_BCI_ITHEN;
+	if (on)
+		regval |= regmask;
+	else
+		regval &= ~regmask;
+
+	ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+			       regval, TWL4030_BCI_BCICTL1);
+	if (ret) {
+		dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n",
+			TWL4030_BCI_BCICTL1);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Function that sets MADC software power on bit to enable MADC
+ * @madc - pointer to twl4030_madc_data struct
+ * @on - Enable or disable MADC software powen on bit.
+ * returns error if i2c read/write fails else 0
+ */
+static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
+{
+	u8 regval;
+	int ret;
+
+	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
+			      &regval, TWL4030_MADC_CTRL1);
+	if (ret) {
+		dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n",
+			TWL4030_MADC_CTRL1);
+		return ret;
+	}
+	if (on)
+		regval |= TWL4030_MADC_MADCON;
+	else
+		regval &= ~TWL4030_MADC_MADCON;
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1);
+	if (ret) {
+		dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n",
+			TWL4030_MADC_CTRL1);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Initialize MADC and request for threaded irq
+ */
+static int twl4030_madc_probe(struct platform_device *pdev)
+{
+	struct twl4030_madc_data *madc;
+	struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct device_node *np = pdev->dev.of_node;
+	int irq, ret;
+	u8 regval;
+	struct iio_dev *iio_dev = NULL;
+
+	if (!pdata && !np) {
+		dev_err(&pdev->dev, "platform_data not available\n");
+		return -EINVAL;
+	}
+
+	iio_dev = devm_iio_device_alloc(&pdev->dev,
+					sizeof(struct twl4030_madc_data));
+	if (!iio_dev) {
+		dev_err(&pdev->dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+
+	madc = iio_priv(iio_dev);
+	madc->dev = &pdev->dev;
+
+	iio_dev->name = dev_name(&pdev->dev);
+	iio_dev->dev.parent = &pdev->dev;
+	iio_dev->dev.of_node = pdev->dev.of_node;
+	iio_dev->info = &twl4030_madc_iio_info;
+	iio_dev->modes = INDIO_DIRECT_MODE;
+	iio_dev->channels = twl4030_madc_iio_channels;
+	iio_dev->num_channels = 16;
+
+	/*
+	 * Phoenix provides 2 interrupt lines. The first one is connected to
+	 * the OMAP. The other one can be connected to the other processor such
+	 * as modem. Hence two separate ISR and IMR registers.
+	 */
+	if (pdata)
+		madc->use_second_irq = pdata->irq_line != 1;
+	else
+		madc->use_second_irq = of_property_read_bool(np,
+				       "ti,system-uses-second-madc-irq");
+
+	madc->imr = (madc->use_second_irq == 1) ?
+	    TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
+	madc->isr = (madc->use_second_irq == 1) ?
+	    TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
+
+	ret = twl4030_madc_set_power(madc, 1);
+	if (ret < 0)
+		return ret;
+	ret = twl4030_madc_set_current_generator(madc, 0, 1);
+	if (ret < 0)
+		goto err_current_generator;
+
+	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
+			      &regval, TWL4030_BCI_BCICTL1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n",
+			TWL4030_BCI_BCICTL1);
+		goto err_i2c;
+	}
+	regval |= TWL4030_BCI_MESBAT;
+	ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+			       regval, TWL4030_BCI_BCICTL1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n",
+			TWL4030_BCI_BCICTL1);
+		goto err_i2c;
+	}
+
+	/* Check that MADC clock is on */
+	ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &regval, TWL4030_REG_GPBR1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n",
+				TWL4030_REG_GPBR1);
+		goto err_i2c;
+	}
+
+	/* If MADC clk is not on, turn it on */
+	if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) {
+		dev_info(&pdev->dev, "clk disabled, enabling\n");
+		regval |= TWL4030_GPBR1_MADC_HFCLK_EN;
+		ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval,
+				       TWL4030_REG_GPBR1);
+		if (ret) {
+			dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n",
+					TWL4030_REG_GPBR1);
+			goto err_i2c;
+		}
+	}
+
+	platform_set_drvdata(pdev, iio_dev);
+	mutex_init(&madc->lock);
+
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+				   twl4030_madc_threaded_irq_handler,
+				   IRQF_TRIGGER_RISING, "twl4030_madc", madc);
+	if (ret) {
+		dev_dbg(&pdev->dev, "could not request irq\n");
+		goto err_i2c;
+	}
+	twl4030_madc = madc;
+
+	ret = iio_device_register(iio_dev);
+	if (ret) {
+		dev_dbg(&pdev->dev, "could not register iio device\n");
+		goto err_i2c;
+	}
+
+	return 0;
+
+err_i2c:
+	twl4030_madc_set_current_generator(madc, 0, 0);
+err_current_generator:
+	twl4030_madc_set_power(madc, 0);
+	return ret;
+}
+
+static int twl4030_madc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *iio_dev = platform_get_drvdata(pdev);
+	struct twl4030_madc_data *madc = iio_priv(iio_dev);
+
+	twl4030_madc_set_current_generator(madc, 0, 0);
+	twl4030_madc_set_power(madc, 0);
+
+	iio_device_unregister(iio_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id twl_madc_of_match[] = {
+	{.compatible = "ti,twl4030-madc", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, twl_madc_of_match);
+#endif
+
+static struct platform_driver twl4030_madc_driver = {
+	.probe = twl4030_madc_probe,
+	.remove = twl4030_madc_remove,
+	.driver = {
+		   .name = "twl4030_madc",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(twl_madc_of_match),
+		   },
+};
+
+module_platform_driver(twl4030_madc_driver);
+
+MODULE_DESCRIPTION("TWL4030 ADC driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("J Keerthy");
+MODULE_ALIAS("platform:twl4030_madc");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 49bb445..23a8a51 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -935,16 +935,6 @@ config TWL4030_CORE
 	  high speed USB OTG transceiver, an audio codec (on most
 	  versions) and many other features.
 
-config TWL4030_MADC
-	tristate "TI TWL4030 MADC"
-	depends on TWL4030_CORE
-	help
-	This driver provides support for triton TWL4030-MADC. The
-	driver supports both RT and SW conversion methods.
-
-	This driver can be built as a module. If so it will be
-	named twl4030-madc
-
 config TWL4030_POWER
 	bool "TI TWL4030 power resources"
 	depends on TWL4030_CORE && ARM
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5aea5ef..c8eb0bc 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -71,7 +71,6 @@ obj-$(CONFIG_MFD_TPS80031)	+= tps80031.o
 obj-$(CONFIG_MENELAUS)		+= menelaus.o
 
 obj-$(CONFIG_TWL4030_CORE)	+= twl-core.o twl4030-irq.o twl6030-irq.o
-obj-$(CONFIG_TWL4030_MADC)      += twl4030-madc.o
 obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
 obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
 obj-$(CONFIG_TWL6040_CORE)	+= twl6040.o
diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c
deleted file mode 100644
index 0bc79a7..0000000
--- a/drivers/mfd/twl4030-madc.c
+++ /dev/null
@@ -1,914 +0,0 @@
-/*
- *
- * TWL4030 MADC module driver-This driver monitors the real time
- * conversion of analog signals like battery temperature,
- * battery type, battery level etc.
- *
- * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
- * J Keerthy <j-keerthy-l0cyMroinI0@public.gmane.org>
- *
- * Based on twl4030-madc.c
- * Copyright (C) 2008 Nokia Corporation
- * Mikko Ylinen <mikko.k.ylinen-xNZwKgViW5gAvxtiuMwx3w@public.gmane.org>
- *
- * Amit Kucheria <amit.kucheria-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/i2c/twl.h>
-#include <linux/i2c/twl4030-madc.h>
-#include <linux/module.h>
-#include <linux/stddef.h>
-#include <linux/mutex.h>
-#include <linux/bitops.h>
-#include <linux/jiffies.h>
-#include <linux/types.h>
-#include <linux/gfp.h>
-#include <linux/err.h>
-
-#include <linux/iio/iio.h>
-
-/*
- * struct twl4030_madc_data - a container for madc info
- * @dev - pointer to device structure for madc
- * @lock - mutex protecting this data structure
- * @requests - Array of request struct corresponding to SW1, SW2 and RT
- * @imr - Interrupt mask register of MADC
- * @isr - Interrupt status register of MADC
- */
-struct twl4030_madc_data {
-	struct device *dev;
-	struct mutex lock;	/* mutex protecting this data structure */
-	struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
-	bool use_second_irq;
-	u8 imr;
-	u8 isr;
-};
-
-static int twl4030_madc_read(struct iio_dev *iio_dev,
-			     const struct iio_chan_spec *chan,
-			     int *val, int *val2, long mask)
-{
-	struct twl4030_madc_data *madc = iio_priv(iio_dev);
-	struct twl4030_madc_request req;
-	int channel = chan->channel;
-	int ret;
-
-	req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1;
-
-	req.channels = BIT(channel);
-	req.active = 0;
-	req.func_cb = NULL;
-	req.raw = !(mask & IIO_CHAN_INFO_PROCESSED);
-	req.do_avg = !!(mask & IIO_CHAN_INFO_AVERAGE_RAW);
-
-	ret = twl4030_madc_conversion(&req);
-	if (ret < 0)
-		return ret;
-
-	*val = req.rbuf[channel];
-
-	return IIO_VAL_INT;
-}
-
-static const struct iio_info twl4030_madc_iio_info = {
-	.read_raw = &twl4030_madc_read,
-	.driver_module = THIS_MODULE,
-};
-
-#define TWL4030_ADC_CHANNEL(_channel, _type, _name, _mask) {	\
-	.type = _type,					\
-	.channel = _channel,				\
-	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
-			      BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
-			      _mask,			\
-	.datasheet_name = _name,			\
-	.indexed = 1,					\
-}
-
-static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
-	TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0", 0),
-	TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1",
-		BIT(IIO_CHAN_INFO_PROCESSED)),
-	TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2", 0),
-	TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3", 0),
-	TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4", 0),
-	TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5", 0),
-	TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6", 0),
-	TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7", 0),
-	TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8", 0),
-	TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9", 0),
-	TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10",
-		BIT(IIO_CHAN_INFO_PROCESSED)),
-	TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11", 0),
-	TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12", 0),
-	TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13", 0),
-	TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14", 0),
-	TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15", 0),
-};
-
-static struct twl4030_madc_data *twl4030_madc;
-
-struct twl4030_prescale_divider_ratios {
-	s16 numerator;
-	s16 denominator;
-};
-
-static const struct twl4030_prescale_divider_ratios
-twl4030_divider_ratios[16] = {
-	{1, 1},		/* CHANNEL 0 No Prescaler */
-	{1, 1},		/* CHANNEL 1 No Prescaler */
-	{6, 10},	/* CHANNEL 2 */
-	{6, 10},	/* CHANNEL 3 */
-	{6, 10},	/* CHANNEL 4 */
-	{6, 10},	/* CHANNEL 5 */
-	{6, 10},	/* CHANNEL 6 */
-	{6, 10},	/* CHANNEL 7 */
-	{3, 14},	/* CHANNEL 8 */
-	{1, 3},		/* CHANNEL 9 */
-	{1, 1},		/* CHANNEL 10 No Prescaler */
-	{15, 100},	/* CHANNEL 11 */
-	{1, 4},		/* CHANNEL 12 */
-	{1, 1},		/* CHANNEL 13 Reserved channels */
-	{1, 1},		/* CHANNEL 14 Reseved channels */
-	{5, 11},	/* CHANNEL 15 */
-};
-
-
-/*
- * Conversion table from -3 to 55 degree Celcius
- */
-static int therm_tbl[] = {
-30800,	29500,	28300,	27100,
-26000,	24900,	23900,	22900,	22000,	21100,	20300,	19400,	18700,	17900,
-17200,	16500,	15900,	15300,	14700,	14100,	13600,	13100,	12600,	12100,
-11600,	11200,	10800,	10400,	10000,	9630,	9280,	8950,	8620,	8310,
-8020,	7730,	7460,	7200,	6950,	6710,	6470,	6250,	6040,	5830,
-5640,	5450,	5260,	5090,	4920,	4760,	4600,	4450,	4310,	4170,
-4040,	3910,	3790,	3670,	3550
-};
-
-/*
- * Structure containing the registers
- * of different conversion methods supported by MADC.
- * Hardware or RT real time conversion request initiated by external host
- * processor for RT Signal conversions.
- * External host processors can also request for non RT conversions
- * SW1 and SW2 software conversions also called asynchronous or GPC request.
- */
-static
-const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
-	[TWL4030_MADC_RT] = {
-			     .sel = TWL4030_MADC_RTSELECT_LSB,
-			     .avg = TWL4030_MADC_RTAVERAGE_LSB,
-			     .rbase = TWL4030_MADC_RTCH0_LSB,
-			     },
-	[TWL4030_MADC_SW1] = {
-			      .sel = TWL4030_MADC_SW1SELECT_LSB,
-			      .avg = TWL4030_MADC_SW1AVERAGE_LSB,
-			      .rbase = TWL4030_MADC_GPCH0_LSB,
-			      .ctrl = TWL4030_MADC_CTRL_SW1,
-			      },
-	[TWL4030_MADC_SW2] = {
-			      .sel = TWL4030_MADC_SW2SELECT_LSB,
-			      .avg = TWL4030_MADC_SW2AVERAGE_LSB,
-			      .rbase = TWL4030_MADC_GPCH0_LSB,
-			      .ctrl = TWL4030_MADC_CTRL_SW2,
-			      },
-};
-
-/*
- * Function to read a particular channel value.
- * @madc - pointer to struct twl4030_madc_data
- * @reg - lsb of ADC Channel
- * If the i2c read fails it returns an error else returns 0.
- */
-static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
-{
-	u8 msb, lsb;
-	int ret;
-	/*
-	 * For each ADC channel, we have MSB and LSB register pair. MSB address
-	 * is always LSB address+1. reg parameter is the address of LSB register
-	 */
-	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1);
-	if (ret) {
-		dev_err(madc->dev, "unable to read MSB register 0x%X\n",
-			reg + 1);
-		return ret;
-	}
-	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg);
-	if (ret) {
-		dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg);
-		return ret;
-	}
-
-	return (int)(((msb << 8) | lsb) >> 6);
-}
-
-/*
- * Return battery temperature
- * Or < 0 on failure.
- */
-static int twl4030battery_temperature(int raw_volt)
-{
-	u8 val;
-	int temp, curr, volt, res, ret;
-
-	volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R;
-	/* Getting and calculating the supply current in micro ampers */
-	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
-		REG_BCICTL2);
-	if (ret < 0)
-		return ret;
-
-	curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
-	/* Getting and calculating the thermistor resistance in ohms */
-	res = volt * 1000 / curr;
-	/* calculating temperature */
-	for (temp = 58; temp >= 0; temp--) {
-		int actual = therm_tbl[temp];
-		if ((actual - res) >= 0)
-			break;
-	}
-
-	return temp + 1;
-}
-
-static int twl4030battery_current(int raw_volt)
-{
-	int ret;
-	u8 val;
-
-	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
-		TWL4030_BCI_BCICTL1);
-	if (ret)
-		return ret;
-	if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */
-		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1;
-	else /* slope of 0.88 mV/mA */
-		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
-}
-
-/*
- * Function to read channel values
- * @madc - pointer to twl4030_madc_data struct
- * @reg_base - Base address of the first channel
- * @Channels - 16 bit bitmap. If the bit is set, channel value is read
- * @buf - The channel values are stored here. if read fails error
- * @raw - Return raw values without conversion
- * value is stored
- * Returns the number of successfully read channels.
- */
-static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
-				      u8 reg_base, unsigned
-				      long channels, int *buf,
-				      bool raw)
-{
-	int count = 0, i;
-
-	for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
-		buf[i] = twl4030_madc_channel_raw_read(madc, reg_base + 2 * i);
-		if (buf[i] < 0) {
-			dev_err(madc->dev, "Unable to read register 0x%X\n",
-				reg_base + 2 * i);
-			return buf[i];
-		}
-		if (raw) {
-			count++;
-			continue;
-		}
-		switch (i) {
-		case 10:
-			buf[i] = twl4030battery_current(buf[i]);
-			if (buf[i] < 0) {
-				dev_err(madc->dev, "err reading current\n");
-				return buf[i];
-			} else {
-				count++;
-				buf[i] = buf[i] - 750;
-			}
-			break;
-		case 1:
-			buf[i] = twl4030battery_temperature(buf[i]);
-			if (buf[i] < 0) {
-				dev_err(madc->dev, "err reading temperature\n");
-				return buf[i];
-			} else {
-				buf[i] -= 3;
-				count++;
-			}
-			break;
-		default:
-			count++;
-			/* Analog Input (V) = conv_result * step_size / R
-			 * conv_result = decimal value of 10-bit conversion
-			 *		 result
-			 * step size = 1.5 / (2 ^ 10 -1)
-			 * R = Prescaler ratio for input channels.
-			 * Result given in mV hence multiplied by 1000.
-			 */
-			buf[i] = (buf[i] * 3 * 1000 *
-				 twl4030_divider_ratios[i].denominator)
-				/ (2 * 1023 *
-				twl4030_divider_ratios[i].numerator);
-		}
-	}
-
-	return count;
-}
-
-/*
- * Enables irq.
- * @madc - pointer to twl4030_madc_data struct
- * @id - irq number to be enabled
- * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
- * corresponding to RT, SW1, SW2 conversion requests.
- * If the i2c read fails it returns an error else returns 0.
- */
-static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id)
-{
-	u8 val;
-	int ret;
-
-	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
-	if (ret) {
-		dev_err(madc->dev, "unable to read imr register 0x%X\n",
-			madc->imr);
-		return ret;
-	}
-
-	val &= ~(1 << id);
-	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
-	if (ret) {
-		dev_err(madc->dev,
-			"unable to write imr register 0x%X\n", madc->imr);
-		return ret;
-	}
-
-	return 0;
-}
-
-/*
- * Disables irq.
- * @madc - pointer to twl4030_madc_data struct
- * @id - irq number to be disabled
- * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
- * corresponding to RT, SW1, SW2 conversion requests.
- * Returns error if i2c read/write fails.
- */
-static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id)
-{
-	u8 val;
-	int ret;
-
-	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
-	if (ret) {
-		dev_err(madc->dev, "unable to read imr register 0x%X\n",
-			madc->imr);
-		return ret;
-	}
-	val |= (1 << id);
-	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
-	if (ret) {
-		dev_err(madc->dev,
-			"unable to write imr register 0x%X\n", madc->imr);
-		return ret;
-	}
-
-	return 0;
-}
-
-static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
-{
-	struct twl4030_madc_data *madc = _madc;
-	const struct twl4030_madc_conversion_method *method;
-	u8 isr_val, imr_val;
-	int i, len, ret;
-	struct twl4030_madc_request *r;
-
-	mutex_lock(&madc->lock);
-	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr);
-	if (ret) {
-		dev_err(madc->dev, "unable to read isr register 0x%X\n",
-			madc->isr);
-		goto err_i2c;
-	}
-	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr);
-	if (ret) {
-		dev_err(madc->dev, "unable to read imr register 0x%X\n",
-			madc->imr);
-		goto err_i2c;
-	}
-	isr_val &= ~imr_val;
-	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
-		if (!(isr_val & (1 << i)))
-			continue;
-		ret = twl4030_madc_disable_irq(madc, i);
-		if (ret < 0)
-			dev_dbg(madc->dev, "Disable interrupt failed%d\n", i);
-		madc->requests[i].result_pending = 1;
-	}
-	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
-		r = &madc->requests[i];
-		/* No pending results for this method, move to next one */
-		if (!r->result_pending)
-			continue;
-		method = &twl4030_conversion_methods[r->method];
-		/* Read results */
-		len = twl4030_madc_read_channels(madc, method->rbase,
-						 r->channels, r->rbuf, r->raw);
-		/* Return results to caller */
-		if (r->func_cb != NULL) {
-			r->func_cb(len, r->channels, r->rbuf);
-			r->func_cb = NULL;
-		}
-		/* Free request */
-		r->result_pending = 0;
-		r->active = 0;
-	}
-	mutex_unlock(&madc->lock);
-
-	return IRQ_HANDLED;
-
-err_i2c:
-	/*
-	 * In case of error check whichever request is active
-	 * and service the same.
-	 */
-	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
-		r = &madc->requests[i];
-		if (r->active == 0)
-			continue;
-		method = &twl4030_conversion_methods[r->method];
-		/* Read results */
-		len = twl4030_madc_read_channels(madc, method->rbase,
-						 r->channels, r->rbuf, r->raw);
-		/* Return results to caller */
-		if (r->func_cb != NULL) {
-			r->func_cb(len, r->channels, r->rbuf);
-			r->func_cb = NULL;
-		}
-		/* Free request */
-		r->result_pending = 0;
-		r->active = 0;
-	}
-	mutex_unlock(&madc->lock);
-
-	return IRQ_HANDLED;
-}
-
-static int twl4030_madc_set_irq(struct twl4030_madc_data *madc,
-				struct twl4030_madc_request *req)
-{
-	struct twl4030_madc_request *p;
-	int ret;
-
-	p = &madc->requests[req->method];
-	memcpy(p, req, sizeof(*req));
-	ret = twl4030_madc_enable_irq(madc, req->method);
-	if (ret < 0) {
-		dev_err(madc->dev, "enable irq failed!!\n");
-		return ret;
-	}
-
-	return 0;
-}
-
-/*
- * Function which enables the madc conversion
- * by writing to the control register.
- * @madc - pointer to twl4030_madc_data struct
- * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1
- * corresponding to RT SW1 or SW2 conversion methods.
- * Returns 0 if succeeds else a negative error value
- */
-static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
-					 int conv_method)
-{
-	const struct twl4030_madc_conversion_method *method;
-	int ret = 0;
-
-	if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2)
-		return -ENOTSUPP;
-
-	method = &twl4030_conversion_methods[conv_method];
-	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START,
-			       method->ctrl);
-	if (ret) {
-		dev_err(madc->dev, "unable to write ctrl register 0x%X\n",
-			method->ctrl);
-		return ret;
-	}
-
-	return 0;
-}
-
-/*
- * Function that waits for conversion to be ready
- * @madc - pointer to twl4030_madc_data struct
- * @timeout_ms - timeout value in milliseconds
- * @status_reg - ctrl register
- * returns 0 if succeeds else a negative error value
- */
-static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc,
-					      unsigned int timeout_ms,
-					      u8 status_reg)
-{
-	unsigned long timeout;
-	int ret;
-
-	timeout = jiffies + msecs_to_jiffies(timeout_ms);
-	do {
-		u8 reg;
-
-		ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &reg, status_reg);
-		if (ret) {
-			dev_err(madc->dev,
-				"unable to read status register 0x%X\n",
-				status_reg);
-			return ret;
-		}
-		if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW))
-			return 0;
-		usleep_range(500, 2000);
-	} while (!time_after(jiffies, timeout));
-	dev_err(madc->dev, "conversion timeout!\n");
-
-	return -EAGAIN;
-}
-
-/*
- * An exported function which can be called from other kernel drivers.
- * @req twl4030_madc_request structure
- * req->rbuf will be filled with read values of channels based on the
- * channel index. If a particular channel reading fails there will
- * be a negative error value in the corresponding array element.
- * returns 0 if succeeds else error value
- */
-int twl4030_madc_conversion(struct twl4030_madc_request *req)
-{
-	const struct twl4030_madc_conversion_method *method;
-	u8 ch_msb, ch_lsb;
-	int ret;
-
-	if (!req || !twl4030_madc)
-		return -EINVAL;
-
-	mutex_lock(&twl4030_madc->lock);
-	if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) {
-		ret = -EINVAL;
-		goto out;
-	}
-	/* Do we have a conversion request ongoing */
-	if (twl4030_madc->requests[req->method].active) {
-		ret = -EBUSY;
-		goto out;
-	}
-	ch_msb = (req->channels >> 8) & 0xff;
-	ch_lsb = req->channels & 0xff;
-	method = &twl4030_conversion_methods[req->method];
-	/* Select channels to be converted */
-	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1);
-	if (ret) {
-		dev_err(twl4030_madc->dev,
-			"unable to write sel register 0x%X\n", method->sel + 1);
-		goto out;
-	}
-	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel);
-	if (ret) {
-		dev_err(twl4030_madc->dev,
-			"unable to write sel register 0x%X\n", method->sel + 1);
-		goto out;
-	}
-	/* Select averaging for all channels if do_avg is set */
-	if (req->do_avg) {
-		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
-				       ch_msb, method->avg + 1);
-		if (ret) {
-			dev_err(twl4030_madc->dev,
-				"unable to write avg register 0x%X\n",
-				method->avg + 1);
-			goto out;
-		}
-		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
-				       ch_lsb, method->avg);
-		if (ret) {
-			dev_err(twl4030_madc->dev,
-				"unable to write sel reg 0x%X\n",
-				method->avg);
-			goto out;
-		}
-	}
-	if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) {
-		ret = twl4030_madc_set_irq(twl4030_madc, req);
-		if (ret < 0)
-			goto out;
-		ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
-		if (ret < 0)
-			goto out;
-		twl4030_madc->requests[req->method].active = 1;
-		ret = 0;
-		goto out;
-	}
-	/* With RT method we should not be here anymore */
-	if (req->method == TWL4030_MADC_RT) {
-		ret = -EINVAL;
-		goto out;
-	}
-	ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
-	if (ret < 0)
-		goto out;
-	twl4030_madc->requests[req->method].active = 1;
-	/* Wait until conversion is ready (ctrl register returns EOC) */
-	ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl);
-	if (ret) {
-		twl4030_madc->requests[req->method].active = 0;
-		goto out;
-	}
-	ret = twl4030_madc_read_channels(twl4030_madc, method->rbase,
-					 req->channels, req->rbuf, req->raw);
-	twl4030_madc->requests[req->method].active = 0;
-
-out:
-	mutex_unlock(&twl4030_madc->lock);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(twl4030_madc_conversion);
-
-int twl4030_get_madc_conversion(int channel_no)
-{
-	struct twl4030_madc_request req;
-	int temp = 0;
-	int ret;
-
-	req.channels = (1 << channel_no);
-	req.method = TWL4030_MADC_SW2;
-	req.active = 0;
-	req.func_cb = NULL;
-	ret = twl4030_madc_conversion(&req);
-	if (ret < 0)
-		return ret;
-	if (req.rbuf[channel_no] > 0)
-		temp = req.rbuf[channel_no];
-
-	return temp;
-}
-EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
-
-/*
- * Function to enable or disable bias current for
- * main battery type reading or temperature sensing
- * @madc - pointer to twl4030_madc_data struct
- * @chan - can be one of the two values
- * TWL4030_BCI_ITHEN - Enables bias current for main battery type reading
- * TWL4030_BCI_TYPEN - Enables bias current for main battery temperature
- * sensing
- * @on - enable or disable chan.
- */
-static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
-					      int chan, int on)
-{
-	int ret;
-	int regmask;
-	u8 regval;
-
-	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
-			      &regval, TWL4030_BCI_BCICTL1);
-	if (ret) {
-		dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X",
-			TWL4030_BCI_BCICTL1);
-		return ret;
-	}
-
-	regmask = (chan == 0) ? TWL4030_BCI_TYPEN : TWL4030_BCI_ITHEN;
-	if (on)
-		regval |= regmask;
-	else
-		regval &= ~regmask;
-
-	ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
-			       regval, TWL4030_BCI_BCICTL1);
-	if (ret) {
-		dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n",
-			TWL4030_BCI_BCICTL1);
-		return ret;
-	}
-
-	return 0;
-}
-
-/*
- * Function that sets MADC software power on bit to enable MADC
- * @madc - pointer to twl4030_madc_data struct
- * @on - Enable or disable MADC software powen on bit.
- * returns error if i2c read/write fails else 0
- */
-static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
-{
-	u8 regval;
-	int ret;
-
-	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
-			      &regval, TWL4030_MADC_CTRL1);
-	if (ret) {
-		dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n",
-			TWL4030_MADC_CTRL1);
-		return ret;
-	}
-	if (on)
-		regval |= TWL4030_MADC_MADCON;
-	else
-		regval &= ~TWL4030_MADC_MADCON;
-	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1);
-	if (ret) {
-		dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n",
-			TWL4030_MADC_CTRL1);
-		return ret;
-	}
-
-	return 0;
-}
-
-/*
- * Initialize MADC and request for threaded irq
- */
-static int twl4030_madc_probe(struct platform_device *pdev)
-{
-	struct twl4030_madc_data *madc;
-	struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
-	struct device_node *np = pdev->dev.of_node;
-	int irq, ret;
-	u8 regval;
-	struct iio_dev *iio_dev = NULL;
-
-	if (!pdata && !np) {
-		dev_err(&pdev->dev, "platform_data not available\n");
-		return -EINVAL;
-	}
-
-	iio_dev = devm_iio_device_alloc(&pdev->dev,
-					sizeof(struct twl4030_madc_data));
-	if (!iio_dev) {
-		dev_err(&pdev->dev, "failed allocating iio device\n");
-		return -ENOMEM;
-	}
-
-	madc = iio_priv(iio_dev);
-	madc->dev = &pdev->dev;
-
-	iio_dev->name = dev_name(&pdev->dev);
-	iio_dev->dev.parent = &pdev->dev;
-	iio_dev->dev.of_node = pdev->dev.of_node;
-	iio_dev->info = &twl4030_madc_iio_info;
-	iio_dev->modes = INDIO_DIRECT_MODE;
-	iio_dev->channels = twl4030_madc_iio_channels;
-	iio_dev->num_channels = 16;
-
-	/*
-	 * Phoenix provides 2 interrupt lines. The first one is connected to
-	 * the OMAP. The other one can be connected to the other processor such
-	 * as modem. Hence two separate ISR and IMR registers.
-	 */
-	if (pdata)
-		madc->use_second_irq = pdata->irq_line != 1;
-	else
-		madc->use_second_irq = of_property_read_bool(np,
-				       "ti,system-uses-second-madc-irq");
-
-	madc->imr = (madc->use_second_irq == 1) ?
-	    TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
-	madc->isr = (madc->use_second_irq == 1) ?
-	    TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
-
-	ret = twl4030_madc_set_power(madc, 1);
-	if (ret < 0)
-		return ret;
-	ret = twl4030_madc_set_current_generator(madc, 0, 1);
-	if (ret < 0)
-		goto err_current_generator;
-
-	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
-			      &regval, TWL4030_BCI_BCICTL1);
-	if (ret) {
-		dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n",
-			TWL4030_BCI_BCICTL1);
-		goto err_i2c;
-	}
-	regval |= TWL4030_BCI_MESBAT;
-	ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
-			       regval, TWL4030_BCI_BCICTL1);
-	if (ret) {
-		dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n",
-			TWL4030_BCI_BCICTL1);
-		goto err_i2c;
-	}
-
-	/* Check that MADC clock is on */
-	ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &regval, TWL4030_REG_GPBR1);
-	if (ret) {
-		dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n",
-				TWL4030_REG_GPBR1);
-		goto err_i2c;
-	}
-
-	/* If MADC clk is not on, turn it on */
-	if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) {
-		dev_info(&pdev->dev, "clk disabled, enabling\n");
-		regval |= TWL4030_GPBR1_MADC_HFCLK_EN;
-		ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval,
-				       TWL4030_REG_GPBR1);
-		if (ret) {
-			dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n",
-					TWL4030_REG_GPBR1);
-			goto err_i2c;
-		}
-	}
-
-	platform_set_drvdata(pdev, iio_dev);
-	mutex_init(&madc->lock);
-
-	irq = platform_get_irq(pdev, 0);
-	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
-				   twl4030_madc_threaded_irq_handler,
-				   IRQF_TRIGGER_RISING, "twl4030_madc", madc);
-	if (ret) {
-		dev_dbg(&pdev->dev, "could not request irq\n");
-		goto err_i2c;
-	}
-	twl4030_madc = madc;
-
-	ret = iio_device_register(iio_dev);
-	if (ret) {
-		dev_dbg(&pdev->dev, "could not register iio device\n");
-		goto err_i2c;
-	}
-
-	return 0;
-
-err_i2c:
-	twl4030_madc_set_current_generator(madc, 0, 0);
-err_current_generator:
-	twl4030_madc_set_power(madc, 0);
-	return ret;
-}
-
-static int twl4030_madc_remove(struct platform_device *pdev)
-{
-	struct iio_dev *iio_dev = platform_get_drvdata(pdev);
-	struct twl4030_madc_data *madc = iio_priv(iio_dev);
-
-	twl4030_madc_set_current_generator(madc, 0, 0);
-	twl4030_madc_set_power(madc, 0);
-
-	iio_device_unregister(iio_dev);
-
-	return 0;
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id twl_madc_of_match[] = {
-	{.compatible = "ti,twl4030-madc", },
-	{ },
-};
-MODULE_DEVICE_TABLE(of, twl_madc_of_match);
-#endif
-
-static struct platform_driver twl4030_madc_driver = {
-	.probe = twl4030_madc_probe,
-	.remove = twl4030_madc_remove,
-	.driver = {
-		   .name = "twl4030_madc",
-		   .owner = THIS_MODULE,
-		   .of_match_table = of_match_ptr(twl_madc_of_match),
-		   },
-};
-
-module_platform_driver(twl4030_madc_driver);
-
-MODULE_DESCRIPTION("TWL4030 ADC driver");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("J Keerthy");
-MODULE_ALIAS("platform:twl4030_madc");
-- 
1.8.5.3

^ permalink raw reply related

* [RFCv2 4/5] Documentation: DT: Document twl4030-madc binding
From: Sebastian Reichel @ 2014-02-23 22:07 UTC (permalink / raw)
  To: Sebastian Reichel, Marek Belisko, Jonathan Cameron
  Cc: Lee Jones, Samuel Ortiz, Lars-Peter Clausen, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
	linux-kernel, devicetree, linux-iio, Sebastian Reichel
In-Reply-To: <1393193271-16717-1-git-send-email-sre@debian.org>

Add devicetree binding documentation for twl4030-madc
analog digital converter.

Signed-off-by: Sebastian Reichel <sre@debian.org>
---
 .../devicetree/bindings/iio/adc/twl4030-madc.txt   | 24 ++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/twl4030-madc.txt

diff --git a/Documentation/devicetree/bindings/iio/adc/twl4030-madc.txt b/Documentation/devicetree/bindings/iio/adc/twl4030-madc.txt
new file mode 100644
index 0000000..6bdd214
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/twl4030-madc.txt
@@ -0,0 +1,24 @@
+* TWL4030 Monitoring Analog to Digital Converter (MADC)
+
+The MADC subsystem in the TWL4030 consists of a 10-bit ADC
+combined with a 16-input analog multiplexer.
+
+Required properties:
+  - compatible: Should contain "ti,twl4030-madc".
+  - interrupts: IRQ line for the MADC submodule.
+  - #io-channel-cells: Should be set to <1>.
+
+Optional properties:
+  - ti,system-uses-second-madc-irq: boolean, set if the second madc irq register
+				    should be used, which is intended to be used
+				    by Co-Processors (e.g. a modem).
+
+Example:
+
+&twl {
+	madc {
+		compatible = "ti,twl4030-madc";
+		interrupts = <3>;
+		#io-channel-cells = <1>;
+	};
+};
-- 
1.8.5.3

^ permalink raw reply related

* [RFCv2 3/5] mfd: twl4030-madc: Cleanup driver
From: Sebastian Reichel @ 2014-02-23 22:07 UTC (permalink / raw)
  To: Sebastian Reichel, Marek Belisko, Jonathan Cameron
  Cc: Lee Jones, Samuel Ortiz, Lars-Peter Clausen, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
	linux-kernel, devicetree, linux-iio, Sebastian Reichel
In-Reply-To: <1393193271-16717-1-git-send-email-sre@debian.org>

Some style fixes in twl4030-madc driver.

Reported-by: Jonathan Cameron <jic23@kernel.org>
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
 drivers/mfd/twl4030-madc.c | 66 ++++++++++++++++++++--------------------------
 1 file changed, 29 insertions(+), 37 deletions(-)

diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c
index 5e67c75..0bc79a7 100644
--- a/drivers/mfd/twl4030-madc.c
+++ b/drivers/mfd/twl4030-madc.c
@@ -62,8 +62,8 @@ struct twl4030_madc_data {
 	struct mutex lock;	/* mutex protecting this data structure */
 	struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
 	bool use_second_irq;
-	int imr;
-	int isr;
+	u8 imr;
+	u8 isr;
 };
 
 static int twl4030_madc_read(struct iio_dev *iio_dev,
@@ -242,13 +242,13 @@ static int twl4030battery_temperature(int raw_volt)
 		REG_BCICTL2);
 	if (ret < 0)
 		return ret;
+
 	curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
 	/* Getting and calculating the thermistor resistance in ohms */
 	res = volt * 1000 / curr;
 	/* calculating temperature */
 	for (temp = 58; temp >= 0; temp--) {
 		int actual = therm_tbl[temp];
-
 		if ((actual - res) >= 0)
 			break;
 	}
@@ -270,6 +270,7 @@ static int twl4030battery_current(int raw_volt)
 	else /* slope of 0.88 mV/mA */
 		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
 }
+
 /*
  * Function to read channel values
  * @madc - pointer to twl4030_madc_data struct
@@ -285,17 +286,14 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
 				      long channels, int *buf,
 				      bool raw)
 {
-	int count = 0, count_req = 0, i;
-	u8 reg;
+	int count = 0, i;
 
 	for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
-		reg = reg_base + 2 * i;
-		buf[i] = twl4030_madc_channel_raw_read(madc, reg);
+		buf[i] = twl4030_madc_channel_raw_read(madc, reg_base + 2 * i);
 		if (buf[i] < 0) {
-			dev_err(madc->dev,
-				"Unable to read register 0x%X\n", reg);
-			count_req++;
-			continue;
+			dev_err(madc->dev, "Unable to read register 0x%X\n",
+				reg_base + 2 * i);
+			return buf[i];
 		}
 		if (raw) {
 			count++;
@@ -306,7 +304,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
 			buf[i] = twl4030battery_current(buf[i]);
 			if (buf[i] < 0) {
 				dev_err(madc->dev, "err reading current\n");
-				count_req++;
+				return buf[i];
 			} else {
 				count++;
 				buf[i] = buf[i] - 750;
@@ -316,7 +314,7 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
 			buf[i] = twl4030battery_temperature(buf[i]);
 			if (buf[i] < 0) {
 				dev_err(madc->dev, "err reading temperature\n");
-				count_req++;
+				return buf[i];
 			} else {
 				buf[i] -= 3;
 				count++;
@@ -337,8 +335,6 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
 				twl4030_divider_ratios[i].numerator);
 		}
 	}
-	if (count_req)
-		dev_err(madc->dev, "%d channel conversion failed\n", count_req);
 
 	return count;
 }
@@ -362,13 +358,13 @@ static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id)
 			madc->imr);
 		return ret;
 	}
+
 	val &= ~(1 << id);
 	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
 	if (ret) {
 		dev_err(madc->dev,
 			"unable to write imr register 0x%X\n", madc->imr);
 		return ret;
-
 	}
 
 	return 0;
@@ -513,21 +509,17 @@ static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
 {
 	const struct twl4030_madc_conversion_method *method;
 	int ret = 0;
+
+	if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2)
+		return -ENOTSUPP;
+
 	method = &twl4030_conversion_methods[conv_method];
-	switch (conv_method) {
-	case TWL4030_MADC_SW1:
-	case TWL4030_MADC_SW2:
-		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
-				       TWL4030_MADC_SW_START, method->ctrl);
-		if (ret) {
-			dev_err(madc->dev,
-				"unable to write ctrl register 0x%X\n",
-				method->ctrl);
-			return ret;
-		}
-		break;
-	default:
-		break;
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START,
+			       method->ctrl);
+	if (ret) {
+		dev_err(madc->dev, "unable to write ctrl register 0x%X\n",
+			method->ctrl);
+		return ret;
 	}
 
 	return 0;
@@ -625,7 +617,7 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req)
 		if (ret) {
 			dev_err(twl4030_madc->dev,
 				"unable to write sel reg 0x%X\n",
-				method->sel + 1);
+				method->avg);
 			goto out;
 		}
 	}
@@ -666,10 +658,6 @@ out:
 }
 EXPORT_SYMBOL_GPL(twl4030_madc_conversion);
 
-/*
- * Return channel value
- * Or < 0 on failure.
- */
 int twl4030_get_madc_conversion(int channel_no)
 {
 	struct twl4030_madc_request req;
@@ -704,6 +692,7 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
 					      int chan, int on)
 {
 	int ret;
+	int regmask;
 	u8 regval;
 
 	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
@@ -713,10 +702,13 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
 			TWL4030_BCI_BCICTL1);
 		return ret;
 	}
+
+	regmask = (chan == 0) ? TWL4030_BCI_TYPEN : TWL4030_BCI_ITHEN;
 	if (on)
-		regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
+		regval |= regmask;
 	else
-		regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN;
+		regval &= ~regmask;
+
 	ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
 			       regval, TWL4030_BCI_BCICTL1);
 	if (ret) {
-- 
1.8.5.3

^ permalink raw reply related

* [RFCv2 2/5] mfd: twl4030-madc: Add DT support and convert to IIO framework
From: Sebastian Reichel @ 2014-02-23 22:07 UTC (permalink / raw)
  To: Sebastian Reichel, Marek Belisko, Jonathan Cameron
  Cc: Lee Jones, Samuel Ortiz, Lars-Peter Clausen, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
	linux-kernel, devicetree, linux-iio, Sebastian Reichel
In-Reply-To: <1393193271-16717-1-git-send-email-sre@debian.org>

This converts twl4030-madc module to use the Industrial IO ADC
framework and adds device tree support.

Signed-off-by: Sebastian Reichel <sre@debian.org>
---
 drivers/mfd/twl4030-madc.c | 121 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 114 insertions(+), 7 deletions(-)

diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c
index 5458561..5e67c75 100644
--- a/drivers/mfd/twl4030-madc.c
+++ b/drivers/mfd/twl4030-madc.c
@@ -47,6 +47,8 @@
 #include <linux/gfp.h>
 #include <linux/err.h>
 
+#include <linux/iio/iio.h>
+
 /*
  * struct twl4030_madc_data - a container for madc info
  * @dev - pointer to device structure for madc
@@ -59,10 +61,73 @@ struct twl4030_madc_data {
 	struct device *dev;
 	struct mutex lock;	/* mutex protecting this data structure */
 	struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
+	bool use_second_irq;
 	int imr;
 	int isr;
 };
 
+static int twl4030_madc_read(struct iio_dev *iio_dev,
+			     const struct iio_chan_spec *chan,
+			     int *val, int *val2, long mask)
+{
+	struct twl4030_madc_data *madc = iio_priv(iio_dev);
+	struct twl4030_madc_request req;
+	int channel = chan->channel;
+	int ret;
+
+	req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1;
+
+	req.channels = BIT(channel);
+	req.active = 0;
+	req.func_cb = NULL;
+	req.raw = !(mask & IIO_CHAN_INFO_PROCESSED);
+	req.do_avg = !!(mask & IIO_CHAN_INFO_AVERAGE_RAW);
+
+	ret = twl4030_madc_conversion(&req);
+	if (ret < 0)
+		return ret;
+
+	*val = req.rbuf[channel];
+
+	return IIO_VAL_INT;
+}
+
+static const struct iio_info twl4030_madc_iio_info = {
+	.read_raw = &twl4030_madc_read,
+	.driver_module = THIS_MODULE,
+};
+
+#define TWL4030_ADC_CHANNEL(_channel, _type, _name, _mask) {	\
+	.type = _type,					\
+	.channel = _channel,				\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
+			      BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
+			      _mask,			\
+	.datasheet_name = _name,			\
+	.indexed = 1,					\
+}
+
+static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
+	TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0", 0),
+	TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1",
+		BIT(IIO_CHAN_INFO_PROCESSED)),
+	TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2", 0),
+	TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3", 0),
+	TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4", 0),
+	TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5", 0),
+	TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6", 0),
+	TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7", 0),
+	TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8", 0),
+	TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9", 0),
+	TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10",
+		BIT(IIO_CHAN_INFO_PROCESSED)),
+	TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11", 0),
+	TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12", 0),
+	TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13", 0),
+	TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14", 0),
+	TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15", 0),
+};
+
 static struct twl4030_madc_data *twl4030_madc;
 
 struct twl4030_prescale_divider_ratios {
@@ -702,28 +767,50 @@ static int twl4030_madc_probe(struct platform_device *pdev)
 {
 	struct twl4030_madc_data *madc;
 	struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct device_node *np = pdev->dev.of_node;
 	int irq, ret;
 	u8 regval;
+	struct iio_dev *iio_dev = NULL;
 
-	if (!pdata) {
+	if (!pdata && !np) {
 		dev_err(&pdev->dev, "platform_data not available\n");
 		return -EINVAL;
 	}
-	madc = devm_kzalloc(&pdev->dev, sizeof(*madc), GFP_KERNEL);
-	if (!madc)
+
+	iio_dev = devm_iio_device_alloc(&pdev->dev,
+					sizeof(struct twl4030_madc_data));
+	if (!iio_dev) {
+		dev_err(&pdev->dev, "failed allocating iio device\n");
 		return -ENOMEM;
+	}
 
+	madc = iio_priv(iio_dev);
 	madc->dev = &pdev->dev;
 
+	iio_dev->name = dev_name(&pdev->dev);
+	iio_dev->dev.parent = &pdev->dev;
+	iio_dev->dev.of_node = pdev->dev.of_node;
+	iio_dev->info = &twl4030_madc_iio_info;
+	iio_dev->modes = INDIO_DIRECT_MODE;
+	iio_dev->channels = twl4030_madc_iio_channels;
+	iio_dev->num_channels = 16;
+
 	/*
 	 * Phoenix provides 2 interrupt lines. The first one is connected to
 	 * the OMAP. The other one can be connected to the other processor such
 	 * as modem. Hence two separate ISR and IMR registers.
 	 */
-	madc->imr = (pdata->irq_line == 1) ?
+	if (pdata)
+		madc->use_second_irq = pdata->irq_line != 1;
+	else
+		madc->use_second_irq = of_property_read_bool(np,
+				       "ti,system-uses-second-madc-irq");
+
+	madc->imr = (madc->use_second_irq == 1) ?
 	    TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
-	madc->isr = (pdata->irq_line == 1) ?
+	madc->isr = (madc->use_second_irq == 1) ?
 	    TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
+
 	ret = twl4030_madc_set_power(madc, 1);
 	if (ret < 0)
 		return ret;
@@ -768,7 +855,7 @@ static int twl4030_madc_probe(struct platform_device *pdev)
 		}
 	}
 
-	platform_set_drvdata(pdev, madc);
+	platform_set_drvdata(pdev, iio_dev);
 	mutex_init(&madc->lock);
 
 	irq = platform_get_irq(pdev, 0);
@@ -780,7 +867,15 @@ static int twl4030_madc_probe(struct platform_device *pdev)
 		goto err_i2c;
 	}
 	twl4030_madc = madc;
+
+	ret = iio_device_register(iio_dev);
+	if (ret) {
+		dev_dbg(&pdev->dev, "could not register iio device\n");
+		goto err_i2c;
+	}
+
 	return 0;
+
 err_i2c:
 	twl4030_madc_set_current_generator(madc, 0, 0);
 err_current_generator:
@@ -790,20 +885,32 @@ err_current_generator:
 
 static int twl4030_madc_remove(struct platform_device *pdev)
 {
-	struct twl4030_madc_data *madc = platform_get_drvdata(pdev);
+	struct iio_dev *iio_dev = platform_get_drvdata(pdev);
+	struct twl4030_madc_data *madc = iio_priv(iio_dev);
 
 	twl4030_madc_set_current_generator(madc, 0, 0);
 	twl4030_madc_set_power(madc, 0);
 
+	iio_device_unregister(iio_dev);
+
 	return 0;
 }
 
+#ifdef CONFIG_OF
+static const struct of_device_id twl_madc_of_match[] = {
+	{.compatible = "ti,twl4030-madc", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, twl_madc_of_match);
+#endif
+
 static struct platform_driver twl4030_madc_driver = {
 	.probe = twl4030_madc_probe,
 	.remove = twl4030_madc_remove,
 	.driver = {
 		   .name = "twl4030_madc",
 		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(twl_madc_of_match),
 		   },
 };
 
-- 
1.8.5.3

^ permalink raw reply related

* [RFCv2 1/5] mfd: twl4030-madc: Use managed resources
From: Sebastian Reichel @ 2014-02-23 22:07 UTC (permalink / raw)
  To: Sebastian Reichel, Marek Belisko, Jonathan Cameron
  Cc: Lee Jones, Samuel Ortiz, Lars-Peter Clausen, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
	linux-kernel, devicetree, linux-iio, Sebastian Reichel
In-Reply-To: <1393193271-16717-1-git-send-email-sre@debian.org>

Update twl4030-madc driver to use managed resources.

Signed-off-by: Sebastian Reichel <sre@debian.org>
---
 drivers/mfd/twl4030-madc.c | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c
index 4c583e4..5458561 100644
--- a/drivers/mfd/twl4030-madc.c
+++ b/drivers/mfd/twl4030-madc.c
@@ -702,14 +702,14 @@ static int twl4030_madc_probe(struct platform_device *pdev)
 {
 	struct twl4030_madc_data *madc;
 	struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
-	int ret;
+	int irq, ret;
 	u8 regval;
 
 	if (!pdata) {
 		dev_err(&pdev->dev, "platform_data not available\n");
 		return -EINVAL;
 	}
-	madc = kzalloc(sizeof(*madc), GFP_KERNEL);
+	madc = devm_kzalloc(&pdev->dev, sizeof(*madc), GFP_KERNEL);
 	if (!madc)
 		return -ENOMEM;
 
@@ -726,7 +726,7 @@ static int twl4030_madc_probe(struct platform_device *pdev)
 	    TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
 	ret = twl4030_madc_set_power(madc, 1);
 	if (ret < 0)
-		goto err_power;
+		return ret;
 	ret = twl4030_madc_set_current_generator(madc, 0, 1);
 	if (ret < 0)
 		goto err_current_generator;
@@ -770,7 +770,9 @@ static int twl4030_madc_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, madc);
 	mutex_init(&madc->lock);
-	ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL,
+
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
 				   twl4030_madc_threaded_irq_handler,
 				   IRQF_TRIGGER_RISING, "twl4030_madc", madc);
 	if (ret) {
@@ -783,9 +785,6 @@ err_i2c:
 	twl4030_madc_set_current_generator(madc, 0, 0);
 err_current_generator:
 	twl4030_madc_set_power(madc, 0);
-err_power:
-	kfree(madc);
-
 	return ret;
 }
 
@@ -793,10 +792,8 @@ static int twl4030_madc_remove(struct platform_device *pdev)
 {
 	struct twl4030_madc_data *madc = platform_get_drvdata(pdev);
 
-	free_irq(platform_get_irq(pdev, 0), madc);
 	twl4030_madc_set_current_generator(madc, 0, 0);
 	twl4030_madc_set_power(madc, 0);
-	kfree(madc);
 
 	return 0;
 }
-- 
1.8.5.3

^ permalink raw reply related

* [RFCv2 0/5] Convert twl4030-madc to IIO API
From: Sebastian Reichel @ 2014-02-23 22:07 UTC (permalink / raw)
  To: Sebastian Reichel, Marek Belisko, Jonathan Cameron
  Cc: Lee Jones, Samuel Ortiz, Lars-Peter Clausen, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Sebastian Reichel
In-Reply-To: <20140223220140.GA1258-SfvFxonMDyemK9LvCR3Hrw@public.gmane.org>

Hi,

This is RFCv2 for converting twl4030-madc to the IIO API and
adding DT support. The patchset compiles, but has not yet been
tested on real hardware. I plan to convert rx51-battery to the
IIO framework in the near future and test this patchset on my
Nokia N900.

Changes since RFCv1:
 * Add ti,system-uses-second-madc-irq DT property needed by Belisko Marek
 * Fix issues reported by Jonathan Cameron

TODO:
 * Test patchset on real hardware
 * Convert consumer drivers to IIO API
  - rx51-battery
  - twl4030-madc-battery
  - twl4030-madc-hwmon (=> deprecate in favour of iio-hwmon)
 * remove old kernel API from twl4030-madc

-- Sebastian

Sebastian Reichel (5):
  mfd: twl4030-madc: Use managed resources
  mfd: twl4030-madc: Add DT support and convert to IIO framework
  mfd: twl4030-madc: Cleanup driver
  Documentation: DT: Document twl4030-madc binding
  mfd: twl4030-madc: Move driver to drivers/iio/adc

 .../devicetree/bindings/iio/adc/twl4030-madc.txt   |  24 +
 drivers/iio/adc/Kconfig                            |  10 +
 drivers/iio/adc/Makefile                           |   1 +
 drivers/iio/adc/twl4030-madc.c                     | 914 +++++++++++++++++++++
 drivers/mfd/Kconfig                                |  10 -
 drivers/mfd/Makefile                               |   1 -
 drivers/mfd/twl4030-madc.c                         | 818 ------------------
 7 files changed, 949 insertions(+), 829 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/twl4030-madc.txt
 create mode 100644 drivers/iio/adc/twl4030-madc.c
 delete mode 100644 drivers/mfd/twl4030-madc.c

-- 
1.8.5.3

^ permalink raw reply

* Re: [RFCv1 4/4] mfd: twl4030-madc: Move driver to drivers/iio/adc
From: Sebastian Reichel @ 2014-02-23 22:01 UTC (permalink / raw)
  To: Jonathan Cameron, Marek Belisko
  Cc: Lee Jones, Samuel Ortiz, Lars-Peter Clausen, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <5309D536.9020404-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 8680 bytes --]

Hi,

On Sun, Feb 23, 2014 at 11:02:14AM +0000, Jonathan Cameron wrote:
> On 23/02/14 00:35, Sebastian Reichel wrote:
> >I think the optimal workflow is:
> >
> >1. convert madc driver to IIO
> >2. convert twl4030-madc-battery driver to IIO API
> >3. convert rx51-battery to IIO API
> >4. convert twl4030-madc-hwmon to IIO API / deprecate it
> >5. remove old in-kernel ABI from madc
> >6. cleanup/simplify madc
> >
> >I guess its much simpler to do the driver cleanup/simplification
> >once we can get rid of the old API.
> 
> Perhaps.  I guess it depends on your planned schedule.  If you are
> intending to plough through the above in the near future then fair
> enough.  If the chances are it'll take a while, I'd be inclined to
> fix some of the more glarringly hideous bits now (such as error
> code reporting) and then visit your list above over time.

I plan to update the rx51-battery driver, since that's the only one
I can test. Actually I only did the changes to the twl4030-madc
driver, so that I can use the standard DT API for AD converters for
rx51-battery. I hope to get it updated ASAP.

Marek seems to work on getting DT support for the other two
consumers (twl4030-madc-battery and twl4030-madc-hwmon), so chances
are, that the old API can be removed in the near future :)

> Note that to my mind it is converted drivers like this that sneak some
> nasty code into an a subsystem I look after.  We have a lot of 'interesting'
> code out in staging still, but for new submissions we are being increasingly
> fussy about little bits.  So I hope I don't come across as being too mean
> about this driver.  I'm glad you are putting in the work to convert
> it over.  There are a quite a few similar drivers sculking through the tree
> so it's nice that someone has gotten started on bringing one over to IIO.

If it makes you feel more at ease I will try to forward the errors
and replace the error code with 0 at the end.

> [...]
>
> >>Is the average have an adjustable number of samples?
> >
> >The TWL supports reading a value directly (1 sample) or reading
> >the average of 4 samples.
> >
> >>If not I'd be tempted to use the more common option of IIO_CHAN_INFO_RAW
> >>rather than the average version (tends only to be used in fairly obscure
> >>cases where both a raw access and an averaged one are available).
> >
> >currently some drivers use the averaged read and some use the direct
> >read.
> >
> 
> In that case I'd expose both the 1 sample and averaged versions via the
> IIO interfaces. If you want to pick 1 then do IIO_CHAN_INFO_RAW not the
> averaged version.

That's what I intended to do. Now I see, that I forgot to expose
IIO_CHAN_INFO_RAW. Sorry for the confusion.

> >>>+static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
> >>>+	ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(1, IIO_TEMP, "ADCIN1", BIT(IIO_CHAN_INFO_PROCESSED) |
> >>>+					   BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10", BIT(IIO_CHAN_INFO_PROCESSED) |
> >>>+						BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+	ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >>>+};
> >>>+
> >>Why the artificial limitation of one of these devices?  I guess this is to
> >>allow the exported function to work without needing to be associated with
> >>any particular device...  Hmm.
> >
> >Artificial limitation? The madc is a ADC, which has some special
> >functions defined for some pins in the datasheet:
> >
> >ADC0 = Battery Voltage
> >ADC1 = Battery Temperature
> >ADC10 = Battery Current
> >
> >Nokia misused some of those pins on the Nokia N900, though. For
> >example they have their own conversion tables for the temperature,
> >which is not compatible with the generic one for the madc module.
> 
> *laughs*.  If someone documents a pin as having a particular purpose
> but it isn't wired inside a package. Someone will always think they know
> better.  Basic principal is to never limit ourselves to one instance of
> a chip because the chances of someone deciding to do something interesting
> with multiple chips is way to high.

;)

> >
> >I planned to convert the rx51-battery driver to simply read the raw
> >values. All other users can use the processed information.
> Makes sense.

ok :)

> [...]
> >>>+	if (count_req)
> >>>+		dev_err(madc->dev, "%d channel conversion failed\n", count_req);
> >>>+
> >>>+	return count;
> >>This is already apparant from the errors emmited earlier. I'd drop this
> >>and the count_req counter in general.
> >>Personally I'd error out in those cases anyway rather than carrying on
> >>with the rest of the channels. If it's worth of a dev_err it's worthy
> >>of getting out as fast as possible rather than trying to muddle on.
> >
> >I also think this should be postponed until the old API is removed.
> I'd prefer to see standalone cleanups and fixes integrated first as I suspect
> it may be a little while before we manage to fully drop the old API.

ok. I have included this change in the fixup patch. This slightly
changes the old API (callback may be called with negative error
values), but the callback feature doesn't seem to be used by any
mainline user.

> [...]
> >>Can this and the previous loop not be combined thus simplifying some tests?
> >
> >It probably can be, but I think this can wait until the old API is
> >removed.
> Hmm. As I state below, if this is moving over to IIO (which is sensible) it
> becomes my problem so I reserve the right to be fussy about less than optimal
> code.  If it was coming in clean as a new driver I'd push back hard on a lot
> of this stuff before merging it.  I have plenty of slightly uggly drivers
> in staging to look after as it is!

I would feel more comfortable leaving the second loop as is for now.
It's a speed optimization, which acknowledges all IRQs as fast as
possible (=> before triggering further conversion of the raw data).

> [...]
> >>>+static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
> >>>+					 int conv_method)
> >>>+{
> >>>+	const struct twl4030_madc_conversion_method *method;
> >>>+	int ret = 0;
> >>>+	method = &twl4030_conversion_methods[conv_method];
> >>>+	switch (conv_method) {
> >>Can we get here via any paths where these aren't the methods set?
> >
> >conv_method is set from outside of the driver, so it's probably
> >safer to check. This can be simplified once the old API is removed.
> Then we should be returning an error if it's not one of these.

right and reading the code more carefully it should be tested before
using it for array access.

> >I assume, that the driver will get a huge cleanup once all consumers
> >of the old API are converted. Maybe a deprecation flag should be
> >added together with this patchset?
> Probably not worth bothering for an inkernel interface that seems to have
> relatively few users.  How many out of tree users will be moving to
> new kernels and using this device?

At least no new twl4030-madc consumer has been posted to
linux-kernel or linux-omap in the last months. So I will
ignore this.

> [...]
> >>Personally I'd rank the driver as rather to vebose on read error messages
> >>given they are pretty unusual.  Ah well I guess each to their own.
> >
> >All of this is not written by me.
> Sure.  Doesn't stop me moaning :)  At the end of the day you are proposing moving
> this driver into a place where it becomes at least partly my problem.  As such
> I'm keen on any tidying / cleaning occuring ASAP.

Fair enough.

-- Sebastian

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply

* Re: [PATCH v7 2/8] clk: sunxi: Implement MMC phase control
From: Mike Turquette @ 2014-02-23 20:31 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Lanzendörfer, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Ulf Hansson, Laurent Pinchart, Simon Baatz, Hans de Goede,
	Emilio López, linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo,
	Guennadi Liakhovetski,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	dinguyen-EIB2kfCEclfQT0dZR+AlfA
In-Reply-To: <20140219083606.GR3142@lukather>

Quoting Maxime Ripard (2014-02-19 00:36:06)
> Hi Mike,
> 
> On Tue, Feb 18, 2014 at 09:21:25PM -0800, Mike Turquette wrote:
> > Quoting Maxime Ripard (2014-02-18 06:15:32)
> > > Hi,
> > > 
> > > On Mon, Feb 17, 2014 at 11:02:21AM +0100, David Lanzendörfer wrote:
> > > > From: Emilio López <emilio-0Z03zUJReD5OxF6Tv1QG9Q@public.gmane.org>
> > > > 
> > > > Signed-off-by: Emilio López <emilio-0Z03zUJReD5OxF6Tv1QG9Q@public.gmane.org>
> > > 
> > > You're missing your Signed-off-by here too.  Remember, for every patch
> > > you send, your Signed-off-by must be there, regardless wether you're
> > > the author or not.
> > > 
> > > A commit log would be very much welcome too.
> > > 
> > > Now, down to the patch itself, I remember Mike saying that he would
> > > prefer adding a function in the framework instead of hardcoding
> > > it. Mike, what's your feeling on this? Would merging this seem
> > > reasonnable to you as is, or do you want to take this to the
> > > framework?
> > 
> > Hi Maxime & Emilio,
> > 
> > Maybe something like the following RFC? If it seems sufficient for this
> > case then I will post to the wider list with an eye towards merging it
> > for 3.15. I've Cc'd Dinh since this came up on the socfpga thread as
> > well.
> > 
> > Regards,
> > Mike
> > 
> > 
> > 
> > From 56fa297591be5c5e22df6d2ca43fccf285a45636 Mon Sep 17 00:00:00 2001
> > From: Mike Turquette <mturquette-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> > Date: Tue, 18 Feb 2014 20:33:50 -0800
> > Subject: [PATCH] clk: introduce clk_set_phase function & callback
> > 
> > A common operation for a clock signal generator is to shift the phase of
> > that signal. This patch introduces a new function to the clk.h API to
> > dynamically adjust the phase of a clock signal. Additionally this patch
> > introduces support for the new function in the common clock framework
> > via the .set_phase call back in struct clk_ops.
> > 
> > Signed-off-by: Mike Turquette <mturquette-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> > ---
> > This patch is totally untested. It may make your board burst into
> > flames.
> > 
> >  drivers/clk/clk.c            | 84 +++++++++++++++++++++++++++++++++++++++++---
> >  include/linux/clk-private.h  |  1 +
> >  include/linux/clk-provider.h |  5 +++
> >  include/linux/clk.h          | 29 +++++++++++++++
> >  4 files changed, 115 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> > index ea2ca9f..635170a 100644
> > --- a/drivers/clk/clk.c
> > +++ b/drivers/clk/clk.c
> > @@ -106,11 +106,11 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level)
> >       if (!c)
> >               return;
> >  
> > -     seq_printf(s, "%*s%-*s %-11d %-12d %-10lu %-11lu",
> > +     seq_printf(s, "%*s%-*s %-11d %-12d %-10lu %-11lu %-3d",
> >                  level * 3 + 1, "",
> >                  30 - level * 3, c->name,
> >                  c->enable_count, c->prepare_count, clk_get_rate(c),
> > -                clk_get_accuracy(c));
> > +                clk_get_accuracy(c), clk_get_phase(c));
> >       seq_printf(s, "\n");
> >  }
> >  
> > @@ -132,8 +132,8 @@ static int clk_summary_show(struct seq_file *s, void *data)
> >  {
> >       struct clk *c;
> >  
> > -     seq_printf(s, "   clock                        enable_cnt  prepare_cnt  rate        accuracy\n");
> > -     seq_printf(s, "---------------------------------------------------------------------------------\n");
> > +     seq_printf(s, "   clock                        enable_cnt  prepare_cnt  rate        accuracy   phase\n");
> > +     seq_printf(s, "-----------------------------------------------------------------------------------------\n");
> >  
> >       clk_prepare_lock();
> >  
> > @@ -171,6 +171,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level)
> >       seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);
> >       seq_printf(s, "\"rate\": %lu", clk_get_rate(c));
> >       seq_printf(s, "\"accuracy\": %lu", clk_get_accuracy(c));
> > +     seq_printf(s, "\"phase\": %d", clk_get_phase(c));
> >  }
> >  
> >  static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level)
> > @@ -257,6 +258,11 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
> >       if (!d)
> >               goto err_out;
> >  
> > +     d = debugfs_create_u32("clk_phase", S_IRUGO, clk->dentry,
> > +                     (u32 *)&clk->phase);
> > +     if (!d)
> > +             goto err_out;
> > +
> >       d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry,
> >                       (u32 *)&clk->flags);
> >       if (!d)
> > @@ -1766,6 +1772,76 @@ out:
> >  EXPORT_SYMBOL_GPL(clk_set_parent);
> >  
> >  /**
> > + * clk_set_phase - adjust the phase shift of a clock signal
> > + * @clk: clock signal source
> > + * @degrees: number of degrees the signal is shifted
> > + *
> > + * Shifts the phase of a clock signal by the specified degrees. Returns 0 on
> > + * success, -EERROR otherwise.
> > + *
> > + * This function makes no distiction about the input or reference signal that
> > + * we adjust the clock signal phase against. For example phase locked-loop
> > + * clock signal generators we may shift phase with respect to feedback clock
> > + * signal input, but for other cases the clock phase may be shifted with
> > + * respect to some other, unspecified signal.
> > + *
> > + * Additionally the concept of phase shift does not propagate through the clock
> > + * tree hierarchy, which sets it appart from clock rates and clock accuracy. A
> > + * parent clock phase attribute does not have an impact on the phase attribute
> > + * of a child clock.
> > + */
> > +int clk_set_phase(struct clk *clk, int degrees)
> 
> Actually, while this is what I had in mind too, we'd need a bit more
> control here. We have two phases to control (namely, input and
> sample).
> 
> Maybe passing an additional enum to tell which phase we want to
> adjust, that could easily be extended by new drivers need would fit
> our need here?

Maxime,

Do you have any documentation you could share with me on your
requirements? I'd like to better understand them so we can get the API
right.

Shifting phase with respect to an input signal makes a lot of sense to
me, but I don't really understand "sample".

Thanks,
Mike

> 
> Thanks!
> Maxime
> 
> 
> -- 
> Maxime Ripard, Free Electrons
> Embedded Linux, Kernel and Android engineering
> http://free-electrons.com

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.

^ permalink raw reply

* Re: [PATCH RFC 04/10] base: power: Add generic OF-based power domain look-up
From: Tomasz Figa @ 2014-02-23 17:07 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: linux-pm, Mark Rutland, devicetree, linux-samsung-soc,
	Russell King, Pawel Moll, Len Brown, Greg Kroah-Hartman,
	Tomasz Figa, Ian Campbell, Rafael J. Wysocki, linux-kernel,
	Rob Herring, Bartlomiej Zolnierkiewicz, Kukjin Kim, Pavel Machek,
	Kumar Gala, Stephen Warren, linux-arm-kernel
In-Reply-To: <1392828827.3445.54.camel@pizza.hi.pengutronix.de>

Hi Philipp,

On 19.02.2014 17:53, Philipp Zabel wrote:
> Am Samstag, den 11.01.2014, 20:42 +0100 schrieb Tomasz Figa:

[snip]

>> +	pd = of_genpd_get_from_provider(&pd_args);
>> +	if (IS_ERR(pd))
>> +		return PTR_ERR(pd);
>> +
>> +	dev_dbg(dev, "adding to power domain %s\n", pd->name);
>> +
>> +	while (1) {
>> +		ret = pm_genpd_add_device(pd, dev);
>
> Since pm_genpd_add_device is used here, no gpd_timing_data can be
> provided. Do you have a plan to solve this? Should the timing data be
> provided from the device tree?

Hmm, a quick grep over kernel sources for genpd_.*_add_device
gives just a single user of __pm_genpd_name_add_device(), with custom 
timing data:

> arch/arm/mach-shmobile/pm-rmobile.c-void rmobile_add_device_to_domain_td(const char *domain_name,
> arch/arm/mach-shmobile/pm-rmobile.c-                                 struct platform_device *pdev,
> arch/arm/mach-shmobile/pm-rmobile.c-                                 struct gpd_timing_data *td)
> arch/arm/mach-shmobile/pm-rmobile.c-{
> arch/arm/mach-shmobile/pm-rmobile.c-    struct device *dev = &pdev->dev;
> arch/arm/mach-shmobile/pm-rmobile.c-
> arch/arm/mach-shmobile/pm-rmobile.c:    __pm_genpd_name_add_device(domain_name, dev, td);
> arch/arm/mach-shmobile/pm-rmobile.c-    if (pm_clk_no_clocks(dev))
> arch/arm/mach-shmobile/pm-rmobile.c-            pm_clk_add(dev, NULL);
> arch/arm/mach-shmobile/pm-rmobile.c-}
> arch/arm/mach-shmobile/pm-rmobile.c-
> arch/arm/mach-shmobile/pm-rmobile.c-void rmobile_add_devices_to_domains(struct pm_domain_device data[],
> arch/arm/mach-shmobile/pm-rmobile.c-                                int size)
> arch/arm/mach-shmobile/pm-rmobile.c-{
> arch/arm/mach-shmobile/pm-rmobile.c-    struct gpd_timing_data latencies = {
> arch/arm/mach-shmobile/pm-rmobile.c-            .stop_latency_ns = DEFAULT_DEV_LATENCY_NS,
> arch/arm/mach-shmobile/pm-rmobile.c-            .start_latency_ns = DEFAULT_DEV_LATENCY_NS,
> arch/arm/mach-shmobile/pm-rmobile.c-            .save_state_latency_ns = DEFAULT_DEV_LATENCY_NS,
> arch/arm/mach-shmobile/pm-rmobile.c-            .restore_state_latency_ns = DEFAULT_DEV_LATENCY_NS,
> arch/arm/mach-shmobile/pm-rmobile.c-    };
> arch/arm/mach-shmobile/pm-rmobile.c-    int j;
> arch/arm/mach-shmobile/pm-rmobile.c-
> arch/arm/mach-shmobile/pm-rmobile.c-    for (j = 0; j < size; j++)
> arch/arm/mach-shmobile/pm-rmobile.c-            rmobile_add_device_to_domain_td(data[j].domain_name,
> arch/arm/mach-shmobile/pm-rmobile.c-                                            data[j].pdev, &latencies);
> arch/arm/mach-shmobile/pm-rmobile.c-}

Moreover the timings used there are just defaults, which makes me wonder 
if there is any reason to specify them explicitly. Even more interesting 
is the fact that genpd code can measure those latencies itself.

Do you have a particular use case for those timing data or just 
wondering? I don't think we need to implement support for them right 
away, if there is no real need to do so. The code and bindings can be 
extended later to handle them, if needed.

As for whether DT is appropriate place to define them, I'm not quite 
sure. Stop and start latencies look like hardware parameters, but state 
save and restore are likely to be driver-specific, as it depends on 
driver code how much time it takes to save and restore needed state 
(e.g. driver with register cache will not need to do any state save), if 
I understand these timing data correctly.

Best regards,
Tomasz

^ permalink raw reply

* Re: [PATCH] ahci_sunxi: Use msleep instead of mdelay
From: Tejun Heo @ 2014-02-23 16:10 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Maxime Ripard, Oliver Schinagl, linux-ide-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <1393156361-18414-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

On Sun, Feb 23, 2014 at 12:52:41PM +0100, Hans de Goede wrote:
> ahci_sunxi_phy_init is called from the probe and resume code paths, and
> sleeping is safe in both, so use msleep instead of mdelay.
> 
> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

Applied to libata/for-3.15.  Thanks.

-- 
tejun

^ permalink raw reply

* [PATCH v3 4/4] ARM: sun7i: dt: Add USB host bindings
From: Hans de Goede @ 2014-02-23 12:09 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Maxime Ripard
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Roman Byshko, Hans de Goede
In-Reply-To: <1393157352-21104-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

From: Roman Byshko <rbyshko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Add nodes for the usb-phy and ehci- and ohci-usb-host controllers.

Signed-off-by: Roman Byshko <rbyshko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/boot/dts/sun7i-a20.dtsi | 52 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index ed9b8cc..bdfa594 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -453,6 +453,38 @@
 			status = "disabled";
 		};
 
+		usbphy: phy@01c13400 {
+			#phy-cells = <1>;
+			compatible = "allwinner,sun7i-a20-usb-phy";
+			reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
+			reg-names = "phy_ctrl", "pmu1", "pmu2";
+			clocks = <&usb_clk 8>;
+			clock-names = "usb_phy";
+			resets = <&usb_clk 1>, <&usb_clk 2>;
+			reset-names = "usb1_reset", "usb2_reset";
+			status = "disabled";
+		};
+
+		ehci0: usb@01c14000 {
+			compatible = "allwinner,sun7i-a20-ehci", "generic-ehci";
+			reg = <0x01c14000 0x100>;
+			interrupts = <0 39 4>;
+			clocks = <&ahb_gates 1>;
+			phys = <&usbphy 1>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
+		ohci0: usb@01c14400 {
+			compatible = "allwinner,sun7i-a20-ohci", "generic-ohci";
+			reg = <0x01c14400 0x100>;
+			interrupts = <0 64 4>;
+			clocks = <&usb_clk 6>, <&ahb_gates 2>;
+			phys = <&usbphy 1>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
 		ahci: sata@01c18000 {
 			compatible = "allwinner,sun4i-a10-ahci";
 			reg = <0x01c18000 0x1000>;
@@ -461,6 +493,26 @@
 			status = "disabled";
 		};
 
+		ehci1: usb@01c1c000 {
+			compatible = "allwinner,sun7i-a20-ehci", "generic-ehci";
+			reg = <0x01c1c000 0x100>;
+			interrupts = <0 40 4>;
+			clocks = <&ahb_gates 3>;
+			phys = <&usbphy 2>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
+		ohci1: usb@01c1c400 {
+			compatible = "allwinner,sun7i-a20-ohci", "generic-ohci";
+			reg = <0x01c1c400 0x100>;
+			interrupts = <0 65 4>;
+			clocks = <&usb_clk 7>, <&ahb_gates 4>;
+			phys = <&usbphy 2>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
 		pio: pinctrl@01c20800 {
 			compatible = "allwinner,sun7i-a20-pinctrl";
 			reg = <0x01c20800 0x400>;
-- 
1.9.0

^ permalink raw reply related

* [PATCH v3 3/4] ARM: sun5i: dt: Add USB host bindings
From: Hans de Goede @ 2014-02-23 12:09 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Maxime Ripard
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Roman Byshko, Hans de Goede
In-Reply-To: <1393157352-21104-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

From: Roman Byshko <rbyshko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Add nodes for the usb-phy and ehci- and ohci-usb-host controllers.

Signed-off-by: Roman Byshko <rbyshko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/boot/dts/sun5i-a10s.dtsi | 32 ++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/sun5i-a13.dtsi  | 32 ++++++++++++++++++++++++++++++++
 2 files changed, 64 insertions(+)

diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
index 0565040..a5431ed 100644
--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -341,6 +341,38 @@
 			status = "disabled";
 		};
 
+		usbphy: phy@01c13400 {
+			#phy-cells = <1>;
+			compatible = "allwinner,sun5i-a13-usb-phy";
+			reg = <0x01c13400 0x10 0x01c14800 0x4>;
+			reg-names = "phy_ctrl", "pmu1";
+			clocks = <&usb_clk 8>;
+			clock-names = "usb_phy";
+			resets = <&usb_clk 1>;
+			reset-names = "usb1_reset";
+			status = "disabled";
+		};
+
+		ehci0: usb@01c14000 {
+			compatible = "allwinner,sun5i-a10s-ehci", "generic-ehci";
+			reg = <0x01c14000 0x100>;
+			interrupts = <39>;
+			clocks = <&ahb_gates 1>;
+			phys = <&usbphy 1>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
+		ohci0: usb@01c14400 {
+			compatible = "allwinner,sun5i-a10s-ohci", "generic-ohci";
+			reg = <0x01c14400 0x100>;
+			interrupts = <40>;
+			clocks = <&usb_clk 6>, <&ahb_gates 2>;
+			phys = <&usbphy 1>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
 		intc: interrupt-controller@01c20400 {
 			compatible = "allwinner,sun4i-ic";
 			reg = <0x01c20400 0x400>;
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index 785dea5..7fe9cdc 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -312,6 +312,38 @@
 			status = "disabled";
 		};
 
+		usbphy: phy@01c13400 {
+			#phy-cells = <1>;
+			compatible = "allwinner,sun5i-a13-usb-phy";
+			reg = <0x01c13400 0x10 0x01c14800 0x4>;
+			reg-names = "phy_ctrl", "pmu1";
+			clocks = <&usb_clk 8>;
+			clock-names = "usb_phy";
+			resets = <&usb_clk 1>;
+			reset-names = "usb1_reset";
+			status = "disabled";
+		};
+
+		ehci0: usb@01c14000 {
+			compatible = "allwinner,sun5i-a13-ehci", "generic-ehci";
+			reg = <0x01c14000 0x100>;
+			interrupts = <39>;
+			clocks = <&ahb_gates 1>;
+			phys = <&usbphy 1>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
+		ohci0: usb@01c14400 {
+			compatible = "allwinner,sun5i-a13-ohci", "generic-ohci";
+			reg = <0x01c14400 0x100>;
+			interrupts = <40>;
+			clocks = <&usb_clk 6>, <&ahb_gates 2>;
+			phys = <&usbphy 1>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
 		intc: interrupt-controller@01c20400 {
 			compatible = "allwinner,sun4i-ic";
 			reg = <0x01c20400 0x400>;
-- 
1.9.0

^ permalink raw reply related

* [PATCH v3 2/4] ARM: sun4i: dt: Add USB host bindings
From: Hans de Goede @ 2014-02-23 12:09 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Maxime Ripard
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Roman Byshko, Hans de Goede
In-Reply-To: <1393157352-21104-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

From: Roman Byshko <rbyshko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Add nodes for the usb-phy and ehci- and ohci-usb-host controllers.

Signed-off-by: Roman Byshko <rbyshko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/boot/dts/sun4i-a10.dtsi | 52 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index 7550c4e..80bbdeb 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -391,6 +391,38 @@
 			status = "disabled";
 		};
 
+		usbphy: phy@01c13400 {
+			#phy-cells = <1>;
+			compatible = "allwinner,sun4i-a10-usb-phy";
+			reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
+			reg-names = "phy_ctrl", "pmu1", "pmu2";
+			clocks = <&usb_clk 8>;
+			clock-names = "usb_phy";
+			resets = <&usb_clk 1>, <&usb_clk 2>;
+			reset-names = "usb1_reset", "usb2_reset";
+			status = "disabled";
+		};
+
+		ehci0: usb@01c14000 {
+			compatible = "allwinner,sun4i-a10-ehci", "generic-ehci";
+			reg = <0x01c14000 0x100>;
+			interrupts = <39>;
+			clocks = <&ahb_gates 1>;
+			phys = <&usbphy 1>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
+		ohci0: usb@01c14400 {
+			compatible = "allwinner,sun4i-a10-ohci", "generic-ohci";
+			reg = <0x01c14400 0x100>;
+			interrupts = <64>;
+			clocks = <&usb_clk 6>, <&ahb_gates 2>;
+			phys = <&usbphy 1>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
 		ahci: sata@01c18000 {
 			compatible = "allwinner,sun4i-a10-ahci";
 			reg = <0x01c18000 0x1000>;
@@ -399,6 +431,26 @@
 			status = "disabled";
 		};
 
+		ehci1: usb@01c1c000 {
+			compatible = "allwinner,sun4i-a10-ehci", "generic-ehci";
+			reg = <0x01c1c000 0x100>;
+			interrupts = <40>;
+			clocks = <&ahb_gates 3>;
+			phys = <&usbphy 2>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
+		ohci1: usb@01c1c400 {
+			compatible = "allwinner,sun4i-a10-ohci", "generic-ohci";
+			reg = <0x01c1c400 0x100>;
+			interrupts = <65>;
+			clocks = <&usb_clk 7>, <&ahb_gates 4>;
+			phys = <&usbphy 2>;
+			phy-names = "usb";
+			status = "disabled";
+		};
+
 		intc: interrupt-controller@01c20400 {
 			compatible = "allwinner,sun4i-ic";
 			reg = <0x01c20400 0x400>;
-- 
1.9.0

^ permalink raw reply related

* [PATCH v3 1/4] ARM: sunxi: Add driver for sunxi usb phy
From: Hans de Goede @ 2014-02-23 12:09 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Maxime Ripard
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Hans de Goede
In-Reply-To: <1393157352-21104-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

The Allwinner A1x / A2x SoCs have 2 or 3 usb phys which are all accessed
through a single set of registers. Besides this there are also some other
phy related bits which need poking, which are per phy, but shared between the
ohci and ehci controllers, so these are also controlled from this new phy
driver.

Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  26 ++
 drivers/phy/Kconfig                                |  11 +
 drivers/phy/Makefile                               |   1 +
 drivers/phy/phy-sun4i-usb.c                        | 329 +++++++++++++++++++++
 4 files changed, 367 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
 create mode 100644 drivers/phy/phy-sun4i-usb.c

diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
new file mode 100644
index 0000000..a82361b
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
@@ -0,0 +1,26 @@
+Allwinner sun4i USB PHY
+-----------------------
+
+Required properties:
+- compatible : should be one of "allwinner,sun4i-a10-usb-phy",
+  "allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy"
+- reg : a list of offset + length pairs
+- reg-names : "phy_ctrl", "pmu1" and for sun4i or sun7i "pmu2"
+- #phy-cells : from the generic phy bindings, must be 1
+- clocks : phandle + clock specifier for the phy clock
+- clock-names : "usb_phy"
+- resets : a list of phandle + reset specifier pairs
+- reset-names : "usb0_reset", "usb1_reset" and for sun4i or sun7i "usb2_reset"
+
+Example:
+	usbphy: phy@0x01c13400 {
+		#phy-cells = <1>;
+		compatible = "allwinner,sun4i-a10-usb-phy";
+		/* phy base regs, phy1 pmu reg, phy2 pmu reg */
+		reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
+		reg-names = "phy_ctrl", "pmu1", "pmu2";
+		clocks = <&usb_clk 8>;
+		clock-names = "usb_phy";
+		resets = <&usb_clk 1>, <&usb_clk 2>;
+		reset-names = "usb1_reset", "usb2_reset";
+	};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 4ef8755..6e336b4 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -64,4 +64,15 @@ config BCM_KONA_USB2_PHY
 	help
 	  Enable this to support the Broadcom Kona USB 2.0 PHY.
 
+config PHY_SUN4I_USB
+	tristate "Allwinner sunxi SoC USB PHY driver"
+	depends on ARCH_SUNXI
+	select GENERIC_PHY
+	help
+	  Enable this to support the transceiver that is part of Allwinner
+	  sunxi SoCs.
+
+	  This driver controls the entire USB PHY block, both the USB OTG
+	  parts, as well as the 2 regular USB 2 host PHYs.
+
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index b57c253..9d4f8bb 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)	+= phy-exynos-mipi-video.o
 obj-$(CONFIG_PHY_MVEBU_SATA)		+= phy-mvebu-sata.o
 obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
 obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
+obj-$(CONFIG_PHY_SUN4I_USB)		+= phy-sun4i-usb.o
diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
new file mode 100644
index 0000000..31c4611
--- /dev/null
+++ b/drivers/phy/phy-sun4i-usb.c
@@ -0,0 +1,329 @@
+/*
+ * Allwinner sun4i USB phy driver
+ *
+ * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
+ *
+ * Based on code from
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ *
+ * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+
+#define REG_ISCR			0x00
+#define REG_PHYCTL			0x04
+#define REG_PHYBIST			0x08
+#define REG_PHYTUNE			0x0c
+
+#define SUNXI_AHB_ICHR8_EN		BIT(10)
+#define SUNXI_AHB_INCR4_BURST_EN	BIT(9)
+#define SUNXI_AHB_INCRX_ALIGN_EN	BIT(8)
+#define SUNXI_ULPI_BYPASS_EN		BIT(0)
+
+/* Common Control Bits for Both PHYs */
+#define PHY_PLL_BW			0x03
+#define PHY_RES45_CAL_EN		0x0c
+
+/* Private Control Bits for Each PHY */
+#define PHY_TX_AMPLITUDE_TUNE		0x20
+#define PHY_TX_SLEWRATE_TUNE		0x22
+#define PHY_VBUSVALID_TH_SEL		0x25
+#define PHY_PULLUP_RES_SEL		0x27
+#define PHY_OTG_FUNC_EN			0x28
+#define PHY_VBUS_DET_EN			0x29
+#define PHY_DISCON_TH_SEL		0x2a
+
+#define MAX_PHYS			3
+
+struct sun4i_usb_phy_data {
+	struct clk *clk;
+	void __iomem *base;
+	struct mutex mutex;
+	int num_phys;
+	u32 disc_thresh;
+	struct sun4i_usb_phy {
+		struct phy *phy;
+		void __iomem *pmu;
+		struct regulator *vbus;
+		struct reset_control *reset;
+		int index;
+	} phys[MAX_PHYS];
+};
+
+#define to_sun4i_usb_phy_data(phy) \
+	container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
+
+static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
+				int len)
+{
+	struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
+	u32 temp, usbc_bit = BIT(phy->index * 2);
+	int i;
+
+	mutex_lock(&phy_data->mutex);
+
+	for (i = 0; i < len; i++) {
+		temp = readl(phy_data->base + REG_PHYCTL);
+
+		/* clear the address portion */
+		temp &= ~(0xff << 8);
+
+		/* set the address */
+		temp |= ((addr + i) << 8);
+		writel(temp, phy_data->base + REG_PHYCTL);
+
+		/* set the data bit and clear usbc bit*/
+		temp = readb(phy_data->base + REG_PHYCTL);
+		if (data & 0x1)
+			temp |= BIT(7);
+		else
+			temp &= ~BIT(7);
+		temp &= ~usbc_bit;
+		writeb(temp, phy_data->base + REG_PHYCTL);
+
+		/* pulse usbc_bit */
+		temp = readb(phy_data->base + REG_PHYCTL);
+		temp |= usbc_bit;
+		writeb(temp, phy_data->base + REG_PHYCTL);
+
+		temp = readb(phy_data->base + REG_PHYCTL);
+		temp &= ~usbc_bit;
+		writeb(temp, phy_data->base + REG_PHYCTL);
+
+		data >>= 1;
+	}
+	mutex_unlock(&phy_data->mutex);
+}
+
+static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
+{
+	u32 bits, reg_value;
+
+	if (!phy->pmu)
+		return;
+
+	bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
+		SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
+
+	reg_value = readl(phy->pmu);
+
+	if (enable)
+		reg_value |= bits;
+	else
+		reg_value &= ~bits;
+
+	writel(reg_value, phy->pmu);
+}
+
+static int sun4i_usb_phy_init(struct phy *_phy)
+{
+	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+	int ret;
+
+	ret = clk_prepare_enable(data->clk);
+	if (ret)
+		return ret;
+
+	ret = reset_control_deassert(phy->reset);
+	if (ret) {
+		clk_disable_unprepare(data->clk);
+		return ret;
+	}
+
+	/* Adjust PHY's magnitude and rate */
+	sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
+
+	/* Disconnect threshold adjustment */
+	sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
+
+	sun4i_usb_phy_passby(phy, 1);
+
+	return 0;
+}
+
+static int sun4i_usb_phy_exit(struct phy *_phy)
+{
+	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+
+	sun4i_usb_phy_passby(phy, 0);
+	reset_control_assert(phy->reset);
+	clk_disable_unprepare(data->clk);
+
+	return 0;
+}
+
+static int sun4i_usb_phy_power_on(struct phy *_phy)
+{
+	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+	int ret = 0;
+
+	if (phy->vbus)
+		ret = regulator_enable(phy->vbus);
+
+	return ret;
+}
+
+static int sun4i_usb_phy_power_off(struct phy *_phy)
+{
+	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+
+	if (phy->vbus)
+		regulator_disable(phy->vbus);
+
+	return 0;
+}
+
+static struct phy_ops sun4i_usb_phy_ops = {
+	.init		= sun4i_usb_phy_init,
+	.exit		= sun4i_usb_phy_exit,
+	.power_on	= sun4i_usb_phy_power_on,
+	.power_off	= sun4i_usb_phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static struct phy *sun4i_usb_phy_xlate(struct device *dev,
+					struct of_phandle_args *args)
+{
+	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
+
+	if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
+		return ERR_PTR(-ENODEV);
+
+	return data->phys[args->args[0]].phy;
+}
+
+static int sun4i_usb_phy_probe(struct platform_device *pdev)
+{
+	struct sun4i_usb_phy_data *data;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	void __iomem *pmu = NULL;
+	struct phy_provider *phy_provider;
+	struct reset_control *reset;
+	struct regulator *vbus;
+	struct resource *res;
+	struct phy *phy;
+	char name[16];
+	int i;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	mutex_init(&data->mutex);
+
+	if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
+		data->num_phys = 2;
+	else
+		data->num_phys = 3;
+
+	if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy"))
+		data->disc_thresh = 3;
+	else
+		data->disc_thresh = 2;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
+	data->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(data->base))
+		return PTR_ERR(data->base);
+
+	data->clk = devm_clk_get(dev, "usb_phy");
+	if (IS_ERR(data->clk)) {
+		dev_err(dev, "could not get usb_phy clock\n");
+		return PTR_ERR(data->clk);
+	}
+
+	/* Skip 0, 0 is the phy for otg which is not yet supported. */
+	for (i = 1; i < data->num_phys; i++) {
+		snprintf(name, sizeof(name), "usb%d_vbus", i);
+		vbus = devm_regulator_get_optional(dev, name);
+		if (IS_ERR(vbus)) {
+			if (PTR_ERR(vbus) == -EPROBE_DEFER)
+				return -EPROBE_DEFER;
+			vbus = NULL;
+		}
+
+		snprintf(name, sizeof(name), "usb%d_reset", i);
+		reset = devm_reset_control_get(dev, name);
+		if (IS_ERR(reset)) {
+			dev_err(dev, "failed to get reset %s\n", name);
+			return PTR_ERR(phy);
+		}
+
+		if (i) { /* No pmu for usbc0 */
+			snprintf(name, sizeof(name), "pmu%d", i);
+			res = platform_get_resource_byname(pdev,
+							IORESOURCE_MEM, name);
+			pmu = devm_ioremap_resource(dev, res);
+			if (IS_ERR(pmu))
+				return PTR_ERR(pmu);
+		}
+
+		phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
+		if (IS_ERR(phy)) {
+			dev_err(dev, "failed to create PHY %d\n", i);
+			return PTR_ERR(phy);
+		}
+
+		data->phys[i].phy = phy;
+		data->phys[i].pmu = pmu;
+		data->phys[i].vbus = vbus;
+		data->phys[i].reset = reset;
+		data->phys[i].index = i;
+		phy_set_drvdata(phy, &data->phys[i]);
+	}
+
+	dev_set_drvdata(dev, data);
+	phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
+	if (IS_ERR(phy_provider))
+		return PTR_ERR(phy_provider);
+
+	return 0;
+}
+
+static const struct of_device_id sun4i_usb_phy_of_match[] = {
+	{ .compatible = "allwinner,sun4i-a10-usb-phy" },
+	{ .compatible = "allwinner,sun5i-a13-usb-phy" },
+	{ .compatible = "allwinner,sun7i-a20-usb-phy" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
+
+static struct platform_driver sun4i_usb_phy_driver = {
+	.probe	= sun4i_usb_phy_probe,
+	.driver = {
+		.of_match_table	= sun4i_usb_phy_of_match,
+		.name  = "sun4i-usb-phy",
+		.owner = THIS_MODULE,
+	}
+};
+module_platform_driver(sun4i_usb_phy_driver);
+
+MODULE_DESCRIPTION("Allwinner sun4i USB phy driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.0

^ permalink raw reply related

* [PATCH v3 0/4] ARM: sunxi: Add driver for sunxi usb phy + usb dts bindings
From: Hans de Goede @ 2014-02-23 12:09 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Maxime Ripard
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi Kishon, Maxime,

Here is v3 of my sunxi-usb-phy driver it addresses all review remarks made
in response to v2, and as such this should be the final version, changes:

-Fix check for wrong variable in error handling path pointed out by wens
-Switch to using reg-names to differentiate between the different register
 ranges needed

Kishon, can you please queue up the 1st patch of this series for 3.15 ?

Maxime, can you please add patches 2-4 to your dt-for-3.15 branch? Note these
are only the dtsi bits.

I'm holding back all the board .dts patches until it is clear how we want to
deal with the ahci + usb regulator bits. Specifically I'm waiting for an answer
to this question:

"I hope this helps explain my reasoning, as said I'm fine with
either way, if you want to change over to a single file +
explicit enabling, let me know and I'll respin the ahci dts
patches.  Note I'm going on vacation for a week starting Monday,
so you likely won't get a new version until next weekend."

In my last reply to "[PATCH v6 17/18] ARM: sun4i: dt: Add ahci / sata support"

I've a feeling you (Maxime) want me to rework things in to a single dtsi
for all common regulators + explicit enabling, which works for me, but before
doing the necessary refactoring I would like to hear that this is what you
want from you.

Regards,

Hans


p.s.

I'm going on vacation for 5 days leaving tomorrow, so I won't send the
(refactored) board .dts file patches until next weekend.

^ permalink raw reply

* [PATCH] ahci_sunxi: Use msleep instead of mdelay
From: Hans de Goede @ 2014-02-23 11:52 UTC (permalink / raw)
  To: Tejun Heo
  Cc: Maxime Ripard, Oliver Schinagl, linux-ide-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Hans de Goede

ahci_sunxi_phy_init is called from the probe and resume code paths, and
sleeping is safe in both, so use msleep instead of mdelay.

Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 drivers/ata/ahci_sunxi.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c
index 001f7dfc..d1bf3f7 100644
--- a/drivers/ata/ahci_sunxi.c
+++ b/drivers/ata/ahci_sunxi.c
@@ -90,7 +90,7 @@ static int ahci_sunxi_phy_init(struct device *dev, void __iomem *reg_base)
 
 	/* This magic is from the original code */
 	writel(0, reg_base + AHCI_RWCR);
-	mdelay(5);
+	msleep(5);
 
 	sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(19));
 	sunxi_clrsetbits(reg_base + AHCI_PHYCS0R,
@@ -105,7 +105,7 @@ static int ahci_sunxi_phy_init(struct device *dev, void __iomem *reg_base)
 			 (0x7 << 20), (0x3 << 20));
 	sunxi_clrsetbits(reg_base + AHCI_PHYCS2R,
 			 (0x1f << 5), (0x19 << 5));
-	mdelay(5);
+	msleep(5);
 
 	sunxi_setbits(reg_base + AHCI_PHYCS0R, (0x1 << 19));
 
@@ -137,7 +137,7 @@ static int ahci_sunxi_phy_init(struct device *dev, void __iomem *reg_base)
 		udelay(1);
 	} while (1);
 
-	mdelay(15);
+	msleep(15);
 
 	writel(0x7, reg_base + AHCI_RWCR);
 
-- 
1.9.0

^ permalink raw reply related

* Re: [RFCv1 4/4] mfd: twl4030-madc: Move driver to drivers/iio/adc
From: Jonathan Cameron @ 2014-02-23 11:02 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Marek Belisko, Lee Jones, Samuel Ortiz, Lars-Peter Clausen,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Grant Likely, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20140223003504.GA28154-SfvFxonMDyemK9LvCR3Hrw@public.gmane.org>

On 23/02/14 00:35, Sebastian Reichel wrote:
> On Sat, Feb 22, 2014 at 12:47:03PM +0000, Jonathan Cameron wrote:
>> On 14/02/14 18:46, Sebastian Reichel wrote:
>>> This is a driver for an A/D converter, which belongs into
>>> drivers/iio/adc.
>>>
>>> Signed-off-by: Sebastian Reichel <sre-8fiUuRrzOP0dnm+yROfE0A@public.gmane.org>
>>
>> Being inherently lazy I'm going to review this patch as it gives the complete
>> driver rather than taking on the conversion patch directly!
>
> OK.
>
>> It's a long shot, but are there any docs freely available for this part?
>
> There is documentation on Texas Instrument's website for TPS65950,
> which is supposed to be very similar to the TWL4030 and contains a
> section about the MADC module:
>
> http://www.ti.com/lit/gpn/tps65950
I had a look at that and to put it lightly the docs aren't particularly
informative. Ah well, such is life.
>
>> Obviously that may well mean that some of my comments apply to the driver
>> in general rather than the changes you've made.  Please feel free to
>> pick and choose!
>
> Having skipped over your comments I guess 70% are about the driver
> in general, but I can add another patch to the patchset, which fixes
> the driver style.
That would be great thanks!
>
>> I'd like ideally to see a little more generic tidying up in this driver.
>> As you'll notice inline I get irritated at having lots and lots of error
>> messages.  Still that's personal preference but I felt a lot more justified
>> in moaning after finding one that was incorrect ;)
>>
>> There is also a bit of functionality in here that I'm not sure is ever
>> used (the request callback functions).  It complicates the code so if no
>> using it I'd be tempted to drop it.
>
> I think the optimal workflow is:
>
> 1. convert madc driver to IIO
> 2. convert twl4030-madc-battery driver to IIO API
> 3. convert rx51-battery to IIO API
> 4. convert twl4030-madc-hwmon to IIO API / deprecate it
> 5. remove old in-kernel ABI from madc
> 6. cleanup/simplify madc
>
> I guess its much simpler to do the driver cleanup/simplification
> once we can get rid of the old API.

Perhaps.  I guess it depends on your planned schedule.  If you are intending
to plough through the above in the near future then fair enough.  If the
chances are it'll take a while, I'd be inclined to fix some of the more
glarringly hideous bits now (such as error code reporting) and then
visit your list above over time.

Note that to my mind it is converted drivers like this that sneak some
nasty code into an a subsystem I look after.  We have a lot of 'interesting'
code out in staging still, but for new submissions we are being increasingly
fussy about little bits.  So I hope I don't come across as being too mean
about this driver.  I'm glad you are putting in the work to convert
it over.  There are a quite a few similar drivers sculking through the tree
so it's nice that someone has gotten started on bringing one over to IIO.


>
>> It's nice in a way that the driver before your conversion provided only
>> a very limited and in kernel interface given we don't have to hang on to
>> any old ABI elements.
>>
>>> ---
>>>   drivers/iio/adc/Kconfig        |  10 +
>>>   drivers/iio/adc/Makefile       |   1 +
>>>   drivers/iio/adc/twl4030-madc.c | 922 +++++++++++++++++++++++++++++++++++++++++
>>>   drivers/mfd/Kconfig            |  10 -
>>>   drivers/mfd/Makefile           |   1 -
>>>   drivers/mfd/twl4030-madc.c     | 922 -----------------------------------------
>>>   6 files changed, 933 insertions(+), 933 deletions(-)
>>>   create mode 100644 drivers/iio/adc/twl4030-madc.c
>>>   delete mode 100644 drivers/mfd/twl4030-madc.c
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index 2209f28..427f75c 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -183,6 +183,16 @@ config TI_AM335X_ADC
>>>   	  Say yes here to build support for Texas Instruments ADC
>>>   	  driver which is also a MFD client.
>>>
>>> +config TWL4030_MADC
>>> +	tristate "TWL4030 MADC (Monitoring A/D Converter)"
>>> +	depends on TWL4030_CORE
>>> +	help
>>> +	This driver provides support for Triton TWL4030-MADC. The
>>> +	driver supports both RT and SW conversion methods.
>>> +
>>> +	This driver can also be built as a module. If so, the module will be
>>> +	called twl4030-madc.
>>> +
>>>   config TWL6030_GPADC
>>>   	tristate "TWL6030 GPADC (General Purpose A/D Converter) Support"
>>>   	depends on TWL4030_CORE
>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>> index ba9a10a..9acf2df 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -20,5 +20,6 @@ obj-$(CONFIG_MCP3422) += mcp3422.o
>>>   obj-$(CONFIG_NAU7802) += nau7802.o
>>>   obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>>>   obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
>>> +obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
>>>   obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
>>>   obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
>>> diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
>>> new file mode 100644
>>> index 0000000..4da61c4
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/twl4030-madc.c
>>> @@ -0,0 +1,922 @@
>>> +/*
>>> + *
>>> + * TWL4030 MADC module driver-This driver monitors the real time
>>> + * conversion of analog signals like battery temperature,
>>> + * battery type, battery level etc.
>>> + *
>>> + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
>>> + * J Keerthy <j-keerthy-l0cyMroinI0@public.gmane.org>
>>> + *
>>> + * Based on twl4030-madc.c
>>> + * Copyright (C) 2008 Nokia Corporation
>>> + * Mikko Ylinen <mikko.k.ylinen-xNZwKgViW5gAvxtiuMwx3w@public.gmane.org>
>>> + *
>>> + * Amit Kucheria <amit.kucheria-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.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.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>> + * General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
>>> + * 02110-1301 USA
>>> + *
>>> + */
>>> +
>>> +#include <linux/init.h>
>>> +#include <linux/device.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/i2c/twl.h>
>>> +#include <linux/i2c/twl4030-madc.h>
>>> +#include <linux/module.h>
>>> +#include <linux/stddef.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/bitops.h>
>>> +#include <linux/jiffies.h>
>>> +#include <linux/types.h>
>>> +#include <linux/gfp.h>
>>> +#include <linux/err.h>
>>> +
>>> +#include <linux/iio/iio.h>
>> Why include machine.h or driver.h. You aren't currently using the
>> mapping coding anyway that these provide.
>
> My bad. I copied that over from another driver without checking if
> it is actually needed.
>
>>> +#include <linux/iio/machine.h>
>>> +#include <linux/iio/driver.h>
>>> +
>>> +/*
>>> + * struct twl4030_madc_data - a container for madc info
>>> + * @dev - pointer to device structure for madc
>>> + * @lock - mutex protecting this data structure
>>> + * @requests - Array of request struct corresponding to SW1, SW2 and RT
>>> + * @imr - Interrupt mask register of MADC
>>> + * @isr - Interrupt status register of MADC
>>> + */
>>> +struct twl4030_madc_data {
>>> +	struct device *dev;
>>> +	struct mutex lock;	/* mutex protecting this data structure */
>>> +	struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
>>> +	bool use_second_irq;
>>
>> These are 32 bit signed values?
>
> These were already defined as int in the original driver. I will
> change these to u8 in an additional patch.
>
>>> +	int imr;
>>> +	int isr;
>>> +};
>>> +
>>> +static int twl4030_madc_read(struct iio_dev *iio_dev,
>>> +			     const struct iio_chan_spec *chan,
>>> +			     int *val, int *val2, long mask)
>>> +{
>>> +	struct twl4030_madc_data *madc = iio_priv(iio_dev);
>>> +	struct twl4030_madc_request req;
>>> +	int channel = chan->channel;
>>> +	int ret;
>>> +
>>> +	req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1;
>>> +
>>> +	req.channels = BIT(channel);
>>> +	req.active = 0;
>>> +	req.func_cb = NULL;
>> req.raw = !(mask & IIO_CHAN_INFO_PROCESSED);
>> req.do_avg = !!(mask & IIO_CHAN_INFO_AVERAGE_RAW);
>
> Right.
>
>>> +	req.raw = (mask & IIO_CHAN_INFO_PROCESSED) ? false : true;
>>> +	req.do_avg = (mask & IIO_CHAN_INFO_AVERAGE_RAW) ? true : false;
>>> +
>>> +	ret = twl4030_madc_conversion(&req);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	*val = req.rbuf[channel];
>>> +
>>> +	return IIO_VAL_INT;
>>> +}
>>> +
>>> +static const struct iio_info twl4030_madc_iio_info = {
>>> +	.read_raw = &twl4030_madc_read,
>>> +	.driver_module = THIS_MODULE,
>>> +};
>>> +
>> Please give this a prefixed name.  Chances of ADC_CHANNEL
>> turning up in an IIO header is a little too high to do this
>> without!  e.g. #define TWL4030_ADC_CHANNEL
>
> Probably drivers/iio/adc/exynos_adc.c should also be fixed.
Sounds like it.  Patches always welcome ;)  Like any other reviewer
I'm not known for absolute consistency.
>
>>> +#define ADC_CHANNEL(_channel, _type, _name, _mask) {	\
>>> +	.type = _type,					\
>> I don't think you use scan_type anywhere so don't bother specifying it.
>> here.
>
> ok.
>
>>> +	.scan_type = IIO_ST('u', 10, 16, 0),		\
>>> +	.channel = _channel,				\
>>> +	.info_mask_separate = _mask,			\
>>> +	.datasheet_name = _name,			\
>>> +	.indexed = 1,					\
>>> +}
>>> +
>>
>> Is the average have an adjustable number of samples?
>
> The TWL supports reading a value directly (1 sample) or reading
> the average of 4 samples.
>
>> If not I'd be tempted to use the more common option of IIO_CHAN_INFO_RAW
>> rather than the average version (tends only to be used in fairly obscure
>> cases where both a raw access and an averaged one are available).
>
> currently some drivers use the averaged read and some use the direct
> read.
>

In that case I'd expose both the 1 sample and averaged versions via the
IIO interfaces.  If you want to pick 1 then do IIO_CHAN_INFO_RAW not the
averaged version.
  
>>> +static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
>>> +	ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(1, IIO_TEMP, "ADCIN1", BIT(IIO_CHAN_INFO_PROCESSED) |
>>> +					   BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10", BIT(IIO_CHAN_INFO_PROCESSED) |
>>> +						BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +	ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
>>> +};
>>> +
>> Why the artificial limitation of one of these devices?  I guess this is to
>> allow the exported function to work without needing to be associated with
>> any particular device...  Hmm.
>
> Artificial limitation? The madc is a ADC, which has some special
> functions defined for some pins in the datasheet:
>
> ADC0 = Battery Voltage
> ADC1 = Battery Temperature
> ADC10 = Battery Current
>
> Nokia misused some of those pins on the Nokia N900, though. For
> example they have their own conversion tables for the temperature,
> which is not compatible with the generic one for the madc module.

*laughs*.  If someone documents a pin as having a particular purpose
but it isn't wired inside a package. Someone will always think they know
better.  Basic principal is to never limit ourselves to one instance of
a chip because the chances of someone deciding to do something interesting
with multiple chips is way to high.

>
> I planned to convert the rx51-battery driver to simply read the raw
> values. All other users can use the processed information.
Makes sense.
>
>>> +static struct twl4030_madc_data *twl4030_madc;
>>> +
>>> +struct twl4030_prescale_divider_ratios {
>>> +	s16 numerator;
>>> +	s16 denominator;
>>> +};
>>> +
>>> +static const struct twl4030_prescale_divider_ratios
>>> +twl4030_divider_ratios[16] = {
>>> +	{1, 1},		/* CHANNEL 0 No Prescaler */
>>> +	{1, 1},		/* CHANNEL 1 No Prescaler */
>>> +	{6, 10},	/* CHANNEL 2 */
>>> +	{6, 10},	/* CHANNEL 3 */
>>> +	{6, 10},	/* CHANNEL 4 */
>>> +	{6, 10},	/* CHANNEL 5 */
>>> +	{6, 10},	/* CHANNEL 6 */
>>> +	{6, 10},	/* CHANNEL 7 */
>>> +	{3, 14},	/* CHANNEL 8 */
>>> +	{1, 3},		/* CHANNEL 9 */
>>> +	{1, 1},		/* CHANNEL 10 No Prescaler */
>>> +	{15, 100},	/* CHANNEL 11 */
>>> +	{1, 4},		/* CHANNEL 12 */
>>> +	{1, 1},		/* CHANNEL 13 Reserved channels */
>>> +	{1, 1},		/* CHANNEL 14 Reseved channels */
>>> +	{5, 11},	/* CHANNEL 15 */
>>> +};
>>> +
>>> +
>>> +/*
>>> + * Conversion table from -3 to 55 degree Celcius
>>> + */
>>> +static int therm_tbl[] = {
>>> +30800,	29500,	28300,	27100,
>>> +26000,	24900,	23900,	22900,	22000,	21100,	20300,	19400,	18700,	17900,
>>> +17200,	16500,	15900,	15300,	14700,	14100,	13600,	13100,	12600,	12100,
>>> +11600,	11200,	10800,	10400,	10000,	9630,	9280,	8950,	8620,	8310,
>>> +8020,	7730,	7460,	7200,	6950,	6710,	6470,	6250,	6040,	5830,
>>> +5640,	5450,	5260,	5090,	4920,	4760,	4600,	4450,	4310,	4170,
>>> +4040,	3910,	3790,	3670,	3550
>>> +};
>>> +
>>> +/*
>>> + * Structure containing the registers
>>> + * of different conversion methods supported by MADC.
>>> + * Hardware or RT real time conversion request initiated by external host
>>> + * processor for RT Signal conversions.
>>> + * External host processors can also request for non RT conversions
>>> + * SW1 and SW2 software conversions also called asynchronous or GPC request.
>>> + */
>>> +static
>>> +const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
>>> +	[TWL4030_MADC_RT] = {
>>> +			     .sel = TWL4030_MADC_RTSELECT_LSB,
>>> +			     .avg = TWL4030_MADC_RTAVERAGE_LSB,
>>> +			     .rbase = TWL4030_MADC_RTCH0_LSB,
>>> +			     },
>>> +	[TWL4030_MADC_SW1] = {
>>> +			      .sel = TWL4030_MADC_SW1SELECT_LSB,
>>> +			      .avg = TWL4030_MADC_SW1AVERAGE_LSB,
>>> +			      .rbase = TWL4030_MADC_GPCH0_LSB,
>>> +			      .ctrl = TWL4030_MADC_CTRL_SW1,
>>> +			      },
>>> +	[TWL4030_MADC_SW2] = {
>>> +			      .sel = TWL4030_MADC_SW2SELECT_LSB,
>>> +			      .avg = TWL4030_MADC_SW2AVERAGE_LSB,
>>> +			      .rbase = TWL4030_MADC_GPCH0_LSB,
>>> +			      .ctrl = TWL4030_MADC_CTRL_SW2,
>>> +			      },
>>> +};
>>> +
>>> +/*
>>> + * Function to read a particular channel value.
>>> + * @madc - pointer to struct twl4030_madc_data
>>> + * @reg - lsb of ADC Channel
>>> + * If the i2c read fails it returns an error else returns 0.
>>> + */
>>> +static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
>>> +{
>>> +	u8 msb, lsb;
>>> +	int ret;
>>> +	/*
>>> +	 * For each ADC channel, we have MSB and LSB register pair. MSB address
>>> +	 * is always LSB address+1. reg parameter is the address of LSB register
>>> +	 */
>>> +	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1);
>>> +	if (ret) {
>>> +		dev_err(madc->dev, "unable to read MSB register 0x%X\n",
>>> +			reg + 1);
>>> +		return ret;
>>> +	}
>>> +	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg);
>>> +	if (ret) {
>>> +		dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg);
>>> +		return ret;
>>> +	}
>>> +
>>> +	return (int)(((msb << 8) | lsb) >> 6);
>>> +}
>>> +
>>> +/*
>>> + * Return battery temperature
>>> + * Or < 0 on failure.
>>> + */
>>> +static int twl4030battery_temperature(int raw_volt)
>>> +{
>>> +	u8 val;
>>> +	int temp, curr, volt, res, ret;
>>> +
>>> +	volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R;
>>> +	/* Getting and calculating the supply current in micro ampers */
>>> +	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
>>> +		REG_BCICTL2);
>>> +	if (ret < 0)
>>> +		return ret;
>> blank line here and after similar error cases makes it a little easier to
>> read.
>>
>>> +	curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
>>> +	/* Getting and calculating the thermistor resistance in ohms */
>>> +	res = volt * 1000 / curr;
>>> +	/* calculating temperature */
>>> +	for (temp = 58; temp >= 0; temp--) {
>>> +		int actual = therm_tbl[temp];
>>> +
>> No blank line here.
>
> I added the newline to the style fix patch, since these lines were
> not touched by me before.
>
>>> +		if ((actual - res) >= 0)
>>> +			break;
>>> +	}
>>> +
>>> +	return temp + 1;
>>> +}
>>> +
>>> +static int twl4030battery_current(int raw_volt)
>>> +{
>>> +	int ret;
>>> +	u8 val;
>>> +
>>> +	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
>>> +		TWL4030_BCI_BCICTL1);
>>> +	if (ret)
>>> +		return ret;
>>> +	if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */
>>> +		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1;
>>> +	else /* slope of 0.88 mV/mA */
>>> +		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
>>> +}
>> blank line here please.
>
> Also added to the style fix patch.
>
>> Various comments on this in the next function:
>> This would be much simpler if any error immediately resulted in an
>> exit with error code.  If it's gone wrong enough to issue a dev_err
>> then don't try to muddle through.  Given there is nothing to clear
>> up in here, just error out directly on the first problem.
>
> I guess this should be fixed once the old API is removed.
>
>>> +/*
>>> + * Function to read channel values
>>> + * @madc - pointer to twl4030_madc_data struct
>>> + * @reg_base - Base address of the first channel
>>> + * @Channels - 16 bit bitmap. If the bit is set, channel value is read
>>> + * @buf - The channel values are stored here. if read fails error
>>> + * @raw - Return raw values without conversion
>>> + * value is stored
>>> + * Returns the number of successfully read channels.
>>> + */
>>> +static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
>>> +				      u8 reg_base, unsigned
>>> +				      long channels, int *buf,
>>> +				      bool raw)
>>> +{
>>> +	int count = 0, count_req = 0, i;
>>> +	u8 reg;
>>> +
>>> +	for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
>> Skip the temporary for reg.  Yes, it gets used twice, but easier to read
>> inline.
>
> Also fixed in the style patch.
>
>>> +		reg = reg_base + 2 * i;
>>> +		buf[i] = twl4030_madc_channel_raw_read(madc, reg);
>>> +		if (buf[i] < 0) {
>>> +			dev_err(madc->dev,
>>> +				"Unable to read register 0x%X\n", reg);
>>> +			count_req++;
>>> +			continue;
>>> +		}
>> I'd prefer this as
>> if (raw) {
>>    count ++;
>> } else {
>>    switch (i) {
>> ..
>> }
>
> That increases the code indention by another level. I don't think
> that's a good idea.
For clarity I'd do it anyway, but if the count is brought out of the
statements then the flow will be more obvious anyway.
>
>> Also near as I can tell count gets incremented unless there is an error
>> anyway. So just increment it outside and allow this function to return the
>> error code.
>> Returning error codes would change the existing API. I would prefer
> to do this change after the old API is removed.
This function isn't exported directly and I suspect a quick look would show
no one relying on the successfully reading some subset of drivers.  It might
make sense to eat the error at the exported interface and return a length
of 0.

>
>>> +		if (raw) {
>>> +			count++;
>>> +			continue;
>>> +		}
>>> +		switch (i) {
>> These cases could do with a suitable #define to give them a name.
>
> TWL4030_MADC_ADCIN10 is already in use for (1 << 10). I guess this
> can be simplified more easily once the old API is removed.
>
>>> +		case 10:
>>> +			buf[i] = twl4030battery_current(buf[i]);
>>> +			if (buf[i] < 0) {
>>> +				dev_err(madc->dev, "err reading current\n");
>>> +				count_req++;
>>> +			} else {
>>> +				count++;
>>> +				buf[i] = buf[i] - 750;
>>> +			}
>>> +			break;
>>> +		case 1:
>>> +			buf[i] = twl4030battery_temperature(buf[i]);
>>> +			if (buf[i] < 0) {
>>> +				dev_err(madc->dev, "err reading temperature\n");
>>> +				count_req++;
>>> +			} else {
>>> +				buf[i] -= 3;
>>> +				count++;
>>> +			}
>>> +			break;
>>> +		default:
>>> +			count++;
>>> +			/* Analog Input (V) = conv_result * step_size / R
>>> +			 * conv_result = decimal value of 10-bit conversion
>>> +			 *		 result
>>> +			 * step size = 1.5 / (2 ^ 10 -1)
>>> +			 * R = Prescaler ratio for input channels.
>>> +			 * Result given in mV hence multiplied by 1000.
>>> +			 */
>>> +			buf[i] = (buf[i] * 3 * 1000 *
>>> +				 twl4030_divider_ratios[i].denominator)
>>> +				/ (2 * 1023 *
>>> +				twl4030_divider_ratios[i].numerator);
>>> +		}
>>> +	}
>> This is already apparant from the errors emmited earlier. I'd drop this
>> and the count_req counter in general.
>> Personally I'd error out in those cases anyway rather than carrying on
>> with the rest of the channels. If it's worth of a dev_err it's worthy
>> of getting out as fast as possible rather than trying to muddle on.
>
> I also think this should be postponed until the old API is removed.
I'd prefer to see standalone cleanups and fixes integrated first as I suspect
it may be a little while before we manage to fully drop the old API.
>
>>> +	if (count_req)
>>> +		dev_err(madc->dev, "%d channel conversion failed\n", count_req);
>>> +
>>> +	return count;
>>> +}
>>> +
>>> +/*
>>> + * Enables irq.
>>> + * @madc - pointer to twl4030_madc_data struct
>>> + * @id - irq number to be enabled
>>> + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
>>> + * corresponding to RT, SW1, SW2 conversion requests.
>>> + * If the i2c read fails it returns an error else returns 0.
>>> + */
>>> +static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id)
>>> +{
>>> +	u8 val;
>>> +	int ret;
>>> +
>>> +	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
>>> +	if (ret) {
>>> +		dev_err(madc->dev, "unable to read imr register 0x%X\n",
>>> +			madc->imr);
>>> +		return ret;
>>> +	}
>> A blank line here and in similar cases would slight aid readability.
>
>
>
>>> +	val &= ~(1 << id);
>>> +	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
>>> +	if (ret) {
>>> +		dev_err(madc->dev,
>>> +			"unable to write imr register 0x%X\n", madc->imr);
>>> +		return ret;
>>> +
>> Loose blank line here.
>
> both fixed in the style patch.
>
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/*
>>> + * Disables irq.
>>> + * @madc - pointer to twl4030_madc_data struct
>>> + * @id - irq number to be disabled
>>> + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
>>> + * corresponding to RT, SW1, SW2 conversion requests.
>>> + * Returns error if i2c read/write fails.
>>> + */
>>> +static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id)
>>> +{
>>> +	u8 val;
>>> +	int ret;
>>> +
>>> +	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
>>> +	if (ret) {
>>> +		dev_err(madc->dev, "unable to read imr register 0x%X\n",
>>> +			madc->imr);
>>> +		return ret;
>>> +	}
>>> +	val |= (1 << id);
>>> +	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
>>> +	if (ret) {
>>> +		dev_err(madc->dev,
>>> +			"unable to write imr register 0x%X\n", madc->imr);
>>> +		return ret;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
>>> +{
>>> +	struct twl4030_madc_data *madc = _madc;
>>> +	const struct twl4030_madc_conversion_method *method;
>>> +	u8 isr_val, imr_val;
>>> +	int i, len, ret;
>>> +	struct twl4030_madc_request *r;
>>> +
>>> +	mutex_lock(&madc->lock);
>>> +	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr);
>>> +	if (ret) {
>>> +		dev_err(madc->dev, "unable to read isr register 0x%X\n",
>>> +			madc->isr);
>>> +		goto err_i2c;
>>> +	}
>>> +	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr);
>>> +	if (ret) {
>>> +		dev_err(madc->dev, "unable to read imr register 0x%X\n",
>>> +			madc->imr);
>>> +		goto err_i2c;
>>> +	}
>>> +	isr_val &= ~imr_val;
>>> +	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
>>> +		if (!(isr_val & (1 << i)))
>>> +			continue;
>>> +		ret = twl4030_madc_disable_irq(madc, i);
>>> +		if (ret < 0)
>>> +			dev_dbg(madc->dev, "Disable interrupt failed%d\n", i);
>>> +		madc->requests[i].result_pending = 1;
>>> +	}
>> Can this and the previous loop not be combined thus simplifying some tests?
>
> It probably can be, but I think this can wait until the old API is
> removed.
Hmm. As I state below, if this is moving over to IIO (which is sensible) it
becomes my problem so I reserve the right to be fussy about less than optimal
code.  If it was coming in clean as a new driver I'd push back hard on a lot
of this stuff before merging it.  I have plenty of slightly uggly drivers
in staging to look after as it is!
>
>>> +	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
>>> +		r = &madc->requests[i];
>>> +		/* No pending results for this method, move to next one */
>>> +		if (!r->result_pending)
>>> +			continue;
>>> +		method = &twl4030_conversion_methods[r->method];
>>> +		/* Read results */
>>> +		len = twl4030_madc_read_channels(madc, method->rbase,
>>> +						 r->channels, r->rbuf, r->raw);
>>> +		/* Return results to caller */
>>> +		if (r->func_cb != NULL) {
>>> +			r->func_cb(len, r->channels, r->rbuf);
>>> +			r->func_cb = NULL;
>>> +		}
>>> +		/* Free request */
>>> +		r->result_pending = 0;
>>> +		r->active = 0;
>>> +	}
>>> +	mutex_unlock(&madc->lock);
>>> +
>>> +	return IRQ_HANDLED;
>>> +
>>> +err_i2c:
>>> +	/*
>>> +	 * In case of error check whichever request is active
>>> +	 * and service the same.
>>> +	 */
>>> +	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
>>> +		r = &madc->requests[i];
>>> +		if (r->active == 0)
>>> +			continue;
>>> +		method = &twl4030_conversion_methods[r->method];
>>> +		/* Read results */
>>> +		len = twl4030_madc_read_channels(madc, method->rbase,
>>> +						 r->channels, r->rbuf, r->raw);
>>> +		/* Return results to caller */
>>> +		if (r->func_cb != NULL) {
>>> +			r->func_cb(len, r->channels, r->rbuf);
>>> +			r->func_cb = NULL;
>>> +		}
>>> +		/* Free request */
>>> +		r->result_pending = 0;
>>> +		r->active = 0;
>>> +	}
>>> +	mutex_unlock(&madc->lock);
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>> This structure does seem a little complex.  I'd have been more tempted
>> by using a completion and having the calling function wait on it.  How
>> often does an actual callback make sense?
>
> This is part of the old API, so let's convert all consumers to the
> IIO API and deprecate the callback stuff.
>
>>> +static int twl4030_madc_set_irq(struct twl4030_madc_data *madc,
>>> +				struct twl4030_madc_request *req)
>>> +{
>>> +	struct twl4030_madc_request *p;
>>> +	int ret;
>>> +
>>> +	p = &madc->requests[req->method];
>>> +	memcpy(p, req, sizeof(*req));
>>> +	ret = twl4030_madc_enable_irq(madc, req->method);
>>> +	if (ret < 0) {
>>> +		dev_err(madc->dev, "enable irq failed!!\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/*
>>> + * Function which enables the madc conversion
>>> + * by writing to the control register.
>>> + * @madc - pointer to twl4030_madc_data struct
>>> + * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1
>>> + * corresponding to RT SW1 or SW2 conversion methods.
>>> + * Returns 0 if succeeds else a negative error value
>>> + */
>>> +static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
>>> +					 int conv_method)
>>> +{
>>> +	const struct twl4030_madc_conversion_method *method;
>>> +	int ret = 0;
>>> +	method = &twl4030_conversion_methods[conv_method];
>>> +	switch (conv_method) {
>> Can we get here via any paths where these aren't the methods set?
>
> conv_method is set from outside of the driver, so it's probably
> safer to check. This can be simplified once the old API is removed.
Then we should be returning an error if it's not one of these.
>
>>> +	case TWL4030_MADC_SW1:
>>> +	case TWL4030_MADC_SW2:
>>> +		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
>>> +				       TWL4030_MADC_SW_START, method->ctrl);
>>> +		if (ret) {
>>> +			dev_err(madc->dev,
>>> +				"unable to write ctrl register 0x%X\n",
>>> +				method->ctrl);
>>> +			return ret;
>>> +		}
>>> +		break;
>>> +	default:
>>> +		break;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>> Could we fix up the kernel doc in this file in general. It's so nearly
>> there but not quite!
>
> I assume, that the driver will get a huge cleanup once all consumers
> of the old API are converted. Maybe a deprecation flag should be
> added together with this patchset?
Probably not worth bothering for an inkernel interface that seems to have
relatively few users.  How many out of tree users will be moving to
new kernels and using this device?
>
>>> +/*
>>> + * Function that waits for conversion to be ready
>>> + * @madc - pointer to twl4030_madc_data struct
>>> + * @timeout_ms - timeout value in milliseconds
>>> + * @status_reg - ctrl register
>>> + * returns 0 if succeeds else a negative error value
>>> + */
>>> +static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc,
>>> +					      unsigned int timeout_ms,
>>> +					      u8 status_reg)
>>> +{
>>> +	unsigned long timeout;
>>> +	int ret;
>>> +
>>> +	timeout = jiffies + msecs_to_jiffies(timeout_ms);
>>> +	do {
>>> +		u8 reg;
>>> +
>>> +		ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &reg, status_reg);
>>> +		if (ret) {
>>> +			dev_err(madc->dev,
>>> +				"unable to read status register 0x%X\n",
>>> +				status_reg);
>>> +			return ret;
>>> +		}
>>> +		if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW))
>>> +			return 0;
>>> +		usleep_range(500, 2000);
>>> +	} while (!time_after(jiffies, timeout));
>>> +	dev_err(madc->dev, "conversion timeout!\n");
>>> +
>>> +	return -EAGAIN;
>>> +}
>>> +
>>
>>> +/*
>>> + * An exported function which can be called from other kernel drivers.
>> What uses this currently?  Ideally we'd do this through the standard IIO paths
>> for in kernel users.  Right now the device tree mappings for that are less
>> than ideal (ask Mark Rutland if you want some choice comments ;)  Perhaps
>> we take the view this can be cleaned up later. There are some elements in here
>> we haven't yet looked at supporting for client drivers.  Will have to think
>> about that.
>>
>> It would be particularly nice if possible to use a generic battery driver
>> for the two cases that seem to be using this functionality at the moment.
>
> Currently there are 3 consumer drivers using this:
>
> * twl4030-madc-battery
> * rx51-battery
> * twl4030-madc-hwmon
>
> I guess the first two can be updated to use the IIO API and the last
> one can be replaced by the IIO hwmon driver. Since it's an
> in-kernel-api there should be no problems to simply remove the API
> afterwards.
Yes that sounds like a good paln.
>
>>> + * @req twl4030_madc_request structure
>>> + * req->rbuf will be filled with read values of channels based on the
>>> + * channel index. If a particular channel reading fails there will
>>> + * be a negative error value in the corresponding array element.
>>> + * returns 0 if succeeds else error value
>>> + */
>>> +int twl4030_madc_conversion(struct twl4030_madc_request *req)
>>> +{
>>> +	const struct twl4030_madc_conversion_method *method;
>>> +	u8 ch_msb, ch_lsb;
>>> +	int ret;
>>> +
>>> +	if (!req || !twl4030_madc)
>>> +		return -EINVAL;
>>> +
>>> +	mutex_lock(&twl4030_madc->lock);
>>> +	if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) {
>>> +		ret = -EINVAL;
>>> +		goto out;
>>> +	}
>>> +	/* Do we have a conversion request ongoing */
>>> +	if (twl4030_madc->requests[req->method].active) {
>>> +		ret = -EBUSY;
>>> +		goto out;
>>> +	}
>>> +	ch_msb = (req->channels >> 8) & 0xff;
>>> +	ch_lsb = req->channels & 0xff;
>>> +	method = &twl4030_conversion_methods[req->method];
>>> +	/* Select channels to be converted */
>>> +	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1);
>>> +	if (ret) {
>>> +		dev_err(twl4030_madc->dev,
>>> +			"unable to write sel register 0x%X\n", method->sel + 1);
>>> +		goto out;
>>> +	}
>>> +	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel);
>>> +	if (ret) {
>>> +		dev_err(twl4030_madc->dev,
>>> +			"unable to write sel register 0x%X\n", method->sel + 1);
>>> +		goto out;
>>> +	}
>>> +	/* Select averaging for all channels if do_avg is set */
>>> +	if (req->do_avg) {
>>> +		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
>>> +				       ch_msb, method->avg + 1);
>>> +		if (ret) {
>>> +			dev_err(twl4030_madc->dev,
>>> +				"unable to write avg register 0x%X\n",
>>> +				method->avg + 1);
>>> +			goto out;
>>> +		}
>>> +		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
>>> +				       ch_lsb, method->avg);
>>> +		if (ret) {
>>> +			dev_err(twl4030_madc->dev,
>> Excesive error messages are sometimes irritating as they break up code
>> flow.  They are really irritating when incorrect ;)
>
> Added to the fixes patch.
>
>>> +				"unable to write sel reg 0x%X\n",
>>> +				method->sel + 1);
>>> +			goto out;
>>> +		}
>>> +	}
>>> +	if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) {
>>> +		ret = twl4030_madc_set_irq(twl4030_madc, req);
>>> +		if (ret < 0)
>>> +			goto out;
>>> +		ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
>>> +		if (ret < 0)
>>> +			goto out;
>>> +		twl4030_madc->requests[req->method].active = 1;
>>> +		ret = 0;
>>> +		goto out;
>>> +	}
>> Is it possible to get here?  This comment suggests not. If so, why have
>> the test?
>
> I guess it's an additional safety check. This function can be
> simplified a lot once the old API is removed, so I guess this
> can also be postponed.
>
>>> +	/* With RT method we should not be here anymore */
>>> +	if (req->method == TWL4030_MADC_RT) {
>>> +		ret = -EINVAL;
>>> +		goto out;
>>> +	}
>>> +	ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
>>> +	if (ret < 0)
>>> +		goto out;
>>> +	twl4030_madc->requests[req->method].active = 1;
>>> +	/* Wait until conversion is ready (ctrl register returns EOC) */
>> So no interrupts in this case?
>>> +	ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl);
>>> +	if (ret) {
>>> +		twl4030_madc->requests[req->method].active = 0;
>>> +		goto out;
>>> +	}
>>> +	ret = twl4030_madc_read_channels(twl4030_madc, method->rbase,
>>> +					 req->channels, req->rbuf, req->raw);
>>> +	twl4030_madc->requests[req->method].active = 0;
>>> +
>>> +out:
>>> +	mutex_unlock(&twl4030_madc->lock);
>>> +
>>> +	return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(twl4030_madc_conversion);
>>> +
>>> +/*
>> Proper kerneldoc would be nice here.  Otherwise the comment doesn't
>> really add anything so I'd be tempted to drop it ;)
>
> removed in fixes patch.
>
>>> + * Return channel value
>>> + * Or < 0 on failure.
>>> + */
>>> +int twl4030_get_madc_conversion(int channel_no)
>>> +{
>>> +	struct twl4030_madc_request req;
>>> +	int temp = 0;
>>> +	int ret;
>>> +
>>> +	req.channels = (1 << channel_no);
>>> +	req.method = TWL4030_MADC_SW2;
>>> +	req.active = 0;
>>> +	req.func_cb = NULL;
>>> +	ret = twl4030_madc_conversion(&req);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +	if (req.rbuf[channel_no] > 0)
>>> +		temp = req.rbuf[channel_no];
>>> +
>>> +	return temp;
>>> +}
>>> +EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
>>> +
>>> +/*
>>> + * Function to enable or disable bias current for
>>> + * main battery type reading or temperature sensing
>>> + * @madc - pointer to twl4030_madc_data struct
>>> + * @chan - can be one of the two values
>> What is a battery type reading?  Voltage? Current? Charge?
>
> It's used for battery type identification.
Ah the comment really did mean what it said!
>
>>> + * TWL4030_BCI_ITHEN - Enables bias current for main battery type reading
>>> + * TWL4030_BCI_TYPEN - Enables bias current for main battery temperature
>> These constants have rather incomprehensible names. Whilst I gues they
>> are straight off the data sheet, seems to me that we could make them a little
>> longer and easier to follow!  The comment here helps, but sensible names
>> would be better!
>
> The names come from the datasheet. I think we should fix this after
> the old API is removed, since the constants are exposed for the API.
Fair enough.
>
>>> + * sensing
>>> + * @on - enable or disable chan.
>>> + */
>>> +static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
>>> +					      int chan, int on)
>>> +{
>>> +	int ret;
>>> +	u8 regval;
>>> +
>>> +	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
>>> +			      &regval, TWL4030_BCI_BCICTL1);
>>> +	if (ret) {
>>> +		dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X",
>>> +			TWL4030_BCI_BCICTL1);
>>> +		return ret;
>>> +	}
>> Introduce an intermediate variable and this could be nice and easy to read
>> Say
>>
>> int regmask;
>> if (chan == 0)
>>     regmask = TWL4030_BCI_TYPEN;
>> else
>>     regmask = TWL4030_BFI_ITHEN;
>>
>> if (on)
>>     regval |= regmask;
>> else
>>     regval &= ~regmask;
>>
>> It's longer but doesn't give me a headache.
>
> I added this to the fixes patch:
>
>      int regmask;
> 	regmask = (chan == 0) ? TWL4030_BCI_TYPEN : TWL4030_BFI_ITHEN;
> 	if (on)
> 		regval |= regmask;
> 	else
> 		regval &= ~regmask;
>
>>> +	if (on)
>>> +		regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
>>> +	else
>>> +		regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN;
>>> +	ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
>>> +			       regval, TWL4030_BCI_BCICTL1);
>>> +	if (ret) {
>>> +		dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n",
>>> +			TWL4030_BCI_BCICTL1);
>>> +		return ret;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/*
>>> + * Function that sets MADC software power on bit to enable MADC
>>> + * @madc - pointer to twl4030_madc_data struct
>>> + * @on - Enable or disable MADC software powen on bit.
>>> + * returns error if i2c read/write fails else 0
>>> + */
>>> +static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
>>> +{
>>> +	u8 regval;
>>> +	int ret;
>>> +
>>> +	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
>>> +			      &regval, TWL4030_MADC_CTRL1);
>>> +	if (ret) {
>>> +		dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n",
>>> +			TWL4030_MADC_CTRL1);
>>> +		return ret;
>>> +	}
>>> +	if (on)
>>> +		regval |= TWL4030_MADC_MADCON;
>>> +	else
>>> +		regval &= ~TWL4030_MADC_MADCON;
>>> +	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1);
>>> +	if (ret) {
>>> +		dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n",
>>> +			TWL4030_MADC_CTRL1);
>>> +		return ret;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/*
>>> + * Initialize MADC and request for threaded irq
>>> + */
>>> +static int twl4030_madc_probe(struct platform_device *pdev)
>>> +{
>>> +	struct twl4030_madc_data *madc;
>>> +	struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
>>> +	struct device_node *np = pdev->dev.of_node;
>>> +	int irq, ret;
>>> +	u8 regval;
>>> +	struct iio_dev *iio_dev = NULL;
>>> +
>>> +	if (!pdata && !np) {
>>> +		dev_err(&pdev->dev, "platform_data not available\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	iio_dev = devm_iio_device_alloc(&pdev->dev,
>>> +					sizeof(struct twl4030_madc_data));
>>> +	if (!iio_dev) {
>>> +		dev_err(&pdev->dev, "failed allocating iio device\n");
>>> +		return -ENOMEM;
>>> +	}
>>> +
>>> +	madc = iio_priv(iio_dev);
>>> +	madc->dev = &pdev->dev;
>>> +
>>> +	iio_dev->name = dev_name(&pdev->dev);
>>> +	iio_dev->dev.parent = &pdev->dev;
>>> +	iio_dev->dev.of_node = pdev->dev.of_node;
>>> +	iio_dev->info = &twl4030_madc_iio_info;
>>> +	iio_dev->modes = INDIO_DIRECT_MODE;
>>> +	iio_dev->channels = twl4030_madc_iio_channels;
>>> +	iio_dev->num_channels = 16;
>>> +
>>> +	/*
>>> +	 * Phoenix provides 2 interrupt lines. The first one is connected to
>>> +	 * the OMAP. The other one can be connected to the other processor such
>>> +	 * as modem. Hence two separate ISR and IMR registers.
>>> +	 */
>>> +	if (pdata)
>>> +		madc->use_second_irq = pdata->irq_line != 1;
>>> +	else
>>> +		madc->use_second_irq = false;
>>> +
>>> +	madc->imr = (madc->use_second_irq == 1) ?
>>> +	    TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
>>> +	madc->isr = (madc->use_second_irq == 1) ?
>>> +	    TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
>>> +
>>> +	ret = twl4030_madc_set_power(madc, 1);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +	ret = twl4030_madc_set_current_generator(madc, 0, 1);
>>> +	if (ret < 0)
>>> +		goto err_current_generator;
>>> +
>>> +	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
>>> +			      &regval, TWL4030_BCI_BCICTL1);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n",
>>> +			TWL4030_BCI_BCICTL1);
>>> +		goto err_i2c;
>>> +	}
>>> +	regval |= TWL4030_BCI_MESBAT;
>>> +	ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
>>> +			       regval, TWL4030_BCI_BCICTL1);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n",
>>> +			TWL4030_BCI_BCICTL1);
>>> +		goto err_i2c;
>>> +	}
>>> +
>>> +	/* Check that MADC clock is on */
>>> +	ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &regval, TWL4030_REG_GPBR1);
>>> +	if (ret) {
>> Personally I'd rank the driver as rather to vebose on read error messages
>> given they are pretty unusual.  Ah well I guess each to their own.
>
> All of this is not written by me.
Sure.  Doesn't stop me moaning :)  At the end of the day you are proposing moving
this driver into a place where it becomes at least partly my problem.  As such
I'm keen on any tidying / cleaning occuring ASAP.
>
>>> +		dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n",
>>> +				TWL4030_REG_GPBR1);
>>> +		goto err_i2c;
>>> +	}
>>> +
>>> +	/* If MADC clk is not on, turn it on */
>>> +	if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) {
>>> +		dev_info(&pdev->dev, "clk disabled, enabling\n");
>>> +		regval |= TWL4030_GPBR1_MADC_HFCLK_EN;
>>> +		ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval,
>>> +				       TWL4030_REG_GPBR1);
>>> +		if (ret) {
>>> +			dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n",
>>> +					TWL4030_REG_GPBR1);
>>> +			goto err_i2c;
>>> +		}
>>> +	}
>>> +
>>> +	platform_set_drvdata(pdev, iio_dev);
>>> +	mutex_init(&madc->lock);
>>> +
>>> +	irq = platform_get_irq(pdev, 0);
>>
>> As ever using devm for a irq request makes for a bit of a review
>> nightmare as we have to be very careful that nothing has been removed
>> that this might use before the devm cleanup occurs (at end of remove).
>> In this case the fact that the current generator is off and the adc unit
>> is disabled sounds to me like it might cause interesting results in the
>> interrupt handler?
>>
>> It's one of those things that will probably never happen but I find it
>> hard to convince myself 'can't happen'
>>
>>> +	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
>>> +				   twl4030_madc_threaded_irq_handler,
>>> +				   IRQF_TRIGGER_RISING, "twl4030_madc", madc);
>>> +	if (ret) {
>>> +		dev_dbg(&pdev->dev, "could not request irq\n");
>>> +		goto err_i2c;
>>> +	}
>>
>> Err, where is this defined?  Ah, found it above.  I'm not keen on
>> preventing multiple instances of a device like this.  It is so easy
>> to not do this I'd prefer to see the driver fixed to remove this.
>
> This concept has also not introduced by me of course. I think we
> should fix this after removal of the old API, since the old API does
> not have an device parameter.
Fair enough.
>
>>> +	twl4030_madc = madc;
>>> +
>>> +	ret = iio_device_register(iio_dev);
>>> +	if (ret) {
>>> +		dev_dbg(&pdev->dev, "could not register iio device\n");
>>> +		goto err_i2c;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +err_i2c:
>>> +	twl4030_madc_set_current_generator(madc, 0, 0);
>>> +err_current_generator:
>>> +	twl4030_madc_set_power(madc, 0);
>>> +	return ret;
>>> +}
>>> +
>>> +static int twl4030_madc_remove(struct platform_device *pdev)
>>> +{
>>> +	struct iio_dev *iio_dev = platform_get_drvdata(pdev);
>>> +	struct twl4030_madc_data *madc = iio_priv(iio_dev);
>>> +
>>> +	twl4030_madc_set_current_generator(madc, 0, 0);
>>> +	twl4030_madc_set_power(madc, 0);
>>> +
>>> +	iio_device_unregister(iio_dev);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +#ifdef CONFIG_OF
>>> +static const struct of_device_id twl_madc_of_match[] = {
>>> +	{.compatible = "ti,twl4030-madc", },
>>> +	{ },
>>> +};
>>> +MODULE_DEVICE_TABLE(of, twl_madc_of_match);
>>> +#endif
>>> +
>>> +static struct platform_driver twl4030_madc_driver = {
>>> +	.probe = twl4030_madc_probe,
>>> +	.remove = twl4030_madc_remove,
>>> +	.driver = {
>>> +		   .name = "twl4030_madc",
>>> +		   .owner = THIS_MODULE,
>>> +		   .of_match_table = of_match_ptr(twl_madc_of_match),
>>> +		   },
>>> +};
>>> +
>>> +module_platform_driver(twl4030_madc_driver);
>>> +
>>> +MODULE_DESCRIPTION("TWL4030 ADC driver");
>>> +MODULE_LICENSE("GPL");
>>> +MODULE_AUTHOR("J Keerthy");
>>> +MODULE_ALIAS("platform:twl4030_madc");
>>
>> Rest of patch was removal of code so no comments!
>
> FYI: For me it was a bit odd to see inline comments about code lines
> coming after the inline comment. Normally I receive patch reviews
> with comments after the referenced code, which makes more sense imho.
I never claimed to be consistent ;)  Tend to review backwards that means
that sometimes comments end up in fairly random places.
>
> -- Sebastian
>

^ permalink raw reply

* Re: [PATCH v7 13/15] ARM: sun4i: dt: Remove grouping + simple-bus compatible for regulators
From: Hans de Goede @ 2014-02-23  8:03 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Tejun Heo, Oliver Schinagl, Richard Zhu, Roger Quadros, Lee Jones,
	linux-ide-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20140222214421.GC3610@lukather>

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi,

On 02/22/2014 10:44 PM, Maxime Ripard wrote:
> Hi,
> 
> On Sat, Feb 22, 2014 at 04:53:42PM +0100, Hans de Goede wrote:
>> According to Documentation/devicetree/bindings/regulator/regulator.txt regulator nodes should not be placed under 'simple-bus'.
>> 
>> Mark Rutland also explains about it at: http://www.spinics.net/lists/linux-usb/msg101497.html
>> 
>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> 
> Applied in my sunxi/dt-for-3.15 branch.

Thanks, I assume this only applies to this patch, since we were still
discussing the common regulator bits of the ahci enablement dts patches ?

I'm not seeing any of your recent merges here:
https://github.com/mripard/linux/commits/sunxi/dt-for-3.15

Did you not push them, or am I looking at the wrong tree ?

Regards,

Hans

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iEYEARECAAYFAlMJq18ACgkQF3VEtJrzE/vngACfbYd5K2ZixBm45Y+e3cMFSq9M
v68Anjze6R6meI1Z5R0l4nFCNS8txl9p
=0XGo
-----END PGP SIGNATURE-----

^ permalink raw reply

* Re: [PATCH 2/3] input: touchscreen: imx25 tcq driver
From: Dmitry Torokhov @ 2014-02-23  6:44 UTC (permalink / raw)
  To: Markus Pargmann
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Samuel Ortiz, Lee Jones,
	Jonathan Cameron,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	kernel-bIcnvbaLZ9MEGnE8C9+IrQ
In-Reply-To: <1392913312-9030-3-git-send-email-mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>

Hi Marjus,

On Thu, Feb 20, 2014 at 05:21:51PM +0100, Markus Pargmann wrote:
> This is a driver for the imx25 ADC/TSC module. It controls the
> touchscreen conversion queue and creates a touchscreen input device.
> The driver currently only supports 4 wire touchscreens. The driver uses
> a simple conversion queue of precharge, touch detection, X measurement,
> Y measurement, precharge and another touch detection.
> 
> This driver uses the regmap from the parent to setup some touch specific
> settings in the core driver and setup a idle configuration with touch
> detection.
> 
> Signed-off-by: Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> ---
>  .../bindings/input/touchscreen/fsl-mx25-tcq.txt    |  29 +
>  drivers/input/touchscreen/Kconfig                  |   6 +
>  drivers/input/touchscreen/Makefile                 |   1 +
>  drivers/input/touchscreen/fsl-imx25-tcq.c          | 589 +++++++++++++++++++++
>  4 files changed, 625 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
>  create mode 100644 drivers/input/touchscreen/fsl-imx25-tcq.c
> 
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> new file mode 100644
> index 0000000..4214a99
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt
> @@ -0,0 +1,29 @@
> +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: Pen debounce time.
> + - fsl,pen-threshold: Pen-down threshold for the touchscreen.
> + - fsl,settling-time: Settling time in nanoseconds.
> +
> +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>;
> +	};
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 07e9e82..d52c055 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -715,6 +715,12 @@ 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.
> +
>  config TOUCHSCREEN_MC13783
>  	tristate "Freescale MC13783 touchscreen input driver"
>  	depends on MFD_MC13XXX
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 62801f2..c891f30 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
>  obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
>  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MAX11801)	+= max11801_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_MX25)		+= fsl-imx25-tcq.o
>  obj-$(CONFIG_TOUCHSCREEN_MC13783)	+= mc13783_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MCS5000)	+= mcs5000_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
> diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c
> new file mode 100644
> index 0000000..436cc8b
> --- /dev/null
> +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c
> @@ -0,0 +1,589 @@
> +/*
> + * Copyright 2014 Markus Pargmann, Pengutronix <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> + * Based on driver from 2011 Juergen Beisert, Pengutronix <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> + *
> + * The code contained herein is licensed under the GNU General Public
> + * License. You may obtain a copy of the GNU General Public License
> + * Version 2 or later at the following locations:
> + *
> + * http://www.opensource.org/licenses/gpl-license.html
> + * http://www.gnu.org/copyleft/gpl.html
> + *
> + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue)
> + * connected to the imx25 ADC.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/input.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 repeat_wait;
> +	unsigned int pen_debounce;
> +	unsigned int settling_time;
> +	struct clk *clk;
> +};
> +
> +static struct regmap_config mx25_tcq_regconfig = {
> +	.fast_io = true,
> +	.max_register = 0x5c,
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +};
> +
> +static struct of_device_id mx25_tcq_ids[] = {
> +	{ .compatible = "fsl,imx25-tcq", },
> +	{ /* Senitel */ }
> +};
> +
> +#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
> +
> +
> +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_time)
> +{
> +	u32 precharge_cfg =
> +			MX25_PRECHARGE_VALUE |
> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_time);
> +	u32 touch_detect_cfg =
> +			MX25_TOUCH_DETECT_VALUE |
> +			MX25_ADCQ_CFG_NOS(1) |
> +			MX25_ADCQ_CFG_SETTLING_TIME(settling_time);
> +
> +	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_time));
> +
> +	/* 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_time));
> +
> +	/* 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_time, int *items)
> +{
> +	imx25_setup_queue_cfgs(priv, settling_time);
> +
> +	/* 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);
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, 0);
> +}
> +
> +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 int mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv,
> +		u32 *sample_buf, int samples)
> +{
> +	unsigned int x_pos = 0;
> +	unsigned int y_pos = 0;
> +	unsigned int touch_pre = 0;
> +	unsigned int touch_post = 0;
> +	unsigned i;
> +	int ret = 0;
> +
> +	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:
> +			ret = -EINVAL;
> +			break;
> +		}
> +	}
> +
> +	if (ret == 0 && samples != 0) {
> +		/*
> +		 * only if both touch measures are below a treshold,
> +		 * 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,
> +					0xfff - ((touch_pre + touch_post) / 2));

Hmm, are you trying to report pressure here?

> +			input_sync(priv->idev);
> +
> +			/* get next sample */
> +			mx25_tcq_force_queue_start(priv);
> +			mx25_tcq_enable_fifo_irq(priv);
> +		} else {
> +			if (touch_pre >= priv->pen_threshold &&

You can convert this to "else if" and save indentation level here.

> +					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_force_queue_start(priv);
> +				mx25_tcq_enable_fifo_irq(priv);
> +			}
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id)
> +{
> +	struct mx25_tcq_priv *priv = (struct mx25_tcq_priv *) dev_id;
> +	u32 sample_buf[TSC_MAX_SAMPLES];
> +	int samples = 0;
> +
> +	/* read all samples */
> +	while (1) {
> +		u32 stats;
> +
> +		regmap_read(priv->regs, MX25_ADCQ_SR, &stats);

Error handling for I/O operations?

> +		if (stats & MX25_ADCQ_SR_EMPT)
> +			break;
> +
> +		if (samples < TSC_MAX_SAMPLES) {
> +			regmap_read(priv->regs, MX25_ADCQ_FIFO,
> +					&sample_buf[samples]);
> +			++samples;
> +		} else {
> +			u32 discarded;
> +			/* discard samples */
> +			regmap_read(priv->regs, MX25_ADCQ_FIFO, &discarded);

Should there be some upper bound for number of discarded samples?

> +		}
> +	}
> +
> +	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 = (struct mx25_tcq_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_fifo_reset(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_EOQ,
> +			MX25_ADCQ_SR_FRR |
> +			MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD |
> +			MX25_ADCQ_SR_EOQ);
> +
> +	return ret;
> +}
> +
> +/* configure the statemachine 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 repeat_wait;
> +	unsigned int debounce_cnt;
> +	unsigned int settling_time;
> +	int itemct;
> +	int ret;
> +
> +	regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr);
> +	ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr));
> +	adc_period = clk_get_rate(priv->clk) / (ipg_div * 2 + 2);
> +	repeat_wait = fls(DIV_ROUND_UP(priv->repeat_wait, adc_period));
> +	debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1;
> +	settling_time = DIV_ROUND_UP(priv->settling_time, adc_period);
> +
> +
> +	/* 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;
> +
> +	if (repeat_wait > 15)
> +		repeat_wait = 15;
> +
> +	regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_RWAIT_MASK,
> +			MX25_ADCQ_CR_RWAIT(repeat_wait));
> +
> +	ret = imx25_setup_queue_4wire(priv, 0x0, &itemct);
> +	if (ret)
> +		return ret;
> +
> +	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_FQS,
> +			MX25_ADCQ_CR_QSM_FQS);
> +
> +	mx25_tcq_re_enable_touch_detection(priv);
> +
> +	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 ret;
> +
> +	/* Setup defaults */
> +	priv->pen_threshold = 500;
> +	priv->sample_count = 3;
> +	priv->repeat_wait = 15000000;
> +	priv->pen_debounce = 1000000;
> +	priv->settling_time = 250000;
> +
> +	ret = of_property_read_u32(np, "fsl,wires", &wires);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to find fsl,wires properties\n");
> +		return ret;
> +	}
> +
> +	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", &priv->settling_time);
> +	of_property_read_u32(np, "fsl,pen-debounce", &priv->pen_debounce);
> +
> +	return 0;
> +}
> +
> +static int mx25_tcq_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct input_dev *idev;
> +	struct mx25_tcq_priv *priv;
> +	struct resource *res;
> +	void __iomem *mem;
> +	int ret;
> +	int irq;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	mem = devm_ioremap_resource(dev, res);
> +	if (!mem) {
> +		dev_err(dev, "Failed to get iomem");
> +		return -ENOMEM;
> +	}
> +
> +	ret = mx25_tcq_parse_dt(pdev, priv);
> +	if (ret)
> +		return ret;
> +
> +	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);
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(dev, "Failed to get IRQ\n");
> +		return irq;
> +	}
> +
> +	ret = devm_request_threaded_irq(dev, irq, mx25_tcq_irq,
> +			mx25_tcq_irq_thread, IRQF_ONESHOT, pdev->name, priv);
> +	if (ret) {
> +		dev_err(dev, "Failed requesting IRQ\n");
> +		return ret;
> +	}

Are we sure the device is quiesce here? Otherwise interrupts will start
coming but input device is not there yet.

> +
> +	idev = devm_input_allocate_device(dev);
> +	if (!idev) {
> +		dev_err(dev, "Failed to allocate input device\n");
> +		return -ENOMEM;
> +	}
> +
> +	idev->name = mx25_tcq_name;
> +	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> +	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(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;
> +
> +	ret = input_register_device(idev);
> +	if (ret) {
> +		dev_err(dev, "Failed to register input device\n");
> +		return ret;
> +	}
> +
> +	priv->idev = idev;
> +
> +	priv->core_regs = mx25_tsadc_get_regmap(pdev->dev.parent);
> +	priv->clk = mx25_tsadc_get_ipg(pdev->dev.parent);
> +
> +	ret = clk_prepare_enable(priv->clk);
> +	if (ret) {
> +		dev_err(dev, "Failed to enable ipg clock\n");
> +		return ret;
> +	}
> +
> +	ret = mx25_tcq_init(priv);
> +	if (ret) {
> +		dev_err(dev, "Failed to init tcq\n");
> +		goto error_tcq_init;
> +	}
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	return 0;
> +
> +error_tcq_init:
> +	clk_disable_unprepare(priv->clk);
> +	return ret;
> +}
> +
> +static int mx25_tcq_remove(struct platform_device *pdev)
> +{
> +	struct mx25_tcq_priv *priv = platform_get_drvdata(pdev);
> +
> +	clk_disable_unprepare(priv->clk);

Hmm, if you disable clk all other operations will likely to fail. We
really need devm clk interface, I guess I need to dust off my old
patch...

> +
> +	return 0;
> +}
> +
> +static struct platform_driver mx25_tcq_driver = {
> +	.driver		= {
> +		.name	= "mx25-tcq",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = mx25_tcq_ids,
> +	},
> +	.probe		= mx25_tcq_probe,
> +	.remove		= mx25_tcq_remove,
> +};
> +module_platform_driver(mx25_tcq_driver);
> +
> +MODULE_DESCRIPTION("TS input driver for Freescale mx25");
> +MODULE_AUTHOR("Markus Pargmann <mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>");
> +MODULE_LICENSE("GPL v2");
> -- 
> 1.8.5.3
> 

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH v6 1/4] ASoC: tlv320aic32x4: Support for master clock
From: Mark Brown @ 2014-02-23  3:59 UTC (permalink / raw)
  To: Markus Pargmann
  Cc: Liam Girdwood, Lars-Peter Clausen,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1392916981-9333-2-git-send-email-mpa-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 208 bytes --]

On Thu, Feb 20, 2014 at 06:22:58PM +0100, Markus Pargmann wrote:
> Add support for a master clock passed through DT. The master clock of
> the codec is only active when the codec is in use.

Applied, thanks.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 1/4] spi: rspi: List full example compatible properties in bindings
From: Mark Brown @ 2014-02-23  3:27 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: linux-spi, linux-sh, linux-kernel, Geert Uytterhoeven, devicetree
In-Reply-To: <1393000158-6622-1-git-send-email-geert@linux-m68k.org>

[-- Attachment #1: Type: text/plain, Size: 280 bytes --]

On Fri, Feb 21, 2014 at 05:29:15PM +0100, Geert Uytterhoeven wrote:
> From: Geert Uytterhoeven <geert+renesas@linux-m68k.org>
> 
> List full example compatible properties with soctypes instead of just the
> soctypes, so checkpatch can validate DTSes.

Applied all, thanks.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [RFCv1 4/4] mfd: twl4030-madc: Move driver to drivers/iio/adc
From: Sebastian Reichel @ 2014-02-23  0:35 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Marek Belisko, Lee Jones, Samuel Ortiz, Lars-Peter Clausen,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Grant Likely, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <53089C47.5050404-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 43202 bytes --]

On Sat, Feb 22, 2014 at 12:47:03PM +0000, Jonathan Cameron wrote:
> On 14/02/14 18:46, Sebastian Reichel wrote:
> >This is a driver for an A/D converter, which belongs into
> >drivers/iio/adc.
> >
> >Signed-off-by: Sebastian Reichel <sre-8fiUuRrzOP0dnm+yROfE0A@public.gmane.org>
> 
> Being inherently lazy I'm going to review this patch as it gives the complete
> driver rather than taking on the conversion patch directly!

OK.

> It's a long shot, but are there any docs freely available for this part?

There is documentation on Texas Instrument's website for TPS65950,
which is supposed to be very similar to the TWL4030 and contains a
section about the MADC module:

http://www.ti.com/lit/gpn/tps65950

> Obviously that may well mean that some of my comments apply to the driver
> in general rather than the changes you've made.  Please feel free to
> pick and choose!

Having skipped over your comments I guess 70% are about the driver
in general, but I can add another patch to the patchset, which fixes
the driver style.

> I'd like ideally to see a little more generic tidying up in this driver.
> As you'll notice inline I get irritated at having lots and lots of error
> messages.  Still that's personal preference but I felt a lot more justified
> in moaning after finding one that was incorrect ;)
> 
> There is also a bit of functionality in here that I'm not sure is ever
> used (the request callback functions).  It complicates the code so if no
> using it I'd be tempted to drop it.

I think the optimal workflow is:

1. convert madc driver to IIO
2. convert twl4030-madc-battery driver to IIO API
3. convert rx51-battery to IIO API
4. convert twl4030-madc-hwmon to IIO API / deprecate it
5. remove old in-kernel ABI from madc
6. cleanup/simplify madc

I guess its much simpler to do the driver cleanup/simplification
once we can get rid of the old API.

> It's nice in a way that the driver before your conversion provided only
> a very limited and in kernel interface given we don't have to hang on to
> any old ABI elements.
> 
> >---
> >  drivers/iio/adc/Kconfig        |  10 +
> >  drivers/iio/adc/Makefile       |   1 +
> >  drivers/iio/adc/twl4030-madc.c | 922 +++++++++++++++++++++++++++++++++++++++++
> >  drivers/mfd/Kconfig            |  10 -
> >  drivers/mfd/Makefile           |   1 -
> >  drivers/mfd/twl4030-madc.c     | 922 -----------------------------------------
> >  6 files changed, 933 insertions(+), 933 deletions(-)
> >  create mode 100644 drivers/iio/adc/twl4030-madc.c
> >  delete mode 100644 drivers/mfd/twl4030-madc.c
> >
> >diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> >index 2209f28..427f75c 100644
> >--- a/drivers/iio/adc/Kconfig
> >+++ b/drivers/iio/adc/Kconfig
> >@@ -183,6 +183,16 @@ config TI_AM335X_ADC
> >  	  Say yes here to build support for Texas Instruments ADC
> >  	  driver which is also a MFD client.
> >
> >+config TWL4030_MADC
> >+	tristate "TWL4030 MADC (Monitoring A/D Converter)"
> >+	depends on TWL4030_CORE
> >+	help
> >+	This driver provides support for Triton TWL4030-MADC. The
> >+	driver supports both RT and SW conversion methods.
> >+
> >+	This driver can also be built as a module. If so, the module will be
> >+	called twl4030-madc.
> >+
> >  config TWL6030_GPADC
> >  	tristate "TWL6030 GPADC (General Purpose A/D Converter) Support"
> >  	depends on TWL4030_CORE
> >diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> >index ba9a10a..9acf2df 100644
> >--- a/drivers/iio/adc/Makefile
> >+++ b/drivers/iio/adc/Makefile
> >@@ -20,5 +20,6 @@ obj-$(CONFIG_MCP3422) += mcp3422.o
> >  obj-$(CONFIG_NAU7802) += nau7802.o
> >  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
> >  obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
> >+obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
> >  obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
> >  obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
> >diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
> >new file mode 100644
> >index 0000000..4da61c4
> >--- /dev/null
> >+++ b/drivers/iio/adc/twl4030-madc.c
> >@@ -0,0 +1,922 @@
> >+/*
> >+ *
> >+ * TWL4030 MADC module driver-This driver monitors the real time
> >+ * conversion of analog signals like battery temperature,
> >+ * battery type, battery level etc.
> >+ *
> >+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
> >+ * J Keerthy <j-keerthy-l0cyMroinI0@public.gmane.org>
> >+ *
> >+ * Based on twl4030-madc.c
> >+ * Copyright (C) 2008 Nokia Corporation
> >+ * Mikko Ylinen <mikko.k.ylinen-xNZwKgViW5gAvxtiuMwx3w@public.gmane.org>
> >+ *
> >+ * Amit Kucheria <amit.kucheria-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.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.
> >+ *
> >+ * This program is distributed in the hope that it will be useful, but
> >+ * WITHOUT ANY WARRANTY; without even the implied warranty of
> >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >+ * General Public License for more details.
> >+ *
> >+ * You should have received a copy of the GNU General Public License
> >+ * along with this program; if not, write to the Free Software
> >+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> >+ * 02110-1301 USA
> >+ *
> >+ */
> >+
> >+#include <linux/init.h>
> >+#include <linux/device.h>
> >+#include <linux/interrupt.h>
> >+#include <linux/kernel.h>
> >+#include <linux/delay.h>
> >+#include <linux/platform_device.h>
> >+#include <linux/slab.h>
> >+#include <linux/i2c/twl.h>
> >+#include <linux/i2c/twl4030-madc.h>
> >+#include <linux/module.h>
> >+#include <linux/stddef.h>
> >+#include <linux/mutex.h>
> >+#include <linux/bitops.h>
> >+#include <linux/jiffies.h>
> >+#include <linux/types.h>
> >+#include <linux/gfp.h>
> >+#include <linux/err.h>
> >+
> >+#include <linux/iio/iio.h>
> Why include machine.h or driver.h. You aren't currently using the
> mapping coding anyway that these provide.

My bad. I copied that over from another driver without checking if
it is actually needed.

> >+#include <linux/iio/machine.h>
> >+#include <linux/iio/driver.h>
> >+
> >+/*
> >+ * struct twl4030_madc_data - a container for madc info
> >+ * @dev - pointer to device structure for madc
> >+ * @lock - mutex protecting this data structure
> >+ * @requests - Array of request struct corresponding to SW1, SW2 and RT
> >+ * @imr - Interrupt mask register of MADC
> >+ * @isr - Interrupt status register of MADC
> >+ */
> >+struct twl4030_madc_data {
> >+	struct device *dev;
> >+	struct mutex lock;	/* mutex protecting this data structure */
> >+	struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
> >+	bool use_second_irq;
> 
> These are 32 bit signed values?

These were already defined as int in the original driver. I will
change these to u8 in an additional patch.

> >+	int imr;
> >+	int isr;
> >+};
> >+
> >+static int twl4030_madc_read(struct iio_dev *iio_dev,
> >+			     const struct iio_chan_spec *chan,
> >+			     int *val, int *val2, long mask)
> >+{
> >+	struct twl4030_madc_data *madc = iio_priv(iio_dev);
> >+	struct twl4030_madc_request req;
> >+	int channel = chan->channel;
> >+	int ret;
> >+
> >+	req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1;
> >+
> >+	req.channels = BIT(channel);
> >+	req.active = 0;
> >+	req.func_cb = NULL;
> req.raw = !(mask & IIO_CHAN_INFO_PROCESSED);
> req.do_avg = !!(mask & IIO_CHAN_INFO_AVERAGE_RAW);

Right.

> >+	req.raw = (mask & IIO_CHAN_INFO_PROCESSED) ? false : true;
> >+	req.do_avg = (mask & IIO_CHAN_INFO_AVERAGE_RAW) ? true : false;
> >+
> >+	ret = twl4030_madc_conversion(&req);
> >+	if (ret < 0)
> >+		return ret;
> >+
> >+	*val = req.rbuf[channel];
> >+
> >+	return IIO_VAL_INT;
> >+}
> >+
> >+static const struct iio_info twl4030_madc_iio_info = {
> >+	.read_raw = &twl4030_madc_read,
> >+	.driver_module = THIS_MODULE,
> >+};
> >+
> Please give this a prefixed name.  Chances of ADC_CHANNEL
> turning up in an IIO header is a little too high to do this
> without!  e.g. #define TWL4030_ADC_CHANNEL

Probably drivers/iio/adc/exynos_adc.c should also be fixed.

> >+#define ADC_CHANNEL(_channel, _type, _name, _mask) {	\
> >+	.type = _type,					\
> I don't think you use scan_type anywhere so don't bother specifying it.
> here.

ok.

> >+	.scan_type = IIO_ST('u', 10, 16, 0),		\
> >+	.channel = _channel,				\
> >+	.info_mask_separate = _mask,			\
> >+	.datasheet_name = _name,			\
> >+	.indexed = 1,					\
> >+}
> >+
> 
> Is the average have an adjustable number of samples?

The TWL supports reading a value directly (1 sample) or reading
the average of 4 samples.

> If not I'd be tempted to use the more common option of IIO_CHAN_INFO_RAW
> rather than the average version (tends only to be used in fairly obscure
> cases where both a raw access and an averaged one are available).

currently some drivers use the averaged read and some use the direct
read.

> >+static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
> >+	ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(1, IIO_TEMP, "ADCIN1", BIT(IIO_CHAN_INFO_PROCESSED) |
> >+					   BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10", BIT(IIO_CHAN_INFO_PROCESSED) |
> >+						BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+	ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15", BIT(IIO_CHAN_INFO_AVERAGE_RAW)),
> >+};
> >+
> Why the artificial limitation of one of these devices?  I guess this is to
> allow the exported function to work without needing to be associated with
> any particular device...  Hmm.

Artificial limitation? The madc is a ADC, which has some special
functions defined for some pins in the datasheet:

ADC0 = Battery Voltage
ADC1 = Battery Temperature
ADC10 = Battery Current

Nokia misused some of those pins on the Nokia N900, though. For
example they have their own conversion tables for the temperature,
which is not compatible with the generic one for the madc module.

I planned to convert the rx51-battery driver to simply read the raw
values. All other users can use the processed information.

> >+static struct twl4030_madc_data *twl4030_madc;
> >+
> >+struct twl4030_prescale_divider_ratios {
> >+	s16 numerator;
> >+	s16 denominator;
> >+};
> >+
> >+static const struct twl4030_prescale_divider_ratios
> >+twl4030_divider_ratios[16] = {
> >+	{1, 1},		/* CHANNEL 0 No Prescaler */
> >+	{1, 1},		/* CHANNEL 1 No Prescaler */
> >+	{6, 10},	/* CHANNEL 2 */
> >+	{6, 10},	/* CHANNEL 3 */
> >+	{6, 10},	/* CHANNEL 4 */
> >+	{6, 10},	/* CHANNEL 5 */
> >+	{6, 10},	/* CHANNEL 6 */
> >+	{6, 10},	/* CHANNEL 7 */
> >+	{3, 14},	/* CHANNEL 8 */
> >+	{1, 3},		/* CHANNEL 9 */
> >+	{1, 1},		/* CHANNEL 10 No Prescaler */
> >+	{15, 100},	/* CHANNEL 11 */
> >+	{1, 4},		/* CHANNEL 12 */
> >+	{1, 1},		/* CHANNEL 13 Reserved channels */
> >+	{1, 1},		/* CHANNEL 14 Reseved channels */
> >+	{5, 11},	/* CHANNEL 15 */
> >+};
> >+
> >+
> >+/*
> >+ * Conversion table from -3 to 55 degree Celcius
> >+ */
> >+static int therm_tbl[] = {
> >+30800,	29500,	28300,	27100,
> >+26000,	24900,	23900,	22900,	22000,	21100,	20300,	19400,	18700,	17900,
> >+17200,	16500,	15900,	15300,	14700,	14100,	13600,	13100,	12600,	12100,
> >+11600,	11200,	10800,	10400,	10000,	9630,	9280,	8950,	8620,	8310,
> >+8020,	7730,	7460,	7200,	6950,	6710,	6470,	6250,	6040,	5830,
> >+5640,	5450,	5260,	5090,	4920,	4760,	4600,	4450,	4310,	4170,
> >+4040,	3910,	3790,	3670,	3550
> >+};
> >+
> >+/*
> >+ * Structure containing the registers
> >+ * of different conversion methods supported by MADC.
> >+ * Hardware or RT real time conversion request initiated by external host
> >+ * processor for RT Signal conversions.
> >+ * External host processors can also request for non RT conversions
> >+ * SW1 and SW2 software conversions also called asynchronous or GPC request.
> >+ */
> >+static
> >+const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
> >+	[TWL4030_MADC_RT] = {
> >+			     .sel = TWL4030_MADC_RTSELECT_LSB,
> >+			     .avg = TWL4030_MADC_RTAVERAGE_LSB,
> >+			     .rbase = TWL4030_MADC_RTCH0_LSB,
> >+			     },
> >+	[TWL4030_MADC_SW1] = {
> >+			      .sel = TWL4030_MADC_SW1SELECT_LSB,
> >+			      .avg = TWL4030_MADC_SW1AVERAGE_LSB,
> >+			      .rbase = TWL4030_MADC_GPCH0_LSB,
> >+			      .ctrl = TWL4030_MADC_CTRL_SW1,
> >+			      },
> >+	[TWL4030_MADC_SW2] = {
> >+			      .sel = TWL4030_MADC_SW2SELECT_LSB,
> >+			      .avg = TWL4030_MADC_SW2AVERAGE_LSB,
> >+			      .rbase = TWL4030_MADC_GPCH0_LSB,
> >+			      .ctrl = TWL4030_MADC_CTRL_SW2,
> >+			      },
> >+};
> >+
> >+/*
> >+ * Function to read a particular channel value.
> >+ * @madc - pointer to struct twl4030_madc_data
> >+ * @reg - lsb of ADC Channel
> >+ * If the i2c read fails it returns an error else returns 0.
> >+ */
> >+static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
> >+{
> >+	u8 msb, lsb;
> >+	int ret;
> >+	/*
> >+	 * For each ADC channel, we have MSB and LSB register pair. MSB address
> >+	 * is always LSB address+1. reg parameter is the address of LSB register
> >+	 */
> >+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1);
> >+	if (ret) {
> >+		dev_err(madc->dev, "unable to read MSB register 0x%X\n",
> >+			reg + 1);
> >+		return ret;
> >+	}
> >+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg);
> >+	if (ret) {
> >+		dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg);
> >+		return ret;
> >+	}
> >+
> >+	return (int)(((msb << 8) | lsb) >> 6);
> >+}
> >+
> >+/*
> >+ * Return battery temperature
> >+ * Or < 0 on failure.
> >+ */
> >+static int twl4030battery_temperature(int raw_volt)
> >+{
> >+	u8 val;
> >+	int temp, curr, volt, res, ret;
> >+
> >+	volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R;
> >+	/* Getting and calculating the supply current in micro ampers */
> >+	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
> >+		REG_BCICTL2);
> >+	if (ret < 0)
> >+		return ret;
> blank line here and after similar error cases makes it a little easier to
> read.
> 
> >+	curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
> >+	/* Getting and calculating the thermistor resistance in ohms */
> >+	res = volt * 1000 / curr;
> >+	/* calculating temperature */
> >+	for (temp = 58; temp >= 0; temp--) {
> >+		int actual = therm_tbl[temp];
> >+
> No blank line here.

I added the newline to the style fix patch, since these lines were
not touched by me before.

> >+		if ((actual - res) >= 0)
> >+			break;
> >+	}
> >+
> >+	return temp + 1;
> >+}
> >+
> >+static int twl4030battery_current(int raw_volt)
> >+{
> >+	int ret;
> >+	u8 val;
> >+
> >+	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
> >+		TWL4030_BCI_BCICTL1);
> >+	if (ret)
> >+		return ret;
> >+	if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */
> >+		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1;
> >+	else /* slope of 0.88 mV/mA */
> >+		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
> >+}
> blank line here please.

Also added to the style fix patch.

> Various comments on this in the next function:
> This would be much simpler if any error immediately resulted in an
> exit with error code.  If it's gone wrong enough to issue a dev_err
> then don't try to muddle through.  Given there is nothing to clear
> up in here, just error out directly on the first problem.

I guess this should be fixed once the old API is removed.

> >+/*
> >+ * Function to read channel values
> >+ * @madc - pointer to twl4030_madc_data struct
> >+ * @reg_base - Base address of the first channel
> >+ * @Channels - 16 bit bitmap. If the bit is set, channel value is read
> >+ * @buf - The channel values are stored here. if read fails error
> >+ * @raw - Return raw values without conversion
> >+ * value is stored
> >+ * Returns the number of successfully read channels.
> >+ */
> >+static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
> >+				      u8 reg_base, unsigned
> >+				      long channels, int *buf,
> >+				      bool raw)
> >+{
> >+	int count = 0, count_req = 0, i;
> >+	u8 reg;
> >+
> >+	for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
> Skip the temporary for reg.  Yes, it gets used twice, but easier to read
> inline.

Also fixed in the style patch.

> >+		reg = reg_base + 2 * i;
> >+		buf[i] = twl4030_madc_channel_raw_read(madc, reg);
> >+		if (buf[i] < 0) {
> >+			dev_err(madc->dev,
> >+				"Unable to read register 0x%X\n", reg);
> >+			count_req++;
> >+			continue;
> >+		}
> I'd prefer this as
> if (raw) {
>   count ++;
> } else {
>   switch (i) {
> ..
> }

That increases the code indention by another level. I don't think
that's a good idea.

> Also near as I can tell count gets incremented unless there is an error
> anyway. So just increment it outside and allow this function to return the
> error code.

Returning error codes would change the existing API. I would prefer
to do this change after the old API is removed.

> >+		if (raw) {
> >+			count++;
> >+			continue;
> >+		}
> >+		switch (i) {
> These cases could do with a suitable #define to give them a name.

TWL4030_MADC_ADCIN10 is already in use for (1 << 10). I guess this
can be simplified more easily once the old API is removed.

> >+		case 10:
> >+			buf[i] = twl4030battery_current(buf[i]);
> >+			if (buf[i] < 0) {
> >+				dev_err(madc->dev, "err reading current\n");
> >+				count_req++;
> >+			} else {
> >+				count++;
> >+				buf[i] = buf[i] - 750;
> >+			}
> >+			break;
> >+		case 1:
> >+			buf[i] = twl4030battery_temperature(buf[i]);
> >+			if (buf[i] < 0) {
> >+				dev_err(madc->dev, "err reading temperature\n");
> >+				count_req++;
> >+			} else {
> >+				buf[i] -= 3;
> >+				count++;
> >+			}
> >+			break;
> >+		default:
> >+			count++;
> >+			/* Analog Input (V) = conv_result * step_size / R
> >+			 * conv_result = decimal value of 10-bit conversion
> >+			 *		 result
> >+			 * step size = 1.5 / (2 ^ 10 -1)
> >+			 * R = Prescaler ratio for input channels.
> >+			 * Result given in mV hence multiplied by 1000.
> >+			 */
> >+			buf[i] = (buf[i] * 3 * 1000 *
> >+				 twl4030_divider_ratios[i].denominator)
> >+				/ (2 * 1023 *
> >+				twl4030_divider_ratios[i].numerator);
> >+		}
> >+	}
> This is already apparant from the errors emmited earlier. I'd drop this
> and the count_req counter in general.
> Personally I'd error out in those cases anyway rather than carrying on
> with the rest of the channels. If it's worth of a dev_err it's worthy
> of getting out as fast as possible rather than trying to muddle on.

I also think this should be postponed until the old API is removed.

> >+	if (count_req)
> >+		dev_err(madc->dev, "%d channel conversion failed\n", count_req);
> >+
> >+	return count;
> >+}
> >+
> >+/*
> >+ * Enables irq.
> >+ * @madc - pointer to twl4030_madc_data struct
> >+ * @id - irq number to be enabled
> >+ * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
> >+ * corresponding to RT, SW1, SW2 conversion requests.
> >+ * If the i2c read fails it returns an error else returns 0.
> >+ */
> >+static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id)
> >+{
> >+	u8 val;
> >+	int ret;
> >+
> >+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
> >+	if (ret) {
> >+		dev_err(madc->dev, "unable to read imr register 0x%X\n",
> >+			madc->imr);
> >+		return ret;
> >+	}
> A blank line here and in similar cases would slight aid readability.



> >+	val &= ~(1 << id);
> >+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
> >+	if (ret) {
> >+		dev_err(madc->dev,
> >+			"unable to write imr register 0x%X\n", madc->imr);
> >+		return ret;
> >+
> Loose blank line here.

both fixed in the style patch.

> >+	}
> >+
> >+	return 0;
> >+}
> >+
> >+/*
> >+ * Disables irq.
> >+ * @madc - pointer to twl4030_madc_data struct
> >+ * @id - irq number to be disabled
> >+ * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
> >+ * corresponding to RT, SW1, SW2 conversion requests.
> >+ * Returns error if i2c read/write fails.
> >+ */
> >+static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id)
> >+{
> >+	u8 val;
> >+	int ret;
> >+
> >+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
> >+	if (ret) {
> >+		dev_err(madc->dev, "unable to read imr register 0x%X\n",
> >+			madc->imr);
> >+		return ret;
> >+	}
> >+	val |= (1 << id);
> >+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
> >+	if (ret) {
> >+		dev_err(madc->dev,
> >+			"unable to write imr register 0x%X\n", madc->imr);
> >+		return ret;
> >+	}
> >+
> >+	return 0;
> >+}
> >+
> >+static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
> >+{
> >+	struct twl4030_madc_data *madc = _madc;
> >+	const struct twl4030_madc_conversion_method *method;
> >+	u8 isr_val, imr_val;
> >+	int i, len, ret;
> >+	struct twl4030_madc_request *r;
> >+
> >+	mutex_lock(&madc->lock);
> >+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr);
> >+	if (ret) {
> >+		dev_err(madc->dev, "unable to read isr register 0x%X\n",
> >+			madc->isr);
> >+		goto err_i2c;
> >+	}
> >+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr);
> >+	if (ret) {
> >+		dev_err(madc->dev, "unable to read imr register 0x%X\n",
> >+			madc->imr);
> >+		goto err_i2c;
> >+	}
> >+	isr_val &= ~imr_val;
> >+	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
> >+		if (!(isr_val & (1 << i)))
> >+			continue;
> >+		ret = twl4030_madc_disable_irq(madc, i);
> >+		if (ret < 0)
> >+			dev_dbg(madc->dev, "Disable interrupt failed%d\n", i);
> >+		madc->requests[i].result_pending = 1;
> >+	}
> Can this and the previous loop not be combined thus simplifying some tests?

It probably can be, but I think this can wait until the old API is
removed.

> >+	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
> >+		r = &madc->requests[i];
> >+		/* No pending results for this method, move to next one */
> >+		if (!r->result_pending)
> >+			continue;
> >+		method = &twl4030_conversion_methods[r->method];
> >+		/* Read results */
> >+		len = twl4030_madc_read_channels(madc, method->rbase,
> >+						 r->channels, r->rbuf, r->raw);
> >+		/* Return results to caller */
> >+		if (r->func_cb != NULL) {
> >+			r->func_cb(len, r->channels, r->rbuf);
> >+			r->func_cb = NULL;
> >+		}
> >+		/* Free request */
> >+		r->result_pending = 0;
> >+		r->active = 0;
> >+	}
> >+	mutex_unlock(&madc->lock);
> >+
> >+	return IRQ_HANDLED;
> >+
> >+err_i2c:
> >+	/*
> >+	 * In case of error check whichever request is active
> >+	 * and service the same.
> >+	 */
> >+	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
> >+		r = &madc->requests[i];
> >+		if (r->active == 0)
> >+			continue;
> >+		method = &twl4030_conversion_methods[r->method];
> >+		/* Read results */
> >+		len = twl4030_madc_read_channels(madc, method->rbase,
> >+						 r->channels, r->rbuf, r->raw);
> >+		/* Return results to caller */
> >+		if (r->func_cb != NULL) {
> >+			r->func_cb(len, r->channels, r->rbuf);
> >+			r->func_cb = NULL;
> >+		}
> >+		/* Free request */
> >+		r->result_pending = 0;
> >+		r->active = 0;
> >+	}
> >+	mutex_unlock(&madc->lock);
> >+
> >+	return IRQ_HANDLED;
> >+}
> >+
> This structure does seem a little complex.  I'd have been more tempted
> by using a completion and having the calling function wait on it.  How
> often does an actual callback make sense?

This is part of the old API, so let's convert all consumers to the
IIO API and deprecate the callback stuff.

> >+static int twl4030_madc_set_irq(struct twl4030_madc_data *madc,
> >+				struct twl4030_madc_request *req)
> >+{
> >+	struct twl4030_madc_request *p;
> >+	int ret;
> >+
> >+	p = &madc->requests[req->method];
> >+	memcpy(p, req, sizeof(*req));
> >+	ret = twl4030_madc_enable_irq(madc, req->method);
> >+	if (ret < 0) {
> >+		dev_err(madc->dev, "enable irq failed!!\n");
> >+		return ret;
> >+	}
> >+
> >+	return 0;
> >+}
> >+
> >+/*
> >+ * Function which enables the madc conversion
> >+ * by writing to the control register.
> >+ * @madc - pointer to twl4030_madc_data struct
> >+ * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1
> >+ * corresponding to RT SW1 or SW2 conversion methods.
> >+ * Returns 0 if succeeds else a negative error value
> >+ */
> >+static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
> >+					 int conv_method)
> >+{
> >+	const struct twl4030_madc_conversion_method *method;
> >+	int ret = 0;
> >+	method = &twl4030_conversion_methods[conv_method];
> >+	switch (conv_method) {
> Can we get here via any paths where these aren't the methods set?

conv_method is set from outside of the driver, so it's probably
safer to check. This can be simplified once the old API is removed.

> >+	case TWL4030_MADC_SW1:
> >+	case TWL4030_MADC_SW2:
> >+		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
> >+				       TWL4030_MADC_SW_START, method->ctrl);
> >+		if (ret) {
> >+			dev_err(madc->dev,
> >+				"unable to write ctrl register 0x%X\n",
> >+				method->ctrl);
> >+			return ret;
> >+		}
> >+		break;
> >+	default:
> >+		break;
> >+	}
> >+
> >+	return 0;
> >+}
> >+
> Could we fix up the kernel doc in this file in general. It's so nearly
> there but not quite!

I assume, that the driver will get a huge cleanup once all consumers
of the old API are converted. Maybe a deprecation flag should be
added together with this patchset?

> >+/*
> >+ * Function that waits for conversion to be ready
> >+ * @madc - pointer to twl4030_madc_data struct
> >+ * @timeout_ms - timeout value in milliseconds
> >+ * @status_reg - ctrl register
> >+ * returns 0 if succeeds else a negative error value
> >+ */
> >+static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc,
> >+					      unsigned int timeout_ms,
> >+					      u8 status_reg)
> >+{
> >+	unsigned long timeout;
> >+	int ret;
> >+
> >+	timeout = jiffies + msecs_to_jiffies(timeout_ms);
> >+	do {
> >+		u8 reg;
> >+
> >+		ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &reg, status_reg);
> >+		if (ret) {
> >+			dev_err(madc->dev,
> >+				"unable to read status register 0x%X\n",
> >+				status_reg);
> >+			return ret;
> >+		}
> >+		if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW))
> >+			return 0;
> >+		usleep_range(500, 2000);
> >+	} while (!time_after(jiffies, timeout));
> >+	dev_err(madc->dev, "conversion timeout!\n");
> >+
> >+	return -EAGAIN;
> >+}
> >+
> 
> >+/*
> >+ * An exported function which can be called from other kernel drivers.
> What uses this currently?  Ideally we'd do this through the standard IIO paths
> for in kernel users.  Right now the device tree mappings for that are less
> than ideal (ask Mark Rutland if you want some choice comments ;)  Perhaps
> we take the view this can be cleaned up later. There are some elements in here
> we haven't yet looked at supporting for client drivers.  Will have to think
> about that.
> 
> It would be particularly nice if possible to use a generic battery driver
> for the two cases that seem to be using this functionality at the moment.

Currently there are 3 consumer drivers using this:

* twl4030-madc-battery
* rx51-battery
* twl4030-madc-hwmon

I guess the first two can be updated to use the IIO API and the last
one can be replaced by the IIO hwmon driver. Since it's an
in-kernel-api there should be no problems to simply remove the API
afterwards.

> >+ * @req twl4030_madc_request structure
> >+ * req->rbuf will be filled with read values of channels based on the
> >+ * channel index. If a particular channel reading fails there will
> >+ * be a negative error value in the corresponding array element.
> >+ * returns 0 if succeeds else error value
> >+ */
> >+int twl4030_madc_conversion(struct twl4030_madc_request *req)
> >+{
> >+	const struct twl4030_madc_conversion_method *method;
> >+	u8 ch_msb, ch_lsb;
> >+	int ret;
> >+
> >+	if (!req || !twl4030_madc)
> >+		return -EINVAL;
> >+
> >+	mutex_lock(&twl4030_madc->lock);
> >+	if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) {
> >+		ret = -EINVAL;
> >+		goto out;
> >+	}
> >+	/* Do we have a conversion request ongoing */
> >+	if (twl4030_madc->requests[req->method].active) {
> >+		ret = -EBUSY;
> >+		goto out;
> >+	}
> >+	ch_msb = (req->channels >> 8) & 0xff;
> >+	ch_lsb = req->channels & 0xff;
> >+	method = &twl4030_conversion_methods[req->method];
> >+	/* Select channels to be converted */
> >+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1);
> >+	if (ret) {
> >+		dev_err(twl4030_madc->dev,
> >+			"unable to write sel register 0x%X\n", method->sel + 1);
> >+		goto out;
> >+	}
> >+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel);
> >+	if (ret) {
> >+		dev_err(twl4030_madc->dev,
> >+			"unable to write sel register 0x%X\n", method->sel + 1);
> >+		goto out;
> >+	}
> >+	/* Select averaging for all channels if do_avg is set */
> >+	if (req->do_avg) {
> >+		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
> >+				       ch_msb, method->avg + 1);
> >+		if (ret) {
> >+			dev_err(twl4030_madc->dev,
> >+				"unable to write avg register 0x%X\n",
> >+				method->avg + 1);
> >+			goto out;
> >+		}
> >+		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
> >+				       ch_lsb, method->avg);
> >+		if (ret) {
> >+			dev_err(twl4030_madc->dev,
> Excesive error messages are sometimes irritating as they break up code
> flow.  They are really irritating when incorrect ;)

Added to the fixes patch.

> >+				"unable to write sel reg 0x%X\n",
> >+				method->sel + 1);
> >+			goto out;
> >+		}
> >+	}
> >+	if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) {
> >+		ret = twl4030_madc_set_irq(twl4030_madc, req);
> >+		if (ret < 0)
> >+			goto out;
> >+		ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
> >+		if (ret < 0)
> >+			goto out;
> >+		twl4030_madc->requests[req->method].active = 1;
> >+		ret = 0;
> >+		goto out;
> >+	}
> Is it possible to get here?  This comment suggests not. If so, why have
> the test?

I guess it's an additional safety check. This function can be
simplified a lot once the old API is removed, so I guess this
can also be postponed.

> >+	/* With RT method we should not be here anymore */
> >+	if (req->method == TWL4030_MADC_RT) {
> >+		ret = -EINVAL;
> >+		goto out;
> >+	}
> >+	ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
> >+	if (ret < 0)
> >+		goto out;
> >+	twl4030_madc->requests[req->method].active = 1;
> >+	/* Wait until conversion is ready (ctrl register returns EOC) */
> So no interrupts in this case?
> >+	ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl);
> >+	if (ret) {
> >+		twl4030_madc->requests[req->method].active = 0;
> >+		goto out;
> >+	}
> >+	ret = twl4030_madc_read_channels(twl4030_madc, method->rbase,
> >+					 req->channels, req->rbuf, req->raw);
> >+	twl4030_madc->requests[req->method].active = 0;
> >+
> >+out:
> >+	mutex_unlock(&twl4030_madc->lock);
> >+
> >+	return ret;
> >+}
> >+EXPORT_SYMBOL_GPL(twl4030_madc_conversion);
> >+
> >+/*
> Proper kerneldoc would be nice here.  Otherwise the comment doesn't
> really add anything so I'd be tempted to drop it ;)

removed in fixes patch.

> >+ * Return channel value
> >+ * Or < 0 on failure.
> >+ */
> >+int twl4030_get_madc_conversion(int channel_no)
> >+{
> >+	struct twl4030_madc_request req;
> >+	int temp = 0;
> >+	int ret;
> >+
> >+	req.channels = (1 << channel_no);
> >+	req.method = TWL4030_MADC_SW2;
> >+	req.active = 0;
> >+	req.func_cb = NULL;
> >+	ret = twl4030_madc_conversion(&req);
> >+	if (ret < 0)
> >+		return ret;
> >+	if (req.rbuf[channel_no] > 0)
> >+		temp = req.rbuf[channel_no];
> >+
> >+	return temp;
> >+}
> >+EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
> >+
> >+/*
> >+ * Function to enable or disable bias current for
> >+ * main battery type reading or temperature sensing
> >+ * @madc - pointer to twl4030_madc_data struct
> >+ * @chan - can be one of the two values
> What is a battery type reading?  Voltage? Current? Charge?

It's used for battery type identification.

> >+ * TWL4030_BCI_ITHEN - Enables bias current for main battery type reading
> >+ * TWL4030_BCI_TYPEN - Enables bias current for main battery temperature
> These constants have rather incomprehensible names. Whilst I gues they
> are straight off the data sheet, seems to me that we could make them a little
> longer and easier to follow!  The comment here helps, but sensible names
> would be better!

The names come from the datasheet. I think we should fix this after
the old API is removed, since the constants are exposed for the API.

> >+ * sensing
> >+ * @on - enable or disable chan.
> >+ */
> >+static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
> >+					      int chan, int on)
> >+{
> >+	int ret;
> >+	u8 regval;
> >+
> >+	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
> >+			      &regval, TWL4030_BCI_BCICTL1);
> >+	if (ret) {
> >+		dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X",
> >+			TWL4030_BCI_BCICTL1);
> >+		return ret;
> >+	}
> Introduce an intermediate variable and this could be nice and easy to read
> Say
> 
> int regmask;
> if (chan == 0)
>    regmask = TWL4030_BCI_TYPEN;
> else
>    regmask = TWL4030_BFI_ITHEN;
> 
> if (on)
>    regval |= regmask;
> else
>    regval &= ~regmask;
> 
> It's longer but doesn't give me a headache.

I added this to the fixes patch:

    int regmask;
	regmask = (chan == 0) ? TWL4030_BCI_TYPEN : TWL4030_BFI_ITHEN;
	if (on)
		regval |= regmask;
	else
		regval &= ~regmask;

> >+	if (on)
> >+		regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
> >+	else
> >+		regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN;
> >+	ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
> >+			       regval, TWL4030_BCI_BCICTL1);
> >+	if (ret) {
> >+		dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n",
> >+			TWL4030_BCI_BCICTL1);
> >+		return ret;
> >+	}
> >+
> >+	return 0;
> >+}
> >+
> >+/*
> >+ * Function that sets MADC software power on bit to enable MADC
> >+ * @madc - pointer to twl4030_madc_data struct
> >+ * @on - Enable or disable MADC software powen on bit.
> >+ * returns error if i2c read/write fails else 0
> >+ */
> >+static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
> >+{
> >+	u8 regval;
> >+	int ret;
> >+
> >+	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
> >+			      &regval, TWL4030_MADC_CTRL1);
> >+	if (ret) {
> >+		dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n",
> >+			TWL4030_MADC_CTRL1);
> >+		return ret;
> >+	}
> >+	if (on)
> >+		regval |= TWL4030_MADC_MADCON;
> >+	else
> >+		regval &= ~TWL4030_MADC_MADCON;
> >+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1);
> >+	if (ret) {
> >+		dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n",
> >+			TWL4030_MADC_CTRL1);
> >+		return ret;
> >+	}
> >+
> >+	return 0;
> >+}
> >+
> >+/*
> >+ * Initialize MADC and request for threaded irq
> >+ */
> >+static int twl4030_madc_probe(struct platform_device *pdev)
> >+{
> >+	struct twl4030_madc_data *madc;
> >+	struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
> >+	struct device_node *np = pdev->dev.of_node;
> >+	int irq, ret;
> >+	u8 regval;
> >+	struct iio_dev *iio_dev = NULL;
> >+
> >+	if (!pdata && !np) {
> >+		dev_err(&pdev->dev, "platform_data not available\n");
> >+		return -EINVAL;
> >+	}
> >+
> >+	iio_dev = devm_iio_device_alloc(&pdev->dev,
> >+					sizeof(struct twl4030_madc_data));
> >+	if (!iio_dev) {
> >+		dev_err(&pdev->dev, "failed allocating iio device\n");
> >+		return -ENOMEM;
> >+	}
> >+
> >+	madc = iio_priv(iio_dev);
> >+	madc->dev = &pdev->dev;
> >+
> >+	iio_dev->name = dev_name(&pdev->dev);
> >+	iio_dev->dev.parent = &pdev->dev;
> >+	iio_dev->dev.of_node = pdev->dev.of_node;
> >+	iio_dev->info = &twl4030_madc_iio_info;
> >+	iio_dev->modes = INDIO_DIRECT_MODE;
> >+	iio_dev->channels = twl4030_madc_iio_channels;
> >+	iio_dev->num_channels = 16;
> >+
> >+	/*
> >+	 * Phoenix provides 2 interrupt lines. The first one is connected to
> >+	 * the OMAP. The other one can be connected to the other processor such
> >+	 * as modem. Hence two separate ISR and IMR registers.
> >+	 */
> >+	if (pdata)
> >+		madc->use_second_irq = pdata->irq_line != 1;
> >+	else
> >+		madc->use_second_irq = false;
> >+
> >+	madc->imr = (madc->use_second_irq == 1) ?
> >+	    TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
> >+	madc->isr = (madc->use_second_irq == 1) ?
> >+	    TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
> >+
> >+	ret = twl4030_madc_set_power(madc, 1);
> >+	if (ret < 0)
> >+		return ret;
> >+	ret = twl4030_madc_set_current_generator(madc, 0, 1);
> >+	if (ret < 0)
> >+		goto err_current_generator;
> >+
> >+	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
> >+			      &regval, TWL4030_BCI_BCICTL1);
> >+	if (ret) {
> >+		dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n",
> >+			TWL4030_BCI_BCICTL1);
> >+		goto err_i2c;
> >+	}
> >+	regval |= TWL4030_BCI_MESBAT;
> >+	ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
> >+			       regval, TWL4030_BCI_BCICTL1);
> >+	if (ret) {
> >+		dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n",
> >+			TWL4030_BCI_BCICTL1);
> >+		goto err_i2c;
> >+	}
> >+
> >+	/* Check that MADC clock is on */
> >+	ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &regval, TWL4030_REG_GPBR1);
> >+	if (ret) {
> Personally I'd rank the driver as rather to vebose on read error messages
> given they are pretty unusual.  Ah well I guess each to their own.

All of this is not written by me.

> >+		dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n",
> >+				TWL4030_REG_GPBR1);
> >+		goto err_i2c;
> >+	}
> >+
> >+	/* If MADC clk is not on, turn it on */
> >+	if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) {
> >+		dev_info(&pdev->dev, "clk disabled, enabling\n");
> >+		regval |= TWL4030_GPBR1_MADC_HFCLK_EN;
> >+		ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval,
> >+				       TWL4030_REG_GPBR1);
> >+		if (ret) {
> >+			dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n",
> >+					TWL4030_REG_GPBR1);
> >+			goto err_i2c;
> >+		}
> >+	}
> >+
> >+	platform_set_drvdata(pdev, iio_dev);
> >+	mutex_init(&madc->lock);
> >+
> >+	irq = platform_get_irq(pdev, 0);
> 
> As ever using devm for a irq request makes for a bit of a review
> nightmare as we have to be very careful that nothing has been removed
> that this might use before the devm cleanup occurs (at end of remove).
> In this case the fact that the current generator is off and the adc unit
> is disabled sounds to me like it might cause interesting results in the
> interrupt handler?
> 
> It's one of those things that will probably never happen but I find it
> hard to convince myself 'can't happen'
> 
> >+	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
> >+				   twl4030_madc_threaded_irq_handler,
> >+				   IRQF_TRIGGER_RISING, "twl4030_madc", madc);
> >+	if (ret) {
> >+		dev_dbg(&pdev->dev, "could not request irq\n");
> >+		goto err_i2c;
> >+	}
> 
> Err, where is this defined?  Ah, found it above.  I'm not keen on
> preventing multiple instances of a device like this.  It is so easy
> to not do this I'd prefer to see the driver fixed to remove this.

This concept has also not introduced by me of course. I think we
should fix this after removal of the old API, since the old API does
not have an device parameter.

> >+	twl4030_madc = madc;
> >+
> >+	ret = iio_device_register(iio_dev);
> >+	if (ret) {
> >+		dev_dbg(&pdev->dev, "could not register iio device\n");
> >+		goto err_i2c;
> >+	}
> >+
> >+	return 0;
> >+
> >+err_i2c:
> >+	twl4030_madc_set_current_generator(madc, 0, 0);
> >+err_current_generator:
> >+	twl4030_madc_set_power(madc, 0);
> >+	return ret;
> >+}
> >+
> >+static int twl4030_madc_remove(struct platform_device *pdev)
> >+{
> >+	struct iio_dev *iio_dev = platform_get_drvdata(pdev);
> >+	struct twl4030_madc_data *madc = iio_priv(iio_dev);
> >+
> >+	twl4030_madc_set_current_generator(madc, 0, 0);
> >+	twl4030_madc_set_power(madc, 0);
> >+
> >+	iio_device_unregister(iio_dev);
> >+
> >+	return 0;
> >+}
> >+
> >+#ifdef CONFIG_OF
> >+static const struct of_device_id twl_madc_of_match[] = {
> >+	{.compatible = "ti,twl4030-madc", },
> >+	{ },
> >+};
> >+MODULE_DEVICE_TABLE(of, twl_madc_of_match);
> >+#endif
> >+
> >+static struct platform_driver twl4030_madc_driver = {
> >+	.probe = twl4030_madc_probe,
> >+	.remove = twl4030_madc_remove,
> >+	.driver = {
> >+		   .name = "twl4030_madc",
> >+		   .owner = THIS_MODULE,
> >+		   .of_match_table = of_match_ptr(twl_madc_of_match),
> >+		   },
> >+};
> >+
> >+module_platform_driver(twl4030_madc_driver);
> >+
> >+MODULE_DESCRIPTION("TWL4030 ADC driver");
> >+MODULE_LICENSE("GPL");
> >+MODULE_AUTHOR("J Keerthy");
> >+MODULE_ALIAS("platform:twl4030_madc");
> 
> Rest of patch was removal of code so no comments!

FYI: For me it was a bit odd to see inline comments about code lines
coming after the inline comment. Normally I receive patch reviews
with comments after the referenced code, which makes more sense imho.

-- Sebastian

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox