|
@@ -1,5 +1,5 @@
|
|
|
/*
|
|
|
- * ci13xxx_udc.c - MIPS USB IP core family device controller
|
|
|
+ * udc.h - ChipIdea UDC driver
|
|
|
*
|
|
|
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
|
|
|
*
|
|
@@ -10,46 +10,6 @@
|
|
|
* published by the Free Software Foundation.
|
|
|
*/
|
|
|
|
|
|
-/*
|
|
|
- * Description: MIPS USB IP core family device controller
|
|
|
- * Currently it only supports IP part number CI13412
|
|
|
- *
|
|
|
- * This driver is composed of several blocks:
|
|
|
- * - HW: hardware interface
|
|
|
- * - DBG: debug facilities (optional)
|
|
|
- * - UTIL: utilities
|
|
|
- * - ISR: interrupts handling
|
|
|
- * - ENDPT: endpoint operations (Gadget API)
|
|
|
- * - GADGET: gadget operations (Gadget API)
|
|
|
- * - BUS: bus glue code, bus abstraction layer
|
|
|
- *
|
|
|
- * Compile Options
|
|
|
- * - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities
|
|
|
- * - STALL_IN: non-empty bulk-in pipes cannot be halted
|
|
|
- * if defined mass storage compliance succeeds but with warnings
|
|
|
- * => case 4: Hi > Dn
|
|
|
- * => case 5: Hi > Di
|
|
|
- * => case 8: Hi <> Do
|
|
|
- * if undefined usbtest 13 fails
|
|
|
- * - TRACE: enable function tracing (depends on DEBUG)
|
|
|
- *
|
|
|
- * Main Features
|
|
|
- * - Chapter 9 & Mass Storage Compliance with Gadget File Storage
|
|
|
- * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined)
|
|
|
- * - Normal & LPM support
|
|
|
- *
|
|
|
- * USBTEST Report
|
|
|
- * - OK: 0-12, 13 (STALL_IN defined) & 14
|
|
|
- * - Not Supported: 15 & 16 (ISO)
|
|
|
- *
|
|
|
- * TODO List
|
|
|
- * - OTG
|
|
|
- * - Isochronous & Interrupt Traffic
|
|
|
- * - Handle requests which spawns into several TDs
|
|
|
- * - GET_STATUS(device) - always reports 0
|
|
|
- * - Gadget API (majority of optional features)
|
|
|
- * - Suspend & Remote Wakeup
|
|
|
- */
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/device.h>
|
|
|
#include <linux/dmapool.h>
|
|
@@ -66,14 +26,12 @@
|
|
|
#include <linux/usb/ch9.h>
|
|
|
#include <linux/usb/gadget.h>
|
|
|
#include <linux/usb/otg.h>
|
|
|
+#include <linux/usb/chipidea.h>
|
|
|
|
|
|
-#include "ci13xxx_udc.h"
|
|
|
-
|
|
|
-/******************************************************************************
|
|
|
- * DEFINE
|
|
|
- *****************************************************************************/
|
|
|
-
|
|
|
-#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
|
|
+#include "ci.h"
|
|
|
+#include "udc.h"
|
|
|
+#include "bits.h"
|
|
|
+#include "debug.h"
|
|
|
|
|
|
/* control endpoint description */
|
|
|
static const struct usb_endpoint_descriptor
|
|
@@ -96,112 +54,6 @@ ctrl_endpt_in_desc = {
|
|
|
.wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX),
|
|
|
};
|
|
|
|
|
|
-/* Interrupt statistics */
|
|
|
-#define ISR_MASK 0x1F
|
|
|
-static struct {
|
|
|
- u32 test;
|
|
|
- u32 ui;
|
|
|
- u32 uei;
|
|
|
- u32 pci;
|
|
|
- u32 uri;
|
|
|
- u32 sli;
|
|
|
- u32 none;
|
|
|
- struct {
|
|
|
- u32 cnt;
|
|
|
- u32 buf[ISR_MASK+1];
|
|
|
- u32 idx;
|
|
|
- } hndl;
|
|
|
-} isr_statistics;
|
|
|
-
|
|
|
-/**
|
|
|
- * ffs_nr: find first (least significant) bit set
|
|
|
- * @x: the word to search
|
|
|
- *
|
|
|
- * This function returns bit number (instead of position)
|
|
|
- */
|
|
|
-static int ffs_nr(u32 x)
|
|
|
-{
|
|
|
- int n = ffs(x);
|
|
|
-
|
|
|
- return n ? n-1 : 32;
|
|
|
-}
|
|
|
-
|
|
|
-/******************************************************************************
|
|
|
- * HW block
|
|
|
- *****************************************************************************/
|
|
|
-
|
|
|
-/* MSM specific */
|
|
|
-#define ABS_AHBBURST (0x0090UL)
|
|
|
-#define ABS_AHBMODE (0x0098UL)
|
|
|
-/* UDC register map */
|
|
|
-static uintptr_t ci_regs_nolpm[] = {
|
|
|
- [CAP_CAPLENGTH] = 0x000UL,
|
|
|
- [CAP_HCCPARAMS] = 0x008UL,
|
|
|
- [CAP_DCCPARAMS] = 0x024UL,
|
|
|
- [CAP_TESTMODE] = 0x038UL,
|
|
|
- [OP_USBCMD] = 0x000UL,
|
|
|
- [OP_USBSTS] = 0x004UL,
|
|
|
- [OP_USBINTR] = 0x008UL,
|
|
|
- [OP_DEVICEADDR] = 0x014UL,
|
|
|
- [OP_ENDPTLISTADDR] = 0x018UL,
|
|
|
- [OP_PORTSC] = 0x044UL,
|
|
|
- [OP_DEVLC] = 0x084UL,
|
|
|
- [OP_USBMODE] = 0x068UL,
|
|
|
- [OP_ENDPTSETUPSTAT] = 0x06CUL,
|
|
|
- [OP_ENDPTPRIME] = 0x070UL,
|
|
|
- [OP_ENDPTFLUSH] = 0x074UL,
|
|
|
- [OP_ENDPTSTAT] = 0x078UL,
|
|
|
- [OP_ENDPTCOMPLETE] = 0x07CUL,
|
|
|
- [OP_ENDPTCTRL] = 0x080UL,
|
|
|
-};
|
|
|
-
|
|
|
-static uintptr_t ci_regs_lpm[] = {
|
|
|
- [CAP_CAPLENGTH] = 0x000UL,
|
|
|
- [CAP_HCCPARAMS] = 0x008UL,
|
|
|
- [CAP_DCCPARAMS] = 0x024UL,
|
|
|
- [CAP_TESTMODE] = 0x0FCUL,
|
|
|
- [OP_USBCMD] = 0x000UL,
|
|
|
- [OP_USBSTS] = 0x004UL,
|
|
|
- [OP_USBINTR] = 0x008UL,
|
|
|
- [OP_DEVICEADDR] = 0x014UL,
|
|
|
- [OP_ENDPTLISTADDR] = 0x018UL,
|
|
|
- [OP_PORTSC] = 0x044UL,
|
|
|
- [OP_DEVLC] = 0x084UL,
|
|
|
- [OP_USBMODE] = 0x0C8UL,
|
|
|
- [OP_ENDPTSETUPSTAT] = 0x0D8UL,
|
|
|
- [OP_ENDPTPRIME] = 0x0DCUL,
|
|
|
- [OP_ENDPTFLUSH] = 0x0E0UL,
|
|
|
- [OP_ENDPTSTAT] = 0x0E4UL,
|
|
|
- [OP_ENDPTCOMPLETE] = 0x0E8UL,
|
|
|
- [OP_ENDPTCTRL] = 0x0ECUL,
|
|
|
-};
|
|
|
-
|
|
|
-static int hw_alloc_regmap(struct ci13xxx *udc, bool is_lpm)
|
|
|
-{
|
|
|
- int i;
|
|
|
-
|
|
|
- kfree(udc->hw_bank.regmap);
|
|
|
-
|
|
|
- udc->hw_bank.regmap = kzalloc((OP_LAST + 1) * sizeof(void *),
|
|
|
- GFP_KERNEL);
|
|
|
- if (!udc->hw_bank.regmap)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- for (i = 0; i < OP_ENDPTCTRL; i++)
|
|
|
- udc->hw_bank.regmap[i] =
|
|
|
- (i <= CAP_LAST ? udc->hw_bank.cap : udc->hw_bank.op) +
|
|
|
- (is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]);
|
|
|
-
|
|
|
- for (; i <= OP_LAST; i++)
|
|
|
- udc->hw_bank.regmap[i] = udc->hw_bank.op +
|
|
|
- 4 * (i - OP_ENDPTCTRL) +
|
|
|
- (is_lpm
|
|
|
- ? ci_regs_lpm[OP_ENDPTCTRL]
|
|
|
- : ci_regs_nolpm[OP_ENDPTCTRL]);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* hw_ep_bit: calculates the bit number
|
|
|
* @num: endpoint number
|
|
@@ -214,7 +66,7 @@ static inline int hw_ep_bit(int num, int dir)
|
|
|
return num + (dir ? 16 : 0);
|
|
|
}
|
|
|
|
|
|
-static int ep_to_bit(struct ci13xxx *udc, int n)
|
|
|
+static inline int ep_to_bit(struct ci13xxx *udc, int n)
|
|
|
{
|
|
|
int fill = 16 - udc->hw_ep_max / 2;
|
|
|
|
|
@@ -224,145 +76,6 @@ static int ep_to_bit(struct ci13xxx *udc, int 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
|
|
|
* without interruption)
|
|
@@ -536,42 +249,6 @@ static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value)
|
|
|
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
|
|
|
*
|
|
@@ -583,33 +260,6 @@ static int hw_port_is_high_speed(struct ci13xxx *udc)
|
|
|
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
|
|
|
*
|
|
@@ -630,48 +280,6 @@ static u32 hw_read_intr_status(struct ci13xxx *udc)
|
|
|
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
|
|
|
* interruption)
|
|
@@ -765,673 +373,6 @@ static int hw_usb_reset(struct ci13xxx *udc)
|
|
|
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
|
|
|
*****************************************************************************/
|
|
@@ -2656,7 +1597,7 @@ static int ci13xxx_stop(struct usb_gadget *gadget,
|
|
|
* This function returns IRQ_HANDLED if the IRQ has been handled
|
|
|
* It locks access to registers
|
|
|
*/
|
|
|
-static irqreturn_t udc_irq(int irq, void *data)
|
|
|
+irqreturn_t udc_irq(int irq, void *data)
|
|
|
{
|
|
|
struct ci13xxx *udc = data;
|
|
|
irqreturn_t retval;
|
|
@@ -2675,18 +1616,14 @@ static irqreturn_t udc_irq(int irq, void *data)
|
|
|
}
|
|
|
}
|
|
|
intr = hw_test_and_clear_intr_active(udc);
|
|
|
- if (intr) {
|
|
|
- isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intr;
|
|
|
- isr_statistics.hndl.idx &= ISR_MASK;
|
|
|
- isr_statistics.hndl.cnt++;
|
|
|
+ dbg_interrupt(intr);
|
|
|
|
|
|
+ if (intr) {
|
|
|
/* order defines priority - do NOT change it */
|
|
|
- if (USBi_URI & intr) {
|
|
|
- isr_statistics.uri++;
|
|
|
+ if (USBi_URI & intr)
|
|
|
isr_reset_handler(udc);
|
|
|
- }
|
|
|
+
|
|
|
if (USBi_PCI & intr) {
|
|
|
- isr_statistics.pci++;
|
|
|
udc->gadget.speed = hw_port_is_high_speed(udc) ?
|
|
|
USB_SPEED_HIGH : USB_SPEED_FULL;
|
|
|
if (udc->suspended && udc->driver->resume) {
|
|
@@ -2696,12 +1633,10 @@ static irqreturn_t udc_irq(int irq, void *data)
|
|
|
udc->suspended = 0;
|
|
|
}
|
|
|
}
|
|
|
- if (USBi_UEI & intr)
|
|
|
- isr_statistics.uei++;
|
|
|
- if (USBi_UI & intr) {
|
|
|
- isr_statistics.ui++;
|
|
|
+
|
|
|
+ if (USBi_UI & intr)
|
|
|
isr_tr_complete_handler(udc);
|
|
|
- }
|
|
|
+
|
|
|
if (USBi_SLI & intr) {
|
|
|
if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
|
|
|
udc->driver->suspend) {
|
|
@@ -2710,11 +1645,9 @@ static irqreturn_t udc_irq(int irq, void *data)
|
|
|
udc->driver->suspend(&udc->gadget);
|
|
|
spin_lock(&udc->lock);
|
|
|
}
|
|
|
- isr_statistics.sli++;
|
|
|
}
|
|
|
retval = IRQ_HANDLED;
|
|
|
} else {
|
|
|
- isr_statistics.none++;
|
|
|
retval = IRQ_NONE;
|
|
|
}
|
|
|
spin_unlock(&udc->lock);
|
|
@@ -2742,8 +1675,8 @@ static void udc_release(struct device *dev)
|
|
|
* No interrupts active, the IRQ has not been requested yet
|
|
|
* Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask
|
|
|
*/
|
|
|
-static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
|
|
|
- void __iomem *regs, struct ci13xxx **_udc)
|
|
|
+int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
|
|
|
+ void __iomem *regs, struct ci13xxx **_udc)
|
|
|
{
|
|
|
struct ci13xxx *udc;
|
|
|
int retval = 0;
|
|
@@ -2824,9 +1757,7 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
|
|
|
goto put_transceiver;
|
|
|
}
|
|
|
|
|
|
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
|
|
retval = dbg_create_files(&udc->gadget.dev);
|
|
|
-#endif
|
|
|
if (retval)
|
|
|
goto unreg_device;
|
|
|
|
|
@@ -2855,9 +1786,7 @@ remove_trans:
|
|
|
|
|
|
dev_err(dev, "error = %i\n", retval);
|
|
|
remove_dbg:
|
|
|
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
|
|
dbg_remove_files(&udc->gadget.dev);
|
|
|
-#endif
|
|
|
unreg_device:
|
|
|
device_unregister(&udc->gadget.dev);
|
|
|
put_transceiver:
|
|
@@ -2878,7 +1807,7 @@ free_udc:
|
|
|
*
|
|
|
* No interrupts active, the IRQ has been released
|
|
|
*/
|
|
|
-static void udc_remove(struct ci13xxx *udc)
|
|
|
+void udc_remove(struct ci13xxx *udc)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
@@ -2900,84 +1829,9 @@ static void udc_remove(struct ci13xxx *udc)
|
|
|
otg_set_peripheral(udc->transceiver->otg, &udc->gadget);
|
|
|
usb_put_transceiver(udc->transceiver);
|
|
|
}
|
|
|
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
|
|
dbg_remove_files(&udc->gadget.dev);
|
|
|
-#endif
|
|
|
device_unregister(&udc->gadget.dev);
|
|
|
|
|
|
kfree(udc->hw_bank.regmap);
|
|
|
kfree(udc);
|
|
|
}
|
|
|
-
|
|
|
-static int __devinit ci_udc_probe(struct platform_device *pdev)
|
|
|
-{
|
|
|
- struct device *dev = &pdev->dev;
|
|
|
- struct ci13xxx_udc_driver *driver = dev->platform_data;
|
|
|
- struct ci13xxx *udc;
|
|
|
- struct resource *res;
|
|
|
- void __iomem *base;
|
|
|
- int ret;
|
|
|
-
|
|
|
- if (!driver) {
|
|
|
- dev_err(dev, "platform data missing\n");
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
-
|
|
|
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
- if (!res) {
|
|
|
- dev_err(dev, "missing resource\n");
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
-
|
|
|
- base = devm_request_and_ioremap(dev, res);
|
|
|
- if (!res) {
|
|
|
- dev_err(dev, "can't request and ioremap resource\n");
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
-
|
|
|
- ret = udc_probe(driver, dev, base, &udc);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
-
|
|
|
- udc->irq = platform_get_irq(pdev, 0);
|
|
|
- if (udc->irq < 0) {
|
|
|
- dev_err(dev, "missing IRQ\n");
|
|
|
- ret = -ENODEV;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- platform_set_drvdata(pdev, udc);
|
|
|
- ret = request_irq(udc->irq, udc_irq, IRQF_SHARED, driver->name, udc);
|
|
|
-
|
|
|
-out:
|
|
|
- if (ret)
|
|
|
- udc_remove(udc);
|
|
|
-
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-static int __devexit ci_udc_remove(struct platform_device *pdev)
|
|
|
-{
|
|
|
- struct ci13xxx *udc = platform_get_drvdata(pdev);
|
|
|
-
|
|
|
- free_irq(udc->irq, udc);
|
|
|
- udc_remove(udc);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static struct platform_driver ci_udc_driver = {
|
|
|
- .probe = ci_udc_probe,
|
|
|
- .remove = __devexit_p(ci_udc_remove),
|
|
|
- .driver = {
|
|
|
- .name = "ci_udc",
|
|
|
- },
|
|
|
-};
|
|
|
-
|
|
|
-module_platform_driver(ci_udc_driver);
|
|
|
-
|
|
|
-MODULE_ALIAS("platform:ci_udc");
|
|
|
-MODULE_ALIAS("platform:ci13xxx");
|
|
|
-MODULE_LICENSE("GPL v2");
|
|
|
-MODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>");
|
|
|
-MODULE_DESCRIPTION("ChipIdea UDC Driver");
|