Split the driver into the following parts: * core -- resources, register access, capabilities, etc; * udc -- device controller functionality; * debug -- tracing, events, debugging output. Signed-off-by: Alexander Shishkin <alexander.shishkin@xxxxxxxxxxxxxxx> --- drivers/usb/chipidea/Kconfig | 20 +- drivers/usb/chipidea/Makefile | 4 +- drivers/usb/chipidea/{ci13xxx_udc.h => ci.h} | 206 ++--- drivers/usb/chipidea/ci13xxx_msm.c | 3 +- drivers/usb/chipidea/ci13xxx_pci.c | 3 +- drivers/usb/chipidea/core.c | 296 +++++++ drivers/usb/chipidea/debug.c | 825 +++++++++++++++++ drivers/usb/chipidea/debug.h | 76 ++ drivers/usb/chipidea/{ci13xxx_udc.c => udc.c} | 1178 +------------------------ drivers/usb/chipidea/udc.h | 119 +++ include/linux/usb/chipidea.h | 27 + 11 files changed, 1474 insertions(+), 1283 deletions(-) rename drivers/usb/chipidea/{ci13xxx_udc.h => ci.h} (57%) create mode 100644 drivers/usb/chipidea/core.c create mode 100644 drivers/usb/chipidea/debug.c create mode 100644 drivers/usb/chipidea/debug.h rename drivers/usb/chipidea/{ci13xxx_udc.c => udc.c} (61%) create mode 100644 drivers/usb/chipidea/udc.h create mode 100644 include/linux/usb/chipidea.h diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig index 71725dd..553c197 100644 --- a/drivers/usb/chipidea/Kconfig +++ b/drivers/usb/chipidea/Kconfig @@ -1,10 +1,26 @@ config USB_CHIPIDEA tristate "ChipIdea Highspeed Dual Role Controller" - depends on USB && USB_GADGET - select USB_GADGET_DUALSPEED + depends on USB help Say Y here if your system has a dual role high speed USB controller based on ChipIdea silicon IP. Currently, only the peripheral mode is supported. When compiled dynamically, the module will be called ci-hdrc.ko. + +if USB_CHIPIDEA + +config USB_CHIPIDEA_UDC + bool "ChipIdea device controller" + depends on USB_GADGET + select USB_GADGET_DUALSPEED + help + Say Y here to enable device controller functionality of the + ChipIdea driver. + +config USB_CHIPIDEA_DEBUG + bool "ChipIdea driver debug" + help + Say Y here to enable debugging output of the ChipIdea driver. + +endif diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index e56bedb..a8279aa 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -1,6 +1,8 @@ obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o -ci_hdrc-y := ci13xxx_udc.o +ci_hdrc-y := core.o +ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o +ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o ifneq ($(CONFIG_PCI),) obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_pci.o diff --git a/drivers/usb/chipidea/ci13xxx_udc.h b/drivers/usb/chipidea/ci.h similarity index 57% rename from drivers/usb/chipidea/ci13xxx_udc.h rename to drivers/usb/chipidea/ci.h index a84dffb..1d3590a 100644 --- a/drivers/usb/chipidea/ci13xxx_udc.h +++ b/drivers/usb/chipidea/ci.h @@ -1,5 +1,5 @@ /* - * ci13xxx_udc.h - structures, registers, and macros MIPS USB IP core + * ci.h - structures, registers, and macros ChipIdea USB IP core * * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. * @@ -13,110 +13,22 @@ * Structures, registers and logging macros */ -#ifndef _CI13XXX_h_ -#define _CI13XXX_h_ +#ifndef __DRIVERS_USB_CHIPIDEA_CI_H +#define __DRIVERS_USB_CHIPIDEA_CI_H + +#include <linux/list.h> +#include <linux/usb/gadget.h> +#include "udc.h" /****************************************************************************** * DEFINE *****************************************************************************/ +#define DMA_ADDR_INVALID (~(dma_addr_t)0) #define CI13XXX_PAGE_SIZE 4096ul /* page size for TD's */ -#define ENDPT_MAX 32 -#define CTRL_PAYLOAD_MAX 64 -#define RX 0 /* similar to USB_DIR_OUT but can be used as an index */ -#define TX 1 /* similar to USB_DIR_IN but can be used as an index */ /****************************************************************************** * STRUCTURES *****************************************************************************/ -/* DMA layout of transfer descriptors */ -struct ci13xxx_td { - /* 0 */ - u32 next; -#define TD_TERMINATE BIT(0) -#define TD_ADDR_MASK (0xFFFFFFEUL << 5) - /* 1 */ - u32 token; -#define TD_STATUS (0x00FFUL << 0) -#define TD_STATUS_TR_ERR BIT(3) -#define TD_STATUS_DT_ERR BIT(5) -#define TD_STATUS_HALTED BIT(6) -#define TD_STATUS_ACTIVE BIT(7) -#define TD_MULTO (0x0003UL << 10) -#define TD_IOC BIT(15) -#define TD_TOTAL_BYTES (0x7FFFUL << 16) - /* 2 */ - u32 page[5]; -#define TD_CURR_OFFSET (0x0FFFUL << 0) -#define TD_FRAME_NUM (0x07FFUL << 0) -#define TD_RESERVED_MASK (0x0FFFUL << 0) -} __attribute__ ((packed)); - -/* DMA layout of queue heads */ -struct ci13xxx_qh { - /* 0 */ - u32 cap; -#define QH_IOS BIT(15) -#define QH_MAX_PKT (0x07FFUL << 16) -#define QH_ZLT BIT(29) -#define QH_MULT (0x0003UL << 30) - /* 1 */ - u32 curr; - /* 2 - 8 */ - struct ci13xxx_td td; - /* 9 */ - u32 RESERVED; - struct usb_ctrlrequest setup; -} __attribute__ ((packed)); - -/* Extension of usb_request */ -struct ci13xxx_req { - struct usb_request req; - unsigned map; - struct list_head queue; - struct ci13xxx_td *ptr; - dma_addr_t dma; - struct ci13xxx_td *zptr; - dma_addr_t zdma; -}; - -/* Extension of usb_ep */ -struct ci13xxx_ep { - struct usb_ep ep; - const struct usb_endpoint_descriptor *desc; - u8 dir; - u8 num; - u8 type; - char name[16]; - struct { - struct list_head queue; - struct ci13xxx_qh *ptr; - dma_addr_t dma; - } qh; - int wedge; - - /* global resources */ - struct ci13xxx *udc; - spinlock_t *lock; - struct device *device; - struct dma_pool *td_pool; -}; - -struct ci13xxx; -struct ci13xxx_udc_driver { - const char *name; - /* offset of the capability registers */ - uintptr_t capoffset; - unsigned long flags; -#define CI13XXX_REGS_SHARED BIT(0) -#define CI13XXX_REQUIRE_TRANSCEIVER BIT(1) -#define CI13XXX_PULLUP_ON_VBUS BIT(2) -#define CI13XXX_DISABLE_STREAMING BIT(3) - -#define CI13XXX_CONTROLLER_RESET_EVENT 0 -#define CI13XXX_CONTROLLER_STOPPED_EVENT 1 - void (*notify_event) (struct ci13xxx *udc, unsigned event); -}; - struct hw_bank { unsigned lpm; /* is LPM? */ void __iomem *abs; /* bus map offset */ @@ -159,9 +71,6 @@ struct ci13xxx { /****************************************************************************** * REGISTERS *****************************************************************************/ -/* Default offset of capability registers */ -#define DEF_CAPOFFSET 0x100 - /* register size */ #define REG_BITS (32) @@ -245,21 +154,84 @@ enum ci13xxx_regs { #define ENDPTCTRL_TXR BIT(22) /* reserved for port 0 */ #define ENDPTCTRL_TXE BIT(23) -/****************************************************************************** - * LOGGING - *****************************************************************************/ -#ifdef TRACE -#define trace(dev, format, args...) \ - do { \ - if (dev == NULL) \ - pr_debug("[%s] " format "\n", __func__, \ - ## args); \ - else \ - dev_printk(KERN_DEBUG, dev, "[%s] " format "\n", \ - __func__, ## args); \ - } while (0) -#else -#define trace(dev, format, args...) do {} while (0) -#endif - -#endif /* _CI13XXX_h_ */ +/** + * ffs_nr: find first (least significant) bit set + * @x: the word to search + * + * This function returns bit number (instead of position) + */ +static inline int ffs_nr(u32 x) +{ + int n = ffs(x); + + return n ? n-1 : 32; +} + +/** + * hw_read: reads from a hw register + * @reg: register index + * @mask: bitfield mask + * + * This function returns register contents + */ +static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask) +{ + return ioread32(udc->hw_bank.regmap[reg]) & mask; +} + +/** + * hw_write: writes to a hw register + * @reg: register index + * @mask: bitfield mask + * @data: new value + */ +static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg, + u32 mask, u32 data) +{ + if (~mask) + data = (ioread32(udc->hw_bank.regmap[reg]) & ~mask) + | (data & mask); + + iowrite32(data, udc->hw_bank.regmap[reg]); +} + +/** + * hw_test_and_clear: tests & clears a hw register + * @reg: register index + * @mask: bitfield mask + * + * This function returns register contents + */ +static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg, + u32 mask) +{ + u32 val = ioread32(udc->hw_bank.regmap[reg]) & mask; + + iowrite32(val, udc->hw_bank.regmap[reg]); + return val; +} + +/** + * hw_test_and_write: tests & writes a hw register + * @reg: register index + * @mask: bitfield mask + * @data: new value + * + * This function returns register contents + */ +static inline u32 hw_test_and_write(struct ci13xxx *udc, enum ci13xxx_regs reg, + u32 mask, u32 data) +{ + u32 val = hw_read(udc, reg, ~0); + + hw_write(udc, reg, mask, data); + return (val & mask) >> ffs_nr(mask); +} + +int hw_device_init(struct ci13xxx *udc, void __iomem *base, + uintptr_t cap_offset); +int hw_device_reset(struct ci13xxx *ci); + +int hw_port_test_set(struct ci13xxx *udc, u8 mode); + +#endif /* __DRIVERS_USB_CHIPIDEA_CI_H */ diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 418de0e..2742793 100644 --- a/drivers/usb/chipidea/ci13xxx_msm.c +++ b/drivers/usb/chipidea/ci13xxx_msm.c @@ -11,8 +11,9 @@ #include <linux/usb/msm_hsusb_hw.h> #include <linux/usb/ulpi.h> #include <linux/usb/gadget.h> +#include <linux/usb/chipidea.h> -#include "ci13xxx_udc.h" +#include "ci.h" #define MSM_USB_BASE (udc->regs) diff --git a/drivers/usb/chipidea/ci13xxx_pci.c b/drivers/usb/chipidea/ci13xxx_pci.c index 9d232c8..934275e 100644 --- a/drivers/usb/chipidea/ci13xxx_pci.c +++ b/drivers/usb/chipidea/ci13xxx_pci.c @@ -15,8 +15,7 @@ #include <linux/pci.h> #include <linux/interrupt.h> #include <linux/usb/gadget.h> - -#include "ci13xxx_udc.h" +#include <linux/usb/chipidea.h> /* driver name */ #define UDC_DRIVER_NAME "ci13xxx_pci" diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c new file mode 100644 index 0000000..a00f614 --- /dev/null +++ b/drivers/usb/chipidea/core.c @@ -0,0 +1,296 @@ +/* + * core.c - ChipIdea USB IP core family device controller + * + * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. + * + * Author: David Lopo + * + * 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. + */ + +/* + * Description: ChipIdea USB IP core family device controller + * + * This driver is composed of several blocks: + * - HW: hardware interface + * - DBG: debug facilities (optional) + * - UTIL: utilities + * - ISR: interrupts handling + * - ENDPT: endpoint operations (Gadget API) + * - GADGET: gadget operations (Gadget API) + * - BUS: bus glue code, bus abstraction layer + * + * Compile Options + * - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities + * - STALL_IN: non-empty bulk-in pipes cannot be halted + * if defined mass storage compliance succeeds but with warnings + * => case 4: Hi > Dn + * => case 5: Hi > Di + * => case 8: Hi <> Do + * if undefined usbtest 13 fails + * - TRACE: enable function tracing (depends on DEBUG) + * + * Main Features + * - Chapter 9 & Mass Storage Compliance with Gadget File Storage + * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined) + * - Normal & LPM support + * + * USBTEST Report + * - OK: 0-12, 13 (STALL_IN defined) & 14 + * - Not Supported: 15 & 16 (ISO) + * + * TODO List + * - OTG + * - Isochronous & Interrupt Traffic + * - Handle requests which spawns into several TDs + * - GET_STATUS(device) - always reports 0 + * - Gadget API (majority of optional features) + * - Suspend & Remote Wakeup + */ +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dmapool.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/otg.h> +#include <linux/usb/chipidea.h> + +#include "ci.h" +#include "udc.h" +#include "debug.h" + +/* MSM specific */ +#define ABS_AHBBURST (0x0090UL) +#define ABS_AHBMODE (0x0098UL) +/* UDC register map */ +static uintptr_t ci_regs_nolpm[] = { + [CAP_CAPLENGTH] = 0x000UL, + [CAP_HCCPARAMS] = 0x008UL, + [CAP_DCCPARAMS] = 0x024UL, + [CAP_TESTMODE] = 0x038UL, + [OP_USBCMD] = 0x000UL, + [OP_USBSTS] = 0x004UL, + [OP_USBINTR] = 0x008UL, + [OP_DEVICEADDR] = 0x014UL, + [OP_ENDPTLISTADDR] = 0x018UL, + [OP_PORTSC] = 0x044UL, + [OP_DEVLC] = 0x084UL, + [OP_USBMODE] = 0x068UL, + [OP_ENDPTSETUPSTAT] = 0x06CUL, + [OP_ENDPTPRIME] = 0x070UL, + [OP_ENDPTFLUSH] = 0x074UL, + [OP_ENDPTSTAT] = 0x078UL, + [OP_ENDPTCOMPLETE] = 0x07CUL, + [OP_ENDPTCTRL] = 0x080UL, +}; + +static uintptr_t ci_regs_lpm[] = { + [CAP_CAPLENGTH] = 0x000UL, + [CAP_HCCPARAMS] = 0x008UL, + [CAP_DCCPARAMS] = 0x024UL, + [CAP_TESTMODE] = 0x0FCUL, + [OP_USBCMD] = 0x000UL, + [OP_USBSTS] = 0x004UL, + [OP_USBINTR] = 0x008UL, + [OP_DEVICEADDR] = 0x014UL, + [OP_ENDPTLISTADDR] = 0x018UL, + [OP_PORTSC] = 0x044UL, + [OP_DEVLC] = 0x084UL, + [OP_USBMODE] = 0x0C8UL, + [OP_ENDPTSETUPSTAT] = 0x0D8UL, + [OP_ENDPTPRIME] = 0x0DCUL, + [OP_ENDPTFLUSH] = 0x0E0UL, + [OP_ENDPTSTAT] = 0x0E4UL, + [OP_ENDPTCOMPLETE] = 0x0E8UL, + [OP_ENDPTCTRL] = 0x0ECUL, +}; + +static int hw_alloc_regmap(struct ci13xxx *udc, bool is_lpm) +{ + int i; + + kfree(udc->hw_bank.regmap); + + udc->hw_bank.regmap = kzalloc((OP_LAST + 1) * sizeof(void *), + GFP_KERNEL); + if (!udc->hw_bank.regmap) + return -ENOMEM; + + for (i = 0; i < OP_ENDPTCTRL; i++) + udc->hw_bank.regmap[i] = + (i <= CAP_LAST ? udc->hw_bank.cap : udc->hw_bank.op) + + (is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]); + + for (; i <= OP_LAST; i++) + udc->hw_bank.regmap[i] = udc->hw_bank.op + + 4 * (i - OP_ENDPTCTRL) + + (is_lpm + ? ci_regs_lpm[OP_ENDPTCTRL] + : ci_regs_nolpm[OP_ENDPTCTRL]); + + return 0; +} + +int hw_device_init(struct ci13xxx *udc, void __iomem *base, + uintptr_t cap_offset) +{ + u32 reg; + + /* bank is a module variable */ + udc->hw_bank.abs = base; + + udc->hw_bank.cap = udc->hw_bank.abs; + udc->hw_bank.cap += cap_offset; + udc->hw_bank.op = udc->hw_bank.cap + ioread8(udc->hw_bank.cap); + + hw_alloc_regmap(udc, false); + reg = hw_read(udc, CAP_HCCPARAMS, HCCPARAMS_LEN) >> + ffs_nr(HCCPARAMS_LEN); + udc->hw_bank.lpm = reg; + hw_alloc_regmap(udc, !!reg); + udc->hw_bank.size = udc->hw_bank.op - udc->hw_bank.abs; + udc->hw_bank.size += OP_LAST; + udc->hw_bank.size /= sizeof(u32); + + reg = hw_read(udc, CAP_DCCPARAMS, DCCPARAMS_DEN) >> + ffs_nr(DCCPARAMS_DEN); + udc->hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */ + + if (udc->hw_ep_max == 0 || udc->hw_ep_max > ENDPT_MAX) + return -ENODEV; + + dev_info(udc->dev, "ChipIdea UDC found, lpm: %d; cap: %p op: %p\n", + udc->hw_bank.lpm, udc->hw_bank.cap, udc->hw_bank.op); + + /* setup lock mode ? */ + + /* ENDPTSETUPSTAT is '0' by default */ + + /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ + + return 0; +} + +/** + * hw_device_reset: resets chip (execute without interruption) + * @ci: the controller + * + * This function returns an error code + */ +int hw_device_reset(struct ci13xxx *ci) +{ + /* should flush & stop before reset */ + hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); + + hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST); + while (hw_read(ci, OP_USBCMD, USBCMD_RST)) + udelay(10); /* not RTOS friendly */ + + + if (ci->udc_driver->notify_event) + ci->udc_driver->notify_event(ci, + CI13XXX_CONTROLLER_RESET_EVENT); + + if (ci->udc_driver->flags & CI13XXX_DISABLE_STREAMING) + hw_write(ci, OP_USBMODE, USBMODE_SDIS, USBMODE_SDIS); + + /* USBMODE should be configured step by step */ + hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); + hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE); + /* HW >= 2.3 */ + hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); + + if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) { + pr_err("cannot enter in device mode"); + pr_err("lpm = %i", ci->hw_bank.lpm); + return -ENODEV; + } + + return 0; +} + +static int __devinit ci_udc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ci13xxx_udc_driver *driver = dev->platform_data; + struct ci13xxx *udc; + struct resource *res; + void __iomem *base; + int ret; + + if (!driver) { + dev_err(dev, "platform data missing\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "missing resource\n"); + return -ENODEV; + } + + base = devm_request_and_ioremap(dev, res); + if (!res) { + dev_err(dev, "can't request and ioremap resource\n"); + return -ENOMEM; + } + + ret = udc_probe(driver, dev, base, &udc); + if (ret) + return ret; + + udc->irq = platform_get_irq(pdev, 0); + if (udc->irq < 0) { + dev_err(dev, "missing IRQ\n"); + ret = -ENODEV; + goto out; + } + + platform_set_drvdata(pdev, udc); + ret = request_irq(udc->irq, udc_irq, IRQF_SHARED, driver->name, udc); + +out: + if (ret) + udc_remove(udc); + + return ret; +} + +static int __devexit ci_udc_remove(struct platform_device *pdev) +{ + struct ci13xxx *udc = platform_get_drvdata(pdev); + + free_irq(udc->irq, udc); + udc_remove(udc); + + return 0; +} + +static struct platform_driver ci_udc_driver = { + .probe = ci_udc_probe, + .remove = __devexit_p(ci_udc_remove), + .driver = { + .name = "ci_udc", + }, +}; + +module_platform_driver(ci_udc_driver); + +MODULE_ALIAS("platform:ci_udc"); +MODULE_ALIAS("platform:ci13xxx"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("David Lopo <dlopo@xxxxxxxxxxxxxxxxx>"); +MODULE_DESCRIPTION("ChipIdea UDC Driver"); diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c new file mode 100644 index 0000000..667d57a --- /dev/null +++ b/drivers/usb/chipidea/debug.c @@ -0,0 +1,825 @@ +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dmapool.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/otg.h> +#include <linux/usb/chipidea.h> + +#include "ci.h" +#include "udc.h" +#include "debug.h" + +/* Interrupt statistics */ +#define ISR_MASK 0x1F +static struct isr_statistics { + u32 test; + u32 ui; + u32 uei; + u32 pci; + u32 uri; + u32 sli; + u32 none; + struct { + u32 cnt; + u32 buf[ISR_MASK+1]; + u32 idx; + } hndl; +} isr_statistics; + +void dbg_interrupt(u32 intmask) +{ + if (!intmask) { + isr_statistics.none++; + return; + } + + isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intmask; + isr_statistics.hndl.idx &= ISR_MASK; + isr_statistics.hndl.cnt++; + + if (USBi_URI & intmask) + isr_statistics.uri++; + if (USBi_PCI & intmask) + isr_statistics.pci++; + if (USBi_UEI & intmask) + isr_statistics.uei++; + if (USBi_UI & intmask) + isr_statistics.ui++; + if (USBi_SLI & intmask) + isr_statistics.sli++; +} + +/** + * hw_register_read: reads all device registers (execute without interruption) + * @buf: destination buffer + * @size: buffer size + * + * This function returns number of registers read + */ +static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size) +{ + unsigned i; + + if (size > udc->hw_bank.size) + size = udc->hw_bank.size; + + for (i = 0; i < size; i++) + buf[i] = hw_read(udc, i * sizeof(u32), ~0); + + return size; +} + +/** + * hw_register_write: writes to register + * @addr: register address + * @data: register value + * + * This function returns an error code + */ +static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data) +{ + /* align */ + addr /= sizeof(u32); + + if (addr >= udc->hw_bank.size) + return -EINVAL; + + /* align */ + addr *= sizeof(u32); + + hw_write(udc, addr, ~0, data); + return 0; +} + +/** + * hw_intr_clear: disables interrupt & clears interrupt status (execute without + * interruption) + * @n: interrupt bit + * + * This function returns an error code + */ +static int hw_intr_clear(struct ci13xxx *udc, int n) +{ + if (n >= REG_BITS) + return -EINVAL; + + hw_write(udc, OP_USBINTR, BIT(n), 0); + hw_write(udc, OP_USBSTS, BIT(n), BIT(n)); + return 0; +} + +/** + * hw_intr_force: enables interrupt & forces interrupt status (execute without + * interruption) + * @n: interrupt bit + * + * This function returns an error code + */ +static int hw_intr_force(struct ci13xxx *udc, int n) +{ + if (n >= REG_BITS) + return -EINVAL; + + hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE); + hw_write(udc, OP_USBINTR, BIT(n), BIT(n)); + hw_write(udc, OP_USBSTS, BIT(n), BIT(n)); + hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, 0); + return 0; +} + +/** + * hw_port_test_get: reads port test mode value + * + * This function returns port test mode value + */ +static u8 hw_port_test_get(struct ci13xxx *udc) +{ + return hw_read(udc, OP_PORTSC, PORTSC_PTC) >> ffs_nr(PORTSC_PTC); +} + +/** + * show_device: prints information about device capabilities and status + * + * Check "device.h" for details + */ +static ssize_t show_device(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct usb_gadget *gadget = &udc->gadget; + int n = 0; + + trace(udc->dev, "%p\n", buf); + if (attr == NULL || buf == NULL) { + dev_err(udc->dev, "[%s] EINVAL\n", __func__); + return 0; + } + + n += scnprintf(buf + n, PAGE_SIZE - n, "speed = %d\n", + gadget->speed); + n += scnprintf(buf + n, PAGE_SIZE - n, "max_speed = %d\n", + gadget->max_speed); + /* TODO: Scheduled for removal in 3.8. */ + n += scnprintf(buf + n, PAGE_SIZE - n, "is_dualspeed = %d\n", + gadget_is_dualspeed(gadget)); + n += scnprintf(buf + n, PAGE_SIZE - n, "is_otg = %d\n", + gadget->is_otg); + n += scnprintf(buf + n, PAGE_SIZE - n, "is_a_peripheral = %d\n", + gadget->is_a_peripheral); + n += scnprintf(buf + n, PAGE_SIZE - n, "b_hnp_enable = %d\n", + gadget->b_hnp_enable); + n += scnprintf(buf + n, PAGE_SIZE - n, "a_hnp_support = %d\n", + gadget->a_hnp_support); + n += scnprintf(buf + n, PAGE_SIZE - n, "a_alt_hnp_support = %d\n", + gadget->a_alt_hnp_support); + n += scnprintf(buf + n, PAGE_SIZE - n, "name = %s\n", + (gadget->name ? gadget->name : "")); + + return n; +} +static DEVICE_ATTR(device, S_IRUSR, show_device, NULL); + +/** + * show_driver: prints information about attached gadget (if any) + * + * Check "device.h" for details + */ +static ssize_t show_driver(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct usb_gadget_driver *driver = udc->driver; + int n = 0; + + trace(udc->dev, "%p\n", buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + if (driver == NULL) + return scnprintf(buf, PAGE_SIZE, + "There is no gadget attached!\n"); + + n += scnprintf(buf + n, PAGE_SIZE - n, "function = %s\n", + (driver->function ? driver->function : "")); + n += scnprintf(buf + n, PAGE_SIZE - n, "max speed = %d\n", + driver->max_speed); + + return n; +} +static DEVICE_ATTR(driver, S_IRUSR, show_driver, NULL); + +/* Maximum event message length */ +#define DBG_DATA_MSG 64UL + +/* Maximum event messages */ +#define DBG_DATA_MAX 128UL + +/* Event buffer descriptor */ +static struct { + char (buf[DBG_DATA_MAX])[DBG_DATA_MSG]; /* buffer */ + unsigned idx; /* index */ + unsigned tty; /* print to console? */ + rwlock_t lck; /* lock */ +} dbg_data = { + .idx = 0, + .tty = 0, + .lck = __RW_LOCK_UNLOCKED(lck) +}; + +/** + * dbg_dec: decrements debug event index + * @idx: buffer index + */ +static void dbg_dec(unsigned *idx) +{ + *idx = (*idx - 1) & (DBG_DATA_MAX-1); +} + +/** + * dbg_inc: increments debug event index + * @idx: buffer index + */ +static void dbg_inc(unsigned *idx) +{ + *idx = (*idx + 1) & (DBG_DATA_MAX-1); +} + +/** + * dbg_print: prints the common part of the event + * @addr: endpoint address + * @name: event name + * @status: status + * @extra: extra information + */ +static void dbg_print(u8 addr, const char *name, int status, const char *extra) +{ + struct timeval tval; + unsigned int stamp; + unsigned long flags; + + write_lock_irqsave(&dbg_data.lck, flags); + + do_gettimeofday(&tval); + stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s */ + stamp = stamp * 1000000 + tval.tv_usec; + + scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG, + "%04X\t? %02X %-7.7s %4i ?\t%s\n", + stamp, addr, name, status, extra); + + dbg_inc(&dbg_data.idx); + + write_unlock_irqrestore(&dbg_data.lck, flags); + + if (dbg_data.tty != 0) + pr_notice("%04X\t? %02X %-7.7s %4i ?\t%s\n", + stamp, addr, name, status, extra); +} + +/** + * dbg_done: prints a DONE event + * @addr: endpoint address + * @td: transfer descriptor + * @status: status + */ +void dbg_done(u8 addr, const u32 token, int status) +{ + char msg[DBG_DATA_MSG]; + + scnprintf(msg, sizeof(msg), "%d %02X", + (int)(token & TD_TOTAL_BYTES) >> ffs_nr(TD_TOTAL_BYTES), + (int)(token & TD_STATUS) >> ffs_nr(TD_STATUS)); + dbg_print(addr, "DONE", status, msg); +} + +/** + * dbg_event: prints a generic event + * @addr: endpoint address + * @name: event name + * @status: status + */ +void dbg_event(u8 addr, const char *name, int status) +{ + if (name != NULL) + dbg_print(addr, name, status, ""); +} + +/* + * dbg_queue: prints a QUEUE event + * @addr: endpoint address + * @req: USB request + * @status: status + */ +void dbg_queue(u8 addr, const struct usb_request *req, int status) +{ + char msg[DBG_DATA_MSG]; + + if (req != NULL) { + scnprintf(msg, sizeof(msg), + "%d %d", !req->no_interrupt, req->length); + dbg_print(addr, "QUEUE", status, msg); + } +} + +/** + * dbg_setup: prints a SETUP event + * @addr: endpoint address + * @req: setup request + */ +void dbg_setup(u8 addr, const struct usb_ctrlrequest *req) +{ + char msg[DBG_DATA_MSG]; + + if (req != NULL) { + scnprintf(msg, sizeof(msg), + "%02X %02X %04X %04X %d", req->bRequestType, + req->bRequest, le16_to_cpu(req->wValue), + le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength)); + dbg_print(addr, "SETUP", 0, msg); + } +} + +/** + * show_events: displays the event buffer + * + * Check "device.h" for details + */ +static ssize_t show_events(struct device *dev, struct device_attribute *attr, + char *buf) +{ + unsigned long flags; + unsigned i, j, n = 0; + + trace(dev->parent, "%p\n", buf); + if (attr == NULL || buf == NULL) { + dev_err(dev->parent, "[%s] EINVAL\n", __func__); + return 0; + } + + read_lock_irqsave(&dbg_data.lck, flags); + + i = dbg_data.idx; + for (dbg_dec(&i); i != dbg_data.idx; dbg_dec(&i)) { + n += strlen(dbg_data.buf[i]); + if (n >= PAGE_SIZE) { + n -= strlen(dbg_data.buf[i]); + break; + } + } + for (j = 0, dbg_inc(&i); j < n; dbg_inc(&i)) + j += scnprintf(buf + j, PAGE_SIZE - j, + "%s", dbg_data.buf[i]); + + read_unlock_irqrestore(&dbg_data.lck, flags); + + return n; +} + +/** + * store_events: configure if events are going to be also printed to console + * + * Check "device.h" for details + */ +static ssize_t store_events(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned tty; + + trace(dev->parent, "[%s] %p, %d\n", __func__, buf, count); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + goto done; + } + + if (sscanf(buf, "%u", &tty) != 1 || tty > 1) { + dev_err(dev, "<1|0>: enable|disable console log\n"); + goto done; + } + + dbg_data.tty = tty; + dev_info(dev, "tty = %u", dbg_data.tty); + + done: + return count; +} +static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events); + +/** + * show_inters: interrupt status, enable status and historic + * + * Check "device.h" for details + */ +static ssize_t show_inters(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + u32 intr; + unsigned i, j, n = 0; + + trace(udc->dev, "%p\n", buf); + if (attr == NULL || buf == NULL) { + dev_err(udc->dev, "[%s] EINVAL\n", __func__); + return 0; + } + + spin_lock_irqsave(&udc->lock, flags); + + /*n += scnprintf(buf + n, PAGE_SIZE - n, + "status = %08x\n", hw_read_intr_status(udc)); + n += scnprintf(buf + n, PAGE_SIZE - n, + "enable = %08x\n", hw_read_intr_enable(udc));*/ + + n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n", + isr_statistics.test); + n += scnprintf(buf + n, PAGE_SIZE - n, "? ui = %d\n", + isr_statistics.ui); + n += scnprintf(buf + n, PAGE_SIZE - n, "? uei = %d\n", + isr_statistics.uei); + n += scnprintf(buf + n, PAGE_SIZE - n, "? pci = %d\n", + isr_statistics.pci); + n += scnprintf(buf + n, PAGE_SIZE - n, "? uri = %d\n", + isr_statistics.uri); + n += scnprintf(buf + n, PAGE_SIZE - n, "? sli = %d\n", + isr_statistics.sli); + n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n", + isr_statistics.none); + n += scnprintf(buf + n, PAGE_SIZE - n, "*hndl = %d\n", + isr_statistics.hndl.cnt); + + for (i = isr_statistics.hndl.idx, j = 0; j <= ISR_MASK; j++, i++) { + i &= ISR_MASK; + intr = isr_statistics.hndl.buf[i]; + + if (USBi_UI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "ui "); + intr &= ~USBi_UI; + if (USBi_UEI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "uei "); + intr &= ~USBi_UEI; + if (USBi_PCI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "pci "); + intr &= ~USBi_PCI; + if (USBi_URI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "uri "); + intr &= ~USBi_URI; + if (USBi_SLI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "sli "); + intr &= ~USBi_SLI; + if (intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "??? "); + if (isr_statistics.hndl.buf[i]) + n += scnprintf(buf + n, PAGE_SIZE - n, "\n"); + } + + spin_unlock_irqrestore(&udc->lock, flags); + + return n; +} + +/** + * store_inters: enable & force or disable an individual interrutps + * (to be used for test purposes only) + * + * Check "device.h" for details + */ +static ssize_t store_inters(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + unsigned en, bit; + + trace(udc->dev, "%p, %d\n", buf, count); + if (attr == NULL || buf == NULL) { + dev_err(udc->dev, "EINVAL\n"); + goto done; + } + + if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) { + dev_err(udc->dev, "<1|0> <bit>: enable|disable interrupt\n"); + goto done; + } + + spin_lock_irqsave(&udc->lock, flags); + if (en) { + if (hw_intr_force(udc, bit)) + dev_err(dev, "invalid bit number\n"); + else + isr_statistics.test++; + } else { + if (hw_intr_clear(udc, bit)) + dev_err(dev, "invalid bit number\n"); + } + spin_unlock_irqrestore(&udc->lock, flags); + + done: + return count; +} +static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters); + +/** + * show_port_test: reads port test mode + * + * Check "device.h" for details + */ +static ssize_t show_port_test(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + unsigned mode; + + trace(udc->dev, "%p\n", buf); + if (attr == NULL || buf == NULL) { + dev_err(udc->dev, "EINVAL\n"); + return 0; + } + + spin_lock_irqsave(&udc->lock, flags); + mode = hw_port_test_get(udc); + spin_unlock_irqrestore(&udc->lock, flags); + + return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode); +} + +/** + * store_port_test: writes port test mode + * + * Check "device.h" for details + */ +static ssize_t store_port_test(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + unsigned mode; + + trace(udc->dev, "%p, %d\n", buf, count); + if (attr == NULL || buf == NULL) { + dev_err(udc->dev, "[%s] EINVAL\n", __func__); + goto done; + } + + if (sscanf(buf, "%u", &mode) != 1) { + dev_err(udc->dev, "<mode>: set port test mode"); + goto done; + } + + spin_lock_irqsave(&udc->lock, flags); + if (hw_port_test_set(udc, mode)) + dev_err(udc->dev, "invalid mode\n"); + spin_unlock_irqrestore(&udc->lock, flags); + + done: + return count; +} +static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR, + show_port_test, store_port_test); + +/** + * show_qheads: DMA contents of all queue heads + * + * Check "device.h" for details + */ +static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + unsigned i, j, n = 0; + + trace(udc->dev, "%p\n", buf); + if (attr == NULL || buf == NULL) { + dev_err(udc->dev, "[%s] EINVAL\n", __func__); + return 0; + } + + spin_lock_irqsave(&udc->lock, flags); + for (i = 0; i < udc->hw_ep_max/2; i++) { + struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i]; + struct ci13xxx_ep *mEpTx = + &udc->ci13xxx_ep[i + udc->hw_ep_max/2]; + n += scnprintf(buf + n, PAGE_SIZE - n, + "EP=%02i: RX=%08X TX=%08X\n", + i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma); + for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) { + n += scnprintf(buf + n, PAGE_SIZE - n, + " %04X: %08X %08X\n", j, + *((u32 *)mEpRx->qh.ptr + j), + *((u32 *)mEpTx->qh.ptr + j)); + } + } + spin_unlock_irqrestore(&udc->lock, flags); + + return n; +} +static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL); + +/** + * show_registers: dumps all registers + * + * Check "device.h" for details + */ +#define DUMP_ENTRIES 512 +static ssize_t show_registers(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + u32 *dump; + unsigned i, k, n = 0; + + trace(udc->dev, "%p\n", buf); + if (attr == NULL || buf == NULL) { + dev_err(udc->dev, "[%s] EINVAL\n", __func__); + return 0; + } + + dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL); + if (!dump) { + dev_err(udc->dev, "%s: out of memory\n", __func__); + return 0; + } + + spin_lock_irqsave(&udc->lock, flags); + k = hw_register_read(udc, dump, DUMP_ENTRIES); + spin_unlock_irqrestore(&udc->lock, flags); + + for (i = 0; i < k; i++) { + n += scnprintf(buf + n, PAGE_SIZE - n, + "reg[0x%04X] = 0x%08X\n", + i * (unsigned)sizeof(u32), dump[i]); + } + kfree(dump); + + return n; +} + +/** + * store_registers: writes value to register address + * + * Check "device.h" for details + */ +static ssize_t store_registers(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long addr, data, flags; + + trace(udc->dev, "%p, %d\n", buf, count); + if (attr == NULL || buf == NULL) { + dev_err(udc->dev, "[%s] EINVAL\n", __func__); + goto done; + } + + if (sscanf(buf, "%li %li", &addr, &data) != 2) { + dev_err(udc->dev, + "<addr> <data>: write data to register address\n"); + goto done; + } + + spin_lock_irqsave(&udc->lock, flags); + if (hw_register_write(udc, addr, data)) + dev_err(udc->dev, "invalid address range\n"); + spin_unlock_irqrestore(&udc->lock, flags); + + done: + return count; +} +static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR, + show_registers, store_registers); + +/** + * show_requests: DMA contents of all requests currently queued (all endpts) + * + * Check "device.h" for details + */ +static ssize_t show_requests(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + struct list_head *ptr = NULL; + struct ci13xxx_req *req = NULL; + unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32); + + trace(udc->dev, "%p\n", buf); + if (attr == NULL || buf == NULL) { + dev_err(udc->dev, "[%s] EINVAL\n", __func__); + return 0; + } + + spin_lock_irqsave(&udc->lock, flags); + for (i = 0; i < udc->hw_ep_max; i++) + list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue) + { + req = list_entry(ptr, struct ci13xxx_req, queue); + + n += scnprintf(buf + n, PAGE_SIZE - n, + "EP=%02i: TD=%08X %s\n", + i % udc->hw_ep_max/2, (u32)req->dma, + ((i < udc->hw_ep_max/2) ? "RX" : "TX")); + + for (j = 0; j < qSize; j++) + n += scnprintf(buf + n, PAGE_SIZE - n, + " %04X: %08X\n", j, + *((u32 *)req->ptr + j)); + } + spin_unlock_irqrestore(&udc->lock, flags); + + return n; +} +static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL); + +/** + * dbg_create_files: initializes the attribute interface + * @dev: device + * + * This function returns an error code + */ +int dbg_create_files(struct device *dev) +{ + int retval = 0; + + if (dev == NULL) + return -EINVAL; + retval = device_create_file(dev, &dev_attr_device); + if (retval) + goto done; + retval = device_create_file(dev, &dev_attr_driver); + if (retval) + goto rm_device; + retval = device_create_file(dev, &dev_attr_events); + if (retval) + goto rm_driver; + retval = device_create_file(dev, &dev_attr_inters); + if (retval) + goto rm_events; + retval = device_create_file(dev, &dev_attr_port_test); + if (retval) + goto rm_inters; + retval = device_create_file(dev, &dev_attr_qheads); + if (retval) + goto rm_port_test; + retval = device_create_file(dev, &dev_attr_registers); + if (retval) + goto rm_qheads; + retval = device_create_file(dev, &dev_attr_requests); + if (retval) + goto rm_registers; + return 0; + + rm_registers: + device_remove_file(dev, &dev_attr_registers); + rm_qheads: + device_remove_file(dev, &dev_attr_qheads); + rm_port_test: + device_remove_file(dev, &dev_attr_port_test); + rm_inters: + device_remove_file(dev, &dev_attr_inters); + rm_events: + device_remove_file(dev, &dev_attr_events); + rm_driver: + device_remove_file(dev, &dev_attr_driver); + rm_device: + device_remove_file(dev, &dev_attr_device); + done: + return retval; +} + +/** + * dbg_remove_files: destroys the attribute interface + * @dev: device + * + * This function returns an error code + */ +int dbg_remove_files(struct device *dev) +{ + if (dev == NULL) + return -EINVAL; + device_remove_file(dev, &dev_attr_requests); + device_remove_file(dev, &dev_attr_registers); + device_remove_file(dev, &dev_attr_qheads); + device_remove_file(dev, &dev_attr_port_test); + device_remove_file(dev, &dev_attr_inters); + device_remove_file(dev, &dev_attr_events); + device_remove_file(dev, &dev_attr_driver); + device_remove_file(dev, &dev_attr_device); + return 0; +} diff --git a/drivers/usb/chipidea/debug.h b/drivers/usb/chipidea/debug.h new file mode 100644 index 0000000..c8504c1 --- /dev/null +++ b/drivers/usb/chipidea/debug.h @@ -0,0 +1,76 @@ +/* + * debug.h - ChipIdea USB driver debug interfaces + * + * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. + * + * Author: David Lopo + * + * 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. + * + * Description: MIPS USB IP core family device controller + * Structures, registers and logging macros + */ + +#ifndef __DRIVERS_USB_CHIPIDEA_DEBUG_H +#define __DRIVERS_USB_CHIPIDEA_DEBUG_H + +#ifdef CONFIG_USB_CHIPIDEA_DEBUG +void dbg_interrupt(u32 intmask); +void dbg_done(u8 addr, const u32 token, int status); +void dbg_event(u8 addr, const char *name, int status); +void dbg_queue(u8 addr, const struct usb_request *req, int status); +void dbg_setup(u8 addr, const struct usb_ctrlrequest *req); +int dbg_create_files(struct device *dev); +int dbg_remove_files(struct device *dev); +#else +static inline void dbg_interrupt(u32 intmask) +{ +} + +static inline void dbg_done(u8 addr, const u32 token, int status) +{ +} + +static inline void dbg_event(u8 addr, const char *name, int status) +{ +} + +static inline void dbg_queue(u8 addr, const struct usb_request *req, int status) +{ +} + +static inline void dbg_setup(u8 addr, const struct usb_ctrlrequest *req) +{ +} + +static inline int dbg_create_files(struct device *dev) +{ + return 0; +} + +static inline int dbg_remove_files(struct device *dev) +{ + return 0; +} +#endif + +/****************************************************************************** + * LOGGING + *****************************************************************************/ +#ifdef TRACE +#define trace(dev, format, args...) \ + do { \ + if (dev == NULL) \ + pr_debug("[%s] " format "\n", __func__, \ + ## args); \ + else \ + dev_printk(KERN_DEBUG, dev, "[%s] " format "\n", \ + __func__, ## args); \ + } while (0) +#else +#define trace(dev, format, args...) do {} while (0) +#endif + +#endif /* __DRIVERS_USB_CHIPIDEA_DEBUG_H */ diff --git a/drivers/usb/chipidea/ci13xxx_udc.c b/drivers/usb/chipidea/udc.c similarity index 61% rename from drivers/usb/chipidea/ci13xxx_udc.c rename to drivers/usb/chipidea/udc.c index cec92a1..71bb0ea 100644 --- a/drivers/usb/chipidea/ci13xxx_udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1,5 +1,5 @@ /* - * ci13xxx_udc.c - MIPS USB IP core family device controller + * udc.h - ChipIdea UDC driver * * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. * @@ -10,46 +10,6 @@ * published by the Free Software Foundation. */ -/* - * Description: MIPS USB IP core family device controller - * Currently it only supports IP part number CI13412 - * - * This driver is composed of several blocks: - * - HW: hardware interface - * - DBG: debug facilities (optional) - * - UTIL: utilities - * - ISR: interrupts handling - * - ENDPT: endpoint operations (Gadget API) - * - GADGET: gadget operations (Gadget API) - * - BUS: bus glue code, bus abstraction layer - * - * Compile Options - * - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities - * - STALL_IN: non-empty bulk-in pipes cannot be halted - * if defined mass storage compliance succeeds but with warnings - * => case 4: Hi > Dn - * => case 5: Hi > Di - * => case 8: Hi <> Do - * if undefined usbtest 13 fails - * - TRACE: enable function tracing (depends on DEBUG) - * - * Main Features - * - Chapter 9 & Mass Storage Compliance with Gadget File Storage - * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined) - * - Normal & LPM support - * - * USBTEST Report - * - OK: 0-12, 13 (STALL_IN defined) & 14 - * - Not Supported: 15 & 16 (ISO) - * - * TODO List - * - OTG - * - Isochronous & Interrupt Traffic - * - Handle requests which spawns into several TDs - * - GET_STATUS(device) - always reports 0 - * - Gadget API (majority of optional features) - * - Suspend & Remote Wakeup - */ #include <linux/delay.h> #include <linux/device.h> #include <linux/dmapool.h> @@ -66,14 +26,11 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/otg.h> +#include <linux/usb/chipidea.h> -#include "ci13xxx_udc.h" - -/****************************************************************************** - * DEFINE - *****************************************************************************/ - -#define DMA_ADDR_INVALID (~(dma_addr_t)0) +#include "ci.h" +#include "udc.h" +#include "debug.h" /* control endpoint description */ static const struct usb_endpoint_descriptor @@ -96,112 +53,6 @@ ctrl_endpt_in_desc = { .wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX), }; -/* Interrupt statistics */ -#define ISR_MASK 0x1F -static struct { - u32 test; - u32 ui; - u32 uei; - u32 pci; - u32 uri; - u32 sli; - u32 none; - struct { - u32 cnt; - u32 buf[ISR_MASK+1]; - u32 idx; - } hndl; -} isr_statistics; - -/** - * ffs_nr: find first (least significant) bit set - * @x: the word to search - * - * This function returns bit number (instead of position) - */ -static int ffs_nr(u32 x) -{ - int n = ffs(x); - - return n ? n-1 : 32; -} - -/****************************************************************************** - * HW block - *****************************************************************************/ - -/* MSM specific */ -#define ABS_AHBBURST (0x0090UL) -#define ABS_AHBMODE (0x0098UL) -/* UDC register map */ -static uintptr_t ci_regs_nolpm[] = { - [CAP_CAPLENGTH] = 0x000UL, - [CAP_HCCPARAMS] = 0x008UL, - [CAP_DCCPARAMS] = 0x024UL, - [CAP_TESTMODE] = 0x038UL, - [OP_USBCMD] = 0x000UL, - [OP_USBSTS] = 0x004UL, - [OP_USBINTR] = 0x008UL, - [OP_DEVICEADDR] = 0x014UL, - [OP_ENDPTLISTADDR] = 0x018UL, - [OP_PORTSC] = 0x044UL, - [OP_DEVLC] = 0x084UL, - [OP_USBMODE] = 0x068UL, - [OP_ENDPTSETUPSTAT] = 0x06CUL, - [OP_ENDPTPRIME] = 0x070UL, - [OP_ENDPTFLUSH] = 0x074UL, - [OP_ENDPTSTAT] = 0x078UL, - [OP_ENDPTCOMPLETE] = 0x07CUL, - [OP_ENDPTCTRL] = 0x080UL, -}; - -static uintptr_t ci_regs_lpm[] = { - [CAP_CAPLENGTH] = 0x000UL, - [CAP_HCCPARAMS] = 0x008UL, - [CAP_DCCPARAMS] = 0x024UL, - [CAP_TESTMODE] = 0x0FCUL, - [OP_USBCMD] = 0x000UL, - [OP_USBSTS] = 0x004UL, - [OP_USBINTR] = 0x008UL, - [OP_DEVICEADDR] = 0x014UL, - [OP_ENDPTLISTADDR] = 0x018UL, - [OP_PORTSC] = 0x044UL, - [OP_DEVLC] = 0x084UL, - [OP_USBMODE] = 0x0C8UL, - [OP_ENDPTSETUPSTAT] = 0x0D8UL, - [OP_ENDPTPRIME] = 0x0DCUL, - [OP_ENDPTFLUSH] = 0x0E0UL, - [OP_ENDPTSTAT] = 0x0E4UL, - [OP_ENDPTCOMPLETE] = 0x0E8UL, - [OP_ENDPTCTRL] = 0x0ECUL, -}; - -static int hw_alloc_regmap(struct ci13xxx *udc, bool is_lpm) -{ - int i; - - kfree(udc->hw_bank.regmap); - - udc->hw_bank.regmap = kzalloc((OP_LAST + 1) * sizeof(void *), - GFP_KERNEL); - if (!udc->hw_bank.regmap) - return -ENOMEM; - - for (i = 0; i < OP_ENDPTCTRL; i++) - udc->hw_bank.regmap[i] = - (i <= CAP_LAST ? udc->hw_bank.cap : udc->hw_bank.op) + - (is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]); - - for (; i <= OP_LAST; i++) - udc->hw_bank.regmap[i] = udc->hw_bank.op + - 4 * (i - OP_ENDPTCTRL) + - (is_lpm - ? ci_regs_lpm[OP_ENDPTCTRL] - : ci_regs_nolpm[OP_ENDPTCTRL]); - - return 0; -} - /** * hw_ep_bit: calculates the bit number * @num: endpoint number @@ -214,7 +65,7 @@ static inline int hw_ep_bit(int num, int dir) return num + (dir ? 16 : 0); } -static int ep_to_bit(struct ci13xxx *udc, int n) +static inline int ep_to_bit(struct ci13xxx *udc, int n) { int fill = 16 - udc->hw_ep_max / 2; @@ -225,145 +76,6 @@ static int ep_to_bit(struct ci13xxx *udc, int n) } /** - * hw_read: reads from a hw register - * @reg: register index - * @mask: bitfield mask - * - * This function returns register contents - */ -static u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask) -{ - return ioread32(udc->hw_bank.regmap[reg]) & mask; -} - -/** - * hw_write: writes to a hw register - * @reg: register index - * @mask: bitfield mask - * @data: new value - */ -static void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask, - u32 data) -{ - if (~mask) - data = (ioread32(udc->hw_bank.regmap[reg]) & ~mask) - | (data & mask); - - iowrite32(data, udc->hw_bank.regmap[reg]); -} - -/** - * hw_test_and_clear: tests & clears a hw register - * @reg: register index - * @mask: bitfield mask - * - * This function returns register contents - */ -static u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg, - u32 mask) -{ - u32 val = ioread32(udc->hw_bank.regmap[reg]) & mask; - - iowrite32(val, udc->hw_bank.regmap[reg]); - return val; -} - -/** - * hw_test_and_write: tests & writes a hw register - * @reg: register index - * @mask: bitfield mask - * @data: new value - * - * This function returns register contents - */ -static u32 hw_test_and_write(struct ci13xxx *udc, enum ci13xxx_regs reg, - u32 mask, u32 data) -{ - u32 val = hw_read(udc, reg, ~0); - - hw_write(udc, reg, mask, data); - return (val & mask) >> ffs_nr(mask); -} - -static int hw_device_init(struct ci13xxx *udc, void __iomem *base, - uintptr_t cap_offset) -{ - u32 reg; - - /* bank is a module variable */ - udc->hw_bank.abs = base; - - udc->hw_bank.cap = udc->hw_bank.abs; - udc->hw_bank.cap += cap_offset; - udc->hw_bank.op = udc->hw_bank.cap + ioread8(udc->hw_bank.cap); - - hw_alloc_regmap(udc, false); - reg = hw_read(udc, CAP_HCCPARAMS, HCCPARAMS_LEN) >> - ffs_nr(HCCPARAMS_LEN); - udc->hw_bank.lpm = reg; - hw_alloc_regmap(udc, !!reg); - udc->hw_bank.size = udc->hw_bank.op - udc->hw_bank.abs; - udc->hw_bank.size += OP_LAST; - udc->hw_bank.size /= sizeof(u32); - - reg = hw_read(udc, CAP_DCCPARAMS, DCCPARAMS_DEN) >> - ffs_nr(DCCPARAMS_DEN); - udc->hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */ - - if (udc->hw_ep_max == 0 || udc->hw_ep_max > ENDPT_MAX) - return -ENODEV; - - dev_info(udc->dev, "ChipIdea UDC found, lpm: %d; cap: %p op: %p\n", - udc->hw_bank.lpm, udc->hw_bank.cap, udc->hw_bank.op); - - /* setup lock mode ? */ - - /* ENDPTSETUPSTAT is '0' by default */ - - /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ - - return 0; -} -/** - * hw_device_reset: resets chip (execute without interruption) - * @base: register base address - * - * This function returns an error code - */ -static int hw_device_reset(struct ci13xxx *udc) -{ - /* should flush & stop before reset */ - hw_write(udc, OP_ENDPTFLUSH, ~0, ~0); - hw_write(udc, OP_USBCMD, USBCMD_RS, 0); - - hw_write(udc, OP_USBCMD, USBCMD_RST, USBCMD_RST); - while (hw_read(udc, OP_USBCMD, USBCMD_RST)) - udelay(10); /* not RTOS friendly */ - - - if (udc->udc_driver->notify_event) - udc->udc_driver->notify_event(udc, - CI13XXX_CONTROLLER_RESET_EVENT); - - if (udc->udc_driver->flags & CI13XXX_DISABLE_STREAMING) - hw_write(udc, OP_USBMODE, USBMODE_SDIS, USBMODE_SDIS); - - /* USBMODE should be configured step by step */ - hw_write(udc, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); - hw_write(udc, OP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE); - /* HW >= 2.3 */ - hw_write(udc, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); - - if (hw_read(udc, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) { - pr_err("cannot enter in device mode"); - pr_err("lpm = %i", udc->hw_bank.lpm); - return -ENODEV; - } - - return 0; -} - -/** * hw_device_state: enables/disables interrupts & starts/stops device (execute * without interruption) * @dma: 0 => disable, !0 => enable and set dma engine @@ -537,42 +249,6 @@ static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value) } /** - * hw_intr_clear: disables interrupt & clears interrupt status (execute without - * interruption) - * @n: interrupt bit - * - * This function returns an error code - */ -static int hw_intr_clear(struct ci13xxx *udc, int n) -{ - if (n >= REG_BITS) - return -EINVAL; - - hw_write(udc, OP_USBINTR, BIT(n), 0); - hw_write(udc, OP_USBSTS, BIT(n), BIT(n)); - return 0; -} - -/** - * hw_intr_force: enables interrupt & forces interrupt status (execute without - * interruption) - * @n: interrupt bit - * - * This function returns an error code - */ -static int hw_intr_force(struct ci13xxx *udc, int n) -{ - if (n >= REG_BITS) - return -EINVAL; - - hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE); - hw_write(udc, OP_USBINTR, BIT(n), BIT(n)); - hw_write(udc, OP_USBSTS, BIT(n), BIT(n)); - hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, 0); - return 0; -} - -/** * hw_is_port_high_speed: test if port is high speed * * This function returns true if high speed port @@ -584,22 +260,12 @@ static int hw_port_is_high_speed(struct ci13xxx *udc) } /** - * hw_port_test_get: reads port test mode value - * - * This function returns port test mode value - */ -static u8 hw_port_test_get(struct ci13xxx *udc) -{ - return hw_read(udc, OP_PORTSC, PORTSC_PTC) >> ffs_nr(PORTSC_PTC); -} - -/** * hw_port_test_set: writes port test mode (execute without interruption) * @mode: new value * * This function returns an error code */ -static int hw_port_test_set(struct ci13xxx *udc, u8 mode) +int hw_port_test_set(struct ci13xxx *udc, u8 mode) { const u8 TEST_MODE_MAX = 7; @@ -631,48 +297,6 @@ static u32 hw_read_intr_status(struct ci13xxx *udc) } /** - * hw_register_read: reads all device registers (execute without interruption) - * @buf: destination buffer - * @size: buffer size - * - * This function returns number of registers read - */ -static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size) -{ - unsigned i; - - if (size > udc->hw_bank.size) - size = udc->hw_bank.size; - - for (i = 0; i < size; i++) - buf[i] = hw_read(udc, i * sizeof(u32), ~0); - - return size; -} - -/** - * hw_register_write: writes to register - * @addr: register address - * @data: register value - * - * This function returns an error code - */ -static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data) -{ - /* align */ - addr /= sizeof(u32); - - if (addr >= udc->hw_bank.size) - return -EINVAL; - - /* align */ - addr *= sizeof(u32); - - hw_write(udc, addr, ~0, data); - return 0; -} - -/** * hw_test_and_clear_complete: test & clear complete status (execute without * interruption) * @n: endpoint number @@ -766,685 +390,6 @@ static int hw_usb_reset(struct ci13xxx *udc) } /****************************************************************************** - * DBG block - *****************************************************************************/ -/** - * show_device: prints information about device capabilities and status - * - * Check "device.h" for details - */ -static ssize_t show_device(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - struct usb_gadget *gadget = &udc->gadget; - int n = 0; - - trace(udc->dev, "%p\n", buf); - if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); - return 0; - } - - n += scnprintf(buf + n, PAGE_SIZE - n, "speed = %d\n", - gadget->speed); - n += scnprintf(buf + n, PAGE_SIZE - n, "max_speed = %d\n", - gadget->max_speed); - /* TODO: Scheduled for removal in 3.8. */ - n += scnprintf(buf + n, PAGE_SIZE - n, "is_dualspeed = %d\n", - gadget_is_dualspeed(gadget)); - n += scnprintf(buf + n, PAGE_SIZE - n, "is_otg = %d\n", - gadget->is_otg); - n += scnprintf(buf + n, PAGE_SIZE - n, "is_a_peripheral = %d\n", - gadget->is_a_peripheral); - n += scnprintf(buf + n, PAGE_SIZE - n, "b_hnp_enable = %d\n", - gadget->b_hnp_enable); - n += scnprintf(buf + n, PAGE_SIZE - n, "a_hnp_support = %d\n", - gadget->a_hnp_support); - n += scnprintf(buf + n, PAGE_SIZE - n, "a_alt_hnp_support = %d\n", - gadget->a_alt_hnp_support); - n += scnprintf(buf + n, PAGE_SIZE - n, "name = %s\n", - (gadget->name ? gadget->name : "")); - - return n; -} -static DEVICE_ATTR(device, S_IRUSR, show_device, NULL); - -/** - * show_driver: prints information about attached gadget (if any) - * - * Check "device.h" for details - */ -static ssize_t show_driver(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - struct usb_gadget_driver *driver = udc->driver; - int n = 0; - - trace(udc->dev, "%p\n", buf); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - return 0; - } - - if (driver == NULL) - return scnprintf(buf, PAGE_SIZE, - "There is no gadget attached!\n"); - - n += scnprintf(buf + n, PAGE_SIZE - n, "function = %s\n", - (driver->function ? driver->function : "")); - n += scnprintf(buf + n, PAGE_SIZE - n, "max speed = %d\n", - driver->max_speed); - - return n; -} -static DEVICE_ATTR(driver, S_IRUSR, show_driver, NULL); - -/* Maximum event message length */ -#define DBG_DATA_MSG 64UL - -/* Maximum event messages */ -#define DBG_DATA_MAX 128UL - -/* Event buffer descriptor */ -static struct { - char (buf[DBG_DATA_MAX])[DBG_DATA_MSG]; /* buffer */ - unsigned idx; /* index */ - unsigned tty; /* print to console? */ - rwlock_t lck; /* lock */ -} dbg_data = { - .idx = 0, - .tty = 0, - .lck = __RW_LOCK_UNLOCKED(lck) -}; - -/** - * dbg_dec: decrements debug event index - * @idx: buffer index - */ -static void dbg_dec(unsigned *idx) -{ - *idx = (*idx - 1) & (DBG_DATA_MAX-1); -} - -/** - * dbg_inc: increments debug event index - * @idx: buffer index - */ -static void dbg_inc(unsigned *idx) -{ - *idx = (*idx + 1) & (DBG_DATA_MAX-1); -} - -/** - * dbg_print: prints the common part of the event - * @addr: endpoint address - * @name: event name - * @status: status - * @extra: extra information - */ -static void dbg_print(u8 addr, const char *name, int status, const char *extra) -{ - struct timeval tval; - unsigned int stamp; - unsigned long flags; - - write_lock_irqsave(&dbg_data.lck, flags); - - do_gettimeofday(&tval); - stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s */ - stamp = stamp * 1000000 + tval.tv_usec; - - scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG, - "%04X\t? %02X %-7.7s %4i ?\t%s\n", - stamp, addr, name, status, extra); - - dbg_inc(&dbg_data.idx); - - write_unlock_irqrestore(&dbg_data.lck, flags); - - if (dbg_data.tty != 0) - pr_notice("%04X\t? %02X %-7.7s %4i ?\t%s\n", - stamp, addr, name, status, extra); -} - -/** - * dbg_done: prints a DONE event - * @addr: endpoint address - * @td: transfer descriptor - * @status: status - */ -static void dbg_done(u8 addr, const u32 token, int status) -{ - char msg[DBG_DATA_MSG]; - - scnprintf(msg, sizeof(msg), "%d %02X", - (int)(token & TD_TOTAL_BYTES) >> ffs_nr(TD_TOTAL_BYTES), - (int)(token & TD_STATUS) >> ffs_nr(TD_STATUS)); - dbg_print(addr, "DONE", status, msg); -} - -/** - * dbg_event: prints a generic event - * @addr: endpoint address - * @name: event name - * @status: status - */ -static void dbg_event(u8 addr, const char *name, int status) -{ - if (name != NULL) - dbg_print(addr, name, status, ""); -} - -/* - * dbg_queue: prints a QUEUE event - * @addr: endpoint address - * @req: USB request - * @status: status - */ -static void dbg_queue(u8 addr, const struct usb_request *req, int status) -{ - char msg[DBG_DATA_MSG]; - - if (req != NULL) { - scnprintf(msg, sizeof(msg), - "%d %d", !req->no_interrupt, req->length); - dbg_print(addr, "QUEUE", status, msg); - } -} - -/** - * dbg_setup: prints a SETUP event - * @addr: endpoint address - * @req: setup request - */ -static void dbg_setup(u8 addr, const struct usb_ctrlrequest *req) -{ - char msg[DBG_DATA_MSG]; - - if (req != NULL) { - scnprintf(msg, sizeof(msg), - "%02X %02X %04X %04X %d", req->bRequestType, - req->bRequest, le16_to_cpu(req->wValue), - le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength)); - dbg_print(addr, "SETUP", 0, msg); - } -} - -/** - * show_events: displays the event buffer - * - * Check "device.h" for details - */ -static ssize_t show_events(struct device *dev, struct device_attribute *attr, - char *buf) -{ - unsigned long flags; - unsigned i, j, n = 0; - - trace(dev->parent, "%p\n", buf); - if (attr == NULL || buf == NULL) { - dev_err(dev->parent, "[%s] EINVAL\n", __func__); - return 0; - } - - read_lock_irqsave(&dbg_data.lck, flags); - - i = dbg_data.idx; - for (dbg_dec(&i); i != dbg_data.idx; dbg_dec(&i)) { - n += strlen(dbg_data.buf[i]); - if (n >= PAGE_SIZE) { - n -= strlen(dbg_data.buf[i]); - break; - } - } - for (j = 0, dbg_inc(&i); j < n; dbg_inc(&i)) - j += scnprintf(buf + j, PAGE_SIZE - j, - "%s", dbg_data.buf[i]); - - read_unlock_irqrestore(&dbg_data.lck, flags); - - return n; -} - -/** - * store_events: configure if events are going to be also printed to console - * - * Check "device.h" for details - */ -static ssize_t store_events(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - unsigned tty; - - trace(dev->parent, "[%s] %p, %d\n", __func__, buf, count); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - goto done; - } - - if (sscanf(buf, "%u", &tty) != 1 || tty > 1) { - dev_err(dev, "<1|0>: enable|disable console log\n"); - goto done; - } - - dbg_data.tty = tty; - dev_info(dev, "tty = %u", dbg_data.tty); - - done: - return count; -} -static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events); - -/** - * show_inters: interrupt status, enable status and historic - * - * Check "device.h" for details - */ -static ssize_t show_inters(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - u32 intr; - unsigned i, j, n = 0; - - trace(udc->dev, "%p\n", buf); - if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); - return 0; - } - - spin_lock_irqsave(&udc->lock, flags); - - n += scnprintf(buf + n, PAGE_SIZE - n, - "status = %08x\n", hw_read_intr_status(udc)); - n += scnprintf(buf + n, PAGE_SIZE - n, - "enable = %08x\n", hw_read_intr_enable(udc)); - - n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n", - isr_statistics.test); - n += scnprintf(buf + n, PAGE_SIZE - n, "? ui = %d\n", - isr_statistics.ui); - n += scnprintf(buf + n, PAGE_SIZE - n, "? uei = %d\n", - isr_statistics.uei); - n += scnprintf(buf + n, PAGE_SIZE - n, "? pci = %d\n", - isr_statistics.pci); - n += scnprintf(buf + n, PAGE_SIZE - n, "? uri = %d\n", - isr_statistics.uri); - n += scnprintf(buf + n, PAGE_SIZE - n, "? sli = %d\n", - isr_statistics.sli); - n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n", - isr_statistics.none); - n += scnprintf(buf + n, PAGE_SIZE - n, "*hndl = %d\n", - isr_statistics.hndl.cnt); - - for (i = isr_statistics.hndl.idx, j = 0; j <= ISR_MASK; j++, i++) { - i &= ISR_MASK; - intr = isr_statistics.hndl.buf[i]; - - if (USBi_UI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "ui "); - intr &= ~USBi_UI; - if (USBi_UEI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "uei "); - intr &= ~USBi_UEI; - if (USBi_PCI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "pci "); - intr &= ~USBi_PCI; - if (USBi_URI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "uri "); - intr &= ~USBi_URI; - if (USBi_SLI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "sli "); - intr &= ~USBi_SLI; - if (intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "??? "); - if (isr_statistics.hndl.buf[i]) - n += scnprintf(buf + n, PAGE_SIZE - n, "\n"); - } - - spin_unlock_irqrestore(&udc->lock, flags); - - return n; -} - -/** - * store_inters: enable & force or disable an individual interrutps - * (to be used for test purposes only) - * - * Check "device.h" for details - */ -static ssize_t store_inters(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - unsigned en, bit; - - trace(udc->dev, "%p, %d\n", buf, count); - if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "EINVAL\n"); - goto done; - } - - if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) { - dev_err(udc->dev, "<1|0> <bit>: enable|disable interrupt\n"); - goto done; - } - - spin_lock_irqsave(&udc->lock, flags); - if (en) { - if (hw_intr_force(udc, bit)) - dev_err(dev, "invalid bit number\n"); - else - isr_statistics.test++; - } else { - if (hw_intr_clear(udc, bit)) - dev_err(dev, "invalid bit number\n"); - } - spin_unlock_irqrestore(&udc->lock, flags); - - done: - return count; -} -static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters); - -/** - * show_port_test: reads port test mode - * - * Check "device.h" for details - */ -static ssize_t show_port_test(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - unsigned mode; - - trace(udc->dev, "%p\n", buf); - if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "EINVAL\n"); - return 0; - } - - spin_lock_irqsave(&udc->lock, flags); - mode = hw_port_test_get(udc); - spin_unlock_irqrestore(&udc->lock, flags); - - return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode); -} - -/** - * store_port_test: writes port test mode - * - * Check "device.h" for details - */ -static ssize_t store_port_test(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - unsigned mode; - - trace(udc->dev, "%p, %d\n", buf, count); - if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); - goto done; - } - - if (sscanf(buf, "%u", &mode) != 1) { - dev_err(udc->dev, "<mode>: set port test mode"); - goto done; - } - - spin_lock_irqsave(&udc->lock, flags); - if (hw_port_test_set(udc, mode)) - dev_err(udc->dev, "invalid mode\n"); - spin_unlock_irqrestore(&udc->lock, flags); - - done: - return count; -} -static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR, - show_port_test, store_port_test); - -/** - * show_qheads: DMA contents of all queue heads - * - * Check "device.h" for details - */ -static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - unsigned i, j, n = 0; - - trace(udc->dev, "%p\n", buf); - if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); - return 0; - } - - spin_lock_irqsave(&udc->lock, flags); - for (i = 0; i < udc->hw_ep_max/2; i++) { - struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i]; - struct ci13xxx_ep *mEpTx = - &udc->ci13xxx_ep[i + udc->hw_ep_max/2]; - n += scnprintf(buf + n, PAGE_SIZE - n, - "EP=%02i: RX=%08X TX=%08X\n", - i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma); - for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) { - n += scnprintf(buf + n, PAGE_SIZE - n, - " %04X: %08X %08X\n", j, - *((u32 *)mEpRx->qh.ptr + j), - *((u32 *)mEpTx->qh.ptr + j)); - } - } - spin_unlock_irqrestore(&udc->lock, flags); - - return n; -} -static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL); - -/** - * show_registers: dumps all registers - * - * Check "device.h" for details - */ -#define DUMP_ENTRIES 512 -static ssize_t show_registers(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - u32 *dump; - unsigned i, k, n = 0; - - trace(udc->dev, "%p\n", buf); - if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); - return 0; - } - - dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL); - if (!dump) { - dev_err(udc->dev, "%s: out of memory\n", __func__); - return 0; - } - - spin_lock_irqsave(&udc->lock, flags); - k = hw_register_read(udc, dump, DUMP_ENTRIES); - spin_unlock_irqrestore(&udc->lock, flags); - - for (i = 0; i < k; i++) { - n += scnprintf(buf + n, PAGE_SIZE - n, - "reg[0x%04X] = 0x%08X\n", - i * (unsigned)sizeof(u32), dump[i]); - } - kfree(dump); - - return n; -} - -/** - * store_registers: writes value to register address - * - * Check "device.h" for details - */ -static ssize_t store_registers(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long addr, data, flags; - - trace(udc->dev, "%p, %d\n", buf, count); - if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); - goto done; - } - - if (sscanf(buf, "%li %li", &addr, &data) != 2) { - dev_err(udc->dev, - "<addr> <data>: write data to register address\n"); - goto done; - } - - spin_lock_irqsave(&udc->lock, flags); - if (hw_register_write(udc, addr, data)) - dev_err(udc->dev, "invalid address range\n"); - spin_unlock_irqrestore(&udc->lock, flags); - - done: - return count; -} -static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR, - show_registers, store_registers); - -/** - * show_requests: DMA contents of all requests currently queued (all endpts) - * - * Check "device.h" for details - */ -static ssize_t show_requests(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - struct list_head *ptr = NULL; - struct ci13xxx_req *req = NULL; - unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32); - - trace(udc->dev, "%p\n", buf); - if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); - return 0; - } - - spin_lock_irqsave(&udc->lock, flags); - for (i = 0; i < udc->hw_ep_max; i++) - list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue) - { - req = list_entry(ptr, struct ci13xxx_req, queue); - - n += scnprintf(buf + n, PAGE_SIZE - n, - "EP=%02i: TD=%08X %s\n", - i % udc->hw_ep_max/2, (u32)req->dma, - ((i < udc->hw_ep_max/2) ? "RX" : "TX")); - - for (j = 0; j < qSize; j++) - n += scnprintf(buf + n, PAGE_SIZE - n, - " %04X: %08X\n", j, - *((u32 *)req->ptr + j)); - } - spin_unlock_irqrestore(&udc->lock, flags); - - return n; -} -static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL); - -/** - * dbg_create_files: initializes the attribute interface - * @dev: device - * - * This function returns an error code - */ -__maybe_unused static int dbg_create_files(struct device *dev) -{ - int retval = 0; - - if (dev == NULL) - return -EINVAL; - retval = device_create_file(dev, &dev_attr_device); - if (retval) - goto done; - retval = device_create_file(dev, &dev_attr_driver); - if (retval) - goto rm_device; - retval = device_create_file(dev, &dev_attr_events); - if (retval) - goto rm_driver; - retval = device_create_file(dev, &dev_attr_inters); - if (retval) - goto rm_events; - retval = device_create_file(dev, &dev_attr_port_test); - if (retval) - goto rm_inters; - retval = device_create_file(dev, &dev_attr_qheads); - if (retval) - goto rm_port_test; - retval = device_create_file(dev, &dev_attr_registers); - if (retval) - goto rm_qheads; - retval = device_create_file(dev, &dev_attr_requests); - if (retval) - goto rm_registers; - return 0; - - rm_registers: - device_remove_file(dev, &dev_attr_registers); - rm_qheads: - device_remove_file(dev, &dev_attr_qheads); - rm_port_test: - device_remove_file(dev, &dev_attr_port_test); - rm_inters: - device_remove_file(dev, &dev_attr_inters); - rm_events: - device_remove_file(dev, &dev_attr_events); - rm_driver: - device_remove_file(dev, &dev_attr_driver); - rm_device: - device_remove_file(dev, &dev_attr_device); - done: - return retval; -} - -/** - * dbg_remove_files: destroys the attribute interface - * @dev: device - * - * This function returns an error code - */ -__maybe_unused static int dbg_remove_files(struct device *dev) -{ - if (dev == NULL) - return -EINVAL; - device_remove_file(dev, &dev_attr_requests); - device_remove_file(dev, &dev_attr_registers); - device_remove_file(dev, &dev_attr_qheads); - device_remove_file(dev, &dev_attr_port_test); - device_remove_file(dev, &dev_attr_inters); - device_remove_file(dev, &dev_attr_events); - device_remove_file(dev, &dev_attr_driver); - device_remove_file(dev, &dev_attr_device); - return 0; -} - -/****************************************************************************** * UTIL block *****************************************************************************/ /** @@ -2717,7 +1662,7 @@ static int ci13xxx_stop(struct usb_gadget *gadget, * This function returns IRQ_HANDLED if the IRQ has been handled * It locks access to registers */ -static irqreturn_t udc_irq(int irq, void *data) +irqreturn_t udc_irq(int irq, void *data) { struct ci13xxx *udc = data; irqreturn_t retval; @@ -2740,18 +1685,14 @@ static irqreturn_t udc_irq(int irq, void *data) } } intr = hw_test_and_clear_intr_active(udc); - if (intr) { - isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intr; - isr_statistics.hndl.idx &= ISR_MASK; - isr_statistics.hndl.cnt++; + dbg_interrupt(intr); + if (intr) { /* order defines priority - do NOT change it */ - if (USBi_URI & intr) { - isr_statistics.uri++; + if (USBi_URI & intr) isr_reset_handler(udc); - } + if (USBi_PCI & intr) { - isr_statistics.pci++; udc->gadget.speed = hw_port_is_high_speed(udc) ? USB_SPEED_HIGH : USB_SPEED_FULL; if (udc->suspended && udc->driver->resume) { @@ -2761,12 +1702,10 @@ static irqreturn_t udc_irq(int irq, void *data) udc->suspended = 0; } } - if (USBi_UEI & intr) - isr_statistics.uei++; - if (USBi_UI & intr) { - isr_statistics.ui++; + + if (USBi_UI & intr) isr_tr_complete_handler(udc); - } + if (USBi_SLI & intr) { if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver->suspend) { @@ -2775,11 +1714,9 @@ static irqreturn_t udc_irq(int irq, void *data) udc->driver->suspend(&udc->gadget); spin_lock(&udc->lock); } - isr_statistics.sli++; } retval = IRQ_HANDLED; } else { - isr_statistics.none++; retval = IRQ_NONE; } spin_unlock(&udc->lock); @@ -2808,8 +1745,8 @@ static void udc_release(struct device *dev) * No interrupts active, the IRQ has not been requested yet * Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask */ -static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, - void __iomem *regs, struct ci13xxx **_udc) +int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, + void __iomem *regs, struct ci13xxx **_udc) { struct ci13xxx *udc; int retval = 0; @@ -2895,9 +1832,7 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, goto put_transceiver; } -#ifdef CONFIG_USB_GADGET_DEBUG_FILES retval = dbg_create_files(&udc->gadget.dev); -#endif if (retval) goto unreg_device; @@ -2926,9 +1861,7 @@ remove_trans: dev_err(dev, "error = %i\n", retval); remove_dbg: -#ifdef CONFIG_USB_GADGET_DEBUG_FILES dbg_remove_files(&udc->gadget.dev); -#endif unreg_device: device_unregister(&udc->gadget.dev); put_transceiver: @@ -2949,7 +1882,7 @@ free_udc: * * No interrupts active, the IRQ has been released */ -static void udc_remove(struct ci13xxx *udc) +void udc_remove(struct ci13xxx *udc) { int i; @@ -2971,84 +1904,9 @@ static void udc_remove(struct ci13xxx *udc) otg_set_peripheral(udc->transceiver->otg, &udc->gadget); usb_put_transceiver(udc->transceiver); } -#ifdef CONFIG_USB_GADGET_DEBUG_FILES dbg_remove_files(&udc->gadget.dev); -#endif device_unregister(&udc->gadget.dev); kfree(udc->hw_bank.regmap); kfree(udc); } - -static int __devinit ci_udc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct ci13xxx_udc_driver *driver = dev->platform_data; - struct ci13xxx *udc; - struct resource *res; - void __iomem *base; - int ret; - - if (!driver) { - dev_err(dev, "platform data missing\n"); - return -ENODEV; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "missing resource\n"); - return -ENODEV; - } - - base = devm_request_and_ioremap(dev, res); - if (!res) { - dev_err(dev, "can't request and ioremap resource\n"); - return -ENOMEM; - } - - ret = udc_probe(driver, dev, base, &udc); - if (ret) - return ret; - - udc->irq = platform_get_irq(pdev, 0); - if (udc->irq < 0) { - dev_err(dev, "missing IRQ\n"); - ret = -ENODEV; - goto out; - } - - platform_set_drvdata(pdev, udc); - ret = request_irq(udc->irq, udc_irq, IRQF_SHARED, driver->name, udc); - -out: - if (ret) - udc_remove(udc); - - return ret; -} - -static int __devexit ci_udc_remove(struct platform_device *pdev) -{ - struct ci13xxx *udc = platform_get_drvdata(pdev); - - free_irq(udc->irq, udc); - udc_remove(udc); - - return 0; -} - -static struct platform_driver ci_udc_driver = { - .probe = ci_udc_probe, - .remove = __devexit_p(ci_udc_remove), - .driver = { - .name = "ci_udc", - }, -}; - -module_platform_driver(ci_udc_driver); - -MODULE_ALIAS("platform:ci_udc"); -MODULE_ALIAS("platform:ci13xxx"); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("David Lopo <dlopo@xxxxxxxxxxxxxxxxx>"); -MODULE_DESCRIPTION("ChipIdea UDC Driver"); diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h new file mode 100644 index 0000000..61a735d --- /dev/null +++ b/drivers/usb/chipidea/udc.h @@ -0,0 +1,119 @@ +/* + * udc.h - ChipIdea UDC structures + * + * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. + * + * Author: David Lopo + * + * 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 __DRIVERS_USB_CHIPIDEA_UDC_H +#define __DRIVERS_USB_CHIPIDEA_UDC_H + +#include <linux/list.h> + +#define ENDPT_MAX 32 +#define CTRL_PAYLOAD_MAX 64 +#define RX 0 /* similar to USB_DIR_OUT but can be used as an index */ +#define TX 1 /* similar to USB_DIR_IN but can be used as an index */ + +/* DMA layout of transfer descriptors */ +struct ci13xxx_td { + /* 0 */ + u32 next; +#define TD_TERMINATE BIT(0) +#define TD_ADDR_MASK (0xFFFFFFEUL << 5) + /* 1 */ + u32 token; +#define TD_STATUS (0x00FFUL << 0) +#define TD_STATUS_TR_ERR BIT(3) +#define TD_STATUS_DT_ERR BIT(5) +#define TD_STATUS_HALTED BIT(6) +#define TD_STATUS_ACTIVE BIT(7) +#define TD_MULTO (0x0003UL << 10) +#define TD_IOC BIT(15) +#define TD_TOTAL_BYTES (0x7FFFUL << 16) + /* 2 */ + u32 page[5]; +#define TD_CURR_OFFSET (0x0FFFUL << 0) +#define TD_FRAME_NUM (0x07FFUL << 0) +#define TD_RESERVED_MASK (0x0FFFUL << 0) +} __attribute__ ((packed)); + +/* DMA layout of queue heads */ +struct ci13xxx_qh { + /* 0 */ + u32 cap; +#define QH_IOS BIT(15) +#define QH_MAX_PKT (0x07FFUL << 16) +#define QH_ZLT BIT(29) +#define QH_MULT (0x0003UL << 30) + /* 1 */ + u32 curr; + /* 2 - 8 */ + struct ci13xxx_td td; + /* 9 */ + u32 RESERVED; + struct usb_ctrlrequest setup; +} __attribute__ ((packed)); + +/* Extension of usb_request */ +struct ci13xxx_req { + struct usb_request req; + unsigned map; + struct list_head queue; + struct ci13xxx_td *ptr; + dma_addr_t dma; + struct ci13xxx_td *zptr; + dma_addr_t zdma; +}; + +/* Extension of usb_ep */ +struct ci13xxx_ep { + struct usb_ep ep; + const struct usb_endpoint_descriptor *desc; + u8 dir; + u8 num; + u8 type; + char name[16]; + struct { + struct list_head queue; + struct ci13xxx_qh *ptr; + dma_addr_t dma; + } qh; + int wedge; + + /* global resources */ + struct ci13xxx *udc; + spinlock_t *lock; + struct device *device; + struct dma_pool *td_pool; +}; + +#ifdef CONFIG_USB_CHIPIDEA_UDC +irqreturn_t udc_irq(int irq, void *data); +int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, + void __iomem *regs, struct ci13xxx **_udc); +void udc_remove(struct ci13xxx *udc); +#else +static inline irqreturn_t udc_irq(int irq, void *data) +{ + return IRQ_NONE; +} + +static inline +int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, + void __iomem *regs, struct ci13xxx **_udc) +{ + return -ENODEV; +} + +static inline void udc_remove(struct ci13xxx *udc) +{ +} +#endif + +#endif /* __DRIVERS_USB_CHIPIDEA_UDC_H */ diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h new file mode 100644 index 0000000..86273ce --- /dev/null +++ b/include/linux/usb/chipidea.h @@ -0,0 +1,27 @@ +/* + * Platform data for the chipidea USB dual role controller + */ + +#ifndef __LINUX_USB_CHIPIDEA_H +#define __LINUX_USB_CHIPIDEA_H + +struct ci13xxx; +struct ci13xxx_udc_driver { + const char *name; + /* offset of the capability registers */ + uintptr_t capoffset; + unsigned long flags; +#define CI13XXX_REGS_SHARED BIT(0) +#define CI13XXX_REQUIRE_TRANSCEIVER BIT(1) +#define CI13XXX_PULLUP_ON_VBUS BIT(2) +#define CI13XXX_DISABLE_STREAMING BIT(3) + +#define CI13XXX_CONTROLLER_RESET_EVENT 0 +#define CI13XXX_CONTROLLER_STOPPED_EVENT 1 + void (*notify_event) (struct ci13xxx *udc, unsigned event); +}; + +/* Default offset of capability registers */ +#define DEF_CAPOFFSET 0x100 + +#endif -- 1.7.10 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html