mirror of https://gitee.com/openkylin/linux.git
usb: chipidea: split the driver code into units
Split the driver into the following parts: * core -- resources, register access, capabilities, etc; * udc -- device controller functionality; * debug -- logging events. Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
bc25a80d12
commit
e443b33362
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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_ */
|
|
@ -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");
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
|
@ -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
|
Loading…
Reference in New Issue