Allwiner H3 and A83T SoCs have a thermal sensor, which is a large refactored
version of the old Allwinner "GPADC" (although it have already only
thermal part left in A33).
This patch tried to add support for the sensor in H3 and A83T based on
the A33 thermal sensor driver by Quentin Schulz, which is already merged.
This Patchseries is based on Icenowy Zhengs v4 patchseries [1].
The first 5 patches are reworked patches from the v4 patchseries.
The rest of the patches add step by step a feature to support multible
sensors, nvmem calibration and interupts. This patchseries should make it
easy also to add other sunxi SoCs, like the H5, A64 and A80.
Patches that adds support for H5, A64 and A80 SoCs are allready prepared,
and will be upstreamed if this patchseries is applied and the testing is done.
I tried to pick up all the feedback from [1]. I hope I didn't miss anything.
Regards,
Philipp
[1]: https://lkml.org/lkml/2017/9/14/317
Icenowy Zheng (1):
iio: adc: sun4i-gpadc-iio: rename A33-specified registers to contain
A33
Philipp Rossak (15):
dt-bindings: update the Allwinner GPADC device tree binding for H3 &
A83T
arm: config: sunxi_defconfig: enable SUN4I_GPADC
iio: adc: sun4i-gpadc-iio: rework: sampling start/end code readout reg
iio: adc: sun4i-gpadc-iio: rework: support clocks and reset
iio: adc: sun4i-gpadc-iio: rework: support multible sensors
iio: adc: sun4i-gpadc-iio: rework: support nvmem calibration data
iio: adc: sun4i-gpadc-iio: rework: add interrupt support
iio: adc: sun4i-gpadc-iio: add support for H3 thermal sensor
iio: adc: sun4i-gpadc-iio: add support for A83T thermal sensor
arm: dts: sunxi-h3-h5: add support for the thermal sensor in H3 and H5
arm: dts: sun8i: h3: add support for the thermal sensor in H3
arm: dts: sun8i: h3: add thermal zone to H3
arm: dts: sun8i: h3: enable H3 sid controller
arm: dts: sun8i: a83t: add support for the thermal sensor in A83T
arm: dts: sun8i: a83t: add thermal zone to A83T
.../devicetree/bindings/mfd/sun4i-gpadc.txt | 50 ++-
arch/arm/boot/dts/sun8i-a83t.dtsi | 28 ++
arch/arm/boot/dts/sun8i-h3.dtsi | 21 ++
arch/arm/boot/dts/sunxi-h3-h5.dtsi | 9 +
arch/arm/configs/sunxi_defconfig | 1 +
drivers/iio/adc/sun4i-gpadc-iio.c | 378 +++++++++++++++++++--
include/linux/mfd/sun4i-gpadc.h | 66 +++-
7 files changed, 522 insertions(+), 31 deletions(-)
--
2.11.0
As we have gained the support for the thermal sensor in A83T,
we can now add its device nodes to the device tree.
The A83T seems to have a broken IRQ 31, thus we use here IRQ 41.
Signed-off-by: Philipp Rossak <[email protected]>
---
arch/arm/boot/dts/sun8i-a83t.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi
index 7f4955a5fab7..9e53ff5ac4ed 100644
--- a/arch/arm/boot/dts/sun8i-a83t.dtsi
+++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
@@ -518,6 +518,14 @@
clocks = <&osc24M>;
};
+ ths: thermal-sensor@1f04000 {
+ compatible = "allwinner,sun8i-a83t-ths";
+ reg = <0x01f04000 0x100>;
+ interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
+ #thermal-sensor-cells = <1>;
+ #io-channel-cells = <0>;
+ };
+
watchdog@1c20ca0 {
compatible = "allwinner,sun6i-a31-wdt";
reg = <0x01c20ca0 0x20>;
--
2.11.0
As we have gained the support for the thermal sensor in H3 and H5,
we can now add its device nodes to the device tree. The H3 and H5 share
most of its compatible. The compatible and the thermal sensor cells
will be added in an additional patch per device.
Signed-off-by: Philipp Rossak <[email protected]>
---
arch/arm/boot/dts/sunxi-h3-h5.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
index 7a83b15225c7..413c789b588d 100644
--- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
+++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
@@ -426,6 +426,15 @@
};
};
+ ths: thermal-sensor@1c25000 {
+ reg = <0x01c25000 0x100>;
+ interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_THS>, <&ccu CLK_THS>;
+ clock-names = "bus", "mod";
+ resets = <&ccu RST_BUS_THS>;
+ #io-channel-cells = <0>;
+ };
+
timer@1c20c00 {
compatible = "allwinner,sun4i-a10-timer";
reg = <0x01c20c00 0xa0>;
--
2.11.0
This patch enables the the sid controller in the H3. It can be used
for thermal calibration data.
Signed-off-by: Philipp Rossak <[email protected]>
---
arch/arm/boot/dts/sun8i-h3.dtsi | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
index 3f83f6a27c74..9bb5cc29fec5 100644
--- a/arch/arm/boot/dts/sun8i-h3.dtsi
+++ b/arch/arm/boot/dts/sun8i-h3.dtsi
@@ -72,6 +72,13 @@
};
};
+ soc {
+ sid: eeprom@1c14000 {
+ compatible = "allwinner,sun8i-h3-sid";
+ reg = <0x01c14000 0x400>;
+ };
+ };
+
thermal-zones {
cpu-thermal {
/* milliseconds */
--
2.11.0
This patch adds the thermal zones to the A83T. Sensor 0 is located in the
cpu cluster 0. Sensor 1 is located in cluster 1 and Sensor 3 is located
in the gpu.
Signed-off-by: Philipp Rossak <[email protected]>
---
arch/arm/boot/dts/sun8i-a83t.dtsi | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi
index 9e53ff5ac4ed..4259a8726031 100644
--- a/arch/arm/boot/dts/sun8i-a83t.dtsi
+++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
@@ -747,4 +747,24 @@
#size-cells = <0>;
};
};
+
+ thermal-zones {
+ cpu0_thermal: cpu0-thermal {
+ polling-delay-passive = <1000>;
+ polling-delay = <5000>;
+ thermal-sensors = <&ths 0>;
+ };
+
+ cpu1_thermal: cpu1-thermal {
+ polling-delay-passive = <1000>;
+ polling-delay = <5000>;
+ thermal-sensors = <&ths 1>;
+ };
+
+ gpu_thermal: gpu-thermal {
+ polling-delay-passive = <1000>;
+ polling-delay = <5000>;
+ thermal-sensors = <&ths 2>;
+ };
+ };
};
--
2.11.0
This patch adds the thermal zones to the H3. We have only one sensor and
that is placed in the cpu.
Signed-off-by: Philipp Rossak <[email protected]>
---
arch/arm/boot/dts/sun8i-h3.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
index fbb007e5798e..3f83f6a27c74 100644
--- a/arch/arm/boot/dts/sun8i-h3.dtsi
+++ b/arch/arm/boot/dts/sun8i-h3.dtsi
@@ -72,6 +72,15 @@
};
};
+ thermal-zones {
+ cpu-thermal {
+ /* milliseconds */
+ polling-delay-passive = <250>;
+ polling-delay = <1000>;
+ thermal-sensors = <&ths 0>;
+ };
+ };
+
timer {
compatible = "arm,armv7-timer";
interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
--
2.11.0
This patch adds the missing compatible and the thermal sensor cells.
The H3 has one sensor.
Signed-off-by: Philipp Rossak <[email protected]>
---
arch/arm/boot/dts/sun8i-h3.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
index 8495deecedad..fbb007e5798e 100644
--- a/arch/arm/boot/dts/sun8i-h3.dtsi
+++ b/arch/arm/boot/dts/sun8i-h3.dtsi
@@ -128,3 +128,8 @@
&pio {
compatible = "allwinner,sun8i-h3-pinctrl";
};
+
+&ths {
+ compatible = "allwinner,sun8i-h3-ths";
+ #thermal-sensor-cells = <0>;
+};
--
2.11.0
This patch rewors the driver to support interrupts for the thermal part
of the sensor.
This is only available for the newer sensor (currently H3 and A83T).
The interrupt will be trigerd on data available and triggers the update
for the thermal sensors. All newer sensors have different amount of
sensors and different interrupts for each device the reset of the
interrupts need to be done different
For the newer sensors is the autosuspend disabled.
Signed-off-by: Philipp Rossak <[email protected]>
---
drivers/iio/adc/sun4i-gpadc-iio.c | 68 +++++++++++++++++++++++++++++++++++----
include/linux/mfd/sun4i-gpadc.h | 33 +++++++++++++++++++
2 files changed, 95 insertions(+), 6 deletions(-)
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
index 7b12666cdd9e..77e07f042730 100644
--- a/drivers/iio/adc/sun4i-gpadc-iio.c
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -78,11 +78,14 @@ struct gpadc_data {
u32 ctrl2_map;
u32 sensor_en_map;
u32 filter_map;
+ u32 irq_clear_map;
+ u32 irq_control_map;
bool has_bus_clk;
bool has_bus_rst;
bool has_mod_clk;
int sensor_count;
bool supports_nvmem;
+ bool support_irq;
};
static const struct gpadc_data sun4i_gpadc_data = {
@@ -97,6 +100,7 @@ static const struct gpadc_data sun4i_gpadc_data = {
.sample_end = sun4i_gpadc_sample_end,
.sensor_count = 1,
.supports_nvmem = false,
+ .support_irq = false,
};
static const struct gpadc_data sun5i_gpadc_data = {
@@ -111,6 +115,7 @@ static const struct gpadc_data sun5i_gpadc_data = {
.sample_end = sun4i_gpadc_sample_end,
.sensor_count = 1,
.supports_nvmem = false,
+ .support_irq = false,
};
static const struct gpadc_data sun6i_gpadc_data = {
@@ -125,6 +130,7 @@ static const struct gpadc_data sun6i_gpadc_data = {
.sample_end = sun4i_gpadc_sample_end,
.sensor_count = 1,
.supports_nvmem = false,
+ .support_irq = false,
};
static const struct gpadc_data sun8i_a33_gpadc_data = {
@@ -136,6 +142,7 @@ static const struct gpadc_data sun8i_a33_gpadc_data = {
.sample_end = sun4i_gpadc_sample_end,
.sensor_count = 1,
.supports_nvmem = false,
+ .support_irq = false,
};
struct sun4i_gpadc_iio {
@@ -339,6 +346,11 @@ static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val,
return 0;
}
+ if (info->data->support_irq) {
+ regmap_read(info->regmap, info->data->temp_data[sensor], val);
+ return 0;
+ }
+
return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
}
@@ -436,6 +448,17 @@ static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static irqreturn_t sunxi_irq_thread(int irq, void *data)
+{
+ struct sun4i_gpadc_iio *info = data;
+
+ regmap_write(info->regmap, SUNXI_THS_STAT, info->data->irq_clear_map);
+
+ thermal_zone_device_update(info->tzd, THERMAL_EVENT_TEMP_SAMPLE);
+
+ return IRQ_HANDLED;
+}
+
static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info)
{
/* Disable the ADC on IP */
@@ -448,6 +471,8 @@ static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info)
static int sunxi_ths_sample_end(struct sun4i_gpadc_iio *info)
{
+ /* Disable ths interrupt*/
+ regmap_write(info->regmap, SUNXI_THS_INTC, 0x0);
/* Disable temperature sensor */
regmap_write(info->regmap, SUNXI_THS_CTRL2, 0x0);
@@ -509,9 +534,15 @@ static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info)
regmap_write(info->regmap, SUNXI_THS_CTRL2,
info->data->ctrl2_map);
+ regmap_write(info->regmap, SUNXI_THS_STAT,
+ info->data->irq_clear_map);
+
regmap_write(info->regmap, SUNXI_THS_FILTER,
info->data->filter_map);
+ regmap_write(info->regmap, SUNXI_THS_INTC,
+ info->data->irq_control_map);
+
regmap_read(info->regmap, SUNXI_THS_CTRL2, &value);
regmap_write(info->regmap, SUNXI_THS_CTRL2,
@@ -625,12 +656,29 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
struct nvmem_cell *cell;
ssize_t cell_size;
u64 *cell_data;
+ int irq;
info->data = of_device_get_match_data(&pdev->dev);
if (!info->data)
return -ENODEV;
- info->no_irq = true;
+ if (info->data->support_irq) {
+ /* only the new versions of ths support right now irqs */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get IRQ: %d\n", irq);
+ return irq;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ sunxi_irq_thread, IRQF_ONESHOT,
+ dev_name(&pdev->dev), info);
+ if (ret)
+ return ret;
+
+ } else
+ info->no_irq = true;
+
indio_dev->num_channels = ARRAY_SIZE(sun8i_a33_gpadc_channels);
indio_dev->channels = sun8i_a33_gpadc_channels;
@@ -840,11 +888,13 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
if (ret)
return ret;
- pm_runtime_set_autosuspend_delay(&pdev->dev,
- SUN4I_GPADC_AUTOSUSPEND_DELAY);
- pm_runtime_use_autosuspend(&pdev->dev);
- pm_runtime_set_suspended(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
+ if (!info->data->support_irq) {
+ pm_runtime_set_autosuspend_delay(&pdev->dev,
+ SUN4I_GPADC_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ }
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
for (i = 0; i < info->data->sensor_count; i++) {
@@ -865,6 +915,9 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
}
}
+ if (info->data->support_irq)
+ info->data->sample_start(info);
+
ret = devm_iio_device_register(&pdev->dev, indio_dev);
if (ret < 0) {
dev_err(&pdev->dev, "could not register the device\n");
@@ -894,6 +947,9 @@ static int sun4i_gpadc_remove(struct platform_device *pdev)
if (!IS_ENABLED(CONFIG_THERMAL_OF))
return 0;
+ if (info->data->support_irq)
+ info->data->sample_end(info);
+
thermal_zone_of_sensor_unregister(info->sensor_device, info->tzd);
if (!info->no_irq)
diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
index c251002431bd..ab34a96a7ff3 100644
--- a/include/linux/mfd/sun4i-gpadc.h
+++ b/include/linux/mfd/sun4i-gpadc.h
@@ -89,6 +89,8 @@
/* SUNXI_THS COMMON REGISTERS + DEFINES */
#define SUNXI_THS_CTRL0 0x00
#define SUNXI_THS_CTRL2 0x40
+#define SUNXI_THS_INTC 0x44
+#define SUNXI_THS_STAT 0x48
#define SUNXI_THS_FILTER 0x70
#define SUNXI_THS_CDATA_0_1 0x74
#define SUNXI_THS_CDATA_2_3 0x78
@@ -107,6 +109,37 @@
#define SUNXI_THS_TEMP_SENSE_EN2 BIT(2)
#define SUNXI_THS_TEMP_SENSE_EN3 BIT(3)
+#define SUNXI_THS_TEMP_PERIOD(x) (GENMASK(31, 12) & ((x) << 12))
+
+#define SUNXI_THS_INTS_ALARM_OFF_2 BIT(14)
+#define SUNXI_THS_INTS_ALARM_OFF_1 BIT(13)
+#define SUNXI_THS_INTS_ALARM_OFF_0 BIT(12)
+#define SUNXI_THS_INTS_TDATA_IRQ_3 BIT(11)
+#define SUNXI_THS_INTS_TDATA_IRQ_2 BIT(10)
+#define SUNXI_THS_INTS_TDATA_IRQ_1 BIT(9)
+#define SUNXI_THS_INTS_TDATA_IRQ_0 BIT(8)
+#define SUNXI_THS_INTS_SHUT_INT_3 BIT(7)
+#define SUNXI_THS_INTS_SHUT_INT_2 BIT(6)
+#define SUNXI_THS_INTS_SHUT_INT_1 BIT(5)
+#define SUNXI_THS_INTS_SHUT_INT_0 BIT(4)
+#define SUNXI_THS_INTS_ALARM_INT_3 BIT(3)
+#define SUNXI_THS_INTS_ALARM_INT_2 BIT(2)
+#define SUNXI_THS_INTS_ALARM_INT_1 BIT(1)
+#define SUNXI_THS_INTS_ALARM_INT_0 BIT(0)
+
+#define SUNXI_THS_INTC_TDATA_IRQ_EN3 BIT(11)
+#define SUNXI_THS_INTC_TDATA_IRQ_EN2 BIT(10)
+#define SUNXI_THS_INTC_TDATA_IRQ_EN1 BIT(9)
+#define SUNXI_THS_INTC_TDATA_IRQ_EN0 BIT(8)
+#define SUNXI_THS_INTC_SHUT_INT_EN3 BIT(7)
+#define SUNXI_THS_INTC_SHUT_INT_EN2 BIT(6)
+#define SUNXI_THS_INTC_SHUT_INT_EN1 BIT(5)
+#define SUNXI_THS_INTC_SHUT_INT_EN0 BIT(4)
+#define SUNXI_THS_INTC_ALARM_INT_EN3 BIT(3)
+#define SUNXI_THS_INTC_ALARM_INT_EN2 BIT(2)
+#define SUNXI_THS_INTC_ALARM_INT_EN1 BIT(1)
+#define SUNXI_THS_INTC_ALARM_INT_EN0 BIT(0)
+
#define MAX_SENSOR_COUNT 4
struct sun4i_gpadc_dev {
--
2.11.0
This patch adds support for the A83T ths sensor.
The A83T does not support interrupts. This seems to be broken.
The calibration data is writen at the begin of the init process.
Signed-off-by: Philipp Rossak <[email protected]>
---
drivers/iio/adc/sun4i-gpadc-iio.c | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
index f2e0ec65c53e..b8693afcdbea 100644
--- a/drivers/iio/adc/sun4i-gpadc-iio.c
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -170,6 +170,40 @@ static const struct gpadc_data sun8i_h3_ths_data = {
SUNXI_THS_TEMP_PERIOD(0x7),
};
+static const struct gpadc_data sun8i_a83t_ths_data = {
+ .temp_offset = -2724,
+ .temp_scale = -70,
+ .temp_data = {SUNXI_THS_TDATA0,
+ SUNXI_THS_TDATA1,
+ SUNXI_THS_TDATA2,
+ 0},
+ .sample_start = sunxi_ths_sample_start,
+ .sample_end = sunxi_ths_sample_end,
+ .sensor_count = 3,
+ .supports_nvmem = false,
+ .support_irq = true,
+ .ctrl0_map = SUNXI_THS_ACQ0(0x1f3),
+ .ctrl2_map = SUNXI_THS_ACQ1(0x1f3),
+ .sensor_en_map = SUNXI_THS_TEMP_SENSE_EN0 |
+ SUNXI_THS_TEMP_SENSE_EN1 |
+ SUNXI_THS_TEMP_SENSE_EN2,
+ .filter_map = SUNXI_THS_FILTER_EN |
+ SUNXI_THS_FILTER_TYPE(0x2),
+ .irq_clear_map = SUNXI_THS_INTS_ALARM_INT_0 |
+ SUNXI_THS_INTS_ALARM_INT_1 |
+ SUNXI_THS_INTS_ALARM_INT_2 |
+ SUNXI_THS_INTS_SHUT_INT_0 |
+ SUNXI_THS_INTS_SHUT_INT_1 |
+ SUNXI_THS_INTS_SHUT_INT_2 |
+ SUNXI_THS_INTS_TDATA_IRQ_0 |
+ SUNXI_THS_INTS_TDATA_IRQ_1 |
+ SUNXI_THS_INTS_TDATA_IRQ_2,
+ .irq_control_map = SUNXI_THS_INTC_TDATA_IRQ_EN0 |
+ SUNXI_THS_INTC_TDATA_IRQ_EN1 |
+ SUNXI_THS_INTC_TDATA_IRQ_EN2 |
+ SUNXI_THS_TEMP_PERIOD(0x257),
+};
+
struct sun4i_gpadc_iio {
struct iio_dev *indio_dev;
struct completion completion;
@@ -672,6 +706,10 @@ static const struct of_device_id sun4i_gpadc_of_id[] = {
.compatible = "allwinner,sun8i-h3-ths",
.data = &sun8i_h3_ths_data,
},
+ {
+ .compatible = "allwinner,sun8i-a83t-ths",
+ .data = &sun8i_a83t_ths_data,
+ },
{ /* sentinel */ }
};
--
2.11.0
This patch adds support for the H3 ths sensor.
The H3 supports interrupts. The interrupt is configured to update the
the sensor values every second. The calibration data is writen at the
begin of the init process.
Signed-off-by: Philipp Rossak <[email protected]>
---
drivers/iio/adc/sun4i-gpadc-iio.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
index 77e07f042730..f2e0ec65c53e 100644
--- a/drivers/iio/adc/sun4i-gpadc-iio.c
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -145,6 +145,31 @@ static const struct gpadc_data sun8i_a33_gpadc_data = {
.support_irq = false,
};
+static const struct gpadc_data sun8i_h3_ths_data = {
+ .temp_offset = -1791,
+ .temp_scale = -121,
+ .temp_data = {SUNXI_THS_TDATA0, 0, 0, 0},
+ .sample_start = sunxi_ths_sample_start,
+ .sample_end = sunxi_ths_sample_end,
+ .has_bus_clk = true,
+ .has_bus_rst = true,
+ .has_mod_clk = true,
+ .sensor_count = 1,
+ .supports_nvmem = true,
+ .support_irq = true,
+ .ctrl0_map = SUNXI_THS_ACQ0(0xff),
+ .ctrl2_map = SUNXI_THS_ACQ1(0x3f),
+ .sensor_en_map = SUNXI_THS_TEMP_SENSE_EN0,
+ .filter_map = SUNXI_THS_FILTER_EN |
+ SUNXI_THS_FILTER_TYPE(0x2),
+ .irq_clear_map = SUNXI_THS_INTS_ALARM_INT_0 |
+ SUNXI_THS_INTS_SHUT_INT_0 |
+ SUNXI_THS_INTS_TDATA_IRQ_0 |
+ SUNXI_THS_INTS_ALARM_OFF_0,
+ .irq_control_map = SUNXI_THS_INTC_TDATA_IRQ_EN0 |
+ SUNXI_THS_TEMP_PERIOD(0x7),
+};
+
struct sun4i_gpadc_iio {
struct iio_dev *indio_dev;
struct completion completion;
@@ -643,6 +668,10 @@ static const struct of_device_id sun4i_gpadc_of_id[] = {
.compatible = "allwinner,sun8i-a33-ths",
.data = &sun8i_a33_gpadc_data,
},
+ {
+ .compatible = "allwinner,sun8i-h3-ths",
+ .data = &sun8i_h3_ths_data,
+ },
{ /* sentinel */ }
};
--
2.11.0
For adding newer sensor some basic rework of the code is necessary.
The SoCs after H3 has newer thermal sensor ADCs, which have two clock
inputs (bus clock and sampling clock) and a reset. The registers are
also re-arranged.
This commit reworks the code, adds the process of the clocks and
resets.
Signed-off-by: Philipp Rossak <[email protected]>
Signed-off-by: Icenowy Zheng <[email protected]>
---
drivers/iio/adc/sun4i-gpadc-iio.c | 80 +++++++++++++++++++++++++++++++++++++++
1 file changed, 80 insertions(+)
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
index 363936b37c5a..1a80744bd472 100644
--- a/drivers/iio/adc/sun4i-gpadc-iio.c
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -22,6 +22,7 @@
* shutdown for not being used.
*/
+#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -31,6 +32,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+#include <linux/reset.h>
#include <linux/thermal.h>
#include <linux/delay.h>
@@ -75,6 +77,9 @@ struct gpadc_data {
u32 ctrl2_map;
u32 sensor_en_map;
u32 filter_map;
+ bool has_bus_clk;
+ bool has_bus_rst;
+ bool has_mod_clk;
};
static const struct gpadc_data sun4i_gpadc_data = {
@@ -134,6 +139,9 @@ struct sun4i_gpadc_iio {
atomic_t ignore_temp_data_irq;
const struct gpadc_data *data;
bool no_irq;
+ struct clk *bus_clk;
+ struct clk *mod_clk;
+ struct reset_control *reset;
/* prevents concurrent reads of temperature and ADC */
struct mutex mutex;
struct thermal_zone_device *tzd;
@@ -435,6 +443,12 @@ static int sun4i_gpadc_runtime_suspend(struct device *dev)
{
struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
+ if (info->data->has_mod_clk)
+ clk_disable(info->mod_clk);
+
+ if (info->data->has_bus_clk)
+ clk_disable(info->bus_clk);
+
return info->data->sample_end(info);
}
@@ -483,6 +497,12 @@ static int sun4i_gpadc_runtime_resume(struct device *dev)
{
struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
+ if (info->data->has_mod_clk)
+ clk_enable(info->mod_clk);
+
+ if (info->data->has_bus_clk)
+ clk_enable(info->bus_clk);
+
return info->data->sample_start(info);
}
@@ -597,10 +617,61 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
return ret;
}
+ if (info->data->has_bus_rst) {
+ info->reset = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(info->reset)) {
+ ret = PTR_ERR(info->reset);
+ return ret;
+ }
+
+ ret = reset_control_deassert(info->reset);
+ if (ret)
+ return ret;
+ }
+
+ if (info->data->has_bus_clk) {
+ info->bus_clk = devm_clk_get(&pdev->dev, "bus");
+ if (IS_ERR(info->bus_clk)) {
+ ret = PTR_ERR(info->bus_clk);
+ goto assert_reset;
+ }
+
+ ret = clk_prepare_enable(info->bus_clk);
+ if (ret)
+ goto assert_reset;
+ }
+
+ if (info->data->has_mod_clk) {
+ info->mod_clk = devm_clk_get(&pdev->dev, "mod");
+ if (IS_ERR(info->mod_clk)) {
+ ret = PTR_ERR(info->mod_clk);
+ goto disable_bus_clk;
+ }
+
+ /* Running at 6MHz */
+ ret = clk_set_rate(info->mod_clk, 4000000);
+ if (ret)
+ goto disable_bus_clk;
+
+ ret = clk_prepare_enable(info->mod_clk);
+ if (ret)
+ goto disable_bus_clk;
+ }
+
if (IS_ENABLED(CONFIG_THERMAL_OF))
info->sensor_device = &pdev->dev;
return 0;
+
+disable_bus_clk:
+ if (info->data->has_bus_clk)
+ clk_disable_unprepare(info->bus_clk);
+
+assert_reset:
+ if (info->data->has_bus_rst)
+ reset_control_assert(info->reset);
+
+ return ret;
}
static int sun4i_gpadc_probe_mfd(struct platform_device *pdev,
@@ -766,6 +837,15 @@ static int sun4i_gpadc_remove(struct platform_device *pdev)
if (!info->no_irq)
iio_map_array_unregister(indio_dev);
+ if (info->data->has_mod_clk)
+ clk_disable_unprepare(info->mod_clk);
+
+ if (info->data->has_bus_clk)
+ clk_disable_unprepare(info->bus_clk);
+
+ if (info->data->has_bus_rst)
+ reset_control_assert(info->reset);
+
return 0;
}
--
2.11.0
This patch reworks the driver to support nvmem calibration cells.
The driver checks if the nvmem calibration is supported and reads out
the nvmem. At the beginning of the startup process the calibration data
is written to the related registers.
Signed-off-by: Philipp Rossak <[email protected]>
---
drivers/iio/adc/sun4i-gpadc-iio.c | 52 +++++++++++++++++++++++++++++++++++++++
include/linux/mfd/sun4i-gpadc.h | 2 ++
2 files changed, 54 insertions(+)
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
index bff06f2798e8..7b12666cdd9e 100644
--- a/drivers/iio/adc/sun4i-gpadc-iio.c
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -27,6 +27,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
@@ -81,6 +82,7 @@ struct gpadc_data {
bool has_bus_rst;
bool has_mod_clk;
int sensor_count;
+ bool supports_nvmem;
};
static const struct gpadc_data sun4i_gpadc_data = {
@@ -94,6 +96,7 @@ static const struct gpadc_data sun4i_gpadc_data = {
.sample_start = sun4i_gpadc_sample_start,
.sample_end = sun4i_gpadc_sample_end,
.sensor_count = 1,
+ .supports_nvmem = false,
};
static const struct gpadc_data sun5i_gpadc_data = {
@@ -107,6 +110,7 @@ static const struct gpadc_data sun5i_gpadc_data = {
.sample_start = sun4i_gpadc_sample_start,
.sample_end = sun4i_gpadc_sample_end,
.sensor_count = 1,
+ .supports_nvmem = false,
};
static const struct gpadc_data sun6i_gpadc_data = {
@@ -120,6 +124,7 @@ static const struct gpadc_data sun6i_gpadc_data = {
.sample_start = sun4i_gpadc_sample_start,
.sample_end = sun4i_gpadc_sample_end,
.sensor_count = 1,
+ .supports_nvmem = false,
};
static const struct gpadc_data sun8i_a33_gpadc_data = {
@@ -130,6 +135,7 @@ static const struct gpadc_data sun8i_a33_gpadc_data = {
.sample_start = sun4i_gpadc_sample_start,
.sample_end = sun4i_gpadc_sample_end,
.sensor_count = 1,
+ .supports_nvmem = false,
};
struct sun4i_gpadc_iio {
@@ -148,6 +154,8 @@ struct sun4i_gpadc_iio {
struct clk *mod_clk;
struct reset_control *reset;
int sensor_id;
+ u32 calibration_data[2];
+ bool has_calibration_data[2];
/* prevents concurrent reads of temperature and ADC */
struct mutex mutex;
struct thermal_zone_device *tzd;
@@ -459,6 +467,17 @@ static int sun4i_gpadc_runtime_suspend(struct device *dev)
return info->data->sample_end(info);
}
+static void sunxi_calibrate(struct sun4i_gpadc_iio *info)
+{
+ if (info->has_calibration_data[0])
+ regmap_write(info->regmap, SUNXI_THS_CDATA_0_1,
+ info->calibration_data[0]);
+
+ if (info->has_calibration_data[1])
+ regmap_write(info->regmap, SUNXI_THS_CDATA_2_3,
+ info->calibration_data[1]);
+}
+
static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
{
/* clkin = 6MHz */
@@ -481,6 +500,7 @@ static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info)
{
u32 value;
+ sunxi_calibrate(info);
if (info->data->ctrl0_map)
regmap_write(info->regmap, SUNXI_THS_CTRL0,
@@ -602,6 +622,9 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
struct resource *mem;
void __iomem *base;
int ret;
+ struct nvmem_cell *cell;
+ ssize_t cell_size;
+ u64 *cell_data;
info->data = of_device_get_match_data(&pdev->dev);
if (!info->data)
@@ -616,6 +639,35 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
if (IS_ERR(base))
return PTR_ERR(base);
+ info->has_calibration_data[0] = false;
+ info->has_calibration_data[1] = false;
+
+ if (!info->data->supports_nvmem)
+ goto no_nvmem;
+
+ cell = devm_nvmem_cell_get(&pdev->dev, "calibration");
+ if (IS_ERR(cell)) {
+ if (PTR_ERR(cell) == -EPROBE_DEFER)
+ return PTR_ERR(cell);
+ } else {
+ cell_data = (u64 *)nvmem_cell_read(cell, &cell_size);
+ devm_nvmem_cell_put(&pdev->dev, cell);
+ if (cell_size <= 4) {
+ info->has_calibration_data[0] = true;
+ info->calibration_data[0] = be32_to_cpu(cell_data[0] &
+ GENMASK(31, 0));
+ } else if (cell_size <= 8) {
+ info->has_calibration_data[0] = true;
+ info->calibration_data[0] = be32_to_cpu(cell_data[0] &
+ GENMASK(31, 0));
+ info->has_calibration_data[1] = true;
+ info->calibration_data[1] = be32_to_cpu(
+ (cell_data[0] >> 32) & GENMASK(31, 0));
+ }
+ }
+
+no_nvmem:
+
info->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&sun4i_gpadc_regmap_config);
if (IS_ERR(info->regmap)) {
diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
index 40b4dd9d2405..c251002431bd 100644
--- a/include/linux/mfd/sun4i-gpadc.h
+++ b/include/linux/mfd/sun4i-gpadc.h
@@ -90,6 +90,8 @@
#define SUNXI_THS_CTRL0 0x00
#define SUNXI_THS_CTRL2 0x40
#define SUNXI_THS_FILTER 0x70
+#define SUNXI_THS_CDATA_0_1 0x74
+#define SUNXI_THS_CDATA_2_3 0x78
#define SUNXI_THS_TDATA0 0x80
#define SUNXI_THS_TDATA1 0x84
#define SUNXI_THS_TDATA2 0x88
--
2.11.0
Allwinner H3 features a thermal sensor like the one in A33, but has its
register re-arranged, the clock divider moved to CCU (originally the
clock divider is in ADC) and added a pair of bus clock and reset.
Allwinner A83T features a thermal sensor similar to the H3, the ths clock,
the bus clock and the reset was removed from the CCU. The THS in A83T
has a clock that is directly connected and runs with 24 MHz.
Update the binding document to cover H3 and A83T.
Signed-off-by: Philipp Rossak <[email protected]>
---
.../devicetree/bindings/mfd/sun4i-gpadc.txt | 50 ++++++++++++++++++++--
1 file changed, 47 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/mfd/sun4i-gpadc.txt b/Documentation/devicetree/bindings/mfd/sun4i-gpadc.txt
index 86dd8191b04c..f6b939617a6d 100644
--- a/Documentation/devicetree/bindings/mfd/sun4i-gpadc.txt
+++ b/Documentation/devicetree/bindings/mfd/sun4i-gpadc.txt
@@ -4,12 +4,35 @@ The Allwinner SoCs all have an ADC that can also act as a thermal sensor
and sometimes as a touchscreen controller.
Required properties:
- - compatible: "allwinner,sun8i-a33-ths",
+ - compatible: must contain one of the following compatibles:
+ - "allwinner,sun8i-a33-ths"
+ - "allwinner,sun8i-h3-ths"
+ - "allwinner,sun8i-a83t-ths"
- reg: mmio address range of the chip,
- - #thermal-sensor-cells: shall be 0,
+ - #thermal-sensor-cells: shall be 0 or 1,
- #io-channel-cells: shall be 0,
-Example:
+Required properties for the following compatibles:
+ - "allwinner,sun8i-h3-ths"
+ - "allwinner,sun8i-a83t-ths"
+ - interrupts: the sampling interrupt of the ADC,
+
+Required properties for the following compatibles:
+ - "allwinner,sun8i-h3-ths"
+ - clocks: the bus clock and the input clock of the ADC,
+ - clock-names: should be "bus" and "mod",
+ - resets: the bus reset of the ADC,
+
+Optional properties for the following compatibles:
+ - "allwinner,sun8i-h3-ths"
+ - nvmem-cells: A phandle to the calibration data provided by a nvmem device.
+ If unspecified default values shall be used. The size should
+ be 0x4 or 0x8, depending on the amount of CDATA registers.
+ - nvmem-cell-names: Should be "calibration".
+
+Details see: bindings/nvmem/nvmem.txt
+
+Example for A33:
ths: ths@1c25000 {
compatible = "allwinner,sun8i-a33-ths";
reg = <0x01c25000 0x100>;
@@ -17,6 +40,27 @@ Example:
#io-channel-cells = <0>;
};
+Example for H3:
+ ths: thermal-sensor@1c25000 {
+ compatible = "allwinner,sun8i-h3-ths";
+ reg = <0x01c25000 0x400>;
+ clocks = <&ccu CLK_BUS_THS>, <&ccu CLK_THS>;
+ clock-names = "bus", "mod";
+ resets = <&ccu RST_BUS_THS>;
+ interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+ #thermal-sensor-cells = <0>;
+ #io-channel-cells = <0>;
+ };
+
+Example for A83T:
+ ths: thermal-sensor@1f04000 {
+ compatible = "allwinner,sun8i-a83t-ths";
+ reg = <0x01f04000 0x100>;
+ interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
+ #thermal-sensor-cells = <1>;
+ #io-channel-cells = <0>;
+ };
+
sun4i, sun5i and sun6i SoCs are also supported via the older binding:
sun4i resistive touchscreen controller
--
2.11.0
For adding newer sensor some basic rework of the code is necessary.
This patch reworks the driver to be able to handle more than one
thermal sensor. Newer SoC like the A80 have 4 thermal sensors.
Because of this the maximal sensor count value was set to 4.
The sensor_id value is set during sensor registration and is for each
registered sensor indiviual. This makes it able to differntiate the
sensors when the value is read from the register.
Signed-off-by: Philipp Rossak <[email protected]>
---
drivers/iio/adc/sun4i-gpadc-iio.c | 36 +++++++++++++++++++++++-------------
include/linux/mfd/sun4i-gpadc.h | 6 ++++++
2 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
index 1a80744bd472..bff06f2798e8 100644
--- a/drivers/iio/adc/sun4i-gpadc-iio.c
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -70,7 +70,7 @@ struct gpadc_data {
unsigned int tp_adc_select;
unsigned int (*adc_chan_select)(unsigned int chan);
unsigned int adc_chan_mask;
- unsigned int temp_data;
+ unsigned int temp_data[MAX_SENSOR_COUNT];
int (*sample_start)(struct sun4i_gpadc_iio *info);
int (*sample_end)(struct sun4i_gpadc_iio *info);
u32 ctrl0_map;
@@ -80,6 +80,7 @@ struct gpadc_data {
bool has_bus_clk;
bool has_bus_rst;
bool has_mod_clk;
+ int sensor_count;
};
static const struct gpadc_data sun4i_gpadc_data = {
@@ -89,9 +90,10 @@ static const struct gpadc_data sun4i_gpadc_data = {
.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
.adc_chan_select = &sun4i_gpadc_chan_select,
.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
- .temp_data = SUN4I_GPADC_TEMP_DATA,
+ .temp_data = {SUN4I_GPADC_TEMP_DATA, 0, 0, 0},
.sample_start = sun4i_gpadc_sample_start,
.sample_end = sun4i_gpadc_sample_end,
+ .sensor_count = 1,
};
static const struct gpadc_data sun5i_gpadc_data = {
@@ -101,9 +103,10 @@ static const struct gpadc_data sun5i_gpadc_data = {
.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
.adc_chan_select = &sun4i_gpadc_chan_select,
.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
- .temp_data = SUN4I_GPADC_TEMP_DATA,
+ .temp_data = {SUN4I_GPADC_TEMP_DATA, 0, 0, 0},
.sample_start = sun4i_gpadc_sample_start,
.sample_end = sun4i_gpadc_sample_end,
+ .sensor_count = 1,
};
static const struct gpadc_data sun6i_gpadc_data = {
@@ -113,18 +116,20 @@ static const struct gpadc_data sun6i_gpadc_data = {
.tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
.adc_chan_select = &sun6i_gpadc_chan_select,
.adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
- .temp_data = SUN4I_GPADC_TEMP_DATA,
+ .temp_data = {SUN4I_GPADC_TEMP_DATA, 0, 0, 0},
.sample_start = sun4i_gpadc_sample_start,
.sample_end = sun4i_gpadc_sample_end,
+ .sensor_count = 1,
};
static const struct gpadc_data sun8i_a33_gpadc_data = {
.temp_offset = -1662,
.temp_scale = 162,
.tp_mode_en = SUN8I_A33_GPADC_CTRL1_CHOP_TEMP_EN,
- .temp_data = SUN4I_GPADC_TEMP_DATA,
+ .temp_data = {SUN4I_GPADC_TEMP_DATA, 0, 0, 0},
.sample_start = sun4i_gpadc_sample_start,
.sample_end = sun4i_gpadc_sample_end,
+ .sensor_count = 1,
};
struct sun4i_gpadc_iio {
@@ -142,6 +147,7 @@ struct sun4i_gpadc_iio {
struct clk *bus_clk;
struct clk *mod_clk;
struct reset_control *reset;
+ int sensor_id;
/* prevents concurrent reads of temperature and ADC */
struct mutex mutex;
struct thermal_zone_device *tzd;
@@ -309,14 +315,15 @@ static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel,
return sun4i_gpadc_read(indio_dev, channel, val, info->fifo_data_irq);
}
-static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
+static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val,
+ int sensor)
{
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
if (info->no_irq) {
pm_runtime_get_sync(indio_dev->dev.parent);
- regmap_read(info->regmap, info->data->temp_data, val);
+ regmap_read(info->regmap, info->data->temp_data[sensor], val);
pm_runtime_mark_last_busy(indio_dev->dev.parent);
pm_runtime_put_autosuspend(indio_dev->dev.parent);
@@ -363,7 +370,7 @@ static int sun4i_gpadc_read_raw(struct iio_dev *indio_dev,
ret = sun4i_gpadc_adc_read(indio_dev, chan->channel,
val);
else
- ret = sun4i_gpadc_temp_read(indio_dev, val);
+ ret = sun4i_gpadc_temp_read(indio_dev, val, 0);
if (ret)
return ret;
@@ -511,7 +518,7 @@ static int sun4i_gpadc_get_temp(void *data, int *temp)
struct sun4i_gpadc_iio *info = data;
int val, scale, offset;
- if (sun4i_gpadc_temp_read(info->indio_dev, &val))
+ if (sun4i_gpadc_temp_read(info->indio_dev, &val, info->sensor_id))
return -ETIMEDOUT;
sun4i_gpadc_temp_scale(info->indio_dev, &scale);
@@ -755,7 +762,7 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
{
struct sun4i_gpadc_iio *info;
struct iio_dev *indio_dev;
- int ret;
+ int ret, i;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
if (!indio_dev)
@@ -788,9 +795,12 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
- info->tzd = thermal_zone_of_sensor_register(info->sensor_device,
- 0, info,
- &sun4i_ts_tz_ops);
+ for (i = 0; i < info->data->sensor_count; i++) {
+ info->sensor_id = i;
+ info->tzd = thermal_zone_of_sensor_register(
+ info->sensor_device,
+ i, info, &sun4i_ts_tz_ops);
+ }
/*
* Do not fail driver probing when failing to register in
* thermal because no thermal DT node is found.
diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
index 39e096c3ddac..40b4dd9d2405 100644
--- a/include/linux/mfd/sun4i-gpadc.h
+++ b/include/linux/mfd/sun4i-gpadc.h
@@ -90,6 +90,10 @@
#define SUNXI_THS_CTRL0 0x00
#define SUNXI_THS_CTRL2 0x40
#define SUNXI_THS_FILTER 0x70
+#define SUNXI_THS_TDATA0 0x80
+#define SUNXI_THS_TDATA1 0x84
+#define SUNXI_THS_TDATA2 0x88
+#define SUNXI_THS_TDATA3 0x8c
#define SUNXI_THS_FILTER_EN BIT(2)
#define SUNXI_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
@@ -101,6 +105,8 @@
#define SUNXI_THS_TEMP_SENSE_EN2 BIT(2)
#define SUNXI_THS_TEMP_SENSE_EN3 BIT(3)
+#define MAX_SENSOR_COUNT 4
+
struct sun4i_gpadc_dev {
struct device *dev;
struct regmap *regmap;
--
2.11.0
For adding newer sensor some basic rework of the code is necessary.
This commit reworks the code and allows the sampling start/end code and
the position of value readout register to be altered. Later the start/end
functions will be used to configure the ths and start/stop the
sampling.
Signed-off-by: Icenowy Zheng <[email protected]>
Signed-off-by: Philipp Rossak <[email protected]>
---
drivers/iio/adc/sun4i-gpadc-iio.c | 87 +++++++++++++++++++++++++++++++++++----
include/linux/mfd/sun4i-gpadc.h | 19 +++++++--
2 files changed, 94 insertions(+), 12 deletions(-)
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
index 03804ff9c006..363936b37c5a 100644
--- a/drivers/iio/adc/sun4i-gpadc-iio.c
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -49,6 +49,18 @@ static unsigned int sun6i_gpadc_chan_select(unsigned int chan)
return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
}
+struct sun4i_gpadc_iio;
+
+/*
+ * Prototypes for these functions, which enable these functions to be
+ * referenced in gpadc_data structures.
+ */
+static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info);
+static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info);
+
+static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info);
+static int sunxi_ths_sample_end(struct sun4i_gpadc_iio *info);
+
struct gpadc_data {
int temp_offset;
int temp_scale;
@@ -56,6 +68,13 @@ struct gpadc_data {
unsigned int tp_adc_select;
unsigned int (*adc_chan_select)(unsigned int chan);
unsigned int adc_chan_mask;
+ unsigned int temp_data;
+ int (*sample_start)(struct sun4i_gpadc_iio *info);
+ int (*sample_end)(struct sun4i_gpadc_iio *info);
+ u32 ctrl0_map;
+ u32 ctrl2_map;
+ u32 sensor_en_map;
+ u32 filter_map;
};
static const struct gpadc_data sun4i_gpadc_data = {
@@ -65,6 +84,9 @@ static const struct gpadc_data sun4i_gpadc_data = {
.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
.adc_chan_select = &sun4i_gpadc_chan_select,
.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
+ .temp_data = SUN4I_GPADC_TEMP_DATA,
+ .sample_start = sun4i_gpadc_sample_start,
+ .sample_end = sun4i_gpadc_sample_end,
};
static const struct gpadc_data sun5i_gpadc_data = {
@@ -74,6 +96,9 @@ static const struct gpadc_data sun5i_gpadc_data = {
.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
.adc_chan_select = &sun4i_gpadc_chan_select,
.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
+ .temp_data = SUN4I_GPADC_TEMP_DATA,
+ .sample_start = sun4i_gpadc_sample_start,
+ .sample_end = sun4i_gpadc_sample_end,
};
static const struct gpadc_data sun6i_gpadc_data = {
@@ -83,12 +108,18 @@ static const struct gpadc_data sun6i_gpadc_data = {
.tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
.adc_chan_select = &sun6i_gpadc_chan_select,
.adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
+ .temp_data = SUN4I_GPADC_TEMP_DATA,
+ .sample_start = sun4i_gpadc_sample_start,
+ .sample_end = sun4i_gpadc_sample_end,
};
static const struct gpadc_data sun8i_a33_gpadc_data = {
.temp_offset = -1662,
.temp_scale = 162,
.tp_mode_en = SUN8I_A33_GPADC_CTRL1_CHOP_TEMP_EN,
+ .temp_data = SUN4I_GPADC_TEMP_DATA,
+ .sample_start = sun4i_gpadc_sample_start,
+ .sample_end = sun4i_gpadc_sample_end,
};
struct sun4i_gpadc_iio {
@@ -277,7 +308,7 @@ static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
if (info->no_irq) {
pm_runtime_get_sync(indio_dev->dev.parent);
- regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
+ regmap_read(info->regmap, info->data->temp_data, val);
pm_runtime_mark_last_busy(indio_dev->dev.parent);
pm_runtime_put_autosuspend(indio_dev->dev.parent);
@@ -382,10 +413,8 @@ static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int sun4i_gpadc_runtime_suspend(struct device *dev)
+static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info)
{
- struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
-
/* Disable the ADC on IP */
regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0);
/* Disable temperature sensor on IP */
@@ -394,19 +423,32 @@ static int sun4i_gpadc_runtime_suspend(struct device *dev)
return 0;
}
-static int sun4i_gpadc_runtime_resume(struct device *dev)
+static int sunxi_ths_sample_end(struct sun4i_gpadc_iio *info)
+{
+ /* Disable temperature sensor */
+ regmap_write(info->regmap, SUNXI_THS_CTRL2, 0x0);
+
+ return 0;
+}
+
+static int sun4i_gpadc_runtime_suspend(struct device *dev)
{
struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
+ return info->data->sample_end(info);
+}
+
+static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
+{
/* clkin = 6MHz */
regmap_write(info->regmap, SUN4I_GPADC_CTRL0,
SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) |
SUN4I_GPADC_CTRL0_FS_DIV(7) |
- SUN4I_GPADC_CTRL0_T_ACQ(63));
+ SUNXI_THS_ACQ0(63));
regmap_write(info->regmap, SUN4I_GPADC_CTRL1, info->data->tp_mode_en);
regmap_write(info->regmap, SUN4I_GPADC_CTRL3,
- SUN4I_GPADC_CTRL3_FILTER_EN |
- SUN4I_GPADC_CTRL3_FILTER_TYPE(1));
+ SUNXI_THS_FILTER_EN |
+ SUNXI_THS_FILTER_TYPE(1));
/* period = SUN4I_GPADC_TPR_TEMP_PERIOD * 256 * 16 / clkin; ~0.6s */
regmap_write(info->regmap, SUN4I_GPADC_TPR,
SUN4I_GPADC_TPR_TEMP_ENABLE |
@@ -415,6 +457,35 @@ static int sun4i_gpadc_runtime_resume(struct device *dev)
return 0;
}
+static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info)
+{
+ u32 value;
+
+ if (info->data->ctrl0_map)
+ regmap_write(info->regmap, SUNXI_THS_CTRL0,
+ info->data->ctrl0_map);
+
+ regmap_write(info->regmap, SUNXI_THS_CTRL2,
+ info->data->ctrl2_map);
+
+ regmap_write(info->regmap, SUNXI_THS_FILTER,
+ info->data->filter_map);
+
+ regmap_read(info->regmap, SUNXI_THS_CTRL2, &value);
+
+ regmap_write(info->regmap, SUNXI_THS_CTRL2,
+ info->data->sensor_en_map | value);
+
+ return 0;
+}
+
+static int sun4i_gpadc_runtime_resume(struct device *dev)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
+
+ return info->data->sample_start(info);
+}
+
static int sun4i_gpadc_get_temp(void *data, int *temp)
{
struct sun4i_gpadc_iio *info = data;
diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
index 78d31984a222..39e096c3ddac 100644
--- a/include/linux/mfd/sun4i-gpadc.h
+++ b/include/linux/mfd/sun4i-gpadc.h
@@ -17,7 +17,6 @@
#define SUN4I_GPADC_CTRL0_ADC_CLK_SELECT BIT(22)
#define SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(x) ((GENMASK(1, 0) & (x)) << 20)
#define SUN4I_GPADC_CTRL0_FS_DIV(x) ((GENMASK(3, 0) & (x)) << 16)
-#define SUN4I_GPADC_CTRL0_T_ACQ(x) (GENMASK(15, 0) & (x))
#define SUN4I_GPADC_CTRL1 0x04
@@ -51,9 +50,6 @@
#define SUN4I_GPADC_CTRL3 0x0c
-#define SUN4I_GPADC_CTRL3_FILTER_EN BIT(2)
-#define SUN4I_GPADC_CTRL3_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
-
#define SUN4I_GPADC_TPR 0x18
#define SUN4I_GPADC_TPR_TEMP_ENABLE BIT(16)
@@ -90,6 +86,21 @@
/* 10s delay before suspending the IP */
#define SUN4I_GPADC_AUTOSUSPEND_DELAY 10000
+/* SUNXI_THS COMMON REGISTERS + DEFINES */
+#define SUNXI_THS_CTRL0 0x00
+#define SUNXI_THS_CTRL2 0x40
+#define SUNXI_THS_FILTER 0x70
+
+#define SUNXI_THS_FILTER_EN BIT(2)
+#define SUNXI_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
+#define SUNXI_THS_ACQ0(x) (GENMASK(15, 0) & (x))
+#define SUNXI_THS_ACQ1(x) (GENMASK(31, 16) & ((x) << 16))
+
+#define SUNXI_THS_TEMP_SENSE_EN0 BIT(0)
+#define SUNXI_THS_TEMP_SENSE_EN1 BIT(1)
+#define SUNXI_THS_TEMP_SENSE_EN2 BIT(2)
+#define SUNXI_THS_TEMP_SENSE_EN3 BIT(3)
+
struct sun4i_gpadc_dev {
struct device *dev;
struct regmap *regmap;
--
2.11.0
This commit enables the SUN4I_GPADC config option and
sets the value to yes. This is needed to enable the ths sensors.
Signed-off-by: Philipp Rossak <[email protected]>
---
arch/arm/configs/sunxi_defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
index 5caaf971fb50..52c3990b9d91 100644
--- a/arch/arm/configs/sunxi_defconfig
+++ b/arch/arm/configs/sunxi_defconfig
@@ -131,6 +131,7 @@ CONFIG_DMA_SUN6I=y
CONFIG_EXTCON=y
CONFIG_IIO=y
CONFIG_AXP20X_ADC=y
+CONFIG_SUN4I_GPADC=y
CONFIG_PWM=y
CONFIG_PWM_SUN4I=y
CONFIG_PHY_SUN4I_USB=y
--
2.11.0
From: Icenowy Zheng <[email protected]>
As the H3 SoC, which is also in sun8i line, has totally different
register map for the thermal sensor (a cut down version of GPADC), we
should rename A23/A33-specified registers to contain A33, in order to
prevent obfuscation with H3 registers. Currently these registers are
only prefixed "SUN8I", not "SUN8I_A33".
Add "_A33" after "SUN8I" on the register names.
Signed-off-by: Icenowy Zheng <[email protected]>
Reviewed-by: Chen-Yu Tsai <[email protected]>
Acked-by: Maxime Ripard <[email protected]>
Acked-by: Lee Jones <[email protected]>
---
drivers/iio/adc/sun4i-gpadc-iio.c | 2 +-
include/linux/mfd/sun4i-gpadc.h | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
index 04d7147e0110..03804ff9c006 100644
--- a/drivers/iio/adc/sun4i-gpadc-iio.c
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -88,7 +88,7 @@ static const struct gpadc_data sun6i_gpadc_data = {
static const struct gpadc_data sun8i_a33_gpadc_data = {
.temp_offset = -1662,
.temp_scale = 162,
- .tp_mode_en = SUN8I_GPADC_CTRL1_CHOP_TEMP_EN,
+ .tp_mode_en = SUN8I_A33_GPADC_CTRL1_CHOP_TEMP_EN,
};
struct sun4i_gpadc_iio {
diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
index 139872c2e0fe..78d31984a222 100644
--- a/include/linux/mfd/sun4i-gpadc.h
+++ b/include/linux/mfd/sun4i-gpadc.h
@@ -38,9 +38,9 @@
#define SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(x) (GENMASK(3, 0) & BIT(x))
#define SUN6I_GPADC_CTRL1_ADC_CHAN_MASK GENMASK(3, 0)
-/* TP_CTRL1 bits for sun8i SoCs */
-#define SUN8I_GPADC_CTRL1_CHOP_TEMP_EN BIT(8)
-#define SUN8I_GPADC_CTRL1_GPADC_CALI_EN BIT(7)
+/* TP_CTRL1 bits for A33 */
+#define SUN8I_A33_GPADC_CTRL1_CHOP_TEMP_EN BIT(8)
+#define SUN8I_A33_GPADC_CTRL1_GPADC_CALI_EN BIT(7)
#define SUN4I_GPADC_CTRL2 0x08
--
2.11.0
On 01/26/18 09:19, Philipp Rossak wrote:
> This patch adds the thermal zones to the A83T. Sensor 0 is located in the
> cpu cluster 0. Sensor 1 is located in cluster 1 and Sensor 3 is located
> in the gpu.
You mention sensor 3 here, but have sensor 2 in the device tree.
> Signed-off-by: Philipp Rossak <[email protected]>
> ---
> arch/arm/boot/dts/sun8i-a83t.dtsi | 20 ++++++++++++++++++++
> 1 file changed, 20 insertions(+)
>
> diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi
> index 9e53ff5ac4ed..4259a8726031 100644
> --- a/arch/arm/boot/dts/sun8i-a83t.dtsi
> +++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
> @@ -747,4 +747,24 @@
> #size-cells = <0>;
> };
> };
> +
> + thermal-zones {
> + cpu0_thermal: cpu0-thermal {
> + polling-delay-passive = <1000>;
> + polling-delay = <5000>;
> + thermal-sensors = <&ths 0>;
> + };
> +
> + cpu1_thermal: cpu1-thermal {
> + polling-delay-passive = <1000>;
> + polling-delay = <5000>;
> + thermal-sensors = <&ths 1>;
> + };
> +
> + gpu_thermal: gpu-thermal {
> + polling-delay-passive = <1000>;
> + polling-delay = <5000>;
> + thermal-sensors = <&ths 2>;
^^^^ here
> + };
> + };
> };
>
Thanks,
Samuel
On 01/26/18 09:19, Philipp Rossak wrote:
> This patch adds the thermal zones to the H3. We have only one sensor and
> that is placed in the cpu.
>
> Signed-off-by: Philipp Rossak <[email protected]>
> ---
> arch/arm/boot/dts/sun8i-h3.dtsi | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
> index fbb007e5798e..3f83f6a27c74 100644
> --- a/arch/arm/boot/dts/sun8i-h3.dtsi
> +++ b/arch/arm/boot/dts/sun8i-h3.dtsi
> @@ -72,6 +72,15 @@
> };
> };
>
> + thermal-zones {
> + cpu-thermal {
> + /* milliseconds */
> + polling-delay-passive = <250>;
> + polling-delay = <1000>;
> + thermal-sensors = <&ths 0>;
If #thermal-sensor-cells = <0>, shouldn't this just be <&ths> ?
> + };
> + };
> +
> timer {
> compatible = "arm,armv7-timer";
> interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
>
Thanks,
Samuel
On 26.01.2018 17:25, Samuel Holland wrote:
> On 01/26/18 09:19, Philipp Rossak wrote:
>> This patch adds the thermal zones to the A83T. Sensor 0 is located in the
>> cpu cluster 0. Sensor 1 is located in cluster 1 and Sensor 3 is located
>> in the gpu.
>
> You mention sensor 3 here, but have sensor 2 in the device tree.
That is a typo/wrong counting error. This should be sensor 2.
I will fix that in the next version of this patch series.
>> Signed-off-by: Philipp Rossak <[email protected]>
>> ---
>> arch/arm/boot/dts/sun8i-a83t.dtsi | 20 ++++++++++++++++++++
>> 1 file changed, 20 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi
>> index 9e53ff5ac4ed..4259a8726031 100644
>> --- a/arch/arm/boot/dts/sun8i-a83t.dtsi
>> +++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
>> @@ -747,4 +747,24 @@
>> #size-cells = <0>;
>> };
>> };
>> +
>> + thermal-zones {
>> + cpu0_thermal: cpu0-thermal {
>> + polling-delay-passive = <1000>;
>> + polling-delay = <5000>;
>> + thermal-sensors = <&ths 0>;
>> + };
>> +
>> + cpu1_thermal: cpu1-thermal {
>> + polling-delay-passive = <1000>;
>> + polling-delay = <5000>;
>> + thermal-sensors = <&ths 1>;
>> + };
>> +
>> + gpu_thermal: gpu-thermal {
>> + polling-delay-passive = <1000>;
>> + polling-delay = <5000>;
>> + thermal-sensors = <&ths 2>;
>
> ^^^^ here
>
>> + };
>> + };
>> };
>>
>
> Thanks,
> Samuel
>
Thanks,
Philipp
On 26.01.2018 17:26, Samuel Holland wrote:
> On 01/26/18 09:19, Philipp Rossak wrote:
>> This patch adds the thermal zones to the H3. We have only one sensor and
>> that is placed in the cpu.
>>
>> Signed-off-by: Philipp Rossak <[email protected]>
>> ---
>> arch/arm/boot/dts/sun8i-h3.dtsi | 9 +++++++++
>> 1 file changed, 9 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
>> index fbb007e5798e..3f83f6a27c74 100644
>> --- a/arch/arm/boot/dts/sun8i-h3.dtsi
>> +++ b/arch/arm/boot/dts/sun8i-h3.dtsi
>> @@ -72,6 +72,15 @@
>> };
>> };
>>
>> + thermal-zones {
>> + cpu-thermal {
>> + /* milliseconds */
>> + polling-delay-passive = <250>;
>> + polling-delay = <1000>;
>> + thermal-sensors = <&ths 0>;
>
> If #thermal-sensor-cells = <0>, shouldn't this just be <&ths> ?
I think both works, but I my intention was to have an accurate
description in DT.
I thought that that is important in DT.
>> + };
>> + };
>> +
>> timer {
>> compatible = "arm,armv7-timer";
>> interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
>>
>
> Thanks,
> Samuel
>
Regards,
Philipp
Hi,
On Fri, Jan 26, 2018 at 04:19:35PM +0100, Philipp Rossak wrote:
> This patch adds support for the A83T ths sensor.
>
> The A83T does not support interrupts. This seems to be broken.
Though, you use support_irq = true below. And in my tests, IRQ for THS works on
A83T.
regards,
o.
> The calibration data is writen at the begin of the init process.
>
> Signed-off-by: Philipp Rossak <[email protected]>
> ---
> drivers/iio/adc/sun4i-gpadc-iio.c | 38 ++++++++++++++++++++++++++++++++++++++
> 1 file changed, 38 insertions(+)
>
> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
> index f2e0ec65c53e..b8693afcdbea 100644
> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
> @@ -170,6 +170,40 @@ static const struct gpadc_data sun8i_h3_ths_data = {
> SUNXI_THS_TEMP_PERIOD(0x7),
> };
>
> +static const struct gpadc_data sun8i_a83t_ths_data = {
> + .temp_offset = -2724,
> + .temp_scale = -70,
> + .temp_data = {SUNXI_THS_TDATA0,
> + SUNXI_THS_TDATA1,
> + SUNXI_THS_TDATA2,
> + 0},
> + .sample_start = sunxi_ths_sample_start,
> + .sample_end = sunxi_ths_sample_end,
> + .sensor_count = 3,
> + .supports_nvmem = false,
> + .support_irq = true,
> + .ctrl0_map = SUNXI_THS_ACQ0(0x1f3),
> + .ctrl2_map = SUNXI_THS_ACQ1(0x1f3),
> + .sensor_en_map = SUNXI_THS_TEMP_SENSE_EN0 |
> + SUNXI_THS_TEMP_SENSE_EN1 |
> + SUNXI_THS_TEMP_SENSE_EN2,
> + .filter_map = SUNXI_THS_FILTER_EN |
> + SUNXI_THS_FILTER_TYPE(0x2),
> + .irq_clear_map = SUNXI_THS_INTS_ALARM_INT_0 |
> + SUNXI_THS_INTS_ALARM_INT_1 |
> + SUNXI_THS_INTS_ALARM_INT_2 |
> + SUNXI_THS_INTS_SHUT_INT_0 |
> + SUNXI_THS_INTS_SHUT_INT_1 |
> + SUNXI_THS_INTS_SHUT_INT_2 |
> + SUNXI_THS_INTS_TDATA_IRQ_0 |
> + SUNXI_THS_INTS_TDATA_IRQ_1 |
> + SUNXI_THS_INTS_TDATA_IRQ_2,
> + .irq_control_map = SUNXI_THS_INTC_TDATA_IRQ_EN0 |
> + SUNXI_THS_INTC_TDATA_IRQ_EN1 |
> + SUNXI_THS_INTC_TDATA_IRQ_EN2 |
> + SUNXI_THS_TEMP_PERIOD(0x257),
> +};
> +
> struct sun4i_gpadc_iio {
> struct iio_dev *indio_dev;
> struct completion completion;
> @@ -672,6 +706,10 @@ static const struct of_device_id sun4i_gpadc_of_id[] = {
> .compatible = "allwinner,sun8i-h3-ths",
> .data = &sun8i_h3_ths_data,
> },
> + {
> + .compatible = "allwinner,sun8i-a83t-ths",
> + .data = &sun8i_a83t_ths_data,
> + },
> { /* sentinel */ }
> };
>
> --
> 2.11.0
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
> For more options, visit https://groups.google.com/d/optout.
On 26.01.2018 18:46, Ondřej Jirman wrote:
> Hi,
>
> On Fri, Jan 26, 2018 at 04:19:35PM +0100, Philipp Rossak wrote:
>> This patch adds support for the A83T ths sensor.
>>
>> The A83T does not support interrupts. This seems to be broken.
>
> Though, you use support_irq = true below. And in my tests, IRQ for THS works on
> A83T.
>
> regards,
> o.
Oh I totally forgot to update this commit message, after I fixed the
devicetree and got it running.
I will fix that in the next version of this patch series.
>> The calibration data is writen at the begin of the init process.
>>
>> Signed-off-by: Philipp Rossak <[email protected]>
>> ---
>> drivers/iio/adc/sun4i-gpadc-iio.c | 38 ++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 38 insertions(+)
>>
>> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
>> index f2e0ec65c53e..b8693afcdbea 100644
>> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
>> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
>> @@ -170,6 +170,40 @@ static const struct gpadc_data sun8i_h3_ths_data = {
>> SUNXI_THS_TEMP_PERIOD(0x7),
>> };
>>
>> +static const struct gpadc_data sun8i_a83t_ths_data = {
>> + .temp_offset = -2724,
>> + .temp_scale = -70,
>> + .temp_data = {SUNXI_THS_TDATA0,
>> + SUNXI_THS_TDATA1,
>> + SUNXI_THS_TDATA2,
>> + 0},
>> + .sample_start = sunxi_ths_sample_start,
>> + .sample_end = sunxi_ths_sample_end,
>> + .sensor_count = 3,
>> + .supports_nvmem = false,
>> + .support_irq = true,
>> + .ctrl0_map = SUNXI_THS_ACQ0(0x1f3),
>> + .ctrl2_map = SUNXI_THS_ACQ1(0x1f3),
>> + .sensor_en_map = SUNXI_THS_TEMP_SENSE_EN0 |
>> + SUNXI_THS_TEMP_SENSE_EN1 |
>> + SUNXI_THS_TEMP_SENSE_EN2,
>> + .filter_map = SUNXI_THS_FILTER_EN |
>> + SUNXI_THS_FILTER_TYPE(0x2),
>> + .irq_clear_map = SUNXI_THS_INTS_ALARM_INT_0 |
>> + SUNXI_THS_INTS_ALARM_INT_1 |
>> + SUNXI_THS_INTS_ALARM_INT_2 |
>> + SUNXI_THS_INTS_SHUT_INT_0 |
>> + SUNXI_THS_INTS_SHUT_INT_1 |
>> + SUNXI_THS_INTS_SHUT_INT_2 |
>> + SUNXI_THS_INTS_TDATA_IRQ_0 |
>> + SUNXI_THS_INTS_TDATA_IRQ_1 |
>> + SUNXI_THS_INTS_TDATA_IRQ_2,
>> + .irq_control_map = SUNXI_THS_INTC_TDATA_IRQ_EN0 |
>> + SUNXI_THS_INTC_TDATA_IRQ_EN1 |
>> + SUNXI_THS_INTC_TDATA_IRQ_EN2 |
>> + SUNXI_THS_TEMP_PERIOD(0x257),
>> +};
>> +
>> struct sun4i_gpadc_iio {
>> struct iio_dev *indio_dev;
>> struct completion completion;
>> @@ -672,6 +706,10 @@ static const struct of_device_id sun4i_gpadc_of_id[] = {
>> .compatible = "allwinner,sun8i-h3-ths",
>> .data = &sun8i_h3_ths_data,
>> },
>> + {
>> + .compatible = "allwinner,sun8i-a83t-ths",
>> + .data = &sun8i_a83t_ths_data,
>> + },
>> { /* sentinel */ }
>> };
>>
>> --
>> 2.11.0
>>
>> --
>> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
>> To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
>> For more options, visit https://groups.google.com/d/optout.
Thanks,
Philipp
On Fri, 26 Jan 2018 16:19:28 +0100
Philipp Rossak <[email protected]> wrote:
> From: Icenowy Zheng <[email protected]>
>
> As the H3 SoC, which is also in sun8i line, has totally different
> register map for the thermal sensor (a cut down version of GPADC), we
> should rename A23/A33-specified registers to contain A33, in order to
> prevent obfuscation with H3 registers. Currently these registers are
> only prefixed "SUN8I", not "SUN8I_A33".
>
> Add "_A33" after "SUN8I" on the register names.
>
> Signed-off-by: Icenowy Zheng <[email protected]>
> Reviewed-by: Chen-Yu Tsai <[email protected]>
> Acked-by: Maxime Ripard <[email protected]>
> Acked-by: Lee Jones <[email protected]>
Acked-by: Jonathan Cameron <[email protected]>
(for the trivial change in the IIO driver)
> ---
> drivers/iio/adc/sun4i-gpadc-iio.c | 2 +-
> include/linux/mfd/sun4i-gpadc.h | 6 +++---
> 2 files changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
> index 04d7147e0110..03804ff9c006 100644
> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
> @@ -88,7 +88,7 @@ static const struct gpadc_data sun6i_gpadc_data = {
> static const struct gpadc_data sun8i_a33_gpadc_data = {
> .temp_offset = -1662,
> .temp_scale = 162,
> - .tp_mode_en = SUN8I_GPADC_CTRL1_CHOP_TEMP_EN,
> + .tp_mode_en = SUN8I_A33_GPADC_CTRL1_CHOP_TEMP_EN,
> };
>
> struct sun4i_gpadc_iio {
> diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
> index 139872c2e0fe..78d31984a222 100644
> --- a/include/linux/mfd/sun4i-gpadc.h
> +++ b/include/linux/mfd/sun4i-gpadc.h
> @@ -38,9 +38,9 @@
> #define SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(x) (GENMASK(3, 0) & BIT(x))
> #define SUN6I_GPADC_CTRL1_ADC_CHAN_MASK GENMASK(3, 0)
>
> -/* TP_CTRL1 bits for sun8i SoCs */
> -#define SUN8I_GPADC_CTRL1_CHOP_TEMP_EN BIT(8)
> -#define SUN8I_GPADC_CTRL1_GPADC_CALI_EN BIT(7)
> +/* TP_CTRL1 bits for A33 */
> +#define SUN8I_A33_GPADC_CTRL1_CHOP_TEMP_EN BIT(8)
> +#define SUN8I_A33_GPADC_CTRL1_GPADC_CALI_EN BIT(7)
>
> #define SUN4I_GPADC_CTRL2 0x08
>
On Fri, 26 Jan 2018 16:19:29 +0100
Philipp Rossak <[email protected]> wrote:
> For adding newer sensor some basic rework of the code is necessary.
>
> This commit reworks the code and allows the sampling start/end code and
> the position of value readout register to be altered. Later the start/end
> functions will be used to configure the ths and start/stop the
> sampling.
>
> Signed-off-by: Icenowy Zheng <[email protected]>
> Signed-off-by: Philipp Rossak <[email protected]>
Hmm. It almost always turns out to be a bad idea to assume a particular
set of registers will be consistent across a hardware family. Usual convention
is just to prefix them with the first supported device and use them across
the variants where they are correct. I.e. don't use wild cards or generic
names as they often end up implying a wider range of applicability than
we want.
A few other comments inline. Reworking code to make it ready for later
patches is fine, but this also adds a lot of new code which isn't used until
much later in the series. Move it there...
> ---
> drivers/iio/adc/sun4i-gpadc-iio.c | 87 +++++++++++++++++++++++++++++++++++----
> include/linux/mfd/sun4i-gpadc.h | 19 +++++++--
> 2 files changed, 94 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
> index 03804ff9c006..363936b37c5a 100644
> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
> @@ -49,6 +49,18 @@ static unsigned int sun6i_gpadc_chan_select(unsigned int chan)
> return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
> }
>
> +struct sun4i_gpadc_iio;
> +
> +/*
> + * Prototypes for these functions, which enable these functions to be
> + * referenced in gpadc_data structures.
> + */
> +static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info);
> +static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info);
> +
> +static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info);
Superficially this appears to be introduced but not used... Patch 9
is first to use it I think? Introduce it then rather than here.
> +static int sunxi_ths_sample_end(struct sun4i_gpadc_iio *info);
> +
> struct gpadc_data {
> int temp_offset;
> int temp_scale;
> @@ -56,6 +68,13 @@ struct gpadc_data {
> unsigned int tp_adc_select;
> unsigned int (*adc_chan_select)(unsigned int chan);
> unsigned int adc_chan_mask;
> + unsigned int temp_data;
> + int (*sample_start)(struct sun4i_gpadc_iio *info);
> + int (*sample_end)(struct sun4i_gpadc_iio *info);
> + u32 ctrl0_map;
> + u32 ctrl2_map;
> + u32 sensor_en_map;
> + u32 filter_map;
> };
>
> static const struct gpadc_data sun4i_gpadc_data = {
> @@ -65,6 +84,9 @@ static const struct gpadc_data sun4i_gpadc_data = {
> .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
> .adc_chan_select = &sun4i_gpadc_chan_select,
> .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
> + .temp_data = SUN4I_GPADC_TEMP_DATA,
> + .sample_start = sun4i_gpadc_sample_start,
> + .sample_end = sun4i_gpadc_sample_end,
> };
>
> static const struct gpadc_data sun5i_gpadc_data = {
> @@ -74,6 +96,9 @@ static const struct gpadc_data sun5i_gpadc_data = {
> .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
> .adc_chan_select = &sun4i_gpadc_chan_select,
> .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
> + .temp_data = SUN4I_GPADC_TEMP_DATA,
> + .sample_start = sun4i_gpadc_sample_start,
> + .sample_end = sun4i_gpadc_sample_end,
> };
>
> static const struct gpadc_data sun6i_gpadc_data = {
> @@ -83,12 +108,18 @@ static const struct gpadc_data sun6i_gpadc_data = {
> .tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
> .adc_chan_select = &sun6i_gpadc_chan_select,
> .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
> + .temp_data = SUN4I_GPADC_TEMP_DATA,
> + .sample_start = sun4i_gpadc_sample_start,
> + .sample_end = sun4i_gpadc_sample_end,
> };
>
> static const struct gpadc_data sun8i_a33_gpadc_data = {
> .temp_offset = -1662,
> .temp_scale = 162,
> .tp_mode_en = SUN8I_A33_GPADC_CTRL1_CHOP_TEMP_EN,
> + .temp_data = SUN4I_GPADC_TEMP_DATA,
> + .sample_start = sun4i_gpadc_sample_start,
> + .sample_end = sun4i_gpadc_sample_end,
> };
>
> struct sun4i_gpadc_iio {
> @@ -277,7 +308,7 @@ static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
> if (info->no_irq) {
> pm_runtime_get_sync(indio_dev->dev.parent);
>
> - regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
> + regmap_read(info->regmap, info->data->temp_data, val);
>
> pm_runtime_mark_last_busy(indio_dev->dev.parent);
> pm_runtime_put_autosuspend(indio_dev->dev.parent);
> @@ -382,10 +413,8 @@ static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
> return IRQ_HANDLED;
> }
>
> -static int sun4i_gpadc_runtime_suspend(struct device *dev)
> +static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info)
> {
> - struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
> -
> /* Disable the ADC on IP */
> regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0);
> /* Disable temperature sensor on IP */
> @@ -394,19 +423,32 @@ static int sun4i_gpadc_runtime_suspend(struct device *dev)
> return 0;
> }
>
> -static int sun4i_gpadc_runtime_resume(struct device *dev)
> +static int sunxi_ths_sample_end(struct sun4i_gpadc_iio *info)
> +{
> + /* Disable temperature sensor */
> + regmap_write(info->regmap, SUNXI_THS_CTRL2, 0x0);
> +
> + return 0;
> +}
> +
> +static int sun4i_gpadc_runtime_suspend(struct device *dev)
> {
> struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>
> + return info->data->sample_end(info);
> +}
> +
> +static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
> +{
> /* clkin = 6MHz */
> regmap_write(info->regmap, SUN4I_GPADC_CTRL0,
> SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) |
> SUN4I_GPADC_CTRL0_FS_DIV(7) |
> - SUN4I_GPADC_CTRL0_T_ACQ(63));
> + SUNXI_THS_ACQ0(63));
> regmap_write(info->regmap, SUN4I_GPADC_CTRL1, info->data->tp_mode_en);
> regmap_write(info->regmap, SUN4I_GPADC_CTRL3,
> - SUN4I_GPADC_CTRL3_FILTER_EN |
> - SUN4I_GPADC_CTRL3_FILTER_TYPE(1));
> + SUNXI_THS_FILTER_EN |
> + SUNXI_THS_FILTER_TYPE(1));
Why if these are temperature sensor features are we setting them in the
general purpose ADC start function?
> /* period = SUN4I_GPADC_TPR_TEMP_PERIOD * 256 * 16 / clkin; ~0.6s */
> regmap_write(info->regmap, SUN4I_GPADC_TPR,
> SUN4I_GPADC_TPR_TEMP_ENABLE |
> @@ -415,6 +457,35 @@ static int sun4i_gpadc_runtime_resume(struct device *dev)
> return 0;
> }
>
> +static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info)
> +{
> + u32 value;
> +
> + if (info->data->ctrl0_map)
> + regmap_write(info->regmap, SUNXI_THS_CTRL0,
> + info->data->ctrl0_map);
> +
> + regmap_write(info->regmap, SUNXI_THS_CTRL2,
> + info->data->ctrl2_map);
> +
> + regmap_write(info->regmap, SUNXI_THS_FILTER,
> + info->data->filter_map);
> +
> + regmap_read(info->regmap, SUNXI_THS_CTRL2, &value);
> +
> + regmap_write(info->regmap, SUNXI_THS_CTRL2,
> + info->data->sensor_en_map | value);
> +
> + return 0;
> +}
> +
> +static int sun4i_gpadc_runtime_resume(struct device *dev)
> +{
> + struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
> +
> + return info->data->sample_start(info);
> +}
> +
> static int sun4i_gpadc_get_temp(void *data, int *temp)
> {
> struct sun4i_gpadc_iio *info = data;
> diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
> index 78d31984a222..39e096c3ddac 100644
> --- a/include/linux/mfd/sun4i-gpadc.h
> +++ b/include/linux/mfd/sun4i-gpadc.h
> @@ -17,7 +17,6 @@
> #define SUN4I_GPADC_CTRL0_ADC_CLK_SELECT BIT(22)
> #define SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(x) ((GENMASK(1, 0) & (x)) << 20)
> #define SUN4I_GPADC_CTRL0_FS_DIV(x) ((GENMASK(3, 0) & (x)) << 16)
> -#define SUN4I_GPADC_CTRL0_T_ACQ(x) (GENMASK(15, 0) & (x))
>
> #define SUN4I_GPADC_CTRL1 0x04
>
> @@ -51,9 +50,6 @@
>
> #define SUN4I_GPADC_CTRL3 0x0c
>
> -#define SUN4I_GPADC_CTRL3_FILTER_EN BIT(2)
> -#define SUN4I_GPADC_CTRL3_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
> -
> #define SUN4I_GPADC_TPR 0x18
>
> #define SUN4I_GPADC_TPR_TEMP_ENABLE BIT(16)
> @@ -90,6 +86,21 @@
> /* 10s delay before suspending the IP */
> #define SUN4I_GPADC_AUTOSUSPEND_DELAY 10000
>
> +/* SUNXI_THS COMMON REGISTERS + DEFINES */
> +#define SUNXI_THS_CTRL0 0x00
> +#define SUNXI_THS_CTRL2 0x40
> +#define SUNXI_THS_FILTER 0x70
> +
> +#define SUNXI_THS_FILTER_EN BIT(2)
> +#define SUNXI_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
> +#define SUNXI_THS_ACQ0(x) (GENMASK(15, 0) & (x))
> +#define SUNXI_THS_ACQ1(x) (GENMASK(31, 16) & ((x) << 16))
> +
> +#define SUNXI_THS_TEMP_SENSE_EN0 BIT(0)
> +#define SUNXI_THS_TEMP_SENSE_EN1 BIT(1)
> +#define SUNXI_THS_TEMP_SENSE_EN2 BIT(2)
> +#define SUNXI_THS_TEMP_SENSE_EN3 BIT(3)
> +
> struct sun4i_gpadc_dev {
> struct device *dev;
> struct regmap *regmap;
On Fri, 26 Jan 2018 16:19:30 +0100
Philipp Rossak <[email protected]> wrote:
> For adding newer sensor some basic rework of the code is necessary.
>
> The SoCs after H3 has newer thermal sensor ADCs, which have two clock
> inputs (bus clock and sampling clock) and a reset. The registers are
> also re-arranged.
>
> This commit reworks the code, adds the process of the clocks and
> resets.
>
> Signed-off-by: Philipp Rossak <[email protected]>
> Signed-off-by: Icenowy Zheng <[email protected]>
Both clock and reset code is safe against null parameters, so you can
drop a lot of 'is it here' checks in this. They could be argued to
act as documentation of the fact we really don't expect them in some cases
I suppose...
> ---
> drivers/iio/adc/sun4i-gpadc-iio.c | 80 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 80 insertions(+)
>
> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
> index 363936b37c5a..1a80744bd472 100644
> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
> @@ -22,6 +22,7 @@
> * shutdown for not being used.
> */
>
> +#include <linux/clk.h>
> #include <linux/completion.h>
> #include <linux/interrupt.h>
> #include <linux/io.h>
> @@ -31,6 +32,7 @@
> #include <linux/platform_device.h>
> #include <linux/pm_runtime.h>
> #include <linux/regmap.h>
> +#include <linux/reset.h>
> #include <linux/thermal.h>
> #include <linux/delay.h>
>
> @@ -75,6 +77,9 @@ struct gpadc_data {
> u32 ctrl2_map;
> u32 sensor_en_map;
> u32 filter_map;
> + bool has_bus_clk;
> + bool has_bus_rst;
> + bool has_mod_clk;
> };
>
> static const struct gpadc_data sun4i_gpadc_data = {
> @@ -134,6 +139,9 @@ struct sun4i_gpadc_iio {
> atomic_t ignore_temp_data_irq;
> const struct gpadc_data *data;
> bool no_irq;
> + struct clk *bus_clk;
> + struct clk *mod_clk;
> + struct reset_control *reset;
> /* prevents concurrent reads of temperature and ADC */
> struct mutex mutex;
> struct thermal_zone_device *tzd;
> @@ -435,6 +443,12 @@ static int sun4i_gpadc_runtime_suspend(struct device *dev)
> {
> struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>
> + if (info->data->has_mod_clk)
> + clk_disable(info->mod_clk);
Safe against null parameter... (see below)
> +
> + if (info->data->has_bus_clk)
> + clk_disable(info->bus_clk);
> +
> return info->data->sample_end(info);
> }
>
> @@ -483,6 +497,12 @@ static int sun4i_gpadc_runtime_resume(struct device *dev)
> {
> struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>
> + if (info->data->has_mod_clk)
> + clk_enable(info->mod_clk);
clk_enable is safe against null parameter so could do without the checks.
> +
> + if (info->data->has_bus_clk)
> + clk_enable(info->bus_clk);
> +
> return info->data->sample_start(info);
> }
>
> @@ -597,10 +617,61 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
> return ret;
> }
>
> + if (info->data->has_bus_rst) {
> + info->reset = devm_reset_control_get(&pdev->dev, NULL);
> + if (IS_ERR(info->reset)) {
> + ret = PTR_ERR(info->reset);
> + return ret;
> + }
> +
> + ret = reset_control_deassert(info->reset);
> + if (ret)
> + return ret;
> + }
> +
> + if (info->data->has_bus_clk) {
> + info->bus_clk = devm_clk_get(&pdev->dev, "bus");
> + if (IS_ERR(info->bus_clk)) {
> + ret = PTR_ERR(info->bus_clk);
> + goto assert_reset;
> + }
> +
> + ret = clk_prepare_enable(info->bus_clk);
> + if (ret)
> + goto assert_reset;
> + }
> +
> + if (info->data->has_mod_clk) {
> + info->mod_clk = devm_clk_get(&pdev->dev, "mod");
> + if (IS_ERR(info->mod_clk)) {
> + ret = PTR_ERR(info->mod_clk);
> + goto disable_bus_clk;
> + }
> +
> + /* Running at 6MHz */
> + ret = clk_set_rate(info->mod_clk, 4000000);
> + if (ret)
> + goto disable_bus_clk;
> +
> + ret = clk_prepare_enable(info->mod_clk);
> + if (ret)
> + goto disable_bus_clk;
> + }
> +
> if (IS_ENABLED(CONFIG_THERMAL_OF))
> info->sensor_device = &pdev->dev;
>
> return 0;
> +
> +disable_bus_clk:
> + if (info->data->has_bus_clk)
> + clk_disable_unprepare(info->bus_clk);
> +
> +assert_reset:
> + if (info->data->has_bus_rst)
> + reset_control_assert(info->reset);
> +
> + return ret;
> }
>
> static int sun4i_gpadc_probe_mfd(struct platform_device *pdev,
> @@ -766,6 +837,15 @@ static int sun4i_gpadc_remove(struct platform_device *pdev)
> if (!info->no_irq)
> iio_map_array_unregister(indio_dev);
>
> + if (info->data->has_mod_clk)
> + clk_disable_unprepare(info->mod_clk);
clk_disable_unprepare is safe against a null parameter
so I don't think the checks are needed.
> +
> + if (info->data->has_bus_clk)
> + clk_disable_unprepare(info->bus_clk);
> +
> + if (info->data->has_bus_rst)
> + reset_control_assert(info->reset);
Safe against null parameter...
> +
> return 0;
> }
>
On Fri, 26 Jan 2018 16:19:32 +0100
Philipp Rossak <[email protected]> wrote:
> This patch reworks the driver to support nvmem calibration cells.
> The driver checks if the nvmem calibration is supported and reads out
> the nvmem. At the beginning of the startup process the calibration data
> is written to the related registers.
>
> Signed-off-by: Philipp Rossak <[email protected]>
A few minor suggestions inline.
Jonathan
> ---
> drivers/iio/adc/sun4i-gpadc-iio.c | 52 +++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/sun4i-gpadc.h | 2 ++
> 2 files changed, 54 insertions(+)
>
> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
> index bff06f2798e8..7b12666cdd9e 100644
> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
> @@ -27,6 +27,7 @@
> #include <linux/interrupt.h>
> #include <linux/io.h>
> #include <linux/module.h>
> +#include <linux/nvmem-consumer.h>
> #include <linux/of.h>
> #include <linux/of_device.h>
> #include <linux/platform_device.h>
> @@ -81,6 +82,7 @@ struct gpadc_data {
> bool has_bus_rst;
> bool has_mod_clk;
> int sensor_count;
> + bool supports_nvmem;
> };
>
> static const struct gpadc_data sun4i_gpadc_data = {
> @@ -94,6 +96,7 @@ static const struct gpadc_data sun4i_gpadc_data = {
> .sample_start = sun4i_gpadc_sample_start,
> .sample_end = sun4i_gpadc_sample_end,
> .sensor_count = 1,
> + .supports_nvmem = false,
> };
>
> static const struct gpadc_data sun5i_gpadc_data = {
> @@ -107,6 +110,7 @@ static const struct gpadc_data sun5i_gpadc_data = {
> .sample_start = sun4i_gpadc_sample_start,
> .sample_end = sun4i_gpadc_sample_end,
> .sensor_count = 1,
> + .supports_nvmem = false,
> };
>
> static const struct gpadc_data sun6i_gpadc_data = {
> @@ -120,6 +124,7 @@ static const struct gpadc_data sun6i_gpadc_data = {
> .sample_start = sun4i_gpadc_sample_start,
> .sample_end = sun4i_gpadc_sample_end,
> .sensor_count = 1,
> + .supports_nvmem = false,
> };
>
> static const struct gpadc_data sun8i_a33_gpadc_data = {
> @@ -130,6 +135,7 @@ static const struct gpadc_data sun8i_a33_gpadc_data = {
> .sample_start = sun4i_gpadc_sample_start,
> .sample_end = sun4i_gpadc_sample_end,
> .sensor_count = 1,
> + .supports_nvmem = false,
> };
>
> struct sun4i_gpadc_iio {
> @@ -148,6 +154,8 @@ struct sun4i_gpadc_iio {
> struct clk *mod_clk;
> struct reset_control *reset;
> int sensor_id;
> + u32 calibration_data[2];
> + bool has_calibration_data[2];
> /* prevents concurrent reads of temperature and ADC */
> struct mutex mutex;
> struct thermal_zone_device *tzd;
> @@ -459,6 +467,17 @@ static int sun4i_gpadc_runtime_suspend(struct device *dev)
> return info->data->sample_end(info);
> }
>
> +static void sunxi_calibrate(struct sun4i_gpadc_iio *info)
> +{
> + if (info->has_calibration_data[0])
> + regmap_write(info->regmap, SUNXI_THS_CDATA_0_1,
> + info->calibration_data[0]);
> +
> + if (info->has_calibration_data[1])
> + regmap_write(info->regmap, SUNXI_THS_CDATA_2_3,
> + info->calibration_data[1]);
> +}
> +
> static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
> {
> /* clkin = 6MHz */
> @@ -481,6 +500,7 @@ static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
> static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info)
> {
> u32 value;
> + sunxi_calibrate(info);
>
> if (info->data->ctrl0_map)
> regmap_write(info->regmap, SUNXI_THS_CTRL0,
> @@ -602,6 +622,9 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
> struct resource *mem;
> void __iomem *base;
> int ret;
> + struct nvmem_cell *cell;
> + ssize_t cell_size;
> + u64 *cell_data;
>
> info->data = of_device_get_match_data(&pdev->dev);
> if (!info->data)
> @@ -616,6 +639,35 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
> if (IS_ERR(base))
> return PTR_ERR(base);
>
> + info->has_calibration_data[0] = false;
> + info->has_calibration_data[1] = false;
> +
> + if (!info->data->supports_nvmem)
> + goto no_nvmem;
> +
> + cell = devm_nvmem_cell_get(&pdev->dev, "calibration");
> + if (IS_ERR(cell)) {
> + if (PTR_ERR(cell) == -EPROBE_DEFER)
> + return PTR_ERR(cell);
Use a goto here to no_nvmem.
Then you can drop the below else to make things more readable.
> + } else {
> + cell_data = (u64 *)nvmem_cell_read(cell, &cell_size);
> + devm_nvmem_cell_put(&pdev->dev, cell);
I'm really not keen on use of devm for things we are intending
to drop almost immediately. Just use the non managed versions
and clean up properly in the error paths (if there are any
where it is needed - which there aren't that I can see)
> + if (cell_size <= 4) {
Is it valid if it is anything other than 4?
> + info->has_calibration_data[0] = true;
> + info->calibration_data[0] = be32_to_cpu(cell_data[0] &
> + GENMASK(31, 0));
Masking isn't needed, If you want to be paranoid there is the lower_32_bits
function..
> + } else if (cell_size <= 8) {
> + info->has_calibration_data[0] = true;
> + info->calibration_data[0] = be32_to_cpu(cell_data[0] &
> + GENMASK(31, 0));
This first block is repeated. Easy enough to avoid I think...
> + info->has_calibration_data[1] = true;
> + info->calibration_data[1] = be32_to_cpu(
> + (cell_data[0] >> 32) & GENMASK(31, 0));
> + }
> + }
> +
> +no_nvmem:
> +
> info->regmap = devm_regmap_init_mmio(&pdev->dev, base,
> &sun4i_gpadc_regmap_config);
> if (IS_ERR(info->regmap)) {
> diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
> index 40b4dd9d2405..c251002431bd 100644
> --- a/include/linux/mfd/sun4i-gpadc.h
> +++ b/include/linux/mfd/sun4i-gpadc.h
> @@ -90,6 +90,8 @@
> #define SUNXI_THS_CTRL0 0x00
> #define SUNXI_THS_CTRL2 0x40
> #define SUNXI_THS_FILTER 0x70
> +#define SUNXI_THS_CDATA_0_1 0x74
> +#define SUNXI_THS_CDATA_2_3 0x78
> #define SUNXI_THS_TDATA0 0x80
> #define SUNXI_THS_TDATA1 0x84
> #define SUNXI_THS_TDATA2 0x88
On Fri, 26 Jan 2018 16:19:33 +0100
Philipp Rossak <[email protected]> wrote:
> This patch rewors the driver to support interrupts for the thermal part
> of the sensor.
>
> This is only available for the newer sensor (currently H3 and A83T).
> The interrupt will be trigerd on data available and triggers the update
> for the thermal sensors. All newer sensors have different amount of
> sensors and different interrupts for each device the reset of the
> interrupts need to be done different
>
> For the newer sensors is the autosuspend disabled.
>
> Signed-off-by: Philipp Rossak <[email protected]>
Really minor point inline, otherwise this looks fine to me.
Acked-by: Jonathan Cameron <[email protected]>
> ---
> drivers/iio/adc/sun4i-gpadc-iio.c | 68 +++++++++++++++++++++++++++++++++++----
> include/linux/mfd/sun4i-gpadc.h | 33 +++++++++++++++++++
> 2 files changed, 95 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
> index 7b12666cdd9e..77e07f042730 100644
> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
> @@ -78,11 +78,14 @@ struct gpadc_data {
> u32 ctrl2_map;
> u32 sensor_en_map;
> u32 filter_map;
> + u32 irq_clear_map;
> + u32 irq_control_map;
> bool has_bus_clk;
> bool has_bus_rst;
> bool has_mod_clk;
> int sensor_count;
> bool supports_nvmem;
> + bool support_irq;
> };
>
> static const struct gpadc_data sun4i_gpadc_data = {
> @@ -97,6 +100,7 @@ static const struct gpadc_data sun4i_gpadc_data = {
> .sample_end = sun4i_gpadc_sample_end,
> .sensor_count = 1,
> .supports_nvmem = false,
> + .support_irq = false,
> };
>
> static const struct gpadc_data sun5i_gpadc_data = {
> @@ -111,6 +115,7 @@ static const struct gpadc_data sun5i_gpadc_data = {
> .sample_end = sun4i_gpadc_sample_end,
> .sensor_count = 1,
> .supports_nvmem = false,
> + .support_irq = false,
> };
>
> static const struct gpadc_data sun6i_gpadc_data = {
> @@ -125,6 +130,7 @@ static const struct gpadc_data sun6i_gpadc_data = {
> .sample_end = sun4i_gpadc_sample_end,
> .sensor_count = 1,
> .supports_nvmem = false,
> + .support_irq = false,
> };
>
> static const struct gpadc_data sun8i_a33_gpadc_data = {
> @@ -136,6 +142,7 @@ static const struct gpadc_data sun8i_a33_gpadc_data = {
> .sample_end = sun4i_gpadc_sample_end,
> .sensor_count = 1,
> .supports_nvmem = false,
> + .support_irq = false,
> };
>
> struct sun4i_gpadc_iio {
> @@ -339,6 +346,11 @@ static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val,
> return 0;
> }
>
> + if (info->data->support_irq) {
> + regmap_read(info->regmap, info->data->temp_data[sensor], val);
> + return 0;
> + }
> +
> return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
> }
>
> @@ -436,6 +448,17 @@ static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
> return IRQ_HANDLED;
> }
>
> +static irqreturn_t sunxi_irq_thread(int irq, void *data)
> +{
> + struct sun4i_gpadc_iio *info = data;
> +
> + regmap_write(info->regmap, SUNXI_THS_STAT, info->data->irq_clear_map);
> +
> + thermal_zone_device_update(info->tzd, THERMAL_EVENT_TEMP_SAMPLE);
> +
> + return IRQ_HANDLED;
> +}
> +
> static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info)
> {
> /* Disable the ADC on IP */
> @@ -448,6 +471,8 @@ static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info)
>
> static int sunxi_ths_sample_end(struct sun4i_gpadc_iio *info)
> {
> + /* Disable ths interrupt*/
Space before */
> + regmap_write(info->regmap, SUNXI_THS_INTC, 0x0);
> /* Disable temperature sensor */
> regmap_write(info->regmap, SUNXI_THS_CTRL2, 0x0);
>
> @@ -509,9 +534,15 @@ static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info)
> regmap_write(info->regmap, SUNXI_THS_CTRL2,
> info->data->ctrl2_map);
>
> + regmap_write(info->regmap, SUNXI_THS_STAT,
> + info->data->irq_clear_map);
> +
> regmap_write(info->regmap, SUNXI_THS_FILTER,
> info->data->filter_map);
>
> + regmap_write(info->regmap, SUNXI_THS_INTC,
> + info->data->irq_control_map);
> +
> regmap_read(info->regmap, SUNXI_THS_CTRL2, &value);
>
> regmap_write(info->regmap, SUNXI_THS_CTRL2,
> @@ -625,12 +656,29 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
> struct nvmem_cell *cell;
> ssize_t cell_size;
> u64 *cell_data;
> + int irq;
>
> info->data = of_device_get_match_data(&pdev->dev);
> if (!info->data)
> return -ENODEV;
>
> - info->no_irq = true;
> + if (info->data->support_irq) {
> + /* only the new versions of ths support right now irqs */
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + dev_err(&pdev->dev, "failed to get IRQ: %d\n", irq);
> + return irq;
> + }
> +
> + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
> + sunxi_irq_thread, IRQF_ONESHOT,
> + dev_name(&pdev->dev), info);
> + if (ret)
> + return ret;
> +
> + } else
> + info->no_irq = true;
> +
> indio_dev->num_channels = ARRAY_SIZE(sun8i_a33_gpadc_channels);
> indio_dev->channels = sun8i_a33_gpadc_channels;
>
> @@ -840,11 +888,13 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
> if (ret)
> return ret;
>
> - pm_runtime_set_autosuspend_delay(&pdev->dev,
> - SUN4I_GPADC_AUTOSUSPEND_DELAY);
> - pm_runtime_use_autosuspend(&pdev->dev);
> - pm_runtime_set_suspended(&pdev->dev);
> - pm_runtime_enable(&pdev->dev);
> + if (!info->data->support_irq) {
> + pm_runtime_set_autosuspend_delay(&pdev->dev,
> + SUN4I_GPADC_AUTOSUSPEND_DELAY);
> + pm_runtime_use_autosuspend(&pdev->dev);
> + pm_runtime_set_suspended(&pdev->dev);
> + pm_runtime_enable(&pdev->dev);
> + }
>
> if (IS_ENABLED(CONFIG_THERMAL_OF)) {
> for (i = 0; i < info->data->sensor_count; i++) {
> @@ -865,6 +915,9 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
> }
> }
>
> + if (info->data->support_irq)
> + info->data->sample_start(info);
> +
> ret = devm_iio_device_register(&pdev->dev, indio_dev);
> if (ret < 0) {
> dev_err(&pdev->dev, "could not register the device\n");
> @@ -894,6 +947,9 @@ static int sun4i_gpadc_remove(struct platform_device *pdev)
> if (!IS_ENABLED(CONFIG_THERMAL_OF))
> return 0;
>
> + if (info->data->support_irq)
> + info->data->sample_end(info);
> +
> thermal_zone_of_sensor_unregister(info->sensor_device, info->tzd);
>
> if (!info->no_irq)
> diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
> index c251002431bd..ab34a96a7ff3 100644
> --- a/include/linux/mfd/sun4i-gpadc.h
> +++ b/include/linux/mfd/sun4i-gpadc.h
> @@ -89,6 +89,8 @@
> /* SUNXI_THS COMMON REGISTERS + DEFINES */
> #define SUNXI_THS_CTRL0 0x00
> #define SUNXI_THS_CTRL2 0x40
> +#define SUNXI_THS_INTC 0x44
> +#define SUNXI_THS_STAT 0x48
> #define SUNXI_THS_FILTER 0x70
> #define SUNXI_THS_CDATA_0_1 0x74
> #define SUNXI_THS_CDATA_2_3 0x78
> @@ -107,6 +109,37 @@
> #define SUNXI_THS_TEMP_SENSE_EN2 BIT(2)
> #define SUNXI_THS_TEMP_SENSE_EN3 BIT(3)
>
> +#define SUNXI_THS_TEMP_PERIOD(x) (GENMASK(31, 12) & ((x) << 12))
> +
> +#define SUNXI_THS_INTS_ALARM_OFF_2 BIT(14)
> +#define SUNXI_THS_INTS_ALARM_OFF_1 BIT(13)
> +#define SUNXI_THS_INTS_ALARM_OFF_0 BIT(12)
> +#define SUNXI_THS_INTS_TDATA_IRQ_3 BIT(11)
> +#define SUNXI_THS_INTS_TDATA_IRQ_2 BIT(10)
> +#define SUNXI_THS_INTS_TDATA_IRQ_1 BIT(9)
> +#define SUNXI_THS_INTS_TDATA_IRQ_0 BIT(8)
> +#define SUNXI_THS_INTS_SHUT_INT_3 BIT(7)
> +#define SUNXI_THS_INTS_SHUT_INT_2 BIT(6)
> +#define SUNXI_THS_INTS_SHUT_INT_1 BIT(5)
> +#define SUNXI_THS_INTS_SHUT_INT_0 BIT(4)
> +#define SUNXI_THS_INTS_ALARM_INT_3 BIT(3)
> +#define SUNXI_THS_INTS_ALARM_INT_2 BIT(2)
> +#define SUNXI_THS_INTS_ALARM_INT_1 BIT(1)
> +#define SUNXI_THS_INTS_ALARM_INT_0 BIT(0)
> +
> +#define SUNXI_THS_INTC_TDATA_IRQ_EN3 BIT(11)
> +#define SUNXI_THS_INTC_TDATA_IRQ_EN2 BIT(10)
> +#define SUNXI_THS_INTC_TDATA_IRQ_EN1 BIT(9)
> +#define SUNXI_THS_INTC_TDATA_IRQ_EN0 BIT(8)
> +#define SUNXI_THS_INTC_SHUT_INT_EN3 BIT(7)
> +#define SUNXI_THS_INTC_SHUT_INT_EN2 BIT(6)
> +#define SUNXI_THS_INTC_SHUT_INT_EN1 BIT(5)
> +#define SUNXI_THS_INTC_SHUT_INT_EN0 BIT(4)
> +#define SUNXI_THS_INTC_ALARM_INT_EN3 BIT(3)
> +#define SUNXI_THS_INTC_ALARM_INT_EN2 BIT(2)
> +#define SUNXI_THS_INTC_ALARM_INT_EN1 BIT(1)
> +#define SUNXI_THS_INTC_ALARM_INT_EN0 BIT(0)
> +
> #define MAX_SENSOR_COUNT 4
>
> struct sun4i_gpadc_dev {
On Fri, 26 Jan 2018 16:19:31 +0100
Philipp Rossak <[email protected]> wrote:
multible -> multiple
> For adding newer sensor some basic rework of the code is necessary.
>
> This patch reworks the driver to be able to handle more than one
> thermal sensor. Newer SoC like the A80 have 4 thermal sensors.
> Because of this the maximal sensor count value was set to 4.
>
> The sensor_id value is set during sensor registration and is for each
> registered sensor indiviual. This makes it able to differntiate the
> sensors when the value is read from the register.
>
> Signed-off-by: Philipp Rossak <[email protected]>
Question inline about why you aren't exposing the additional temperature
sensors as IIO channels?
Fine to not do so I suppose, but needs justifying.
Jonathan
> ---
> drivers/iio/adc/sun4i-gpadc-iio.c | 36 +++++++++++++++++++++++-------------
> include/linux/mfd/sun4i-gpadc.h | 6 ++++++
> 2 files changed, 29 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
> index 1a80744bd472..bff06f2798e8 100644
> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
> @@ -70,7 +70,7 @@ struct gpadc_data {
> unsigned int tp_adc_select;
> unsigned int (*adc_chan_select)(unsigned int chan);
> unsigned int adc_chan_mask;
> - unsigned int temp_data;
> + unsigned int temp_data[MAX_SENSOR_COUNT];
> int (*sample_start)(struct sun4i_gpadc_iio *info);
> int (*sample_end)(struct sun4i_gpadc_iio *info);
> u32 ctrl0_map;
> @@ -80,6 +80,7 @@ struct gpadc_data {
> bool has_bus_clk;
> bool has_bus_rst;
> bool has_mod_clk;
> + int sensor_count;
> };
>
> static const struct gpadc_data sun4i_gpadc_data = {
> @@ -89,9 +90,10 @@ static const struct gpadc_data sun4i_gpadc_data = {
> .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
> .adc_chan_select = &sun4i_gpadc_chan_select,
> .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
> - .temp_data = SUN4I_GPADC_TEMP_DATA,
> + .temp_data = {SUN4I_GPADC_TEMP_DATA, 0, 0, 0},
> .sample_start = sun4i_gpadc_sample_start,
> .sample_end = sun4i_gpadc_sample_end,
> + .sensor_count = 1,
> };
>
> static const struct gpadc_data sun5i_gpadc_data = {
> @@ -101,9 +103,10 @@ static const struct gpadc_data sun5i_gpadc_data = {
> .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
> .adc_chan_select = &sun4i_gpadc_chan_select,
> .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
> - .temp_data = SUN4I_GPADC_TEMP_DATA,
> + .temp_data = {SUN4I_GPADC_TEMP_DATA, 0, 0, 0},
> .sample_start = sun4i_gpadc_sample_start,
> .sample_end = sun4i_gpadc_sample_end,
> + .sensor_count = 1,
> };
>
> static const struct gpadc_data sun6i_gpadc_data = {
> @@ -113,18 +116,20 @@ static const struct gpadc_data sun6i_gpadc_data = {
> .tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
> .adc_chan_select = &sun6i_gpadc_chan_select,
> .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
> - .temp_data = SUN4I_GPADC_TEMP_DATA,
> + .temp_data = {SUN4I_GPADC_TEMP_DATA, 0, 0, 0},
> .sample_start = sun4i_gpadc_sample_start,
> .sample_end = sun4i_gpadc_sample_end,
> + .sensor_count = 1,
> };
>
> static const struct gpadc_data sun8i_a33_gpadc_data = {
> .temp_offset = -1662,
> .temp_scale = 162,
> .tp_mode_en = SUN8I_A33_GPADC_CTRL1_CHOP_TEMP_EN,
> - .temp_data = SUN4I_GPADC_TEMP_DATA,
> + .temp_data = {SUN4I_GPADC_TEMP_DATA, 0, 0, 0},
> .sample_start = sun4i_gpadc_sample_start,
> .sample_end = sun4i_gpadc_sample_end,
> + .sensor_count = 1,
> };
>
> struct sun4i_gpadc_iio {
> @@ -142,6 +147,7 @@ struct sun4i_gpadc_iio {
> struct clk *bus_clk;
> struct clk *mod_clk;
> struct reset_control *reset;
> + int sensor_id;
> /* prevents concurrent reads of temperature and ADC */
> struct mutex mutex;
> struct thermal_zone_device *tzd;
> @@ -309,14 +315,15 @@ static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel,
> return sun4i_gpadc_read(indio_dev, channel, val, info->fifo_data_irq);
> }
>
> -static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
> +static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val,
> + int sensor)
> {
> struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
>
> if (info->no_irq) {
> pm_runtime_get_sync(indio_dev->dev.parent);
>
> - regmap_read(info->regmap, info->data->temp_data, val);
> + regmap_read(info->regmap, info->data->temp_data[sensor], val);
>
> pm_runtime_mark_last_busy(indio_dev->dev.parent);
> pm_runtime_put_autosuspend(indio_dev->dev.parent);
> @@ -363,7 +370,7 @@ static int sun4i_gpadc_read_raw(struct iio_dev *indio_dev,
> ret = sun4i_gpadc_adc_read(indio_dev, chan->channel,
> val);
> else
> - ret = sun4i_gpadc_temp_read(indio_dev, val);
> + ret = sun4i_gpadc_temp_read(indio_dev, val, 0);
Wouldn't it make more sense to pass the channel number in here?
Looking at it, seems you only expose the first temperature sensor
as an IIO channel? Perhaps fair enough, but please justify that
decision in the patch description.
>
> if (ret)
> return ret;
> @@ -511,7 +518,7 @@ static int sun4i_gpadc_get_temp(void *data, int *temp)
> struct sun4i_gpadc_iio *info = data;
> int val, scale, offset;
>
> - if (sun4i_gpadc_temp_read(info->indio_dev, &val))
> + if (sun4i_gpadc_temp_read(info->indio_dev, &val, info->sensor_id))
> return -ETIMEDOUT;
>
> sun4i_gpadc_temp_scale(info->indio_dev, &scale);
> @@ -755,7 +762,7 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
> {
> struct sun4i_gpadc_iio *info;
> struct iio_dev *indio_dev;
> - int ret;
> + int ret, i;
>
> indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
> if (!indio_dev)
> @@ -788,9 +795,12 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
> pm_runtime_enable(&pdev->dev);
>
> if (IS_ENABLED(CONFIG_THERMAL_OF)) {
> - info->tzd = thermal_zone_of_sensor_register(info->sensor_device,
> - 0, info,
> - &sun4i_ts_tz_ops);
> + for (i = 0; i < info->data->sensor_count; i++) {
> + info->sensor_id = i;
> + info->tzd = thermal_zone_of_sensor_register(
> + info->sensor_device,
> + i, info, &sun4i_ts_tz_ops);
> + }
> /*
> * Do not fail driver probing when failing to register in
> * thermal because no thermal DT node is found.
> diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
> index 39e096c3ddac..40b4dd9d2405 100644
> --- a/include/linux/mfd/sun4i-gpadc.h
> +++ b/include/linux/mfd/sun4i-gpadc.h
> @@ -90,6 +90,10 @@
> #define SUNXI_THS_CTRL0 0x00
> #define SUNXI_THS_CTRL2 0x40
> #define SUNXI_THS_FILTER 0x70
> +#define SUNXI_THS_TDATA0 0x80
> +#define SUNXI_THS_TDATA1 0x84
> +#define SUNXI_THS_TDATA2 0x88
> +#define SUNXI_THS_TDATA3 0x8c
>
> #define SUNXI_THS_FILTER_EN BIT(2)
> #define SUNXI_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
> @@ -101,6 +105,8 @@
> #define SUNXI_THS_TEMP_SENSE_EN2 BIT(2)
> #define SUNXI_THS_TEMP_SENSE_EN3 BIT(3)
>
> +#define MAX_SENSOR_COUNT 4
> +
> struct sun4i_gpadc_dev {
> struct device *dev;
> struct regmap *regmap;
On 28.01.2018 09:50, Jonathan Cameron wrote:
> On Fri, 26 Jan 2018 16:19:30 +0100
> Philipp Rossak <[email protected]> wrote:
>
>> For adding newer sensor some basic rework of the code is necessary.
>>
>> The SoCs after H3 has newer thermal sensor ADCs, which have two clock
>> inputs (bus clock and sampling clock) and a reset. The registers are
>> also re-arranged.
>>
>> This commit reworks the code, adds the process of the clocks and
>> resets.
>>
>> Signed-off-by: Philipp Rossak <[email protected]>
>> Signed-off-by: Icenowy Zheng <[email protected]>
>
> Both clock and reset code is safe against null parameters, so you can
> drop a lot of 'is it here' checks in this. They could be argued to
> act as documentation of the fact we really don't expect them in some cases
> I suppose...
>
Ok, this sounds good for me!
I will fix that in the next version of this patch series.
>> ---
>> drivers/iio/adc/sun4i-gpadc-iio.c | 80 +++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 80 insertions(+)
>>
>> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
>> index 363936b37c5a..1a80744bd472 100644
>> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
>> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
>> @@ -22,6 +22,7 @@
>> * shutdown for not being used.
>> */
>>
>> +#include <linux/clk.h>
>> #include <linux/completion.h>
>> #include <linux/interrupt.h>
>> #include <linux/io.h>
>> @@ -31,6 +32,7 @@
>> #include <linux/platform_device.h>
>> #include <linux/pm_runtime.h>
>> #include <linux/regmap.h>
>> +#include <linux/reset.h>
>> #include <linux/thermal.h>
>> #include <linux/delay.h>
>>
>> @@ -75,6 +77,9 @@ struct gpadc_data {
>> u32 ctrl2_map;
>> u32 sensor_en_map;
>> u32 filter_map;
>> + bool has_bus_clk;
>> + bool has_bus_rst;
>> + bool has_mod_clk;
>> };
>>
>> static const struct gpadc_data sun4i_gpadc_data = {
>> @@ -134,6 +139,9 @@ struct sun4i_gpadc_iio {
>> atomic_t ignore_temp_data_irq;
>> const struct gpadc_data *data;
>> bool no_irq;
>> + struct clk *bus_clk;
>> + struct clk *mod_clk;
>> + struct reset_control *reset;
>> /* prevents concurrent reads of temperature and ADC */
>> struct mutex mutex;
>> struct thermal_zone_device *tzd;
>> @@ -435,6 +443,12 @@ static int sun4i_gpadc_runtime_suspend(struct device *dev)
>> {
>> struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>>
>> + if (info->data->has_mod_clk)
>> + clk_disable(info->mod_clk);
>
> Safe against null parameter... (see below)
>
>> +
>> + if (info->data->has_bus_clk)
>> + clk_disable(info->bus_clk);
>> +
>> return info->data->sample_end(info);
>> }
>>
>> @@ -483,6 +497,12 @@ static int sun4i_gpadc_runtime_resume(struct device *dev)
>> {
>> struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>>
>> + if (info->data->has_mod_clk)
>> + clk_enable(info->mod_clk);
>
> clk_enable is safe against null parameter so could do without the checks.
>
>> +
>> + if (info->data->has_bus_clk)
>> + clk_enable(info->bus_clk);
>> +
>> return info->data->sample_start(info);
>> }
>>
>> @@ -597,10 +617,61 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
>> return ret;
>> }
>>
>> + if (info->data->has_bus_rst) {
>> + info->reset = devm_reset_control_get(&pdev->dev, NULL);
>> + if (IS_ERR(info->reset)) {
>> + ret = PTR_ERR(info->reset);
>> + return ret;
>> + }
>> +
>> + ret = reset_control_deassert(info->reset);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + if (info->data->has_bus_clk) {
>> + info->bus_clk = devm_clk_get(&pdev->dev, "bus");
>> + if (IS_ERR(info->bus_clk)) {
>> + ret = PTR_ERR(info->bus_clk);
>> + goto assert_reset;
>> + }
>> +
>> + ret = clk_prepare_enable(info->bus_clk);
>> + if (ret)
>> + goto assert_reset;
>> + }
>> +
>> + if (info->data->has_mod_clk) {
>> + info->mod_clk = devm_clk_get(&pdev->dev, "mod");
>> + if (IS_ERR(info->mod_clk)) {
>> + ret = PTR_ERR(info->mod_clk);
>> + goto disable_bus_clk;
>> + }
>> +
>> + /* Running at 6MHz */
>> + ret = clk_set_rate(info->mod_clk, 4000000);
>> + if (ret)
>> + goto disable_bus_clk;
>> +
>> + ret = clk_prepare_enable(info->mod_clk);
>> + if (ret)
>> + goto disable_bus_clk;
>> + }
>> +
>> if (IS_ENABLED(CONFIG_THERMAL_OF))
>> info->sensor_device = &pdev->dev;
>>
>> return 0;
>> +
>> +disable_bus_clk:
>> + if (info->data->has_bus_clk)
>> + clk_disable_unprepare(info->bus_clk);
>> +
>> +assert_reset:
>> + if (info->data->has_bus_rst)
>> + reset_control_assert(info->reset);
>> +
>> + return ret;
>> }
>>
>> static int sun4i_gpadc_probe_mfd(struct platform_device *pdev,
>> @@ -766,6 +837,15 @@ static int sun4i_gpadc_remove(struct platform_device *pdev)
>> if (!info->no_irq)
>> iio_map_array_unregister(indio_dev);
>>
>> + if (info->data->has_mod_clk)
>> + clk_disable_unprepare(info->mod_clk);
>
> clk_disable_unprepare is safe against a null parameter
> so I don't think the checks are needed.
>
>> +
>> + if (info->data->has_bus_clk)
>> + clk_disable_unprepare(info->bus_clk);
>> +
>> + if (info->data->has_bus_rst)
>> + reset_control_assert(info->reset);
>
> Safe against null parameter...
>
>> +
>> return 0;
>> }
>>
>
Thanks,
Philipp
On 28.01.2018 09:43, Jonathan Cameron wrote:
> On Fri, 26 Jan 2018 16:19:29 +0100
> Philipp Rossak <[email protected]> wrote:
>
>> For adding newer sensor some basic rework of the code is necessary.
>>
>> This commit reworks the code and allows the sampling start/end code and
>> the position of value readout register to be altered. Later the start/end
>> functions will be used to configure the ths and start/stop the
>> sampling.
>>
>> Signed-off-by: Icenowy Zheng <[email protected]>
>> Signed-off-by: Philipp Rossak <[email protected]>
> Hmm. It almost always turns out to be a bad idea to assume a particular
> set of registers will be consistent across a hardware family. Usual convention
> is just to prefix them with the first supported device and use them across
> the variants where they are correct. I.e. don't use wild cards or generic
> names as they often end up implying a wider range of applicability than
> we want.
>
The new THS sensors seems to use the same THS ip core, only configured
with different amount of sensors. So for H3, A83T, H5, A80 and A64 the
registers are all the same.
I think the big question is, do we want to have a few very big patches.
Or more small patches with code that is used in later patches.
> A few other comments inline. Reworking code to make it ready for later
> patches is fine, but this also adds a lot of new code which isn't used until
> much later in the series. Move it there...
please see comments below.
>> ---
>> drivers/iio/adc/sun4i-gpadc-iio.c | 87 +++++++++++++++++++++++++++++++++++----
>> include/linux/mfd/sun4i-gpadc.h | 19 +++++++--
>> 2 files changed, 94 insertions(+), 12 deletions(-)
>>
>> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
>> index 03804ff9c006..363936b37c5a 100644
>> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
>> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
>> @@ -49,6 +49,18 @@ static unsigned int sun6i_gpadc_chan_select(unsigned int chan)
>> return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
>> }
>>
>> +struct sun4i_gpadc_iio;
>> +
>> +/*
>> + * Prototypes for these functions, which enable these functions to be
>> + * referenced in gpadc_data structures.
>> + */
>> +static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info);
>> +static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info);
>> +
>> +static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info);
>
> Superficially this appears to be introduced but not used... Patch 9
> is first to use it I think? Introduce it then rather than here.
>
You are right, this function is used first in Patch 9. In an very early
version, I had this together with Patch 9. But then I had the feeling
that everything got too messy... Right now this is a very big patch
series and I wanted to have a clear structure in it and easy to review.
If I would rework this it will end up in squashing, Patch 4, Parts of
Patch 7, Patch 8 and Patch 9.
>> +static int sunxi_ths_sample_end(struct sun4i_gpadc_iio *info);
>> +
>> struct gpadc_data {
>> int temp_offset;
>> int temp_scale;
>> @@ -56,6 +68,13 @@ struct gpadc_data {
>> unsigned int tp_adc_select;
>> unsigned int (*adc_chan_select)(unsigned int chan);
>> unsigned int adc_chan_mask;
>> + unsigned int temp_data;
>> + int (*sample_start)(struct sun4i_gpadc_iio *info);
>> + int (*sample_end)(struct sun4i_gpadc_iio *info);
>> + u32 ctrl0_map;
>> + u32 ctrl2_map;
>> + u32 sensor_en_map;
>> + u32 filter_map;
>> };
>>
>> static const struct gpadc_data sun4i_gpadc_data = {
>> @@ -65,6 +84,9 @@ static const struct gpadc_data sun4i_gpadc_data = {
>> .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
>> .adc_chan_select = &sun4i_gpadc_chan_select,
>> .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
>> + .temp_data = SUN4I_GPADC_TEMP_DATA,
>> + .sample_start = sun4i_gpadc_sample_start,
>> + .sample_end = sun4i_gpadc_sample_end,
>> };
>>
>> static const struct gpadc_data sun5i_gpadc_data = {
>> @@ -74,6 +96,9 @@ static const struct gpadc_data sun5i_gpadc_data = {
>> .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
>> .adc_chan_select = &sun4i_gpadc_chan_select,
>> .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
>> + .temp_data = SUN4I_GPADC_TEMP_DATA,
>> + .sample_start = sun4i_gpadc_sample_start,
>> + .sample_end = sun4i_gpadc_sample_end,
>> };
>>
>> static const struct gpadc_data sun6i_gpadc_data = {
>> @@ -83,12 +108,18 @@ static const struct gpadc_data sun6i_gpadc_data = {
>> .tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
>> .adc_chan_select = &sun6i_gpadc_chan_select,
>> .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
>> + .temp_data = SUN4I_GPADC_TEMP_DATA,
>> + .sample_start = sun4i_gpadc_sample_start,
>> + .sample_end = sun4i_gpadc_sample_end,
>> };
>>
>> static const struct gpadc_data sun8i_a33_gpadc_data = {
>> .temp_offset = -1662,
>> .temp_scale = 162,
>> .tp_mode_en = SUN8I_A33_GPADC_CTRL1_CHOP_TEMP_EN,
>> + .temp_data = SUN4I_GPADC_TEMP_DATA,
>> + .sample_start = sun4i_gpadc_sample_start,
>> + .sample_end = sun4i_gpadc_sample_end,
>> };
>>
>> struct sun4i_gpadc_iio {
>> @@ -277,7 +308,7 @@ static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
>> if (info->no_irq) {
>> pm_runtime_get_sync(indio_dev->dev.parent);
>>
>> - regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
>> + regmap_read(info->regmap, info->data->temp_data, val);
>>
>> pm_runtime_mark_last_busy(indio_dev->dev.parent);
>> pm_runtime_put_autosuspend(indio_dev->dev.parent);
>> @@ -382,10 +413,8 @@ static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
>> return IRQ_HANDLED;
>> }
>>
>> -static int sun4i_gpadc_runtime_suspend(struct device *dev)
>> +static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info)
>> {
>> - struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>> -
>> /* Disable the ADC on IP */
>> regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0);
>> /* Disable temperature sensor on IP */
>> @@ -394,19 +423,32 @@ static int sun4i_gpadc_runtime_suspend(struct device *dev)
>> return 0;
>> }
>>
>> -static int sun4i_gpadc_runtime_resume(struct device *dev)
>> +static int sunxi_ths_sample_end(struct sun4i_gpadc_iio *info)
>> +{
>> + /* Disable temperature sensor */
>> + regmap_write(info->regmap, SUNXI_THS_CTRL2, 0x0);
>> +
>> + return 0;
>> +}
>> +
>> +static int sun4i_gpadc_runtime_suspend(struct device *dev)
>> {
>> struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>>
>> + return info->data->sample_end(info);
>> +}
>> +
>> +static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
>> +{
>> /* clkin = 6MHz */
>> regmap_write(info->regmap, SUN4I_GPADC_CTRL0,
>> SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) |
>> SUN4I_GPADC_CTRL0_FS_DIV(7) |
>> - SUN4I_GPADC_CTRL0_T_ACQ(63));
>> + SUNXI_THS_ACQ0(63));
>> regmap_write(info->regmap, SUN4I_GPADC_CTRL1, info->data->tp_mode_en);
>> regmap_write(info->regmap, SUN4I_GPADC_CTRL3,
>> - SUN4I_GPADC_CTRL3_FILTER_EN |
>> - SUN4I_GPADC_CTRL3_FILTER_TYPE(1));
>> + SUNXI_THS_FILTER_EN |
>> + SUNXI_THS_FILTER_TYPE(1));
>
> Why if these are temperature sensor features are we setting them in the
> general purpose ADC start function?
This is part of the olds sensors (before Allwinner reworked their
temperature sensor (only ths) ). Those "old" sensors integrate an ADC
and a Thermal sensor in one ip block/register space. I don't want to
break those sensors/adc, since I'm not able to test them!
>> /* period = SUN4I_GPADC_TPR_TEMP_PERIOD * 256 * 16 / clkin; ~0.6s */
>> regmap_write(info->regmap, SUN4I_GPADC_TPR,
>> SUN4I_GPADC_TPR_TEMP_ENABLE |
>> @@ -415,6 +457,35 @@ static int sun4i_gpadc_runtime_resume(struct device *dev)
>> return 0;
>> }
>>
>> +static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info)
>> +{
>> + u32 value;
>> +
>> + if (info->data->ctrl0_map)
>> + regmap_write(info->regmap, SUNXI_THS_CTRL0,
>> + info->data->ctrl0_map);
>> +
>> + regmap_write(info->regmap, SUNXI_THS_CTRL2,
>> + info->data->ctrl2_map);
>> +
>> + regmap_write(info->regmap, SUNXI_THS_FILTER,
>> + info->data->filter_map);
>> +
>> + regmap_read(info->regmap, SUNXI_THS_CTRL2, &value);
>> +
>> + regmap_write(info->regmap, SUNXI_THS_CTRL2,
>> + info->data->sensor_en_map | value);
>> +
>> + return 0;
>> +}
>> +
>> +static int sun4i_gpadc_runtime_resume(struct device *dev)
>> +{
>> + struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>> +
>> + return info->data->sample_start(info);
>> +}
>> +
>> static int sun4i_gpadc_get_temp(void *data, int *temp)
>> {
>> struct sun4i_gpadc_iio *info = data;
>> diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
>> index 78d31984a222..39e096c3ddac 100644
>> --- a/include/linux/mfd/sun4i-gpadc.h
>> +++ b/include/linux/mfd/sun4i-gpadc.h
>> @@ -17,7 +17,6 @@
>> #define SUN4I_GPADC_CTRL0_ADC_CLK_SELECT BIT(22)
>> #define SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(x) ((GENMASK(1, 0) & (x)) << 20)
>> #define SUN4I_GPADC_CTRL0_FS_DIV(x) ((GENMASK(3, 0) & (x)) << 16)
>> -#define SUN4I_GPADC_CTRL0_T_ACQ(x) (GENMASK(15, 0) & (x))
>>
>> #define SUN4I_GPADC_CTRL1 0x04
>>
>> @@ -51,9 +50,6 @@
>>
>> #define SUN4I_GPADC_CTRL3 0x0c
>>
>> -#define SUN4I_GPADC_CTRL3_FILTER_EN BIT(2)
>> -#define SUN4I_GPADC_CTRL3_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
>> -
>> #define SUN4I_GPADC_TPR 0x18
>>
>> #define SUN4I_GPADC_TPR_TEMP_ENABLE BIT(16)
>> @@ -90,6 +86,21 @@
>> /* 10s delay before suspending the IP */
>> #define SUN4I_GPADC_AUTOSUSPEND_DELAY 10000
>>
>> +/* SUNXI_THS COMMON REGISTERS + DEFINES */
>> +#define SUNXI_THS_CTRL0 0x00
>> +#define SUNXI_THS_CTRL2 0x40
>> +#define SUNXI_THS_FILTER 0x70
>> +
>> +#define SUNXI_THS_FILTER_EN BIT(2)
>> +#define SUNXI_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
>> +#define SUNXI_THS_ACQ0(x) (GENMASK(15, 0) & (x))
>> +#define SUNXI_THS_ACQ1(x) (GENMASK(31, 16) & ((x) << 16))
>> +
>> +#define SUNXI_THS_TEMP_SENSE_EN0 BIT(0)
>> +#define SUNXI_THS_TEMP_SENSE_EN1 BIT(1)
>> +#define SUNXI_THS_TEMP_SENSE_EN2 BIT(2)
>> +#define SUNXI_THS_TEMP_SENSE_EN3 BIT(3)
>> +
>> struct sun4i_gpadc_dev {
>> struct device *dev;
>> struct regmap *regmap;
>
In general I'm ok with changing those things, but I would like to wait
for some more feedback/thoughts about this.
Thanks,
Philipp
于 2018年1月28日 GMT+08:00 下午9:34:17, Philipp Rossak <[email protected]> 写到:
>
>
>On 28.01.2018 09:43, Jonathan Cameron wrote:
>> On Fri, 26 Jan 2018 16:19:29 +0100
>> Philipp Rossak <[email protected]> wrote:
>>
>>> For adding newer sensor some basic rework of the code is necessary.
>>>
>>> This commit reworks the code and allows the sampling start/end code
>and
>>> the position of value readout register to be altered. Later the
>start/end
>>> functions will be used to configure the ths and start/stop the
>>> sampling.
>>>
>>> Signed-off-by: Icenowy Zheng <[email protected]>
>>> Signed-off-by: Philipp Rossak <[email protected]>
>> Hmm. It almost always turns out to be a bad idea to assume a
>particular
>> set of registers will be consistent across a hardware family. Usual
>convention
>> is just to prefix them with the first supported device and use them
>across
>> the variants where they are correct. I.e. don't use wild cards or
>generic
>> names as they often end up implying a wider range of applicability
>than
>> we want.
>>
>
>The new THS sensors seems to use the same THS ip core, only configured
>with different amount of sensors. So for H3, A83T, H5, A80 and A64 the
>registers are all the same.
>
>I think the big question is, do we want to have a few very big patches.
>
>Or more small patches with code that is used in later patches.
Allwinner H6 has changed the THS core again.
Please consider this :-)
>
>> A few other comments inline. Reworking code to make it ready for
>later
>> patches is fine, but this also adds a lot of new code which isn't
>used until
>> much later in the series. Move it there...
>
>please see comments below.
>
>>> ---
>>> drivers/iio/adc/sun4i-gpadc-iio.c | 87
>+++++++++++++++++++++++++++++++++++----
>>> include/linux/mfd/sun4i-gpadc.h | 19 +++++++--
>>> 2 files changed, 94 insertions(+), 12 deletions(-)
>>>
>>> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c
>b/drivers/iio/adc/sun4i-gpadc-iio.c
>>> index 03804ff9c006..363936b37c5a 100644
>>> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
>>> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
>>> @@ -49,6 +49,18 @@ static unsigned int
>sun6i_gpadc_chan_select(unsigned int chan)
>>> return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
>>> }
>>>
>>> +struct sun4i_gpadc_iio;
>>> +
>>> +/*
>>> + * Prototypes for these functions, which enable these functions to
>be
>>> + * referenced in gpadc_data structures.
>>> + */
>>> +static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info);
>>> +static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info);
>>> +
>>> +static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info);
>>
>> Superficially this appears to be introduced but not used... Patch 9
>> is first to use it I think? Introduce it then rather than here.
>>
>
>You are right, this function is used first in Patch 9. In an very early
>
>version, I had this together with Patch 9. But then I had the feeling
>that everything got too messy... Right now this is a very big patch
>series and I wanted to have a clear structure in it and easy to review.
>
>If I would rework this it will end up in squashing, Patch 4, Parts of
>Patch 7, Patch 8 and Patch 9.
>
>>> +static int sunxi_ths_sample_end(struct sun4i_gpadc_iio *info);
>>> +
>>> struct gpadc_data {
>>> int temp_offset;
>>> int temp_scale;
>>> @@ -56,6 +68,13 @@ struct gpadc_data {
>>> unsigned int tp_adc_select;
>>> unsigned int (*adc_chan_select)(unsigned int chan);
>>> unsigned int adc_chan_mask;
>>> + unsigned int temp_data;
>>> + int (*sample_start)(struct sun4i_gpadc_iio *info);
>>> + int (*sample_end)(struct sun4i_gpadc_iio *info);
>>> + u32 ctrl0_map;
>>> + u32 ctrl2_map;
>>> + u32 sensor_en_map;
>>> + u32 filter_map;
>>> };
>>>
>>> static const struct gpadc_data sun4i_gpadc_data = {
>>> @@ -65,6 +84,9 @@ static const struct gpadc_data sun4i_gpadc_data =
>{
>>> .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
>>> .adc_chan_select = &sun4i_gpadc_chan_select,
>>> .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
>>> + .temp_data = SUN4I_GPADC_TEMP_DATA,
>>> + .sample_start = sun4i_gpadc_sample_start,
>>> + .sample_end = sun4i_gpadc_sample_end,
>>> };
>>>
>>> static const struct gpadc_data sun5i_gpadc_data = {
>>> @@ -74,6 +96,9 @@ static const struct gpadc_data sun5i_gpadc_data =
>{
>>> .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
>>> .adc_chan_select = &sun4i_gpadc_chan_select,
>>> .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
>>> + .temp_data = SUN4I_GPADC_TEMP_DATA,
>>> + .sample_start = sun4i_gpadc_sample_start,
>>> + .sample_end = sun4i_gpadc_sample_end,
>>> };
>>>
>>> static const struct gpadc_data sun6i_gpadc_data = {
>>> @@ -83,12 +108,18 @@ static const struct gpadc_data sun6i_gpadc_data
>= {
>>> .tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
>>> .adc_chan_select = &sun6i_gpadc_chan_select,
>>> .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
>>> + .temp_data = SUN4I_GPADC_TEMP_DATA,
>>> + .sample_start = sun4i_gpadc_sample_start,
>>> + .sample_end = sun4i_gpadc_sample_end,
>>> };
>>>
>>> static const struct gpadc_data sun8i_a33_gpadc_data = {
>>> .temp_offset = -1662,
>>> .temp_scale = 162,
>>> .tp_mode_en = SUN8I_A33_GPADC_CTRL1_CHOP_TEMP_EN,
>>> + .temp_data = SUN4I_GPADC_TEMP_DATA,
>>> + .sample_start = sun4i_gpadc_sample_start,
>>> + .sample_end = sun4i_gpadc_sample_end,
>>> };
>>>
>>> struct sun4i_gpadc_iio {
>>> @@ -277,7 +308,7 @@ static int sun4i_gpadc_temp_read(struct iio_dev
>*indio_dev, int *val)
>>> if (info->no_irq) {
>>> pm_runtime_get_sync(indio_dev->dev.parent);
>>>
>>> - regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
>>> + regmap_read(info->regmap, info->data->temp_data, val);
>>>
>>> pm_runtime_mark_last_busy(indio_dev->dev.parent);
>>> pm_runtime_put_autosuspend(indio_dev->dev.parent);
>>> @@ -382,10 +413,8 @@ static irqreturn_t
>sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
>>> return IRQ_HANDLED;
>>> }
>>>
>>> -static int sun4i_gpadc_runtime_suspend(struct device *dev)
>>> +static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info)
>>> {
>>> - struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>>> -
>>> /* Disable the ADC on IP */
>>> regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0);
>>> /* Disable temperature sensor on IP */
>>> @@ -394,19 +423,32 @@ static int sun4i_gpadc_runtime_suspend(struct
>device *dev)
>>> return 0;
>>> }
>>>
>>> -static int sun4i_gpadc_runtime_resume(struct device *dev)
>>> +static int sunxi_ths_sample_end(struct sun4i_gpadc_iio *info)
>>> +{
>>> + /* Disable temperature sensor */
>>> + regmap_write(info->regmap, SUNXI_THS_CTRL2, 0x0);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int sun4i_gpadc_runtime_suspend(struct device *dev)
>>> {
>>> struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>>>
>>> + return info->data->sample_end(info);
>>> +}
>>> +
>>> +static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
>>> +{
>>> /* clkin = 6MHz */
>>> regmap_write(info->regmap, SUN4I_GPADC_CTRL0,
>>> SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) |
>>> SUN4I_GPADC_CTRL0_FS_DIV(7) |
>>> - SUN4I_GPADC_CTRL0_T_ACQ(63));
>>> + SUNXI_THS_ACQ0(63));
>>> regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
>info->data->tp_mode_en);
>>> regmap_write(info->regmap, SUN4I_GPADC_CTRL3,
>>> - SUN4I_GPADC_CTRL3_FILTER_EN |
>>> - SUN4I_GPADC_CTRL3_FILTER_TYPE(1));
>>> + SUNXI_THS_FILTER_EN |
>>> + SUNXI_THS_FILTER_TYPE(1));
>>
>> Why if these are temperature sensor features are we setting them in
>the
>> general purpose ADC start function?
>
>This is part of the olds sensors (before Allwinner reworked their
>temperature sensor (only ths) ). Those "old" sensors integrate an ADC
>and a Thermal sensor in one ip block/register space. I don't want to
>break those sensors/adc, since I'm not able to test them!
>
>>> /* period = SUN4I_GPADC_TPR_TEMP_PERIOD * 256 * 16 / clkin; ~0.6s
>*/
>>> regmap_write(info->regmap, SUN4I_GPADC_TPR,
>>> SUN4I_GPADC_TPR_TEMP_ENABLE |
>>> @@ -415,6 +457,35 @@ static int sun4i_gpadc_runtime_resume(struct
>device *dev)
>>> return 0;
>>> }
>>>
>>> +static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info)
>>> +{
>>> + u32 value;
>>> +
>>> + if (info->data->ctrl0_map)
>>> + regmap_write(info->regmap, SUNXI_THS_CTRL0,
>>> + info->data->ctrl0_map);
>>> +
>>> + regmap_write(info->regmap, SUNXI_THS_CTRL2,
>>> + info->data->ctrl2_map);
>>> +
>>> + regmap_write(info->regmap, SUNXI_THS_FILTER,
>>> + info->data->filter_map);
>>> +
>>> + regmap_read(info->regmap, SUNXI_THS_CTRL2, &value);
>>> +
>>> + regmap_write(info->regmap, SUNXI_THS_CTRL2,
>>> + info->data->sensor_en_map | value);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int sun4i_gpadc_runtime_resume(struct device *dev)
>>> +{
>>> + struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>>> +
>>> + return info->data->sample_start(info);
>>> +}
>>> +
>>> static int sun4i_gpadc_get_temp(void *data, int *temp)
>>> {
>>> struct sun4i_gpadc_iio *info = data;
>>> diff --git a/include/linux/mfd/sun4i-gpadc.h
>b/include/linux/mfd/sun4i-gpadc.h
>>> index 78d31984a222..39e096c3ddac 100644
>>> --- a/include/linux/mfd/sun4i-gpadc.h
>>> +++ b/include/linux/mfd/sun4i-gpadc.h
>>> @@ -17,7 +17,6 @@
>>> #define SUN4I_GPADC_CTRL0_ADC_CLK_SELECT BIT(22)
>>> #define SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(x) ((GENMASK(1, 0) &
>(x)) << 20)
>>> #define SUN4I_GPADC_CTRL0_FS_DIV(x) ((GENMASK(3, 0) & (x)) <<
>16)
>>> -#define SUN4I_GPADC_CTRL0_T_ACQ(x) (GENMASK(15, 0) & (x))
>>>
>>> #define SUN4I_GPADC_CTRL1 0x04
>>>
>>> @@ -51,9 +50,6 @@
>>>
>>> #define SUN4I_GPADC_CTRL3 0x0c
>>>
>>> -#define SUN4I_GPADC_CTRL3_FILTER_EN BIT(2)
>>> -#define SUN4I_GPADC_CTRL3_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
>>> -
>>> #define SUN4I_GPADC_TPR 0x18
>>>
>>> #define SUN4I_GPADC_TPR_TEMP_ENABLE BIT(16)
>>> @@ -90,6 +86,21 @@
>>> /* 10s delay before suspending the IP */
>>> #define SUN4I_GPADC_AUTOSUSPEND_DELAY 10000
>>>
>>> +/* SUNXI_THS COMMON REGISTERS + DEFINES */
>>> +#define SUNXI_THS_CTRL0 0x00
>>> +#define SUNXI_THS_CTRL2 0x40
>>> +#define SUNXI_THS_FILTER 0x70
>>> +
>>> +#define SUNXI_THS_FILTER_EN BIT(2)
>>> +#define SUNXI_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
>>> +#define SUNXI_THS_ACQ0(x) (GENMASK(15, 0) & (x))
>>> +#define SUNXI_THS_ACQ1(x) (GENMASK(31, 16) & ((x) << 16))
>>> +
>>> +#define SUNXI_THS_TEMP_SENSE_EN0 BIT(0)
>>> +#define SUNXI_THS_TEMP_SENSE_EN1 BIT(1)
>>> +#define SUNXI_THS_TEMP_SENSE_EN2 BIT(2)
>>> +#define SUNXI_THS_TEMP_SENSE_EN3 BIT(3)
>>> +
>>> struct sun4i_gpadc_dev {
>>> struct device *dev;
>>> struct regmap *regmap;
>>
>
>In general I'm ok with changing those things, but I would like to wait
>for some more feedback/thoughts about this.
>
>Thanks,
>Philipp
>
>_______________________________________________
>linux-arm-kernel mailing list
>[email protected]
>http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On 28.01.2018 10:02, Jonathan Cameron wrote:
> On Fri, 26 Jan 2018 16:19:32 +0100
> Philipp Rossak <[email protected]> wrote:
>
>> This patch reworks the driver to support nvmem calibration cells.
>> The driver checks if the nvmem calibration is supported and reads out
>> the nvmem. At the beginning of the startup process the calibration data
>> is written to the related registers.
>>
>> Signed-off-by: Philipp Rossak <[email protected]>
>
> A few minor suggestions inline.
>
> Jonathan
>
>> ---
>> drivers/iio/adc/sun4i-gpadc-iio.c | 52 +++++++++++++++++++++++++++++++++++++++
>> include/linux/mfd/sun4i-gpadc.h | 2 ++
>> 2 files changed, 54 insertions(+)
>>
>> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
>> index bff06f2798e8..7b12666cdd9e 100644
>> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
>> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
>> @@ -27,6 +27,7 @@
>> #include <linux/interrupt.h>
>> #include <linux/io.h>
>> #include <linux/module.h>
>> +#include <linux/nvmem-consumer.h>
>> #include <linux/of.h>
>> #include <linux/of_device.h>
>> #include <linux/platform_device.h>
>> @@ -81,6 +82,7 @@ struct gpadc_data {
>> bool has_bus_rst;
>> bool has_mod_clk;
>> int sensor_count;
>> + bool supports_nvmem;
>> };
>>
>> static const struct gpadc_data sun4i_gpadc_data = {
>> @@ -94,6 +96,7 @@ static const struct gpadc_data sun4i_gpadc_data = {
>> .sample_start = sun4i_gpadc_sample_start,
>> .sample_end = sun4i_gpadc_sample_end,
>> .sensor_count = 1,
>> + .supports_nvmem = false,
>> };
>>
>> static const struct gpadc_data sun5i_gpadc_data = {
>> @@ -107,6 +110,7 @@ static const struct gpadc_data sun5i_gpadc_data = {
>> .sample_start = sun4i_gpadc_sample_start,
>> .sample_end = sun4i_gpadc_sample_end,
>> .sensor_count = 1,
>> + .supports_nvmem = false,
>> };
>>
>> static const struct gpadc_data sun6i_gpadc_data = {
>> @@ -120,6 +124,7 @@ static const struct gpadc_data sun6i_gpadc_data = {
>> .sample_start = sun4i_gpadc_sample_start,
>> .sample_end = sun4i_gpadc_sample_end,
>> .sensor_count = 1,
>> + .supports_nvmem = false,
>> };
>>
>> static const struct gpadc_data sun8i_a33_gpadc_data = {
>> @@ -130,6 +135,7 @@ static const struct gpadc_data sun8i_a33_gpadc_data = {
>> .sample_start = sun4i_gpadc_sample_start,
>> .sample_end = sun4i_gpadc_sample_end,
>> .sensor_count = 1,
>> + .supports_nvmem = false,
>> };
>>
>> struct sun4i_gpadc_iio {
>> @@ -148,6 +154,8 @@ struct sun4i_gpadc_iio {
>> struct clk *mod_clk;
>> struct reset_control *reset;
>> int sensor_id;
>> + u32 calibration_data[2];
>> + bool has_calibration_data[2];
>> /* prevents concurrent reads of temperature and ADC */
>> struct mutex mutex;
>> struct thermal_zone_device *tzd;
>> @@ -459,6 +467,17 @@ static int sun4i_gpadc_runtime_suspend(struct device *dev)
>> return info->data->sample_end(info);
>> }
>>
>> +static void sunxi_calibrate(struct sun4i_gpadc_iio *info)
>> +{
>> + if (info->has_calibration_data[0])
>> + regmap_write(info->regmap, SUNXI_THS_CDATA_0_1,
>> + info->calibration_data[0]);
>> +
>> + if (info->has_calibration_data[1])
>> + regmap_write(info->regmap, SUNXI_THS_CDATA_2_3,
>> + info->calibration_data[1]);
>> +}
>> +
>> static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
>> {
>> /* clkin = 6MHz */
>> @@ -481,6 +500,7 @@ static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
>> static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info)
>> {
>> u32 value;
>> + sunxi_calibrate(info);
>>
>> if (info->data->ctrl0_map)
>> regmap_write(info->regmap, SUNXI_THS_CTRL0,
>> @@ -602,6 +622,9 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
>> struct resource *mem;
>> void __iomem *base;
>> int ret;
>> + struct nvmem_cell *cell;
>> + ssize_t cell_size;
>> + u64 *cell_data;
>>
>> info->data = of_device_get_match_data(&pdev->dev);
>> if (!info->data)
>> @@ -616,6 +639,35 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
>> if (IS_ERR(base))
>> return PTR_ERR(base);
>>
>> + info->has_calibration_data[0] = false;
>> + info->has_calibration_data[1] = false;
>> +
>> + if (!info->data->supports_nvmem)
>> + goto no_nvmem;
>> +
>> + cell = devm_nvmem_cell_get(&pdev->dev, "calibration");
>> + if (IS_ERR(cell)) {
>> + if (PTR_ERR(cell) == -EPROBE_DEFER)
>> + return PTR_ERR(cell);
> Use a goto here to no_nvmem.
>
> Then you can drop the below else to make things more readable.
>> + } else {
>> + cell_data = (u64 *)nvmem_cell_read(cell, &cell_size);
>> + devm_nvmem_cell_put(&pdev->dev, cell);
>
> I'm really not keen on use of devm for things we are intending
> to drop almost immediately. Just use the non managed versions
> and clean up properly in the error paths (if there are any
> where it is needed - which there aren't that I can see)
>
^^ Ok, I will rework that.
>> + if (cell_size <= 4) {
> Is it valid if it is anything other than 4?
For sensors with only one sensor would be also a 2 valid, for those with
2 sensors ==> 4, with 3 sensors ==> 6 and with and with 4 sensors ==> 8.
In hardware we have in total to 32bit registers for calibration, thus I
thought it would be a good idea to use always four bytes per register.
If two bytes are used, they should be the default value.
But I agree, this needs a rework.
>> + info->has_calibration_data[0] = true;
>> + info->calibration_data[0] = be32_to_cpu(cell_data[0] &
>> + GENMASK(31, 0));
>
> Masking isn't needed, If you want to be paranoid there is the lower_32_bits
> function..
>
^^ Ok, I will rework that.
>> + } else if (cell_size <= 8) {
>> + info->has_calibration_data[0] = true;
>> + info->calibration_data[0] = be32_to_cpu(cell_data[0] &
>> + GENMASK(31, 0));
>
> This first block is repeated. Easy enough to avoid I think...
>
^^ Ok, I will rework that.
>> + info->has_calibration_data[1] = true;
>> + info->calibration_data[1] = be32_to_cpu(
>> + (cell_data[0] >> 32) & GENMASK(31, 0));
>> + }
>> + }
>> +
>> +no_nvmem:
>> +
>> info->regmap = devm_regmap_init_mmio(&pdev->dev, base,
>> &sun4i_gpadc_regmap_config);
>> if (IS_ERR(info->regmap)) {
>> diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
>> index 40b4dd9d2405..c251002431bd 100644
>> --- a/include/linux/mfd/sun4i-gpadc.h
>> +++ b/include/linux/mfd/sun4i-gpadc.h
>> @@ -90,6 +90,8 @@
>> #define SUNXI_THS_CTRL0 0x00
>> #define SUNXI_THS_CTRL2 0x40
>> #define SUNXI_THS_FILTER 0x70
>> +#define SUNXI_THS_CDATA_0_1 0x74
>> +#define SUNXI_THS_CDATA_2_3 0x78
>> #define SUNXI_THS_TDATA0 0x80
>> #define SUNXI_THS_TDATA1 0x84
>> #define SUNXI_THS_TDATA2 0x88
>
Thanks,
Philipp
On 28.01.2018 10:06, Jonathan Cameron wrote:
> On Fri, 26 Jan 2018 16:19:33 +0100
> Philipp Rossak <[email protected]> wrote:
>
>> This patch rewors the driver to support interrupts for the thermal part
>> of the sensor.
>>
>> This is only available for the newer sensor (currently H3 and A83T).
>> The interrupt will be trigerd on data available and triggers the update
>> for the thermal sensors. All newer sensors have different amount of
>> sensors and different interrupts for each device the reset of the
>> interrupts need to be done different
>>
>> For the newer sensors is the autosuspend disabled.
>>
>> Signed-off-by: Philipp Rossak <[email protected]>
> Really minor point inline, otherwise this looks fine to me.
>
> Acked-by: Jonathan Cameron <[email protected]>
>> ---
>> drivers/iio/adc/sun4i-gpadc-iio.c | 68 +++++++++++++++++++++++++++++++++++----
>> include/linux/mfd/sun4i-gpadc.h | 33 +++++++++++++++++++
>> 2 files changed, 95 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
>> index 7b12666cdd9e..77e07f042730 100644
>> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
>> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
>> @@ -78,11 +78,14 @@ struct gpadc_data {
>> u32 ctrl2_map;
>> u32 sensor_en_map;
>> u32 filter_map;
>> + u32 irq_clear_map;
>> + u32 irq_control_map;
>> bool has_bus_clk;
>> bool has_bus_rst;
>> bool has_mod_clk;
>> int sensor_count;
>> bool supports_nvmem;
>> + bool support_irq;
>> };
>>
>> static const struct gpadc_data sun4i_gpadc_data = {
>> @@ -97,6 +100,7 @@ static const struct gpadc_data sun4i_gpadc_data = {
>> .sample_end = sun4i_gpadc_sample_end,
>> .sensor_count = 1,
>> .supports_nvmem = false,
>> + .support_irq = false,
>> };
>>
>> static const struct gpadc_data sun5i_gpadc_data = {
>> @@ -111,6 +115,7 @@ static const struct gpadc_data sun5i_gpadc_data = {
>> .sample_end = sun4i_gpadc_sample_end,
>> .sensor_count = 1,
>> .supports_nvmem = false,
>> + .support_irq = false,
>> };
>>
>> static const struct gpadc_data sun6i_gpadc_data = {
>> @@ -125,6 +130,7 @@ static const struct gpadc_data sun6i_gpadc_data = {
>> .sample_end = sun4i_gpadc_sample_end,
>> .sensor_count = 1,
>> .supports_nvmem = false,
>> + .support_irq = false,
>> };
>>
>> static const struct gpadc_data sun8i_a33_gpadc_data = {
>> @@ -136,6 +142,7 @@ static const struct gpadc_data sun8i_a33_gpadc_data = {
>> .sample_end = sun4i_gpadc_sample_end,
>> .sensor_count = 1,
>> .supports_nvmem = false,
>> + .support_irq = false,
>> };
>>
>> struct sun4i_gpadc_iio {
>> @@ -339,6 +346,11 @@ static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val,
>> return 0;
>> }
>>
>> + if (info->data->support_irq) {
>> + regmap_read(info->regmap, info->data->temp_data[sensor], val);
>> + return 0;
>> + }
>> +
>> return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
>> }
>>
>> @@ -436,6 +448,17 @@ static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
>> return IRQ_HANDLED;
>> }
>>
>> +static irqreturn_t sunxi_irq_thread(int irq, void *data)
>> +{
>> + struct sun4i_gpadc_iio *info = data;
>> +
>> + regmap_write(info->regmap, SUNXI_THS_STAT, info->data->irq_clear_map);
>> +
>> + thermal_zone_device_update(info->tzd, THERMAL_EVENT_TEMP_SAMPLE);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info)
>> {
>> /* Disable the ADC on IP */
>> @@ -448,6 +471,8 @@ static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info)
>>
>> static int sunxi_ths_sample_end(struct sun4i_gpadc_iio *info)
>> {
>> + /* Disable ths interrupt*/
>
> Space before */
>
^^ Ok, I will rework that.
>> + regmap_write(info->regmap, SUNXI_THS_INTC, 0x0);
>> /* Disable temperature sensor */
>> regmap_write(info->regmap, SUNXI_THS_CTRL2, 0x0);
>>
>> @@ -509,9 +534,15 @@ static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info)
>> regmap_write(info->regmap, SUNXI_THS_CTRL2,
>> info->data->ctrl2_map);
>>
>> + regmap_write(info->regmap, SUNXI_THS_STAT,
>> + info->data->irq_clear_map);
>> +
>> regmap_write(info->regmap, SUNXI_THS_FILTER,
>> info->data->filter_map);
>>
>> + regmap_write(info->regmap, SUNXI_THS_INTC,
>> + info->data->irq_control_map);
>> +
>> regmap_read(info->regmap, SUNXI_THS_CTRL2, &value);
>>
>> regmap_write(info->regmap, SUNXI_THS_CTRL2,
>> @@ -625,12 +656,29 @@ static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
>> struct nvmem_cell *cell;
>> ssize_t cell_size;
>> u64 *cell_data;
>> + int irq;
>>
>> info->data = of_device_get_match_data(&pdev->dev);
>> if (!info->data)
>> return -ENODEV;
>>
>> - info->no_irq = true;
>> + if (info->data->support_irq) {
>> + /* only the new versions of ths support right now irqs */
>> + irq = platform_get_irq(pdev, 0);
>> + if (irq < 0) {
>> + dev_err(&pdev->dev, "failed to get IRQ: %d\n", irq);
>> + return irq;
>> + }
>> +
>> + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
>> + sunxi_irq_thread, IRQF_ONESHOT,
>> + dev_name(&pdev->dev), info);
>> + if (ret)
>> + return ret;
>> +
>> + } else
>> + info->no_irq = true;
>> +
>> indio_dev->num_channels = ARRAY_SIZE(sun8i_a33_gpadc_channels);
>> indio_dev->channels = sun8i_a33_gpadc_channels;
>>
>> @@ -840,11 +888,13 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
>> if (ret)
>> return ret;
>>
>> - pm_runtime_set_autosuspend_delay(&pdev->dev,
>> - SUN4I_GPADC_AUTOSUSPEND_DELAY);
>> - pm_runtime_use_autosuspend(&pdev->dev);
>> - pm_runtime_set_suspended(&pdev->dev);
>> - pm_runtime_enable(&pdev->dev);
>> + if (!info->data->support_irq) {
>> + pm_runtime_set_autosuspend_delay(&pdev->dev,
>> + SUN4I_GPADC_AUTOSUSPEND_DELAY);
>> + pm_runtime_use_autosuspend(&pdev->dev);
>> + pm_runtime_set_suspended(&pdev->dev);
>> + pm_runtime_enable(&pdev->dev);
>> + }
>>
>> if (IS_ENABLED(CONFIG_THERMAL_OF)) {
>> for (i = 0; i < info->data->sensor_count; i++) {
>> @@ -865,6 +915,9 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
>> }
>> }
>>
>> + if (info->data->support_irq)
>> + info->data->sample_start(info);
>> +
>> ret = devm_iio_device_register(&pdev->dev, indio_dev);
>> if (ret < 0) {
>> dev_err(&pdev->dev, "could not register the device\n");
>> @@ -894,6 +947,9 @@ static int sun4i_gpadc_remove(struct platform_device *pdev)
>> if (!IS_ENABLED(CONFIG_THERMAL_OF))
>> return 0;
>>
>> + if (info->data->support_irq)
>> + info->data->sample_end(info);
>> +
>> thermal_zone_of_sensor_unregister(info->sensor_device, info->tzd);
>>
>> if (!info->no_irq)
>> diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
>> index c251002431bd..ab34a96a7ff3 100644
>> --- a/include/linux/mfd/sun4i-gpadc.h
>> +++ b/include/linux/mfd/sun4i-gpadc.h
>> @@ -89,6 +89,8 @@
>> /* SUNXI_THS COMMON REGISTERS + DEFINES */
>> #define SUNXI_THS_CTRL0 0x00
>> #define SUNXI_THS_CTRL2 0x40
>> +#define SUNXI_THS_INTC 0x44
>> +#define SUNXI_THS_STAT 0x48
>> #define SUNXI_THS_FILTER 0x70
>> #define SUNXI_THS_CDATA_0_1 0x74
>> #define SUNXI_THS_CDATA_2_3 0x78
>> @@ -107,6 +109,37 @@
>> #define SUNXI_THS_TEMP_SENSE_EN2 BIT(2)
>> #define SUNXI_THS_TEMP_SENSE_EN3 BIT(3)
>>
>> +#define SUNXI_THS_TEMP_PERIOD(x) (GENMASK(31, 12) & ((x) << 12))
>> +
>> +#define SUNXI_THS_INTS_ALARM_OFF_2 BIT(14)
>> +#define SUNXI_THS_INTS_ALARM_OFF_1 BIT(13)
>> +#define SUNXI_THS_INTS_ALARM_OFF_0 BIT(12)
>> +#define SUNXI_THS_INTS_TDATA_IRQ_3 BIT(11)
>> +#define SUNXI_THS_INTS_TDATA_IRQ_2 BIT(10)
>> +#define SUNXI_THS_INTS_TDATA_IRQ_1 BIT(9)
>> +#define SUNXI_THS_INTS_TDATA_IRQ_0 BIT(8)
>> +#define SUNXI_THS_INTS_SHUT_INT_3 BIT(7)
>> +#define SUNXI_THS_INTS_SHUT_INT_2 BIT(6)
>> +#define SUNXI_THS_INTS_SHUT_INT_1 BIT(5)
>> +#define SUNXI_THS_INTS_SHUT_INT_0 BIT(4)
>> +#define SUNXI_THS_INTS_ALARM_INT_3 BIT(3)
>> +#define SUNXI_THS_INTS_ALARM_INT_2 BIT(2)
>> +#define SUNXI_THS_INTS_ALARM_INT_1 BIT(1)
>> +#define SUNXI_THS_INTS_ALARM_INT_0 BIT(0)
>> +
>> +#define SUNXI_THS_INTC_TDATA_IRQ_EN3 BIT(11)
>> +#define SUNXI_THS_INTC_TDATA_IRQ_EN2 BIT(10)
>> +#define SUNXI_THS_INTC_TDATA_IRQ_EN1 BIT(9)
>> +#define SUNXI_THS_INTC_TDATA_IRQ_EN0 BIT(8)
>> +#define SUNXI_THS_INTC_SHUT_INT_EN3 BIT(7)
>> +#define SUNXI_THS_INTC_SHUT_INT_EN2 BIT(6)
>> +#define SUNXI_THS_INTC_SHUT_INT_EN1 BIT(5)
>> +#define SUNXI_THS_INTC_SHUT_INT_EN0 BIT(4)
>> +#define SUNXI_THS_INTC_ALARM_INT_EN3 BIT(3)
>> +#define SUNXI_THS_INTC_ALARM_INT_EN2 BIT(2)
>> +#define SUNXI_THS_INTC_ALARM_INT_EN1 BIT(1)
>> +#define SUNXI_THS_INTC_ALARM_INT_EN0 BIT(0)
>> +
>> #define MAX_SENSOR_COUNT 4
>>
>> struct sun4i_gpadc_dev {
>
Thanks,
Philipp
于 2018年1月28日 GMT+08:00 下午9:46:18, Philipp Rossak <[email protected]> 写到:
>
>
>On 28.01.2018 10:02, Jonathan Cameron wrote:
>> On Fri, 26 Jan 2018 16:19:32 +0100
>> Philipp Rossak <[email protected]> wrote:
>>
>>> This patch reworks the driver to support nvmem calibration cells.
>>> The driver checks if the nvmem calibration is supported and reads
>out
>>> the nvmem. At the beginning of the startup process the calibration
>data
>>> is written to the related registers.
>>>
>>> Signed-off-by: Philipp Rossak <[email protected]>
>>
>> A few minor suggestions inline.
>>
>> Jonathan
>>
>>> ---
>>> drivers/iio/adc/sun4i-gpadc-iio.c | 52
>+++++++++++++++++++++++++++++++++++++++
>>> include/linux/mfd/sun4i-gpadc.h | 2 ++
>>> 2 files changed, 54 insertions(+)
>>>
>>> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c
>b/drivers/iio/adc/sun4i-gpadc-iio.c
>>> index bff06f2798e8..7b12666cdd9e 100644
>>> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
>>> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
>>> @@ -27,6 +27,7 @@
>>> #include <linux/interrupt.h>
>>> #include <linux/io.h>
>>> #include <linux/module.h>
>>> +#include <linux/nvmem-consumer.h>
>>> #include <linux/of.h>
>>> #include <linux/of_device.h>
>>> #include <linux/platform_device.h>
>>> @@ -81,6 +82,7 @@ struct gpadc_data {
>>> bool has_bus_rst;
>>> bool has_mod_clk;
>>> int sensor_count;
>>> + bool supports_nvmem;
>>> };
>>>
>>> static const struct gpadc_data sun4i_gpadc_data = {
>>> @@ -94,6 +96,7 @@ static const struct gpadc_data sun4i_gpadc_data =
>{
>>> .sample_start = sun4i_gpadc_sample_start,
>>> .sample_end = sun4i_gpadc_sample_end,
>>> .sensor_count = 1,
>>> + .supports_nvmem = false,
>>> };
>>>
>>> static const struct gpadc_data sun5i_gpadc_data = {
>>> @@ -107,6 +110,7 @@ static const struct gpadc_data sun5i_gpadc_data
>= {
>>> .sample_start = sun4i_gpadc_sample_start,
>>> .sample_end = sun4i_gpadc_sample_end,
>>> .sensor_count = 1,
>>> + .supports_nvmem = false,
>>> };
>>>
>>> static const struct gpadc_data sun6i_gpadc_data = {
>>> @@ -120,6 +124,7 @@ static const struct gpadc_data sun6i_gpadc_data
>= {
>>> .sample_start = sun4i_gpadc_sample_start,
>>> .sample_end = sun4i_gpadc_sample_end,
>>> .sensor_count = 1,
>>> + .supports_nvmem = false,
>>> };
>>>
>>> static const struct gpadc_data sun8i_a33_gpadc_data = {
>>> @@ -130,6 +135,7 @@ static const struct gpadc_data
>sun8i_a33_gpadc_data = {
>>> .sample_start = sun4i_gpadc_sample_start,
>>> .sample_end = sun4i_gpadc_sample_end,
>>> .sensor_count = 1,
>>> + .supports_nvmem = false,
BTW A33 claims to support calibration data according to the manual.
>>> };
>>>
>>> struct sun4i_gpadc_iio {
>>> @@ -148,6 +154,8 @@ struct sun4i_gpadc_iio {
>>> struct clk *mod_clk;
>>> struct reset_control *reset;
>>> int sensor_id;
>>> + u32 calibration_data[2];
>>> + bool has_calibration_data[2];
>>> /* prevents concurrent reads of temperature and ADC */
>>> struct mutex mutex;
>>> struct thermal_zone_device *tzd;
>>> @@ -459,6 +467,17 @@ static int sun4i_gpadc_runtime_suspend(struct
>device *dev)
>>> return info->data->sample_end(info);
>>> }
>>>
>>> +static void sunxi_calibrate(struct sun4i_gpadc_iio *info)
>>> +{
>>> + if (info->has_calibration_data[0])
>>> + regmap_write(info->regmap, SUNXI_THS_CDATA_0_1,
>>> + info->calibration_data[0]);
>>> +
>>> + if (info->has_calibration_data[1])
>>> + regmap_write(info->regmap, SUNXI_THS_CDATA_2_3,
>>> + info->calibration_data[1]);
>>> +}
>>> +
>>> static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
>>> {
>>> /* clkin = 6MHz */
>>> @@ -481,6 +500,7 @@ static int sun4i_gpadc_sample_start(struct
>sun4i_gpadc_iio *info)
>>> static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info)
>>> {
>>> u32 value;
>>> + sunxi_calibrate(info);
>>>
>>> if (info->data->ctrl0_map)
>>> regmap_write(info->regmap, SUNXI_THS_CTRL0,
>>> @@ -602,6 +622,9 @@ static int sun4i_gpadc_probe_dt(struct
>platform_device *pdev,
>>> struct resource *mem;
>>> void __iomem *base;
>>> int ret;
>>> + struct nvmem_cell *cell;
>>> + ssize_t cell_size;
>>> + u64 *cell_data;
>>>
>>> info->data = of_device_get_match_data(&pdev->dev);
>>> if (!info->data)
>>> @@ -616,6 +639,35 @@ static int sun4i_gpadc_probe_dt(struct
>platform_device *pdev,
>>> if (IS_ERR(base))
>>> return PTR_ERR(base);
>>>
>>> + info->has_calibration_data[0] = false;
>>> + info->has_calibration_data[1] = false;
>>> +
>>> + if (!info->data->supports_nvmem)
>>> + goto no_nvmem;
>>> +
>>> + cell = devm_nvmem_cell_get(&pdev->dev, "calibration");
>>> + if (IS_ERR(cell)) {
>>> + if (PTR_ERR(cell) == -EPROBE_DEFER)
>>> + return PTR_ERR(cell);
>> Use a goto here to no_nvmem.
>>
>> Then you can drop the below else to make things more readable.
>>> + } else {
>>> + cell_data = (u64 *)nvmem_cell_read(cell, &cell_size);
>>> + devm_nvmem_cell_put(&pdev->dev, cell);
>>
>> I'm really not keen on use of devm for things we are intending
>> to drop almost immediately. Just use the non managed versions
>> and clean up properly in the error paths (if there are any
>> where it is needed - which there aren't that I can see)
>>
>
>^^ Ok, I will rework that.
>
>>> + if (cell_size <= 4) {
>> Is it valid if it is anything other than 4?
>
>For sensors with only one sensor would be also a 2 valid, for those
>with
>2 sensors ==> 4, with 3 sensors ==> 6 and with and with 4 sensors ==>
>8.
>
>In hardware we have in total to 32bit registers for calibration, thus I
>
>thought it would be a good idea to use always four bytes per register.
>If two bytes are used, they should be the default value.
>
>But I agree, this needs a rework.
>
>>> + info->has_calibration_data[0] = true;
>>> + info->calibration_data[0] = be32_to_cpu(cell_data[0] &
>>> + GENMASK(31, 0));
>>
>> Masking isn't needed, If you want to be paranoid there is the
>lower_32_bits
>> function..
>>
>^^ Ok, I will rework that.
>>> + } else if (cell_size <= 8) {
>>> + info->has_calibration_data[0] = true;
>>> + info->calibration_data[0] = be32_to_cpu(cell_data[0] &
>>> + GENMASK(31, 0));
>>
>> This first block is repeated. Easy enough to avoid I think...
>>
>
>^^ Ok, I will rework that.
>
>>> + info->has_calibration_data[1] = true;
>>> + info->calibration_data[1] = be32_to_cpu(
>>> + (cell_data[0] >> 32) & GENMASK(31, 0));
>>> + }
>>> + }
>>> +
>>> +no_nvmem:
>>> +
>>> info->regmap = devm_regmap_init_mmio(&pdev->dev, base,
>>> &sun4i_gpadc_regmap_config);
>>> if (IS_ERR(info->regmap)) {
>>> diff --git a/include/linux/mfd/sun4i-gpadc.h
>b/include/linux/mfd/sun4i-gpadc.h
>>> index 40b4dd9d2405..c251002431bd 100644
>>> --- a/include/linux/mfd/sun4i-gpadc.h
>>> +++ b/include/linux/mfd/sun4i-gpadc.h
>>> @@ -90,6 +90,8 @@
>>> #define SUNXI_THS_CTRL0 0x00
>>> #define SUNXI_THS_CTRL2 0x40
>>> #define SUNXI_THS_FILTER 0x70
>>> +#define SUNXI_THS_CDATA_0_1 0x74
>>> +#define SUNXI_THS_CDATA_2_3 0x78
>>> #define SUNXI_THS_TDATA0 0x80
>>> #define SUNXI_THS_TDATA1 0x84
>>> #define SUNXI_THS_TDATA2 0x88
>>
>Thanks,
>Philipp
>
>_______________________________________________
>linux-arm-kernel mailing list
>[email protected]
>http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On 28.01.2018 10:08, Jonathan Cameron wrote:
> On Fri, 26 Jan 2018 16:19:31 +0100
> Philipp Rossak <[email protected]> wrote:
>
> multible -> multiple
>
^^ Ok, I will fix that.
>> For adding newer sensor some basic rework of the code is necessary.
>>
>> This patch reworks the driver to be able to handle more than one
>> thermal sensor. Newer SoC like the A80 have 4 thermal sensors.
>> Because of this the maximal sensor count value was set to 4.
>>
>> The sensor_id value is set during sensor registration and is for each
>> registered sensor indiviual. This makes it able to differntiate the
>> sensors when the value is read from the register.
>>
>> Signed-off-by: Philipp Rossak <[email protected]>
> Question inline about why you aren't exposing the additional temperature
> sensors as IIO channels?
>
> Fine to not do so I suppose, but needs justifying.
>
^^ Ok, I will rework the commit mesage. Detailed explanation see below.
> Jonathan
>
>> ---
>> drivers/iio/adc/sun4i-gpadc-iio.c | 36 +++++++++++++++++++++++-------------
>> include/linux/mfd/sun4i-gpadc.h | 6 ++++++
>> 2 files changed, 29 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
>> index 1a80744bd472..bff06f2798e8 100644
>> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
>> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
>> @@ -70,7 +70,7 @@ struct gpadc_data {
>> unsigned int tp_adc_select;
>> unsigned int (*adc_chan_select)(unsigned int chan);
>> unsigned int adc_chan_mask;
>> - unsigned int temp_data;
>> + unsigned int temp_data[MAX_SENSOR_COUNT];
>> int (*sample_start)(struct sun4i_gpadc_iio *info);
>> int (*sample_end)(struct sun4i_gpadc_iio *info);
>> u32 ctrl0_map;
>> @@ -80,6 +80,7 @@ struct gpadc_data {
>> bool has_bus_clk;
>> bool has_bus_rst;
>> bool has_mod_clk;
>> + int sensor_count;
>> };
>>
>> static const struct gpadc_data sun4i_gpadc_data = {
>> @@ -89,9 +90,10 @@ static const struct gpadc_data sun4i_gpadc_data = {
>> .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
>> .adc_chan_select = &sun4i_gpadc_chan_select,
>> .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
>> - .temp_data = SUN4I_GPADC_TEMP_DATA,
>> + .temp_data = {SUN4I_GPADC_TEMP_DATA, 0, 0, 0},
>> .sample_start = sun4i_gpadc_sample_start,
>> .sample_end = sun4i_gpadc_sample_end,
>> + .sensor_count = 1,
>> };
>>
>> static const struct gpadc_data sun5i_gpadc_data = {
>> @@ -101,9 +103,10 @@ static const struct gpadc_data sun5i_gpadc_data = {
>> .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
>> .adc_chan_select = &sun4i_gpadc_chan_select,
>> .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
>> - .temp_data = SUN4I_GPADC_TEMP_DATA,
>> + .temp_data = {SUN4I_GPADC_TEMP_DATA, 0, 0, 0},
>> .sample_start = sun4i_gpadc_sample_start,
>> .sample_end = sun4i_gpadc_sample_end,
>> + .sensor_count = 1,
>> };
>>
>> static const struct gpadc_data sun6i_gpadc_data = {
>> @@ -113,18 +116,20 @@ static const struct gpadc_data sun6i_gpadc_data = {
>> .tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
>> .adc_chan_select = &sun6i_gpadc_chan_select,
>> .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
>> - .temp_data = SUN4I_GPADC_TEMP_DATA,
>> + .temp_data = {SUN4I_GPADC_TEMP_DATA, 0, 0, 0},
>> .sample_start = sun4i_gpadc_sample_start,
>> .sample_end = sun4i_gpadc_sample_end,
>> + .sensor_count = 1,
>> };
>>
>> static const struct gpadc_data sun8i_a33_gpadc_data = {
>> .temp_offset = -1662,
>> .temp_scale = 162,
>> .tp_mode_en = SUN8I_A33_GPADC_CTRL1_CHOP_TEMP_EN,
>> - .temp_data = SUN4I_GPADC_TEMP_DATA,
>> + .temp_data = {SUN4I_GPADC_TEMP_DATA, 0, 0, 0},
[*] ^^
>> .sample_start = sun4i_gpadc_sample_start,
>> .sample_end = sun4i_gpadc_sample_end,
>> + .sensor_count = 1,
>> };
>>
>> struct sun4i_gpadc_iio {
>> @@ -142,6 +147,7 @@ struct sun4i_gpadc_iio {
>> struct clk *bus_clk;
>> struct clk *mod_clk;
>> struct reset_control *reset;
>> + int sensor_id;
>> /* prevents concurrent reads of temperature and ADC */
>> struct mutex mutex;
>> struct thermal_zone_device *tzd;
>> @@ -309,14 +315,15 @@ static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel,
>> return sun4i_gpadc_read(indio_dev, channel, val, info->fifo_data_irq);
>> }
>>
>> -static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
>> +static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val,
>> + int sensor)
>> {
>> struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
>>
>> if (info->no_irq) {
>> pm_runtime_get_sync(indio_dev->dev.parent);
>>
>> - regmap_read(info->regmap, info->data->temp_data, val);
>> + regmap_read(info->regmap, info->data->temp_data[sensor], val);
>>
>> pm_runtime_mark_last_busy(indio_dev->dev.parent);
>> pm_runtime_put_autosuspend(indio_dev->dev.parent);
>> @@ -363,7 +370,7 @@ static int sun4i_gpadc_read_raw(struct iio_dev *indio_dev,
>> ret = sun4i_gpadc_adc_read(indio_dev, chan->channel,
>> val);
>> else
>> - ret = sun4i_gpadc_temp_read(indio_dev, val);
>> + ret = sun4i_gpadc_temp_read(indio_dev, val, 0);
>
> Wouldn't it make more sense to pass the channel number in here?
>
> Looking at it, seems you only expose the first temperature sensor
> as an IIO channel? Perhaps fair enough, but please justify that
> decision in the patch description.
>
The "old" thermal sensors only have one ths sensor. This function is
only used with the older sensors when the adc and the ths is used. The
channel number is not related to that. This here is related to the
temp_data. register selection above (marked with [*]).
>
>>
>> if (ret)
>> return ret;
>> @@ -511,7 +518,7 @@ static int sun4i_gpadc_get_temp(void *data, int *temp)
>> struct sun4i_gpadc_iio *info = data;
>> int val, scale, offset;
>>
>> - if (sun4i_gpadc_temp_read(info->indio_dev, &val))
>> + if (sun4i_gpadc_temp_read(info->indio_dev, &val, info->sensor_id))
>> return -ETIMEDOUT;
>>
>> sun4i_gpadc_temp_scale(info->indio_dev, &scale);
>> @@ -755,7 +762,7 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
>> {
>> struct sun4i_gpadc_iio *info;
>> struct iio_dev *indio_dev;
>> - int ret;
>> + int ret, i;
>>
>> indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
>> if (!indio_dev)
>> @@ -788,9 +795,12 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
>> pm_runtime_enable(&pdev->dev);
>>
>> if (IS_ENABLED(CONFIG_THERMAL_OF)) {
>> - info->tzd = thermal_zone_of_sensor_register(info->sensor_device,
>> - 0, info,
>> - &sun4i_ts_tz_ops);
>> + for (i = 0; i < info->data->sensor_count; i++) {
>> + info->sensor_id = i;
>> + info->tzd = thermal_zone_of_sensor_register(
>> + info->sensor_device,
>> + i, info, &sun4i_ts_tz_ops);
>> + }
>> /*
>> * Do not fail driver probing when failing to register in
>> * thermal because no thermal DT node is found.
>> diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
>> index 39e096c3ddac..40b4dd9d2405 100644
>> --- a/include/linux/mfd/sun4i-gpadc.h
>> +++ b/include/linux/mfd/sun4i-gpadc.h
>> @@ -90,6 +90,10 @@
>> #define SUNXI_THS_CTRL0 0x00
>> #define SUNXI_THS_CTRL2 0x40
>> #define SUNXI_THS_FILTER 0x70
>> +#define SUNXI_THS_TDATA0 0x80
>> +#define SUNXI_THS_TDATA1 0x84
>> +#define SUNXI_THS_TDATA2 0x88
>> +#define SUNXI_THS_TDATA3 0x8c
>>
>> #define SUNXI_THS_FILTER_EN BIT(2)
>> #define SUNXI_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
>> @@ -101,6 +105,8 @@
>> #define SUNXI_THS_TEMP_SENSE_EN2 BIT(2)
>> #define SUNXI_THS_TEMP_SENSE_EN3 BIT(3)
>>
>> +#define MAX_SENSOR_COUNT 4
>> +
>> struct sun4i_gpadc_dev {
>> struct device *dev;
>> struct regmap *regmap;
>
Thanks,
Philipp
On 28.01.2018 14:37, Icenowy Zheng wrote:
>
>
> 于 2018年1月28日 GMT+08:00 下午9:34:17, Philipp Rossak <[email protected]> 写到:
>>
>>
>> On 28.01.2018 09:43, Jonathan Cameron wrote:
>>> On Fri, 26 Jan 2018 16:19:29 +0100
>>> Philipp Rossak <[email protected]> wrote:
>>>
>>>> For adding newer sensor some basic rework of the code is necessary.
>>>>
>>>> This commit reworks the code and allows the sampling start/end code
>> and
>>>> the position of value readout register to be altered. Later the
>> start/end
>>>> functions will be used to configure the ths and start/stop the
>>>> sampling.
>>>>
>>>> Signed-off-by: Icenowy Zheng <[email protected]>
>>>> Signed-off-by: Philipp Rossak <[email protected]>
>>> Hmm. It almost always turns out to be a bad idea to assume a
>> particular
>>> set of registers will be consistent across a hardware family. Usual
>> convention
>>> is just to prefix them with the first supported device and use them
>> across
>>> the variants where they are correct. I.e. don't use wild cards or
>> generic
>>> names as they often end up implying a wider range of applicability
>> than
>>> we want.
>>>
>>
>> The new THS sensors seems to use the same THS ip core, only configured
>> with different amount of sensors. So for H3, A83T, H5, A80 and A64 the
>> registers are all the same.
>>
>> I think the big question is, do we want to have a few very big patches.
>>
>> Or more small patches with code that is used in later patches.
>
> Allwinner H6 has changed the THS core again.
>
> Please consider this :-)
>
Yes you are right the core is different! But it is already considered it.
They dropped ACQ1, the IRQs control/status are split in separate
registers. I already have a good idea how to implement it, but I would
like to add this in the second part of this patch series (containing
support for A80, A64, and H5) or even later since the initial support
and the CCU driver is missing right now.
>>
>>> A few other comments inline. Reworking code to make it ready for
>> later
>>> patches is fine, but this also adds a lot of new code which isn't
>> used until
>>> much later in the series. Move it there...
>>
>> please see comments below.
>>
>>>> ---
>>>> drivers/iio/adc/sun4i-gpadc-iio.c | 87
>> +++++++++++++++++++++++++++++++++++----
>>>> include/linux/mfd/sun4i-gpadc.h | 19 +++++++--
>>>> 2 files changed, 94 insertions(+), 12 deletions(-)
>>>>
>>>> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c
>> b/drivers/iio/adc/sun4i-gpadc-iio.c
>>>> index 03804ff9c006..363936b37c5a 100644
>>>> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
>>>> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
>>>> @@ -49,6 +49,18 @@ static unsigned int
>> sun6i_gpadc_chan_select(unsigned int chan)
>>>> return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
>>>> }
>>>>
>>>> +struct sun4i_gpadc_iio;
>>>> +
>>>> +/*
>>>> + * Prototypes for these functions, which enable these functions to
>> be
>>>> + * referenced in gpadc_data structures.
>>>> + */
>>>> +static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info);
>>>> +static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info);
>>>> +
>>>> +static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info);
>>>
>>> Superficially this appears to be introduced but not used... Patch 9
>>> is first to use it I think? Introduce it then rather than here.
>>>
>>
>> You are right, this function is used first in Patch 9. In an very early
>>
>> version, I had this together with Patch 9. But then I had the feeling
>> that everything got too messy... Right now this is a very big patch
>> series and I wanted to have a clear structure in it and easy to review.
>>
>> If I would rework this it will end up in squashing, Patch 4, Parts of
>> Patch 7, Patch 8 and Patch 9.
>>
>>>> +static int sunxi_ths_sample_end(struct sun4i_gpadc_iio *info);
>>>> +
>>>> struct gpadc_data {
>>>> int temp_offset;
>>>> int temp_scale;
>>>> @@ -56,6 +68,13 @@ struct gpadc_data {
>>>> unsigned int tp_adc_select;
>>>> unsigned int (*adc_chan_select)(unsigned int chan);
>>>> unsigned int adc_chan_mask;
>>>> + unsigned int temp_data;
>>>> + int (*sample_start)(struct sun4i_gpadc_iio *info);
>>>> + int (*sample_end)(struct sun4i_gpadc_iio *info);
>>>> + u32 ctrl0_map;
>>>> + u32 ctrl2_map;
>>>> + u32 sensor_en_map;
>>>> + u32 filter_map;
>>>> };
>>>>
>>>> static const struct gpadc_data sun4i_gpadc_data = {
>>>> @@ -65,6 +84,9 @@ static const struct gpadc_data sun4i_gpadc_data =
>> {
>>>> .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
>>>> .adc_chan_select = &sun4i_gpadc_chan_select,
>>>> .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
>>>> + .temp_data = SUN4I_GPADC_TEMP_DATA,
>>>> + .sample_start = sun4i_gpadc_sample_start,
>>>> + .sample_end = sun4i_gpadc_sample_end,
>>>> };
>>>>
>>>> static const struct gpadc_data sun5i_gpadc_data = {
>>>> @@ -74,6 +96,9 @@ static const struct gpadc_data sun5i_gpadc_data =
>> {
>>>> .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
>>>> .adc_chan_select = &sun4i_gpadc_chan_select,
>>>> .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
>>>> + .temp_data = SUN4I_GPADC_TEMP_DATA,
>>>> + .sample_start = sun4i_gpadc_sample_start,
>>>> + .sample_end = sun4i_gpadc_sample_end,
>>>> };
>>>>
>>>> static const struct gpadc_data sun6i_gpadc_data = {
>>>> @@ -83,12 +108,18 @@ static const struct gpadc_data sun6i_gpadc_data
>> = {
>>>> .tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
>>>> .adc_chan_select = &sun6i_gpadc_chan_select,
>>>> .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
>>>> + .temp_data = SUN4I_GPADC_TEMP_DATA,
>>>> + .sample_start = sun4i_gpadc_sample_start,
>>>> + .sample_end = sun4i_gpadc_sample_end,
>>>> };
>>>>
>>>> static const struct gpadc_data sun8i_a33_gpadc_data = {
>>>> .temp_offset = -1662,
>>>> .temp_scale = 162,
>>>> .tp_mode_en = SUN8I_A33_GPADC_CTRL1_CHOP_TEMP_EN,
>>>> + .temp_data = SUN4I_GPADC_TEMP_DATA,
>>>> + .sample_start = sun4i_gpadc_sample_start,
>>>> + .sample_end = sun4i_gpadc_sample_end,
>>>> };
>>>>
>>>> struct sun4i_gpadc_iio {
>>>> @@ -277,7 +308,7 @@ static int sun4i_gpadc_temp_read(struct iio_dev
>> *indio_dev, int *val)
>>>> if (info->no_irq) {
>>>> pm_runtime_get_sync(indio_dev->dev.parent);
>>>>
>>>> - regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
>>>> + regmap_read(info->regmap, info->data->temp_data, val);
>>>>
>>>> pm_runtime_mark_last_busy(indio_dev->dev.parent);
>>>> pm_runtime_put_autosuspend(indio_dev->dev.parent);
>>>> @@ -382,10 +413,8 @@ static irqreturn_t
>> sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
>>>> return IRQ_HANDLED;
>>>> }
>>>>
>>>> -static int sun4i_gpadc_runtime_suspend(struct device *dev)
>>>> +static int sun4i_gpadc_sample_end(struct sun4i_gpadc_iio *info)
>>>> {
>>>> - struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>>>> -
>>>> /* Disable the ADC on IP */
>>>> regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0);
>>>> /* Disable temperature sensor on IP */
>>>> @@ -394,19 +423,32 @@ static int sun4i_gpadc_runtime_suspend(struct
>> device *dev)
>>>> return 0;
>>>> }
>>>>
>>>> -static int sun4i_gpadc_runtime_resume(struct device *dev)
>>>> +static int sunxi_ths_sample_end(struct sun4i_gpadc_iio *info)
>>>> +{
>>>> + /* Disable temperature sensor */
>>>> + regmap_write(info->regmap, SUNXI_THS_CTRL2, 0x0);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static int sun4i_gpadc_runtime_suspend(struct device *dev)
>>>> {
>>>> struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>>>>
>>>> + return info->data->sample_end(info);
>>>> +}
>>>> +
>>>> +static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
>>>> +{
>>>> /* clkin = 6MHz */
>>>> regmap_write(info->regmap, SUN4I_GPADC_CTRL0,
>>>> SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) |
>>>> SUN4I_GPADC_CTRL0_FS_DIV(7) |
>>>> - SUN4I_GPADC_CTRL0_T_ACQ(63));
>>>> + SUNXI_THS_ACQ0(63));
>>>> regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
>> info->data->tp_mode_en);
>>>> regmap_write(info->regmap, SUN4I_GPADC_CTRL3,
>>>> - SUN4I_GPADC_CTRL3_FILTER_EN |
>>>> - SUN4I_GPADC_CTRL3_FILTER_TYPE(1));
>>>> + SUNXI_THS_FILTER_EN |
>>>> + SUNXI_THS_FILTER_TYPE(1));
>>>
>>> Why if these are temperature sensor features are we setting them in
>> the
>>> general purpose ADC start function?
>>
>> This is part of the olds sensors (before Allwinner reworked their
>> temperature sensor (only ths) ). Those "old" sensors integrate an ADC
>> and a Thermal sensor in one ip block/register space. I don't want to
>> break those sensors/adc, since I'm not able to test them!
>>
>>>> /* period = SUN4I_GPADC_TPR_TEMP_PERIOD * 256 * 16 / clkin; ~0.6s
>> */
>>>> regmap_write(info->regmap, SUN4I_GPADC_TPR,
>>>> SUN4I_GPADC_TPR_TEMP_ENABLE |
>>>> @@ -415,6 +457,35 @@ static int sun4i_gpadc_runtime_resume(struct
>> device *dev)
>>>> return 0;
>>>> }
>>>>
>>>> +static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info)
>>>> +{
>>>> + u32 value;
>>>> +
>>>> + if (info->data->ctrl0_map)
>>>> + regmap_write(info->regmap, SUNXI_THS_CTRL0,
>>>> + info->data->ctrl0_map);
>>>> +
>>>> + regmap_write(info->regmap, SUNXI_THS_CTRL2,
>>>> + info->data->ctrl2_map);
>>>> +
>>>> + regmap_write(info->regmap, SUNXI_THS_FILTER,
>>>> + info->data->filter_map);
>>>> +
>>>> + regmap_read(info->regmap, SUNXI_THS_CTRL2, &value);
>>>> +
>>>> + regmap_write(info->regmap, SUNXI_THS_CTRL2,
>>>> + info->data->sensor_en_map | value);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static int sun4i_gpadc_runtime_resume(struct device *dev)
>>>> +{
>>>> + struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
>>>> +
>>>> + return info->data->sample_start(info);
>>>> +}
>>>> +
>>>> static int sun4i_gpadc_get_temp(void *data, int *temp)
>>>> {
>>>> struct sun4i_gpadc_iio *info = data;
>>>> diff --git a/include/linux/mfd/sun4i-gpadc.h
>> b/include/linux/mfd/sun4i-gpadc.h
>>>> index 78d31984a222..39e096c3ddac 100644
>>>> --- a/include/linux/mfd/sun4i-gpadc.h
>>>> +++ b/include/linux/mfd/sun4i-gpadc.h
>>>> @@ -17,7 +17,6 @@
>>>> #define SUN4I_GPADC_CTRL0_ADC_CLK_SELECT BIT(22)
>>>> #define SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(x) ((GENMASK(1, 0) &
>> (x)) << 20)
>>>> #define SUN4I_GPADC_CTRL0_FS_DIV(x) ((GENMASK(3, 0) & (x)) <<
>> 16)
>>>> -#define SUN4I_GPADC_CTRL0_T_ACQ(x) (GENMASK(15, 0) & (x))
>>>>
>>>> #define SUN4I_GPADC_CTRL1 0x04
>>>>
>>>> @@ -51,9 +50,6 @@
>>>>
>>>> #define SUN4I_GPADC_CTRL3 0x0c
>>>>
>>>> -#define SUN4I_GPADC_CTRL3_FILTER_EN BIT(2)
>>>> -#define SUN4I_GPADC_CTRL3_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
>>>> -
>>>> #define SUN4I_GPADC_TPR 0x18
>>>>
>>>> #define SUN4I_GPADC_TPR_TEMP_ENABLE BIT(16)
>>>> @@ -90,6 +86,21 @@
>>>> /* 10s delay before suspending the IP */
>>>> #define SUN4I_GPADC_AUTOSUSPEND_DELAY 10000
>>>>
>>>> +/* SUNXI_THS COMMON REGISTERS + DEFINES */
>>>> +#define SUNXI_THS_CTRL0 0x00
>>>> +#define SUNXI_THS_CTRL2 0x40
>>>> +#define SUNXI_THS_FILTER 0x70
>>>> +
>>>> +#define SUNXI_THS_FILTER_EN BIT(2)
>>>> +#define SUNXI_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
>>>> +#define SUNXI_THS_ACQ0(x) (GENMASK(15, 0) & (x))
>>>> +#define SUNXI_THS_ACQ1(x) (GENMASK(31, 16) & ((x) << 16))
>>>> +
>>>> +#define SUNXI_THS_TEMP_SENSE_EN0 BIT(0)
>>>> +#define SUNXI_THS_TEMP_SENSE_EN1 BIT(1)
>>>> +#define SUNXI_THS_TEMP_SENSE_EN2 BIT(2)
>>>> +#define SUNXI_THS_TEMP_SENSE_EN3 BIT(3)
>>>> +
>>>> struct sun4i_gpadc_dev {
>>>> struct device *dev;
>>>> struct regmap *regmap;
>>>
>>
>> In general I'm ok with changing those things, but I would like to wait
>> for some more feedback/thoughts about this.
>>
>> Thanks,
>> Philipp
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> [email protected]
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On 28.01.2018 14:52, Icenowy Zheng wrote:
>
>
> 于 2018年1月28日 GMT+08:00 下午9:46:18, Philipp Rossak <[email protected]> 写到:
>>
>>
>> On 28.01.2018 10:02, Jonathan Cameron wrote:
>>> On Fri, 26 Jan 2018 16:19:32 +0100
>>> Philipp Rossak <[email protected]> wrote:
>>>
>>>> This patch reworks the driver to support nvmem calibration cells.
>>>> The driver checks if the nvmem calibration is supported and reads
>> out
>>>> the nvmem. At the beginning of the startup process the calibration
>> data
>>>> is written to the related registers.
>>>>
>>>> Signed-off-by: Philipp Rossak <[email protected]>
>>>
>>> A few minor suggestions inline.
>>>
>>> Jonathan
>>>
>>>> ---
>>>> drivers/iio/adc/sun4i-gpadc-iio.c | 52
>> +++++++++++++++++++++++++++++++++++++++
>>>> include/linux/mfd/sun4i-gpadc.h | 2 ++
>>>> 2 files changed, 54 insertions(+)
>>>>
>>>> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c
>> b/drivers/iio/adc/sun4i-gpadc-iio.c
>>>> index bff06f2798e8..7b12666cdd9e 100644
>>>> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
>>>> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
>>>> @@ -27,6 +27,7 @@
>>>> #include <linux/interrupt.h>
>>>> #include <linux/io.h>
>>>> #include <linux/module.h>
>>>> +#include <linux/nvmem-consumer.h>
>>>> #include <linux/of.h>
>>>> #include <linux/of_device.h>
>>>> #include <linux/platform_device.h>
>>>> @@ -81,6 +82,7 @@ struct gpadc_data {
>>>> bool has_bus_rst;
>>>> bool has_mod_clk;
>>>> int sensor_count;
>>>> + bool supports_nvmem;
>>>> };
>>>>
>>>> static const struct gpadc_data sun4i_gpadc_data = {
>>>> @@ -94,6 +96,7 @@ static const struct gpadc_data sun4i_gpadc_data =
>> {
>>>> .sample_start = sun4i_gpadc_sample_start,
>>>> .sample_end = sun4i_gpadc_sample_end,
>>>> .sensor_count = 1,
>>>> + .supports_nvmem = false,
>>>> };
>>>>
>>>> static const struct gpadc_data sun5i_gpadc_data = {
>>>> @@ -107,6 +110,7 @@ static const struct gpadc_data sun5i_gpadc_data
>> = {
>>>> .sample_start = sun4i_gpadc_sample_start,
>>>> .sample_end = sun4i_gpadc_sample_end,
>>>> .sensor_count = 1,
>>>> + .supports_nvmem = false,
>>>> };
>>>>
>>>> static const struct gpadc_data sun6i_gpadc_data = {
>>>> @@ -120,6 +124,7 @@ static const struct gpadc_data sun6i_gpadc_data
>> = {
>>>> .sample_start = sun4i_gpadc_sample_start,
>>>> .sample_end = sun4i_gpadc_sample_end,
>>>> .sensor_count = 1,
>>>> + .supports_nvmem = false,
>>>> };
>>>>
>>>> static const struct gpadc_data sun8i_a33_gpadc_data = {
>>>> @@ -130,6 +135,7 @@ static const struct gpadc_data
>> sun8i_a33_gpadc_data = {
>>>> .sample_start = sun4i_gpadc_sample_start,
>>>> .sample_end = sun4i_gpadc_sample_end,
>>>> .sensor_count = 1,
>>>> + .supports_nvmem = false,
>
> BTW A33 claims to support calibration data according to the manual.
>
Yes that's true, but I haven't seen a sid/nvmem driver for the a33. If
that is available, we can change this for true (same on a83t).
>>>> };
>>>>
>>>> struct sun4i_gpadc_iio {
>>>> @@ -148,6 +154,8 @@ struct sun4i_gpadc_iio {
>>>> struct clk *mod_clk;
>>>> struct reset_control *reset;
>>>> int sensor_id;
>>>> + u32 calibration_data[2];
>>>> + bool has_calibration_data[2];
>>>> /* prevents concurrent reads of temperature and ADC */
>>>> struct mutex mutex;
>>>> struct thermal_zone_device *tzd;
>>>> @@ -459,6 +467,17 @@ static int sun4i_gpadc_runtime_suspend(struct
>> device *dev)
>>>> return info->data->sample_end(info);
>>>> }
>>>>
>>>> +static void sunxi_calibrate(struct sun4i_gpadc_iio *info)
>>>> +{
>>>> + if (info->has_calibration_data[0])
>>>> + regmap_write(info->regmap, SUNXI_THS_CDATA_0_1,
>>>> + info->calibration_data[0]);
>>>> +
>>>> + if (info->has_calibration_data[1])
>>>> + regmap_write(info->regmap, SUNXI_THS_CDATA_2_3,
>>>> + info->calibration_data[1]);
>>>> +}
>>>> +
>>>> static int sun4i_gpadc_sample_start(struct sun4i_gpadc_iio *info)
>>>> {
>>>> /* clkin = 6MHz */
>>>> @@ -481,6 +500,7 @@ static int sun4i_gpadc_sample_start(struct
>> sun4i_gpadc_iio *info)
>>>> static int sunxi_ths_sample_start(struct sun4i_gpadc_iio *info)
>>>> {
>>>> u32 value;
>>>> + sunxi_calibrate(info);
>>>>
>>>> if (info->data->ctrl0_map)
>>>> regmap_write(info->regmap, SUNXI_THS_CTRL0,
>>>> @@ -602,6 +622,9 @@ static int sun4i_gpadc_probe_dt(struct
>> platform_device *pdev,
>>>> struct resource *mem;
>>>> void __iomem *base;
>>>> int ret;
>>>> + struct nvmem_cell *cell;
>>>> + ssize_t cell_size;
>>>> + u64 *cell_data;
>>>>
>>>> info->data = of_device_get_match_data(&pdev->dev);
>>>> if (!info->data)
>>>> @@ -616,6 +639,35 @@ static int sun4i_gpadc_probe_dt(struct
>> platform_device *pdev,
>>>> if (IS_ERR(base))
>>>> return PTR_ERR(base);
>>>>
>>>> + info->has_calibration_data[0] = false;
>>>> + info->has_calibration_data[1] = false;
>>>> +
>>>> + if (!info->data->supports_nvmem)
>>>> + goto no_nvmem;
>>>> +
>>>> + cell = devm_nvmem_cell_get(&pdev->dev, "calibration");
>>>> + if (IS_ERR(cell)) {
>>>> + if (PTR_ERR(cell) == -EPROBE_DEFER)
>>>> + return PTR_ERR(cell);
>>> Use a goto here to no_nvmem.
>>>
>>> Then you can drop the below else to make things more readable.
>>>> + } else {
>>>> + cell_data = (u64 *)nvmem_cell_read(cell, &cell_size);
>>>> + devm_nvmem_cell_put(&pdev->dev, cell);
>>>
>>> I'm really not keen on use of devm for things we are intending
>>> to drop almost immediately. Just use the non managed versions
>>> and clean up properly in the error paths (if there are any
>>> where it is needed - which there aren't that I can see)
>>>
>>
>> ^^ Ok, I will rework that.
>>
>>>> + if (cell_size <= 4) {
>>> Is it valid if it is anything other than 4?
>>
>> For sensors with only one sensor would be also a 2 valid, for those
>> with
>> 2 sensors ==> 4, with 3 sensors ==> 6 and with and with 4 sensors ==>
>> 8.
>>
>> In hardware we have in total to 32bit registers for calibration, thus I
>>
>> thought it would be a good idea to use always four bytes per register.
>> If two bytes are used, they should be the default value.
>>
>> But I agree, this needs a rework.
>>
>>>> + info->has_calibration_data[0] = true;
>>>> + info->calibration_data[0] = be32_to_cpu(cell_data[0] &
>>>> + GENMASK(31, 0));
>>>
>>> Masking isn't needed, If you want to be paranoid there is the
>> lower_32_bits
>>> function..
>>>
>> ^^ Ok, I will rework that.
>>>> + } else if (cell_size <= 8) {
>>>> + info->has_calibration_data[0] = true;
>>>> + info->calibration_data[0] = be32_to_cpu(cell_data[0] &
>>>> + GENMASK(31, 0));
>>>
>>> This first block is repeated. Easy enough to avoid I think...
>>>
>>
>> ^^ Ok, I will rework that.
>>
>>>> + info->has_calibration_data[1] = true;
>>>> + info->calibration_data[1] = be32_to_cpu(
>>>> + (cell_data[0] >> 32) & GENMASK(31, 0));
>>>> + }
>>>> + }
>>>> +
>>>> +no_nvmem:
>>>> +
>>>> info->regmap = devm_regmap_init_mmio(&pdev->dev, base,
>>>> &sun4i_gpadc_regmap_config);
>>>> if (IS_ERR(info->regmap)) {
>>>> diff --git a/include/linux/mfd/sun4i-gpadc.h
>> b/include/linux/mfd/sun4i-gpadc.h
>>>> index 40b4dd9d2405..c251002431bd 100644
>>>> --- a/include/linux/mfd/sun4i-gpadc.h
>>>> +++ b/include/linux/mfd/sun4i-gpadc.h
>>>> @@ -90,6 +90,8 @@
>>>> #define SUNXI_THS_CTRL0 0x00
>>>> #define SUNXI_THS_CTRL2 0x40
>>>> #define SUNXI_THS_FILTER 0x70
>>>> +#define SUNXI_THS_CDATA_0_1 0x74
>>>> +#define SUNXI_THS_CDATA_2_3 0x78
>>>> #define SUNXI_THS_TDATA0 0x80
>>>> #define SUNXI_THS_TDATA1 0x84
>>>> #define SUNXI_THS_TDATA2 0x88
>>>
>> Thanks,
>> Philipp
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> [email protected]
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel