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 1/2] PM / OPP: Allow boost frequency to be looked up from device tree
From: Sudeep Holla @ 2014-02-07 16:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391786342-11812-2-git-send-email-thomas.ab@samsung.com>

On 07/02/14 15:19, Thomas Abraham wrote:
> From: Thomas Abraham <thomas.ab@samsung.com>
> 
> Commit 6f19efc0 ("cpufreq: Add boost frequency support in core") adds
> support for CPU boost mode. This patch adds support for finding available
> boost frequencies from device tree and marking them as usable in boost mode.
> 
> Cc: Nishanth Menon <nm@ti.com>
> Cc: Lukasz Majewski <l.majewski@samsung.com>
> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> ---
>  drivers/base/power/opp.c |   34 +++++++++++++++++++++++++++++++++-
>  1 file changed, 33 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
> index fa41874..b636826 100644
> --- a/drivers/base/power/opp.c
> +++ b/drivers/base/power/opp.c
> @@ -628,7 +628,8 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
>  	struct device_opp *dev_opp;
>  	struct dev_pm_opp *opp;
>  	struct cpufreq_frequency_table *freq_table;
> -	int i = 0;
> +	int i = 0, j, len, ret;
> +	u32 *boost_freqs = NULL;
>  
>  	/* Pretend as if I am an updater */
>  	mutex_lock(&dev_opp_list_lock);
> @@ -650,10 +651,35 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
>  		return -ENOMEM;
>  	}
>  
> +	if (of_find_property(dev->of_node, "boost-frequency", &len)) {
> +		if (len == 0 || (len & (sizeof(u32) - 1)) != 0) {
> +			dev_err(dev, "%s: invalid boost frequency\n", __func__);
> +			ret = -EINVAL;
> +			goto err_boost;
> +		}
> +
> +		boost_freqs = kzalloc(len, GFP_KERNEL);
> +		if (!boost_freqs) {
> +			dev_warn(dev, "%s: no memory for boost freq table\n",
> +					__func__);
> +			ret = -ENOMEM;
> +			goto err_boost;
> +		}
> +		of_property_read_u32_array(dev->of_node, "boost-frequency",
> +			boost_freqs, len / sizeof(u32));
> +	}
> +
>  	list_for_each_entry(opp, &dev_opp->opp_list, node) {
>  		if (opp->available) {
>  			freq_table[i].driver_data = i;
>  			freq_table[i].frequency = opp->rate / 1000;
> +			for (j = 0; j < len / sizeof(u32) && boost_freqs; j++) {
> +				if (boost_freqs[j] == freq_table[i].frequency) {
> +					freq_table[i].driver_data =
> +							CPUFREQ_BOOST_FREQ;
> +					break;
> +				}
> +			}
>  			i++;
>  		}
>  	}
IIRC you had mentioned that the boost-opp was not limited to be a cpufreq, but
this change seems to be cpufreq only.

Regards,
Sudeep

^ permalink raw reply

* [PATCH] ARM: sunxi: Add driver for sunxi usb phy
From: Hans de Goede @ 2014-02-07 15:57 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52D6A27A.4090600@ti.com>

Hi,

On 01/15/2014 04:00 PM, Kishon Vijay Abraham I wrote:
> On Wednesday 15 January 2014 04:28 AM, 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",
>> +  "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
>> +- #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>;
>> +		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;
>
> 'pmu_irq' is misleading. Can you think of a better name?

Changed this to pmu in my local tree, I'll send a v2 after some testing.

>
> Btw Kamil uses syscon interface to set pmu bits. Is it applicable here also?

The syscon interface is useful only for shared registers, this register is only
used by the phy driver, so I see no value in using the syscon interface.

>> +		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);
>
> No magic values. Use macros instead.

Fixed for the addresses, the 0x14 will stay magic though, as we've no
idea what it actually does.

>> +
>> +	/* 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);
>
> Actually PHY API's can be called in interrupt context, in that case
> clk_disable_unprepare can't be used.
>> +
>> +	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;
>> +}
>
> This can simply be
> int ret = 0;
> if (phy->vbus)
> 	ret = regulator_enable(phy->vbus);
> return ret;

Fixed.

>> +
>> +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;
>> +	}
>> +
>> +	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_irq for usbc0 */
>> +			pmu_irq = devm_ioremap_resource(dev,
>> +			      platform_get_resource(pdev, IORESOURCE_MEM, i));
>> +			if (IS_ERR(pmu_irq))
>> +				return PTR_ERR(pmu_irq);
>> +		}
>> +
>> +		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_irq = pmu_irq;
>> +		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");
>
> GPL v2?

Fixed.

> This patch looks good apart from those minor comments.

Ok v2 is on its way.

Regards,

Hans

^ permalink raw reply

* [PATCH v3 7/7] cpufreq: exynos: remove all exynos specific cpufreq driver support
From: Thomas Abraham @ 2014-02-07 15:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391788548-13056-1-git-send-email-thomas.ab@samsung.com>

From: Thomas Abraham <thomas.ab@samsung.com>

Exynos4210, Exynos4x12 and Exynos5250 based platforms have switched over
to use cpufreq-cpu0 driver for cpufreq functionality. So the Exynos
specific cpufreq drivers for these platforms can be removed.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
---
 drivers/cpufreq/Kconfig.arm          |   51 ------
 drivers/cpufreq/Makefile             |    4 -
 drivers/cpufreq/exynos-cpufreq.c     |  292 ----------------------------------
 drivers/cpufreq/exynos-cpufreq.h     |   91 -----------
 drivers/cpufreq/exynos4210-cpufreq.c |  157 ------------------
 drivers/cpufreq/exynos4x12-cpufreq.c |  211 ------------------------
 drivers/cpufreq/exynos5250-cpufreq.c |  183 ---------------------
 7 files changed, 989 deletions(-)
 delete mode 100644 drivers/cpufreq/exynos-cpufreq.c
 delete mode 100644 drivers/cpufreq/exynos-cpufreq.h
 delete mode 100644 drivers/cpufreq/exynos4210-cpufreq.c
 delete mode 100644 drivers/cpufreq/exynos4x12-cpufreq.c
 delete mode 100644 drivers/cpufreq/exynos5250-cpufreq.c

diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 3129749..38f691a 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -16,42 +16,6 @@ config ARM_DT_BL_CPUFREQ
 	  This enables probing via DT for Generic CPUfreq driver for ARM
 	  big.LITTLE platform. This gets frequency tables from DT.
 
-config ARM_EXYNOS_CPUFREQ
-	bool
-
-config ARM_EXYNOS4210_CPUFREQ
-	bool "SAMSUNG EXYNOS4210"
-	depends on CPU_EXYNOS4210
-	default y
-	select ARM_EXYNOS_CPUFREQ
-	help
-	  This adds the CPUFreq driver for Samsung EXYNOS4210
-	  SoC (S5PV310 or S5PC210).
-
-	  If in doubt, say N.
-
-config ARM_EXYNOS4X12_CPUFREQ
-	bool "SAMSUNG EXYNOS4x12"
-	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
-	default y
-	select ARM_EXYNOS_CPUFREQ
-	help
-	  This adds the CPUFreq driver for Samsung EXYNOS4X12
-	  SoC (EXYNOS4212 or EXYNOS4412).
-
-	  If in doubt, say N.
-
-config ARM_EXYNOS5250_CPUFREQ
-	bool "SAMSUNG EXYNOS5250"
-	depends on SOC_EXYNOS5250
-	default y
-	select ARM_EXYNOS_CPUFREQ
-	help
-	  This adds the CPUFreq driver for Samsung EXYNOS5250
-	  SoC.
-
-	  If in doubt, say N.
-
 config ARM_EXYNOS5440_CPUFREQ
 	bool "SAMSUNG EXYNOS5440"
 	depends on SOC_EXYNOS5440
@@ -66,21 +30,6 @@ config ARM_EXYNOS5440_CPUFREQ
 
 	  If in doubt, say N.
 
-config ARM_EXYNOS_CPU_FREQ_BOOST_SW
-	bool "EXYNOS Frequency Overclocking - Software"
-	depends on ARM_EXYNOS_CPUFREQ
-	select CPU_FREQ_BOOST_SW
-	select EXYNOS_THERMAL
-	help
-	  This driver supports software managed overclocking (BOOST).
-	  It allows usage of special frequencies for Samsung Exynos
-	  processors if thermal conditions are appropriate.
-
-	  It reguires, for safe operation, thermal framework with properly
-	  defined trip points.
-
-	  If in doubt, say N.
-
 config ARM_HIGHBANK_CPUFREQ
 	tristate "Calxeda Highbank-based"
 	depends on ARCH_HIGHBANK
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 7494565..3abfe05 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -49,10 +49,6 @@ obj-$(CONFIG_ARM_DT_BL_CPUFREQ)		+= arm_big_little_dt.o
 
 obj-$(CONFIG_ARCH_DAVINCI_DA850)	+= davinci-cpufreq.o
 obj-$(CONFIG_UX500_SOC_DB8500)		+= dbx500-cpufreq.o
-obj-$(CONFIG_ARM_EXYNOS_CPUFREQ)	+= exynos-cpufreq.o
-obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ)	+= exynos4210-cpufreq.o
-obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ)	+= exynos4x12-cpufreq.o
-obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ)	+= exynos5250-cpufreq.o
 obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ)	+= exynos5440-cpufreq.o
 obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ)	+= highbank-cpufreq.o
 obj-$(CONFIG_ARM_IMX6Q_CPUFREQ)		+= imx6q-cpufreq.o
diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c
deleted file mode 100644
index fcd2914..0000000
--- a/drivers/cpufreq/exynos-cpufreq.c
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- *		http://www.samsung.com
- *
- * EXYNOS - CPU frequency scaling support for EXYNOS series
- *
- * 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/kernel.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/regulator/consumer.h>
-#include <linux/cpufreq.h>
-#include <linux/suspend.h>
-#include <linux/platform_device.h>
-
-#include <plat/cpu.h>
-
-#include "exynos-cpufreq.h"
-
-static struct exynos_dvfs_info *exynos_info;
-
-static struct regulator *arm_regulator;
-
-static unsigned int locking_frequency;
-static bool frequency_locked;
-static DEFINE_MUTEX(cpufreq_lock);
-
-static int exynos_cpufreq_get_index(unsigned int freq)
-{
-	struct cpufreq_frequency_table *freq_table = exynos_info->freq_table;
-	int index;
-
-	for (index = 0;
-		freq_table[index].frequency != CPUFREQ_TABLE_END; index++)
-		if (freq_table[index].frequency == freq)
-			break;
-
-	if (freq_table[index].frequency == CPUFREQ_TABLE_END)
-		return -EINVAL;
-
-	return index;
-}
-
-static int exynos_cpufreq_scale(unsigned int target_freq)
-{
-	struct cpufreq_frequency_table *freq_table = exynos_info->freq_table;
-	unsigned int *volt_table = exynos_info->volt_table;
-	struct cpufreq_policy *policy = cpufreq_cpu_get(0);
-	unsigned int arm_volt, safe_arm_volt = 0;
-	unsigned int mpll_freq_khz = exynos_info->mpll_freq_khz;
-	unsigned int old_freq;
-	int index, old_index;
-	int ret = 0;
-
-	old_freq = policy->cur;
-
-	/*
-	 * The policy max have been changed so that we cannot get proper
-	 * old_index with cpufreq_frequency_table_target(). Thus, ignore
-	 * policy and get the index from the raw frequency table.
-	 */
-	old_index = exynos_cpufreq_get_index(old_freq);
-	if (old_index < 0) {
-		ret = old_index;
-		goto out;
-	}
-
-	index = exynos_cpufreq_get_index(target_freq);
-	if (index < 0) {
-		ret = index;
-		goto out;
-	}
-
-	/*
-	 * ARM clock source will be changed APLL to MPLL temporary
-	 * To support this level, need to control regulator for
-	 * required voltage level
-	 */
-	if (exynos_info->need_apll_change != NULL) {
-		if (exynos_info->need_apll_change(old_index, index) &&
-		   (freq_table[index].frequency < mpll_freq_khz) &&
-		   (freq_table[old_index].frequency < mpll_freq_khz))
-			safe_arm_volt = volt_table[exynos_info->pll_safe_idx];
-	}
-	arm_volt = volt_table[index];
-
-	/* When the new frequency is higher than current frequency */
-	if ((target_freq > old_freq) && !safe_arm_volt) {
-		/* Firstly, voltage up to increase frequency */
-		ret = regulator_set_voltage(arm_regulator, arm_volt, arm_volt);
-		if (ret) {
-			pr_err("%s: failed to set cpu voltage to %d\n",
-				__func__, arm_volt);
-			return ret;
-		}
-	}
-
-	if (safe_arm_volt) {
-		ret = regulator_set_voltage(arm_regulator, safe_arm_volt,
-				      safe_arm_volt);
-		if (ret) {
-			pr_err("%s: failed to set cpu voltage to %d\n",
-				__func__, safe_arm_volt);
-			return ret;
-		}
-	}
-
-	exynos_info->set_freq(old_index, index);
-
-	/* When the new frequency is lower than current frequency */
-	if ((target_freq < old_freq) ||
-	   ((target_freq > old_freq) && safe_arm_volt)) {
-		/* down the voltage after frequency change */
-		ret = regulator_set_voltage(arm_regulator, arm_volt,
-				arm_volt);
-		if (ret) {
-			pr_err("%s: failed to set cpu voltage to %d\n",
-				__func__, arm_volt);
-			goto out;
-		}
-	}
-
-out:
-	cpufreq_cpu_put(policy);
-
-	return ret;
-}
-
-static int exynos_target(struct cpufreq_policy *policy, unsigned int index)
-{
-	struct cpufreq_frequency_table *freq_table = exynos_info->freq_table;
-	int ret = 0;
-
-	mutex_lock(&cpufreq_lock);
-
-	if (frequency_locked)
-		goto out;
-
-	ret = exynos_cpufreq_scale(freq_table[index].frequency);
-
-out:
-	mutex_unlock(&cpufreq_lock);
-
-	return ret;
-}
-
-#ifdef CONFIG_PM
-static int exynos_cpufreq_suspend(struct cpufreq_policy *policy)
-{
-	return 0;
-}
-
-static int exynos_cpufreq_resume(struct cpufreq_policy *policy)
-{
-	return 0;
-}
-#endif
-
-/**
- * exynos_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume
- *			context
- * @notifier
- * @pm_event
- * @v
- *
- * While frequency_locked == true, target() ignores every frequency but
- * locking_frequency. The locking_frequency value is the initial frequency,
- * which is set by the bootloader. In order to eliminate possible
- * inconsistency in clock values, we save and restore frequencies during
- * suspend and resume and block CPUFREQ activities. Note that the standard
- * suspend/resume cannot be used as they are too deep (syscore_ops) for
- * regulator actions.
- */
-static int exynos_cpufreq_pm_notifier(struct notifier_block *notifier,
-				       unsigned long pm_event, void *v)
-{
-	int ret;
-
-	switch (pm_event) {
-	case PM_SUSPEND_PREPARE:
-		mutex_lock(&cpufreq_lock);
-		frequency_locked = true;
-		mutex_unlock(&cpufreq_lock);
-
-		ret = exynos_cpufreq_scale(locking_frequency);
-		if (ret < 0)
-			return NOTIFY_BAD;
-
-		break;
-
-	case PM_POST_SUSPEND:
-		mutex_lock(&cpufreq_lock);
-		frequency_locked = false;
-		mutex_unlock(&cpufreq_lock);
-		break;
-	}
-
-	return NOTIFY_OK;
-}
-
-static struct notifier_block exynos_cpufreq_nb = {
-	.notifier_call = exynos_cpufreq_pm_notifier,
-};
-
-static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
-{
-	policy->clk = exynos_info->cpu_clk;
-	return cpufreq_generic_init(policy, exynos_info->freq_table, 100000);
-}
-
-static struct cpufreq_driver exynos_driver = {
-	.flags		= CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
-	.verify		= cpufreq_generic_frequency_table_verify,
-	.target_index	= exynos_target,
-	.get		= cpufreq_generic_get,
-	.init		= exynos_cpufreq_cpu_init,
-	.exit		= cpufreq_generic_exit,
-	.name		= "exynos_cpufreq",
-	.attr		= cpufreq_generic_attr,
-#ifdef CONFIG_ARM_EXYNOS_CPU_FREQ_BOOST_SW
-	.boost_supported = true,
-#endif
-#ifdef CONFIG_PM
-	.suspend	= exynos_cpufreq_suspend,
-	.resume		= exynos_cpufreq_resume,
-#endif
-};
-
-static int exynos_cpufreq_probe(struct platform_device *pdev)
-{
-	int ret = -EINVAL;
-
-	exynos_info = kzalloc(sizeof(*exynos_info), GFP_KERNEL);
-	if (!exynos_info)
-		return -ENOMEM;
-
-	if (soc_is_exynos4210())
-		ret = exynos4210_cpufreq_init(exynos_info);
-	else if (soc_is_exynos4212() || soc_is_exynos4412())
-		ret = exynos4x12_cpufreq_init(exynos_info);
-	else if (soc_is_exynos5250())
-		ret = exynos5250_cpufreq_init(exynos_info);
-	else
-		return 0;
-
-	if (ret)
-		goto err_vdd_arm;
-
-	if (exynos_info->set_freq == NULL) {
-		pr_err("%s: No set_freq function (ERR)\n", __func__);
-		goto err_vdd_arm;
-	}
-
-	arm_regulator = regulator_get(NULL, "vdd_arm");
-	if (IS_ERR(arm_regulator)) {
-		pr_err("%s: failed to get resource vdd_arm\n", __func__);
-		goto err_vdd_arm;
-	}
-
-	locking_frequency = clk_get_rate(exynos_info->cpu_clk) / 1000;
-
-	register_pm_notifier(&exynos_cpufreq_nb);
-
-	if (cpufreq_register_driver(&exynos_driver)) {
-		pr_err("%s: failed to register cpufreq driver\n", __func__);
-		goto err_cpufreq;
-	}
-
-	return 0;
-err_cpufreq:
-	unregister_pm_notifier(&exynos_cpufreq_nb);
-
-	regulator_put(arm_regulator);
-err_vdd_arm:
-	kfree(exynos_info);
-	return -EINVAL;
-}
-
-static struct platform_driver exynos_cpufreq_platdrv = {
-	.driver = {
-		.name	= "exynos-cpufreq",
-		.owner	= THIS_MODULE,
-	},
-	.probe = exynos_cpufreq_probe,
-};
-module_platform_driver(exynos_cpufreq_platdrv);
diff --git a/drivers/cpufreq/exynos-cpufreq.h b/drivers/cpufreq/exynos-cpufreq.h
deleted file mode 100644
index 3ddade8..0000000
--- a/drivers/cpufreq/exynos-cpufreq.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 2010 Samsung Electronics Co., Ltd.
- *		http://www.samsung.com
- *
- * EXYNOS - CPUFreq support
- *
- * 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.
-*/
-
-enum cpufreq_level_index {
-	L0, L1, L2, L3, L4,
-	L5, L6, L7, L8, L9,
-	L10, L11, L12, L13, L14,
-	L15, L16, L17, L18, L19,
-	L20,
-};
-
-#define APLL_FREQ(f, a0, a1, a2, a3, a4, a5, a6, a7, b0, b1, b2, m, p, s) \
-	{ \
-		.freq = (f) * 1000, \
-		.clk_div_cpu0 = ((a0) | (a1) << 4 | (a2) << 8 | (a3) << 12 | \
-			(a4) << 16 | (a5) << 20 | (a6) << 24 | (a7) << 28), \
-		.clk_div_cpu1 = (b0 << 0 | b1 << 4 | b2 << 8), \
-		.mps = ((m) << 16 | (p) << 8 | (s)), \
-	}
-
-struct apll_freq {
-	unsigned int freq;
-	u32 clk_div_cpu0;
-	u32 clk_div_cpu1;
-	u32 mps;
-};
-
-struct exynos_dvfs_info {
-	unsigned long	mpll_freq_khz;
-	unsigned int	pll_safe_idx;
-	struct clk	*cpu_clk;
-	unsigned int	*volt_table;
-	struct cpufreq_frequency_table	*freq_table;
-	void (*set_freq)(unsigned int, unsigned int);
-	bool (*need_apll_change)(unsigned int, unsigned int);
-};
-
-#ifdef CONFIG_ARM_EXYNOS4210_CPUFREQ
-extern int exynos4210_cpufreq_init(struct exynos_dvfs_info *);
-#else
-static inline int exynos4210_cpufreq_init(struct exynos_dvfs_info *info)
-{
-	return -EOPNOTSUPP;
-}
-#endif
-#ifdef CONFIG_ARM_EXYNOS4X12_CPUFREQ
-extern int exynos4x12_cpufreq_init(struct exynos_dvfs_info *);
-#else
-static inline int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info)
-{
-	return -EOPNOTSUPP;
-}
-#endif
-#ifdef CONFIG_ARM_EXYNOS5250_CPUFREQ
-extern int exynos5250_cpufreq_init(struct exynos_dvfs_info *);
-#else
-static inline int exynos5250_cpufreq_init(struct exynos_dvfs_info *info)
-{
-	return -EOPNOTSUPP;
-}
-#endif
-
-#include <plat/cpu.h>
-#include <mach/map.h>
-
-#define EXYNOS4_CLKSRC_CPU			(S5P_VA_CMU + 0x14200)
-#define EXYNOS4_CLKMUX_STATCPU			(S5P_VA_CMU + 0x14400)
-
-#define EXYNOS4_CLKDIV_CPU			(S5P_VA_CMU + 0x14500)
-#define EXYNOS4_CLKDIV_CPU1			(S5P_VA_CMU + 0x14504)
-#define EXYNOS4_CLKDIV_STATCPU			(S5P_VA_CMU + 0x14600)
-#define EXYNOS4_CLKDIV_STATCPU1			(S5P_VA_CMU + 0x14604)
-
-#define EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT	(16)
-#define EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK	(0x7 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT)
-
-#define EXYNOS5_APLL_LOCK			(S5P_VA_CMU + 0x00000)
-#define EXYNOS5_APLL_CON0			(S5P_VA_CMU + 0x00100)
-#define EXYNOS5_CLKMUX_STATCPU			(S5P_VA_CMU + 0x00400)
-#define EXYNOS5_CLKDIV_CPU0			(S5P_VA_CMU + 0x00500)
-#define EXYNOS5_CLKDIV_CPU1			(S5P_VA_CMU + 0x00504)
-#define EXYNOS5_CLKDIV_STATCPU0			(S5P_VA_CMU + 0x00600)
-#define EXYNOS5_CLKDIV_STATCPU1			(S5P_VA_CMU + 0x00604)
diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c
deleted file mode 100644
index 40d84c4..0000000
--- a/drivers/cpufreq/exynos4210-cpufreq.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- *		http://www.samsung.com
- *
- * EXYNOS4210 - CPU frequency scaling support
- *
- * 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/module.h>
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/cpufreq.h>
-
-#include "exynos-cpufreq.h"
-
-static struct clk *cpu_clk;
-static struct clk *moutcore;
-static struct clk *mout_mpll;
-static struct clk *mout_apll;
-
-static unsigned int exynos4210_volt_table[] = {
-	1250000, 1150000, 1050000, 975000, 950000,
-};
-
-static struct cpufreq_frequency_table exynos4210_freq_table[] = {
-	{L0, 1200 * 1000},
-	{L1, 1000 * 1000},
-	{L2,  800 * 1000},
-	{L3,  500 * 1000},
-	{L4,  200 * 1000},
-	{0, CPUFREQ_TABLE_END},
-};
-
-static struct apll_freq apll_freq_4210[] = {
-	/*
-	 * values:
-	 * freq
-	 * clock divider for CORE, COREM0, COREM1, PERIPH, ATB, PCLK_DBG, APLL, RESERVED
-	 * clock divider for COPY, HPM, RESERVED
-	 * PLL M, P, S
-	 */
-	APLL_FREQ(1200, 0, 3, 7, 3, 4, 1, 7, 0, 5, 0, 0, 150, 3, 1),
-	APLL_FREQ(1000, 0, 3, 7, 3, 4, 1, 7, 0, 4, 0, 0, 250, 6, 1),
-	APLL_FREQ(800,  0, 3, 7, 3, 3, 1, 7, 0, 3, 0, 0, 200, 6, 1),
-	APLL_FREQ(500,  0, 3, 7, 3, 3, 1, 7, 0, 3, 0, 0, 250, 6, 2),
-	APLL_FREQ(200,  0, 1, 3, 1, 3, 1, 0, 0, 3, 0, 0, 200, 6, 3),
-};
-
-static void exynos4210_set_clkdiv(unsigned int div_index)
-{
-	unsigned int tmp;
-
-	/* Change Divider - CPU0 */
-
-	tmp = apll_freq_4210[div_index].clk_div_cpu0;
-
-	__raw_writel(tmp, EXYNOS4_CLKDIV_CPU);
-
-	do {
-		tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU);
-	} while (tmp & 0x1111111);
-
-	/* Change Divider - CPU1 */
-
-	tmp = apll_freq_4210[div_index].clk_div_cpu1;
-
-	__raw_writel(tmp, EXYNOS4_CLKDIV_CPU1);
-
-	do {
-		tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU1);
-	} while (tmp & 0x11);
-}
-
-static void exynos4210_set_apll(unsigned int index)
-{
-	unsigned int tmp, freq = apll_freq_4210[index].freq;
-
-	/* MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
-	clk_set_parent(moutcore, mout_mpll);
-
-	do {
-		tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU)
-			>> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT);
-		tmp &= 0x7;
-	} while (tmp != 0x2);
-
-	clk_set_rate(mout_apll, freq * 1000);
-
-	/* MUX_CORE_SEL = APLL */
-	clk_set_parent(moutcore, mout_apll);
-
-	do {
-		tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU);
-		tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK;
-	} while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT));
-}
-
-static void exynos4210_set_frequency(unsigned int old_index,
-				     unsigned int new_index)
-{
-	if (old_index > new_index) {
-		exynos4210_set_clkdiv(new_index);
-		exynos4210_set_apll(new_index);
-	} else if (old_index < new_index) {
-		exynos4210_set_apll(new_index);
-		exynos4210_set_clkdiv(new_index);
-	}
-}
-
-int exynos4210_cpufreq_init(struct exynos_dvfs_info *info)
-{
-	unsigned long rate;
-
-	cpu_clk = clk_get(NULL, "armclk");
-	if (IS_ERR(cpu_clk))
-		return PTR_ERR(cpu_clk);
-
-	moutcore = clk_get(NULL, "moutcore");
-	if (IS_ERR(moutcore))
-		goto err_moutcore;
-
-	mout_mpll = clk_get(NULL, "mout_mpll");
-	if (IS_ERR(mout_mpll))
-		goto err_mout_mpll;
-
-	rate = clk_get_rate(mout_mpll) / 1000;
-
-	mout_apll = clk_get(NULL, "mout_apll");
-	if (IS_ERR(mout_apll))
-		goto err_mout_apll;
-
-	info->mpll_freq_khz = rate;
-	/* 800Mhz */
-	info->pll_safe_idx = L2;
-	info->cpu_clk = cpu_clk;
-	info->volt_table = exynos4210_volt_table;
-	info->freq_table = exynos4210_freq_table;
-	info->set_freq = exynos4210_set_frequency;
-
-	return 0;
-
-err_mout_apll:
-	clk_put(mout_mpll);
-err_mout_mpll:
-	clk_put(moutcore);
-err_moutcore:
-	clk_put(cpu_clk);
-
-	pr_debug("%s: failed initialization\n", __func__);
-	return -EINVAL;
-}
diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c
deleted file mode 100644
index 7c11ace..0000000
--- a/drivers/cpufreq/exynos4x12-cpufreq.c
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd.
- *		http://www.samsung.com
- *
- * EXYNOS4X12 - CPU frequency scaling support
- *
- * 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/module.h>
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/cpufreq.h>
-
-#include "exynos-cpufreq.h"
-
-static struct clk *cpu_clk;
-static struct clk *moutcore;
-static struct clk *mout_mpll;
-static struct clk *mout_apll;
-
-static unsigned int exynos4x12_volt_table[] = {
-	1350000, 1287500, 1250000, 1187500, 1137500, 1087500, 1037500,
-	1000000,  987500,  975000,  950000,  925000,  900000,  900000
-};
-
-static struct cpufreq_frequency_table exynos4x12_freq_table[] = {
-	{CPUFREQ_BOOST_FREQ, 1500 * 1000},
-	{L1, 1400 * 1000},
-	{L2, 1300 * 1000},
-	{L3, 1200 * 1000},
-	{L4, 1100 * 1000},
-	{L5, 1000 * 1000},
-	{L6,  900 * 1000},
-	{L7,  800 * 1000},
-	{L8,  700 * 1000},
-	{L9,  600 * 1000},
-	{L10, 500 * 1000},
-	{L11, 400 * 1000},
-	{L12, 300 * 1000},
-	{L13, 200 * 1000},
-	{0, CPUFREQ_TABLE_END},
-};
-
-static struct apll_freq *apll_freq_4x12;
-
-static struct apll_freq apll_freq_4212[] = {
-	/*
-	 * values:
-	 * freq
-	 * clock divider for CORE, COREM0, COREM1, PERIPH, ATB, PCLK_DBG, APLL, CORE2
-	 * clock divider for COPY, HPM, RESERVED
-	 * PLL M, P, S
-	 */
-	APLL_FREQ(1500, 0, 3, 7, 0, 6, 1, 2, 0, 6, 2, 0, 250, 4, 0),
-	APLL_FREQ(1400, 0, 3, 7, 0, 6, 1, 2, 0, 6, 2, 0, 175, 3, 0),
-	APLL_FREQ(1300, 0, 3, 7, 0, 5, 1, 2, 0, 5, 2, 0, 325, 6, 0),
-	APLL_FREQ(1200, 0, 3, 7, 0, 5, 1, 2, 0, 5, 2, 0, 200, 4, 0),
-	APLL_FREQ(1100, 0, 3, 6, 0, 4, 1, 2, 0, 4, 2, 0, 275, 6, 0),
-	APLL_FREQ(1000, 0, 2, 5, 0, 4, 1, 1, 0, 4, 2, 0, 125, 3, 0),
-	APLL_FREQ(900,  0, 2, 5, 0, 3, 1, 1, 0, 3, 2, 0, 150, 4, 0),
-	APLL_FREQ(800,  0, 2, 5, 0, 3, 1, 1, 0, 3, 2, 0, 100, 3, 0),
-	APLL_FREQ(700,  0, 2, 4, 0, 3, 1, 1, 0, 3, 2, 0, 175, 3, 1),
-	APLL_FREQ(600,  0, 2, 4, 0, 3, 1, 1, 0, 3, 2, 0, 200, 4, 1),
-	APLL_FREQ(500,  0, 2, 4, 0, 3, 1, 1, 0, 3, 2, 0, 125, 3, 1),
-	APLL_FREQ(400,  0, 2, 4, 0, 3, 1, 1, 0, 3, 2, 0, 100, 3, 1),
-	APLL_FREQ(300,  0, 2, 4, 0, 2, 1, 1, 0, 3, 2, 0, 200, 4, 2),
-	APLL_FREQ(200,  0, 1, 3, 0, 1, 1, 1, 0, 3, 2, 0, 100, 3, 2),
-};
-
-static struct apll_freq apll_freq_4412[] = {
-	/*
-	 * values:
-	 * freq
-	 * clock divider for CORE, COREM0, COREM1, PERIPH, ATB, PCLK_DBG, APLL, CORE2
-	 * clock divider for COPY, HPM, CORES
-	 * PLL M, P, S
-	 */
-	APLL_FREQ(1500, 0, 3, 7, 0, 6, 1, 2, 0, 6, 0, 7, 250, 4, 0),
-	APLL_FREQ(1400, 0, 3, 7, 0, 6, 1, 2, 0, 6, 0, 6, 175, 3, 0),
-	APLL_FREQ(1300, 0, 3, 7, 0, 5, 1, 2, 0, 5, 0, 6, 325, 6, 0),
-	APLL_FREQ(1200, 0, 3, 7, 0, 5, 1, 2, 0, 5, 0, 5, 200, 4, 0),
-	APLL_FREQ(1100, 0, 3, 6, 0, 4, 1, 2, 0, 4, 0, 5, 275, 6, 0),
-	APLL_FREQ(1000, 0, 2, 5, 0, 4, 1, 1, 0, 4, 0, 4, 125, 3, 0),
-	APLL_FREQ(900,  0, 2, 5, 0, 3, 1, 1, 0, 3, 0, 4, 150, 4, 0),
-	APLL_FREQ(800,  0, 2, 5, 0, 3, 1, 1, 0, 3, 0, 3, 100, 3, 0),
-	APLL_FREQ(700,  0, 2, 4, 0, 3, 1, 1, 0, 3, 0, 3, 175, 3, 1),
-	APLL_FREQ(600,  0, 2, 4, 0, 3, 1, 1, 0, 3, 0, 2, 200, 4, 1),
-	APLL_FREQ(500,  0, 2, 4, 0, 3, 1, 1, 0, 3, 0, 2, 125, 3, 1),
-	APLL_FREQ(400,  0, 2, 4, 0, 3, 1, 1, 0, 3, 0, 1, 100, 3, 1),
-	APLL_FREQ(300,  0, 2, 4, 0, 2, 1, 1, 0, 3, 0, 1, 200, 4, 2),
-	APLL_FREQ(200,  0, 1, 3, 0, 1, 1, 1, 0, 3, 0, 0, 100, 3, 2),
-};
-
-static void exynos4x12_set_clkdiv(unsigned int div_index)
-{
-	unsigned int tmp;
-	unsigned int stat_cpu1;
-
-	/* Change Divider - CPU0 */
-
-	tmp = apll_freq_4x12[div_index].clk_div_cpu0;
-
-	__raw_writel(tmp, EXYNOS4_CLKDIV_CPU);
-
-	while (__raw_readl(EXYNOS4_CLKDIV_STATCPU) & 0x11111111)
-		cpu_relax();
-
-	/* Change Divider - CPU1 */
-	tmp = apll_freq_4x12[div_index].clk_div_cpu1;
-
-	__raw_writel(tmp, EXYNOS4_CLKDIV_CPU1);
-	if (soc_is_exynos4212())
-		stat_cpu1 = 0x11;
-	else
-		stat_cpu1 = 0x111;
-
-	while (__raw_readl(EXYNOS4_CLKDIV_STATCPU1) & stat_cpu1)
-		cpu_relax();
-}
-
-static void exynos4x12_set_apll(unsigned int index)
-{
-	unsigned int tmp, freq = apll_freq_4x12[index].freq;
-
-	/* MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
-	clk_set_parent(moutcore, mout_mpll);
-
-	do {
-		cpu_relax();
-		tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU)
-			>> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT);
-		tmp &= 0x7;
-	} while (tmp != 0x2);
-
-	clk_set_rate(mout_apll, freq * 1000);
-
-	/* MUX_CORE_SEL = APLL */
-	clk_set_parent(moutcore, mout_apll);
-
-	do {
-		cpu_relax();
-		tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU);
-		tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK;
-	} while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT));
-}
-
-static void exynos4x12_set_frequency(unsigned int old_index,
-				  unsigned int new_index)
-{
-	if (old_index > new_index) {
-		exynos4x12_set_clkdiv(new_index);
-		exynos4x12_set_apll(new_index);
-	} else if (old_index < new_index) {
-		exynos4x12_set_apll(new_index);
-		exynos4x12_set_clkdiv(new_index);
-	}
-}
-
-int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info)
-{
-	unsigned long rate;
-
-	cpu_clk = clk_get(NULL, "armclk");
-	if (IS_ERR(cpu_clk))
-		return PTR_ERR(cpu_clk);
-
-	moutcore = clk_get(NULL, "moutcore");
-	if (IS_ERR(moutcore))
-		goto err_moutcore;
-
-	mout_mpll = clk_get(NULL, "mout_mpll");
-	if (IS_ERR(mout_mpll))
-		goto err_mout_mpll;
-
-	rate = clk_get_rate(mout_mpll) / 1000;
-
-	mout_apll = clk_get(NULL, "mout_apll");
-	if (IS_ERR(mout_apll))
-		goto err_mout_apll;
-
-	if (soc_is_exynos4212())
-		apll_freq_4x12 = apll_freq_4212;
-	else
-		apll_freq_4x12 = apll_freq_4412;
-
-	info->mpll_freq_khz = rate;
-	/* 800Mhz */
-	info->pll_safe_idx = L7;
-	info->cpu_clk = cpu_clk;
-	info->volt_table = exynos4x12_volt_table;
-	info->freq_table = exynos4x12_freq_table;
-	info->set_freq = exynos4x12_set_frequency;
-
-	return 0;
-
-err_mout_apll:
-	clk_put(mout_mpll);
-err_mout_mpll:
-	clk_put(moutcore);
-err_moutcore:
-	clk_put(cpu_clk);
-
-	pr_debug("%s: failed initialization\n", __func__);
-	return -EINVAL;
-}
diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c
deleted file mode 100644
index 5f90b82..0000000
--- a/drivers/cpufreq/exynos5250-cpufreq.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (c) 2010-20122Samsung Electronics Co., Ltd.
- *		http://www.samsung.com
- *
- * EXYNOS5250 - CPU frequency scaling support
- *
- * 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/module.h>
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/cpufreq.h>
-
-#include <mach/map.h>
-
-#include "exynos-cpufreq.h"
-
-static struct clk *cpu_clk;
-static struct clk *moutcore;
-static struct clk *mout_mpll;
-static struct clk *mout_apll;
-
-static unsigned int exynos5250_volt_table[] = {
-	1300000, 1250000, 1225000, 1200000, 1150000,
-	1125000, 1100000, 1075000, 1050000, 1025000,
-	1012500, 1000000,  975000,  950000,  937500,
-	925000
-};
-
-static struct cpufreq_frequency_table exynos5250_freq_table[] = {
-	{L0, 1700 * 1000},
-	{L1, 1600 * 1000},
-	{L2, 1500 * 1000},
-	{L3, 1400 * 1000},
-	{L4, 1300 * 1000},
-	{L5, 1200 * 1000},
-	{L6, 1100 * 1000},
-	{L7, 1000 * 1000},
-	{L8,  900 * 1000},
-	{L9,  800 * 1000},
-	{L10, 700 * 1000},
-	{L11, 600 * 1000},
-	{L12, 500 * 1000},
-	{L13, 400 * 1000},
-	{L14, 300 * 1000},
-	{L15, 200 * 1000},
-	{0, CPUFREQ_TABLE_END},
-};
-
-static struct apll_freq apll_freq_5250[] = {
-	/*
-	 * values:
-	 * freq
-	 * clock divider for ARM, CPUD, ACP, PERIPH, ATB, PCLK_DBG, APLL, ARM2
-	 * clock divider for COPY, HPM, RESERVED
-	 * PLL M, P, S
-	 */
-	APLL_FREQ(1700, 0, 3, 7, 7, 7, 3, 5, 0, 0, 2, 0, 425, 6, 0),
-	APLL_FREQ(1600, 0, 3, 7, 7, 7, 1, 4, 0, 0, 2, 0, 200, 3, 0),
-	APLL_FREQ(1500, 0, 2, 7, 7, 7, 1, 4, 0, 0, 2, 0, 250, 4, 0),
-	APLL_FREQ(1400, 0, 2, 7, 7, 6, 1, 4, 0, 0, 2, 0, 175, 3, 0),
-	APLL_FREQ(1300, 0, 2, 7, 7, 6, 1, 3, 0, 0, 2, 0, 325, 6, 0),
-	APLL_FREQ(1200, 0, 2, 7, 7, 5, 1, 3, 0, 0, 2, 0, 200, 4, 0),
-	APLL_FREQ(1100, 0, 3, 7, 7, 5, 1, 3, 0, 0, 2, 0, 275, 6, 0),
-	APLL_FREQ(1000, 0, 1, 7, 7, 4, 1, 2, 0, 0, 2, 0, 125, 3, 0),
-	APLL_FREQ(900,  0, 1, 7, 7, 4, 1, 2, 0, 0, 2, 0, 150, 4, 0),
-	APLL_FREQ(800,  0, 1, 7, 7, 4, 1, 2, 0, 0, 2, 0, 100, 3, 0),
-	APLL_FREQ(700,  0, 1, 7, 7, 3, 1, 1, 0, 0, 2, 0, 175, 3, 1),
-	APLL_FREQ(600,  0, 1, 7, 7, 3, 1, 1, 0, 0, 2, 0, 200, 4, 1),
-	APLL_FREQ(500,  0, 1, 7, 7, 2, 1, 1, 0, 0, 2, 0, 125, 3, 1),
-	APLL_FREQ(400,  0, 1, 7, 7, 2, 1, 1, 0, 0, 2, 0, 100, 3, 1),
-	APLL_FREQ(300,  0, 1, 7, 7, 1, 1, 1, 0, 0, 2, 0, 200, 4, 2),
-	APLL_FREQ(200,  0, 1, 7, 7, 1, 1, 1, 0, 0, 2, 0, 100, 3, 2),
-};
-
-static void set_clkdiv(unsigned int div_index)
-{
-	unsigned int tmp;
-
-	/* Change Divider - CPU0 */
-
-	tmp = apll_freq_5250[div_index].clk_div_cpu0;
-
-	__raw_writel(tmp, EXYNOS5_CLKDIV_CPU0);
-
-	while (__raw_readl(EXYNOS5_CLKDIV_STATCPU0) & 0x11111111)
-		cpu_relax();
-
-	/* Change Divider - CPU1 */
-	tmp = apll_freq_5250[div_index].clk_div_cpu1;
-
-	__raw_writel(tmp, EXYNOS5_CLKDIV_CPU1);
-
-	while (__raw_readl(EXYNOS5_CLKDIV_STATCPU1) & 0x11)
-		cpu_relax();
-}
-
-static void set_apll(unsigned int index)
-{
-	unsigned int tmp;
-	unsigned int freq = apll_freq_5250[index].freq;
-
-	/* MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
-	clk_set_parent(moutcore, mout_mpll);
-
-	do {
-		cpu_relax();
-		tmp = (__raw_readl(EXYNOS5_CLKMUX_STATCPU) >> 16);
-		tmp &= 0x7;
-	} while (tmp != 0x2);
-
-	clk_set_rate(mout_apll, freq * 1000);
-
-	/* MUX_CORE_SEL = APLL */
-	clk_set_parent(moutcore, mout_apll);
-
-	do {
-		cpu_relax();
-		tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU);
-		tmp &= (0x7 << 16);
-	} while (tmp != (0x1 << 16));
-}
-
-static void exynos5250_set_frequency(unsigned int old_index,
-				  unsigned int new_index)
-{
-	if (old_index > new_index) {
-		set_clkdiv(new_index);
-		set_apll(new_index);
-	} else if (old_index < new_index) {
-		set_apll(new_index);
-		set_clkdiv(new_index);
-	}
-}
-
-int exynos5250_cpufreq_init(struct exynos_dvfs_info *info)
-{
-	unsigned long rate;
-
-	cpu_clk = clk_get(NULL, "armclk");
-	if (IS_ERR(cpu_clk))
-		return PTR_ERR(cpu_clk);
-
-	moutcore = clk_get(NULL, "mout_cpu");
-	if (IS_ERR(moutcore))
-		goto err_moutcore;
-
-	mout_mpll = clk_get(NULL, "mout_mpll");
-	if (IS_ERR(mout_mpll))
-		goto err_mout_mpll;
-
-	rate = clk_get_rate(mout_mpll) / 1000;
-
-	mout_apll = clk_get(NULL, "mout_apll");
-	if (IS_ERR(mout_apll))
-		goto err_mout_apll;
-
-	info->mpll_freq_khz = rate;
-	/* 800Mhz */
-	info->pll_safe_idx = L9;
-	info->cpu_clk = cpu_clk;
-	info->volt_table = exynos5250_volt_table;
-	info->freq_table = exynos5250_freq_table;
-	info->set_freq = exynos5250_set_frequency;
-
-	return 0;
-
-err_mout_apll:
-	clk_put(mout_mpll);
-err_mout_mpll:
-	clk_put(moutcore);
-err_moutcore:
-	clk_put(cpu_clk);
-
-	pr_err("%s: failed initialization\n", __func__);
-	return -EINVAL;
-}
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH v3 6/7] ARM: Exynos: switch to using generic cpufreq-cpu0 driver
From: Thomas Abraham @ 2014-02-07 15:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391788548-13056-1-git-send-email-thomas.ab@samsung.com>

From: Thomas Abraham <thomas.ab@samsung.com>

Remove the platform device instantiation for Exynos specific cpufreq
driver and add the platform device for cpufreq-cpu0 driver.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Reviewed-by: Lukasz Majewski <l.majewski@samsung.com>
---
 arch/arm/mach-exynos/common.c          |    5 -----
 arch/arm/mach-exynos/common.h          |    1 -
 arch/arm/mach-exynos/mach-exynos4-dt.c |    2 +-
 arch/arm/mach-exynos/mach-exynos5-dt.c |    2 +-
 4 files changed, 2 insertions(+), 8 deletions(-)

diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c
index f18be40..72ae5d3 100644
--- a/arch/arm/mach-exynos/common.c
+++ b/arch/arm/mach-exynos/common.c
@@ -303,11 +303,6 @@ void __init exynos_cpuidle_init(void)
 	platform_device_register(&exynos_cpuidle);
 }
 
-void __init exynos_cpufreq_init(void)
-{
-	platform_device_register_simple("exynos-cpufreq", -1, NULL, 0);
-}
-
 void __init exynos_init_late(void)
 {
 	if (of_machine_is_compatible("samsung,exynos5440"))
diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
index f76967b..0c31b34 100644
--- a/arch/arm/mach-exynos/common.h
+++ b/arch/arm/mach-exynos/common.h
@@ -22,7 +22,6 @@ void exynos_init_io(void);
 void exynos4_restart(enum reboot_mode mode, const char *cmd);
 void exynos5_restart(enum reboot_mode mode, const char *cmd);
 void exynos_cpuidle_init(void);
-void exynos_cpufreq_init(void);
 void exynos_init_late(void);
 
 void exynos_firmware_init(void);
diff --git a/arch/arm/mach-exynos/mach-exynos4-dt.c b/arch/arm/mach-exynos/mach-exynos4-dt.c
index d3e54b7..9a25320 100644
--- a/arch/arm/mach-exynos/mach-exynos4-dt.c
+++ b/arch/arm/mach-exynos/mach-exynos4-dt.c
@@ -22,8 +22,8 @@
 static void __init exynos4_dt_machine_init(void)
 {
 	exynos_cpuidle_init();
-	exynos_cpufreq_init();
 
+	platform_device_register_simple("cpufreq-cpu0", -1, NULL, 0);
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 }
 
diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c
index 37ea261..9dc3710 100644
--- a/arch/arm/mach-exynos/mach-exynos5-dt.c
+++ b/arch/arm/mach-exynos/mach-exynos5-dt.c
@@ -44,8 +44,8 @@ static void __init exynos5_dt_machine_init(void)
 	}
 
 	exynos_cpuidle_init();
-	exynos_cpufreq_init();
 
+	platform_device_register_simple("cpufreq-cpu0", -1, NULL, 0);
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 }
 
-- 
1.7.10.4

^ permalink raw reply related

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

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>
---
 .../devicetree/bindings/clock/exynos5250-clock.txt |    1 +
 arch/arm/boot/dts/exynos4210-origen.dts            |    6 +++
 arch/arm/boot/dts/exynos4210-trats.dts             |    6 +++
 arch/arm/boot/dts/exynos4210-universal_c210.dts    |    6 +++
 arch/arm/boot/dts/exynos4210.dtsi                  |   35 +++++++++++++++++
 arch/arm/boot/dts/exynos4212.dtsi                  |   18 +++++++++
 arch/arm/boot/dts/exynos4412-odroidx.dts           |    6 +++
 arch/arm/boot/dts/exynos4412-origen.dts            |    6 +++
 arch/arm/boot/dts/exynos4412-trats2.dts            |    6 +++
 arch/arm/boot/dts/exynos4412.dtsi                  |   31 +++++++++++++++
 arch/arm/boot/dts/exynos4x12.dtsi                  |   36 +++++++++++++++++
 arch/arm/boot/dts/exynos5250-arndale.dts           |    6 +++
 arch/arm/boot/dts/exynos5250-cros-common.dtsi      |    6 +++
 arch/arm/boot/dts/exynos5250-smdk5250.dts          |    6 +++
 arch/arm/boot/dts/exynos5250.dtsi                  |   41 ++++++++++++++++++++
 15 files changed, 216 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
index 9ca818e..4513cbb 100644
--- a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
+++ b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
@@ -43,6 +43,7 @@ clock which they consume.
   ----------------------------
 
   fin_pll		1
+  armclk		12
 
   [Clock Gate for Special Clocks]
 
diff --git a/arch/arm/boot/dts/exynos4210-origen.dts b/arch/arm/boot/dts/exynos4210-origen.dts
index 2aa13cb..dd17e93 100644
--- a/arch/arm/boot/dts/exynos4210-origen.dts
+++ b/arch/arm/boot/dts/exynos4210-origen.dts
@@ -32,6 +32,12 @@
 		bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc";
 	};
 
+	cpus {
+		cpu at 0 {
+			cpu0-supply = <&buck1_reg>;
+		};
+	};
+
 	regulators {
 		compatible = "simple-bus";
 		#address-cells = <1>;
diff --git a/arch/arm/boot/dts/exynos4210-trats.dts b/arch/arm/boot/dts/exynos4210-trats.dts
index 63cc571..25487d76 100644
--- a/arch/arm/boot/dts/exynos4210-trats.dts
+++ b/arch/arm/boot/dts/exynos4210-trats.dts
@@ -30,6 +30,12 @@
 		bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5";
 	};
 
+	cpus {
+		cpu: cpu at 0 {
+			cpu0-supply = <&varm_breg>;
+		};
+	};
+
 	regulators {
 		compatible = "simple-bus";
 
diff --git a/arch/arm/boot/dts/exynos4210-universal_c210.dts b/arch/arm/boot/dts/exynos4210-universal_c210.dts
index d2e3f5f..74d5a70 100644
--- a/arch/arm/boot/dts/exynos4210-universal_c210.dts
+++ b/arch/arm/boot/dts/exynos4210-universal_c210.dts
@@ -28,6 +28,12 @@
 		bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rw rootwait earlyprintk panic=5 maxcpus=1";
 	};
 
+	cpus {
+		cpu: cpu at 0 {
+			cpu0-supply = <&vdd_arm_reg>;
+		};
+	};
+
 	mct at 10050000 {
 		compatible = "none";
 	};
diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi
index 48ecd7a..a774247 100644
--- a/arch/arm/boot/dts/exynos4210.dtsi
+++ b/arch/arm/boot/dts/exynos4210.dtsi
@@ -36,6 +36,33 @@
 		reg = <0x10023CA0 0x20>;
 	};
 
+	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 = <
+				1200000 1250000
+				1000000 1150000
+				800000	1075000
+				500000	975000
+				400000	975000
+				200000	950000
+			>;
+		};
+
+		cpu at 1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a9";
+			reg = <1>;
+		};
+	};
+
 	gic: interrupt-controller at 10490000 {
 		cpu-offset = <0x8000>;
 	};
@@ -73,6 +100,14 @@
 		compatible = "samsung,exynos4210-clock";
 		reg = <0x10030000 0x20000>;
 		#clock-cells = <1>;
+
+		samsung,armclk-cells = <9>;
+		samsung,armclk-divider-table = <1200000 3 7 3 4 1 7 5 0>,
+					       <1000000 3 7 3 4 1 7 4 0>,
+					       < 800000 3 7 3 3 1 7 3 0>,
+					       < 500000 3 7 3 3 1 7 3 0>,
+					       < 400000 3 7 3 3 1 7 3 0>,
+					       < 200000 1 3 1 1 1 0 3 0>;
 	};
 
 	pmu {
diff --git a/arch/arm/boot/dts/exynos4212.dtsi b/arch/arm/boot/dts/exynos4212.dtsi
index 94a43f9..efa8f25 100644
--- a/arch/arm/boot/dts/exynos4212.dtsi
+++ b/arch/arm/boot/dts/exynos4212.dtsi
@@ -22,6 +22,24 @@
 / {
 	compatible = "samsung,exynos4212";
 
+	clock: clock-controller at 10030000 {
+		samsung,armclk-cells = <9>;
+		samsung,armclk-divider-table = <1500000 3 7 0 6 1 2 6 0>,
+					       <1400000 3 7 0 6 1 2 6 0>,
+					       <1300000 3 7 0 5 1 2 5 0>,
+					       <1200000 3 7 0 5 1 2 5 0>,
+					       <1100000 3 6 0 4 1 2 4 0>,
+					       <1000000 2 5 0 4 1 1 4 0>,
+					       < 900000 2 5 0 3 1 1 3 0>,
+					       < 800000 2 5 0 3 1 1 3 0>,
+					       < 700000 2 4 0 3 1 1 3 0>,
+					       < 600000 2 4 0 3 1 1 3 0>,
+					       < 500000 2 4 0 3 1 1 3 0>,
+					       < 400000 2 4 0 3 1 1 3 0>,
+					       < 300000 2 4 0 2 1 1 3 0>,
+					       < 200000 1 3 0 1 1 1 3 0>;
+	};
+
 	gic: interrupt-controller at 10490000 {
 		cpu-offset = <0x8000>;
 	};
diff --git a/arch/arm/boot/dts/exynos4412-odroidx.dts b/arch/arm/boot/dts/exynos4412-odroidx.dts
index 9804fcb..04c14dc 100644
--- a/arch/arm/boot/dts/exynos4412-odroidx.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidx.dts
@@ -22,6 +22,12 @@
 		reg = <0x40000000 0x40000000>;
 	};
 
+	cpus {
+		cpu at 0 {
+			cpu0-supply = <&buck2_reg>;
+		};
+	};
+
 	leds {
 		compatible = "gpio-leds";
 		led1 {
diff --git a/arch/arm/boot/dts/exynos4412-origen.dts b/arch/arm/boot/dts/exynos4412-origen.dts
index 6bc0539..89bcf2c 100644
--- a/arch/arm/boot/dts/exynos4412-origen.dts
+++ b/arch/arm/boot/dts/exynos4412-origen.dts
@@ -27,6 +27,12 @@
 		bootargs ="console=ttySAC2,115200";
 	};
 
+	cpus {
+		cpu at 0 {
+			cpu0-supply = <&buck2_reg>;
+		};
+	};
+
 	firmware at 0203F000 {
 		compatible = "samsung,secure-firmware";
 		reg = <0x0203F000 0x1000>;
diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts
index 4f851cc..4a4d446 100644
--- a/arch/arm/boot/dts/exynos4412-trats2.dts
+++ b/arch/arm/boot/dts/exynos4412-trats2.dts
@@ -31,6 +31,12 @@
 		bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5";
 	};
 
+	cpus {
+		cpu at 0 {
+			cpu0-supply = <&buck2_reg>;
+		};
+	};
+
 	firmware at 0204F000 {
 		compatible = "samsung,secure-firmware";
 		reg = <0x0204F000 0x1000>;
diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi
index 87b339c..f7efdbb 100644
--- a/arch/arm/boot/dts/exynos4412.dtsi
+++ b/arch/arm/boot/dts/exynos4412.dtsi
@@ -22,6 +22,37 @@
 / {
 	compatible = "samsung,exynos4412";
 
+	cpus {
+		cpu at 2 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a9";
+			reg = <2>;
+		};
+		cpu at 3 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a9";
+			reg = <3>;
+		};
+	};
+
+	clock: clock-controller at 10030000 {
+		samsung,armclk-cells = <10>;
+		samsung,armclk-divider-table = <1500000 3 7 0 6 1 2 6 0 7>,
+					       <1400000 3 7 0 6 1 2 6 0 6>,
+					       <1300000 3 7 0 5 1 2 5 0 6>,
+					       <1200000 3 7 0 5 1 2 5 0 5>,
+					       <1100000 3 6 0 4 1 2 4 0 5>,
+					       <1000000 2 5 0 4 1 1 4 0 4>,
+					       < 900000 2 5 0 3 1 1 3 0 4>,
+					       < 800000 2 5 0 3 1 1 3 0 3>,
+					       < 700000 2 4 0 3 1 1 3 0 3>,
+					       < 600000 2 4 0 3 1 1 3 0 2>,
+					       < 500000 2 4 0 3 1 1 3 0 2>,
+					       < 400000 2 4 0 3 1 1 3 0 1>,
+					       < 300000 2 4 0 2 1 1 3 0 1>,
+					       < 200000 1 3 0 1 1 1 3 0 0>;
+	};
+
 	gic: interrupt-controller at 10490000 {
 		cpu-offset = <0x4000>;
 	};
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>;
+		};
+		cpu at 1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a9";
+			reg = <1>;
+		};
+	};
+
 	pd_isp: isp-power-domain at 10023CA0 {
 		compatible = "samsung,exynos4210-pd";
 		reg = <0x10023CA0 0x20>;
diff --git a/arch/arm/boot/dts/exynos5250-arndale.dts b/arch/arm/boot/dts/exynos5250-arndale.dts
index b42e658..4716eef 100644
--- a/arch/arm/boot/dts/exynos5250-arndale.dts
+++ b/arch/arm/boot/dts/exynos5250-arndale.dts
@@ -25,6 +25,12 @@
 		bootargs = "console=ttySAC2,115200";
 	};
 
+	cpus {
+		cpu at 0 {
+			cpu0-supply = <&buck2_reg>;
+		};
+	};
+
 	codec at 11000000 {
 		samsung,mfc-r = <0x43000000 0x800000>;
 		samsung,mfc-l = <0x51000000 0x800000>;
diff --git a/arch/arm/boot/dts/exynos5250-cros-common.dtsi b/arch/arm/boot/dts/exynos5250-cros-common.dtsi
index 2c1560d..4bde756 100644
--- a/arch/arm/boot/dts/exynos5250-cros-common.dtsi
+++ b/arch/arm/boot/dts/exynos5250-cros-common.dtsi
@@ -19,6 +19,12 @@
 	chosen {
 	};
 
+	cpus {
+		cpu at 0 {
+			cpu0-supply = <&buck2_reg>;
+		};
+	};
+
 	pinctrl at 11400000 {
 		/*
 		 * Disabled pullups since external part has its own pullups and
diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts
index 3e69837..6ce964f 100644
--- a/arch/arm/boot/dts/exynos5250-smdk5250.dts
+++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts
@@ -27,6 +27,12 @@
 		bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc";
 	};
 
+	cpus {
+		cpu at 0 {
+			cpu0-supply = <&buck2_reg>;
+		};
+	};
+
 	i2c at 12C60000 {
 		samsung,i2c-sda-delay = <100>;
 		samsung,i2c-max-bus-freq = <20000>;
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index b7dec41..0a8545e 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -61,6 +61,29 @@
 			compatible = "arm,cortex-a15";
 			reg = <0>;
 			clock-frequency = <1700000000>;
+
+			clocks = <&clock 12>;
+			clock-names = "cpu";
+
+			operating-points = <
+				1700000 1300000
+				1600000 1250000
+				1500000 1225000
+				1400000 1200000
+				1300000 1150000
+				1200000 1125000
+				1100000 1100000
+				1000000 1075000
+				 900000 1050000
+				 800000 1025000
+				 700000 1012500
+				 600000 1000000
+				 500000  975000
+				 400000  950000
+				 300000  937500
+				 200000  925000
+			>;
+			clock-latency = <200000>;
 		};
 		cpu at 1 {
 			device_type = "cpu";
@@ -84,6 +107,24 @@
 		compatible = "samsung,exynos5250-clock";
 		reg = <0x10010000 0x30000>;
 		#clock-cells = <1>;
+
+		samsung,armclk-cells = <9>;
+		samsung,armclk-divider-table = <1700000 3 7 7 7 3 5 0 2>,
+					       <1600000 3 7 7 7 1 4 0 2>,
+					       <1500000 2 7 7 7 1 4 0 2>,
+					       <1400000 2 7 7 6 1 4 0 2>,
+					       <1300000 2 7 7 6 1 3 0 2>,
+					       <1200000 2 7 7 5 1 3 0 2>,
+					       <1100000 3 7 7 5 1 3 0 2>,
+					       <1000000 1 7 7 4 1 2 0 2>,
+					       < 900000 1 7 7 4 1 2 0 2>,
+					       < 800000 1 7 7 4 1 2 0 2>,
+					       < 700000 1 7 7 3 1 1 0 2>,
+					       < 600000 1 7 7 3 1 1 0 2>,
+					       < 500000 1 7 7 2 1 1 0 2>,
+					       < 400000 1 7 7 2 1 1 0 2>,
+					       < 300000 1 7 7 1 1 1 0 2>,
+					       < 200000 1 7 7 1 1 1 0 2>;
 	};
 
 	clock_audss: audss-clock-controller at 3810000 {
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH v3 4/7] clk: exynos: use cpu-clock provider type to represent arm clock
From: Thomas Abraham @ 2014-02-07 15:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391788548-13056-1-git-send-email-thomas.ab@samsung.com>

From: Thomas Abraham <thomas.ab@samsung.com>

With the addition of the new Samsung specific cpu-clock type, the
arm clock can be represented as a cpu-clock type and the independent
clock blocks that made up the arm clock can be removed.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
---
 drivers/clk/samsung/clk-exynos4.c      |   23 +++++++----------------
 drivers/clk/samsung/clk-exynos5250.c   |   10 +++++-----
 include/dt-bindings/clock/exynos5250.h |    1 +
 3 files changed, 13 insertions(+), 21 deletions(-)

diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c
index 010f071..a52c622 100644
--- a/drivers/clk/samsung/clk-exynos4.c
+++ b/drivers/clk/samsung/clk-exynos4.c
@@ -336,7 +336,6 @@ static struct samsung_mux_clock exynos4210_mux_clks[] __initdata = {
 	MUX(0, "mout_fimd1", group1_p4210, E4210_SRC_LCD1, 0, 4),
 	MUX(0, "mout_mipi1", group1_p4210, E4210_SRC_LCD1, 12, 4),
 	MUX(CLK_SCLK_MPLL, "sclk_mpll", mout_mpll_p, SRC_CPU, 8, 1),
-	MUX(CLK_MOUT_CORE, "mout_core", mout_core_p4210, SRC_CPU, 16, 1),
 	MUX(CLK_SCLK_VPLL, "sclk_vpll", sclk_vpll_p4210, SRC_TOP0, 8, 1),
 	MUX(CLK_MOUT_FIMC0, "mout_fimc0", group1_p4210, SRC_CAM, 0, 4),
 	MUX(CLK_MOUT_FIMC1, "mout_fimc1", group1_p4210, SRC_CAM, 4, 4),
@@ -395,7 +394,6 @@ static struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = {
 	MUX(0, "mout_jpeg", mout_jpeg_p, E4X12_SRC_CAM1, 8, 1),
 	MUX(CLK_SCLK_MPLL, "sclk_mpll", mout_mpll_p, SRC_DMC, 12, 1),
 	MUX(CLK_SCLK_VPLL, "sclk_vpll", mout_vpll_p, SRC_TOP0, 8, 1),
-	MUX(CLK_MOUT_CORE, "mout_core", mout_core_p4x12, SRC_CPU, 16, 1),
 	MUX(CLK_MOUT_FIMC0, "mout_fimc0", group1_p4x12, SRC_CAM, 0, 4),
 	MUX(CLK_MOUT_FIMC1, "mout_fimc1", group1_p4x12, SRC_CAM, 4, 4),
 	MUX(CLK_MOUT_FIMC2, "mout_fimc2", group1_p4x12, SRC_CAM, 8, 4),
@@ -437,8 +435,6 @@ static struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = {
 
 /* list of divider clocks supported in all exynos4 soc's */
 static struct samsung_div_clock exynos4_div_clks[] __initdata = {
-	DIV(0, "div_core", "mout_core", DIV_CPU0, 0, 3),
-	DIV(0, "div_core2", "div_core", DIV_CPU0, 28, 3),
 	DIV(0, "div_fimc0", "mout_fimc0", DIV_CAM, 0, 4),
 	DIV(0, "div_fimc1", "mout_fimc1", DIV_CAM, 4, 4),
 	DIV(0, "div_fimc2", "mout_fimc2", DIV_CAM, 8, 4),
@@ -484,8 +480,8 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = {
 	DIV(0, "div_spi_pre2", "div_spi2", DIV_PERIL2, 8, 8),
 	DIV(0, "div_audio1", "mout_audio1", DIV_PERIL4, 0, 4),
 	DIV(0, "div_audio2", "mout_audio2", DIV_PERIL4, 16, 4),
-	DIV(CLK_ARM_CLK, "arm_clk", "div_core2", DIV_CPU0, 28, 3),
-	DIV(CLK_SCLK_APLL, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3),
+	DIV_F(CLK_SCLK_APLL, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3,
+			CLK_GET_RATE_NOCACHE, 0),
 	DIV_F(0, "div_mipi_pre0", "div_mipi0", DIV_LCD0, 20, 4,
 			CLK_SET_RATE_PARENT, 0),
 	DIV_F(0, "div_mmc_pre0", "div_mmc0", DIV_FSYS1, 8, 8,
@@ -868,12 +864,6 @@ static struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = {
 		0),
 };
 
-static struct samsung_clock_alias exynos4_aliases[] __initdata = {
-	ALIAS(CLK_MOUT_CORE, NULL, "moutcore"),
-	ALIAS(CLK_ARM_CLK, NULL, "armclk"),
-	ALIAS(CLK_SCLK_APLL, NULL, "mout_apll"),
-};
-
 static struct samsung_clock_alias exynos4210_aliases[] __initdata = {
 	ALIAS(CLK_SCLK_MPLL, NULL, "mout_mpll"),
 };
@@ -1111,6 +1101,8 @@ static void __init exynos4_clk_init(struct device_node *np,
 			ARRAY_SIZE(exynos4210_gate_clks));
 		samsung_clk_register_alias(exynos4210_aliases,
 			ARRAY_SIZE(exynos4210_aliases));
+		samsung_register_arm_clock(CLK_ARM_CLK, mout_core_p4210,
+			ARRAY_SIZE(mout_core_p4210), reg_base, np, NULL);
 	} else {
 		samsung_clk_register_mux(exynos4x12_mux_clks,
 			ARRAY_SIZE(exynos4x12_mux_clks));
@@ -1120,17 +1112,16 @@ static void __init exynos4_clk_init(struct device_node *np,
 			ARRAY_SIZE(exynos4x12_gate_clks));
 		samsung_clk_register_alias(exynos4x12_aliases,
 			ARRAY_SIZE(exynos4x12_aliases));
+		samsung_register_arm_clock(CLK_ARM_CLK, mout_core_p4x12,
+			ARRAY_SIZE(mout_core_p4x12), reg_base, np, NULL);
 	}
 
-	samsung_clk_register_alias(exynos4_aliases,
-			ARRAY_SIZE(exynos4_aliases));
-
 	pr_info("%s clocks: sclk_apll = %ld, sclk_mpll = %ld\n"
 		"\tsclk_epll = %ld, sclk_vpll = %ld, arm_clk = %ld\n",
 		exynos4_soc == EXYNOS4210 ? "Exynos4210" : "Exynos4x12",
 		_get_rate("sclk_apll"),	_get_rate("sclk_mpll"),
 		_get_rate("sclk_epll"), _get_rate("sclk_vpll"),
-		_get_rate("arm_clk"));
+		_get_rate("armclk"));
 }
 
 
diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c
index ff4beeb..59742bb 100644
--- a/drivers/clk/samsung/clk-exynos5250.c
+++ b/drivers/clk/samsung/clk-exynos5250.c
@@ -219,7 +219,6 @@ static struct samsung_mux_clock exynos5250_mux_clks[] __initdata = {
 	 */
 	MUX_FA(0, "mout_apll", mout_apll_p, SRC_CPU, 0, 1,
 					CLK_SET_RATE_PARENT, 0, "mout_apll"),
-	MUX_A(0, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1, "mout_cpu"),
 
 	/*
 	 * CMU_CORE
@@ -298,9 +297,8 @@ static struct samsung_div_clock exynos5250_div_clks[] __initdata = {
 	/*
 	 * CMU_CPU
 	 */
-	DIV(0, "div_arm", "mout_cpu", DIV_CPU0, 0, 3),
-	DIV(0, "div_apll", "mout_apll", DIV_CPU0, 24, 3),
-	DIV_A(0, "div_arm2", "div_arm", DIV_CPU0, 28, 3, "armclk"),
+	DIV_F(0, "div_apll", "mout_apll", DIV_CPU0, 24, 3,
+			CLK_GET_RATE_NOCACHE, 0),
 
 	/*
 	 * CMU_TOP
@@ -684,8 +682,10 @@ static void __init exynos5250_clk_init(struct device_node *np)
 			ARRAY_SIZE(exynos5250_div_clks));
 	samsung_clk_register_gate(exynos5250_gate_clks,
 			ARRAY_SIZE(exynos5250_gate_clks));
+	samsung_register_arm_clock(CLK_ARM_CLK, mout_cpu_p,
+			ARRAY_SIZE(mout_cpu_p), reg_base, np, NULL);
 
 	pr_info("Exynos5250: clock setup completed, armclk=%ld\n",
-			_get_rate("div_arm2"));
+			_get_rate("armclk"));
 }
 CLK_OF_DECLARE(exynos5250_clk, "samsung,exynos5250-clock", exynos5250_clk_init);
diff --git a/include/dt-bindings/clock/exynos5250.h b/include/dt-bindings/clock/exynos5250.h
index 922f2dc..59a10fb 100644
--- a/include/dt-bindings/clock/exynos5250.h
+++ b/include/dt-bindings/clock/exynos5250.h
@@ -21,6 +21,7 @@
 #define CLK_FOUT_CPLL		6
 #define CLK_FOUT_EPLL		7
 #define CLK_FOUT_VPLL		8
+#define CLK_ARM_CLK		12
 
 /* gate for special clocks (sclk) */
 #define CLK_SCLK_CAM_BAYER	128
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH v3 3/7] Documentation: devicetree: add cpu clock configuration data binding for Exynos4/5
From: Thomas Abraham @ 2014-02-07 15:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391788548-13056-1-git-send-email-thomas.ab@samsung.com>

From: Thomas Abraham <thomas.ab@samsung.com>

The clock blocks within the CMU_CPU clock domain are put together into a
new composite clock type called the cpu clock. This clock type requires
configuration data that will be atomically programmed in the multiple
clock blocks encapsulated within the cpu clock type when the parent clock
frequency is changed. This configuration data is held in the clock controller
node. Update clock binding documentation about this configuration data format
for Samsung Exynos4 and Exynos5 platforms.

Cc: Tomasz Figa <t.figa@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>
Cc: <devicetree@vger.kernel.org>
Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
---
 .../devicetree/bindings/clock/exynos4-clock.txt    |   37 +++++++++++++++++++
 .../devicetree/bindings/clock/exynos5250-clock.txt |   38 ++++++++++++++++++++
 2 files changed, 75 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/exynos4-clock.txt b/Documentation/devicetree/bindings/clock/exynos4-clock.txt
index a2ac2d9..b505e17 100644
--- a/Documentation/devicetree/bindings/clock/exynos4-clock.txt
+++ b/Documentation/devicetree/bindings/clock/exynos4-clock.txt
@@ -15,6 +15,35 @@ Required Properties:
 
 - #clock-cells: should be 1.
 
+- samsung,armclk-divider-table: when the frequency of the APLL is changed
+  the divider clocks in CMU_CPU clock domain also need to be updated. These
+  divider clocks have SoC specific divider clock output requirements for a
+  specific APLL clock speeds. When APLL clock rate is changed, these divider
+  clocks are reprogrammed with pre-determined values in order to maintain the
+  SoC specific divider clock outputs. This property lists the divider values
+  for divider clocks in the CMU_CPU block for supported APLL clock speeds.
+  The format of each entry included in the arm-frequency-table should be
+  as defined below
+
+  - for Exynos4210 and Exynos4212 based platforms:
+      cell #1: arm clock parent frequency
+      cell #2 ~ cell 9#: value of clock divider in the following order
+	        corem0_ratio, corem1_ratio, periph_ratio, atb_ratio,
+		pclk_dbg_ratio, apll_ratio, copy_ratio, hpm_ratio.
+
+  - for Exynos4412 based platforms:
+      cell #1: expected arm clock parent frequency
+      cell #2 ~ cell #10: value of clock divider in the following order
+	       corem0_ratio, corem1_ratio, periph_ratio, atb_ratio,
+               pclk_dbg_ratio, apll_ratio, copy_ratio, hpm_ratio, cores_ratio
+
+- samsung,armclk-cells: defines the number of cells in
+  samsung,armclk-divider-table property. The value of this property depends on
+  the SoC type.
+
+  - for Exynos4210 and Exynos4212: the value should be 9.
+  - for Exynos4412: the value should be 10.
+
 The following is the list of clocks generated by the controller. Each clock is
 assigned an identifier and client nodes use this identifier to specify the
 clock which they consume. Some of the clocks are available only on a particular
@@ -275,6 +304,14 @@ Example 1: An example of a clock controller node is listed below.
 		compatible = "samsung,exynos4210-clock";
 		reg = <0x10030000 0x20000>;
 		#clock-cells = <1>;
+
+		samsung,armclk-cells = <9>;
+		samsung,armclk-divider-table = <1200000 3 7 3 4 1 7 5 0>,
+					       <1000000 3 7 3 4 1 7 4 0>,
+					       < 800000 3 7 3 3 1 7 3 0>,
+					       < 500000 3 7 3 3 1 7 3 0>,
+					       < 400000 3 7 3 3 1 7 3 0>,
+					       < 200000 1 3 1 1 1 0 3 0>;
 	};
 
 Example 2: UART controller node that consumes the clock generated by the clock
diff --git a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
index 72ce617..9ca818e 100644
--- a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
+++ b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
@@ -13,6 +13,25 @@ Required Properties:
 
 - #clock-cells: should be 1.
 
+- samsung,armclk-divider-table: when the frequency of the APLL is changed
+  the divider clocks in CMU_CPU clock domain also need to be updated. These
+  divider clocks have SoC specific divider clock output requirements for a
+  specific APLL clock speeds. When APLL clock rate is changed, these divider
+  clocks are reprogrammed with pre-determined values in order to maintain the
+  SoC specific divider clock outputs. This property lists the divider values
+  for divider clocks in the CMU_CPU block for supported APLL clock speeds.
+  The format of each entry included in the arm-frequency-table should be
+  as defined below
+
+      cell #1: expected arm clock parent frequency
+      cell #2 ~ cell #9: value of clock divider in the following order
+	       cpud_ratio, acp_ratio, periph_ratio, atb_ratio,
+               pclk_dbg_ratio, apll_ratio, copy_ratio, hpm_ratio
+
+- samsung,armclk-cells: defines the number of cells in
+  samsung,armclk-divider-table property. The value of this property should be 9.
+
+
 The following is the list of clocks generated by the controller. Each clock is
 assigned an identifier and client nodes use this identifier to specify the
 clock which they consume.
@@ -177,6 +196,25 @@ Example 1: An example of a clock controller node is listed below.
 		compatible = "samsung,exynos5250-clock";
 		reg = <0x10010000 0x30000>;
 		#clock-cells = <1>;
+
+		samsung,armclk-cells = <9>;
+		samsung,armclk-divider-table = <1700000 3 7 7 7 3 5 0 2>,
+					       <1600000 3 7 7 7 1 4 0 2>,
+					       <1500000 2 7 7 7 1 4 0 2>,
+					       <1400000 2 7 7 6 1 4 0 2>,
+					       <1300000 2 7 7 6 1 3 0 2>,
+					       <1200000 2 7 7 5 1 3 0 2>,
+					       <1100000 3 7 7 5 1 3 0 2>,
+					       <1000000 1 7 7 4 1 2 0 2>,
+					       < 900000 1 7 7 4 1 2 0 2>,
+					       < 800000 1 7 7 4 1 2 0 2>,
+					       < 700000 1 7 7 3 1 1 0 2>,
+					       < 600000 1 7 7 3 1 1 0 2>,
+					       < 500000 1 7 7 2 1 1 0 2>,
+					       < 400000 1 7 7 2 1 1 0 2>,
+					       < 300000 1 7 7 1 1 1 0 2>,
+					       < 200000 1 7 7 1 1 1 0 2>;
+
 	};
 
 Example 2: UART controller node that consumes the clock generated by the clock
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH v3 2/7] clk: samsung: add infrastructure to register cpu clocks
From: Thomas Abraham @ 2014-02-07 15:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391788548-13056-1-git-send-email-thomas.ab@samsung.com>

From: Thomas Abraham <thomas.ab@samsung.com>

The CPU clock provider supplies the clock to the CPU clock domain. The
composition and organization of the CPU clock provider could vary among
Exynos SoCs. A CPU clock provider can be composed of clock mux, dividers
and gates. This patch defines a new clock type for CPU clock provider and
adds infrastructure to register the CPU clock providers for Samsung
platforms.

In addition to this, the arm cpu clock provider for Exynos4210 and
compatible SoCs is instantiated using the new cpu clock type. The clock
configuration data for this clock is obtained from device tree. This
implementation is reusable for Exynos4x12 and Exynos5250 SoCs as well.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
---
 drivers/clk/samsung/Makefile  |    2 +-
 drivers/clk/samsung/clk-cpu.c |  409 +++++++++++++++++++++++++++++++++++++++++
 drivers/clk/samsung/clk.h     |    5 +
 3 files changed, 415 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/samsung/clk-cpu.c

diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
index 8eb4799..e2b453f 100644
--- a/drivers/clk/samsung/Makefile
+++ b/drivers/clk/samsung/Makefile
@@ -2,7 +2,7 @@
 # Samsung Clock specific Makefile
 #
 
-obj-$(CONFIG_COMMON_CLK)	+= clk.o clk-pll.o
+obj-$(CONFIG_COMMON_CLK)	+= clk.o clk-pll.o clk-cpu.o
 obj-$(CONFIG_ARCH_EXYNOS4)	+= clk-exynos4.o
 obj-$(CONFIG_SOC_EXYNOS5250)	+= clk-exynos5250.o
 obj-$(CONFIG_SOC_EXYNOS5420)	+= clk-exynos5420.o
diff --git a/drivers/clk/samsung/clk-cpu.c b/drivers/clk/samsung/clk-cpu.c
new file mode 100644
index 0000000..673f620
--- /dev/null
+++ b/drivers/clk/samsung/clk-cpu.c
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This file contains the utility functions to register the cpu clocks
+ * for samsung platforms.
+*/
+
+#include <linux/errno.h>
+#include "clk.h"
+
+#define SRC_CPU			0x0
+#define STAT_CPU		0x200
+#define DIV_CPU0		0x300
+#define DIV_CPU1		0x304
+#define DIV_STAT_CPU0		0x400
+#define DIV_STAT_CPU1		0x404
+
+#define MAX_DIV			8
+
+#define EXYNOS4210_ARM_DIV1(base) ((readl(base + DIV_CPU0) & 0xf) + 1)
+#define EXYNOS4210_ARM_DIV2(base) (((readl(base + DIV_CPU0) >> 28) & 0xf) + 1)
+
+#define EXYNOS4210_DIV_CPU0(d5, d4, d3, d2, d1, d0)			\
+		((d5 << 24) | (d4 << 20) | (d3 << 16) | (d2 << 12) |	\
+		 (d1 << 8) | (d0 <<  4))
+#define EXYNOS4210_DIV_CPU1(d2, d1, d0)					\
+		((d2 << 8) | (d1 << 4) | (d0 << 0))
+
+#define EXYNOS4210_DIV1_HPM_MASK	((0x7 << 0) | (0x7 << 4))
+#define EXYNOS4210_MUX_HPM_MASK		(1 << 20)
+
+/**
+ * struct exynos4210_armclk_data: config data to setup exynos4210 cpu clocks.
+ * @prate:	frequency of the parent clock.
+ * @div0:	value to be programmed in the div_cpu0 register.
+ * @div1:	value to be programmed in the div_cpu1 register.
+ *
+ * This structure holds the divider configuration data for divider clocks
+ * belonging to the CMU_CPU clock domain. The parent frequency@which these
+ * divider values are vaild is specified in @prate.
+ */
+struct exynos4210_armclk_data {
+	unsigned long	prate;
+	unsigned int	div0;
+	unsigned int	div1;
+};
+
+/**
+ * struct samsung_cpuclk: information about clock supplied to a CPU core.
+ * @hw:		handle between ccf and cpu clock.
+ * @alt_parent:	alternate parent clock to use when switching the speed
+ *		of the primary parent clock.
+ * @ctrl_base:	base address of the clock controller.
+ * @offset:	offset from the ctrl_base address where the cpu clock div/mux
+ *		registers can be accessed.
+ * @clk_nb:	clock notifier registered for changes in clock speed of the
+ *		primary parent clock.
+ * @data:	optional data which the acutal instantiation of this clock
+ *		can use.
+ */
+struct samsung_cpuclk {
+	struct clk_hw		hw;
+	struct clk		*alt_parent;
+	void __iomem		*ctrl_base;
+	unsigned long		offset;
+	struct notifier_block	clk_nb;
+	void			*data;
+};
+
+#define to_samsung_cpuclk_hw(hw) container_of(hw, struct samsung_cpuclk, hw)
+#define to_samsung_cpuclk_nb(nb) container_of(nb, struct samsung_cpuclk, clk_nb)
+
+/**
+ * struct samsung_cpuclk_soc_data: soc specific data for cpu clocks.
+ * @parser:	pointer to a function that can parse SoC specific data.
+ * @ops:	clock operations to be used for this clock.
+ * @offset:	optional offset from base of clock controller register base, to
+ *		be used when accessing clock controller registers related to the
+ *		cpu clock.
+ * @clk_cb:	the clock notifier callback to be called for changes in the
+ *		clock rate of the primary parent clock.
+ *
+ * This structure provides SoC specific data for ARM clocks. Based on
+ * the compatible value of the clock controller node, the value of the
+ * fields in this structure can be populated.
+ */
+struct samsung_cpuclk_soc_data {
+	int (*parser)(struct device_node *, void **);
+	const struct clk_ops *ops;
+	unsigned int offset;
+	int (*clk_cb)(struct notifier_block *nb, unsigned long evt, void *data);
+};
+
+/* common round rate callback useable for all types of cpu clocks */
+static long samsung_cpuclk_round_rate(struct clk_hw *hw,
+			unsigned long drate, unsigned long *prate)
+{
+	struct clk *parent = __clk_get_parent(hw->clk);
+	unsigned long max_prate = __clk_round_rate(parent, UINT_MAX);
+	unsigned long t_prate, best_div = 1;
+	unsigned long delta, min_delta = UINT_MAX;
+
+	do {
+		t_prate = __clk_round_rate(parent, drate * best_div);
+		delta = drate - (t_prate / best_div);
+		if (delta < min_delta) {
+			*prate = t_prate;
+			min_delta = delta;
+		}
+		if (!delta)
+			break;
+		best_div++;
+	} while ((drate * best_div) < max_prate && best_div <= MAX_DIV);
+
+	return t_prate / best_div;
+}
+
+static unsigned long _calc_div(unsigned long prate, unsigned long drate)
+{
+	unsigned long div = prate / drate;
+
+	WARN_ON(div >= MAX_DIV);
+	return (!(prate % drate)) ? div-- : div;
+}
+
+/* helper function to register a cpu clock */
+static int __init samsung_cpuclk_register(unsigned int lookup_id,
+		const char *name, const char **parents,
+		unsigned int num_parents, void __iomem *base,
+		const struct samsung_cpuclk_soc_data *soc_data,
+		struct device_node *np, const struct clk_ops *ops)
+{
+	struct samsung_cpuclk *cpuclk;
+	struct clk_init_data init;
+	struct clk *clk;
+	int ret;
+
+	cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
+	if (!cpuclk) {
+		pr_err("%s: could not allocate memory for %s clock\n",
+					__func__, name);
+		return -ENOMEM;
+	}
+
+	init.name = name;
+	init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT;
+	init.parent_names = parents;
+	init.num_parents = 1;
+	init.ops = ops;
+
+	cpuclk->hw.init = &init;
+	cpuclk->ctrl_base = base;
+
+	if (soc_data && soc_data->parser) {
+		ret = soc_data->parser(np, &cpuclk->data);
+		if (ret) {
+			pr_err("%s: error %d in parsing %s clock data",
+					__func__, ret, name);
+			ret = -EINVAL;
+			goto free_cpuclk;
+		}
+		cpuclk->offset = soc_data->offset;
+		init.ops = soc_data->ops;
+	}
+
+	if (soc_data && soc_data->clk_cb) {
+		cpuclk->clk_nb.notifier_call = soc_data->clk_cb;
+		if (clk_notifier_register(__clk_lookup(parents[0]),
+				&cpuclk->clk_nb)) {
+			pr_err("%s: failed to register clock notifier for %s\n",
+					__func__, name);
+			goto free_cpuclk_data;
+		}
+	}
+
+	if (num_parents == 2) {
+		cpuclk->alt_parent = __clk_lookup(parents[1]);
+		if (!cpuclk->alt_parent) {
+			pr_err("%s: could not lookup alternate parent %s\n",
+					__func__, parents[1]);
+			ret = -EINVAL;
+			goto free_cpuclk_data;
+		}
+	}
+
+	clk = clk_register(NULL, &cpuclk->hw);
+	if (IS_ERR(clk)) {
+		pr_err("%s: could not register cpuclk %s\n", __func__,	name);
+		ret = PTR_ERR(clk);
+		goto free_cpuclk_data;
+	}
+
+	samsung_clk_add_lookup(clk, lookup_id);
+	return 0;
+
+free_cpuclk_data:
+	kfree(cpuclk->data);
+free_cpuclk:
+	kfree(cpuclk);
+	return ret;
+}
+
+static inline void _exynos4210_set_armclk_div(void __iomem *base,
+			unsigned long div)
+{
+	writel((readl(base + DIV_CPU0) & ~0xf) | div, base + DIV_CPU0);
+	while (readl(base + DIV_STAT_CPU0) != 0)
+		;
+}
+
+static unsigned long exynos4210_armclk_recalc_rate(struct clk_hw *hw,
+				unsigned long parent_rate)
+{
+	struct samsung_cpuclk *armclk = to_samsung_cpuclk_hw(hw);
+	void __iomem *base = armclk->ctrl_base + armclk->offset;
+
+	return parent_rate / EXYNOS4210_ARM_DIV1(base) /
+				EXYNOS4210_ARM_DIV2(base);
+}
+
+/*
+ * This clock notifier is called when the frequency of the parent clock
+ * of armclk is to be changed. This notifier handles the setting up all
+ * the divider clocks, remux to temporary parent and handling the safe
+ * frequency levels when using temporary parent.
+ */
+static int exynos4210_armclk_notifier_cb(struct notifier_block *nb,
+				unsigned long event, void *data)
+{
+	struct clk_notifier_data *ndata = data;
+	struct samsung_cpuclk *armclk = to_samsung_cpuclk_nb(nb);
+	struct exynos4210_armclk_data *armclk_data;
+	unsigned long alt_prate, alt_div, div0, div1, mux_reg;
+	void __iomem *base;
+	bool need_safe_freq;
+
+	armclk_data = armclk->data;
+	base = armclk->ctrl_base + armclk->offset;
+	alt_prate = clk_get_rate(armclk->alt_parent);
+
+	if (event == POST_RATE_CHANGE)
+		goto e4210_armclk_post_rate_change;
+
+	/* pre-rate change. find out the divider values to use for clock data */
+	while (armclk_data->prate != ndata->new_rate) {
+		if (armclk_data->prate == 0)
+			return NOTIFY_BAD;
+		armclk_data++;
+	}
+
+	div0 = armclk_data->div0;
+	div1 = armclk_data->div1;
+	if (readl(base + SRC_CPU) & EXYNOS4210_MUX_HPM_MASK) {
+		div1 = readl(base + DIV_CPU1) & EXYNOS4210_DIV1_HPM_MASK;
+		div1 |= ((armclk_data->div1) & ~EXYNOS4210_DIV1_HPM_MASK);
+	}
+
+	/*
+	 * if the new and old parent clock speed is less than the clock speed
+	 * of the alternate parent, then it should be ensured that at no point
+	 * the armclk speed is more than the old_prate until the dividers are
+	 * set.
+	 */
+	need_safe_freq = ndata->old_rate < alt_prate &&
+				ndata->new_rate < alt_prate;
+	if (need_safe_freq) {
+		alt_div = _calc_div(alt_prate, ndata->old_rate);
+		_exynos4210_set_armclk_div(base, alt_div);
+		div0 |= alt_div;
+	}
+
+	mux_reg = readl(base + SRC_CPU);
+	writel(mux_reg | (1 << 16), base + SRC_CPU);
+	while (((readl(base + STAT_CPU) >> 16) & 0x7) != 2)
+		;
+
+	writel(div0, base + DIV_CPU0);
+	while (readl(base + DIV_STAT_CPU0) != 0)
+		;
+	writel(div1, base + DIV_CPU1);
+	while (readl(base + DIV_STAT_CPU1) != 0)
+		;
+	return NOTIFY_OK;
+
+e4210_armclk_post_rate_change:
+	/* post-rate change event, re-mux back to primary parent */
+	mux_reg = readl(base + SRC_CPU);
+	writel(mux_reg & ~(1 << 16), base + SRC_CPU);
+	while (((readl(base + STAT_CPU) >> 16) & 0x7) != 1)
+			;
+
+	return NOTIFY_OK;
+}
+
+static int exynos4210_armclk_set_rate(struct clk_hw *hw, unsigned long drate,
+					unsigned long prate)
+{
+	struct samsung_cpuclk *armclk = to_samsung_cpuclk_hw(hw);
+	void __iomem *base = armclk->ctrl_base + armclk->offset;
+	unsigned long div;
+
+	div = drate < prate ? _calc_div(prate, drate) : 0;
+	_exynos4210_set_armclk_div(base, div);
+	return 0;
+}
+
+static const struct clk_ops exynos4210_armclk_clk_ops = {
+	.recalc_rate = exynos4210_armclk_recalc_rate,
+	.round_rate = samsung_cpuclk_round_rate,
+	.set_rate = exynos4210_armclk_set_rate,
+};
+
+/*
+ * parse divider configuration data from dt for all the cpu clock domain
+ * clocks in exynos4210 and compatible SoC's.
+ */
+static int __init exynos4210_armclk_parser(struct device_node *np, void **data)
+{
+	struct exynos4210_armclk_data *tdata;
+	unsigned long cfg[10], row, col;
+	const struct property *prop;
+	const __be32 *val;
+	u32 cells;
+	int ret;
+
+	if (of_property_read_u32(np, "samsung,armclk-cells", &cells))
+		return -EINVAL;
+	prop = of_find_property(np, "samsung,armclk-divider-table", NULL);
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -EINVAL;
+	if ((prop->length / sizeof(u32)) % cells)
+		return -EINVAL;
+	row = ((prop->length / sizeof(u32)) / cells) + 1;
+
+	*data = kzalloc(sizeof(*tdata) * row, GFP_KERNEL);
+	if (!*data)
+		ret = -ENOMEM;
+	tdata = *data;
+
+	val = prop->value;
+	for (; row > 1; row--, tdata++) {
+		for (col = 0; col < cells; col++)
+			cfg[col] = be32_to_cpup(val++);
+
+		tdata->prate = cfg[0] * 1000;
+		tdata->div0 = EXYNOS4210_DIV_CPU0(cfg[6], cfg[5], cfg[4],
+						cfg[3], cfg[2], cfg[1]);
+		tdata->div1 = cells == 10 ?
+				EXYNOS4210_DIV_CPU1(cfg[9], cfg[8], cfg[7]) :
+				EXYNOS4210_DIV_CPU1(0, cfg[8], cfg[7]);
+	}
+	tdata->prate = 0;
+	return 0;
+}
+
+static const struct samsung_cpuclk_soc_data exynos4210_cpuclk_soc_data = {
+	.parser = exynos4210_armclk_parser,
+	.ops = &exynos4210_armclk_clk_ops,
+	.offset = 0x14200,
+	.clk_cb = exynos4210_armclk_notifier_cb,
+};
+
+static const struct samsung_cpuclk_soc_data exynos5250_cpuclk_soc_data = {
+	.parser = exynos4210_armclk_parser,
+	.ops = &exynos4210_armclk_clk_ops,
+	.offset = 0x200,
+	.clk_cb = exynos4210_armclk_notifier_cb,
+};
+
+static const struct of_device_id samsung_clock_ids_armclk[] = {
+	{ .compatible = "samsung,exynos4210-clock",
+			.data = &exynos4210_cpuclk_soc_data, },
+	{ .compatible = "samsung,exynos4412-clock",
+			.data = &exynos4210_cpuclk_soc_data, },
+	{ .compatible = "samsung,exynos5250-clock",
+			.data = &exynos5250_cpuclk_soc_data, },
+	{ },
+};
+
+/**
+ * samsung_register_arm_clock: register arm clock with ccf.
+ * @lookup_id: armclk clock output id for the clock controller.
+ * @parent: name of the parent clock for armclk.
+ * @base: base address of the clock controller from which armclk is generated.
+ * @np: device tree node pointer of the clock controller (optional).
+ * @ops: clock ops for this clock (optional)
+ */
+int __init samsung_register_arm_clock(unsigned int lookup_id,
+		const char **parent_names, unsigned int num_parents,
+		void __iomem *base, struct device_node *np, struct clk_ops *ops)
+{
+	const struct of_device_id *match;
+	const struct samsung_cpuclk_soc_data *data = NULL;
+
+	if (np) {
+		match = of_match_node(samsung_clock_ids_armclk, np);
+		data = match ? match->data : NULL;
+	}
+
+	return samsung_cpuclk_register(lookup_id, "armclk", parent_names,
+			num_parents, base, data, np, ops);
+}
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index 31b4174..5459856 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -340,4 +340,9 @@ extern void __init samsung_clk_register_pll(struct samsung_pll_clock *pll_list,
 
 extern unsigned long _get_rate(const char *clk_name);
 
+int __init samsung_register_arm_clock(unsigned int lookup_id,
+		const char **parent_names, unsigned int num_parents,
+		void __iomem *base, struct device_node *np,
+		struct clk_ops *ops);
+
 #endif /* __SAMSUNG_CLK_H */
-- 
1.7.10.4

^ permalink raw reply related

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

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);
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH v3 0/7] cpufreq: use cpufreq-cpu0 driver for exynos based platforms
From: Thomas Abraham @ 2014-02-07 15:55 UTC (permalink / raw)
  To: linux-arm-kernel

Changes since v2:
- Safe operating voltage is not required while switching APLL frequency
  since the temporary parent's clock is divided down to keep armclk within
  permissible limits. Thanks to Heiko St?bner <heiko@sntech.de> for this
  suggesting this.
- Rob had suggested to use max frequency for each of the divider clock
  outputs instead of divider values. But due to certain SoC specific
  characteristics, the divider values corresponding to the input clock
  frequency for the CMU_CPU clock blocks have to be used.

Changes since v1:
- Removes Exynos4x12 and Exynos5250 cpufreq driver also.
- Device tree based clock configuration lookup as suggested by Lukasz
  Majewski and Tomasz Figa.
- safe operating point binding reworked as suggested by Shawn Guo.

The patch series removes the use of Exynos specific cpufreq driver and enables
the use of cpufreq-cpu0 driver for Exynos4210, Exynos4x12 and Exynos5250 based
platforms. This is being done for few reasons.

(a) The Exynos cpufreq driver reads/writes clock controller registers
    bypassing the Exynos CCF driver which is sort of problematic.
(b) Removes the need for having clock controller register definitions
    in the cpufreq driver and also removes the need for statically
    io-remapping clock controller address space (helps in moving towards
    multiplatform kernel).

Thomas Abraham (7):
  cpufreq: cpufreq-cpu0: allow use of optional boost mode frequencies
  clk: samsung: add infrastructure to register cpu clocks
  Documentation: devicetree: add cpu clock configuration data binding for Exynos4/5
  clk: exynos: use cpu-clock provider type to represent arm clock
  ARM: dts: Exynos: add cpu nodes, opp and cpu clock configuration data
  ARM: Exynos: switch to using generic cpufreq-cpu0 driver
  cpufreq: exynos: remove all exynos specific cpufreq driver support

 .../devicetree/bindings/clock/exynos4-clock.txt    |   37 ++
 .../devicetree/bindings/clock/exynos5250-clock.txt |   39 ++
 .../devicetree/bindings/cpufreq/cpufreq-cpu0.txt   |    2 +
 arch/arm/boot/dts/exynos4210-origen.dts            |    6 +
 arch/arm/boot/dts/exynos4210-trats.dts             |    6 +
 arch/arm/boot/dts/exynos4210-universal_c210.dts    |    6 +
 arch/arm/boot/dts/exynos4210.dtsi                  |   35 ++
 arch/arm/boot/dts/exynos4212.dtsi                  |   18 +
 arch/arm/boot/dts/exynos4412-odroidx.dts           |    6 +
 arch/arm/boot/dts/exynos4412-origen.dts            |    6 +
 arch/arm/boot/dts/exynos4412-trats2.dts            |    6 +
 arch/arm/boot/dts/exynos4412.dtsi                  |   31 ++
 arch/arm/boot/dts/exynos4x12.dtsi                  |   36 ++
 arch/arm/boot/dts/exynos5250-arndale.dts           |    6 +
 arch/arm/boot/dts/exynos5250-cros-common.dtsi      |    6 +
 arch/arm/boot/dts/exynos5250-smdk5250.dts          |    6 +
 arch/arm/boot/dts/exynos5250.dtsi                  |   41 ++
 arch/arm/mach-exynos/common.c                      |    5 -
 arch/arm/mach-exynos/common.h                      |    1 -
 arch/arm/mach-exynos/mach-exynos4-dt.c             |    2 +-
 arch/arm/mach-exynos/mach-exynos5-dt.c             |    2 +-
 drivers/clk/samsung/Makefile                       |    2 +-
 drivers/clk/samsung/clk-cpu.c                      |  409 ++++++++++++++++++++
 drivers/clk/samsung/clk-exynos4.c                  |   23 +-
 drivers/clk/samsung/clk-exynos5250.c               |   10 +-
 drivers/clk/samsung/clk.h                          |    5 +
 drivers/cpufreq/Kconfig                            |    1 +
 drivers/cpufreq/Kconfig.arm                        |   51 ---
 drivers/cpufreq/Makefile                           |    4 -
 drivers/cpufreq/cpufreq-cpu0.c                     |    3 +
 drivers/cpufreq/exynos-cpufreq.c                   |  292 --------------
 drivers/cpufreq/exynos-cpufreq.h                   |   91 -----
 drivers/cpufreq/exynos4210-cpufreq.c               |  157 --------
 drivers/cpufreq/exynos4x12-cpufreq.c               |  211 ----------
 drivers/cpufreq/exynos5250-cpufreq.c               |  183 ---------
 include/dt-bindings/clock/exynos5250.h             |    1 +
 36 files changed, 727 insertions(+), 1019 deletions(-)
 create mode 100644 drivers/clk/samsung/clk-cpu.c
 delete mode 100644 drivers/cpufreq/exynos-cpufreq.c
 delete mode 100644 drivers/cpufreq/exynos-cpufreq.c
 delete mode 100644 drivers/cpufreq/exynos-cpufreq.h
 delete mode 100644 drivers/cpufreq/exynos4210-cpufreq.c
 delete mode 100644 drivers/cpufreq/exynos4x12-cpufreq.c
 delete mode 100644 drivers/cpufreq/exynos5250-cpufreq.c

^ permalink raw reply

* [PATCH v3 1/2] drivers/base: permit base components to omit the bind/unbind ops
From: Jean-Francois Moine @ 2014-02-07 15:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1391792986.git.moinejf@free.fr>

Some simple components don't need to do any specific action on
bind to / unbind from a master component.

This patch permits such components to omit the bind/unbind
operations.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 drivers/base/component.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/base/component.c b/drivers/base/component.c
index c53efe6..0a39d7a 100644
--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -225,7 +225,8 @@ static void component_unbind(struct component *component,
 {
 	WARN_ON(!component->bound);
 
-	component->ops->unbind(component->dev, master->dev, data);
+	if (component->ops)
+		component->ops->unbind(component->dev, master->dev, data);
 	component->bound = false;
 
 	/* Release all resources claimed in the binding of this component */
@@ -274,7 +275,11 @@ static int component_bind(struct component *component, struct master *master,
 	dev_dbg(master->dev, "binding %s (ops %ps)\n",
 		dev_name(component->dev), component->ops);
 
-	ret = component->ops->bind(component->dev, master->dev, data);
+	if (component->ops)
+		ret = component->ops->bind(component->dev, master->dev, data);
+	else
+		ret = 0;
+
 	if (!ret) {
 		component->bound = true;
 
-- 
1.9.rc1

^ permalink raw reply related

* [PATCH RFC 1/2] drivers/base: permit base components to omit the bind/unbind ops
From: Jean-Francois Moine @ 2014-02-07 15:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1391793068.git.moinejf@free.fr>

Some simple components don't need to do any specific action on
bind to / unbind from a master component.

This patch permits such components to omit the bind/unbind
operations.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 drivers/base/component.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/base/component.c b/drivers/base/component.c
index c53efe6..0a39d7a 100644
--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -225,7 +225,8 @@ static void component_unbind(struct component *component,
 {
 	WARN_ON(!component->bound);
 
-	component->ops->unbind(component->dev, master->dev, data);
+	if (component->ops)
+		component->ops->unbind(component->dev, master->dev, data);
 	component->bound = false;
 
 	/* Release all resources claimed in the binding of this component */
@@ -274,7 +275,11 @@ static int component_bind(struct component *component, struct master *master,
 	dev_dbg(master->dev, "binding %s (ops %ps)\n",
 		dev_name(component->dev), component->ops);
 
-	ret = component->ops->bind(component->dev, master->dev, data);
+	if (component->ops)
+		ret = component->ops->bind(component->dev, master->dev, data);
+	else
+		ret = 0;
+
 	if (!ret) {
 		component->bound = true;
 
-- 
1.9.rc1

^ permalink raw reply related

* [PATCH v2 1/2] PM / OPP: Allow boost frequency to be looked up from device tree
From: Nishanth Menon @ 2014-02-07 15:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAJuA9aiEDXyK8ABNznbzjk7s2uMfwo_diRpmLNsKBFd+pH=ffQ@mail.gmail.com>

On 02/07/2014 09:38 AM, Thomas Abraham wrote:
[...]
>>> diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
>>> index fa41874..b636826 100644
>>> --- a/drivers/base/power/opp.c
>>> +++ b/drivers/base/power/opp.c
>>> @@ -628,7 +628,8 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
>>>         struct device_opp *dev_opp;
>>>         struct dev_pm_opp *opp;
>>>         struct cpufreq_frequency_table *freq_table;
>>> -       int i = 0;
>>> +       int i = 0, j, len, ret;
>>> +       u32 *boost_freqs = NULL;
>>>
>>>         /* Pretend as if I am an updater */
>>>         mutex_lock(&dev_opp_list_lock);
>>> @@ -650,10 +651,35 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
>>>                 return -ENOMEM;
>>>         }
>>>
>>> +       if (of_find_property(dev->of_node, "boost-frequency", &len)) {
>>> +               if (len == 0 || (len & (sizeof(u32) - 1)) != 0) {
>>> +                       dev_err(dev, "%s: invalid boost frequency\n", __func__);
>>> +                       ret = -EINVAL;
>>> +                       goto err_boost;
>>> +               }
>>> +
>>> +               boost_freqs = kzalloc(len, GFP_KERNEL);
>>> +               if (!boost_freqs) {
>>> +                       dev_warn(dev, "%s: no memory for boost freq table\n",
>>> +                                       __func__);
>>> +                       ret = -ENOMEM;
>>> +                       goto err_boost;
>>> +               }
>>> +               of_property_read_u32_array(dev->of_node, "boost-frequency",
>>> +                       boost_freqs, len / sizeof(u32));
>>> +       }
>>> +
>>>         list_for_each_entry(opp, &dev_opp->opp_list, node) {
>>>                 if (opp->available) {
>>>                         freq_table[i].driver_data = i;
>>>                         freq_table[i].frequency = opp->rate / 1000;
>>> +                       for (j = 0; j < len / sizeof(u32) && boost_freqs; j++) {
>>> +                               if (boost_freqs[j] == freq_table[i].frequency) {
>>> +                                       freq_table[i].driver_data =
>>> +                                                       CPUFREQ_BOOST_FREQ;
>>> +                                       break;
>>> +                               }
>>> +                       }
>>
>> What if any one of the boost_freqs are not contained in the enabled frequencies?
> 
> It is not used as a boost frequency because its corresponding voltage
> is not known. If required a warning can be printed out for the same.

yes - that would be good, as it helps debug if there are developer
errors in dts.


-- 
Regards,
Nishanth Menon

^ permalink raw reply

* [PATCH] ARM: at91: enable USB host on at91sam9n12ek board
From: Nicolas Ferre @ 2014-02-07 15:49 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1389681284-28833-1-git-send-email-voice.shen@atmel.com>

On 14/01/2014 07:34, Bo Shen :
> Enable USB host on at91sam9n12ek board.
> 
> Signed-off-by: Bo Shen <voice.shen@atmel.com>

Added to at91-3.14-fixes because there is no benefit in keeping it
outside of mainline for a longer time.

Bye,


> ---
>  arch/arm/boot/dts/at91sam9n12ek.dts | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/at91sam9n12ek.dts b/arch/arm/boot/dts/at91sam9n12ek.dts
> index e9487f6..924a6a6 100644
> --- a/arch/arm/boot/dts/at91sam9n12ek.dts
> +++ b/arch/arm/boot/dts/at91sam9n12ek.dts
> @@ -124,6 +124,10 @@
>  			nand-on-flash-bbt;
>  			status = "okay";
>  		};
> +
> +		usb0: ohci at 00500000 {
> +			status = "okay";
> +		};
>  	};
>  
>  	leds {
> 


-- 
Nicolas Ferre

^ permalink raw reply

* [PATCH] at91: dt: sam9263: fixed compatibility string for the I2C
From: Nicolas Ferre @ 2014-02-07 15:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1389781486-29670-1-git-send-email-jjhiblot@traphandler.com>

On 15/01/2014 11:24, Jean-Jacques Hiblot :
> Signed-off-by: Jean-Jacques Hiblot <jjhiblot@traphandler.com>

Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>

Added to at91-3.14-fixes

Thanks, bye,

> ---
>  arch/arm/boot/dts/at91sam9263.dtsi | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/arm/boot/dts/at91sam9263.dtsi b/arch/arm/boot/dts/at91sam9263.dtsi
> index c8fa9b9..7d6dd09 100644
> --- a/arch/arm/boot/dts/at91sam9263.dtsi
> +++ b/arch/arm/boot/dts/at91sam9263.dtsi
> @@ -523,7 +523,7 @@
>  			};
>  
>  			i2c0: i2c at fff88000 {
> -				compatible = "atmel,at91sam9263-i2c";
> +				compatible = "atmel,at91sam9260-i2c";
>  				reg = <0xfff88000 0x100>;
>  				interrupts = <13 IRQ_TYPE_LEVEL_HIGH 6>;
>  				#address-cells = <1>;
> 


-- 
Nicolas Ferre

^ permalink raw reply

* [PATCH] ARM: at91/dt: fix sama5d3 ohci hclk clock reference
From: Nicolas Ferre @ 2014-02-07 15:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1389885934-26119-1-git-send-email-b.brezillon@overkiz.com>

On 16/01/2014 16:25, Boris BREZILLON :
> The hclk clock of the ohci node is referencing udphs_clk instead of
> uhphs_clk.
> 
> Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>

Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>

Added to at91-3.14-fixes

Thanks,

> ---
>  arch/arm/boot/dts/sama5d3.dtsi |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/arm/boot/dts/sama5d3.dtsi b/arch/arm/boot/dts/sama5d3.dtsi
> index e398e5d..31356e2 100644
> --- a/arch/arm/boot/dts/sama5d3.dtsi
> +++ b/arch/arm/boot/dts/sama5d3.dtsi
> @@ -1195,7 +1195,7 @@
>  			compatible = "atmel,at91rm9200-ohci", "usb-ohci";
>  			reg = <0x00600000 0x100000>;
>  			interrupts = <32 IRQ_TYPE_LEVEL_HIGH 2>;
> -			clocks = <&usb>, <&uhphs_clk>, <&udphs_clk>,
> +			clocks = <&usb>, <&uhphs_clk>, <&uhphs_clk>,
>  				 <&uhpck>;
>  			clock-names = "usb_clk", "ohci_clk", "hclk", "uhpck";
>  			status = "disabled";
> 


-- 
Nicolas Ferre

^ permalink raw reply

* [PATCH v6 05/19] watchdog: orion: Make sure the watchdog is initially stopped
From: Jason Cooper @ 2014-02-07 15:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20140207151727.GB8218@localhost>

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?

> > >
> > > 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).

thx,

Jason.

^ permalink raw reply

* [PATCH v3 05/11] ARM: LPAE: provide an IPA capable pmd_addr_end
From: Catalin Marinas @ 2014-02-07 15:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20140207040456.GN9157@cbox>

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.

-- 
Catalin

^ permalink raw reply

* [linux-sunxi] Re: [PATCH] irq: Add new flag to ack level-triggered interrupts before unmasking
From: Carlo Caione @ 2014-02-07 15:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <alpine.DEB.2.02.1402071121310.21991@ionos.tec.linutronix.de>

On Fri, Feb 7, 2014 at 12:51 PM, Thomas Gleixner <tglx@linutronix.de> wrote:
> I can't see why it would be specific for the threaded case.
>
> The explanation says that the NMI chip is ignoring the ack on mask,
> which is basically the entry of the interrupt handler. Now it does not
> matter whether you are threaded or not. The interrupt line at the NMI
> controller is asserted in both cases. So the same issue should be
> visible for a non threaded interrupt, if the NMI controller really
> needs an ack on unmask.

Yes, you are right. I was focusing on the threaded case because it was
the only case I have experienced with the hardware I have.
The problem should be there also for the non threaded case (even
though I don't know how to double check this).
At this point I suspect that here the problem is that ACKing the NMI
controller without ACKing ahead the device connected always fails (or
at least is useless and the pending flag is not cleared), so when I
unmask later the irqchip I have a spurious interrupt. That's why I
need another ACK for the NMI controller _after_ having ACKed at device
level.

> But there is another detail:
>
>         sunxi_sc_nmi_handle_irq()
>         chained_irq_enter()
>                 NOP, because GIC uses EOI
>
>         generic_handle_irq();
>                 nmi->mask();
>                 dev_handler();
>                 nmi->unmask();
>
>         chained_irq_exit()
>                 gic->eoi();
>
> In the threaded case this looks like:
>
>         sunxi_sc_nmi_handle_irq()
>         chained_irq_enter()
>                 NOP, because GIC uses EOI
>
>         generic_handle_irq();
>                 nmi->mask();
>                 wake_thread();
>
>         chained_irq_exit()
>                 gic->eoi();
>
>         run_thread()
>                 dev_handler();
>                 nmi->unmask();
>
> So the difference is that in the non threaded case the gic->eoi is
> called after the device interrupt has been cleared and the
> nmi->interrupt has been unmasked. And in the threaded case its the
> other way round. I have no idea how that stuff is connected internaly,
> but I suspect that the gic->eoi is related to this as it might
> actually ack the NMI chip, which of course only works in the non
> threaded case.

Yeah, no really difference between threaded and non threaded.
For the record, from a mail exchange with Allwinner's engineers: "the
NMI module is a signal conversion module. It catches the NMI pin's
state and generates irq to GIC", so GIC does not really ACK anything.
BTW being a dummy "signal conversion module" this is probably why I
still need to clear the pending status even though my IRQ line has
already been cleared.

> Now back to your patch.
>
> I'm not against having a flag, but this should be done less convoluted
> and have proper names which make the use case clear along with a good
> technical explanation of the flag in the comment.

Ok, at this point do you think that a patch in the core could be
useful or is it better to stick with modifying the unmask callback?

> unmask_and_ack() plus IRQCHIP_ACK_ON_UNMASK are not really telling
> what the heck is going on. You can restrict it to level irqs in the
> core, but please use the proper functions and not some opencoded
> hackery.

I'll do in case of v2.

Thanks,

-- 
Carlo Caione

^ permalink raw reply

* [PATCH v2 2/2] Documentation: devicetree: Add boost-frequency binding to list boost mode frequency
From: Thomas Abraham @ 2014-02-07 15:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAGo_u6rymex=EZf0jj6rs2SpRARz2tA5F5jtT0+D-8BnSrfi9A@mail.gmail.com>

On Fri, Feb 7, 2014 at 8:57 PM, Nishanth Menon <nm@ti.com> wrote:
> On Fri, Feb 7, 2014 at 9:19 AM, Thomas Abraham <ta.omasab@gmail.com> 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.
>
> boost-frequencies?

Okay, I will change it.

Thanks,
Thomas.

>
>> +  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.
>> --
>> 1.7.10.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-pm" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v2 1/2] PM / OPP: Allow boost frequency to be looked up from device tree
From: Thomas Abraham @ 2014-02-07 15:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAGo_u6qsh0wVLGjvqKmaXjDGVyy2tKgSzkh-t9RZCQS-JJND8g@mail.gmail.com>

On Fri, Feb 7, 2014 at 8:59 PM, Nishanth Menon <nm@ti.com> wrote:
> On Fri, Feb 7, 2014 at 9:19 AM, Thomas Abraham <ta.omasab@gmail.com> wrote:
>> From: Thomas Abraham <thomas.ab@samsung.com>
>>
>> Commit 6f19efc0 ("cpufreq: Add boost frequency support in core") adds
>> support for CPU boost mode. This patch adds support for finding available
>> boost frequencies from device tree and marking them as usable in boost mode.
>>
>> Cc: Nishanth Menon <nm@ti.com>
>> Cc: Lukasz Majewski <l.majewski@samsung.com>
>> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
>> ---
>>  drivers/base/power/opp.c |   34 +++++++++++++++++++++++++++++++++-
>>  1 file changed, 33 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
>> index fa41874..b636826 100644
>> --- a/drivers/base/power/opp.c
>> +++ b/drivers/base/power/opp.c
>> @@ -628,7 +628,8 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
>>         struct device_opp *dev_opp;
>>         struct dev_pm_opp *opp;
>>         struct cpufreq_frequency_table *freq_table;
>> -       int i = 0;
>> +       int i = 0, j, len, ret;
>> +       u32 *boost_freqs = NULL;
>>
>>         /* Pretend as if I am an updater */
>>         mutex_lock(&dev_opp_list_lock);
>> @@ -650,10 +651,35 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
>>                 return -ENOMEM;
>>         }
>>
>> +       if (of_find_property(dev->of_node, "boost-frequency", &len)) {
>> +               if (len == 0 || (len & (sizeof(u32) - 1)) != 0) {
>> +                       dev_err(dev, "%s: invalid boost frequency\n", __func__);
>> +                       ret = -EINVAL;
>> +                       goto err_boost;
>> +               }
>> +
>> +               boost_freqs = kzalloc(len, GFP_KERNEL);
>> +               if (!boost_freqs) {
>> +                       dev_warn(dev, "%s: no memory for boost freq table\n",
>> +                                       __func__);
>> +                       ret = -ENOMEM;
>> +                       goto err_boost;
>> +               }
>> +               of_property_read_u32_array(dev->of_node, "boost-frequency",
>> +                       boost_freqs, len / sizeof(u32));
>> +       }
>> +
>>         list_for_each_entry(opp, &dev_opp->opp_list, node) {
>>                 if (opp->available) {
>>                         freq_table[i].driver_data = i;
>>                         freq_table[i].frequency = opp->rate / 1000;
>> +                       for (j = 0; j < len / sizeof(u32) && boost_freqs; j++) {
>> +                               if (boost_freqs[j] == freq_table[i].frequency) {
>> +                                       freq_table[i].driver_data =
>> +                                                       CPUFREQ_BOOST_FREQ;
>> +                                       break;
>> +                               }
>> +                       }
>
> What if any one of the boost_freqs are not contained in the enabled frequencies?

It is not used as a boost frequency because its corresponding voltage
is not known. If required a warning can be printed out for the same.

Thanks,
Thomas.

>
>>                         i++;
>>                 }
>>         }
>> @@ -663,8 +689,14 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
>>         freq_table[i].frequency = CPUFREQ_TABLE_END;
>>
>>         *table = &freq_table[0];
>> +       kfree(boost_freqs);
>>
>>         return 0;
>> +
>> +err_boost:
>> +       kfree(freq_table);
>> +       mutex_unlock(&dev_opp_list_lock);
>> +       return ret;
>>  }
>>  EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table);
>>
>> --
>> 1.7.10.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-pm" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v9 4/4] ehci-platform: Add support for controllers with big-endian regs / descriptors
From: Hans de Goede @ 2014-02-07 15:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391787403-20961-1-git-send-email-hdegoede@redhat.com>

This uses the already documented devicetree booleans for this, see:
Documentation/devicetree/bindings/usb/usb-ehci.txt

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/usb/host/ehci-platform.c | 33 +++++++++++++++++++++++++++++++--
 1 file changed, 31 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index 5ebd0b7..8fde649 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -55,8 +55,10 @@ static int ehci_platform_reset(struct usb_hcd *hcd)
 
 	hcd->has_tt = pdata->has_tt;
 	ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug;
-	ehci->big_endian_desc = pdata->big_endian_desc;
-	ehci->big_endian_mmio = pdata->big_endian_mmio;
+	if (pdata->big_endian_desc)
+		ehci->big_endian_desc = 1;
+	if (pdata->big_endian_mmio)
+		ehci->big_endian_mmio = 1;
 
 	if (pdata->pre_setup) {
 		retval = pdata->pre_setup(hcd);
@@ -142,6 +144,7 @@ static int ehci_platform_probe(struct platform_device *dev)
 	struct resource *res_mem;
 	struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
 	struct ehci_platform_priv *priv;
+	struct ehci_hcd *ehci;
 	int err, irq, clk = 0;
 
 	if (usb_disabled())
@@ -177,8 +180,34 @@ static int ehci_platform_probe(struct platform_device *dev)
 	platform_set_drvdata(dev, hcd);
 	dev->dev.platform_data = pdata;
 	priv = hcd_to_ehci_priv(hcd);
+	ehci = hcd_to_ehci(hcd);
 
 	if (pdata == &ehci_platform_defaults && dev->dev.of_node) {
+		if (of_property_read_bool(dev->dev.of_node, "big-endian-regs"))
+			ehci->big_endian_mmio = 1;
+
+		if (of_property_read_bool(dev->dev.of_node, "big-endian-desc"))
+			ehci->big_endian_desc = 1;
+
+		if (of_property_read_bool(dev->dev.of_node, "big-endian"))
+			ehci->big_endian_mmio = ehci->big_endian_desc = 1;
+
+#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
+		if (ehci->big_endian_mmio) {
+			dev_err(&dev->dev,
+				"Error big-endian-regs not compiled in\n");
+			err = -EINVAL;
+			goto err_put_hcd;
+		}
+#endif
+#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
+		if (ehci->big_endian_desc) {
+			dev_err(&dev->dev,
+				"Error big-endian-desc not compiled in\n");
+			err = -EINVAL;
+			goto err_put_hcd;
+		}
+#endif
 		priv->phy = devm_phy_get(&dev->dev, "usb");
 		if (IS_ERR(priv->phy)) {
 			err = PTR_ERR(priv->phy);
-- 
1.8.4.2

^ permalink raw reply related

* [PATCH v9 3/4] ohci-platform: Add support for controllers with big-endian regs / descriptors
From: Hans de Goede @ 2014-02-07 15:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391787403-20961-1-git-send-email-hdegoede@redhat.com>

Note this commit uses the same devicetree booleans for this as the ones
already existing in the usb-ehci bindings, see:
Documentation/devicetree/bindings/usb/usb-ehci.txt

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 Documentation/devicetree/bindings/usb/usb-ohci.txt |  3 +++
 drivers/usb/host/ohci-platform.c                   | 27 ++++++++++++++++++++++
 2 files changed, 30 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/usb-ohci.txt b/Documentation/devicetree/bindings/usb/usb-ohci.txt
index 6ba38d9..6933b0c 100644
--- a/Documentation/devicetree/bindings/usb/usb-ohci.txt
+++ b/Documentation/devicetree/bindings/usb/usb-ohci.txt
@@ -6,6 +6,9 @@ Required properties:
 - interrupts : ohci controller interrupt
 
 Optional properties:
+- big-endian-regs : boolean, set this for hcds with big-endian registers
+- big-endian-desc : boolean, set this for hcds with big-endian descriptors
+- big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
 - clocks : a list of phandle + clock specifier pairs
 - phys : phandle + phy specifier pair
 - phy-names : "usb"
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
index 49304dd..e2c28fd 100644
--- a/drivers/usb/host/ohci-platform.c
+++ b/drivers/usb/host/ohci-platform.c
@@ -128,6 +128,7 @@ static int ohci_platform_probe(struct platform_device *dev)
 	struct resource *res_mem;
 	struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
 	struct ohci_platform_priv *priv;
+	struct ohci_hcd *ohci;
 	int err, irq, clk = 0;
 
 	if (usb_disabled())
@@ -164,8 +165,34 @@ static int ohci_platform_probe(struct platform_device *dev)
 	platform_set_drvdata(dev, hcd);
 	dev->dev.platform_data = pdata;
 	priv = hcd_to_ohci_priv(hcd);
+	ohci = hcd_to_ohci(hcd);
 
 	if (pdata == &ohci_platform_defaults && dev->dev.of_node) {
+		if (of_property_read_bool(dev->dev.of_node, "big-endian-regs"))
+			ohci->flags |= OHCI_QUIRK_BE_MMIO;
+
+		if (of_property_read_bool(dev->dev.of_node, "big-endian-desc"))
+			ohci->flags |= OHCI_QUIRK_BE_DESC;
+
+		if (of_property_read_bool(dev->dev.of_node, "big-endian"))
+			ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC;
+
+#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO
+		if (ohci->flags & OHCI_QUIRK_BE_MMIO) {
+			dev_err(&dev->dev,
+				"Error big-endian-regs not compiled in\n");
+			err = -EINVAL;
+			goto err_put_hcd;
+		}
+#endif
+#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_DESC
+		if (ohci->flags & OHCI_QUIRK_BE_DESC) {
+			dev_err(&dev->dev,
+				"Error big-endian-desc not compiled in\n");
+			err = -EINVAL;
+			goto err_put_hcd;
+		}
+#endif
 		priv->phy = devm_phy_get(&dev->dev, "usb");
 		if (IS_ERR(priv->phy)) {
 			err = PTR_ERR(priv->phy);
-- 
1.8.4.2

^ permalink raw reply related

* [PATCH v9 2/4] ehci-platform: Add support for clks and phy passed through devicetree
From: Hans de Goede @ 2014-02-07 15:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391787403-20961-1-git-send-email-hdegoede@redhat.com>

Currently ehci-platform is only used in combination with devicetree when used
with some Via socs. By extending it to (optionally) get clks and a phy from
devicetree, and enabling / disabling those on power_on / off, it can be used
more generically. Specifically after this commit it can be used for the
ehci controller on Allwinner sunxi SoCs.

Since ehci-platform is intended to handle any generic enough non pci ehci
device, add a "usb-ehci" compatibility string.

There already is a usb-ehci device-tree bindings document, update this
with clks and phy bindings info.

Although actually quite generic so far the via,vt8500 compatibilty string
had its own bindings document. Somehow we even ended up with 2 of them. Since
these provide no extra information over the generic usb-ehci documentation,
this patch removes them.

The ehci-ppc-of.c driver also claims the usb-ehci compatibility string,
even though it mostly is ibm,usb-ehci-440epx specific. ehci-platform.c is
not needed on ppc platforms, so add a !PPC_OF dependency to it to avoid
2 drivers claiming the same compatibility string getting build on ppc.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
---
 Documentation/devicetree/bindings/usb/usb-ehci.txt |  25 +++-
 .../devicetree/bindings/usb/via,vt8500-ehci.txt    |  15 ---
 .../devicetree/bindings/usb/vt8500-ehci.txt        |  12 --
 drivers/usb/host/Kconfig                           |   1 +
 drivers/usb/host/ehci-platform.c                   | 147 +++++++++++++++++----
 5 files changed, 142 insertions(+), 58 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt
 delete mode 100644 Documentation/devicetree/bindings/usb/vt8500-ehci.txt

diff --git a/Documentation/devicetree/bindings/usb/usb-ehci.txt b/Documentation/devicetree/bindings/usb/usb-ehci.txt
index fa18612..2c1aeeb 100644
--- a/Documentation/devicetree/bindings/usb/usb-ehci.txt
+++ b/Documentation/devicetree/bindings/usb/usb-ehci.txt
@@ -7,13 +7,14 @@ Required properties:
     (debug-port or other) can be also specified here, but only after
     definition of standard EHCI registers.
   - interrupts : one EHCI interrupt should be described here.
-If device registers are implemented in big endian mode, the device
-node should have "big-endian-regs" property.
-If controller implementation operates with big endian descriptors,
-"big-endian-desc" property should be specified.
-If both big endian registers and descriptors are used by the controller
-implementation, "big-endian" property can be specified instead of having
-both "big-endian-regs" and "big-endian-desc".
+
+Optional properties:
+ - big-endian-regs : boolean, set this for hcds with big-endian registers
+ - big-endian-desc : boolean, set this for hcds with big-endian descriptors
+ - big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
+ - clocks : a list of phandle + clock specifier pairs
+ - phys : phandle + phy specifier pair
+ - phy-names : "usb"
 
 Example (Sequoia 440EPx):
     ehci at e0000300 {
@@ -23,3 +24,13 @@ Example (Sequoia 440EPx):
 	   reg = <0 e0000300 90 0 e0000390 70>;
 	   big-endian;
    };
+
+Example (Allwinner sun4i A10 SoC):
+   ehci0: usb at 01c14000 {
+	   compatible = "allwinner,sun4i-a10-ehci", "usb-ehci";
+	   reg = <0x01c14000 0x100>;
+	   interrupts = <39>;
+	   clocks = <&ahb_gates 1>;
+	   phys = <&usbphy 1>;
+	   phy-names = "usb";
+   };
diff --git a/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt b/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt
deleted file mode 100644
index 17b3ad1..0000000
--- a/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-VIA/Wondermedia VT8500 EHCI Controller
------------------------------------------------------
-
-Required properties:
-- compatible : "via,vt8500-ehci"
-- reg : Should contain 1 register ranges(address and length)
-- interrupts : ehci controller interrupt
-
-Example:
-
-	ehci at d8007900 {
-		compatible = "via,vt8500-ehci";
-		reg = <0xd8007900 0x200>;
-		interrupts = <43>;
-	};
diff --git a/Documentation/devicetree/bindings/usb/vt8500-ehci.txt b/Documentation/devicetree/bindings/usb/vt8500-ehci.txt
deleted file mode 100644
index 5fb8fd6..0000000
--- a/Documentation/devicetree/bindings/usb/vt8500-ehci.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-VIA VT8500 and Wondermedia WM8xxx SoC USB controllers.
-
-Required properties:
- - compatible: Should be "via,vt8500-ehci" or "wm,prizm-ehci".
- - reg: Address range of the ehci registers. size should be 0x200
- - interrupts: Should contain the ehci interrupt.
-
-usb: ehci at D8007100 {
-	compatible = "wm,prizm-ehci", "usb-ehci";
-	reg = <0xD8007100 0x200>;
-	interrupts = <1>;
-};
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index a9707da..e28cbe0 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -255,6 +255,7 @@ config USB_EHCI_ATH79
 
 config USB_EHCI_HCD_PLATFORM
 	tristate "Generic EHCI driver for a platform device"
+	depends on !PPC_OF
 	default n
 	---help---
 	  Adds an EHCI host driver for a generic platform device, which
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index 01536cf..5ebd0b7 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -3,6 +3,7 @@
  *
  * Copyright 2007 Steven Brown <sbrown@cortland.com>
  * Copyright 2010-2012 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright 2014 Hans de Goede <hdegoede@redhat.com>
  *
  * Derived from the ohci-ssb driver
  * Copyright 2007 Michael Buesch <m@bues.ch>
@@ -18,6 +19,7 @@
  *
  * Licensed under the GNU/GPL. See COPYING for details.
  */
+#include <linux/clk.h>
 #include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/kernel.h>
@@ -25,6 +27,7 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
@@ -33,6 +36,13 @@
 #include "ehci.h"
 
 #define DRIVER_DESC "EHCI generic platform driver"
+#define EHCI_MAX_CLKS 3
+#define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv)
+
+struct ehci_platform_priv {
+	struct clk *clks[EHCI_MAX_CLKS];
+	struct phy *phy;
+};
 
 static const char hcd_name[] = "ehci-platform";
 
@@ -64,38 +74,90 @@ static int ehci_platform_reset(struct usb_hcd *hcd)
 	return 0;
 }
 
+static int ehci_platform_power_on(struct platform_device *dev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(dev);
+	struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
+	int clk, ret;
+
+	for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) {
+		ret = clk_prepare_enable(priv->clks[clk]);
+		if (ret)
+			goto err_disable_clks;
+	}
+
+	if (priv->phy) {
+		ret = phy_init(priv->phy);
+		if (ret)
+			goto err_disable_clks;
+
+		ret = phy_power_on(priv->phy);
+		if (ret)
+			goto err_exit_phy;
+	}
+
+	return 0;
+
+err_exit_phy:
+	phy_exit(priv->phy);
+err_disable_clks:
+	while (--clk >= 0)
+		clk_disable_unprepare(priv->clks[clk]);
+
+	return ret;
+}
+
+static void ehci_platform_power_off(struct platform_device *dev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(dev);
+	struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
+	int clk;
+
+	if (priv->phy) {
+		phy_power_off(priv->phy);
+		phy_exit(priv->phy);
+	}
+
+	for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
+		if (priv->clks[clk])
+			clk_disable_unprepare(priv->clks[clk]);
+}
+
 static struct hc_driver __read_mostly ehci_platform_hc_driver;
 
 static const struct ehci_driver_overrides platform_overrides __initconst = {
-	.reset =	ehci_platform_reset,
+	.reset =		ehci_platform_reset,
+	.extra_priv_size =	sizeof(struct ehci_platform_priv),
 };
 
-static struct usb_ehci_pdata ehci_platform_defaults;
+static struct usb_ehci_pdata ehci_platform_defaults = {
+	.power_on =		ehci_platform_power_on,
+	.power_suspend =	ehci_platform_power_off,
+	.power_off =		ehci_platform_power_off,
+};
 
 static int ehci_platform_probe(struct platform_device *dev)
 {
 	struct usb_hcd *hcd;
 	struct resource *res_mem;
-	struct usb_ehci_pdata *pdata;
-	int irq;
-	int err;
+	struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
+	struct ehci_platform_priv *priv;
+	int err, irq, clk = 0;
 
 	if (usb_disabled())
 		return -ENODEV;
 
 	/*
-	 * use reasonable defaults so platforms don't have to provide these.
-	 * with DT probing on ARM, none of these are set.
+	 * Use reasonable defaults so platforms don't have to provide these
+	 * with DT probing on ARM.
 	 */
-	if (!dev_get_platdata(&dev->dev))
-		dev->dev.platform_data = &ehci_platform_defaults;
+	if (!pdata)
+		pdata = &ehci_platform_defaults;
 
 	err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
 	if (err)
 		return err;
 
-	pdata = dev_get_platdata(&dev->dev);
-
 	irq = platform_get_irq(dev, 0);
 	if (irq < 0) {
 		dev_err(&dev->dev, "no irq provided");
@@ -107,17 +169,40 @@ static int ehci_platform_probe(struct platform_device *dev)
 		return -ENXIO;
 	}
 
+	hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
+			     dev_name(&dev->dev));
+	if (!hcd)
+		return -ENOMEM;
+
+	platform_set_drvdata(dev, hcd);
+	dev->dev.platform_data = pdata;
+	priv = hcd_to_ehci_priv(hcd);
+
+	if (pdata == &ehci_platform_defaults && dev->dev.of_node) {
+		priv->phy = devm_phy_get(&dev->dev, "usb");
+		if (IS_ERR(priv->phy)) {
+			err = PTR_ERR(priv->phy);
+			if (err == -EPROBE_DEFER)
+				goto err_put_hcd;
+			priv->phy = NULL;
+		}
+
+		for (clk = 0; clk < EHCI_MAX_CLKS; clk++) {
+			priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
+			if (IS_ERR(priv->clks[clk])) {
+				err = PTR_ERR(priv->clks[clk]);
+				if (err == -EPROBE_DEFER)
+					goto err_put_clks;
+				priv->clks[clk] = NULL;
+				break;
+			}
+		}
+	}
+
 	if (pdata->power_on) {
 		err = pdata->power_on(dev);
 		if (err < 0)
-			return err;
-	}
-
-	hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
-			     dev_name(&dev->dev));
-	if (!hcd) {
-		err = -ENOMEM;
-		goto err_power;
+			goto err_put_clks;
 	}
 
 	hcd->rsrc_start = res_mem->start;
@@ -126,22 +211,28 @@ static int ehci_platform_probe(struct platform_device *dev)
 	hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
 	if (IS_ERR(hcd->regs)) {
 		err = PTR_ERR(hcd->regs);
-		goto err_put_hcd;
+		goto err_power;
 	}
 	err = usb_add_hcd(hcd, irq, IRQF_SHARED);
 	if (err)
-		goto err_put_hcd;
+		goto err_power;
 
 	device_wakeup_enable(hcd->self.controller);
 	platform_set_drvdata(dev, hcd);
 
 	return err;
 
-err_put_hcd:
-	usb_put_hcd(hcd);
 err_power:
 	if (pdata->power_off)
 		pdata->power_off(dev);
+err_put_clks:
+	while (--clk >= 0)
+		clk_put(priv->clks[clk]);
+err_put_hcd:
+	if (pdata == &ehci_platform_defaults)
+		dev->dev.platform_data = NULL;
+
+	usb_put_hcd(hcd);
 
 	return err;
 }
@@ -150,13 +241,19 @@ static int ehci_platform_remove(struct platform_device *dev)
 {
 	struct usb_hcd *hcd = platform_get_drvdata(dev);
 	struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
+	struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
+	int clk;
 
 	usb_remove_hcd(hcd);
-	usb_put_hcd(hcd);
 
 	if (pdata->power_off)
 		pdata->power_off(dev);
 
+	for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++)
+		clk_put(priv->clks[clk]);
+
+	usb_put_hcd(hcd);
+
 	if (pdata == &ehci_platform_defaults)
 		dev->dev.platform_data = NULL;
 
@@ -207,8 +304,10 @@ static int ehci_platform_resume(struct device *dev)
 static const struct of_device_id vt8500_ehci_ids[] = {
 	{ .compatible = "via,vt8500-ehci", },
 	{ .compatible = "wm,prizm-ehci", },
+	{ .compatible = "usb-ehci", },
 	{}
 };
+MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);
 
 static const struct platform_device_id ehci_platform_table[] = {
 	{ "ehci-platform", 0 },
-- 
1.8.4.2

^ permalink raw reply related


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