Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v15 12/13] clocksource/drivers/arm_arch_timer: Add GTDT support for memory-mapped timer
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>

From: Fu Wei <fu.wei@linaro.org>

The patch add memory-mapped timer register support by using the
information provided by the new GTDT driver of ACPI.

Signed-off-by: Fu Wei <fu.wei@linaro.org>
---
 drivers/clocksource/arm_arch_timer.c | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 653374c..350d0b9 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -1053,7 +1053,28 @@ CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
 		       arch_timer_mem_of_init);
 
 #ifdef CONFIG_ACPI_GTDT
-/* Initialize per-processor generic timer */
+static int __init arch_timer_mem_acpi_init(void)
+{
+	struct arch_timer_mem *timer_mem;
+	int ret = 0;
+	int i = 0;
+
+	timer_mem = kzalloc(sizeof(*timer_mem), GFP_KERNEL);
+	if (!timer_mem)
+		return -ENOMEM;
+
+	while (!gtdt_arch_timer_mem_init(timer_mem, i)) {
+		ret = arch_timer_mem_init(timer_mem);
+		if (ret)
+			break;
+		i++;
+	}
+
+	kfree(timer_mem);
+	return ret;
+}
+
+/* Initialize per-processor generic timer and memory-mapped timer(if present) */
 static int __init arch_timer_acpi_init(struct acpi_table_header *table)
 {
 	int ret;
@@ -1090,6 +1111,9 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
 	/* Get the frequency from CNTFRQ */
 	arch_timer_detect_rate(NULL, NULL);
 
+	if (arch_timer_mem_acpi_init())
+		pr_err("Failed to initialize memory-mapped timer.\n");
+
 	ret = arch_timer_init();
 
 error:
-- 
2.7.4

^ permalink raw reply related

* [PATCH v15 13/13] acpi/arm64: Add SBSA Generic Watchdog support in GTDT driver
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>

From: Fu Wei <fu.wei@linaro.org>

This driver adds support for parsing SBSA Generic Watchdog timer
in GTDT, parse all info in SBSA Generic Watchdog Structure in GTDT,
and creating a platform device with that information.

This allows the operating system to obtain device data from the
resource of platform device. The platform device named "sbsa-gwdt"
can be used by the ARM SBSA Generic Watchdog driver.

Signed-off-by: Fu Wei <fu.wei@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Tested-by: Xiongfeng Wang <wangxiongfeng2@huawei.com>
---
 drivers/acpi/arm64/gtdt.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/watchdog/Kconfig  |   1 +
 2 files changed, 101 insertions(+)

diff --git a/drivers/acpi/arm64/gtdt.c b/drivers/acpi/arm64/gtdt.c
index 08d9506..69483f6 100644
--- a/drivers/acpi/arm64/gtdt.c
+++ b/drivers/acpi/arm64/gtdt.c
@@ -14,6 +14,7 @@
 #include <linux/acpi.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/platform_device.h>
 
 #include <clocksource/arm_arch_timer.h>
 
@@ -66,6 +67,14 @@ static inline bool is_watchdog(void *platform_timer)
 	return gh->type == ACPI_GTDT_TYPE_WATCHDOG;
 }
 
+static inline struct acpi_gtdt_watchdog *get_watchdog(unsigned int index)
+{
+	if (index >= acpi_gtdt_desc.watchdog_count || !watchdog)
+		return NULL;
+
+	return watchdog[index];
+}
+
 static int __init map_gt_gsi(u32 interrupt, u32 flags)
 {
 	int trigger, polarity;
@@ -309,3 +318,94 @@ int __init gtdt_arch_timer_mem_init(struct arch_timer_mem *data,
 
 	return 0;
 }
+
+/*
+ * Initialize a SBSA generic Watchdog platform device info from GTDT
+ */
+static int __init gtdt_import_sbsa_gwdt(struct acpi_gtdt_watchdog *wd,
+					int index)
+{
+	struct platform_device *pdev;
+	int irq = map_gt_gsi(wd->timer_interrupt, wd->timer_flags);
+	int no_irq = 1;
+
+	/*
+	 * According to SBSA specification the size of refresh and control
+	 * frames of SBSA Generic Watchdog is SZ_4K(Offset 0x000 ? 0xFFF).
+	 */
+	struct resource res[] = {
+		DEFINE_RES_MEM(wd->control_frame_address, SZ_4K),
+		DEFINE_RES_MEM(wd->refresh_frame_address, SZ_4K),
+		DEFINE_RES_IRQ(irq),
+	};
+
+	pr_debug("found a Watchdog (0x%llx/0x%llx gsi:%u flags:0x%x).\n",
+		 wd->refresh_frame_address, wd->control_frame_address,
+		 wd->timer_interrupt, wd->timer_flags);
+
+	if (!(wd->refresh_frame_address && wd->control_frame_address)) {
+		pr_err(FW_BUG "failed to get the Watchdog base address.\n");
+		return -EINVAL;
+	}
+
+	if (!wd->timer_interrupt)
+		pr_warn(FW_BUG "failed to get the Watchdog interrupt.\n");
+	else if (irq <= 0)
+		pr_warn("failed to map the Watchdog interrupt.\n");
+	else
+		no_irq = 0;
+
+	/*
+	 * Add a platform device named "sbsa-gwdt" to match the platform driver.
+	 * "sbsa-gwdt": SBSA(Server Base System Architecture) Generic Watchdog
+	 * The platform driver (like drivers/watchdog/sbsa_gwdt.c)can get device
+	 * info below by matching this name.
+	 */
+	pdev = platform_device_register_simple("sbsa-gwdt", index, res,
+					       ARRAY_SIZE(res) - no_irq);
+	if (IS_ERR(pdev)) {
+		acpi_unregister_gsi(wd->timer_interrupt);
+		return PTR_ERR(pdev);
+	}
+
+	return 0;
+}
+
+static int __init gtdt_sbsa_gwdt_init(void)
+{
+	int i, ret;
+	struct acpi_table_header *table;
+	struct acpi_gtdt_watchdog *watchdog;
+
+	if (acpi_disabled)
+		return 0;
+
+	if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_GTDT, 0, &table)))
+		return -EINVAL;
+
+	ret = acpi_gtdt_init(table);
+	if (ret)
+		return ret;
+
+	if (!acpi_gtdt_desc.watchdog_count)
+		return 0;
+
+	for (i = 0; i < acpi_gtdt_desc.watchdog_count; i++) {
+		watchdog = get_watchdog(i);
+		if (!watchdog) {
+			ret = -ENODEV;
+			break;
+		}
+		ret = gtdt_import_sbsa_gwdt(watchdog, i);
+		if (ret)
+			break;
+	}
+
+	pr_info("found %d SBSA generic Watchdog(s), %d imported.\n",
+		acpi_gtdt_desc.watchdog_count, i);
+
+	acpi_gtdt_release();
+	return ret;
+}
+
+device_initcall(gtdt_sbsa_gwdt_init);
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index fdd3228..e5ba1f0 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -218,6 +218,7 @@ config ARM_SBSA_WATCHDOG
 	tristate "ARM SBSA Generic Watchdog"
 	depends on ARM64
 	depends on ARM_ARCH_TIMER
+	depends on ACPI_GTDT || !ACPI
 	select WATCHDOG_CORE
 	help
 	  ARM SBSA Generic Watchdog has two stage timeouts:
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 2/6] mfd: stm32-adc: Add support for stm32 ADC
From: Jonathan Cameron @ 2016-11-15 13:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <687b2001-c658-d0fb-a205-e5b6743fa9bc@st.com>



On 15 November 2016 10:47:52 GMT+00:00, Fabrice Gasnier <fabrice.gasnier@st.com> wrote:
>On 11/14/2016 05:47 PM, Lee Jones wrote:
>> On Sat, 12 Nov 2016, Jonathan Cameron wrote:
>>
>>> On 10/11/16 16:18, Fabrice Gasnier wrote:
>>>> 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>
>>> Looks good to me (other than the build issue obviously ;)
>
>Hi Jonathan,
>Thanks for your review.
>I'll fix build issue, sure ;-)
>
>>>
>>> The fun bit will be trying to keep the whole thing this clean as you
>>> add the more 'interesting' functionality.  *fingers crossed*
>Yes... But in the end, splitting shared resources in core driver makes 
>it more simple.
>Not sure there will be more complexity here.
>
>>>
>>> Acked-by: Jonathan Cameron <jic23@kernel.org>
>> There isn't anything MFD about this driver.
>>
>> Please move it into IIO.
>
>Hmm, there is no other sub sysbtem that may be used here, ADC driver 
>belongs to IIO.
>Also, of_platform_populate() is being used here. This can perfectly be 
>called from within IIO.
>
>Jonathan, can this "stm32-adc-core" driver be moved to, and live in 
>drivers/iio/adc ?
>(e.g. in addition to stm32-adc iio driver)
>Is it ok for you ?
Yes. That's ideal. 
>
>Please advise,
>Best Regards,
>Fabrice
>
>>
>>>> ---
>>>>   drivers/mfd/Kconfig                |  14 ++
>>>>   drivers/mfd/Makefile               |   1 +
>>>>   drivers/mfd/stm32-adc-core.c       | 301
>+++++++++++++++++++++++++++++++++++++
>>>>   include/linux/mfd/stm32-adc-core.h |  52 +++++++
>>>>   4 files changed, 368 insertions(+)
>>>>   create mode 100644 drivers/mfd/stm32-adc-core.c
>>>>   create mode 100644 include/linux/mfd/stm32-adc-core.h
>>>>
>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>> index c6df644..2580cee 100644
>>>> --- a/drivers/mfd/Kconfig
>>>> +++ b/drivers/mfd/Kconfig
>>>> @@ -1152,6 +1152,20 @@ config MFD_PALMAS
>>>>   	  If you say yes here you get support for the Palmas
>>>>   	  series of PMIC chips from Texas Instruments.
>>>>   
>>>> +config MFD_STM32_ADC
>>>> +	tristate "STMicroelectronics STM32 adc"
>>>> +	depends on ARCH_STM32 || COMPILE_TEST
>>>> +	depends on OF
>>>> +	select MFD_CORE
>>>> +	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 TPS6105X
>>>>   	tristate "TI TPS61050/61052 Boost Converters"
>>>>   	depends on I2C
>>>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>>>> index 9834e66..4571506 100644
>>>> --- a/drivers/mfd/Makefile
>>>> +++ b/drivers/mfd/Makefile
>>>> @@ -185,6 +185,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS_PCI)	+=
>intel-lpss-pci.o
>>>>   obj-$(CONFIG_MFD_INTEL_LPSS_ACPI)	+= intel-lpss-acpi.o
>>>>   obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
>>>>   obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
>>>> +obj-$(CONFIG_MFD_STM32_ADC) 	+= stm32-adc-core.o
>>>>   obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
>>>>   obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
>>>>   obj-$(CONFIG_MFD_RK808)		+= rk808.o
>>>> diff --git a/drivers/mfd/stm32-adc-core.c
>b/drivers/mfd/stm32-adc-core.c
>>>> new file mode 100644
>>>> index 0000000..bcf52fb
>>>> --- /dev/null
>>>> +++ b/drivers/mfd/stm32-adc-core.c
>>>> @@ -0,0 +1,301 @@
>>>> +/*
>>>> + * 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/mfd/stm32-adc-core.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of_device.h>
>>>> +#include <linux/regulator/consumer.h>
>>>> +#include <linux/slab.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 MFD driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>> +MODULE_ALIAS("platform:stm32-adc-core");
>>>> diff --git a/include/linux/mfd/stm32-adc-core.h
>b/include/linux/mfd/stm32-adc-core.h
>>>> new file mode 100644
>>>> index 0000000..081fa5f
>>>> --- /dev/null
>>>> +++ b/include/linux/mfd/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
>>>>
>
>--
>To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>the body of a message to majordomo at vger.kernel.org
>More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.

^ permalink raw reply

* [PATCH v2 3/6] iio: adc: Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-11-15 13:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4c5cd26d-97a5-f7a8-fdb6-9413975a3b10@kernel.org>

On 11/12/2016 06:08 PM, Jonathan Cameron wrote:
> On 10/11/16 16:18, Fabrice Gasnier wrote:
>> This patch adds support for STMicroelectronics STM32 MCU's analog to
>> digital converter.
>>
>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> Nice and clean - few minor things inline.
>
> Jonathan
>> ---
>>   drivers/iio/adc/Kconfig     |  10 +
>>   drivers/iio/adc/Makefile    |   1 +
>>   drivers/iio/adc/stm32-adc.c | 525 ++++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 536 insertions(+)
>>   create mode 100644 drivers/iio/adc/stm32-adc.c
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 7edcf32..61ba674 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -419,6 +419,16 @@ config ROCKCHIP_SARADC
>>   	  To compile this driver as a module, choose M here: the
>>   	  module will be called rockchip_saradc.
>>   
>> +config STM32_ADC
>> +	tristate "STMicroelectronics STM32 adc"
>> +	depends on MFD_STM32_ADC
>> +	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 7a40c04..df7a221 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) += 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..2be5fee
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-adc.c
>> @@ -0,0 +1,525 @@
>> +/*
>> + * 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/mfd/stm32-adc-core.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of.h>
>> +
>> +/* STM32F4 - Registers for each ADC instance */
> Not really sure the X adds anything in these, but doesn't do much harm
> I guess ;)
You're right. I'll remove the X. I initially added that to remind this 
stands for ADC1, 2 or 3...
Moreover, removing it will make register name match with reference manual.
>> +#define STM32F4_ADCX_SR			0x00
>> +#define STM32F4_ADCX_CR1		0x04
>> +#define STM32F4_ADCX_CR2		0x08
>> +#define STM32F4_ADCX_SMPR1		0x0C
>> +#define STM32F4_ADCX_SMPR2		0x10
>> +#define STM32F4_ADCX_HTR		0x24
>> +#define STM32F4_ADCX_LTR		0x28
>> +#define STM32F4_ADCX_SQR1		0x2C
>> +#define STM32F4_ADCX_SQR2		0x30
>> +#define STM32F4_ADCX_SQR3		0x34
>> +#define STM32F4_ADCX_JSQR		0x38
>> +#define STM32F4_ADCX_JDR1		0x3C
>> +#define STM32F4_ADCX_JDR2		0x40
>> +#define STM32F4_ADCX_JDR3		0x44
>> +#define STM32F4_ADCX_JDR4		0x48
>> +#define STM32F4_ADCX_DR			0x4C
>> +
>> +/* STM32F4_ADCX_SR - bit fields */
>> +#define STM32F4_OVR			BIT(5)
>> +#define STM32F4_STRT			BIT(4)
>> +#define STM32F4_EOC			BIT(1)
>> +
>> +/* STM32F4_ADCX_CR1 - bit fields */
>> +#define STM32F4_OVRIE			BIT(26)
>> +#define STM32F4_SCAN			BIT(8)
>> +#define STM32F4_EOCIE			BIT(5)
>> +
>> +/* STM32F4_ADCX_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_ADCX_SQR1 - bit fields */
>> +#define STM32F4_L_SHIFT			20
>> +#define STM32F4_L_MASK			GENMASK(23, 20)
>> +
>> +/* STM32F4_ADCX_SQR3 - bit fields */
>> +#define STM32F4_SQ1_SHIFT		0
>> +#define STM32F4_SQ1_MASK		GENMASK(4, 0)
>> +
>> +#define STM32_ADC_MAX_SQ		16	/* SQ1..SQ16 */
>> +#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:		optional adc 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 u32 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_ADCX_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_ADCX_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_ADCX_CR1, STM32F4_SCAN);
>> +	stm32_adc_set_bits(adc, STM32F4_ADCX_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_ADCX_CR2) & STM32F4_EXTEN_MASK))
>> +		stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, STM32F4_SWSTART);
>> +}
>> +
>> +static void stm32_adc_stop_conv(struct stm32_adc *adc)
>> +{
>> +	stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
>> +	stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_STRT);
>> +
>> +	stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
>> +	stm32_adc_clr_bits(adc, STM32F4_ADCX_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_ADCX_SQR3);
>> +	val &= ~STM32F4_SQ1_MASK;
>> +	val |= chan->channel << STM32F4_SQ1_SHIFT;
>> +	stm32_adc_writel(adc, STM32F4_ADCX_SQR3, val);
>> +
>> +	/* Set regular sequence len (0 for 1 conversion) */
>> +	stm32_adc_clr_bits(adc, STM32F4_ADCX_SQR1, STM32F4_L_MASK);
>> +
>> +	/* Trigger detection disabled (conversion can be launched in SW) */
>> +	stm32_adc_clr_bits(adc, STM32F4_ADCX_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) {
>> +		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
>> +		ret = -ETIMEDOUT;
>> +	} else if (timeout < 0) {
>> +		dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
>> +		ret = -EINTR;
>> +	} 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 = -EINVAL;
>> +
>> +	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 directly here.  Basically always preferred to return directly if
> there is not cleanup to be done.
I will fix it.
>> +		break;
>> +	case IIO_CHAN_INFO_SCALE:
>> +		*val = adc->common->vref_mv;
>> +		*val2 = chan->scan_type.realbits;
>> +		ret = IIO_VAL_FRACTIONAL_LOG2;
> return directly here.
I will fix it.
>> +		break;
>> +	default:
> return -EINVAL here.
I will fix it.
>> +		break;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static irqreturn_t stm32_adc_isr(int irq, void *data)
>> +{
>> +	struct stm32_adc *adc = data;
>> +	u32 status = stm32_adc_readl(adc, STM32F4_ADCX_SR);
>> +	irqreturn_t ret = IRQ_NONE;
>> +
>> +	if (status & STM32F4_EOC) {
>> +		*adc->buffer = stm32_adc_readw(adc, STM32F4_ADCX_DR);
>> +		complete(&adc->completion);
>> +		ret = IRQ_HANDLED;
> Slightly tidier to return IRQ_HANDLED here and directly return
> IRQ_NONE below.
I will fix it.
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +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->extend_name = channel->name;
> Don't set extend_name. That name doesn't add sufficient information to
> make it worth adding custom ABI to the userspace interface.
I will fix it.
>
>
>> +	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)) {
> Could it concievably be deferred?  Would be happier if this explicitly
> checked for -ENODEV or whatever gets returned when not clock has
> been specified.
I will fix it.
>> +		adc->clk = NULL;
>> +		dev_dbg(&pdev->dev, "No child clk found\n");
>> +	} else {
>> +		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 = devm_iio_device_register(&pdev->dev, indio_dev);
> This use of devm registration is going to cause a race in the remove.
> The userspace interface will not be removed until after the remove
> function has run.  That disables the clock thus leaving us a window
> where we could try and access the device with no clock enabled.
>
> Basic rule of thumb is that use of devm must not effect the ordering
> of unrolling what you do in probe when it comes to remove.
> (which more or less means that you can't use devm_iio_device_register
> unless you have no remove at all).
I will fix it.

Thanks,
Fabrice
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "iio dev register failed\n");
>> +		goto err_clk_disable;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_clk_disable:
>> +	if (adc->clk)
>> +		clk_disable_unprepare(adc->clk);
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_adc_remove(struct platform_device *pdev)
>> +{
>> +	struct stm32_adc *adc = platform_get_drvdata(pdev);
>> +
>> +	if (adc->clk)
>> +		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");
>>

^ permalink raw reply

* [PATCH v2 3/6] iio: adc: Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-11-15 13:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <535ffb14-a92f-1556-a4cb-f2be0508289a@metafoo.de>

On 11/14/2016 01:11 PM, Lars-Peter Clausen wrote:
> On 11/10/2016 05:18 PM, Fabrice Gasnier wrote:
> [...]
>> + 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_ADCX_SQR3);
>> +	val &= ~STM32F4_SQ1_MASK;
>> +	val |= chan->channel << STM32F4_SQ1_SHIFT;
>> +	stm32_adc_writel(adc, STM32F4_ADCX_SQR3, val);
>> +
>> +	/* Set regular sequence len (0 for 1 conversion) */
>> +	stm32_adc_clr_bits(adc, STM32F4_ADCX_SQR1, STM32F4_L_MASK);
>> +
>> +	/* Trigger detection disabled (conversion can be launched in SW) */
>> +	stm32_adc_clr_bits(adc, STM32F4_ADCX_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) {
>> +		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
> This should be dev_dbg() at most. This out of band reporting is not
> particular useful for applications as it is impossible to match the error to
> the action that triggered it. And you also report the error through the
> error code, so the applications knows what is going on.
>
>> +		ret = -ETIMEDOUT;
>> +	} else if (timeout < 0) {
>> +		dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
>> +		ret = -EINTR;
> This should just propagate the error returned by wait_for_completion...().
> This will make sure that the right behavior occurs based on the SA_RESTART
> policy.
Hi Lars,

Thanks for reviewing.
I'll update this in next revision.

Regards,
Fabrice

>
>> +	} else {
>> +		*res = result;
>> +		ret = IIO_VAL_INT;
>> +	}
>> +
>> +	stm32_adc_stop_conv(adc);
>> +
>> +	stm32_adc_conv_irq_disable(adc);
>> +
>> +	return ret;

^ 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 13:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <aeccbb02-6e33-390c-d72d-2a580dd67ed8@redhat.com>

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 ?

-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply

* [PATCH] arm: spin one more cycle in timer-based delays
From: Mason @ 2016-11-15 13:36 UTC (permalink / raw)
  To: linux-arm-kernel

When polling a tick counter in a busy loop, one might sample the
counter just *before* it is updated, and then again just *after*
it is updated. In that case, while mathematically v2-v1 equals 1,
only epsilon has really passed.

So, if a caller requests an N-cycle delay, we spin until v2-v1
is strictly greater than N to avoid these random corner cases.

Signed-off-by: Mason <slash.tmp@free.fr>
---
 arch/arm/lib/delay.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/lib/delay.c b/arch/arm/lib/delay.c
index 69aad80a3af4..3f1cd15ca102 100644
--- a/arch/arm/lib/delay.c
+++ b/arch/arm/lib/delay.c
@@ -60,7 +60,7 @@ static void __timer_delay(unsigned long cycles)
 {
 	cycles_t start = get_cycles();
 
-	while ((get_cycles() - start) < cycles)
+	while ((get_cycles() - start) <= cycles)
 		cpu_relax();
 }
 
-- 
2.9.0

^ permalink raw reply related

* [PATCH v3 1/2] phy: rockchip-inno-usb2: support otg-port for rk3399
From: Kishon Vijay Abraham I @ 2016-11-15 13:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1478520529-8869-2-git-send-email-wulf@rock-chips.com>



On Monday 07 November 2016 05:38 PM, William Wu wrote:
> The rk3399 SoC USB2 PHY is comprised of one Host port and
> one OTG port. And OTG port is for USB2.0 part of USB3.0 OTG
> controller, as a part to construct a fully feature Type-C
> subsystem.
> 
> With this patch, we can support OTG port with the following
> functions:
> - Support BC1.2 charger detect, and use extcon notifier to
>   send USB charger types to power driver.
> - Support PHY suspend for power management.
> - Support OTG Host only mode.
> 
> Signed-off-by: William Wu <wulf@rock-chips.com>

merged.

Thanks
Kishon
> ---
> Changes in v3:
> - split the clock fix into a separate patch 
> 
> Changes in v2:
> - remove wakelock
> 
>  drivers/phy/phy-rockchip-inno-usb2.c | 591 +++++++++++++++++++++++++++++++++--
>  1 file changed, 561 insertions(+), 30 deletions(-)
> 
> diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c
> index ac20310..ecfd7d1 100644
> --- a/drivers/phy/phy-rockchip-inno-usb2.c
> +++ b/drivers/phy/phy-rockchip-inno-usb2.c
> @@ -17,6 +17,7 @@
>  #include <linux/clk.h>
>  #include <linux/clk-provider.h>
>  #include <linux/delay.h>
> +#include <linux/extcon.h>
>  #include <linux/interrupt.h>
>  #include <linux/io.h>
>  #include <linux/gpio/consumer.h>
> @@ -30,11 +31,15 @@
>  #include <linux/of_platform.h>
>  #include <linux/phy/phy.h>
>  #include <linux/platform_device.h>
> +#include <linux/power_supply.h>
>  #include <linux/regmap.h>
>  #include <linux/mfd/syscon.h>
> +#include <linux/usb/of.h>
> +#include <linux/usb/otg.h>
>  
>  #define BIT_WRITEABLE_SHIFT	16
> -#define SCHEDULE_DELAY	(60 * HZ)
> +#define SCHEDULE_DELAY		(60 * HZ)
> +#define OTG_SCHEDULE_DELAY	(2 * HZ)
>  
>  enum rockchip_usb2phy_port_id {
>  	USB2PHY_PORT_OTG,
> @@ -49,6 +54,37 @@ enum rockchip_usb2phy_host_state {
>  	PHY_STATE_FS_LS_ONLINE	= 4,
>  };
>  
> +/**
> + * Different states involved in USB charger detection.
> + * USB_CHG_STATE_UNDEFINED	USB charger is not connected or detection
> + *				process is not yet started.
> + * USB_CHG_STATE_WAIT_FOR_DCD	Waiting for Data pins contact.
> + * USB_CHG_STATE_DCD_DONE	Data pin contact is detected.
> + * USB_CHG_STATE_PRIMARY_DONE	Primary detection is completed (Detects
> + *				between SDP and DCP/CDP).
> + * USB_CHG_STATE_SECONDARY_DONE	Secondary detection is completed (Detects
> + *				between DCP and CDP).
> + * USB_CHG_STATE_DETECTED	USB charger type is determined.
> + */
> +enum usb_chg_state {
> +	USB_CHG_STATE_UNDEFINED = 0,
> +	USB_CHG_STATE_WAIT_FOR_DCD,
> +	USB_CHG_STATE_DCD_DONE,
> +	USB_CHG_STATE_PRIMARY_DONE,
> +	USB_CHG_STATE_SECONDARY_DONE,
> +	USB_CHG_STATE_DETECTED,
> +};
> +
> +static const unsigned int rockchip_usb2phy_extcon_cable[] = {
> +	EXTCON_USB,
> +	EXTCON_USB_HOST,
> +	EXTCON_CHG_USB_SDP,
> +	EXTCON_CHG_USB_CDP,
> +	EXTCON_CHG_USB_DCP,
> +	EXTCON_CHG_USB_SLOW,
> +	EXTCON_NONE,
> +};
> +
>  struct usb2phy_reg {
>  	unsigned int	offset;
>  	unsigned int	bitend;
> @@ -58,19 +94,55 @@ struct usb2phy_reg {
>  };
>  
>  /**
> + * struct rockchip_chg_det_reg: usb charger detect registers
> + * @cp_det: charging port detected successfully.
> + * @dcp_det: dedicated charging port detected successfully.
> + * @dp_det: assert data pin connect successfully.
> + * @idm_sink_en: open dm sink curren.
> + * @idp_sink_en: open dp sink current.
> + * @idp_src_en: open dm source current.
> + * @rdm_pdwn_en: open dm pull down resistor.
> + * @vdm_src_en: open dm voltage source.
> + * @vdp_src_en: open dp voltage source.
> + * @opmode: utmi operational mode.
> + */
> +struct rockchip_chg_det_reg {
> +	struct usb2phy_reg	cp_det;
> +	struct usb2phy_reg	dcp_det;
> +	struct usb2phy_reg	dp_det;
> +	struct usb2phy_reg	idm_sink_en;
> +	struct usb2phy_reg	idp_sink_en;
> +	struct usb2phy_reg	idp_src_en;
> +	struct usb2phy_reg	rdm_pdwn_en;
> +	struct usb2phy_reg	vdm_src_en;
> +	struct usb2phy_reg	vdp_src_en;
> +	struct usb2phy_reg	opmode;
> +};
> +
> +/**
>   * struct rockchip_usb2phy_port_cfg: usb-phy port configuration.
>   * @phy_sus: phy suspend register.
> + * @bvalid_det_en: vbus valid rise detection enable register.
> + * @bvalid_det_st: vbus valid rise detection status register.
> + * @bvalid_det_clr: vbus valid rise detection clear register.
>   * @ls_det_en: linestate detection enable register.
>   * @ls_det_st: linestate detection state register.
>   * @ls_det_clr: linestate detection clear register.
> + * @utmi_avalid: utmi vbus avalid status register.
> + * @utmi_bvalid: utmi vbus bvalid status register.
>   * @utmi_ls: utmi linestate state register.
>   * @utmi_hstdet: utmi host disconnect register.
>   */
>  struct rockchip_usb2phy_port_cfg {
>  	struct usb2phy_reg	phy_sus;
> +	struct usb2phy_reg	bvalid_det_en;
> +	struct usb2phy_reg	bvalid_det_st;
> +	struct usb2phy_reg	bvalid_det_clr;
>  	struct usb2phy_reg	ls_det_en;
>  	struct usb2phy_reg	ls_det_st;
>  	struct usb2phy_reg	ls_det_clr;
> +	struct usb2phy_reg	utmi_avalid;
> +	struct usb2phy_reg	utmi_bvalid;
>  	struct usb2phy_reg	utmi_ls;
>  	struct usb2phy_reg	utmi_hstdet;
>  };
> @@ -80,31 +152,51 @@ struct rockchip_usb2phy_port_cfg {
>   * @reg: the address offset of grf for usb-phy config.
>   * @num_ports: specify how many ports that the phy has.
>   * @clkout_ctl: keep on/turn off output clk of phy.
> + * @chg_det: charger detection registers.
>   */
>  struct rockchip_usb2phy_cfg {
>  	unsigned int	reg;
>  	unsigned int	num_ports;
>  	struct usb2phy_reg	clkout_ctl;
>  	const struct rockchip_usb2phy_port_cfg	port_cfgs[USB2PHY_NUM_PORTS];
> +	const struct rockchip_chg_det_reg	chg_det;
>  };
>  
>  /**
>   * struct rockchip_usb2phy_port: usb-phy port data.
>   * @port_id: flag for otg port or host port.
>   * @suspended: phy suspended flag.
> + * @utmi_avalid: utmi avalid status usage flag.
> + *	true	- use avalid to get vbus status
> + *	flase	- use bvalid to get vbus status
> + * @vbus_attached: otg device vbus status.
> + * @bvalid_irq: IRQ number assigned for vbus valid rise detection.
>   * @ls_irq: IRQ number assigned for linestate detection.
>   * @mutex: for register updating in sm_work.
> - * @sm_work: OTG state machine work.
> + * @chg_work: charge detect work.
> + * @otg_sm_work: OTG state machine work.
> + * @sm_work: HOST state machine work.
>   * @phy_cfg: port register configuration, assigned by driver data.
> + * @event_nb: hold event notification callback.
> + * @state: define OTG enumeration states before device reset.
> + * @mode: the dr_mode of the controller.
>   */
>  struct rockchip_usb2phy_port {
>  	struct phy	*phy;
>  	unsigned int	port_id;
>  	bool		suspended;
> +	bool		utmi_avalid;
> +	bool		vbus_attached;
> +	int		bvalid_irq;
>  	int		ls_irq;
>  	struct mutex	mutex;
> +	struct		delayed_work chg_work;
> +	struct		delayed_work otg_sm_work;
>  	struct		delayed_work sm_work;
>  	const struct	rockchip_usb2phy_port_cfg *port_cfg;
> +	struct notifier_block	event_nb;
> +	enum usb_otg_state	state;
> +	enum usb_dr_mode	mode;
>  };
>  
>  /**
> @@ -113,6 +205,11 @@ struct rockchip_usb2phy_port {
>   * @clk: clock struct of phy input clk.
>   * @clk480m: clock struct of phy output clk.
>   * @clk_hw: clock struct of phy output clk management.
> + * @chg_state: states involved in USB charger detection.
> + * @chg_type: USB charger types.
> + * @dcd_retries: The retry count used to track Data contact
> + *		 detection process.
> + * @edev: extcon device for notification registration
>   * @phy_cfg: phy register configuration, assigned by driver data.
>   * @ports: phy port instance.
>   */
> @@ -122,6 +219,10 @@ struct rockchip_usb2phy {
>  	struct clk	*clk;
>  	struct clk	*clk480m;
>  	struct clk_hw	clk480m_hw;
> +	enum usb_chg_state	chg_state;
> +	enum power_supply_type	chg_type;
> +	u8			dcd_retries;
> +	struct extcon_dev	*edev;
>  	const struct rockchip_usb2phy_cfg	*phy_cfg;
>  	struct rockchip_usb2phy_port	ports[USB2PHY_NUM_PORTS];
>  };
> @@ -263,33 +364,84 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
>  	return ret;
>  }
>  
> -static int rockchip_usb2phy_init(struct phy *phy)
> +static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy)
>  {
> -	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
> -	struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
>  	int ret;
> +	struct device_node *node = rphy->dev->of_node;
> +	struct extcon_dev *edev;
> +
> +	if (of_property_read_bool(node, "extcon")) {
> +		edev = extcon_get_edev_by_phandle(rphy->dev, 0);
> +		if (IS_ERR(edev)) {
> +			if (PTR_ERR(edev) != -EPROBE_DEFER)
> +				dev_err(rphy->dev, "Invalid or missing extcon\n");
> +			return PTR_ERR(edev);
> +		}
> +	} else {
> +		/* Initialize extcon device */
> +		edev = devm_extcon_dev_allocate(rphy->dev,
> +						rockchip_usb2phy_extcon_cable);
>  
> -	if (rport->port_id == USB2PHY_PORT_HOST) {
> -		/* clear linestate and enable linestate detect irq */
> -		mutex_lock(&rport->mutex);
> +		if (IS_ERR(edev))
> +			return -ENOMEM;
>  
> -		ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
> +		ret = devm_extcon_dev_register(rphy->dev, edev);
>  		if (ret) {
> -			mutex_unlock(&rport->mutex);
> +			dev_err(rphy->dev, "failed to register extcon device\n");
>  			return ret;
>  		}
> +	}
>  
> -		ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true);
> -		if (ret) {
> -			mutex_unlock(&rport->mutex);
> -			return ret;
> +	rphy->edev = edev;
> +
> +	return 0;
> +}
> +
> +static int rockchip_usb2phy_init(struct phy *phy)
> +{
> +	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
> +	struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
> +	int ret = 0;
> +
> +	mutex_lock(&rport->mutex);
> +
> +	if (rport->port_id == USB2PHY_PORT_OTG) {
> +		if (rport->mode != USB_DR_MODE_HOST) {
> +			/* clear bvalid status and enable bvalid detect irq */
> +			ret = property_enable(rphy,
> +					      &rport->port_cfg->bvalid_det_clr,
> +					      true);
> +			if (ret)
> +				goto out;
> +
> +			ret = property_enable(rphy,
> +					      &rport->port_cfg->bvalid_det_en,
> +					      true);
> +			if (ret)
> +				goto out;
> +
> +			schedule_delayed_work(&rport->otg_sm_work,
> +					      OTG_SCHEDULE_DELAY);
> +		} else {
> +			/* If OTG works in host only mode, do nothing. */
> +			dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode);
>  		}
> +	} else if (rport->port_id == USB2PHY_PORT_HOST) {
> +		/* clear linestate and enable linestate detect irq */
> +		ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
> +		if (ret)
> +			goto out;
> +
> +		ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true);
> +		if (ret)
> +			goto out;
>  
> -		mutex_unlock(&rport->mutex);
>  		schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
>  	}
>  
> -	return 0;
> +out:
> +	mutex_unlock(&rport->mutex);
> +	return ret;
>  }
>  
>  static int rockchip_usb2phy_power_on(struct phy *phy)
> @@ -340,7 +492,11 @@ static int rockchip_usb2phy_exit(struct phy *phy)
>  {
>  	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
>  
> -	if (rport->port_id == USB2PHY_PORT_HOST)
> +	if (rport->port_id == USB2PHY_PORT_OTG &&
> +	    rport->mode != USB_DR_MODE_HOST) {
> +		cancel_delayed_work_sync(&rport->otg_sm_work);
> +		cancel_delayed_work_sync(&rport->chg_work);
> +	} else if (rport->port_id == USB2PHY_PORT_HOST)
>  		cancel_delayed_work_sync(&rport->sm_work);
>  
>  	return 0;
> @@ -354,6 +510,249 @@ static const struct phy_ops rockchip_usb2phy_ops = {
>  	.owner		= THIS_MODULE,
>  };
>  
> +static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
> +{
> +	struct rockchip_usb2phy_port *rport =
> +		container_of(work, struct rockchip_usb2phy_port,
> +			     otg_sm_work.work);
> +	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
> +	static unsigned int cable;
> +	unsigned long delay;
> +	bool vbus_attach, sch_work, notify_charger;
> +
> +	if (rport->utmi_avalid)
> +		vbus_attach =
> +			property_enabled(rphy, &rport->port_cfg->utmi_avalid);
> +	else
> +		vbus_attach =
> +			property_enabled(rphy, &rport->port_cfg->utmi_bvalid);
> +
> +	sch_work = false;
> +	notify_charger = false;
> +	delay = OTG_SCHEDULE_DELAY;
> +	dev_dbg(&rport->phy->dev, "%s otg sm work\n",
> +		usb_otg_state_string(rport->state));
> +
> +	switch (rport->state) {
> +	case OTG_STATE_UNDEFINED:
> +		rport->state = OTG_STATE_B_IDLE;
> +		if (!vbus_attach)
> +			rockchip_usb2phy_power_off(rport->phy);
> +		/* fall through */
> +	case OTG_STATE_B_IDLE:
> +		if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) > 0) {
> +			dev_dbg(&rport->phy->dev, "usb otg host connect\n");
> +			rport->state = OTG_STATE_A_HOST;
> +			rockchip_usb2phy_power_on(rport->phy);
> +			return;
> +		} else if (vbus_attach) {
> +			dev_dbg(&rport->phy->dev, "vbus_attach\n");
> +			switch (rphy->chg_state) {
> +			case USB_CHG_STATE_UNDEFINED:
> +				schedule_delayed_work(&rport->chg_work, 0);
> +				return;
> +			case USB_CHG_STATE_DETECTED:
> +				switch (rphy->chg_type) {
> +				case POWER_SUPPLY_TYPE_USB:
> +					dev_dbg(&rport->phy->dev,
> +						"sdp cable is connecetd\n");
> +					rockchip_usb2phy_power_on(rport->phy);
> +					rport->state = OTG_STATE_B_PERIPHERAL;
> +					notify_charger = true;
> +					sch_work = true;
> +					cable = EXTCON_CHG_USB_SDP;
> +					break;
> +				case POWER_SUPPLY_TYPE_USB_DCP:
> +					dev_dbg(&rport->phy->dev,
> +						"dcp cable is connecetd\n");
> +					rockchip_usb2phy_power_off(rport->phy);
> +					notify_charger = true;
> +					sch_work = true;
> +					cable = EXTCON_CHG_USB_DCP;
> +					break;
> +				case POWER_SUPPLY_TYPE_USB_CDP:
> +					dev_dbg(&rport->phy->dev,
> +						"cdp cable is connecetd\n");
> +					rockchip_usb2phy_power_on(rport->phy);
> +					rport->state = OTG_STATE_B_PERIPHERAL;
> +					notify_charger = true;
> +					sch_work = true;
> +					cable = EXTCON_CHG_USB_CDP;
> +					break;
> +				default:
> +					break;
> +				}
> +				break;
> +			default:
> +				break;
> +			}
> +		} else {
> +			notify_charger = true;
> +			rphy->chg_state = USB_CHG_STATE_UNDEFINED;
> +			rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
> +		}
> +
> +		if (rport->vbus_attached != vbus_attach) {
> +			rport->vbus_attached = vbus_attach;
> +
> +			if (notify_charger && rphy->edev)
> +				extcon_set_cable_state_(rphy->edev,
> +							cable, vbus_attach);
> +		}
> +		break;
> +	case OTG_STATE_B_PERIPHERAL:
> +		if (!vbus_attach) {
> +			dev_dbg(&rport->phy->dev, "usb disconnect\n");
> +			rphy->chg_state = USB_CHG_STATE_UNDEFINED;
> +			rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
> +			rport->state = OTG_STATE_B_IDLE;
> +			delay = 0;
> +			rockchip_usb2phy_power_off(rport->phy);
> +		}
> +		sch_work = true;
> +		break;
> +	case OTG_STATE_A_HOST:
> +		if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) == 0) {
> +			dev_dbg(&rport->phy->dev, "usb otg host disconnect\n");
> +			rport->state = OTG_STATE_B_IDLE;
> +			rockchip_usb2phy_power_off(rport->phy);
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	if (sch_work)
> +		schedule_delayed_work(&rport->otg_sm_work, delay);
> +}
> +
> +static const char *chg_to_string(enum power_supply_type chg_type)
> +{
> +	switch (chg_type) {
> +	case POWER_SUPPLY_TYPE_USB:
> +		return "USB_SDP_CHARGER";
> +	case POWER_SUPPLY_TYPE_USB_DCP:
> +		return "USB_DCP_CHARGER";
> +	case POWER_SUPPLY_TYPE_USB_CDP:
> +		return "USB_CDP_CHARGER";
> +	default:
> +		return "INVALID_CHARGER";
> +	}
> +}
> +
> +static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy,
> +				    bool en)
> +{
> +	property_enable(rphy, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en);
> +	property_enable(rphy, &rphy->phy_cfg->chg_det.idp_src_en, en);
> +}
> +
> +static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy,
> +					    bool en)
> +{
> +	property_enable(rphy, &rphy->phy_cfg->chg_det.vdp_src_en, en);
> +	property_enable(rphy, &rphy->phy_cfg->chg_det.idm_sink_en, en);
> +}
> +
> +static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy,
> +					      bool en)
> +{
> +	property_enable(rphy, &rphy->phy_cfg->chg_det.vdm_src_en, en);
> +	property_enable(rphy, &rphy->phy_cfg->chg_det.idp_sink_en, en);
> +}
> +
> +#define CHG_DCD_POLL_TIME	(100 * HZ / 1000)
> +#define CHG_DCD_MAX_RETRIES	6
> +#define CHG_PRIMARY_DET_TIME	(40 * HZ / 1000)
> +#define CHG_SECONDARY_DET_TIME	(40 * HZ / 1000)
> +static void rockchip_chg_detect_work(struct work_struct *work)
> +{
> +	struct rockchip_usb2phy_port *rport =
> +		container_of(work, struct rockchip_usb2phy_port, chg_work.work);
> +	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
> +	bool is_dcd, tmout, vout;
> +	unsigned long delay;
> +
> +	dev_dbg(&rport->phy->dev, "chg detection work state = %d\n",
> +		rphy->chg_state);
> +	switch (rphy->chg_state) {
> +	case USB_CHG_STATE_UNDEFINED:
> +		if (!rport->suspended)
> +			rockchip_usb2phy_power_off(rport->phy);
> +		/* put the controller in non-driving mode */
> +		property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, false);
> +		/* Start DCD processing stage 1 */
> +		rockchip_chg_enable_dcd(rphy, true);
> +		rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
> +		rphy->dcd_retries = 0;
> +		delay = CHG_DCD_POLL_TIME;
> +		break;
> +	case USB_CHG_STATE_WAIT_FOR_DCD:
> +		/* get data contact detection status */
> +		is_dcd = property_enabled(rphy, &rphy->phy_cfg->chg_det.dp_det);
> +		tmout = ++rphy->dcd_retries == CHG_DCD_MAX_RETRIES;
> +		/* stage 2 */
> +		if (is_dcd || tmout) {
> +			/* stage 4 */
> +			/* Turn off DCD circuitry */
> +			rockchip_chg_enable_dcd(rphy, false);
> +			/* Voltage Source on DP, Probe on DM */
> +			rockchip_chg_enable_primary_det(rphy, true);
> +			delay = CHG_PRIMARY_DET_TIME;
> +			rphy->chg_state = USB_CHG_STATE_DCD_DONE;
> +		} else {
> +			/* stage 3 */
> +			delay = CHG_DCD_POLL_TIME;
> +		}
> +		break;
> +	case USB_CHG_STATE_DCD_DONE:
> +		vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.cp_det);
> +		rockchip_chg_enable_primary_det(rphy, false);
> +		if (vout) {
> +			/* Voltage Source on DM, Probe on DP  */
> +			rockchip_chg_enable_secondary_det(rphy, true);
> +			delay = CHG_SECONDARY_DET_TIME;
> +			rphy->chg_state = USB_CHG_STATE_PRIMARY_DONE;
> +		} else {
> +			if (tmout) {
> +				/* floating charger found */
> +				rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP;
> +				rphy->chg_state = USB_CHG_STATE_DETECTED;
> +				delay = 0;
> +			} else {
> +				rphy->chg_type = POWER_SUPPLY_TYPE_USB;
> +				rphy->chg_state = USB_CHG_STATE_DETECTED;
> +				delay = 0;
> +			}
> +		}
> +		break;
> +	case USB_CHG_STATE_PRIMARY_DONE:
> +		vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.dcp_det);
> +		/* Turn off voltage source */
> +		rockchip_chg_enable_secondary_det(rphy, false);
> +		if (vout)
> +			rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP;
> +		else
> +			rphy->chg_type = POWER_SUPPLY_TYPE_USB_CDP;
> +		/* fall through */
> +	case USB_CHG_STATE_SECONDARY_DONE:
> +		rphy->chg_state = USB_CHG_STATE_DETECTED;
> +		delay = 0;
> +		/* fall through */
> +	case USB_CHG_STATE_DETECTED:
> +		/* put the controller in normal mode */
> +		property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, true);
> +		rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work);
> +		dev_info(&rport->phy->dev, "charger = %s\n",
> +			 chg_to_string(rphy->chg_type));
> +		return;
> +	default:
> +		return;
> +	}
> +
> +	schedule_delayed_work(&rport->chg_work, delay);
> +}
> +
>  /*
>   * The function manage host-phy port state and suspend/resume phy port
>   * to save power.
> @@ -485,6 +884,26 @@ static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data)
>  	return IRQ_HANDLED;
>  }
>  
> +static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data)
> +{
> +	struct rockchip_usb2phy_port *rport = data;
> +	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
> +
> +	if (!property_enabled(rphy, &rport->port_cfg->bvalid_det_st))
> +		return IRQ_NONE;
> +
> +	mutex_lock(&rport->mutex);
> +
> +	/* clear bvalid detect irq pending status */
> +	property_enable(rphy, &rport->port_cfg->bvalid_det_clr, true);
> +
> +	mutex_unlock(&rport->mutex);
> +
> +	rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work);
> +
> +	return IRQ_HANDLED;
> +}
> +
>  static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
>  					   struct rockchip_usb2phy_port *rport,
>  					   struct device_node *child_np)
> @@ -509,13 +928,86 @@ static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
>  					IRQF_ONESHOT,
>  					"rockchip_usb2phy", rport);
>  	if (ret) {
> -		dev_err(rphy->dev, "failed to request irq handle\n");
> +		dev_err(rphy->dev, "failed to request linestate irq handle\n");
>  		return ret;
>  	}
>  
>  	return 0;
>  }
>  
> +static int rockchip_otg_event(struct notifier_block *nb,
> +			      unsigned long event, void *ptr)
> +{
> +	struct rockchip_usb2phy_port *rport =
> +		container_of(nb, struct rockchip_usb2phy_port, event_nb);
> +
> +	schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
> +					  struct rockchip_usb2phy_port *rport,
> +					  struct device_node *child_np)
> +{
> +	int ret;
> +
> +	rport->port_id = USB2PHY_PORT_OTG;
> +	rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG];
> +	rport->state = OTG_STATE_UNDEFINED;
> +
> +	/*
> +	 * set suspended flag to true, but actually don't
> +	 * put phy in suspend mode, it aims to enable usb
> +	 * phy and clock in power_on() called by usb controller
> +	 * driver during probe.
> +	 */
> +	rport->suspended = true;
> +	rport->vbus_attached = false;
> +
> +	mutex_init(&rport->mutex);
> +
> +	rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1);
> +	if (rport->mode == USB_DR_MODE_HOST) {
> +		ret = 0;
> +		goto out;
> +	}
> +
> +	INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work);
> +	INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work);
> +
> +	rport->utmi_avalid =
> +		of_property_read_bool(child_np, "rockchip,utmi-avalid");
> +
> +	rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid");
> +	if (rport->bvalid_irq < 0) {
> +		dev_err(rphy->dev, "no vbus valid irq provided\n");
> +		ret = rport->bvalid_irq;
> +		goto out;
> +	}
> +
> +	ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, NULL,
> +					rockchip_usb2phy_bvalid_irq,
> +					IRQF_ONESHOT,
> +					"rockchip_usb2phy_bvalid", rport);
> +	if (ret) {
> +		dev_err(rphy->dev, "failed to request otg-bvalid irq handle\n");
> +		goto out;
> +	}
> +
> +	if (!IS_ERR(rphy->edev)) {
> +		rport->event_nb.notifier_call = rockchip_otg_event;
> +
> +		ret = extcon_register_notifier(rphy->edev, EXTCON_USB_HOST,
> +					       &rport->event_nb);
> +		if (ret)
> +			dev_err(rphy->dev, "register USB HOST notifier failed\n");
> +	}
> +
> +out:
> +	return ret;
> +}
> +
>  static int rockchip_usb2phy_probe(struct platform_device *pdev)
>  {
>  	struct device *dev = &pdev->dev;
> @@ -553,8 +1045,14 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
>  
>  	rphy->dev = dev;
>  	phy_cfgs = match->data;
> +	rphy->chg_state = USB_CHG_STATE_UNDEFINED;
> +	rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
>  	platform_set_drvdata(pdev, rphy);
>  
> +	ret = rockchip_usb2phy_extcon_register(rphy);
> +	if (ret)
> +		return ret;
> +
>  	/* find out a proper config which can be matched with dt. */
>  	index = 0;
>  	while (phy_cfgs[index].reg) {
> @@ -591,13 +1089,9 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
>  		struct rockchip_usb2phy_port *rport = &rphy->ports[index];
>  		struct phy *phy;
>  
> -		/*
> -		 * This driver aim to support both otg-port and host-port,
> -		 * but unfortunately, the otg part is not ready in current,
> -		 * so this comments and below codes are interim, which should
> -		 * be changed after otg-port is supplied soon.
> -		 */
> -		if (of_node_cmp(child_np->name, "host-port"))
> +		/* This driver aims to support both otg-port and host-port */
> +		if (of_node_cmp(child_np->name, "host-port") &&
> +		    of_node_cmp(child_np->name, "otg-port"))
>  			goto next_child;
>  
>  		phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
> @@ -610,9 +1104,18 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
>  		rport->phy = phy;
>  		phy_set_drvdata(rport->phy, rport);
>  
> -		ret = rockchip_usb2phy_host_port_init(rphy, rport, child_np);
> -		if (ret)
> -			goto put_child;
> +		/* initialize otg/host port separately */
> +		if (!of_node_cmp(child_np->name, "host-port")) {
> +			ret = rockchip_usb2phy_host_port_init(rphy, rport,
> +							      child_np);
> +			if (ret)
> +				goto put_child;
> +		} else {
> +			ret = rockchip_usb2phy_otg_port_init(rphy, rport,
> +							     child_np);
> +			if (ret)
> +				goto put_child;
> +		}
>  
>  next_child:
>  		/* to prevent out of boundary */
> @@ -654,10 +1157,18 @@ static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = {
>  
>  static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
>  	{
> -		.reg = 0xe450,
> +		.reg		= 0xe450,
>  		.num_ports	= 2,
>  		.clkout_ctl	= { 0xe450, 4, 4, 1, 0 },
>  		.port_cfgs	= {
> +			[USB2PHY_PORT_OTG] = {
> +				.phy_sus	= { 0xe454, 1, 0, 2, 1 },
> +				.bvalid_det_en	= { 0xe3c0, 3, 3, 0, 1 },
> +				.bvalid_det_st	= { 0xe3e0, 3, 3, 0, 1 },
> +				.bvalid_det_clr	= { 0xe3d0, 3, 3, 0, 1 },
> +				.utmi_avalid	= { 0xe2ac, 7, 7, 0, 1 },
> +				.utmi_bvalid	= { 0xe2ac, 12, 12, 0, 1 },
> +			},
>  			[USB2PHY_PORT_HOST] = {
>  				.phy_sus	= { 0xe458, 1, 0, 0x2, 0x1 },
>  				.ls_det_en	= { 0xe3c0, 6, 6, 0, 1 },
> @@ -667,12 +1178,32 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
>  				.utmi_hstdet	= { 0xe2ac, 23, 23, 0, 1 }
>  			}
>  		},
> +		.chg_det = {
> +			.opmode		= { 0xe454, 3, 0, 5, 1 },
> +			.cp_det		= { 0xe2ac, 2, 2, 0, 1 },
> +			.dcp_det	= { 0xe2ac, 1, 1, 0, 1 },
> +			.dp_det		= { 0xe2ac, 0, 0, 0, 1 },
> +			.idm_sink_en	= { 0xe450, 8, 8, 0, 1 },
> +			.idp_sink_en	= { 0xe450, 7, 7, 0, 1 },
> +			.idp_src_en	= { 0xe450, 9, 9, 0, 1 },
> +			.rdm_pdwn_en	= { 0xe450, 10, 10, 0, 1 },
> +			.vdm_src_en	= { 0xe450, 12, 12, 0, 1 },
> +			.vdp_src_en	= { 0xe450, 11, 11, 0, 1 },
> +		},
>  	},
>  	{
> -		.reg = 0xe460,
> +		.reg		= 0xe460,
>  		.num_ports	= 2,
>  		.clkout_ctl	= { 0xe460, 4, 4, 1, 0 },
>  		.port_cfgs	= {
> +			[USB2PHY_PORT_OTG] = {
> +				.phy_sus        = { 0xe464, 1, 0, 2, 1 },
> +				.bvalid_det_en  = { 0xe3c0, 8, 8, 0, 1 },
> +				.bvalid_det_st  = { 0xe3e0, 8, 8, 0, 1 },
> +				.bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 },
> +				.utmi_avalid	= { 0xe2ac, 10, 10, 0, 1 },
> +				.utmi_bvalid    = { 0xe2ac, 16, 16, 0, 1 },
> +			},
>  			[USB2PHY_PORT_HOST] = {
>  				.phy_sus	= { 0xe468, 1, 0, 0x2, 0x1 },
>  				.ls_det_en	= { 0xe3c0, 11, 11, 0, 1 },
> 

^ permalink raw reply

* [PATCH -next] phy: meson8b-usb2: fix missing clk_disable_unprepare() on error
From: Kishon Vijay Abraham I @ 2016-11-15 13:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1477146822-31327-1-git-send-email-weiyj.lk@gmail.com>



On Saturday 22 October 2016 08:03 PM, Wei Yongjun wrote:
> From: Wei Yongjun <weiyongjun1@huawei.com>
> 
> Fix the missing clk_disable_unprepare() before return from
> phy_meson8b_usb2_power_on() in the error handling case.
> 
> Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>

merged, thanks.

-Kishon
> ---
>  drivers/phy/phy-meson8b-usb2.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/phy/phy-meson8b-usb2.c b/drivers/phy/phy-meson8b-usb2.c
> index 73bf632..dca3947 100644
> --- a/drivers/phy/phy-meson8b-usb2.c
> +++ b/drivers/phy/phy-meson8b-usb2.c
> @@ -158,6 +158,7 @@ static int phy_meson8b_usb2_power_on(struct phy *phy)
>  	ret = clk_prepare_enable(priv->clk_usb);
>  	if (ret) {
>  		dev_err(&phy->dev, "Failed to enable USB DDR clock\n");
> +		clk_disable_unprepare(priv->clk_usb_general);
>  		return ret;
>  	}
>  
> @@ -190,6 +191,8 @@ static int phy_meson8b_usb2_power_on(struct phy *phy)
>  		if (phy_meson8b_usb2_read(priv, REG_ADP_BC) &
>  			REG_ADP_BC_ACA_PIN_FLOAT) {
>  			dev_warn(&phy->dev, "USB ID detect failed!\n");
> +			clk_disable_unprepare(priv->clk_usb);
> +			clk_disable_unprepare(priv->clk_usb_general);
>  			return -EINVAL;
>  		}
>  	}
> 

^ permalink raw reply

* wdt, gpio: move arch_initcall into subsys_initcall ?
From: Guenter Roeck @ 2016-11-15 13:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <e4472c2d-effa-9583-aded-8dcdfdd57ee6@mentor.com>

On 11/15/2016 03:32 AM, Vladimir Zapolskiy wrote:
> On 11/15/2016 01:10 PM, Vladimir Zapolskiy wrote:
>> Hello Heiko,
>>
>> On 11/15/2016 12:20 PM, Heiko Schocher wrote:
>>> Hello,
>>>
>>> commit e188cbf7564f: "gpio: mxc: shift gpio_mxc_init() to subsys_initcall level"
>>> moves the gpio initialization of the mxc gpio driver
>>> from the arch_initcall level into subsys_initcall level.
>>>
>>> This leads now on mxc boards, which use a gpio wdt driver
>>> and the CONFIG_GPIO_WATCHDOG_ARCH_INITCALL option enabled,
>>> to unwanted driver probe deferrals during kernel boot.
>>>
>>> I see this currently on an imx6 based board (which has unfortunately
>>> 3 WDT: imx6 internal (disabled), gpio wdt and da9063 WDT ...).
>>>
>>> Also a side effect from above commit is, that the da9063 WDT driver
>>> is now probed before the gpio WDT driver ... so /dev/watchdog now
>>> does not point to the gpio_wdt, instead it points to the da9063 WDT.
>>>
>>> So there are 2 solutions possible:
>>>
>>> - add a CONFIG_GPIO_MCX_ARCH_INITCALL option
>>>   in drivers/gpio/gpio-mxc.c like for the gpio_wdt.c driver?
>>
>> in my opinion this is overly heavy solution and it might be
>> better to avoid it if possible.
>>
>> I would rather prefer to reconsider GPIO_WATCHDOG_ARCH_INITCALL
>> usage in the watchdog driver.
>>
>> Moreover adding this proposed GPIO_MCX_ARCH_INITCALL to call
>> the driver on arch level will result in deferring the GPIO driver.
>>
>>>   But how can we guarantee, that first the gpio driver and then
>>>   the gpio_wdt driver gets probed?
>>>
>>> - move the arch_initcall in gpio_wdt.c into a subsys_initcall
>>>   (Tested this, and the probe dereferral messages are gone ...)
>>>
>>>   But this may results in problems on boards, which needs an early
>>>   trigger on an gpio wdt ...
>>
>> The level of "earliness" can not be defined in absolute time value
>> in any case, why decreasing the init level of the watchdog driver
>> to subsys level can cause problems? For that there should exist
>> some kind of a dependency on IC or PCB hardware level, can you
>> name it please?
>>
>> Also please note that more than a half of all GPIO drivers settle
>> on subsys or later initcall level, this means that there is
>> an expected GPIO watchdog driver deferral for all of them.
>
> Please find two more late notes though.
>
>> I propose to send two patches for review:
>>
>> 1. remove GPIO_WATCHDOG_ARCH_INITCALL option completely and decouple
>>    module_platform_driver() into arch_initcall() and module_exit()
>>    unconditionally.
>>
>> 2. change arch_initcall() in the watchdog driver to subsys_initcall().
>>    This change removes probe deferrals on boot, when the driver is
>>    used with the most of the GPIO controllers.
>
> Alternatively commit 5e53c8ed813d ("watchdog: gpio_wdt: Add option for
> early registration") can be reverted and then module_platform_driver()
> is decoupled into subsys_initcall() and module_exit() as its replacement.
>
Sure, only the reason for that was that there are situations where
subsys_initcall() was too late. Also, when using arch_initcall() only,
we get deferrals again, which is apparently hated by many and a reason
for all those "avoid probe deferrals" patches.

> And also please note that since quite many GPIO controller drivers
> live on initcall levels after subsys_initcall(), the solution won't
> let to avoid watchdog driver deferrals totally, this should be accepted.
>
... except for others it isn't, and we are back to square one.

GPIO_WATCHDOG_ARCH_INITCALL was intended to be only used in situations
where needed. Why is it used here in the first place if that is not
the case ?

Guenter

^ 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 13:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <9e7d5e0f-c4ca-6930-63b9-83dc28517f33@samsung.com>

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.

Regards,

Hans

^ permalink raw reply

* [PATCH v3 1/3] phy_sun4i_usb: set_mode: Allow using set_mode to force end the current session
From: Kishon Vijay Abraham I @ 2016-11-15 13:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160923134058.26828-1-hdegoede@redhat.com>



On Friday 23 September 2016 07:10 PM, Hans de Goede wrote:
> The sunxi musb has a bug where sometimes it will generate a babble
> error on device disconnect instead of a disconnect irq. When this
> happens the musb-controller switches from host mode to device mode
> (it clears MUSB_DEVCTL_SESSION and sets MUSB_DEVCTL_BDEVICE) and
> gets stuck in this state.
> 
> Clearing this requires reporting Vbus low for 200 or more ms, but
> on some devices Vbus is simply always high (host-only mode, no Vbus
> control).
> 
> This commit modifies sun4i_usb_phy_set_mode so that it will force
> end the current session when called with the current mode, before this
> commit calling set_mode with the current mode was a nop since id_det
> would stay the same resulting in the detect_work not doing anything.
> 
> This allows the sunxi-musb glue to use sun4i_usb_phy_set_mode to force
> end the current session without changing the mode, to fixup the stuck
> state after a babble error.
> 
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>

merged, thanks.

-Kishon
> ---
> Changes in v2:
> -New patch in v2 of this series replacing the
>  "phy-sun4i-usb: Add sun4i_usb_phy_force_session_end() function"
>  from v1
> Changes in v3:
> -Fix dev_info so that it prints the new-mode instead of the old one
> ---
>  drivers/phy/phy-sun4i-usb.c | 14 ++++++++++----
>  1 file changed, 10 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
> index 43c0d98..cbd338d 100644
> --- a/drivers/phy/phy-sun4i-usb.c
> +++ b/drivers/phy/phy-sun4i-usb.c
> @@ -437,25 +437,31 @@ static int sun4i_usb_phy_set_mode(struct phy *_phy, enum phy_mode mode)
>  {
>  	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>  	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +	int new_mode;
>  
>  	if (phy->index != 0)
>  		return -EINVAL;
>  
>  	switch (mode) {
>  	case PHY_MODE_USB_HOST:
> -		data->dr_mode = USB_DR_MODE_HOST;
> +		new_mode = USB_DR_MODE_HOST;
>  		break;
>  	case PHY_MODE_USB_DEVICE:
> -		data->dr_mode = USB_DR_MODE_PERIPHERAL;
> +		new_mode = USB_DR_MODE_PERIPHERAL;
>  		break;
>  	case PHY_MODE_USB_OTG:
> -		data->dr_mode = USB_DR_MODE_OTG;
> +		new_mode = USB_DR_MODE_OTG;
>  		break;
>  	default:
>  		return -EINVAL;
>  	}
>  
> -	dev_info(&_phy->dev, "Changing dr_mode to %d\n", (int)data->dr_mode);
> +	if (new_mode != data->dr_mode) {
> +		dev_info(&_phy->dev, "Changing dr_mode to %d\n", new_mode);
> +		data->dr_mode = new_mode;
> +	}
> +
> +	data->id_det = -1; /* Force reprocessing of id */
>  	data->force_session_end = true;
>  	queue_delayed_work(system_wq, &data->detect, 0);
>  
> 

^ permalink raw reply

* [PATCH] ASoC: mioa701_wm9713: add missing white space in dev_err message
From: Lothar Waßmann @ 2016-11-15 13:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161112163025.4801-1-colin.king@canonical.com>

Hi,

On Sat, 12 Nov 2016 16:30:25 +0000 Colin King wrote:
> From: Colin Ian King <colin.king@canonical.com>
> 
> There is a missing whitespace in the dev_err message between
> "will" and "lead".  Add the whitespace.
> 
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
> ---
>  sound/soc/pxa/mioa701_wm9713.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
> index d1661fa..0fe0abe 100644
> --- a/sound/soc/pxa/mioa701_wm9713.c
> +++ b/sound/soc/pxa/mioa701_wm9713.c
> @@ -187,7 +187,7 @@ static int mioa701_wm9713_probe(struct platform_device *pdev)
>  	mioa701.dev = &pdev->dev;
>  	rc = devm_snd_soc_register_card(&pdev->dev, &mioa701);
>  	if (!rc)
> -		dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will"
> +		dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will "
>  			 "lead to overheating and possible destruction of your device."
>  			 " Do not use without a good knowledge of mio's board design!\n");
>
There is already a continuation line that has a whitespace in front. It
would be nice to be consistent and also add the missing whitespace in
front of the succeeding line...


Lothar Wa?mann

^ permalink raw reply

* [PATCH v2 0/3] ARM: dts: sun7i: BPI-M1+ USB support
From: Chen-Yu Tsai @ 2016-11-15 13:51 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Maxime,

These are the remaining patches of my BPI-M1+ fixes series from July.

Changes since v1:

  - Split out USB PHY enable patch.

  - Dropped custom OPP table. Tested the A20 default one with
    cpufreq-ljt-stress-test and it seemed stable.

  - Dropped voltage range for cpu supply regulator to normal 1.0V ~ 1.4V.

  - Dropped pinmux setting for OTG ID pin.


Please have a look.

Regards
ChenYu

Chen-Yu Tsai (3):
  ARM: dts: sun7i: bananapi-m1-plus: Enable USB PHY for USB host support
  ARM: dts: sun7i: bananapi-m1-plus: Add PMIC regulators
  ARM: dts: sun7i: bananapi-m1-plus: Enable USB OTG

 arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts | 60 ++++++++++++++++++++++--
 1 file changed, 56 insertions(+), 4 deletions(-)

-- 
2.10.2

^ permalink raw reply

* [PATCH v2 1/3] ARM: dts: sun7i: bananapi-m1-plus: Enable USB PHY for USB host support
From: Chen-Yu Tsai @ 2016-11-15 13:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161115135106.438-1-wens@csie.org>

The 2 USB host ports are directly tied to the 2 USB hosts in the SoC.
The 2 host pairs were already enabled, but the USB PHY wasn't.
VBUS on the 2 ports are always on.

Enable the USB PHY.

Fixes: 04c85ecad32a ("ARM: dts: sun7i: Add dts file for Bananapi M1 Plus
		      board")
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
index ba5bca0fe997..44377a98cc89 100644
--- a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
+++ b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
@@ -227,3 +227,8 @@
 	pinctrl-0 = <&uart0_pins_a>;
 	status = "okay";
 };
+
+&usbphy {
+	/* VBUS on usb host ports are tied to DC5V and therefore always on */
+	status = "okay";
+};
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 2/3] ARM: dts: sun7i: bananapi-m1-plus: Add PMIC regulators
From: Chen-Yu Tsai @ 2016-11-15 13:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161115135106.438-1-wens@csie.org>

The Bananapi M1+, like other Allwinner A20 based boards, uses the
AXP209 PMIC to supply its power.

Add the AXP209 regulators.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts | 35 +++++++++++++++++++++---
 1 file changed, 31 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
index 44377a98cc89..ac19630c1c23 100644
--- a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
+++ b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
@@ -105,6 +105,10 @@
 	status = "okay";
 };
 
+&cpu0 {
+	cpu-supply = <&reg_dcdc2>;
+};
+
 &ehci0 {
 	status = "okay";
 };
@@ -132,16 +136,14 @@
 	status = "okay";
 
 	axp209: pmic at 34 {
-		compatible = "x-powers,axp209";
 		reg = <0x34>;
 		interrupt-parent = <&nmi_intc>;
 		interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
-
-		interrupt-controller;
-		#interrupt-cells = <1>;
 	};
 };
 
+#include "axp209.dtsi"
+
 &ir0 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&ir0_rx_pins_a>;
@@ -222,6 +224,31 @@
 	};
 };
 
+&reg_dcdc2 {
+	regulator-always-on;
+	regulator-min-microvolt = <1000000>;
+	regulator-max-microvolt = <1400000>;
+	regulator-name = "vdd-cpu";
+};
+
+&reg_dcdc3 {
+	regulator-always-on;
+	regulator-min-microvolt = <1000000>;
+	regulator-max-microvolt = <1400000>;
+	regulator-name = "vdd-int-dll";
+};
+
+&reg_ldo1 {
+	regulator-name = "vdd-rtc";
+};
+
+&reg_ldo2 {
+	regulator-always-on;
+	regulator-min-microvolt = <3000000>;
+	regulator-max-microvolt = <3000000>;
+	regulator-name = "avcc";
+};
+
 &uart0 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&uart0_pins_a>;
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 3/3] ARM: dts: sun7i: bananapi-m1-plus: Enable USB OTG
From: Chen-Yu Tsai @ 2016-11-15 13:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161115135106.438-1-wens@csie.org>

The Bananapi M1+ supports USB OTG, with the PMIC doing VBUS sensing.
Enable the USB OTG related functions.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
index ac19630c1c23..5f7114e13850 100644
--- a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
+++ b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
@@ -194,6 +194,10 @@
 	status = "okay";
 };
 
+&otg_sram {
+	status = "okay";
+};
+
 &pio {
 	gmac_power_pin_bpi_m1p: gmac_power_pin at 0 {
 		allwinner,pins = "PH23";
@@ -249,13 +253,29 @@
 	regulator-name = "avcc";
 };
 
+&reg_usb0_vbus {
+	status = "okay";
+};
+
 &uart0 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&uart0_pins_a>;
 	status = "okay";
 };
 
+&usb_otg {
+	dr_mode = "otg";
+	status = "okay";
+};
+
+&usb_power_supply {
+	status = "okay";
+};
+
 &usbphy {
+	usb0_id_det-gpios = <&pio 7 4 GPIO_ACTIVE_HIGH>; /* PH4 */
+	usb0_vbus_power-supply = <&usb_power_supply>;
+	usb0_vbus-supply = <&reg_usb0_vbus>;
 	/* VBUS on usb host ports are tied to DC5V and therefore always on */
 	status = "okay";
 };
-- 
2.10.2

^ permalink raw reply related

* 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:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <9d42546d-5917-891c-2b18-ccf7f7328624@redhat.com>

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.

-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply

* [PATCH] ARM64: zynqmp: Fix W=1 dtc 1.4 warnings
From: Michal Simek @ 2016-11-15 14:04 UTC (permalink / raw)
  To: linux-arm-kernel

The patch removes these warnings reported by dtc 1.4:
Warning (unit_address_vs_reg): Node /amba_apu has a reg or ranges
property, but no unit name
Warning (unit_address_vs_reg): Node /memory has a reg or ranges
property, but no unit name

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---

 arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts | 2 +-
 arch/arm64/boot/dts/xilinx/zynqmp.dtsi      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts b/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts
index 358089687a69..ef1b9e573af0 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts
+++ b/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts
@@ -27,7 +27,7 @@
 		stdout-path = "serial0:115200n8";
 	};
 
-	memory {
+	memory at 0 {
 		device_type = "memory";
 		reg = <0x0 0x0 0x0 0x40000000>;
 	};
diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
index 68a908334c7b..83791eadff41 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
+++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
@@ -72,7 +72,7 @@
 			     <1 10 0xf08>;
 	};
 
-	amba_apu {
+	amba_apu: amba_apu at 0 {
 		compatible = "simple-bus";
 		#address-cells = <2>;
 		#size-cells = <1>;
-- 
1.9.1

^ permalink raw reply related

* [PATCH 1/2] ARM: zynq: Remove skeleton.dtsi
From: Michal Simek @ 2016-11-15 14:07 UTC (permalink / raw)
  To: linux-arm-kernel

Based on
"ARM: dts: explicitly mark skeleton.dtsi as deprecated"
(sha1: 9c0da3cc61f1233c2782e2d3d91e3d0707dd4ba5)
skeleton.dtsi is deprecated.
Move address and size-cells directly to zynq-7000.dtsi.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---

 arch/arm/boot/dts/zynq-7000.dtsi | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi
index f283ff08381c..f47a6c1cc752 100644
--- a/arch/arm/boot/dts/zynq-7000.dtsi
+++ b/arch/arm/boot/dts/zynq-7000.dtsi
@@ -10,9 +10,10 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
-/include/ "skeleton.dtsi"
 
 / {
+	#address-cells = <1>;
+	#size-cells = <1>;
 	compatible = "xlnx,zynq-7000";
 
 	cpus {
-- 
1.9.1

^ permalink raw reply related

* [PATCH 2/2] ARM: zynq: Fix W=1 dtc 1.4 warnings
From: Michal Simek @ 2016-11-15 14:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <2d42e25d7dd03e562e12e71fd037837737115940.1479218844.git.michal.simek@xilinx.com>

The patch removes these warnings reported by dtc 1.4:
Warning (unit_address_vs_reg): Node /pmu has a reg or ranges property,
but no unit name
Warning (unit_address_vs_reg): Node /fixedregulator at 0 has a unit name,
but no reg property
Warning (unit_address_vs_reg): Node /memory has a reg or ranges
property, but no unit name

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---

 arch/arm/boot/dts/zynq-7000.dtsi      | 4 ++--
 arch/arm/boot/dts/zynq-parallella.dts | 2 +-
 arch/arm/boot/dts/zynq-zc702.dts      | 2 +-
 arch/arm/boot/dts/zynq-zc706.dts      | 2 +-
 arch/arm/boot/dts/zynq-zed.dts        | 2 +-
 arch/arm/boot/dts/zynq-zybo.dts       | 2 +-
 6 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi
index f47a6c1cc752..402b5bbe3b5b 100644
--- a/arch/arm/boot/dts/zynq-7000.dtsi
+++ b/arch/arm/boot/dts/zynq-7000.dtsi
@@ -42,14 +42,14 @@
 		};
 	};
 
-	pmu {
+	pmu at f8891000 {
 		compatible = "arm,cortex-a9-pmu";
 		interrupts = <0 5 4>, <0 6 4>;
 		interrupt-parent = <&intc>;
 		reg = < 0xf8891000 0x1000 0xf8893000 0x1000 >;
 	};
 
-	regulator_vccpint: fixedregulator at 0 {
+	regulator_vccpint: fixedregulator {
 		compatible = "regulator-fixed";
 		regulator-name = "VCCPINT";
 		regulator-min-microvolt = <1000000>;
diff --git a/arch/arm/boot/dts/zynq-parallella.dts b/arch/arm/boot/dts/zynq-parallella.dts
index 307ed201d658..64a6390fc501 100644
--- a/arch/arm/boot/dts/zynq-parallella.dts
+++ b/arch/arm/boot/dts/zynq-parallella.dts
@@ -28,7 +28,7 @@
 		serial0 = &uart1;
 	};
 
-	memory {
+	memory at 0 {
 		device_type = "memory";
 		reg = <0x0 0x40000000>;
 	};
diff --git a/arch/arm/boot/dts/zynq-zc702.dts b/arch/arm/boot/dts/zynq-zc702.dts
index e96959b2e67a..0cdad2cc8b78 100644
--- a/arch/arm/boot/dts/zynq-zc702.dts
+++ b/arch/arm/boot/dts/zynq-zc702.dts
@@ -24,7 +24,7 @@
 		serial0 = &uart1;
 	};
 
-	memory {
+	memory at 0 {
 		device_type = "memory";
 		reg = <0x0 0x40000000>;
 	};
diff --git a/arch/arm/boot/dts/zynq-zc706.dts b/arch/arm/boot/dts/zynq-zc706.dts
index be6a986bbbd8..ad4bb06dba25 100644
--- a/arch/arm/boot/dts/zynq-zc706.dts
+++ b/arch/arm/boot/dts/zynq-zc706.dts
@@ -24,7 +24,7 @@
 		serial0 = &uart1;
 	};
 
-	memory {
+	memory at 0 {
 		device_type = "memory";
 		reg = <0x0 0x40000000>;
 	};
diff --git a/arch/arm/boot/dts/zynq-zed.dts b/arch/arm/boot/dts/zynq-zed.dts
index 7250c1eac7f9..325379f7983c 100644
--- a/arch/arm/boot/dts/zynq-zed.dts
+++ b/arch/arm/boot/dts/zynq-zed.dts
@@ -23,7 +23,7 @@
 		serial0 = &uart1;
 	};
 
-	memory {
+	memory at 0 {
 		device_type = "memory";
 		reg = <0x0 0x20000000>;
 	};
diff --git a/arch/arm/boot/dts/zynq-zybo.dts b/arch/arm/boot/dts/zynq-zybo.dts
index d9e0f3e70671..590ec24b8749 100644
--- a/arch/arm/boot/dts/zynq-zybo.dts
+++ b/arch/arm/boot/dts/zynq-zybo.dts
@@ -23,7 +23,7 @@
 		serial0 = &uart1;
 	};
 
-	memory {
+	memory at 0 {
 		device_type = "memory";
 		reg = <0x0 0x20000000>;
 	};
-- 
1.9.1

^ permalink raw reply related

* [PATCH v7 00/16] ACPI IORT ARM SMMU support
From: Lorenzo Pieralisi @ 2016-11-15 14:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAJZ5v0gh9qB41zcnvnETDA-5ho577uhxE4rehZqEXorFViY__w@mail.gmail.com>

On Tue, Nov 15, 2016 at 02:04:09PM +0100, Rafael J. Wysocki wrote:
> On Tue, Nov 15, 2016 at 11:12 AM, Lorenzo Pieralisi
> <lorenzo.pieralisi@arm.com> wrote:
> > Hi Rafael,
> >
> > On Thu, Nov 10, 2016 at 12:36:12AM +0100, Rafael J. Wysocki wrote:
> >> Hi Lorenzo,
> >>
> >> On Wed, Nov 9, 2016 at 3:19 PM, Lorenzo Pieralisi
> >> <lorenzo.pieralisi@arm.com> wrote:
> >> > This patch series is v7 of a previous posting:
> >> >
> >> > https://lkml.org/lkml/2016/10/18/506
> >>
> >> I don't see anything objectionable in this series.
> >>
> >> Please let me know which patches in particular to look at in detail.
> >
> > Are patches 7 and consequently 16 (that builds on top of 7) ok with
> > you ? They should be equivalent to nop on anything other than ARM64
> > but they touch ACPI core so they require an ACK if they are ok please.
> 
> Yes, they are.  Please feel free to add my ACKs to those or I will
> send them separately later today.

Thank you very much, I will add them to v8 that should be final,
I will send it when the dust has settled on patch 4, which looks
like the last bit to sort out (we may not even need a v8 at all).

Thanks !
Lorenzo

^ permalink raw reply

* [PATCH/RESEND] recordmcount: arm: Implement make_nop
From: Ard Biesheuvel @ 2016-11-15 14:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161018234200.5804-1-sboyd@codeaurora.org>

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.

> +static unsigned char *ideal_nop4_arm;
> +
> +static unsigned char bl_mcount_arm_le[4] = { 0xfe, 0xff, 0xff, 0xeb }; /* bl */
> +static unsigned char bl_mcount_arm_be[4] = { 0xeb, 0xff, 0xff, 0xfe }; /* bl */
> +static unsigned char *bl_mcount_arm;
> +
> +static unsigned char push_arm_le[4] = { 0x04, 0xe0, 0x2d, 0xe5 }; /* push {lr} */
> +static unsigned char push_arm_be[4] = { 0xe5, 0x2d, 0xe0, 0x04 }; /* push {lr} */
> +static unsigned char *push_arm;
> +
> +static unsigned char ideal_nop2_thumb_le[2] = { 0x00, 0xbf }; /* nop */
> +static unsigned char ideal_nop2_thumb_be[2] = { 0xbf, 0x00 }; /* nop */
> +static unsigned char *ideal_nop2_thumb;
> +
> +static unsigned char push_bl_mcount_thumb_le[6] = { 0x00, 0xb5, 0xff, 0xf7, 0xfe, 0xff }; /* push {lr}, bl */
> +static unsigned char push_bl_mcount_thumb_be[6] = { 0xb5, 0x00, 0xf7, 0xff, 0xff, 0xfe }; /* push {lr}, bl */
> +static unsigned char *push_bl_mcount_thumb;
> +
> +static int make_nop_arm(void *map, size_t const offset)
> +{
> +       char *ptr;
> +       int cnt = 1;
> +       int nop_size;
> +       size_t off = offset;
> +
> +       ptr = map + offset;
> +       if (memcmp(ptr, bl_mcount_arm, 4) == 0) {
> +               if (memcmp(ptr - 4, push_arm, 4) == 0) {
> +                       off -= 4;
> +                       cnt = 2;
> +               }
> +               ideal_nop = ideal_nop4_arm;
> +               nop_size = 4;
> +       } else if (memcmp(ptr - 2, push_bl_mcount_thumb, 6) == 0) {
> +               cnt = 3;
> +               nop_size = 2;
> +               off -= 2;
> +               ideal_nop = ideal_nop2_thumb;
> +       } else
> +               return -1;
> +
> +       /* Convert to nop */
> +       ulseek(fd_map, off, SEEK_SET);
> +
> +       do {
> +               uwrite(fd_map, ideal_nop, nop_size);
> +       } while (--cnt > 0);
> +
> +       return 0;
> +}
> +
>  static unsigned char ideal_nop4_arm64[4] = {0x1f, 0x20, 0x03, 0xd5};
>  static int make_nop_arm64(void *map, size_t const offset)
>  {
> @@ -430,6 +483,11 @@ do_file(char const *const fname)
>                         w2 = w2rev;
>                         w8 = w8rev;
>                 }
> +               ideal_nop4_arm = ideal_nop4_arm_le;
> +               bl_mcount_arm = bl_mcount_arm_le;
> +               push_arm = push_arm_le;
> +               ideal_nop2_thumb = ideal_nop2_thumb_le;
> +               push_bl_mcount_thumb = push_bl_mcount_thumb_le;
>                 break;
>         case ELFDATA2MSB:
>                 if (*(unsigned char const *)&endian != 0) {
> @@ -438,6 +496,11 @@ do_file(char const *const fname)
>                         w2 = w2rev;
>                         w8 = w8rev;
>                 }
> +               ideal_nop4_arm = ideal_nop4_arm_be;
> +               bl_mcount_arm = bl_mcount_arm_be;
> +               push_arm = push_arm_be;
> +               ideal_nop2_thumb = ideal_nop2_thumb_be;
> +               push_bl_mcount_thumb = push_bl_mcount_thumb_be;
>                 break;
>         }  /* end switch */
>         if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0
> @@ -463,6 +526,8 @@ do_file(char const *const fname)
>                 break;
>         case EM_ARM:     reltype = R_ARM_ABS32;
>                          altmcount = "__gnu_mcount_nc";
> +                        make_nop = make_nop_arm;
> +                        rel_type_nop = R_ARM_NONE;
>                          break;
>         case EM_AARCH64:
>                         reltype = R_AARCH64_ABS64;
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [GIT PULL] STM32 SOC changes for v4.10 #1
From: Alexandre Torgue @ 2016-11-15 14:22 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Olof, Arnd and Kevin,

Please consider this first round of STM32 SOC updates for v4.10:

The following changes since commit 1001354ca34179f3db924eb66672442a173147dc:

   Linux 4.9-rc1 (2016-10-15 12:17:50 -0700)

are available in the git repository at:

   git://git.kernel.org/pub/scm/linux/kernel/git/atorgue/stm32.git 
tags/soc-for-4.10-1

for you to fetch changes up to 6bc18b83c0c3b5d56137a31ce98ca2802036e7a9:

   ARM: Kconfig: Introduce MACH_STM32F746 flag (2016-11-15 12:02:59 +0100)

----------------------------------------------------------------
STM32 SOC updates for v4.10, round 1.

Highlights:
----------
  - Add new MCU SOC STM32F746

----------------------------------------------------------------
Alexandre TORGUE (2):
       ARM: mach-stm32: Add a new SOC - STM32F746
       ARM: Kconfig: Introduce MACH_STM32F746 flag

  Documentation/arm/stm32/overview.txt           |  3 ++-
  Documentation/arm/stm32/stm32f746-overview.txt | 34 
++++++++++++++++++++++++++
  arch/arm/Kconfig                               |  5 ++++
  arch/arm/mach-stm32/board-dt.c                 |  1 +
  4 files changed, 42 insertions(+), 1 deletion(-)
  create mode 100644 Documentation/arm/stm32/stm32f746-overview.txt

^ 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


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