diff options
author | Hans Petter SELASKY <hans.petter.selasky@stericsson.com> | 2010-04-20 14:38:55 +0530 |
---|---|---|
committer | John Rigby <john.rigby@linaro.org> | 2010-09-02 22:45:05 -0600 |
commit | ffda6399bb264b0933be20ac57686887adfd1d45 (patch) | |
tree | 2fcb9c06aa3140007f92c6b172ab05b4010c0900 /drivers/usb | |
parent | 9fa98f5fb5139e249c097f957a8d9f1132ea1487 (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.c | 25 | ||||
-rw-r--r-- | drivers/usb/musb/stm_musb.c | 198 | ||||
-rw-r--r-- | drivers/usb/musb/stm_musb.h | 23 |
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(¬ify_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__ */ |