Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/4] SMMU 52-bit address support
From: Robin Murphy @ 2017-12-14 16:58 UTC (permalink / raw)
  To: linux-arm-kernel

Hi all,

Here's a (hopefully final) update of 52-bit SMMU support. The only
material changes from v1[1] are a small cosmetic tweak to patch #1,
and correction of the silly error in patch #2 as reported by Nate in
testing.

Robin.

[1] https://lists.linuxfoundation.org/pipermail/iommu/2017-November/025073.html

Robin Murphy (4):
  iommu/arm-smmu-v3: Clean up address masking
  iommu/io-pgtable-arm: Support 52-bit physical address
  iommu/arm-smmu-v3: Support 52-bit physical address
  iommu/arm-smmu-v3: Support 52-bit virtual address

 drivers/iommu/arm-smmu-v3.c    | 76 ++++++++++++++++++++++--------------------
 drivers/iommu/io-pgtable-arm.c | 65 ++++++++++++++++++++++++++----------
 2 files changed, 86 insertions(+), 55 deletions(-)

-- 
2.13.4.dirty

^ permalink raw reply

* [RFC PATCH 2/2] ASoC: select sysclk clock from mlck clock provider in wm8994 driver
From: Olivier Moysan @ 2017-12-14 16:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513270438-18523-1-git-send-email-olivier.moysan@st.com>

When defined in device tree, MCLK1 and MCLK2 are used
as sysclk for aif1 and aif2 interfaces respectively.
If clock rate is let 0, the frequency provided by
wm8994_set_dai_sysclk() is used instead.

Signed-off-by: Olivier Moysan <olivier.moysan@st.com>
---
 sound/soc/codecs/wm8994.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 21ffd64..7a84e37 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -11,6 +11,7 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/clk.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
@@ -2376,18 +2377,35 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
 {
 	struct snd_soc_codec *codec = dai->codec;
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+	struct wm8994 *control = wm8994->wm8994;
+	struct wm8994_pdata *pdata = &control->pdata;
+	unsigned long rate;
 	int i;
 
 	switch (dai->id) {
 	case 1:
+		if (pdata->mclk1) {
+			rate = clk_get_rate(pdata->mclk1);
+			if (rate)
+				freq = (unsigned int)rate;
+			clk_id = WM8994_SYSCLK_MCLK1;
+		}
+		break;
 	case 2:
+		if (pdata->mclk2) {
+			rate = clk_get_rate(pdata->mclk2);
+			if (rate)
+				freq = (unsigned int)rate;
+			clk_id = WM8994_SYSCLK_MCLK2;
+		}
 		break;
-
 	default:
 		/* AIF3 shares clocking with AIF1/2 */
 		return -EINVAL;
 	}
 
+	dev_info(codec->dev, "%s:.clock id %d\n", __func__, clk_id);
+
 	switch (clk_id) {
 	case WM8994_SYSCLK_MCLK1:
 		wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK1;
-- 
1.9.1

^ permalink raw reply related

* [RFC PATCH 1/2] ASoC: add support of mclk clock providers in wm8894 driver
From: Olivier Moysan @ 2017-12-14 16:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513270438-18523-1-git-send-email-olivier.moysan@st.com>

Wolfson wm8994 codec bindings exposes MCLK1 and MCLK1 clocks.
This patch adds support of MCLK1 and MCLK2 in mfd driver.

Signed-off-by: Olivier Moysan <olivier.moysan@st.com>
---
 drivers/mfd/wm8994-core.c        | 9 +++++++++
 include/linux/mfd/wm8994/pdata.h | 6 ++++++
 2 files changed, 15 insertions(+)

diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index 953d079..f1ff9d8 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -12,6 +12,7 @@
  *
  */
 
+#include <linux/clk.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -310,6 +311,14 @@ static int wm8994_set_pdata_from_of(struct wm8994 *wm8994)
 	if (pdata->ldo[1].enable < 0)
 		pdata->ldo[1].enable = 0;
 
+	pdata->mclk1 = devm_clk_get(wm8994->dev, "MCLK1");
+	if (IS_ERR(pdata->mclk1))
+		pdata->mclk1 = NULL;
+
+	pdata->mclk2 = devm_clk_get(wm8994->dev, "MCLK2");
+	if (IS_ERR(pdata->mclk2))
+		pdata->mclk2 = NULL;
+
 	return 0;
 }
 #else
diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h
index 90c6052..8037d26 100644
--- a/include/linux/mfd/wm8994/pdata.h
+++ b/include/linux/mfd/wm8994/pdata.h
@@ -233,6 +233,12 @@ struct wm8994_pdata {
 	 * GPIO for the IRQ pin if host only supports edge triggering
 	 */
 	int irq_gpio;
+
+	/* MCLK1 clock provider */
+	struct clk *mclk1;
+
+	/* MCLK2 clock provider */
+	struct clk *mclk2;
 };
 
 #endif
-- 
1.9.1

^ permalink raw reply related

* [RFC PATCH 0/2] select master clock in wm8994 driver based on DT clocks configuration
From: Olivier Moysan @ 2017-12-14 16:53 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

This RFC follows a previous RFC related to master clock issues with Wolfson wm8994 codec:
http://mailman.alsa-project.org/pipermail/alsa-devel/2017-March/118834.html

This RFC provides a new proposal regarding master clock selection in wm8994 driver.
Below is a reminder of the problem:

Use case:
CPU DAI and codec are managed through audio graph card.
Wolson codec wm8994 is set as slave of CPU DAI and CPU DAI feeds codec with master clock.
Master clock is derived from mclk-fs property and provided to CPU DAI and codec through snd_soc_dai_set_sysclk API. 

Analysis:
The audio graph card sets clock id to 0, while wm8994 codec driver expects a clock id in the range [1..4]. (MCLK1, MCLK2 ..)

Proposal:
Wolfson wm8994 codec bindings exposes MCLK1 and MCLK1 clocks.
It seems that these clocks are not supported in wm8994 driver, yet.
First patch adds support of these clocks.

Second patch sets master clock according to clocks provided in DT.
The patch assumes that MCLK1 and MCLK2 are linked to aif1 and aif2 interfaces respectively.
If MCLKx is defined, is it used as source clock for aifx interface.
Otherwise clock id parameter is used as usual. 
By default clock rate is requested from clock framework. 
This is not convenient, when mclk clock frequency is computed for mclk-fs ratio,
as codec set_sysclk() is called before cpu set_sysclk() callback.
In this case frequency provided by set_sysclk() must be used.
So, if MCLKx rate is 0, frequency parameter provided by wm8994_set_dai_sysclk() is used.

I have a limited view of potential side effects here, so any comments are welcome.
If some adaptations are required to make this change more generic, please let me know.

Regards
Olivier

Olivier Moysan (2):
  ASoC: add support of mclk clock providers in wm8894 driver
  ASoC: select sysclk clock from mlck clock provider in wm8994 driver

 drivers/mfd/wm8994-core.c        |  9 +++++++++
 include/linux/mfd/wm8994/pdata.h |  6 ++++++
 sound/soc/codecs/wm8994.c        | 20 +++++++++++++++++++-
 3 files changed, 34 insertions(+), 1 deletion(-)

-- 
1.9.1

^ permalink raw reply

* [PATCH v7 6/6] arm64: dts: meson-axg: switch uart_ao clock to CLK81
From: Jerome Brunet @ 2017-12-14 16:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171211141348.22048-7-yixun.lan@amlogic.com>

On Mon, 2017-12-11 at 22:13 +0800, Yixun Lan wrote:
> Switch the uart_ao pclk to CLK81 since the clock driver is ready.
> Also move the clock info to the board.dts instead in the soc.dtsi.

Same comment as for ethmac, is it really wise ?
Isn't the clock setup the same for the axg family ?

> 
> Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>

^ permalink raw reply

* [PATCH v2] ARM64: dts: meson-axg: add ethernet mac controller
From: Jerome Brunet @ 2017-12-14 16:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214030242.113152-1-yixun.lan@amlogic.com>

On Thu, 2017-12-14 at 11:02 +0800, Yixun Lan wrote:
> ---
> Changes in v2 since [1]:
>  - rebase to kevin's v4.16/dt64 branch
>  - add Neil's Reviewed-by
>  - move clock info to board.dts instead of in soc.dtsi

You got this comment regarding the pwm clock setup. the setup of the pwm clocks
depends on the use case, so should defined depending on the requirement on the
board

This is not the case for the ethmac, the clock setup will be same for every
board, unless I missed something. the clock bindings should be defined in
meson-axg.dtsi, I think

>  - drop "meson-axg-dwmac" compatible string, since we didn't use this
>    we could re-add it later when we really need.
>  - note: to make ethernet work properly,it depend on clock & pinctrl[2],
>    to compile the DTS, the patch [3] is required.
>    the code part will be taken via clock & pinctrl subsystem tree.

^ permalink raw reply

* [PATCH 4/4] PM / OPP: Add ti-opp-supply driver
From: Dave Gerlach @ 2017-12-14 16:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214043432.GT3322@vireshk-i7>

Hi,
On 12/13/2017 10:34 PM, Viresh Kumar wrote:
> On 13-12-17, 14:33, Dave Gerlach wrote:
>> Introduce a ti-opp-supply driver that will use new multiple regulator
>> support that is part of the OPP core This is needed on TI platforms like
>> DRA7/AM57 in order to control both CPU regulator and Adaptive Body Bias
>> (ABB) regulator. These regulators must be scaled in sequence during an
>> OPP transition depending on whether or not the frequency is being scaled
>> up or down.
>>
>> This driver also implements AVS Class0 for these parts by looking up the
>> required values from registers in the SoC and programming adjusted
>> optimal voltage values for each OPP.
>>
>> Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
>> ---
>>  drivers/opp/Makefile        |   1 +
>>  drivers/opp/ti-opp-supply.c | 428 ++++++++++++++++++++++++++++++++++++++++++++
> 
> Why is this added as a separate driver and not part of the same ti-cpufreq.c
> file?
> 

Although we are only currently using this for cpufreq, in the future it can be
used for the other voltage domains that are unrelated to the CPU that support
AVS Class0. Table 5-7 in http://www.ti.com/lit/ds/symlink/am5728.pdf shows the
support for each voltage domain.

>>  2 files changed, 429 insertions(+)
>>  create mode 100644 drivers/opp/ti-opp-supply.c
>>
>> diff --git a/drivers/opp/Makefile b/drivers/opp/Makefile
>> index e70ceb406fe9..6ce6aefacc81 100644
>> --- a/drivers/opp/Makefile
>> +++ b/drivers/opp/Makefile
>> @@ -2,3 +2,4 @@ ccflags-$(CONFIG_DEBUG_DRIVER)	:= -DDEBUG
>>  obj-y				+= core.o cpu.o
>>  obj-$(CONFIG_OF)		+= of.o
>>  obj-$(CONFIG_DEBUG_FS)		+= debugfs.o
>> +obj-$(CONFIG_ARM_TI_CPUFREQ)	+= ti-opp-supply.o
>> diff --git a/drivers/opp/ti-opp-supply.c b/drivers/opp/ti-opp-supply.c
>> new file mode 100644
>> index 000000000000..73d795c90b79
>> --- /dev/null
>> +++ b/drivers/opp/ti-opp-supply.c
>> @@ -0,0 +1,428 @@
>> +/*
>> + * Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/
>> + *	Nishanth Menon <nm@ti.com>
>> + *	Dave Gerlach <d-gerlach@ti.com>
>> + *
>> + * 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.
> 
> Please use the new SPDX format for licenses.

Ah ok, will do.

> 
>> + * TI OPP supply driver that provides override into the regulator control
>> + * for generic opp core to handle devices with ABB regulator and/or
>> + * SmartReflex Class0.
>> + */
>> +#include <linux/clk.h>
>> +#include <linux/cpufreq.h>
>> +#include <linux/device.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/notifier.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_opp.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/slab.h>
>> +
>> +/**
>> + * struct ti_opp_supply_optimum_voltage_table - optimized voltage table
>> + * @reference_uv:	reference voltage (usually Nominal voltage)
>> + * @optimized_uv:	Optimized voltage from efuse
>> + */
>> +struct ti_opp_supply_optimum_voltage_table {
>> +	unsigned int reference_uv;
>> +	unsigned int optimized_uv;
>> +};
>> +
>> +/**
>> + * struct ti_opp_supply_data - OMAP specific opp supply data
>> + * @vdd_table:	Optimized voltage mapping table
>> + * @num_vdd_table: number of entries in vdd_table
>> + * @vdd_absolute_max_voltage_uv: absolute maximum voltage in UV for the supply
>> + */
>> +struct ti_opp_supply_data {
>> +	struct ti_opp_supply_optimum_voltage_table *vdd_table;
>> +	u32 num_vdd_table;
>> +	u32 vdd_absolute_max_voltage_uv;
>> +};
>> +
>> +static struct ti_opp_supply_data opp_data;
>> +
>> +/**
>> + * struct ti_opp_supply_of_data - device tree match data
>> + * @flags:	specific type of opp supply
>> + * @efuse_voltage_mask: mask required for efuse register representing voltage
>> + * @efuse_voltage_uv: Are the efuse entries in micro-volts? if not, assume
>> + *		milli-volts.
>> + */
>> +struct ti_opp_supply_of_data {
>> +#define OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE	BIT(1)
>> +#define OPPDM_HAS_NO_ABB			BIT(2)
>> +	const u8 flags;
>> +	const u32 efuse_voltage_mask;
>> +	const bool efuse_voltage_uv;
>> +};
>> +
>> +/**
>> + * _store_optimized_voltages() - store optimized voltages
>> + * @dev:	ti opp supply device for which we need to store info
>> + * @data:	data specific to the device
>> + *
>> + * Picks up efuse based optimized voltages for VDD unique per device and
>> + * stores it in internal data structure for use during transition requests.
>> + *
>> + * Return: If successful, 0, else appropriate error value.
>> + */
>> +static int _store_optimized_voltages(struct device *dev,
>> +				     struct ti_opp_supply_data *data)
>> +{
>> +	void __iomem *base;
>> +	struct property *prop;
>> +	struct resource *res;
>> +	const __be32 *val;
>> +	int proplen, i;
>> +	int ret = 0;
>> +	struct ti_opp_supply_optimum_voltage_table *table;
>> +	const struct ti_opp_supply_of_data *of_data = dev_get_drvdata(dev);
>> +
>> +	/* pick up Efuse based voltages */
>> +	res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(dev, "Unable to get IO resource\n");
>> +		ret = -ENODEV;
>> +		goto out_map;
>> +	}
>> +
>> +	base = ioremap_nocache(res->start, resource_size(res));
>> +	if (!base) {
>> +		dev_err(dev, "Unable to map Efuse registers\n");
>> +		ret = -ENOMEM;
>> +		goto out_map;
>> +	}
>> +
>> +	/* Fetch efuse-settings. */
>> +	prop = of_find_property(dev->of_node, "ti,efuse-settings", NULL);
>> +	if (!prop) {
>> +		dev_err(dev, "No 'ti,efuse-settings' property found\n");
>> +		ret = -EINVAL;
>> +		goto out;
>> +	}
>> +
>> +	proplen = prop->length / sizeof(int);
>> +	data->num_vdd_table = proplen / 2;
>> +	/* Verify for corrupted OPP entries in dt */
>> +	if (data->num_vdd_table * 2 * sizeof(int) != prop->length) {
>> +		dev_err(dev, "Invalid 'ti,efuse-settings'\n");
>> +		ret = -EINVAL;
>> +		goto out;
>> +	}
>> +
>> +	ret = of_property_read_u32(dev->of_node, "ti,absolute-max-voltage-uv",
>> +				   &data->vdd_absolute_max_voltage_uv);
>> +	if (ret) {
>> +		dev_err(dev, "ti,absolute-max-voltage-uv is missing\n");
>> +		ret = -EINVAL;
>> +		goto out;
>> +	}
>> +
>> +	table = kzalloc(sizeof(*data->vdd_table) *
>> +				  data->num_vdd_table, GFP_KERNEL);
>> +	if (!table) {
>> +		ret = -ENOMEM;
>> +		goto out;
>> +	}
>> +	data->vdd_table = table;
>> +
>> +	val = prop->value;
>> +	for (i = 0; i < data->num_vdd_table; i++, table++) {
>> +		u32 efuse_offset;
>> +		u32 tmp;
>> +
>> +		table->reference_uv = be32_to_cpup(val++);
>> +		efuse_offset = be32_to_cpup(val++);
>> +
>> +		tmp = readl(base + efuse_offset);
>> +		tmp &= of_data->efuse_voltage_mask;
>> +		tmp >>= __ffs(of_data->efuse_voltage_mask);
>> +
>> +		table->optimized_uv = of_data->efuse_voltage_uv ? tmp :
>> +					tmp * 1000;
>> +
>> +		dev_dbg(dev, "[%d] efuse=0x%08x volt_table=%d vset=%d\n",
>> +			i, efuse_offset, table->reference_uv,
>> +			table->optimized_uv);
>> +
>> +		/*
>> +		 * Some older samples might not have optimized efuse
>> +		 * Use reference voltage for those - just add debug message
>> +		 * for them.
>> +		 */
>> +		if (!table->optimized_uv) {
>> +			dev_dbg(dev, "[%d] efuse=0x%08x volt_table=%d:vset0\n",
>> +				i, efuse_offset, table->reference_uv);
>> +			table->optimized_uv = table->reference_uv;
>> +		}
>> +	}
>> +out:
>> +	iounmap(base);
>> +out_map:
>> +	return ret;
>> +}
>> +
>> +/**
>> + * _free_optimized_voltages() - free resources for optvoltages
>> + * @dev:	device for which we need to free info
>> + * @data:	data specific to the device
>> + */
>> +static void _free_optimized_voltages(struct device *dev,
>> +				     struct ti_opp_supply_data *data)
>> +{
>> +	kfree(data->vdd_table);
>> +	data->vdd_table = NULL;
>> +	data->num_vdd_table = 0;
>> +}
>> +
>> +/**
>> + * _get_optimal_vdd_voltage() - Finds optimal voltage for the supply
>> + * @dev:	device for which we need to find info
>> + * @data:	data specific to the device
>> + * @reference_uv:	reference voltage (OPP voltage) for which we need value
>> + *
>> + * Return: if a match is found, return optimized voltage, else return
>> + * reference_uv, also return reference_uv if no optimization is needed.
>> + */
>> +static int _get_optimal_vdd_voltage(struct device *dev,
>> +				    struct ti_opp_supply_data *data,
>> +				    int reference_uv)
>> +{
>> +	int i;
>> +	struct ti_opp_supply_optimum_voltage_table *table;
>> +
>> +	if (!data->num_vdd_table)
>> +		return reference_uv;
>> +
>> +	table = data->vdd_table;
>> +	if (!table)
>> +		return -EINVAL;
>> +
>> +	/* Find a exact match - this list is usually very small */
>> +	for (i = 0; i < data->num_vdd_table; i++, table++)
>> +		if (table->reference_uv == reference_uv)
>> +			return table->optimized_uv;
>> +
>> +	/* IF things are screwed up, we'd make a mess on console.. ratelimit */
>> +	dev_err_ratelimited(dev, "%s: Failed optimized voltage match for %d\n",
>> +			    __func__, reference_uv);
>> +	return reference_uv;
>> +}
>> +
>> +static int _opp_set_voltage(struct device *dev,
>> +			    struct dev_pm_opp_supply *supply,
>> +			    int new_target_uv, struct regulator *reg,
>> +			    char *reg_name)
>> +{
>> +	int ret;
>> +	unsigned long vdd_uv, uv_max;
>> +
>> +	if (new_target_uv)
>> +		vdd_uv = new_target_uv;
>> +	else
>> +		vdd_uv = supply->u_volt;
>> +
>> +	/*
>> +	 * If we do have an absolute max voltage specified, then we should
>> +	 * use that voltage instead to allow for cases where the voltage rails
>> +	 * are ganged (example if we set the max for an opp as 1.12v, and
>> +	 * the absolute max is 1.5v, for another rail to get 1.25v, it cannot
>> +	 * be achieved if the regulator is constrainted to max of 1.12v, even
>> +	 * if it can function at 1.25v
>> +	 */
>> +	if (opp_data.vdd_absolute_max_voltage_uv)
>> +		uv_max = opp_data.vdd_absolute_max_voltage_uv;
>> +	else
>> +		uv_max = supply->u_volt_max;
>> +
>> +	if (vdd_uv > uv_max ||
>> +	    vdd_uv < supply->u_volt_min ||
>> +	    supply->u_volt_min > uv_max) {
>> +		dev_warn(dev,
>> +			 "Invalid range voltages [Min:%lu target:%lu Max:%lu]\n",
>> +			 supply->u_volt_min, vdd_uv, uv_max);
>> +		return -EINVAL;
>> +	}
>> +
>> +	dev_dbg(dev, "%s scaling to %luuV[min %luuV max %luuV]\n", reg_name,
>> +		vdd_uv, supply->u_volt_min,
>> +		uv_max);
>> +
>> +	ret = regulator_set_voltage_triplet(reg,
>> +					    supply->u_volt_min,
>> +					    vdd_uv,
>> +					    uv_max);
>> +	if (ret) {
>> +		dev_err(dev, "%s failed for %luuV[min %luuV max %luuV]\n",
>> +			reg_name, vdd_uv, supply->u_volt_min,
>> +			uv_max);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * ti_opp_supply_set_opp() - do the opp supply transition
>> + * @data:	information on regulators and new and old opps provided by
>> + *		opp core to use in transition
>> + *
>> + * Return: If successful, 0, else appropriate error value.
>> + */
>> +int ti_opp_supply_set_opp(struct dev_pm_set_opp_data *data)
>> +{
>> +	struct dev_pm_opp_supply *old_supply_vdd = &data->old_opp.supplies[0];
>> +	struct dev_pm_opp_supply *old_supply_vbb = &data->old_opp.supplies[1];
>> +	struct dev_pm_opp_supply *new_supply_vdd = &data->new_opp.supplies[0];
>> +	struct dev_pm_opp_supply *new_supply_vbb = &data->new_opp.supplies[1];
>> +	struct device *dev = data->dev;
>> +	unsigned long old_freq = data->old_opp.rate, freq = data->new_opp.rate;
>> +	struct clk *clk = data->clk;
>> +	struct regulator *vdd_reg = data->regulators[0];
>> +	struct regulator *vbb_reg = data->regulators[1];
>> +	int vdd_uv;
>> +	int ret;
>> +
>> +	vdd_uv = _get_optimal_vdd_voltage(dev, &opp_data,
>> +					  new_supply_vbb->u_volt);
>> +
>> +	/* Scaling up? Scale voltage before frequency */
>> +	if (freq > old_freq) {
>> +		ret = _opp_set_voltage(dev, new_supply_vdd, vdd_uv, vdd_reg,
>> +				       "vdd");
>> +		if (ret)
>> +			goto restore_voltage;
>> +
>> +		ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb");
>> +		if (ret)
>> +			goto restore_voltage;
>> +	}
>> +
>> +	/* Change frequency */
>> +	dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n",
>> +		__func__, old_freq, freq);
>> +
>> +	ret = clk_set_rate(clk, freq);
>> +	if (ret) {
>> +		dev_err(dev, "%s: failed to set clock rate: %d\n", __func__,
>> +			ret);
>> +		goto restore_voltage;
>> +	}
>> +
>> +	/* Scaling down? Scale voltage after frequency */
>> +	if (freq < old_freq) {
>> +		ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb");
>> +		if (ret)
>> +			goto restore_freq;
>> +
>> +		ret = _opp_set_voltage(dev, new_supply_vdd, vdd_uv, vdd_reg,
>> +				       "vdd");
>> +		if (ret)
>> +			goto restore_freq;
>> +	}
>> +
>> +	return 0;
>> +
>> +restore_freq:
>> +	ret = clk_set_rate(clk, old_freq);
>> +	if (ret)
>> +		dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
>> +			__func__, old_freq);
>> +restore_voltage:
>> +	/* This shouldn't harm even if the voltages weren't updated earlier */
>> +	if (old_supply_vdd->u_volt) {
>> +		ret = _opp_set_voltage(dev, old_supply_vbb, 0, vbb_reg, "vbb");
>> +		if (ret)
>> +			return ret;
>> +
>> +		ret = _opp_set_voltage(dev, old_supply_vdd, 0, vdd_reg,
>> +				       "vdd");
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct ti_opp_supply_of_data omap_generic_of_data = {
>> +};
>> +
>> +static const struct ti_opp_supply_of_data omap_omap5_of_data = {
>> +	.flags = OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE,
>> +	.efuse_voltage_mask = 0xFFF,
>> +	.efuse_voltage_uv = false,
>> +};
>> +
>> +static const struct ti_opp_supply_of_data omap_omap5core_of_data = {
>> +	.flags = OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE | OPPDM_HAS_NO_ABB,
>> +	.efuse_voltage_mask = 0xFFF,
>> +	.efuse_voltage_uv = false,
>> +};
>> +
>> +static const struct of_device_id ti_opp_supply_of_match[] = {
>> +	{.compatible = "ti,omap-opp-supply", .data = &omap_generic_of_data},
>> +	{.compatible = "ti,omap5-opp-supply", .data = &omap_omap5_of_data},
>> +	{.compatible = "ti,omap5-core-opp-supply",
>> +	 .data = &omap_omap5core_of_data},
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, ti_opp_supply_of_match);
>> +
>> +static int ti_opp_supply_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct device *cpu_dev = get_cpu_device(0);
>> +	const struct of_device_id *match;
>> +	const struct ti_opp_supply_of_data *of_data;
>> +	int ret = 0;
>> +
>> +	match = of_match_device(ti_opp_supply_of_match, dev);
>> +	if (!match) {
>> +		/* We do not expect this to happen */
>> +		dev_err(dev, "%s: Unable to match device\n", __func__);
>> +		return -ENODEV;
>> +	}
>> +	if (!match->data) {
>> +		/* Again, unlikely.. but mistakes do happen */
>> +		dev_err(dev, "%s: Bad data in match\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +	of_data = match->data;
>> +
>> +	dev_set_drvdata(dev, (void *)of_data);
>> +
>> +	/* If we need optimized voltage */
>> +	if (of_data->flags & OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE) {
>> +		ret = _store_optimized_voltages(dev, &opp_data);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	ret = PTR_ERR_OR_ZERO(dev_pm_opp_register_set_opp_helper(cpu_dev,
>> +								 ti_opp_supply_set_opp));
>> +	if (ret)
>> +		_free_optimized_voltages(dev, &opp_data);
>> +
>> +	return ret;
>> +}
>> +
>> +static struct platform_driver ti_opp_supply_driver = {
>> +	.probe = ti_opp_supply_probe,
>> +	.driver = {
>> +		   .name = "ti_opp_supply",
>> +		   .owner = THIS_MODULE,
>> +		   .of_match_table = of_match_ptr(ti_opp_supply_of_match),
>> +		   },
>> +};
>> +module_platform_driver(ti_opp_supply_driver);
>> +
>> +MODULE_DESCRIPTION("Texas Instruments OMAP OPP Supply driver");
>> +MODULE_AUTHOR("Texas Instruments Inc.");
>> +MODULE_LICENSE("GPL v2");
> 
> Looks fine otherwise.
> 

Thanks, appreciate the review, will resend with fixed license.

Regards,
Dave

^ permalink raw reply

* [patch v14 4/4] Documentation: jtag: Add ABI documentation
From: Oleksandr Shamray @ 2017-12-14 16:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513268971-13518-1-git-send-email-oleksandrs@mellanox.com>

Added document that describe the ABI for JTAG class drivrer

Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
v13->v14
v12->v13
v11->v12
Tobias Klauser <tklauser@distanz.ch>
- rename /Documentation/ABI/testing/jatg-dev -> jtag-dev
- Typo: s/interfase/interface
v10->v11
v9->v10
Fixes added by Oleksandr:
- change jtag-cdev to jtag-dev in documentation
- update Kernel Version and Date in jtag-dev documentation;
v8->v9
v7->v8
v6->v7
Comments pointed by Pavel Machek <pavel@ucw.cz>
- Added jtag-cdev documentation to Documentation/ABI/testing folder
---
 Documentation/ABI/testing/jtag-dev |   27 +++++++++++++++++++++++++++
 1 files changed, 27 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/jtag-dev

diff --git a/Documentation/ABI/testing/jtag-dev b/Documentation/ABI/testing/jtag-dev
new file mode 100644
index 0000000..cab867d
--- /dev/null
+++ b/Documentation/ABI/testing/jtag-dev
@@ -0,0 +1,27 @@
+What:		/dev/jtag[0-9]+
+Date:		October 2017
+KernelVersion:	4.15
+Contact:	oleksandrs at mellanox.com
+Description:
+		The misc device files /dev/jtag* are the interface
+		between JTAG master interface and userspace.
+
+		The ioctl(2)-based ABI is defined and documented in
+		[include/uapi]<linux/jtag.h>.
+
+		The following file operations are supported:
+
+		open(2)
+		The argument flag currently support only one access
+		mode O_RDWR.
+
+		ioctl(2)
+		Initiate various actions.
+		See the inline documentation in [include/uapi]<linux/jtag.h>
+		for descriptions of all ioctls.
+
+		close(2)
+		Stops and free up the I/O contexts that was associated
+		with the file descriptor.
+
+Users:		TBD
\ No newline at end of file
-- 
1.7.1

^ permalink raw reply related

* [patch v14 3/4] Documentation: jtag: Add bindings for Aspeed SoC 24xx and 25xx families JTAG master driver
From: Oleksandr Shamray @ 2017-12-14 16:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513268971-13518-1-git-send-email-oleksandrs@mellanox.com>

It has been tested on Mellanox system with BMC equipped with
Aspeed 2520 SoC for programming CPLD devices.

Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Acked-by: Rob Herring <robh@kernel.org>
---
v13->v14
v12->v13
v11->v12
v10->v11
v9->v10
v8->v9
v7->v8
Comments pointed by pointed by Joel Stanley <joel.stan@gmail.com>
- Change compatible string to ast2400 and ast2000

V6->v7
Comments pointed by Tobias Klauser <tklauser@distanz.ch>
 - Fix spell "Doccumentation" -> "Documentation"

v5->v6
Comments pointed by Tobias Klauser <tklauser@distanz.ch>
- Small nit: s/documentation/Documentation/

v4->v5

V3->v4
Comments pointed by Rob Herring <robh@kernel.org>
- delete unnecessary "status" and "reg-shift" descriptions in
  bndings file

v2->v3
Comments pointed by Rob Herring <robh@kernel.org>
- split Aspeed jtag driver and binding to sepatrate patches
- delete unnecessary "status" and "reg-shift" descriptions in
  bndings file
---
 .../devicetree/bindings/jtag/aspeed-jtag.txt       |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/jtag/aspeed-jtag.txt

diff --git a/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
new file mode 100644
index 0000000..8cfc610
--- /dev/null
+++ b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
@@ -0,0 +1,18 @@
+Aspeed JTAG driver for ast2400 and ast2500 SoC
+
+Required properties:
+- compatible:		Should be one of
+      - "aspeed,ast2400-jtag"
+      - "aspeed,ast2500-jtag"
+- reg			contains the offset and length of the JTAG memory
+			region
+- clocks		root clock of bus, should reference the APB clock
+- interrupts		should contain JTAG controller interrupt
+
+Example:
+jtag: jtag at 1e6e4000 {
+	compatible = "aspeed,ast2500-jtag";
+	reg = <0x1e6e4000 0x1c>;
+	clocks = <&clk_apb>;
+	interrupts = <43>;
+};
-- 
1.7.1

^ permalink raw reply related

* [patch v14 2/4] drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master driver
From: Oleksandr Shamray @ 2017-12-14 16:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513268971-13518-1-git-send-email-oleksandrs@mellanox.com>

Driver adds support of Aspeed 2500/2400 series SOC JTAG master controller.

Driver implements the following jtag ops:
- freq_get;
- freq_set;
- status_get;
- idle;
- xfer;

It has been tested on Mellanox system with BMC equipped with
Aspeed 2520 SoC for programming CPLD devices.

Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
v13->v14
Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
- Change style of head block comment from /**/ to //

v12->v13
Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
- Change jtag-aspeed.c licence type to
  SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
  and reorder line with license in description
Comments pointed by Kun Yi <kunyi@google.com>
- Changed capability check for aspeed,ast2400-jtag/ast200-jtag

v11->v12
Comments pointed by Chip Bilbrey <chip@bilbrey.org>
- Remove access mode from xfer and idle transactions
- Add new ioctl JTAG_SIOCMODE for set hw mode

v10->v11
v9->v10
V8->v9
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- add *data parameter to xfer function prototype

v7->v8
Comments pointed by Joel Stanley <joel.stan@gmail.com>
- aspeed_jtag_init replace goto to return;
- change input variables type from __u32 to u32
  in functios freq_get, freq_set, status_get
- change sm_ variables type from char to u8
- in jatg_init add disable clocks on error case
- remove release_mem_region on error case
- remove devm_free_irq on jtag_deinit
- Fix misspelling Disabe/Disable
- Change compatible string to ast2400 and ast2000

v6->v7
Notifications from kbuild test robot <lkp@intel.com>
- Add include <linux/types.h> to jtag-asapeed.c

v5->v6
v4->v5
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- Added HAS_IOMEM dependence in Kconfig to avoid
  "undefined reference to `devm_ioremap_resource'" error,
  because in some arch this not supported

v3->v4
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- change transaction pointer tdio type  to __u64
- change internal status type from enum to __u32

v2->v3

v1->v2
Comments pointed by Greg KH <gregkh@linuxfoundation.org>
- change license type from GPLv2/BSD to GPLv2

Comments pointed by Neil Armstrong <narmstrong@baylibre.com>
- Add clk_prepare_enable/clk_disable_unprepare in clock init/deinit
- Change .compatible to soc-specific compatible names
  aspeed,aspeed4000-jtag/aspeed5000-jtag
- Added dt-bindings

Comments pointed by Arnd Bergmann <arnd@arndb.de>
- Reorder functions and removed the forward declarations
- Add static const qualifier to state machine states transitions
- Change .compatible to soc-specific compatible names
  aspeed,aspeed4000-jtag/aspeed5000-jtag
- Add dt-bindings

Comments pointed by Randy Dunlap <rdunlap@infradead.org>
- Change module name jtag-aspeed in description in Kconfig

Comments pointed by kbuild test robot <lkp@intel.com>
- Remove invalid include <asm/mach-types.h>
- add resource_size instead of calculation
---
 drivers/jtag/Kconfig       |   13 +
 drivers/jtag/Makefile      |    1 +
 drivers/jtag/jtag-aspeed.c |  783 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 797 insertions(+), 0 deletions(-)
 create mode 100644 drivers/jtag/jtag-aspeed.c

diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
index 0fad1a3..098beb0 100644
--- a/drivers/jtag/Kconfig
+++ b/drivers/jtag/Kconfig
@@ -14,3 +14,16 @@ menuconfig JTAG
 
 	  To compile this driver as a module, choose M here: the module will
 	  be called jtag.
+
+menuconfig JTAG_ASPEED
+	tristate "Aspeed SoC JTAG controller support"
+	depends on JTAG && HAS_IOMEM
+	---help---
+	  This provides a support for Aspeed JTAG device, equipped on
+	  Aspeed SoC 24xx and 25xx families. Drivers allows programming
+	  of hardware devices, connected to SoC through the JTAG interface.
+
+	  If you want this support, you should say Y here.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called jtag-aspeed.
diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
index af37493..04a855e 100644
--- a/drivers/jtag/Makefile
+++ b/drivers/jtag/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_JTAG)		+= jtag.o
+obj-$(CONFIG_JTAG_ASPEED)	+= jtag-aspeed.o
diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c
new file mode 100644
index 0000000..99277d2
--- /dev/null
+++ b/drivers/jtag/jtag-aspeed.c
@@ -0,0 +1,783 @@
+// SPDX-License-Identifier: GPL-2.0
+// drivers/jtag/aspeed-jtag.c
+//
+// Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/jtag.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <uapi/linux/jtag.h>
+
+#define ASPEED_JTAG_DATA		0x00
+#define ASPEED_JTAG_INST		0x04
+#define ASPEED_JTAG_CTRL		0x08
+#define ASPEED_JTAG_ISR			0x0C
+#define ASPEED_JTAG_SW			0x10
+#define ASPEED_JTAG_TCK			0x14
+#define ASPEED_JTAG_EC			0x18
+
+#define ASPEED_JTAG_DATA_MSB		0x01
+#define ASPEED_JTAG_DATA_CHUNK_SIZE	0x20
+
+/* ASPEED_JTAG_CTRL: Engine Control */
+#define ASPEED_JTAG_CTL_ENG_EN		BIT(31)
+#define ASPEED_JTAG_CTL_ENG_OUT_EN	BIT(30)
+#define ASPEED_JTAG_CTL_FORCE_TMS	BIT(29)
+#define ASPEED_JTAG_CTL_INST_LEN(x)	((x) << 20)
+#define ASPEED_JTAG_CTL_LASPEED_INST	BIT(17)
+#define ASPEED_JTAG_CTL_INST_EN		BIT(16)
+#define ASPEED_JTAG_CTL_DR_UPDATE	BIT(10)
+#define ASPEED_JTAG_CTL_DATA_LEN(x)	((x) << 4)
+#define ASPEED_JTAG_CTL_LASPEED_DATA	BIT(1)
+#define ASPEED_JTAG_CTL_DATA_EN		BIT(0)
+
+/* ASPEED_JTAG_ISR : Interrupt status and enable */
+#define ASPEED_JTAG_ISR_INST_PAUSE	BIT(19)
+#define ASPEED_JTAG_ISR_INST_COMPLETE	BIT(18)
+#define ASPEED_JTAG_ISR_DATA_PAUSE	BIT(17)
+#define ASPEED_JTAG_ISR_DATA_COMPLETE	BIT(16)
+#define ASPEED_JTAG_ISR_INST_PAUSE_EN	BIT(3)
+#define ASPEED_JTAG_ISR_INST_COMPLETE_EN BIT(2)
+#define ASPEED_JTAG_ISR_DATA_PAUSE_EN	BIT(1)
+#define ASPEED_JTAG_ISR_DATA_COMPLETE_EN BIT(0)
+#define ASPEED_JTAG_ISR_INT_EN_MASK	GENMASK(3, 0)
+#define ASPEED_JTAG_ISR_INT_MASK	GENMASK(19, 16)
+
+/* ASPEED_JTAG_SW : Software Mode and Status */
+#define ASPEED_JTAG_SW_MODE_EN		BIT(19)
+#define ASPEED_JTAG_SW_MODE_TCK		BIT(18)
+#define ASPEED_JTAG_SW_MODE_TMS		BIT(17)
+#define ASPEED_JTAG_SW_MODE_TDIO	BIT(16)
+
+/* ASPEED_JTAG_TCK : TCK Control */
+#define ASPEED_JTAG_TCK_DIVISOR_MASK	GENMASK(10, 0)
+#define ASPEED_JTAG_TCK_GET_DIV(x)	((x) & ASPEED_JTAG_TCK_DIVISOR_MASK)
+
+/* ASPEED_JTAG_EC : Controller set for go to IDLE */
+#define ASPEED_JTAG_EC_GO_IDLE		BIT(0)
+
+#define ASPEED_JTAG_IOUT_LEN(len)	(ASPEED_JTAG_CTL_ENG_EN |\
+					 ASPEED_JTAG_CTL_ENG_OUT_EN |\
+					 ASPEED_JTAG_CTL_INST_LEN(len))
+
+#define ASPEED_JTAG_DOUT_LEN(len)	(ASPEED_JTAG_CTL_ENG_EN |\
+					 ASPEED_JTAG_CTL_ENG_OUT_EN |\
+					 ASPEED_JTAG_CTL_DATA_LEN(len))
+
+#define ASPEED_JTAG_TCK_WAIT		10
+#define ASPEED_JTAG_RESET_CNTR		10
+
+#define ASPEED_JTAG_NAME		"jtag-aspeed"
+
+struct aspeed_jtag {
+	void __iomem			*reg_base;
+	struct device			*dev;
+	struct clk			*pclk;
+	enum jtag_endstate		status;
+	int				irq;
+	u32				flag;
+	wait_queue_head_t		jtag_wq;
+	u32				mode;
+};
+
+static char *end_status_str[] = {"idle", "ir pause", "drpause"};
+
+static u32 aspeed_jtag_read(struct aspeed_jtag *aspeed_jtag, u32 reg)
+{
+	return readl(aspeed_jtag->reg_base + reg);
+}
+
+static void
+aspeed_jtag_write(struct aspeed_jtag *aspeed_jtag, u32 val, u32 reg)
+{
+	writel(val, aspeed_jtag->reg_base + reg);
+}
+
+static int aspeed_jtag_freq_set(struct jtag *jtag, u32 freq)
+{
+	struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+	unsigned long apb_frq;
+	u32 tck_val;
+	u16 div;
+
+	apb_frq = clk_get_rate(aspeed_jtag->pclk);
+	div = (apb_frq % freq == 0) ? (apb_frq / freq) - 1 : (apb_frq / freq);
+	tck_val = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK);
+	aspeed_jtag_write(aspeed_jtag,
+			  (tck_val & ASPEED_JTAG_TCK_DIVISOR_MASK) | div,
+			  ASPEED_JTAG_TCK);
+	return 0;
+}
+
+static int aspeed_jtag_freq_get(struct jtag *jtag, u32 *frq)
+{
+	struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+	u32 pclk;
+	u32 tck;
+
+	pclk = clk_get_rate(aspeed_jtag->pclk);
+	tck = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK);
+	*frq = pclk / (ASPEED_JTAG_TCK_GET_DIV(tck) + 1);
+
+	return 0;
+}
+
+static int aspeed_jtag_mode_set(struct jtag *jtag, u32 mode)
+{
+	struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+
+	aspeed_jtag->mode = mode;
+	return 0;
+}
+
+static void aspeed_jtag_sw_delay(struct aspeed_jtag *aspeed_jtag, int cnt)
+{
+	int i;
+
+	for (i = 0; i < cnt; i++)
+		aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW);
+}
+
+static char aspeed_jtag_tck_cycle(struct aspeed_jtag *aspeed_jtag,
+				  u8 tms, u8 tdi)
+{
+	char tdo = 0;
+
+	/* TCK = 0 */
+	aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+			  (tms * ASPEED_JTAG_SW_MODE_TMS) |
+			  (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
+
+	aspeed_jtag_sw_delay(aspeed_jtag, ASPEED_JTAG_TCK_WAIT);
+
+	/* TCK = 1 */
+	aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+			  ASPEED_JTAG_SW_MODE_TCK |
+			  (tms * ASPEED_JTAG_SW_MODE_TMS) |
+			  (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
+
+	if (aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW) &
+	    ASPEED_JTAG_SW_MODE_TDIO)
+		tdo = 1;
+
+	aspeed_jtag_sw_delay(aspeed_jtag, ASPEED_JTAG_TCK_WAIT);
+
+	/* TCK = 0 */
+	aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+			  (tms * ASPEED_JTAG_SW_MODE_TMS) |
+			  (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
+	return tdo;
+}
+
+static void aspeed_jtag_wait_instruction_pause(struct aspeed_jtag *aspeed_jtag)
+{
+	wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+				 ASPEED_JTAG_ISR_INST_PAUSE);
+	aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_PAUSE;
+}
+
+static void
+aspeed_jtag_wait_instruction_complete(struct aspeed_jtag *aspeed_jtag)
+{
+	wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+				 ASPEED_JTAG_ISR_INST_COMPLETE);
+	aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_COMPLETE;
+}
+
+static void
+aspeed_jtag_wait_data_pause_complete(struct aspeed_jtag *aspeed_jtag)
+{
+	wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+				 ASPEED_JTAG_ISR_DATA_PAUSE);
+	aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_PAUSE;
+}
+
+static void aspeed_jtag_wait_data_complete(struct aspeed_jtag *aspeed_jtag)
+{
+	wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+				 ASPEED_JTAG_ISR_DATA_COMPLETE);
+	aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_COMPLETE;
+}
+
+static void aspeed_jtag_sm_cycle(struct aspeed_jtag *aspeed_jtag, const u8 *tms,
+				 int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		aspeed_jtag_tck_cycle(aspeed_jtag, tms[i], 0);
+}
+
+static void aspeed_jtag_run_test_idle_sw(struct aspeed_jtag *aspeed_jtag,
+					 struct jtag_run_test_idle *runtest)
+{
+	static const u8 sm_pause_irpause[] = {1, 1, 1, 1, 0, 1, 0};
+	static const u8 sm_pause_drpause[] = {1, 1, 1, 0, 1, 0};
+	static const u8 sm_idle_irpause[] = {1, 1, 0, 1, 0};
+	static const u8 sm_idle_drpause[] = {1, 0, 1, 0};
+	static const u8 sm_pause_idle[] = {1, 1, 0};
+	int i;
+
+	/* SW mode from idle/pause-> to pause/idle */
+	if (runtest->reset) {
+		for (i = 0; i < ASPEED_JTAG_RESET_CNTR; i++)
+			aspeed_jtag_tck_cycle(aspeed_jtag, 1, 0);
+	}
+
+	switch (aspeed_jtag->status) {
+	case JTAG_STATE_IDLE:
+		switch (runtest->endstate) {
+		case JTAG_STATE_PAUSEIR:
+			/* ->DRSCan->IRSCan->IRCap->IRExit1->PauseIR */
+			aspeed_jtag_sm_cycle(aspeed_jtag, sm_idle_irpause,
+					     sizeof(sm_idle_irpause));
+
+			aspeed_jtag->status = JTAG_STATE_PAUSEIR;
+			break;
+		case JTAG_STATE_PAUSEDR:
+			/* ->DRSCan->DRCap->DRExit1->PauseDR */
+			aspeed_jtag_sm_cycle(aspeed_jtag, sm_idle_drpause,
+					     sizeof(sm_idle_drpause));
+
+			aspeed_jtag->status = JTAG_STATE_PAUSEDR;
+			break;
+		case JTAG_STATE_IDLE:
+			/* IDLE */
+			aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0);
+			aspeed_jtag->status = JTAG_STATE_IDLE;
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case JTAG_STATE_PAUSEIR:
+	/* Fall-through */
+	case JTAG_STATE_PAUSEDR:
+		/* From IR/DR Pause */
+		switch (runtest->endstate) {
+		case JTAG_STATE_PAUSEIR:
+			/*
+			 * to Exit2 IR/DR->Updt IR/DR->DRSCan->IRSCan->IRCap->
+			 * IRExit1->PauseIR
+			 */
+			aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_irpause,
+					     sizeof(sm_pause_irpause));
+
+			aspeed_jtag->status = JTAG_STATE_PAUSEIR;
+			break;
+		case JTAG_STATE_PAUSEDR:
+			/*
+			 * to Exit2 IR/DR->Updt IR/DR->DRSCan->DRCap->
+			 * DRExit1->PauseDR
+			 */
+			aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_drpause,
+					     sizeof(sm_pause_drpause));
+			aspeed_jtag->status = JTAG_STATE_PAUSEDR;
+			break;
+		case JTAG_STATE_IDLE:
+			/* to Exit2 IR/DR->Updt IR/DR->IDLE */
+			aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_idle,
+					     sizeof(sm_pause_idle));
+			aspeed_jtag->status = JTAG_STATE_IDLE;
+			break;
+		default:
+			break;
+		}
+		break;
+
+	default:
+		dev_err(aspeed_jtag->dev, "aspeed_jtag_run_test_idle error\n");
+		break;
+	}
+
+	/* Stay on IDLE for at least  TCK cycle */
+	for (i = 0; i < runtest->tck; i++)
+		aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0);
+}
+
+/**
+ * aspeed_jtag_run_test_idle:
+ * JTAG reset: generates at least 9 TMS high and 1 TMS low to force
+ * devices into Run-Test/Idle State.
+ */
+static int aspeed_jtag_idle(struct jtag *jtag,
+			    struct jtag_run_test_idle *runtest)
+{
+	struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+
+	dev_dbg(aspeed_jtag->dev, "aspeed_jtag runtest, status:%d, mode:%s, state:%s, reset:%d, tck:%d\n",
+		aspeed_jtag->status,
+		aspeed_jtag->mode & JTAG_XFER_HW_MODE ? "HW" : "SW",
+		end_status_str[runtest->endstate], runtest->reset,
+		runtest->tck);
+
+	if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) {
+		aspeed_jtag_run_test_idle_sw(aspeed_jtag, runtest);
+		return 0;
+	}
+
+	/* Disable sw mode */
+	aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW);
+	/* x TMS high + 1 TMS low */
+	if (runtest->reset)
+		aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_CTL_ENG_EN |
+				  ASPEED_JTAG_CTL_ENG_OUT_EN |
+				  ASPEED_JTAG_CTL_FORCE_TMS, ASPEED_JTAG_CTRL);
+	else
+		aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_EC_GO_IDLE,
+				  ASPEED_JTAG_EC);
+
+	aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+			  ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+
+	aspeed_jtag->status = JTAG_STATE_IDLE;
+	return 0;
+}
+
+static void aspeed_jtag_xfer_sw(struct aspeed_jtag *aspeed_jtag,
+				struct jtag_xfer *xfer, unsigned long *data)
+{
+	unsigned long remain_xfer = xfer->length;
+	unsigned long shift_bits = 0;
+	unsigned long index = 0;
+	unsigned long tdi;
+	char          tdo;
+
+	if (xfer->direction == JTAG_READ_XFER)
+		tdi = UINT_MAX;
+	else
+		tdi = data[index];
+
+	while (remain_xfer > 1) {
+		tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 0,
+					    tdi & ASPEED_JTAG_DATA_MSB);
+		data[index] |= tdo << (shift_bits %
+					    ASPEED_JTAG_DATA_CHUNK_SIZE);
+
+		tdi >>= 1;
+		shift_bits++;
+		remain_xfer--;
+
+		if (shift_bits % ASPEED_JTAG_DATA_CHUNK_SIZE == 0) {
+			dev_dbg(aspeed_jtag->dev, "R/W data[%lu]:%lx\n",
+				index, data[index]);
+
+			tdo = 0;
+			index++;
+
+			if (xfer->direction == JTAG_READ_XFER)
+				tdi = UINT_MAX;
+			else
+				tdi = data[index];
+		}
+	}
+
+	tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 1, tdi & ASPEED_JTAG_DATA_MSB);
+	data[index] |= tdo << (shift_bits % ASPEED_JTAG_DATA_CHUNK_SIZE);
+}
+
+static void aspeed_jtag_xfer_push_data(struct aspeed_jtag *aspeed_jtag,
+				       enum jtag_xfer_type type, u32 bits_len)
+{
+	dev_dbg(aspeed_jtag->dev, "shift bits %d\n", bits_len);
+
+	if (type == JTAG_SIR_XFER) {
+		aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_IOUT_LEN(bits_len),
+				  ASPEED_JTAG_CTRL);
+		aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len) |
+				  ASPEED_JTAG_CTL_INST_EN, ASPEED_JTAG_CTRL);
+	} else {
+		aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len),
+				  ASPEED_JTAG_CTRL);
+		aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len) |
+				  ASPEED_JTAG_CTL_DATA_EN, ASPEED_JTAG_CTRL);
+	}
+}
+
+static void aspeed_jtag_xfer_push_data_last(struct aspeed_jtag *aspeed_jtag,
+					    enum jtag_xfer_type type,
+					    u32 shift_bits,
+					    enum jtag_endstate endstate)
+{
+	if (endstate != JTAG_STATE_IDLE) {
+		if (type == JTAG_SIR_XFER) {
+			dev_dbg(aspeed_jtag->dev, "IR Keep Pause\n");
+
+			aspeed_jtag_write(aspeed_jtag,
+					  ASPEED_JTAG_IOUT_LEN(shift_bits),
+					  ASPEED_JTAG_CTRL);
+			aspeed_jtag_write(aspeed_jtag,
+					  ASPEED_JTAG_IOUT_LEN(shift_bits) |
+					  ASPEED_JTAG_CTL_INST_EN,
+					  ASPEED_JTAG_CTRL);
+			aspeed_jtag_wait_instruction_pause(aspeed_jtag);
+		} else {
+			dev_dbg(aspeed_jtag->dev, "DR Keep Pause\n");
+			aspeed_jtag_write(aspeed_jtag,
+					  ASPEED_JTAG_DOUT_LEN(shift_bits) |
+					  ASPEED_JTAG_CTL_DR_UPDATE,
+					  ASPEED_JTAG_CTRL);
+			aspeed_jtag_write(aspeed_jtag,
+					  ASPEED_JTAG_DOUT_LEN(shift_bits) |
+					  ASPEED_JTAG_CTL_DR_UPDATE |
+					  ASPEED_JTAG_CTL_DATA_EN,
+					  ASPEED_JTAG_CTRL);
+			aspeed_jtag_wait_data_pause_complete(aspeed_jtag);
+		}
+	} else {
+		if (type == JTAG_SIR_XFER) {
+			dev_dbg(aspeed_jtag->dev, "IR go IDLE\n");
+
+			aspeed_jtag_write(aspeed_jtag,
+					  ASPEED_JTAG_IOUT_LEN(shift_bits) |
+					  ASPEED_JTAG_CTL_LASPEED_INST,
+					  ASPEED_JTAG_CTRL);
+			aspeed_jtag_write(aspeed_jtag,
+					  ASPEED_JTAG_IOUT_LEN(shift_bits) |
+					  ASPEED_JTAG_CTL_LASPEED_INST |
+					  ASPEED_JTAG_CTL_INST_EN,
+					  ASPEED_JTAG_CTRL);
+			aspeed_jtag_wait_instruction_complete(aspeed_jtag);
+		} else {
+			dev_dbg(aspeed_jtag->dev, "DR go IDLE\n");
+
+			aspeed_jtag_write(aspeed_jtag,
+					  ASPEED_JTAG_DOUT_LEN(shift_bits) |
+					  ASPEED_JTAG_CTL_LASPEED_DATA,
+					  ASPEED_JTAG_CTRL);
+			aspeed_jtag_write(aspeed_jtag,
+					  ASPEED_JTAG_DOUT_LEN(shift_bits) |
+					  ASPEED_JTAG_CTL_LASPEED_DATA |
+					  ASPEED_JTAG_CTL_DATA_EN,
+					  ASPEED_JTAG_CTRL);
+			aspeed_jtag_wait_data_complete(aspeed_jtag);
+		}
+	}
+}
+
+static void aspeed_jtag_xfer_hw(struct aspeed_jtag *aspeed_jtag,
+				struct jtag_xfer *xfer, unsigned long *data)
+{
+	unsigned long remain_xfer = xfer->length;
+	unsigned long index = 0;
+	char shift_bits;
+	u32 data_reg;
+
+	data_reg = xfer->type == JTAG_SIR_XFER ?
+		   ASPEED_JTAG_INST : ASPEED_JTAG_DATA;
+	while (remain_xfer) {
+		if (xfer->direction == JTAG_WRITE_XFER) {
+			dev_dbg(aspeed_jtag->dev, "W dr->dr_data[%lu]:%lx\n",
+				index, data[index]);
+
+			aspeed_jtag_write(aspeed_jtag, data[index], data_reg);
+		} else {
+			aspeed_jtag_write(aspeed_jtag, 0, data_reg);
+		}
+
+		if (remain_xfer > ASPEED_JTAG_DATA_CHUNK_SIZE) {
+			shift_bits = ASPEED_JTAG_DATA_CHUNK_SIZE;
+
+			/*
+			 * Read bytes were not equals to column length
+			 * and go to Pause-DR
+			 */
+			aspeed_jtag_xfer_push_data(aspeed_jtag, xfer->type,
+						   shift_bits);
+		} else {
+			/*
+			 * Read bytes equals to column length =>
+			 * Update-DR
+			 */
+			shift_bits = remain_xfer;
+			aspeed_jtag_xfer_push_data_last(aspeed_jtag, xfer->type,
+							shift_bits,
+							xfer->endstate);
+		}
+
+		if (xfer->direction == JTAG_READ_XFER) {
+			if (shift_bits < ASPEED_JTAG_DATA_CHUNK_SIZE) {
+				data[index] = aspeed_jtag_read(aspeed_jtag,
+							       data_reg);
+
+				data[index] >>= ASPEED_JTAG_DATA_CHUNK_SIZE -
+								shift_bits;
+			} else {
+				data[index] = aspeed_jtag_read(aspeed_jtag,
+							       data_reg);
+			}
+			dev_dbg(aspeed_jtag->dev, "R dr->dr_data[%lu]:%lx\n",
+				index, data[index]);
+		}
+
+		remain_xfer = remain_xfer - shift_bits;
+		index++;
+		dev_dbg(aspeed_jtag->dev, "remain_xfer %lu\n", remain_xfer);
+	}
+}
+
+static int aspeed_jtag_xfer(struct jtag *jtag, struct jtag_xfer *xfer,
+			    u8 *xfer_data)
+{
+	static const u8 sm_update_shiftir[] = {1, 1, 0, 0};
+	static const u8 sm_update_shiftdr[] = {1, 0, 0};
+	static const u8 sm_pause_idle[] = {1, 1, 0};
+	static const u8 sm_pause_update[] = {1, 1};
+	struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+	unsigned long *data = (unsigned long *)xfer_data;
+	unsigned long remain_xfer = xfer->length;
+	unsigned long offset;
+	char dbg_str[256];
+	int pos = 0;
+	int i;
+
+	for (offset = 0, i = 0; offset < xfer->length;
+			offset += ASPEED_JTAG_DATA_CHUNK_SIZE, i++) {
+		pos += snprintf(&dbg_str[pos], sizeof(dbg_str) - pos,
+				"0x%08lx ", data[i]);
+	}
+
+	dev_dbg(aspeed_jtag->dev, "aspeed_jtag %s %s xfer, mode:%s, END:%d, len:%lu, TDI[%s]\n",
+		xfer->type == JTAG_SIR_XFER ? "SIR" : "SDR",
+		xfer->direction == JTAG_READ_XFER ? "READ" : "WRITE",
+		aspeed_jtag->mode & JTAG_XFER_HW_MODE ? "HW" : "SW",
+		xfer->endstate, remain_xfer, dbg_str);
+
+	if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) {
+		/* SW mode */
+		aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+				  ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+
+		if (aspeed_jtag->status != JTAG_STATE_IDLE) {
+			/*IR/DR Pause->Exit2 IR / DR->Update IR /DR */
+			aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_update,
+					     sizeof(sm_pause_update));
+		}
+
+		if (xfer->type == JTAG_SIR_XFER)
+			/* ->IRSCan->CapIR->ShiftIR */
+			aspeed_jtag_sm_cycle(aspeed_jtag, sm_update_shiftir,
+					     sizeof(sm_update_shiftir));
+		else
+			/* ->DRScan->DRCap->DRShift */
+			aspeed_jtag_sm_cycle(aspeed_jtag, sm_update_shiftdr,
+					     sizeof(sm_update_shiftdr));
+
+		aspeed_jtag_xfer_sw(aspeed_jtag, xfer, data);
+
+		/* DIPause/DRPause */
+		aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0);
+
+		if (xfer->endstate == JTAG_STATE_IDLE) {
+			/* ->DRExit2->DRUpdate->IDLE */
+			aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_idle,
+					     sizeof(sm_pause_idle));
+		}
+	} else {
+		/* hw mode */
+		aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW);
+		aspeed_jtag_xfer_hw(aspeed_jtag, xfer, data);
+	}
+
+	aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+			  ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+	aspeed_jtag->status = xfer->endstate;
+	return 0;
+}
+
+static int aspeed_jtag_status_get(struct jtag *jtag, u32 *status)
+{
+	struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+
+	*status = aspeed_jtag->status;
+	return 0;
+}
+
+static irqreturn_t aspeed_jtag_interrupt(s32 this_irq, void *dev_id)
+{
+	struct aspeed_jtag *aspeed_jtag = dev_id;
+	irqreturn_t ret;
+	u32 status;
+
+	status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
+	dev_dbg(aspeed_jtag->dev, "status %x\n", status);
+
+	if (status & ASPEED_JTAG_ISR_INT_MASK) {
+		aspeed_jtag_write(aspeed_jtag,
+				  (status & ASPEED_JTAG_ISR_INT_MASK)
+				  | (status & ASPEED_JTAG_ISR_INT_EN_MASK),
+				  ASPEED_JTAG_ISR);
+		aspeed_jtag->flag |= status & ASPEED_JTAG_ISR_INT_MASK;
+	}
+
+	if (aspeed_jtag->flag) {
+		wake_up_interruptible(&aspeed_jtag->jtag_wq);
+		ret = IRQ_HANDLED;
+	} else {
+		dev_err(aspeed_jtag->dev, "aspeed_jtag irq status:%x\n",
+			status);
+		ret = IRQ_NONE;
+	}
+	return ret;
+}
+
+int aspeed_jtag_init(struct platform_device *pdev,
+		     struct aspeed_jtag *aspeed_jtag)
+{
+	struct resource *res;
+	int err;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	aspeed_jtag->reg_base = devm_ioremap_resource(aspeed_jtag->dev, res);
+	if (IS_ERR(aspeed_jtag->reg_base))
+		return -ENOMEM;
+
+	aspeed_jtag->pclk = devm_clk_get(aspeed_jtag->dev, NULL);
+	if (IS_ERR(aspeed_jtag->pclk)) {
+		dev_err(aspeed_jtag->dev, "devm_clk_get failed\n");
+		return PTR_ERR(aspeed_jtag->pclk);
+	}
+
+	clk_prepare_enable(aspeed_jtag->pclk);
+
+	aspeed_jtag->irq = platform_get_irq(pdev, 0);
+	if (aspeed_jtag->irq < 0) {
+		dev_err(aspeed_jtag->dev, "no irq specified\n");
+		err = -ENOENT;
+		goto clk_unprep;
+	}
+
+	/* Enable clock */
+	aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_CTL_ENG_EN |
+			  ASPEED_JTAG_CTL_ENG_OUT_EN, ASPEED_JTAG_CTRL);
+	aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+			  ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+
+	err = devm_request_irq(aspeed_jtag->dev, aspeed_jtag->irq,
+			       aspeed_jtag_interrupt, 0,
+			       "aspeed-jtag", aspeed_jtag);
+	if (err) {
+		dev_err(aspeed_jtag->dev, "aspeed_jtag unable to get IRQ");
+		goto clk_unprep;
+	}
+	dev_dbg(&pdev->dev, "aspeed_jtag:IRQ %d.\n", aspeed_jtag->irq);
+
+	aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_INST_PAUSE |
+			  ASPEED_JTAG_ISR_INST_COMPLETE |
+			  ASPEED_JTAG_ISR_DATA_PAUSE |
+			  ASPEED_JTAG_ISR_DATA_COMPLETE |
+			  ASPEED_JTAG_ISR_INST_PAUSE_EN |
+			  ASPEED_JTAG_ISR_INST_COMPLETE_EN |
+			  ASPEED_JTAG_ISR_DATA_PAUSE_EN |
+			  ASPEED_JTAG_ISR_DATA_COMPLETE_EN,
+			  ASPEED_JTAG_ISR);
+
+	aspeed_jtag->flag = 0;
+	aspeed_jtag->mode = 0;
+	init_waitqueue_head(&aspeed_jtag->jtag_wq);
+	return 0;
+
+clk_unprep:
+	clk_disable_unprepare(aspeed_jtag->pclk);
+	return err;
+}
+
+int aspeed_jtag_deinit(struct platform_device *pdev,
+		       struct aspeed_jtag *aspeed_jtag)
+{
+	aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_ISR);
+	/* Disable clock */
+	aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_CTRL);
+	clk_disable_unprepare(aspeed_jtag->pclk);
+	return 0;
+}
+
+static const struct jtag_ops aspeed_jtag_ops = {
+	.freq_get = aspeed_jtag_freq_get,
+	.freq_set = aspeed_jtag_freq_set,
+	.status_get = aspeed_jtag_status_get,
+	.idle = aspeed_jtag_idle,
+	.xfer = aspeed_jtag_xfer,
+	.mode_set = aspeed_jtag_mode_set
+};
+
+static int aspeed_jtag_probe(struct platform_device *pdev)
+{
+	struct aspeed_jtag *aspeed_jtag;
+	struct device *dev;
+	struct jtag *jtag;
+	int err;
+
+	dev = &pdev->dev;
+	if (!of_device_is_compatible(pdev->dev.of_node,
+				     "aspeed,ast2500-jtag") &&
+	    !of_device_is_compatible(pdev->dev.of_node,
+				     "aspeed,ast2400-jtag"))
+		return -ENODEV;
+
+	jtag = jtag_alloc(sizeof(*aspeed_jtag), &aspeed_jtag_ops);
+	if (!jtag)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, jtag);
+	aspeed_jtag = jtag_priv(jtag);
+	aspeed_jtag->dev = &pdev->dev;
+
+	/* Initialize device*/
+	err = aspeed_jtag_init(pdev, aspeed_jtag);
+	if (err)
+		goto err_jtag_init;
+
+	/* Initialize JTAG core structure*/
+	err = jtag_register(jtag);
+	if (err)
+		goto err_jtag_register;
+
+	return 0;
+
+err_jtag_register:
+	aspeed_jtag_deinit(pdev, aspeed_jtag);
+err_jtag_init:
+	jtag_free(jtag);
+	return err;
+}
+
+static int aspeed_jtag_remove(struct platform_device *pdev)
+{
+	struct jtag *jtag;
+
+	jtag = platform_get_drvdata(pdev);
+	aspeed_jtag_deinit(pdev, jtag_priv(jtag));
+	jtag_unregister(jtag);
+	jtag_free(jtag);
+	return 0;
+}
+
+static const struct of_device_id aspeed_jtag_of_match[] = {
+	{ .compatible = "aspeed,ast2400-jtag", },
+	{ .compatible = "aspeed,ast2500-jtag", },
+	{}
+};
+
+static struct platform_driver aspeed_jtag_driver = {
+	.probe = aspeed_jtag_probe,
+	.remove = aspeed_jtag_remove,
+	.driver = {
+		.name = ASPEED_JTAG_NAME,
+		.of_match_table = aspeed_jtag_of_match,
+	},
+};
+module_platform_driver(aspeed_jtag_driver);
+
+MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>");
+MODULE_DESCRIPTION("ASPEED JTAG driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.1

^ permalink raw reply related

* [patch v14 1/4] drivers: jtag: Add JTAG core driver
From: Oleksandr Shamray @ 2017-12-14 16:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513268971-13518-1-git-send-email-oleksandrs@mellanox.com>

Initial patch for JTAG driver
JTAG class driver provide infrastructure to support hardware/software
JTAG platform drivers. It provide user layer API interface for flashing
and debugging external devices which equipped with JTAG interface
using standard transactions.

Driver exposes set of IOCTL to user space for:
- XFER:
- SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
- SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
- RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
  number of clocks).
- SIOCFREQ/GIOCFREQ for setting and reading JTAG frequency.

Driver core provides set of internal APIs for allocation and
registration:
- jtag_register;
- jtag_unregister;
- jtag_alloc;
- jtag_free;

Platform driver on registration with jtag-core creates the next
entry in dev folder:
/dev/jtagX

Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
v13->v14
Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
- Change style of head block comment from /**/ to //

v12->v13
Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
- Change jtag.c licence type to
  SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
  and reorder line with license in description
v11->v12
Comments pointed by Greg KH <gregkh@linuxfoundation.org>
- Change jtag.h licence type to
  SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
  and reorder line with license in description

Chip Bilbrey <chip@bilbrey.org>
- Remove Apeed reference from uapi jtag.h header
- Remove access mode from xfer and idle transactions
- Add new ioctl JTAG_SIOCMODE for set hw mode
- Add only one open per JTAG port blocking with mutex blocking

v10->v11
Notifications from kbuild test robot <lkp@intel.com>
- include types.h headeri to jtag.h
- fix incompatible type of xfer callback
- remove rdundant class defination
- Fix return order in case of xfer error

V9->v10
Comments pointed by Greg KH <gregkh@linuxfoundation.org>
- remove unnecessary alignment for pirv data
- move jtag_copy_to_user and jtag_copy_from_user code just to ioctl
- move int jtag_run_test_idle_op and jtag_xfer_op code
  just to ioctl
- change return error codes to more applicable
- add missing error checks
- fix error check order in ioctl
- remove unnecessary blank lines
- add param validation to ioctl
- remove compat_ioctl
- remove only one open per JTAG port blocking.
  User will care about this.
- Fix idr memory leak on jtag_exit
- change cdev device type to misc

V8->v9
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- use get_user() instead of __get_user().
- change jtag->open type from int to atomic_t
- remove spinlock on jtg_open
- remove mutex on jtag_register
- add unregister_chrdev_region on jtag_init err
- add unregister_chrdev_region on jtag_exit
- remove unnecessary pointer casts
- add *data parameter to xfer function prototype

v7->v8
Comments pointed by Moritz Fischer <moritz.fischer@ettus.com>
- Fix misspelling s/friver/driver

v6->v7
Notifications from kbuild test robot <lkp@intel.com>
- Remove include asm/types.h from jtag.h
- Add include <linux/types.h> to jtag.c

v5->v6
v4->v5

v3->v4
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- change transaction pointer tdio type  to __u64
- change internal status type from enum to __u32
- reorder jtag_xfer members to avoid the implied padding
- add __packed attribute to jtag_xfer and jtag_run_test_idle

v2->v3
Notifications from kbuild test robot <lkp@intel.com>
- Change include path to <linux/types.h> in jtag.h

v1->v2
Comments pointed by Greg KH <gregkh@linuxfoundation.org>
- Change license type from GPLv2/BSD to GPLv2
- Change type of variables which crossed user/kernel to __type
- Remove "default n" from Kconfig

Comments pointed by Andrew Lunn <andrew@lunn.ch>
- Change list_add_tail in jtag_unregister to list_del

Comments pointed by Neil Armstrong <narmstrong@baylibre.com>
- Add SPDX-License-Identifier instead of license text

Comments pointed by Arnd Bergmann <arnd@arndb.de>
- Change __copy_to_user to memdup_user
- Change __put_user to put_user
- Change type of variables to __type for compatible 32 and 64-bit systems
- Add check for maximum xfer data size
- Change lookup data mechanism to get jtag data from inode
- Add .compat_ioctl to file ops
- Add mem alignment for jtag priv data

Comments pointed by Tobias Klauser <tklauser@distanz.ch>
- Change function names to avoid match with variable types
- Fix description for jtag_ru_test_idle in uapi jtag.h
- Fix misprints IDEL/IDLE, trough/through
---
 Documentation/ioctl/ioctl-number.txt |    2 +
 MAINTAINERS                          |   10 ++
 drivers/Kconfig                      |    2 +
 drivers/Makefile                     |    1 +
 drivers/jtag/Kconfig                 |   16 ++
 drivers/jtag/Makefile                |    1 +
 drivers/jtag/jtag.c                  |  288 ++++++++++++++++++++++++++++++++++
 include/linux/jtag.h                 |   45 ++++++
 include/uapi/linux/jtag.h            |  104 ++++++++++++
 9 files changed, 469 insertions(+), 0 deletions(-)
 create mode 100644 drivers/jtag/Kconfig
 create mode 100644 drivers/jtag/Makefile
 create mode 100644 drivers/jtag/jtag.c
 create mode 100644 include/linux/jtag.h
 create mode 100644 include/uapi/linux/jtag.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 3e3fdae..1af2508 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -321,6 +321,8 @@ Code  Seq#(hex)	Include File		Comments
 0xB0	all	RATIO devices		in development:
 					<mailto:vgo@ratio.de>
 0xB1	00-1F	PPPoX			<mailto:mostrows@styx.uwaterloo.ca>
+0xB2	00-0f	linux/jtag.h		JTAG driver
+					<mailto:oleksandrs@mellanox.com>
 0xB3	00	linux/mmc/ioctl.h
 0xB4	00-0F	linux/gpio.h		<mailto:linux-gpio@vger.kernel.org>
 0xB5	00-0F	uapi/linux/rpmsg.h	<mailto:linux-remoteproc@vger.kernel.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index 205d397..dfcf49c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7292,6 +7292,16 @@ L:	linux-serial at vger.kernel.org
 S:	Maintained
 F:	drivers/tty/serial/jsm/
 
+JTAG SUBSYSTEM
+M:	Oleksandr Shamray <oleksandrs@mellanox.com>
+M:	Vadim Pasternak <vadimp@mellanox.com>
+S:	Maintained
+F:	include/linux/jtag.h
+F:	include/uapi/linux/jtag.h
+F:	drivers/jtag/
+F:	Documentation/devicetree/bindings/jtag/
+F:	Documentation/ABI/testing/jtag-cdev
+
 K10TEMP HARDWARE MONITORING DRIVER
 M:	Clemens Ladisch <clemens@ladisch.de>
 L:	linux-hwmon at vger.kernel.org
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 505c676..2214678 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -208,4 +208,6 @@ source "drivers/tee/Kconfig"
 
 source "drivers/mux/Kconfig"
 
+source "drivers/jtag/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index dfdcda0..6a2059b 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -182,3 +182,4 @@ obj-$(CONFIG_FPGA)		+= fpga/
 obj-$(CONFIG_FSI)		+= fsi/
 obj-$(CONFIG_TEE)		+= tee/
 obj-$(CONFIG_MULTIPLEXER)	+= mux/
+obj-$(CONFIG_JTAG)		+= jtag/
diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
new file mode 100644
index 0000000..0fad1a3
--- /dev/null
+++ b/drivers/jtag/Kconfig
@@ -0,0 +1,16 @@
+menuconfig JTAG
+	tristate "JTAG support"
+	---help---
+	  This provides basic core functionality support for jtag class devices
+	  Hardware equipped with JTAG microcontroller which can be built
+	  on top of this drivers. Driver exposes the set of IOCTL to the
+	  user space for:
+	  SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
+	  SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
+	  RUNTEST (Forces IEEE 1149.1 bus to a run state for specified
+	  number of clocks).
+
+	  If you want this support, you should say Y here.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called jtag.
diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
new file mode 100644
index 0000000..af37493
--- /dev/null
+++ b/drivers/jtag/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_JTAG)		+= jtag.o
diff --git a/drivers/jtag/jtag.c b/drivers/jtag/jtag.c
new file mode 100644
index 0000000..39cbce9
--- /dev/null
+++ b/drivers/jtag/jtag.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0
+// drivers/jtag/jtag.c
+//
+// Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/jtag.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <uapi/linux/jtag.h>
+
+#define JTAG_NAME	"jtag0"
+#define MAX_JTAG_NAME_LEN (sizeof("jtag") + 5)
+
+struct jtag {
+	struct miscdevice miscdev;
+	struct device *dev;
+	const struct jtag_ops *ops;
+	int id;
+	bool opened;
+	struct mutex open_lock;
+	unsigned long priv[0];
+};
+
+static DEFINE_IDA(jtag_ida);
+
+void *jtag_priv(struct jtag *jtag)
+{
+	return jtag->priv;
+}
+EXPORT_SYMBOL_GPL(jtag_priv);
+
+static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct jtag *jtag = file->private_data;
+	struct jtag_run_test_idle idle;
+	struct jtag_xfer xfer;
+	u8 *xfer_data;
+	u32 data_size;
+	u32 value;
+	int err;
+
+	if (!arg)
+		return -EINVAL;
+
+	switch (cmd) {
+	case JTAG_GIOCFREQ:
+
+		if (jtag->ops->freq_get)
+			err = jtag->ops->freq_get(jtag, &value);
+		else
+			err = -EOPNOTSUPP;
+		if (err)
+			break;
+
+		if (put_user(value, (__u32 *)arg))
+			err = -EFAULT;
+		break;
+
+	case JTAG_SIOCFREQ:
+		if (get_user(value, (__u32 *)arg))
+			return -EFAULT;
+		if (value == 0)
+			return -EINVAL;
+
+		if (jtag->ops->freq_set)
+			err = jtag->ops->freq_set(jtag, value);
+		else
+			err = -EOPNOTSUPP;
+		break;
+
+	case JTAG_IOCRUNTEST:
+		if (copy_from_user(&idle, (void *)arg,
+				   sizeof(struct jtag_run_test_idle)))
+			return -EFAULT;
+
+		if (idle.endstate > JTAG_STATE_PAUSEDR)
+			return -EINVAL;
+
+		if (jtag->ops->idle)
+			err = jtag->ops->idle(jtag, &idle);
+		else
+			err = -EOPNOTSUPP;
+		break;
+
+	case JTAG_IOCXFER:
+		if (copy_from_user(&xfer, (void *)arg,
+				   sizeof(struct jtag_xfer)))
+			return -EFAULT;
+
+		if (xfer.length >= JTAG_MAX_XFER_DATA_LEN)
+			return -EINVAL;
+
+		if (xfer.type > JTAG_SDR_XFER)
+			return -EINVAL;
+
+		if (xfer.direction > JTAG_WRITE_XFER)
+			return -EINVAL;
+
+		if (xfer.endstate > JTAG_STATE_PAUSEDR)
+			return -EINVAL;
+
+		data_size = DIV_ROUND_UP(xfer.length, BITS_PER_BYTE);
+		xfer_data = memdup_user(u64_to_user_ptr(xfer.tdio), data_size);
+
+		if (!xfer_data)
+			return -EFAULT;
+
+		if (jtag->ops->xfer) {
+			err = jtag->ops->xfer(jtag, &xfer, xfer_data);
+		} else {
+			kfree(xfer_data);
+			return -EOPNOTSUPP;
+		}
+
+		if (err) {
+			kfree(xfer_data);
+			return -EFAULT;
+		}
+
+		err = copy_to_user(u64_to_user_ptr(xfer.tdio),
+				   (void *)(xfer_data), data_size);
+
+		if (err) {
+			kfree(xfer_data);
+			return -EFAULT;
+		}
+
+		kfree(xfer_data);
+		if (copy_to_user((void *)arg, &xfer, sizeof(struct jtag_xfer)))
+			return -EFAULT;
+		break;
+
+	case JTAG_GIOCSTATUS:
+		if (jtag->ops->status_get)
+			err = jtag->ops->status_get(jtag, &value);
+		else
+			err = -EOPNOTSUPP;
+		if (err)
+			break;
+
+		err = put_user(value, (__u32 *)arg);
+		if (err)
+			err = -EFAULT;
+		break;
+	case JTAG_SIOCMODE:
+		if (get_user(value, (__u32 *)arg))
+			return -EFAULT;
+		if (value == 0)
+			return -EINVAL;
+
+		if (jtag->ops->mode_set)
+			err = jtag->ops->mode_set(jtag, value);
+		else
+			err = -EOPNOTSUPP;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	return err;
+}
+
+static int jtag_open(struct inode *inode, struct file *file)
+{
+	struct jtag *jtag = container_of(file->private_data, struct jtag,
+					 miscdev);
+
+	if (mutex_lock_interruptible(&jtag->open_lock))
+		return -ERESTARTSYS;
+
+	if (jtag->opened) {
+		mutex_unlock(&jtag->open_lock);
+		return -EINVAL;
+	}
+
+	nonseekable_open(inode, file);
+	file->private_data = jtag;
+	jtag->opened = true;
+	mutex_unlock(&jtag->open_lock);
+	return 0;
+}
+
+static int jtag_release(struct inode *inode, struct file *file)
+{
+	struct jtag *jtag = file->private_data;
+
+	mutex_lock(&jtag->open_lock);
+	jtag->opened = false;
+	mutex_unlock(&jtag->open_lock);
+	return 0;
+}
+
+static const struct file_operations jtag_fops = {
+	.owner		= THIS_MODULE,
+	.open		= jtag_open,
+	.release	= jtag_release,
+	.llseek		= noop_llseek,
+	.unlocked_ioctl = jtag_ioctl,
+};
+
+struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops)
+{
+	struct jtag *jtag;
+
+	jtag = kzalloc(sizeof(*jtag) + round_up(priv_size, ARCH_DMA_MINALIGN),
+		       GFP_KERNEL);
+	if (!jtag)
+		return NULL;
+
+	jtag->ops = ops;
+	return jtag;
+}
+EXPORT_SYMBOL_GPL(jtag_alloc);
+
+void jtag_free(struct jtag *jtag)
+{
+	kfree(jtag);
+}
+EXPORT_SYMBOL_GPL(jtag_free);
+
+int jtag_register(struct jtag *jtag)
+{
+	char *name;
+	int err;
+	int id;
+
+	id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL);
+	if (id < 0)
+		return id;
+
+	jtag->id = id;
+
+	name = kzalloc(MAX_JTAG_NAME_LEN, GFP_KERNEL);
+	if (!name) {
+		err = -ENOMEM;
+		goto err_jtag_alloc;
+	}
+
+	err = snprintf(name, MAX_JTAG_NAME_LEN, "jtag%d", id);
+	if (err < 0)
+		goto err_jtag_name;
+
+	mutex_init(&jtag->open_lock);
+	jtag->miscdev.fops =  &jtag_fops;
+	jtag->miscdev.minor = MISC_DYNAMIC_MINOR;
+	jtag->miscdev.name = name;
+
+	err = misc_register(&jtag->miscdev);
+	if (err)
+		dev_err(jtag->dev, "Unable to register device\n");
+	else
+		return 0;
+	jtag->opened = false;
+
+err_jtag_name:
+	kfree(name);
+err_jtag_alloc:
+	ida_simple_remove(&jtag_ida, id);
+	return err;
+}
+EXPORT_SYMBOL_GPL(jtag_register);
+
+void jtag_unregister(struct jtag *jtag)
+{
+	misc_deregister(&jtag->miscdev);
+	kfree(jtag->miscdev.name);
+	ida_simple_remove(&jtag_ida, jtag->id);
+}
+EXPORT_SYMBOL_GPL(jtag_unregister);
+
+static void __exit jtag_exit(void)
+{
+	ida_destroy(&jtag_ida);
+}
+
+module_exit(jtag_exit);
+
+MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>");
+MODULE_DESCRIPTION("Generic jtag support");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/jtag.h b/include/linux/jtag.h
new file mode 100644
index 0000000..312c641
--- /dev/null
+++ b/include/linux/jtag.h
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+// include/linux/jtag.h - JTAG class driver
+//
+// Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+
+#ifndef __JTAG_H
+#define __JTAG_H
+
+#include <uapi/linux/jtag.h>
+
+#ifndef ARCH_DMA_MINALIGN
+#define ARCH_DMA_MINALIGN 1
+#endif
+
+#define jtag_u64_to_ptr(arg) ((void *)(uintptr_t)arg)
+
+#define JTAG_MAX_XFER_DATA_LEN 65535
+
+struct jtag;
+/**
+ * struct jtag_ops - callbacks for jtag control functions:
+ *
+ * @freq_get: get frequency function. Filled by device driver
+ * @freq_set: set frequency function. Filled by device driver
+ * @status_get: set status function. Filled by device driver
+ * @idle: set JTAG to idle state function. Filled by device driver
+ * @xfer: send JTAG xfer function. Filled by device driver
+ */
+struct jtag_ops {
+	int (*freq_get)(struct jtag *jtag, u32 *freq);
+	int (*freq_set)(struct jtag *jtag, u32 freq);
+	int (*status_get)(struct jtag *jtag, u32 *state);
+	int (*idle)(struct jtag *jtag, struct jtag_run_test_idle *idle);
+	int (*xfer)(struct jtag *jtag, struct jtag_xfer *xfer, u8 *xfer_data);
+	int (*mode_set)(struct jtag *jtag, u32 mode_mask);
+};
+
+void *jtag_priv(struct jtag *jtag);
+int jtag_register(struct jtag *jtag);
+void jtag_unregister(struct jtag *jtag);
+struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops);
+void jtag_free(struct jtag *jtag);
+
+#endif /* __JTAG_H */
diff --git a/include/uapi/linux/jtag.h b/include/uapi/linux/jtag.h
new file mode 100644
index 0000000..cda2520
--- /dev/null
+++ b/include/uapi/linux/jtag.h
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+// include/uapi/linux/jtag.h - JTAG class driver uapi
+//
+// Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+
+#ifndef __UAPI_LINUX_JTAG_H
+#define __UAPI_LINUX_JTAG_H
+
+#include <linux/types.h>
+/*
+ * JTAG_XFER_HW_MODE: JTAG hardware mode. Used to set HW drived or bitbang
+ * mode. This is bitmask param of ioctl JTAG_SIOCMODE command
+ */
+#define  JTAG_XFER_HW_MODE 1
+
+/**
+ * enum jtag_endstate:
+ *
+ * @JTAG_STATE_IDLE: JTAG state machine IDLE state
+ * @JTAG_STATE_PAUSEIR: JTAG state machine PAUSE_IR state
+ * @JTAG_STATE_PAUSEDR: JTAG state machine PAUSE_DR state
+ */
+enum jtag_endstate {
+	JTAG_STATE_IDLE,
+	JTAG_STATE_PAUSEIR,
+	JTAG_STATE_PAUSEDR,
+};
+
+/**
+ * enum jtag_xfer_type:
+ *
+ * @JTAG_SIR_XFER: SIR transfer
+ * @JTAG_SDR_XFER: SDR transfer
+ */
+enum jtag_xfer_type {
+	JTAG_SIR_XFER,
+	JTAG_SDR_XFER,
+};
+
+/**
+ * enum jtag_xfer_direction:
+ *
+ * @JTAG_READ_XFER: read transfer
+ * @JTAG_WRITE_XFER: write transfer
+ */
+enum jtag_xfer_direction {
+	JTAG_READ_XFER,
+	JTAG_WRITE_XFER,
+};
+
+/**
+ * struct jtag_run_test_idle - forces JTAG state machine to
+ * RUN_TEST/IDLE state
+ *
+ * @reset: 0 - run IDLE/PAUSE from current state
+ *         1 - go through TEST_LOGIC/RESET state before  IDLE/PAUSE
+ * @end: completion flag
+ * @tck: clock counter
+ *
+ * Structure represents interface to JTAG device for jtag idle
+ * execution.
+ */
+struct jtag_run_test_idle {
+	__u8	reset;
+	__u8	endstate;
+	__u8	tck;
+};
+
+/**
+ * struct jtag_xfer - jtag xfer:
+ *
+ * @type: transfer type
+ * @direction: xfer direction
+ * @length: xfer bits len
+ * @tdio : xfer data array
+ * @endir: xfer end state
+ *
+ * Structure represents interface to JTAG device for jtag sdr xfer
+ * execution.
+ */
+struct jtag_xfer {
+	__u8	type;
+	__u8	direction;
+	__u8	endstate;
+	__u32	length;
+	__u64	tdio;
+};
+
+/* ioctl interface */
+#define __JTAG_IOCTL_MAGIC	0xb2
+
+#define JTAG_IOCRUNTEST	_IOW(__JTAG_IOCTL_MAGIC, 0,\
+			     struct jtag_run_test_idle)
+#define JTAG_SIOCFREQ	_IOW(__JTAG_IOCTL_MAGIC, 1, unsigned int)
+#define JTAG_GIOCFREQ	_IOR(__JTAG_IOCTL_MAGIC, 2, unsigned int)
+#define JTAG_IOCXFER	_IOWR(__JTAG_IOCTL_MAGIC, 3, struct jtag_xfer)
+#define JTAG_GIOCSTATUS _IOWR(__JTAG_IOCTL_MAGIC, 4, enum jtag_endstate)
+#define JTAG_SIOCMODE	_IOW(__JTAG_IOCTL_MAGIC, 5, unsigned int)
+
+#define JTAG_FIRST_MINOR 0
+#define JTAG_MAX_DEVICES 32
+
+#endif /* __UAPI_LINUX_JTAG_H */
-- 
1.7.1

^ permalink raw reply related

* [patch v14 0/4] JTAG driver introduction
From: Oleksandr Shamray @ 2017-12-14 16:29 UTC (permalink / raw)
  To: linux-arm-kernel

When a need raise up to use JTAG interface for system's devices
programming or CPU debugging, usually the user layer
application implements jtag protocol by bit-bang or using a 
proprietary connection to vendor hardware.
This method can be slow and not generic.
 
We propose to implement general JTAG interface and infrastructure
to communicate with user layer application. In such way, we can
have the standard JTAG interface core part and separation from
specific HW implementation.
This allow new capability to debug the CPU or program system's 
device via BMC without additional devices nor cost. 

This patch purpose is to add JTAG master core infrastructure by 
defining new JTAG class and provide generic JTAG interface
to allow hardware specific drivers to connect this interface.
This will enable all JTAG drivers to use the common interface
part and will have separate for hardware implementation.

The JTAG (Joint Test Action Group) core driver provides minimal generic
JTAG interface, which can be used by hardware specific JTAG master
controllers. By providing common interface for the JTAG controllers,
user space device programing is hardware independent.
 
Modern SoC which in use for embedded system' equipped with
internal JTAG master interface.
This interface is used for programming and debugging system's
hardware components, like CPLD, FPGA, CPU, voltage and
industrial controllers.
Firmware for such devices can be upgraded through JTAG interface during
Runtime. The JTAG standard support for multiple devices programming,
is in case their lines are daisy-chained together.

For example, systems which equipped with host CPU, BMC SoC or/and 
number of programmable devices are capable to connect a pin and
select system components dynamically for programming and debugging,
This is using by the BMC which is equipped with internal SoC master
controller.
For example:

BMC JTAG master --> pin selected to CPLDs chain for programming (filed
upgrade, production) 
BMC JTAG master --> pin selected to voltage monitors for programming 
(field upgrade, production) 
BMC JTAG master --> pin selected to host CPU (on-site debugging 
and developers debugging)

For example, we can have application in user space which using calls
to JTAG driver executes CPLD programming directly from SVF file
 
The JTAG standard (IEEE 1149.1) defines the next connector pins:
- TDI (Test Data In);
- TDO (Test Data Out);
- TCK (Test Clock);
- TMS (Test Mode Select);
- TRST (Test Reset) (Optional);

The SoC equipped with JTAG master controller, performs
device programming on command or vector level. For example
a file in a standard SVF (Serial Vector Format) that contains
boundary scan vectors, can be used by sending each vector
to the JTAG interface and the JTAG controller will execute
the programming.

Initial version provides the system calls set for:
- SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
- SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
- RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
  number of clocks.

SoC which are not equipped with JTAG master interface, can be built
on top of JTAG core driver infrastructure, by applying bit-banging of
TDI, TDO, TCK and TMS pins within the hardware specific driver.

Oleksandr Shamray (4):
  drivers: jtag: Add JTAG core driver
  drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master
    driver
  Documentation: jtag: Add bindings for Aspeed SoC 24xx and 25xx
    families     JTAG master driver
  Documentation: jtag: Add ABI documentation

 Documentation/ABI/testing/jtag-dev                 |   27 +
 .../devicetree/bindings/jtag/aspeed-jtag.txt       |   18 +
 Documentation/ioctl/ioctl-number.txt               |    2 +
 MAINTAINERS                                        |   10 +
 drivers/Kconfig                                    |    2 +
 drivers/Makefile                                   |    1 +
 drivers/jtag/Kconfig                               |   29 +
 drivers/jtag/Makefile                              |    2 +
 drivers/jtag/jtag-aspeed.c                         |  783 ++++++++++++++++++++
 drivers/jtag/jtag.c                                |  288 +++++++
 include/linux/jtag.h                               |   45 ++
 include/uapi/linux/jtag.h                          |  104 +++
 12 files changed, 1311 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/jtag-dev
 create mode 100644 Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
 create mode 100644 drivers/jtag/Kconfig
 create mode 100644 drivers/jtag/Makefile
 create mode 100644 drivers/jtag/jtag-aspeed.c
 create mode 100644 drivers/jtag/jtag.c
 create mode 100644 include/linux/jtag.h
 create mode 100644 include/uapi/linux/jtag.h

^ permalink raw reply

* [PATCH] iommu/arm-smmu-v3: Don't free page table ops twice
From: Robin Murphy @ 2017-12-14 16:25 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214110301.2998-1-jean-philippe.brucker@arm.com>

On 14/12/17 11:03, Jean-Philippe Brucker wrote:
> Kasan reports a double free when finalise_stage_fn fails: the io_pgtable
> ops are freed by arm_smmu_domain_finalise and then again by
> arm_smmu_domain_free. Prevent this by leaving pgtbl_ops empty on failure.

It might be a tiny bit more consistent to simply clear the domain ops 
again upon failure (cf. how arm_smmu_attach_dev() treats 
smmu_domain->smmu), but I'm sure nobody really cares that much:

Reviewed-by: Robin Murphy <robin.murphy@arm.com>

> Fixes: 48ec83bcbcf5 ("iommu/arm-smmu: Add initial driver support for ARM SMMUv3 devices")
> Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
> ---
>   drivers/iommu/arm-smmu-v3.c | 8 +++++---
>   1 file changed, 5 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
> index f122071688fd..db4281d0e269 100644
> --- a/drivers/iommu/arm-smmu-v3.c
> +++ b/drivers/iommu/arm-smmu-v3.c
> @@ -1698,13 +1698,15 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
>   	domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
>   	domain->geometry.aperture_end = (1UL << ias) - 1;
>   	domain->geometry.force_aperture = true;
> -	smmu_domain->pgtbl_ops = pgtbl_ops;
>   
>   	ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);
> -	if (ret < 0)
> +	if (ret < 0) {
>   		free_io_pgtable_ops(pgtbl_ops);
> +		return ret;
> +	}
>   
> -	return ret;
> +	smmu_domain->pgtbl_ops = pgtbl_ops;
> +	return 0;
>   }
>   
>   static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
> 

^ permalink raw reply

* [PATCH 1/1] dt-bindings: arm: document supported STM32 SoC family
From: Ludovic BARRE @ 2017-12-14 16:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAL_JsqJ8+NrOOpg1VcC8ZVVtm3tFTL2qvj8GSkrPU91CWDqzDA@mail.gmail.com>


On 12/14/2017 04:05 PM, Rob Herring wrote:
> On Wed, Dec 13, 2017 at 2:40 AM, Ludovic BARRE <ludovic.barre@st.com> wrote:
>> hi Rob
>>
>> Today there was no bindings documentation for STM32 SoC
>> already upstreamed. This patch adds initial list of STM32
>> existing in kernel.
>> The next serie adds just new soc and one compatible on STM32 list.
> 
> Yes, and that series will go thru arm-soc tree so this patch needs to too.
You are right, to avoid merge conflict.
I will included this patch in my series
on "ARM: stm32: add initial STM32MPU support"

This patch can be abandoned.
> 
>> So, I think you could apply this patch.
> 
> If your series is not going into 4.16, then I can take this one.
> 
> Rob
> 

^ permalink raw reply

* [PATCH v12 3/3] arm64:dts:hisilicon Disable hisilicon smmu node on hip06/hip07
From: Shameer Kolothum @ 2017-12-14 16:09 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214160957.13716-1-shameerali.kolothum.thodi@huawei.com>

The HiSilicon erratum 161010801 describes the limitation of
HiSilicon platforms hip06/hip07 to support the SMMUv3 mappings
for MSI transactions.

PCIe controller on these platforms has to differentiate the
MSI payload against other DMA payload and has to modify the
MSI  payload. This makes it difficult for these platforms to
have SMMU translation for MSI. In order to workaround this,
ARM SMMUv3 driver requires a quirk to treat the MSI regions
separately. Such a quirk is currently missing for DT based
systems and therefore we need to explicitly disable the
hip06/hip07 smmu entries in dts.

Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Signed-off-by: Wei Xu <xuwei5@hisilicon.com>
---
 arch/arm64/boot/dts/hisilicon/hip06.dtsi | 56 ++++++++++++++++++++++++++++++++
 arch/arm64/boot/dts/hisilicon/hip07.dtsi | 25 ++++++++++++++
 2 files changed, 81 insertions(+)

diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
index a049b64..35202eb 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
@@ -291,6 +291,13 @@
 			#interrupt-cells = <2>;
 			num-pins = <128>;
 		};
+
+		mbigen_pcie0: intc_pcie0 {
+			msi-parent = <&its_dsa 0x40085>;
+			interrupt-controller;
+			#interrupt-cells = <2>;
+			num-pins = <10>;
+		};
 	};
 
 	mbigen_dsa at c0080000 {
@@ -312,6 +319,31 @@
 		};
 	};
 
+	/**
+	 *  HiSilicon erratum 161010801: This describes the limitation
+	 *  of HiSilicon platforms hip06/hip07 to support the SMMUv3
+	 *  mappings for PCIe MSI transactions.
+	 *  PCIe controller on these platforms has to differentiate the
+	 *  MSI payload against other DMA payload and has to modify the
+	 *  MSI payload. This makes it difficult for these platforms to
+	 *  have a SMMU translation for MSI. In order to workaround this,
+	 *  ARM SMMUv3 driver requires a quirk to treat the MSI regions
+	 *  separately. Such a quirk is currently missing for DT based
+	 *  systems. Hence please make sure that the smmu pcie node on
+	 *  hip06 is disabled as this will break the PCIe functionality
+	 *  when iommu-map entry is used along with the PCIe node.
+	 *  Refer:https://www.spinics.net/lists/arm-kernel/msg602812.html
+	 */
+	smmu0: smmu_pcie {
+		compatible = "arm,smmu-v3";
+		reg = <0x0 0xa0040000 0x0 0x20000>;
+		#iommu-cells = <1>;
+		dma-coherent;
+		smmu-cb-memtype = <0x0 0x1>;
+		hisilicon,broken-prefetch-cmd;
+		status = "disabled";
+	};
+
 	soc {
 		compatible = "simple-bus";
 		#address-cells = <2>;
@@ -676,6 +708,30 @@
 				     <637 1>,<638 1>,<639 1>;
 			status = "disabled";
 		};
+
+		pcie0: pcie at a0090000 {
+			compatible = "hisilicon,hip06-pcie-ecam";
+			reg = <0 0xb0000000 0 0x2000000>,
+			      <0 0xa0090000 0 0x10000>;
+			bus-range = <0  31>;
+			msi-map = <0x0000 &its_dsa 0x0000 0x2000>;
+			msi-map-mask = <0xffff>;
+			#address-cells = <3>;
+			#size-cells = <2>;
+			device_type = "pci";
+			dma-coherent;
+			ranges = <0x02000000 0 0xb2000000 0x0 0xb2000000 0
+				 0x5ff0000 0x01000000 0 0 0 0xb7ff0000
+				 0 0x10000>;
+			#interrupt-cells = <1>;
+			interrupt-map-mask = <0xf800 0 0 7>;
+			interrupt-map = <0x0 0 0 1 &mbigen_pcie0 650 4
+					0x0 0 0 2 &mbigen_pcie0 650 4
+					0x0 0 0 3 &mbigen_pcie0 650 4
+					0x0 0 0 4 &mbigen_pcie0 650 4>;
+			status = "disabled";
+		};
+
 	};
 
 };
diff --git a/arch/arm64/boot/dts/hisilicon/hip07.dtsi b/arch/arm64/boot/dts/hisilicon/hip07.dtsi
index 2c01a21..3e80bf3 100644
--- a/arch/arm64/boot/dts/hisilicon/hip07.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip07.dtsi
@@ -1083,6 +1083,31 @@
 		};
 	};
 
+	/**
+	 *  HiSilicon erratum 161010801: This describes the limitation
+	 *  of HiSilicon platforms hip06/hip07 to support the SMMUv3
+	 *  mappings for PCIe MSI transactions.
+	 *  PCIe controller on these platforms has to differentiate the
+	 *  MSI payload against other DMA payload and has to modify the
+	 *  MSI payload. This makes it difficult for these platforms to
+	 *  have a SMMU translation for MSI. In order to workaround this,
+	 *  ARM SMMUv3 driver requires a quirk to treat the MSI regions
+	 *  separately. Such a quirk is currently missing for DT based
+	 *  systems. Hence please make sure that the smmu pcie node on
+	 *  hip07 is disabled as this will break the PCIe functionality
+	 *  when iommu-map entry is used along with the PCIe node.
+	 *  Refer:https://www.spinics.net/lists/arm-kernel/msg602812.html
+	 */
+	smmu0: smmu_pcie {
+		compatible = "arm,smmu-v3";
+		reg = <0x0 0xa0040000 0x0 0x20000>;
+		#iommu-cells = <1>;
+		dma-coherent;
+		smmu-cb-memtype = <0x0 0x1>;
+		hisilicon,broken-prefetch-cmd;
+		status = "disabled";
+	};
+
 	soc {
 		compatible = "simple-bus";
 		#address-cells = <2>;
-- 
1.9.1

^ permalink raw reply related

* [PATCH v12 2/3] iommu/dma: Add HW MSI(GICv3 ITS) address regions reservation
From: Shameer Kolothum @ 2017-12-14 16:09 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214160957.13716-1-shameerali.kolothum.thodi@huawei.com>

Modified iommu_dma_get_resv_regions() to include GICv3 ITS
region on ACPI based ARM platfiorms which may require HW MSI
reservations.

Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/dma-iommu.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 25914d3..f05f3cf 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -19,6 +19,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/acpi_iort.h>
 #include <linux/device.h>
 #include <linux/dma-iommu.h>
 #include <linux/gfp.h>
@@ -167,13 +168,18 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
  *
  * IOMMU drivers can use this to implement their .get_resv_regions callback
  * for general non-IOMMU-specific reservations. Currently, this covers host
- * bridge windows for PCI devices.
+ * bridge windows for PCI devices and GICv3 ITS region reservation on ACPI
+ * based ARM platforms that may require HW MSI reservation.
  */
 void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list)
 {
 	struct pci_host_bridge *bridge;
 	struct resource_entry *window;
 
+	if (!is_of_node(dev->iommu_fwspec->iommu_fwnode) &&
+		iort_iommu_msi_get_resv_regions(dev, list) < 0)
+		return;
+
 	if (!dev_is_pci(dev))
 		return;
 
-- 
1.9.1

^ permalink raw reply related

* [PATCH v12 1/3] ACPI/IORT: Add msi address regions reservation helper
From: Shameer Kolothum @ 2017-12-14 16:09 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214160957.13716-1-shameerali.kolothum.thodi@huawei.com>

On some platforms msi parent address regions have to be excluded from
normal IOVA allocation in that they are detected and decoded in a HW
specific way by system components and so they cannot be considered normal
IOVA address space.

Add a helper function that retrieves ITS address regions - the msi
parent - through IORT device <-> ITS mappings and reserves it so that
these regions will not be translated by IOMMU and will be excluded from
IOVA allocations. The function checks for the smmu model number and
only applies the msi reservation if the platform requires it.

Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
---
 drivers/acpi/arm64/iort.c        | 111 +++++++++++++++++++++++++++++++++++++--
 drivers/irqchip/irq-gic-v3-its.c |   3 +-
 include/linux/acpi_iort.h        |   7 ++-
 3 files changed, 116 insertions(+), 5 deletions(-)

diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 95255ec..e2f7bdd 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -39,6 +39,7 @@
 struct iort_its_msi_chip {
 	struct list_head	list;
 	struct fwnode_handle	*fw_node;
+	phys_addr_t		base_addr;
 	u32			translation_id;
 };
 
@@ -161,14 +162,16 @@ typedef acpi_status (*iort_find_node_callback)
 static DEFINE_SPINLOCK(iort_msi_chip_lock);
 
 /**
- * iort_register_domain_token() - register domain token and related ITS ID
- * to the list from where we can get it back later on.
+ * iort_register_domain_token() - register domain token along with related
+ * ITS ID and base address to the list from where we can get it back later on.
  * @trans_id: ITS ID.
+ * @base: ITS base address.
  * @fw_node: Domain token.
  *
  * Returns: 0 on success, -ENOMEM if no memory when allocating list element
  */
-int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node)
+int iort_register_domain_token(int trans_id, phys_addr_t base,
+			       struct fwnode_handle *fw_node)
 {
 	struct iort_its_msi_chip *its_msi_chip;
 
@@ -178,6 +181,7 @@ int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node)
 
 	its_msi_chip->fw_node = fw_node;
 	its_msi_chip->translation_id = trans_id;
+	its_msi_chip->base_addr = base;
 
 	spin_lock(&iort_msi_chip_lock);
 	list_add(&its_msi_chip->list, &iort_msi_chip_list);
@@ -581,6 +585,24 @@ int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
 	return -ENODEV;
 }
 
+static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
+{
+	struct iort_its_msi_chip *its_msi_chip;
+	int ret = -ENODEV;
+
+	spin_lock(&iort_msi_chip_lock);
+	list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
+		if (its_msi_chip->translation_id == its_id) {
+			*base = its_msi_chip->base_addr;
+			ret = 0;
+			break;
+		}
+	}
+	spin_unlock(&iort_msi_chip_lock);
+
+	return ret;
+}
+
 /**
  * iort_dev_find_its_id() - Find the ITS identifier for a device
  * @dev: The device.
@@ -766,6 +788,24 @@ static inline bool iort_iommu_driver_enabled(u8 type)
 }
 
 #ifdef CONFIG_IOMMU_API
+static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev)
+{
+	struct acpi_iort_node *iommu;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+	iommu = iort_get_iort_node(fwspec->iommu_fwnode);
+
+	if (iommu && (iommu->type == ACPI_IORT_NODE_SMMU_V3)) {
+		struct acpi_iort_smmu_v3 *smmu;
+
+		smmu = (struct acpi_iort_smmu_v3 *)iommu->node_data;
+		if (smmu->model == ACPI_IORT_SMMU_V3_HISILICON_HI161X)
+			return iommu;
+	}
+
+	return NULL;
+}
+
 static inline const struct iommu_ops *iort_fwspec_iommu_ops(
 				struct iommu_fwspec *fwspec)
 {
@@ -782,6 +822,69 @@ static inline int iort_add_device_replay(const struct iommu_ops *ops,
 
 	return err;
 }
+
+/**
+ * iort_iommu_msi_get_resv_regions - Reserved region driver helper
+ * @dev: Device from iommu_get_resv_regions()
+ * @head: Reserved region list from iommu_get_resv_regions()
+ *
+ * Returns: Number of msi reserved regions on success (0 if platform
+ *          doesn't require the reservation or no associated msi regions),
+ *          appropriate error value otherwise. The ITS interrupt translation
+ *          spaces (ITS_base + SZ_64K, SZ_64K) associated with the device
+ *          are the msi reserved regions.
+ */
+int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head)
+{
+	struct acpi_iort_its_group *its;
+	struct acpi_iort_node *iommu_node, *its_node = NULL;
+	int i, resv = 0;
+
+	iommu_node = iort_get_msi_resv_iommu(dev);
+	if (!iommu_node)
+		return 0;
+
+	/*
+	 * Current logic to reserve ITS regions relies on HW topologies
+	 * where a given PCI or named component maps its IDs to only one
+	 * ITS group; if a PCI or named component can map its IDs to
+	 * different ITS groups through IORT mappings this function has
+	 * to be reworked to ensure we reserve regions for all ITS groups
+	 * a given PCI or named component may map IDs to.
+	 */
+
+	for (i = 0; i < dev->iommu_fwspec->num_ids; i++) {
+		its_node = iort_node_map_id(iommu_node,
+					dev->iommu_fwspec->ids[i],
+					NULL, IORT_MSI_TYPE);
+		if (its_node)
+			break;
+	}
+
+	if (!its_node)
+		return 0;
+
+	/* Move to ITS specific data */
+	its = (struct acpi_iort_its_group *)its_node->node_data;
+
+	for (i = 0; i < its->its_count; i++) {
+		phys_addr_t base;
+
+		if (!iort_find_its_base(its->identifiers[i], &base)) {
+			int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+			struct iommu_resv_region *region;
+
+			region = iommu_alloc_resv_region(base + SZ_64K, SZ_64K,
+							 prot, IOMMU_RESV_MSI);
+			if (region) {
+				list_add_tail(&region->list, head);
+				resv++;
+			}
+		}
+	}
+
+	return (resv == its->its_count) ? resv : -ENODEV;
+}
 #else
 static inline const struct iommu_ops *iort_fwspec_iommu_ops(
 				struct iommu_fwspec *fwspec)
@@ -789,6 +892,8 @@ static inline const struct iommu_ops *iort_fwspec_iommu_ops(
 static inline int iort_add_device_replay(const struct iommu_ops *ops,
 					 struct device *dev)
 { return 0; }
+int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head)
+{ return 0; }
 #endif
 
 static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 4039e64..d4cff12 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -3450,7 +3450,8 @@ static int __init gic_acpi_parse_madt_its(struct acpi_subtable_header *header,
 		return -ENOMEM;
 	}
 
-	err = iort_register_domain_token(its_entry->translation_id, dom_handle);
+	err = iort_register_domain_token(its_entry->translation_id, res.start,
+					 dom_handle);
 	if (err) {
 		pr_err("ITS@%pa: Unable to register GICv3 ITS domain token (ITS ID %d) to IORT\n",
 		       &res.start, its_entry->translation_id);
diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
index 2f7a292..38cd77b 100644
--- a/include/linux/acpi_iort.h
+++ b/include/linux/acpi_iort.h
@@ -26,7 +26,8 @@
 #define IORT_IRQ_MASK(irq)		(irq & 0xffffffffULL)
 #define IORT_IRQ_TRIGGER_MASK(irq)	((irq >> 32) & 0xffffffffULL)
 
-int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node);
+int iort_register_domain_token(int trans_id, phys_addr_t base,
+			       struct fwnode_handle *fw_node);
 void iort_deregister_domain_token(int trans_id);
 struct fwnode_handle *iort_find_domain_token(int trans_id);
 #ifdef CONFIG_ACPI_IORT
@@ -38,6 +39,7 @@
 /* IOMMU interface */
 void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *size);
 const struct iommu_ops *iort_iommu_configure(struct device *dev);
+int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head);
 #else
 static inline void acpi_iort_init(void) { }
 static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id)
@@ -52,6 +54,9 @@ static inline void iort_dma_setup(struct device *dev, u64 *dma_addr,
 static inline const struct iommu_ops *iort_iommu_configure(
 				      struct device *dev)
 { return NULL; }
+static inline
+int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head)
+{ return 0; }
 #endif
 
 #endif /* __ACPI_IORT_H__ */
-- 
1.9.1

^ permalink raw reply related

* [PATCH v12 0/3] iommu/smmu-v3: Workaround for hisilicon 161010801 erratum(reserve HW MSI)
From: Shameer Kolothum @ 2017-12-14 16:09 UTC (permalink / raw)
  To: linux-arm-kernel

On certain HiSilicon platforms (hip06/hip07) the GIC ITS and PCIe RC
deviates from the standard implementation and this breaks PCIe MSI
functionality when SMMU is enabled.

The HiSilicon erratum 161010801 describes this limitation of certain
HiSilicon platforms to support the SMMU mappings for MSI transactions.
On these platforms GICv3 ITS translator is presented with the deviceID
by extending the MSI payload data to 64 bits to include the deviceID.
Hence, the PCIe controller on this platforms has to differentiate the MSI
payload against other DMA payload and has to modify the MSI payload.
This basically makes it difficult for this platforms to have a SMMU
translation for MSI.

This patch implements an ACPI based quirk to reserve the hw msi regions
in the smmu-v3 driver which means these address regions will not be
translated and will be excluded from iova allocations.

To implement this quirk, the following changes are incorporated:
1. Added a generic helper function to IORT code to retrieve and reserve
   the associated ITS base address from a device IORT node. The function
   has a check for smmu model to determine whether the platform requires
   the HW MSI reservation or not.
2. Added smmu node entries and explicitly disabled them in hip06/hip07
    dts files so that users are warned about the non-DT support for this
    erratum.

Changelog:

v11--> v12
-Thanks to Lorenzo, Fixed !CONFIG_IOMMU_API compile error(patch #1).

v10 --> v11
-Addressed comments from Lorenzo(patch#1)
-Added Robin's Reviewed-by to patch #2

v9 --> v10
Addressed comments:
-Moved smmu model check to iort helper function to selectively apply
 the msi reservation which will make the fn call generic from iommu-dma.
-Removed PCI blacklisting patch, instead added smmu nodes(disabled)
 with comments to hip06/hip07 dts file.

v8 --> v9
-Thanks to Marc, fixed IORT helper function to reserve the ITS
 translater region only.
-Removed the DT support for MSI reservation and blacklisted
 HiSilicon PCIe controllers on DT based systems when SMMUv3 is
 enabled.

v7 --> v8
Addressed comments from Rob and Lorenzo:
 -Modified to use DT compatible string for errata.
 -Changed logic to retrieve the msi-parent for DT case.

v6 --> v7
Addressed request from Will to add DT support for the erratum:
 - added bt binding
 - add of_iommu_msi_get_resv_regions()
New arm64 silicon errata entry
Rename iort_iommu_{its->msi}_get_resv_regions

v5 --> v6
Addressed comments from Robin and Lorenzo:
-No change to patch#1 .
-Reverted v5 patch#2 as this might break the platforms where this quirk
  is not applicable. Provided a generic function in iommu code and added
  back the quirk implementation in SMMU v3 driver(patch#3)

v4 --> v5
Addressed comments from Robin and Lorenzo:
-Added a comment to make it clear that, for now, only straightforward
  HW topologies are handled while reserving ITS regions(patch #1).

v3 --> v4
Rebased on 4.13-rc1.
Addressed comments from Robin, Will and Lorenzo:
-As suggested by Robin, moved the ITS msi reservation into
  iommu_dma_get_resv_regions().
-Added its_count != resv region failure case(patch #1).

v2 --> v3
Addressed comments from Lorenzo and Robin:
-Removed dev_is_pci() check in smmuV3 driver.
-Don't treat device not having an ITS mapping as an error in
  iort helper function.

v1 --> v2
-patch 2/2: Invoke iort helper fn based on fwnode type(acpi).

RFCv2 -->PATCH
-Incorporated Lorenzo's review comments.

RFC v1 --> RFC v2
Based on Robin's review comments,
-Removed  the generic erratum framework.
-Using IORT/MADT tables to retrieve the ITS base addr instead
 of vendor specific CSRT table.

Shameer Kolothum (3):
  ACPI/IORT: Add msi address regions reservation helper
  iommu/dma: Add HW MSI(GICv3 ITS) address regions reservation
  arm64:dts:hisilicon Disable hisilicon smmu node on hip06/hip07

 arch/arm64/boot/dts/hisilicon/hip06.dtsi |  56 ++++++++++++++++
 arch/arm64/boot/dts/hisilicon/hip07.dtsi |  25 +++++++
 drivers/acpi/arm64/iort.c                | 111 ++++++++++++++++++++++++++++++-
 drivers/iommu/dma-iommu.c                |   8 ++-
 drivers/irqchip/irq-gic-v3-its.c         |   3 +-
 include/linux/acpi_iort.h                |   7 +-
 6 files changed, 204 insertions(+), 6 deletions(-)

-- 
1.9.1

^ permalink raw reply

* [RFC PATCH 0/7] gpiolib: add bias support
From: Andrew Lunn @ 2017-12-14 16:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214142138.23008-1-ludovic.desroches@microchip.com>

> I have not introduced a gpiod_set_bias() because I think the only place to
> setup this is in the device tree. It's useless to setup a bias pull up if there
> is an external pull up on the board.

Hi Ludovic

I have a use case where having gpiod_set_bias() would be good. The
DLN2 is a USB device, which offers GPIO, I2C, and SPI services on its
pins. It is a hot pluggable device, and i'm using it on Intel
machines, making it double hard to use with Device Tree.

It would be good to be able to set the pull up/down on the GPIO pins,
probably from a small kernel module which calls gpiod_set_bias(), or
via /sysfs.

    Andrew

^ permalink raw reply

* [GIT PULL 3/3] dts changes for omaps for v4.16 merge window
From: Tony Lindgren @ 2017-12-14 15:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <pull-1513266986-900907@atomide.com>

From: "Tony Lindgren" <tony@atomide.com>

The following changes since commit bc686442f8a601bccac1f22506ecdb4b0d62cadd:

  Merge branch 'dts-fixes' into omap-for-v4.15/fixes-dt (2017-11-28 08:12:32 -0800)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap tags/omap-for-v4.16/dt-signed

for you to fetch changes up to a38867305203ef5a27f0c9ff1e943a0c2fabdbce:

  ARM: dts: Move move WiFi bindings to logicpd-torpedo-37xx-devkit (2017-12-11 09:43:53 -0800)

----------------------------------------------------------------
Dts related changes for omaps for v4.16 merge window

These changes are mostly improvments for various devices.
Note that these are based on my earlier fixes branch
omap-for-v4.15/fixes-dt to avoid a pointless merge conflict
between a fix and removal. The summary of changes is:

- Fix audio codec reset pin for am335x-pepper and n900, this
  has been always broken and won't get fixed until the related
  driver changes are also merged in for v4.16, so not urgent

- Fix tps65917 powerhold property for dra76-evm

- Changes to logicpd boards to remove MTD partition information
  and to add support for omap35xx variants by setting up common
  dts files for the logicpd boards

- Disable dra7 USB metastability workaround, this won't do
  anything until the related driver changes are also merged
  into v4.16, so not urgent

----------------------------------------------------------------
Adam Ford (6):
      ARM: dts: omap3: logicpd kits: Remove partition information
      ARM: dts: Move most of logicpd-som-lv-37xx-devkit.dts to logicpd-som-lv-baseboard.dtsi
      ARM: dts: Add minimal support for LogicPD OMAP35xx SOM-LV devkit
      ARM: dts: Move most of logicpd-torpedo-37xx-devkit to logicpd-torpedo-baseboard
      ARM: dts: Add minimal support for LogicPD OMAP35xx Torpedo devkit
      ARM: dts: Move move WiFi bindings to logicpd-torpedo-37xx-devkit

Andrew F. Davis (2):
      ARM: dts: am335x-pepper: Fix the audio CODEC's reset pin
      ARM: dts: omap3-n900: Fix the audio CODEC's reset pin

Keerthy (1):
      ARM: dts: DRA76-EVM: Set powerhold property for tps65917

Peter Ujfalusi (1):
      ARM: dts: dra7: Add missing hdmi audio DMA channel information

Roger Quadros (1):
      ARM: dts: dra7: Disable USB metastability workaround for USB2

Tony Lindgren (1):
      Merge branch 'omap-for-v4.15/fixes-dt' into omap-for-v4.16/dt

 arch/arm/boot/dts/am335x-pepper.dts               |   2 +-
 arch/arm/boot/dts/dra7.dtsi                       |   3 +
 arch/arm/boot/dts/dra76-evm.dts                   |   1 +
 arch/arm/boot/dts/logicpd-som-lv-35xx-devkit.dts  |  17 +
 arch/arm/boot/dts/logicpd-som-lv-37xx-devkit.dts  | 249 +------------
 arch/arm/boot/dts/logicpd-som-lv-baseboard.dtsi   | 256 ++++++++++++++
 arch/arm/boot/dts/logicpd-som-lv.dtsi             |  27 --
 arch/arm/boot/dts/logicpd-torpedo-35xx-devkit.dts |  17 +
 arch/arm/boot/dts/logicpd-torpedo-37xx-devkit.dts | 411 +++-------------------
 arch/arm/boot/dts/logicpd-torpedo-baseboard.dtsi  | 395 +++++++++++++++++++++
 arch/arm/boot/dts/logicpd-torpedo-som.dtsi        |  84 -----
 arch/arm/boot/dts/omap3-n900.dts                  |   4 +-
 12 files changed, 735 insertions(+), 731 deletions(-)
 create mode 100644 arch/arm/boot/dts/logicpd-som-lv-35xx-devkit.dts
 create mode 100644 arch/arm/boot/dts/logicpd-som-lv-baseboard.dtsi
 create mode 100644 arch/arm/boot/dts/logicpd-torpedo-35xx-devkit.dts
 create mode 100644 arch/arm/boot/dts/logicpd-torpedo-baseboard.dtsi

^ permalink raw reply

* [GIT PULL 2/3] clock related dts changes for omaps for v4.16
From: Tony Lindgren @ 2017-12-14 15:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <pull-1513266986-900907@atomide.com>

From: "Tony Lindgren" <tony@atomide.com>

The following changes since commit 998601de3c05fb02795d6d9cb1d4dcb25a6019a1:

  Merge commit 'fe7020e64f04' into omap-for-v4.16/dt-clk (2017-12-11 08:26:36 -0800)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap tags/omap-for-v4.16/dt-clk-signed

for you to fetch changes up to 80a06c0d8357d1e75a8fbc10813fcdada4d897fb:

  ARM: dts: dm816x: add clkctrl nodes (2017-12-11 08:28:37 -0800)

----------------------------------------------------------------
Clock related dts changes for omaps for v4.16 merge window

This branch contains a series of dts changes from Tero Kristo to
start using clkctrl clocks.

Note that this branch is based on a merge of omap-for-v4.16/soc-signed
and an immutable commit from Tero Kristo fe7020e64f04 ("clk: ti: omap4:
clkctrl data fixes for opt-clocks") that is also in clk-next.

----------------------------------------------------------------
Tero Kristo (20):
      ARM: dts: omap5: add fck under timer1
      ARM: dts: omap4: add fck under timer1
      ARM: dts: am33xx: add fck under timers1/2
      ARM: dts: am43xx: add fck under timers1/2
      ARM: dts: dra7: add fck under timer1
      ARM: dts: dm814x: add fck under timers1/2
      ARM: dts: dm816x: add fck under timers1/2
      ARM: dts: omap5: add bus functionality to base PRCM nodes
      ARM: dts: omap4: add bus functionality to base PRCM nodes
      ARM: dts: dra7: add bus functionality to base PRCM nodes
      ARM: dts: am33xx: add bus functionality to base PRCM node
      ARM: dts: am43xx: add bus functionality to base PRCM node
      ARM: dts: dm816x: add bus functionality to base PRCM node
      ARM: dts: omap4: add clkctrl nodes
      ARM: dts: omap5: add clkctrl nodes
      ARM: dts: dra7: add clkctrl nodes
      ARM: dts: am33xx: add clkctrl nodes
      ARM: dts: am43xx: add clkctrl nodes
      ARM: dts: dm814x: add clkctrl nodes
      ARM: dts: dm816x: add clkctrl nodes

 arch/arm/boot/dts/am335x-bone-common.dtsi       |   2 +-
 arch/arm/boot/dts/am335x-boneblue.dts           |   2 +-
 arch/arm/boot/dts/am335x-evm.dts                |   2 +-
 arch/arm/boot/dts/am335x-evmsk.dts              |   2 +-
 arch/arm/boot/dts/am33xx-clocks.dtsi            | 205 +++---
 arch/arm/boot/dts/am33xx.dtsi                   |  14 +-
 arch/arm/boot/dts/am4372.dtsi                   |  16 +-
 arch/arm/boot/dts/am43x-epos-evm.dts            |   2 +-
 arch/arm/boot/dts/am43xx-clocks.dtsi            | 230 ++----
 arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi |   2 +-
 arch/arm/boot/dts/dm814x-clocks.dtsi            |  30 +
 arch/arm/boot/dts/dm814x.dtsi                   |   4 +
 arch/arm/boot/dts/dm816x-clocks.dtsi            |  30 +
 arch/arm/boot/dts/dm816x.dtsi                   |   9 +-
 arch/arm/boot/dts/dra7-evm-common.dtsi          |   4 +-
 arch/arm/boot/dts/dra7.dtsi                     |  84 ++-
 arch/arm/boot/dts/dra72-evm-common.dtsi         |   4 +-
 arch/arm/boot/dts/dra72x.dtsi                   |   4 +-
 arch/arm/boot/dts/dra74x.dtsi                   |   6 +-
 arch/arm/boot/dts/dra7xx-clocks.dtsi            | 909 ++++++------------------
 arch/arm/boot/dts/omap4.dtsi                    |  37 +-
 arch/arm/boot/dts/omap44xx-clocks.dtsi          | 895 ++++++++---------------
 arch/arm/boot/dts/omap5.dtsi                    |  46 +-
 arch/arm/boot/dts/omap54xx-clocks.dtsi          | 623 ++++++----------
 24 files changed, 1091 insertions(+), 2071 deletions(-)

^ permalink raw reply

* [GIT PULL 1/3] soc changes for omaps for v4.16 merge window
From: Tony Lindgren @ 2017-12-14 15:58 UTC (permalink / raw)
  To: linux-arm-kernel

From: "Tony Lindgren" <tony@atomide.com>

The following changes since commit 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323:

  Linux 4.15-rc1 (2017-11-26 16:01:47 -0800)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap tags/omap-for-v4.16/soc-signed

for you to fetch changes up to fdf3632938a646c13f7407f2f8c33ff81eed9c76:

  Merge branch '4.15-rc1-clkctrl-mach-omap2' of https://github.com/t-kristo/linux-pm into omap-for-v4.16/soc (2017-12-11 07:46:40 -0800)

----------------------------------------------------------------
SoC changes for omaps for v4.16 merge window

For most part this is a series from Tero Kristo to prepare things
for using clkctrl clocks with DTS data. The other changes are to
make few data structures const.

----------------------------------------------------------------
Bhumika Goyal (2):
      ARM: OMAP2+: CM: make some pointers and function arguments as const
      ARM: OMAP2+: CM: make cm_ll_data structures as const

Tero Kristo (7):
      ARM: OMAP2+: CM: add support for getting phys address for a clkctrl register
      ARM: OMAP4: CMINST: add support for translating clkctrl addresses
      ARM: OMAP2+: hwmod: fix clkctrl address translation logic
      ARM: OMAP2+: clockdomain: remove the obsolete clkdm_xlate_address API
      ARM: AM33xx: CM: add support for getting physical address for a register
      ARM: OMAP2+: hwmod: calculate physical register address on am33xx
      ARM: DM816x: hwmod_data: fix clockdomain name for sata hwmod

Tony Lindgren (1):
      Merge branch '4.15-rc1-clkctrl-mach-omap2' of https://github.com/t-kristo/linux-pm into omap-for-v4.16/soc

 arch/arm/mach-omap2/clockdomain.c          |  8 ----
 arch/arm/mach-omap2/clockdomain.h          |  2 -
 arch/arm/mach-omap2/cm.h                   |  7 ++-
 arch/arm/mach-omap2/cm2xxx.c               |  2 +-
 arch/arm/mach-omap2/cm33xx.c               |  8 +++-
 arch/arm/mach-omap2/cm3xxx.c               |  2 +-
 arch/arm/mach-omap2/cm_common.c            | 16 +++++--
 arch/arm/mach-omap2/cminst44xx.c           | 12 ++---
 arch/arm/mach-omap2/omap_hwmod.c           | 73 +++++++++++++++---------------
 arch/arm/mach-omap2/omap_hwmod_81xx_data.c |  2 +-
 10 files changed, 68 insertions(+), 64 deletions(-)

^ permalink raw reply

* [PATCH v2 1/2] acpi, spcr: Make SPCR avialable to other architectures
From: Lorenzo Pieralisi @ 2017-12-14 15:49 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <6541a055-bfd4-daa7-5b91-38384bd65c3f@codeaurora.org>

On Thu, Dec 14, 2017 at 08:08:08AM -0600, Timur Tabi wrote:
> On 12/14/17 4:30 AM, Lorenzo Pieralisi wrote:
> >>I didn't want to put any ACPI code in amba-pl011.c, so putting it in spcr.c
> >>made the most sense.  I agree the global variable is ugly.  If you have a
> >>better idea, I'm all ears.
> 
> >I told you my idea. It could have been made easier by reusing the
> >ACPI_DECLARE_PROBE_ENTRY() mechanism.
> 
> Sorry, I don't mean to be difficult, but when did you tell *me* this idea of
> yours?  I don't see any email from you to me that mentions

I said that IMO it would have been better if the quirk was managed in
amba-pl011.c - you had your reasons not to do it, end of the story.

> ACPI_DECLARE_PROBE_ENTRY().  I've never even heard of that macro before.
> Please note that I'm not the original author of this code.

It is what it is, let's move on, we will keep this in mind if a similar
quirk is required.

Thanks,
Lorenzo

^ permalink raw reply

* [PATCH] KVM: arm/arm64: don't set vtimer->cnt_ctl in kvm_arch_timer_handler
From: Christoffer Dall @ 2017-12-14 15:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <5615f3e1-756e-0537-f0b6-20ae8626ac87@gmail.com>

On Thu, Dec 14, 2017 at 11:28:04PM +0800, Jia He wrote:
> 
> On 12/14/2017 9:09 PM, Christoffer Dall Wrote:
> >On Thu, Dec 14, 2017 at 12:57:54PM +0800, Jia He wrote:
> >Hi Jia,
> >
> >>I have tried your newer level-mapped-v7 branch, but bug is still there.
> >>
> >>There is no special load in both host and guest. The guest (kernel
> >>4.14) is often hanging when booting
> >>
> >>the guest kernel log
> >>
> >>[ OK ] Reached target Remote File Systems.
> >>Starting File System Check on /dev/mapper/fedora-root...
> >>[ OK ] Started File System Check on /dev/mapper/fedora-root.
> >>Mounting /sysroot...
> >>[ 2.670764] SGI XFS with ACLs, security attributes, no debug enabled
> >>[ 2.678180] XFS (dm-0): Mounting V5 Filesystem
> >>[ 2.740364] XFS (dm-0): Ending clean mount
> >>[ OK ] Mounted /sysroot.
> >>[ OK ] Reached target Initrd Root File System.
> >>Starting Reload Configuration from the Real Root...
> >>[ 61.288215] INFO: rcu_sched detected stalls on CPUs/tasks:
> >>[ 61.290791] 1-...!: (0 ticks this GP) idle=574/0/0 softirq=5/5 fqs=1
> >>[ 61.293664] (detected by 0, t=6002 jiffies, g=-263, c=-264, q=39760)
> >>[ 61.296480] Task dump for CPU 1:
> >>[ 61.297938] swapper/1 R running task 0 0 1 0x00000020
> >>[ 61.300643] Call trace:
> >>[ 61.301260] __switch_to+0x6c/0x78
> >>[ 61.302095] cpu_number+0x0/0x8
> >>[ 61.302867] rcu_sched kthread starved for 6000 jiffies!
> >>g18446744073709551353 c18446744073709551352 f0x0 RCU_GP_WAIT_FQS(3)
> >>->state=0x402 ->cpu=1
> >>[ 61.305941] rcu_sched I 0 8 2 0x00000020
> >>[ 61.307250] Call trace:
> >>[ 61.307854] __switch_to+0x6c/0x78
> >>[ 61.308693] __schedule+0x268/0x8f0
> >>[ 61.309545] schedule+0x2c/0x88
> >>[ 61.310325] schedule_timeout+0x84/0x3b8
> >>[ 61.311278] rcu_gp_kthread+0x4d4/0x7d8
> >>[ 61.312213] kthread+0x134/0x138
> >>[ 61.313001] ret_from_fork+0x10/0x1c
> >>
> >>Maybe my previous patch is not perfect enough, thanks for your comments.
> >>
> >>I digged it futher more, do you think below code logic is possibly
> >>problematic?
> >>
> >>
> >>vtimer_save_state?????????? (vtimer->loaded = false, cntv_ctl is 0)
> >>
> >>kvm_arch_timer_handler????????(read cntv_ctl and set vtimer->cnt_ctl = 0)
> >>
> >>vtimer_restore_state ? ? ? ? ?? (write vtimer->cnt_ctl to cntv_ctl,
> >>then cntv_ctl will
> >>
> >> ??? ??? ??? ??? ?? ? ? be 0 forever)
> >>
> >>
> >>If above analysis is reasonable
> >Yes, I think there's something there if the hardware doesn't retire the
> >signal fast enough...
> >
> >>how about below patch? already
> >>tested in my arm64 server.
> >>
> >>diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
> >>index f9555b1..ee6dd3f 100644
> >>--- a/virt/kvm/arm/arch_timer.c
> >>+++ b/virt/kvm/arm/arch_timer.c
> >>@@ -99,7 +99,7 @@ static irqreturn_t kvm_arch_timer_handler(int irq,
> >>void *dev_id)
> >> ??????? }
> >> ??????? vtimer = vcpu_vtimer(vcpu);
> >>
> >>-?????? if (!vtimer->irq.level) {
> >>+?????? if (vtimer->loaded && !vtimer->irq.level) {
> >> ??????????????? vtimer->cnt_ctl = read_sysreg_el0(cntv_ctl);
> >> ??????????????? if (kvm_timer_irq_can_fire(vtimer))
> >> ??????????????????????? kvm_timer_update_irq(vcpu, true, vtimer);
> >>
> >There's nothing really wrong with that patch, I just didn't think it
> >would be necessary, as we really shouldn't see interrupts if the timer
> >is not loaded.  Can you confirm that a WARN_ON(!vtimer->loaded) in
> >kvm_arch_timer_handler() gives you a splat?
> Please see the WARN_ON result (without my patch)
> [?? 72.171706] WARNING: CPU: 24 PID: 1768 at
> arch/arm64/kvm/../../../virt/kvm/arm/arch_timer.c:101
> kvm_arch_timer_handler+0xc0/0xc8
> 
> >Also, could you give the following a try (without your patch):
> >
> >diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
> >index 73d262c4712b..4751255345d1 100644
> >--- a/virt/kvm/arm/arch_timer.c
> >+++ b/virt/kvm/arm/arch_timer.c
> >@@ -367,6 +367,7 @@ static void vtimer_save_state(struct kvm_vcpu *vcpu)
> >  	/* Disable the virtual timer */
> >  	write_sysreg_el0(0, cntv_ctl);
> >+	isb();
> No luck? the bug is still there
> 

ok, so this is a slightly different approach to what you were trying to
do.  Can you please give this a try and let me know how it goes?


diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index 73d262c4712b..544ed15fbbb3 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -46,7 +46,7 @@ static const struct kvm_irq_level default_vtimer_irq = {
 	.level	= 1,
 };
 
-static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx);
+static bool kvm_timer_irq_can_fire(u32 cnt_ctl);
 static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level,
 				 struct arch_timer_context *timer_ctx);
 static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx);
@@ -94,6 +94,7 @@ static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id)
 {
 	struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id;
 	struct arch_timer_context *vtimer;
+	u32 cnt_ctl;
 
 	if (!vcpu) {
 		pr_warn_once("Spurious arch timer IRQ on non-VCPU thread\n");
@@ -101,8 +102,8 @@ static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id)
 	}
 	vtimer = vcpu_vtimer(vcpu);
 
-	vtimer->cnt_ctl = read_sysreg_el0(cntv_ctl);
-	if (kvm_timer_irq_can_fire(vtimer))
+	cnt_ctl = read_sysreg_el0(cntv_ctl);
+	if (kvm_timer_irq_can_fire(cnt_ctl))
 		kvm_timer_update_irq(vcpu, true, vtimer);
 
 	if (unlikely(!irqchip_in_kernel(vcpu->kvm)))
@@ -148,10 +149,10 @@ static u64 kvm_timer_compute_delta(struct arch_timer_context *timer_ctx)
 	return 0;
 }
 
-static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx)
+static bool kvm_timer_irq_can_fire(u32 cnt_ctl)
 {
-	return !(timer_ctx->cnt_ctl & ARCH_TIMER_CTRL_IT_MASK) &&
-		(timer_ctx->cnt_ctl & ARCH_TIMER_CTRL_ENABLE);
+	return !(cnt_ctl & ARCH_TIMER_CTRL_IT_MASK) &&
+		(cnt_ctl & ARCH_TIMER_CTRL_ENABLE);
 }
 
 /*
@@ -164,10 +165,10 @@ static u64 kvm_timer_earliest_exp(struct kvm_vcpu *vcpu)
 	struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
 	struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
 
-	if (kvm_timer_irq_can_fire(vtimer))
+	if (kvm_timer_irq_can_fire(vtimer->cnt_ctl))
 		min_virt = kvm_timer_compute_delta(vtimer);
 
-	if (kvm_timer_irq_can_fire(ptimer))
+	if (kvm_timer_irq_can_fire(ptimer->cnt_ctl))
 		min_phys = kvm_timer_compute_delta(ptimer);
 
 	/* If none of timers can fire, then return 0 */
@@ -231,7 +232,7 @@ static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx)
 {
 	u64 cval, now;
 
-	if (!kvm_timer_irq_can_fire(timer_ctx))
+	if (!kvm_timer_irq_can_fire(timer_ctx->cnt_ctl))
 		return false;
 
 	cval = timer_ctx->cnt_cval;
@@ -306,7 +307,7 @@ static void phys_timer_emulate(struct kvm_vcpu *vcpu)
 	 * don't need to have a soft timer scheduled for the future.  If the
 	 * timer cannot fire at all, then we also don't need a soft timer.
 	 */
-	if (kvm_timer_should_fire(ptimer) || !kvm_timer_irq_can_fire(ptimer)) {
+	if (kvm_timer_should_fire(ptimer) || !kvm_timer_irq_can_fire(ptimer->cnt_ctl)) {
 		soft_timer_cancel(&timer->phys_timer, NULL);
 		return;
 	}
@@ -367,6 +368,7 @@ static void vtimer_save_state(struct kvm_vcpu *vcpu)
 
 	/* Disable the virtual timer */
 	write_sysreg_el0(0, cntv_ctl);
+	isb();
 
 	vtimer->loaded = false;
 out:
@@ -398,7 +400,8 @@ void kvm_timer_schedule(struct kvm_vcpu *vcpu)
 	 * If both timers are not capable of raising interrupts (disabled or
 	 * masked), then there's no more work for us to do.
 	 */
-	if (!kvm_timer_irq_can_fire(vtimer) && !kvm_timer_irq_can_fire(ptimer))
+	if (!kvm_timer_irq_can_fire(vtimer->cnt_ctl) &&
+	    !kvm_timer_irq_can_fire(ptimer->cnt_ctl))
 		return;
 
 	/*

Thanks,
-Christoffer

^ permalink raw reply related

* [PATCH v4 3/4] cpufreq: Add DVFS support for Armada 37xx
From: Viresh Kumar @ 2017-12-14 15:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214150006.25438-4-gregory.clement@free-electrons.com>

On 14-12-17, 16:00, Gregory CLEMENT wrote:
> This patch adds DVFS support for the Armada 37xx SoCs
> 
> There are up to four CPU frequency loads for Armada 37xx controlled by
> the hardware.
> 
> This driver associates the CPU load level to a frequency, then the
> hardware will switch while selecting a load level.
> 
> The hardware also can associate a voltage for each level (AVS support)
> but it is not yet supported
> 
> Tested-by: Andre Heider <a.heider@gmail.com>
> Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
> ---
>  drivers/cpufreq/Kconfig.arm           |   7 +
>  drivers/cpufreq/Makefile              |   1 +
>  drivers/cpufreq/armada-37xx-cpufreq.c | 241 ++++++++++++++++++++++++++++++++++
>  3 files changed, 249 insertions(+)
>  create mode 100644 drivers/cpufreq/armada-37xx-cpufreq.c

Thanks for taking care of very minor review comments I had. All the
patches look good now to me :)

-- 
viresh

^ permalink raw reply


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