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:
Alexander Shishkin 2012-05-11 17:25:46 +03:00 committed by Greg Kroah-Hartman
parent bc25a80d12
commit e443b33362
13 changed files with 1624 additions and 1418 deletions

View file

@ -1,10 +1,26 @@
config USB_CHIPIDEA config USB_CHIPIDEA
tristate "ChipIdea Highspeed Dual Role Controller" tristate "ChipIdea Highspeed Dual Role Controller"
depends on USB && USB_GADGET depends on USB
select USB_GADGET_DUALSPEED
help help
Say Y here if your system has a dual role high speed USB Say Y here if your system has a dual role high speed USB
controller based on ChipIdea silicon IP. Currently, only the controller based on ChipIdea silicon IP. Currently, only the
peripheral mode is supported. peripheral mode is supported.
When compiled dynamically, the module will be called ci-hdrc.ko. 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

View file

@ -1,6 +1,8 @@
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o 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),) ifneq ($(CONFIG_PCI),)
obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_pci.o obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_pci.o

View file

@ -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 */

204
drivers/usb/chipidea/ci.h Normal file
View file

@ -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 */

View file

@ -11,8 +11,9 @@
#include <linux/usb/msm_hsusb_hw.h> #include <linux/usb/msm_hsusb_hw.h>
#include <linux/usb/ulpi.h> #include <linux/usb/ulpi.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/usb/chipidea.h>
#include "ci13xxx_udc.h" #include "ci.h"
#define MSM_USB_BASE (udc->regs) #define MSM_USB_BASE (udc->regs)

View file

@ -15,8 +15,7 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/usb/chipidea.h>
#include "ci13xxx_udc.h"
/* driver name */ /* driver name */
#define UDC_DRIVER_NAME "ci13xxx_pci" #define UDC_DRIVER_NAME "ci13xxx_pci"

View file

@ -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_ */

324
drivers/usb/chipidea/core.c Normal file
View file

@ -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");

View file

@ -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;
}

View file

@ -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 */

View file

@ -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 */

View file

@ -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