* [PATCH v3 6/6] ARM: dts: stm32f429: enable adc on eval board
From: Fabrice Gasnier @ 2016-11-15 15:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479223861-21747-1-git-send-email-fabrice.gasnier@st.com>
Enable analog to digital converter on stm32f429i-eval board.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/boot/dts/stm32429i-eval.dts | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/arch/arm/boot/dts/stm32429i-eval.dts b/arch/arm/boot/dts/stm32429i-eval.dts
index 6bfc595..c144735 100644
--- a/arch/arm/boot/dts/stm32429i-eval.dts
+++ b/arch/arm/boot/dts/stm32429i-eval.dts
@@ -65,6 +65,20 @@
serial0 = &usart1;
};
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_vref: regulator at 0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "vref";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ };
+
leds {
compatible = "gpio-leds";
green {
@@ -123,3 +137,14 @@
pinctrl-names = "default";
status = "okay";
};
+
+&adc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&adc3_in8_pin>;
+ vref-supply = <®_vref>;
+ status = "okay";
+ adc3: adc at 200 {
+ st,adc-channels = <8>;
+ status = "okay";
+ };
+};
--
1.9.1
^ permalink raw reply related
* [PATCH v3 5/6] ARM: dts: stm32f429: Add adc support
From: Fabrice Gasnier @ 2016-11-15 15:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479223861-21747-1-git-send-email-fabrice.gasnier@st.com>
Add adc support & pinctrl analog phandle (adc3_in8) to stm32f429.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/boot/dts/stm32f429.dtsi | 49 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 336ee4f..f198132 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -172,6 +172,49 @@
status = "disabled";
};
+ adc: adc at 40012000 {
+ compatible = "st,stm32f4-adc-core";
+ reg = <0x40012000 0x400>;
+ interrupts = <18>;
+ clocks = <&rcc 0 168>;
+ clock-names = "adc";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+
+ adc1: adc at 0 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ interrupt-parent = <&adc>;
+ interrupts = <0>;
+ status = "disabled";
+ };
+
+ adc2: adc at 100 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x100>;
+ clocks = <&rcc 0 169>;
+ interrupt-parent = <&adc>;
+ interrupts = <1>;
+ status = "disabled";
+ };
+
+ adc3: adc at 200 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x200>;
+ clocks = <&rcc 0 170>;
+ interrupt-parent = <&adc>;
+ interrupts = <2>;
+ status = "disabled";
+ };
+ };
+
syscfg: system-config at 40013800 {
compatible = "syscon";
reg = <0x40013800 0x400>;
@@ -332,6 +375,12 @@
slew-rate = <2>;
};
};
+
+ adc3_in8_pin: adc at 200 {
+ pins {
+ pinmux = <STM32F429_PF10_FUNC_ANALOG>;
+ };
+ };
};
rcc: rcc at 40023810 {
--
1.9.1
^ permalink raw reply related
* [PATCH v3 4/6] ARM: configs: stm32: enable ADC driver
From: Fabrice Gasnier @ 2016-11-15 15:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479223861-21747-1-git-send-email-fabrice.gasnier@st.com>
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/configs/stm32_defconfig | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig
index 1e5ec2a..5d241e0 100644
--- a/arch/arm/configs/stm32_defconfig
+++ b/arch/arm/configs/stm32_defconfig
@@ -57,6 +57,9 @@ CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_DMADEVICES=y
CONFIG_STM32_DMA=y
+CONFIG_IIO=y
+CONFIG_STM32_ADC_CORE=y
+CONFIG_STM32_ADC=y
# CONFIG_FILE_LOCKING is not set
# CONFIG_DNOTIFY is not set
# CONFIG_INOTIFY_USER is not set
--
1.9.1
^ permalink raw reply related
* [PATCH v3 3/6] iio: adc: Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-11-15 15:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479223861-21747-1-git-send-email-fabrice.gasnier@st.com>
This patch adds support for STMicroelectronics STM32 MCU's analog to
digital converter.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/Kconfig | 10 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32-adc.c | 518 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 529 insertions(+)
create mode 100644 drivers/iio/adc/stm32-adc.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index ff30239..f93b990 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -432,6 +432,16 @@ config STM32_ADC_CORE
This driver can also be built as a module. If so, the module
will be called stm32-adc-core.
+config STM32_ADC
+ tristate "STMicroelectronics STM32 adc"
+ depends on STM32_ADC_CORE
+ help
+ Say yes here to build support for STMicroelectronics stm32 Analog
+ to Digital Converter (ADC).
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32-adc.
+
config STX104
tristate "Apex Embedded Systems STX104 driver"
depends on X86 && ISA_BUS_API
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index a1e8f44..8e02a94 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_STX104) += stx104.o
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
+obj-$(CONFIG_STM32_ADC) += stm32-adc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
new file mode 100644
index 0000000..5715e79
--- /dev/null
+++ b/drivers/iio/adc/stm32-adc.c
@@ -0,0 +1,518 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+
+#include "stm32-adc-core.h"
+
+/* STM32F4 - Registers for each ADC instance */
+#define STM32F4_ADC_SR 0x00
+#define STM32F4_ADC_CR1 0x04
+#define STM32F4_ADC_CR2 0x08
+#define STM32F4_ADC_SMPR1 0x0C
+#define STM32F4_ADC_SMPR2 0x10
+#define STM32F4_ADC_HTR 0x24
+#define STM32F4_ADC_LTR 0x28
+#define STM32F4_ADC_SQR1 0x2C
+#define STM32F4_ADC_SQR2 0x30
+#define STM32F4_ADC_SQR3 0x34
+#define STM32F4_ADC_JSQR 0x38
+#define STM32F4_ADC_JDR1 0x3C
+#define STM32F4_ADC_JDR2 0x40
+#define STM32F4_ADC_JDR3 0x44
+#define STM32F4_ADC_JDR4 0x48
+#define STM32F4_ADC_DR 0x4C
+
+/* STM32F4_ADC_SR - bit fields */
+#define STM32F4_STRT BIT(4)
+#define STM32F4_EOC BIT(1)
+
+/* STM32F4_ADC_CR1 - bit fields */
+#define STM32F4_SCAN BIT(8)
+#define STM32F4_EOCIE BIT(5)
+
+/* STM32F4_ADC_CR2 - bit fields */
+#define STM32F4_SWSTART BIT(30)
+#define STM32F4_EXTEN_MASK GENMASK(29, 28)
+#define STM32F4_EOCS BIT(10)
+#define STM32F4_ADON BIT(0)
+
+/* STM32F4_ADC_SQR1 - bit fields */
+#define STM32F4_L_SHIFT 20
+#define STM32F4_L_MASK GENMASK(23, 20)
+
+/* STM32F4_ADC_SQR3 - bit fields */
+#define STM32F4_SQ1_SHIFT 0
+#define STM32F4_SQ1_MASK GENMASK(4, 0)
+
+#define STM32_ADC_TIMEOUT_US 100000
+#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
+
+/**
+ * struct stm32_adc - private data of each ADC IIO instance
+ * @common: reference to ADC block common data
+ * @offset: ADC instance register offset in ADC block
+ * @completion: end of single conversion completion
+ * @buffer: data buffer
+ * @clk: clock for this adc instance
+ * @irq: interrupt for this adc instance
+ * @lock: spinlock
+ */
+struct stm32_adc {
+ struct stm32_adc_common *common;
+ u32 offset;
+ struct completion completion;
+ u16 *buffer;
+ struct clk *clk;
+ int irq;
+ spinlock_t lock; /* interrupt lock */
+};
+
+/**
+ * struct stm32_adc_chan_spec - specification of stm32 adc channel
+ * @type: IIO channel type
+ * @channel: channel number (single ended)
+ * @name: channel name (single ended)
+ */
+struct stm32_adc_chan_spec {
+ enum iio_chan_type type;
+ int channel;
+ const char *name;
+};
+
+/* Input definitions common for all STM32F4 instances */
+static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
+ { IIO_VOLTAGE, 0, "in0" },
+ { IIO_VOLTAGE, 1, "in1" },
+ { IIO_VOLTAGE, 2, "in2" },
+ { IIO_VOLTAGE, 3, "in3" },
+ { IIO_VOLTAGE, 4, "in4" },
+ { IIO_VOLTAGE, 5, "in5" },
+ { IIO_VOLTAGE, 6, "in6" },
+ { IIO_VOLTAGE, 7, "in7" },
+ { IIO_VOLTAGE, 8, "in8" },
+ { IIO_VOLTAGE, 9, "in9" },
+ { IIO_VOLTAGE, 10, "in10" },
+ { IIO_VOLTAGE, 11, "in11" },
+ { IIO_VOLTAGE, 12, "in12" },
+ { IIO_VOLTAGE, 13, "in13" },
+ { IIO_VOLTAGE, 14, "in14" },
+ { IIO_VOLTAGE, 15, "in15" },
+};
+
+/**
+ * STM32 ADC registers access routines
+ * @adc: stm32 adc instance
+ * @reg: reg offset in adc instance
+ *
+ * Note: All instances share same base, with 0x0, 0x100 or 0x200 offset resp.
+ * for adc1, adc2 and adc3.
+ */
+static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
+{
+ return readl_relaxed(adc->common->base + adc->offset + reg);
+}
+
+static u16 stm32_adc_readw(struct stm32_adc *adc, u32 reg)
+{
+ return readw_relaxed(adc->common->base + adc->offset + reg);
+}
+
+static void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
+{
+ writel_relaxed(val, adc->common->base + adc->offset + reg);
+}
+
+static void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+/**
+ * stm32_adc_conv_irq_enable() - Enable end of conversion interrupt
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
+{
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
+};
+
+/**
+ * stm32_adc_conv_irq_disable() - Disable end of conversion interrupt
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
+{
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
+}
+
+/**
+ * stm32_adc_start_conv() - Start conversions for regular channels.
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_start_conv(struct stm32_adc *adc)
+{
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON);
+
+ /* Wait for Power-up time (tSTAB from datasheet) */
+ usleep_range(2, 3);
+
+ /* Software start ? (e.g. trigger detection disabled ?) */
+ if (!(stm32_adc_readl(adc, STM32F4_ADC_CR2) & STM32F4_EXTEN_MASK))
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART);
+}
+
+static void stm32_adc_stop_conv(struct stm32_adc *adc)
+{
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
+ stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT);
+
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_ADON);
+}
+
+/**
+ * stm32_adc_single_conv() - Performs a single conversion
+ * @indio_dev: IIO device
+ * @chan: IIO channel
+ * @res: conversion result
+ *
+ * The function performs a single conversion on a given channel:
+ * - Program sequencer with one channel (e.g. in SQ1 with len = 1)
+ * - Use SW trigger
+ * - Start conversion, then wait for interrupt completion.
+ */
+static int stm32_adc_single_conv(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *res)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ long timeout;
+ u32 val;
+ u16 result;
+ int ret;
+
+ reinit_completion(&adc->completion);
+
+ adc->buffer = &result;
+
+ /* Program chan number in regular sequence */
+ val = stm32_adc_readl(adc, STM32F4_ADC_SQR3);
+ val &= ~STM32F4_SQ1_MASK;
+ val |= chan->channel << STM32F4_SQ1_SHIFT;
+ stm32_adc_writel(adc, STM32F4_ADC_SQR3, val);
+
+ /* Set regular sequence len (0 for 1 conversion) */
+ stm32_adc_clr_bits(adc, STM32F4_ADC_SQR1, STM32F4_L_MASK);
+
+ /* Trigger detection disabled (conversion can be launched in SW) */
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
+
+ stm32_adc_conv_irq_enable(adc);
+
+ stm32_adc_start_conv(adc);
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &adc->completion, STM32_ADC_TIMEOUT);
+ if (timeout == 0) {
+ ret = -ETIMEDOUT;
+ } else if (timeout < 0) {
+ ret = timeout;
+ } else {
+ *res = result;
+ ret = IIO_VAL_INT;
+ }
+
+ stm32_adc_stop_conv(adc);
+
+ stm32_adc_conv_irq_disable(adc);
+
+ return ret;
+}
+
+static int stm32_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ if (chan->type == IIO_VOLTAGE)
+ ret = stm32_adc_single_conv(indio_dev, chan, val);
+ else
+ ret = -EINVAL;
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = adc->common->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t stm32_adc_isr(int irq, void *data)
+{
+ struct stm32_adc *adc = data;
+ u32 status = stm32_adc_readl(adc, STM32F4_ADC_SR);
+
+ if (status & STM32F4_EOC) {
+ *adc->buffer = stm32_adc_readw(adc, STM32F4_ADC_DR);
+ complete(&adc->completion);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
+ const struct of_phandle_args *iiospec)
+{
+ int i;
+
+ for (i = 0; i < indio_dev->num_channels; i++)
+ if (indio_dev->channels[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+/**
+ * stm32_adc_debugfs_reg_access - read or write register value
+ *
+ * To read a value from an ADC register:
+ * echo [ADC reg offset] > direct_reg_access
+ * cat direct_reg_access
+ *
+ * To write a value in a ADC register:
+ * echo [ADC_reg_offset] [value] > direct_reg_access
+ */
+static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ if (!readval)
+ stm32_adc_writel(adc, reg, writeval);
+ else
+ *readval = stm32_adc_readl(adc, reg);
+
+ return 0;
+}
+
+static const struct iio_info stm32_adc_iio_info = {
+ .read_raw = stm32_adc_read_raw,
+ .debugfs_reg_access = stm32_adc_debugfs_reg_access,
+ .of_xlate = stm32_adc_of_xlate,
+ .driver_module = THIS_MODULE,
+};
+
+static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan,
+ const struct stm32_adc_chan_spec *channel,
+ int scan_index)
+{
+ chan->type = channel->type;
+ chan->channel = channel->channel;
+ chan->datasheet_name = channel->name;
+ chan->scan_index = scan_index;
+ chan->indexed = 1;
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = 12;
+ chan->scan_type.storagebits = 16;
+}
+
+static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
+{
+ struct device_node *node = indio_dev->dev.of_node;
+ struct property *prop;
+ const __be32 *cur;
+ struct iio_chan_spec *channels;
+ int scan_index = 0, num_channels;
+ u32 val;
+
+ num_channels = of_property_count_u32_elems(node, "st,adc-channels");
+ if (num_channels < 0 ||
+ num_channels >= ARRAY_SIZE(stm32f4_adc123_channels)) {
+ dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
+ return num_channels < 0 ? num_channels : -EINVAL;
+ }
+
+ channels = devm_kcalloc(&indio_dev->dev, num_channels,
+ sizeof(struct iio_chan_spec), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
+ if (val >= ARRAY_SIZE(stm32f4_adc123_channels)) {
+ dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
+ return -EINVAL;
+ }
+ stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+ &stm32f4_adc123_channels[val],
+ scan_index);
+ scan_index++;
+ }
+
+ indio_dev->num_channels = scan_index;
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static int stm32_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct stm32_adc *adc;
+ int ret;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->common = dev_get_drvdata(pdev->dev.parent);
+ spin_lock_init(&adc->lock);
+ init_completion(&adc->completion);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &stm32_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ platform_set_drvdata(pdev, adc);
+
+ ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "missing reg property\n");
+ return -EINVAL;
+ }
+
+ adc->irq = platform_get_irq(pdev, 0);
+ if (adc->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return adc->irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
+ 0, pdev->name, adc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ return ret;
+ }
+
+ adc->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(adc->clk)) {
+ dev_err(&pdev->dev, "Can't get clock\n");
+ return PTR_ERR(adc->clk);
+ }
+
+ ret = clk_prepare_enable(adc->clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "clk enable failed\n");
+ return ret;
+ }
+
+ ret = stm32_adc_chan_of_init(indio_dev);
+ if (ret < 0)
+ goto err_clk_disable;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "iio dev register failed\n");
+ goto err_clk_disable;
+ }
+
+ return 0;
+
+err_clk_disable:
+ clk_disable_unprepare(adc->clk);
+
+ return ret;
+}
+
+static int stm32_adc_remove(struct platform_device *pdev)
+{
+ struct stm32_adc *adc = platform_get_drvdata(pdev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ iio_device_unregister(indio_dev);
+ clk_disable_unprepare(adc->clk);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_adc_of_match[] = {
+ { .compatible = "st,stm32f4-adc" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
+
+static struct platform_driver stm32_adc_driver = {
+ .probe = stm32_adc_probe,
+ .remove = stm32_adc_remove,
+ .driver = {
+ .name = "stm32-adc",
+ .of_match_table = stm32_adc_of_match,
+ },
+};
+module_platform_driver(stm32_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC IIO driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-adc");
--
1.9.1
^ permalink raw reply related
* [PATCH v3 2/6] iio: adc: Add support for STM32 ADC core
From: Fabrice Gasnier @ 2016-11-15 15:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479223861-21747-1-git-send-email-fabrice.gasnier@st.com>
Add core driver for STMicroelectronics STM32 ADC (Analog to Digital
Converter). STM32 ADC can be composed of up to 3 ADCs with shared
resources like clock prescaler, common interrupt line and analog
reference voltage.
This core driver basically manages shared resources.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/Kconfig | 13 ++
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32-adc-core.c | 303 +++++++++++++++++++++++++++++++++++++++
drivers/iio/adc/stm32-adc-core.h | 52 +++++++
4 files changed, 369 insertions(+)
create mode 100644 drivers/iio/adc/stm32-adc-core.c
create mode 100644 drivers/iio/adc/stm32-adc-core.h
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7edcf32..ff30239 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -419,6 +419,19 @@ config ROCKCHIP_SARADC
To compile this driver as a module, choose M here: the
module will be called rockchip_saradc.
+config STM32_ADC_CORE
+ tristate "STMicroelectronics STM32 adc core"
+ depends on ARCH_STM32 || COMPILE_TEST
+ depends on OF
+ select REGULATOR
+ select REGULATOR_FIXED_VOLTAGE
+ help
+ Select this option to enable the core driver for STMicroelectronics
+ STM32 analog-to-digital converter (ADC).
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32-adc-core.
+
config STX104
tristate "Apex Embedded Systems STX104 driver"
depends on X86 && ISA_BUS_API
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04..a1e8f44 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_STX104) += stx104.o
+obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
new file mode 100644
index 0000000..4214b0c
--- /dev/null
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -0,0 +1,303 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * Inspired from: fsl-imx25-tsadc
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdesc.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include "stm32-adc-core.h"
+
+/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
+#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
+
+/* STM32F4_ADC_CSR - bit fields */
+#define STM32F4_EOC3 BIT(17)
+#define STM32F4_EOC2 BIT(9)
+#define STM32F4_EOC1 BIT(1)
+
+/* STM32F4_ADC_CCR - bit fields */
+#define STM32F4_ADC_ADCPRE_SHIFT 16
+#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
+
+/* STM32 F4 maximum analog clock rate (from datasheet) */
+#define STM32F4_ADC_MAX_CLK_RATE 36000000
+
+/**
+ * struct stm32_adc_priv - stm32 ADC core private data
+ * @irq: irq for ADC block
+ * @domain: irq domain reference
+ * @aclk: clock reference for the analog circuitry
+ * @vref: regulator reference
+ * @common: common data for all ADC instances
+ */
+struct stm32_adc_priv {
+ int irq;
+ struct irq_domain *domain;
+ struct clk *aclk;
+ struct regulator *vref;
+ struct stm32_adc_common common;
+};
+
+static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
+{
+ return container_of(com, struct stm32_adc_priv, common);
+}
+
+/* STM32F4 ADC internal common clock prescaler division ratios */
+static int stm32f4_pclk_div[] = {2, 4, 6, 8};
+
+/**
+ * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler
+ * @priv: stm32 ADC core private data
+ * Select clock prescaler used for analog conversions, before using ADC.
+ */
+static int stm32f4_adc_clk_sel(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ unsigned long rate;
+ u32 val;
+ int i;
+
+ rate = clk_get_rate(priv->aclk);
+ for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
+ if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
+ break;
+ }
+ if (i >= ARRAY_SIZE(stm32f4_pclk_div))
+ return -EINVAL;
+
+ val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
+ val &= ~STM32F4_ADC_ADCPRE_MASK;
+ val |= i << STM32F4_ADC_ADCPRE_SHIFT;
+ writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR);
+
+ dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n",
+ rate / (stm32f4_pclk_div[i] * 1000));
+
+ return 0;
+}
+
+/* ADC common interrupt for all instances */
+static void stm32_adc_irq_handler(struct irq_desc *desc)
+{
+ struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u32 status;
+
+ chained_irq_enter(chip, desc);
+ status = readl_relaxed(priv->common.base + STM32F4_ADC_CSR);
+
+ if (status & STM32F4_EOC1)
+ generic_handle_irq(irq_find_mapping(priv->domain, 0));
+
+ if (status & STM32F4_EOC2)
+ generic_handle_irq(irq_find_mapping(priv->domain, 1));
+
+ if (status & STM32F4_EOC3)
+ generic_handle_irq(irq_find_mapping(priv->domain, 2));
+
+ chained_irq_exit(chip, desc);
+};
+
+static int stm32_adc_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_data(irq, d->host_data);
+ irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
+
+ return 0;
+}
+
+static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops stm32_adc_domain_ops = {
+ .map = stm32_adc_domain_map,
+ .unmap = stm32_adc_domain_unmap,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int stm32_adc_irq_probe(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ struct device_node *np = pdev->dev.of_node;
+
+ priv->irq = platform_get_irq(pdev, 0);
+ if (priv->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return priv->irq;
+ }
+
+ priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
+ &stm32_adc_domain_ops,
+ priv);
+ if (!priv->domain) {
+ dev_err(&pdev->dev, "Failed to add irq domain\n");
+ return -ENOMEM;
+ }
+
+ irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
+ irq_set_handler_data(priv->irq, priv);
+
+ return 0;
+}
+
+static void stm32_adc_irq_remove(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ int hwirq;
+
+ for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
+ irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
+ irq_domain_remove(priv->domain);
+ irq_set_chained_handler(priv->irq, NULL);
+}
+
+static int stm32_adc_probe(struct platform_device *pdev)
+{
+ struct stm32_adc_priv *priv;
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res;
+ int ret;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->common.base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->common.base))
+ return PTR_ERR(priv->common.base);
+
+ priv->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(priv->vref)) {
+ ret = PTR_ERR(priv->vref);
+ dev_err(&pdev->dev, "vref get failed, %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(priv->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref enable failed\n");
+ return ret;
+ }
+
+ ret = regulator_get_voltage(priv->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
+ goto err_regulator_disable;
+ }
+ priv->common.vref_mv = ret / 1000;
+ dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
+
+ priv->aclk = devm_clk_get(&pdev->dev, "adc");
+ if (IS_ERR(priv->aclk)) {
+ ret = PTR_ERR(priv->aclk);
+ dev_err(&pdev->dev, "Can't get 'adc' clock\n");
+ goto err_regulator_disable;
+ }
+
+ ret = clk_prepare_enable(priv->aclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "adc clk enable failed\n");
+ goto err_regulator_disable;
+ }
+
+ ret = stm32f4_adc_clk_sel(pdev, priv);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "adc clk selection failed\n");
+ goto err_clk_disable;
+ }
+
+ ret = stm32_adc_irq_probe(pdev, priv);
+ if (ret < 0)
+ goto err_clk_disable;
+
+ platform_set_drvdata(pdev, &priv->common);
+
+ ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to populate DT children\n");
+ goto err_irq_remove;
+ }
+
+ return 0;
+
+err_irq_remove:
+ stm32_adc_irq_remove(pdev, priv);
+
+err_clk_disable:
+ clk_disable_unprepare(priv->aclk);
+
+err_regulator_disable:
+ regulator_disable(priv->vref);
+
+ return ret;
+}
+
+static int stm32_adc_remove(struct platform_device *pdev)
+{
+ struct stm32_adc_common *common = platform_get_drvdata(pdev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+
+ of_platform_depopulate(&pdev->dev);
+ stm32_adc_irq_remove(pdev, priv);
+ clk_disable_unprepare(priv->aclk);
+ regulator_disable(priv->vref);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_adc_of_match[] = {
+ { .compatible = "st,stm32f4-adc-core" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
+
+static struct platform_driver stm32_adc_driver = {
+ .probe = stm32_adc_probe,
+ .remove = stm32_adc_remove,
+ .driver = {
+ .name = "stm32-adc-core",
+ .of_match_table = stm32_adc_of_match,
+ },
+};
+module_platform_driver(stm32_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC core driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-adc-core");
diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
new file mode 100644
index 0000000..081fa5f
--- /dev/null
+++ b/drivers/iio/adc/stm32-adc-core.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STM32_ADC_H
+#define __STM32_ADC_H
+
+/*
+ * STM32 - ADC global register map
+ * ________________________________________________________
+ * | Offset | Register |
+ * --------------------------------------------------------
+ * | 0x000 | Master ADC1 |
+ * --------------------------------------------------------
+ * | 0x100 | Slave ADC2 |
+ * --------------------------------------------------------
+ * | 0x200 | Slave ADC3 |
+ * --------------------------------------------------------
+ * | 0x300 | Master & Slave common regs |
+ * --------------------------------------------------------
+ */
+#define STM32_ADC_MAX_ADCS 3
+#define STM32_ADCX_COMN_OFFSET 0x300
+
+/**
+ * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
+ * @base: control registers base cpu addr
+ * @vref_mv: vref voltage (mv)
+ */
+struct stm32_adc_common {
+ void __iomem *base;
+ int vref_mv;
+};
+
+#endif
--
1.9.1
^ permalink raw reply related
* [PATCH v3 1/6] Documentation: dt-bindings: Document STM32 ADC DT bindings
From: Fabrice Gasnier @ 2016-11-15 15:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479223861-21747-1-git-send-email-fabrice.gasnier@st.com>
This patch adds documentation of device tree bindings for the STM32 ADC.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
.../devicetree/bindings/iio/adc/st,stm32-adc.txt | 83 ++++++++++++++++++++++
1 file changed, 83 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
new file mode 100644
index 0000000..49ed82e
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
@@ -0,0 +1,83 @@
+STMicroelectronics STM32 ADC device driver
+
+STM32 ADC is a successive approximation analog-to-digital converter.
+It has several multiplexed input channels. Conversions can be performed
+in single, continuous, scan or discontinuous mode. Result of the ADC is
+stored in a left-aligned or right-aligned 32-bit data register.
+Conversions can be launched in software or using hardware triggers.
+
+The analog watchdog feature allows the application to detect if the input
+voltage goes beyond the user-defined, higher or lower thresholds.
+
+Each STM32 ADC block can have up to 3 ADC instances.
+
+Each instance supports two contexts to manage conversions, each one has its
+own configurable sequence and trigger:
+- regular conversion can be done in sequence, running in background
+- injected conversions have higher priority, and so have the ability to
+ interrupt regular conversion sequence (either triggered in SW or HW).
+ Regular sequence is resumed, in case it has been interrupted.
+
+Contents of a stm32 adc root node:
+-----------------------------------
+Required properties:
+- compatible: Should be "st,stm32f4-adc-core".
+- reg: Offset and length of the ADC block register set.
+- interrupts: Must contain the interrupt for ADC block.
+- clocks: Clock for the analog circuitry (common to all ADCs).
+- clock-names: Must be "adc".
+- interrupt-controller: Identifies the controller node as interrupt-parent
+- vref-supply: Phandle to the vref input analog reference voltage.
+- #interrupt-cells = <1>;
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties:
+- A pinctrl state named "default" for each ADC channel may be defined to set
+ inX ADC pins in mode of operation for analog input on external pin.
+
+Contents of a stm32 adc child node:
+-----------------------------------
+An ADC block node should contain at least one subnode, representing an
+ADC instance available on the machine.
+
+Required properties:
+- compatible: Should be "st,stm32f4-adc".
+- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
+- clocks: Input clock private to this ADC instance.
+- interrupt-parent: Phandle to the parent interrupt controller.
+- interrupts: IRQ Line for the ADC (e.g. may be 0 for adc at 0, 1 for adc at 100 or
+ 2 for adc at 200).
+- st,adc-channels: List of single-ended channels muxed for this ADC.
+ It can have up to 16 channels, numbered from 0 to 15 (resp. for in0..in15).
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
+ Documentation/devicetree/bindings/iio/iio-bindings.txt
+
+Example:
+ adc: adc at 40012000 {
+ compatible = "st,stm32f4-adc-core";
+ reg = <0x40012000 0x400>;
+ interrupts = <18>;
+ clocks = <&rcc 0 168>;
+ clock-names = "adc";
+ vref-supply = <®_vref>;
+ interrupt-controller;
+ pinctrl-names = "default";
+ pinctrl-0 = <&adc3_in8_pin>;
+
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc at 0 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ interrupt-parent = <&adc>;
+ interrupts = <0>;
+ st,adc-channels = <8>;
+ };
+ ...
+ other adc child nodes follow...
+ };
--
1.9.1
^ permalink raw reply related
* [PATCH v3 0/6] Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-11-15 15:30 UTC (permalink / raw)
To: linux-arm-kernel
This series adds support for STM32F4 ADC into IIO framework.
STM32F4 ADC is a 12-bit successive approximation analog-to-digital
converter with multiplexed input channels. Conversions can
be performed in single, continuous, scan or discontinuous mode.
Conversions can be launched in software or using hardware triggers.
This driver has been developed and tested on STM32F429 eval board.
It consist of a core driver, to manage common resources shared
between up to 3 ADC instances and an ADC driver to manage each adc
instance.
Changes in v3:
- Core driver moved to iio/adc.
- Build fix.
- Updates following Jonathan's and Lars's remarks.
- Binding: adc child clock is mandatory.
Changes in v2:
- Replace single driver model by MFD approach, to handle up to 3 ADCs
as separate devices. Each ADC device then registers a unique IIO
device.
- Make driver as simple as possible for the first instance, to ease
review. For now, I dropped complexity by removing injected support,
triggered buffer mode, dmas.
- Removed abstraction layer (indirection routines, ops) as only stm32f4
is supported.
Fabrice Gasnier (6):
Documentation: dt-bindings: Document STM32 ADC DT bindings
iio: adc: Add support for STM32 ADC core
iio: adc: Add support for STM32 ADC
ARM: configs: stm32: enable ADC driver
ARM: dts: stm32f429: Add adc support
ARM: dts: stm32f429: enable adc on eval board
.../devicetree/bindings/iio/adc/st,stm32-adc.txt | 83 ++++
arch/arm/boot/dts/stm32429i-eval.dts | 25 +
arch/arm/boot/dts/stm32f429.dtsi | 49 ++
arch/arm/configs/stm32_defconfig | 3 +
drivers/iio/adc/Kconfig | 23 +
drivers/iio/adc/Makefile | 2 +
drivers/iio/adc/stm32-adc-core.c | 303 ++++++++++++
drivers/iio/adc/stm32-adc-core.h | 52 +++
drivers/iio/adc/stm32-adc.c | 518 +++++++++++++++++++++
9 files changed, 1058 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
create mode 100644 drivers/iio/adc/stm32-adc-core.c
create mode 100644 drivers/iio/adc/stm32-adc-core.h
create mode 100644 drivers/iio/adc/stm32-adc.c
--
1.9.1
^ permalink raw reply
* [PATCH V7 1/3] tracing: add a possibility of exporting function trace to other places instead of ring buffer only
From: Steven Rostedt @ 2016-11-15 15:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAG2=9p-2ZU3Zk0musHcwAEk4nM6RR0U7JhgyjZ+zjLaVLwz1ig@mail.gmail.com>
On Tue, 15 Nov 2016 16:14:29 +0800
Chunyan Zhang <zhang.chunyan@linaro.org> wrote:
>
> > Then why have a
> >
> > if (export->write)
> >
> >
> > Is there every going to be a case where export will not have a write
> > function?
>
> There shouldn't be.
>
> I can move this if statement to the register_ftrace_export() to ensure
> users won't wrongly use it, that's saying the write() of trace_export
> has been set before being registered to 'ftrace_exports_list'.
>
Looks like it's already there:
+int register_ftrace_export(struct trace_export *export)
+{
+ if (WARN_ON_ONCE(!export->write))
+ return -1;
-- Steve
^ permalink raw reply
* [PATCH/RESEND] recordmcount: arm: Implement make_nop
From: Steven Rostedt @ 2016-11-15 15:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu-Cp775gbLxPcJQ4G2nEnwm9G_gxh5B1Sf2LGkkov_OZA@mail.gmail.com>
On Tue, 15 Nov 2016 14:19:44 +0000
Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
> On 19 October 2016 at 00:42, Stephen Boyd <sboyd@codeaurora.org> wrote:
> > In similar spirit to x86 and arm64 support, add a make_nop_arm()
> > to replace calls to mcount with a nop in sections that aren't
> > traced.
> >
> > Cc: Russell King <linux@arm.linux.org.uk>
> > Acked-by: Rabin Vincent <rabin@rab.in>
> > Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> > ---
> > scripts/recordmcount.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 65 insertions(+)
> >
> > diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c
> > index 5423a58d1b06..aeb34223167c 100644
> > --- a/scripts/recordmcount.c
> > +++ b/scripts/recordmcount.c
> > @@ -213,6 +213,59 @@ static int make_nop_x86(void *map, size_t const offset)
> > return 0;
> > }
> >
> > +static unsigned char ideal_nop4_arm_le[4] = { 0x00, 0x00, 0xa0, 0xe1 }; /* mov r0, r0 */
> > +static unsigned char ideal_nop4_arm_be[4] = { 0xe1, 0xa0, 0x00, 0x00 }; /* mov r0, r0 */
>
> Shouldn't you be taking the difference between BE8 and BE32 into
> account here? IIRC, BE8 uses little endian encoding for instructions.
>
I was just about to push this to linux-next (where I don't rebase). I'm
guessing I should hold off then.
Luckily, this was the last patch of my tree that I tested, and I can
just remove that one.
-- Steve
^ permalink raw reply
* [PATCH v2 2/3] efi/libstub: add random.c to ARM build
From: Ard Biesheuvel @ 2016-11-15 15:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <2189664.D28O3TYR8x@wuerfel>
On 15 November 2016 at 15:11, Arnd Bergmann <arnd@arndb.de> wrote:
> On Wednesday, November 2, 2016 9:37:13 AM CET Ard Biesheuvel wrote:
>> @@ -98,7 +100,7 @@
>> efi_memory_desc_t *md = (void *)memory_map + map_offset;
>> unsigned long slots;
>>
>> - slots = get_entry_num_slots(md, size, align);
>> + slots = get_entry_num_slots(md, size, ilog2(align));
>> MD_NUM_SLOTS(md) = slots;
>> total_slots += slots;
>> }
>> """
>>
>> This is because ARM does not have a division routine in the
>> decompressor, and the fact that the division by 'align' should always
>> involve a power of 2 is not visible to the compiler.
>>
>> If nobody objects, I will fold this in when applying
>>
>>
>
> I'm getting a link error here when building with -Os:
>
> drivers/firmware/efi/libstub/random.stub.o: In function `efi_random_alloc':
> random.c:(.text.efi_random_alloc+0x264): undefined reference to `__aeabi_llsr'
>
> If I compile this with -O2, the ilog2 gets inlined and everything
> works.
>
This is caused by the fact that 'start' and 'end are u64 rather than
unsigned long, and the stub does not have the u64 logical shift right
routines.
But interestingly, it does cover another issue with this code, i.e.,
that you cannot do allocations over 4 GB in the ARM stub, even on LPAE
capable hardware.
I will send out a patch to fix this.
Thanks,
Ard.
^ permalink raw reply
* [PATCH] ARM: ftrace: fix syscall name matching
From: Rabin Vincent @ 2016-11-15 15:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161114104008.36e9c40d@gandalf.local.home>
On Mon, Nov 14, 2016 at 10:40:08AM -0500, Steven Rostedt wrote:
> On Mon, 14 Nov 2016 13:40:17 +0000
> Russell King - ARM Linux <linux@armlinux.org.uk> wrote:
> > On Mon, Nov 14, 2016 at 02:03:45PM +0100, Rabin Vincent wrote:
> > > +static inline bool arch_syscall_match_sym_name(const char *sym,
> > > + const char *name)
> > > +{
> > > + /* Skip sys_ */
> > > + sym += 4;
> > > + name += 4;
> >
> > Is this really safe? What guarantees that we can wind forward four
> > bytes here? If it's always safe, it needs a better comment than just
> > two words.
>
> I believe it is, but a comment would do well.
I ended up just getting rid of the skip and comparing the whole name
instead. I've sent a v2.
^ permalink raw reply
* [PATCH v2 2/3] efi/libstub: add random.c to ARM build
From: Arnd Bergmann @ 2016-11-15 15:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu_=et=2zHBTYOr9thz2kS0cXHHYg96oWGRdD3D10fqXtw@mail.gmail.com>
On Wednesday, November 2, 2016 9:37:13 AM CET Ard Biesheuvel wrote:
> @@ -98,7 +100,7 @@
> efi_memory_desc_t *md = (void *)memory_map + map_offset;
> unsigned long slots;
>
> - slots = get_entry_num_slots(md, size, align);
> + slots = get_entry_num_slots(md, size, ilog2(align));
> MD_NUM_SLOTS(md) = slots;
> total_slots += slots;
> }
> """
>
> This is because ARM does not have a division routine in the
> decompressor, and the fact that the division by 'align' should always
> involve a power of 2 is not visible to the compiler.
>
> If nobody objects, I will fold this in when applying
>
>
I'm getting a link error here when building with -Os:
drivers/firmware/efi/libstub/random.stub.o: In function `efi_random_alloc':
random.c:(.text.efi_random_alloc+0x264): undefined reference to `__aeabi_llsr'
If I compile this with -O2, the ilog2 gets inlined and everything
works.
Arnd
^ permalink raw reply
* [BUG] Suspicious RCU usage: NFS client - 4.9-rc4
From: Anna Schumaker @ 2016-11-15 15:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161115150706.GW1041@n2100.armlinux.org.uk>
Hi Russel,
On 11/15/2016 10:07 AM, Russell King - ARM Linux wrote:
> While booting two of my machines, I noticed this splat on the console.
> Not sure if this is due to a RCU change or NFS change, so adding folk
> on both sides.
The patch to fix this went into rc5 :)
Thanks for reporting!
Anna
>
> ===============================
> [ INFO: suspicious RCU usage. ]
> 4.9.0-rc4+ #2050 Not tainted
> -------------------------------
> net/sunrpc/clnt.c:2773 suspicious rcu_dereference_check() usage!
>
> other info that might help us debug this:
>
>
> rcu_scheduler_active = 1, debug_locks = 0
> 1 lock held by mount.nfs/1499:
> #0:
> (
> &(&nn->nfs_client_lock)->rlock
> ){+.+...}
> , at:
> [<c026c6d4>] nfs_get_client+0xe8/0x580
>
> stack backtrace:
> CPU: 1 PID: 1499 Comm: mount.nfs Not tainted 4.9.0-rc4+ #2050
> Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
> Backtrace:
> [<c0013b84>] (dump_backtrace) from [<c0013dc4>] (show_stack+0x18/0x1c)
> r6:60010013 r5:ffffffff r4:00000000 r3:00000000
> [<c0013dac>] (show_stack) from [<c035d300>] (dump_stack+0xa4/0xdc)
> [<c035d25c>] (dump_stack) from [<c008093c>] (lockdep_rcu_suspicious+0xbc/0x11c)
> r6:00000ad5 r5:c08f4078 r4:ee58a4c0 r3:ee58a4c0
> [<c0080880>] (lockdep_rcu_suspicious) from [<c0721ba4>] (rpc_clnt_xprt_switch_has_addr+0x134/0x15c)
> r7:ef27dd80 r6:c0ae3972 r5:eeb5a110 r4:ee930400
> [<c0721a70>] (rpc_clnt_xprt_switch_has_addr) from [<c026c814>] (nfs_get_client+0x228/0x580)
> r6:ef27ddd0 r5:eeb5a110 r4:00000000
> [<c026c5ec>] (nfs_get_client) from [<c026cc20>] (nfs_create_server+0xb4/0x3d4)
> r10:edaebe6c r9:c0a84d54 r8:edaebd30 r7:edf7b480 r6:00000000 r5:ed50a000
> r4:eeb5a000
> [<c026cb6c>] (nfs_create_server) from [<c028a2f8>] (nfs3_create_server+0x10/0x28)
> r10:00000000 r9:edbea200 r8:c0a84d54 r7:edaebe6c r6:00000000 r5:00000001
> r4:eeb5a000
> [<c028a2e8>] (nfs3_create_server) from [<c02792a0>] (nfs_try_mount+0x188/0x280)
> r4:eeb5a000 r3:c028a2e8
> [<c0279118>] (nfs_try_mount) from [<c027b28c>] (nfs_fs_mount+0x420/0x8c0)
> r10:00000000 r9:00000400 r8:edbea200 r7:edbea200 r6:c0a84d54 r5:edbea206
> r4:eeb5a000
> [<c027ae6c>] (nfs_fs_mount) from [<c016eaf4>] (mount_fs+0x1c/0xa8)
> r10:ed4f5000 r9:edbea340 r8:c0a82204 r7:c019091c r6:c0a82204 r5:edbea200
> r4:ed8a3700
> [<c016ead8>] (mount_fs) from [<c018d6d0>] (vfs_kern_mount+0x5c/0x138)
> r6:00000000 r5:edbea200 r4:ed8a3700
> [<c018d674>] (vfs_kern_mount) from [<c019091c>] (do_mount+0x164/0xc58)
> r10:c0191774 r8:edbea200 r7:00000020 r6:ed4f5000 r5:c0a82204 r4:00000000
> [<c01907b8>] (do_mount) from [<c0191774>] (SyS_mount+0x7c/0xa4)
> r10:00000000 r9:edaea000 r8:0078c0f8 r7:00000000 r6:ed4f5000 r5:edbea200
> r4:edbea340
> [<c01916f8>] (SyS_mount) from [<c000fd60>] (ret_fast_syscall+0x0/0x1c)
> r8:c000ff04 r7:00000015 r6:0078dd50 r5:bed2bad0 r4:007968a8
>
>
^ permalink raw reply
* [BUG] Suspicious RCU usage: NFS client - 4.9-rc4
From: Russell King - ARM Linux @ 2016-11-15 15:07 UTC (permalink / raw)
To: linux-arm-kernel
While booting two of my machines, I noticed this splat on the console.
Not sure if this is due to a RCU change or NFS change, so adding folk
on both sides.
===============================
[ INFO: suspicious RCU usage. ]
4.9.0-rc4+ #2050 Not tainted
-------------------------------
net/sunrpc/clnt.c:2773 suspicious rcu_dereference_check() usage!
other info that might help us debug this:
rcu_scheduler_active = 1, debug_locks = 0
1 lock held by mount.nfs/1499:
#0:
(
&(&nn->nfs_client_lock)->rlock
){+.+...}
, at:
[<c026c6d4>] nfs_get_client+0xe8/0x580
stack backtrace:
CPU: 1 PID: 1499 Comm: mount.nfs Not tainted 4.9.0-rc4+ #2050
Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
Backtrace:
[<c0013b84>] (dump_backtrace) from [<c0013dc4>] (show_stack+0x18/0x1c)
r6:60010013 r5:ffffffff r4:00000000 r3:00000000
[<c0013dac>] (show_stack) from [<c035d300>] (dump_stack+0xa4/0xdc)
[<c035d25c>] (dump_stack) from [<c008093c>] (lockdep_rcu_suspicious+0xbc/0x11c)
r6:00000ad5 r5:c08f4078 r4:ee58a4c0 r3:ee58a4c0
[<c0080880>] (lockdep_rcu_suspicious) from [<c0721ba4>] (rpc_clnt_xprt_switch_has_addr+0x134/0x15c)
r7:ef27dd80 r6:c0ae3972 r5:eeb5a110 r4:ee930400
[<c0721a70>] (rpc_clnt_xprt_switch_has_addr) from [<c026c814>] (nfs_get_client+0x228/0x580)
r6:ef27ddd0 r5:eeb5a110 r4:00000000
[<c026c5ec>] (nfs_get_client) from [<c026cc20>] (nfs_create_server+0xb4/0x3d4)
r10:edaebe6c r9:c0a84d54 r8:edaebd30 r7:edf7b480 r6:00000000 r5:ed50a000
r4:eeb5a000
[<c026cb6c>] (nfs_create_server) from [<c028a2f8>] (nfs3_create_server+0x10/0x28)
r10:00000000 r9:edbea200 r8:c0a84d54 r7:edaebe6c r6:00000000 r5:00000001
r4:eeb5a000
[<c028a2e8>] (nfs3_create_server) from [<c02792a0>] (nfs_try_mount+0x188/0x280)
r4:eeb5a000 r3:c028a2e8
[<c0279118>] (nfs_try_mount) from [<c027b28c>] (nfs_fs_mount+0x420/0x8c0)
r10:00000000 r9:00000400 r8:edbea200 r7:edbea200 r6:c0a84d54 r5:edbea206
r4:eeb5a000
[<c027ae6c>] (nfs_fs_mount) from [<c016eaf4>] (mount_fs+0x1c/0xa8)
r10:ed4f5000 r9:edbea340 r8:c0a82204 r7:c019091c r6:c0a82204 r5:edbea200
r4:ed8a3700
[<c016ead8>] (mount_fs) from [<c018d6d0>] (vfs_kern_mount+0x5c/0x138)
r6:00000000 r5:edbea200 r4:ed8a3700
[<c018d674>] (vfs_kern_mount) from [<c019091c>] (do_mount+0x164/0xc58)
r10:c0191774 r8:edbea200 r7:00000020 r6:ed4f5000 r5:c0a82204 r4:00000000
[<c01907b8>] (do_mount) from [<c0191774>] (SyS_mount+0x7c/0xa4)
r10:00000000 r9:edaea000 r8:0078c0f8 r7:00000000 r6:ed4f5000 r5:edbea200
r4:edbea340
[<c01916f8>] (SyS_mount) from [<c000fd60>] (ret_fast_syscall+0x0/0x1c)
r8:c000ff04 r7:00000015 r6:0078dd50 r5:bed2bad0 r4:007968a8
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
^ permalink raw reply
* [PATCH] ARM: fix backtrace
From: Russell King @ 2016-11-15 15:01 UTC (permalink / raw)
To: linux-arm-kernel
Recent kernels have changed their behaviour to be more inconsistent
when handling printk continuations. With todays kernels, the output
looks sane on the console, but dmesg splits individual printk()s which
do not have the KERN_CONT prefix into separate lines.
Since the assembly code is not trivial to add the KERN_CONT, and we
ideally want to avoid using KERN_CONT (as multiple printk()s can race
between different threads), convert the assembly dumping the register
values to C code, and have the C code build the output a line at a
time before dumping to the console.
This avoids the KERN_CONT issue, and also avoids situations where the
output is intermixed with other console activity.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
Noticed due to a NFS bug in 4.9-rc.
arch/arm/kernel/traps.c | 20 ++++++++++++++++++++
arch/arm/lib/backtrace.S | 37 +++----------------------------------
2 files changed, 23 insertions(+), 34 deletions(-)
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 2771ba243f36..ff2ae872d555 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -75,6 +75,26 @@ void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long
dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
}
+void dump_backtrace_stm(u32 *stack, u32 instruction)
+{
+ char str[80], *p;
+ unsigned int x;
+ int reg;
+
+ for (reg = 10, x = 0, p = str; reg >= 0; reg--) {
+ if (instruction & BIT(reg)) {
+ p += sprintf(p, " r%d:%08x", reg, *stack--);
+ if (++x == 6) {
+ x = 0;
+ p = str;
+ printk("%s\n", str);
+ }
+ }
+ }
+ if (p != str)
+ printk("%s\n", str);
+}
+
#ifndef CONFIG_ARM_UNWIND
/*
* Stack pointers should always be within the kernels view of
diff --git a/arch/arm/lib/backtrace.S b/arch/arm/lib/backtrace.S
index fab5a50503ae..7d7952e5a3b1 100644
--- a/arch/arm/lib/backtrace.S
+++ b/arch/arm/lib/backtrace.S
@@ -10,6 +10,7 @@
* 27/03/03 Ian Molton Clean up CONFIG_CPU
*
*/
+#include <linux/kern_levels.h>
#include <linux/linkage.h>
#include <asm/assembler.h>
.text
@@ -83,13 +84,13 @@ for_each_frame: tst frame, mask @ Check for address exceptions
teq r3, r1, lsr #11
ldreq r0, [frame, #-8] @ get sp
subeq r0, r0, #4 @ point at the last arg
- bleq .Ldumpstm @ dump saved registers
+ bleq dump_backtrace_stm @ dump saved registers
1004: ldr r1, [sv_pc, #0] @ if stmfd sp!, {..., fp, ip, lr, pc}
ldr r3, .Ldsi @ instruction exists,
teq r3, r1, lsr #11
subeq r0, frame, #16
- bleq .Ldumpstm @ dump saved registers
+ bleq dump_backtrace_stm @ dump saved registers
teq sv_fp, #0 @ zero saved fp means
beq no_frame @ no further frames
@@ -112,38 +113,6 @@ ENDPROC(c_backtrace)
.long 1004b, 1006b
.popsection
-#define instr r4
-#define reg r5
-#define stack r6
-
-.Ldumpstm: stmfd sp!, {instr, reg, stack, r7, lr}
- mov stack, r0
- mov instr, r1
- mov reg, #10
- mov r7, #0
-1: mov r3, #1
- ARM( tst instr, r3, lsl reg )
- THUMB( lsl r3, reg )
- THUMB( tst instr, r3 )
- beq 2f
- add r7, r7, #1
- teq r7, #6
- moveq r7, #0
- adr r3, .Lcr
- addne r3, r3, #1 @ skip newline
- ldr r2, [stack], #-4
- mov r1, reg
- adr r0, .Lfp
- bl printk
-2: subs reg, reg, #1
- bpl 1b
- teq r7, #0
- adrne r0, .Lcr
- blne printk
- ldmfd sp!, {instr, reg, stack, r7, pc}
-
-.Lfp: .asciz " r%d:%08x%s"
-.Lcr: .asciz "\n"
.Lbad: .asciz "Backtrace aborted due to bad frame pointer <%p>\n"
.align
.Ldsi: .word 0xe92dd800 >> 11 @ stmfd sp!, {... fp, ip, lr, pc}
--
2.7.4
^ permalink raw reply related
* [RFC v2 3/8] iommu/dma: Allow MSI-only cookies
From: Robin Murphy @ 2016-11-15 14:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <0af2373f-db86-e93f-b3e1-01c3076ce6fc@redhat.com>
On 14/11/16 23:23, Auger Eric wrote:
> Hi Robin,
>
> On 14/11/2016 13:36, Robin Murphy wrote:
>> On 04/11/16 11:24, Eric Auger wrote:
>>> From: Robin Murphy <robin.murphy@arm.com>
>>>
>>> IOMMU domain users such as VFIO face a similar problem to DMA API ops
>>> with regard to mapping MSI messages in systems where the MSI write is
>>> subject to IOMMU translation. With the relevant infrastructure now in
>>> place for managed DMA domains, it's actually really simple for other
>>> users to piggyback off that and reap the benefits without giving up
>>> their own IOVA management, and without having to reinvent their own
>>> wheel in the MSI layer.
>>>
>>> Allow such users to opt into automatic MSI remapping by dedicating a
>>> region of their IOVA space to a managed cookie.
>>>
>>> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
>>> Signed-off-by: Eric Auger <eric.auger@redhat.com>
>>
>> OK, following the discussion elsewhere I've had a go at the less stupid,
>> but more involved, version. Thoughts?
>
> Conceptually I don't have any major objection with the minimalist
> allocation scheme all the more so it follows Joerg's guidance. Maybe the
> only thing is we do not check we don't overshoot the reserved msi-region.
Yes, I thought about that and came to the conclusion that it was hard to
justify the extra complexity. Since the caller has to calculate an
appropriate region size to reserve anyway, we might as well just trust
it to be correct. And if the caller did get things wrong, then one or
other iommu_map() is going to fail on the overlapping IOVAs anyway.
>
> Besides there are 2 issues reported below.
>
>>
>> Robin.
>>
>> ----->8-----
>> From: Robin Murphy <robin.murphy@arm.com>
>> Subject: [RFC PATCH] iommu/dma: Allow MSI-only cookies
>>
>> IOMMU domain users such as VFIO face a similar problem to DMA API ops
>> with regard to mapping MSI messages in systems where the MSI write is
>> subject to IOMMU translation. With the relevant infrastructure now in
>> place for managed DMA domains, it's actually really simple for other
>> users to piggyback off that and reap the benefits without giving up
>> their own IOVA management, and without having to reinvent their own
>> wheel in the MSI layer.
>>
>> Allow such users to opt into automatic MSI remapping by dedicating a
>> region of their IOVA space to a managed cookie, and extend the mapping
>> routine to implement a trivial linear allocator in such cases, to avoid
>> the needless overhead of a full-blown IOVA domain.
>>
>> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
>> ---
>> drivers/iommu/dma-iommu.c | 118 ++++++++++++++++++++++++++++++++++++----------
>> include/linux/dma-iommu.h | 6 +++
>> 2 files changed, 100 insertions(+), 24 deletions(-)
>>
>> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
>> index c5ab8667e6f2..33d66a8273c6 100644
>> --- a/drivers/iommu/dma-iommu.c
>> +++ b/drivers/iommu/dma-iommu.c
>> @@ -37,10 +37,19 @@ struct iommu_dma_msi_page {
>> phys_addr_t phys;
>> };
>>
>> +enum iommu_dma_cookie_type {
>> + IOMMU_DMA_IOVA_COOKIE,
>> + IOMMU_DMA_MSI_COOKIE,
>> +};
>> +
>> struct iommu_dma_cookie {
>> - struct iova_domain iovad;
>> - struct list_head msi_page_list;
>> - spinlock_t msi_lock;
>> + union {
>> + struct iova_domain iovad;
>> + dma_addr_t msi_iova;
>> + };
>> + struct list_head msi_page_list;
>> + spinlock_t msi_lock;
>> + enum iommu_dma_cookie_type type;
>> };
>>
>> static inline struct iova_domain *cookie_iovad(struct iommu_domain *domain)
>> @@ -53,6 +62,19 @@ int iommu_dma_init(void)
>> return iova_cache_get();
>> }
>>
>> +static struct iommu_dma_cookie *__cookie_alloc(enum iommu_dma_cookie_type type)
>> +{
>> + struct iommu_dma_cookie *cookie;
>> +
>> + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
>> + if (cookie) {
>> + spin_lock_init(&cookie->msi_lock);
>> + INIT_LIST_HEAD(&cookie->msi_page_list);
>> + cookie->type = type;
>> + }
>> + return cookie;
>> +}
>> +
>> /**
>> * iommu_get_dma_cookie - Acquire DMA-API resources for a domain
>> * @domain: IOMMU domain to prepare for DMA-API usage
>> @@ -62,25 +84,53 @@ int iommu_dma_init(void)
>> */
>> int iommu_get_dma_cookie(struct iommu_domain *domain)
>> {
>> - struct iommu_dma_cookie *cookie;
>> -
>> if (domain->iova_cookie)
>> return -EEXIST;
>>
>> - cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
>> - if (!cookie)
>> + domain->iova_cookie = __cookie_alloc(IOMMU_DMA_IOVA_COOKIE);
>> + if (!domain->iova_cookie)
>> return -ENOMEM;
>>
>> - spin_lock_init(&cookie->msi_lock);
>> - INIT_LIST_HEAD(&cookie->msi_page_list);
>> - domain->iova_cookie = cookie;
>> return 0;
>> }
>> EXPORT_SYMBOL(iommu_get_dma_cookie);
>>
>> /**
>> + * iommu_get_msi_cookie - Acquire just MSI remapping resources
>> + * @domain: IOMMU domain to prepare
>> + * @base: Start address of IOVA region for MSI mappings
>> + *
>> + * Users who manage their own IOVA allocation and do not want DMA API support,
>> + * but would still like to take advantage of automatic MSI remapping, can use
>> + * this to initialise their own domain appropriately. Users should reserve a
>> + * contiguous IOVA region, starting at @base, large enough to accommodate the
>> + * number of PAGE_SIZE mappings necessary to cover every MSI doorbell address
>> + * used by the devices attached to @domain.
>> + */
>> +int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base)
>> +{
>> + struct iommu_dma_cookie *cookie;
>> +
>> + if (domain->type != IOMMU_DOMAIN_UNMANAGED)
>> + return -EINVAL;
>> +
>> + if (domain->iova_cookie)
>> + return -EEXIST;
>> +
>> + cookie = __cookie_alloc(IOMMU_DMA_IOVA_COOKIE);
> must be IOMMU_DMA_MSI_COOKIE else it has bad consequences.
Oops, quite right!
>> + if (!cookie)
>> + return -ENOMEM;
>> +
>> + cookie->msi_iova = base;
>> + domain->iova_cookie = cookie;
>> + return 0;
>> +}
>> +EXPORT_SYMBOL(iommu_get_msi_cookie);
>> +
>> +/**
>> * iommu_put_dma_cookie - Release a domain's DMA mapping resources
>> - * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
>> + * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie() or
>> + * iommu_get_msi_cookie()
>> *
>> * IOMMU drivers should normally call this from their domain_free callback.
>> */
>> @@ -92,7 +142,7 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
>> if (!cookie)
>> return;
>>
>> - if (cookie->iovad.granule)
>> + if (cookie->type == IOMMU_DMA_IOVA_COOKIE && cookie->iovad.granule)
>> put_iova_domain(&cookie->iovad);
>>
>> list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
>> @@ -137,11 +187,12 @@ static void iova_reserve_pci_windows(struct pci_dev *dev,
>> int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
>> u64 size, struct device *dev)
>> {
>> - struct iova_domain *iovad = cookie_iovad(domain);
>> + struct iommu_dma_cookie *cookie = domain->iova_cookie;
>> + struct iova_domain *iovad = &cookie->iovad;
>> unsigned long order, base_pfn, end_pfn;
>>
>> - if (!iovad)
>> - return -ENODEV;
>> + if (!cookie || cookie->type != IOMMU_DMA_IOVA_COOKIE)
>> + return -EINVAL;
>>
>> /* Use the smallest supported page size for IOVA granularity */
>> order = __ffs(domain->pgsize_bitmap);
>> @@ -644,11 +695,21 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
>> {
>> struct iommu_dma_cookie *cookie = domain->iova_cookie;
>> struct iommu_dma_msi_page *msi_page;
>> - struct iova_domain *iovad = &cookie->iovad;
>> + struct iova_domain *iovad;
>> struct iova *iova;
>> int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
>> + size_t size;
>> +
>> + if (cookie->type == IOMMU_DMA_IOVA_COOKIE) {
>> + iovad = &cookie->iovad;
>> + size = iovad->granule;
>> + } else {
>> + iovad = NULL;
>> + size = PAGE_SIZE;
>> + }
>> +
>> + msi_addr &= ~(phys_addr_t)(size - 1);
>>
>> - msi_addr &= ~(phys_addr_t)iova_mask(iovad);
>> list_for_each_entry(msi_page, &cookie->msi_page_list, list)
>> if (msi_page->phys == msi_addr)
>> return msi_page;
>> @@ -657,13 +718,18 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
>> if (!msi_page)
>> return NULL;
>>
>> - iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));
>> - if (!iova)
>> - goto out_free_page;
>> -
>> msi_page->phys = msi_addr;
>> - msi_page->iova = iova_dma_addr(iovad, iova);
>> - if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule, prot))
>> + if (iovad) {
>> + iova = __alloc_iova(domain, size, dma_get_mask(dev));
>> + if (!iova)
>> + goto out_free_page;
>> + msi_page->iova = iova_dma_addr(iovad, iova);
>> + } else {
>> + msi_page->iova = cookie->msi_iova;
>> + cookie->msi_iova += size;
>> + }
>> +
>> + if (iommu_map(domain, msi_page->iova, msi_addr, size, prot))
>> goto out_free_iova;
>>
>> INIT_LIST_HEAD(&msi_page->list);
>> @@ -671,7 +737,10 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
>> return msi_page;
>>
>> out_free_iova:
>> - __free_iova(iovad, iova);
>> + if (iovad)
>> + __free_iova(iovad, iova);
>> + else
>> + cookie->msi_iova -= size;
>> out_free_page:
>> kfree(msi_page);
>> return NULL;
>> @@ -716,3 +785,4 @@ void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
>> msg->address_lo += lower_32_bits(msi_page->iova);
>> }
>> }
>
> in iommu_dma_map_msi_msg there is another issue at:
> msg->address_lo &= iova_mask(&cookie->iovad);
> iovad might not exist
Ah yes, I'd overlooked that one, thanks - seems compile-testing isn't
that magic bullet...
Completely factoring out the alloc/free seemed like overkill when all
the IOVA stuff seemed to be in the one function, but in light of this
I'll have another go and see if I can get it any tidier - the RFC was
primarily for the simplified interface and allocator. In the meantime I
think your fixed-up version looks correct.
> Thanks
>
> Eric
>
>> +
(now, this line I had at least already taken care of. Oh well)
Thanks,
Robin.
>> diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
>> index 32c589062bd9..d69932474576 100644
>> --- a/include/linux/dma-iommu.h
>> +++ b/include/linux/dma-iommu.h
>> @@ -27,6 +27,7 @@ int iommu_dma_init(void);
>>
>> /* Domain management interface for IOMMU drivers */
>> int iommu_get_dma_cookie(struct iommu_domain *domain);
>> +int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base);
>> void iommu_put_dma_cookie(struct iommu_domain *domain);
>>
>> /* Setup call for arch DMA mapping code */
>> @@ -82,6 +83,11 @@ static inline int iommu_get_dma_cookie(struct iommu_domain *domain)
>> return -ENODEV;
>> }
>>
>> +static inline int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base)
>> +{
>> + return -ENODEV;
>> +}
>> +
>> static inline void iommu_put_dma_cookie(struct iommu_domain *domain)
>> {
>> }
>>
^ permalink raw reply
* [PATCH] arm/arm64: KVM: VGIC: limit ITARGETSR bits to number of VCPUs
From: Marc Zyngier @ 2016-11-15 14:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161115142749.19955-1-andre.przywara@arm.com>
Hi Andre,
On 15/11/16 14:27, Andre Przywara wrote:
> The GICv2 spec says in section 4.3.12 that a "CPU targets field bit that
> corresponds to an unimplemented CPU interface is RAZ/WI."
> Currently we allow the guest to write any value in there and it can
> read that back.
> Mask the written value with the proper CPU mask to be spec compliant.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
> virt/kvm/arm/vgic/vgic-mmio-v2.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v2.c b/virt/kvm/arm/vgic/vgic-mmio-v2.c
> index b44b359..e59d4c7 100644
> --- a/virt/kvm/arm/vgic/vgic-mmio-v2.c
> +++ b/virt/kvm/arm/vgic/vgic-mmio-v2.c
> @@ -129,6 +129,7 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
> unsigned long val)
> {
> u32 intid = VGIC_ADDR_TO_INTID(addr, 8);
> + u8 cpu_mask = (1 << atomic_read(&vcpu->kvm->online_vcpus)) - 1;
For the sake of avoiding open-coding things, how about using GENMASK?
> int i;
>
> /* GICD_ITARGETSR[0-7] are read-only */
> @@ -141,7 +142,7 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
>
> spin_lock(&irq->irq_lock);
>
> - irq->targets = (val >> (i * 8)) & 0xff;
> + irq->targets = ((val >> (i * 8)) & 0xff) & cpu_mask;
Can't you just drop the '& 0xff' part, since cpu_mask is guaranteed to
be more restrictive?
> target = irq->targets ? __ffs(irq->targets) : 0;
> irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target);
>
>
Thanks,
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* LEDs that change brightness "itself" -- that's a trigger. Re: PM regression with LED changes in next-20161109
From: Jacek Anaszewski @ 2016-11-15 14:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <e1c1db1e-2a78-bbe0-e3bc-84be449dbcb6@redhat.com>
On 11/15/2016 03:30 PM, Hans de Goede wrote:
> Hi,
>
> On 15-11-16 15:04, Jacek Anaszewski wrote:
>> On 11/15/2016 02:48 PM, Hans de Goede wrote:
>>> Hi,
>>>
>>> On 15-11-16 14:28, Jacek Anaszewski wrote:
>>>> On 11/15/2016 01:06 PM, Hans de Goede wrote:
>>>>> Hi,
>>>>>
>>>>> On 15-11-16 12:48, Pavel Machek wrote:
>>>>>> Hi!
>>>>>>
>>>>>>>>>> The LED you are talking about _has_ a trigger, implemented in
>>>>>>>>>> hardware. That trigger can change LED brightness behind kernel's
>>>>>>>>>> (and
>>>>>>>>>> userspace's) back. Don't pretend the trigger does not exist, it
>>>>>>>>>> does.
>>>>>>>>>>
>>>>>>>>>> And when you do that, you'll have nice place to report changes to
>>>>>>>>>> userspace -- trigger can now export that information, and offer
>>>>>>>>>> poll()
>>>>>>>>>> interface.
>>>>>>>>>
>>>>>>>>> Well, that sounds interesting. It is logically justifiable.
>>>>>>>>
>>>>>>>> Thanks.
>>>>>>>>
>>>>>>>>> I initially proposed exactly this solution, with recently
>>>>>>>>> added userspace LED being a trigger listener. It seems a bit
>>>>>>>>> awkward though. How would you listen to the trigger events?
>>>>>>>>
>>>>>>>> Trigger exposes a file in sysfs, with poll() working on that file
>>>>>>>
>>>>>>> Hmm, a new file would give the advantage of making it easy for
>>>>>>> userspace to see if the trigger is poll-able, this is likely
>>>>>>> better then my own proposal I just send.
>>>>>>
>>>>>> Good.
>>>>>>
>>>>>>>> (and
>>>>>>>> probably read exposing the current brightness).
>>>>>>>
>>>>>>> If we do this, can we please make it mirror brightness, iow
>>>>>>> also make it writable, that will make it easier for userspace
>>>>>>> to deal with it. We can simply re-use the existing show / store
>>>>>>> methods for brightness for this.
>>>>>>
>>>>>> Actually, echo 0 > brightness disables the trigger, IIRC. I'd avoid
>>>>>> that here, you want to be able to turn off the backlight but still
>>>>>> keep the trigger (and be notified of future changes).
>>>>>
>>>>> True, that is easy to do the store method will just need to call
>>>>> led_set_brightness_nosleep instead of led_set_brightness, this
>>>>> will skip the checks to stop blinking in led_set_brightness and
>>>>> otherwise is equivalent.
>>>>>
>>>>>>> I suggest we call it:
>>>>>>>
>>>>>>> trigger_brightness
>>>>>>>
>>>>>>> And only register it when a poll-able trigger is present.
>>>>>>
>>>>>> I'd call it 'current_brightness', but that's no big deal. Yes, only
>>>>>> registering it for poll-able triggers makes sense.
>>>>>
>>>>> current_brightness works for me. I will take a shot a patch-set
>>>>> implementing this.
>>>>
>>>> Word "current" is not precise here.
>>>>
>>>> It can be thought of as either last brightness set by the
>>>> user or the brightness currently written to the device
>>>> (returned by brightness file).
>>>>
>>>> There is a semantic discrepancy in our requirements -
>>>> we want the file representing both permanent brightness
>>>> set by the user and brightness set by the hardware.
>>>>
>>>> The two stand in contradiction to each other since
>>>> brightness set by the user can be adjusted by the hardware.
>>>>
>>>> Reading the file shouldn't update brightness property of
>>>> struct led_classdev, so it shouldn't call led_update_brightness()
>>>> but it still should allow reading brightness set by the
>>>> hardware, as a result of each POLLPRI event. So in fact in
>>>> the same time it should report both according to our requirements
>>>> which is impossible. Do we need three brightness files ?
>>>
>>> I don't think so, current_brightness actually is an accurate
>>> name, if the brightness was last changed by writing from
>>> sysfs, the keyboard backlight will honor that and the current_brightness
>>> attribute will show the brightness last set through writing it,
>>> which matches the actual current brightness of the keyboard backlight.
>>>
>>> Likewise if it was changed with the hotkey last then the keyboard
>>> backlight brightness will be changed and reading from current_brightness
>>> will return the new actual brightness. Basically reading from this
>>> file will be no different then reading from the normal "brightness"
>>> file the difference will be in that it is poll-able and that
>>> writing 0 turns off the LED without stopping blinking.
>>
>> If so then when software blinking enabled, it will return 0 on low
>> blink cycle no matter what current brightness level is.
>
> For software blinking there will not be a current_brightness atrribute,
> as we will only add that for LEDs with poll-able triggers.
>
> Also during the low cycle the LED is off, so its brightness at the
> moment of reading (iow currently) is 0.
OK, I wanted to make sure that we know what we've agreed on.
Earlier there were doubts raised about brightness during
software blinking being periodically reported 0, but actually
your reasoning seems to be the best answer to that.
--
Best regards,
Jacek Anaszewski
^ permalink raw reply
* [GIT PULL] STM32 DT changes for v4.10 #2
From: Alexandre Torgue @ 2016-11-15 14:40 UTC (permalink / raw)
To: linux-arm-kernel
Hi Olof, Arnd and Kevin,
Please consider this second round of STM32 DT updates for v4.10:
The following changes since commit f6dbbff4f0af1a5c0d6eaf414572b5eff7a73a8b:
ARM: dts: stm32f429: add LSI and LSE clocks (2016-11-04 15:08:08 +0100)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/atorgue/stm32.git
tags/stm32-dt-for-v4.10-2
for you to fetch changes up to 2ecaa477b404d707bac93c56f09a0a6162e04ed7:
ARM: dts: stm32f429: Add QSPI clock (2016-11-15 13:59:11 +0100)
----------------------------------------------------------------
STM32 DT updates for v4.10, round 2.
Highlights:
----------
- Add support of STM32F746 MCU and STM32746G-Eval board
- Add QSPI support for STM32F469-Disco board
----------------------------------------------------------------
Alexandre TORGUE (1):
ARM: dts: Add STM32F746 MCU and STM32746g-EVAL board
Gabriel Fernandez (1):
ARM: dts: stm32f429: Add QSPI clock
arch/arm/boot/dts/Makefile | 3 +-
arch/arm/boot/dts/stm32746g-eval.dts | 96 +++++++++++
arch/arm/boot/dts/stm32f469-disco.dts | 4 +
arch/arm/boot/dts/stm32f746.dtsi | 304
++++++++++++++++++++++++++++++++++
4 files changed, 406 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/boot/dts/stm32746g-eval.dts
create mode 100644 arch/arm/boot/dts/stm32f746.dtsi
^ permalink raw reply
* LEDs that change brightness "itself" -- that's a trigger. Re: PM regression with LED changes in next-20161109
From: Hans de Goede @ 2016-11-15 14:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <3bb538bb-3c80-70c7-2c44-46b174c90503@samsung.com>
Hi,
On 15-11-16 15:04, Jacek Anaszewski wrote:
> On 11/15/2016 02:48 PM, Hans de Goede wrote:
>> Hi,
>>
>> On 15-11-16 14:28, Jacek Anaszewski wrote:
>>> On 11/15/2016 01:06 PM, Hans de Goede wrote:
>>>> Hi,
>>>>
>>>> On 15-11-16 12:48, Pavel Machek wrote:
>>>>> Hi!
>>>>>
>>>>>>>>> The LED you are talking about _has_ a trigger, implemented in
>>>>>>>>> hardware. That trigger can change LED brightness behind kernel's
>>>>>>>>> (and
>>>>>>>>> userspace's) back. Don't pretend the trigger does not exist, it
>>>>>>>>> does.
>>>>>>>>>
>>>>>>>>> And when you do that, you'll have nice place to report changes to
>>>>>>>>> userspace -- trigger can now export that information, and offer
>>>>>>>>> poll()
>>>>>>>>> interface.
>>>>>>>>
>>>>>>>> Well, that sounds interesting. It is logically justifiable.
>>>>>>>
>>>>>>> Thanks.
>>>>>>>
>>>>>>>> I initially proposed exactly this solution, with recently
>>>>>>>> added userspace LED being a trigger listener. It seems a bit
>>>>>>>> awkward though. How would you listen to the trigger events?
>>>>>>>
>>>>>>> Trigger exposes a file in sysfs, with poll() working on that file
>>>>>>
>>>>>> Hmm, a new file would give the advantage of making it easy for
>>>>>> userspace to see if the trigger is poll-able, this is likely
>>>>>> better then my own proposal I just send.
>>>>>
>>>>> Good.
>>>>>
>>>>>>> (and
>>>>>>> probably read exposing the current brightness).
>>>>>>
>>>>>> If we do this, can we please make it mirror brightness, iow
>>>>>> also make it writable, that will make it easier for userspace
>>>>>> to deal with it. We can simply re-use the existing show / store
>>>>>> methods for brightness for this.
>>>>>
>>>>> Actually, echo 0 > brightness disables the trigger, IIRC. I'd avoid
>>>>> that here, you want to be able to turn off the backlight but still
>>>>> keep the trigger (and be notified of future changes).
>>>>
>>>> True, that is easy to do the store method will just need to call
>>>> led_set_brightness_nosleep instead of led_set_brightness, this
>>>> will skip the checks to stop blinking in led_set_brightness and
>>>> otherwise is equivalent.
>>>>
>>>>>> I suggest we call it:
>>>>>>
>>>>>> trigger_brightness
>>>>>>
>>>>>> And only register it when a poll-able trigger is present.
>>>>>
>>>>> I'd call it 'current_brightness', but that's no big deal. Yes, only
>>>>> registering it for poll-able triggers makes sense.
>>>>
>>>> current_brightness works for me. I will take a shot a patch-set
>>>> implementing this.
>>>
>>> Word "current" is not precise here.
>>>
>>> It can be thought of as either last brightness set by the
>>> user or the brightness currently written to the device
>>> (returned by brightness file).
>>>
>>> There is a semantic discrepancy in our requirements -
>>> we want the file representing both permanent brightness
>>> set by the user and brightness set by the hardware.
>>>
>>> The two stand in contradiction to each other since
>>> brightness set by the user can be adjusted by the hardware.
>>>
>>> Reading the file shouldn't update brightness property of
>>> struct led_classdev, so it shouldn't call led_update_brightness()
>>> but it still should allow reading brightness set by the
>>> hardware, as a result of each POLLPRI event. So in fact in
>>> the same time it should report both according to our requirements
>>> which is impossible. Do we need three brightness files ?
>>
>> I don't think so, current_brightness actually is an accurate
>> name, if the brightness was last changed by writing from
>> sysfs, the keyboard backlight will honor that and the current_brightness
>> attribute will show the brightness last set through writing it,
>> which matches the actual current brightness of the keyboard backlight.
>>
>> Likewise if it was changed with the hotkey last then the keyboard
>> backlight brightness will be changed and reading from current_brightness
>> will return the new actual brightness. Basically reading from this
>> file will be no different then reading from the normal "brightness"
>> file the difference will be in that it is poll-able and that
>> writing 0 turns off the LED without stopping blinking.
>
> If so then when software blinking enabled, it will return 0 on low
> blink cycle no matter what current brightness level is.
For software blinking there will not be a current_brightness atrribute,
as we will only add that for LEDs with poll-able triggers.
Also during the low cycle the LED is off, so its brightness at the
moment of reading (iow currently) is 0.
Regards,
Hans
^ permalink raw reply
* [PATCH net 3/3] ARM64: dts: meson: odroidc2: disable 1000t-eee advertisement
From: Jerome Brunet @ 2016-11-15 14:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479220154-25851-1-git-send-email-jbrunet@baylibre.com>
Reported-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Cc: Giuseppe Cavallaro <peppe.cavallaro@st.com>
Cc: Alexandre TORGUE <alexandre.torgue@st.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Tested-by: Andre Roth <neolynx@gmail.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
index e6e3491d48a5..1f4416ecb183 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
@@ -98,3 +98,18 @@
pinctrl-0 = <&i2c_a_pins>;
pinctrl-names = "default";
};
+
+ðmac {
+ phy-handle = <ð_phy0>;
+
+ mdio {
+ compatible = "snps,dwmac-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eth_phy0: ethernet-phy at 0 {
+ reg = <0>;
+ realtek,disable-eee-1000t;
+ };
+ };
+};
--
2.7.4
^ permalink raw reply related
* [PATCH net 2/3] dt-bindings: net: add DT bindings for realtek phys
From: Jerome Brunet @ 2016-11-15 14:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479220154-25851-1-git-send-email-jbrunet@baylibre.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
.../devicetree/bindings/net/realtek-phy.txt | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/realtek-phy.txt
diff --git a/Documentation/devicetree/bindings/net/realtek-phy.txt b/Documentation/devicetree/bindings/net/realtek-phy.txt
new file mode 100644
index 000000000000..dc2845a6b387
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/realtek-phy.txt
@@ -0,0 +1,20 @@
+Realtek Ethernet PHY
+
+Some boards require special tuning values of the phy.
+
+Optional properties:
+
+realtek,disable-eee-1000t:
+realtek,disable-eee-100tx:
+ If set, respectively disable 1000-BaseT and 100-BaseTx energy efficient
+ ethernet capabilty advertisement
+ default: Leave the phy default settings unchanged (capabilities advertised)
+
+Example:
+
+&mdio0 {
+ ethernetphy0: ethernet-phy at 0 {
+ reg = <0>;
+ realtek,disable-eee-1000t;
+ };
+};
--
2.7.4
^ permalink raw reply related
* [PATCH net 1/3] net: phy: realtek: add eee advertisement disable options
From: Jerome Brunet @ 2016-11-15 14:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479220154-25851-1-git-send-email-jbrunet@baylibre.com>
On some platforms, energy efficient ethernet with rtl8211 devices is
causing issue, like throughput drop or broken link.
This was reported on the OdroidC2 (DWMAC + RTL8211F). While the issue root
cause is not fully understood yet, disabling EEE advertisement prevent auto
negotiation from enabling EEE.
This patch provides options to disable 1000T and 100TX EEE advertisement
individually for the realtek phys supporting this feature.
Reported-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Cc: Giuseppe Cavallaro <peppe.cavallaro@st.com>
Cc: Alexandre TORGUE <alexandre.torgue@st.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Tested-by: Andre Roth <neolynx@gmail.com>
---
drivers/net/phy/realtek.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 64 insertions(+), 1 deletion(-)
diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
index aadd6e9f54ad..77235fd5faaf 100644
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -15,6 +15,12 @@
*/
#include <linux/phy.h>
#include <linux/module.h>
+#include <linux/of.h>
+
+struct rtl8211x_phy_priv {
+ bool eee_1000t_disable;
+ bool eee_100tx_disable;
+};
#define RTL821x_PHYSR 0x11
#define RTL821x_PHYSR_DUPLEX 0x2000
@@ -93,12 +99,44 @@ static int rtl8211f_config_intr(struct phy_device *phydev)
return err;
}
+static void rtl8211x_clear_eee_adv(struct phy_device *phydev)
+{
+ struct rtl8211x_phy_priv *priv = phydev->priv;
+ u16 val;
+
+ if (priv->eee_1000t_disable || priv->eee_100tx_disable) {
+ val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
+ MDIO_MMD_AN);
+
+ if (priv->eee_1000t_disable)
+ val &= ~MDIO_AN_EEE_ADV_1000T;
+ if (priv->eee_100tx_disable)
+ val &= ~MDIO_AN_EEE_ADV_100TX;
+
+ phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
+ MDIO_MMD_AN, val);
+ }
+}
+
+static int rtl8211x_config_init(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_config_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ rtl8211x_clear_eee_adv(phydev);
+
+ return 0;
+}
+
static int rtl8211f_config_init(struct phy_device *phydev)
{
int ret;
u16 reg;
- ret = genphy_config_init(phydev);
+ ret = rtl8211x_config_init(phydev);
if (ret < 0)
return ret;
@@ -115,6 +153,26 @@ static int rtl8211f_config_init(struct phy_device *phydev)
return 0;
}
+static int rtl8211x_phy_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ struct device_node *of_node = dev->of_node;
+ struct rtl8211x_phy_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->eee_1000t_disable =
+ of_property_read_bool(of_node, "realtek,disable-eee-1000t");
+ priv->eee_100tx_disable =
+ of_property_read_bool(of_node, "realtek,disable-eee-100tx");
+
+ phydev->priv = priv;
+
+ return 0;
+}
+
static struct phy_driver realtek_drvs[] = {
{
.phy_id = 0x00008201,
@@ -140,7 +198,9 @@ static struct phy_driver realtek_drvs[] = {
.phy_id_mask = 0x001fffff,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = &rtl8211x_phy_probe,
.config_aneg = genphy_config_aneg,
+ .config_init = &rtl8211x_config_init,
.read_status = genphy_read_status,
.ack_interrupt = rtl821x_ack_interrupt,
.config_intr = rtl8211e_config_intr,
@@ -152,7 +212,9 @@ static struct phy_driver realtek_drvs[] = {
.phy_id_mask = 0x001fffff,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = &rtl8211x_phy_probe,
.config_aneg = &genphy_config_aneg,
+ .config_init = &rtl8211x_config_init,
.read_status = &genphy_read_status,
.ack_interrupt = &rtl821x_ack_interrupt,
.config_intr = &rtl8211e_config_intr,
@@ -164,6 +226,7 @@ static struct phy_driver realtek_drvs[] = {
.phy_id_mask = 0x001fffff,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = &rtl8211x_phy_probe,
.config_aneg = &genphy_config_aneg,
.config_init = &rtl8211f_config_init,
.read_status = &genphy_read_status,
--
2.7.4
^ permalink raw reply related
* [PATCH net 0/3] Fix OdroidC2 Gigabit Tx link issue
From: Jerome Brunet @ 2016-11-15 14:29 UTC (permalink / raw)
To: linux-arm-kernel
This patchset fixes an issue with the OdroidC2 board (DWMAC + RTL8211F).
Initially reported as a low Tx throughput issue at gigabit speed, the
platform enters LPI too often. This eventually break the link (both Tx
and Rx), and require to bring the interface down and up again to get the
Rx path working again.
The root cause of this issue is not fully understood yet but disabling EEE
advertisement on the PHY prevent this feature to be negotiated.
With this change, the link is stable and reliable, with the expected
throughput performance.
The patchset adds options in the realtek phy driver to disable EEE
advertisement, through device tree, for the phy version supporting EEE.
Then EEE is disabled in the OdroidC2 device tree for Gigabit speed.
100M is not affected by this issue.
Jerome Brunet (3):
net: phy: realtek: add eee advertisement disable options
dt-bindings: net: add DT bindings for realtek phys
ARM64: dts: meson: odroidc2: disable 1000t-eee advertisement
.../devicetree/bindings/net/realtek-phy.txt | 20 +++++++
.../arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 15 +++++
drivers/net/phy/realtek.c | 65 +++++++++++++++++++++-
3 files changed, 99 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/net/realtek-phy.txt
--
2.7.4
^ permalink raw reply
* [PATCH] arm/arm64: KVM: VGIC: limit ITARGETSR bits to number of VCPUs
From: Andre Przywara @ 2016-11-15 14:27 UTC (permalink / raw)
To: linux-arm-kernel
The GICv2 spec says in section 4.3.12 that a "CPU targets field bit that
corresponds to an unimplemented CPU interface is RAZ/WI."
Currently we allow the guest to write any value in there and it can
read that back.
Mask the written value with the proper CPU mask to be spec compliant.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
virt/kvm/arm/vgic/vgic-mmio-v2.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v2.c b/virt/kvm/arm/vgic/vgic-mmio-v2.c
index b44b359..e59d4c7 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v2.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v2.c
@@ -129,6 +129,7 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
unsigned long val)
{
u32 intid = VGIC_ADDR_TO_INTID(addr, 8);
+ u8 cpu_mask = (1 << atomic_read(&vcpu->kvm->online_vcpus)) - 1;
int i;
/* GICD_ITARGETSR[0-7] are read-only */
@@ -141,7 +142,7 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
spin_lock(&irq->irq_lock);
- irq->targets = (val >> (i * 8)) & 0xff;
+ irq->targets = ((val >> (i * 8)) & 0xff) & cpu_mask;
target = irq->targets ? __ffs(irq->targets) : 0;
irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target);
--
2.9.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox