Commit db11e47d authored by Sebastian Siewior's avatar Sebastian Siewior Committed by Greg Kroah-Hartman

USB: ISP1760 HCD driver

This driver has been written from scratch and supports the ISP1760. ISP1761
might (should) work as well but the OTG isn't supported. Also ISO packets are
not. However, it works on my little PowerPC board.
Signed-off-by: default avatarSebastian Siewior <bigeasy@linutronix.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 886c35fb
...@@ -95,6 +95,32 @@ config USB_ISP116X_HCD ...@@ -95,6 +95,32 @@ config USB_ISP116X_HCD
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called isp116x-hcd. module will be called isp116x-hcd.
config USB_ISP1760_HCD
tristate "ISP 1760 HCD support"
depends on USB && EXPERIMENTAL
---help---
The ISP1760 chip is a USB 2.0 host controller.
This driver does not support isochronous transfers or OTG.
To compile this driver as a module, choose M here: the
module will be called isp1760-hcd.
config USB_ISP1760_PCI
bool "Support for the PCI bus"
depends on USB_ISP1760_HCD && PCI
---help---
Enables support for the device present on the PCI bus.
This should only be required if you happen to have the eval kit from
NXP and you are going to test it.
config USB_ISP1760_OF
bool "Support for the OF platform bus"
depends on USB_ISP1760_HCD && OF
---help---
Enables support for the device present on the PowerPC
OpenFirmware platform bus.
config USB_OHCI_HCD config USB_OHCI_HCD
tristate "OHCI HCD support" tristate "OHCI HCD support"
depends on USB && USB_ARCH_HAS_OHCI depends on USB && USB_ARCH_HAS_OHCI
......
...@@ -6,6 +6,8 @@ ifeq ($(CONFIG_USB_DEBUG),y) ...@@ -6,6 +6,8 @@ ifeq ($(CONFIG_USB_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG EXTRA_CFLAGS += -DDEBUG
endif endif
isp1760-objs := isp1760-hcd.o isp1760-if.o
obj-$(CONFIG_PCI) += pci-quirks.o obj-$(CONFIG_PCI) += pci-quirks.o
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
...@@ -16,4 +18,4 @@ obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o ...@@ -16,4 +18,4 @@ obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o
This diff is collapsed.
#ifndef _ISP1760_HCD_H_
#define _ISP1760_HCD_H_
/* exports for if */
struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq,
u64 irqflags, struct device *dev, const char *busname);
int init_kmem_once(void);
void deinit_kmem_cache(void);
/* EHCI capability registers */
#define HC_CAPLENGTH 0x00
#define HC_HCSPARAMS 0x04
#define HC_HCCPARAMS 0x08
/* EHCI operational registers */
#define HC_USBCMD 0x20
#define HC_USBSTS 0x24
#define HC_FRINDEX 0x2c
#define HC_CONFIGFLAG 0x60
#define HC_PORTSC1 0x64
#define HC_ISO_PTD_DONEMAP_REG 0x130
#define HC_ISO_PTD_SKIPMAP_REG 0x134
#define HC_ISO_PTD_LASTPTD_REG 0x138
#define HC_INT_PTD_DONEMAP_REG 0x140
#define HC_INT_PTD_SKIPMAP_REG 0x144
#define HC_INT_PTD_LASTPTD_REG 0x148
#define HC_ATL_PTD_DONEMAP_REG 0x150
#define HC_ATL_PTD_SKIPMAP_REG 0x154
#define HC_ATL_PTD_LASTPTD_REG 0x158
/* Configuration Register */
#define HC_HW_MODE_CTRL 0x300
#define ALL_ATX_RESET (1 << 31)
#define HW_DATA_BUS_32BIT (1 << 8)
#define HW_DACK_POL_HIGH (1 << 6)
#define HW_DREQ_POL_HIGH (1 << 5)
#define HW_INTR_HIGH_ACT (1 << 2)
#define HW_INTR_EDGE_TRIG (1 << 1)
#define HW_GLOBAL_INTR_EN (1 << 0)
#define HC_CHIP_ID_REG 0x304
#define HC_SCRATCH_REG 0x308
#define HC_RESET_REG 0x30c
#define SW_RESET_RESET_HC (1 << 1)
#define SW_RESET_RESET_ALL (1 << 0)
#define HC_BUFFER_STATUS_REG 0x334
#define ATL_BUFFER 0x1
#define INT_BUFFER 0x2
#define ISO_BUFFER 0x4
#define BUFFER_MAP 0x7
#define HC_MEMORY_REG 0x33c
#define HC_PORT1_CTRL 0x374
#define PORT1_POWER (3 << 3)
#define PORT1_INIT1 (1 << 7)
#define PORT1_INIT2 (1 << 23)
/* Interrupt Register */
#define HC_INTERRUPT_REG 0x310
#define HC_INTERRUPT_ENABLE 0x314
#define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT | HC_EOT_INT)
#define FINAL_HW_CONFIG (HW_GLOBAL_INTR_EN | HW_DATA_BUS_32BIT)
#define HC_ISO_INT (1 << 9)
#define HC_ATL_INT (1 << 8)
#define HC_INTL_INT (1 << 7)
#define HC_EOT_INT (1 << 3)
#define HC_SOT_INT (1 << 1)
#define HC_ISO_IRQ_MASK_OR_REG 0x318
#define HC_INT_IRQ_MASK_OR_REG 0x31C
#define HC_ATL_IRQ_MASK_OR_REG 0x320
#define HC_ISO_IRQ_MASK_AND_REG 0x324
#define HC_INT_IRQ_MASK_AND_REG 0x328
#define HC_ATL_IRQ_MASK_AND_REG 0x32C
/* Register sets */
#define HC_BEGIN_OF_ATL 0x0c00
#define HC_BEGIN_OF_INT 0x0800
#define HC_BEGIN_OF_ISO 0x0400
#define HC_BEGIN_OF_PAYLOAD 0x1000
/* urb state*/
#define DELETE_URB (0x0008)
#define NO_TRANSFER_ACTIVE (0xffffffff)
#define ATL_REGS_OFFSET (0xc00)
#define INT_REGS_OFFSET (0x800)
/* Philips Transfer Descriptor (PTD) */
struct ptd {
__le32 dw0;
__le32 dw1;
__le32 dw2;
__le32 dw3;
__le32 dw4;
__le32 dw5;
__le32 dw6;
__le32 dw7;
};
struct inter_packet_info {
void *data_buffer;
u32 payload;
#define PTD_FIRE_NEXT (1 << 0)
#define PTD_URB_FINISHED (1 << 1)
struct urb *urb;
struct isp1760_qh *qh;
struct isp1760_qtd *qtd;
};
typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
struct isp1760_qtd *qtd);
#define isp1760_info(priv, fmt, args...) \
dev_info(priv_to_hcd(priv)->self.controller, fmt, ##args)
#define isp1760_err(priv, fmt, args...) \
dev_err(priv_to_hcd(priv)->self.controller, fmt, ##args)
/* chip memory management */
struct memory_chunk {
unsigned int start;
unsigned int size;
unsigned int free;
};
/*
* 60kb divided in:
* - 32 blocks @ 256 bytes
* - 20 blocks @ 1024 bytes
* - 4 blocks @ 8192 bytes
*/
#define BLOCK_1_NUM 32
#define BLOCK_2_NUM 20
#define BLOCK_3_NUM 4
#define BLOCK_1_SIZE 256
#define BLOCK_2_SIZE 1024
#define BLOCK_3_SIZE 8192
#define BLOCKS (BLOCK_1_NUM + BLOCK_2_NUM + BLOCK_3_NUM)
#define PAYLOAD_SIZE 0xf000
/* I saw if some reloads if the pointer was negative */
#define ISP1760_NULL_POINTER (0x400)
/* ATL */
/* DW0 */
#define PTD_VALID 1
#define PTD_LENGTH(x) (((u32) x) << 3)
#define PTD_MAXPACKET(x) (((u32) x) << 18)
#define PTD_MULTI(x) (((u32) x) << 29)
#define PTD_ENDPOINT(x) (((u32) x) << 31)
/* DW1 */
#define PTD_DEVICE_ADDR(x) (((u32) x) << 3)
#define PTD_PID_TOKEN(x) (((u32) x) << 10)
#define PTD_TRANS_BULK ((u32) 2 << 12)
#define PTD_TRANS_INT ((u32) 3 << 12)
#define PTD_TRANS_SPLIT ((u32) 1 << 14)
#define PTD_SE_USB_LOSPEED ((u32) 2 << 16)
#define PTD_PORT_NUM(x) (((u32) x) << 18)
#define PTD_HUB_NUM(x) (((u32) x) << 25)
#define PTD_PING(x) (((u32) x) << 26)
/* DW2 */
#define PTD_RL_CNT(x) (((u32) x) << 25)
#define PTD_DATA_START_ADDR(x) (((u32) x) << 8)
#define BASE_ADDR 0x1000
/* DW3 */
#define PTD_CERR(x) (((u32) x) << 23)
#define PTD_NAC_CNT(x) (((u32) x) << 19)
#define PTD_ACTIVE ((u32) 1 << 31)
#define PTD_DATA_TOGGLE(x) (((u32) x) << 25)
#define DW3_HALT_BIT (1 << 30)
#define DW3_ERROR_BIT (1 << 28)
#define DW3_QTD_ACTIVE (1 << 31)
#define INT_UNDERRUN (1 << 2)
#define INT_BABBLE (1 << 1)
#define INT_EXACT (1 << 0)
#define DW1_GET_PID(x) (((x) >> 10) & 0x3)
#define PTD_XFERRED_LENGTH(x) ((x) & 0x7fff)
#define PTD_XFERRED_LENGTH_LO(x) ((x) & 0x7ff)
#define SETUP_PID (2)
#define IN_PID (1)
#define OUT_PID (0)
#define GET_QTD_TOKEN_TYPE(x) ((x) & 0x3)
#define DATA_TOGGLE (1 << 31)
#define GET_DATA_TOGGLE(x) ((x) >> 31)
/* Errata 1 */
#define RL_COUNTER (0)
#define NAK_COUNTER (0)
#define ERR_COUNTER (2)
#define HC_ATL_PL_SIZE (8192)
#endif
/*
* Glue code for the ISP1760 driver and bus
* Currently there is support for
* - OpenFirmware
* - PCI
*
* (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
*
*/
#include <linux/usb.h>
#include <linux/io.h>
#include "../core/hcd.h"
#include "isp1760-hcd.h"
#ifdef CONFIG_USB_ISP1760_OF
#include <linux/of.h>
#include <linux/of_platform.h>
#endif
#ifdef CONFIG_USB_ISP1760_PCI
#include <linux/pci.h>
#endif
#ifdef CONFIG_USB_ISP1760_OF
static int of_isp1760_probe(struct of_device *dev,
const struct of_device_id *match)
{
struct usb_hcd *hcd;
struct device_node *dp = dev->node;
struct resource *res;
struct resource memory;
struct of_irq oirq;
int virq;
u64 res_len;
int ret;
ret = of_address_to_resource(dp, 0, &memory);
if (ret)
return -ENXIO;
res = request_mem_region(memory.start, memory.end - memory.start + 1,
dev->dev.bus_id);
if (!res)
return -EBUSY;
res_len = memory.end - memory.start + 1;
if (of_irq_map_one(dp, 0, &oirq)) {
ret = -ENODEV;
goto release_reg;
}
virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
oirq.size);
hcd = isp1760_register(memory.start, res_len, virq,
IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev->dev.bus_id);
if (IS_ERR(hcd)) {
ret = PTR_ERR(hcd);
goto release_reg;
}
dev_set_drvdata(&dev->dev, hcd);
return ret;
release_reg:
release_mem_region(memory.start, memory.end - memory.start + 1);
return ret;
}
static int of_isp1760_remove(struct of_device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(&dev->dev);
dev_set_drvdata(&dev->dev, NULL);
usb_remove_hcd(hcd);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
return 0;
}
static struct of_device_id of_isp1760_match[] = {
{
.compatible = "nxp,usb-isp1760",
},
{ },
};
MODULE_DEVICE_TABLE(of, of_isp1760_match);
static struct of_platform_driver isp1760_of_driver = {
.name = "nxp-isp1760",
.match_table = of_isp1760_match,
.probe = of_isp1760_probe,
.remove = of_isp1760_remove,
};
#endif
#ifdef CONFIG_USB_ISP1760_PCI
static u32 nxp_pci_io_base;
static u32 iolength;
static u32 pci_mem_phy0;
static u32 length;
static u8 *chip_addr;
static u8 *iobase;
static int __devinit isp1761_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
u8 latency, limit;
__u32 reg_data;
int retry_count;
int length;
int status = 1;
struct usb_hcd *hcd;
if (usb_disabled())
return -ENODEV;
if (pci_enable_device(dev) < 0)
return -ENODEV;
if (!dev->irq)
return -ENODEV;
/* Grab the PLX PCI mem maped port start address we need */
nxp_pci_io_base = pci_resource_start(dev, 0);
iolength = pci_resource_len(dev, 0);
if (!request_mem_region(nxp_pci_io_base, iolength, "ISP1761 IO MEM")) {
printk(KERN_ERR "request region #1\n");
return -EBUSY;
}
iobase = ioremap_nocache(nxp_pci_io_base, iolength);
if (!iobase) {
printk(KERN_ERR "ioremap #1\n");
release_mem_region(nxp_pci_io_base, iolength);
return -ENOMEM;
}
/* Grab the PLX PCI shared memory of the ISP 1761 we need */
pci_mem_phy0 = pci_resource_start(dev, 3);
length = pci_resource_len(dev, 3);
if (length < 0xffff) {
printk(KERN_ERR "memory length for this resource is less than "
"required\n");
release_mem_region(nxp_pci_io_base, iolength);
iounmap(iobase);
return -ENOMEM;
}
if (!request_mem_region(pci_mem_phy0, length, "ISP-PCI")) {
printk(KERN_ERR "host controller already in use\n");
release_mem_region(nxp_pci_io_base, iolength);
iounmap(iobase);
return -EBUSY;
}
/* bad pci latencies can contribute to overruns */
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
if (latency) {
pci_read_config_byte(dev, PCI_MAX_LAT, &limit);
if (limit && limit < latency)
pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit);
}
/* Try to check whether we can access Scratch Register of
* Host Controller or not. The initial PCI access is retried until
* local init for the PCI bridge is completed
*/
retry_count = 20;
reg_data = 0;
while ((reg_data != 0xFACE) && retry_count) {
/*by default host is in 16bit mode, so
* io operations at this stage must be 16 bit
* */
writel(0xface, chip_addr + HC_SCRATCH_REG);
udelay(100);
reg_data = readl(chip_addr + HC_SCRATCH_REG);
retry_count--;
}
/* Host Controller presence is detected by writing to scratch register
* and reading back and checking the contents are same or not
*/
if (reg_data != 0xFACE) {
err("scratch register mismatch %x", reg_data);
goto clean;
}
pci_set_master(dev);
status = readl(iobase + 0x68);
status |= 0x900;
writel(status, iobase + 0x68);
dev->dev.dma_mask = NULL;
hcd = isp1760_register(pci_mem_phy0, length, dev->irq,
IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev->dev.bus_id);
pci_set_drvdata(dev, hcd);
if (!hcd)
return 0;
clean:
status = -ENODEV;
iounmap(iobase);
release_mem_region(pci_mem_phy0, length);
release_mem_region(nxp_pci_io_base, iolength);
return status;
}
static void isp1761_pci_remove(struct pci_dev *dev)
{
struct usb_hcd *hcd;
hcd = pci_get_drvdata(dev);
usb_remove_hcd(hcd);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
pci_disable_device(dev);
iounmap(iobase);
iounmap(chip_addr);
release_mem_region(nxp_pci_io_base, iolength);
release_mem_region(pci_mem_phy0, length);
}
static void isp1761_pci_shutdown(struct pci_dev *dev)
{
printk(KERN_ERR "ips1761_pci_shutdown\n");
}
static const struct pci_device_id isp1760_plx [] = { {
/* handle any USB 2.0 EHCI controller */
PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_OTHER << 8) | (0x06 << 16)), ~0),
.driver_data = 0,
},
{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, isp1760_plx);
static struct pci_driver isp1761_pci_driver = {
.name = "isp1760",
.id_table = isp1760_plx,
.probe = isp1761_pci_probe,
.remove = isp1761_pci_remove,
.shutdown = isp1761_pci_shutdown,
};
#endif
static int __init isp1760_init(void)
{
int ret;
init_kmem_once();
#ifdef CONFIG_USB_ISP1760_OF
ret = of_register_platform_driver(&isp1760_of_driver);
if (ret) {
deinit_kmem_cache();
return ret;
}
#endif
#ifdef CONFIG_USB_ISP1760_PCI
ret = pci_register_driver(&isp1761_pci_driver);
if (ret)
goto unreg_of;
#endif
return ret;
#ifdef CONFIG_USB_ISP1760_PCI
unreg_of:
#endif
#ifdef CONFIG_USB_ISP1760_OF
of_unregister_platform_driver(&isp1760_of_driver);
#endif
deinit_kmem_cache();
return ret;
}
module_init(isp1760_init);
static void __exit isp1760_exit(void)
{
#ifdef CONFIG_USB_ISP1760_OF
of_unregister_platform_driver(&isp1760_of_driver);
#endif
#ifdef CONFIG_USB_ISP1760_PCI
pci_unregister_driver(&isp1761_pci_driver);
#endif
deinit_kmem_cache();
}
module_exit(isp1760_exit);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment