Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ARM: sunxi: Add driver for sunxi usb phy
From: Hans de Goede @ 2014-02-07 16:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20140115225255.GB31779@lukather>

Hi,

On 01/15/2014 11:52 PM, Maxime Ripard wrote:
> Hi Hans,
>
> Please keep me in CC for all the Allwinner-related patches.

Ok will do.

> On Tue, Jan 14, 2014 at 11:58:25PM +0100, Hans de Goede wrote:
>> The Allwinner A1x / A2x SoCs have 2 or 3 usb phys which are all accessed
>> through a single set of registers. Besides this there are also some other
>> phy related bits which need poking, which are per phy, but shared between the
>> ohci and ehci controllers, so these are also controlled from this new phy
>> driver.
>>
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>>   .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  26 ++
>>   drivers/phy/Kconfig                                |  11 +
>>   drivers/phy/Makefile                               |   1 +
>>   drivers/phy/phy-sun4i-usb.c                        | 318 +++++++++++++++++++++
>>   4 files changed, 356 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>   create mode 100644 drivers/phy/phy-sun4i-usb.c
>>
>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> new file mode 100644
>> index 0000000..6c54b3b
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> @@ -0,0 +1,26 @@
>> +Allwinner sun4i USB PHY
>> +-----------------------
>> +
>> +Required properties:
>> +- compatible : should be one of "allwinner,sun4i-a10-usb-phy",
>
> It is sun4i-usb-phy.

For completeness sake: this has been discussed elsewhere and we've agreed upon
using sun4i-a10-foo for all compat strings.

>
>> +  "allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy"
>> +- reg : 2 or 3 register offset + length pairs, 1 phy base reg pair +
>> +  1 pair for the pmu-irq register of each hcd
>
> In which order they should be set? Maybe you should use reg-names here
> to clarify things. From that documentation, I have no idea how I
> should put the values if I just want the common stuff and the (for
> example) usb1 configuration.

I've improved the doc text in my next revision.

>> +- #phy-cells : from the generic phy bindings, must be 1
>> +
>> +Optional properties:
>> +- clocks : phandle + clock specifier for the phy clock
>> +- clock-names : "usb_phy"
>> +- resets : a list of phandle + reset specifier pairs
>> +- reset-names : "usb0_reset", "usb1_reset", and / or "usb2_reset"
>> +
>> +Example:
>> +	usbphy: phy at 0x01c13400 {
>> +		#phy-cells = <1>;
>> +		compatible = "allwinner,sun4i-a10-usb-phy";
>> +		reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
>
> If you prefer not to use reg-names after all, please put a comment
> stating what each pair correspond to.

Fixed (added a comment).

>
>> +		clocks = <&usb_clk 8>;
>> +		clock-names = "usb_phy";
>> +		resets = <&usb_clk 1>, <&usb_clk 2>;
>> +		reset-names = "usb1_reset", "usb2_reset";
>> +	};
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index 330ef2d..dcce4cf 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -51,4 +51,15 @@ config PHY_EXYNOS_DP_VIDEO
>>   	help
>>   	  Support for Display Port PHY found on Samsung EXYNOS SoCs.
>>
>> +config PHY_SUN4I_USB
>> +	tristate "Allwinner sunxi SoC USB PHY driver"
>> +	depends on ARCH_SUNXI
>> +	select GENERIC_PHY
>> +	help
>> +	  Enable this to support the transceiver that is part of Allwinner
>> +	  sunxi SoCs.
>> +
>> +	  This driver controls the entire USB PHY block, both the USB OTG
>> +	  parts, as well as the 2 regular USB 2 host PHYs.
>> +
>>   endmenu
>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>> index d0caae9..e9e82f0 100644
>> --- a/drivers/phy/Makefile
>> +++ b/drivers/phy/Makefile
>> @@ -7,3 +7,4 @@ obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO)	+= phy-exynos-dp-video.o
>>   obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)	+= phy-exynos-mipi-video.o
>>   obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
>>   obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
>> +obj-$(CONFIG_PHY_SUN4I_USB)		+= phy-sun4i-usb.o
>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>> new file mode 100644
>> index 0000000..a15ecc1
>> --- /dev/null
>> +++ b/drivers/phy/phy-sun4i-usb.c
>> @@ -0,0 +1,318 @@
>> +/*
>> + * Allwinner sun4i USB phy driver
>> + *
>> + * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
>> + *
>> + * Based on code from
>> + * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>> + *
>> + * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
>> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
>> + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/reset.h>
>> +
>> +#define REG_ISCR			0x00
>> +#define REG_PHYCTL			0x04
>> +#define REG_PHYBIST			0x08
>> +#define REG_PHYTUNE			0x0c
>> +
>> +#define SUNXI_AHB_ICHR8_EN		BIT(10)
>> +#define SUNXI_AHB_INCR4_BURST_EN	BIT(9)
>> +#define SUNXI_AHB_INCRX_ALIGN_EN	BIT(8)
>> +#define SUNXI_ULPI_BYPASS_EN		BIT(0)
>> +
>> +#define MAX_PHYS			3
>> +
>> +struct sun4i_usb_phy_data {
>> +	struct clk *clk;
>> +	void __iomem *base;
>> +	struct mutex mutex;
>> +	int num_phys;
>> +	u32 disc_thresh;
>> +	struct sun4i_usb_phy {
>> +		struct phy *phy;
>> +		void __iomem *pmu_irq;
>> +		struct regulator *vbus;
>> +		struct reset_control *reset;
>> +		int index;
>> +	} phys[MAX_PHYS];
>> +};
>> +
>> +#define to_sun4i_usb_phy_data(phy) \
>> +	container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>> +
>> +static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>> +				int len)
>> +{
>> +	struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
>> +	u32 temp, usbc_bit = BIT(phy->index * 2);
>> +	int i;
>> +
>> +	mutex_lock(&phy_data->mutex);
>> +
>> +	for (i = 0; i < len; i++) {
>> +		temp = readl(phy_data->base + REG_PHYCTL);
>> +
>> +		/* clear the address portion */
>> +		temp &= ~(0xff << 8);
>> +
>> +		/* set the address */
>> +		temp |= ((addr + i) << 8);
>> +		writel(temp, phy_data->base + REG_PHYCTL);
>> +
>> +		/* set the data bit and clear usbc bit*/
>> +		temp = readb(phy_data->base + REG_PHYCTL);
>> +		if (data & 0x1)
>> +			temp |= BIT(7);
>> +		else
>> +			temp &= ~BIT(7);
>> +		temp &= ~usbc_bit;
>> +		writeb(temp, phy_data->base + REG_PHYCTL);
>> +
>> +		/* pulse usbc_bit */
>> +		temp = readb(phy_data->base + REG_PHYCTL);
>> +		temp |= usbc_bit;
>> +		writeb(temp, phy_data->base + REG_PHYCTL);
>> +
>> +		temp = readb(phy_data->base + REG_PHYCTL);
>> +		temp &= ~usbc_bit;
>> +		writeb(temp, phy_data->base + REG_PHYCTL);
>> +
>> +		data >>= 1;
>> +	}
>> +	mutex_unlock(&phy_data->mutex);
>> +}
>> +
>> +static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
>> +{
>> +	u32 bits, reg_value;
>> +
>> +	if (!phy->pmu_irq)
>> +		return;
>> +
>> +	bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
>> +		SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
>> +
>> +	reg_value = readl(phy->pmu_irq);
>> +
>> +	if (enable)
>> +		reg_value |= bits;
>> +	else
>> +		reg_value &= ~bits;
>> +
>> +	writel(reg_value, phy->pmu_irq);
>> +}
>> +
>> +static int sun4i_usb_phy_init(struct phy *_phy)
>> +{
>> +	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +	int ret;
>> +
>> +	ret = clk_prepare_enable(data->clk);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = reset_control_deassert(phy->reset);
>> +	if (ret) {
>> +		clk_disable_unprepare(data->clk);
>> +		return ret;
>> +	}
>> +
>> +	/* Adjust PHY's magnitude and rate */
>> +	sun4i_usb_phy_write(phy, 0x20, 0x14, 5);
>> +
>> +	/* Disconnect threshold adjustment */
>> +	sun4i_usb_phy_write(phy, 0x2a, data->disc_thresh, 2);
>> +
>> +	sun4i_usb_phy_passby(phy, 1);
>> +
>> +	return 0;
>> +}
>> +
>> +static int sun4i_usb_phy_exit(struct phy *_phy)
>> +{
>> +	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> +	sun4i_usb_phy_passby(phy, 0);
>> +	reset_control_assert(phy->reset);
>> +	clk_disable_unprepare(data->clk);
>> +
>> +	return 0;
>> +}
>> +
>> +static int sun4i_usb_phy_power_on(struct phy *_phy)
>> +{
>> +	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +	int ret;
>> +
>> +	if (phy->vbus) {
>> +		ret = regulator_enable(phy->vbus);
>> +		if (ret)
>> +			return ret;
>> +
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int sun4i_usb_phy_power_off(struct phy *_phy)
>> +{
>> +	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +
>> +	if (phy->vbus)
>> +		regulator_disable(phy->vbus);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct phy_ops sun4i_usb_phy_ops = {
>> +	.init		= sun4i_usb_phy_init,
>> +	.exit		= sun4i_usb_phy_exit,
>> +	.power_on	= sun4i_usb_phy_power_on,
>> +	.power_off	= sun4i_usb_phy_power_off,
>> +	.owner		= THIS_MODULE,
>> +};
>> +
>> +static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>> +					struct of_phandle_args *args)
>> +{
>> +	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>> +
>> +	if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
>> +		return ERR_PTR(-ENODEV);
>> +
>> +	return data->phys[args->args[0]].phy;
>> +}
>> +
>> +static int sun4i_usb_phy_probe(struct platform_device *pdev)
>> +{
>> +	struct sun4i_usb_phy_data *data;
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *np = dev->of_node;
>> +	void __iomem *pmu_irq = NULL;
>> +	struct phy_provider *phy_provider;
>> +	struct reset_control *reset;
>> +	struct regulator *vbus;
>> +	struct phy *phy;
>> +	char name[16];
>> +	int i;
>> +
>> +	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>> +	if (!data)
>> +		return -ENOMEM;
>> +
>> +	mutex_init(&data->mutex);
>> +	if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy")) {
>> +		data->num_phys = 3;
>> +		data->disc_thresh = 3;
>> +	} else if (of_device_is_compatible(np,
>> +					"allwinner,sun5i-a13-usb-phy")) {
>> +		data->num_phys = 2;
>> +		data->disc_thresh = 2;
>> +	} else { /* allwinner,sun7i-a20-usb-phy */
>> +		data->num_phys = 3;
>> +		data->disc_thresh = 2;
>> +	}
>
> Maybe we can use of_match_data() here instead of having an ever-growing
> list of if-else statements?

I've refactored this a bit to get rid of the if .. else if .. else structure.

Regards,

Hans

^ permalink raw reply

* [PATCH v2 2/2] Documentation: devicetree: Add boost-frequency binding to list boost mode frequency
From: Sudeep Holla @ 2014-02-07 16:15 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391786342-11812-3-git-send-email-thomas.ab@samsung.com>

On 07/02/14 15:19, Thomas Abraham wrote:
> From: Thomas Abraham <thomas.ab@samsung.com>
> 
> Add a new optional boost-frequency binding for specifying the frequencies
> usable in boost mode.
> 
> Cc: Nishanth Menon <nm@ti.com>
> Cc: Lukasz Majewski <l.majewski@samsung.com>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Pawel Moll <pawel.moll@arm.com>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
> Cc: Kumar Gala <galak@codeaurora.org>
> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> ---
>  Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt |   11 +++++++++++
>  1 file changed, 11 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
> 
> diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
> new file mode 100644
> index 0000000..d925e38
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
> @@ -0,0 +1,11 @@
> +* Device tree binding for CPU boost frequency (aka over-clocking)
> +
> +Certain CPU's can be operated in optional 'boost' mode (or sometimes referred as
> +overclocking) in which the CPU can operate in frequencies beyond the normal
> +operating conditions.
> +
> +Optional Properties:
> +- boost-frequency: list of frequencies in KHz to be used only in boost mode.
> +  This list should be a subset of frequencies listed in "operating-points"
> +  property. Refer to Documentation/devicetree/bindings/power/opp.txt for
> +  details about "operating-points" property.
> 

Won't single entry for boost frequency suffice which would be the starting
frequency in the boost range. IWO will there be OPP list with frequencies:
A > B > C > D, but only B and C are boost frequency. That seems little odd,
unless it's some configuration chosen purely on software basis rather than
hardware. For me B marks the beginning of over-clocking.

Regards,
Sudeep

^ permalink raw reply

* [PATCH] clk: respect the clock dependencies in of_clk_init
From: Emilio López @ 2014-02-07 16:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52F4F7EF.7090900@free-electrons.com>

Hi Gregory,

El 07/02/14 12:12, Gregory CLEMENT escribi?:
> On 07/02/2014 16:00, Emilio L?pez wrote:
>> El 07/02/14 11:49, Gregory CLEMENT escribi?:
>>> On 07/02/2014 15:43, Ezequiel Garcia wrote:
>>>> On Fri, Feb 07, 2014 at 09:24:30AM -0500, Jason Cooper wrote:
>>>>> On Fri, Feb 07, 2014 at 10:06:08AM -0300, Emilio L?pez wrote:
>>>>>
>>>>> [snip a great explanation]
>>>>>
>>>>> Guys, can I get some Tested-by's on this?
>>>>>
>>>>
>>>> In case someone missed Emilio's comment about it, I gave his oneliner
>>>> a test on A370 Reference Design. It worked just as well as Sebastian's.
>>>
>>> Well ok it's working but this patch is not better than Sebastian, it is
>>> even worth. I don't think it is a good idea at all to totally ignore the
>>> information given by the device tree.
>>
>> With a bit more work, you can replace the clk_get magic with a call to
>> of_clk_get_parent_name() or similar to be able to keep overriding stuff
>> from DT. This way it would completely match the behaviour on
>> mvebu_coreclk_setup (default to "tclk", allow overriding with DT).
>>
>
> I think you didn't have a look on our implementation:

I did, several times in fact.

> the name of the clock
> are created by the driver during the initialization.

The name of the clock is always "tclk", as hardcoded on the driver, 
unless overridden via clock-output-names from DT (see 
mvebu_coreclk_setup). Currently none of your DT that I can see does 
this, so in practice it's always "tclk".

> That's why we need that
> the parent clock are initialized before the gating clock.

You don't really *need* that. The driver just does that because it may 
have been convenient at the time and it worked. Defaulting to "tclk" on 
mvebu_clk_gating_setup and overriding it if it turns out it has some 
other name on the DT (just like on mvebu_coreclk_setup!) should work as 
well, and doesn't require complex, bloaty, dependency management.

Rough, untested patch below, so you get the idea.

Cheers,

Emilio



---
  drivers/clk/mvebu/common.c | 13 +++++++------
  1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
index 25ceccf..730625b 100644
--- a/drivers/clk/mvebu/common.c
+++ b/drivers/clk/mvebu/common.c
@@ -119,19 +119,20 @@ void __init mvebu_clk_gating_setup(struct 
device_node *np,
  				   const struct clk_gating_soc_desc *desc)
  {
  	struct clk_gating_ctrl *ctrl;
-	struct clk *clk;
  	void __iomem *base;
-	const char *default_parent = NULL;
+	struct of_phandle_args clkspec;
+	const char *default_parent = "tclk";
  	int n;

  	base = of_iomap(np, 0);
  	if (WARN_ON(!base))
  		return;

-	clk = of_clk_get(np, 0);
-	if (!IS_ERR(clk)) {
-		default_parent = __clk_get_name(clk);
-		clk_put(clk);
+	if (!of_parse_phandle_with_args(np, "clocks", "#clock-cells", 0, 
&clkspec)) {
+		of_property_read_string_index(clkspec.np, "clock-output-names",
+					      clkspec.args_count ? clkspec.args[0] : 0,
+					      &default_parent);
+		of_node_put(clkspec.np);
  	}

  	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
-- 
1.8.5.3

^ permalink raw reply related

* [PATCH v3 1/7] cpufreq: cpufreq-cpu0: allow use of optional boost mode frequencies
From: Nishanth Menon @ 2014-02-07 16:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391788548-13056-2-git-send-email-thomas.ab@samsung.com>

On 02/07/2014 09:55 AM, Thomas Abraham wrote:
> From: Thomas Abraham <thomas.ab@samsung.com>
> 
> Lookup for the optional boost-frequency property in cpu0 node and if
> available, enable support for boost mode frequencies. The frequencies
> usable in boost mode are determined while preparing the cpufreq table
> from the list of operating points available.
> 
> In addition to this, enable the CPU_FREQ_BOOST_SW config option for
> this driver by default. On platforms that do not support boost mode,
> the boost mode frequencies will not be specified in cpu0 node and
> hence the boost mode support will not be enabled. Since this driver
> anyways depends on THERMAL config option, it is safe to enable
> CPU_FREQ_BOOST_SW config option as default.
> 
> Cc: Shawn Guo <shawn.guo@linaro.org>
> Cc: Lukasz Majewski <l.majewski@samsung.com>
> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> ---
>  Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt |    2 ++
>  drivers/cpufreq/Kconfig                                    |    1 +
>  drivers/cpufreq/cpufreq-cpu0.c                             |    3 +++
>  3 files changed, 6 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt
> index f055515..60f321a 100644
> --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt
> +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt
> @@ -19,6 +19,8 @@ Optional properties:
>  - cooling-min-level:
>  - cooling-max-level:
>       Please refer to Documentation/devicetree/bindings/thermal/thermal.txt.
> +- boost-frequency:
> +     Please refer to Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>  
>  Examples:
>  
> diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
> index 4b029c0..52cc704 100644
> --- a/drivers/cpufreq/Kconfig
> +++ b/drivers/cpufreq/Kconfig
> @@ -187,6 +187,7 @@ config GENERIC_CPUFREQ_CPU0
>  	tristate "Generic CPU0 cpufreq driver"
>  	depends on HAVE_CLK && REGULATOR && OF && THERMAL && CPU_THERMAL
>  	select PM_OPP
> +	select CPU_FREQ_BOOST_SW
>  	help
>  	  This adds a generic cpufreq driver for CPU0 frequency management.
>  	  It supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
> diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c
> index 0c12ffc..06539eb 100644
> --- a/drivers/cpufreq/cpufreq-cpu0.c
> +++ b/drivers/cpufreq/cpufreq-cpu0.c
> @@ -195,6 +195,9 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev)
>  			transition_latency += ret * 1000;
>  	}
>  
> +	if (of_find_property(cpu_dev->of_node, "boost-frequency", NULL))
> +		cpu0_cpufreq_driver.boost_supported = true;
> +
>  	ret = cpufreq_register_driver(&cpu0_cpufreq_driver);
>  	if (ret) {
>  		pr_err("failed register driver: %d\n", ret);
> 

might as well hide that under the opp api which returns a bool that
says if boost is supported or not. that allows the parse to be
isolated to a single location.

-- 
Regards,
Nishanth Menon

^ permalink raw reply

* [RFC PATCH]  ARM: Enable imprecise external aborts earlier
From: Fabrice GASNIER @ 2014-02-07 16:19 UTC (permalink / raw)
  To: linux-arm-kernel

Some HW / drivers like PCIe use imprecise aborts on ARM to detect ports
that are behind a switch, by using: hook_fault_code(16+6, ...);

At PCIe bus enumeration, imprecise aborts are not enabled. Imprecise
aborts are enabled late during bootup process, when userland
init starts. It seems unmasked later when starting userland init
process (e.g. when CPSR.A bit on arm is cleared). Aborts are deferred
until then.

Enable imprecise external aborts earlier during bootup.

Reference :
http://marc.info/?l=linux-arm-kernel&m=139118404708291&w=2

Fabrice Gasnier (1):
  ARM: Add imprecise abort enable/disable macro

 arch/arm/include/asm/irqflags.h |   33 +++++++++++++++++++++++++++++++++
 arch/arm/kernel/smp.c           |    1 +
 arch/arm/kernel/traps.c         |    4 ++++
 3 files changed, 38 insertions(+)

-- 
1.7.9.5

^ permalink raw reply

* [RFC PATCH] ARM: Add imprecise abort enable/disable macro
From: Fabrice GASNIER @ 2014-02-07 16:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391789955-26927-1-git-send-email-fabrice.gasnier@st.com>

This patch adds imprecise abort enable/disable macros.
It also enables imprecise aborts when starting kernel.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 arch/arm/include/asm/irqflags.h |   33 +++++++++++++++++++++++++++++++++
 arch/arm/kernel/smp.c           |    1 +
 arch/arm/kernel/traps.c         |    4 ++++
 3 files changed, 38 insertions(+)

diff --git a/arch/arm/include/asm/irqflags.h b/arch/arm/include/asm/irqflags.h
index 3b763d6..82e3834 100644
--- a/arch/arm/include/asm/irqflags.h
+++ b/arch/arm/include/asm/irqflags.h
@@ -51,6 +51,9 @@ static inline void arch_local_irq_disable(void)
 
 #define local_fiq_enable()  __asm__("cpsie f	@ __stf" : : : "memory", "cc")
 #define local_fiq_disable() __asm__("cpsid f	@ __clf" : : : "memory", "cc")
+
+#define local_abt_enable()  __asm__("cpsie a	@ __sta" : : : "memory", "cc")
+#define local_abt_disable() __asm__("cpsid a	@ __cla" : : : "memory", "cc")
 #else
 
 /*
@@ -130,6 +133,36 @@ static inline void arch_local_irq_disable(void)
 	: "memory", "cc");					\
 	})
 
+/*
+ * Enable Aborts
+ */
+#define local_abt_enable()					\
+	({							\
+		unsigned long temp;				\
+	__asm__ __volatile__(					\
+	"mrs	%0, cpsr		@ sta\n"		\
+"	bic	%0, %0, %1\n"					\
+"	msr	cpsr_c, %0"					\
+	: "=r" (temp)						\
+	: "r" (PSR_A_BIT)					\
+	: "memory", "cc");					\
+	})
+
+/*
+ * Disable Aborts
+ */
+#define local_abt_disable()					\
+	({							\
+		unsigned long temp;				\
+	__asm__ __volatile__(					\
+	"mrs	%0, cpsr		@ cla\n"		\
+"	orr	%0, %0, %1\n"					\
+"	msr	cpsr_c, %0"					\
+	: "=r" (temp)						\
+	: "r" (PSR_A_BIT)					\
+	: "memory", "cc");					\
+	})
+
 #endif
 
 /*
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index dc894ab..c2093cb 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -377,6 +377,7 @@ asmlinkage void secondary_start_kernel(void)
 
 	local_irq_enable();
 	local_fiq_enable();
+	local_abt_enable();
 
 	/*
 	 * OK, it's off to the idle thread for us
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 4636d56..ef15709 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -900,6 +900,10 @@ void __init early_trap_init(void *vectors_base)
 
 	flush_icache_range(vectors, vectors + PAGE_SIZE * 2);
 	modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
+
+	/* Enable imprecise aborts */
+	local_abt_enable();
+
 #else /* ifndef CONFIG_CPU_V7M */
 	/*
 	 * on V7-M there is no need to copy the vector table to a dedicated
-- 
1.7.9.5

^ permalink raw reply related

* Why are imprecise external aborts masked on recent kernel while booting ?
From: Fabrice Gasnier @ 2014-02-07 16:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52EFC72A.6060608@st.com>

Hi all,

I think there should be differences depending on ARM version.
I'm using armv7, "cpsie     a" is ok,
On other arm version, it might be necessary to use different instruction 
as Russel advised previously.

Does it makes sense to add an abort enable/disable macro or function ?

I'm sending a separate patch to illustrate.

Thanks,
BR,
Fabrice

On 02/03/2014 05:43 PM, Fabrice Gasnier wrote:
> Hi,
>
> I had no success with msr instruction to set CPSR.A bit.
>
> I re-checked on a 3.4 kernel, msr instruction present in former 
> "kernel_thread_helper()" routine was not responsible for clearing 
> CPSR.A bit.
> 'A' bit was cleared because SPSR was altered before executing 
> following instruction in arch/arm/kernel/entry-header.S :
> movs    pc, lr                @ return & move spsr_svc into cpsr
>
> Sorry for the confusion in my first email: that movs instruction was 
> responsible for clearing 'A' bit on 3.4 kernel. But on recent kernel, 
> "restore_user_regs" macro seems no longer called for a kernel thread.
>
> So, I tried the 'cps'instruction that does it! I re-worked slightly 
> your previous patch.
> I also noticed that secondary needs to be set separately.
>
> Please, could you comment on the following patch ? (I can resend 
> correctly formated patch if you wish) :
>
> diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
> index dc894ab..e22b109 100644
> --- a/arch/arm/kernel/smp.c
> +++ b/arch/arm/kernel/smp.c
> @@ -378,6 +378,9 @@ asmlinkage void secondary_start_kernel(void)
>         local_irq_enable();
>         local_fiq_enable();
>
> +       /* Enable imprecise aborts */
> +       asm volatile("cpsie     a");
> +
>         /*
>          * OK, it's off to the idle thread for us
>          */
> diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
> index 4636d56..a9567bb 100644
> --- a/arch/arm/kernel/traps.c
> +++ b/arch/arm/kernel/traps.c
> @@ -900,6 +900,10 @@ void __init early_trap_init(void *vectors_base)
>
>         flush_icache_range(vectors, vectors + PAGE_SIZE * 2);
>         modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
> +
> +       /* Enable imprecise aborts */
> +       asm volatile("cpsie     a");
> +
>  #else /* ifndef CONFIG_CPU_V7M */
>         /*
>          * on V7-M there is no need to copy the vector table to a 
> dedicated
>
> Thanks,
> BR,
> Fabrice
>
> On 02/03/2014 10:12 AM, Fabrice Gasnier wrote:
>> Hi Russell,
>>
>> Thank you for your help.
>> I just tried following patch on both 3.10 and above vanilla 3.13.1.
>> Unfortunately, these instructions have no effect on the arm cpsr.
>> I dumped regs right after msr instruction have been executed. It 
>> remains untouched :
>>
>> Here is assembly from gdb:
>>
>>    0xc064a400 <+128>:    mov    r3, #256    ; 0x100
>>    0xc064a404 <+132>:    mrs    r2, CPSR
>>    0xc064a408 <+136>:    bic    r2, r2, r3
>>    0xc064a40c <+140>:    msr    CPSR_c, r2
>>
>> CPSR.A bit is still set after these instructions : 0x600001d3
>> Although, I see it has been cleared in r2: 0x600000d3
>>
>> Please advise.
>> Thanks,
>> BR,
>> Fabrice
>> On 01/31/2014 06:08 PM, Russell King - ARM Linux wrote:
>>>> Is it possible to unmask imprecise data aborts earlier in the boot
>>>> >process (e.g. before PCIe bus enumeration, when drivers are being 
>>>> probed)
>>>> >?
>>> How about this patch?
>>>
>>> diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
>>> index 172ee18ff124..b0ff06f49cd0 100644
>>> --- a/arch/arm/kernel/traps.c
>>> +++ b/arch/arm/kernel/traps.c
>>> @@ -900,6 +900,15 @@ void __init early_trap_init(void *vectors_base)
>>>         flush_icache_range(vectors, vectors + PAGE_SIZE * 2);
>>>       modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
>>> +
>>> +    /* Enable imprecise aborts */
>>> +    asm volatile(
>>> +        "mrs    %0, cpsr\n"
>>> +    "    bic    %0, %0, %1\n"
>>> +    "    msr    cpsr_c, %0"
>>> +        : "=&r" (i)
>>> +        : "r" (PSR_A_BIT));
>>> +
>>>   #else /* ifndef CONFIG_CPU_V7M */
>>>       /*
>>>        * on V7-M there is no need to copy the vector table to a 
>>> dedicated
>>
>

^ permalink raw reply

* [PATCH v3 5/7] ARM: dts: Exynos: add cpu nodes, opp and cpu clock configuration data
From: Sudeep Holla @ 2014-02-07 16:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391788548-13056-6-git-send-email-thomas.ab@samsung.com>

On 07/02/14 15:55, Thomas Abraham wrote:
> From: Thomas Abraham <thomas.ab@samsung.com>
> 
> For all Exynos based platforms, add CPU nodes, operating points and cpu
> clock data for migrating from Exynos specific cpufreq driver to using
> generic cpufreq-cpu0 driver.
> 
> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>

[...]

> diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi
> index 5c412aa..c613fc2 100644
> --- a/arch/arm/boot/dts/exynos4x12.dtsi
> +++ b/arch/arm/boot/dts/exynos4x12.dtsi
> @@ -31,6 +31,42 @@
>                 mshc0 = &mshc_0;
>         };
> 
> +       cpus {
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +               cpu at 0 {
> +                       device_type = "cpu";
> +                       compatible = "arm,cortex-a9";
> +                       reg = <0>;
> +                       clocks = <&clock 12>;
> +                       clock-names = "cpu";
> +
> +                       operating-points = <
> +                               1500000 1350000
> +                               1400000 1287500
> +                               1300000 1250000
> +                               1200000 1187500
> +                               1100000 1137500
> +                               1000000 1087500
> +                                900000 1037500
> +                                800000 1000000
> +                                700000  987500
> +                                600000  975000
> +                                500000  950000
> +                                400000  925000
> +                                300000  900000
> +                                200000  900000
> +                       >;
> +                       clock-latency = <200000>;
> +                       boost-frequency = <1500000 1350000>;

This is confusing, 1350000 is not in the OPP frequency list or this is still
following old binding with voltage. Either case this needs to be fixed.

Regards,
Sudeep

^ permalink raw reply

* [PATCH] ARM: enable IRQs in user undefined instruction vector
From: Catalin Marinas @ 2014-02-07 16:25 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20140206205447.GX26684@n2100.arm.linux.org.uk>

On Thu, Feb 06, 2014 at 08:54:48PM +0000, Russell King - ARM Linux wrote:
> On Thu, Feb 06, 2014 at 08:10:44PM +0200, Kevin Bracey wrote:
> > The VFP code did take pains to increment the pre-emption count before  
> > enabling interrupts, but it's not obvious whether it requires no  
> > pre-emption between the bounce and handler, or just no pre-emption  
> > during the handler.
> 
> Just take a moment to think about this.
> 
> - Userspace raises an undefined instruction exception, running on CPU 0.
> - The kernel starts to handle the exception by saving the ARM state.
> - Enables interrupts.
> - Context switch occurs.
> - VFP state is saved and the VFP is disabled.

There is one case when it isn't saved because of FPEXC_EN being cleared
but we don't care since the VFP registers haven't been touched.

> Now, on CPU1...
> - Context switch occurs to the above thread (due to thread migration).
> - VFP will be in a disabled state.
> - We read FPEXC, find that the VFP is disabled
> - Load saved state into the VFP
> - Check for an exception recorded in the VFP state, and handle it.

This should work since we check the restored registers.

> > What about the iwmmxt and the crunchbits? They are only lazy-enable  
> > routines, to activate an inactive coprocessor. Which I think makes them  
> > safe. If we schedule before reaching the handler, when this context is  
> > returned to, the coprocessor must still be disabled in our context - the  
> > handler can proceed to enable it. And there can't be any other context  
> > state to worry about, assuming the lazy enable scheme works.
> 
> Again, the restore needs to happen with preemption disabled so that
> you don't end up with the state half-restored, and then when you
> resume the thread, you restore the other half.
> 
> This is actually the case I'm more worried about - whether all the
> various handlers are safe being entered with preemption enabled.
> They weren't written for it so the current answer is that they
> aren't safe.

My patchset disables preemption on entering those handlers via
__und_usr, so they don't touch their state with preemption enabled
(well, review is still needed, I can repost them).

> > I share Alexey's Ignatov's concern that your patch ends up running the  
> > support code with interrupts either on or off depending on whether you  
> > came from user or supervisor mode, which feels a little odd. But then,  
> > always enabling interrupts like the current code does, when they might  
> > have been off in the SVC mode context, also seems wrong.
> 
> That is indeed wrong, but then we /used/ to have the requirement that
> the kernel will _never_ execute VFP instructions.  That's changed
> recently since we now permit neon instructions to be executed.

Even if we would take VFP faults from the kernel, I think they are fine
to be executed with interrupts disabled. The problem is when we get page
faults and these may sleep but that's not the case with kernel mappings.

An alternative would be to only enable interrupts around user access in
__und_usr and disable them again afterwards.

-- 
Catalin

^ permalink raw reply

* [PATCH v4 1/5] clk: sunxi: Add support for USB clock-register reset bits
From: Philipp Zabel @ 2014-02-07 16:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391786513-20780-2-git-send-email-hdegoede@redhat.com>

Am Freitag, den 07.02.2014, 16:21 +0100 schrieb Hans de Goede:
> The usb-clk register is special in that it not only contains clk gate bits,
> but also has a few reset bits. This commit adds support for this by allowing
> gates type sunxi clks to also register a reset controller.
> 
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>

Acked-by: Philipp Zabel <p.zabel@pengutronix.de>

> ---
>  drivers/clk/sunxi/clk-sunxi.c | 71 +++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 71 insertions(+)
> 
> diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
> index 64bda21..1e15e4c 100644
> --- a/drivers/clk/sunxi/clk-sunxi.c
> +++ b/drivers/clk/sunxi/clk-sunxi.c
> @@ -18,6 +18,7 @@
>  #include <linux/clkdev.h>
>  #include <linux/of.h>
>  #include <linux/of_address.h>
> +#include <linux/reset-controller.h>
>  
>  #include "clk-factors.h"
>  
> @@ -838,6 +839,59 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
>  
> 
>  /**
> + * sunxi_gates_reset... - reset bits in leaf gate clk registers handling
> + */
> +
> +struct gates_reset_data {
> +	void __iomem			*reg;
> +	spinlock_t			*lock;
> +	struct reset_controller_dev	rcdev;
> +};
> +
> +static int sunxi_gates_reset_assert(struct reset_controller_dev *rcdev,
> +			      unsigned long id)
> +{
> +	struct gates_reset_data *data = container_of(rcdev,
> +						     struct gates_reset_data,
> +						     rcdev);
> +	unsigned long flags;
> +	u32 reg;
> +
> +	spin_lock_irqsave(data->lock, flags);
> +
> +	reg = readl(data->reg);
> +	writel(reg & ~BIT(id), data->reg);
> +
> +	spin_unlock_irqrestore(data->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int sunxi_gates_reset_deassert(struct reset_controller_dev *rcdev,
> +				unsigned long id)
> +{
> +	struct gates_reset_data *data = container_of(rcdev,
> +						     struct gates_reset_data,
> +						     rcdev);
> +	unsigned long flags;
> +	u32 reg;
> +
> +	spin_lock_irqsave(data->lock, flags);
> +
> +	reg = readl(data->reg);
> +	writel(reg | BIT(id), data->reg);
> +
> +	spin_unlock_irqrestore(data->lock, flags);
> +
> +	return 0;
> +}
> +
> +static struct reset_control_ops sunxi_gates_reset_ops = {
> +	.assert		= sunxi_gates_reset_assert,
> +	.deassert	= sunxi_gates_reset_deassert,
> +};
> +
> +/**
>   * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks
>   */
>  
> @@ -845,6 +899,7 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
>  
>  struct gates_data {
>  	DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE);
> +	u32 reset_mask;
>  };
>  
>  static const struct gates_data sun4i_axi_gates_data __initconst = {
> @@ -915,6 +970,7 @@ static void __init sunxi_gates_clk_setup(struct device_node *node,
>  					 struct gates_data *data)
>  {
>  	struct clk_onecell_data *clk_data;
> +	struct gates_reset_data *reset_data;
>  	const char *clk_parent;
>  	const char *clk_name;
>  	void *reg;
> @@ -958,6 +1014,21 @@ static void __init sunxi_gates_clk_setup(struct device_node *node,
>  	clk_data->clk_num = i;
>  
>  	of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
> +
> +	/* Register a reset controler for gates with reset bits */
> +	if (data->reset_mask == 0)
> +		return;
> +
> +	reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
> +	if (!reset_data)
> +		return;
> +
> +	reset_data->reg = reg;
> +	reset_data->lock = &clk_lock;
> +	reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1;
> +	reset_data->rcdev.ops = &sunxi_gates_reset_ops;
> +	reset_data->rcdev.of_node = node;
> +	reset_controller_register(&reset_data->rcdev);
>  }
>  
> 

^ permalink raw reply

* [PATCH v2 2/2] Documentation: devicetree: Add boost-frequency binding to list boost mode frequency
From: Sudeep Holla @ 2014-02-07 16:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52F50698.4060305@arm.com>

On 07/02/14 16:15, Sudeep Holla wrote:
> On 07/02/14 15:19, Thomas Abraham wrote:
>> From: Thomas Abraham <thomas.ab@samsung.com>
>>
>> Add a new optional boost-frequency binding for specifying the frequencies
>> usable in boost mode.
>>
>> Cc: Nishanth Menon <nm@ti.com>
>> Cc: Lukasz Majewski <l.majewski@samsung.com>
>> Cc: Rob Herring <robh+dt@kernel.org>
>> Cc: Pawel Moll <pawel.moll@arm.com>
>> Cc: Mark Rutland <mark.rutland@arm.com>
>> Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
>> Cc: Kumar Gala <galak@codeaurora.org>
>> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
>> ---
>>  Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt |   11 +++++++++++
>>  1 file changed, 11 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>>
>> diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>> new file mode 100644
>> index 0000000..d925e38
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>> @@ -0,0 +1,11 @@
>> +* Device tree binding for CPU boost frequency (aka over-clocking)
>> +
>> +Certain CPU's can be operated in optional 'boost' mode (or sometimes referred as
>> +overclocking) in which the CPU can operate in frequencies beyond the normal
>> +operating conditions.
>> +
>> +Optional Properties:
>> +- boost-frequency: list of frequencies in KHz to be used only in boost mode.
>> +  This list should be a subset of frequencies listed in "operating-points"
>> +  property. Refer to Documentation/devicetree/bindings/power/opp.txt for
>> +  details about "operating-points" property.
>>
> 
> Won't single entry for boost frequency suffice which would be the starting
> frequency in the boost range. IOW will there be OPP list with frequencies:
> A > B > C > D, but only B and C are boost frequency. That seems little odd,
> unless it's some configuration chosen purely on software basis rather than
> hardware. For me B marks the beginning of over-clocking.
> 
Ah, I meant A < B < C < D in the above example.

Regards,
Sudeep

^ permalink raw reply

* [PATCH v2] ARM: sunxi: Add driver for sunxi usb phy
From: Hans de Goede @ 2014-02-07 16:33 UTC (permalink / raw)
  To: linux-arm-kernel

The Allwinner A1x / A2x SoCs have 2 or 3 usb phys which are all accessed
through a single set of registers. Besides this there are also some other
phy related bits which need poking, which are per phy, but shared between the
ohci and ehci controllers, so these are also controlled from this new phy
driver.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  28 ++
 drivers/phy/Kconfig                                |  11 +
 drivers/phy/Makefile                               |   1 +
 drivers/phy/phy-sun4i-usb.c                        | 326 +++++++++++++++++++++
 4 files changed, 366 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
 create mode 100644 drivers/phy/phy-sun4i-usb.c

diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
new file mode 100644
index 0000000..f7eccb2
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
@@ -0,0 +1,28 @@
+Allwinner sun4i USB PHY
+-----------------------
+
+Required properties:
+- compatible : should be one of "allwinner,sun4i-a10-usb-phy",
+  "allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy"
+- reg : a list of offset + length pairs, the 1st list entry should point to
+  the phy base regs, the 2nd entry to the pmu reg for phy1, and the 3th
+  entry to the pmu reg of phy2 (for devices which have a phy2).
+- #phy-cells : from the generic phy bindings, must be 1
+
+Optional properties:
+- clocks : phandle + clock specifier for the phy clock
+- clock-names : "usb_phy"
+- resets : a list of phandle + reset specifier pairs
+- reset-names : "usb0_reset", "usb1_reset", and / or "usb2_reset"
+
+Example:
+	usbphy: phy at 0x01c13400 {
+		#phy-cells = <1>;
+		compatible = "allwinner,sun4i-a10-usb-phy";
+		/* phy base regs, phy1 pmu reg, phy2 pmu reg */
+		reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
+		clocks = <&usb_clk 8>;
+		clock-names = "usb_phy";
+		resets = <&usb_clk 1>, <&usb_clk 2>;
+		reset-names = "usb1_reset", "usb2_reset";
+	};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index afa2354..6070c99 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -64,4 +64,15 @@ config BCM_KONA_USB2_PHY
 	help
 	  Enable this to support the Broadcom Kona USB 2.0 PHY.
 
+config PHY_SUN4I_USB
+	tristate "Allwinner sunxi SoC USB PHY driver"
+	depends on ARCH_SUNXI
+	select GENERIC_PHY
+	help
+	  Enable this to support the transceiver that is part of Allwinner
+	  sunxi SoCs.
+
+	  This driver controls the entire USB PHY block, both the USB OTG
+	  parts, as well as the 2 regular USB 2 host PHYs.
+
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index b57c253..9d4f8bb 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)	+= phy-exynos-mipi-video.o
 obj-$(CONFIG_PHY_MVEBU_SATA)		+= phy-mvebu-sata.o
 obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
 obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
+obj-$(CONFIG_PHY_SUN4I_USB)		+= phy-sun4i-usb.o
diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
new file mode 100644
index 0000000..bd9cb7fa
--- /dev/null
+++ b/drivers/phy/phy-sun4i-usb.c
@@ -0,0 +1,326 @@
+/*
+ * Allwinner sun4i USB phy driver
+ *
+ * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on code from
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ *
+ * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+
+#define REG_ISCR			0x00
+#define REG_PHYCTL			0x04
+#define REG_PHYBIST			0x08
+#define REG_PHYTUNE			0x0c
+
+#define SUNXI_AHB_ICHR8_EN		BIT(10)
+#define SUNXI_AHB_INCR4_BURST_EN	BIT(9)
+#define SUNXI_AHB_INCRX_ALIGN_EN	BIT(8)
+#define SUNXI_ULPI_BYPASS_EN		BIT(0)
+
+/* Common Control Bits for Both PHYs */
+#define PHY_PLL_BW			0x03
+#define PHY_RES45_CAL_EN		0x0c
+
+/* Private Control Bits for Each PHY */
+#define PHY_TX_AMPLITUDE_TUNE		0x20
+#define PHY_TX_SLEWRATE_TUNE		0x22
+#define PHY_VBUSVALID_TH_SEL		0x25
+#define PHY_PULLUP_RES_SEL		0x27
+#define PHY_OTG_FUNC_EN			0x28
+#define PHY_VBUS_DET_EN			0x29
+#define PHY_DISCON_TH_SEL		0x2a
+
+#define MAX_PHYS			3
+
+struct sun4i_usb_phy_data {
+	struct clk *clk;
+	void __iomem *base;
+	struct mutex mutex;
+	int num_phys;
+	u32 disc_thresh;
+	struct sun4i_usb_phy {
+		struct phy *phy;
+		void __iomem *pmu;
+		struct regulator *vbus;
+		struct reset_control *reset;
+		int index;
+	} phys[MAX_PHYS];
+};
+
+#define to_sun4i_usb_phy_data(phy) \
+	container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
+
+static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
+				int len)
+{
+	struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
+	u32 temp, usbc_bit = BIT(phy->index * 2);
+	int i;
+
+	mutex_lock(&phy_data->mutex);
+
+	for (i = 0; i < len; i++) {
+		temp = readl(phy_data->base + REG_PHYCTL);
+
+		/* clear the address portion */
+		temp &= ~(0xff << 8);
+
+		/* set the address */
+		temp |= ((addr + i) << 8);
+		writel(temp, phy_data->base + REG_PHYCTL);
+
+		/* set the data bit and clear usbc bit*/
+		temp = readb(phy_data->base + REG_PHYCTL);
+		if (data & 0x1)
+			temp |= BIT(7);
+		else
+			temp &= ~BIT(7);
+		temp &= ~usbc_bit;
+		writeb(temp, phy_data->base + REG_PHYCTL);
+
+		/* pulse usbc_bit */
+		temp = readb(phy_data->base + REG_PHYCTL);
+		temp |= usbc_bit;
+		writeb(temp, phy_data->base + REG_PHYCTL);
+
+		temp = readb(phy_data->base + REG_PHYCTL);
+		temp &= ~usbc_bit;
+		writeb(temp, phy_data->base + REG_PHYCTL);
+
+		data >>= 1;
+	}
+	mutex_unlock(&phy_data->mutex);
+}
+
+static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
+{
+	u32 bits, reg_value;
+
+	if (!phy->pmu)
+		return;
+
+	bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
+		SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
+
+	reg_value = readl(phy->pmu);
+
+	if (enable)
+		reg_value |= bits;
+	else
+		reg_value &= ~bits;
+
+	writel(reg_value, phy->pmu);
+}
+
+static int sun4i_usb_phy_init(struct phy *_phy)
+{
+	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+	int ret;
+
+	ret = clk_prepare_enable(data->clk);
+	if (ret)
+		return ret;
+
+	ret = reset_control_deassert(phy->reset);
+	if (ret) {
+		clk_disable_unprepare(data->clk);
+		return ret;
+	}
+
+	/* Adjust PHY's magnitude and rate */
+	sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
+
+	/* Disconnect threshold adjustment */
+	sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
+
+	sun4i_usb_phy_passby(phy, 1);
+
+	return 0;
+}
+
+static int sun4i_usb_phy_exit(struct phy *_phy)
+{
+	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+
+	sun4i_usb_phy_passby(phy, 0);
+	reset_control_assert(phy->reset);
+	clk_disable_unprepare(data->clk);
+
+	return 0;
+}
+
+static int sun4i_usb_phy_power_on(struct phy *_phy)
+{
+	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+	int ret = 0;
+
+	if (phy->vbus)
+		ret = regulator_enable(phy->vbus);
+
+	return ret;
+}
+
+static int sun4i_usb_phy_power_off(struct phy *_phy)
+{
+	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+
+	if (phy->vbus)
+		regulator_disable(phy->vbus);
+
+	return 0;
+}
+
+static struct phy_ops sun4i_usb_phy_ops = {
+	.init		= sun4i_usb_phy_init,
+	.exit		= sun4i_usb_phy_exit,
+	.power_on	= sun4i_usb_phy_power_on,
+	.power_off	= sun4i_usb_phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static struct phy *sun4i_usb_phy_xlate(struct device *dev,
+					struct of_phandle_args *args)
+{
+	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
+
+	if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
+		return ERR_PTR(-ENODEV);
+
+	return data->phys[args->args[0]].phy;
+}
+
+static int sun4i_usb_phy_probe(struct platform_device *pdev)
+{
+	struct sun4i_usb_phy_data *data;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	void __iomem *pmu = NULL;
+	struct phy_provider *phy_provider;
+	struct reset_control *reset;
+	struct regulator *vbus;
+	struct phy *phy;
+	char name[16];
+	int i;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	mutex_init(&data->mutex);
+
+	if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
+		data->num_phys = 2;
+	else
+		data->num_phys = 3;
+
+	if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy"))
+		data->disc_thresh = 3;
+	else
+		data->disc_thresh = 2;
+
+	data->clk = devm_clk_get(dev, "usb_phy");
+	if (IS_ERR(data->clk)) {
+		dev_err(dev, "could not get usb_phy clock\n");
+		return PTR_ERR(data->clk);
+	}
+
+	/* Skip 0, 0 is the phy for otg which is not yet supported. */
+	for (i = 1; i < data->num_phys; i++) {
+		snprintf(name, sizeof(name), "usb%d_vbus", i);
+		vbus = devm_regulator_get_optional(dev, name);
+		if (IS_ERR(vbus)) {
+			if (PTR_ERR(vbus) == -EPROBE_DEFER)
+				return -EPROBE_DEFER;
+			vbus = NULL;
+		}
+
+		snprintf(name, sizeof(name), "usb%d_reset", i);
+		reset = devm_reset_control_get(dev, name);
+		if (IS_ERR(phy)) {
+			dev_err(dev, "failed to get reset %s\n", name);
+			return PTR_ERR(phy);
+		}
+
+		if (i) { /* No pmu for usbc0 */
+			pmu = devm_ioremap_resource(dev,
+			      platform_get_resource(pdev, IORESOURCE_MEM, i));
+			if (IS_ERR(pmu))
+				return PTR_ERR(pmu);
+		}
+
+		phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
+		if (IS_ERR(phy)) {
+			dev_err(dev, "failed to create PHY %d\n", i);
+			return PTR_ERR(phy);
+		}
+
+		data->phys[i].phy = phy;
+		data->phys[i].pmu = pmu;
+		data->phys[i].vbus = vbus;
+		data->phys[i].reset = reset;
+		data->phys[i].index = i;
+		phy_set_drvdata(phy, &data->phys[i]);
+	}
+
+	data->base = devm_ioremap_resource(dev,
+			platform_get_resource(pdev, IORESOURCE_MEM, 0));
+	if (IS_ERR(data->base))
+		return PTR_ERR(data->base);
+
+	dev_set_drvdata(dev, data);
+	phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
+	if (IS_ERR(phy_provider))
+		return PTR_ERR(phy_provider);
+
+	return 0;
+}
+
+static const struct of_device_id sun4i_usb_phy_of_match[] = {
+	{ .compatible = "allwinner,sun4i-a10-usb-phy" },
+	{ .compatible = "allwinner,sun5i-a13-usb-phy" },
+	{ .compatible = "allwinner,sun7i-a20-usb-phy" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
+
+static struct platform_driver sun4i_usb_phy_driver = {
+	.probe	= sun4i_usb_phy_probe,
+	.driver = {
+		.of_match_table	= sun4i_usb_phy_of_match,
+		.name  = "sun4i-usb-phy",
+		.owner = THIS_MODULE,
+	}
+};
+module_platform_driver(sun4i_usb_phy_driver);
+
+MODULE_DESCRIPTION("Allwinner sun4i USB phy driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.8.4.2

^ permalink raw reply related

* [PATCH v2 13/15] regulator/mfd: max14577: Export symbols for calculating charger current
From: Mark Brown @ 2014-02-07 16:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391767487-10017-14-git-send-email-k.kozlowski@samsung.com>

On Fri, Feb 07, 2014 at 11:04:45AM +0100, Krzysztof Kozlowski wrote:
> This patch prepares for changing the max14577 charger driver to allow
> configuring battery-dependent settings from DTS.

Acked-by: Mark Brown <broonie@linaro.org>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140207/a6e45ebf/attachment.sig>

^ permalink raw reply

* [GIT PULL] at91: fixes for 3.14 #1
From: Nicolas Ferre @ 2014-02-07 16:36 UTC (permalink / raw)
  To: linux-arm-kernel

Arnd, Olof, Kevin,

This is a first "fixes" series for AT91 on 3.14.
The content is only DT-related and quite boring.
I took the opportunity of this early "fixes" pull-request to collect
some Documentation patches that were lying around and I guess that
they can be merged by arm-soc nicely (addition of CCF to 2 drivers).
A new board is added to the lot because it is dead simple and integrates
well with the new sama5d36 SoC that we added earlier in this development cycle.

Thanks, best regards,

The following changes since commit 38dbfb59d1175ef458d006556061adeaa8751b72:

  Linus 3.14-rc1 (2014-02-02 16:42:13 -0800)

are available in the git repository at:

  git://github.com/at91linux/linux-at91.git tags/at91-fixes

for you to fetch changes up to b7c2b6157079811586180d13c44a1095b57c2d47:

  ARM: at91: add Atmel's SAMA5D3 Xplained board (2014-02-07 17:20:39 +0100)

----------------------------------------------------------------
First series of AT91 fixes for 3.14.
All of them are DT-related.
- fixes for typos in i2c and ohci clocks
- addition of a USB host node for at91sam9n12ek
- 2 DT documentation updates that have been sent a long time ago
- a new board based on the sama5d36 SoC

----------------------------------------------------------------
Bo Shen (1):
      ARM: at91: enable USB host on at91sam9n12ek board

Boris BREZILLON (3):
      ARM: at91/dt: fix sama5d3 ohci hclk clock reference
      mmc: atmel-mci: document clock properties
      spi/atmel: document clock properties

Jean-Jacques Hiblot (1):
      ARM: at91/dt: sam9263: fix compatibility string for the I2C

Nicolas Ferre (1):
      ARM: at91: add Atmel's SAMA5D3 Xplained board

 .../devicetree/bindings/mmc/atmel-hsmci.txt        |   5 +
 .../devicetree/bindings/spi/spi_atmel.txt          |   5 +
 arch/arm/boot/dts/Makefile                         |   1 +
 arch/arm/boot/dts/at91-sama5d3_xplained.dts        | 229 +++++++++++++++++++++
 arch/arm/boot/dts/at91sam9263.dtsi                 |   2 +-
 arch/arm/boot/dts/at91sam9n12ek.dts                |   4 +
 arch/arm/boot/dts/sama5d3.dtsi                     |   2 +-
 7 files changed, 246 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/boot/dts/at91-sama5d3_xplained.dts

-- 
Nicolas Ferre

^ permalink raw reply

* [PATCH] dma-buf: avoid using IS_ERR_OR_NULL
From: Greg Kroah-Hartman @ 2014-02-07 16:43 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAF6AEGuQSWOw6KWVo-uorJ+8M3-kLzYHdOOfdHWUDi=SkzUUVA@mail.gmail.com>

On Sat, Dec 21, 2013 at 07:42:17AM -0500, Rob Clark wrote:
> On Fri, Dec 20, 2013 at 7:43 PM, Colin Cross <ccross@android.com> wrote:
> > dma_buf_map_attachment and dma_buf_vmap can return NULL or
> > ERR_PTR on a error.  This encourages a common buggy pattern in
> > callers:
> >         sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
> >         if (IS_ERR_OR_NULL(sgt))
> >                 return PTR_ERR(sgt);
> >
> > This causes the caller to return 0 on an error.  IS_ERR_OR_NULL
> > is almost always a sign of poorly-defined error handling.
> >
> > This patch converts dma_buf_map_attachment to always return
> > ERR_PTR, and fixes the callers that incorrectly handled NULL.
> > There are a few more callers that were not checking for NULL
> > at all, which would have dereferenced a NULL pointer later.
> > There are also a few more callers that correctly handled NULL
> > and ERR_PTR differently, I left those alone but they could also
> > be modified to delete the NULL check.
> >
> > This patch also converts dma_buf_vmap to always return NULL.
> > All the callers to dma_buf_vmap only check for NULL, and would
> > have dereferenced an ERR_PTR and panic'd if one was ever
> > returned. This is not consistent with the rest of the dma buf
> > APIs, but matches the expectations of all of the callers.
> >
> > Signed-off-by: Colin Cross <ccross@android.com>
> > ---
> >  drivers/base/dma-buf.c                         | 18 +++++++++++-------
> >  drivers/gpu/drm/drm_prime.c                    |  2 +-
> >  drivers/gpu/drm/exynos/exynos_drm_dmabuf.c     |  2 +-
> >  drivers/media/v4l2-core/videobuf2-dma-contig.c |  2 +-
> >  4 files changed, 14 insertions(+), 10 deletions(-)
> >
> > diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c
> > index 1e16cbd61da2..cfe1d8bc7bb8 100644
> > --- a/drivers/base/dma-buf.c
> > +++ b/drivers/base/dma-buf.c
> > @@ -251,9 +251,8 @@ EXPORT_SYMBOL_GPL(dma_buf_put);
> >   * @dmabuf:    [in]    buffer to attach device to.
> >   * @dev:       [in]    device to be attached.
> >   *
> > - * Returns struct dma_buf_attachment * for this attachment; may return negative
> > - * error codes.
> > - *
> > + * Returns struct dma_buf_attachment * for this attachment; returns ERR_PTR on
> > + * error.
> >   */
> >  struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
> >                                           struct device *dev)
> > @@ -319,9 +318,8 @@ EXPORT_SYMBOL_GPL(dma_buf_detach);
> >   * @attach:    [in]    attachment whose scatterlist is to be returned
> >   * @direction: [in]    direction of DMA transfer
> >   *
> > - * Returns sg_table containing the scatterlist to be returned; may return NULL
> > - * or ERR_PTR.
> > - *
> > + * Returns sg_table containing the scatterlist to be returned; returns ERR_PTR
> > + * on error.
> >   */
> >  struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
> >                                         enum dma_data_direction direction)
> > @@ -334,6 +332,8 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
> >                 return ERR_PTR(-EINVAL);
> >
> >         sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction);
> > +       if (!sg_table)
> > +               sg_table = ERR_PTR(-ENOMEM);
> >
> >         return sg_table;
> >  }
> > @@ -544,6 +544,8 @@ EXPORT_SYMBOL_GPL(dma_buf_mmap);
> >   * These calls are optional in drivers. The intended use for them
> >   * is for mapping objects linear in kernel space for high use objects.
> >   * Please attempt to use kmap/kunmap before thinking about these interfaces.
> > + *
> > + * Returns NULL on error.
> >   */
> >  void *dma_buf_vmap(struct dma_buf *dmabuf)
> >  {
> > @@ -566,7 +568,9 @@ void *dma_buf_vmap(struct dma_buf *dmabuf)
> >         BUG_ON(dmabuf->vmap_ptr);
> >
> >         ptr = dmabuf->ops->vmap(dmabuf);
> > -       if (IS_ERR_OR_NULL(ptr))
> > +       if (WARN_ON_ONCE(IS_ERR(ptr)))
> 
> since vmap is optional, the WARN_ON might be a bit strong..  although
> it would be a bit strange for an exporter to supply a vmap fxn which
> always returned NULL, not sure about that.  Just thought I'd mention
> it in case anyone else had an opinion about that.

Yeah, I don't like this, it could cause unnecessary reports of problems.

Care to fix it up?

thanks,

greg k-h

^ permalink raw reply

* [PATCH v2 2/2] Documentation: devicetree: Add boost-frequency binding to list boost mode frequency
From: Nishanth Menon @ 2014-02-07 16:43 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52F509BD.70903@arm.com>

On Fri, Feb 7, 2014 at 10:28 AM, Sudeep Holla <Sudeep.Holla@arm.com> wrote:
> On 07/02/14 16:15, Sudeep Holla wrote:
>> On 07/02/14 15:19, Thomas Abraham wrote:
>>> From: Thomas Abraham <thomas.ab@samsung.com>
>>>
>>> Add a new optional boost-frequency binding for specifying the frequencies
>>> usable in boost mode.
>>>
>>> Cc: Nishanth Menon <nm@ti.com>
>>> Cc: Lukasz Majewski <l.majewski@samsung.com>
>>> Cc: Rob Herring <robh+dt@kernel.org>
>>> Cc: Pawel Moll <pawel.moll@arm.com>
>>> Cc: Mark Rutland <mark.rutland@arm.com>
>>> Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
>>> Cc: Kumar Gala <galak@codeaurora.org>
>>> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
>>> ---
>>>  Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt |   11 +++++++++++
>>>  1 file changed, 11 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>>> new file mode 100644
>>> index 0000000..d925e38
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-boost.txt
>>> @@ -0,0 +1,11 @@
>>> +* Device tree binding for CPU boost frequency (aka over-clocking)
>>> +
>>> +Certain CPU's can be operated in optional 'boost' mode (or sometimes referred as
>>> +overclocking) in which the CPU can operate in frequencies beyond the normal
>>> +operating conditions.
>>> +
>>> +Optional Properties:
>>> +- boost-frequency: list of frequencies in KHz to be used only in boost mode.
>>> +  This list should be a subset of frequencies listed in "operating-points"
>>> +  property. Refer to Documentation/devicetree/bindings/power/opp.txt for
>>> +  details about "operating-points" property.
>>>
>>
>> Won't single entry for boost frequency suffice which would be the starting
>> frequency in the boost range. IOW will there be OPP list with frequencies:
>> A > B > C > D, but only B and C are boost frequency. That seems little odd,
>> unless it's some configuration chosen purely on software basis rather than
>> hardware. For me B marks the beginning of over-clocking.
>>
> Ah, I meant A < B < C < D in the above example.
Should'nt we let the SoC dts define that - traditionally, yes, but
consider the following:
A, B, C uses clk_parent X which describes B, C as overclocked.
and say D uses clk_parent Y which is not "over clocked", then you have
the scenario that on the first look seems counter-intutive.

Regards,
Nishanth Menon

^ permalink raw reply

* [PATCH RFC/RFT v2 6/8] ARM64: kernel: add support for cpu cache information
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391791763-28518-1-git-send-email-sudeep.holla@arm.com>

From: Sudeep Holla <sudeep.holla@arm.com>

This patch adds support for cacheinfo on ARM64.

On ARMv8, the cache hierarchy can be identified through Cache Level ID
(CLIDR) register while the cache geometry is provided by Cache Size ID
(CCSIDR) register.

Since the architecture doesn't provide any way of detecting the cpus
sharing particular cache, device tree is used for the same purpose.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: linux-arm-kernel at lists.infradead.org
---
 arch/arm64/kernel/Makefile    |   2 +-
 arch/arm64/kernel/cacheinfo.c | 134 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 135 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm64/kernel/cacheinfo.c

diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 2d4554b..7d94a51 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -9,7 +9,7 @@ AFLAGS_head.o		:= -DTEXT_OFFSET=$(TEXT_OFFSET)
 arm64-obj-y		:= cputable.o debug-monitors.o entry.o irq.o fpsimd.o	\
 			   entry-fpsimd.o process.o ptrace.o setup.o signal.o	\
 			   sys.o stacktrace.o time.o traps.o io.o vdso.o	\
-			   hyp-stub.o psci.o cpu_ops.o insn.o
+			   hyp-stub.o psci.o cpu_ops.o insn.o cacheinfo.o
 
 arm64-obj-$(CONFIG_COMPAT)		+= sys32.o kuser32.o signal32.o 	\
 					   sys_compat.o
diff --git a/arch/arm64/kernel/cacheinfo.c b/arch/arm64/kernel/cacheinfo.c
new file mode 100644
index 0000000..5d8b840
--- /dev/null
+++ b/arch/arm64/kernel/cacheinfo.c
@@ -0,0 +1,134 @@
+/*
+ *  ARM64 cacheinfo support
+ *
+ *  Copyright (C) 2014 ARM Ltd.
+ *  All Rights Reserved
+ *
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/cacheinfo.h>
+#include <linux/cpu.h>
+#include <linux/compiler.h>
+#include <linux/of.h>
+
+#include <asm/processor.h>
+
+#define MAX_CACHE_LEVEL			7	/* Max 7 level supported */
+/* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */
+#define CLIDR_CTYPE_SHIFT(level)	(3 * (level - 1))
+#define CLIDR_CTYPE_MASK(level)		(7 << CLIDR_CTYPE_SHIFT(level))
+#define CLIDR_CTYPE(clidr, level)	\
+	(((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level))
+
+static inline enum cache_type get_cache_type(int level)
+{
+	unsigned int clidr;
+	if (level > MAX_CACHE_LEVEL)
+		return CACHE_TYPE_NOCACHE;
+	asm volatile ("mrs     %0, clidr_el1" : "=r" (clidr));
+	return CLIDR_CTYPE(clidr, level);
+}
+
+/*
+ * NumSets, bits[27:13] - (Number of sets in cache) - 1
+ * Associativity, bits[12:3] - (Associativity of cache) - 1
+ * LineSize, bits[2:0] - (Log2(Number of words in cache line)) - 2
+ */
+#define CCSIDR_WRITE_THROUGH	BIT(31)
+#define CCSIDR_WRITE_BACK	BIT(30)
+#define CCSIDR_READ_ALLOCATE	BIT(29)
+#define CCSIDR_WRITE_ALLOCATE	BIT(28)
+#define CCSIDR_LINESIZE_MASK	0x7
+#define CCSIDR_ASSOCIAT_SHIFT	3
+#define CCSIDR_ASSOCIAT_MASK	0x3FF
+#define CCSIDR_NUMSETS_SHIFT	13
+#define CCSIDR_NUMSETS_MASK	0x7FF
+
+/*
+ * Which cache CCSIDR represents depends on CSSELR value
+ * Make sure no one else changes CSSELR during this
+ * smp_call_function_single prevents preemption for us
+ */
+static inline u32 get_ccsidr(u32 csselr)
+{
+	u32 ccsidr;
+
+	/* Put value into CSSELR */
+	asm volatile("msr csselr_el1, %x0" : : "r" (csselr));
+	isb();
+	/* Read result out of CCSIDR */
+	asm volatile("mrs %0, ccsidr_el1" : "=r" (ccsidr));
+
+	return ccsidr;
+}
+
+static void ci_leaf_init(struct cache_info *this_leaf,
+			 enum cache_type type, unsigned int level)
+{
+	bool is_instr_cache = type & CACHE_TYPE_INST;
+	u32 tmp = get_ccsidr((level - 1) << 1 | is_instr_cache);
+
+	this_leaf->level = level;
+	this_leaf->type = type;
+	this_leaf->coherency_line_size =
+	    (1 << ((tmp & CCSIDR_LINESIZE_MASK) + 2)) * 4;
+	this_leaf->number_of_sets =
+	    ((tmp >> CCSIDR_NUMSETS_SHIFT) & CCSIDR_NUMSETS_MASK) + 1;
+	this_leaf->ways_of_associativity =
+	    ((tmp >> CCSIDR_ASSOCIAT_SHIFT) & CCSIDR_ASSOCIAT_MASK) + 1;
+	this_leaf->size = this_leaf->number_of_sets *
+	    this_leaf->coherency_line_size * this_leaf->ways_of_associativity;
+	this_leaf->attributes =
+		((tmp & CCSIDR_WRITE_THROUGH) ? CACHE_WRITE_THROUGH : 0) |
+		((tmp & CCSIDR_WRITE_BACK) ? CACHE_WRITE_BACK : 0) |
+		((tmp & CCSIDR_READ_ALLOCATE) ? CACHE_READ_ALLOCATE : 0) |
+		((tmp & CCSIDR_WRITE_ALLOCATE) ? CACHE_WRITE_ALLOCATE : 0);
+}
+
+int init_cache_level(unsigned int cpu)
+{
+	unsigned int ctype, level = 1, leaves = 0;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+
+	if (!this_cpu_ci)
+		return -EINVAL;
+
+	do {
+		ctype = get_cache_type(level);
+		if (ctype == CACHE_TYPE_NOCACHE)
+			break;
+		/* Separate instruction and data caches */
+		leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
+	} while (++level <= MAX_CACHE_LEVEL);
+
+	this_cpu_ci->num_levels = level - 1;
+	this_cpu_ci->num_leaves = leaves;
+	return 0;
+}
+
+int populate_cache_leaves(unsigned int cpu)
+{
+	unsigned int level, idx;
+	enum cache_type type;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+
+	for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
+			idx < this_cpu_ci->num_leaves; idx++, level++) {
+		if (!this_leaf)
+			return -EINVAL;
+
+		type = get_cache_type(level);
+		if (type == CACHE_TYPE_SEPARATE) {
+			ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
+			ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level);
+		} else {
+			ci_leaf_init(this_leaf++, type, level);
+		}
+	}
+	return 0;
+}
-- 
1.8.3.2

^ permalink raw reply related

* [PATCH RFC/RFT v2 7/8] ARM: kernel: add support for cpu cache information
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391791763-28518-1-git-send-email-sudeep.holla@arm.com>

From: Sudeep Holla <sudeep.holla@arm.com>

This patch adds support for cacheinfo on ARM platforms.

On ARMv7, the cache hierarchy can be identified through Cache Level ID
register(CLIDR) while the cache geometry is provided by Cache Size ID
register(CCSIDR).

On architecture versions before ARMv7, CLIDR and CCSIDR is not
implemented. The cache type register(CTR) provides both cache hierarchy
and geometry if implemented. For implementations that doesn't support
CTR, we need to list the probable value of CTR if it was implemented
along with the cpuid for the sake of simplicity to handle them.

Since the architecture doesn't provide any way of detecting the cpus
sharing particular cache, device tree is used fo the same purpose.
On non-DT platforms, first level caches are per-cpu while higher level
caches are assumed system-wide.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Nicolas Pitre <nicolas.pitre@linaro.org>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: linux-arm-kernel at lists.infradead.org
---
 arch/arm/kernel/Makefile    |   1 +
 arch/arm/kernel/cacheinfo.c | 228 ++++++++++++++++++++++++++++++++++++++++++++
 arch/arm/mm/Kconfig         |  13 +++
 3 files changed, 242 insertions(+)
 create mode 100644 arch/arm/kernel/cacheinfo.c

diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index a30fc9b..f86a4ff 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -29,6 +29,7 @@ obj-y		+= entry-v7m.o v7m.o
 else
 obj-y		+= entry-armv.o
 endif
+obj-$(CONFIG_CPU_HAS_CACHE) += cacheinfo.o
 
 obj-$(CONFIG_OC_ETM)		+= etm.o
 obj-$(CONFIG_CPU_IDLE)		+= cpuidle.o
diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
new file mode 100644
index 0000000..10045b7
--- /dev/null
+++ b/arch/arm/kernel/cacheinfo.c
@@ -0,0 +1,228 @@
+/*
+ *  ARM cacheinfo support
+ *
+ *  Copyright (C) 2014 ARM Ltd.
+ *  All Rights Reserved
+ *
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/cacheinfo.h>
+#include <linux/cpu.h>
+#include <linux/compiler.h>
+#include <linux/of.h>
+
+#include <asm/cputype.h>
+#include <asm/processor.h>
+
+#if __LINUX_ARM_ARCH__ < 7 /* pre ARMv7 */
+
+#define MAX_CACHE_LEVEL		1	/* Only 1 level supported */
+#define CTR_CTYPE_SHIFT		24
+#define CTR_CTYPE_MASK		(1 << CTR_CTYPE_SHIFT)
+
+struct ctr_info {
+	unsigned int cpuid_id;
+	unsigned int ctr;
+};
+
+static struct ctr_info cache_ctr_list[] = {
+};
+
+static int get_unimplemented_ctr(unsigned int *ctr)
+{
+	int i, cpuid_id = read_cpuid_id();
+	for (i = 0; i < ARRAY_SIZE(cache_ctr_list); i++)
+		if (cache_ctr_list[i].cpuid_id == cpuid_id) {
+			*ctr = cache_ctr_list[i].ctr;
+			return 0;
+		}
+	return -ENOENT;
+}
+
+static unsigned int get_ctr(void)
+{
+	unsigned int ctr;
+	if (get_unimplemented_ctr(&ctr))
+		asm volatile ("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr));
+	return ctr;
+}
+
+static enum cache_type get_cache_type(int level)
+{
+	if (level > MAX_CACHE_LEVEL)
+		return CACHE_TYPE_NOCACHE;
+	return get_ctr() & CTR_CTYPE_MASK ?
+		CACHE_TYPE_SEPARATE : CACHE_TYPE_UNIFIED;
+}
+
+/*
+ *  +---------------------------------+
+ *  | 9  8  7  6 | 5  4  3 | 2 | 1  0 |
+ *  +---------------------------------+
+ *  |    size    |  assoc  | m |  len |
+ *  +---------------------------------+
+ * linelen        = 1 << (len + 3)
+ * multiplier     = 2 + m
+ * nsets          = 1 << (size + 6 - assoc - len)
+ * associativity  = multiplier << (assoc - 1)
+ * cache_size     = multiplier << (size + 8)
+ */
+#define CTR_LINESIZE_MASK	0x3
+#define CTR_MULTIPLIER_SHIFT	2
+#define CTR_MULTIPLIER_MASK	0x1
+#define CTR_ASSOCIAT_SHIFT	3
+#define CTR_ASSOCIAT_MASK	0x7
+#define CTR_SIZE_SHIFT		6
+#define CTR_SIZE_MASK		0xF
+#define CTR_DCACHE_SHIFT	12
+
+static void __ci_leaf_init(enum cache_type type,
+				  struct cache_info *this_leaf)
+{
+	unsigned int size, multiplier, assoc, len, tmp = get_ctr();
+
+	if (type == CACHE_TYPE_DATA)
+		tmp >>= CTR_DCACHE_SHIFT;
+
+	len = tmp & CTR_LINESIZE_MASK;
+	size = (tmp >> CTR_SIZE_SHIFT) & CTR_SIZE_MASK;
+	assoc = (tmp >> CTR_ASSOCIAT_SHIFT) & CTR_ASSOCIAT_MASK;
+	multiplier = ((tmp >> CTR_MULTIPLIER_SHIFT) & CTR_MULTIPLIER_MASK) + 2;
+
+	this_leaf->type = type;
+	this_leaf->coherency_line_size = 1 << (len + 3);
+	this_leaf->number_of_sets = 1 << (size + 6 - assoc - len);
+	this_leaf->ways_of_associativity = multiplier << (assoc - 1);
+	this_leaf->size = multiplier << (size + 8);
+}
+
+#else /* ARMv7 */
+
+#define MAX_CACHE_LEVEL			7	/* Max 7 level supported */
+/* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */
+#define CLIDR_CTYPE_SHIFT(level)	(3 * (level - 1))
+#define CLIDR_CTYPE_MASK(level)		(7 << CLIDR_CTYPE_SHIFT(level))
+#define CLIDR_CTYPE(clidr, level)	\
+	(((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level))
+
+static inline enum cache_type get_cache_type(int level)
+{
+	unsigned int clidr;
+	if (level > MAX_CACHE_LEVEL)
+		return CACHE_TYPE_NOCACHE;
+	asm volatile ("mrc p15, 1, %0, c0, c0, 1" : "=r" (clidr));
+	return CLIDR_CTYPE(clidr, level);
+}
+
+/*
+ * NumSets, bits[27:13] - (Number of sets in cache) - 1
+ * Associativity, bits[12:3] - (Associativity of cache) - 1
+ * LineSize, bits[2:0] - (Log2(Number of words in cache line)) - 2
+ */
+#define CCSIDR_WRITE_THROUGH	BIT(31)
+#define CCSIDR_WRITE_BACK	BIT(30)
+#define CCSIDR_READ_ALLOCATE	BIT(29)
+#define CCSIDR_WRITE_ALLOCATE	BIT(28)
+#define CCSIDR_LINESIZE_MASK	0x7
+#define CCSIDR_ASSOCIAT_SHIFT	3
+#define CCSIDR_ASSOCIAT_MASK	0x3FF
+#define CCSIDR_NUMSETS_SHIFT	13
+#define CCSIDR_NUMSETS_MASK	0x7FF
+
+/*
+ * Which cache CCSIDR represents depends on CSSELR value
+ * Make sure no one else changes CSSELR during this
+ * smp_call_function_single prevents preemption for us
+ */
+static inline u32 get_ccsidr(u32 csselr)
+{
+	u32 ccsidr;
+
+	/* Put value into CSSELR */
+	asm volatile ("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr));
+	isb();
+	/* Read result out of CCSIDR */
+	asm volatile ("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr));
+
+	return ccsidr;
+}
+
+static void __ci_leaf_init(enum cache_type type,
+				  struct cache_info *this_leaf)
+{
+	bool is_instr_cache = type & CACHE_TYPE_INST;
+	u32 tmp = get_ccsidr((this_leaf->level - 1) << 1 | is_instr_cache);
+
+	this_leaf->type = type;
+	this_leaf->coherency_line_size =
+	    (1 << ((tmp & CCSIDR_LINESIZE_MASK) + 2)) * 4;
+	this_leaf->number_of_sets =
+	    ((tmp >> CCSIDR_NUMSETS_SHIFT) & CCSIDR_NUMSETS_MASK) + 1;
+	this_leaf->ways_of_associativity =
+	    ((tmp >> CCSIDR_ASSOCIAT_SHIFT) & CCSIDR_ASSOCIAT_MASK) + 1;
+	this_leaf->size = this_leaf->number_of_sets *
+	    this_leaf->coherency_line_size * this_leaf->ways_of_associativity;
+	this_leaf->attributes =
+		((tmp & CCSIDR_WRITE_THROUGH) ? CACHE_WRITE_THROUGH : 0) |
+		((tmp & CCSIDR_WRITE_BACK) ? CACHE_WRITE_BACK : 0) |
+		((tmp & CCSIDR_READ_ALLOCATE) ? CACHE_READ_ALLOCATE : 0) |
+		((tmp & CCSIDR_WRITE_ALLOCATE) ? CACHE_WRITE_ALLOCATE : 0);
+}
+
+#endif
+
+static void ci_leaf_init(struct cache_info *this_leaf,
+			 enum cache_type type, unsigned int level)
+{
+	this_leaf->level = level;
+	__ci_leaf_init(type, this_leaf);
+}
+
+int init_cache_level(unsigned int cpu)
+{
+	unsigned int ctype, level = 1, leaves = 0;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+
+	if (!this_cpu_ci)
+		return -EINVAL;
+
+	do {
+		ctype = get_cache_type(level);
+		if (ctype == CACHE_TYPE_NOCACHE)
+			break;
+		/* Separate instruction and data caches */
+		leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
+	} while (++level <= MAX_CACHE_LEVEL);
+
+	this_cpu_ci->num_levels = level - 1;
+	this_cpu_ci->num_leaves = leaves;
+
+	return 0;
+}
+
+int populate_cache_leaves(unsigned int cpu)
+{
+	unsigned int level, idx;
+	enum cache_type type;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cache_info *this_leaf = this_cpu_ci->info_list;
+
+	for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
+			idx < this_cpu_ci->num_leaves; idx++, level++) {
+		if (!this_leaf)
+			return -EINVAL;
+
+		type = get_cache_type(level);
+		if (type == CACHE_TYPE_SEPARATE) {
+			ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
+			ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level);
+		} else {
+			ci_leaf_init(this_leaf++, type, level);
+		}
+	}
+	return 0;
+}
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 1f8fed9..c4abb89 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -495,30 +495,42 @@ config CPU_PABRT_V7
 # The cache model
 config CPU_CACHE_V4
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_V4WT
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_V4WB
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_V6
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_V7
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_NOP
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_VIVT
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_VIPT
 	bool
+	select CPU_HAS_CACHE
 
 config CPU_CACHE_FA
 	bool
+	select CPU_HAS_CACHE
+
+config CPU_HAS_CACHE
+	bool
 
 if MMU
 # The copy-page model
@@ -846,6 +858,7 @@ config DMA_CACHE_RWFO
 
 config OUTER_CACHE
 	bool
+	select CPU_HAS_CACHE
 
 config OUTER_CACHE_SYNC
 	bool
-- 
1.8.3.2

^ permalink raw reply related

* [PATCH RFC/RFT v2 8/8] ARM: kernel: add outer cache support for cacheinfo implementation
From: Sudeep Holla @ 2014-02-07 16:49 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391791763-28518-1-git-send-email-sudeep.holla@arm.com>

From: Sudeep Holla <sudeep.holla@arm.com>

In order to support outer cache in the cacheinfo infrastructure, a new
function 'get_info' is added to outer_cache_fns. This function is used
to get the outer cache information namely: line size, number of ways of
associativity and number of sets.

This patch adds 'get_info' supports to all L2 cache implementations on
ARM except Marvell's Feroceon L2 cache.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: linux-arm-kernel at lists.infradead.org
---
 arch/arm/include/asm/outercache.h | 13 +++++++++++++
 arch/arm/kernel/cacheinfo.c       | 22 +++++++++++++++++++++-
 arch/arm/mm/cache-l2x0.c          | 14 ++++++++++++++
 arch/arm/mm/cache-tauros2.c       | 35 +++++++++++++++++++++++++++++++++++
 arch/arm/mm/cache-xsc3l2.c        | 15 +++++++++++++++
 5 files changed, 98 insertions(+), 1 deletion(-)

diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h
index f94784f..1471ff2 100644
--- a/arch/arm/include/asm/outercache.h
+++ b/arch/arm/include/asm/outercache.h
@@ -23,7 +23,14 @@
 
 #include <linux/types.h>
 
+struct outer_cache_info {
+	unsigned int num_ways;
+	unsigned int num_sets;
+	unsigned int line_size;
+};
+
 struct outer_cache_fns {
+	void (*get_info)(struct outer_cache_info *info);
 	void (*inv_range)(unsigned long, unsigned long);
 	void (*clean_range)(unsigned long, unsigned long);
 	void (*flush_range)(unsigned long, unsigned long);
@@ -41,6 +48,11 @@ extern struct outer_cache_fns outer_cache;
 
 #ifdef CONFIG_OUTER_CACHE
 
+static inline void outer_get_info(struct outer_cache_info *info)
+{
+	if (outer_cache.get_info)
+		outer_cache.get_info(info);
+}
 static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
 {
 	if (outer_cache.inv_range)
@@ -83,6 +95,7 @@ static inline void outer_resume(void)
 
 #else
 
+static inline void outer_get_info(struct outer_cache_info *info) { }
 static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
 { }
 static inline void outer_clean_range(phys_addr_t start, phys_addr_t end)
diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
index 10045b7..4463833 100644
--- a/arch/arm/kernel/cacheinfo.c
+++ b/arch/arm/kernel/cacheinfo.c
@@ -16,6 +16,7 @@
 #include <linux/of.h>
 
 #include <asm/cputype.h>
+#include <asm/outercache.h>
 #include <asm/processor.h>
 
 #if __LINUX_ARM_ARCH__ < 7 /* pre ARMv7 */
@@ -175,11 +176,27 @@ static void __ci_leaf_init(enum cache_type type,
 
 #endif
 
+static void __outer_ci_leaf_init(struct cache_info *this_leaf)
+{
+	struct outer_cache_info info;
+
+	outer_get_info(&info);
+
+	this_leaf->type = CACHE_TYPE_UNIFIED;/* record it as Unified */
+	this_leaf->ways_of_associativity = info.num_ways;
+	this_leaf->number_of_sets = info.num_sets;
+	this_leaf->coherency_line_size = info.line_size;
+	this_leaf->size = info.num_ways * info.num_sets * info.line_size;
+}
+
 static void ci_leaf_init(struct cache_info *this_leaf,
 			 enum cache_type type, unsigned int level)
 {
 	this_leaf->level = level;
-	__ci_leaf_init(type, this_leaf);
+	if (type == CACHE_TYPE_NOCACHE)	/* must be outer cache */
+		__outer_ci_leaf_init(this_leaf);
+	else
+		__ci_leaf_init(type, this_leaf);
 }
 
 int init_cache_level(unsigned int cpu)
@@ -201,6 +218,9 @@ int init_cache_level(unsigned int cpu)
 	this_cpu_ci->num_levels = level - 1;
 	this_cpu_ci->num_leaves = leaves;
 
+	if (IS_ENABLED(CONFIG_OUTER_CACHE) && outer_cache.get_info)
+		this_cpu_ci->num_leaves++, this_cpu_ci->num_levels++;
+
 	return 0;
 }
 
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index 7abde2ce..b850771 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -183,6 +183,15 @@ static void l2x0_inv_all(void)
 	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
+static void l2x0_getinfo(struct outer_cache_info *info)
+{
+	if (!info)
+		return;
+	info->num_ways = get_count_order(l2x0_way_mask);
+	info->line_size = CACHE_LINE_SIZE;
+	info->num_sets = l2x0_size / (info->num_ways * CACHE_LINE_SIZE);
+}
+
 static void l2x0_inv_range(unsigned long start, unsigned long end)
 {
 	void __iomem *base = l2x0_base;
@@ -416,6 +425,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
 		outer_cache.flush_all = l2x0_flush_all;
 		outer_cache.inv_all = l2x0_inv_all;
 		outer_cache.disable = l2x0_disable;
+		outer_cache.get_info = l2x0_getinfo;
 	}
 
 	pr_info("%s cache controller enabled\n", type);
@@ -886,6 +896,7 @@ static const struct l2x0_of_data pl310_data = {
 		.flush_all   = l2x0_flush_all,
 		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
+		.get_info    = l2x0_getinfo,
 	},
 };
 
@@ -901,6 +912,7 @@ static const struct l2x0_of_data l2x0_data = {
 		.flush_all   = l2x0_flush_all,
 		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
+		.get_info    = l2x0_getinfo,
 	},
 };
 
@@ -916,6 +928,7 @@ static const struct l2x0_of_data aurora_with_outer_data = {
 		.flush_all   = l2x0_flush_all,
 		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
+		.get_info    = l2x0_getinfo,
 	},
 };
 
@@ -948,6 +961,7 @@ static const struct l2x0_of_data bcm_l2x0_data = {
 		.flush_all   = l2x0_flush_all,
 		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
+		.get_info    = l2x0_getinfo,
 	},
 };
 
diff --git a/arch/arm/mm/cache-tauros2.c b/arch/arm/mm/cache-tauros2.c
index 1be0f4e..3a43401 100644
--- a/arch/arm/mm/cache-tauros2.c
+++ b/arch/arm/mm/cache-tauros2.c
@@ -60,6 +60,7 @@ static inline void tauros2_inv_pa(unsigned long addr)
  * noninclusive.
  */
 #define CACHE_LINE_SIZE		32
+#define CACHE_LINE_SHIFT	5
 
 static void tauros2_inv_range(unsigned long start, unsigned long end)
 {
@@ -131,6 +132,38 @@ static void tauros2_resume(void)
 	"mcr	p15, 0, %0, c1, c0, 0 @Enable L2 Cache\n\t"
 	: : "r" (0x0));
 }
+
+/*
+ *  +----------------------------------------+
+ *  | 11 10 9  8 | 7  6  5  4  3 | 2 |  1  0 |
+ *  +----------------------------------------+
+ *  |  way size  | associativity | - |line_sz|
+ *  +----------------------------------------+
+ */
+#define L2CTR_ASSOCIAT_SHIFT	3
+#define L2CTR_ASSOCIAT_MASK	0x1F
+#define L2CTR_WAYSIZE_SHIFT	8
+#define L2CTR_WAYSIZE_MASK	0xF
+#define CACHE_WAY_PER_SET(l2ctr)	\
+	(((l2_ctr) >> L2CTR_ASSOCIAT_SHIFT) & L2CTR_ASSOCIAT_MASK)
+#define CACHE_WAY_SIZE(l2ctr)		\
+	(8192 << (((l2ctr) >> L2CTR_WAYSIZE_SHIFT) & L2CTR_WAYSIZE_MASK))
+#define CACHE_SET_SIZE(l2ctr)	(CACHE_WAY_SIZE(l2ctr) >> CACHE_LINE_SHIFT)
+
+static void tauros2_getinfo(struct outer_cache_info *info)
+{
+	unsigned int l2_ctr;
+
+	if (!info)
+		return;
+
+	__asm__("mrc p15, 1, %0, c0, c0, 1" : "=r" (l2_ctr));
+
+	info->line_size = CACHE_LINE_SIZE;
+	info->num_ways = CACHE_WAY_PER_SET(l2_ctr);
+	info->num_sets = CACHE_SET_SIZE(l2_ctr);
+}
+
 #endif
 
 static inline u32 __init read_extra_features(void)
@@ -226,6 +259,7 @@ static void __init tauros2_internal_init(unsigned int features)
 		outer_cache.flush_range = tauros2_flush_range;
 		outer_cache.disable = tauros2_disable;
 		outer_cache.resume = tauros2_resume;
+		outer_cache.get_info = tauros2_getinfo;
 	}
 #endif
 
@@ -253,6 +287,7 @@ static void __init tauros2_internal_init(unsigned int features)
 		outer_cache.flush_range = tauros2_flush_range;
 		outer_cache.disable = tauros2_disable;
 		outer_cache.resume = tauros2_resume;
+		outer_cache.get_info = tauros2_getinfo;
 	}
 #endif
 
diff --git a/arch/arm/mm/cache-xsc3l2.c b/arch/arm/mm/cache-xsc3l2.c
index 6c3edeb..353c642 100644
--- a/arch/arm/mm/cache-xsc3l2.c
+++ b/arch/arm/mm/cache-xsc3l2.c
@@ -201,6 +201,20 @@ static void xsc3_l2_flush_range(unsigned long start, unsigned long end)
 	dsb();
 }
 
+static void xsc3_l2_getinfo(struct outer_cache_info *info)
+{
+	unsigned long l2ctype;
+
+	if (!info)
+		return;
+
+	__asm__("mrc p15, 1, %0, c0, c0, 1" : "=r" (l2ctype));
+
+	info->num_ways = CACHE_WAY_PER_SET;
+	info->line_size = CACHE_LINE_SIZE;
+	info->num_sets = CACHE_SET_SIZE(l2ctype);
+}
+
 static int __init xsc3_l2_init(void)
 {
 	if (!cpu_is_xsc3() || !xsc3_l2_present())
@@ -213,6 +227,7 @@ static int __init xsc3_l2_init(void)
 		outer_cache.inv_range = xsc3_l2_inv_range;
 		outer_cache.clean_range = xsc3_l2_clean_range;
 		outer_cache.flush_range = xsc3_l2_flush_range;
+		outer_cache.get_info    = xsc3_l2_getinfo;
 	}
 
 	return 0;
-- 
1.8.3.2

^ permalink raw reply related

* [PATCH RFC 2/2] drivers/base: declare phandle DT nodes as components
From: Jean-Francois Moine @ 2014-02-07 16:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1391793068.git.moinejf@free.fr>

At system startup time, some devices depends on the availability of
some other devices before starting. The infrastructure for componentised
subsystems permits to handle this dependence, each driver defining
its own role.

This patch does an automatic creation of the lowest components in
case of DT. This permits simple devices to be part of complex
componentised subsystems without any specific code.

When created from DT, the device dependence is generally indicated by
phandle: a device which is pointed to by a phandle must be started
before the pointing device(s).

So, at device register time, the devices which are pointed to by a
phandle are declared as components, except when they declared
themselves as such in their probe function.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 drivers/base/component.c | 12 ++++++++++++
 drivers/base/core.c      | 18 ++++++++++++++++++
 include/linux/of.h       |  2 ++
 3 files changed, 32 insertions(+)

diff --git a/drivers/base/component.c b/drivers/base/component.c
index 0a39d7a..3cab26b 100644
--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 struct master {
 	struct list_head node;
@@ -336,6 +337,7 @@ EXPORT_SYMBOL_GPL(component_bind_all);
 int component_add(struct device *dev, const struct component_ops *ops)
 {
 	struct component *component;
+	struct device_node *np;
 	int ret;
 
 	component = kzalloc(sizeof(*component), GFP_KERNEL);
@@ -356,6 +358,11 @@ int component_add(struct device *dev, const struct component_ops *ops)
 
 		kfree(component);
 	}
+
+	np = dev->of_node;
+	if (np)
+		np->_flags |= OF_DEV_COMPONENT;
+
 	mutex_unlock(&component_mutex);
 
 	return ret < 0 ? ret : 0;
@@ -365,6 +372,7 @@ EXPORT_SYMBOL_GPL(component_add);
 void component_del(struct device *dev, const struct component_ops *ops)
 {
 	struct component *c, *component = NULL;
+	struct device_node *np;
 
 	mutex_lock(&component_mutex);
 	list_for_each_entry(c, &component_list, node)
@@ -377,6 +385,10 @@ void component_del(struct device *dev, const struct component_ops *ops)
 	if (component && component->master)
 		take_down_master(component->master);
 
+	np = dev->of_node;
+	if (np)
+		np->_flags &= ~OF_DEV_COMPONENT;
+
 	mutex_unlock(&component_mutex);
 
 	WARN_ON(!component);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 2b56717..0517b91 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -27,6 +27,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/netdevice.h>
 #include <linux/sysfs.h>
+#include <linux/component.h>
 
 #include "base.h"
 #include "power/power.h"
@@ -980,6 +981,7 @@ int device_add(struct device *dev)
 	struct device *parent = NULL;
 	struct kobject *kobj;
 	struct class_interface *class_intf;
+	struct device_node *np;
 	int error = -EINVAL;
 
 	dev = get_device(dev);
@@ -1088,6 +1090,15 @@ int device_add(struct device *dev)
 				class_intf->add_dev(dev, class_intf);
 		mutex_unlock(&dev->class->p->mutex);
 	}
+
+	/* if DT-created device referenced by phandle, create a component */
+	np = dev->of_node;
+	if (np && np->phandle &&
+	    !(np->_flags & OF_DEV_COMPONENT)) {
+		component_add(dev, NULL);
+		np->_flags |= OF_DEV_IMPLICIT_COMPONENT;
+	}
+
 done:
 	put_device(dev);
 	return error;
@@ -1189,10 +1200,17 @@ void device_del(struct device *dev)
 {
 	struct device *parent = dev->parent;
 	struct class_interface *class_intf;
+	struct device_node *np;
 
 	/* Notify clients of device removal.  This call must come
 	 * before dpm_sysfs_remove().
 	 */
+	np = dev->of_node;
+	if (np && (np->_flags & OF_DEV_COMPONENT)) {
+		component_del(dev, NULL);
+		np->_flags &= ~OF_DEV_IMPLICIT_COMPONENT;
+	}
+
 	if (dev->bus)
 		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 					     BUS_NOTIFY_DEL_DEVICE, dev);
diff --git a/include/linux/of.h b/include/linux/of.h
index 70c64ba..40f1c34 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -59,6 +59,8 @@ struct device_node {
 	struct	proc_dir_entry *pde;	/* this node's proc directory */
 	struct	kref kref;
 	unsigned long _flags;
+#define OF_DEV_COMPONENT 1
+#define OF_DEV_IMPLICIT_COMPONENT 2
 	void	*data;
 #if defined(CONFIG_SPARC)
 	const char *path_component_name;
-- 
1.9.rc1

^ permalink raw reply related

* [PATCH v3 2/2] drivers/base: declare phandle DT nodes as components
From: Jean-Francois Moine @ 2014-02-07 16:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1391792986.git.moinejf@free.fr>

At system startup time, some devices depends on the availability of
some other devices before starting. The infrastructure for componentised
subsystems permits to handle this dependence, each driver defining
its own role.

This patch does an automatic creation of the lowest components in
case of DT. This permits simple devices to be part of complex
componentised subsystems without any specific code.

When created from DT, the device dependence is generally indicated by
phandle: a device which is pointed to by a phandle must be started
before the pointing device(s).

So, at device register time, the devices which are pointed to by a
phandle are declared as components, except when they declared
themselves as such in their probe function.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 drivers/base/component.c | 12 ++++++++++++
 drivers/base/core.c      | 18 ++++++++++++++++++
 include/linux/of.h       |  2 ++
 3 files changed, 32 insertions(+)

diff --git a/drivers/base/component.c b/drivers/base/component.c
index 0a39d7a..3cab26b 100644
--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 struct master {
 	struct list_head node;
@@ -336,6 +337,7 @@ EXPORT_SYMBOL_GPL(component_bind_all);
 int component_add(struct device *dev, const struct component_ops *ops)
 {
 	struct component *component;
+	struct device_node *np;
 	int ret;
 
 	component = kzalloc(sizeof(*component), GFP_KERNEL);
@@ -356,6 +358,11 @@ int component_add(struct device *dev, const struct component_ops *ops)
 
 		kfree(component);
 	}
+
+	np = dev->of_node;
+	if (np)
+		np->_flags |= OF_DEV_COMPONENT;
+
 	mutex_unlock(&component_mutex);
 
 	return ret < 0 ? ret : 0;
@@ -365,6 +372,7 @@ EXPORT_SYMBOL_GPL(component_add);
 void component_del(struct device *dev, const struct component_ops *ops)
 {
 	struct component *c, *component = NULL;
+	struct device_node *np;
 
 	mutex_lock(&component_mutex);
 	list_for_each_entry(c, &component_list, node)
@@ -377,6 +385,10 @@ void component_del(struct device *dev, const struct component_ops *ops)
 	if (component && component->master)
 		take_down_master(component->master);
 
+	np = dev->of_node;
+	if (np)
+		np->_flags &= ~OF_DEV_COMPONENT;
+
 	mutex_unlock(&component_mutex);
 
 	WARN_ON(!component);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 2b56717..0517b91 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -27,6 +27,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/netdevice.h>
 #include <linux/sysfs.h>
+#include <linux/component.h>
 
 #include "base.h"
 #include "power/power.h"
@@ -980,6 +981,7 @@ int device_add(struct device *dev)
 	struct device *parent = NULL;
 	struct kobject *kobj;
 	struct class_interface *class_intf;
+	struct device_node *np;
 	int error = -EINVAL;
 
 	dev = get_device(dev);
@@ -1088,6 +1090,15 @@ int device_add(struct device *dev)
 				class_intf->add_dev(dev, class_intf);
 		mutex_unlock(&dev->class->p->mutex);
 	}
+
+	/* if DT-created device referenced by phandle, create a component */
+	np = dev->of_node;
+	if (np && np->phandle &&
+	    !(np->_flags & OF_DEV_COMPONENT)) {
+		component_add(dev, NULL);
+		np->_flags |= OF_DEV_IMPLICIT_COMPONENT;
+	}
+
 done:
 	put_device(dev);
 	return error;
@@ -1189,10 +1200,17 @@ void device_del(struct device *dev)
 {
 	struct device *parent = dev->parent;
 	struct class_interface *class_intf;
+	struct device_node *np;
 
 	/* Notify clients of device removal.  This call must come
 	 * before dpm_sysfs_remove().
 	 */
+	np = dev->of_node;
+	if (np && (np->_flags & OF_DEV_COMPONENT)) {
+		component_del(dev, NULL);
+		np->_flags &= ~OF_DEV_IMPLICIT_COMPONENT;
+	}
+
 	if (dev->bus)
 		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 					     BUS_NOTIFY_DEL_DEVICE, dev);
diff --git a/include/linux/of.h b/include/linux/of.h
index 70c64ba..40f1c34 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -59,6 +59,8 @@ struct device_node {
 	struct	proc_dir_entry *pde;	/* this node's proc directory */
 	struct	kref kref;
 	unsigned long _flags;
+#define OF_DEV_COMPONENT 1
+#define OF_DEV_IMPLICIT_COMPONENT 2
 	void	*data;
 #if defined(CONFIG_SPARC)
 	const char *path_component_name;
-- 
1.9.rc1

^ permalink raw reply related

* [PATCH v6 05/19] watchdog: orion: Make sure the watchdog is initially stopped
From: Guenter Roeck @ 2014-02-07 16:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20140207154453.GA8533@titan.lakedaemon.net>

On Fri, Feb 07, 2014 at 10:44:53AM -0500, Jason Cooper wrote:
> On Fri, Feb 07, 2014 at 12:17:28PM -0300, Ezequiel Garcia wrote:
> > On Fri, Feb 07, 2014 at 05:38:09AM -0800, Guenter Roeck wrote:
> > > On 02/07/2014 02:40 AM, Ezequiel Garcia wrote:
> > > > On Thu, Feb 06, 2014 at 06:02:56PM -0800, Guenter Roeck wrote:
> > > >> On 02/06/2014 09:20 AM, Ezequiel Garcia wrote:
> > > >>> Having the watchdog initially fully stopped is important to avoid
> > > >>> any spurious watchdog triggers, in case the registers are not in
> > > >>> its reset state.
> > > >>>
> > > >>> Reviewed-by: Guenter Roeck <linux@roeck-us.net>
> > > >>> Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> > > >>> Tested-by: Willy Tarreau <w@1wt.eu>
> > > >>> Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
> > > >>> ---
> > > >>>    drivers/watchdog/orion_wdt.c | 3 +++
> > > >>>    1 file changed, 3 insertions(+)
> > > >>>
> > > >>> diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
> > > >>> index 6746033..2dbeee9 100644
> > > >>> --- a/drivers/watchdog/orion_wdt.c
> > > >>> +++ b/drivers/watchdog/orion_wdt.c
> > > >>> @@ -142,6 +142,9 @@ static int orion_wdt_probe(struct platform_device *pdev)
> > > >>>    	orion_wdt.max_timeout = wdt_max_duration;
> > > >>>    	watchdog_init_timeout(&orion_wdt, heartbeat, &pdev->dev);
> > > >>>
> > > >>> +	/* Let's make sure the watchdog is fully stopped */
> > > >>> +	orion_wdt_stop(&orion_wdt);
> > > >>> +
> > > >>
> > > >> Actually we just had that in another driver, and I stumbled over it there.
> > > >>
> > > >> Problem with stopping the watchdog in probe unconditionally is that you can
> > > >> use it to defeat nowayout: unload the module, then load it again,
> > > >> and the watchdog is stopped even if nowayout is true.
> > > >>
> 
> How often would a user legitimately want to unload/load the watchdog
> module?
> 
Not sure what you are saying here. If you don't think the nowayout option
should be supported, drop it. Don't claim it is supported when it isn't.

> > > >
> > > > Hm... I see.
> > > >
> > > >> Is this really what you want ? Or, in other words, what is the problem
> > > >> you are trying to solve ?
> > > >>
> > > >
> > > > Well, this is related to the discussion about the bootloader not
> > > > reseting the watchdog properly, provoking spurious watchdog triggering.
> > > >
> > > > Jason Gunthorpe explained [1] that we needed a particular sequence:
> > > >
> > > >   1. Disable WDT
> > > >   2. Clear bridge
> > > >   3. Enable WDT
> > > >
> > > > We added the irq handling to satisfy (2), and the watchdog stop for (1).
> > > >
> > > > The watchdog stop was agreed specifically [2].
> > > >
> > > > Ideas?
> > > >
> > > 
> > > Other drivers assume that if the watchdog is running, it is supposed
> > > to be running. The more common approach in such cases is to ping the
> > > watchdog once to give userspace more time to get ready, but leave
> > > it enabled. So you could check if the watchdog is enabled, and if
> > > it was enabled re-enable it after initialization is complete
> > > (and maybe log a message stating that the watchdog is enabled).
> > > 
> > > If you don't want to do that, and if you are defeating nowayout
> > > on purpose to fix a problem with a broken bootloader,
> > > you should at least put in comment describing the problem you are
> > > trying to solve, and that you accept breaking nowayout with your fix.
> 
> Yes, this should be commented.
> 
> > I'm not fond of not having "nowayout" option on our driver, given I'm sure
> > it's a watchdog feature for a good reason.
> > 
> > On the other side, I can't see how can we distinguish a previously
> > and explicitly enabled watchdog, from a spurious enable by broken bootloader.
> 
> How about we just don't define module_exit() and leave a comment as
> such?  It's not unprecedented, a couple of the atm drivers are
> explicitly setup like this (uPD98402.c, zatm.c, eni.c).
> 
Or don't support tristate in the first place. There was some argument from
others earlier that a watchdog should never be optional. Or drop the nowayout
option from the driver.

There is one problem, though, if you don't support a pre-enabled watchdog:
If the system dies before the watchdog application starts running, it will
hang. This is the reason why the watchdog is enabled on purpose by some
boot loaders.

Guenter

^ permalink raw reply

* [RFC PATCH] ARM: Add imprecise abort enable/disable macro
From: Will Deacon @ 2014-02-07 17:09 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391789955-26927-2-git-send-email-fabrice.gasnier@st.com>

On Fri, Feb 07, 2014 at 04:19:15PM +0000, Fabrice GASNIER wrote:
> This patch adds imprecise abort enable/disable macros.
> It also enables imprecise aborts when starting kernel.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> ---
>  arch/arm/include/asm/irqflags.h |   33 +++++++++++++++++++++++++++++++++
>  arch/arm/kernel/smp.c           |    1 +
>  arch/arm/kernel/traps.c         |    4 ++++
>  3 files changed, 38 insertions(+)
> 
> diff --git a/arch/arm/include/asm/irqflags.h b/arch/arm/include/asm/irqflags.h
> index 3b763d6..82e3834 100644
> --- a/arch/arm/include/asm/irqflags.h
> +++ b/arch/arm/include/asm/irqflags.h
> @@ -51,6 +51,9 @@ static inline void arch_local_irq_disable(void)
>  
>  #define local_fiq_enable()  __asm__("cpsie f	@ __stf" : : : "memory", "cc")
>  #define local_fiq_disable() __asm__("cpsid f	@ __clf" : : : "memory", "cc")
> +
> +#define local_abt_enable()  __asm__("cpsie a	@ __sta" : : : "memory", "cc")
> +#define local_abt_disable() __asm__("cpsid a	@ __cla" : : : "memory", "cc")
>  #else
>  
>  /*
> @@ -130,6 +133,36 @@ static inline void arch_local_irq_disable(void)
>  	: "memory", "cc");					\
>  	})
>  
> +/*
> + * Enable Aborts
> + */
> +#define local_abt_enable()					\
> +	({							\
> +		unsigned long temp;				\
> +	__asm__ __volatile__(					\
> +	"mrs	%0, cpsr		@ sta\n"		\
> +"	bic	%0, %0, %1\n"					\
> +"	msr	cpsr_c, %0"					\
> +	: "=r" (temp)						\
> +	: "r" (PSR_A_BIT)					\

Can you use "i" instead of a register for this constant?

> +	: "memory", "cc");					\

You don't need the "cc" clobber.

> +	})
> +
> +/*
> + * Disable Aborts
> + */
> +#define local_abt_disable()					\
> +	({							\
> +		unsigned long temp;				\
> +	__asm__ __volatile__(					\
> +	"mrs	%0, cpsr		@ cla\n"		\
> +"	orr	%0, %0, %1\n"					\
> +"	msr	cpsr_c, %0"					\
> +	: "=r" (temp)						\
> +	: "r" (PSR_A_BIT)					\
> +	: "memory", "cc");					\
> +	})

Same comments here.

>  #endif
>  
>  /*
> diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
> index dc894ab..c2093cb 100644
> --- a/arch/arm/kernel/smp.c
> +++ b/arch/arm/kernel/smp.c
> @@ -377,6 +377,7 @@ asmlinkage void secondary_start_kernel(void)
>  
>  	local_irq_enable();
>  	local_fiq_enable();
> +	local_abt_enable();
>  
>  	/*
>  	 * OK, it's off to the idle thread for us
> diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
> index 4636d56..ef15709 100644
> --- a/arch/arm/kernel/traps.c
> +++ b/arch/arm/kernel/traps.c
> @@ -900,6 +900,10 @@ void __init early_trap_init(void *vectors_base)
>  
>  	flush_icache_range(vectors, vectors + PAGE_SIZE * 2);
>  	modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
> +
> +	/* Enable imprecise aborts */
> +	local_abt_enable();

Surely we want to enable this as early as possible? Now, putting this into
head.S is ugly, as it duplicating it across all the proc*.S files, so why
not setup_arch?

Will

^ permalink raw reply

* [PATCH v3 0/2] *** SUBJECT HERE ***
From: Jean-Francois Moine @ 2014-02-07 17:09 UTC (permalink / raw)
  To: linux-arm-kernel

*** BLURB HERE ***

Jean-Francois Moine (2):
  drivers/base: permit base components to omit the bind/unbind ops
  drivers/base: declare phandle DT nodes as components

 drivers/base/component.c | 21 +++++++++++++++++++--
 drivers/base/core.c      | 18 ++++++++++++++++++
 include/linux/of.h       |  2 ++
 3 files changed, 39 insertions(+), 2 deletions(-)

-- 
1.9.rc1

^ permalink raw reply

* [PATCH v3 05/11] ARM: LPAE: provide an IPA capable pmd_addr_end
From: Christoffer Dall @ 2014-02-07 17:10 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20140207154401.GC5925@arm.com>

On Fri, Feb 07, 2014 at 03:44:01PM +0000, Catalin Marinas wrote:
> On Fri, Feb 07, 2014 at 04:04:56AM +0000, Christoffer Dall wrote:
> > On Thu, Feb 06, 2014 at 10:43:28AM +0000, Catalin Marinas wrote:
> > > On Wed, Feb 05, 2014 at 07:55:45PM +0000, Marc Zyngier wrote:
> > > > The default pmd_addr_end macro uses an unsigned long to represent
> > > > the VA. When used with KVM and stage-2 translation, the VA is
> > > > actually an IPA, which is up to 40 bits. This also affect the
> > > > SMMU driver, which also deals with stage-2 translation.
> > > > 
> > > > Instead, provide an implementation that can cope with larger VAs
> > > > by using a u64 instead. This version will overload the default
> > > > one provided in include/asm-generic/pgtable.h.
> > > > 
> > > > Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> > > > ---
> > > >  arch/arm/include/asm/pgtable-3level.h | 5 +++++
> > > >  1 file changed, 5 insertions(+)
> > > > 
> > > > diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h
> > > > index 03243f7..594867b 100644
> > > > --- a/arch/arm/include/asm/pgtable-3level.h
> > > > +++ b/arch/arm/include/asm/pgtable-3level.h
> > > > @@ -262,6 +262,11 @@ static inline int has_transparent_hugepage(void)
> > > >  	return 1;
> > > >  }
> > > >  
> > > > +#define pmd_addr_end(addr, end)						\
> > > > +({	u64 __boundary = ((addr) + PMD_SIZE) & PMD_MASK;		\
> > > > +	(__boundary - 1 < (end) - 1)? __boundary: (end);		\
> > > > +})
> > > 
> > > I see why you need this but it affects all the other uses of
> > > pmd_addr_end() with 32-bit VA. It would be slight performance, I don't
> > > think it's noticeable.
> > > 
> > > A different approach could be something like (untested):
> > > 
> > > #define pmd_addr_end(addr, end)					\
> > > ({	__typeof__(addr) __boundary = ...
> > > 	...
> > > })
> > > 
> > > What about the pgd_addr_end(), do we need this or it's not used by KVM?
> > > 
> > 
> > What about pud_addr_end(), is that defined as a noop on LPAE, or?
> > 
> > I would be in favor of introducing them all using your approach to avoid
> > somebody being inspired by the KVM code when dealing with IPAs and
> > breaking things unknowingly.
> 
> I had a brief chat with Marc yesterday around this and it may be safer
> to simply introduce kvm_p*d_addr_end() macros. You already do this for
> pgd_addr_end() since it cannot be overridden in the generic headers.
> 
Sounds good to me.  We should introduce all of them then.

Thanks,
-Christoffer

^ 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