usb: isp1760: Add device controller support

The ISP1761 is a dual-mode host and device controller backward
compatible on the host side with the ISP1760. Add support for the device
controller.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
Laurent Pinchart 2015-01-21 00:56:01 +02:00 committed by Felipe Balbi
parent 9a66e13290
commit 0316ca6319
7 changed files with 1768 additions and 5 deletions

View File

@ -109,6 +109,13 @@ config USB_GR_UDC
Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB
VHDL IP core library. VHDL IP core library.
config USB_ISP1761_UDC
boolean "NXP ISP1761 USB Device Controller"
depends on USB_ISP1760_HCD
help
The NXP ISP1761 is a dual-role high-speed USB host and device
controller.
config USB_OMAP config USB_OMAP
tristate "OMAP USB Device Controller" tristate "OMAP USB Device Controller"
depends on ARCH_OMAP1 depends on ARCH_OMAP1

View File

@ -6,6 +6,7 @@
CFLAGS_xhci-trace.o := -I$(src) CFLAGS_xhci-trace.o := -I$(src)
isp1760-y := isp1760-core.o isp1760-hcd.o isp1760-if.o isp1760-y := isp1760-core.o isp1760-hcd.o isp1760-if.o
isp1760-$(CONFIG_USB_ISP1761_UDC) += isp1760-udc.o
fhci-y := fhci-hcd.o fhci-hub.o fhci-q.o fhci-y := fhci-hcd.o fhci-hub.o fhci-q.o
fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o

View File

@ -24,9 +24,11 @@
#include "isp1760-core.h" #include "isp1760-core.h"
#include "isp1760-hcd.h" #include "isp1760-hcd.h"
#include "isp1760-regs.h" #include "isp1760-regs.h"
#include "isp1760-udc.h"
static void isp1760_init_core(struct isp1760_device *isp) static void isp1760_init_core(struct isp1760_device *isp)
{ {
u32 otgctrl;
u32 hwmode; u32 hwmode;
/* Low-level chip reset */ /* Low-level chip reset */
@ -59,6 +61,17 @@ static void isp1760_init_core(struct isp1760_device *isp)
if (isp->devflags & ISP1760_FLAG_INTR_EDGE_TRIG) if (isp->devflags & ISP1760_FLAG_INTR_EDGE_TRIG)
hwmode |= HW_INTR_EDGE_TRIG; hwmode |= HW_INTR_EDGE_TRIG;
/*
* The ISP1761 has a dedicated DC IRQ line but supports sharing the HC
* IRQ line for both the host and device controllers. Hardcode IRQ
* sharing for now and disable the DC interrupts globally to avoid
* spurious interrupts during HCD registration.
*/
if (isp->devflags & ISP1760_FLAG_ISP1761) {
isp1760_write32(isp->regs, DC_MODE, 0);
hwmode |= HW_COMN_IRQ;
}
/* /*
* We have to set this first in case we're in 16-bit mode. * We have to set this first in case we're in 16-bit mode.
* Write it twice to ensure correct upper bits if switching * Write it twice to ensure correct upper bits if switching
@ -68,18 +81,33 @@ static void isp1760_init_core(struct isp1760_device *isp)
isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode); isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode);
/* /*
* PORT 1 Control register of the ISP1760 is the OTG control register on * PORT 1 Control register of the ISP1760 is the OTG control register
* ISP1761. Since there is no OTG or device controller support in this * on ISP1761.
* driver, we use port 1 as a "normal" USB host port on both chips. *
* TODO: Really support OTG. For now we configure port 1 in device mode
* when OTG is requested.
*/ */
isp1760_write32(isp->regs, HC_PORT1_CTRL, PORT1_POWER | PORT1_INIT2); if ((isp->devflags & ISP1760_FLAG_ISP1761) &&
usleep_range(10000, 11000); (isp->devflags & ISP1760_FLAG_OTG_EN))
otgctrl = ((HW_DM_PULLDOWN | HW_DP_PULLDOWN) << 16)
| HW_OTG_DISABLE;
else
otgctrl = (HW_SW_SEL_HC_DC << 16)
| (HW_VBUS_DRV | HW_SEL_CP_EXT);
isp1760_write32(isp->regs, HC_PORT1_CTRL, otgctrl);
dev_info(isp->dev, "bus width: %u, oc: %s\n", dev_info(isp->dev, "bus width: %u, oc: %s\n",
isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32, isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32,
isp->devflags & ISP1760_FLAG_ANALOG_OC ? "analog" : "digital"); isp->devflags & ISP1760_FLAG_ANALOG_OC ? "analog" : "digital");
} }
void isp1760_set_pullup(struct isp1760_device *isp, bool enable)
{
isp1760_write32(isp->regs, HW_OTG_CTRL_SET,
enable ? HW_DP_PULLUP : HW_DP_PULLUP << 16);
}
int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
struct device *dev, unsigned int devflags) struct device *dev, unsigned int devflags)
{ {
@ -114,6 +142,15 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
if (ret < 0) if (ret < 0)
return ret; return ret;
if (devflags & ISP1760_FLAG_ISP1761) {
ret = isp1760_udc_register(isp, irq, irqflags | IRQF_SHARED |
IRQF_DISABLED);
if (ret < 0) {
isp1760_hcd_unregister(&isp->hcd);
return ret;
}
}
dev_set_drvdata(dev, isp); dev_set_drvdata(dev, isp);
return 0; return 0;
@ -123,6 +160,9 @@ void isp1760_unregister(struct device *dev)
{ {
struct isp1760_device *isp = dev_get_drvdata(dev); struct isp1760_device *isp = dev_get_drvdata(dev);
if (isp->devflags & ISP1760_FLAG_ISP1761)
isp1760_udc_unregister(isp);
isp1760_hcd_unregister(&isp->hcd); isp1760_hcd_unregister(&isp->hcd);
} }

View File

@ -19,6 +19,7 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include "isp1760-hcd.h" #include "isp1760-hcd.h"
#include "isp1760-udc.h"
struct device; struct device;
struct gpio_desc; struct gpio_desc;
@ -45,12 +46,15 @@ struct isp1760_device {
struct gpio_desc *rst_gpio; struct gpio_desc *rst_gpio;
struct isp1760_hcd hcd; struct isp1760_hcd hcd;
struct isp1760_udc udc;
}; };
int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
struct device *dev, unsigned int devflags); struct device *dev, unsigned int devflags);
void isp1760_unregister(struct device *dev); void isp1760_unregister(struct device *dev);
void isp1760_set_pullup(struct isp1760_device *isp, bool enable);
static inline u32 isp1760_read32(void __iomem *base, u32 reg) static inline u32 isp1760_read32(void __iomem *base, u32 reg)
{ {
return readl(base + reg); return readl(base + reg);

View File

@ -16,6 +16,10 @@
#ifndef _ISP1760_REGS_H_ #ifndef _ISP1760_REGS_H_
#define _ISP1760_REGS_H_ #define _ISP1760_REGS_H_
/* -----------------------------------------------------------------------------
* Host Controller
*/
/* EHCI capability registers */ /* EHCI capability registers */
#define HC_CAPLENGTH 0x000 #define HC_CAPLENGTH 0x000
#define HC_LENGTH(p) (((p) >> 00) & 0x00ff) /* bits 7:0 */ #define HC_LENGTH(p) (((p) >> 00) & 0x00ff) /* bits 7:0 */
@ -70,6 +74,9 @@
#define HC_HW_MODE_CTRL 0x300 #define HC_HW_MODE_CTRL 0x300
#define ALL_ATX_RESET (1 << 31) #define ALL_ATX_RESET (1 << 31)
#define HW_ANA_DIGI_OC (1 << 15) #define HW_ANA_DIGI_OC (1 << 15)
#define HW_DEV_DMA (1 << 11)
#define HW_COMN_IRQ (1 << 10)
#define HW_COMN_DMA (1 << 9)
#define HW_DATA_BUS_32BIT (1 << 8) #define HW_DATA_BUS_32BIT (1 << 8)
#define HW_DACK_POL_HIGH (1 << 6) #define HW_DACK_POL_HIGH (1 << 6)
#define HW_DREQ_POL_HIGH (1 << 5) #define HW_DREQ_POL_HIGH (1 << 5)
@ -98,6 +105,17 @@
#define PORT1_INIT2 (1 << 23) #define PORT1_INIT2 (1 << 23)
#define HW_OTG_CTRL_SET 0x374 #define HW_OTG_CTRL_SET 0x374
#define HW_OTG_CTRL_CLR 0x376 #define HW_OTG_CTRL_CLR 0x376
#define HW_OTG_DISABLE (1 << 10)
#define HW_OTG_SE0_EN (1 << 9)
#define HW_BDIS_ACON_EN (1 << 8)
#define HW_SW_SEL_HC_DC (1 << 7)
#define HW_VBUS_CHRG (1 << 6)
#define HW_VBUS_DISCHRG (1 << 5)
#define HW_VBUS_DRV (1 << 4)
#define HW_SEL_CP_EXT (1 << 3)
#define HW_DM_PULLDOWN (1 << 2)
#define HW_DP_PULLDOWN (1 << 1)
#define HW_DP_PULLUP (1 << 0)
/* Interrupt Register */ /* Interrupt Register */
#define HC_INTERRUPT_REG 0x310 #define HC_INTERRUPT_REG 0x310
@ -117,4 +135,96 @@
#define HC_INT_IRQ_MASK_AND_REG 0x328 #define HC_INT_IRQ_MASK_AND_REG 0x328
#define HC_ATL_IRQ_MASK_AND_REG 0x32c #define HC_ATL_IRQ_MASK_AND_REG 0x32c
/* -----------------------------------------------------------------------------
* Peripheral Controller
*/
/* Initialization Registers */
#define DC_ADDRESS 0x0200
#define DC_DEVEN (1 << 7)
#define DC_MODE 0x020c
#define DC_DMACLKON (1 << 9)
#define DC_VBUSSTAT (1 << 8)
#define DC_CLKAON (1 << 7)
#define DC_SNDRSU (1 << 6)
#define DC_GOSUSP (1 << 5)
#define DC_SFRESET (1 << 4)
#define DC_GLINTENA (1 << 3)
#define DC_WKUPCS (1 << 2)
#define DC_INTCONF 0x0210
#define DC_CDBGMOD_ACK_NAK (0 << 6)
#define DC_CDBGMOD_ACK (1 << 6)
#define DC_CDBGMOD_ACK_1NAK (2 << 6)
#define DC_DDBGMODIN_ACK_NAK (0 << 4)
#define DC_DDBGMODIN_ACK (1 << 4)
#define DC_DDBGMODIN_ACK_1NAK (2 << 4)
#define DC_DDBGMODOUT_ACK_NYET_NAK (0 << 2)
#define DC_DDBGMODOUT_ACK_NYET (1 << 2)
#define DC_DDBGMODOUT_ACK_NYET_1NAK (2 << 2)
#define DC_INTLVL (1 << 1)
#define DC_INTPOL (1 << 0)
#define DC_DEBUG 0x0212
#define DC_INTENABLE 0x0214
#define DC_IEPTX(n) (1 << (11 + 2 * (n)))
#define DC_IEPRX(n) (1 << (10 + 2 * (n)))
#define DC_IEPRXTX(n) (3 << (10 + 2 * (n)))
#define DC_IEP0SETUP (1 << 8)
#define DC_IEVBUS (1 << 7)
#define DC_IEDMA (1 << 6)
#define DC_IEHS_STA (1 << 5)
#define DC_IERESM (1 << 4)
#define DC_IESUSP (1 << 3)
#define DC_IEPSOF (1 << 2)
#define DC_IESOF (1 << 1)
#define DC_IEBRST (1 << 0)
/* Data Flow Registers */
#define DC_EPINDEX 0x022c
#define DC_EP0SETUP (1 << 5)
#define DC_ENDPIDX(n) ((n) << 1)
#define DC_EPDIR (1 << 0)
#define DC_CTRLFUNC 0x0228
#define DC_CLBUF (1 << 4)
#define DC_VENDP (1 << 3)
#define DC_DSEN (1 << 2)
#define DC_STATUS (1 << 1)
#define DC_STALL (1 << 0)
#define DC_DATAPORT 0x0220
#define DC_BUFLEN 0x021c
#define DC_DATACOUNT_MASK 0xffff
#define DC_BUFSTAT 0x021e
#define DC_EPMAXPKTSZ 0x0204
#define DC_EPTYPE 0x0208
#define DC_NOEMPKT (1 << 4)
#define DC_EPENABLE (1 << 3)
#define DC_DBLBUF (1 << 2)
#define DC_ENDPTYP_ISOC (1 << 0)
#define DC_ENDPTYP_BULK (2 << 0)
#define DC_ENDPTYP_INTERRUPT (3 << 0)
/* DMA Registers */
#define DC_DMACMD 0x0230
#define DC_DMATXCOUNT 0x0234
#define DC_DMACONF 0x0238
#define DC_DMAHW 0x023c
#define DC_DMAINTREASON 0x0250
#define DC_DMAINTEN 0x0254
#define DC_DMAEP 0x0258
#define DC_DMABURSTCOUNT 0x0264
/* General Registers */
#define DC_INTERRUPT 0x0218
#define DC_CHIPID 0x0270
#define DC_FRAMENUM 0x0274
#define DC_SCRATCH 0x0278
#define DC_UNLOCKDEV 0x027c
#define DC_INTPULSEWIDTH 0x0280
#define DC_TESTMODE 0x0284
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,106 @@
/*
* Driver for the NXP ISP1761 device controller
*
* Copyright 2014 Ideas on Board Oy
*
* Contacts:
* Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#ifndef _ISP1760_UDC_H_
#define _ISP1760_UDC_H_
#include <linux/ioport.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/usb/gadget.h>
struct isp1760_device;
struct isp1760_udc;
enum isp1760_ctrl_state {
ISP1760_CTRL_SETUP, /* Waiting for a SETUP transaction */
ISP1760_CTRL_DATA_IN, /* Setup received, data IN stage */
ISP1760_CTRL_DATA_OUT, /* Setup received, data OUT stage */
ISP1760_CTRL_STATUS, /* 0-length request in status stage */
};
struct isp1760_ep {
struct isp1760_udc *udc;
struct usb_ep ep;
struct list_head queue;
unsigned int addr;
unsigned int maxpacket;
char name[7];
const struct usb_endpoint_descriptor *desc;
bool rx_pending;
bool halted;
bool wedged;
};
/**
* struct isp1760_udc - UDC state information
* irq: IRQ number
* irqname: IRQ name (as passed to request_irq)
* regs: Base address of the UDC registers
* driver: Gadget driver
* gadget: Gadget device
* lock: Protects driver, vbus_timer, ep, ep0_*, DC_EPINDEX register
* ep: Array of endpoints
* ep0_state: Control request state for endpoint 0
* ep0_dir: Direction of the current control request
* ep0_length: Length of the current control request
* connected: Tracks gadget driver bus connection state
*/
struct isp1760_udc {
#if CONFIG_USB_ISP1761_UDC
struct isp1760_device *isp;
int irq;
char *irqname;
void __iomem *regs;
struct usb_gadget_driver *driver;
struct usb_gadget gadget;
spinlock_t lock;
struct timer_list vbus_timer;
struct isp1760_ep ep[15];
enum isp1760_ctrl_state ep0_state;
u8 ep0_dir;
u16 ep0_length;
bool connected;
unsigned int devstatus;
#endif
};
#if CONFIG_USB_ISP1761_UDC
int isp1760_udc_register(struct isp1760_device *isp, int irq,
unsigned long irqflags);
void isp1760_udc_unregister(struct isp1760_device *isp);
#else
static inline int isp1760_udc_register(struct isp1760_device *isp, int irq,
unsigned long irqflags)
{
return 0;
}
static inline void isp1760_udc_unregister(struct isp1760_device *isp)
{
}
#endif
#endif