aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHema HK <hemahk@ti.com>2011-04-19 11:00:00 -0500
committerDan Murphy <dmurphy@ti.com>2011-04-25 14:45:10 -0500
commit5b6227833cabbb4b31d44c2de81ba37aad1a3407 (patch)
tree39e7d4566cf0559705864534c0f20f0653ab115c
parent5e8b56a9d391027fc4f7899b82f2be689ca7062d (diff)
downloadpandroid-5b6227833cabbb4b31d44c2de81ba37aad1a3407.tar.gz
Adding the twl6030-usb transceiver support for OMAP4 musb driver.
OMAP4 supports 2 types of transceiver interface. 1. UTMI: The PHY is embedded within OMAP4. The transceiver functionality is split between the twl6030 PMIC chip and OMAP4430. The VBUS, ID pin sensing and OTG SRP generation part is integrated in TWL6030 and UTMI PHY functionality is embedded within the OMAP4430. There is no direct interactions between the MUSB controller and TWL6030 chip to communicate the session-valid, session-end and ID-GND events. It has to be done through a software by setting/resetting bits in one of the control module register of OMAP4430 which in turn toggles the appropriate signals to MUSB controller. The internal transceiver has functional clocks and powerdown bits to powerdown the PHY for power saving. Since there is no option available for having 2 transceiver drivers for one USB controller, internal PHY specific APIs are passed through plaform_data function pointers to use in the twl6030-usb transceiver driver. 2. ULPI interface is provided for off-chip transceivers. So that we dont break compile, This patch also includes: Selecting the twl6030-usb for OMAP4430SDP and OMAP4PANDA boards and adding OMAP4 internal phy code for compilation Added the TWL6030-usb transceiver option in the Kconfig Populated twl4030_usb_data platform data structure with the function pointers for OMAP4430 internal PHY operation to be used by twl630-usb driver. Change-Id: Ib3e19aaad97825a48f30d487807cfbf1d7d34429 Signed-off-by: Hema HK <hemahk@ti.com> Cc: Tony Lindgren <tony@atomide.com> Cc: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Felipe Balbi <balbi@ti.com> Signed-off-by: Axel Haslam <axelhaslam@ti.com>
-rw-r--r--arch/arm/mach-omap2/Makefile2
-rw-r--r--arch/arm/mach-omap2/board-4430sdp.c8
-rw-r--r--arch/arm/mach-omap2/board-omap4panda.c7
-rw-r--r--arch/arm/mach-omap2/omap_phy_internal.c242
-rw-r--r--arch/arm/plat-omap/include/plat/usb.h25
-rw-r--r--drivers/mfd/twl-core.c48
-rw-r--r--drivers/usb/otg/Kconfig7
-rw-r--r--drivers/usb/otg/twl6030-usb.c360
-rw-r--r--include/linux/i2c/twl.h7
9 files changed, 482 insertions, 224 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 8188806b8fc..1259df840ba 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -190,8 +190,10 @@ obj-$(CONFIG_MACH_IGEP0020) += board-igep0020.o \
obj-$(CONFIG_MACH_OMAP3_TOUCHBOOK) += board-omap3touchbook.o \
hsmmc.o
obj-$(CONFIG_MACH_OMAP_4430SDP) += board-4430sdp.o \
+ omap_phy_internal.o \
hsmmc.o
obj-$(CONFIG_MACH_OMAP4_PANDA) += board-omap4panda.o \
+ omap_phy_internal.o \
hsmmc.o
ifeq ($(CONFIG_ARCH_OMAP4),y)
diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c
index 58bd669af23..c8289f49baf 100644
--- a/arch/arm/mach-omap2/board-4430sdp.c
+++ b/arch/arm/mach-omap2/board-4430sdp.c
@@ -859,6 +859,13 @@ static int wifi_set_power(struct device *dev, int slot, int power_on, int vdd)
}
#endif
+static struct twl4030_usb_data omap4_usbphy_data = {
+ .phy_init = omap4430_phy_init,
+ .phy_exit = omap4430_phy_exit,
+ .phy_power = omap4430_phy_power,
+ .phy_set_clock = omap4430_phy_set_clk,
+};
+
static struct omap2_hsmmc_info mmc[] = {
{
.mmc = 2,
@@ -1224,6 +1231,7 @@ static struct twl4030_platform_data sdp4430_twldata = {
.vaux1 = &sdp4430_vaux1,
.vaux2 = &sdp4430_vaux2,
.vaux3 = &sdp4430_vaux3,
+ .usb = &omap4_usbphy_data,
.clk32kg = &sdp4430_clk32kg,
.madc = &sdp4430_gpadc_data,
.bci = &sdp4430_bci_data,
diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c
index 7a6964ee2ec..dde55d09df0 100644
--- a/arch/arm/mach-omap2/board-omap4panda.c
+++ b/arch/arm/mach-omap2/board-omap4panda.c
@@ -202,6 +202,12 @@ static struct omap_musb_board_data musb_board_data = {
.power = 100,
};
+static struct twl4030_usb_data omap4_usbphy_data = {
+ .phy_init = omap4430_phy_init,
+ .phy_exit = omap4430_phy_exit,
+ .phy_power = omap4430_phy_power,
+ .phy_set_clock = omap4430_phy_set_clk,
+};
static struct omap2_hsmmc_info mmc[] = {
{
.mmc = 1,
@@ -412,6 +418,7 @@ static struct twl4030_platform_data panda_twldata = {
.vusb = &panda_vusb,
.vaux2 = &panda_vaux2,
.vaux3 = &panda_vaux3,
+ .usb = &omap4_usbphy_data,
.madc = &panda_gpadc_data,
/* children */
diff --git a/arch/arm/mach-omap2/omap_phy_internal.c b/arch/arm/mach-omap2/omap_phy_internal.c
new file mode 100644
index 00000000000..f172ec06c06
--- /dev/null
+++ b/arch/arm/mach-omap2/omap_phy_internal.c
@@ -0,0 +1,242 @@
+/*
+ * This file configures the internal USB PHY in OMAP4430. Used
+ * with TWL6030 transceiver and MUSB on OMAP4430.
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.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.
+ *
+ * Author: Hema HK <hemahk@ti.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/usb.h>
+
+#include <plat/usb.h>
+#include "control.h"
+
+/* OMAP control module register for UTMI PHY */
+#define CONTROL_DEV_CONF 0x300
+#define PHY_PD 0x1
+
+#define USBOTGHS_CONTROL 0x33c
+#define AVALID BIT(0)
+#define BVALID BIT(1)
+#define VBUSVALID BIT(2)
+#define SESSEND BIT(3)
+#define IDDIG BIT(4)
+
+static struct clk *phyclk, *clk48m, *clk32k;
+static void __iomem *ctrl_base;
+
+int omap4430_phy_init(struct device *dev)
+{
+ ctrl_base = ioremap(OMAP443X_SCM_BASE, SZ_1K);
+ if (!ctrl_base) {
+ dev_err(dev, "control module ioremap failed\n");
+ return -ENOMEM;
+ }
+ /* Power down the phy */
+ __raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF);
+ phyclk = clk_get(dev, "ocp2scp_usb_phy_ick");
+
+ if (IS_ERR(phyclk)) {
+ dev_err(dev, "cannot clk_get ocp2scp_usb_phy_ick\n");
+ iounmap(ctrl_base);
+ return PTR_ERR(phyclk);
+ }
+
+ clk48m = clk_get(dev, "ocp2scp_usb_phy_phy_48m");
+ if (IS_ERR(clk48m)) {
+ dev_err(dev, "cannot clk_get ocp2scp_usb_phy_phy_48m\n");
+ clk_put(phyclk);
+ iounmap(ctrl_base);
+ return PTR_ERR(clk48m);
+ }
+
+ clk32k = clk_get(dev, "usb_phy_cm_clk32k");
+ if (IS_ERR(clk32k)) {
+ dev_err(dev, "cannot clk_get usb_phy_cm_clk32k\n");
+ clk_put(phyclk);
+ clk_put(clk48m);
+ iounmap(ctrl_base);
+ return PTR_ERR(clk32k);
+ }
+ return 0;
+}
+
+int omap4430_phy_set_clk(struct device *dev, int on)
+{
+ static int state;
+
+ if (on && !state) {
+ /* Enable the phy clocks */
+ clk_enable(phyclk);
+ clk_enable(clk48m);
+ clk_enable(clk32k);
+ state = 1;
+ } else if (state) {
+ /* Disable the phy clocks */
+ clk_disable(phyclk);
+ clk_disable(clk48m);
+ clk_disable(clk32k);
+ state = 0;
+ }
+ return 0;
+}
+
+int omap4430_phy_power(struct device *dev, int ID, int on)
+{
+ if (on) {
+ /* enabled the clocks */
+ omap4430_phy_set_clk(dev, 1);
+ /* power on the phy */
+ if (__raw_readl(ctrl_base + CONTROL_DEV_CONF) & PHY_PD) {
+ __raw_writel(~PHY_PD, ctrl_base + CONTROL_DEV_CONF);
+ mdelay(200);
+ }
+ if (ID)
+ /* enable VBUS valid, IDDIG groung */
+ __raw_writel(AVALID | VBUSVALID, ctrl_base +
+ USBOTGHS_CONTROL);
+ else
+ /*
+ * Enable VBUS Valid, AValid and IDDIG
+ * high impedence
+ */
+ __raw_writel(IDDIG | AVALID | VBUSVALID,
+ ctrl_base + USBOTGHS_CONTROL);
+ } else {
+ /* Enable session END and IDIG to high impedence. */
+ __raw_writel(SESSEND | IDDIG, ctrl_base +
+ USBOTGHS_CONTROL);
+ /* Disable the clocks */
+ omap4430_phy_set_clk(dev, 0);
+ /* Power down the phy */
+ __raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF);
+ }
+
+ return 0;
+}
+
+int omap4430_phy_exit(struct device *dev)
+{
+ if (ctrl_base)
+ iounmap(ctrl_base);
+ if (phyclk)
+ clk_put(phyclk);
+ if (clk48m)
+ clk_put(clk48m);
+ if (clk32k)
+ clk_put(clk32k);
+
+ return 0;
+}
+
+void am35x_musb_reset(void)
+{
+ u32 regval;
+
+ /* Reset the musb interface */
+ regval = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET);
+
+ regval |= AM35XX_USBOTGSS_SW_RST;
+ omap_ctrl_writel(regval, AM35XX_CONTROL_IP_SW_RESET);
+
+ regval &= ~AM35XX_USBOTGSS_SW_RST;
+ omap_ctrl_writel(regval, AM35XX_CONTROL_IP_SW_RESET);
+
+ regval = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET);
+}
+
+void am35x_musb_phy_power(u8 on)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(100);
+ u32 devconf2;
+
+ if (on) {
+ /*
+ * Start the on-chip PHY and its PLL.
+ */
+ devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
+
+ devconf2 &= ~(CONF2_RESET | CONF2_PHYPWRDN | CONF2_OTGPWRDN);
+ devconf2 |= CONF2_PHY_PLLON;
+
+ omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
+
+ pr_info(KERN_INFO "Waiting for PHY clock good...\n");
+ while (!(omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2)
+ & CONF2_PHYCLKGD)) {
+ cpu_relax();
+
+ if (time_after(jiffies, timeout)) {
+ pr_err(KERN_ERR "musb PHY clock good timed out\n");
+ break;
+ }
+ }
+ } else {
+ /*
+ * Power down the on-chip PHY.
+ */
+ devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
+
+ devconf2 &= ~CONF2_PHY_PLLON;
+ devconf2 |= CONF2_PHYPWRDN | CONF2_OTGPWRDN;
+ omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
+ }
+}
+
+void am35x_musb_clear_irq(void)
+{
+ u32 regval;
+
+ regval = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR);
+ regval |= AM35XX_USBOTGSS_INT_CLR;
+ omap_ctrl_writel(regval, AM35XX_CONTROL_LVL_INTR_CLEAR);
+ regval = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR);
+}
+
+void am35x_musb_set_mode(u8 musb_mode)
+{
+ u32 devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
+
+ devconf2 &= ~CONF2_OTGMODE;
+ switch (musb_mode) {
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ case MUSB_HOST: /* Force VBUS valid, ID = 0 */
+ devconf2 |= CONF2_FORCE_HOST;
+ break;
+#endif
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ case MUSB_PERIPHERAL: /* Force VBUS valid, ID = 1 */
+ devconf2 |= CONF2_FORCE_DEVICE;
+ break;
+#endif
+#ifdef CONFIG_USB_MUSB_OTG
+ case MUSB_OTG: /* Don't override the VBUS/ID comparators */
+ devconf2 |= CONF2_NO_OVERRIDE;
+ break;
+#endif
+ default:
+ pr_info(KERN_INFO "Unsupported mode %u\n", musb_mode);
+ }
+
+ omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
+}
diff --git a/arch/arm/plat-omap/include/plat/usb.h b/arch/arm/plat-omap/include/plat/usb.h
index 49e31bd4cca..5b215b14003 100644
--- a/arch/arm/plat-omap/include/plat/usb.h
+++ b/arch/arm/plat-omap/include/plat/usb.h
@@ -114,6 +114,10 @@ extern void usb_musb_init(struct omap_musb_board_data *board_data);
extern void usb_uhhtll_init(const struct usbhs_omap_platform_data *pdata);
+extern int omap4430_phy_power(struct device *dev, int ID, int on);
+extern int omap4430_phy_set_clk(struct device *dev, int on);
+extern int omap4430_phy_init(struct device *dev);
+extern int omap4430_phy_exit(struct device *dev);
extern void usbhs_wakeup(void);
/* For saving and restoring the musb context during off/wakeup*/
@@ -230,5 +234,26 @@ void omap_usb_init(struct omap_usb_config *pdata);
# define USBT2TLL5PI (1 << 17)
# define USB0PUENACTLOI (1 << 16)
# define USBSTANDBYCTRL (1 << 15)
+/* AM35x */
+/* USB 2.0 PHY Control */
+#define CONF2_PHY_GPIOMODE (1 << 23)
+#define CONF2_OTGMODE (3 << 14)
+#define CONF2_NO_OVERRIDE (0 << 14)
+#define CONF2_FORCE_HOST (1 << 14)
+#define CONF2_FORCE_DEVICE (2 << 14)
+#define CONF2_FORCE_HOST_VBUS_LOW (3 << 14)
+#define CONF2_SESENDEN (1 << 13)
+#define CONF2_VBDTCTEN (1 << 12)
+#define CONF2_REFFREQ_24MHZ (2 << 8)
+#define CONF2_REFFREQ_26MHZ (7 << 8)
+#define CONF2_REFFREQ_13MHZ (6 << 8)
+#define CONF2_REFFREQ (0xf << 8)
+#define CONF2_PHYCLKGD (1 << 7)
+#define CONF2_VBUSSENSE (1 << 6)
+#define CONF2_PHY_PLLON (1 << 5)
+#define CONF2_RESET (1 << 4)
+#define CONF2_PHYPWRDN (1 << 3)
+#define CONF2_OTGPWRDN (1 << 2)
+#define CONF2_DATPOL (1 << 1)
#endif /* __ASM_ARCH_OMAP_USB_H */
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index fe72c79e90c..c2fecd2ecdd 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -726,16 +726,42 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
usb3v1.dev = child;
}
}
- if (twl_has_usb() && twl_class_is_6030()) {
+ if (twl_has_usb() && pdata->usb && twl_class_is_6030()) {
+
+ static struct regulator_consumer_supply usb3v3 = {
+ .supply = "vusb",
+ };
+
+ if (twl_has_regulator()) {
+ /* this is a template that gets copied */
+ struct regulator_init_data usb_fixed = {
+ .constraints.valid_modes_mask =
+ REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .constraints.valid_ops_mask =
+ REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ };
+
+ child = add_regulator_linked(TWL6030_REG_VUSB,
+ &usb_fixed, &usb3v3, 1);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
child = add_child(0, "twl6030_usb",
- pdata->usb, sizeof(*pdata->usb),
- true,
- /* irq0 = USB_PRES, irq1 = USB */
- pdata->irq_base + USBOTG_INTR_OFFSET,
- pdata->irq_base + USB_PRES_INTR_OFFSET);
-
- if (IS_ERR(child))
- return PTR_ERR(child);
+ pdata->usb, sizeof(*pdata->usb),
+ true,
+ /* irq1 = VBUS_PRES, irq0 = USB ID */
+ pdata->irq_base + USBOTG_INTR_OFFSET,
+ pdata->irq_base + USB_PRES_INTR_OFFSET);
+
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ /* we need to connect regulators to this transceiver */
+ if (twl_has_regulator() && child)
+ usb3v3.dev = child;
+
}
if (twl_has_watchdog()) {
@@ -874,10 +900,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL6030_REG_VUSB, pdata->vusb);
- if (IS_ERR(child))
- return PTR_ERR(child);
-
child = add_regulator(TWL6030_REG_VAUX1_6030, pdata->vaux1);
if (IS_ERR(child))
return PTR_ERR(child);
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index 3f9f1eba48e..1faec0f8695 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -65,8 +65,11 @@ config TWL6030_USB
select USB_OTG_UTILS
help
Enable this to support the USB OTG transceiver on TWL6030
- family chips. this tranciever has the VBUS and ID GND and OTG
- event capability. This tranciever does not have the USB PHY like TWL4030.
+ family chips. This TWL6030 transceiver has the VBUS and ID GND
+ and OTG SRP events capabilities. For all other transceiver functionality
+ UTMI PHY is embedded in OMAP4430. The internal PHY configurations APIs
+ are hooked to this driver through platform_data structure.
+ The definition of internal PHY APIs are in the mach-omap2 layer.
config NOP_USB_XCEIV
tristate "NOP USB Transceiver Driver"
diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c
index 8a8a59273f1..83646b7f0f0 100644
--- a/drivers/usb/otg/twl6030-usb.c
+++ b/drivers/usb/otg/twl6030-usb.c
@@ -1,15 +1,14 @@
/*
- * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG controller
- *
- * Copyright (C) 2004-2007 Texas Instruments
- * Copyright (C) 2008 Nokia Corporation
- * Contact: Felipe Balbi <felipe.balbi@nokia.com>
+ * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver.
*
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.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.
*
+ * Author: Hema HK <hemahk@ti.com>
+ *
* 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
@@ -19,28 +18,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * Current status:
- * - HS USB ULPI mode works.
- * - 3-pin mode support may be added in future.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
#include <linux/io.h>
-#include <linux/delay.h>
#include <linux/usb/otg.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/notifier.h>
#include <linux/slab.h>
-#include <linux/clk.h>
-/* usb register definitions*/
+/* usb register definitions */
#define USB_VENDOR_ID_LSB 0x00
#define USB_VENDOR_ID_MSB 0x01
#define USB_PRODUCT_ID_LSB 0x02
@@ -57,13 +49,9 @@
#define USB_VBUS_INT_EN_HI_SET 0x0D
#define USB_VBUS_INT_EN_HI_CLR 0x0E
#define USB_ID_INT_SRC 0x0F
-#define ID_GND (1 << 0)
-#define ID_FLOAT (1 << 4)
-
#define USB_ID_INT_LATCH_SET 0x10
#define USB_ID_INT_LATCH_CLR 0x11
-
#define USB_ID_INT_EN_LO_SET 0x12
#define USB_ID_INT_EN_LO_CLR 0x13
#define USB_ID_INT_EN_HI_SET 0x14
@@ -74,73 +62,47 @@
#define USB_OTG_ADP_RISE 0x19
#define USB_OTG_REVISION 0x1A
-/* to be moved to LDO*/
-#define MISC2 0xE5
-#define CFG_LDO_PD2 0xF5
-#define USB_BACKUP_REG 0xFA
+/* to be moved to LDO */
+#define TWL6030_MISC2 0xE5
+#define TWL6030_CFG_LDO_PD2 0xF5
+#define TWL6030_BACKUP_REG 0xFA
#define STS_HW_CONDITIONS 0x21
-#define STS_USB_ID (1 << 2)
-
/* In module TWL6030_MODULE_PM_MASTER */
-#define PROTECT_KEY 0x0E
#define STS_HW_CONDITIONS 0x21
-
-/* In module TWL6030_MODULE_PM_RECEIVER */
-#define VUSB_DEDICATED1 0x7D
-#define VUSB_DEDICATED2 0x7E
-#define VUSB1V5_DEV_GRP 0x71
-#define VUSB1V5_TYPE 0x72
-#define VUSB1V5_REMAP 0x73
-#define VUSB1V8_DEV_GRP 0x74
-#define VUSB1V8_TYPE 0x75
-#define VUSB1V8_REMAP 0x76
-#define VUSB3V1_DEV_GRP 0x77
-#define VUSB3V1_TYPE 0x78
-#define VUSB3V1_REMAP 0x79
+#define STS_USB_ID BIT(2)
/* In module TWL6030_MODULE_PM_RECEIVER */
#define VUSB_CFG_TRANS 0x71
#define VUSB_CFG_STATE 0x72
#define VUSB_CFG_VOLTAGE 0x73
-/* in module TWL6030_MODULE_MAIN_CHARGE*/
+/* in module TWL6030_MODULE_MAIN_CHARGE */
#define CHARGERUSB_CTRL1 0x8
#define CONTROLLER_STAT1 0x03
-#define VBUS_DET (1 << 2)
-
-
-/* OMAP control module register for UTMI PHY*/
-#define CONTROL_DEV_CONF 0x300
-#define PHY_PD 0x1
+#define VBUS_DET BIT(2)
struct twl6030_usb {
struct otg_transceiver otg;
struct device *dev;
- /* TWL6030 internal USB regulator supplies */
- struct regulator *usb1v5;
- struct regulator *usb1v8;
- struct regulator *usb3v1;
-
/* for vbus reporting with irqs disabled */
spinlock_t lock;
- int irq;
+ struct regulator *usb3v3;
+
+ int irq1;
+ int irq2;
u8 linkstat;
u8 asleep;
bool irq_enabled;
- int prev_vbus;
};
-/* internal define on top of container_of */
#define xceiv_to_twl(x) container_of((x), struct twl6030_usb, otg);
-static void __iomem *ctrl_base;
-
/*-------------------------------------------------------------------------*/
static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module,
@@ -150,46 +112,95 @@ static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module,
ret = twl_i2c_write_u8(module, data, address);
if (ret < 0)
- dev_dbg(twl->dev,
- "TWL6030:USB:Write[0x%x] Error %d\n", address, ret);
+ dev_err(twl->dev,
+ "Write[0x%x] Error %d\n", address, ret);
return ret;
}
-static inline int twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address)
+static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address)
{
- u8 data = 0;
- int ret = 0;
+ u8 data, ret = 0;
ret = twl_i2c_read_u8(module, &data, address);
if (ret >= 0)
ret = data;
else
- dev_dbg(twl->dev,
- "TWL6030:readb[0x%x,0x%x] Error %d\n",
+ dev_err(twl->dev,
+ "readb[0x%x,0x%x] Error %d\n",
module, address, ret);
-
return ret;
}
/*-------------------------------------------------------------------------*/
+#if 0
+static int twl6030_set_phy_clk(struct otg_transceiver *x, int on)
+{
+ struct twl6030_usb *twl;
+ struct device *dev;
+ struct twl4030_usb_data *pdata;
+
+ twl = xceiv_to_twl(x);
+ dev = twl->dev;
+ pdata = dev->platform_data;
+
+ pdata->phy_set_clock(twl->dev, on);
+
+ return 0;
+}
+#endif
+static int twl6030_phy_init(struct otg_transceiver *x)
+{
+ u8 hw_state;
+ struct twl6030_usb *twl;
+ struct device *dev;
+ struct twl4030_usb_data *pdata;
+
+ twl = xceiv_to_twl(x);
+ dev = twl->dev;
+ pdata = dev->platform_data;
+
+ regulator_enable(twl->usb3v3);
+
+ hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
+
+ if (hw_state & STS_USB_ID)
+ pdata->phy_power(twl->dev, 1, 1);
+ else
+ pdata->phy_power(twl->dev, 0, 1);
+
+ return 0;
+}
+
+static void twl6030_phy_shutdown(struct otg_transceiver *x)
+{
+ struct twl6030_usb *twl;
+ struct device *dev;
+ struct twl4030_usb_data *pdata;
+
+ twl = xceiv_to_twl(x);
+ dev = twl->dev;
+ pdata = dev->platform_data;
+ pdata->phy_power(twl->dev, 0, 0);
+ regulator_disable(twl->usb3v3);
+}
static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
{
- /* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP*/
- twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, USB_BACKUP_REG);
+ /* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */
+ twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_BACKUP_REG);
/* Program CFG_LDO_PD2 register and set VUSB bit */
- twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, CFG_LDO_PD2);
+ twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_CFG_LDO_PD2);
/* Program MISC2 register and set bit VUSB_IN_VBAT */
- twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, MISC2);
- /*
- * Program the VUSB_CFG_VOLTAGE register to set the VUSB
- * voltage to 3.3V
- */
- twl6030_writeb(twl, TWL_MODULE_PM_RECEIVER, 0x18,
- VUSB_CFG_VOLTAGE);
+ twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, TWL6030_MISC2);
+
+ twl->usb3v3 = regulator_get(twl->dev, "vusb");
+ if (IS_ERR(twl->usb3v3))
+ return -ENODEV;
+
+ regulator_enable(twl->usb3v3);
/* Program the VUSB_CFG_TRANS for ACTIVE state. */
twl6030_writeb(twl, TWL_MODULE_PM_RECEIVER, 0x3F,
@@ -206,22 +217,33 @@ static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
* Program the USB_ID_CTRL_SET register to enable GND drive
* and the ID comparators
*/
- twl6030_writeb(twl, TWL_MODULE_USB, 0x4, USB_ID_CTRL_SET);
+ twl6030_writeb(twl, TWL_MODULE_USB, 0x14, USB_ID_CTRL_SET);
return 0;
-
}
static ssize_t twl6030_usb_vbus_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
struct twl6030_usb *twl = dev_get_drvdata(dev);
unsigned long flags;
int ret = -EINVAL;
spin_lock_irqsave(&twl->lock, flags);
- ret = sprintf(buf, "%s\n",
- (twl->linkstat == USB_EVENT_VBUS) ? "on" : "off");
+
+ switch (twl->linkstat) {
+ case USB_EVENT_VBUS:
+ ret = snprintf(buf, PAGE_SIZE, "vbus\n");
+ break;
+ case USB_EVENT_ID:
+ ret = snprintf(buf, PAGE_SIZE, "id\n");
+ break;
+ case USB_EVENT_NONE:
+ ret = snprintf(buf, PAGE_SIZE, "none\n");
+ break;
+ default:
+ ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n");
+ }
spin_unlock_irqrestore(&twl->lock, flags);
return ret;
@@ -231,35 +253,28 @@ static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL);
static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
{
struct twl6030_usb *twl = _twl;
- int status = USB_EVENT_NONE;
- int vbus_state, hw_state;
+ int status;
+ u8 vbus_state, hw_state;
hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
- vbus_state = twl6030_readb(twl, TWL6030_MODULE_CHARGER,
+ vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE,
CONTROLLER_STAT1);
- /* AC unplugg can also generate this IRQ
- * we only call the notifier in case of VBUS change
- */
- if (twl->prev_vbus != (vbus_state & VBUS_DET)) {
- if (!(hw_state & STS_USB_ID)) {
- if (vbus_state & VBUS_DET) {
- status = USB_EVENT_VBUS;
- twl->otg.default_a = false;
- twl->otg.state = OTG_STATE_B_IDLE;
- }
- else
- status = USB_EVENT_NONE;
-
- if (status >= 0) {
- twl->linkstat = status;
- blocking_notifier_call_chain(&twl->otg.notifier,
+ if (!(hw_state & STS_USB_ID)) {
+ if (vbus_state & VBUS_DET) {
+ status = USB_EVENT_VBUS;
+ twl->otg.default_a = false;
+ twl->otg.state = OTG_STATE_B_IDLE;
+ } else {
+ status = USB_EVENT_NONE;
+ }
+ if (status >= 0) {
+ twl->linkstat = status;
+ blocking_notifier_call_chain(&twl->otg.notifier,
status, twl->otg.gadget);
- }
}
- sysfs_notify(&twl->dev->kobj, NULL, "vbus");
}
- twl->prev_vbus = vbus_state & VBUS_DET;
+ sysfs_notify(&twl->dev->kobj, NULL, "vbus");
return IRQ_HANDLED;
}
@@ -278,17 +293,17 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, 0x1);
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET,
- 0x10);
+ 0x10);
status = USB_EVENT_ID;
twl->otg.default_a = true;
twl->otg.state = OTG_STATE_A_IDLE;
blocking_notifier_call_chain(&twl->otg.notifier, status,
twl->otg.gadget);
- } else {
+ } else {
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR,
- 0x10);
+ 0x10);
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET,
- 0x1);
+ 0x1);
}
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_LATCH_CLR, status);
twl->linkstat = status;
@@ -297,11 +312,6 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
return IRQ_HANDLED;
}
-static int twl6030_set_suspend(struct otg_transceiver *x, int suspend)
-{
- return 0;
-}
-
static int twl6030_set_peripheral(struct otg_transceiver *x,
struct usb_gadget *gadget)
{
@@ -330,8 +340,8 @@ static int twl6030_enable_irq(struct otg_transceiver *x)
REG_INT_MSK_LINE_C);
twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
REG_INT_MSK_STS_C);
- twl6030_usb_irq(twl->irq, twl);
- twl6030_usbotg_irq(twl->irq, twl);
+ twl6030_usb_irq(twl->irq2, twl);
+ twl6030_usbotg_irq(twl->irq1, twl);
return 0;
}
@@ -344,15 +354,12 @@ static int twl6030_set_vbus(struct otg_transceiver *x, bool enabled)
* Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1
* register. This enables boost mode.
*/
- if (enabled) {
-
+ if (enabled)
twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x40,
CHARGERUSB_CTRL1);
- } else {
+ else
twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x00,
CHARGERUSB_CTRL1);
- }
-
return 0;
}
@@ -370,91 +377,28 @@ static int twl6030_set_host(struct otg_transceiver *x, struct usb_bus *host)
return 0;
}
-static int set_phy_clk(struct otg_transceiver *x, int on)
-{
- static int state;
- struct clk *phyclk;
- struct clk *clk48m;
- struct clk *clk32k;
-
- phyclk = clk_get(NULL, "ocp2scp_usb_phy_ick");
- if (IS_ERR(phyclk)) {
- pr_warning("cannot clk_get ocp2scp_usb_phy_ick\n");
- return PTR_ERR(phyclk);
- }
-
- clk48m = clk_get(NULL, "ocp2scp_usb_phy_phy_48m");
- if (IS_ERR(clk48m)) {
- pr_warning("cannot clk_get ocp2scp_usb_phy_phy_48m\n");
- clk_put(phyclk);
- return PTR_ERR(clk48m);
- }
-
- clk32k = clk_get(NULL, "usb_phy_cm_clk32k");
- if (IS_ERR(clk32k)) {
- pr_warning("cannot clk_get usb_phy_cm_clk32k\n");
- clk_put(phyclk);
- clk_put(clk48m);
- return PTR_ERR(clk32k);
- }
-
- if (on) {
- if (!state) {
- /* Enable the phy clocks*/
- clk_enable(phyclk);
- clk_enable(clk48m);
- clk_enable(clk32k);
- state = 1;
- }
- } else if (state) {
- /* Disable the phy clocks*/
- clk_disable(phyclk);
- clk_disable(clk48m);
- clk_disable(clk32k);
- state = 0;
- }
- return 0;
-}
-
-static int phy_init(struct otg_transceiver *x)
-{
- set_phy_clk(x, 1);
-
- if (__raw_readl(ctrl_base + CONTROL_DEV_CONF) & PHY_PD) {
- __raw_writel(~PHY_PD, ctrl_base + CONTROL_DEV_CONF);
- msleep(200);
- }
- return 0;
-}
-
-static void phy_shutdown(struct otg_transceiver *x)
-{
- set_phy_clk(x, 0);
- __raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF);
-}
-
static int __devinit twl6030_usb_probe(struct platform_device *pdev)
{
struct twl6030_usb *twl;
int status, err;
+ struct twl4030_usb_data *pdata;
+ struct device *dev = &pdev->dev;
+ pdata = dev->platform_data;
+
twl = kzalloc(sizeof *twl, GFP_KERNEL);
if (!twl)
return -ENOMEM;
twl->dev = &pdev->dev;
- twl->irq = platform_get_irq(pdev, 0);
+ twl->irq1 = platform_get_irq(pdev, 0);
+ twl->irq2 = platform_get_irq(pdev, 1);
twl->otg.dev = twl->dev;
twl->otg.label = "twl6030";
twl->otg.set_host = twl6030_set_host;
twl->otg.set_peripheral = twl6030_set_peripheral;
- twl->otg.set_suspend = twl6030_set_suspend;
- twl->asleep = 1;
twl->otg.set_vbus = twl6030_set_vbus;
- twl->otg.init = phy_init;
- twl->otg.enable_irq = twl6030_enable_irq;
- twl->otg.set_clk = set_phy_clk;
- twl->otg.shutdown = phy_shutdown;
- twl->prev_vbus = 0;
+ twl->otg.init = twl6030_phy_init;
+ twl->otg.shutdown = twl6030_phy_shutdown;
/* init spinlock for workqueue */
spin_lock_init(&twl->lock);
@@ -473,41 +417,34 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
BLOCKING_INIT_NOTIFIER_HEAD(&twl->otg.notifier);
- /* Our job is to use irqs and status from the power module
- * to keep the transceiver disabled when nothing's connected.
- *
- * FIXME we actually shouldn't start enabling it until the
- * USB controller drivers have said they're ready, by calling
- * set_host() and/or set_peripheral() ... OTG_capable boards
- * need both handles, otherwise just one suffices.
- */
twl->irq_enabled = true;
- status = request_threaded_irq(twl->irq, NULL, twl6030_usbotg_irq,
+ status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"twl6030_usb", twl);
if (status < 0) {
- dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n",
- twl->irq, status);
+ dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
+ twl->irq1, status);
+ device_remove_file(twl->dev, &dev_attr_vbus);
kfree(twl);
return status;
}
- twl->irq = platform_get_irq(pdev, 1);
- status = request_threaded_irq(twl->irq, NULL, twl6030_usb_irq,
+ status = request_threaded_irq(twl->irq2, NULL, twl6030_usb_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"twl6030_usb", twl);
if (status < 0) {
- dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n",
- twl->irq, status);
+ dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
+ twl->irq2, status);
+ free_irq(twl->irq1, twl);
+ device_remove_file(twl->dev, &dev_attr_vbus);
kfree(twl);
return status;
}
- ctrl_base = ioremap(0x4A002000, SZ_1K);
- /* power down the phy by default can be enabled on connect */
- __raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF);
-
+ pdata->phy_init(dev);
+ twl6030_enable_irq(&twl->otg);
dev_info(&pdev->dev, "Initialized TWL6030 USB module\n");
+
return 0;
}
@@ -515,15 +452,20 @@ static int __exit twl6030_usb_remove(struct platform_device *pdev)
{
struct twl6030_usb *twl = platform_get_drvdata(pdev);
- free_irq(twl->irq, twl);
- device_remove_file(twl->dev, &dev_attr_vbus);
+ struct twl4030_usb_data *pdata;
+ struct device *dev = &pdev->dev;
+ pdata = dev->platform_data;
twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
REG_INT_MSK_LINE_C);
twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
REG_INT_MSK_STS_C);
+ free_irq(twl->irq1, twl);
+ free_irq(twl->irq2, twl);
+ regulator_put(twl->usb3v3);
+ pdata->phy_exit(twl->dev);
+ device_remove_file(twl->dev, &dev_attr_vbus);
kfree(twl);
- iounmap(ctrl_base);
return 0;
}
@@ -550,6 +492,6 @@ static void __exit twl6030_usb_exit(void)
module_exit(twl6030_usb_exit);
MODULE_ALIAS("platform:twl6030_usb");
-MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation");
+MODULE_AUTHOR("Hema HK <hemahk@ti.com>");
MODULE_DESCRIPTION("TWL6030 USB transceiver driver");
MODULE_LICENSE("GPL");
diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h
index 06ca72c90f9..ff7d8dc055a 100644
--- a/include/linux/i2c/twl.h
+++ b/include/linux/i2c/twl.h
@@ -574,6 +574,13 @@ enum twl4030_usb_mode {
struct twl4030_usb_data {
enum twl4030_usb_mode usb_mode;
+
+ int (*phy_init)(struct device *dev);
+ int (*phy_exit)(struct device *dev);
+ /* Power on/off the PHY */
+ int (*phy_power)(struct device *dev, int iD, int on);
+ /* enable/disable phy clocks */
+ int (*phy_set_clock)(struct device *dev, int on);
};
struct twl4030_ins {