* Re: [PATCH] of/platform: dynamic: Use of_platform_bus_create() to create devices
From: Rob Herring @ 2017-01-21 19:51 UTC (permalink / raw)
To: Alexander Sverdlin
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Frank Rowand,
Pantelis Antoniou, Grant Likely
In-Reply-To: <3da6854d-8361-1724-2a8a-36f28449733e-xNZwKgViW5gAvxtiuMwx3w@public.gmane.org>
On Fri, Jan 20, 2017 at 11:38 AM, Alexander Sverdlin
<alexander.sverdlin-xNZwKgViW5gAvxtiuMwx3w@public.gmane.org> wrote:
> Hello Rob,
>
> On 27/10/16 15:00, Rob Herring wrote:
>>> @@ -534,15 +535,15 @@ static int of_platform_notify(struct notifier_block *nb,
>>>
>>> /* pdev_parent may be NULL when no bus platform device */
>>> pdev_parent = of_find_device_by_node(rd->dn->parent);
>>> - pdev = of_platform_device_create(rd->dn, NULL,
>>> - pdev_parent ? &pdev_parent->dev : NULL);
>>> + ret = of_platform_bus_create(rd->dn, of_default_bus_match_table,
>>> + NULL, pdev_parent ?
>>> + &pdev_parent->dev : NULL, true);
>> I think this should be of_platform_default_populate() instead.
>
> I've just tested: it cannot be. It calls of_platform_populate(), which ignores "status" property.
> This not only brakes half of the unit tests, but also would change the existing behavior of
> overlays (they will completely ignore "status" too).
That's a bug. I assume that's only at the 1st level or things would be
completely broken.
Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 2/2] hwmon: (lm70) Add support for TI TMP122/124
From: Florian Fainelli @ 2017-01-21 19:20 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Florian Fainelli, Rob Herring, Mark Rutland, Jean Delvare,
Guenter Roeck, Jonathan Corbet,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list:HARDWARE MONITORING, open list:DOCUMENTATION
In-Reply-To: <20170121192010.30681-1-f.fainelli-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Add support for Texas Instruments TMP122/124 which are nearly identical to
their TMP121/123 except that they also support programmable temperature
thresholds.
Signed-off-by: Florian Fainelli <f.fainelli-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
Documentation/devicetree/bindings/hwmon/lm70.txt | 1 +
Documentation/hwmon/lm70 | 8 ++++++--
drivers/hwmon/lm70.c | 9 ++++++++-
3 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/hwmon/lm70.txt b/Documentation/devicetree/bindings/hwmon/lm70.txt
index e7fd921aa4f1..ea417a0d32af 100644
--- a/Documentation/devicetree/bindings/hwmon/lm70.txt
+++ b/Documentation/devicetree/bindings/hwmon/lm70.txt
@@ -4,6 +4,7 @@ Required properties:
- compatible: one of
"ti,lm70"
"ti,tmp121"
+ "ti,tmp122"
"ti,lm71"
"ti,lm74"
diff --git a/Documentation/hwmon/lm70 b/Documentation/hwmon/lm70
index 1bb2db440671..c3a1f2ea017d 100644
--- a/Documentation/hwmon/lm70
+++ b/Documentation/hwmon/lm70
@@ -6,6 +6,8 @@ Supported chips:
Datasheet: http://www.national.com/pf/LM/LM70.html
* Texas Instruments TMP121/TMP123
Information: http://focus.ti.com/docs/prod/folders/print/tmp121.html
+ * Texas Instruments TMP122/TMP124
+ Information: http://www.ti.com/product/tmp122
* National Semiconductor LM71
Datasheet: http://www.ti.com/product/LM71
* National Semiconductor LM74
@@ -35,8 +37,10 @@ As a real (in-tree) example of this "SPI protocol driver" interfacing
with a "SPI master controller driver", see drivers/spi/spi_lm70llp.c
and its associated documentation.
-The LM74 and TMP121/TMP123 are very similar; main difference is 13-bit
-temperature data (0.0625 degrees celsius resolution).
+The LM74 and TMP121/TMP122/TMP123/TMP124 are very similar; main difference is
+13-bit temperature data (0.0625 degrees celsius resolution).
+
+The TMP122/TMP124 also feature configurable temperature thresholds.
The LM71 is also very similar; main difference is 14-bit temperature
data (0.03125 degrees celsius resolution).
diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c
index 52c5cdd00448..543556dc563b 100644
--- a/drivers/hwmon/lm70.c
+++ b/drivers/hwmon/lm70.c
@@ -46,6 +46,7 @@
#define LM70_CHIP_TMP121 1 /* TI TMP121/TMP123 */
#define LM70_CHIP_LM71 2 /* NS LM71 */
#define LM70_CHIP_LM74 3 /* NS LM74 */
+#define LM70_CHIP_TMP122 4 /* TI TMP122/TMP124 */
struct lm70 {
struct spi_device *spi;
@@ -92,7 +93,7 @@ static ssize_t temp1_input_show(struct device *dev,
* Celsius.
* So it's equivalent to multiplying by 0.25 * 1000 = 250.
*
- * LM74 and TMP121/TMP123:
+ * LM74 and TMP121/TMP122/TMP123/TMP124:
* 13 bits of 2's complement data, discard LSB 3 bits,
* resolution 0.0625 degrees celsius.
*
@@ -106,6 +107,7 @@ static ssize_t temp1_input_show(struct device *dev,
break;
case LM70_CHIP_TMP121:
+ case LM70_CHIP_TMP122:
case LM70_CHIP_LM74:
val = ((int)raw / 8) * 625 / 10;
break;
@@ -143,6 +145,10 @@ static const struct of_device_id lm70_of_ids[] = {
.data = (void *) LM70_CHIP_TMP121,
},
{
+ .compatible = "ti,tmp122",
+ .data = (void *) LM70_CHIP_TMP122,
+ },
+ {
.compatible = "ti,lm71",
.data = (void *) LM70_CHIP_LM71,
},
@@ -191,6 +197,7 @@ static int lm70_probe(struct spi_device *spi)
static const struct spi_device_id lm70_ids[] = {
{ "lm70", LM70_CHIP_LM70 },
{ "tmp121", LM70_CHIP_TMP121 },
+ { "tmp122", LM70_CHIP_TMP122 },
{ "lm71", LM70_CHIP_LM71 },
{ "lm74", LM70_CHIP_LM74 },
{ },
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 1/2] hwmon: (lm70) Utilize dev_warn instead of pr_warn
From: Florian Fainelli @ 2017-01-21 19:20 UTC (permalink / raw)
To: linux-kernel
Cc: Florian Fainelli, Rob Herring, Mark Rutland, Jean Delvare,
Guenter Roeck, Jonathan Corbet,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list:HARDWARE MONITORING, open list:DOCUMENTATION
In-Reply-To: <20170121192010.30681-1-f.fainelli@gmail.com>
We have a device reference, utilize it instead of pr_warn().
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/hwmon/lm70.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c
index d6ecd1a4be59..52c5cdd00448 100644
--- a/drivers/hwmon/lm70.c
+++ b/drivers/hwmon/lm70.c
@@ -72,7 +72,8 @@ static ssize_t temp1_input_show(struct device *dev,
*/
status = spi_write_then_read(spi, NULL, 0, &rxbuf[0], 2);
if (status < 0) {
- pr_warn("spi_write_then_read failed with status %d\n", status);
+ dev_warn(dev, "spi_write_then_read failed with status %d\n",
+ status);
goto out;
}
raw = (rxbuf[0] << 8) + rxbuf[1];
--
2.9.3
^ permalink raw reply related
* [PATCH 0/2] hwmon: (lm70) Couple patches
From: Florian Fainelli @ 2017-01-21 19:20 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Florian Fainelli, Rob Herring, Mark Rutland, Jean Delvare,
Guenter Roeck, Jonathan Corbet,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list:HARDWARE MONITORING, open list:DOCUMENTATION
Hi Guenter,
Here are a couple of patches against lm70. I plan on submitting
support for configurable thresholds on TI TMP122/124 once I have
resolved the lack SPI_3WIRE on the controller that connects to
this chip on my board.
Thanks!
Florian Fainelli (2):
hwmon: (lm70) Utilize dev_warn instead of pr_warn
hwmon: (lm70) Add support for TI TMP122/124
Documentation/devicetree/bindings/hwmon/lm70.txt | 1 +
Documentation/hwmon/lm70 | 8 ++++++--
drivers/hwmon/lm70.c | 12 ++++++++++--
3 files changed, 17 insertions(+), 4 deletions(-)
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RESENT PATCH] ARM: bcm2835: Add devicetree for the Raspberry Pi 3, for arm (32)
From: Eric Anholt @ 2017-01-21 18:42 UTC (permalink / raw)
To: linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Gerd Hoffmann, Rob Herring, Mark Rutland, Russell King,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
moderated list:ARM PORT, open list
In-Reply-To: <1484845444-4617-1-git-send-email-kraxel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 928 bytes --]
Gerd Hoffmann <kraxel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> writes:
> Signed-off-by: Gerd Hoffmann <kraxel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> ---
> arch/arm/boot/dts/Makefile | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
> index 7327250..82a760d 100644
> --- a/arch/arm/boot/dts/Makefile
> +++ b/arch/arm/boot/dts/Makefile
> @@ -70,7 +70,8 @@ dtb-$(CONFIG_ARCH_BCM2835) += \
> bcm2835-rpi-b-plus.dtb \
> bcm2835-rpi-a-plus.dtb \
> bcm2836-rpi-2-b.dtb \
> - bcm2835-rpi-zero.dtb
> + bcm2835-rpi-zero.dtb \
> + ../../../arm64/boot/dts/broadcom/bcm2837-rpi-3-b.dtb
> dtb-$(CONFIG_ARCH_BCM_5301X) += \
> bcm4708-asus-rt-ac56u.dtb \
> bcm4708-asus-rt-ac68u.dtb \
This would be really nice to have.
Acked-by: Eric Anholt <eric-WhKQ6XTQaPysTnJN9+BGXg@public.gmane.org>
ARM upstream folks, do you have any issues with this?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply
* Re: [PATCH linux v3 4/6] hwmon: occ: Add callbacks for parsing P8 OCC datastructures
From: Guenter Roeck @ 2017-01-21 18:18 UTC (permalink / raw)
To: eajames.ibm
Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, jdelvare,
corbet, mark.rutland, robh+dt, wsa, andrew, joel, benh,
Edward A. James
In-Reply-To: <1484601219-30196-5-git-send-email-eajames.ibm@gmail.com>
On 01/16/2017 01:13 PM, eajames.ibm@gmail.com wrote:
> From: "Edward A. James" <eajames@us.ibm.com>
>
> Add functions to parse the data structures that are specific to the OCC on
> the POWER8 processor. These are the sensor data structures, including
> temperature, frequency, power, and "caps."
>
> Signed-off-by: Edward A. James <eajames@us.ibm.com>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
> Documentation/hwmon/occ | 9 ++
> drivers/hwmon/occ/occ_p8.c | 247 +++++++++++++++++++++++++++++++++++++++++++++
> drivers/hwmon/occ/occ_p8.h | 30 ++++++
> 3 files changed, 286 insertions(+)
> create mode 100644 drivers/hwmon/occ/occ_p8.c
> create mode 100644 drivers/hwmon/occ/occ_p8.h
>
> diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
> index d0bdf06..143951e 100644
> --- a/Documentation/hwmon/occ
> +++ b/Documentation/hwmon/occ
> @@ -25,6 +25,15 @@ Currently, all versions of the OCC support four types of sensor data: power,
> temperature, frequency, and "caps," which indicate limits and thresholds used
> internally on the OCC.
>
> +The format for the POWER8 OCC sensor data can be found in the P8 OCC
> +specification:
> +github.com/open-power/docs/blob/master/occ/OCC_OpenPwr_FW_Interfaces.pdf
> +This document provides the details of the OCC sensors: power, frequency,
> +temperature, and caps. These sensor formats are specific to the POWER8 OCC. A
> +number of data structures, such as command format, response headers, and the
> +like, are also defined in this specification, and are common to both POWER8 and
> +POWER9 OCCs.
> +
> sysfs Entries
> -------------
>
> diff --git a/drivers/hwmon/occ/occ_p8.c b/drivers/hwmon/occ/occ_p8.c
> new file mode 100644
> index 0000000..32215ed
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_p8.c
> @@ -0,0 +1,247 @@
> +/*
> + * occ_p8.c - OCC hwmon driver
> + *
> + * This file contains the Power8-specific methods and data structures for
> + * the OCC hwmon driver.
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * 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 <asm/unaligned.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include "occ.h"
> +#include "occ_p8.h"
> +
> +/* P8 OCC sensor data format */
> +struct p8_occ_sensor {
> + u16 sensor_id;
> + u16 value;
> +};
> +
> +struct p8_power_sensor {
> + u16 sensor_id;
> + u32 update_tag;
> + u32 accumulator;
> + u16 value;
> +};
> +
> +struct p8_caps_sensor {
> + u16 curr_powercap;
> + u16 curr_powerreading;
> + u16 norm_powercap;
> + u16 max_powercap;
> + u16 min_powercap;
> + u16 user_powerlimit;
> +};
> +
> +static const u32 p8_sensor_hwmon_configs[MAX_OCC_SENSOR_TYPE] = {
> + HWMON_I_INPUT | HWMON_I_LABEL, /* freq: value | label */
> + HWMON_T_INPUT | HWMON_T_LABEL, /* temp: value | label */
> + /* power: value | label | accumulator | update_tag */
> + HWMON_P_INPUT | HWMON_P_LABEL | HWMON_P_AVERAGE |
> + HWMON_P_AVERAGE_INTERVAL,
> + /* caps: curr | max | min | norm | user */
> + HWMON_P_CAP | HWMON_P_CAP_MAX | HWMON_P_CAP_MIN | HWMON_P_MAX |
> + HWMON_P_ALARM,
> +};
> +
> +void p8_parse_sensor(u8 *data, void *sensor, int sensor_type, int off,
> + int snum)
> +{
> + switch (sensor_type) {
> + case FREQ:
> + case TEMP:
> + {
> + struct p8_occ_sensor *os =
> + &(((struct p8_occ_sensor *)sensor)[snum]);
> +
> + os->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
> + os->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 2]));
> + }
> + break;
> + case POWER:
> + {
> + struct p8_power_sensor *ps =
> + &(((struct p8_power_sensor *)sensor)[snum]);
> +
> + ps->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
> + ps->update_tag =
> + be32_to_cpu(get_unaligned((u32 *)&data[off + 2]));
> + ps->accumulator =
> + be32_to_cpu(get_unaligned((u32 *)&data[off + 6]));
> + ps->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 10]));
> + }
> + break;
> + case CAPS:
> + {
> + struct p8_caps_sensor *cs =
> + &(((struct p8_caps_sensor *)sensor)[snum]);
> +
> + cs->curr_powercap =
> + be16_to_cpu(get_unaligned((u16 *)&data[off]));
> + cs->curr_powerreading =
> + be16_to_cpu(get_unaligned((u16 *)&data[off + 2]));
> + cs->norm_powercap =
> + be16_to_cpu(get_unaligned((u16 *)&data[off + 4]));
> + cs->max_powercap =
> + be16_to_cpu(get_unaligned((u16 *)&data[off + 6]));
> + cs->min_powercap =
> + be16_to_cpu(get_unaligned((u16 *)&data[off + 8]));
> + cs->user_powerlimit =
> + be16_to_cpu(get_unaligned((u16 *)&data[off + 10]));
> + }
> + break;
> + };
> +}
> +
> +void *p8_alloc_sensor(int sensor_type, int num_sensors)
> +{
> + switch (sensor_type) {
> + case FREQ:
> + case TEMP:
> + return kcalloc(num_sensors, sizeof(struct p8_occ_sensor),
> + GFP_KERNEL);
> + case POWER:
> + return kcalloc(num_sensors, sizeof(struct p8_power_sensor),
> + GFP_KERNEL);
> + case CAPS:
> + return kcalloc(num_sensors, sizeof(struct p8_caps_sensor),
> + GFP_KERNEL);
> + default:
> + return NULL;
> + }
> +}
> +
> +int p8_get_sensor(struct occ *driver, int sensor_type, int sensor_num,
> + u32 hwmon, long *val)
> +{
> + int rc = 0;
> + void *sensor;
> +
> + if (sensor_type == POWER) {
> + if (hwmon == hwmon_power_cap || hwmon == hwmon_power_cap_max ||
> + hwmon == hwmon_power_cap_min || hwmon == hwmon_power_max ||
> + hwmon == hwmon_power_alarm)
> + sensor_type = CAPS;
> + }
> +
> + sensor = occ_get_sensor(driver, sensor_type);
> + if (!sensor)
> + return -ENODEV;
> +
> + switch (sensor_type) {
> + case FREQ:
> + case TEMP:
> + {
> + struct p8_occ_sensor *os =
> + &(((struct p8_occ_sensor *)sensor)[sensor_num]);
> +
> + if (hwmon == hwmon_in_input || hwmon == hwmon_temp_input)
> + *val = os->value;
> + else if (hwmon == hwmon_in_label || hwmon == hwmon_temp_label)
> + *val = os->sensor_id;
Guess that explains the numeric labels I wondered about earlier. Are you really sure
you want to have such numbered labels, ie temp1_label = "1" and so on ? Your call,
really, just wondering.
> + else
> + rc = -EOPNOTSUPP;
> + }
> + break;
> + case POWER:
> + {
> + struct p8_power_sensor *ps =
> + &(((struct p8_power_sensor *)sensor)[sensor_num]);
> +
> + switch (hwmon) {
> + case hwmon_power_input:
> + *val = ps->value;
> + break;
> + case hwmon_power_label:
> + *val = ps->sensor_id;
> + break;
> + case hwmon_power_average:
> + *val = ps->accumulator;
> + break;
> + case hwmon_power_average_interval:
> + *val = ps->update_tag;
> + break;
> + default:
> + rc = -EOPNOTSUPP;
> + }
> + }
> + break;
> + case CAPS:
> + {
> + struct p8_caps_sensor *cs =
> + &(((struct p8_caps_sensor *)sensor)[sensor_num]);
> +
> + switch (hwmon) {
> + case hwmon_power_cap:
> + *val = cs->curr_powercap;
> + break;
> + case hwmon_power_cap_max:
> + *val = cs->max_powercap;
> + break;
> + case hwmon_power_cap_min:
> + *val = cs->min_powercap;
> + break;
> + case hwmon_power_max:
> + *val = cs->norm_powercap;
> + break;
> + case hwmon_power_alarm:
> + *val = cs->user_powerlimit;
> + break;
> + default:
> + rc = -EOPNOTSUPP;
> + }
> + }
> + break;
> + default:
> + rc = -EINVAL;
> + }
> +
> + return rc;
> +}
> +
> +static const struct occ_ops p8_ops = {
> + .parse_sensor = p8_parse_sensor,
> + .alloc_sensor = p8_alloc_sensor,
> + .get_sensor = p8_get_sensor,
> +};
> +
> +static const struct occ_config p8_config = {
> + .command_addr = 0xFFFF6000,
> + .response_addr = 0xFFFF7000,
> +};
> +
> +const u32 *p8_get_sensor_hwmon_configs()
> +{
> + return p8_sensor_hwmon_configs;
> +}
> +EXPORT_SYMBOL(p8_get_sensor_hwmon_configs);
> +
> +struct occ *p8_occ_start(struct device *dev, void *bus,
> + struct occ_bus_ops *bus_ops)
> +{
> + return occ_start(dev, bus, bus_ops, &p8_ops, &p8_config);
> +}
> +EXPORT_SYMBOL(p8_occ_start);
> +
> +MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
> +MODULE_DESCRIPTION("P8 OCC sensors");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/occ/occ_p8.h b/drivers/hwmon/occ/occ_p8.h
> new file mode 100644
> index 0000000..3fe6417
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_p8.h
> @@ -0,0 +1,30 @@
> +/*
> + * occ_p8.h - OCC hwmon driver
> + *
> + * This file contains Power8-specific function prototypes
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * 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.
> + */
> +
> +#ifndef __OCC_P8_H__
> +#define __OCC_P8_H__
> +
> +#include "scom.h"
> +
> +struct device;
> +
> +const u32 *p8_get_sensor_hwmon_configs(void);
> +struct occ *p8_occ_start(struct device *dev, void *bus,
> + struct occ_bus_ops *bus_ops);
> +
> +#endif /* __OCC_P8_H__ */
>
^ permalink raw reply
* Re: [PATCH linux v3 3/6] hwmon: occ: Add I2C transport implementation for SCOM operations
From: Guenter Roeck @ 2017-01-21 18:11 UTC (permalink / raw)
To: eajames.ibm-Re5JQEeQqe8AvxtiuMwx3w
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-hwmon-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA, jdelvare-IBi9RG/b67k,
corbet-T1hC0tSOHrs, mark.rutland-5wv7dgnIgG8,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, wsa-z923LK4zBo2bacvFa/9K2g,
andrew-zrmu5oMJ5Fs, joel-U3u1mxZcP9KHXe+LvDLADg,
benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r, Edward A. James
In-Reply-To: <1484601219-30196-4-git-send-email-eajames.ibm-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On 01/16/2017 01:13 PM, eajames.ibm-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org wrote:
> From: "Edward A. James" <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
>
> Add functions to send SCOM operations over I2C bus. The BMC can
> communicate with the Power8 host processor over I2C, but needs to use SCOM
> operations in order to access the OCC register space.
>
> Signed-off-by: Edward A. James <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
> Signed-off-by: Andrew Jeffery <andrew-zrmu5oMJ5Fs@public.gmane.org>
> ---
> drivers/hwmon/occ/occ_scom_i2c.c | 72 ++++++++++++++++++++++++++++++++++++++++
> drivers/hwmon/occ/occ_scom_i2c.h | 26 +++++++++++++++
> 2 files changed, 98 insertions(+)
> create mode 100644 drivers/hwmon/occ/occ_scom_i2c.c
> create mode 100644 drivers/hwmon/occ/occ_scom_i2c.h
>
> diff --git a/drivers/hwmon/occ/occ_scom_i2c.c b/drivers/hwmon/occ/occ_scom_i2c.c
> new file mode 100644
> index 0000000..8b4ca13
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_scom_i2c.c
> @@ -0,0 +1,72 @@
> +/*
> + * occ_scom_i2c.c - hwmon OCC driver
> + *
> + * This file contains the functions for performing SCOM operations over I2C bus
> + * to access the OCC.
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * 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/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include "occ_scom_i2c.h"
> +#include "scom.h"
> +
> +int occ_i2c_getscom(void *bus, u32 address, u64 *data)
> +{
> + ssize_t rc;
> + u64 buf;
> + struct i2c_client *client = bus;
> +
> + rc = i2c_master_send(client, (const char *)&address, sizeof(u32));
> + if (rc < 0)
> + return rc;
> + else if (rc != sizeof(u32))
> + return -EIO;
> +
> + rc = i2c_master_recv(client, (char *)&buf, sizeof(u64));
> + if (rc < 0)
> + return rc;
> + else if (rc != sizeof(u64))
> + return -EIO;
> +
> + *data = be64_to_cpu(buf);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(occ_i2c_getscom);
> +
> +int occ_i2c_putscom(void *bus, u32 address, u32 data0, u32 data1)
> +{
> + u32 buf[3];
> + ssize_t rc;
> + struct i2c_client *client = bus;
> +
> + buf[0] = address;
> + buf[1] = data1;
> + buf[2] = data0;
> +
Receive data is converted from be64 to host byte order, transmit data is sent as-is.
Yet, if I recall correctly, some of the calling code passes in constants. Are you sure
this works on both little and big endian systems ?
> + rc = i2c_master_send(client, (const char *)buf, sizeof(u32) * 3);
> + if (rc < 0)
> + return rc;
> + else if (rc != sizeof(u32) * 3)
> + return -EIO;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(occ_i2c_putscom);
> +
> +MODULE_AUTHOR("Eddie James <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>");
> +MODULE_DESCRIPTION("I2C OCC SCOM transport");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/occ/occ_scom_i2c.h b/drivers/hwmon/occ/occ_scom_i2c.h
> new file mode 100644
> index 0000000..945739c
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_scom_i2c.h
> @@ -0,0 +1,26 @@
> +/*
> + * occ_scom_i2c.h - hwmon OCC driver
> + *
> + * This file contains function protoypes for peforming SCOM operations over I2C
> + * bus to access the OCC.
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * 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.
> + */
> +
> +#ifndef __OCC_SCOM_I2C_H__
> +#define __OCC_SCOM_I2C_H__
> +
> +int occ_i2c_getscom(void *bus, u32 address, u64 *data);
> +int occ_i2c_putscom(void *bus, u32 address, u32 data0, u32 data1);
> +
> +#endif /* __OCC_SCOM_I2C_H__ */
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH linux v3 2/6] hwmon: occ: Add sysfs interface
From: Guenter Roeck @ 2017-01-21 18:08 UTC (permalink / raw)
To: eajames.ibm-Re5JQEeQqe8AvxtiuMwx3w
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-hwmon-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA, jdelvare-IBi9RG/b67k,
corbet-T1hC0tSOHrs, mark.rutland-5wv7dgnIgG8,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, wsa-z923LK4zBo2bacvFa/9K2g,
andrew-zrmu5oMJ5Fs, joel-U3u1mxZcP9KHXe+LvDLADg,
benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r, Edward A. James
In-Reply-To: <1484601219-30196-3-git-send-email-eajames.ibm-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On 01/16/2017 01:13 PM, eajames.ibm-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org wrote:
> From: "Edward A. James" <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
>
> Add a generic mechanism to expose the sensors provided by the OCC in
> sysfs.
>
> Signed-off-by: Edward A. James <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
> Signed-off-by: Andrew Jeffery <andrew-zrmu5oMJ5Fs@public.gmane.org>
> ---
> Documentation/hwmon/occ | 62 ++++++++++
> drivers/hwmon/occ/Makefile | 2 +-
> drivers/hwmon/occ/occ_sysfs.c | 271 ++++++++++++++++++++++++++++++++++++++++++
> drivers/hwmon/occ/occ_sysfs.h | 44 +++++++
> 4 files changed, 378 insertions(+), 1 deletion(-)
> create mode 100644 drivers/hwmon/occ/occ_sysfs.c
> create mode 100644 drivers/hwmon/occ/occ_sysfs.h
>
> diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
> index 79d1642..d0bdf06 100644
> --- a/Documentation/hwmon/occ
> +++ b/Documentation/hwmon/occ
> @@ -25,6 +25,68 @@ Currently, all versions of the OCC support four types of sensor data: power,
> temperature, frequency, and "caps," which indicate limits and thresholds used
> internally on the OCC.
>
> +sysfs Entries
> +-------------
> +
> +The OCC driver uses the hwmon sysfs framework to provide data to userspace.
> +
> +The driver exports a number of sysfs files for each type of sensor. The
> +sensor-specific files vary depending on the processor type, though many of the
> +attributes are common for both the POWER8 and POWER9.
> +
> +The hwmon interface cannot define every type of sensor that may be used.
> +Therefore, the frequency sensor on the OCC uses the "input" type sensor defined
> +by the hwmon interface, rather than defining a new type of custom sensor.
> +
> +Below are detailed the names and meaning of each sensor file for both types of
> +processors. All sensors are read-only unless otherwise specified. <x> indicates
> +the hwmon index. sensor id indicates the unique internal OCC identifer. Please
> +see the POWER OCC specification for details on all these sensor values.
> +
> +frequency:
> + all processors:
> + in<x>_input - frequency value
> + in<x>_label - sensor id
> +temperature:
> + POWER8:
> + temp<x>_input - temperature value
> + temp<x>_label - sensor id
> + POWER9 (in addition to above):
> + temp<x>_type - FRU type
> +
> +power:
> + POWER8:
> + power<x>_input - power value
> + power<x>_label - sensor id
> + power<x>_average - accumulator
> + power<x>_average_interval - update tag (number of samples in
> + accumulator)
> + POWER9:
> + power<x>_input - power value
> + power<x>_label - sensor id
> + power<x>_average_min - accumulator[0]
> + power<x>_average_max - accumulator[1] (64 bits total)
> + power<x>_average_interval - update tag
> + power<x>_reset_history - (function_id | (apss_channel << 8)
> +
> +caps:
> + POWER8:
> + power<x>_cap - current powercap
> + power<x>_cap_max - max powercap
> + power<x>_cap_min - min powercap
> + power<x>_max - normal powercap
> + power<x>_alarm - user powercap, r/w
> + POWER9:
> + power<x>_cap_alarm - user powercap source
> +
> +The driver also provides two sysfs entries through hwmon to better
> +control the driver and monitor the master OCC. Though there may be multiple
> +OCCs present on the system, these two files are only present for the "master"
> +OCC.
> + name - read the name of the driver
> + update_interval - read or write the minimum interval for polling the
> + OCC.
> +
> BMC - Host Communications
> -------------------------
>
> diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
> index 93cb52f..a6881f9 100644
> --- a/drivers/hwmon/occ/Makefile
> +++ b/drivers/hwmon/occ/Makefile
> @@ -1 +1 @@
> -obj-$(CONFIG_SENSORS_PPC_OCC) += occ.o
> +obj-$(CONFIG_SENSORS_PPC_OCC) += occ.o occ_sysfs.o
> diff --git a/drivers/hwmon/occ/occ_sysfs.c b/drivers/hwmon/occ/occ_sysfs.c
> new file mode 100644
> index 0000000..2f20c40
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_sysfs.c
> @@ -0,0 +1,271 @@
> +/*
> + * occ_sysfs.c - OCC sysfs interface
> + *
> + * This file contains the methods and data structures for implementing the OCC
> + * hwmon sysfs entries.
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * 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/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/init.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +#include "occ.h"
> +#include "occ_sysfs.h"
> +
> +#define RESP_RETURN_CMD_INVAL 0x13
> +
> +static int occ_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long *val)
> +{
> + int rc = 0;
Unnecessary initialization.
> + struct occ_sysfs *driver = dev_get_drvdata(dev);
> + struct occ *occ = driver->occ;
> +
> + switch (type) {
> + case hwmon_in:
> + rc = occ_get_sensor_value(occ, FREQ, channel, attr, val);
> + break;
> + case hwmon_temp:
> + rc = occ_get_sensor_value(occ, TEMP, channel, attr, val);
> + break;
> + case hwmon_power:
> + rc = occ_get_sensor_value(occ, POWER, channel, attr, val);
> + break;
> + default:
> + rc = -EOPNOTSUPP;
> + }
> +
> + return rc;
> +}
> +
> +static int occ_hwmon_read_string(struct device *dev,
> + enum hwmon_sensor_types type, u32 attr,
> + int channel, char **str)
> +{
> + int rc;
> + unsigned long val = 0;
> +
> + if (!((type == hwmon_in && attr == hwmon_in_label) ||
> + (type == hwmon_temp && attr == hwmon_temp_label) ||
> + (type == hwmon_power && attr == hwmon_power_label)))
> + return -EOPNOTSUPP;
> +
> + rc = occ_hwmon_read(dev, type, attr, channel, &val);
> + if (rc < 0)
> + return rc;
> +
> + rc = snprintf(*str, PAGE_SIZE - 1, "%ld", val);
val is unsigned long.
I am quite confused what this is for, though. The function is used for string attributes,
or in other words for labels. What is the benefit of having a label that returns the same
data as the value attribute ? Is this maybe a misunderstanding ?
> + if (rc > 0)
> + rc = 0;
> +
> + return rc;
> +}
> +
> +static int occ_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long val)
> +{
> + int rc = 0;
> + struct occ_sysfs *driver = dev_get_drvdata(dev);
> +
> + if (type == hwmon_chip && attr == hwmon_chip_update_interval) {
> + occ_set_update_interval(driver->occ, val);
As mentioned in the other patch, please drop. This is not the intended use case
for this attribute.
> + return 0;
> + } else if (type == hwmon_power && attr == hwmon_power_alarm) {
> + rc = occ_set_user_powercap(driver->occ, val);
> + if (rc) {
> + if (rc == RESP_RETURN_CMD_INVAL) {
> + dev_err(dev,
> + "set invalid powercap value: %ld\n",
> + val);
> + return -EINVAL;
> + }
> +
> + dev_err(dev, "set user powercap failed: 0x:%x\n", rc);
> + return rc;
> + }
> +
> + driver->user_powercap = val;
> +
> + return rc;
> + }
> +
> + return -EOPNOTSUPP;
> +}
> +
> +static umode_t occ_is_visible(const void *data, enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + const struct occ_sysfs *driver = data;
> +
> + switch (type) {
> + case hwmon_chip:
> + if (attr == hwmon_chip_update_interval)
> + return S_IRUGO | S_IWUSR;
> + break;
> + case hwmon_in:
> + if (BIT(attr) & driver->sensor_hwmon_configs[0])
> + return S_IRUGO;
> + break;
> + case hwmon_temp:
> + if (BIT(attr) & driver->sensor_hwmon_configs[1])
> + return S_IRUGO;
> + break;
> + case hwmon_power:
> + /* user power limit */
> + if (attr == hwmon_power_alarm)
> + return S_IRUGO | S_IWUSR;
> + else if ((BIT(attr) & driver->sensor_hwmon_configs[2]) ||
> + (BIT(attr) & driver->sensor_hwmon_configs[3]))
> + return S_IRUGO;
> + break;
> + default:
> + return 0;
Might as well use break; here for consistency.
> + }
> +
> + return 0;
> +}
> +
> +static const struct hwmon_ops occ_hwmon_ops = {
> + .is_visible = occ_is_visible,
> + .read = occ_hwmon_read,
> + .read_string = occ_hwmon_read_string,
> + .write = occ_hwmon_write,
> +};
> +
> +static const u32 occ_chip_config[] = {
> + HWMON_C_UPDATE_INTERVAL,
> + 0
> +};
> +
> +static const struct hwmon_channel_info occ_chip = {
> + .type = hwmon_chip,
> + .config = occ_chip_config
> +};
> +
> +static const enum hwmon_sensor_types occ_sensor_types[MAX_OCC_SENSOR_TYPE] = {
> + hwmon_in,
> + hwmon_temp,
> + hwmon_power,
> + hwmon_power
> +};
> +
> +struct occ_sysfs *occ_sysfs_start(struct device *dev, struct occ *occ,
> + const u32 *sensor_hwmon_configs,
> + const char *name)
> +{
> + bool master_occ = false;
> + int rc, i, j, sensor_num, index = 0, id;
> + char *brk;
> + struct occ_blocks *resp = NULL;
> + u32 *sensor_config;
> + struct occ_sysfs *hwmon = devm_kzalloc(dev, sizeof(struct occ_sysfs),
> + GFP_KERNEL);
> + if (!hwmon)
> + return ERR_PTR(-ENOMEM);
> +
> + /* need space for null-termination and occ chip */
> + hwmon->occ_sensors =
> + devm_kzalloc(dev, sizeof(struct hwmon_channel_info *) *
> + (MAX_OCC_SENSOR_TYPE + 2), GFP_KERNEL);
> + if (!hwmon->occ_sensors)
> + return ERR_PTR(-ENOMEM);
> +
> + hwmon->occ = occ;
> + hwmon->sensor_hwmon_configs = (u32 *)sensor_hwmon_configs;
Either this is a const or it isn't. If it isn't, it should not be passed in as const.
If it is, it can be declared const in struct occ_sysfs.
> + hwmon->occ_info.ops = &occ_hwmon_ops;
> + hwmon->occ_info.info =
> + (const struct hwmon_channel_info **)hwmon->occ_sensors;
Why is this typecast needed ?
> +
> + occ_get_response_blocks(occ, &resp);
> +
> + for (i = 0; i < MAX_OCC_SENSOR_TYPE; ++i)
> + resp->sensor_block_id[i] = -1;
> +
> + /* read sensor data from occ */
> + rc = occ_update_device(occ);
> + if (rc) {
> + dev_err(dev, "cannot get occ sensor data: %d\n", rc);
> + return ERR_PTR(rc);
> + }
> + if (!resp->blocks)
> + return ERR_PTR(-ENOMEM);
> +
> + master_occ = resp->sensor_block_id[CAPS] >= 0;
> +
> + for (i = 0; i < MAX_OCC_SENSOR_TYPE; i++) {
> + id = resp->sensor_block_id[i];
> + if (id < 0)
> + continue;
> +
> + sensor_num = resp->blocks[id].header.sensor_num;
> + /* need null-termination */
> + sensor_config = devm_kzalloc(dev,
> + sizeof(u32) * (sensor_num + 1),
> + GFP_KERNEL);
> + if (!sensor_config)
> + return ERR_PTR(-ENOMEM);
> +
> + for (j = 0; j < sensor_num; j++)
> + sensor_config[j] = sensor_hwmon_configs[i];
> +
> + hwmon->occ_sensors[index] =
> + devm_kzalloc(dev, sizeof(struct hwmon_channel_info),
> + GFP_KERNEL);
> + if (!hwmon->occ_sensors[index])
> + return ERR_PTR(-ENOMEM);
> +
> + hwmon->occ_sensors[index]->type = occ_sensor_types[i];
> + hwmon->occ_sensors[index]->config = sensor_config;
> + index++;
> + }
I had the impression from patch 1 that the number of sensors can change at runtime.
If so, this doesn't really work well; the sysfs attributes won't be updated in this
case.
Can it really happen that the sensor information can change ?
> +
> + /* only need one of these for any number of occs */
> + if (master_occ)
> + hwmon->occ_sensors[index] =
> + (struct hwmon_channel_info *)&occ_chip;
Why this typecast ?
> +
> + /* search for bad chars */
> + strncpy(hwmon->hwmon_name, name, OCC_HWMON_NAME_LENGTH);
> + brk = strpbrk(hwmon->hwmon_name, "-* \t\n");
> + while (brk) {
> + *brk = '_';
> + brk = strpbrk(brk, "-* \t\n");
> + }
> +
> + hwmon->dev = devm_hwmon_device_register_with_info(dev,
> + hwmon->hwmon_name,
> + hwmon,
> + &hwmon->occ_info,
> + NULL);
> + if (IS_ERR(hwmon->dev)) {
> + dev_err(dev, "cannot register hwmon device %s: %ld\n",
> + hwmon->hwmon_name, PTR_ERR(hwmon->dev));
> + return ERR_CAST(hwmon->dev);
> + }
> +
> + return hwmon;
> +}
> +EXPORT_SYMBOL(occ_sysfs_start);
> +
> +MODULE_AUTHOR("Eddie James <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>");
> +MODULE_DESCRIPTION("OCC sysfs driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/occ/occ_sysfs.h b/drivers/hwmon/occ/occ_sysfs.h
> new file mode 100644
> index 0000000..7de92e7
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_sysfs.h
> @@ -0,0 +1,44 @@
> +/*
> + * occ_sysfs.h - OCC sysfs interface
> + *
> + * This file contains the data structures and function prototypes for the OCC
> + * hwmon sysfs entries.
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * 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.
> + */
> +
> +#ifndef __OCC_SYSFS_H__
> +#define __OCC_SYSFS_H__
> +
> +#include <linux/hwmon.h>
> +
> +struct occ;
> +struct device;
> +
> +#define OCC_HWMON_NAME_LENGTH 32
> +
> +struct occ_sysfs {
> + struct device *dev;
> + struct occ *occ;
> +
> + char hwmon_name[OCC_HWMON_NAME_LENGTH + 1];
> + u32 *sensor_hwmon_configs;
> + struct hwmon_channel_info **occ_sensors;
> + struct hwmon_chip_info occ_info;
> + u16 user_powercap;
> +};
Is this information used outside the sysfs code ? If not, it should be defined
in the source file and not be available outside of it.
> +
> +struct occ_sysfs *occ_sysfs_start(struct device *dev, struct occ *occ,
> + const u32 *sensor_hwmon_configs,
> + const char *name);
> +#endif /* __OCC_SYSFS_H__ */
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH linux v3 1/6] hwmon: Add core On-Chip Controller support for POWER CPUs
From: Guenter Roeck @ 2017-01-21 17:49 UTC (permalink / raw)
To: eajames.ibm
Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, jdelvare,
corbet, mark.rutland, robh+dt, wsa, andrew, joel, benh,
Edward A. James
In-Reply-To: <1484601219-30196-2-git-send-email-eajames.ibm@gmail.com>
On 01/16/2017 01:13 PM, eajames.ibm@gmail.com wrote:
> From: "Edward A. James" <eajames@us.ibm.com>
>
> Add core support for polling the OCC for it's sensor data and parsing that
> data into sensor-specific information.
>
> Signed-off-by: Edward A. James <eajames@us.ibm.com>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
> Documentation/hwmon/occ | 40 ++++
> MAINTAINERS | 7 +
> drivers/hwmon/Kconfig | 2 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/occ/Kconfig | 15 ++
> drivers/hwmon/occ/Makefile | 1 +
> drivers/hwmon/occ/occ.c | 522 +++++++++++++++++++++++++++++++++++++++++++++
> drivers/hwmon/occ/occ.h | 81 +++++++
> drivers/hwmon/occ/scom.h | 47 ++++
> 9 files changed, 716 insertions(+)
> create mode 100644 Documentation/hwmon/occ
> create mode 100644 drivers/hwmon/occ/Kconfig
> create mode 100644 drivers/hwmon/occ/Makefile
> create mode 100644 drivers/hwmon/occ/occ.c
> create mode 100644 drivers/hwmon/occ/occ.h
> create mode 100644 drivers/hwmon/occ/scom.h
>
> diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
> new file mode 100644
> index 0000000..79d1642
> --- /dev/null
> +++ b/Documentation/hwmon/occ
> @@ -0,0 +1,40 @@
> +Kernel driver occ
> +=================
> +
> +Supported chips:
> + * ASPEED AST2400
> + * ASPEED AST2500
> +
> +Please note that the chip must be connected to a POWER8 or POWER9 processor
> +(see the BMC - Host Communications section).
> +
> +Author: Eddie James <eajames@us.ibm.com>
> +
> +Description
> +-----------
> +
> +This driver implements support for the OCC (On-Chip Controller) on the IBM
> +POWER8 and POWER9 processors, from a BMC (Baseboard Management Controller). The
> +OCC is an embedded processor that provides real time power and thermal
> +monitoring.
> +
> +This driver provides an interface on a BMC to poll OCC sensor data, set user
> +power caps, and perform some basic OCC error handling.
> +
> +Currently, all versions of the OCC support four types of sensor data: power,
> +temperature, frequency, and "caps," which indicate limits and thresholds used
> +internally on the OCC.
> +
> +BMC - Host Communications
> +-------------------------
> +
> +For the POWER8 application, the BMC can communicate with the P8 over I2C bus.
> +However, to access the OCC register space, any data transfer must use a SCOM
> +operation. SCOM is a procedure to initiate a data transfer, typically of 8
> +bytes. SCOMs consist of writing a 32-bit command register and then
> +reading/writing two 32-bit data registers. This driver implements these
> +SCOM operations over I2C bus in order to communicate with the OCC.
> +
> +For the POWER9 application, the BMC can communicate with the P9 over FSI bus
> +and SBE engine. Once again, SCOM operations are required. This driver will
> +implement SCOM ops over FSI/SBE. This will require the FSI driver.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5f0420a..f5d4195 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9112,6 +9112,13 @@ T: git git://linuxtv.org/media_tree.git
> S: Maintained
> F: drivers/media/i2c/ov7670.c
>
> +ON-CHIP CONTROLLER HWMON DRIVER
> +M: Eddie James <eajames@us.ibm.com>
> +L: linux-hwmon@vger.kernel.org
> +S: Maintained
> +F: Documentation/hwmon/occ
> +F: drivers/hwmon/occ/
> +
> ONENAND FLASH DRIVER
> M: Kyungmin Park <kyungmin.park@samsung.com>
> L: linux-mtd@lists.infradead.org
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 190d270..e80ca81 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1240,6 +1240,8 @@ config SENSORS_NSA320
> This driver can also be built as a module. If so, the module
> will be called nsa320-hwmon.
>
> +source drivers/hwmon/occ/Kconfig
> +
> config SENSORS_PCF8591
> tristate "Philips PCF8591 ADC/DAC"
> depends on I2C
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index d2cb7e8..c7ec5d4 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -169,6 +169,7 @@ obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
> obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
> obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o
>
> +obj-$(CONFIG_SENSORS_PPC_OCC) += occ/
> obj-$(CONFIG_PMBUS) += pmbus/
>
> ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
> diff --git a/drivers/hwmon/occ/Kconfig b/drivers/hwmon/occ/Kconfig
> new file mode 100644
> index 0000000..cdb64a7
> --- /dev/null
> +++ b/drivers/hwmon/occ/Kconfig
> @@ -0,0 +1,15 @@
> +#
> +# On Chip Controller configuration
> +#
> +
> +menuconfig SENSORS_PPC_OCC
> + bool "PPC On-Chip Controller"
> + help
> + If you say yes here you get support to monitor Power CPU
> + sensors via the On-Chip Controller (OCC).
> +
> + Generally this is used by management controllers such as a BMC
> + on an OpenPower system.
> +
> + This driver can also be built as a module. If so, the module
> + will be called occ.
> diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
> new file mode 100644
> index 0000000..93cb52f
> --- /dev/null
> +++ b/drivers/hwmon/occ/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_SENSORS_PPC_OCC) += occ.o
> diff --git a/drivers/hwmon/occ/occ.c b/drivers/hwmon/occ/occ.c
> new file mode 100644
> index 0000000..3089762
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ.c
> @@ -0,0 +1,522 @@
> +/*
> + * occ.c - OCC hwmon driver
> + *
> + * This file contains the methods and data structures for the OCC hwmon driver.
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * 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 <asm/unaligned.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +#include "occ.h"
> +
> +#define OCC_DATA_MAX 4096
> +#define OCC_BMC_TIMEOUT_MS 20000
> +
> +/* To generate attn to OCC */
> +#define ATTN_DATA 0x0006B035
> +
> +/* For BMC to read/write SRAM */
> +#define OCB_ADDRESS 0x0006B070
> +#define OCB_DATA 0x0006B075
> +#define OCB_STATUS_CONTROL_AND 0x0006B072
> +#define OCB_STATUS_CONTROL_OR 0x0006B073
> +
> +/* To init OCB */
> +#define OCB_AND_INIT0 0xFBFFFFFF
> +#define OCB_AND_INIT1 0xFFFFFFFF
> +#define OCB_OR_INIT0 0x08000000
> +#define OCB_OR_INIT1 0x00000000
> +
> +/* To generate attention on OCC */
> +#define ATTN0 0x01010000
> +#define ATTN1 0x00000000
> +
> +/* OCC return status */
> +#define RESP_RETURN_CMD_IN_PRG 0xFF
> +#define RESP_RETURN_SUCCESS 0
> +#define RESP_RETURN_CMD_INVAL 0x11
> +#define RESP_RETURN_CMD_LEN 0x12
> +#define RESP_RETURN_DATA_INVAL 0x13
> +#define RESP_RETURN_CHKSUM 0x14
> +#define RESP_RETURN_OCC_ERR 0x15
> +#define RESP_RETURN_STATE 0x16
> +
> +/* time interval to retry on "command in progress" return status */
> +#define CMD_IN_PRG_INT_MS 100
> +#define CMD_IN_PRG_RETRIES (OCC_BMC_TIMEOUT_MS / CMD_IN_PRG_INT_MS)
> +
> +/* OCC command definitions */
> +#define OCC_POLL 0
> +#define OCC_SET_USER_POWR_CAP 0x22
> +
> +/* OCC poll command data */
> +#define OCC_POLL_STAT_SENSOR 0x10
> +
> +/* OCC response data offsets */
> +#define RESP_RETURN_STATUS 2
> +#define RESP_DATA_LENGTH 3
> +#define RESP_HEADER_OFFSET 5
> +#define SENSOR_STR_OFFSET 37
> +#define SENSOR_BLOCK_NUM_OFFSET 43
> +#define SENSOR_BLOCK_OFFSET 45
> +
> +/* occ_poll_header
> + * structure to match the raw occ poll response data
> + */
> +struct occ_poll_header {
> + u8 status;
> + u8 ext_status;
> + u8 occs_present;
> + u8 config;
> + u8 occ_state;
> + u8 mode;
> + u8 ips_status;
> + u8 error_log_id;
> + u32 error_log_addr_start;
> + u16 error_log_length;
> + u8 reserved2;
> + u8 reserved3;
> + u8 occ_code_level[16];
> + u8 sensor_eye_catcher[6];
> + u8 sensor_block_num;
> + u8 sensor_data_version;
> +} __attribute__((packed, aligned(4)));
> +
> +struct occ_response {
> + struct occ_poll_header header;
> + struct occ_blocks data;
> +};
> +
> +struct occ {
> + struct device *dev;
> + void *bus;
> + struct occ_bus_ops bus_ops;
> + struct occ_ops ops;
> + struct occ_config config;
> + unsigned long update_interval;
> + unsigned long last_updated;
> + struct mutex update_lock;
> + struct occ_response response;
> + bool valid;
> +};
> +
> +static void deinit_occ_resp_buf(struct occ_response *resp)
> +{
> + int i;
> +
> + if (!resp)
> + return;
> +
This creates the impression that resp can ever be NULL, which is not the case.
> + if (!resp->data.blocks)
> + return;
> +
> + for (i = 0; i < resp->header.sensor_block_num; ++i)
> + kfree(resp->data.blocks[i].sensors);
> +
> + kfree(resp->data.blocks);
> +/sensor_type_s
> + memset(resp, 0, sizeof(struct occ_response));
> +
> + for (i = 0; i < MAX_OCC_SENSOR_TYPE; ++i)
> + resp->data.sensor_block_id[i] = -1;
> +}
> +
> +static void *occ_get_sensor_by_type(struct occ_response *resp,
> + enum sensor_type t)
> +{
> + if (!resp->data.blocks)
> + return NULL;
> +
> + if (resp->data.sensor_block_id[t] == -1)
> + return NULL;
> +
> + return resp->data.blocks[resp->data.sensor_block_id[t]].sensors;
> +}
> +
> +static int occ_check_sensor(struct occ *driver, u8 sensor_length,
> + u8 sensor_num, enum sensor_type t, int block)
> +{
> + void *sensor;
> + int type_block_id;
> + struct occ_response *resp = &driver->response;
> +
> + sensor = occ_get_sensor_by_type(resp, t);
> +
> + /* empty sensor block, release older sensor data */
> + if (sensor_num == 0 || sensor_length == 0) {
> + kfree(sensor);
> + dev_err(driver->dev, "no sensor blocks available\n");
> + return -ENODATA;
> + }
> +
> + type_block_id = resp->data.sensor_block_id[t];
> + if (!sensor || sensor_num !=
> + resp->data.blocks[type_block_id].header.sensor_num) {
> + kfree(sensor);
> + resp->data.blocks[block].sensors =
> + driver->ops.alloc_sensor(t, sensor_num);
> + if (!resp->data.blocks[block].sensors)
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +static int parse_occ_response(struct occ *driver, u8 *data,
> + struct occ_response *resp)
> +{
> + int b;
> + int s;
> + int rc;
> + int offset = SENSOR_BLOCK_OFFSET;
> + int sensor_type;
> + u8 sensor_block_num;
> + char sensor_type_string[5] = { 0 };
The only purpose of this variable seems to be to zero-terminate it to be able
to print it. Why not use length-limited print functions instead ?
> + struct sensor_data_block_header *block;
> + struct device *dev = driver->dev;
> +
> + /* check if the data is valid */
> + if (strncmp(&data[SENSOR_STR_OFFSET], "SENSOR", 6) != 0) {
> + dev_err(dev, "no SENSOR string in response\n");
Are those messages really necessary ? That can create a lot of logging noise
if the occ responds with bad data.
> + rc = -ENODATA;
> + goto err;
> + }
> +
> + sensor_block_num = data[SENSOR_BLOCK_NUM_OFFSET];
There is a lot of trust in assuming that this data was actually received.
At the same time, there is a lot of error checking. Why check if the
information in this field is valid if you don't check if it was
received in the first place ?
> + if (sensor_block_num == 0) {
> + dev_err(dev, "no sensor blocks available\n");
> + rc = -ENODATA;
> + goto err;
> + }
> +
> + /* if number of sensor block has changed, re-malloc */
> + if (sensor_block_num != resp->header.sensor_block_num) {
> + deinit_occ_resp_buf(resp);
> + resp->data.blocks = kcalloc(sensor_block_num,
> + sizeof(struct sensor_data_block),
> + GFP_KERNEL);
> + if (!resp->data.blocks)
> + return -ENOMEM;
Why not just allocate the maximum number of sensors once ? That might waste
a bit of memory, but it would make the code much less error prone. We know that
there will never be more sensors than the number fitting into 4k of memory,
so the "waste" would not really be that much.
> + }
> +
> + memcpy(&resp->header, &data[RESP_HEADER_OFFSET],
> + sizeof(struct occ_poll_header));
> + resp->header.error_log_addr_start =
> + be32_to_cpu(resp->header.error_log_addr_start);
> + resp->header.error_log_length =
> + be16_to_cpu(resp->header.error_log_length);
> +
> + dev_dbg(dev, "Reading %d sensor blocks\n",
> + resp->header.sensor_block_num);
> + for (b = 0; b < sensor_block_num; b++) {
> + block = (struct sensor_data_block_header *)&data[offset];
> + /* copy to a null terminated string */
> + strncpy(sensor_type_string, block->sensor_type, 4);
> + offset += 8;
> +
> + dev_dbg(dev, "sensor block[%d]: type: %s, sensor_num: %d\n", b,
> + sensor_type_string, block->sensor_num);
> +
> + if (strncmp(block->sensor_type, "FREQ", 4) == 0)
> + sensor_type = FREQ;
> + else if (strncmp(block->sensor_type, "TEMP", 4) == 0)
> + sensor_type = TEMP;
> + else if (strncmp(block->sensor_type, "POWR", 4) == 0)
> + sensor_type = POWER;
> + else if (strncmp(block->sensor_type, "CAPS", 4) == 0)
> + sensor_type = CAPS;
> + else {
> + dev_err(dev, "sensor type not supported %s\n",
> + sensor_type_string);
> + continue;
> + }
> +
> + rc = occ_check_sensor(driver, block->sensor_length,
> + block->sensor_num, sensor_type, b);
> + if (rc == -ENOMEM)
> + goto err;
> + else if (rc)
> + continue;
> +
> + resp->data.sensor_block_id[sensor_type] = b;
> + for (s = 0; s < block->sensor_num; s++) {
> + driver->ops.parse_sensor(data,
> + resp->data.blocks[b].sensors,
> + sensor_type, offset, s);
> + offset += block->sensor_length;
> + }
> +
> + /* copy block data over to response pointer */
> + resp->data.blocks[b].header = *block;
> + }
> +
> + return 0;
> +err:
> + deinit_occ_resp_buf(resp);
> + return rc;
> +}
> +
> +static u8 occ_send_cmd(struct occ *driver, u8 seq, u8 type, u16 length,
> + const u8 *data, u8 *resp)
> +{
> + u32 cmd1, cmd2;
> + u16 checksum = 0;
> + u16 length_le = cpu_to_le16(length);
> + bool retry = 0;
> + int i, rc, tries = 0;
> +
> + cmd1 = (seq << 24) | (type << 16) | length_le;
> + memcpy(&cmd2, data, length);
> + cmd2 <<= ((4 - length) * 8);
> +
> + /* checksum: sum of every bytes of cmd1, cmd2 */
> + for (i = 0; i < 4; i++) {
> + checksum += (cmd1 >> (i * 8)) & 0xFF;
> + checksum += (cmd2 >> (i * 8)) & 0xFF;
> + }
> +
> + cmd2 |= checksum << ((2 - length) * 8);
> +
> + /* Init OCB */
> + rc = driver->bus_ops.putscom(driver->bus, OCB_STATUS_CONTROL_OR,
> + OCB_OR_INIT0, OCB_OR_INIT1);
> + if (rc)
> + goto err;
> +
> + rc = driver->bus_ops.putscom(driver->bus, OCB_STATUS_CONTROL_AND,
> + OCB_AND_INIT0, OCB_AND_INIT1);
> + if (rc)
> + goto err;
> +
> + /* Send command, 2nd half of the 64-bit addr is unused (write 0) */
> + rc = driver->bus_ops.putscom(driver->bus, OCB_ADDRESS,
> + driver->config.command_addr, 0);
> + if (rc)
> + goto err;
> +
> + rc = driver->bus_ops.putscom(driver->bus, OCB_DATA, cmd1, cmd2);
> + if (rc)
> + goto err;
> +
> + /* Trigger attention */
> + rc = driver->bus_ops.putscom(driver->bus, ATTN_DATA, ATTN0, ATTN1);
> + if (rc)
> + goto err;
> +
> + /* Get response data */
> + rc = driver->bus_ops.putscom(driver->bus, OCB_ADDRESS,
> + driver->config.response_addr, 0);
> + if (rc)
> + goto err;
> +
> + do {
> + if (retry) {
> + set_current_state(TASK_INTERRUPTIBLE);
> + schedule_timeout(msecs_to_jiffies(CMD_IN_PRG_INT_MS));
> + }
> +
Isn't there a better way to do this ? What exactly is interrupted ? The
getscom function ? If so, shouldn't the timeout be handled there ?
> + rc = driver->bus_ops.getscom(driver->bus, OCB_DATA,
> + (u64 *)resp);
> + if (rc)
> + goto err;
> +
> + /* retry if we get "command in progress" return status */
> + retry = (resp[RESP_RETURN_STATUS] == RESP_RETURN_CMD_IN_PRG) &&
> + (tries++ < CMD_IN_PRG_RETRIES);
There are some unnecessary () in this expression.
> + } while (retry);
> +
> + switch (resp[RESP_RETURN_STATUS]) {
> + case RESP_RETURN_CMD_IN_PRG:
> + rc = -EALREADY;
> + break;
> + case RESP_RETURN_SUCCESS:
> + rc = 0;
> + break;
> + case RESP_RETURN_CMD_INVAL:
> + case RESP_RETURN_CMD_LEN:
> + case RESP_RETURN_DATA_INVAL:
> + case RESP_RETURN_CHKSUM:
> + rc = -EINVAL;
> + break;
> + case RESP_RETURN_OCC_ERR:
> + rc = -EREMOTE;
> + break;
> + default:
> + rc = -EFAULT;
> + }
> +
> + return rc;
> +
> +err:
> + dev_err(driver->dev, "scom op failed rc:%d\n", rc);
Some of the errors result in an error message, some don't. What is
the guiding principle ? Even if there is an error message, it doesn't report
what function caused it, which makes it as good as no error message.
Can this be dropped ?
> + return rc;
> +}
> +
> +static int occ_get_all(struct occ *driver)
> +{
> + int i = 0, rc;
> + u8 *occ_data;
> + u16 num_bytes;
> + const u8 poll_cmd_data = OCC_POLL_STAT_SENSOR;
> + struct device *dev = driver->dev;
> + struct occ_response *resp = &driver->response;
> +
> + occ_data = devm_kzalloc(dev, OCC_DATA_MAX, GFP_KERNEL);
I am a bot lost here. What is the purpose of using a devm function ?
Either case, you might want to allocate the memory once and just keep it allocated.
Allocating a 4k buffer and freeing it each time data is read from the occ seems
to be wasteful and not really worth it.
> + if (!occ_data)
> + return -ENOMEM;
> +
> + rc = occ_send_cmd(driver, 0, OCC_POLL, 1, &poll_cmd_data, occ_data);
> + if (rc) {
> + dev_err(dev, "OCC poll failed: %d\n", rc);
> + goto out;
> + }
> +
> + num_bytes = get_unaligned((u16 *)&occ_data[RESP_DATA_LENGTH]);
> + num_bytes = be16_to_cpu(num_bytes);
What data is in big endian, and what data is in little endian ?
Looks like everything is big endian except for the length in the send command.
Is that correct ? If so, an explanation would be useful, because the logic
isn't entirely clear.
> + dev_dbg(dev, "OCC data length: %d\n", num_bytes);
> +
> + if (num_bytes > OCC_DATA_MAX) {
> + dev_err(dev, "OCC data length must be < 4KB\n");
> + rc = -EINVAL;
> + goto out;
> + }
> +
> + if (num_bytes <= 0) {
This can never be < 0.
> + dev_err(dev, "OCC data length is zero\n");
> + rc = -EINVAL;
> + goto out;
> + }
> +
> + /* read remaining data */
> + for (i = 8; i < num_bytes + 8; i += 8) {
> + rc = driver->bus_ops.getscom(driver->bus, OCB_DATA,
> + (u64 *)&occ_data[i]);
> + if (rc) {
> + dev_err(dev, "scom op failed rc:%d\n", rc);
> + goto out;
> + }
> + }
> +
> + /* don't need more sanity checks; buffer is alloc'd for max response
> + * size so we just check for valid data in parse_occ_response
> + */
Yes, but as mentioned above you don't check in that function if the data
was actually received.
> + rc = parse_occ_response(driver, occ_data, resp);
> +
> +out:
> + devm_kfree(dev, occ_data);
> + return rc;
> +}
> +
> +int occ_update_device(struct occ *driver)
> +{
> + int rc = 0;
> +
> + mutex_lock(&driver->update_lock);
> +
> + if (time_after(jiffies, driver->last_updated + driver->update_interval)
> + || !driver->valid) {
> + driver->valid = 1;
valid is bool.
> +
> + rc = occ_get_all(driver);
> + if (rc)
> + driver->valid = 0;
valid is bool.
> +
> + driver->last_updated = jiffies;
> + }
> +
> + mutex_unlock(&driver->update_lock);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL(occ_update_device);
> +
> +void *occ_get_sensor(struct occ *driver, int sensor_type)
> +{
> + int rc;
> +
> + /* occ_update_device locks the update lock */
> + rc = occ_update_device(driver);
> + if (rc) {
> + dev_err(driver->dev, "cannot get occ sensor data: %d\n",
> + rc);
Have you counted the number of error messages reported if anything goes wrong.
This driver is quite noisy. I won't force you to drop the messages, but it seems
to me that if anything goes wrong, the kernel log will be clogged with messages
just from this driver.
> + return NULL;
> + }
> +
> + return occ_get_sensor_by_type(&driver->response, sensor_type);
> +}
> +EXPORT_SYMBOL(occ_get_sensor);
> +
> +int occ_get_sensor_value(struct occ *occ, int sensor_type, int sensor_num,
> + u32 hwmon, long *val)
> +{
> + return occ->ops.get_sensor(occ, sensor_type, sensor_num, hwmon, val);
> +}
> +EXPORT_SYMBOL(occ_get_sensor_value);
> +
> +void occ_get_response_blocks(struct occ *occ, struct occ_blocks **blocks)
> +{
> + *blocks = &occ->response.data;
> +}
> +EXPORT_SYMBOL(occ_get_response_blocks);
> +
> +void occ_set_update_interval(struct occ *occ, unsigned long interval)
> +{
> + occ->update_interval = msecs_to_jiffies(interval);
> +}
> +EXPORT_SYMBOL(occ_set_update_interval);
> +
> +int occ_set_user_powercap(struct occ *occ, u16 cap)
> +{
> + u8 resp[8];
> +
> + cap = cpu_to_be16(cap);
> +
> + return occ_send_cmd(occ, 0, OCC_SET_USER_POWR_CAP, 2, (const u8 *)&cap,
> + resp);
> +}
> +EXPORT_SYMBOL(occ_set_user_powercap);
> +
> +struct occ *occ_start(struct device *dev, void *bus,
> + struct occ_bus_ops *bus_ops, const struct occ_ops *ops,
> + const struct occ_config *config)
> +{
> + struct occ *driver = devm_kzalloc(dev, sizeof(struct occ), GFP_KERNEL);
> +
> + if (!driver)
> + return ERR_PTR(-ENOMEM);
> +
> + driver->dev = dev;
> + driver->bus = bus;
> + driver->bus_ops = *bus_ops;
> + driver->ops = *ops;
> + driver->config = *config;
> +
> + driver->update_interval = HZ;
The update interval is only supposed to be configurable if it can be written
into the hardware. Otherwise it is really up to user space to decide how often
it wants to poll the data, not up to the kernel.
I don't see a function to send the update interval to the occ. As such, it should
not be configurable but a constant.
> + mutex_init(&driver->update_lock);
> +
> + return driver;
> +}
> +EXPORT_SYMBOL(occ_start);
> +
> +MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
> +MODULE_DESCRIPTION("OCC hwmon core driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/occ/occ.h b/drivers/hwmon/occ/occ.h
> new file mode 100644
> index 0000000..c86b06a
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ.h
> @@ -0,0 +1,81 @@
> +/*
> + * occ.h - hwmon OCC driver
> + *
> + * This file contains data structures and function prototypes for common access
> + * between different bus protocols and host systems.
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * 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.
> + */
> +
> +#ifndef __OCC_H__
> +#define __OCC_H__
> +
> +#include "scom.h"
> +
> +struct device;
> +struct occ;
> +
> +/* sensor_data_block_header
> + * structure to match the raw occ sensor block header
> + */
> +struct sensor_data_block_header {
> + u8 sensor_type[4];
> + u8 reserved0;
> + u8 sensor_format;
> + u8 sensor_length;
> + u8 sensor_num;
> +} __attribute__((packed, aligned(4)));
> +
> +struct sensor_data_block {
> + struct sensor_data_block_header header;
> + void *sensors;
> +};
> +
> +enum sensor_type {
> + FREQ = 0,
> + TEMP,
> + POWER,
> + CAPS,
> + MAX_OCC_SENSOR_TYPE
> +};
> +
> +struct occ_ops {
> + void (*parse_sensor)(u8 *data, void *sensor, int sensor_type, int off,
> + int snum);
> + void *(*alloc_sensor)(int sensor_type, int num_sensors);
> + int (*get_sensor)(struct occ *driver, int sensor_type, int sensor_num,
> + u32 hwmon, long *val);
> +};
> +
> +struct occ_config {
> + u32 command_addr;
> + u32 response_addr;
> +};
> +
> +struct occ_blocks {
> + int sensor_block_id[MAX_OCC_SENSOR_TYPE];
> + struct sensor_data_block *blocks;
> +};
> +
> +struct occ *occ_start(struct device *dev, void *bus,
> + struct occ_bus_ops *bus_ops, const struct occ_ops *ops,
> + const struct occ_config *config);
> +void *occ_get_sensor(struct occ *occ, int sensor_type);
> +int occ_get_sensor_value(struct occ *occ, int sensor_type, int sensor_num,
> + u32 hwmon, long *val);
> +void occ_get_response_blocks(struct occ *occ, struct occ_blocks **blocks);
> +int occ_update_device(struct occ *driver);
> +void occ_set_update_interval(struct occ *occ, unsigned long interval);
> +int occ_set_user_powercap(struct occ *occ, u16 cap);
> +
> +#endif /* __OCC_H__ */
> diff --git a/drivers/hwmon/occ/scom.h b/drivers/hwmon/occ/scom.h
> new file mode 100644
> index 0000000..c1da645
> --- /dev/null
> +++ b/drivers/hwmon/occ/scom.h
> @@ -0,0 +1,47 @@
> +/*
> + * scom.h - hwmon OCC driver
> + *
> + * This file contains data structures for scom operations to the OCC
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * 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.
> + */
> +
> +#ifndef __SCOM_H__
> +#define __SCOM_H__
> +
> +/*
> + * occ_bus_ops - represent the low-level transfer methods to communicate with
> + * the OCC.
> + *
> + * getscom - OCC scom read
> + * @bus: handle to slave device
> + * @address: address
> + * @data: where to store data read from slave; buffer size must be at least
> + * eight bytes.
> + *
> + * Returns 0 on success or a negative errno on error
> + *
> + * putscom - OCC scom write
> + * @bus: handle to slave device
> + * @address: address
> + * @data0: first data byte to write
> + * @data1: second data byte to write
32 bit bytes ? It might also be worthwhile to mention the expected byte order.
> + *
> + * Returns 0 on success or a negative errno on error
> + */
> +struct occ_bus_ops {
> + int (*getscom)(void *bus, u32 address, u64 *data);
> + int (*putscom)(void *bus, u32 address, u32 data0, u32 data1);
> +};
> +
> +#endif /* __SCOM_H__ */
>
^ permalink raw reply
* Re: [PATCH v2 2/3] ARM: dts: r7s72100: add ostm to device tree
From: Sergei Shtylyov @ 2017-01-21 17:38 UTC (permalink / raw)
To: Chris Brandt, Simon Horman, Magnus Damm, Rob Herring,
Mark Rutland, Russell King, Thomas Gleixner, Geert Uytterhoeven
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170120212055.4157-3-chris.brandt-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
Hello!
On 1/21/2017 12:20 AM, Chris Brandt wrote:
> Signed-off-by: Chris Brandt <chris.brandt-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> ---
> v2:
> * wrap clock lines to avoid 80 char max
> * split into 2 separate channel nodes
> ---
> arch/arm/boot/dts/r7s72100.dtsi | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
>
> diff --git a/arch/arm/boot/dts/r7s72100.dtsi b/arch/arm/boot/dts/r7s72100.dtsi
> index d5946df..47b2165 100644
> --- a/arch/arm/boot/dts/r7s72100.dtsi
> +++ b/arch/arm/boot/dts/r7s72100.dtsi
> @@ -505,4 +505,20 @@
> cap-sdio-irq;
> status = "disabled";
> };
> +
> + ostm0: ostm@fcfec000 {
According to devicetree.org, the node name should be generic, i.e.
"timer@...".
> + compatible = "renesas,r7s72100-ostm", "renesas,ostm";
> + reg = <0xfcfec000 0x30>;
> + interrupts = <GIC_SPI 102 IRQ_TYPE_EDGE_RISING>;
> + clocks = <&mstp5_clks R7S72100_CLK_OSTM0>;
> + status = "disabled";
> + };
> +
> + ostm1: ostm@fcfec400 {
Likewise.
> + compatible = "renesas,r7s72100-ostm", "renesas,ostm";
> + reg = <0xfcfec400 0x30>;
> + interrupts = <GIC_SPI 103 IRQ_TYPE_EDGE_RISING>;
> + clocks = <&mstp5_clks R7S72100_CLK_OSTM1>;
> + status = "disabled";
> + };
> };
MBR, Sergei
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 1/3] arm64: dts: exynos: Fix drive strength of sd0_xxx pin definitions
From: Krzysztof Kozlowski @ 2017-01-21 16:26 UTC (permalink / raw)
To: Pankaj Dubey
Cc: linux-samsung-soc, linux-arm-kernel, devicetree, kgene, robh+dt,
sanath, javier, s.nawrocki
In-Reply-To: <1484885997-21004-2-git-send-email-pankaj.dubey@samsung.com>
On Fri, Jan 20, 2017 at 09:49:55AM +0530, Pankaj Dubey wrote:
> As per Exynos7 datasheet FSYS1 pinctrl block does not support drive
> strength value of 0x3. This patch fixes this and update the correct
> drive strength for sd0_xxx pin definitions.
>
> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
> Reviewed-by: Alim Akhtar <alim.akhtar@samsung.com>
> ---
> arch/arm64/boot/dts/exynos/exynos7-pinctrl.dtsi | 14 +++++++-------
> 1 file changed, 7 insertions(+), 7 deletions(-)
>
Thanks, applied.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH V2 1/2] dt-bindings: leds: document new led-triggers property
From: Rafał Miłecki @ 2017-01-21 16:24 UTC (permalink / raw)
To: Jacek Anaszewski
Cc: Greg Kroah-Hartman,
linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
open list:LED SUBSYSTEM, Richard Purdie, Pavel Machek,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
Mark Rutland, Rafał Miłecki
In-Reply-To: <29f27028-20f3-3eb9-502f-1b51958640f9-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On 20 January 2017 at 23:35, Jacek Anaszewski
<jacek.anaszewski-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On 01/20/2017 10:56 PM, Rafał Miłecki wrote:
>> From: Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>
>>
>> Some LEDs can be related to particular devices described in DT. This
>> property allows specifying such relations. E.g. USB LED should usually
>> be used to indicate some USB port(s) state.
>>
>> Signed-off-by: Rafał Miłecki <rafal-g1n6cQUeyibVItvQsEIGlw@public.gmane.org>
>> ---
>> V2: Replace "usb-ports" with "led-triggers" property which is more generic and
>> allows specifying other devices as well.
>>
>> When bindings patch is related to some followup implementation, they usually go
>> through the same tree.
>>
>> Greg: this patch is based on top of e64b8cc72bf9 ("DT: leds: Improve examples by
>> adding some context") from kernel/git/j.anaszewski/linux-leds.git . Is there any
>> way to solve this dependency issue? Or should this patch wait until 3.11 is
>> released?
>> ---
>> Documentation/devicetree/bindings/leds/common.txt | 16 ++++++++++++++++
>> 1 file changed, 16 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/leds/common.txt b/Documentation/devicetree/bindings/leds/common.txt
>> index 24b656014089..17632a041196 100644
>> --- a/Documentation/devicetree/bindings/leds/common.txt
>> +++ b/Documentation/devicetree/bindings/leds/common.txt
>> @@ -49,6 +49,17 @@ Optional properties for child nodes:
>> - panic-indicator : This property specifies that the LED should be used,
>> if at all possible, as a panic indicator.
>>
>> +- led-triggers : List of devices that should trigger this LED activity. Some
>> + LEDs can be related to a specific device and should somehow
>> + indicate its state. E.g. USB 2.0 LED may react to device(s) in
>> + a USB 2.0 port(s). Another common example is switch or router
>> + with multiple Ethernet ports each of them having its own LED
>> + assigned (assuminled-trigger-usbportg they are not hardwired).
>> + In such cases this property should contain phandle(s) of
>> + related device(s). In many cases LED can be related to more
>> + than one device (e.g. one USB LED vs. multiple USB ports) so a
>> + list of entries can be specified.
>> +
>
> This implies that it is possible to define multiple triggers for
> a LED class device but it is not supported by LED Trigger core.
> There is linux,default-trigger property which allows to define one
> trigger that will be initially assigned.
I think we owe some extra explanation to people not closely familiar
with the triggers.
Linux has multiple trigger drivers each supporting some *type* of
events. Linux supports assigning only one trigger driver to LED at a
time.
This means you can use e.g.:
1) "usbport" trigger driver that supports USB events
XOR
2) "timer" trigger driver that uses timer to control LED
XOR
3) "cpu" trigger driver that supports CPU events
at once.
With proposed "led-triggers" property one could assign different
trigger *sources* to a LED. You could e.g. assign 2 USB ports, network
device & CPU to a single LED. For reason explained above Linux
couldn't support all of them at once.
> I am aware that this is renamed usb-ports property from v1,
> that attempts to address Rob's comment, but we can't do that this way.
> Maybe usb-ports property could be documented in led-trigger-usbport's
> specific bindings and a reference to it could be added next to the
> related entry on the list of the available LED triggers (which is
> actually missing) in Documentation/devicetree/bindings/leds/common.txt.
I'm wondering if we need to care about this Linux limitation and if it
should stop us from adding this flexible DT property. Maybe we could
live with Linux having limitation of supporting one trigger type at a
time? So e.g. if one assigns 2 USB ports + network device + CPU and
decides to use "usport" trigger driver he'll get LED indicating status
of USB ports only.
I think we could live with such limitation in Linux support for this
generic "led-triggers" property. I think it's also not very common to
have LED with different types of devices assigned. Personally I've
never seen devices with LED label "USB & CPU" or whatever.
What do you think about this? Maybe we could also document this
limitation in trigger docs.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 0/3] ti,ads7950 device tree bindings
From: Jonathan Cameron @ 2017-01-21 15:08 UTC (permalink / raw)
To: David Lechner, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA
Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
Rob Herring, Mark Rutland, linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <b60edc70-1d0f-fbfc-6ae9-1e6e5e3525d1-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
On 15/01/17 23:44, David Lechner wrote:
> On 01/15/2017 07:58 AM, Jonathan Cameron wrote:
>> On 11/01/17 17:52, David Lechner wrote:
>>> This series adds device tree bindings for the TI ADS7950 family of A/DC chips.
>>> The series includes the bindings documentation and some fixes to the iio driver
>>> to make it work with the device tree bindings.
>>>
>>> FYI, the ads7950 driver has not made it into mainline yet, so no worries about
>>> breaking anyone with these changes.
>>>
>> And here's the bit I failed to read!
>>
>> As an extra point, could you confirm what /sys/bus/iio/iio\:deviceX/name for this
>> one reads? I have a feeling this is another case of what Lars has been pointing
>> out in other drivers this morning. That name should be the device part number..
>>
>
> cat /sys/bus/iio/devices/iio\:device0/name
> ads7957
>
> This is the part number for the specific chip I am using. So, I am guessing that it is correct unless it is supposed to be upper case or something like that.
>
>
Cool. Spot on.
>
>> Thanks,
>>
>> Jonathan
>>> David Lechner (3):
>>> DT/bindings: Add bindings for TI ADS7950 A/DC chips
>>> iio: adc: ti-ads7950: Drop "ti-" prefix from module name
>>> iio: adc: ti-ads7950: Change regulator matching string to "vref"
>>>
>>> .../devicetree/bindings/iio/adc/ti-ads7950.txt | 23 ++++++++++++++++
>>> drivers/iio/adc/ti-ads7950.c | 32 +++++++++++-----------
>>> 2 files changed, 39 insertions(+), 16 deletions(-)
>>> create mode 100644 Documentation/devicetree/bindings/iio/adc/ti-ads7950.txt
>>>
>>
>
^ permalink raw reply
* [PATCH] iio:temperature:Add irq and threshold events support for TI TMP007 sensor
From: Manivannan Sadhasivam @ 2017-01-21 15:01 UTC (permalink / raw)
To: jic23-DgEjT+Ai2ygdnm+yROfE0A, pmeerw-jW+XmwGofnusTnJN9+BGXg
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
This patch adds support for ALERT irq and limit threshold events for TI TMP007 - 16 bit IR thermopile sensor
with integrated math engine.
Following threshold events are supported:
1. TObj high limit
2. TObj low limit
3. TDie high limit
4. TDie low limit
Signed-off-by: Manivannan Sadhasivam <manivannanece23-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
.../devicetree/bindings/iio/temperature/tmp007.txt | 8 +
drivers/iio/temperature/tmp007.c | 334 ++++++++++++++++++---
2 files changed, 304 insertions(+), 38 deletions(-)
diff --git a/Documentation/devicetree/bindings/iio/temperature/tmp007.txt b/Documentation/devicetree/bindings/iio/temperature/tmp007.txt
index 3b8f41f..07c1658 100644
--- a/Documentation/devicetree/bindings/iio/temperature/tmp007.txt
+++ b/Documentation/devicetree/bindings/iio/temperature/tmp007.txt
@@ -18,10 +18,18 @@ Required properties:
1 SDA 0x46
1 SCL 0x47
+Optional properties:
+
+ - interrupt-parent: should be the phandle for the interrupt controller
+
+ - interrupts: interrupt mapping for GPIO IRQ
+
Example:
tmp007@40 {
compatible = "ti,tmp007";
reg = <0x40>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <5 8>;
};
diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c
index 24c6c16..73b5b8d 100644
--- a/drivers/iio/temperature/tmp007.c
+++ b/drivers/iio/temperature/tmp007.c
@@ -11,9 +11,10 @@
*
* (7-bit I2C slave address (0x40 - 0x47), changeable via ADR pins)
*
- * Note: This driver assumes that the sensor has been calibrated beforehand
- *
- * TODO: ALERT irq, limit threshold events
+ * Note:
+ * 1. This driver assumes that the sensor has been calibrated beforehand
+ * 2. Limit threshold events are enabled at the start
+ * 3. Operating mode: INT
*
*/
@@ -24,35 +25,49 @@
#include <linux/pm.h>
#include <linux/bitops.h>
#include <linux/of.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
-
-#define TMP007_TDIE 0x01
-#define TMP007_CONFIG 0x02
-#define TMP007_TOBJECT 0x03
-#define TMP007_STATUS 0x04
-#define TMP007_STATUS_MASK 0x05
-#define TMP007_MANUFACTURER_ID 0x1e
-#define TMP007_DEVICE_ID 0x1f
-
-#define TMP007_CONFIG_CONV_EN BIT(12)
-#define TMP007_CONFIG_COMP_EN BIT(5)
-#define TMP007_CONFIG_TC_EN BIT(6)
-#define TMP007_CONFIG_CR_MASK GENMASK(11, 9)
-#define TMP007_CONFIG_CR_SHIFT 9
-
-#define TMP007_STATUS_CONV_READY BIT(14)
-#define TMP007_STATUS_DATA_VALID BIT(9)
-
-#define TMP007_MANUFACTURER_MAGIC 0x5449
-#define TMP007_DEVICE_MAGIC 0x0078
-
-#define TMP007_TEMP_SHIFT 2
+#include <linux/iio/events.h>
+
+#define TMP007_TDIE 0x01
+#define TMP007_CONFIG 0x02
+#define TMP007_TOBJECT 0x03
+#define TMP007_STATUS 0x04
+#define TMP007_STATUS_MASK 0x05
+#define TMP007_TOBJ_HIGH_LIMIT 0x06
+#define TMP007_TOBJ_LOW_LIMIT 0x07
+#define TMP007_TDIE_HIGH_LIMIT 0x08
+#define TMP007_TDIE_LOW_LIMIT 0x09
+#define TMP007_MANUFACTURER_ID 0x1e
+#define TMP007_DEVICE_ID 0x1f
+
+#define TMP007_CONFIG_CONV_EN BIT(12)
+#define TMP007_CONFIG_TC_EN BIT(6)
+#define TMP007_CONFIG_CR_MASK GENMASK(11, 9)
+#define TMP007_CONFIG_ALERT_EN BIT(8)
+#define TMP007_CONFIG_CR_SHIFT 9
+
+/* Status register flags */
+#define TMP007_STATUS_ALERT BIT(15)
+#define TMP007_STATUS_CONV_READY BIT(14)
+#define TMP007_STATUS_OHF BIT(13)
+#define TMP007_STATUS_OLF BIT(12)
+#define TMP007_STATUS_LHF BIT(11)
+#define TMP007_STATUS_LLF BIT(10)
+#define TMP007_STATUS_DATA_VALID BIT(9)
+
+#define TMP007_MANUFACTURER_MAGIC 0x5449
+#define TMP007_DEVICE_MAGIC 0x0078
+
+#define TMP007_TEMP_SHIFT 2
struct tmp007_data {
struct i2c_client *client;
u16 config;
+ u16 status_mask;
};
static const int tmp007_avgs[5][2] = { {4, 0}, {2, 0}, {1, 0},
@@ -70,7 +85,7 @@ static int tmp007_read_temperature(struct tmp007_data *data, u8 reg)
return ret;
if ((ret & TMP007_STATUS_CONV_READY) &&
!(ret & TMP007_STATUS_DATA_VALID))
- break;
+ break;
msleep(100);
}
@@ -156,6 +171,185 @@ static int tmp007_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
+static irqreturn_t tmp007_interrupt_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct tmp007_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = i2c_smbus_read_word_swapped(data->client, TMP007_STATUS);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ if (ret & TMP007_STATUS_OHF)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_TEMP, 0,
+ IIO_MOD_TEMP_OBJECT,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(indio_dev));
+
+ if (ret & TMP007_STATUS_OLF)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_TEMP, 0,
+ IIO_MOD_TEMP_OBJECT,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ iio_get_time_ns(indio_dev));
+
+ if (ret & TMP007_STATUS_LHF)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_TEMP, 0,
+ IIO_MOD_TEMP_AMBIENT,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(indio_dev));
+
+ if (ret & TMP007_STATUS_LLF)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_TEMP, 0,
+ IIO_MOD_TEMP_AMBIENT,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ iio_get_time_ns(indio_dev));
+
+ return IRQ_HANDLED;
+}
+
+static int tmp007_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+{
+ struct tmp007_data *data = iio_priv(indio_dev);
+ unsigned int status_mask;
+ int ret;
+
+ switch (chan->channel2) {
+ case IIO_MOD_TEMP_AMBIENT:
+ if (dir == IIO_EV_DIR_RISING)
+ status_mask = TMP007_STATUS_LHF;
+ else
+ status_mask = TMP007_STATUS_LLF;
+ break;
+ case IIO_MOD_TEMP_OBJECT:
+ if (dir == IIO_EV_DIR_RISING)
+ status_mask = TMP007_STATUS_OHF;
+ else
+ status_mask = TMP007_STATUS_OLF;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = i2c_smbus_read_word_swapped(data->client, TMP007_STATUS_MASK);
+ if (ret < 0)
+ return ret;
+
+ if (state)
+ ret |= status_mask;
+ else
+ ret &= ~status_mask;
+
+ return i2c_smbus_write_word_swapped(data->client, TMP007_STATUS_MASK,
+ data->status_mask = ret);
+}
+
+static int tmp007_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct tmp007_data *data = iio_priv(indio_dev);
+ unsigned int mask;
+
+ switch (chan->channel2) {
+ case IIO_MOD_TEMP_AMBIENT:
+ if (dir == IIO_EV_DIR_RISING)
+ mask = TMP007_STATUS_LHF;
+ else
+ mask = TMP007_STATUS_LLF;
+ break;
+ case IIO_MOD_TEMP_OBJECT:
+ if (dir == IIO_EV_DIR_RISING)
+ mask = TMP007_STATUS_OHF;
+ else
+ mask = TMP007_STATUS_OLF;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return (bool)(data->status_mask & mask);
+}
+
+static int tmp007_read_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct tmp007_data *data = iio_priv(indio_dev);
+ int ret;
+ u8 reg;
+
+ switch (chan->channel2) {
+ case IIO_MOD_TEMP_AMBIENT: /* LSB: 0.5 degree Celsius */
+ if (dir == IIO_EV_DIR_RISING)
+ reg = TMP007_TDIE_HIGH_LIMIT;
+ else
+ reg = TMP007_TDIE_LOW_LIMIT;
+ break;
+ case IIO_MOD_TEMP_OBJECT:
+ if (dir == IIO_EV_DIR_RISING)
+ reg = TMP007_TOBJ_HIGH_LIMIT;
+ else
+ reg = TMP007_TOBJ_LOW_LIMIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = i2c_smbus_read_word_swapped(data->client, reg);
+ if (ret < 0)
+ return ret;
+
+ /* Shift length 7 bits = 6(15:6) + 1(0.5 LSB) */
+ *val = sign_extend32(ret, 15) >> 7;
+
+ return IIO_VAL_INT;
+}
+
+static int tmp007_write_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, enum iio_event_info info,
+ int val, int val2)
+{
+ struct tmp007_data *data = iio_priv(indio_dev);
+ u8 reg;
+
+ switch (chan->channel2) {
+ case IIO_MOD_TEMP_AMBIENT:
+ if (dir == IIO_EV_DIR_RISING)
+ reg = TMP007_TDIE_HIGH_LIMIT;
+ else
+ reg = TMP007_TDIE_LOW_LIMIT;
+ break;
+ case IIO_MOD_TEMP_OBJECT:
+ if (dir == IIO_EV_DIR_RISING)
+ reg = TMP007_TOBJ_HIGH_LIMIT;
+ else
+ reg = TMP007_TOBJ_LOW_LIMIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Full scale value is +/- 256 degree Celsius */
+ if (val < -256 || val > 255)
+ return -EINVAL;
+
+ /* Shift length 7 bits = 6(15:6) + 1(0.5 LSB) */
+ return i2c_smbus_write_word_swapped(data->client, reg, (val << 7));
+}
+
static IIO_CONST_ATTR(sampling_frequency_available, "4 2 1 0.5 0.25");
static struct attribute *tmp007_attributes[] = {
@@ -167,6 +361,36 @@ static const struct attribute_group tmp007_attribute_group = {
.attrs = tmp007_attributes,
};
+static const struct iio_event_spec tmp007_obj_event[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_event_spec tmp007_die_event[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
static const struct iio_chan_spec tmp007_channels[] = {
{
.type = IIO_TEMP,
@@ -175,6 +399,8 @@ static const struct iio_chan_spec tmp007_channels[] = {
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .event_spec = tmp007_die_event,
+ .num_event_specs = ARRAY_SIZE(tmp007_die_event),
},
{
.type = IIO_TEMP,
@@ -183,12 +409,18 @@ static const struct iio_chan_spec tmp007_channels[] = {
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
- }
+ .event_spec = tmp007_obj_event,
+ .num_event_specs = ARRAY_SIZE(tmp007_obj_event),
+ },
};
static const struct iio_info tmp007_info = {
- .read_raw = tmp007_read_raw,
- .write_raw = tmp007_write_raw,
+ .read_raw = &tmp007_read_raw,
+ .write_raw = &tmp007_write_raw,
+ .read_event_config = &tmp007_read_event_config,
+ .write_event_config = &tmp007_write_event_config,
+ .read_event_value = &tmp007_read_thresh,
+ .write_event_value = &tmp007_write_thresh,
.attrs = &tmp007_attribute_group,
.driver_module = THIS_MODULE,
};
@@ -214,7 +446,6 @@ static int tmp007_probe(struct i2c_client *client,
struct tmp007_data *data;
struct iio_dev *indio_dev;
int ret;
- u16 status;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -EOPNOTSUPP;
@@ -243,8 +474,8 @@ static int tmp007_probe(struct i2c_client *client,
/*
* Set Configuration register:
* 1. Conversion ON
- * 2. Comparator mode
- * 3. Transient correction enable
+ * 2. Transient correction enable
+ * 3. ALERT enable
*/
ret = i2c_smbus_read_word_swapped(data->client, TMP007_CONFIG);
@@ -252,7 +483,7 @@ static int tmp007_probe(struct i2c_client *client,
return ret;
data->config = ret;
- data->config |= (TMP007_CONFIG_CONV_EN | TMP007_CONFIG_COMP_EN | TMP007_CONFIG_TC_EN);
+ data->config |= (TMP007_CONFIG_CONV_EN | TMP007_CONFIG_TC_EN | TMP007_CONFIG_ALERT_EN);
ret = i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG,
data->config);
@@ -260,22 +491,39 @@ static int tmp007_probe(struct i2c_client *client,
return ret;
/*
+ * Only the following flags can activate ALERT pin. Data conversion/validity flags
+ * flags can still be polled for getting temperature data
+ *
* Set Status Mask register:
- * 1. Conversion ready enable
- * 2. Data valid enable
+ * 1. Object temperature high limit enable
+ * 2. Object temperature low limit enable
+ * 3. TDIE temperature high limit enable
+ * 4. TDIE temperature low limit enable
*/
ret = i2c_smbus_read_word_swapped(data->client, TMP007_STATUS_MASK);
if (ret < 0)
goto error_powerdown;
- status = ret;
- status |= (TMP007_STATUS_CONV_READY | TMP007_STATUS_DATA_VALID);
+ data->status_mask = ret;
+ data->status_mask |= (TMP007_STATUS_OHF | TMP007_STATUS_OLF
+ | TMP007_STATUS_LHF | TMP007_STATUS_LLF);
- ret = i2c_smbus_write_word_swapped(data->client, TMP007_STATUS_MASK, status);
+ ret = i2c_smbus_write_word_swapped(data->client, TMP007_STATUS_MASK, data->status_mask);
if (ret < 0)
goto error_powerdown;
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, tmp007_interrupt_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ tmp007_id->name, indio_dev);
+ if (ret) {
+ dev_err(&client->dev, "irq request error %d\n", -ret);
+ goto error_powerdown;
+ }
+ }
+
return iio_device_register(indio_dev);
error_powerdown:
@@ -312,6 +560,16 @@ static int tmp007_resume(struct device *dev)
return i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG,
data->config | TMP007_CONFIG_CONV_EN);
}
+#else
+static int tmp007_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int tmp007_resume(struct device *dev)
+{
+ return 0;
+}
#endif
static SIMPLE_DEV_PM_OPS(tmp007_pm_ops, tmp007_suspend, tmp007_resume);
--
2.7.4
^ permalink raw reply related
* Re: [PATCH V6] iio: adc: Add Renesas GyroADC driver
From: Marek Vasut @ 2017-01-21 14:43 UTC (permalink / raw)
To: Jonathan Cameron, linux-iio
Cc: Geert Uytterhoeven, Simon Horman, linux-renesas-soc, Rob Herring,
Mark Rutland, devicetree@vger.kernel.org
In-Reply-To: <50ac50d5-224e-e7b4-7d1f-584ca26e6078@kernel.org>
On 01/21/2017 02:04 PM, Jonathan Cameron wrote:
> On 15/01/17 17:05, Marek Vasut wrote:
>> Add IIO driver for the Renesas RCar GyroADC block. This block is a
>> simple 4/8-channel ADC which samples 12/15/24 bits of data every
>> cycle from all channels.
>>
>> Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
>> Cc: Geert Uytterhoeven <geert+renesas@glider.be>
>> Cc: Simon Horman <horms+renesas@verge.net.au>
>> Cc: Jonathan Cameron <jic23@kernel.org>
>> Cc: linux-renesas-soc@vger.kernel.org
> Hi Marek,
>
> One thing I hadn't picked up on earlier. This hasn't been going to the devicetree
> list or maintainers. I'd like an ack from them on the bindings as they are a little
> 'unusual' given the odd nature of the part.
>
> Cc'd Rob, Mark and the list.
I sent V7 where I split out the bindings into a separate patch and sent
that to the DT list just not.
> Jonathan
>> ---
>> V2: - Spelling fixes
>> - Rename the driver source file to rcar-gyroadc
>> - Rework the channel sample width handling
>> - Use iio_device_claim_mode_direct()
>> - Rename "renesas,rcar-gyroadc" to "renesas,r8a7791-gyroadc" and
>> rename "renesas,rcar-gyroadc-r8a7792" to "renesas,r8a7792-gyroadc"
>> to match the new naming scheme (WARNING: scif uses the old one!)
>> - Switch to using regulators for channel voltage reference, add new
>> properties renesas,gyroadc-vref-chN-supply for N in 0..7
>> - Handle vref regulators as optional to, make channels without
>> vref regulator return EINVAL on read.
>> - Fix module license to GPL
>> - Drop interrupt.h include
>> - Rename clk to iclk
>> - Rename RCar to R-Car
>> - Rework the invalid mode handling
>> - Don't print error message on EPROBE_DEFER
>> - Drop fclk handling, use runtime PM for that instead
>> V3: - More R-Car spelling fixes
>> - Flip checks for V2H, since that's the only one that has
>> interrupt registers
>> - Replace if-else statement with switch statement in init_mode
>> - Use unsigned types where applicable
>> - Rework timing calculation slightly to drop if-else block
>> - Use DIV_ROUND_CLOSEST
>> V4: - Add renesas,rcar-gyroadc fallback compatible string into the bindings
>> - Rework the ADC bindings to use per-channel subdevs
>> - Support more compatible ADC chips
>> V5: - Reword the DT bindings, improve the description of the modes
>> - Improve the description in Kconfig
>> - Implement special DT bindings for the MB88101A 4-channel ADC and
>> add special handling for this chip into the driver.
>> - Add missing ADC stop on failure, wrap it into a function
>> - Fail hard in case the DT properties are incorrect or missing
>> - Fix reporting of scale, so that channel * scale value is in mV
>> - Use fractional scale instead of int plus nano
>> - Use pm_runtime to disable the sampling 2 seconds after last used
>> V6: - Replace the indio_dev->name with "rcar-gyroadc" instead of OF node name
>> ---
>> .../bindings/iio/adc/renesas,gyroadc.txt | 93 +++
>> MAINTAINERS | 6 +
>> drivers/iio/adc/Kconfig | 13 +
>> drivers/iio/adc/Makefile | 1 +
>> drivers/iio/adc/rcar-gyroadc.c | 631 +++++++++++++++++++++
>> 5 files changed, 744 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
>> create mode 100644 drivers/iio/adc/rcar-gyroadc.c
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
>> new file mode 100644
>> index 000000000000..081947367135
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
>> @@ -0,0 +1,93 @@
>> +* Renesas RCar GyroADC device driver
>> +
>> +Required properties:
>> +- compatible: Should be "renesas,<chip>-gyroadc", "renesas,rcar-gyroadc".
>> + Use "renesas,r8a7792-gyroadc" for a GyroADC with interrupt
>> + block found in R8A7792.
>> +- reg: Address and length of the register set for the device
>> +- clocks: References to all the clocks specified in the clock-names
>> + property as specified in
>> + Documentation/devicetree/bindings/clock/clock-bindings.txt.
>> +- clock-names: Shall contain "fck" and "if". The "fck" is the GyroADC block
>> + clock, the "if" is the interface clock.
>> +- power-domains: Must contain a reference to the PM domain, if available.
>> +- #address-cells: Should be <1> (setting for the subnodes) for all ADCs
>> + except for "fujitsu,mb88101a". Should be <0> (setting for
>> + only subnode) for "fujitsu,mb88101a".
>> +- #size-cells: Should be <0> (setting for the subnodes)
>> +
>> +Sub-nodes:
>> +Optionally you can define subnodes which select the connected ADC type and
>> +reference voltage for the GyroADC channels.
>> +
>> +Required properties for subnodes:
>> +- compatible: Should be either of:
>> + "fujitsu,mb88101a"
>> + - Fujitsu MB88101A compatible mode,
>> + 12bit sampling, up to 4 channels can be sampled in
>> + round-robin fashion. One Fujitsu chip supplies four
>> + GyroADC channels with data as it contains four ADCs
>> + on the chip and thus for 4-channel operation, single
>> + MB88101A is required. The Cx chipselect lines of the
>> + MB88101A connect directly to two CHS lines of the
>> + GyroADC, no demuxer is required. The data out line
>> + of each MB88101A connects to a shared input pin of
>> + the GyroADC.
>> + "ti,adcs7476" or "ti,adc121" or "adi,ad7476"
>> + - TI ADCS7476 / TI ADC121 / ADI AD7476 compatible mode,
>> + 15bit sampling, up to 8 channels can be sampled in
>> + round-robin fashion. One TI/ADI chip supplies single
>> + ADC channel with data, thus for 8-channel operation,
>> + 8 chips are required. A 3:8 chipselect demuxer is
>> + required to connect the nCS line of the TI/ADI chips
>> + to the GyroADC, while MISO line of each TI/ADI ADC
>> + connects to a shared input pin of the GyroADC.
>> + "maxim,max1162" or "maxim,max11100"
>> + - Maxim MAX1162 / Maxim MAX11100 compatible mode,
>> + 16bit sampling, up to 8 channels can be sampled in
>> + round-robin fashion. One Maxim chip supplies single
>> + ADC channel with data, thus for 8-channel operation,
>> + 8 chips are required. A 3:8 chipselect demuxer is
>> + required to connect the nCS line of the MAX chips
>> + to the GyroADC, while MISO line of each Maxim ADC
>> + connects to a shared input pin of the GyroADC.
>> +- reg: Should be the number of the analog input. Should be present
>> + for all ADCs except "fujitsu,mb88101a".
>> +- vref-supply: Reference to the channel reference voltage regulator.
>> +
>> +Example:
>> + vref_max1162: regulator-vref-max1162 {
>> + compatible = "regulator-fixed";
>> +
>> + regulator-name = "MAX1162 Vref";
>> + regulator-min-microvolt = <4096000>;
>> + regulator-max-microvolt = <4096000>;
>> + };
>> +
>> + &adc {
>> + compatible = "renesas,r8a7791-gyroadc", "renesas,rcar-gyroadc";
>> + reg = <0 0xe6e54000 0 64>;
>> + clocks = <&mstp9_clks R8A7791_CLK_GYROADC>, <&clk_65m>;
>> + clock-names = "fck", "if";
>> + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
>> +
>> + pinctrl-0 = <&adc_pins>;
>> + pinctrl-names = "default";
>> +
>> + status = "okay";
>> +
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + adc@0 {
>> + reg = <0>;
>> + compatible = "maxim,max1162";
>> + vref-supply = <&vref_max1162>;
>> + };
>> +
>> + adc@1 {
>> + reg = <1>;
>> + compatible = "maxim,max1162";
>> + vref-supply = <&vref_max1162>;
>> + };
>> + };
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 890fc9e3c191..498e8a755eb6 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -10276,6 +10276,12 @@ L: linux-renesas-soc@vger.kernel.org
>> F: drivers/net/ethernet/renesas/
>> F: include/linux/sh_eth.h
>>
>> +RENESAS R-CAR GYROADC DRIVER
>> +M: Marek Vasut <marek.vasut@gmail.com>
>> +L: linux-iio@vger.kernel.org
>> +S: Supported
>> +F: drivers/iio/adc/rcar_gyro_adc.c
>> +
>> RENESAS USB2 PHY DRIVER
>> M: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
>> L: linux-renesas-soc@vger.kernel.org
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 99c051490eff..2fbc11f42740 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -408,6 +408,19 @@ config QCOM_SPMI_VADC
>> To compile this driver as a module, choose M here: the module will
>> be called qcom-spmi-vadc.
>>
>> +config RCAR_GYRO_ADC
>> + tristate "Renesas R-Car GyroADC driver"
>> + depends on ARCH_RCAR_GEN2 || (ARM && COMPILE_TEST)
>> + help
>> + Say yes here to build support for the GyroADC found in Renesas
>> + R-Car Gen2 SoCs. This block is a simple SPI offload engine for
>> + reading data out of attached compatible ADCs in a round-robin
>> + fashion. Up to 4 or 8 ADC channels are supported by this block,
>> + depending on which ADCs are attached.
>> +
>> + To compile this driver as a module, choose M here: the
>> + module will be called rcar-gyroadc.
>> +
>> config ROCKCHIP_SARADC
>> tristate "Rockchip SARADC driver"
>> depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index 7a40c04c311f..13db7c2bffc8 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -39,6 +39,7 @@ obj-$(CONFIG_NAU7802) += nau7802.o
>> obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
>> obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
>> obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
>> +obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
>> obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>> obj-$(CONFIG_STX104) += stx104.o
>> obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>> diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c
>> new file mode 100644
>> index 000000000000..0c44f72c32a8
>> --- /dev/null
>> +++ b/drivers/iio/adc/rcar-gyroadc.c
>> @@ -0,0 +1,631 @@
>> +/*
>> + * Renesas R-Car GyroADC driver
>> + *
>> + * Copyright 2016 Marek Vasut <marek.vasut@gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is 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/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/delay.h>
>> +#include <linux/kernel.h>
>> +#include <linux/slab.h>
>> +#include <linux/io.h>
>> +#include <linux/clk.h>
>> +#include <linux/of.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/err.h>
>> +#include <linux/pm_runtime.h>
>> +
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/iio/trigger.h>
>> +
>> +#define DRIVER_NAME "rcar-gyroadc"
>> +
>> +/* GyroADC registers. */
>> +#define RCAR_GYROADC_MODE_SELECT 0x00
>> +#define RCAR_GYROADC_MODE_SELECT_1_MB88101A 0x0
>> +#define RCAR_GYROADC_MODE_SELECT_2_ADCS7476 0x1
>> +#define RCAR_GYROADC_MODE_SELECT_3_MAX1162 0x3
>> +
>> +#define RCAR_GYROADC_START_STOP 0x04
>> +#define RCAR_GYROADC_START_STOP_START BIT(0)
>> +
>> +#define RCAR_GYROADC_CLOCK_LENGTH 0x08
>> +#define RCAR_GYROADC_1_25MS_LENGTH 0x0c
>> +
>> +#define RCAR_GYROADC_REALTIME_DATA(ch) (0x10 + ((ch) * 4))
>> +#define RCAR_GYROADC_100MS_ADDED_DATA(ch) (0x30 + ((ch) * 4))
>> +#define RCAR_GYROADC_10MS_AVG_DATA(ch) (0x50 + ((ch) * 4))
>> +
>> +#define RCAR_GYROADC_FIFO_STATUS 0x70
>> +#define RCAR_GYROADC_FIFO_STATUS_EMPTY(ch) BIT(0 + (4 * (ch)))
>> +#define RCAR_GYROADC_FIFO_STATUS_FULL(ch) BIT(1 + (4 * (ch)))
>> +#define RCAR_GYROADC_FIFO_STATUS_ERROR(ch) BIT(2 + (4 * (ch)))
>> +
>> +#define RCAR_GYROADC_INTR 0x74
>> +#define RCAR_GYROADC_INTR_INT BIT(0)
>> +
>> +#define RCAR_GYROADC_INTENR 0x78
>> +#define RCAR_GYROADC_INTENR_INTEN BIT(0)
>> +
>> +#define RCAR_GYROADC_SAMPLE_RATE 800 /* Hz */
>> +
>> +#define RCAR_GYROADC_RUNTIME_PM_DELAY_MS 2000
>> +
>> +enum rcar_gyroadc_model {
>> + RCAR_GYROADC_MODEL_DEFAULT,
>> + RCAR_GYROADC_MODEL_R8A7792,
>> +};
>> +
>> +struct rcar_gyroadc {
>> + struct device *dev;
>> + void __iomem *regs;
>> + struct clk *iclk;
>> + struct regulator *vref[8];
>> + unsigned int num_channels;
>> + enum rcar_gyroadc_model model;
>> + unsigned int mode;
>> + unsigned int sample_width;
>> +};
>> +
>> +static void rcar_gyroadc_hw_init(struct rcar_gyroadc *priv)
>> +{
>> + const unsigned long clk_mhz = clk_get_rate(priv->iclk) / 1000000;
>> + const unsigned long clk_mul =
>> + (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) ? 10 : 5;
>> + unsigned long clk_len = clk_mhz * clk_mul;
>> +
>> + /*
>> + * According to the R-Car Gen2 datasheet Rev. 1.01, Sept 08 2014,
>> + * page 77-7, clock length must be even number. If it's odd number,
>> + * add one.
>> + */
>> + if (clk_len & 1)
>> + clk_len++;
>> +
>> + /* Stop the GyroADC. */
>> + writel(0, priv->regs + RCAR_GYROADC_START_STOP);
>> +
>> + /* Disable IRQ on V2H. */
>> + if (priv->model == RCAR_GYROADC_MODEL_R8A7792)
>> + writel(0, priv->regs + RCAR_GYROADC_INTENR);
>> +
>> + /* Set mode and timing. */
>> + writel(priv->mode, priv->regs + RCAR_GYROADC_MODE_SELECT);
>> + writel(clk_len, priv->regs + RCAR_GYROADC_CLOCK_LENGTH);
>> + writel(clk_mhz * 1250, priv->regs + RCAR_GYROADC_1_25MS_LENGTH);
>> +}
>> +
>> +static void rcar_gyroadc_hw_start(struct rcar_gyroadc *priv)
>> +{
>> + /* Start sampling. */
>> + writel(RCAR_GYROADC_START_STOP_START,
>> + priv->regs + RCAR_GYROADC_START_STOP);
>> +
>> + /*
>> + * Wait for the first conversion to complete. This is longer than
>> + * the 1.25 mS in the datasheet because 1.25 mS is not enough for
>> + * the hardware to deliver the first sample and the hardware does
>> + * then return zeroes instead of valid data.
>> + */
>> + mdelay(3);
>> +}
>> +
>> +static void rcar_gyroadc_hw_stop(struct rcar_gyroadc *priv)
>> +{
>> + /* Stop the GyroADC. */
>> + writel(0, priv->regs + RCAR_GYROADC_START_STOP);
>> +}
>> +
>> +#define RCAR_GYROADC_CHAN(_idx) { \
>> + .type = IIO_VOLTAGE, \
>> + .indexed = 1, \
>> + .channel = (_idx), \
>> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
>> + BIT(IIO_CHAN_INFO_SCALE), \
>> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
>> +}
>> +
>> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_1[] = {
>> + RCAR_GYROADC_CHAN(0),
>> + RCAR_GYROADC_CHAN(1),
>> + RCAR_GYROADC_CHAN(2),
>> + RCAR_GYROADC_CHAN(3),
>> +};
>> +
>> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_2[] = {
>> + RCAR_GYROADC_CHAN(0),
>> + RCAR_GYROADC_CHAN(1),
>> + RCAR_GYROADC_CHAN(2),
>> + RCAR_GYROADC_CHAN(3),
>> + RCAR_GYROADC_CHAN(4),
>> + RCAR_GYROADC_CHAN(5),
>> + RCAR_GYROADC_CHAN(6),
>> + RCAR_GYROADC_CHAN(7),
>> +};
>> +
>> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = {
>> + RCAR_GYROADC_CHAN(0),
>> + RCAR_GYROADC_CHAN(1),
>> + RCAR_GYROADC_CHAN(2),
>> + RCAR_GYROADC_CHAN(3),
>> + RCAR_GYROADC_CHAN(4),
>> + RCAR_GYROADC_CHAN(5),
>> + RCAR_GYROADC_CHAN(6),
>> + RCAR_GYROADC_CHAN(7),
>> +};
>> +
>> +static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on)
>> +{
>> + struct device *dev = priv->dev;
>> + int ret;
>> +
>> + if (on) {
>> + ret = pm_runtime_get_sync(dev);
>> + if (ret < 0)
>> + pm_runtime_put_noidle(dev);
>> + } else {
>> + pm_runtime_mark_last_busy(dev);
>> + ret = pm_runtime_put_autosuspend(dev);
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan,
>> + int *val, int *val2, long mask)
>> +{
>> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
>> + struct regulator *consumer;
>> + unsigned int datareg = RCAR_GYROADC_REALTIME_DATA(chan->channel);
>> + unsigned int vref;
>> + int ret;
>> +
>> + /*
>> + * MB88101 is special in that it has only single regulator for
>> + * all four channels.
>> + */
>> + if (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A)
>> + consumer = priv->vref[0];
>> + else
>> + consumer = priv->vref[chan->channel];
>> +
>> + switch (mask) {
>> + case IIO_CHAN_INFO_RAW:
>> + if (chan->type != IIO_VOLTAGE)
>> + return -EINVAL;
>> +
>> + /* Channel not connected. */
>> + if (!consumer)
>> + return -EINVAL;
>> +
>> + ret = iio_device_claim_direct_mode(indio_dev);
>> + if (ret)
>> + return ret;
>> +
>> + ret = rcar_gyroadc_set_power(priv, true);
>> + if (ret < 0) {
>> + iio_device_release_direct_mode(indio_dev);
>> + return ret;
>> + }
>> +
>> + *val = readl(priv->regs + datareg);
>> + *val &= BIT(priv->sample_width) - 1;
>> +
>> + ret = rcar_gyroadc_set_power(priv, false);
>> + iio_device_release_direct_mode(indio_dev);
>> + if (ret < 0)
>> + return ret;
>> +
>> + return IIO_VAL_INT;
>> + case IIO_CHAN_INFO_SCALE:
>> + /* Channel not connected. */
>> + if (!consumer)
>> + return -EINVAL;
>> +
>> + vref = regulator_get_voltage(consumer);
>> + *val = vref / 1000;
>> + *val2 = 1 << priv->sample_width;
>> +
>> + return IIO_VAL_FRACTIONAL;
>> + case IIO_CHAN_INFO_SAMP_FREQ:
>> + *val = RCAR_GYROADC_SAMPLE_RATE;
>> +
>> + return IIO_VAL_INT;
>> + default:
>> + return -EINVAL;
>> + }
>> +}
>> +
>> +static int rcar_gyroadc_reg_access(struct iio_dev *indio_dev,
>> + unsigned int reg, unsigned int writeval,
>> + unsigned int *readval)
>> +{
>> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
>> + unsigned int maxreg = RCAR_GYROADC_FIFO_STATUS;
>> +
>> + if (readval == NULL)
>> + return -EINVAL;
>> +
>> + if (reg % 4)
>> + return -EINVAL;
>> +
>> + /* Handle the V2H case with extra interrupt block. */
>> + if (priv->model == RCAR_GYROADC_MODEL_R8A7792)
>> + maxreg = RCAR_GYROADC_INTENR;
>> +
>> + if (reg > maxreg)
>> + return -EINVAL;
>> +
>> + *readval = readl(priv->regs + reg);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct iio_info rcar_gyroadc_iio_info = {
>> + .driver_module = THIS_MODULE,
>> + .read_raw = rcar_gyroadc_read_raw,
>> + .debugfs_reg_access = rcar_gyroadc_reg_access,
>> +};
>> +
>> +static const struct of_device_id rcar_gyroadc_match[] = {
>> + {
>> + /* R-Car compatible GyroADC */
>> + .compatible = "renesas,rcar-gyroadc",
>> + .data = (void *)RCAR_GYROADC_MODEL_DEFAULT,
>> + }, {
>> + /* R-Car V2H specialty with interrupt registers. */
>> + .compatible = "renesas,r8a7792-gyroadc",
>> + .data = (void *)RCAR_GYROADC_MODEL_R8A7792,
>> + }, {
>> + /* sentinel */
>> + }
>> +};
>> +
>> +MODULE_DEVICE_TABLE(of, rcar_gyroadc_match);
>> +
>> +static const struct of_device_id rcar_gyroadc_child_match[] = {
>> + /* Mode 1 ADCs */
>> + {
>> + .compatible = "fujitsu,mb88101a",
>> + .data = (void *)RCAR_GYROADC_MODE_SELECT_1_MB88101A,
>> + },
>> + /* Mode 2 ADCs */
>> + {
>> + .compatible = "ti,adcs7476",
>> + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
>> + }, {
>> + .compatible = "ti,adc121",
>> + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
>> + }, {
>> + .compatible = "adi,ad7476",
>> + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
>> + },
>> + /* Mode 3 ADCs */
>> + {
>> + .compatible = "maxim,max1162",
>> + .data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162,
>> + }, {
>> + .compatible = "maxim,max11100",
>> + .data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162,
>> + },
>> + { /* sentinel */ }
>> +};
>> +
>> +static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
>> +{
>> + const struct of_device_id *of_id;
>> + const struct iio_chan_spec *channels;
>> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
>> + struct device *dev = priv->dev;
>> + struct device_node *np = dev->of_node;
>> + struct device_node *child;
>> + struct regulator *vref;
>> + unsigned int reg;
>> + unsigned int adcmode, childmode;
>> + unsigned int sample_width;
>> + unsigned int num_channels;
>> + int ret, first = 1;
>> +
>> + for_each_child_of_node(np, child) {
>> + of_id = of_match_node(rcar_gyroadc_child_match, child);
>> + if (!of_id) {
>> + dev_err(dev, "Ignoring unsupported ADC \"%s\".",
>> + child->name);
>> + continue;
>> + }
>> +
>> + childmode = (unsigned int)of_id->data;
>> + switch (childmode) {
>> + case RCAR_GYROADC_MODE_SELECT_1_MB88101A:
>> + sample_width = 12;
>> + channels = rcar_gyroadc_iio_channels_1;
>> + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_1);
>> + break;
>> + case RCAR_GYROADC_MODE_SELECT_2_ADCS7476:
>> + sample_width = 15;
>> + channels = rcar_gyroadc_iio_channels_2;
>> + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_2);
>> + break;
>> + case RCAR_GYROADC_MODE_SELECT_3_MAX1162:
>> + sample_width = 16;
>> + channels = rcar_gyroadc_iio_channels_3;
>> + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_3);
>> + break;
>> + }
>> +
>> + /*
>> + * MB88101 is special in that it's only a single chip taking
>> + * up all the CHS lines. Thus, the DT binding is also special
>> + * and has no reg property. If we run into such ADC, handle
>> + * it here.
>> + */
>> + if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) {
>> + reg = 0;
>> + } else {
>> + ret = of_property_read_u32(child, "reg", ®);
>> + if (ret) {
>> + dev_err(dev,
>> + "Failed to get child reg property of ADC \"%s\".\n",
>> + child->name);
>> + return ret;
>> + }
>> +
>> + /* Channel number is too high. */
>> + if (reg >= num_channels) {
>> + dev_err(dev,
>> + "Only %i channels supported with %s, but reg = <%i>.\n",
>> + num_channels, child->name, reg);
>> + return ret;
>> + }
>> + }
>> +
>> + /* Child node selected different mode than the rest. */
>> + if (!first && (adcmode != childmode)) {
>> + dev_err(dev,
>> + "Channel %i uses different ADC mode than the rest.\n",
>> + reg);
>> + return ret;
>> + }
>> +
>> + /* Channel is valid, grab the regulator. */
>> + dev->of_node = child;
>> + vref = devm_regulator_get(dev, "vref");
>> + dev->of_node = np;
>> + if (IS_ERR(vref)) {
>> + dev_dbg(dev, "Channel %i 'vref' supply not connected.\n",
>> + reg);
>> + return PTR_ERR(vref);
>> + }
>> +
>> + priv->vref[reg] = vref;
>> +
>> + if (!first)
>> + continue;
>> +
>> + /* First child node which passed sanity tests. */
>> + adcmode = childmode;
>> + first = 0;
>> +
>> + priv->num_channels = num_channels;
>> + priv->mode = childmode;
>> + priv->sample_width = sample_width;
>> +
>> + indio_dev->channels = channels;
>> + indio_dev->num_channels = num_channels;
>> +
>> + /*
>> + * MB88101 is special and we only have one such device
>> + * attached to the GyroADC at a time, so if we found it,
>> + * we can stop parsing here.
>> + */
>> + if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A)
>> + break;
>> + }
>> +
>> + if (first) {
>> + dev_err(dev, "No valid ADC channels found, aborting.\n");
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void rcar_gyroadc_deinit_supplies(struct iio_dev *indio_dev)
>> +{
>> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
>> + unsigned int i;
>> +
>> + for (i = 0; i < priv->num_channels; i++) {
>> + if (!priv->vref[i])
>> + continue;
>> +
>> + regulator_disable(priv->vref[i]);
>> + }
>> +}
>> +
>> +static int rcar_gyroadc_init_supplies(struct iio_dev *indio_dev)
>> +{
>> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
>> + struct device *dev = priv->dev;
>> + unsigned int i;
>> + int ret;
>> +
>> + for (i = 0; i < priv->num_channels; i++) {
>> + if (!priv->vref[i])
>> + continue;
>> +
>> + ret = regulator_enable(priv->vref[i]);
>> + if (ret) {
>> + dev_err(dev, "Failed to enable regulator %i (ret=%i)\n",
>> + i, ret);
>> + goto err;
>> + }
>> + }
>> +
>> + return 0;
>> +
>> +err:
>> + rcar_gyroadc_deinit_supplies(indio_dev);
>> + return ret;
>> +}
>> +
>> +static int rcar_gyroadc_probe(struct platform_device *pdev)
>> +{
>> + const struct of_device_id *of_id =
>> + of_match_device(rcar_gyroadc_match, &pdev->dev);
>> + struct device *dev = &pdev->dev;
>> + struct rcar_gyroadc *priv;
>> + struct iio_dev *indio_dev;
>> + struct resource *mem;
>> + int ret;
>> +
>> + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
>> + if (!indio_dev) {
>> + dev_err(dev, "Failed to allocate IIO device.\n");
>> + return -ENOMEM;
>> + }
>> +
>> + priv = iio_priv(indio_dev);
>> + priv->dev = dev;
>> +
>> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + priv->regs = devm_ioremap_resource(dev, mem);
>> + if (IS_ERR(priv->regs))
>> + return PTR_ERR(priv->regs);
>> +
>> + priv->iclk = devm_clk_get(dev, "if");
>> + if (IS_ERR(priv->iclk)) {
>> + ret = PTR_ERR(priv->iclk);
>> + if (ret != -EPROBE_DEFER)
>> + dev_err(dev, "Failed to get IF clock (ret=%i)\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = rcar_gyroadc_parse_subdevs(indio_dev);
>> + if (ret)
>> + return ret;
>> +
>> + ret = rcar_gyroadc_init_supplies(indio_dev);
>> + if (ret)
>> + return ret;
>> +
>> + priv->model = (enum rcar_gyroadc_model)of_id->data;
>> +
>> + platform_set_drvdata(pdev, indio_dev);
>> +
>> + indio_dev->name = DRIVER_NAME;
>> + indio_dev->dev.parent = dev;
>> + indio_dev->dev.of_node = pdev->dev.of_node;
>> + indio_dev->info = &rcar_gyroadc_iio_info;
>> + indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> + ret = clk_prepare_enable(priv->iclk);
>> + if (ret) {
>> + dev_err(dev, "Could not prepare or enable the IF clock.\n");
>> + goto err_clk_if_enable;
>> + }
>> +
>> + pm_runtime_set_autosuspend_delay(dev, RCAR_GYROADC_RUNTIME_PM_DELAY_MS);
>> + pm_runtime_use_autosuspend(dev);
>> + pm_runtime_enable(dev);
>> +
>> + pm_runtime_get_sync(dev);
>> + rcar_gyroadc_hw_init(priv);
>> + rcar_gyroadc_hw_start(priv);
>> +
>> + ret = iio_device_register(indio_dev);
>> + if (ret) {
>> + dev_err(dev, "Couldn't register IIO device.\n");
>> + goto err_iio_device_register;
>> + }
>> +
>> + pm_runtime_put_sync(dev);
>> +
>> + return 0;
>> +
>> +err_iio_device_register:
>> + rcar_gyroadc_hw_stop(priv);
>> + pm_runtime_put_sync(dev);
>> + pm_runtime_disable(dev);
>> + pm_runtime_set_suspended(dev);
>> + clk_disable_unprepare(priv->iclk);
>> +err_clk_if_enable:
>> + rcar_gyroadc_deinit_supplies(indio_dev);
>> +
>> + return ret;
>> +}
>> +
>> +static int rcar_gyroadc_remove(struct platform_device *pdev)
>> +{
>> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
>> + struct device *dev = priv->dev;
>> +
>> + iio_device_unregister(indio_dev);
>> + pm_runtime_get_sync(dev);
>> + rcar_gyroadc_hw_stop(priv);
>> + pm_runtime_put_sync(dev);
>> + pm_runtime_disable(dev);
>> + pm_runtime_set_suspended(dev);
>> + clk_disable_unprepare(priv->iclk);
>> + rcar_gyroadc_deinit_supplies(indio_dev);
>> +
>> + return 0;
>> +}
>> +
>> +#if defined(CONFIG_PM)
>> +static int rcar_gyroadc_suspend(struct device *dev)
>> +{
>> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
>> +
>> + rcar_gyroadc_hw_stop(priv);
>> +
>> + return 0;
>> +}
>> +
>> +static int rcar_gyroadc_resume(struct device *dev)
>> +{
>> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
>> +
>> + rcar_gyroadc_hw_start(priv);
>> +
>> + return 0;
>> +}
>> +#endif
>> +
>> +static const struct dev_pm_ops rcar_gyroadc_pm_ops = {
>> + SET_RUNTIME_PM_OPS(rcar_gyroadc_suspend, rcar_gyroadc_resume, NULL)
>> +};
>> +
>> +static struct platform_driver rcar_gyroadc_driver = {
>> + .probe = rcar_gyroadc_probe,
>> + .remove = rcar_gyroadc_remove,
>> + .driver = {
>> + .name = DRIVER_NAME,
>> + .of_match_table = rcar_gyroadc_match,
>> + .pm = &rcar_gyroadc_pm_ops,
>> + },
>> +};
>> +
>> +module_platform_driver(rcar_gyroadc_driver);
>> +
>> +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
>> +MODULE_DESCRIPTION("Renesas R-Car GyroADC driver");
>> +MODULE_LICENSE("GPL");
>>
>
--
Best regards,
Marek Vasut
^ permalink raw reply
* [PATCH 1/2] iio: adc: Add Renesas GyroADC bindings
From: Marek Vasut @ 2017-01-21 14:42 UTC (permalink / raw)
To: linux-iio-u79uwXL29TY76Z2rM5mHXA
Cc: Marek Vasut, Geert Uytterhoeven, Simon Horman, Jonathan Cameron,
linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA, Wolfram Sang,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA
Add DT bindings for the Renesas RCar GyroADC block. This block is
a simple 4/8-channel ADC which samples 12/15/24 bits of data every
cycle from all channels.
Signed-off-by: Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
Cc: Simon Horman <horms+renesas-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
Cc: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Wolfram Sang <wsa-z923LK4zBo2bacvFa/9K2g@public.gmane.org>
Cc: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
---
.../bindings/iio/adc/renesas,gyroadc.txt | 93 ++++++++++++++++++++++
1 file changed, 93 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
new file mode 100644
index 000000000000..081947367135
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
@@ -0,0 +1,93 @@
+* Renesas RCar GyroADC device driver
+
+Required properties:
+- compatible: Should be "renesas,<chip>-gyroadc", "renesas,rcar-gyroadc".
+ Use "renesas,r8a7792-gyroadc" for a GyroADC with interrupt
+ block found in R8A7792.
+- reg: Address and length of the register set for the device
+- clocks: References to all the clocks specified in the clock-names
+ property as specified in
+ Documentation/devicetree/bindings/clock/clock-bindings.txt.
+- clock-names: Shall contain "fck" and "if". The "fck" is the GyroADC block
+ clock, the "if" is the interface clock.
+- power-domains: Must contain a reference to the PM domain, if available.
+- #address-cells: Should be <1> (setting for the subnodes) for all ADCs
+ except for "fujitsu,mb88101a". Should be <0> (setting for
+ only subnode) for "fujitsu,mb88101a".
+- #size-cells: Should be <0> (setting for the subnodes)
+
+Sub-nodes:
+Optionally you can define subnodes which select the connected ADC type and
+reference voltage for the GyroADC channels.
+
+Required properties for subnodes:
+- compatible: Should be either of:
+ "fujitsu,mb88101a"
+ - Fujitsu MB88101A compatible mode,
+ 12bit sampling, up to 4 channels can be sampled in
+ round-robin fashion. One Fujitsu chip supplies four
+ GyroADC channels with data as it contains four ADCs
+ on the chip and thus for 4-channel operation, single
+ MB88101A is required. The Cx chipselect lines of the
+ MB88101A connect directly to two CHS lines of the
+ GyroADC, no demuxer is required. The data out line
+ of each MB88101A connects to a shared input pin of
+ the GyroADC.
+ "ti,adcs7476" or "ti,adc121" or "adi,ad7476"
+ - TI ADCS7476 / TI ADC121 / ADI AD7476 compatible mode,
+ 15bit sampling, up to 8 channels can be sampled in
+ round-robin fashion. One TI/ADI chip supplies single
+ ADC channel with data, thus for 8-channel operation,
+ 8 chips are required. A 3:8 chipselect demuxer is
+ required to connect the nCS line of the TI/ADI chips
+ to the GyroADC, while MISO line of each TI/ADI ADC
+ connects to a shared input pin of the GyroADC.
+ "maxim,max1162" or "maxim,max11100"
+ - Maxim MAX1162 / Maxim MAX11100 compatible mode,
+ 16bit sampling, up to 8 channels can be sampled in
+ round-robin fashion. One Maxim chip supplies single
+ ADC channel with data, thus for 8-channel operation,
+ 8 chips are required. A 3:8 chipselect demuxer is
+ required to connect the nCS line of the MAX chips
+ to the GyroADC, while MISO line of each Maxim ADC
+ connects to a shared input pin of the GyroADC.
+- reg: Should be the number of the analog input. Should be present
+ for all ADCs except "fujitsu,mb88101a".
+- vref-supply: Reference to the channel reference voltage regulator.
+
+Example:
+ vref_max1162: regulator-vref-max1162 {
+ compatible = "regulator-fixed";
+
+ regulator-name = "MAX1162 Vref";
+ regulator-min-microvolt = <4096000>;
+ regulator-max-microvolt = <4096000>;
+ };
+
+ &adc {
+ compatible = "renesas,r8a7791-gyroadc", "renesas,rcar-gyroadc";
+ reg = <0 0xe6e54000 0 64>;
+ clocks = <&mstp9_clks R8A7791_CLK_GYROADC>, <&clk_65m>;
+ clock-names = "fck", "if";
+ power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
+
+ pinctrl-0 = <&adc_pins>;
+ pinctrl-names = "default";
+
+ status = "okay";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@0 {
+ reg = <0>;
+ compatible = "maxim,max1162";
+ vref-supply = <&vref_max1162>;
+ };
+
+ adc@1 {
+ reg = <1>;
+ compatible = "maxim,max1162";
+ vref-supply = <&vref_max1162>;
+ };
+ };
--
2.11.0
^ permalink raw reply related
* Re: [PATCH v3 1/1] iio: adc: tlc4541: add support for TI tlc4541 adc
From: Jonathan Cameron @ 2017-01-21 14:11 UTC (permalink / raw)
To: Phil Reid, knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
pmeerw-jW+XmwGofnusTnJN9+BGXg, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1484555904-117718-1-git-send-email-preid-qgqNFa1JUf/o2iN0hyhwsIdd74u8MsAO@public.gmane.org>
On 16/01/17 08:38, Phil Reid wrote:
> This adds TI's tlc4541 16-bit ADC driver. Which is a single channel
> ADC. Supports raw and trigger buffer access.
> Also supports the tlc3541 14-bit device, which has not been tested.
> Implementation of the tlc3541 is fairly straight forward thou.
>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Signed-off-by: Phil Reid <preid-qgqNFa1JUf/o2iN0hyhwsIdd74u8MsAO@public.gmane.org>
Excellent.
Applied to the togreg branch of iio.git and initially pushed out
as testing for the autobuilders to play with it.
Thanks,
Jonathan
> ---
>
> Notes:
> Changes from v2:
> - Fix paste error in binding document.
> - Remove index from channel description. Note this break libiio.
> libiio will no longer recognise that the cahnnel is buffer enabled.
> - Add brackets to macro parameter bitshift
> - Make tcl3541 / tcl4541 order in code consistent.
> - Use spi_message_init_with_transfers
> - Cleanup whitespace lines
> - Add TLC3541 to kconfig description
> - Add Robs Ack.
>
> Changes from v1:
> - Add tlc3541 support and chan spec.
> - remove fields that where already 0 from TLC4541_V_CHAN macro
> - Increase rx_buf size in tlc4541_state to avoid copy in tlc4541_trigger_handle
> - Remove erroneous be16_to_cpu in tlc4541_trigger_handle
> - Docs/binding: spi -> SPI & add ti,tlc3541
>
> .../devicetree/bindings/iio/adc/ti-tlc4541.txt | 17 ++
> drivers/iio/adc/Kconfig | 12 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/ti-tlc4541.c | 271 +++++++++++++++++++++
> 4 files changed, 301 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/ti-tlc4541.txt
> create mode 100644 drivers/iio/adc/ti-tlc4541.c
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/ti-tlc4541.txt b/Documentation/devicetree/bindings/iio/adc/ti-tlc4541.txt
> new file mode 100644
> index 0000000..6b26927
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/ti-tlc4541.txt
> @@ -0,0 +1,17 @@
> +* Texas Instruments' TLC4541
> +
> +Required properties:
> + - compatible: Should be one of
> + * "ti,tlc4541"
> + * "ti,tlc3541"
> + - reg: SPI chip select number for the device
> + - vref-supply: The regulator supply for ADC reference voltage
> + - spi-max-frequency: Max SPI frequency to use (<= 200000)
> +
> +Example:
> +adc@0 {
> + compatible = "ti,tlc4541";
> + reg = <0>;
> + vref-supply = <&vdd_supply>;
> + spi-max-frequency = <200000>;
> +};
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 99c0514..fb9ede7 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -525,6 +525,18 @@ config TI_AM335X_ADC
> To compile this driver as a module, choose M here: the module will be
> called ti_am335x_adc.
>
> +config TI_TLC4541
> + tristate "Texas Instruments TLC4541 ADC driver"
> + depends on SPI
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> + help
> + Say yes here to build support for Texas Instruments TLC4541 / TLC3541
> + ADC chips.
> +
> + This driver can also be built as a module. If so, the module will be
> + called ti-tlc4541.
> +
> config TWL4030_MADC
> tristate "TWL4030 MADC (Monitoring A/D Converter)"
> depends on TWL4030_CORE
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 7a40c04..9bf2377 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -49,6 +49,7 @@ obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
> obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
> obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
> obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
> +obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
> obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
> obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
> obj-$(CONFIG_VF610_ADC) += vf610_adc.o
> diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c
> new file mode 100644
> index 0000000..78d91a0
> --- /dev/null
> +++ b/drivers/iio/adc/ti-tlc4541.c
> @@ -0,0 +1,271 @@
> +/*
> + * TI tlc4541 ADC Driver
> + *
> + * Copyright (C) 2017 Phil Reid
> + *
> + * Datasheets can be found here:
> + * http://www.ti.com/lit/gpn/tlc3541
> + * http://www.ti.com/lit/gpn/tlc4541
> + *
> + * 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.
> + *
> + * The tlc4541 requires 24 clock cycles to start a transfer.
> + * Conversion then takes 2.94us to complete before data is ready
> + * Data is returned MSB first.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +#include <linux/sysfs.h>
> +
> +struct tlc4541_state {
> + struct spi_device *spi;
> + struct regulator *reg;
> + struct spi_transfer scan_single_xfer[3];
> + struct spi_message scan_single_msg;
> +
> + /*
> + * DMA (thus cache coherency maintenance) requires the
> + * transfer buffers to live in their own cache lines.
> + * 2 bytes data + 6 bytes padding + 8 bytes timestamp when
> + * call iio_push_to_buffers_with_timestamp.
> + */
> + __be16 rx_buf[8] ____cacheline_aligned;
> +};
> +
> +struct tlc4541_chip_info {
> + const struct iio_chan_spec *channels;
> + unsigned int num_channels;
> +};
> +
> +enum tlc4541_id {
> + TLC3541,
> + TLC4541,
> +};
> +
> +#define TLC4541_V_CHAN(bits, bitshift) { \
> + .type = IIO_VOLTAGE, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
> + .scan_type = { \
> + .sign = 'u', \
> + .realbits = (bits), \
> + .storagebits = 16, \
> + .shift = (bitshift), \
> + .endianness = IIO_BE, \
> + }, \
> + }
> +
> +#define DECLARE_TLC4541_CHANNELS(name, bits, bitshift) \
> +const struct iio_chan_spec name ## _channels[] = { \
> + TLC4541_V_CHAN(bits, bitshift), \
> + IIO_CHAN_SOFT_TIMESTAMP(1), \
> +}
> +
> +static DECLARE_TLC4541_CHANNELS(tlc3541, 14, 2);
> +static DECLARE_TLC4541_CHANNELS(tlc4541, 16, 0);
> +
> +static const struct tlc4541_chip_info tlc4541_chip_info[] = {
> + [TLC3541] = {
> + .channels = tlc3541_channels,
> + .num_channels = ARRAY_SIZE(tlc3541_channels),
> + },
> + [TLC4541] = {
> + .channels = tlc4541_channels,
> + .num_channels = ARRAY_SIZE(tlc4541_channels),
> + },
> +};
> +
> +static irqreturn_t tlc4541_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct tlc4541_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + ret = spi_sync(st->spi, &st->scan_single_msg);
> + if (ret < 0)
> + goto done;
> +
> + iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
> + iio_get_time_ns(indio_dev));
> +
> +done:
> + iio_trigger_notify_done(indio_dev->trig);
> + return IRQ_HANDLED;
> +}
> +
> +static int tlc4541_get_range(struct tlc4541_state *st)
> +{
> + int vref;
> +
> + vref = regulator_get_voltage(st->reg);
> + if (vref < 0)
> + return vref;
> +
> + vref /= 1000;
> +
> + return vref;
> +}
> +
> +static int tlc4541_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val,
> + int *val2,
> + long m)
> +{
> + int ret = 0;
> + struct tlc4541_state *st = iio_priv(indio_dev);
> +
> + switch (m) {
> + case IIO_CHAN_INFO_RAW:
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> + ret = spi_sync(st->spi, &st->scan_single_msg);
> + iio_device_release_direct_mode(indio_dev);
> + if (ret < 0)
> + return ret;
> + *val = be16_to_cpu(st->rx_buf[0]);
> + *val = *val >> chan->scan_type.shift;
> + *val &= GENMASK(chan->scan_type.realbits - 1, 0);
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_SCALE:
> + ret = tlc4541_get_range(st);
> + if (ret < 0)
> + return ret;
> + *val = ret;
> + *val2 = chan->scan_type.realbits;
> + return IIO_VAL_FRACTIONAL_LOG2;
> + }
> + return -EINVAL;
> +}
> +
> +static const struct iio_info tlc4541_info = {
> + .read_raw = &tlc4541_read_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static int tlc4541_probe(struct spi_device *spi)
> +{
> + struct tlc4541_state *st;
> + struct iio_dev *indio_dev;
> + const struct tlc4541_chip_info *info;
> + int ret;
> + int8_t device_init = 0;
> +
> + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
> + if (indio_dev == NULL)
> + return -ENOMEM;
> +
> + st = iio_priv(indio_dev);
> +
> + spi_set_drvdata(spi, indio_dev);
> +
> + st->spi = spi;
> +
> + info = &tlc4541_chip_info[spi_get_device_id(spi)->driver_data];
> +
> + indio_dev->name = spi_get_device_id(spi)->name;
> + indio_dev->dev.parent = &spi->dev;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->channels = info->channels;
> + indio_dev->num_channels = info->num_channels;
> + indio_dev->info = &tlc4541_info;
> +
> + /* perform reset */
> + spi_write(spi, &device_init, 1);
> +
> + /* Setup default message */
> + st->scan_single_xfer[0].rx_buf = &st->rx_buf[0];
> + st->scan_single_xfer[0].len = 3;
> + st->scan_single_xfer[1].delay_usecs = 3;
> + st->scan_single_xfer[2].rx_buf = &st->rx_buf[0];
> + st->scan_single_xfer[2].len = 2;
> +
> + spi_message_init_with_transfers(&st->scan_single_msg,
> + st->scan_single_xfer, 3);
> +
> + st->reg = devm_regulator_get(&spi->dev, "vref");
> + if (IS_ERR(st->reg))
> + return PTR_ERR(st->reg);
> +
> + ret = regulator_enable(st->reg);
> + if (ret)
> + return ret;
> +
> + ret = iio_triggered_buffer_setup(indio_dev, NULL,
> + &tlc4541_trigger_handler, NULL);
> + if (ret)
> + goto error_disable_reg;
> +
> + ret = iio_device_register(indio_dev);
> + if (ret)
> + goto error_cleanup_buffer;
> +
> + return 0;
> +
> +error_cleanup_buffer:
> + iio_triggered_buffer_cleanup(indio_dev);
> +error_disable_reg:
> + regulator_disable(st->reg);
> +
> + return ret;
> +}
> +
> +static int tlc4541_remove(struct spi_device *spi)
> +{
> + struct iio_dev *indio_dev = spi_get_drvdata(spi);
> + struct tlc4541_state *st = iio_priv(indio_dev);
> +
> + iio_device_unregister(indio_dev);
> + iio_triggered_buffer_cleanup(indio_dev);
> + regulator_disable(st->reg);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id tlc4541_dt_ids[] = {
> + { .compatible = "ti,tlc3541", },
> + { .compatible = "ti,tlc4541", },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, tlc4541_dt_ids);
> +#endif
> +
> +static const struct spi_device_id tlc4541_id[] = {
> + {"tlc3541", TLC3541},
> + {"tlc4541", TLC4541},
> + {}
> +};
> +MODULE_DEVICE_TABLE(spi, tlc4541_id);
> +
> +static struct spi_driver tlc4541_driver = {
> + .driver = {
> + .name = "tlc4541",
> + .of_match_table = of_match_ptr(tlc4541_dt_ids),
> + },
> + .probe = tlc4541_probe,
> + .remove = tlc4541_remove,
> + .id_table = tlc4541_id,
> +};
> +module_spi_driver(tlc4541_driver);
> +
> +MODULE_AUTHOR("Phil Reid <preid-qgqNFa1JUf/o2iN0hyhwsIdd74u8MsAO@public.gmane.org>");
> +MODULE_DESCRIPTION("Texas Instruments TLC4541 ADC");
> +MODULE_LICENSE("GPL v2");
>
^ permalink raw reply
* Re: [PATCHv5 2/2] dt-bindings: iio: document MAX11100 ADC
From: Jonathan Cameron @ 2017-01-21 13:33 UTC (permalink / raw)
To: Jacopo Mondi, wsa+renesas-jBu1N2QxHDJrcw3mvpCnnVaTQe2KTcn/,
magnus.damm-Re5JQEeQqe8AvxtiuMwx3w, knaack.h-Mmb7MZpHnFY,
lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
marek.vasut-Re5JQEeQqe8AvxtiuMwx3w, geert-Td1EMuHUCqxL1ZNQvxDV9g,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <ff915715-98c4-9792-eae7-9f466d5fa459-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
On 21/01/17 13:22, Jonathan Cameron wrote:
> On 18/01/17 16:30, Jacopo Mondi wrote:
>> Add device tree bindings documentation for Maxim MAX11100 single-channel
>> ADC
>>
>> Signed-off-by: Jacopo Mondi <jacopo+renesas-AW8dsiIh9cEdnm+yROfE0A@public.gmane.org>
>> Reviewed-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
>> Acked-by: Wolfram Sang <wsa+renesas-jBu1N2QxHDJrcw3mvpCnnVaTQe2KTcn/@public.gmane.org>
> Simple enough I'm happy to take it without bothering Rob or Mark.
> However, any device tree bindings at all should be cc'd to them and to the devicetree
> list.
>
> Done in case anyone wants to comment.
Also applied to the togreg branch of iio.git which is pushed out as testing for
the autobuilders to play with everything in it.
Thanks,
Jonathan
>> ---
>> .../devicetree/bindings/iio/adc/max11100.txt | 19 +++++++++++++++++++
>> 1 file changed, 19 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/iio/adc/max11100.txt
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/max11100.txt b/Documentation/devicetree/bindings/iio/adc/max11100.txt
>> new file mode 100644
>> index 0000000..ad0bc31
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/max11100.txt
>> @@ -0,0 +1,19 @@
>> +* Maxim max11100 Analog to Digital Converter (ADC)
>> +
>> +Required properties:
>> + - compatible: Should be "maxim,max11100"
>> + - reg: the adc unit address
>> + - vref-supply: phandle to the regulator that provides reference voltage
>> +
>> +Optional properties:
>> + - spi-max-frequency: SPI maximum frequency
>> +
>> +Example:
>> +
>> +max11100: adc@0 {
>> + compatible = "maxim,max11100";
>> + reg = <0>;
>> + vref-supply = <&adc0_vref>;
>> + spi-max-frequency = <240000>;
>> +};
>> +
>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: [PATCH v3 4/4] ARM64: dts: meson: meson-gx: add the SAR ADC
From: Andreas Färber @ 2017-01-21 13:28 UTC (permalink / raw)
To: Martin Blumenstingl, jic23, knaack.h, lars, pmeerw, robh+dt,
mark.rutland, khilman, linux-iio, devicetree, linux-amlogic,
linux-clk
Cc: narmstrong, catalin.marinas, mturquette, will.deacon, carlo,
sboyd, linux-arm-kernel
In-Reply-To: <20170119145822.26239-5-martin.blumenstingl@googlemail.com>
Am 19.01.2017 um 15:58 schrieb Martin Blumenstingl:
> Add the SAR ADC to meson-gxbb.dtsi and meson-gxl.dtsi. GXBB provides a
> 10-bit ADC while GXL (and GXM, which uses the same ADC as GXL) provides
> a 12-bit ADC.
> Some boards use resistor ladder buttons connected through one of the ADC
> channels. On newer devices (GXL and GXM) some boards use pull-ups/downs
> to change the resistance (and thus the ADC value) on of the ADC channels
"on one of"?
> to indicate the board revision.
>
> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> Tested-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
> arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 8 ++++++++
> arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 10 ++++++++++
> arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 10 ++++++++++
> 3 files changed, 28 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
> index 99e6a8d5cb9e..55abfb74aab2 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
> +++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
> @@ -255,6 +255,14 @@
> status = "disabled";
> };
>
> + saradc: adc@8680 {
> + compatible = "amlogic,meson-saradc";
> + #io-channel-cells = <1>;
Personally I think it makes more sense to keep reg after compatible for
consistency (same in example) and any #... "output" properties last.
> + status = "disabled";
> + reg = <0x0 0x8680 0x0 0x34>;
> + interrupts = <GIC_SPI 73 IRQ_TYPE_EDGE_RISING>;
> + };
> +
> pwm_ef: pwm@86c0 {
> compatible = "amlogic,meson-gx-pwm", "amlogic,meson-gxbb-pwm";
> reg = <0x0 0x086c0 0x0 0x10>;
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
> index 39a774ad83ce..04b3324bc132 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
> @@ -490,6 +490,16 @@
> clocks = <&clkc CLKID_I2C>;
> };
>
> +&saradc {
> + compatible = "amlogic,meson-gxbb-saradc", "amlogic,meson-saradc";
> + clocks = <&xtal>,
> + <&clkc CLKID_SAR_ADC>,
> + <&clkc CLKID_SANA>,
> + <&clkc CLKID_SAR_ADC_CLK>,
> + <&clkc CLKID_SAR_ADC_SEL>;
> + clock-names = "clkin", "core", "sana", "adc_clk", "adc_sel";
> +};
> +
> &sd_emmc_a {
> clocks = <&clkc CLKID_SD_EMMC_A>,
> <&xtal>,
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
> index bdf2305a2e25..7c72dbcef1ba 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
> @@ -340,6 +340,16 @@
> clocks = <&clkc CLKID_I2C>;
> };
>
> +&saradc {
> + compatible = "amlogic,meson-gxl-saradc", "amlogic,meson-saradc";
> + clocks = <&xtal>,
> + <&clkc CLKID_SAR_ADC>,
> + <&clkc CLKID_SANA>,
> + <&clkc CLKID_SAR_ADC_CLK>,
> + <&clkc CLKID_SAR_ADC_SEL>;
> + clock-names = "clkin", "core", "sana", "adc_clk", "adc_sel";
> +};
> +
> &sd_emmc_a {
> clocks = <&clkc CLKID_SD_EMMC_A>,
> <&xtal>,
Anyway, saradc before sd_emmc_a looks fine,
Reviewed-by: Andreas Färber <afaerber@suse.de>
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
^ permalink raw reply
* Re: [PATCHv5 2/2] dt-bindings: iio: document MAX11100 ADC
From: Jonathan Cameron @ 2017-01-21 13:22 UTC (permalink / raw)
To: Jacopo Mondi, wsa+renesas, magnus.damm, knaack.h, lars, pmeerw,
marek.vasut, geert, mark.rutland
Cc: linux-iio, devicetree, linux-renesas-soc, Rob Herring,
Mark Rutland
In-Reply-To: <1484757053-7102-3-git-send-email-jacopo+renesas@jmondi.org>
On 18/01/17 16:30, Jacopo Mondi wrote:
> Add device tree bindings documentation for Maxim MAX11100 single-channel
> ADC
>
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Simple enough I'm happy to take it without bothering Rob or Mark.
However, any device tree bindings at all should be cc'd to them and to the devicetree
list.
Done in case anyone wants to comment.
> ---
> .../devicetree/bindings/iio/adc/max11100.txt | 19 +++++++++++++++++++
> 1 file changed, 19 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/max11100.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/max11100.txt b/Documentation/devicetree/bindings/iio/adc/max11100.txt
> new file mode 100644
> index 0000000..ad0bc31
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/max11100.txt
> @@ -0,0 +1,19 @@
> +* Maxim max11100 Analog to Digital Converter (ADC)
> +
> +Required properties:
> + - compatible: Should be "maxim,max11100"
> + - reg: the adc unit address
> + - vref-supply: phandle to the regulator that provides reference voltage
> +
> +Optional properties:
> + - spi-max-frequency: SPI maximum frequency
> +
> +Example:
> +
> +max11100: adc@0 {
> + compatible = "maxim,max11100";
> + reg = <0>;
> + vref-supply = <&adc0_vref>;
> + spi-max-frequency = <240000>;
> +};
> +
>
^ permalink raw reply
* Re: [PATCH v3 1/4] Documentation: dt-bindings: add the Amlogic Meson SAR ADC documentation
From: Andreas Färber @ 2017-01-21 13:21 UTC (permalink / raw)
To: Martin Blumenstingl, jic23, knaack.h, lars, pmeerw, robh+dt,
mark.rutland, khilman, linux-iio, devicetree, linux-amlogic,
linux-clk
Cc: narmstrong, catalin.marinas, mturquette, will.deacon, carlo,
sboyd, linux-arm-kernel
In-Reply-To: <20170119145822.26239-2-martin.blumenstingl@googlemail.com>
Am 19.01.2017 um 15:58 schrieb Martin Blumenstingl:
> This adds the devicetree binding documentation for the SAR ADC found in
> Amlogic Meson SoCs.
> Currently only the GXBB, GXL and GXM SoCs are supported.
>
> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> Tested-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Andreas Färber <afaerber@suse.de>
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
^ permalink raw reply
* Re: [PATCH v9 0/8] drivers: touchscreen: tsc2007 and ads7846/tsc2046 improvements (use common touchscreen bindings, pre-calibration, spi fix and provide iio raw values)
From: H. Nikolaus Schaller @ 2017-01-21 13:17 UTC (permalink / raw)
To: Jonathan Cameron, Dmitry Torokhov
Cc: Sebastian Reichel, Mark Rutland, Benoît Cousson,
Tony Lindgren, Russell King, Arnd Bergmann, Michael Welling,
Mika Penttilä, Javier Martinez Canillas, Igor Grinberg,
Andrew F. Davis, Mark Brown, Rob Herring, Alexander Stein,
Eric Engestrom, Hans de Goede, Benjamin Tissoires, Petr Cvek
In-Reply-To: <17d2c23b-6cbb-d365-71cb-fd4d7af7c958-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Am 21.01.2017 um 14:08 schrieb Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>:
>
> On 16/01/17 06:34, H. Nikolaus Schaller wrote:
>> ping
> Always good to say who you are pinging ;)
default = Maintainer
Only he/she can finally accept a patch series so I assume he has
to take action if there are no more things to debate.
> Saves the rest of us wondering
> if we have missing something we should have reviewed.
>
> Guessing it's Dmitry in this case!
Yes, you are right. Since there have not been any more comments for a
while, I formally ask to add this to some tree so that it appears in
linux-next for further tests.
BR and thanks,
Nikolaus
>
> Jonathan
>>
>>> Am 28.12.2016 um 15:53 schrieb H. Nikolaus Schaller <hns-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>:
>>>
>>> Changes V9:
>>> * added explicit CONFIG for TSC2007_IIO and simplified Makefile (suggested by Dmitry Torokhov <dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>)
>>> * make the tsc2007 iio patch the last one in this sequence
>>>
>>> 2016-11-22 15:02:42: Changes V8:
>>> * fix compilation for CONFIG_IIO=m (reported by Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>)
>>> * add some more Reviewed-by: and Acked-by:
>>> * mutiple improvements suggested by Dmitry Torokhov <dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>:
>>> ** drop patch "send pendown and penup only once" (input core does take care of it now)
>>> ** remove not necessary EXPORT_SYMBOL
>>> ** remove explicit file names from comment header
>>> ** move tsc2007_iio_configure() to the end of the probe process and simplify the error path again
>>> ** remove unnecessary input_unregister_device()
>>> ** improvement for compilation with CONFIG_IIO=m and CONFIG_TSC2007=y
>>>
>>> 2016-11-11 20:02:11: Changes V7:
>>> * rearranged the include files (asked for by Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>)
>>> * forward reference struct iio_dev * instead of condition in tsc2007.h (asked for by Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>)
>>> * add some Acked-by:
>>>
>>> 2016-10-27 10:44:29: Changes V6:
>>> * iio patch (no changes elsewhere)
>>> - tsc2007_iio: fix a missing return 0 for non-iio case (found by kbuid test robot)
>>> - tsc2007_core: group error return paths so that tsc2007_iio_unconfigure is called at only one place
>>> - tsc2007_iio: fix copyright (this file is 100% original work)
>>>
>>> 2016-10-25 21:26:46: Changes V5:
>>> * ads7846: remove an empty line (suggested by Andrew F. Davis <afd-l0cyMroinI0@public.gmane.org>)
>>> * ads7846: remove MODULE_ALIAS for SPI (suggested by Andrew F. Davis <afd-l0cyMroinI0@public.gmane.org>)
>>> * tsc2007: fix a bug from swapping patch 3/n and patch 4/n (found by kbuild test robot)
>>> * refactored tsc2007 into tsc2007_core and tsc2007_iio (asked for by Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>)
>>>
>>> 2016-10-17 16:00:02: Changes V4:
>>> * fix a merge/squash issue resulting in a non-bisectable patch set (suggested by kbuid test robot)
>>> * remove some unnecessary #include (suggested by Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>)
>>> * make the iio extension depend on CONFIG_IIO rather than selecting it (suggested by Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>)
>>> * swapped patch 3/n and patch 4/n to remove internal dependency
>>>
>>> 2016-09-23 14:41:23: Changes V3:
>>> * fix an issue with swapping
>>> * remove hard clipping to min/max rectangle - some systems expect to handle negative coordinates
>>> * make use of commit ed7c9870c9bc ("Input: of_touchscreen - add support for inverted / swapped axes")
>>>
>>> 2015-11-13 21:36:07: Changes V2:
>>> * add a patch to make drivers still recognise the old "ti,swap-xy" property (suggested by Rob Herring)
>>>
>>> 2015-11-06 16:14:53: V1: This patch series improves the drivers for the tsc2007 and
>>> ads7846/tsc2046 touchscreen controllers which are e.g. used by the GTA04
>>> OpenPandora and Pyra devices.
>>>
>>> New common bindings have been defined by
>>> commit b98abe52fa8e ("Input: add common DT binding for touchscreens"):
>>>
>>> Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
>>>
>>> which also defines a helper function to parse the DT. These new parameters
>>> allow to specify the fuzz factors (jitter suppression), inversion of x or y axis and
>>> swapping of x and y to achieve inversion and rotation so that the touch
>>> coordinate axes match the natural orientation of the display panel.
>>>
>>> Another improvement is to better use the min/max ADC values and
>>> scale to the screen size as defined by the DT. This allows to coarsely
>>> calibrate the touch to match the LCD to which it is glued on so that the
>>> touch can quite precisely be operated before any user-space fine-calibration
>>> can be (and needs to be) started.
>>>
>>> For the adc7846 we fix an issue with the spi module table.
>>>
>>> Finally we add an iio interface for the AUX and temperature ADC channels of
>>> the tsc2007 and also provide the touch screen raw values. This allows to read
>>> an optional ambient light sensor installed on the gta04 board and improves
>>> calibration and hardware monitoring.
>>>
>>>
>>> H. Nikolaus Schaller (8):
>>> drivers:input:tsc2007: add new common binding names, pre-calibration,
>>> flipping and rotation
>>> drivers:input:tsc2007: check for presence and power down tsc2007
>>> during probe
>>> DT:omap3+tsc2007: use new common touchscreen bindings
>>> drivers:input:ads7846(+tsc2046): add new common binding names,
>>> pre-calibration and flipping
>>> dt-bindings: input: move ads7846 bindings to touchscreen subdirectory
>>> drivers:input:ads7846(+tsc2046): fix spi module table
>>> DT:omap3+ads7846: use new common touchscreen bindings
>>> drivers:input:tsc2007: add iio interface to read external ADC input
>>> and temperature
>>>
>>> .../bindings/input/{ => touchscreen}/ads7846.txt | 9 +-
>>> .../bindings/input/touchscreen/tsc2007.txt | 20 +-
>>> arch/arm/boot/dts/omap3-gta04.dtsi | 25 ++-
>>> arch/arm/boot/dts/omap3-lilly-a83x.dtsi | 2 +-
>>> arch/arm/boot/dts/omap3-pandora-common.dtsi | 17 +-
>>> .../boot/dts/omap3-panel-sharp-ls037v7dw01.dtsi | 3 +-
>>> drivers/input/touchscreen/Kconfig | 10 +
>>> drivers/input/touchscreen/Makefile | 2 +
>>> drivers/input/touchscreen/ads7846.c | 71 ++++++--
>>> drivers/input/touchscreen/tsc2007.h | 116 ++++++++++++
>>> .../touchscreen/{tsc2007.c => tsc2007_core.c} | 201 ++++++++++++---------
>>> drivers/input/touchscreen/tsc2007_iio.c | 150 +++++++++++++++
>>> include/linux/i2c/tsc2007.h | 8 +
>>> 13 files changed, 520 insertions(+), 114 deletions(-)
>>> rename Documentation/devicetree/bindings/input/{ => touchscreen}/ads7846.txt (90%)
>>> create mode 100644 drivers/input/touchscreen/tsc2007.h
>>> rename drivers/input/touchscreen/{tsc2007.c => tsc2007_core.c} (74%)
>>> create mode 100644 drivers/input/touchscreen/tsc2007_iio.c
>>>
>>> --
>>> 2.7.3
>>>
>>
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v9 0/8] drivers: touchscreen: tsc2007 and ads7846/tsc2046 improvements (use common touchscreen bindings, pre-calibration, spi fix and provide iio raw values)
From: Jonathan Cameron @ 2017-01-21 13:08 UTC (permalink / raw)
To: H. Nikolaus Schaller, Sebastian Reichel, Dmitry Torokhov,
Mark Rutland, Benoît Cousson, Tony Lindgren, Russell King,
Arnd Bergmann, Michael Welling, Mika Penttilä,
Javier Martinez Canillas, Igor Grinberg, Andrew F. Davis,
Mark Brown, Rob Herring, Alexander Stein, Eric Engestrom,
Hans de Goede
Cc: linux-input-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA,
letux-kernel-S0jZdbWzriLCfDggNXIi3w,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
kernel-Jl6IXVxNIMRxAtABVqVhTwC/G2K4zDHf
In-Reply-To: <BB8421B2-6626-4A95-BCD0-25C4CAF30F61-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>
On 16/01/17 06:34, H. Nikolaus Schaller wrote:
> ping
Always good to say who you are pinging ;) Saves the rest of us wondering
if we have missing something we should have reviewed.
Guessing it's Dmitry in this case!
Jonathan
>
>> Am 28.12.2016 um 15:53 schrieb H. Nikolaus Schaller <hns-xXXSsgcRVICgSpxsJD1C4w@public.gmane.org>:
>>
>> Changes V9:
>> * added explicit CONFIG for TSC2007_IIO and simplified Makefile (suggested by Dmitry Torokhov <dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>)
>> * make the tsc2007 iio patch the last one in this sequence
>>
>> 2016-11-22 15:02:42: Changes V8:
>> * fix compilation for CONFIG_IIO=m (reported by Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>)
>> * add some more Reviewed-by: and Acked-by:
>> * mutiple improvements suggested by Dmitry Torokhov <dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>:
>> ** drop patch "send pendown and penup only once" (input core does take care of it now)
>> ** remove not necessary EXPORT_SYMBOL
>> ** remove explicit file names from comment header
>> ** move tsc2007_iio_configure() to the end of the probe process and simplify the error path again
>> ** remove unnecessary input_unregister_device()
>> ** improvement for compilation with CONFIG_IIO=m and CONFIG_TSC2007=y
>>
>> 2016-11-11 20:02:11: Changes V7:
>> * rearranged the include files (asked for by Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>)
>> * forward reference struct iio_dev * instead of condition in tsc2007.h (asked for by Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>)
>> * add some Acked-by:
>>
>> 2016-10-27 10:44:29: Changes V6:
>> * iio patch (no changes elsewhere)
>> - tsc2007_iio: fix a missing return 0 for non-iio case (found by kbuid test robot)
>> - tsc2007_core: group error return paths so that tsc2007_iio_unconfigure is called at only one place
>> - tsc2007_iio: fix copyright (this file is 100% original work)
>>
>> 2016-10-25 21:26:46: Changes V5:
>> * ads7846: remove an empty line (suggested by Andrew F. Davis <afd-l0cyMroinI0@public.gmane.org>)
>> * ads7846: remove MODULE_ALIAS for SPI (suggested by Andrew F. Davis <afd-l0cyMroinI0@public.gmane.org>)
>> * tsc2007: fix a bug from swapping patch 3/n and patch 4/n (found by kbuild test robot)
>> * refactored tsc2007 into tsc2007_core and tsc2007_iio (asked for by Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>)
>>
>> 2016-10-17 16:00:02: Changes V4:
>> * fix a merge/squash issue resulting in a non-bisectable patch set (suggested by kbuid test robot)
>> * remove some unnecessary #include (suggested by Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>)
>> * make the iio extension depend on CONFIG_IIO rather than selecting it (suggested by Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>)
>> * swapped patch 3/n and patch 4/n to remove internal dependency
>>
>> 2016-09-23 14:41:23: Changes V3:
>> * fix an issue with swapping
>> * remove hard clipping to min/max rectangle - some systems expect to handle negative coordinates
>> * make use of commit ed7c9870c9bc ("Input: of_touchscreen - add support for inverted / swapped axes")
>>
>> 2015-11-13 21:36:07: Changes V2:
>> * add a patch to make drivers still recognise the old "ti,swap-xy" property (suggested by Rob Herring)
>>
>> 2015-11-06 16:14:53: V1: This patch series improves the drivers for the tsc2007 and
>> ads7846/tsc2046 touchscreen controllers which are e.g. used by the GTA04
>> OpenPandora and Pyra devices.
>>
>> New common bindings have been defined by
>> commit b98abe52fa8e ("Input: add common DT binding for touchscreens"):
>>
>> Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
>>
>> which also defines a helper function to parse the DT. These new parameters
>> allow to specify the fuzz factors (jitter suppression), inversion of x or y axis and
>> swapping of x and y to achieve inversion and rotation so that the touch
>> coordinate axes match the natural orientation of the display panel.
>>
>> Another improvement is to better use the min/max ADC values and
>> scale to the screen size as defined by the DT. This allows to coarsely
>> calibrate the touch to match the LCD to which it is glued on so that the
>> touch can quite precisely be operated before any user-space fine-calibration
>> can be (and needs to be) started.
>>
>> For the adc7846 we fix an issue with the spi module table.
>>
>> Finally we add an iio interface for the AUX and temperature ADC channels of
>> the tsc2007 and also provide the touch screen raw values. This allows to read
>> an optional ambient light sensor installed on the gta04 board and improves
>> calibration and hardware monitoring.
>>
>>
>> H. Nikolaus Schaller (8):
>> drivers:input:tsc2007: add new common binding names, pre-calibration,
>> flipping and rotation
>> drivers:input:tsc2007: check for presence and power down tsc2007
>> during probe
>> DT:omap3+tsc2007: use new common touchscreen bindings
>> drivers:input:ads7846(+tsc2046): add new common binding names,
>> pre-calibration and flipping
>> dt-bindings: input: move ads7846 bindings to touchscreen subdirectory
>> drivers:input:ads7846(+tsc2046): fix spi module table
>> DT:omap3+ads7846: use new common touchscreen bindings
>> drivers:input:tsc2007: add iio interface to read external ADC input
>> and temperature
>>
>> .../bindings/input/{ => touchscreen}/ads7846.txt | 9 +-
>> .../bindings/input/touchscreen/tsc2007.txt | 20 +-
>> arch/arm/boot/dts/omap3-gta04.dtsi | 25 ++-
>> arch/arm/boot/dts/omap3-lilly-a83x.dtsi | 2 +-
>> arch/arm/boot/dts/omap3-pandora-common.dtsi | 17 +-
>> .../boot/dts/omap3-panel-sharp-ls037v7dw01.dtsi | 3 +-
>> drivers/input/touchscreen/Kconfig | 10 +
>> drivers/input/touchscreen/Makefile | 2 +
>> drivers/input/touchscreen/ads7846.c | 71 ++++++--
>> drivers/input/touchscreen/tsc2007.h | 116 ++++++++++++
>> .../touchscreen/{tsc2007.c => tsc2007_core.c} | 201 ++++++++++++---------
>> drivers/input/touchscreen/tsc2007_iio.c | 150 +++++++++++++++
>> include/linux/i2c/tsc2007.h | 8 +
>> 13 files changed, 520 insertions(+), 114 deletions(-)
>> rename Documentation/devicetree/bindings/input/{ => touchscreen}/ads7846.txt (90%)
>> create mode 100644 drivers/input/touchscreen/tsc2007.h
>> rename drivers/input/touchscreen/{tsc2007.c => tsc2007_core.c} (74%)
>> create mode 100644 drivers/input/touchscreen/tsc2007_iio.c
>>
>> --
>> 2.7.3
>>
>
^ permalink raw reply
* Re: [PATCH V6] iio: adc: Add Renesas GyroADC driver
From: Jonathan Cameron @ 2017-01-21 13:04 UTC (permalink / raw)
To: Marek Vasut, linux-iio-u79uwXL29TY76Z2rM5mHXA
Cc: Geert Uytterhoeven, Simon Horman,
linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <20170115170535.24463-1-marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On 15/01/17 17:05, Marek Vasut wrote:
> Add IIO driver for the Renesas RCar GyroADC block. This block is a
> simple 4/8-channel ADC which samples 12/15/24 bits of data every
> cycle from all channels.
>
> Signed-off-by: Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Cc: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
> Cc: Simon Horman <horms+renesas-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
> Cc: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Hi Marek,
One thing I hadn't picked up on earlier. This hasn't been going to the devicetree
list or maintainers. I'd like an ack from them on the bindings as they are a little
'unusual' given the odd nature of the part.
Cc'd Rob, Mark and the list.
Jonathan
> ---
> V2: - Spelling fixes
> - Rename the driver source file to rcar-gyroadc
> - Rework the channel sample width handling
> - Use iio_device_claim_mode_direct()
> - Rename "renesas,rcar-gyroadc" to "renesas,r8a7791-gyroadc" and
> rename "renesas,rcar-gyroadc-r8a7792" to "renesas,r8a7792-gyroadc"
> to match the new naming scheme (WARNING: scif uses the old one!)
> - Switch to using regulators for channel voltage reference, add new
> properties renesas,gyroadc-vref-chN-supply for N in 0..7
> - Handle vref regulators as optional to, make channels without
> vref regulator return EINVAL on read.
> - Fix module license to GPL
> - Drop interrupt.h include
> - Rename clk to iclk
> - Rename RCar to R-Car
> - Rework the invalid mode handling
> - Don't print error message on EPROBE_DEFER
> - Drop fclk handling, use runtime PM for that instead
> V3: - More R-Car spelling fixes
> - Flip checks for V2H, since that's the only one that has
> interrupt registers
> - Replace if-else statement with switch statement in init_mode
> - Use unsigned types where applicable
> - Rework timing calculation slightly to drop if-else block
> - Use DIV_ROUND_CLOSEST
> V4: - Add renesas,rcar-gyroadc fallback compatible string into the bindings
> - Rework the ADC bindings to use per-channel subdevs
> - Support more compatible ADC chips
> V5: - Reword the DT bindings, improve the description of the modes
> - Improve the description in Kconfig
> - Implement special DT bindings for the MB88101A 4-channel ADC and
> add special handling for this chip into the driver.
> - Add missing ADC stop on failure, wrap it into a function
> - Fail hard in case the DT properties are incorrect or missing
> - Fix reporting of scale, so that channel * scale value is in mV
> - Use fractional scale instead of int plus nano
> - Use pm_runtime to disable the sampling 2 seconds after last used
> V6: - Replace the indio_dev->name with "rcar-gyroadc" instead of OF node name
> ---
> .../bindings/iio/adc/renesas,gyroadc.txt | 93 +++
> MAINTAINERS | 6 +
> drivers/iio/adc/Kconfig | 13 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/rcar-gyroadc.c | 631 +++++++++++++++++++++
> 5 files changed, 744 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
> create mode 100644 drivers/iio/adc/rcar-gyroadc.c
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
> new file mode 100644
> index 000000000000..081947367135
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
> @@ -0,0 +1,93 @@
> +* Renesas RCar GyroADC device driver
> +
> +Required properties:
> +- compatible: Should be "renesas,<chip>-gyroadc", "renesas,rcar-gyroadc".
> + Use "renesas,r8a7792-gyroadc" for a GyroADC with interrupt
> + block found in R8A7792.
> +- reg: Address and length of the register set for the device
> +- clocks: References to all the clocks specified in the clock-names
> + property as specified in
> + Documentation/devicetree/bindings/clock/clock-bindings.txt.
> +- clock-names: Shall contain "fck" and "if". The "fck" is the GyroADC block
> + clock, the "if" is the interface clock.
> +- power-domains: Must contain a reference to the PM domain, if available.
> +- #address-cells: Should be <1> (setting for the subnodes) for all ADCs
> + except for "fujitsu,mb88101a". Should be <0> (setting for
> + only subnode) for "fujitsu,mb88101a".
> +- #size-cells: Should be <0> (setting for the subnodes)
> +
> +Sub-nodes:
> +Optionally you can define subnodes which select the connected ADC type and
> +reference voltage for the GyroADC channels.
> +
> +Required properties for subnodes:
> +- compatible: Should be either of:
> + "fujitsu,mb88101a"
> + - Fujitsu MB88101A compatible mode,
> + 12bit sampling, up to 4 channels can be sampled in
> + round-robin fashion. One Fujitsu chip supplies four
> + GyroADC channels with data as it contains four ADCs
> + on the chip and thus for 4-channel operation, single
> + MB88101A is required. The Cx chipselect lines of the
> + MB88101A connect directly to two CHS lines of the
> + GyroADC, no demuxer is required. The data out line
> + of each MB88101A connects to a shared input pin of
> + the GyroADC.
> + "ti,adcs7476" or "ti,adc121" or "adi,ad7476"
> + - TI ADCS7476 / TI ADC121 / ADI AD7476 compatible mode,
> + 15bit sampling, up to 8 channels can be sampled in
> + round-robin fashion. One TI/ADI chip supplies single
> + ADC channel with data, thus for 8-channel operation,
> + 8 chips are required. A 3:8 chipselect demuxer is
> + required to connect the nCS line of the TI/ADI chips
> + to the GyroADC, while MISO line of each TI/ADI ADC
> + connects to a shared input pin of the GyroADC.
> + "maxim,max1162" or "maxim,max11100"
> + - Maxim MAX1162 / Maxim MAX11100 compatible mode,
> + 16bit sampling, up to 8 channels can be sampled in
> + round-robin fashion. One Maxim chip supplies single
> + ADC channel with data, thus for 8-channel operation,
> + 8 chips are required. A 3:8 chipselect demuxer is
> + required to connect the nCS line of the MAX chips
> + to the GyroADC, while MISO line of each Maxim ADC
> + connects to a shared input pin of the GyroADC.
> +- reg: Should be the number of the analog input. Should be present
> + for all ADCs except "fujitsu,mb88101a".
> +- vref-supply: Reference to the channel reference voltage regulator.
> +
> +Example:
> + vref_max1162: regulator-vref-max1162 {
> + compatible = "regulator-fixed";
> +
> + regulator-name = "MAX1162 Vref";
> + regulator-min-microvolt = <4096000>;
> + regulator-max-microvolt = <4096000>;
> + };
> +
> + &adc {
> + compatible = "renesas,r8a7791-gyroadc", "renesas,rcar-gyroadc";
> + reg = <0 0xe6e54000 0 64>;
> + clocks = <&mstp9_clks R8A7791_CLK_GYROADC>, <&clk_65m>;
> + clock-names = "fck", "if";
> + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
> +
> + pinctrl-0 = <&adc_pins>;
> + pinctrl-names = "default";
> +
> + status = "okay";
> +
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + adc@0 {
> + reg = <0>;
> + compatible = "maxim,max1162";
> + vref-supply = <&vref_max1162>;
> + };
> +
> + adc@1 {
> + reg = <1>;
> + compatible = "maxim,max1162";
> + vref-supply = <&vref_max1162>;
> + };
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 890fc9e3c191..498e8a755eb6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10276,6 +10276,12 @@ L: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> F: drivers/net/ethernet/renesas/
> F: include/linux/sh_eth.h
>
> +RENESAS R-CAR GYROADC DRIVER
> +M: Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> +L: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +S: Supported
> +F: drivers/iio/adc/rcar_gyro_adc.c
> +
> RENESAS USB2 PHY DRIVER
> M: Yoshihiro Shimoda <yoshihiro.shimoda.uh-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> L: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 99c051490eff..2fbc11f42740 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -408,6 +408,19 @@ config QCOM_SPMI_VADC
> To compile this driver as a module, choose M here: the module will
> be called qcom-spmi-vadc.
>
> +config RCAR_GYRO_ADC
> + tristate "Renesas R-Car GyroADC driver"
> + depends on ARCH_RCAR_GEN2 || (ARM && COMPILE_TEST)
> + help
> + Say yes here to build support for the GyroADC found in Renesas
> + R-Car Gen2 SoCs. This block is a simple SPI offload engine for
> + reading data out of attached compatible ADCs in a round-robin
> + fashion. Up to 4 or 8 ADC channels are supported by this block,
> + depending on which ADCs are attached.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called rcar-gyroadc.
> +
> config ROCKCHIP_SARADC
> tristate "Rockchip SARADC driver"
> depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 7a40c04c311f..13db7c2bffc8 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_NAU7802) += nau7802.o
> obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
> obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
> obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
> +obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
> obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
> obj-$(CONFIG_STX104) += stx104.o
> obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
> diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c
> new file mode 100644
> index 000000000000..0c44f72c32a8
> --- /dev/null
> +++ b/drivers/iio/adc/rcar-gyroadc.c
> @@ -0,0 +1,631 @@
> +/*
> + * Renesas R-Car GyroADC driver
> + *
> + * Copyright 2016 Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@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/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/of_platform.h>
> +#include <linux/err.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +
> +#define DRIVER_NAME "rcar-gyroadc"
> +
> +/* GyroADC registers. */
> +#define RCAR_GYROADC_MODE_SELECT 0x00
> +#define RCAR_GYROADC_MODE_SELECT_1_MB88101A 0x0
> +#define RCAR_GYROADC_MODE_SELECT_2_ADCS7476 0x1
> +#define RCAR_GYROADC_MODE_SELECT_3_MAX1162 0x3
> +
> +#define RCAR_GYROADC_START_STOP 0x04
> +#define RCAR_GYROADC_START_STOP_START BIT(0)
> +
> +#define RCAR_GYROADC_CLOCK_LENGTH 0x08
> +#define RCAR_GYROADC_1_25MS_LENGTH 0x0c
> +
> +#define RCAR_GYROADC_REALTIME_DATA(ch) (0x10 + ((ch) * 4))
> +#define RCAR_GYROADC_100MS_ADDED_DATA(ch) (0x30 + ((ch) * 4))
> +#define RCAR_GYROADC_10MS_AVG_DATA(ch) (0x50 + ((ch) * 4))
> +
> +#define RCAR_GYROADC_FIFO_STATUS 0x70
> +#define RCAR_GYROADC_FIFO_STATUS_EMPTY(ch) BIT(0 + (4 * (ch)))
> +#define RCAR_GYROADC_FIFO_STATUS_FULL(ch) BIT(1 + (4 * (ch)))
> +#define RCAR_GYROADC_FIFO_STATUS_ERROR(ch) BIT(2 + (4 * (ch)))
> +
> +#define RCAR_GYROADC_INTR 0x74
> +#define RCAR_GYROADC_INTR_INT BIT(0)
> +
> +#define RCAR_GYROADC_INTENR 0x78
> +#define RCAR_GYROADC_INTENR_INTEN BIT(0)
> +
> +#define RCAR_GYROADC_SAMPLE_RATE 800 /* Hz */
> +
> +#define RCAR_GYROADC_RUNTIME_PM_DELAY_MS 2000
> +
> +enum rcar_gyroadc_model {
> + RCAR_GYROADC_MODEL_DEFAULT,
> + RCAR_GYROADC_MODEL_R8A7792,
> +};
> +
> +struct rcar_gyroadc {
> + struct device *dev;
> + void __iomem *regs;
> + struct clk *iclk;
> + struct regulator *vref[8];
> + unsigned int num_channels;
> + enum rcar_gyroadc_model model;
> + unsigned int mode;
> + unsigned int sample_width;
> +};
> +
> +static void rcar_gyroadc_hw_init(struct rcar_gyroadc *priv)
> +{
> + const unsigned long clk_mhz = clk_get_rate(priv->iclk) / 1000000;
> + const unsigned long clk_mul =
> + (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) ? 10 : 5;
> + unsigned long clk_len = clk_mhz * clk_mul;
> +
> + /*
> + * According to the R-Car Gen2 datasheet Rev. 1.01, Sept 08 2014,
> + * page 77-7, clock length must be even number. If it's odd number,
> + * add one.
> + */
> + if (clk_len & 1)
> + clk_len++;
> +
> + /* Stop the GyroADC. */
> + writel(0, priv->regs + RCAR_GYROADC_START_STOP);
> +
> + /* Disable IRQ on V2H. */
> + if (priv->model == RCAR_GYROADC_MODEL_R8A7792)
> + writel(0, priv->regs + RCAR_GYROADC_INTENR);
> +
> + /* Set mode and timing. */
> + writel(priv->mode, priv->regs + RCAR_GYROADC_MODE_SELECT);
> + writel(clk_len, priv->regs + RCAR_GYROADC_CLOCK_LENGTH);
> + writel(clk_mhz * 1250, priv->regs + RCAR_GYROADC_1_25MS_LENGTH);
> +}
> +
> +static void rcar_gyroadc_hw_start(struct rcar_gyroadc *priv)
> +{
> + /* Start sampling. */
> + writel(RCAR_GYROADC_START_STOP_START,
> + priv->regs + RCAR_GYROADC_START_STOP);
> +
> + /*
> + * Wait for the first conversion to complete. This is longer than
> + * the 1.25 mS in the datasheet because 1.25 mS is not enough for
> + * the hardware to deliver the first sample and the hardware does
> + * then return zeroes instead of valid data.
> + */
> + mdelay(3);
> +}
> +
> +static void rcar_gyroadc_hw_stop(struct rcar_gyroadc *priv)
> +{
> + /* Stop the GyroADC. */
> + writel(0, priv->regs + RCAR_GYROADC_START_STOP);
> +}
> +
> +#define RCAR_GYROADC_CHAN(_idx) { \
> + .type = IIO_VOLTAGE, \
> + .indexed = 1, \
> + .channel = (_idx), \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_SCALE), \
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> +}
> +
> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_1[] = {
> + RCAR_GYROADC_CHAN(0),
> + RCAR_GYROADC_CHAN(1),
> + RCAR_GYROADC_CHAN(2),
> + RCAR_GYROADC_CHAN(3),
> +};
> +
> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_2[] = {
> + RCAR_GYROADC_CHAN(0),
> + RCAR_GYROADC_CHAN(1),
> + RCAR_GYROADC_CHAN(2),
> + RCAR_GYROADC_CHAN(3),
> + RCAR_GYROADC_CHAN(4),
> + RCAR_GYROADC_CHAN(5),
> + RCAR_GYROADC_CHAN(6),
> + RCAR_GYROADC_CHAN(7),
> +};
> +
> +static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = {
> + RCAR_GYROADC_CHAN(0),
> + RCAR_GYROADC_CHAN(1),
> + RCAR_GYROADC_CHAN(2),
> + RCAR_GYROADC_CHAN(3),
> + RCAR_GYROADC_CHAN(4),
> + RCAR_GYROADC_CHAN(5),
> + RCAR_GYROADC_CHAN(6),
> + RCAR_GYROADC_CHAN(7),
> +};
> +
> +static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on)
> +{
> + struct device *dev = priv->dev;
> + int ret;
> +
> + if (on) {
> + ret = pm_runtime_get_sync(dev);
> + if (ret < 0)
> + pm_runtime_put_noidle(dev);
> + } else {
> + pm_runtime_mark_last_busy(dev);
> + ret = pm_runtime_put_autosuspend(dev);
> + }
> +
> + return ret;
> +}
> +
> +static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
> + struct regulator *consumer;
> + unsigned int datareg = RCAR_GYROADC_REALTIME_DATA(chan->channel);
> + unsigned int vref;
> + int ret;
> +
> + /*
> + * MB88101 is special in that it has only single regulator for
> + * all four channels.
> + */
> + if (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A)
> + consumer = priv->vref[0];
> + else
> + consumer = priv->vref[chan->channel];
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + if (chan->type != IIO_VOLTAGE)
> + return -EINVAL;
> +
> + /* Channel not connected. */
> + if (!consumer)
> + return -EINVAL;
> +
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> +
> + ret = rcar_gyroadc_set_power(priv, true);
> + if (ret < 0) {
> + iio_device_release_direct_mode(indio_dev);
> + return ret;
> + }
> +
> + *val = readl(priv->regs + datareg);
> + *val &= BIT(priv->sample_width) - 1;
> +
> + ret = rcar_gyroadc_set_power(priv, false);
> + iio_device_release_direct_mode(indio_dev);
> + if (ret < 0)
> + return ret;
> +
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_SCALE:
> + /* Channel not connected. */
> + if (!consumer)
> + return -EINVAL;
> +
> + vref = regulator_get_voltage(consumer);
> + *val = vref / 1000;
> + *val2 = 1 << priv->sample_width;
> +
> + return IIO_VAL_FRACTIONAL;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + *val = RCAR_GYROADC_SAMPLE_RATE;
> +
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int rcar_gyroadc_reg_access(struct iio_dev *indio_dev,
> + unsigned int reg, unsigned int writeval,
> + unsigned int *readval)
> +{
> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
> + unsigned int maxreg = RCAR_GYROADC_FIFO_STATUS;
> +
> + if (readval == NULL)
> + return -EINVAL;
> +
> + if (reg % 4)
> + return -EINVAL;
> +
> + /* Handle the V2H case with extra interrupt block. */
> + if (priv->model == RCAR_GYROADC_MODEL_R8A7792)
> + maxreg = RCAR_GYROADC_INTENR;
> +
> + if (reg > maxreg)
> + return -EINVAL;
> +
> + *readval = readl(priv->regs + reg);
> +
> + return 0;
> +}
> +
> +static const struct iio_info rcar_gyroadc_iio_info = {
> + .driver_module = THIS_MODULE,
> + .read_raw = rcar_gyroadc_read_raw,
> + .debugfs_reg_access = rcar_gyroadc_reg_access,
> +};
> +
> +static const struct of_device_id rcar_gyroadc_match[] = {
> + {
> + /* R-Car compatible GyroADC */
> + .compatible = "renesas,rcar-gyroadc",
> + .data = (void *)RCAR_GYROADC_MODEL_DEFAULT,
> + }, {
> + /* R-Car V2H specialty with interrupt registers. */
> + .compatible = "renesas,r8a7792-gyroadc",
> + .data = (void *)RCAR_GYROADC_MODEL_R8A7792,
> + }, {
> + /* sentinel */
> + }
> +};
> +
> +MODULE_DEVICE_TABLE(of, rcar_gyroadc_match);
> +
> +static const struct of_device_id rcar_gyroadc_child_match[] = {
> + /* Mode 1 ADCs */
> + {
> + .compatible = "fujitsu,mb88101a",
> + .data = (void *)RCAR_GYROADC_MODE_SELECT_1_MB88101A,
> + },
> + /* Mode 2 ADCs */
> + {
> + .compatible = "ti,adcs7476",
> + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
> + }, {
> + .compatible = "ti,adc121",
> + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
> + }, {
> + .compatible = "adi,ad7476",
> + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
> + },
> + /* Mode 3 ADCs */
> + {
> + .compatible = "maxim,max1162",
> + .data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162,
> + }, {
> + .compatible = "maxim,max11100",
> + .data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162,
> + },
> + { /* sentinel */ }
> +};
> +
> +static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
> +{
> + const struct of_device_id *of_id;
> + const struct iio_chan_spec *channels;
> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
> + struct device *dev = priv->dev;
> + struct device_node *np = dev->of_node;
> + struct device_node *child;
> + struct regulator *vref;
> + unsigned int reg;
> + unsigned int adcmode, childmode;
> + unsigned int sample_width;
> + unsigned int num_channels;
> + int ret, first = 1;
> +
> + for_each_child_of_node(np, child) {
> + of_id = of_match_node(rcar_gyroadc_child_match, child);
> + if (!of_id) {
> + dev_err(dev, "Ignoring unsupported ADC \"%s\".",
> + child->name);
> + continue;
> + }
> +
> + childmode = (unsigned int)of_id->data;
> + switch (childmode) {
> + case RCAR_GYROADC_MODE_SELECT_1_MB88101A:
> + sample_width = 12;
> + channels = rcar_gyroadc_iio_channels_1;
> + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_1);
> + break;
> + case RCAR_GYROADC_MODE_SELECT_2_ADCS7476:
> + sample_width = 15;
> + channels = rcar_gyroadc_iio_channels_2;
> + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_2);
> + break;
> + case RCAR_GYROADC_MODE_SELECT_3_MAX1162:
> + sample_width = 16;
> + channels = rcar_gyroadc_iio_channels_3;
> + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_3);
> + break;
> + }
> +
> + /*
> + * MB88101 is special in that it's only a single chip taking
> + * up all the CHS lines. Thus, the DT binding is also special
> + * and has no reg property. If we run into such ADC, handle
> + * it here.
> + */
> + if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) {
> + reg = 0;
> + } else {
> + ret = of_property_read_u32(child, "reg", ®);
> + if (ret) {
> + dev_err(dev,
> + "Failed to get child reg property of ADC \"%s\".\n",
> + child->name);
> + return ret;
> + }
> +
> + /* Channel number is too high. */
> + if (reg >= num_channels) {
> + dev_err(dev,
> + "Only %i channels supported with %s, but reg = <%i>.\n",
> + num_channels, child->name, reg);
> + return ret;
> + }
> + }
> +
> + /* Child node selected different mode than the rest. */
> + if (!first && (adcmode != childmode)) {
> + dev_err(dev,
> + "Channel %i uses different ADC mode than the rest.\n",
> + reg);
> + return ret;
> + }
> +
> + /* Channel is valid, grab the regulator. */
> + dev->of_node = child;
> + vref = devm_regulator_get(dev, "vref");
> + dev->of_node = np;
> + if (IS_ERR(vref)) {
> + dev_dbg(dev, "Channel %i 'vref' supply not connected.\n",
> + reg);
> + return PTR_ERR(vref);
> + }
> +
> + priv->vref[reg] = vref;
> +
> + if (!first)
> + continue;
> +
> + /* First child node which passed sanity tests. */
> + adcmode = childmode;
> + first = 0;
> +
> + priv->num_channels = num_channels;
> + priv->mode = childmode;
> + priv->sample_width = sample_width;
> +
> + indio_dev->channels = channels;
> + indio_dev->num_channels = num_channels;
> +
> + /*
> + * MB88101 is special and we only have one such device
> + * attached to the GyroADC at a time, so if we found it,
> + * we can stop parsing here.
> + */
> + if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A)
> + break;
> + }
> +
> + if (first) {
> + dev_err(dev, "No valid ADC channels found, aborting.\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static void rcar_gyroadc_deinit_supplies(struct iio_dev *indio_dev)
> +{
> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
> + unsigned int i;
> +
> + for (i = 0; i < priv->num_channels; i++) {
> + if (!priv->vref[i])
> + continue;
> +
> + regulator_disable(priv->vref[i]);
> + }
> +}
> +
> +static int rcar_gyroadc_init_supplies(struct iio_dev *indio_dev)
> +{
> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
> + struct device *dev = priv->dev;
> + unsigned int i;
> + int ret;
> +
> + for (i = 0; i < priv->num_channels; i++) {
> + if (!priv->vref[i])
> + continue;
> +
> + ret = regulator_enable(priv->vref[i]);
> + if (ret) {
> + dev_err(dev, "Failed to enable regulator %i (ret=%i)\n",
> + i, ret);
> + goto err;
> + }
> + }
> +
> + return 0;
> +
> +err:
> + rcar_gyroadc_deinit_supplies(indio_dev);
> + return ret;
> +}
> +
> +static int rcar_gyroadc_probe(struct platform_device *pdev)
> +{
> + const struct of_device_id *of_id =
> + of_match_device(rcar_gyroadc_match, &pdev->dev);
> + struct device *dev = &pdev->dev;
> + struct rcar_gyroadc *priv;
> + struct iio_dev *indio_dev;
> + struct resource *mem;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
> + if (!indio_dev) {
> + dev_err(dev, "Failed to allocate IIO device.\n");
> + return -ENOMEM;
> + }
> +
> + priv = iio_priv(indio_dev);
> + priv->dev = dev;
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + priv->regs = devm_ioremap_resource(dev, mem);
> + if (IS_ERR(priv->regs))
> + return PTR_ERR(priv->regs);
> +
> + priv->iclk = devm_clk_get(dev, "if");
> + if (IS_ERR(priv->iclk)) {
> + ret = PTR_ERR(priv->iclk);
> + if (ret != -EPROBE_DEFER)
> + dev_err(dev, "Failed to get IF clock (ret=%i)\n", ret);
> + return ret;
> + }
> +
> + ret = rcar_gyroadc_parse_subdevs(indio_dev);
> + if (ret)
> + return ret;
> +
> + ret = rcar_gyroadc_init_supplies(indio_dev);
> + if (ret)
> + return ret;
> +
> + priv->model = (enum rcar_gyroadc_model)of_id->data;
> +
> + platform_set_drvdata(pdev, indio_dev);
> +
> + indio_dev->name = DRIVER_NAME;
> + indio_dev->dev.parent = dev;
> + indio_dev->dev.of_node = pdev->dev.of_node;
> + indio_dev->info = &rcar_gyroadc_iio_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + ret = clk_prepare_enable(priv->iclk);
> + if (ret) {
> + dev_err(dev, "Could not prepare or enable the IF clock.\n");
> + goto err_clk_if_enable;
> + }
> +
> + pm_runtime_set_autosuspend_delay(dev, RCAR_GYROADC_RUNTIME_PM_DELAY_MS);
> + pm_runtime_use_autosuspend(dev);
> + pm_runtime_enable(dev);
> +
> + pm_runtime_get_sync(dev);
> + rcar_gyroadc_hw_init(priv);
> + rcar_gyroadc_hw_start(priv);
> +
> + ret = iio_device_register(indio_dev);
> + if (ret) {
> + dev_err(dev, "Couldn't register IIO device.\n");
> + goto err_iio_device_register;
> + }
> +
> + pm_runtime_put_sync(dev);
> +
> + return 0;
> +
> +err_iio_device_register:
> + rcar_gyroadc_hw_stop(priv);
> + pm_runtime_put_sync(dev);
> + pm_runtime_disable(dev);
> + pm_runtime_set_suspended(dev);
> + clk_disable_unprepare(priv->iclk);
> +err_clk_if_enable:
> + rcar_gyroadc_deinit_supplies(indio_dev);
> +
> + return ret;
> +}
> +
> +static int rcar_gyroadc_remove(struct platform_device *pdev)
> +{
> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
> + struct device *dev = priv->dev;
> +
> + iio_device_unregister(indio_dev);
> + pm_runtime_get_sync(dev);
> + rcar_gyroadc_hw_stop(priv);
> + pm_runtime_put_sync(dev);
> + pm_runtime_disable(dev);
> + pm_runtime_set_suspended(dev);
> + clk_disable_unprepare(priv->iclk);
> + rcar_gyroadc_deinit_supplies(indio_dev);
> +
> + return 0;
> +}
> +
> +#if defined(CONFIG_PM)
> +static int rcar_gyroadc_suspend(struct device *dev)
> +{
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
> +
> + rcar_gyroadc_hw_stop(priv);
> +
> + return 0;
> +}
> +
> +static int rcar_gyroadc_resume(struct device *dev)
> +{
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> + struct rcar_gyroadc *priv = iio_priv(indio_dev);
> +
> + rcar_gyroadc_hw_start(priv);
> +
> + return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops rcar_gyroadc_pm_ops = {
> + SET_RUNTIME_PM_OPS(rcar_gyroadc_suspend, rcar_gyroadc_resume, NULL)
> +};
> +
> +static struct platform_driver rcar_gyroadc_driver = {
> + .probe = rcar_gyroadc_probe,
> + .remove = rcar_gyroadc_remove,
> + .driver = {
> + .name = DRIVER_NAME,
> + .of_match_table = rcar_gyroadc_match,
> + .pm = &rcar_gyroadc_pm_ops,
> + },
> +};
> +
> +module_platform_driver(rcar_gyroadc_driver);
> +
> +MODULE_AUTHOR("Marek Vasut <marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
> +MODULE_DESCRIPTION("Renesas R-Car GyroADC driver");
> +MODULE_LICENSE("GPL");
>
^ permalink raw reply
* Re: [PATCH 2/7] iio: adc: stm32: Enable use of stm32 timer triggers
From: Jonathan Cameron @ 2017-01-21 12:55 UTC (permalink / raw)
To: kbuild test robot, Fabrice Gasnier
Cc: kbuild-all-JC7UmRfGjtg, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA, mark.rutland-5wv7dgnIgG8,
mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
alexandre.torgue-qxv4g6HH51o, lars-Qo5EllUWu/uELgA04lAiVw,
knaack.h-Mmb7MZpHnFY, pmeerw-jW+XmwGofnusTnJN9+BGXg,
benjamin.gaignard-QSEj5FYQhm4dnm+yROfE0A,
benjamin.gaignard-qxv4g6HH51o
In-Reply-To: <201701200712.zoWuIMA4%fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
On 19/01/17 23:31, kbuild test robot wrote:
> Hi Fabrice,
>
> [auto build test ERROR on next-20170119]
> [also build test ERROR on v4.10-rc4]
> [cannot apply to iio/togreg robh/for-next v4.9-rc8 v4.9-rc7 v4.9-rc6]
> [if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
>
> url: https://github.com/0day-ci/linux/commits/Fabrice-Gasnier/Add-support-for-triggered-buffer-mode-to-STM32-ADC/20170120-062214
> config: i386-allmodconfig (attached as .config)
> compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
> reproduce:
> # save the attached .config to linux build tree
> make ARCH=i386
There is a precursor patch set mentioned in the cover letter. Not sure there is
a sensible automated way to deal with that!
Jonathan
>
> All errors (new ones prefixed by >>):
>
>>> drivers/iio/adc/stm32-adc.c:26:49: fatal error: linux/iio/timer/stm32-timer-trigger.h: No such file or directory
> #include <linux/iio/timer/stm32-timer-trigger.h>
> ^
> compilation terminated.
>
> vim +26 drivers/iio/adc/stm32-adc.c
>
> 20 */
> 21
> 22 #include <linux/clk.h>
> 23 #include <linux/delay.h>
> 24 #include <linux/iio/iio.h>
> 25 #include <linux/iio/buffer.h>
> > 26 #include <linux/iio/timer/stm32-timer-trigger.h>
> 27 #include <linux/iio/trigger.h>
> 28 #include <linux/iio/trigger_consumer.h>
> 29 #include <linux/iio/triggered_buffer.h>
>
> ---
> 0-DAY kernel test infrastructure Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all Intel Corporation
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox