diff options
Diffstat (limited to 'drivers/usb/chipidea')
| -rw-r--r-- | drivers/usb/chipidea/Kconfig | 20 | ||||
| -rw-r--r-- | drivers/usb/chipidea/Makefile | 4 | ||||
| -rw-r--r-- | drivers/usb/chipidea/bits.h | 71 | ||||
| -rw-r--r-- | drivers/usb/chipidea/ci.h | 204 | ||||
| -rw-r--r-- | drivers/usb/chipidea/ci13xxx_msm.c | 3 | ||||
| -rw-r--r-- | drivers/usb/chipidea/ci13xxx_pci.c | 3 | ||||
| -rw-r--r-- | drivers/usb/chipidea/ci13xxx_udc.h | 248 | ||||
| -rw-r--r-- | drivers/usb/chipidea/core.c | 324 | ||||
| -rw-r--r-- | drivers/usb/chipidea/debug.c | 804 | ||||
| -rw-r--r-- | drivers/usb/chipidea/debug.h | 56 | ||||
| -rw-r--r-- | drivers/usb/chipidea/udc.c (renamed from drivers/usb/chipidea/ci13xxx_udc.c) | 1182 | ||||
| -rw-r--r-- | drivers/usb/chipidea/udc.h | 96 | 
12 files changed, 1597 insertions, 1418 deletions
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig index 71725ddc8f2..553c1976a66 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 e56bedbf9da..a8279aac6a4 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/bits.h b/drivers/usb/chipidea/bits.h new file mode 100644 index 00000000000..5fbff11cf22 --- /dev/null +++ b/drivers/usb/chipidea/bits.h @@ -0,0 +1,71 @@ +/* + * bits.h - register bits of the ChipIdea USB IP core + * + * 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_BITS_H +#define __DRIVERS_USB_CHIPIDEA_BITS_H + +/* HCCPARAMS */ +#define HCCPARAMS_LEN         BIT(17) + +/* DCCPARAMS */ +#define DCCPARAMS_DEN         (0x1F << 0) +#define DCCPARAMS_DC          BIT(7) + +/* TESTMODE */ +#define TESTMODE_FORCE        BIT(0) + +/* USBCMD */ +#define USBCMD_RS             BIT(0) +#define USBCMD_RST            BIT(1) +#define USBCMD_SUTW           BIT(13) +#define USBCMD_ATDTW          BIT(14) + +/* USBSTS & USBINTR */ +#define USBi_UI               BIT(0) +#define USBi_UEI              BIT(1) +#define USBi_PCI              BIT(2) +#define USBi_URI              BIT(6) +#define USBi_SLI              BIT(8) + +/* DEVICEADDR */ +#define DEVICEADDR_USBADRA    BIT(24) +#define DEVICEADDR_USBADR     (0x7FUL << 25) + +/* PORTSC */ +#define PORTSC_FPR            BIT(6) +#define PORTSC_SUSP           BIT(7) +#define PORTSC_HSP            BIT(9) +#define PORTSC_PTC            (0x0FUL << 16) + +/* DEVLC */ +#define DEVLC_PSPD            (0x03UL << 25) +#define    DEVLC_PSPD_HS      (0x02UL << 25) + +/* USBMODE */ +#define USBMODE_CM            (0x03UL <<  0) +#define    USBMODE_CM_IDLE    (0x00UL <<  0) +#define    USBMODE_CM_DEVICE  (0x02UL <<  0) +#define    USBMODE_CM_HOST    (0x03UL <<  0) +#define USBMODE_SLOM          BIT(3) +#define USBMODE_SDIS          BIT(4) + +/* ENDPTCTRL */ +#define ENDPTCTRL_RXS         BIT(0) +#define ENDPTCTRL_RXT         (0x03UL <<  2) +#define ENDPTCTRL_RXR         BIT(6)         /* reserved for port 0 */ +#define ENDPTCTRL_RXE         BIT(7) +#define ENDPTCTRL_TXS         BIT(16) +#define ENDPTCTRL_TXT         (0x03UL << 18) +#define ENDPTCTRL_TXR         BIT(22)        /* reserved for port 0 */ +#define ENDPTCTRL_TXE         BIT(23) + +#endif /* __DRIVERS_USB_CHIPIDEA_BITS_H */ diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h new file mode 100644 index 00000000000..f5b3b8538a3 --- /dev/null +++ b/drivers/usb/chipidea/ci.h @@ -0,0 +1,204 @@ +/* + * ci.h - common structures, functions, and macros of the ChipIdea driver + * + * 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_CI_H +#define __DRIVERS_USB_CHIPIDEA_CI_H + +#include <linux/list.h> +#include <linux/usb/gadget.h> + +/****************************************************************************** + * DEFINE + *****************************************************************************/ +#define DMA_ADDR_INVALID	(~(dma_addr_t)0) +#define CI13XXX_PAGE_SIZE  4096ul /* page size for TD's */ +#define ENDPT_MAX          32 + +/****************************************************************************** + * STRUCTURES + *****************************************************************************/ +/* Extension of usb_ep */ +struct ci13xxx_ep { +	struct usb_ep                          ep; +	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 hw_bank { +	unsigned      lpm;    /* is LPM? */ +	void __iomem *abs;    /* bus map offset */ +	void __iomem *cap;    /* bus map offset + CAP offset */ +	void __iomem *op;     /* bus map offset + OP offset */ +	size_t        size;   /* bank size */ +	void __iomem **regmap; +}; + +/* CI13XXX UDC descriptor & global resources */ +struct ci13xxx { +	spinlock_t		   lock;      /* ctrl register bank access */ +	void __iomem              *regs;      /* registers address space */ + +	struct dma_pool           *qh_pool;   /* DMA pool for queue heads */ +	struct dma_pool           *td_pool;   /* DMA pool for transfer descs */ +	struct usb_request        *status;    /* ep0 status request */ + +	struct device             *dev; +	struct usb_gadget          gadget;     /* USB slave device */ +	struct ci13xxx_ep          ci13xxx_ep[ENDPT_MAX]; /* extended endpts */ +	u32                        ep0_dir;    /* ep0 direction */ +	struct ci13xxx_ep          *ep0out, *ep0in; +	unsigned		   hw_ep_max;  /* number of hw endpoints */ + +	bool			   setaddr; +	u8			   address; +	u8                         remote_wakeup; /* Is remote wakeup feature +							enabled by the host? */ +	u8                         suspended;  /* suspended by the host */ +	u8                         test_mode;  /* the selected test mode */ + +	struct hw_bank             hw_bank; +	int			   irq; +	struct usb_gadget_driver  *driver;     /* 3rd party gadget driver */ +	struct ci13xxx_udc_driver *udc_driver; /* device controller driver */ +	int                        vbus_active; /* is VBUS active */ +	struct usb_phy            *transceiver; /* Transceiver struct */ +}; + +/****************************************************************************** + * REGISTERS + *****************************************************************************/ +/* register size */ +#define REG_BITS   (32) + +/* register indices */ +enum ci13xxx_regs { +	CAP_CAPLENGTH, +	CAP_HCCPARAMS, +	CAP_DCCPARAMS, +	CAP_TESTMODE, +	CAP_LAST = CAP_TESTMODE, +	OP_USBCMD, +	OP_USBSTS, +	OP_USBINTR, +	OP_DEVICEADDR, +	OP_ENDPTLISTADDR, +	OP_PORTSC, +	OP_DEVLC, +	OP_USBMODE, +	OP_ENDPTSETUPSTAT, +	OP_ENDPTPRIME, +	OP_ENDPTFLUSH, +	OP_ENDPTSTAT, +	OP_ENDPTCOMPLETE, +	OP_ENDPTCTRL, +	/* endptctrl1..15 follow */ +	OP_LAST = OP_ENDPTCTRL + ENDPT_MAX / 2, +}; + + +/** + * 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 *ci, u8 mode); + +u8 hw_port_test_get(struct ci13xxx *ci); + +#endif	/* __DRIVERS_USB_CHIPIDEA_CI_H */ diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 418de0e61c5..27427931b68 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 f075ef33834..84e8ab8d4f4 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/ci13xxx_udc.h b/drivers/usb/chipidea/ci13xxx_udc.h deleted file mode 100644 index a8aa1a70dec..00000000000 --- a/drivers/usb/chipidea/ci13xxx_udc.h +++ /dev/null @@ -1,248 +0,0 @@ -/* - * ci13xxx_udc.h - structures, registers, and macros MIPS USB IP core - * - * 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 _CI13XXX_h_ -#define _CI13XXX_h_ - -/****************************************************************************** - * DEFINE - *****************************************************************************/ -#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; -	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 */ -	void __iomem *cap;    /* bus map offset + CAP offset */ -	void __iomem *op;     /* bus map offset + OP offset */ -	size_t        size;   /* bank size */ -	void __iomem **regmap; -}; - -/* CI13XXX UDC descriptor & global resources */ -struct ci13xxx { -	spinlock_t		   lock;      /* ctrl register bank access */ -	void __iomem              *regs;      /* registers address space */ - -	struct dma_pool           *qh_pool;   /* DMA pool for queue heads */ -	struct dma_pool           *td_pool;   /* DMA pool for transfer descs */ -	struct usb_request        *status;    /* ep0 status request */ - -	struct device             *dev; -	struct usb_gadget          gadget;     /* USB slave device */ -	struct ci13xxx_ep          ci13xxx_ep[ENDPT_MAX]; /* extended endpts */ -	u32                        ep0_dir;    /* ep0 direction */ -	struct ci13xxx_ep          *ep0out, *ep0in; -	unsigned		   hw_ep_max;  /* number of hw endpoints */ - -	bool			   setaddr; -	u8			   address; -	u8                         remote_wakeup; /* Is remote wakeup feature -							enabled by the host? */ -	u8                         suspended;  /* suspended by the host */ -	u8                         test_mode;  /* the selected test mode */ - -	struct hw_bank             hw_bank; -	int			   irq; -	struct usb_gadget_driver  *driver;     /* 3rd party gadget driver */ -	struct ci13xxx_udc_driver *udc_driver; /* device controller driver */ -	int                        vbus_active; /* is VBUS active */ -	struct usb_phy            *transceiver; /* Transceiver struct */ -}; - -/****************************************************************************** - * REGISTERS - *****************************************************************************/ -/* Default offset of capability registers */ -#define DEF_CAPOFFSET		0x100 - -/* register size */ -#define REG_BITS   (32) - -/* register indices */ -enum ci13xxx_regs { -	CAP_CAPLENGTH, -	CAP_HCCPARAMS, -	CAP_DCCPARAMS, -	CAP_TESTMODE, -	CAP_LAST = CAP_TESTMODE, -	OP_USBCMD, -	OP_USBSTS, -	OP_USBINTR, -	OP_DEVICEADDR, -	OP_ENDPTLISTADDR, -	OP_PORTSC, -	OP_DEVLC, -	OP_USBMODE, -	OP_ENDPTSETUPSTAT, -	OP_ENDPTPRIME, -	OP_ENDPTFLUSH, -	OP_ENDPTSTAT, -	OP_ENDPTCOMPLETE, -	OP_ENDPTCTRL, -	/* endptctrl1..15 follow */ -	OP_LAST = OP_ENDPTCTRL + ENDPT_MAX / 2, -}; - -/* HCCPARAMS */ -#define HCCPARAMS_LEN         BIT(17) - -/* DCCPARAMS */ -#define DCCPARAMS_DEN         (0x1F << 0) -#define DCCPARAMS_DC          BIT(7) - -/* TESTMODE */ -#define TESTMODE_FORCE        BIT(0) - -/* USBCMD */ -#define USBCMD_RS             BIT(0) -#define USBCMD_RST            BIT(1) -#define USBCMD_SUTW           BIT(13) -#define USBCMD_ATDTW          BIT(14) - -/* USBSTS & USBINTR */ -#define USBi_UI               BIT(0) -#define USBi_UEI              BIT(1) -#define USBi_PCI              BIT(2) -#define USBi_URI              BIT(6) -#define USBi_SLI              BIT(8) - -/* DEVICEADDR */ -#define DEVICEADDR_USBADRA    BIT(24) -#define DEVICEADDR_USBADR     (0x7FUL << 25) - -/* PORTSC */ -#define PORTSC_FPR            BIT(6) -#define PORTSC_SUSP           BIT(7) -#define PORTSC_HSP            BIT(9) -#define PORTSC_PTC            (0x0FUL << 16) - -/* DEVLC */ -#define DEVLC_PSPD            (0x03UL << 25) -#define    DEVLC_PSPD_HS      (0x02UL << 25) - -/* USBMODE */ -#define USBMODE_CM            (0x03UL <<  0) -#define    USBMODE_CM_IDLE    (0x00UL <<  0) -#define    USBMODE_CM_DEVICE  (0x02UL <<  0) -#define    USBMODE_CM_HOST    (0x03UL <<  0) -#define USBMODE_SLOM          BIT(3) -#define USBMODE_SDIS          BIT(4) - -/* ENDPTCTRL */ -#define ENDPTCTRL_RXS         BIT(0) -#define ENDPTCTRL_RXT         (0x03UL <<  2) -#define ENDPTCTRL_RXR         BIT(6)         /* reserved for port 0 */ -#define ENDPTCTRL_RXE         BIT(7) -#define ENDPTCTRL_TXS         BIT(16) -#define ENDPTCTRL_TXT         (0x03UL << 18) -#define ENDPTCTRL_TXR         BIT(22)        /* reserved for port 0 */ -#define ENDPTCTRL_TXE         BIT(23) - -#endif	/* _CI13XXX_h_ */ diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c new file mode 100644 index 00000000000..f6eab327ffe --- /dev/null +++ b/drivers/usb/chipidea/core.c @@ -0,0 +1,324 @@ +/* + * 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 "bits.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; +} + +/** + * hw_port_test_set: writes port test mode (execute without interruption) + * @mode: new value + * + * This function returns an error code + */ +int hw_port_test_set(struct ci13xxx *ci, u8 mode) +{ +	const u8 TEST_MODE_MAX = 7; + +	if (mode > TEST_MODE_MAX) +		return -EINVAL; + +	hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << ffs_nr(PORTSC_PTC)); +	return 0; +} + +/** + * hw_port_test_get: reads port test mode value + * + * This function returns port test mode value + */ +u8 hw_port_test_get(struct ci13xxx *ci) +{ +	return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> ffs_nr(PORTSC_PTC); +} + +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_dbg(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@chipidea.mips.com>"); +MODULE_DESCRIPTION("ChipIdea UDC Driver"); diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c new file mode 100644 index 00000000000..c4b3e15532d --- /dev/null +++ b/drivers/usb/chipidea/debug.c @@ -0,0 +1,804 @@ +#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 "bits.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; +} + +/** + * 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; + +	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; + +	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; + +	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; + +	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; + +	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; + +	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; + +	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; + +	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; + +	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; + +	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; + +	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); + +	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 00000000000..80d96865775 --- /dev/null +++ b/drivers/usb/chipidea/debug.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#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 + +#endif /* __DRIVERS_USB_CHIPIDEA_DEBUG_H */ diff --git a/drivers/usb/chipidea/ci13xxx_udc.c b/drivers/usb/chipidea/udc.c index 819636a1918..6866ef08539 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,12 @@  #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 "bits.h" +#include "debug.h"  /* control endpoint description */  static const struct usb_endpoint_descriptor @@ -96,112 +54,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 +66,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 +77,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_dbg(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 +250,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,33 +261,6 @@ 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) -{ -	const u8 TEST_MODE_MAX = 7; - -	if (mode > TEST_MODE_MAX) -		return -EINVAL; - -	hw_write(udc, OP_PORTSC, PORTSC_PTC, mode << ffs_nr(PORTSC_PTC)); -	return 0; -} - -/**   * hw_read_intr_enable: returns interrupt enable register   *   * This function returns register data @@ -631,48 +281,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,673 +374,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; - -	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; - -	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; - -	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; - -	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; - -	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; - -	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; - -	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; - -	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; - -	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; - -	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; - -	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); - -	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   *****************************************************************************/  /** @@ -2656,7 +1597,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; @@ -2675,18 +1616,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) { @@ -2696,12 +1633,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) { @@ -2710,11 +1645,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); @@ -2742,8 +1675,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; @@ -2824,9 +1757,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; @@ -2855,9 +1786,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: @@ -2878,7 +1807,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; @@ -2900,84 +1829,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@chipidea.mips.com>"); -MODULE_DESCRIPTION("ChipIdea UDC Driver"); diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h new file mode 100644 index 00000000000..82c9e3e772f --- /dev/null +++ b/drivers/usb/chipidea/udc.h @@ -0,0 +1,96 @@ +/* + * 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 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; +}; + +#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 */  |