aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorHans Petter SELASKY <hans.petter.selasky@stericsson.com>2010-04-20 14:38:55 +0530
committerJohn Rigby <john.rigby@linaro.org>2010-09-02 22:45:05 -0600
commitffda6399bb264b0933be20ac57686887adfd1d45 (patch)
tree2fcb9c06aa3140007f92c6b172ab05b4010c0900 /drivers/usb
parent9fa98f5fb5139e249c097f957a8d9f1132ea1487 (diff)
USB gadget driver and support for USB charging
Signed-off-by: Arun Murthy <arun.murthy@stericsson.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/musb/musb_core.c25
-rw-r--r--drivers/usb/musb/stm_musb.c198
-rw-r--r--drivers/usb/musb/stm_musb.h23
3 files changed, 239 insertions, 7 deletions
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 3512d669b36..623ac2076ed 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -115,6 +115,8 @@
#include "davinci.h"
#endif
+#include "stm_musb.h"
+
#define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON)
@@ -386,6 +388,29 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
DBG(3, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl,
int_usb);
+ /*
+ * XXX The following code has been inserted here as a temporary hack
+ * to get some USB events directly from the USB hardware. This code
+ * and callbacks should eventually be integrated into the generic
+ * USB gadget stack.
+ */
+ if (!(devctl & MUSB_DEVCTL_HM)) {
+ if (int_usb & MUSB_INTR_RESET) {
+ if (power & MUSB_POWER_HSMODE)
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_RESET_HS);
+ else
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_RESET_FS);
+ }
+ if (int_usb & MUSB_INTR_RESUME)
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_RESUME);
+ else if (int_usb & MUSB_INTR_SUSPEND)
+ ab8500_bm_usb_state_changed_wrapper(
+ AB8500_BM_USB_STATE_SUSPEND);
+ }
+
/* in host mode, the peripheral may issue remote wakeup.
* in peripheral mode, the host may resume the link.
* spurious RESUME irqs happen too, paired with SUSPEND.
diff --git a/drivers/usb/musb/stm_musb.c b/drivers/usb/musb/stm_musb.c
index a7aad8f6375..7977b17c63e 100644
--- a/drivers/usb/musb/stm_musb.c
+++ b/drivers/usb/musb/stm_musb.c
@@ -25,8 +25,111 @@
#include "stm_musb.h"
#include <mach/musb_db8500.h>
-/* Sys interfaces*/
-struct musb *musb_status;
+static u8 ulpi_read_register(struct musb *musb, u8 address);
+static u8 ulpi_write_register(struct musb *musb, u8 address, u8 data);
+/* callback argument for AB8500 callback functions */
+static struct musb *musb_status;
+static spinlock_t musb_ulpi_spinlock;
+static unsigned musb_power;
+
+#if 0
+static void
+ab8500_bm_ulpi_test(void)
+{
+ u8 x;
+
+ x = ulpi_read_register(musb_status, ULPI_VIDLO);
+ if (x != 0x83)
+ printk(KERN_ALERT "Unexpected USB PHY VID-LO: 0x%02x\n", x);
+
+ x = ulpi_read_register(musb_status, ULPI_VIDHI);
+ if (x != 0x04)
+ printk(KERN_ALERT "Unexpected USB PHY VID-HI: 0x%02x\n", x);
+
+ x = ulpi_read_register(musb_status, ULPI_PIDLO);
+ if (x != 0x00)
+ printk(KERN_ALERT "Unexpected USB PHY PID-LO: 0x%02x\n", x);
+
+ x = ulpi_read_register(musb_status, ULPI_PIDHI);
+ if (x != 0x45)
+ printk(KERN_ALERT "Unexpected USB PHY PID-HI: 0x%02x\n", x);
+
+ ulpi_write_register(musb_status, ULPI_SCRATCH, 0xAA);
+
+ x = ulpi_read_register(musb_status, ULPI_SCRATCH);
+ if (x != 0xAA)
+ printk(KERN_ALERT "Unexpected ULPI "
+ "read-back value: 0x%02x\n", x);
+
+ ulpi_write_register(musb_status, ULPI_SCRATCH, 0x55);
+
+ x = ulpi_read_register(musb_status, ULPI_SCRATCH);
+ if (x != 0x55)
+ printk(KERN_ALERT "Unexpected ULPI "
+ "read-back value: 0x%02x\n", x);
+ else
+ printk(KERN_INFO "USB: ULPI read-back value OK\n");
+}
+#endif
+
+void
+ab8500_bm_usb_state_changed_wrapper(u8 bm_usb_state)
+{
+ if ((bm_usb_state == AB8500_BM_USB_STATE_RESET_HS) ||
+ (bm_usb_state == AB8500_BM_USB_STATE_RESET_FS)) {
+ musb_power = 0;
+#if 0
+ /* for debugging purpose only */
+ ab8500_bm_ulpi_test();
+#endif
+ }
+
+ printk(KERN_INFO "Please implement ab8500_bm_usb_state_changed"
+ "(%d AB8500_BM_USB_STATE_XXX, %d mA)\n",
+ bm_usb_state, musb_power);
+
+ /* ab8500_bm_usb_state_changed(bm_usb_state, musb_power); */
+}
+
+void
+ab8500_bm_ulpi_set_char_speed_mode(u8 mode)
+{
+ if (musb_status == NULL) {
+ printk(KERN_ALERT "musb_status is NULL. "
+ "Cannot write to ULPI.\n");
+ return;
+ }
+
+ mode &= 3;
+ mode <<= 5;
+ mode |= ulpi_read_register(musb_status, ULPI_ULINKSTAT) &
+ ~ULPI_ULINKSTAT_CHARSPEED_MODE;
+
+ printk(KERN_INFO "Writing 0x%02x to USB char-speed mode bits\n", mode);
+
+ ulpi_write_register(musb_status, ULPI_ULINKSTAT, mode);
+}
+
+void
+ab8500_bm_ulpi_set_char_suspend_mode(u8 suspend)
+{
+ if (musb_status == NULL) {
+ printk(KERN_ALERT "musb_status is NULL. "
+ "Cannot write to ULPI.\n");
+ return;
+ }
+
+ suspend &= 1;
+ suspend <<= 7;
+ suspend |= ulpi_read_register(musb_status, ULPI_ULINKSTAT) &
+ ~ULPI_ULINKSTAT_SUSPEND_MODE;
+
+ printk(KERN_INFO "Writing 0x%02x to USB suspend mode bits\n", suspend);
+
+ ulpi_write_register(musb_status, ULPI_ULINKSTAT, suspend);
+}
+
+/* Sys interfaces */
static struct kobject *usbstatus_kobj;
static ssize_t usb_cable_status
(struct kobject *kobj, struct attribute *attr, char *buf)
@@ -71,9 +174,12 @@ static struct timer_list notify_timer;
static u8 ulpi_read_register(struct musb *musb, u8 address)
{
void __iomem *mbase = musb->mregs;
- int count = 20;
+ unsigned long flags;
+ int count = 200;
u8 val;
+ spin_lock_irqsave(&musb_ulpi_spinlock, flags);
+
/* set ULPI register address */
musb_writeb(mbase, OTG_UREGADDR, address);
@@ -89,20 +195,87 @@ static u8 ulpi_read_register(struct musb *musb, u8 address)
/* wait for completion with a time-out */
do {
- udelay(100);
+ udelay(10);
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ count--;
+ } while (!(val & OTG_UREGCTRL_REGCMP) && (count > 0));
+
+ /* check for time-out */
+ if (!(val & OTG_UREGCTRL_REGCMP) && (count == 0)) {
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+ printk(KERN_ALERT "U8500 USB : ULPI read timed out\n");
+ return 0;
+ }
+
+ /* acknowledge completion */
+ val &= ~OTG_UREGCTRL_REGCMP;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* get data */
+ val = musb_readb(mbase, OTG_UREGDATA);
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+
+ return val;
+}
+
+/**
+ * ulpi_write_register() - Write to a usb phy's ULPI register
+ * using the Mentor ULPI wrapper functionality
+ * @musb: struct musb pointer.
+ * @address: address of ULPI register
+ * @data: data for ULPI register
+ * This function writes the value given by data to the specific address
+ */
+static u8 ulpi_write_register(struct musb *musb, u8 address, u8 data)
+{
+ void __iomem *mbase = musb->mregs;
+ unsigned long flags;
+ int count = 200;
+ u8 val;
+
+ spin_lock_irqsave(&musb_ulpi_spinlock, flags);
+
+ /* First write to ULPI wrapper registers */
+ /* set ULPI register address */
+ musb_writeb(mbase, OTG_UREGADDR, address);
+
+ /* request a write access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val &= ~OTG_UREGCTRL_URW;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* Write data to ULPI wrapper data register */
+ musb_writeb(mbase, OTG_UREGDATA, data);
+
+ /* perform access */
+ val = musb_readb(mbase, OTG_UREGCTRL);
+ val |= OTG_UREGCTRL_REGREQ;
+ musb_writeb(mbase, OTG_UREGCTRL, val);
+
+ /* wait for completion with a time-out */
+ do {
+ udelay(10);
val = musb_readb(mbase, OTG_UREGCTRL);
count--;
} while (!(val & OTG_UREGCTRL_REGCMP) && (count > 0));
/* check for time-out */
- if (!(val & OTG_UREGCTRL_REGCMP) && (count == 0))
+ if (!(val & OTG_UREGCTRL_REGCMP) && (count == 0)) {
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+ printk(KERN_ALERT "U8500 USB : ULPI write timed out\n");
return 0;
+ }
+
/* acknowledge completion */
val &= ~OTG_UREGCTRL_REGCMP;
musb_writeb(mbase, OTG_UREGCTRL, val);
- return musb_readb(mbase, OTG_UREGDATA);
+ spin_unlock_irqrestore(&musb_ulpi_spinlock, flags);
+
+ return 0;
+
}
+
/**
* musb_stm_hs_otg_init() - Initialize the USB for paltform specific.
* @musb: struct musb pointer.
@@ -262,6 +435,10 @@ static void set_vbus(struct musb *musb, int is_on)
*/
static int set_power(struct otg_transceiver *x, unsigned mA)
{
+ musb_power = mA;
+
+ ab8500_bm_usb_state_changed_wrapper(AB8500_BM_USB_STATE_CONFIGURED);
+
return 0;
}
/**
@@ -339,8 +516,12 @@ int __init musb_platform_init(struct musb *musb)
if (ret < 0)
return ret;
+ if (musb_status == NULL) {
+ musb_status = musb;
+ spin_lock_init(&musb_ulpi_spinlock);
+ }
+
/* Registering usb device for sysfs */
- musb_status = musb;
usbstatus_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
if (usbstatus_kobj == NULL)
@@ -385,5 +566,8 @@ int musb_platform_exit(struct musb *musb)
}
if (musb->board_mode != MUSB_PERIPHERAL)
del_timer_sync(&notify_timer);
+
+ musb_status = NULL;
+
return 0;
}
diff --git a/drivers/usb/musb/stm_musb.h b/drivers/usb/musb/stm_musb.h
index 18f8640e919..90bd3f19877 100644
--- a/drivers/usb/musb/stm_musb.h
+++ b/drivers/usb/musb/stm_musb.h
@@ -13,6 +13,15 @@
#include <mach/hardware.h>
+enum {
+ AB8500_BM_USB_STATE_RESET_HS, /* HighSpeed Reset */
+ AB8500_BM_USB_STATE_RESET_FS, /* FullSpeed/LowSpeed Reset */
+ AB8500_BM_USB_STATE_CONFIGURED,
+ AB8500_BM_USB_STATE_SUSPEND,
+ AB8500_BM_USB_STATE_RESUME,
+ AB8500_BM_USB_STATE_MAX,
+};
+
/**
* U8500-specific definitions
*/
@@ -89,6 +98,7 @@
#define ULPI_SCRATCH 0x16 /* Scratch Register */
#define ULPI_CCTRL 0x19 /* Carkit Control Register */
#define ULPI_PCTRL 0x3D /* Power Control Register */
+#define ULPI_ULINKSTAT 0x39 /* USB Link Status & Control Register */
/*
* Vendor and Product IDs
@@ -136,4 +146,17 @@
#define ULPI_OCTRL_DRVVBUSEXT (1 << 6)
#define ULPI_OCTRL_EXTVBUSIND (1 << 7)
+/**
+ * STULPI USB Link Status & Control Register
+ */
+#define ULPI_ULINKSTAT_CHARSPEED_MODE (3 << 5)
+#define ULPI_ULINKSTAT_SUSPEND_MODE (1 << 7)
+
+/* function prototypes */
+
+void ab8500_bm_usb_state_changed(u8 bm_usb_state, u16 mA);
+void ab8500_bm_usb_state_changed_wrapper(u8 bm_usb_state);
+void ab8500_bm_ulpi_set_char_speed_mode(u8 mode);
+void ab8500_bm_ulpi_set_char_suspend_mode(u8 suspend);
+
#endif/* __MUSB_U8500_H__ */