Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [V3 PATCH 00/25] mv-usb fix and enhancement patches
From: Chao Xie @ 2013-01-15  6:18 UTC (permalink / raw)
  To: linux-arm-kernel

The patches are divied into 4 parts
1. bug fixes
  usb: gadget: mv_udc: use udc_start and udc_stop functions
  usb: gadget: mv_udc: use devm_xxx for probe
  usb: gadget: mv_udc: fix the clk APIs
  usb: gadget: mv_udc: fix the warning of mv_udc_remove
  usb: otg: mv_otg: use devm_xxx for probe
  usb: otg: mv_otg: fix the clk APIs
  usb: host: ehci-mv: fix clk APIs
  usb: host: ehci-mv: remove unused variable
  usb: gadget: mv_udc: fix the value of tranceiver
Above patches are bug fixes.

2. PHY driver
To remove the callbacks in the platform data, a usb PHY driver
for marvell udc/otg/ehci is written.
For device tree support, it is not good to pass the callback
pointers by platform data. The PHY driver also removes the
block.

  usb: phy: mv_usb2: add PHY driver for marvell usb2 controller
  usb: gadget: mv_udc: use PHY driver for udc
  usb: ehci: ehci-mv: use PHY driver for ehci
  usb: otg: mv_otg: use PHY driver for otg
Above patches are marvell usb PHY driver support.

  arm: mmp2: change the defintion of usb devices
  arm: pxa910: change the defintion of usb devices
  arm: brownstone: add usb support for the board
  arm: ttc_dkb: add usb support
  arm: mmp: remove the usb phy setting
  arm: mmp: remove usb devices from pxa168
Above patches are for SOC/board support for marvell usb PHY
driver.

3. external chip support
The marvell usb controller can detect the vbus/idpin, but it
need PHY and usb clocks to be enabled.
Based on measurement it will import 15mA current, and increase
the power when the usb is not used.
Using a external chip to detect vbus/idpin changes will save
the power.
In fact the marvell PMIC 88pm860x and 88pm80x can do it. The
drivers are located at drivers/mfd.
So add a middle layer in the marvell usb PHY driver.
PMIC call the APIs in middle driver to registers the callback
for vbus/idpin detection/query
udc/otg/ehci driver will call the APIs to get vbus/idpin changes
and query the states of the vbus/idpin.
  usb: phy: mv_usb2_phy: add externel chip support
  usb: gadget: mv_udc: add extern chip support
  usb: ehci: ehci-mv: add extern chip support
  usb: otg: mv_otg: add extern chip support
Above patches are the middle layer suppor for udc/otg/ehci

  arm: mmp: add extern chip support for brownstone
  arm: mmp: add extern chip support for ttc_dkb
Above patches are corresponding board file changes

4. device tree support
After removing the callbacks in platform data, and the not
constant variables in platform data. All the information needed
by udc/otg/ehci driver are constant.

  usb: gadget: mv_udc: add device tree support
  usb: otg: mv_otg: add device tree support
  usb: ehci: ehci-mv: add device tree support
Above patches are device tree support for udc/otg/ehci driver.

V2->V1:
  Change the Signed-off-by to be right email address

v3->v2
  re-format the patches to new code base

Chao Xie (25):
  usb: gadget: mv_udc: use udc_start and udc_stop functions
  usb: gadget: mv_udc: use devm_xxx for probe
  usb: gadget: mv_udc: fix the warning of mv_udc_remove
  usb: otg: mv_otg: use devm_xxx for probe
  usb: host: ehci-mv: remove unused variable
  usb: gadget: mv_udc: fix the value of tranceiver
  usb: phy: mv_usb2: add PHY driver for marvell usb2 controller
  usb: gadget: mv_udc: use PHY driver for udc
  usb: ehci: ehci-mv: use PHY driver for ehci
  usb: otg: mv_otg: use PHY driver for otg
  arm: mmp2: change the defintion of usb devices
  arm: pxa910: change the defintion of usb devices
  arm: brownstone: add usb support for the board
  arm: ttc_dkb: add usb support
  arm: mmp: remove the usb phy setting
  arm: mmp: remove usb devices from pxa168
  usb: phy: mv_usb2_phy: add externel chip support
  usb: gadget: mv_udc: add extern chip support
  usb: ehci: ehci-mv: add extern chip support
  usb: otg: mv_otg: add extern chip support
  arm: mmp: add extern chip support for brownstone
  arm: mmp: add extern chip support for ttc_dkb
  usb: gadget: mv_udc: add device tree support
  usb: otg: mv_otg: add device tree support
  usb: ehci: ehci-mv: add device tree support

 arch/arm/mach-mmp/brownstone.c            |   45 +++
 arch/arm/mach-mmp/devices.c               |  278 ----------------
 arch/arm/mach-mmp/include/mach/mmp2.h     |    4 +
 arch/arm/mach-mmp/include/mach/pxa910.h   |    7 +-
 arch/arm/mach-mmp/include/mach/regs-usb.h |  253 --------------
 arch/arm/mach-mmp/mmp2.c                  |    4 +
 arch/arm/mach-mmp/pxa168.c                |   42 ---
 arch/arm/mach-mmp/pxa910.c                |    4 +
 arch/arm/mach-mmp/ttc_dkb.c               |   32 +-
 drivers/usb/gadget/mv_udc.h               |   10 +-
 drivers/usb/gadget/mv_udc_core.c          |  381 +++++++++++-----------
 drivers/usb/host/ehci-mv.c                |  154 ++++++----
 drivers/usb/otg/mv_otg.c                  |  279 +++++++++-------
 drivers/usb/otg/mv_otg.h                  |   11 +-
 drivers/usb/phy/Kconfig                   |    7 +
 drivers/usb/phy/Makefile                  |    1 +
 drivers/usb/phy/mv_usb2_phy.c             |  508 +++++++++++++++++++++++++++++
 include/linux/platform_data/mv_usb.h      |   22 +-
 include/linux/usb/mv_usb2.h               |   97 ++++++
 19 files changed, 1165 insertions(+), 974 deletions(-)
 delete mode 100644 arch/arm/mach-mmp/include/mach/regs-usb.h
 create mode 100644 drivers/usb/phy/mv_usb2_phy.c
 create mode 100644 include/linux/usb/mv_usb2.h

-- 
1.7.4.1

^ permalink raw reply

* [PATCH v7] usb: phy: samsung: Add support to set pmu isolation
From: Vivek Gautam @ 2013-01-15  6:10 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAFp+6iE7zEJ7E_pRCOYz5mQqbk-MFE6Hwr02pRRT1yP+X3z7kQ@mail.gmail.com>

Adding support to parse device node data in order to get
required properties to set pmu isolation for usb-phy.

Signed-off-by: Vivek Gautam <gautam.vivek@samsung.com>
Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Reviewed-by: Doug Anderson <dianders@chromium.org>
---

Changes from v6:
 - Returning error code in samsung_usbphy_parse_dt() when
   'usbphy-sys' sub-node is not present and thereby putting up
   dev_err() instead of dev_warn()

 .../devicetree/bindings/usb/samsung-usbphy.txt     |   36 +++++
 drivers/usb/phy/samsung-usbphy.c                   |  163 +++++++++++++++++---
 2 files changed, 177 insertions(+), 22 deletions(-)

diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt
index 7b26e2d..22d06cf 100644
--- a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt
+++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt
@@ -9,3 +9,39 @@ Required properties:
 - compatible : should be "samsung,exynos4210-usbphy"
 - reg : base physical address of the phy registers and length of memory mapped
 	region.
+
+Optional properties:
+- #address-cells: should be '1' when usbphy node has a child node with 'reg'
+		  property.
+- #size-cells: should be '1' when usbphy node has a child node with 'reg'
+	       property.
+- ranges: allows valid translation between child's address space and parent's
+	  address space.
+
+- The child node 'usbphy-sys' to the node 'usbphy' is for the system controller
+  interface for usb-phy. It should provide the following information required by
+  usb-phy controller to control phy.
+  - reg : base physical address of PHY_CONTROL registers.
+	  The size of this register is the total sum of size of all PHY_CONTROL
+	  registers that the SoC has. For example, the size will be
+	  '0x4' in case we have only one PHY_CONTROL register (e.g.
+	  OTHERS register in S3C64XX or USB_PHY_CONTROL register in S5PV210)
+	  and, '0x8' in case we have two PHY_CONTROL registers (e.g.
+	  USBDEVICE_PHY_CONTROL and USBHOST_PHY_CONTROL registers in exynos4x).
+	  and so on.
+
+Example:
+ - Exynos4210
+
+	usbphy at 125B0000 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "samsung,exynos4210-usbphy";
+		reg = <0x125B0000 0x100>;
+		ranges;
+
+		usbphy-sys {
+			/* USB device and host PHY_CONTROL registers */
+			reg = <0x10020704 0x8>;
+		};
+	};
diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c
index 5c5e1bb5..30aebb5 100644
--- a/drivers/usb/phy/samsung-usbphy.c
+++ b/drivers/usb/phy/samsung-usbphy.c
@@ -24,6 +24,7 @@
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/usb/otg.h>
 #include <linux/platform_data/samsung-usbphy.h>
 
@@ -60,20 +61,46 @@
 #define MHZ (1000*1000)
 #endif
 
+#define S3C64XX_USBPHY_ENABLE			(0x1 << 16)
+#define EXYNOS_USBPHY_ENABLE			(0x1 << 0)
+
 enum samsung_cpu_type {
 	TYPE_S3C64XX,
 	TYPE_EXYNOS4210,
 };
 
 /*
+ * struct samsung_usbphy_drvdata - driver data for various SoC variants
+ * @cpu_type: machine identifier
+ * @devphy_en_mask: device phy enable mask for PHY CONTROL register
+ * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from
+ *		       mapped address of system controller.
+ *
+ *	Here we have a separate mask for device type phy.
+ *	Having different masks for host and device type phy helps
+ *	in setting independent masks in case of SoCs like S5PV210,
+ *	in which PHY0 and PHY1 enable bits belong to same register
+ *	placed at position 0 and 1 respectively.
+ *	Although for newer SoCs like exynos these bits belong to
+ *	different registers altogether placed@position 0.
+ */
+struct samsung_usbphy_drvdata {
+	int cpu_type;
+	int devphy_en_mask;
+	u32 devphy_reg_offset;
+};
+
+/*
  * struct samsung_usbphy - transceiver driver state
  * @phy: transceiver structure
  * @plat: platform data
  * @dev: The parent device supplied to the probe function
  * @clk: usb phy clock
- * @regs: usb phy register memory base
+ * @regs: usb phy controller registers memory base
+ * @pmuregs: USB device PHY_CONTROL register memory base
  * @ref_clk_freq: reference clock frequency selection
- * @cpu_type: machine identifier
+ * @drv_data: driver data available for different SoCs
+ * @lock: lock for phy operations
  */
 struct samsung_usbphy {
 	struct usb_phy	phy;
@@ -81,12 +108,66 @@ struct samsung_usbphy {
 	struct device	*dev;
 	struct clk	*clk;
 	void __iomem	*regs;
+	void __iomem	*pmuregs;
 	int		ref_clk_freq;
-	int		cpu_type;
+	const struct samsung_usbphy_drvdata *drv_data;
+	spinlock_t	lock;
 };
 
 #define phy_to_sphy(x)		container_of((x), struct samsung_usbphy, phy)
 
+static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy)
+{
+	struct device_node *usbphy_sys;
+
+	/* Getting node for system controller interface for usb-phy */
+	usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys");
+	if (!usbphy_sys) {
+		dev_err(sphy->dev, "No sys-controller interface for usb-phy\n");
+		return -ENODEV;
+	}
+
+	sphy->pmuregs = of_iomap(usbphy_sys, 0);
+
+	of_node_put(usbphy_sys);
+
+	if (sphy->pmuregs == NULL) {
+		dev_err(sphy->dev, "Can't get usb-phy pmu control register\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/*
+ * Set isolation here for phy.
+ * Here 'on = true' would mean USB PHY block is isolated, hence
+ * de-activated and vice-versa.
+ */
+static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on)
+{
+	void __iomem *reg;
+	u32 reg_val;
+	u32 en_mask;
+
+	if (!sphy->pmuregs) {
+		dev_warn(sphy->dev, "Can't set pmu isolation\n");
+		return;
+	}
+
+	reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset;
+	en_mask = sphy->drv_data->devphy_en_mask;
+
+	reg_val = readl(reg);
+
+	if (on)
+		reg_val &= ~en_mask;
+	else
+		reg_val |= en_mask;
+
+	writel(reg_val, reg);
+}
+
 /*
  * Returns reference clock frequency selection value
  */
@@ -112,7 +193,7 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy)
 		refclk_freq = PHYCLK_CLKSEL_48M;
 		break;
 	default:
-		if (sphy->cpu_type == TYPE_S3C64XX)
+		if (sphy->drv_data->cpu_type == TYPE_S3C64XX)
 			refclk_freq = PHYCLK_CLKSEL_48M;
 		else
 			refclk_freq = PHYCLK_CLKSEL_24M;
@@ -135,7 +216,7 @@ static void samsung_usbphy_enable(struct samsung_usbphy *sphy)
 	phypwr = readl(regs + SAMSUNG_PHYPWR);
 	rstcon = readl(regs + SAMSUNG_RSTCON);
 
-	switch (sphy->cpu_type) {
+	switch (sphy->drv_data->cpu_type) {
 	case TYPE_S3C64XX:
 		phyclk &= ~PHYCLK_COMMON_ON_N;
 		phypwr &= ~PHYPWR_NORMAL_MASK;
@@ -165,7 +246,7 @@ static void samsung_usbphy_disable(struct samsung_usbphy *sphy)
 
 	phypwr = readl(regs + SAMSUNG_PHYPWR);
 
-	switch (sphy->cpu_type) {
+	switch (sphy->drv_data->cpu_type) {
 	case TYPE_S3C64XX:
 		phypwr |= PHYPWR_NORMAL_MASK;
 		break;
@@ -185,6 +266,7 @@ static void samsung_usbphy_disable(struct samsung_usbphy *sphy)
 static int samsung_usbphy_init(struct usb_phy *phy)
 {
 	struct samsung_usbphy *sphy;
+	unsigned long flags;
 	int ret = 0;
 
 	sphy = phy_to_sphy(phy);
@@ -196,13 +278,19 @@ static int samsung_usbphy_init(struct usb_phy *phy)
 		return ret;
 	}
 
+	spin_lock_irqsave(&sphy->lock, flags);
+
 	/* Disable phy isolation */
 	if (sphy->plat && sphy->plat->pmu_isolation)
 		sphy->plat->pmu_isolation(false);
+	else
+		samsung_usbphy_set_isolation(sphy, false);
 
 	/* Initialize usb phy registers */
 	samsung_usbphy_enable(sphy);
 
+	spin_unlock_irqrestore(&sphy->lock, flags);
+
 	/* Disable the phy clock */
 	clk_disable_unprepare(sphy->clk);
 	return ret;
@@ -214,6 +302,7 @@ static int samsung_usbphy_init(struct usb_phy *phy)
 static void samsung_usbphy_shutdown(struct usb_phy *phy)
 {
 	struct samsung_usbphy *sphy;
+	unsigned long flags;
 
 	sphy = phy_to_sphy(phy);
 
@@ -222,44 +311,47 @@ static void samsung_usbphy_shutdown(struct usb_phy *phy)
 		return;
 	}
 
+	spin_lock_irqsave(&sphy->lock, flags);
+
 	/* De-initialize usb phy registers */
 	samsung_usbphy_disable(sphy);
 
 	/* Enable phy isolation */
 	if (sphy->plat && sphy->plat->pmu_isolation)
 		sphy->plat->pmu_isolation(true);
+	else
+		samsung_usbphy_set_isolation(sphy, true);
+
+	spin_unlock_irqrestore(&sphy->lock, flags);
 
 	clk_disable_unprepare(sphy->clk);
 }
 
 static const struct of_device_id samsung_usbphy_dt_match[];
 
-static inline int samsung_usbphy_get_driver_data(struct platform_device *pdev)
+static inline const struct samsung_usbphy_drvdata
+*samsung_usbphy_get_driver_data(struct platform_device *pdev)
 {
 	if (pdev->dev.of_node) {
 		const struct of_device_id *match;
 		match = of_match_node(samsung_usbphy_dt_match,
 							pdev->dev.of_node);
-		return (int) match->data;
+		return match->data;
 	}
 
-	return platform_get_device_id(pdev)->driver_data;
+	return (struct samsung_usbphy_drvdata *)
+				platform_get_device_id(pdev)->driver_data;
 }
 
 static int __devinit samsung_usbphy_probe(struct platform_device *pdev)
 {
 	struct samsung_usbphy *sphy;
-	struct samsung_usbphy_data *pdata;
+	struct samsung_usbphy_data *pdata = pdev->dev.platform_data;
 	struct device *dev = &pdev->dev;
 	struct resource *phy_mem;
 	void __iomem	*phy_base;
 	struct clk *clk;
-
-	pdata = pdev->dev.platform_data;
-	if (!pdata) {
-		dev_err(&pdev->dev, "%s: no platform data defined\n", __func__);
-		return -EINVAL;
-	}
+	int ret;
 
 	phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!phy_mem) {
@@ -283,7 +375,19 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev)
 		return PTR_ERR(clk);
 	}
 
-	sphy->dev		= &pdev->dev;
+	sphy->dev = dev;
+
+	if (dev->of_node) {
+		ret = samsung_usbphy_parse_dt(sphy);
+		if (ret < 0)
+			return ret;
+	} else {
+		if (!pdata) {
+			dev_err(dev, "no platform data specified\n");
+			return -EINVAL;
+		}
+	}
+
 	sphy->plat		= pdata;
 	sphy->regs		= phy_base;
 	sphy->clk		= clk;
@@ -291,9 +395,11 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev)
 	sphy->phy.label		= "samsung-usbphy";
 	sphy->phy.init		= samsung_usbphy_init;
 	sphy->phy.shutdown	= samsung_usbphy_shutdown;
-	sphy->cpu_type		= samsung_usbphy_get_driver_data(pdev);
+	sphy->drv_data		= samsung_usbphy_get_driver_data(pdev);
 	sphy->ref_clk_freq	= samsung_usbphy_get_refclk_freq(sphy);
 
+	spin_lock_init(&sphy->lock);
+
 	platform_set_drvdata(pdev, sphy);
 
 	return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2);
@@ -305,17 +411,30 @@ static int __exit samsung_usbphy_remove(struct platform_device *pdev)
 
 	usb_remove_phy(&sphy->phy);
 
+	if (sphy->pmuregs)
+		iounmap(sphy->pmuregs);
+
 	return 0;
 }
 
+static const struct samsung_usbphy_drvdata usbphy_s3c64xx = {
+	.cpu_type		= TYPE_S3C64XX,
+	.devphy_en_mask		= S3C64XX_USBPHY_ENABLE,
+};
+
+static const struct samsung_usbphy_drvdata usbphy_exynos4 = {
+	.cpu_type		= TYPE_EXYNOS4210,
+	.devphy_en_mask		= EXYNOS_USBPHY_ENABLE,
+};
+
 #ifdef CONFIG_OF
 static const struct of_device_id samsung_usbphy_dt_match[] = {
 	{
 		.compatible = "samsung,s3c64xx-usbphy",
-		.data = (void *)TYPE_S3C64XX,
+		.data = &usbphy_s3c64xx,
 	}, {
 		.compatible = "samsung,exynos4210-usbphy",
-		.data = (void *)TYPE_EXYNOS4210,
+		.data = &usbphy_exynos4,
 	},
 	{},
 };
@@ -325,10 +444,10 @@ MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match);
 static struct platform_device_id samsung_usbphy_driver_ids[] = {
 	{
 		.name		= "s3c64xx-usbphy",
-		.driver_data	= TYPE_S3C64XX,
+		.driver_data	= (unsigned long)&usbphy_s3c64xx,
 	}, {
 		.name		= "exynos4210-usbphy",
-		.driver_data	= TYPE_EXYNOS4210,
+		.driver_data	= (unsigned long)&usbphy_exynos4,
 	},
 	{},
 };
-- 
1.7.6.5

^ permalink raw reply related

* [PATCH v2 4/4] usb: chipidea: imx: Add system suspend/resume API
From: Peter Chen @ 2013-01-15  6:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358230117-19443-1-git-send-email-peter.chen@freescale.com>

During the system suspend/resume procedure, the USB also
needs to go suspend/resume procedure, this patch adds
related APIs. It is tested at i.mx6q sabrelite. Meanwhile,
it fixes the bug that the USB will out of work after
system suspend/resume.

Signed-off-by: Peter Chen <peter.chen@freescale.com>
---
 drivers/usb/chipidea/bits.h        |    1 +
 drivers/usb/chipidea/ci13xxx_imx.c |   61 ++++++++++++++++++++++++++++++++++++
 2 files changed, 62 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h
index ba9c6ef..d1467bb 100644
--- a/drivers/usb/chipidea/bits.h
+++ b/drivers/usb/chipidea/bits.h
@@ -47,6 +47,7 @@
 #define PORTSC_FPR            BIT(6)
 #define PORTSC_SUSP           BIT(7)
 #define PORTSC_HSP            BIT(9)
+#define PORTSC_PHCD           BIT(23) /* phy suspend mode */
 #define PORTSC_PTC            (0x0FUL << 16)
 
 /* DEVLC */
diff --git a/drivers/usb/chipidea/ci13xxx_imx.c b/drivers/usb/chipidea/ci13xxx_imx.c
index 342eab0..dd257b1 100644
--- a/drivers/usb/chipidea/ci13xxx_imx.c
+++ b/drivers/usb/chipidea/ci13xxx_imx.c
@@ -25,6 +25,7 @@
 #include <linux/mfd/syscon.h>
 
 #include "ci.h"
+#include "bits.h"
 #include "ci13xxx_imx.h"
 
 #define pdev_to_phy(pdev) \
@@ -321,6 +322,63 @@ static int ci13xxx_imx_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int ci13xxx_imx_suspend(struct device *dev)
+{
+	struct ci13xxx_imx_data *data =
+		platform_get_drvdata(to_platform_device(dev));
+	struct platform_device *plat_ci;
+	struct ci13xxx	*ci;
+
+	plat_ci = data->ci_pdev;
+	ci = platform_get_drvdata(plat_ci);
+
+	hw_write(ci, OP_PORTSC, PORTSC_PHCD, 1);
+
+	if (data->phy)
+		usb_phy_set_suspend(data->phy, 1);
+
+	clk_disable_unprepare(data->clk);
+
+	return 0;
+}
+
+static int ci13xxx_imx_resume(struct device *dev)
+{
+	int ret;
+	struct ci13xxx_imx_data *data =
+		platform_get_drvdata(to_platform_device(dev));
+	struct platform_device *plat_ci;
+	struct ci13xxx	*ci;
+
+	plat_ci = data->ci_pdev;
+	ci = platform_get_drvdata(plat_ci);
+
+	ret = clk_prepare_enable(data->clk);
+	if (ret) {
+		dev_err(dev,
+			"Failed to prepare or enable clock, err=%d\n", ret);
+		return ret;
+	}
+
+	if (hw_read(ci, OP_PORTSC, PORTSC_PHCD)) {
+		hw_write(ci, OP_PORTSC, PORTSC_PHCD, 0);
+		/* Some clks sync between Controller and USB PHY */
+		mdelay(1);
+	}
+
+	if (data->phy)
+		usb_phy_set_suspend(data->phy, 0);
+
+	return ret;
+}
+
+static const struct dev_pm_ops ci13xxx_imx_pm_ops = {
+	.suspend	= ci13xxx_imx_suspend,
+	.resume		= ci13xxx_imx_resume,
+};
+#endif
+
 static const struct of_device_id ci13xxx_imx_dt_ids[] = {
 	{ .compatible = "fsl,imx27-usb", },
 	{ /* sentinel */ }
@@ -334,6 +392,9 @@ static struct platform_driver ci13xxx_imx_driver = {
 		.name = "imx_usb",
 		.owner = THIS_MODULE,
 		.of_match_table = ci13xxx_imx_dt_ids,
+#ifdef CONFIG_PM
+		.pm	= &ci13xxx_imx_pm_ops,
+#endif
 	 },
 };
 
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH v2 3/4] usb: mxs-phy: add set_suspend API
From: Peter Chen @ 2013-01-15  6:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358230117-19443-1-git-send-email-peter.chen@freescale.com>

It needs to call set_suspend during USB suspend/resume

Signed-off-by: Peter Chen <peter.chen@freescale.com>
---
 drivers/usb/otg/mxs-phy.c |   20 ++++++++++++++++++++
 1 files changed, 20 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/otg/mxs-phy.c b/drivers/usb/otg/mxs-phy.c
index 49727dd..cad16e5 100644
--- a/drivers/usb/otg/mxs-phy.c
+++ b/drivers/usb/otg/mxs-phy.c
@@ -84,6 +84,25 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
 	clk_disable_unprepare(mxs_phy->clk);
 }
 
+static int mxs_phy_suspend(struct usb_phy *x, int suspend)
+{
+	struct mxs_phy *mxs_phy = to_mxs_phy(x);
+
+	if (suspend) {
+		writel_relaxed(0xffffffff, x->io_priv + HW_USBPHY_PWD);
+		writel_relaxed(BM_USBPHY_CTRL_CLKGATE,
+			x->io_priv + HW_USBPHY_CTRL_SET);
+		clk_disable_unprepare(mxs_phy->clk);
+	} else {
+		clk_prepare_enable(mxs_phy->clk);
+		writel_relaxed(BM_USBPHY_CTRL_CLKGATE,
+			x->io_priv + HW_USBPHY_CTRL_CLR);
+		writel_relaxed(0, x->io_priv + HW_USBPHY_PWD);
+	}
+
+	return 0;
+}
+
 static int mxs_phy_on_connect(struct usb_phy *phy,
 		enum usb_device_speed speed)
 {
@@ -146,6 +165,7 @@ static int mxs_phy_probe(struct platform_device *pdev)
 	mxs_phy->phy.label		= DRIVER_NAME;
 	mxs_phy->phy.init		= mxs_phy_init;
 	mxs_phy->phy.shutdown		= mxs_phy_shutdown;
+	mxs_phy->phy.set_suspend	= mxs_phy_suspend;
 	mxs_phy->phy.notify_connect	= mxs_phy_on_connect;
 	mxs_phy->phy.notify_disconnect	= mxs_phy_on_disconnect;
 
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH v2 2/4] usb: mxs-phy: change clock usage for i.mx6q
From: Peter Chen @ 2013-01-15  6:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358230117-19443-1-git-send-email-peter.chen@freescale.com>

For mxs-phy user i.mx6q, the PHY's clock is controlled by
hardware automatically, the software only needs to enable it
at probe, disable it at remove. But other mxs-phy users need
to control that clock runtime, so we hardcode clk on/off,
and give a reserved bit for clk on/off at clk code for i.mx6q.

Signed-off-by: Peter Chen <peter.chen@freescale.com>
---
Changes for v2:
- Only control gate bit for phy clk control
- Only open the gate at probe, and close the gate at remove

 Documentation/devicetree/bindings/usb/mxs-phy.txt |    2 +
 arch/arm/boot/dts/imx6q.dtsi                      |    2 +
 drivers/usb/otg/mxs-phy.c                         |   61 +++++++++++++++++++++
 3 files changed, 65 insertions(+), 0 deletions(-)

diff --git a/Documentation/devicetree/bindings/usb/mxs-phy.txt b/Documentation/devicetree/bindings/usb/mxs-phy.txt
index 5835b27..384e700 100644
--- a/Documentation/devicetree/bindings/usb/mxs-phy.txt
+++ b/Documentation/devicetree/bindings/usb/mxs-phy.txt
@@ -4,10 +4,12 @@ Required properties:
 - compatible: Should be "fsl,imx23-usbphy"
 - reg: Should contain registers location and length
 - interrupts: Should contain phy interrupt
+- The reg offset for PHY clock at anatop
 
 Example:
 usbphy1: usbphy at 020c9000 {
 	compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy";
 	reg = <0x020c9000 0x1000>;
 	interrupts = <0 44 0x04>;
+	anatop-phy-reg-offset = <0x10>;
 };
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index d6265ca..1517e93 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -519,6 +519,7 @@
 				reg = <0x020c9000 0x1000>;
 				interrupts = <0 44 0x04>;
 				clocks = <&clks 182>;
+				anatop-phy-reg-offset = <0x10>;
 			};
 
 			usbphy2: usbphy at 020ca000 {
@@ -526,6 +527,7 @@
 				reg = <0x020ca000 0x1000>;
 				interrupts = <0 45 0x04>;
 				clocks = <&clks 183>;
+				anatop-phy-reg-offset = <0x20>;
 			};
 
 			snvs at 020cc000 {
diff --git a/drivers/usb/otg/mxs-phy.c b/drivers/usb/otg/mxs-phy.c
index 7630272..49727dd 100644
--- a/drivers/usb/otg/mxs-phy.c
+++ b/drivers/usb/otg/mxs-phy.c
@@ -20,6 +20,9 @@
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
 
 #define DRIVER_NAME "mxs_phy"
 
@@ -34,6 +37,11 @@
 #define BM_USBPHY_CTRL_ENUTMILEVEL2		BIT(14)
 #define BM_USBPHY_CTRL_ENHOSTDISCONDETECT	BIT(1)
 
+#define CTRL_SET				0x4
+#define CTRL_CLR				0x8
+
+#define BM_ANADIG_USB_PLL_480_CTRL_EN_USB_CLKS		(1 << 6)
+
 struct mxs_phy {
 	struct usb_phy phy;
 	struct clk *clk;
@@ -108,6 +116,7 @@ static int mxs_phy_probe(struct platform_device *pdev)
 	void __iomem *base;
 	struct clk *clk;
 	struct mxs_phy *mxs_phy;
+	struct regmap *anatop;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res) {
@@ -146,11 +155,63 @@ static int mxs_phy_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, &mxs_phy->phy);
 
+	/*
+	 * At mx6x, USB PHY PLL and its output gate is controlled by hardware.
+	 * It just needs to open the gate at init, if the usb device is
+	 * in suspend, it will close related PLL automatically without
+	 * the gate is on or off.
+	 */
+
+	anatop = syscon_regmap_lookup_by_compatible("fsl,imx6q-anatop");
+
+	if (!IS_ERR(anatop)) {
+		struct device *dev = &pdev->dev;
+		struct device_node *np = dev->of_node;
+		u32 phy_reg_offset;
+		int ret;
+
+		ret = of_property_read_u32(np, "anatop-phy-reg-offset",
+					   &phy_reg_offset);
+		if (ret) {
+			dev_err(dev, "no anatop-phy-reg-offset property set\n");
+			return -EINVAL;
+		}
+
+		regmap_write(anatop, phy_reg_offset + CTRL_SET,
+				BM_ANADIG_USB_PLL_480_CTRL_EN_USB_CLKS);
+	} else {
+		pr_warn("failed to find fsl,imx6q-anatop regmap\n");
+	}
+
 	return 0;
 }
 
 static int mxs_phy_remove(struct platform_device *pdev)
 {
+	struct regmap *anatop;
+
+	/* close the clock gate for USB PHY */
+	anatop = syscon_regmap_lookup_by_compatible("fsl,imx6q-anatop");
+
+	if (!IS_ERR(anatop)) {
+		struct device *dev = &pdev->dev;
+		struct device_node *np = dev->of_node;
+		u32 phy_reg_offset;
+		int ret;
+
+		ret = of_property_read_u32(np, "anatop-phy-reg-offset",
+					   &phy_reg_offset);
+		if (ret) {
+			dev_err(dev, "no anatop-phy-reg-offset property set\n");
+			return -EINVAL;
+		}
+
+		regmap_write(anatop, phy_reg_offset + CTRL_CLR,
+				BM_ANADIG_USB_PLL_480_CTRL_EN_USB_CLKS);
+	} else {
+		pr_warn("failed to find fsl,imx6q-anatop regmap\n");
+	}
+
 	platform_set_drvdata(pdev, NULL);
 
 	return 0;
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH v2 1/4] ARM i.MX6: use reserved bit for mxs phy clock gate
From: Peter Chen @ 2013-01-15  6:08 UTC (permalink / raw)
  To: linux-arm-kernel

For mxs-phy user i.mx6q, the PHY's clock is controlled by
hardware automatically, the software only needs to enable it
at probe, disable it at remove. During the runtime,
we don't need to control it. So for the usbphy clk policy:

- Keep refcount for usbphy as clk framework needs to know if
it is off or on.
- Use reserved bit, in that case, clk_enable/disable will
only update refcount, but without any hardware effects.

Signed-off-by: Peter Chen <peter.chen@freescale.com>
---
Changes for v2:
- Use reserved bit for usb phy clk control

 arch/arm/mach-imx/clk-imx6q.c |   10 ++++++++--
 1 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
index 7f2c10c..85dcc89 100644
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -208,8 +208,14 @@ int __init mx6q_clocks_init(void)
 	clk[pll7_usb_host] = imx_clk_pllv3(IMX_PLLV3_USB,	"pll7_usb_host","osc", base + 0x20, 0x3);
 	clk[pll8_mlb]      = imx_clk_pllv3(IMX_PLLV3_MLB,	"pll8_mlb",	"osc", base + 0xd0, 0x0);
 
-	clk[usbphy1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 6);
-	clk[usbphy2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 6);
+	/*
+	 * Bit 20 is the reserved and read-only bit, we do this only for:
+	 * - Do nothing for usbphy clk_enable/disable
+	 * - Keep refcount when do usbphy clk_enable/disable, in that case,
+	 * the clk framework can know the USB phy clk is on or off
+	 */
+	clk[usbphy1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20);
+	clk[usbphy2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 20);
 
 	clk[sata_ref] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5);
 	clk[pcie_ref] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4);
-- 
1.7.0.4

^ permalink raw reply related

* [Bulk] [PATCH] timer: vt8500: Move timer code to drivers/clocksource
From: Olof Johansson @ 2013-01-15  6:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358223166.27472.3.camel@gitbox>

On Mon, Jan 14, 2013 at 8:12 PM, Tony Prisk <linux@prisktech.co.nz> wrote:
> On Mon, 2013-01-14 at 12:07 -0800, Olof Johansson wrote:
>> On Mon, Jan 14, 2013 at 06:47:35PM +1300, Tony Prisk wrote:
>> > On Mon, 2013-01-14 at 18:13 +1300, Tony Prisk wrote:
>> > > On Mon, 2013-01-14 at 18:09 +1300, Tony Prisk wrote:
>> > > > This patch moves arch-vt8500/timer.c into drivers/clocksource and
>> > > > updates the necessary Kconfig/Makefile options.
>> > > >
>> > > > Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
>> > > > ---
>> > > >  arch/arm/mach-vt8500/Kconfig       |    1 +
>> > > >  arch/arm/mach-vt8500/Makefile      |    2 +-
>> > > >  arch/arm/mach-vt8500/common.h      |    1 -
>> > > >  arch/arm/mach-vt8500/timer.c       |  184 ------------------------------------
>> > > >  arch/arm/mach-vt8500/vt8500.c      |    1 +
>> > > >  drivers/clocksource/Kconfig        |    3 +
>> > > >  drivers/clocksource/Makefile       |    1 +
>> > > >  drivers/clocksource/vt8500_timer.c |  184 ++++++++++++++++++++++++++++++++++++
>> > > >  include/linux/vt8500_timer.h       |   22 +++++
>> > > >  9 files changed, 213 insertions(+), 186 deletions(-)
>> > > >  delete mode 100644 arch/arm/mach-vt8500/timer.c
>> > > >  create mode 100644 drivers/clocksource/vt8500_timer.c
>> > > >  create mode 100644 include/linux/vt8500_timer.h
>> > >
>> > > Darn.. forgot the -m again. I'll await your feedback regarding the
>> > > basing of the patch first (and any other feedback), then I'll redo it
>> > > with the correct stats.
>> > >
>> > > Regards
>> > > Tony P
>> >
>> > Oh grr.. forget this completely. It doesn't take into account the
>> > patches I already sent for WM8850.
>> >
>> > I guess it needs to be based on timer/cleanup + vt8500/wm8x50.
>> >
>> > Need a little advise on how to handle this one please :)
>>
>> The normal way to handle these kind of dependencies is to base them on merges
>> of the needed branches. Based on the later email, you only seem to need
>> timer/cleanup, but if you would have needed the other one, then you'd merge
>> that on top of timer/cleanup, and then add your patches.
>>
>> Of course, ideally you would do the cleanup, then add the wm8x50 features,
>> but in reality work doesn't always pan out that way, so you end up with
>> cleanups that depend on including new features in the same (sweeping)
>> cleanup since they have already been merged. That's when things sometimes
>> get hairy, and we need to start a second cleanup branch that's "after"
>> the feature branch in the sequence of topics. But it should be rare,
>> and in your case it seems like it wasn't needed.
>>
>>
>> -Olof
>>
>
> Just to clarify what I did (and to make sure it was as you understood
> it):
>
> #1) I wrote the patch on top of timer/cleanup. This is the branch the
> patch was written for.
>
> #2) I then pulled timer/cleanup and merged vt8500/wm8x50 on top, then
> reapplied the patch from #1 - it applied cleanly.
>
> What I have just realised is that you might?? get a conflict when you
> merge vt8500/wm8x50 on top of wherever this patch ends up due to the few
> lines at the top of arch-vt8500/Kconfig having changed (the addition of
> SELECT VT8500_TIMER). This should be trivial to fix (I assume).

Yeah, a couple of trivial conflicts are fine, but when they start to
stack up we normally have to push back since some of these are exposed
all the way up to Linus. We can also be a little more strategic in how
we chose to pick bases for the topic branches to avoid some of this,
which is what I intend to do this release cycle if it pans out.

In this case, there are no manual conflict resolution to be done,
everything panned out just fine as it was.


-Olof

^ permalink raw reply

* [PATCH] timer: vt8500: Move timer code to drivers/clocksource
From: Olof Johansson @ 2013-01-15  5:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358225583.5576.1.camel@gitbox>

On Mon, Jan 14, 2013 at 8:53 PM, Tony Prisk <linux@prisktech.co.nz> wrote:
> On Mon, 2013-01-14 at 09:34 -0700, Stephen Warren wrote:
>> On 01/13/2013 10:09 PM, Tony Prisk wrote:
>> > This patch moves arch-vt8500/timer.c into drivers/clocksource and
>> > updates the necessary Kconfig/Makefile options.
>>
>> > diff --git a/include/linux/vt8500_timer.h b/include/linux/vt8500_timer.h
>>
>> > +#ifndef __VT8500_TIMER_H
>> > +#define __VT8500_TIMER_H
>> > +
>> > +#include <asm/mach/time.h>
>> > +
>> > +void vt8500_timer_init(void);
>> > +
>> > +#endif
>>
>> Is VT8500 DT-only? If so, it'd be nice not to add this header, but
>> instead use CLOCKSOURCE_OF_DECLARE() inside the driver C file.
>
> Agreed - I didn't like the header when I added it but I didn't know of
> another way and based in on the sunxi code.
>
> Unfortunately Olof already pulled it into arm-soc, so I will try get
> another patch done to undo it :)

Ack, I saw the comments but somehow blanked when it came to pulling
it. Incremental patches on top are fine though.

Sorry Stephen, I didn't mean to ignore your comments. :)


-Olof

^ permalink raw reply

* [PATCH] usb: phy: samsung: Add support to set pmu isolation
From: Vivek Gautam @ 2013-01-15  5:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAD=FV=Vxdjo5aOU25a-iDNDaiGZCu0+G-p5y1NifXHUJBF0AHg@mail.gmail.com>

Hi Doug,


On Tue, Jan 15, 2013 at 3:41 AM, Doug Anderson <dianders@chromium.org> wrote:
> Vivek,
>
> Sorry for being so absent from these reviews.  I'll try to look over a
> few patches today, but please don't hold up anything on account of my
> reviews.  I'm definitely a bit of an interested bystander in USB land.
>  ;)
>
> In general things look pretty good here.  :)  One last comment below...
>
> On Fri, Jan 11, 2013 at 12:08 AM, Vivek Gautam
> <gautam.vivek@samsung.com> wrote:> +static int
> samsung_usbphy_parse_dt(struct samsung_usbphy *sphy)
>> +{
>> +       struct device_node *usbphy_sys;
>> +
>> +       /* Getting node for system controller interface for usb-phy */
>> +       usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys");
>> +       if (!usbphy_sys)
>> +               dev_warn(sphy->dev, "No sys-controller interface for usb-phy\n");
>
> Seems like you ought to return with an error here.  Calling of_iomap()
> with a NULL value seems undesirable.
>

Yeah, true. This should have been returning error value alongwith dev_err().

>> +
>> +       sphy->pmuregs = of_iomap(usbphy_sys, 0);
>> +
>> +       of_node_put(usbphy_sys);
>> +
>> +       if (sphy->pmuregs == NULL) {
>> +               dev_err(sphy->dev, "Can't get usb-phy pmu control register\n");
>> +               return -ENODEV;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +/*
>> + * Set isolation here for phy.
>> + * Here 'on = true' would mean USB PHY block is isolated, hence
>> + * de-activated and vice-versa.
>> + */
>
> Thank you very much for this comment.  :)  This explains one of the
> confusions I had earlier...
>
Your welcome :-)
>
> Once you fix the one error condition above you can add my
> "Reviewed-by".  Thanks!
>
Sure, thanks !!



-- 
Thanks & Regards
Vivek

^ permalink raw reply

* [PATCH v2] gpio: vt8500: memory cleanup missing
From: Tony Prisk @ 2013-01-15  5:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358188644-23838-1-git-send-email-linux@prisktech.co.nz>

On Tue, 2013-01-15 at 07:37 +1300, Tony Prisk wrote:
> This driver is missing a .remove callback, and the fail path on
> probe is incomplete.
> 
> If an error occurs in vt8500_add_chips, gpio_base is not unmapped.
> The driver is also ignoring the return value from this function so
> if a chip fails to register it completes as successful.
> 
> Replaced pr_err with dev_err in vt8500_add_chips since the device is
> available.
> 
> There is also no .remove callback defined. To allow removing the
> registered chips, I have moved *vtchip to be a static global.
> 
> Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
> ---
> Hi Grant,
> 
> Let me know what you think of these changes.
> 
> v2:
> Removed unnecessary whitespace change.
> Removed test against pdev->dev.of_node (np). Replaced code with a
>   devm_request_and_ioremap so np is now unneccessary. This also removes the need
>   for cleanup in the fail path.
> Move struct vt8500_gpio_chip within vt8500_data and store the iobase and
>   num_banks in vt8500_data.
> 

Grant,

If there are no further changes for this patch, would you mind
correcting the commit message when you merge it?

-There is also no .remove callback defined. To allow removing the
-Registered chips, I have moved *vtchip to be a static global.
+ There is also no .remove callback defined.

Otherwise, I will fix it along with whatever other comments come in.

Regards
Tony P

^ permalink raw reply

* [PATCH 2/4] serial: vt8500: ioremap'd resource is never freed
From: Tony Prisk @ 2013-01-15  5:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358225886-5686-3-git-send-email-linux@prisktech.co.nz>

On Tue, 2013-01-15 at 17:58 +1300, Tony Prisk wrote:
> Memory mapped via ioremap call is never released. Rather than add an
> iounmap call, change allocation function to devm_request_and_ioremap.
> 
> Also, change the error on failure for this call to -EBUSY rather than
> -EADDRNOTAVAIL.

Grr.. I thought I fixed this typo before I sent the patch.
I'll send another version but I'll wait for feedback on the series
first.

Regards
Tony P

^ permalink raw reply

* [PATCH 4/4] serial: tty: Cleanup code using devm_ function
From: Tony Prisk @ 2013-01-15  4:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358225886-5686-1-git-send-email-linux@prisktech.co.nz>

Convert the last memory allocation (vt8500_port) to use devm_kzalloc
and remove the fail path cleanup code from vt8500_serial_probe.

Reorder iomem mapping above clk_enable to simplify fail code. The
clock is only enabled if all other resources are available.

Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
---
 drivers/tty/serial/vt8500_serial.c |   13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 7f9e578..1fc6f3d 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -589,7 +589,8 @@ static int vt8500_serial_probe(struct platform_device *pdev)
 		return -EBUSY;
 	}
 
-	vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL);
+	vt8500_port = devm_kzalloc(&pdev->dev, sizeof(struct vt8500_port),
+				   GFP_KERNEL);
 	if (!vt8500_port)
 		return -ENOMEM;
 
@@ -600,14 +601,13 @@ static int vt8500_serial_probe(struct platform_device *pdev)
 	vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0);
 	if (IS_ERR(vt8500_port->clk)) {
 		dev_err(&pdev->dev, "failed to get clock\n");
-		ret = -EINVAL;
-		goto err;
+		return  -EINVAL;
 	}
 
 	ret = clk_prepare_enable(vt8500_port->clk);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to enable clock\n");
- 		goto err;
+ 		return ret;
  	}
 
 	vt8500_port->uart.type = PORT_VT8500;
@@ -631,10 +631,6 @@ static int vt8500_serial_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, vt8500_port);
 
 	return 0;
-
-err:
-	kfree(vt8500_port);
-	return ret;
 }
 
 static int vt8500_serial_remove(struct platform_device *pdev)
@@ -644,7 +640,6 @@ static int vt8500_serial_remove(struct platform_device *pdev)
 	platform_set_drvdata(pdev, NULL);
 	clk_disable_unprepare(vt8500_port->clk);
 	uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart);
-	kfree(vt8500_port);
 
 	return 0;
 }
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 3/4] serial: vt8500: UART uses gated clock rather than 24Mhz reference
From: Tony Prisk @ 2013-01-15  4:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358225886-5686-1-git-send-email-linux@prisktech.co.nz>

UART modules on Wondermedia SoCs are connected via a gated clock
source, rather than directly to the 24Mhz reference clock. While
uboot enables UART0 for debugging, other UART ports are unavailable
until the clock is enabled.

This patch checks that a valid clock is actually passed from devicetree,
enables the clock in probe. This change removes the fallback when a
clock was not specified as it doesn't apply any longer (and would only
work if the UART clock was already enabled).

DTSI files are updated for VT8500, WM8505 and WM8650.

Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
---
 arch/arm/boot/dts/vt8500.dtsi      |   40 +++++++++++++++++++++---
 arch/arm/boot/dts/wm8505.dtsi      |   60 ++++++++++++++++++++++++++++++++----
 arch/arm/boot/dts/wm8650.dtsi      |   20 ++++++++++--
 drivers/tty/serial/vt8500_serial.c |   34 +++++++++++---------
 4 files changed, 127 insertions(+), 27 deletions(-)

diff --git a/arch/arm/boot/dts/vt8500.dtsi b/arch/arm/boot/dts/vt8500.dtsi
index d8645e9..cf31ced 100644
--- a/arch/arm/boot/dts/vt8500.dtsi
+++ b/arch/arm/boot/dts/vt8500.dtsi
@@ -45,6 +45,38 @@
 					compatible = "fixed-clock";
 					clock-frequency = <24000000>;
 				};
+
+				clkuart0: uart0 {
+					#clock-cells = <0>;
+					compatible = "via,vt8500-device-clock";
+					clocks = <&ref24>;
+					enable-reg = <0x250>;
+					enable-bit = <1>;
+				};
+
+				clkuart1: uart1 {
+					#clock-cells = <0>;
+					compatible = "via,vt8500-device-clock";
+					clocks = <&ref24>;
+					enable-reg = <0x250>;
+					enable-bit = <2>;
+				};
+
+				clkuart2: uart2 {
+					#clock-cells = <0>;
+					compatible = "via,vt8500-device-clock";
+					clocks = <&ref24>;
+					enable-reg = <0x250>;
+					enable-bit = <3>;
+				};
+
+				clkuart3: uart3 {
+					#clock-cells = <0>;
+					compatible = "via,vt8500-device-clock";
+					clocks = <&ref24>;
+					enable-reg = <0x250>;
+					enable-bit = <4>;
+				};
 			};
 		};
 
@@ -83,28 +115,28 @@
 			compatible = "via,vt8500-uart";
 			reg = <0xd8200000 0x1040>;
 			interrupts = <32>;
-			clocks = <&ref24>;
+			clocks = <&clkuart0>;
 		};
 
 		uart at d82b0000 {
 			compatible = "via,vt8500-uart";
 			reg = <0xd82b0000 0x1040>;
 			interrupts = <33>;
-			clocks = <&ref24>;
+			clocks = <&clkuart1>;
 		};
 
 		uart at d8210000 {
 			compatible = "via,vt8500-uart";
 			reg = <0xd8210000 0x1040>;
 			interrupts = <47>;
-			clocks = <&ref24>;
+			clocks = <&clkuart2>;
 		};
 
 		uart at d82c0000 {
 			compatible = "via,vt8500-uart";
 			reg = <0xd82c0000 0x1040>;
 			interrupts = <50>;
-			clocks = <&ref24>;
+			clocks = <&clkuart3>;
 		};
 
 		rtc at d8100000 {
diff --git a/arch/arm/boot/dts/wm8505.dtsi b/arch/arm/boot/dts/wm8505.dtsi
index 330f833..e74a1c0 100644
--- a/arch/arm/boot/dts/wm8505.dtsi
+++ b/arch/arm/boot/dts/wm8505.dtsi
@@ -59,6 +59,54 @@
 					compatible = "fixed-clock";
 					clock-frequency = <24000000>;
 				};
+
+				clkuart0: uart0 {
+					#clock-cells = <0>;
+					compatible = "via,vt8500-device-clock";
+					clocks = <&ref24>;
+					enable-reg = <0x250>;
+					enable-bit = <1>;
+				};
+
+				clkuart1: uart1 {
+					#clock-cells = <0>;
+					compatible = "via,vt8500-device-clock";
+					clocks = <&ref24>;
+					enable-reg = <0x250>;
+					enable-bit = <2>;
+				};
+
+				clkuart2: uart2 {
+					#clock-cells = <0>;
+					compatible = "via,vt8500-device-clock";
+					clocks = <&ref24>;
+					enable-reg = <0x250>;
+					enable-bit = <3>;
+				};
+
+				clkuart3: uart3 {
+					#clock-cells = <0>;
+					compatible = "via,vt8500-device-clock";
+					clocks = <&ref24>;
+					enable-reg = <0x250>;
+					enable-bit = <4>;
+				};
+
+				clkuart4: uart4 {
+					#clock-cells = <0>;
+					compatible = "via,vt8500-device-clock";
+					clocks = <&ref24>;
+					enable-reg = <0x250>;
+					enable-bit = <22>;
+				};
+
+				clkuart5: uart5 {
+					#clock-cells = <0>;
+					compatible = "via,vt8500-device-clock";
+					clocks = <&ref24>;
+					enable-reg = <0x250>;
+					enable-bit = <23>;
+				};
 			};
 		};
 
@@ -96,42 +144,42 @@
 			compatible = "via,vt8500-uart";
 			reg = <0xd8200000 0x1040>;
 			interrupts = <32>;
-			clocks = <&ref24>;
+			clocks = <&clkuart0>;
 		};
 
 		uart at d82b0000 {
 			compatible = "via,vt8500-uart";
 			reg = <0xd82b0000 0x1040>;
 			interrupts = <33>;
-			clocks = <&ref24>;
+			clocks = <&clkuart1>;
 		};
 
 		uart at d8210000 {
 			compatible = "via,vt8500-uart";
 			reg = <0xd8210000 0x1040>;
 			interrupts = <47>;
-			clocks = <&ref24>;
+			clocks = <&clkuart2>;
 		};
 
 		uart at d82c0000 {
 			compatible = "via,vt8500-uart";
 			reg = <0xd82c0000 0x1040>;
 			interrupts = <50>;
-			clocks = <&ref24>;
+			clocks = <&clkuart3>;
 		};
 
 		uart at d8370000 {
 			compatible = "via,vt8500-uart";
 			reg = <0xd8370000 0x1040>;
 			interrupts = <31>;
-			clocks = <&ref24>;
+			clocks = <&clkuart4>;
 		};
 
 		uart at d8380000 {
 			compatible = "via,vt8500-uart";
 			reg = <0xd8380000 0x1040>;
 			interrupts = <30>;
-			clocks = <&ref24>;
+			clocks = <&clkuart5>;
 		};
 
 		rtc at d8100000 {
diff --git a/arch/arm/boot/dts/wm8650.dtsi b/arch/arm/boot/dts/wm8650.dtsi
index 83b9467..e0c42ff 100644
--- a/arch/arm/boot/dts/wm8650.dtsi
+++ b/arch/arm/boot/dts/wm8650.dtsi
@@ -75,6 +75,22 @@
 					reg = <0x204>;
 				};
 
+				clkuart0: uart0 {
+ 					#clock-cells = <0>;
+ 					compatible = "via,vt8500-device-clock";
+					clocks = <&ref24>;
+					enable-reg = <0x250>;
+					enable-bit = <1>;
+ 				};
+ 
+				clkuart1: uart1 {
+					#clock-cells = <0>;
+					compatible = "via,vt8500-device-clock";
+					clocks = <&ref24>;
+					enable-reg = <0x250>;
+					enable-bit = <2>;
+				};
+
 				arm: arm {
 					#clock-cells = <0>;
 					compatible = "via,vt8500-device-clock";
@@ -128,14 +144,14 @@
 			compatible = "via,vt8500-uart";
 			reg = <0xd8200000 0x1040>;
 			interrupts = <32>;
-			clocks = <&ref24>;
+			clocks = <&clkuart0>;
 		};
 
 		uart at d82b0000 {
 			compatible = "via,vt8500-uart";
 			reg = <0xd82b0000 0x1040>;
 			interrupts = <33>;
-			clocks = <&ref24>;
+			clocks = <&clkuart1>;
 		};
 
 		rtc at d8100000 {
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 3e76dff..7f9e578 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -593,6 +593,23 @@ static int vt8500_serial_probe(struct platform_device *pdev)
 	if (!vt8500_port)
 		return -ENOMEM;
 
+	vt8500_port->uart.membase = devm_request_and_ioremap(&pdev->dev, mmres);
+	if (!vt8500_port->uart.membase)
+		return -EADDRNOTAVAIL;
+
+	vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0);
+	if (IS_ERR(vt8500_port->clk)) {
+		dev_err(&pdev->dev, "failed to get clock\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = clk_prepare_enable(vt8500_port->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable clock\n");
+ 		goto err;
+ 	}
+
 	vt8500_port->uart.type = PORT_VT8500;
 	vt8500_port->uart.iotype = UPIO_MEM;
 	vt8500_port->uart.mapbase = mmres->start;
@@ -602,25 +619,11 @@ static int vt8500_serial_probe(struct platform_device *pdev)
 	vt8500_port->uart.line = port;
 	vt8500_port->uart.dev = &pdev->dev;
 	vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
-
-	vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0);
-	if (vt8500_port->clk) {
-		vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk);
-	} else {
-		/* use the default of 24Mhz if not specified and warn */
-		pr_warn("%s: serial clock source not specified\n", __func__);
-		vt8500_port->uart.uartclk = 24000000;
-	}
+	vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk);
 
 	snprintf(vt8500_port->name, sizeof(vt8500_port->name),
 		 "VT8500 UART%d", pdev->id);
 
-	vt8500_port->uart.membase = devm_request_and_ioremap(&pdev->dev, mmres);
-	if (!vt8500_port->uart.membase) {
-		ret = -EADDRNOTAVAIL;
-		goto err;
-	}
-
 	vt8500_uart_ports[port] = vt8500_port;
 
 	uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart);
@@ -639,6 +642,7 @@ static int vt8500_serial_remove(struct platform_device *pdev)
 	struct vt8500_port *vt8500_port = platform_get_drvdata(pdev);
 
 	platform_set_drvdata(pdev, NULL);
+	clk_disable_unprepare(vt8500_port->clk);
 	uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart);
 	kfree(vt8500_port);
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 2/4] serial: vt8500: ioremap'd resource is never freed
From: Tony Prisk @ 2013-01-15  4:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358225886-5686-1-git-send-email-linux@prisktech.co.nz>

Memory mapped via ioremap call is never released. Rather than add an
iounmap call, change allocation function to devm_request_and_ioremap.

Also, change the error on failure for this call to -EBUSY rather than
-EADDRNOTAVAIL.

Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
---
 drivers/tty/serial/vt8500_serial.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 4c4a58d..3e76dff 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -615,9 +615,9 @@ static int vt8500_serial_probe(struct platform_device *pdev)
 	snprintf(vt8500_port->name, sizeof(vt8500_port->name),
 		 "VT8500 UART%d", pdev->id);
 
-	vt8500_port->uart.membase = ioremap(mmres->start, resource_size(mmres));
+	vt8500_port->uart.membase = devm_request_and_ioremap(&pdev->dev, mmres);
 	if (!vt8500_port->uart.membase) {
-		ret = -ENOMEM;
+		ret = -EADDRNOTAVAIL;
 		goto err;
 	}
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 1/4] serial: vt8500: Fix range-checking on vt8500_uart_ports
From: Tony Prisk @ 2013-01-15  4:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358225886-5686-1-git-send-email-linux@prisktech.co.nz>

Fix two instances where the index to vt8500_uart_ports is tested
against > VT8500_MAX_PORTS. Correct usage should be >= VT8500_MAX_PORTS.

Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
---
 drivers/tty/serial/vt8500_serial.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 8fd1814..4c4a58d 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -569,7 +569,7 @@ static int vt8500_serial_probe(struct platform_device *pdev)
 
 	if (np)
 		port = of_alias_get_id(np, "serial");
-		if (port > VT8500_MAX_PORTS)
+		if (port >= VT8500_MAX_PORTS)
 			port = -1;
 	else
 		port = -1;
@@ -580,7 +580,7 @@ static int vt8500_serial_probe(struct platform_device *pdev)
 					sizeof(vt8500_ports_in_use));
 	}
 
-	if (port > VT8500_MAX_PORTS)
+	if (port >= VT8500_MAX_PORTS)
 		return -ENODEV;
 
 	/* reserve the port id */
-- 
1.7.9.5

^ permalink raw reply related

* [GIT PULL v2] Fixes/cleanup for vt8500 serial driver
From: Tony Prisk @ 2013-01-15  4:58 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Greg,

This is a series of fixes/cleanup for the vt8500 serial/uart driver.

I'm not sure how you want to handle these.

Arguably #1, #2 and #3 are bug fixes which could go into 3.8. None of
them cause immediate problems, but potentially could.

#4 is definately a cleanup for 3.9.

Let me know if you would rather I split them up and how.

Regards
Tony P

v2 Changes:
Restore the setting of vt8500_port->uart.uartclk which was dropped in v1.
Corrected the return-on-fail of devm_request_and_ioremap to -EADDRNOTAVAIL.


The following changes since commit 9931faca02c604c22335f5a935a501bb2ace6e20:

  Linux 3.8-rc3 (2013-01-09 18:59:55 -0800)

are available in the git repository at:

  git://server.prisktech.co.nz/git/linuxwmt.git tags/vt8500-serial-fixes

for you to fetch changes up to 08bab1720e19e4f980e9e93536add4a7e497b38e:

  serial: tty: Cleanup code using devm_ function (2013-01-15 17:36:50 +1300)

----------------------------------------------------------------
Series of fixes/cleanups for vt8500 serial/UART driver

----------------------------------------------------------------
Tony Prisk (4):
      serial: vt8500: Fix range-checking on vt8500_uart_ports
      serial: vt8500: ioremap'd resource is never freed
      serial: vt8500: UART uses gated clock rather than 24Mhz reference
      serial: tty: Cleanup code using devm_ function

 arch/arm/boot/dts/vt8500.dtsi      |   40 +++++++++++++++++++++---
 arch/arm/boot/dts/wm8505.dtsi      |   60 ++++++++++++++++++++++++++++++++----
 arch/arm/boot/dts/wm8650.dtsi      |   20 ++++++++++--
 drivers/tty/serial/vt8500_serial.c |   45 +++++++++++++--------------
 4 files changed, 130 insertions(+), 35 deletions(-)

^ permalink raw reply

* [PATCH] timer: vt8500: Move timer code to drivers/clocksource
From: Tony Prisk @ 2013-01-15  4:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <50F433A9.8020105@wwwdotorg.org>

On Mon, 2013-01-14 at 09:34 -0700, Stephen Warren wrote:
> On 01/13/2013 10:09 PM, Tony Prisk wrote:
> > This patch moves arch-vt8500/timer.c into drivers/clocksource and
> > updates the necessary Kconfig/Makefile options.
> 
> > diff --git a/include/linux/vt8500_timer.h b/include/linux/vt8500_timer.h
> 
> > +#ifndef __VT8500_TIMER_H
> > +#define __VT8500_TIMER_H
> > +
> > +#include <asm/mach/time.h>
> > +
> > +void vt8500_timer_init(void);
> > +
> > +#endif
> 
> Is VT8500 DT-only? If so, it'd be nice not to add this header, but
> instead use CLOCKSOURCE_OF_DECLARE() inside the driver C file.

Agreed - I didn't like the header when I added it but I didn't know of
another way and based in on the sunxi code.

Unfortunately Olof already pulled it into arm-soc, so I will try get
another patch done to undo it :)

Regards
Tony P

^ permalink raw reply

* [PATCH] ARM: bcm2835: fix clock node aliasing in device tree
From: Stephen Warren @ 2013-01-15  4:26 UTC (permalink / raw)
  To: linux-arm-kernel

Both clock nodes in the current device tree are named "clock" and hence
end up being the same node. Rename the nodes to different names to avoid
this. In fact, fixed-clock uses the node name as the clock name, so name
the nodes after the clock they represent. Move the clocks into a
"clocks" sub-node to group them and avoid any possible naming conflicts
with other nodes also named after the device type.

Signed-off-by: Stephen Warren <swarren@wwwdotorg.org>
---
 arch/arm/boot/dts/bcm2835.dtsi |   26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
index c69a591..4bf2a87 100644
--- a/arch/arm/boot/dts/bcm2835.dtsi
+++ b/arch/arm/boot/dts/bcm2835.dtsi
@@ -89,15 +89,23 @@
 		};
 	};
 
-	clk_i2c: clock {
-		compatible = "fixed-clock";
-		#clock-cells = <0>;
-		clock-frequency = <150000000>;
-	};
+	clocks {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <0>;
 
-	clk_mmc: clock {
-		compatible = "fixed-clock";
-		#clock-cells = <0>;
-		clock-frequency = <100000000>;
+		clk_mmc: mmc {
+			compatible = "fixed-clock";
+			reg = <0>;
+			#clock-cells = <0>;
+			clock-frequency = <100000000>;
+		};
+
+		clk_i2c: i2c {
+			compatible = "fixed-clock";
+			reg = <1>;
+			#clock-cells = <0>;
+			clock-frequency = <150000000>;
+		};
 	};
 };
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH V2] i2c: add bcm2835 driver
From: Stephen Warren @ 2013-01-15  4:25 UTC (permalink / raw)
  To: linux-arm-kernel

This implements a very basic I2C host driver for the BCM2835 SoC. Missing
features so far are:

* 10-bit addressing.
* DMA.

Signed-off-by: Stephen Warren <swarren@wwwdotorg.org>
---
v2:
* Implemented clock divider configuration based on desired bus rate.
* Make use of module_platform_driver().
* Removed use of devinit.
---
 .../devicetree/bindings/i2c/brcm,bcm2835-i2c.txt   |   20 ++
 drivers/i2c/busses/Kconfig                         |   12 +
 drivers/i2c/busses/Makefile                        |    1 +
 drivers/i2c/busses/i2c-bcm2835.c                   |  346 ++++++++++++++++++++
 4 files changed, 379 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt
 create mode 100644 drivers/i2c/busses/i2c-bcm2835.c

diff --git a/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt
new file mode 100644
index 0000000..e9de375
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt
@@ -0,0 +1,20 @@
+Broadcom BCM2835 I2C controller
+
+Required properties:
+- compatible : Should be "brcm,bcm2835-i2c".
+- reg: Should contain register location and length.
+- interrupts: Should contain interrupt.
+- clocks : The clock feeding the I2C controller.
+
+Recommended properties:
+- clock-frequency : desired I2C bus clock frequency in Hz.
+
+Example:
+
+i2c at 20205000 {
+	compatible = "brcm,bcm2835-i2c";
+	reg = <0x7e205000 0x1000>;
+	interrupts = <2 21>;
+	clocks = <&clk_i2c>;
+	clock-frequency = <100000>;
+};
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bdca511..5aab774 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -319,6 +319,18 @@ config I2C_AU1550
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-au1550.
 
+config I2C_BCM2835
+	tristate "Broadcom BCM2835 I2C controller"
+	depends on ARCH_BCM2835
+	help
+	  If you say yes to this option, support will be included for the
+	  BCM2835 I2C controller.
+
+	  If you don't know what to do here, say N.
+
+	  This support is also available as a module.  If so, the module
+	  will be called i2c-bcm2835.
+
 config I2C_BLACKFIN_TWI
 	tristate "Blackfin TWI I2C support"
 	depends on BLACKFIN
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 6181f3f..a52a891 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_I2C_POWERMAC)	+= i2c-powermac.o
 # Embedded system I2C/SMBus host controller drivers
 obj-$(CONFIG_I2C_AT91)		+= i2c-at91.o
 obj-$(CONFIG_I2C_AU1550)	+= i2c-au1550.o
+obj-$(CONFIG_I2C_BCM2835)	+= i2c-bcm2835.o
 obj-$(CONFIG_I2C_BLACKFIN_TWI)	+= i2c-bfin-twi.o
 obj-$(CONFIG_I2C_CBUS_GPIO)	+= i2c-cbus-gpio.o
 obj-$(CONFIG_I2C_CPM)		+= i2c-cpm.o
diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c
new file mode 100644
index 0000000..22a29de
--- /dev/null
+++ b/drivers/i2c/busses/i2c-bcm2835.c
@@ -0,0 +1,346 @@
+/*
+ * BCM2835 master mode driver
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/completion.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define BCM2835_I2C_C		0x0
+#define BCM2835_I2C_S		0x4
+#define BCM2835_I2C_DLEN	0x8
+#define BCM2835_I2C_A		0xc
+#define BCM2835_I2C_FIFO	0x10
+#define BCM2835_I2C_DIV		0x14
+#define BCM2835_I2C_DEL		0x18
+#define BCM2835_I2C_CLKT	0x1c
+
+#define BCM2835_I2C_C_READ	BIT(0)
+#define BCM2835_I2C_C_CLEAR	BIT(4) /* bits 4 and 5 both clear */
+#define BCM2835_I2C_C_ST	BIT(7)
+#define BCM2835_I2C_C_INTD	BIT(8)
+#define BCM2835_I2C_C_INTT	BIT(9)
+#define BCM2835_I2C_C_INTR	BIT(10)
+#define BCM2835_I2C_C_I2CEN	BIT(15)
+
+#define BCM2835_I2C_S_TA	BIT(0)
+#define BCM2835_I2C_S_DONE	BIT(1)
+#define BCM2835_I2C_S_TXW	BIT(2)
+#define BCM2835_I2C_S_RXR	BIT(3)
+#define BCM2835_I2C_S_TXD	BIT(4)
+#define BCM2835_I2C_S_RXD	BIT(5)
+#define BCM2835_I2C_S_TXE	BIT(6)
+#define BCM2835_I2C_S_RXF	BIT(7)
+#define BCM2835_I2C_S_ERR	BIT(8)
+#define BCM2835_I2C_S_CLKT	BIT(9)
+#define BCM2835_I2C_S_LEN	BIT(10) /* Fake bit for SW error reporting */
+
+#define BCM2835_I2C_TIMEOUT (msecs_to_jiffies(1000))
+
+struct bcm2835_i2c_dev {
+	struct device *dev;
+	void __iomem *regs;
+	struct clk *clk;
+	struct i2c_adapter adapter;
+	struct completion completion;
+	u32 msg_err;
+	u8 *msg_buf;
+	size_t msg_buf_remaining;
+};
+
+static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev,
+				      u32 reg, u32 val)
+{
+	writel(val, i2c_dev->regs + reg);
+}
+
+static inline u32 bcm2835_i2c_readl(struct bcm2835_i2c_dev *i2c_dev, u32 reg)
+{
+	return readl(i2c_dev->regs + reg);
+}
+
+static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev *i2c_dev)
+{
+	u32 val;
+
+	for (;;) {
+		if (!i2c_dev->msg_buf_remaining)
+			return;
+		val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
+		if (!(val & BCM2835_I2C_S_TXD))
+			break;
+		bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_FIFO,
+				   *i2c_dev->msg_buf);
+		i2c_dev->msg_buf++;
+		i2c_dev->msg_buf_remaining--;
+	}
+}
+
+static void bcm2835_drain_rxfifo(struct bcm2835_i2c_dev *i2c_dev)
+{
+	u32 val;
+
+	for (;;) {
+		if (!i2c_dev->msg_buf_remaining)
+			return;
+		val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
+		if (!(val & BCM2835_I2C_S_RXD))
+			break;
+		*i2c_dev->msg_buf = bcm2835_i2c_readl(i2c_dev,
+						      BCM2835_I2C_FIFO);
+		i2c_dev->msg_buf++;
+		i2c_dev->msg_buf_remaining--;
+	}
+}
+
+static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data)
+{
+	struct bcm2835_i2c_dev *i2c_dev = data;
+	u32 val, err;
+
+	val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
+	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, val);
+
+	err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR);
+	if (err) {
+		i2c_dev->msg_err = err;
+		complete(&i2c_dev->completion);
+		return IRQ_HANDLED;
+	}
+
+	if (val & BCM2835_I2C_S_RXD) {
+		bcm2835_drain_rxfifo(i2c_dev);
+		if (!(val & BCM2835_I2C_S_DONE))
+			return IRQ_HANDLED;
+	}
+
+	if (val & BCM2835_I2C_S_DONE) {
+		if (i2c_dev->msg_buf_remaining)
+			i2c_dev->msg_err = BCM2835_I2C_S_LEN;
+		else
+			i2c_dev->msg_err = 0;
+		complete(&i2c_dev->completion);
+		return IRQ_HANDLED;
+	}
+
+	if (val & BCM2835_I2C_S_TXD) {
+		bcm2835_fill_txfifo(i2c_dev);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev,
+				struct i2c_msg *msg)
+{
+	u32 c;
+	int ret;
+
+	i2c_dev->msg_buf = msg->buf;
+	i2c_dev->msg_buf_remaining = msg->len;
+	INIT_COMPLETION(i2c_dev->completion);
+
+	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
+
+	if (msg->flags & I2C_M_RD) {
+		c = BCM2835_I2C_C_READ | BCM2835_I2C_C_INTR;
+	} else {
+		c = BCM2835_I2C_C_INTT;
+		bcm2835_fill_txfifo(i2c_dev);
+	}
+	c |= BCM2835_I2C_C_ST | BCM2835_I2C_C_INTD | BCM2835_I2C_C_I2CEN;
+
+	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr);
+	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len);
+	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c);
+
+	ret = wait_for_completion_timeout(&i2c_dev->completion,
+					  BCM2835_I2C_TIMEOUT);
+	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
+	if (WARN_ON(ret == 0)) {
+		dev_err(i2c_dev->dev, "i2c transfer timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	if (likely(!i2c_dev->msg_err))
+		return 0;
+
+	if ((i2c_dev->msg_err & BCM2835_I2C_S_ERR) &&
+	    (msg->flags & I2C_M_IGNORE_NAK))
+		return 0;
+
+	dev_err(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err);
+
+	if (i2c_dev->msg_err & BCM2835_I2C_S_ERR)
+		return -EREMOTEIO;
+	else
+		return -EIO;
+}
+
+static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+			    int num)
+{
+	struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < num; i++) {
+		ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[i]);
+		if (ret)
+			break;
+	}
+
+	return ret ?: i;
+}
+
+static u32 bcm2835_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm bcm2835_i2c_algo = {
+	.master_xfer	= bcm2835_i2c_xfer,
+	.functionality	= bcm2835_i2c_func,
+};
+
+static int bcm2835_i2c_probe(struct platform_device *pdev)
+{
+	struct bcm2835_i2c_dev *i2c_dev;
+	struct resource *mem, *requested, *irq;
+	u32 bus_clk_rate, divider;
+	int ret;
+	struct i2c_adapter *adap;
+
+	i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+	if (!i2c_dev) {
+		dev_err(&pdev->dev, "Cannot allocate i2c_dev\n");
+		return -ENOMEM;
+	}
+	platform_set_drvdata(pdev, i2c_dev);
+	i2c_dev->dev = &pdev->dev;
+	init_completion(&i2c_dev->completion);
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "No mem resource\n");
+		return -ENODEV;
+	}
+
+	requested = devm_request_mem_region(&pdev->dev, mem->start,
+					    resource_size(mem),
+					    dev_name(&pdev->dev));
+	if (!requested) {
+		dev_err(&pdev->dev, "Could not claim register region\n");
+		return -EBUSY;
+	}
+
+	i2c_dev->regs = devm_ioremap(&pdev->dev, mem->start,
+				     resource_size(mem));
+	if (!i2c_dev->regs) {
+		dev_err(&pdev->dev, "Could not map registers\n");
+		return -ENOMEM;
+	}
+
+	i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(i2c_dev->clk)) {
+		dev_err(&pdev->dev, "Could not get clock\n");
+		return PTR_ERR(i2c_dev->clk);
+	}
+
+	ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
+				   &bus_clk_rate);
+	if (ret < 0) {
+		dev_warn(&pdev->dev,
+			 "Could not read clock-frequency property\n");
+		bus_clk_rate = 100000;
+	}
+
+	divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk), bus_clk_rate);
+	/*
+	 * Per the datasheet, the register is always interpreted as an even
+	 * number, by rounding down. In other words, the LSB is ignored. So,
+	 * if the LSB is set, increment the divider to avoid any issue.
+	 */
+	if (divider & 1)
+		divider++;
+	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider);
+
+	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq) {
+		dev_err(&pdev->dev, "No IRQ resource\n");
+		return -ENODEV;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq->start, bcm2835_i2c_isr,
+			       IRQF_SHARED, dev_name(&pdev->dev), i2c_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not request IRQ\n");
+		return -ENODEV;
+	}
+
+	adap = &i2c_dev->adapter;
+	i2c_set_adapdata(adap, i2c_dev);
+	adap->owner = THIS_MODULE;
+	adap->class = I2C_CLASS_HWMON;
+	strlcpy(adap->name, "bcm2835 I2C adapter", sizeof(adap->name));
+	adap->algo = &bcm2835_i2c_algo;
+	adap->dev.parent = &pdev->dev;
+	adap->nr = -1;
+
+	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);
+
+	ret = i2c_add_numbered_adapter(adap);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not add adapter\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int bcm2835_i2c_remove(struct platform_device *pdev)
+{
+	struct bcm2835_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+	i2c_del_adapter(&i2c_dev->adapter);
+
+	return 0;
+}
+
+static const struct of_device_id bcm2835_i2c_of_match[] = {
+	{ .compatible = "brcm,bcm2835-i2c" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, bcm2835_i2c_of_match);
+
+static struct platform_driver bcm2835_i2c_driver = {
+	.probe		= bcm2835_i2c_probe,
+	.remove		= bcm2835_i2c_remove,
+	.driver		= {
+		.name	= "i2c-bcm2835",
+		.owner	= THIS_MODULE,
+		.of_match_table = bcm2835_i2c_of_match,
+	},
+};
+module_platform_driver(bcm2835_i2c_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>");
+MODULE_DESCRIPTION("BCM2835 I2C bus adapter");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:i2c-bcm2835");
-- 
1.7.10.4

^ permalink raw reply related

* [Bulk] [PATCH] timer: vt8500: Move timer code to drivers/clocksource
From: Tony Prisk @ 2013-01-15  4:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130114200743.GA26482@quad.lixom.net>

On Mon, 2013-01-14 at 12:07 -0800, Olof Johansson wrote:
> On Mon, Jan 14, 2013 at 06:47:35PM +1300, Tony Prisk wrote:
> > On Mon, 2013-01-14 at 18:13 +1300, Tony Prisk wrote:
> > > On Mon, 2013-01-14 at 18:09 +1300, Tony Prisk wrote:
> > > > This patch moves arch-vt8500/timer.c into drivers/clocksource and
> > > > updates the necessary Kconfig/Makefile options.
> > > > 
> > > > Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
> > > > ---
> > > >  arch/arm/mach-vt8500/Kconfig       |    1 +
> > > >  arch/arm/mach-vt8500/Makefile      |    2 +-
> > > >  arch/arm/mach-vt8500/common.h      |    1 -
> > > >  arch/arm/mach-vt8500/timer.c       |  184 ------------------------------------
> > > >  arch/arm/mach-vt8500/vt8500.c      |    1 +
> > > >  drivers/clocksource/Kconfig        |    3 +
> > > >  drivers/clocksource/Makefile       |    1 +
> > > >  drivers/clocksource/vt8500_timer.c |  184 ++++++++++++++++++++++++++++++++++++
> > > >  include/linux/vt8500_timer.h       |   22 +++++
> > > >  9 files changed, 213 insertions(+), 186 deletions(-)
> > > >  delete mode 100644 arch/arm/mach-vt8500/timer.c
> > > >  create mode 100644 drivers/clocksource/vt8500_timer.c
> > > >  create mode 100644 include/linux/vt8500_timer.h
> > > 
> > > Darn.. forgot the -m again. I'll await your feedback regarding the
> > > basing of the patch first (and any other feedback), then I'll redo it
> > > with the correct stats.
> > > 
> > > Regards
> > > Tony P
> > 
> > Oh grr.. forget this completely. It doesn't take into account the
> > patches I already sent for WM8850.
> > 
> > I guess it needs to be based on timer/cleanup + vt8500/wm8x50.
> > 
> > Need a little advise on how to handle this one please :)
> 
> The normal way to handle these kind of dependencies is to base them on merges
> of the needed branches. Based on the later email, you only seem to need
> timer/cleanup, but if you would have needed the other one, then you'd merge
> that on top of timer/cleanup, and then add your patches.
> 
> Of course, ideally you would do the cleanup, then add the wm8x50 features,
> but in reality work doesn't always pan out that way, so you end up with
> cleanups that depend on including new features in the same (sweeping)
> cleanup since they have already been merged. That's when things sometimes
> get hairy, and we need to start a second cleanup branch that's "after"
> the feature branch in the sequence of topics. But it should be rare,
> and in your case it seems like it wasn't needed.
> 
> 
> -Olof
> 

Just to clarify what I did (and to make sure it was as you understood
it):

#1) I wrote the patch on top of timer/cleanup. This is the branch the
patch was written for.

#2) I then pulled timer/cleanup and merged vt8500/wm8x50 on top, then
reapplied the patch from #1 - it applied cleanly.

What I have just realised is that you might?? get a conflict when you
merge vt8500/wm8x50 on top of wherever this patch ends up due to the few
lines at the top of arch-vt8500/Kconfig having changed (the addition of
SELECT VT8500_TIMER). This should be trivial to fix (I assume).

Regards
Tony P

^ permalink raw reply

* [GIT PULL] irqchip init infrastructure and GIC/VIC move
From: Olof Johansson @ 2013-01-15  3:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <50F19F3D.60502@gmail.com>

On Sat, Jan 12, 2013 at 9:37 AM, Rob Herring <robherring2@gmail.com> wrote:
> Arnd, Olof,
>
> Please pull for 3.9. This is the initial infrastructure and conversion
> of the GIC and VIC to use it. Several people are waiting for the irqchip
> infrastructure to go in in order to convert other irqchip code over.
>
> I've left "static asmlinkage" in on the irq handlers. There's been no
> more discussion, so I think we are in agreement. If asmlinkage defines
> the procedure call convention for assembly calls, then it still makes
> sense to have it on a static function called thru a function pointer.
>
> Rob
>
> The following changes since commit 9931faca02c604c22335f5a935a501bb2ace6e20:
>
>   Linux 3.8-rc3 (2013-01-09 18:59:55 -0800)
>
> are available in the git repository at:
>
>   git://sources.calxeda.com/kernel/linux.git tags/gic-vic-to-irqchip

Ugh. This conflicts heavily with the timer cleanup from Stephen, due
to adjacent changes in the machine descriptors.

I fixed them up, but I would appreciate a second glance at them for
sanity checking.


-Olof

^ permalink raw reply

* [PATCH] ARM: EXYNOS: Remove wrong I2S0 clock from peril clock
From: Tushar Behera @ 2013-01-15  3:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <003501cd9d65$9611d060$c2357120$@samsung.com>

On 09/28/2012 04:09 PM, Sangsu Park wrote:
> Remove I2S0 clock from peril since peril doesn't have I2S0 clock for EXYNOS4.
> Audio Subsystem block has I2S0 clk.
> 
> Signed-off-by: sangsu <sangsu@sangsu-P560.(none)>
> ---
>  arch/arm/mach-exynos/clock-exynos4.c |    5 -----
>  1 file changed, 5 deletions(-)
> 
> diff --git a/arch/arm/mach-exynos/clock-exynos4.c
> b/arch/arm/mach-exynos/clock-exynos4.c
> index 6a45c9a..fa8a134 100644
> --- a/arch/arm/mach-exynos/clock-exynos4.c
> +++ b/arch/arm/mach-exynos/clock-exynos4.c
> @@ -613,11 +613,6 @@ static struct clk exynos4_init_clocks_off[] = {
>  		.ctrlbit	= (1 << 18),
>  	}, {
>  		.name		= "iis",
> -		.devname	= "samsung-i2s.0",
> -		.enable		= exynos4_clk_ip_peril_ctrl,
> -		.ctrlbit	= (1 << 19),
> -	}, {
> -		.name		= "iis",
>  		.devname	= "samsung-i2s.1",
>  		.enable		= exynos4_clk_ip_peril_ctrl,
>  		.ctrlbit	= (1 << 20),
> 

After this clock has been removed, I2S0 registration fails on EXYNOS4
based boards (clk_get for iis clock fails). The alternate clock from
Audio subsystem block is missing.

-- 
Tushar Behera

^ permalink raw reply

* [PATCH v2] hardlockup: detect hard lockups without NMIs using secondary cpus
From: Colin Cross @ 2013-01-15  3:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAFTL4hxCNqCAbF6+Ny_-0jfL2G2ie8O6Be_7KtYymvJzyJGyCw@mail.gmail.com>

On Mon, Jan 14, 2013 at 6:48 PM, Frederic Weisbecker <fweisbec@gmail.com> wrote:
> 2013/1/15 Colin Cross <ccross@android.com>:
>> On Mon, Jan 14, 2013 at 4:25 PM, Frederic Weisbecker <fweisbec@gmail.com> wrote:
>>> 2013/1/15 Colin Cross <ccross@android.com>:
>>>> On Mon, Jan 14, 2013 at 4:13 PM, Frederic Weisbecker <fweisbec@gmail.com> wrote:
>>>>> I believe this is pretty much what the RCU stall detector does
>>>>> already: checks for other CPUs being responsive. The only difference
>>>>> is on how it checks that. For RCU it's about checking for CPUs
>>>>> reporting quiescent states when requested to do so. In your case it's
>>>>> about ensuring the hrtimer interrupt is well handled.
>>>>>
>>>>> One thing you can do is to enqueue an RCU callback (cal_rcu()) every
>>>>> minute so you can force other CPUs to report quiescent states
>>>>> periodically and thus check for lockups.
>>>>
>>>> That's a good point, I'll take a look at using that.  A minute is too
>>>> long, some SoCs have maximum HW watchdog periods of under 30 seconds,
>>>> but a call_rcu every 10-20 seconds might be sufficient.
>>>
>>> Sure. And you can tune CONFIG_RCU_CPU_STALL_TIMEOUT accordingly.
>>
>> After considering this, I think the hrtimer watchdog is more useful.
>> RCU stalls are not usually panic events, and I wouldn't want to add a
>> panic on every RCU stall.  The lack of stack traces on the affected
>> cpu makes a panic important.  I'm planning to add an ARM DBGPCSR panic
>> handler, which will be able to dump the PC of a stuck cpu even if it
>> is not responding to interrupts.  kexec or kgdb on panic might also
>> allow some inspection of the stack on stuck cpu.
>>
>> Failing to process interrupts is a much more serious event than an RCU
>> stall, and being able to detect them separately may be very valuable
>> for debugging.
>
> RCU stalls can happen for different reasons: softlockup (failure to
> schedule another task), hardlockup (failure to process interrupts), or
> a bug in RCU itself. But if you have a hardlockup, it will report it.

It will report it, but it will report it in the same way that it
reports a less serious issue, and in this case with zero debugging
information since the affected cpu won't dump its backtrace.  Better
than nothing, but not as useful as a panic can be.

> Now why do you need a panic in any case? I don't know DBGPCSR, is this
> a breakpoint register? How do you plan to use it remotely from the CPU
> that detects the lockup?

Panics can trigger extra debugging tools, like my previous examples
kexec and kgdb.

DBGPCSR is the "DeBuG Program Counter Sampling Register".  It is a
memory mapped register available on many (all?) ARM Cortex cpus that
returns a recent PC value for the cpu.  I have used it along with this
patch, and it produces very useful information.

^ permalink raw reply

* [PATCH] ARM: PXA3xx: program the CSMSADRCFG register
From: Eric Miao @ 2013-01-15  3:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358077787-31518-1-git-send-email-grinberg@compulab.co.il>

On Sun, Jan 13, 2013 at 7:49 PM, Igor Grinberg <grinberg@compulab.co.il> wrote:
> The Chip Select Configuration Register must be programmed to 0x2 in
> order to achieve the correct behavior of the Static Memory Controller.
>
> Without this patch devices wired to DFI and accessed through SMC cannot
> be accessed after resume from S2.
>
> Do not rely on the boot loader to program the CSMSADRCFG register by
> programming it in the kernel smemc module.
>

Looks good to me. Acked-by: Eric Miao <eric.y.miao@gmail.com>

> Signed-off-by: Igor Grinberg <grinberg@compulab.co.il>
> Cc: stable at vger.kernel.org
> ---
>  arch/arm/mach-pxa/include/mach/smemc.h |    1 +
>  arch/arm/mach-pxa/smemc.c              |   15 ++++++++++++++-
>  2 files changed, 15 insertions(+), 1 deletions(-)

^ permalink raw reply

* [PATCH V2 2/6] ARM: tegra20: cpuidle: add powered-down state for secondary CPU
From: Joseph Lo @ 2013-01-15  3:00 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130111122417.GE30538@e102568-lin.cambridge.arm.com>

On Fri, 2013-01-11 at 20:24 +0800, Lorenzo Pieralisi wrote:
> On Fri, Jan 11, 2013 at 07:20:29AM +0000, Joseph Lo wrote:
> > On Wed, 2012-12-05 at 18:50 +0800, Lorenzo Pieralisi wrote:
> > > On Wed, Dec 05, 2012 at 10:01:49AM +0000, Joseph Lo wrote:
> > > > The powered-down state of Tegra20 requires power gating both CPU cores.
> > > > When the secondary CPU requests to enter powered-down state, it saves
> > > > its own contexts and then enters WFI. The Tegra20 had a limition to
> > > > power down both CPU cores. The secondary CPU must waits for CPU0 in
> > > > powered-down state too. If the secondary CPU be woken up before CPU0
> > > > entering powered-down state, then it needs to restore its CPU states
> > > > and waits for next chance.
> > > >
> > > > Be aware of that, you may see the legacy power state "LP2" in the code
> > > > which is exactly the same meaning of "CPU power down".
> > > >
> > > > Based on the work by:
> > > > Colin Cross <ccross@android.com>
> > > > Gary King <gking@nvidia.com>
> > > >
> > > > Signed-off-by: Joseph Lo <josephl@nvidia.com>
> > > > +
> > > > +#ifdef CONFIG_PM_SLEEP
> > > > +/*
> > > > + * tegra_pen_lock
> > > > + *
> > > > + * spinlock implementation with no atomic test-and-set and no coherence
> > > > + * using Peterson's algorithm on strongly-ordered registers
> > > > + * used to synchronize a cpu waking up from wfi with entering lp2 on idle
> > > > + *
> > > > + * SCRATCH37 = r1 = !turn (inverted from Peterson's algorithm)
> > > > + * on cpu 0:
> > > > + * SCRATCH38 = r2 = flag[0]
> > > > + * SCRATCH39 = r3 = flag[1]
> > > > + * on cpu1:
> > > > + * SCRATCH39 = r2 = flag[1]
> > > > + * SCRATCH38 = r3 = flag[0]
> > > > + *
> > > > + * must be called with MMU on
> > > > + * corrupts r0-r3, r12
> > > > + */
> > > > +ENTRY(tegra_pen_lock)
> > > > +       mov32   r3, TEGRA_PMC_VIRT
> > > > +       cpu_id  r0
> > > > +       add     r1, r3, #PMC_SCRATCH37
> > > > +       cmp     r0, #0
> > > > +       addeq   r2, r3, #PMC_SCRATCH38
> > > > +       addeq   r3, r3, #PMC_SCRATCH39
> > > > +       addne   r2, r3, #PMC_SCRATCH39
> > > > +       addne   r3, r3, #PMC_SCRATCH38
> > > > +
> > > > +       mov     r12, #1
> > > > +       str     r12, [r2]               @ flag[cpu] = 1
> > > > +       dsb
> > > > +       str     r12, [r1]               @ !turn = cpu
> > > > +1:     dsb
> > > > +       ldr     r12, [r3]
> > > > +       cmp     r12, #1                 @ flag[!cpu] == 1?
> > > > +       ldreq   r12, [r1]
> > > > +       cmpeq   r12, r0                 @ !turn == cpu?
> > > > +       beq     1b                      @ while !turn == cpu && flag[!cpu] == 1
> > > > +
> > > > +       mov     pc, lr                  @ locked
> > > > +ENDPROC(tegra_pen_lock)
> > > > +
> > > > +ENTRY(tegra_pen_unlock)
> > > > +       dsb
> > > > +       mov32   r3, TEGRA_PMC_VIRT
> > > > +       cpu_id  r0
> > > > +       cmp     r0, #0
> > > > +       addeq   r2, r3, #PMC_SCRATCH38
> > > > +       addne   r2, r3, #PMC_SCRATCH39
> > > > +       mov     r12, #0
> > > > +       str     r12, [r2]
> > > > +       mov     pc, lr
> > > > +ENDPROC(tegra_pen_unlock)
> > > 
> > > There is an ongoing work to make this locking scheme for MMU/coherency off
> > > paths ARM generic, and we do not want to merge yet another platform specific
> > > locking mechanism. I will point you to the patchset when it hits LAK.
> > > 
> > 
> > You did mention there is an ARM generic locking scheme for MMU/coherency
> > off case before. Do you mean the patch below?
> > 
> > https://patchwork.kernel.org/patch/1957911/
> > https://patchwork.kernel.org/patch/1957901/
> 
> Those are used for first-man election when multiple CPUs come out of
> idle at once.
> 
> You should have a look at the entire series and in particular:
> 
> https://patchwork.kernel.org/patch/1957891/
> https://patchwork.kernel.org/patch/1957951/
> 
> > I gave it a review today. Looks it can't fit our usage for CPU idle
> > powered-down mode on Tegra20.
> > 
> > The generic mechanism only can be used when CPUs in non coherent world.
> > But our usage needs the mechanism could be used in both coherent and non
> > coherent case. OK. The case is we need to sync the status about the CPU1
> > was ready to power down. In this case, the CPU0 is still in coherent
> > world but the CPU1 isn't. So we need the locking scheme could be still
> > safe in this situation.
> 
> I know, I implemented something of that sort for an A9 based development
> platform, that's why I pointed you to this new patchset.
> 
> Have a look at the series, it should do what you want.
> 
Hi Lorenzo,

May I upstream this stuff first? I can promise to you I will re-work a
common CPUs and cluster power sync wrappers (platform_power_ops) for all
Tegra series that based on the generic framework. Because we didn't have
this work in Tegra tree yet and we indeed need it for supporting cluster
power down and switching.

But it need lots of time for re-work, testing and verification. And I
have lots of patches that need the function of cpu suspend that be
introduced in this patch series to support platform suspend for Tegra.

So I am asking the permission here for upstream this series first and I
will continue the job to come out a common CPUs and cluster power sync
wrappers for Tegra that we indeed need it to support cluster power down
and switching.

Thanks,
Joseph

^ permalink raw reply


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