Commit e443b333 authored by Alexander Shishkin's avatar Alexander Shishkin Committed by Greg Kroah-Hartman

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: default avatarAlexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent bc25a80d
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
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
......
/*
* 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 */
/* /*
* ci13xxx_udc.h - structures, registers, and macros MIPS USB IP core * ci.h - common structures, functions, and macros of the ChipIdea driver
* *
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
* *
...@@ -8,77 +8,24 @@ ...@@ -8,77 +8,24 @@
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*
* Description: MIPS USB IP core family device controller
* Structures, registers and logging macros
*/ */
#ifndef _CI13XXX_h_ #ifndef __DRIVERS_USB_CHIPIDEA_CI_H
#define _CI13XXX_h_ #define __DRIVERS_USB_CHIPIDEA_CI_H
#include <linux/list.h>
#include <linux/usb/gadget.h>
/****************************************************************************** /******************************************************************************
* DEFINE * DEFINE
*****************************************************************************/ *****************************************************************************/
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
#define CI13XXX_PAGE_SIZE 4096ul /* page size for TD's */ #define CI13XXX_PAGE_SIZE 4096ul /* page size for TD's */
#define ENDPT_MAX 32 #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 * 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 */ /* Extension of usb_ep */
struct ci13xxx_ep { struct ci13xxx_ep {
struct usb_ep ep; struct usb_ep ep;
...@@ -100,22 +47,6 @@ struct ci13xxx_ep { ...@@ -100,22 +47,6 @@ struct ci13xxx_ep {
struct dma_pool *td_pool; 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 { struct hw_bank {
unsigned lpm; /* is LPM? */ unsigned lpm; /* is LPM? */
void __iomem *abs; /* bus map offset */ void __iomem *abs; /* bus map offset */
...@@ -159,9 +90,6 @@ struct ci13xxx { ...@@ -159,9 +90,6 @@ struct ci13xxx {
/****************************************************************************** /******************************************************************************
* REGISTERS * REGISTERS
*****************************************************************************/ *****************************************************************************/
/* Default offset of capability registers */
#define DEF_CAPOFFSET 0x100
/* register size */ /* register size */
#define REG_BITS (32) #define REG_BITS (32)
...@@ -190,59 +118,87 @@ enum ci13xxx_regs { ...@@ -190,59 +118,87 @@ enum ci13xxx_regs {
OP_LAST = OP_ENDPTCTRL + ENDPT_MAX / 2, OP_LAST = OP_ENDPTCTRL + ENDPT_MAX / 2,
}; };
/* HCCPARAMS */
#define HCCPARAMS_LEN BIT(17) /**
* ffs_nr: find first (least significant) bit set
/* DCCPARAMS */ * @x: the word to search
#define DCCPARAMS_DEN (0x1F << 0) *
#define DCCPARAMS_DC BIT(7) * This function returns bit number (instead of position)
*/
/* TESTMODE */ static inline int ffs_nr(u32 x)
#define TESTMODE_FORCE BIT(0) {
int n = ffs(x);
/* USBCMD */
#define USBCMD_RS BIT(0) return n ? n-1 : 32;
#define USBCMD_RST BIT(1) }
#define USBCMD_SUTW BIT(13)
#define USBCMD_ATDTW BIT(14) /**
* hw_read: reads from a hw register
/* USBSTS & USBINTR */ * @reg: register index
#define USBi_UI BIT(0) * @mask: bitfield mask
#define USBi_UEI BIT(1) *
#define USBi_PCI BIT(2) * This function returns register contents
#define USBi_URI BIT(6) */
#define USBi_SLI BIT(8) static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask)
{
/* DEVICEADDR */ return ioread32(udc->hw_bank.regmap[reg]) & mask;
#define DEVICEADDR_USBADRA BIT(24) }
#define DEVICEADDR_USBADR (0x7FUL << 25)
/**
/* PORTSC */ * hw_write: writes to a hw register
#define PORTSC_FPR BIT(6) * @reg: register index
#define PORTSC_SUSP BIT(7) * @mask: bitfield mask
#define PORTSC_HSP BIT(9) * @data: new value
#define PORTSC_PTC (0x0FUL << 16) */
static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg,
/* DEVLC */ u32 mask, u32 data)
#define DEVLC_PSPD (0x03UL << 25) {
#define DEVLC_PSPD_HS (0x02UL << 25) if (~mask)
data = (ioread32(udc->hw_bank.regmap[reg]) & ~mask)
/* USBMODE */ | (data & mask);
#define USBMODE_CM (0x03UL << 0)
#define USBMODE_CM_IDLE (0x00UL << 0) iowrite32(data, udc->hw_bank.regmap[reg]);
#define USBMODE_CM_DEVICE (0x02UL << 0) }
#define USBMODE_CM_HOST (0x03UL << 0)
#define USBMODE_SLOM BIT(3) /**
#define USBMODE_SDIS BIT(4) * hw_test_and_clear: tests & clears a hw register
* @reg: register index
/* ENDPTCTRL */ * @mask: bitfield mask
#define ENDPTCTRL_RXS BIT(0) *
#define ENDPTCTRL_RXT (0x03UL << 2) * This function returns register contents
#define ENDPTCTRL_RXR BIT(6) /* reserved for port 0 */ */
#define ENDPTCTRL_RXE BIT(7) static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg,
#define ENDPTCTRL_TXS BIT(16) u32 mask)
#define ENDPTCTRL_TXT (0x03UL << 18) {
#define ENDPTCTRL_TXR BIT(22) /* reserved for port 0 */ u32 val = ioread32(udc->hw_bank.regmap[reg]) & mask;
#define ENDPTCTRL_TXE BIT(23)
iowrite32(val, udc->hw_bank.regmap[reg]);
#endif /* _CI13XXX_h_ */ 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 @@ ...@@ -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)
......
...@@ -15,8 +15,7 @@ ...@@ -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"
......
/*
* 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");
#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;
}
/*
* 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 */
/* /*
* ci13xxx_udc.c - MIPS USB IP core family device controller * udc.h - ChipIdea UDC driver
* *
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
* *
...@@ -10,46 +10,6 @@ ...@@ -10,46 +10,6 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
/*
* Description: MIPS USB IP core family device controller
* Currently it only supports IP part number CI13412
*
* This driver is composed of several blocks:
* - HW: hardware interface
* - DBG: debug facilities (optional)
* - UTIL: utilities
* - ISR: interrupts handling
* - ENDPT: endpoint operations (Gadget API)
* - GADGET: gadget operations (Gadget API)
* - BUS: bus glue code, bus abstraction layer
*
* Compile Options
* - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities
* - STALL_IN: non-empty bulk-in pipes cannot be halted
* if defined mass storage compliance succeeds but with warnings
* => case 4: Hi > Dn
* => case 5: Hi > Di
* => case 8: Hi <> Do
* if undefined usbtest 13 fails
* - TRACE: enable function tracing (depends on DEBUG)
*
* Main Features
* - Chapter 9 & Mass Storage Compliance with Gadget File Storage
* - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined)
* - Normal & LPM support
*
* USBTEST Report
* - OK: 0-12, 13 (STALL_IN defined) & 14
* - Not Supported: 15 & 16 (ISO)
*
* TODO List
* - OTG
* - Isochronous & Interrupt Traffic
* - Handle requests which spawns into several TDs
* - GET_STATUS(device) - always reports 0
* - Gadget API (majority of optional features)
* - Suspend & Remote Wakeup
*/
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/dmapool.h> #include <linux/dmapool.h>
...@@ -66,14 +26,12 @@ ...@@ -66,14 +26,12 @@
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/usb/otg.h> #include <linux/usb/otg.h>
#include <linux/usb/chipidea.h>
#include "ci13xxx_udc.h" #include "ci.h"
#include "udc.h"
/****************************************************************************** #include "bits.h"
* DEFINE #include "debug.h"
*****************************************************************************/
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
/* control endpoint description */ /* control endpoint description */
static const struct usb_endpoint_descriptor static const struct usb_endpoint_descriptor
...@@ -96,112 +54,6 @@ ctrl_endpt_in_desc = { ...@@ -96,112 +54,6 @@ ctrl_endpt_in_desc = {
.wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX), .wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX),
}; };
/* Interrupt statistics */
#define ISR_MASK 0x1F
static struct {
u32 test;
u32 ui;
u32 uei;
u32 pci;
u32 uri;
u32 sli;
u32 none;
struct {
u32 cnt;
u32 buf[ISR_MASK+1];
u32 idx;
} hndl;
} isr_statistics;
/**
* ffs_nr: find first (least significant) bit set
* @x: the word to search
*
* This function returns bit number (instead of position)
*/
static int ffs_nr(u32 x)
{
int n = ffs(x);
return n ? n-1 : 32;
}
/******************************************************************************
* HW block
*****************************************************************************/
/* MSM specific */
#define ABS_AHBBURST (0x0090UL)
#define ABS_AHBMODE (0x0098UL)
/* UDC register map */
static uintptr_t ci_regs_nolpm[] = {
[CAP_CAPLENGTH] = 0x000UL,
[CAP_HCCPARAMS] = 0x008UL,
[CAP_DCCPARAMS] = 0x024UL,
[CAP_TESTMODE] = 0x038UL,
[OP_USBCMD] = 0x000UL,
[OP_USBSTS] = 0x004UL,
[OP_USBINTR] = 0x008UL,
[OP_DEVICEADDR] = 0x014UL,
[OP_ENDPTLISTADDR] = 0x018UL,
[OP_PORTSC] = 0x044UL,
[OP_DEVLC] = 0x084UL,
[OP_USBMODE] = 0x068UL,
[OP_ENDPTSETUPSTAT] = 0x06CUL,
[OP_ENDPTPRIME] = 0x070UL,
[OP_ENDPTFLUSH] = 0x074UL,
[OP_ENDPTSTAT] = 0x078UL,
[OP_ENDPTCOMPLETE] = 0x07CUL,
[OP_ENDPTCTRL] = 0x080UL,
};
static uintptr_t ci_regs_lpm[] = {
[CAP_CAPLENGTH] = 0x000UL,
[CAP_HCCPARAMS] = 0x008UL,
[CAP_DCCPARAMS] = 0x024UL,
[CAP_TESTMODE] = 0x0FCUL,
[OP_USBCMD] = 0x000UL,
[OP_USBSTS] = 0x004UL,
[OP_USBINTR] = 0x008UL,
[OP_DEVICEADDR] = 0x014UL,
[OP_ENDPTLISTADDR] = 0x018UL,
[OP_PORTSC] = 0x044UL,
[OP_DEVLC] = 0x084UL,
[OP_USBMODE] = 0x0C8UL,
[OP_ENDPTSETUPSTAT] = 0x0D8UL,
[OP_ENDPTPRIME] = 0x0DCUL,
[OP_ENDPTFLUSH] = 0x0E0UL,
[OP_ENDPTSTAT] = 0x0E4UL,
[OP_ENDPTCOMPLETE] = 0x0E8UL,
[OP_ENDPTCTRL] = 0x0ECUL,
};
static int hw_alloc_regmap(struct ci13xxx *udc, bool is_lpm)
{
int i;
kfree(udc->hw_bank.regmap);
udc->hw_bank.regmap = kzalloc((OP_LAST + 1) * sizeof(void *),
GFP_KERNEL);
if (!udc->hw_bank.regmap)
return -ENOMEM;
for (i = 0; i < OP_ENDPTCTRL; i++)
udc->hw_bank.regmap[i] =
(i <= CAP_LAST ? udc->hw_bank.cap : udc->hw_bank.op) +
(is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]);
for (; i <= OP_LAST; i++)
udc->hw_bank.regmap[i] = udc->hw_bank.op +
4 * (i - OP_ENDPTCTRL) +
(is_lpm
? ci_regs_lpm[OP_ENDPTCTRL]
: ci_regs_nolpm[OP_ENDPTCTRL]);
return 0;
}
/** /**
* hw_ep_bit: calculates the bit number * hw_ep_bit: calculates the bit number
* @num: endpoint number * @num: endpoint number
...@@ -214,7 +66,7 @@ static inline int hw_ep_bit(int num, int dir) ...@@ -214,7 +66,7 @@ static inline int hw_ep_bit(int num, int dir)
return num + (dir ? 16 : 0); return num + (dir ? 16 : 0);
} }
static int ep_to_bit(struct ci13xxx *udc, int n) static inline int ep_to_bit(struct ci13xxx *udc, int n)
{ {
int fill = 16 - udc->hw_ep_max / 2; int fill = 16 - udc->hw_ep_max / 2;
...@@ -224,145 +76,6 @@ static int ep_to_bit(struct ci13xxx *udc, int n) ...@@ -224,145 +76,6 @@ static int ep_to_bit(struct ci13xxx *udc, int n)
return n; return n;
} }
/**
* hw_read: reads from a hw register
* @reg: register index
* @mask: bitfield mask
*
* This function returns register contents
*/
static u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask)
{
return ioread32(udc->hw_bank.regmap[reg]) & mask;
}
/**
* hw_write: writes to a hw register
* @reg: register index
* @mask: bitfield mask
* @data: new value
*/
static void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask,
u32 data)
{
if (~mask)
data = (ioread32(udc->hw_bank.regmap[reg]) & ~mask)
| (data & mask);
iowrite32(data, udc->hw_bank.regmap[reg]);
}
/**
* hw_test_and_clear: tests & clears a hw register
* @reg: register index
* @mask: bitfield mask
*
* This function returns register contents
*/
static u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg,
u32 mask)
{
u32 val = ioread32(udc->hw_bank.regmap[reg]) & mask;
iowrite32(val, udc->hw_bank.regmap[reg]);
return val;
}
/**
* hw_test_and_write: tests & writes a hw register
* @reg: register index
* @mask: bitfield mask
* @data: new value
*
* This function returns register contents
*/
static u32 hw_test_and_write(struct ci13xxx *udc, enum ci13xxx_regs reg,
u32 mask, u32 data)
{
u32 val = hw_read(udc, reg, ~0);
hw_write(udc, reg, mask, data);
return (val & mask) >> ffs_nr(mask);
}
static int hw_device_init(struct ci13xxx *udc, void __iomem *base,
uintptr_t cap_offset)
{
u32 reg;
/* bank is a module variable */
udc->hw_bank.abs = base;
udc->hw_bank.cap = udc->hw_bank.abs;
udc->hw_bank.cap += cap_offset;
udc->hw_bank.op = udc->hw_bank.cap + ioread8(udc->hw_bank.cap);
hw_alloc_regmap(udc, false);
reg = hw_read(udc, CAP_HCCPARAMS, HCCPARAMS_LEN) >>
ffs_nr(HCCPARAMS_LEN);
udc->hw_bank.lpm = reg;
hw_alloc_regmap(udc, !!reg);
udc->hw_bank.size = udc->hw_bank.op - udc->hw_bank.abs;
udc->hw_bank.size += OP_LAST;
udc->hw_bank.size /= sizeof(u32);
reg = hw_read(udc, CAP_DCCPARAMS, DCCPARAMS_DEN) >>
ffs_nr(DCCPARAMS_DEN);
udc->hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */
if (udc->hw_ep_max == 0 || udc->hw_ep_max > ENDPT_MAX)
return -ENODEV;
dev_dbg(udc->dev, "ChipIdea UDC found, lpm: %d; cap: %p op: %p\n",
udc->hw_bank.lpm, udc->hw_bank.cap, udc->hw_bank.op);
/* setup lock mode ? */
/* ENDPTSETUPSTAT is '0' by default */
/* HCSPARAMS.bf.ppc SHOULD BE zero for device */
return 0;
}
/**
* hw_device_reset: resets chip (execute without interruption)
* @base: register base address
*
* This function returns an error code
*/
static int hw_device_reset(struct ci13xxx *udc)
{
/* should flush & stop before reset */
hw_write(udc, OP_ENDPTFLUSH, ~0, ~0);
hw_write(udc, OP_USBCMD, USBCMD_RS, 0);
hw_write(udc, OP_USBCMD, USBCMD_RST, USBCMD_RST);
while (hw_read(udc, OP_USBCMD, USBCMD_RST))
udelay(10); /* not RTOS friendly */
if (udc->udc_driver->notify_event)
udc->udc_driver->notify_event(udc,
CI13XXX_CONTROLLER_RESET_EVENT);
if (udc->udc_driver->flags & CI13XXX_DISABLE_STREAMING)
hw_write(udc, OP_USBMODE, USBMODE_SDIS, USBMODE_SDIS);
/* USBMODE should be configured step by step */
hw_write(udc, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
hw_write(udc, OP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE);
/* HW >= 2.3 */
hw_write(udc, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM);
if (hw_read(udc, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) {
pr_err("cannot enter in device mode");
pr_err("lpm = %i", udc->hw_bank.lpm);
return -ENODEV;
}
return 0;
}
/** /**
* hw_device_state: enables/disables interrupts & starts/stops device (execute * hw_device_state: enables/disables interrupts & starts/stops device (execute
* without interruption) * without interruption)
...@@ -536,42 +249,6 @@ static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value) ...@@ -536,42 +249,6 @@ static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value)
return 0; return 0;
} }
/**
* hw_intr_clear: disables interrupt & clears interrupt status (execute without
* interruption)
* @n: interrupt bit
*
* This function returns an error code
*/
static int hw_intr_clear(struct ci13xxx *udc, int n)
{
if (n >= REG_BITS)
return -EINVAL;
hw_write(udc, OP_USBINTR, BIT(n), 0);
hw_write(udc, OP_USBSTS, BIT(n), BIT(n));
return 0;
}
/**
* hw_intr_force: enables interrupt & forces interrupt status (execute without
* interruption)
* @n: interrupt bit
*
* This function returns an error code
*/
static int hw_intr_force(struct ci13xxx *udc, int n)
{
if (n >= REG_BITS)
return -EINVAL;
hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE);
hw_write(udc, OP_USBINTR, BIT(n), BIT(n));
hw_write(udc, OP_USBSTS, BIT(n), BIT(n));
hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, 0);
return 0;
}
/** /**
* hw_is_port_high_speed: test if port is high speed * hw_is_port_high_speed: test if port is high speed
* *
...@@ -583,33 +260,6 @@ static int hw_port_is_high_speed(struct ci13xxx *udc) ...@@ -583,33 +260,6 @@ static int hw_port_is_high_speed(struct ci13xxx *udc)
hw_read(udc, OP_PORTSC, PORTSC_HSP); hw_read(udc, OP_PORTSC, PORTSC_HSP);
} }
/**
* hw_port_test_get: reads port test mode value
*
* This function returns port test mode value
*/
static u8 hw_port_test_get(struct ci13xxx *udc)
{
return hw_read(udc, OP_PORTSC, PORTSC_PTC) >> ffs_nr(PORTSC_PTC);
}
/**
* hw_port_test_set: writes port test mode (execute without interruption)
* @mode: new value
*
* This function returns an error code
*/
static int hw_port_test_set(struct ci13xxx *udc, u8 mode)
{
const u8 TEST_MODE_MAX = 7;
if (mode > TEST_MODE_MAX)
return -EINVAL;
hw_write(udc, OP_PORTSC, PORTSC_PTC, mode << ffs_nr(PORTSC_PTC));
return 0;
}
/** /**
* hw_read_intr_enable: returns interrupt enable register * hw_read_intr_enable: returns interrupt enable register
* *
...@@ -630,48 +280,6 @@ static u32 hw_read_intr_status(struct ci13xxx *udc) ...@@ -630,48 +280,6 @@ static u32 hw_read_intr_status(struct ci13xxx *udc)
return hw_read(udc, OP_USBSTS, ~0); return hw_read(udc, OP_USBSTS, ~0);
} }
/**
* hw_register_read: reads all device registers (execute without interruption)
* @buf: destination buffer
* @size: buffer size
*
* This function returns number of registers read
*/
static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size)
{
unsigned i;
if (size > udc->hw_bank.size)
size = udc->hw_bank.size;
for (i = 0; i < size; i++)
buf[i] = hw_read(udc, i * sizeof(u32), ~0);
return size;
}
/**
* hw_register_write: writes to register
* @addr: register address
* @data: register value
*
* This function returns an error code
*/
static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data)
{
/* align */
addr /= sizeof(u32);
if (addr >= udc->hw_bank.size)
return -EINVAL;
/* align */
addr *= sizeof(u32);
hw_write(udc, addr, ~0, data);
return 0;
}
/** /**
* hw_test_and_clear_complete: test & clear complete status (execute without * hw_test_and_clear_complete: test & clear complete status (execute without
* interruption) * interruption)
...@@ -765,673 +373,6 @@ static int hw_usb_reset(struct ci13xxx *udc) ...@@ -765,673 +373,6 @@ static int hw_usb_reset(struct ci13xxx *udc)
return 0; return 0;
} }
/******************************************************************************
* DBG block
*****************************************************************************/
/**
* show_device: prints information about device capabilities and status
*
* Check "device.h" for details
*/
static ssize_t show_device(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
struct usb_gadget *gadget = &udc->gadget;
int n = 0;
if (attr == NULL || buf == NULL) {
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
return 0;
}
n += scnprintf(buf + n, PAGE_SIZE - n, "speed = %d\n",
gadget->speed);
n += scnprintf(buf + n, PAGE_SIZE - n, "max_speed = %d\n",
gadget->max_speed);
/* TODO: Scheduled for removal in 3.8. */
n += scnprintf(buf + n, PAGE_SIZE - n, "is_dualspeed = %d\n",
gadget_is_dualspeed(gadget));
n += scnprintf(buf + n, PAGE_SIZE - n, "is_otg = %d\n",
gadget->is_otg);
n += scnprintf(buf + n, PAGE_SIZE - n, "is_a_peripheral = %d\n",
gadget->is_a_peripheral);
n += scnprintf(buf + n, PAGE_SIZE - n, "b_hnp_enable = %d\n",
gadget->b_hnp_enable);
n += scnprintf(buf + n, PAGE_SIZE - n, "a_hnp_support = %d\n",
gadget->a_hnp_support);
n += scnprintf(buf + n, PAGE_SIZE - n, "a_alt_hnp_support = %d\n",
gadget->a_alt_hnp_support);
n += scnprintf(buf + n, PAGE_SIZE - n, "name = %s\n",
(gadget->name ? gadget->name : ""));
return n;
}
static DEVICE_ATTR(device, S_IRUSR, show_device, NULL);
/**
* show_driver: prints information about attached gadget (if any)
*
* Check "device.h" for details
*/
static ssize_t show_driver(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
struct usb_gadget_driver *driver = udc->driver;
int n = 0;
if (attr == NULL || buf == NULL) {
dev_err(dev, "[%s] EINVAL\n", __func__);
return 0;
}
if (driver == NULL)
return scnprintf(buf, PAGE_SIZE,
"There is no gadget attached!\n");
n += scnprintf(buf + n, PAGE_SIZE - n, "function = %s\n",
(driver->function ? driver->function : ""));
n += scnprintf(buf + n, PAGE_SIZE - n, "max speed = %d\n",
driver->max_speed);
return n;
}
static DEVICE_ATTR(driver, S_IRUSR, show_driver, NULL);
/* Maximum event message length */
#define DBG_DATA_MSG 64UL
/* Maximum event messages */
#define DBG_DATA_MAX 128UL
/* Event buffer descriptor */
static struct {
char (buf[DBG_DATA_MAX])[DBG_DATA_MSG]; /* buffer */
unsigned idx; /* index */
unsigned tty; /* print to console? */
rwlock_t lck; /* lock */
} dbg_data = {
.idx = 0,
.tty = 0,
.lck = __RW_LOCK_UNLOCKED(lck)
};
/**
* dbg_dec: decrements debug event index
* @idx: buffer index
*/
static void dbg_dec(unsigned *idx)
{
*idx = (*idx - 1) & (DBG_DATA_MAX-1);
}
/**
* dbg_inc: increments debug event index
* @idx: buffer index
*/
static void dbg_inc(unsigned *idx)
{
*idx = (*idx + 1) & (DBG_DATA_MAX-1);
}
/**
* dbg_print: prints the common part of the event
* @addr: endpoint address
* @name: event name
* @status: status
* @extra: extra information
*/
static void dbg_print(u8 addr, const char *name, int status, const char *extra)
{
struct timeval tval;
unsigned int stamp;
unsigned long flags;
write_lock_irqsave(&dbg_data.lck, flags);
do_gettimeofday(&tval);
stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s */
stamp = stamp * 1000000 + tval.tv_usec;
scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG,
"%04X\t? %02X %-7.7s %4i ?\t%s\n",
stamp, addr, name, status, extra);
dbg_inc(&dbg_data.idx);
write_unlock_irqrestore(&dbg_data.lck, flags);
if (dbg_data.tty != 0)
pr_notice("%04X\t? %02X %-7.7s %4i ?\t%s\n",
stamp, addr, name, status, extra);
}
/**
* dbg_done: prints a DONE event
* @addr: endpoint address
* @td: transfer descriptor
* @status: status
*/
static void dbg_done(u8 addr, const u32 token, int status)
{
char msg[DBG_DATA_MSG];
scnprintf(msg, sizeof(msg), "%d %02X",
(int)(token & TD_TOTAL_BYTES) >> ffs_nr(TD_TOTAL_BYTES),
(int)(token & TD_STATUS) >> ffs_nr(TD_STATUS));
dbg_print(addr, "DONE", status, msg);
}
/**
* dbg_event: prints a generic event
* @addr: endpoint address
* @name: event name
* @status: status
*/
static void dbg_event(u8 addr, const char *name, int status)
{
if (name != NULL)
dbg_print(addr, name, status, "");
}
/*
* dbg_queue: prints a QUEUE event
* @addr: endpoint address
* @req: USB request
* @status: status
*/
static void dbg_queue(u8 addr, const struct usb_request *req, int status)
{
char msg[DBG_DATA_MSG];
if (req != NULL) {
scnprintf(msg, sizeof(msg),
"%d %d", !req->no_interrupt, req->length);
dbg_print(addr, "QUEUE", status, msg);
}
}
/**
* dbg_setup: prints a SETUP event
* @addr: endpoint address
* @req: setup request
*/
static void dbg_setup(u8 addr, const struct usb_ctrlrequest *req)
{
char msg[DBG_DATA_MSG];
if (req != NULL) {
scnprintf(msg, sizeof(msg),
"%02X %02X %04X %04X %d", req->bRequestType,
req->bRequest, le16_to_cpu(req->wValue),
le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength));
dbg_print(addr, "SETUP", 0, msg);
}
}
/**
* show_events: displays the event buffer
*
* Check "device.h" for details
*/
static ssize_t show_events(struct device *dev, struct device_attribute *attr,
char *buf)
{
unsigned long flags;
unsigned i, j, n = 0;
if (attr == NULL || buf == NULL) {
dev_err(dev->parent, "[%s] EINVAL\n", __func__);
return 0;
}
read_lock_irqsave(&dbg_data.lck, flags);
i = dbg_data.idx;
for (dbg_dec(&i); i != dbg_data.idx; dbg_dec(&i)) {
n += strlen(dbg_data.buf[i]);
if (n >= PAGE_SIZE) {
n -= strlen(dbg_data.buf[i]);
break;
}
}
for (j = 0, dbg_inc(&i); j < n; dbg_inc(&i))
j += scnprintf(buf + j, PAGE_SIZE - j,
"%s", dbg_data.buf[i]);
read_unlock_irqrestore(&dbg_data.lck, flags);
return n;
}
/**
* store_events: configure if events are going to be also printed to console
*
* Check "device.h" for details
*/
static ssize_t store_events(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned tty;
if (attr == NULL || buf == NULL) {
dev_err(dev, "[%s] EINVAL\n", __func__);
goto done;
}
if (sscanf(buf, "%u", &tty) != 1 || tty > 1) {
dev_err(dev, "<1|0>: enable|disable console log\n");
goto done;
}
dbg_data.tty = tty;
dev_info(dev, "tty = %u", dbg_data.tty);
done:
return count;
}
static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events);
/**
* show_inters: interrupt status, enable status and historic
*
* Check "device.h" for details
*/
static ssize_t show_inters(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
unsigned long flags;
u32 intr;
unsigned i, j, n = 0;
if (attr == NULL || buf == NULL) {
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
return 0;
}
spin_lock_irqsave(&udc->lock, flags);
n += scnprintf(buf + n, PAGE_SIZE - n,
"status = %08x\n", hw_read_intr_status(udc));
n += scnprintf(buf + n, PAGE_SIZE - n,
"enable = %08x\n", hw_read_intr_enable(udc));
n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n",
isr_statistics.test);
n += scnprintf(buf + n, PAGE_SIZE - n, "? ui = %d\n",
isr_statistics.ui);
n += scnprintf(buf + n, PAGE_SIZE - n, "? uei = %d\n",
isr_statistics.uei);
n += scnprintf(buf + n, PAGE_SIZE - n, "? pci = %d\n",
isr_statistics.pci);
n += scnprintf(buf + n, PAGE_SIZE - n, "? uri = %d\n",
isr_statistics.uri);
n += scnprintf(buf + n, PAGE_SIZE - n, "? sli = %d\n",
isr_statistics.sli);
n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n",
isr_statistics.none);
n += scnprintf(buf + n, PAGE_SIZE - n, "*hndl = %d\n",
isr_statistics.hndl.cnt);
for (i = isr_statistics.hndl.idx, j = 0; j <= ISR_MASK; j++, i++) {
i &= ISR_MASK;
intr = isr_statistics.hndl.buf[i];
if (USBi_UI & intr)
n += scnprintf(buf + n, PAGE_SIZE - n, "ui ");
intr &= ~USBi_UI;
if (USBi_UEI & intr)
n += scnprintf(buf + n, PAGE_SIZE - n, "uei ");
intr &= ~USBi_UEI;
if (USBi_PCI & intr)
n += scnprintf(buf + n, PAGE_SIZE - n, "pci ");
intr &= ~USBi_PCI;
if (USBi_URI & intr)
n += scnprintf(buf + n, PAGE_SIZE - n, "uri ");
intr &= ~USBi_URI;
if (USBi_SLI & intr)
n += scnprintf(buf + n, PAGE_SIZE - n, "sli ");
intr &= ~USBi_SLI;
if (intr)
n += scnprintf(buf + n, PAGE_SIZE - n, "??? ");
if (isr_statistics.hndl.buf[i])
n += scnprintf(buf + n, PAGE_SIZE - n, "\n");
}
spin_unlock_irqrestore(&udc->lock, flags);
return n;
}
/**
* store_inters: enable & force or disable an individual interrutps
* (to be used for test purposes only)
*
* Check "device.h" for details
*/
static ssize_t store_inters(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
unsigned long flags;
unsigned en, bit;
if (attr == NULL || buf == NULL) {
dev_err(udc->dev, "EINVAL\n");
goto done;
}
if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) {
dev_err(udc->dev, "<1|0> <bit>: enable|disable interrupt\n");
goto done;
}
spin_lock_irqsave(&udc->lock, flags);
if (en) {
if (hw_intr_force(udc, bit))
dev_err(dev, "invalid bit number\n");
else
isr_statistics.test++;
} else {
if (hw_intr_clear(udc, bit))
dev_err(dev, "invalid bit number\n");
}
spin_unlock_irqrestore(&udc->lock, flags);
done:
return count;
}
static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters);
/**
* show_port_test: reads port test mode
*
* Check "device.h" for details
*/
static ssize_t show_port_test(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
unsigned long flags;
unsigned mode;
if (attr == NULL || buf == NULL) {
dev_err(udc->dev, "EINVAL\n");
return 0;
}
spin_lock_irqsave(&udc->lock, flags);
mode = hw_port_test_get(udc);
spin_unlock_irqrestore(&udc->lock, flags);
return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode);
}
/**
* store_port_test: writes port test mode
*
* Check "device.h" for details
*/
static ssize_t store_port_test(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
unsigned long flags;
unsigned mode;
if (attr == NULL || buf == NULL) {
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
goto done;
}
if (sscanf(buf, "%u", &mode) != 1) {
dev_err(udc->dev, "<mode>: set port test mode");
goto done;
}
spin_lock_irqsave(&udc->lock, flags);
if (hw_port_test_set(udc, mode))
dev_err(udc->dev, "invalid mode\n");
spin_unlock_irqrestore(&udc->lock, flags);
done:
return count;
}
static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR,
show_port_test, store_port_test);
/**
* show_qheads: DMA contents of all queue heads
*
* Check "device.h" for details
*/
static ssize_t show_qheads(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
unsigned long flags;
unsigned i, j, n = 0;
if (attr == NULL || buf == NULL) {
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
return 0;
}
spin_lock_irqsave(&udc->lock, flags);
for (i = 0; i < udc->hw_ep_max/2; i++) {
struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i];
struct ci13xxx_ep *mEpTx =
&udc->ci13xxx_ep[i + udc->hw_ep_max/2];
n += scnprintf(buf + n, PAGE_SIZE - n,
"EP=%02i: RX=%08X TX=%08X\n",
i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma);
for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) {
n += scnprintf(buf + n, PAGE_SIZE - n,
" %04X: %08X %08X\n", j,
*((u32 *)mEpRx->qh.ptr + j),
*((u32 *)mEpTx->qh.ptr + j));
}
}
spin_unlock_irqrestore(&udc->lock, flags);
return n;
}
static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL);
/**
* show_registers: dumps all registers
*
* Check "device.h" for details
*/
#define DUMP_ENTRIES 512
static ssize_t show_registers(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
unsigned long flags;
u32 *dump;
unsigned i, k, n = 0;
if (attr == NULL || buf == NULL) {
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
return 0;
}
dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL);
if (!dump) {
dev_err(udc->dev, "%s: out of memory\n", __func__);
return 0;
}
spin_lock_irqsave(&udc->lock, flags);
k = hw_register_read(udc, dump, DUMP_ENTRIES);
spin_unlock_irqrestore(&udc->lock, flags);
for (i = 0; i < k; i++) {
n += scnprintf(buf + n, PAGE_SIZE - n,
"reg[0x%04X] = 0x%08X\n",
i * (unsigned)sizeof(u32), dump[i]);
}
kfree(dump);
return n;
}
/**
* store_registers: writes value to register address
*
* Check "device.h" for details
*/
static ssize_t store_registers(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
unsigned long addr, data, flags;
if (attr == NULL || buf == NULL) {
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
goto done;
}
if (sscanf(buf, "%li %li", &addr, &data) != 2) {
dev_err(udc->dev,
"<addr> <data>: write data to register address\n");
goto done;
}
spin_lock_irqsave(&udc->lock, flags);
if (hw_register_write(udc, addr, data))
dev_err(udc->dev, "invalid address range\n");
spin_unlock_irqrestore(&udc->lock, flags);
done:
return count;
}
static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR,
show_registers, store_registers);
/**
* show_requests: DMA contents of all requests currently queued (all endpts)
*
* Check "device.h" for details
*/
static ssize_t show_requests(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
unsigned long flags;
struct list_head *ptr = NULL;
struct ci13xxx_req *req = NULL;
unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32);
if (attr == NULL || buf == NULL) {
dev_err(udc->dev, "[%s] EINVAL\n", __func__);
return 0;
}
spin_lock_irqsave(&udc->lock, flags);
for (i = 0; i < udc->hw_ep_max; i++)
list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue)
{
req = list_entry(ptr, struct ci13xxx_req, queue);
n += scnprintf(buf + n, PAGE_SIZE - n,
"EP=%02i: TD=%08X %s\n",
i % udc->hw_ep_max/2, (u32)req->dma,
((i < udc->hw_ep_max/2) ? "RX" : "TX"));
for (j = 0; j < qSize; j++)
n += scnprintf(buf + n, PAGE_SIZE - n,
" %04X: %08X\n", j,
*((u32 *)req->ptr + j));
}
spin_unlock_irqrestore(&udc->lock, flags);
return n;
}
static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL);
/**
* dbg_create_files: initializes the attribute interface
* @dev: device
*
* This function returns an error code
*/
__maybe_unused static int dbg_create_files(struct device *dev)
{
int retval = 0;
if (dev == NULL)
return -EINVAL;
retval = device_create_file(dev, &dev_attr_device);
if (retval)
goto done;
retval = device_create_file(dev, &dev_attr_driver);
if (retval)
goto rm_device;
retval = device_create_file(dev, &dev_attr_events);
if (retval)
goto rm_driver;
retval = device_create_file(dev, &dev_attr_inters);
if (retval)
goto rm_events;
retval = device_create_file(dev, &dev_attr_port_test);
if (retval)
goto rm_inters;
retval = device_create_file(dev, &dev_attr_qheads);
if (retval)
goto rm_port_test;
retval = device_create_file(dev, &dev_attr_registers);
if (retval)
goto rm_qheads;
retval = device_create_file(dev, &dev_attr_requests);
if (retval)
goto rm_registers;
return 0;
rm_registers:
device_remove_file(dev, &dev_attr_registers);
rm_qheads:
device_remove_file(dev, &dev_attr_qheads);
rm_port_test:
device_remove_file(dev, &dev_attr_port_test);
rm_inters:
device_remove_file(dev, &dev_attr_inters);
rm_events:
device_remove_file(dev, &dev_attr_events);
rm_driver:
device_remove_file(dev, &dev_attr_driver);
rm_device:
device_remove_file(dev, &dev_attr_device);
done:
return retval;
}
/**
* dbg_remove_files: destroys the attribute interface
* @dev: device
*
* This function returns an error code
*/
__maybe_unused static int dbg_remove_files(struct device *dev)
{
if (dev == NULL)
return -EINVAL;
device_remove_file(dev, &dev_attr_requests);
device_remove_file(dev, &dev_attr_registers);
device_remove_file(dev, &dev_attr_qheads);
device_remove_file(dev, &dev_attr_port_test);
device_remove_file(dev, &dev_attr_inters);
device_remove_file(dev, &dev_attr_events);
device_remove_file(dev, &dev_attr_driver);
device_remove_file(dev, &dev_attr_device);
return 0;
}
/****************************************************************************** /******************************************************************************
* UTIL block * UTIL block
*****************************************************************************/ *****************************************************************************/
...@@ -2656,7 +1597,7 @@ static int ci13xxx_stop(struct usb_gadget *gadget, ...@@ -2656,7 +1597,7 @@ static int ci13xxx_stop(struct usb_gadget *gadget,
* This function returns IRQ_HANDLED if the IRQ has been handled * This function returns IRQ_HANDLED if the IRQ has been handled
* It locks access to registers * It locks access to registers
*/ */
static irqreturn_t udc_irq(int irq, void *data) irqreturn_t udc_irq(int irq, void *data)
{ {
struct ci13xxx *udc = data; struct ci13xxx *udc = data;
irqreturn_t retval; irqreturn_t retval;
...@@ -2675,18 +1616,14 @@ static irqreturn_t udc_irq(int irq, void *data) ...@@ -2675,18 +1616,14 @@ static irqreturn_t udc_irq(int irq, void *data)
} }
} }
intr = hw_test_and_clear_intr_active(udc); intr = hw_test_and_clear_intr_active(udc);
if (intr) { dbg_interrupt(intr);
isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intr;
isr_statistics.hndl.idx &= ISR_MASK;
isr_statistics.hndl.cnt++;
if (intr) {
/* order defines priority - do NOT change it */ /* order defines priority - do NOT change it */
if (USBi_URI & intr) { if (USBi_URI & intr)
isr_statistics.uri++;
isr_reset_handler(udc); isr_reset_handler(udc);
}
if (USBi_PCI & intr) { if (USBi_PCI & intr) {
isr_statistics.pci++;
udc->gadget.speed = hw_port_is_high_speed(udc) ? udc->gadget.speed = hw_port_is_high_speed(udc) ?
USB_SPEED_HIGH : USB_SPEED_FULL; USB_SPEED_HIGH : USB_SPEED_FULL;
if (udc->suspended && udc->driver->resume) { if (udc->suspended && udc->driver->resume) {
...@@ -2696,12 +1633,10 @@ static irqreturn_t udc_irq(int irq, void *data) ...@@ -2696,12 +1633,10 @@ static irqreturn_t udc_irq(int irq, void *data)
udc->suspended = 0; udc->suspended = 0;
} }
} }
if (USBi_UEI & intr)
isr_statistics.uei++; if (USBi_UI & intr)
if (USBi_UI & intr) {
isr_statistics.ui++;
isr_tr_complete_handler(udc); isr_tr_complete_handler(udc);
}
if (USBi_SLI & intr) { if (USBi_SLI & intr) {
if (udc->gadget.speed != USB_SPEED_UNKNOWN && if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
udc->driver->suspend) { udc->driver->suspend) {
...@@ -2710,11 +1645,9 @@ static irqreturn_t udc_irq(int irq, void *data) ...@@ -2710,11 +1645,9 @@ static irqreturn_t udc_irq(int irq, void *data)
udc->driver->suspend(&udc->gadget); udc->driver->suspend(&udc->gadget);
spin_lock(&udc->lock); spin_lock(&udc->lock);
} }
isr_statistics.sli++;
} }
retval = IRQ_HANDLED; retval = IRQ_HANDLED;
} else { } else {
isr_statistics.none++;
retval = IRQ_NONE; retval = IRQ_NONE;
} }
spin_unlock(&udc->lock); spin_unlock(&udc->lock);
...@@ -2742,7 +1675,7 @@ static void udc_release(struct device *dev) ...@@ -2742,7 +1675,7 @@ static void udc_release(struct device *dev)
* No interrupts active, the IRQ has not been requested yet * No interrupts active, the IRQ has not been requested yet
* Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask * Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask
*/ */
static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
void __iomem *regs, struct ci13xxx **_udc) void __iomem *regs, struct ci13xxx **_udc)
{ {
struct ci13xxx *udc; struct ci13xxx *udc;
...@@ -2824,9 +1757,7 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, ...@@ -2824,9 +1757,7 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
goto put_transceiver; goto put_transceiver;
} }
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
retval = dbg_create_files(&udc->gadget.dev); retval = dbg_create_files(&udc->gadget.dev);
#endif
if (retval) if (retval)
goto unreg_device; goto unreg_device;
...@@ -2855,9 +1786,7 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, ...@@ -2855,9 +1786,7 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
dev_err(dev, "error = %i\n", retval); dev_err(dev, "error = %i\n", retval);
remove_dbg: remove_dbg:
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
dbg_remove_files(&udc->gadget.dev); dbg_remove_files(&udc->gadget.dev);
#endif
unreg_device: unreg_device:
device_unregister(&udc->gadget.dev); device_unregister(&udc->gadget.dev);
put_transceiver: put_transceiver:
...@@ -2878,7 +1807,7 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, ...@@ -2878,7 +1807,7 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
* *
* No interrupts active, the IRQ has been released * No interrupts active, the IRQ has been released
*/ */
static void udc_remove(struct ci13xxx *udc) void udc_remove(struct ci13xxx *udc)
{ {
int i; int i;
...@@ -2900,84 +1829,9 @@ static void udc_remove(struct ci13xxx *udc) ...@@ -2900,84 +1829,9 @@ static void udc_remove(struct ci13xxx *udc)
otg_set_peripheral(udc->transceiver->otg, &udc->gadget); otg_set_peripheral(udc->transceiver->otg, &udc->gadget);
usb_put_transceiver(udc->transceiver); usb_put_transceiver(udc->transceiver);
} }
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
dbg_remove_files(&udc->gadget.dev); dbg_remove_files(&udc->gadget.dev);
#endif
device_unregister(&udc->gadget.dev); device_unregister(&udc->gadget.dev);
kfree(udc->hw_bank.regmap); kfree(udc->hw_bank.regmap);
kfree(udc); kfree(udc);
} }
static int __devinit ci_udc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ci13xxx_udc_driver *driver = dev->platform_data;
struct ci13xxx *udc;
struct resource *res;
void __iomem *base;
int ret;
if (!driver) {
dev_err(dev, "platform data missing\n");
return -ENODEV;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "missing resource\n");
return -ENODEV;
}
base = devm_request_and_ioremap(dev, res);
if (!res) {
dev_err(dev, "can't request and ioremap resource\n");
return -ENOMEM;
}
ret = udc_probe(driver, dev, base, &udc);
if (ret)
return ret;
udc->irq = platform_get_irq(pdev, 0);
if (udc->irq < 0) {
dev_err(dev, "missing IRQ\n");
ret = -ENODEV;
goto out;
}
platform_set_drvdata(pdev, udc);
ret = request_irq(udc->irq, udc_irq, IRQF_SHARED, driver->name, udc);
out:
if (ret)
udc_remove(udc);
return ret;
}
static int __devexit ci_udc_remove(struct platform_device *pdev)
{
struct ci13xxx *udc = platform_get_drvdata(pdev);
free_irq(udc->irq, udc);
udc_remove(udc);
return 0;
}
static struct platform_driver ci_udc_driver = {
.probe = ci_udc_probe,
.remove = __devexit_p(ci_udc_remove),
.driver = {
.name = "ci_udc",
},
};
module_platform_driver(ci_udc_driver);
MODULE_ALIAS("platform:ci_udc");
MODULE_ALIAS("platform:ci13xxx");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>");
MODULE_DESCRIPTION("ChipIdea UDC Driver");
/*
* 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 */
/*
* 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment