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
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
tristate "OMAP USB Device Controller"
depends on ARCH_OMAP1

View File

@ -6,6 +6,7 @@
CFLAGS_xhci-trace.o := -I$(src)
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-mem.o fhci-tds.o fhci-sched.o

View File

@ -24,9 +24,11 @@
#include "isp1760-core.h"
#include "isp1760-hcd.h"
#include "isp1760-regs.h"
#include "isp1760-udc.h"
static void isp1760_init_core(struct isp1760_device *isp)
{
u32 otgctrl;
u32 hwmode;
/* 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)
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.
* 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);
/*
* PORT 1 Control register of the ISP1760 is the OTG control register on
* ISP1761. Since there is no OTG or device controller support in this
* driver, we use port 1 as a "normal" USB host port on both chips.
* PORT 1 Control register of the ISP1760 is the OTG control register
* on ISP1761.
*
* 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);
usleep_range(10000, 11000);
if ((isp->devflags & ISP1760_FLAG_ISP1761) &&
(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",
isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32,
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,
struct device *dev, unsigned int devflags)
{
@ -114,6 +142,15 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
if (ret < 0)
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);
return 0;
@ -123,6 +160,9 @@ void isp1760_unregister(struct device *dev)
{
struct isp1760_device *isp = dev_get_drvdata(dev);
if (isp->devflags & ISP1760_FLAG_ISP1761)
isp1760_udc_unregister(isp);
isp1760_hcd_unregister(&isp->hcd);
}

View File

@ -19,6 +19,7 @@
#include <linux/ioport.h>
#include "isp1760-hcd.h"
#include "isp1760-udc.h"
struct device;
struct gpio_desc;
@ -45,12 +46,15 @@ struct isp1760_device {
struct gpio_desc *rst_gpio;
struct isp1760_hcd hcd;
struct isp1760_udc udc;
};
int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
struct device *dev, unsigned int devflags);
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)
{
return readl(base + reg);

View File

@ -16,6 +16,10 @@
#ifndef _ISP1760_REGS_H_
#define _ISP1760_REGS_H_
/* -----------------------------------------------------------------------------
* Host Controller
*/
/* EHCI capability registers */
#define HC_CAPLENGTH 0x000
#define HC_LENGTH(p) (((p) >> 00) & 0x00ff) /* bits 7:0 */
@ -70,6 +74,9 @@
#define HC_HW_MODE_CTRL 0x300
#define ALL_ATX_RESET (1 << 31)
#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_DACK_POL_HIGH (1 << 6)
#define HW_DREQ_POL_HIGH (1 << 5)
@ -98,6 +105,17 @@
#define PORT1_INIT2 (1 << 23)
#define HW_OTG_CTRL_SET 0x374
#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 */
#define HC_INTERRUPT_REG 0x310
@ -117,4 +135,96 @@
#define HC_INT_IRQ_MASK_AND_REG 0x328
#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

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