Cypress c67x00 (EZ-Host/EZ-OTG) support
Subsystems (2)
v5 cover Cypress c67x00 (EZ-Host/EZ-OTG) support
The Cypress c67x00 (EZ-Host/EZ-OTG) controllers are multi-role low/fullspeed USB controllers. This patch series implements a HCD driver and shows the work-in-progress status of a gadget driver.
I believe patch 1..3 are ready, and I would like to see them queued up for 2.6.25.
Changes since v4:
- Addressed Grant's comments (c67x00_dev->c67x00_hcd_dev, label indent)
- Moved c67x00_ll_set_ep_{ctrl,packet_size}_reg() to patch 4 as they are only needed for gadget support.
- Added c67x00_ prefix to struct lcp_int_data
--
Bye, Peter Korsgaard
PATCH v5 1/4 USB: add Cypress c67x00 low level interface code
This patch adds the low level support code for the Cypress c67x00 family of OTG controllers. The low level code is responsible for register access and implements the software protocol for communicating with the 16bit microcontroller inside the c67x00 device.
Communication is done over the HPI interface (16bit SRAM-like parallel bus).
--- drivers/usb/c67x00/c67x00-ll-hpi.c | 409 +++++++++++++++++++++++++++++++++++++ drivers/usb/c67x00/c67x00.h | 286 +++++++++++++++++++++++++ 2 files changed, 695 insertions(+)
drivers/usb/c67x00/c67x00.h new file +286 -0
@@ -0,0 +1,286 @@ +/* +* c67x00.h: Cypress C67X00 USB register and field definitions +* +* Copyright (C) 2006-2008 Barco N.V. +* Derived from the Cypress cy7c67200/300 ezusb linux driver and +* based on multiple host controller drivers inside the linux kernel. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301 USA. +*/ + +#ifndef _USB_C67X00_H +#define _USB_C67X00_H + +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/completion.h> +#include <linux/mutex.h> + +/* --------------------------------------------------------------------- +* Cypress C67x00 register definitions +*/ + +/* Hardware Revision Register */ +#define HW_REV_REG 0xC004 + +/* General USB registers */ +/* ===================== */ + +/* USB Control Register */ +#define USB_CTL_REG (x) ((x) ? 0xC0AA : 0xC08A) + +#define LOW_SPEED_PORT (x) ((x) ? 0x0800 : 0x0400) +#define HOST_MODE 0x0200 +#define PORT_RES_EN (x) ((x) ? 0x0100 : 0x0080) +#define SOF_EOP_EN (x) ((x) ? 0x0002 : 0x0001) + +/* USB status register - Notice it has different content in hcd/udc mode */ +#define USB_STAT_REG (x) ((x) ? 0xC0B0 : 0xC090) + +#define EP0_IRQ_FLG 0x0001 +#define EP1_IRQ_FLG 0x0002 +#define EP2_IRQ_FLG 0x0004 +#define EP3_IRQ_FLG 0x0008 +#define EP4_IRQ_FLG 0x0010 +#define EP5_IRQ_FLG 0x0020 +#define EP6_IRQ_FLG 0x0040 +#define EP7_IRQ_FLG 0x0080 +#define RESET_IRQ_FLG 0x0100 +#define SOF_EOP_IRQ_FLG 0x0200 +#define ID_IRQ_FLG 0x4000 +#define VBUS_IRQ_FLG 0x8000 + +/* USB Host only registers */ +/* ======================= */ + +/* Host n Control Register */ +#define HOST_CTL_REG (x) ((x) ? 0xC0A0 : 0xC080) + +#define PREAMBLE_EN 0x0080/* Preamble enable */ +#define SEQ_SEL 0x0040/* Data Toggle Sequence Bit Select */ +#define ISO_EN 0x0010/* Isochronous enable */ +#define ARM_EN 0x0001/* Arm operation */ + +/* Host n Interrupt Enable Register */ +#define HOST_IRQ_EN_REG (x) ((x) ? 0xC0AC : 0xC08C) + +#define SOF_EOP_IRQ_EN 0x0200/* SOF/EOP Interrupt Enable */ +#define SOF_EOP_TMOUT_IRQ_EN 0x0800/* SOF/EOP Timeout Interrupt Enable */ +#define ID_IRQ_EN 0x4000/* ID interrupt enable */ +#define VBUS_IRQ_EN 0x8000/* VBUS interrupt enable */ +#define DONE_IRQ_EN 0x0001/* Done Interrupt Enable */ + +/* USB status register */ +#define HOST_STAT_MASK 0x02FD +#define PORT_CONNECT_CHANGE (x) ((x) ? 0x0020 : 0x0010) +#define PORT_SE0_STATUS (x) ((x) ? 0x0008 : 0x0004) + +/* Host Frame Register */ +#define HOST_FRAME_REG (x) ((x) ? 0xC0B6 : 0xC096) + +#define HOST_FRAME_MASK 0x07FF + +/* USB Peripheral only registers */ +/* ============================= */ + +/* Device n Port Sel reg */ +#define DEVICE_N_PORT_SEL (x) ((x) ? 0xC0A4 : 0xC084) + +/* Device n Interrupt Enable Register */ +#define DEVICE_N_IRQ_EN_REG (x) ((x) ? 0xC0AC : 0xC08C) + +#define DEVICE_N_ENDPOINT_N_CTL_REG (dev, ep) ((dev) \ + ? (0x0280 + (ep << 4)) \ + : (0x0200 + (ep << 4))) +#define DEVICE_N_ENDPOINT_N_STAT_REG (dev, ep) ((dev) \ + ? (0x0286 + (ep << 4)) \ + : (0x0206 + (ep << 4))) + +#define DEVICE_N_ADDRESS (dev) ((dev) ? (0xC0AE) : (0xC08E)) + +/* HPI registers */ +/* ============= */ + +/* HPI Status register */ +#define SOFEOP_FLG (x) (1 << ((x) ? 12 : 10)) +#define SIEMSG_FLG (x) (1 << (4 + (x))) +#define RESET_FLG (x) ((x) ? 0x0200 : 0x0002) +#define DONE_FLG (x) (1 << (2 + (x))) +#define RESUME_FLG (x) (1 << (6 + (x))) +#define MBX_OUT_FLG 0x0001/* Message out available */ +#define MBX_IN_FLG 0x0100 +#define ID_FLG 0x4000 +#define VBUS_FLG 0x8000 + +/* Interrupt routing register */ +#define HPI_IRQ_ROUTING_REG 0x0142 + +#define HPI_SWAP_ENABLE (x) ((x) ? 0x0100 : 0x0001) +#define RESET_TO_HPI_ENABLE (x) ((x) ? 0x0200 : 0x0002) +#define DONE_TO_HPI_ENABLE (x) ((x) ? 0x0008 : 0x0004) +#define RESUME_TO_HPI_ENABLE (x) ((x) ? 0x0080 : 0x0040) +#define SOFEOP_TO_HPI_EN (x) ((x) ? 0x2000 : 0x0800) +#define SOFEOP_TO_CPU_EN (x) ((x) ? 0x1000 : 0x0400) +#define ID_TO_HPI_ENABLE 0x4000 +#define VBUS_TO_HPI_ENABLE 0x8000 + +/* SIE msg registers */ +#define SIEMSG_REG (x) ((x) ? 0x0148 : 0x0144) + +#define HUSB_TDListDone 0x1000 + +#define SUSB_EP0_MSG 0x0001 +#define SUSB_EP1_MSG 0x0002 +#define SUSB_EP2_MSG 0x0004 +#define SUSB_EP3_MSG 0x0008 +#define SUSB_EP4_MSG 0x0010 +#define SUSB_EP5_MSG 0x0020 +#define SUSB_EP6_MSG 0x0040 +#define SUSB_EP7_MSG 0x0080 +#define SUSB_RST_MSG 0x0100 +#define SUSB_SOF_MSG 0x0200 +#define SUSB_CFG_MSG 0x0400 +#define SUSB_SUS_MSG 0x0800 +#define SUSB_ID_MSG 0x4000 +#define SUSB_VBUS_MSG 0x8000 + +/* BIOS interrupt routines */ + +#define SUSBx_RECEIVE_INT (x) ((x) ? 97 : 81) +#define SUSBx_SEND_INT (x) ((x) ? 96 : 80) + +#define SUSBx_DEV_DESC_VEC (x) ((x) ? 0x00D4 : 0x00B4) +#define SUSBx_CONF_DESC_VEC (x) ((x) ? 0x00D6 : 0x00B6) +#define SUSBx_STRING_DESC_VEC (x) ((x) ? 0x00D8 : 0x00B8) + +#define CY_HCD_BUF_ADDR 0x500/* Base address for host */ +#define SIE_TD_SIZE 0x200/* size of the td list */ +#define SIE_TD_BUF_SIZE 0x400/* size of the data buffer */ + +#define SIE_TD_OFFSET (host) ((host) ? (SIE_TD_SIZE+SIE_TD_BUF_SIZE) : 0) +#define SIE_BUF_OFFSET (host) (SIE_TD_OFFSET(host) + SIE_TD_SIZE) + +/* Base address of HCD + 2 x TD_SIZE + 2 x TD_BUF_SIZE */ +#define CY_UDC_REQ_HEADER_BASE 0x1100 +/* 8- byte request headers for IN/OUT transfers */ +#define CY_UDC_REQ_HEADER_SIZE 8 + +#define CY_UDC_REQ_HEADER_ADDR (ep_num) (CY_UDC_REQ_HEADER_BASE + \ + ((ep_num) * CY_UDC_REQ_HEADER_SIZE)) +#define CY_UDC_DESC_BASE_ADDRESS (CY_UDC_REQ_HEADER_ADDR(8)) + +#define CY_UDC_BIOS_REPLACE_BASE 0x1800 +#define CY_UDC_REQ_BUFFER_BASE 0x2000 +#define CY_UDC_REQ_BUFFER_SIZE 0x0400 +#define CY_UDC_REQ_BUFFER_ADDR (ep_num) (CY_UDC_REQ_BUFFER_BASE + \ + ((ep_num) * CY_UDC_REQ_BUFFER_SIZE)) + +/* --------------------------------------------------------------------- +* Driver data structures +*/ + +struct c67x00_device ; + +/** +* struct c67x00_sie - Common data associated with a SIE +* @lock: lock to protect this struct +* @private_data: subdriver dependent data +* @irq: subdriver dependent irq handler, set NULL when not used +* @dev: link to common driver structure +* @sie_num: SIE number on chip, starting from 0 +* @mode: SIE mode (host/peripheral/otg/not used) +*/ +struct c67x00_sie { +/* Entries to be used by the subdrivers */ +spinlock_t lock;/* protect this structure */ +void *private_data; +void (*irq) (struct c67x00_sie *sie,u16 int_status,u16 msg); + +/* Read only: */ +struct c67x00_device *dev; +int sie_num; +int mode; +}; + +#define sie_dev (s) (&(s)->dev->pdev->dev) + +/** +* struct c67x00_lcp +*/ +struct c67x00_lcp { +/* Internal use only */ +struct mutex mutex; +struct completion msg_received; +u16 last_msg; +}; + +/* +* struct c67x00_hpi +*/ +struct c67x00_hpi { +void __iomem *base; +int regstep; +spinlock_t lock; +struct c67x00_lcp lcp; +}; + +#define C67X00_SIES 2 +#define C67X00_PORTS 2 + +/** +* struct c67x00_device - Common data associated with a c67x00 instance +* @hpi: hpi addresses +* @sie: array of sie's on this chip +* @pdev: platform device of instance +* @pdata: configuration provided by the platform +*/ +struct c67x00_device { +struct c67x00_hpi hpi; +struct c67x00_sie sie[C67X00_SIES ]; +struct platform_device *pdev; +struct c67x00_platform_data *pdata; +}; + +/* --------------------------------------------------------------------- +* Low level interface functions +*/ + +/* Host Port Interface (HPI) functions */ +u16 c67x00_ll_hpi_status (struct c67x00_device *dev); +void c67x00_ll_hpi_reg_init (struct c67x00_device *dev); +void c67x00_ll_hpi_enable_sofeop (struct c67x00_sie *sie); +void c67x00_ll_hpi_disable_sofeop (struct c67x00_sie *sie); + +/* General functions */ +u16 c67x00_ll_get_siemsg (struct c67x00_sie *sie); +void c67x00_ll_set_siemsg (struct c67x00_sie *sie,u16 val); +u16 c67x00_ll_get_usb_ctl (struct c67x00_sie *sie); +void c67x00_ll_usb_clear_status (struct c67x00_sie *sie,u16 bits); +u16 c67x00_ll_usb_get_status (struct c67x00_sie *sie); +void c67x00_ll_write_mem_le16 (struct c67x00_device *dev,u16 addr, +void *data,int len); +void c67x00_ll_read_mem_le16 (struct c67x00_device *dev,u16 addr, +void *data,int len); + +/* Called by c67x00_irq to handle lcp interrupts */ +void c67x00_ll_irq (struct c67x00_device *dev,u16 int_status); + +/* Setup and teardown */ +void c67x00_ll_init (struct c67x00_device *dev); +void c67x00_ll_release (struct c67x00_device *dev); +int c67x00_ll_reset (struct c67x00_device *dev); + +#endif /* _USB_C67X00_H */
drivers/usb/c67x00/c67x00-ll-hpi.c new file +409 -0
409 lines not on this page · raw message
--
Bye, Peter Korsgaard
PATCH v5 2/4 USB: add Cypress c67x00 OTG controller core driver
In reply to Peter KorsgaardThis patch add the core driver for the c67x00 USB OTG controller. The core driver is responsible for the platform bus binding and creating either USB HCD or USB Gadget instances for each of the serial interface engines on the chip.
This driver does not directly implement the HCD or gadget behaviours; it just controls access to the chip.
--- MAINTAINERS | 6 + drivers/usb/c67x00/c67x00-drv.c | 236 ++++++++++++++++++++++++++++++++++++++++ include/linux/usb/c67x00.h | 48 ++++++++ 3 files changed, 290 insertions(+)
drivers/usb/c67x00/c67x00-drv.c new file +236 -0
@@ -0,0 +1,236 @@ +/* +* c67x00-drv.c: Cypress C67X00 USB Common infrastructure +* +* Copyright (C) 2006-2008 Barco N.V. +* Derived from the Cypress cy7c67200/300 ezusb linux driver and +* based on multiple host controller drivers inside the linux kernel. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301 USA. +*/ + +/* +* This file implements the common infrastructure for using the c67x00. +* It is both the link between the platform configuration and subdrivers and +* the link between the common hardware parts and the subdrivers (e.g. +* interrupt handling). +* +* The c67x00 has 2 SIE's (serial interface engine) wich can be configured +* to be host, device or OTG (with some limitations, E.G. only SIE1 can be OTG). +* +* Depending on the platform configuration, the SIE's are created and +* the corresponding subdriver is initialized (c67x00_probe_sie). +*/ + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/usb.h> +#include <linux/usb/c67x00.h> +#include <asm/io.h> + +#include "c67x00.h" + +static void c67x00_probe_sie (struct c67x00_sie *sie, +struct c67x00_device *dev,int sie_num) +{ +spin_lock_init (&sie->lock); + sie->dev = dev; + sie->sie_num = sie_num; + sie->mode =c67x00_sie_config (dev->pdata->sie_config, sie_num); + +switch (sie->mode) { +case C67X00_SIE_UNUSED : +dev_info (sie_dev (sie), +"Not using SIE %d as requested\n" , sie->sie_num); +break ; + +default : +dev_err (sie_dev (sie), +"Unsupported configuration: 0x%x for SIE %d\n" , + sie->mode, sie->sie_num); +break ; + } +} + +static void c67x00_remove_sie (struct c67x00_sie *sie) +{ +} + + +static irqreturn_t c67x00_irq (int irq,void *__dev) +{ +struct c67x00_device *c67x00 = __dev; +struct c67x00_sie *sie; +u16 msg, int_status; +int i, count =8 ; + + int_status =c67x00_ll_hpi_status (c67x00); +if (!int_status) +return IRQ_NONE ; + +while (int_status !=0 && (count-- >=0 )) { +c67x00_ll_irq (c67x00, int_status); +for (i =0 ; i <C67X00_SIES ; i++) { + sie = &c67x00->sie[i]; + msg =0 ; +if (int_status &SIEMSG_FLG (i)) { + msg =c67x00_ll_get_siemsg (sie); +/* clear register to allow next message */ +c67x00_ll_set_siemsg (sie,0 ); + } +if (sie->irq) + sie->irq (sie, int_status, msg); + } + int_status =c67x00_ll_hpi_status (c67x00); + } + +if (int_status) +dev_warn (&c67x00->pdev->dev,"Not all interrupts handled! " +"status = 0x%04x\n" , int_status); + +return IRQ_HANDLED ; +} + +/* ------------------------------------------------------------------------- */ + +static int __devinit c67x00_drv_probe (struct platform_device *pdev) +{ +struct c67x00_device *c67x00; +struct c67x00_platform_data *pdata; +struct resource *res, *res2; +int ret, i; + + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); +if (!res) +return -ENODEV ; + + res2 =platform_get_resource (pdev,IORESOURCE_IRQ ,0 ); +if (!res2) +return -ENODEV ; + + pdata = pdev->dev.platform_data; +if (!pdata) +return -ENODEV ; + + c67x00 =kzalloc (sizeof (*c67x00),GFP_KERNEL ); +if (!c67x00) +return -ENOMEM ; + +if (!request_mem_region (res->start, res->end - res->start +1 , + pdev->name)) { +dev_err (&pdev->dev,"Memory region busy\n" ); + ret = -EBUSY ; + goto request_mem_failed; + } + c67x00->hpi.base =ioremap (res->start, res->end - res->start +1 ); +if (!c67x00->hpi.base) { +dev_err (&pdev->dev,"Unable to map HPI registers\n" ); + ret = -EIO ; + goto map_failed; + } + +spin_lock_init (&c67x00->hpi.lock); + c67x00->hpi.regstep = pdata->hpi_regstep; + c67x00->pdata = pdev->dev.platform_data; + c67x00->pdev = pdev; + +c67x00_ll_init (c67x00); +c67x00_ll_hpi_reg_init (c67x00); + + ret =request_irq (res2->start, c67x00_irq,0 , pdev->name, c67x00); +if (ret) { +dev_err (&pdev->dev,"Cannot claim IRQ\n" ); + goto request_irq_failed; + } + + ret =c67x00_ll_reset (c67x00); +if (ret) { +dev_err (&pdev->dev,"Device reset failed\n" ); + goto reset_failed; + } + +for (i =0 ; i <C67X00_SIES ; i++) +c67x00_probe_sie (&c67x00->sie[i], c67x00, i); + +platform_set_drvdata (pdev, c67x00); + +return 0 ; + + reset_failed: +free_irq (res2->start, c67x00); + request_irq_failed: +iounmap (c67x00->hpi.base); + map_failed: +release_mem_region (res->start, res->end - res->start +1 ); + request_mem_failed: +kfree (c67x00); + +return ret; +} + +static int __devexit c67x00_drv_remove(struct platform_device *pdev) +{ +struct c67x00_device *c67x00 =platform_get_drvdata (pdev); +struct resource *res; +int i; + +for (i =0 ; i <C67X00_SIES ; i++) +c67x00_remove_sie (&c67x00->sie[i]); + +c67x00_ll_release (c67x00); + + res =platform_get_resource (pdev,IORESOURCE_IRQ ,0 ); +if (res) +free_irq (res->start, c67x00); + +iounmap (c67x00->hpi.base); + + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); +if (res) +release_mem_region (res->start, res->end - res->start +1 ); + +kfree (c67x00); + +return 0 ; +} + +static struct platform_driver c67x00_driver = { + .probe = c67x00_drv_probe, + .remove =__devexit_p (c67x00_drv_remove), + .driver = { + .owner =THIS_MODULE , + .name ="c67x00" , + }, +}; + +static int __init c67x00_init (void ) +{ +if (usb_disabled ()) +return -ENODEV ; + +return platform_driver_register (&c67x00_driver); +} + +static void __exit c67x00_exit(void) +{ +platform_driver_unregister (&c67x00_driver); +} + +module_init (c67x00_init); +module_exit (c67x00_exit); + +MODULE_AUTHOR ("Peter Korsgaard, Jan Veldeman, Grant Likely" ); +MODULE_DESCRIPTION ("Cypress C67X00 USB Controller Driver" ); +MODULE_LICENSE ("GPL" );
include/linux/usb/c67x00.h new file +48 -0
48 lines not on this page · raw message
MAINTAINERS +6 -0
@@ -3832,6 +3832,12 @@ S: Maintained W: http://www.kroah.com/linux-usb/ +USB CYPRESS C67X00 DRIVER +P: Peter Korsgaard +M: [email protected] +L: [email protected] +S: Maintained + USB DAVICOM DM9601 DRIVER P: Peter Korsgaard M: [email protected]
--
Bye, Peter Korsgaard
PATCH v5 3/4 USB: add Cypress c67x00 OTG controller HCD driver
In reply to Peter KorsgaardThis patch adds HCD support for the Cypress c67x00 family of devices.
--- drivers/usb/Makefile | 2 drivers/usb/c67x00/Makefile | 11 drivers/usb/c67x00/c67x00-drv.c | 13 drivers/usb/c67x00/c67x00-hcd.c | 410 ++++++++++++ drivers/usb/c67x00/c67x00-hcd.h | 152 ++++ drivers/usb/c67x00/c67x00-ll-hpi.c | 75 ++ drivers/usb/c67x00/c67x00-sched.c | 1194 +++++++++++++++++++++++++++++++++++++ drivers/usb/c67x00/c67x00.h | 9 drivers/usb/host/Kconfig | 12 9 files changed, 1878 insertions(+)
drivers/usb/c67x00/c67x00-hcd.c new file +410 -0
@@ -0,0 +1,410 @@ +/* +* c67x00-hcd.c: Cypress C67X00 USB Host Controller Driver +* +* Copyright (C) 2006-2008 Barco N.V. +* Derived from the Cypress cy7c67200/300 ezusb linux driver and +* based on multiple host controller drivers inside the linux kernel. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301 USA. +*/ + +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/usb.h> + +#include "c67x00.h" +#include "c67x00-hcd.h" + +/* -------------------------------------------------------------------------- +* Root Hub Support +*/ + +static __u8 c67x00_hub_des[] = { +0x09 ,/* __u8 bLength; */ +0x29 ,/* __u8 bDescriptorType; Hub-descriptor */ +0x02 ,/* __u8 bNbrPorts; */ +0x00 ,/* __u16 wHubCharacteristics; */ +0x00 ,/* (per-port OC, no power switching) */ +0x32 ,/* __u8 bPwrOn2pwrGood; 2ms */ +0x00 ,/* __u8 bHubContrCurrent; 0 mA */ +0x00 ,/* __u8 DeviceRemovable; ** 7 Ports max ** */ +0xff ,/* __u8 PortPwrCtrlMask; ** 7 ports max ** */ +}; + +static void c67x00_hub_reset_host_port (struct c67x00_sie *sie,int port) +{ +struct c67x00_hcd *c67x00 = sie->private_data; +unsigned long flags; + +c67x00_ll_husb_reset (sie, port); + +spin_lock_irqsave (&c67x00->lock, flags); +c67x00_ll_husb_reset_port (sie, port); +spin_unlock_irqrestore (&c67x00->lock, flags); + +c67x00_ll_set_husb_eot (sie->dev,DEFAULT_EOT ); +} + +static int c67x00_hub_status_data (struct usb_hcd *hcd,char *buf) +{ +struct c67x00_hcd *c67x00 =hcd_to_c67x00_hcd (hcd); +struct c67x00_sie *sie = c67x00->sie; +u16 status; +int i; + + *buf =0 ; + status =c67x00_ll_usb_get_status (sie); +for (i =0 ; i <C67X00_PORTS ; i++) +if (status &PORT_CONNECT_CHANGE (i)) + *buf |= (1 << i); + +/* bit 0 denotes hub change, b1..n port change */ + *buf <<=1 ; + +return !!*buf; +} + +static int c67x00_hub_control (struct usb_hcd *hcd,u16 typeReq,u16 wValue, +u16 wIndex,char *buf,u16 wLength) +{ +struct c67x00_hcd *c67x00 =hcd_to_c67x00_hcd (hcd); +struct c67x00_sie *sie = c67x00->sie; +u16 status, usb_status; +int len =0 ; +unsigned int port = wIndex-1 ; +u16 wPortChange, wPortStatus; + +switch (typeReq) { + +case GetHubStatus: + *(__le32 *) buf =cpu_to_le32 (0 ); + len =4 ;/* hub power */ +break ; + +case GetPortStatus: +if (wIndex >C67X00_PORTS ) +return -EPIPE ; + + status =c67x00_ll_usb_get_status (sie); + usb_status =c67x00_ll_get_usb_ctl (sie); + + wPortChange =0 ; +if (status &PORT_CONNECT_CHANGE (port)) + wPortChange |=USB_PORT_STAT_C_CONNECTION ; + + wPortStatus =USB_PORT_STAT_POWER ; +if (!(status &PORT_SE0_STATUS (port))) + wPortStatus |=USB_PORT_STAT_CONNECTION ; +if (usb_status &LOW_SPEED_PORT (port)) { + wPortStatus |=USB_PORT_STAT_LOW_SPEED ; + c67x00->low_speed_ports |= (1 << port); + }else + c67x00->low_speed_ports &= ~(1 << port); + +if (usb_status &SOF_EOP_EN (port)) + wPortStatus |=USB_PORT_STAT_ENABLE ; + + *(__le16 *) buf =cpu_to_le16 (wPortStatus); + *(__le16 *) (buf +2 ) =cpu_to_le16 (wPortChange); + len =4 ; +break ; + +case SetHubFeature:/* We don't implement these */ +case ClearHubFeature: +switch (wValue) { +case C_HUB_OVER_CURRENT : +case C_HUB_LOCAL_POWER : + len =0 ; +break ; + +default : +return -EPIPE ; + } +break ; + +case SetPortFeature: +if (wIndex >C67X00_PORTS ) +return -EPIPE ; + +switch (wValue) { +case USB_PORT_FEAT_SUSPEND : +dev_dbg (c67x00_hcd_dev (c67x00), +"SetPortFeature %d (SUSPEND)\n" , port); + len =0 ; +break ; + +case USB_PORT_FEAT_RESET : +c67x00_hub_reset_host_port (sie, port); + len =0 ; +break ; + +case USB_PORT_FEAT_POWER : +/* Power always enabled */ + len =0 ; +break ; + +default : +dev_dbg (c67x00_hcd_dev (c67x00), +"%s: SetPortFeature %d (0x%04x) Error!\n" , + __FUNCTION__, port, wValue); +return -EPIPE ; + } +break ; + +case ClearPortFeature: +if (wIndex >C67X00_PORTS ) +return -EPIPE ; + +switch (wValue) { +case USB_PORT_FEAT_ENABLE : +/* Reset the port so that the c67x00 also notices the +* disconnect */ +c67x00_hub_reset_host_port (sie, port); + len =0 ; +break ; + +case USB_PORT_FEAT_C_ENABLE : +dev_dbg (c67x00_hcd_dev (c67x00), +"ClearPortFeature (%d): C_ENABLE\n" , port); + len =0 ; +break ; + +case USB_PORT_FEAT_SUSPEND : +dev_dbg (c67x00_hcd_dev (c67x00), +"ClearPortFeature (%d): SUSPEND\n" , port); + len =0 ; +break ; + +case USB_PORT_FEAT_C_SUSPEND : +dev_dbg (c67x00_hcd_dev (c67x00), +"ClearPortFeature (%d): C_SUSPEND\n" , port); + len =0 ; +break ; + +case USB_PORT_FEAT_POWER : +dev_dbg (c67x00_hcd_dev (c67x00), +"ClearPortFeature (%d): POWER\n" , port); +return -EPIPE ; + +case USB_PORT_FEAT_C_CONNECTION : +c67x00_ll_usb_clear_status (sie, +PORT_CONNECT_CHANGE (port)); + len =0 ; +break ; + +case USB_PORT_FEAT_C_OVER_CURRENT : +dev_dbg (c67x00_hcd_dev (c67x00), +"ClearPortFeature (%d): OVER_CURRENT\n" , port); + len =0 ; +break ; + +case USB_PORT_FEAT_C_RESET : +dev_dbg (c67x00_hcd_dev (c67x00), +"ClearPortFeature (%d): C_RESET\n" , port); + len =0 ; +break ; + +default : +dev_dbg (c67x00_hcd_dev (c67x00), +"%s: ClearPortFeature %d (0x%04x) Error!\n" , + __FUNCTION__, port, wValue); +return -EPIPE ; + } +break ; + +case GetHubDescriptor: + len = min_t(unsigned int, sizeof(c67x00_hub_des), wLength); +memcpy (buf, c67x00_hub_des, len); +break ; + +default : +dev_dbg (c67x00_hcd_dev (c67x00),"%s: unknown\n" , __FUNCTION__); +return -EPIPE ; + } + +return 0 ; +} + +/* --------------------------------------------------------------------- +* Main part of host controller driver +*/ + +/** +* c67x00_hcd_irq +* +* This function is called from the interrupt handler in c67x00-drv.c +*/ +static void c67x00_hcd_irq (struct c67x00_sie *sie,u16 int_status,u16 msg) +{ +struct c67x00_hcd *c67x00 = sie->private_data; +struct usb_hcd *hcd =c67x00_hcd_to_hcd (c67x00); + +/* Handle sie message flags */ +if (msg) { +if (msg & HUSB_TDListDone) +c67x00_sched_kick (c67x00); +else +dev_warn (c67x00_hcd_dev (c67x00), +"Unknown SIE msg flag(s): 0x%04x\n" , msg); + } + +if (unlikely (hcd->state ==HC_STATE_HALT )) +return ; + +if (!test_bit (HCD_FLAG_HW_ACCESSIBLE , &hcd->flags)) +return ; + +/* Handle Start of frame events */ +if (int_status &SOFEOP_FLG (sie->sie_num)) { +c67x00_ll_usb_clear_status (sie,SOF_EOP_IRQ_FLG ); +c67x00_sched_kick (c67x00); +set_bit (HCD_FLAG_SAW_IRQ , &hcd->flags); + } +} + +/** +* c67x00_hcd_start: Host controller start hook +*/ +static int c67x00_hcd_start (struct usb_hcd *hcd) +{ + hcd->uses_new_polling =1 ; + hcd->state =HC_STATE_RUNNING ; + hcd->poll_rh =1 ; + +return 0 ; +} + +/** +* c67x00_hcd_stop: Host controller stop hook +*/ +static void c67x00_hcd_stop (struct usb_hcd *hcd) +{ +/* Nothing to do */ +} + +static int c67x00_hcd_get_frame (struct usb_hcd *hcd) +{ +struct c67x00_hcd *c67x00 =hcd_to_c67x00_hcd (hcd); +u16 temp_val; + +dev_dbg (c67x00_hcd_dev (c67x00),"%s\n" , __FUNCTION__); + temp_val =c67x00_ll_husb_get_frame (c67x00->sie); + temp_val &=HOST_FRAME_MASK ; +return temp_val ? (temp_val -1 ) :HOST_FRAME_MASK ; +} + +static struct hc_driver c67x00_hc_driver = { + .description ="c67x00-hcd" , + .product_desc ="Cypress C67X00 Host Controller" , + .hcd_priv_size =sizeof (struct c67x00_hcd ), + .flags =HCD_USB11 |HCD_MEMORY , + +/* +* basic lifecycle operations +*/ + .start = c67x00_hcd_start, + .stop = c67x00_hcd_stop, + +/* +* managing i/o requests and associated device resources +*/ + .urb_enqueue = c67x00_urb_enqueue, + .urb_dequeue = c67x00_urb_dequeue, + .endpoint_disable = c67x00_endpoint_disable, + +/* +* scheduling support +*/ + .get_frame_number = c67x00_hcd_get_frame, + +/* +* root hub support +*/ + .hub_status_data = c67x00_hub_status_data, + .hub_control = c67x00_hub_control, +}; + +/* --------------------------------------------------------------------- +* Setup/Teardown routines +*/ + +int c67x00_hcd_probe (struct c67x00_sie *sie) +{ +struct c67x00_hcd *c67x00; +struct usb_hcd *hcd; +unsigned long flags; +int retval; + + hcd =usb_create_hcd (&c67x00_hc_driver,sie_dev (sie),"c67x00_sie" ); +if (!hcd) { + retval = -ENOMEM ; + goto err0; + } + c67x00 =hcd_to_c67x00_hcd (hcd); + +spin_lock_init (&c67x00->lock); + c67x00->sie = sie; + +INIT_LIST_HEAD (&c67x00->list[PIPE_ISOCHRONOUS ]); +INIT_LIST_HEAD (&c67x00->list[PIPE_INTERRUPT ]); +INIT_LIST_HEAD (&c67x00->list[PIPE_CONTROL ]); +INIT_LIST_HEAD (&c67x00->list[PIPE_BULK ]); + c67x00->urb_count =0 ; +INIT_LIST_HEAD (&c67x00->td_list); +INIT_LIST_HEAD (&c67x00->done_list); + c67x00->td_base_addr =CY_HCD_BUF_ADDR +SIE_TD_OFFSET (sie->sie_num); + c67x00->buf_base_addr =CY_HCD_BUF_ADDR +SIE_BUF_OFFSET (sie->sie_num); + c67x00->max_frame_bw =MAX_FRAME_BW_STD ; + +c67x00_ll_husb_init_host_port (sie); + +init_completion (&c67x00->endpoint_disable); + retval =c67x00_sched_start_scheduler (c67x00); +if (retval) + goto err1; + + retval =usb_add_hcd (hcd,0 ,0 ); +if (retval) { +dev_dbg (sie_dev (sie),"%s: usb_add_hcd returned %d\n" , + __FUNCTION__, retval); + goto err2; + } + +spin_lock_irqsave (&sie->lock, flags); + sie->private_data = c67x00; + sie->irq = c67x00_hcd_irq; +spin_unlock_irqrestore (&sie->lock, flags); + +return retval; + + err2: +c67x00_sched_stop_scheduler (c67x00); + err1: +usb_put_hcd (hcd); + err0: +return retval; +} + +/* may be called with controller, bus, and devices active */ +void c67x00_hcd_remove (struct c67x00_sie *sie) +{ +struct c67x00_hcd *c67x00 = sie->private_data; +struct usb_hcd *hcd =c67x00_hcd_to_hcd (c67x00); + +c67x00_sched_stop_scheduler (c67x00); +usb_remove_hcd (hcd); +usb_put_hcd (hcd); +}
drivers/usb/c67x00/c67x00-hcd.h new file +152 -0
152 lines not on this page · raw message
drivers/usb/c67x00/c67x00-sched.c new file +1194 -0
1,194 lines not on this page · raw message
drivers/usb/c67x00/Makefile new file +11 -0
@@ -0,0 +1,11 @@ +# +# Makefile for Cypress C67X00 USB Controller +# + +ifeq ($(CONFIG_USB_DEBUG),y) + EXTRA_CFLAGS += -DDEBUG +endif + +obj-$(CONFIG_USB_C67X00_HCD) += c67x00.o + +c67x00-objs := c67x00-drv.o c67x00-ll-hpi.o c67x00-hcd.o c67x00-sched.o
drivers/usb/Makefile +2 -0
@@ -17,6 +17,8 @@ obj-$(CONFIG_USB_U132_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ +obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ + obj-$(CONFIG_USB_ACM) += class/ obj-$(CONFIG_USB_PRINTER) += class/
drivers/usb/c67x00/c67x00-drv.c +13 -0
@@ -41,6 +41,7 @@#include <asm/io.h> #include "c67x00.h" +#include "c67x00-hcd.h" static void c67x00_probe_sie (struct c67x00_sie *sie,struct c67x00_device *dev,int sie_num)
@@ -51,6 +52,10 @@ sie->mode =c67x00_sie_config (dev->pdata->sie_config, sie_num);switch (sie->mode) { +case C67X00_SIE_HOST : +c67x00_hcd_probe (sie); +break ; +case C67X00_SIE_UNUSED :dev_info (sie_dev (sie),"Not using SIE %d as requested\n" , sie->sie_num);
@@ -66,6 +71,14 @@static void c67x00_remove_sie (struct c67x00_sie *sie) { +switch (sie->mode) { +case C67X00_SIE_HOST : +c67x00_hcd_remove (sie); +break ; + +default : +break ; + } }
drivers/usb/c67x00/c67x00.h +9 -0
@@ -275,6 +275,15 @@void c67x00_ll_read_mem_le16 (struct c67x00_device *dev,u16 addr,void *data,int len); +/* Host specific functions */ +void c67x00_ll_set_husb_eot (struct c67x00_device *dev,u16 value); +void c67x00_ll_husb_reset (struct c67x00_sie *sie,int port); +void c67x00_ll_husb_set_current_td (struct c67x00_sie *sie,u16 addr); +u16 c67x00_ll_husb_get_current_td (struct c67x00_sie *sie); +u16 c67x00_ll_husb_get_frame (struct c67x00_sie *sie); +void c67x00_ll_husb_init_host_port (struct c67x00_sie *sie); +void c67x00_ll_husb_reset_port (struct c67x00_sie *sie,int port); +/* Called by c67x00_irq to handle lcp interrupts */ void c67x00_ll_irq (struct c67x00_device *dev,u16 int_status);
drivers/usb/c67x00/c67x00-ll-hpi.c +75 -0
81 lines not on this page · raw message
drivers/usb/host/Kconfig +12 -0
@@ -261,3 +261,15 @@ To compile this driver as a module, choose M here: the module will be called r8a66597-hcd. +config USB_C67X00_HCD + tristate "Cypress C67x00 HCD support" + depends on USB + help + The Cypress C67x00 (EZ-Host/EZ-OTG) chips are dual-role + host/peripheral/OTG USB controllers. + + Enable this option to support this chip in host controller mode. + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called c67x00.
--
Bye, Peter Korsgaard
PATCH v5 4/4 USB: add Cypress c67x00 OTG controller gadget driver
In reply to Peter KorsgaardThis patch adds USB gadget support for the Cypress c67x00 family of devices.
This is work in progress and not ready to be committed yet. I'm posting this to show how it fits with the rest of the driver and to collect feedback.
The driver works good enought to use g_serial, but there are still issues to be solved. The biggest issue is that endpoint 0 is currently handled by the BIOS inside the c67x00, so the gadget stack never sees the data. The BIOS also has other deficiencies, E.G. see the patching done in c67x00_ll_susb_init().
--- drivers/usb/Kconfig | 2 drivers/usb/Makefile | 2 drivers/usb/c67x00/Kconfig | 21 drivers/usb/c67x00/Makefile | 7 drivers/usb/c67x00/c67x00-drv.c | 11 drivers/usb/c67x00/c67x00-ll-hpi.c | 201 ++++++++ drivers/usb/c67x00/c67x00-udc.c | 905 +++++++++++++++++++++++++++++++++++++ drivers/usb/c67x00/c67x00-udc.h | 50 ++ drivers/usb/c67x00/c67x00.h | 21 drivers/usb/gadget/Kconfig | 7 drivers/usb/gadget/gadget_chips.h | 8 drivers/usb/host/Kconfig | 12 12 files changed, 1232 insertions(+), 15 deletions(-)
drivers/usb/c67x00/c67x00-udc.c new file +905 -0
@@ -0,0 +1,905 @@ +/* +* c67x00-udc.c: Cypress C67X00 USB device controller +* +* Copyright (C) 2006-2008 Barco N.V. +* Derived from the Cypress cy7c67200/300 ezusb linux driver and +* based on multiple device controller drivers inside the linux kernel. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +* MA 02110-1301 USA. +*/ + +#include <linux/device.h> +#include <linux/usb.h> +#include <linux/usb/c67x00.h> +#include <linux/usb/gadget.h> +#include <linux/usb/ch9.h> + +#include "c67x00.h" +#include "c67x00-udc.h" + +/* Defined in DEVICE n ENDPOINT STATUS REGISTERS */ +#define OVERFLOW_FLG 0x0800/* Receive overflow */ +#define UNDERFLOW_FLG 0x0400/* Receive underflow */ +#define OUT_EXCEPTION_FLG 0x0200/* OUT received when armed for IN */ +#define IN_EXCEPTION_FLG 0x0100/* IN received when armed for OUT */ +#define STALL_FLG 0x0080/* Stall sent */ +#define NAK_FLG 0x0040/* NAK sent */ +#define LENGTH_EXCEPT_FLG 0x0020/* Overflow or Underflow occured */ +#define SETUP_FLG 0x0010/* SETUP packet received */ +#define SEQ_STAT 0x0008/* Last Data Toggle Sequence bit sent +or received */ +#define TIMEOUT_FLG 0x0004/* Last transmission timed out */ +#define ERROR_FLG 0x0002/* CRC Err detected in last +reception*/ +#define ACK_FLG 0x0001/* Last transaction ACK'D (sent +or received) */ + +/* Defined in DEVICE n ENDPOINT CONTROL REGISTERS */ +#define DIR_SEL_IN 0x0004/* Last transmission timed out */ +#define EP_ENABLE 0x0002/* Enable Endpoint */ + + + +struct c67x00_request { +struct usb_request req; +struct list_head queue; +}; + + + +struct c67x00_udc_ep { +struct usb_ep ep; +struct c67x00_udc *udc; + +struct list_head queue; +int ep_num; +int is_ep_in; +int enable; +int stopped; +int start_io; +}; + +#define C67X00_MAX_NB_END_POINTS 8 + +struct c67x00_udc { +spinlock_t lock; +struct c67x00_sie *sie; +struct usb_gadget gadget; +struct usb_gadget_driver *driver; +struct c67x00_udc_ep ep[C67X00_MAX_NB_END_POINTS ]; +struct work_struct io_work; +int config_nr; +/* The highest string descriptor entry +(used to retrieve descriptors from gadget driver) */ +int top_str_id; +u16 string_desc_addr; +}; + +const static unsigned char get_descriptor_device[] = { +USB_DIR_IN |USB_TYPE_STANDARD |USB_RECIP_DEVICE , +USB_REQ_GET_DESCRIPTOR , +0x00 , +USB_DT_DEVICE , +0x00 , +0x00 , +0x12 , +0x00 +}; + +const static unsigned char get_descriptor_config[] = { +USB_DIR_IN |USB_TYPE_STANDARD |USB_RECIP_DEVICE , +USB_REQ_GET_DESCRIPTOR , +0x00 , +USB_DT_CONFIG , +0x00 , +0x00 , +0x40 , +0x00 +}; + +static unsigned char get_descriptor_string[] = { +USB_DIR_IN |USB_TYPE_STANDARD |USB_RECIP_DEVICE , +USB_REQ_GET_DESCRIPTOR , +0x00 , +USB_DT_STRING , +0x00 , +0x00 , +0xFF , +0x00 +}; + +#define SIEx_DEV_DESC_LOC (x) ((x) ? (CY_UDC_DESC_BASE_ADDRESS + 0x200) \ + : CY_UDC_DESC_BASE_ADDRESS) +#define SIEx_CONF_DESC_LOC (x) (SIEx_DEV_DESC_LOC(x) + 32) + +/* ------------------------------------------------------------------------- */ +/* gadget ops */ + +static int c67x00_get_frame (struct usb_gadget *_gadget) +{ +printk (KERN_DEBUG "c67x00-udc : c67x00_get_frame\n" ); +return -ENODEV ; +} + +static int c67x00_wakeup (struct usb_gadget *_gadget) +{ +printk (KERN_DEBUG "c67x00-udc : c67x00_wakeup\n" ); +return -ENODEV ; +} + +static int c67x00_selfpowered (struct usb_gadget *_gadget, +int is_selfpowered) +{ +printk (KERN_DEBUG "c67x00-udc : c67x00_selfpowered\n" ); +return -ENODEV ; +} + +static int c67x00_vbus_session (struct usb_gadget *_gadget,int is_active) +{ +printk (KERN_DEBUG "c67x00-udc : c67x00_vbus_session\n" ); +return -ENODEV ; +} + +static int c67x00_pullup (struct usb_gadget *_gadget,int is_on) +{ +printk (KERN_DEBUG "c67x00-udc : c67x00_pullup\n" ); +return -ENODEV ; +} + + +static const struct usb_gadget_ops c67x00_udc_ops = { + .get_frame = c67x00_get_frame, + .wakeup = c67x00_wakeup, + .set_selfpowered = c67x00_selfpowered, + .vbus_session = c67x00_vbus_session, + .pullup = c67x00_pullup +}; + + + +/* +* the sie and gadget_driver get probed/registered from 2 totally independant +* places, this datastructure binds them together +*/ +static struct c67x00_udc controller = { + .gadget = { + .ops = &c67x00_udc_ops, + .name ="c67x00_udc" , + .speed =USB_SPEED_FULL , + .is_dualspeed =1 , + .is_a_peripheral =1 , + .dev = { + .bus_id ="gadget" , + }, + }, +}; + + +/*----------------------------------------------------------------------------*/ + +static void c67x00_udc_set_configuration (struct c67x00_sie *sie,u16 config) +{ +struct usb_ctrlrequest request; +struct c67x00_udc *udc = sie->private_data; + +dev_dbg (sie_dev (sie),"set configuration %d\n" , config); + + request.bRequest =USB_REQ_SET_CONFIGURATION ; + request.bRequestType =0 ; + request.wValue =cpu_to_le16 (config); + udc->driver->setup (&udc->gadget, &request); +} + +static int c67x00_udc_parse_descriptor (struct c67x00_udc *udc, +struct c67x00_request *req) +{ +int retval =0 ; +u8 *buf = req->req.buf; + +if (req->req.length <2 ) +return 0 ; + +switch (buf[1 ]) { +case USB_DT_DEVICE : { +struct usb_device_descriptor *desc = req->req.buf; + +/* Look for the highest stringIndex */ +if (desc->iManufacturer > udc->top_str_id) + udc->top_str_id = desc->iManufacturer; +if (desc->iProduct > udc->top_str_id) + udc->top_str_id = desc->iProduct; +if (desc->iSerialNumber > udc->top_str_id) + udc->top_str_id = desc->iSerialNumber; + +/* Write descriptor to C67x00 memory */ +c67x00_ll_write_mem_le16 (udc->sie->dev, +SIEx_DEV_DESC_LOC (udc->sie->sie_num), + req->req.buf, req->req.length); + +/* Write vector address to c67x00 */ +c67x00_ll_set_device_descriptor_location ( + udc->sie,SIEx_DEV_DESC_LOC (udc->sie->sie_num)); + + retval =1 ; +break ; + } + +case USB_DT_CONFIG : { +struct usb_config_descriptor *desc = req->req.buf; +int offset; +u16 length; + +/* store config number to pass to the gadget driver, +once the c67x00 is configured */ + udc->config_nr = desc->bConfigurationValue; + + length =le16_to_cpu (desc->wTotalLength); + +if (desc->iConfiguration > udc->top_str_id) + udc->top_str_id = desc->iConfiguration; + + offset = desc->bLength; + +while (offset < length) { +if (buf[offset +1 ] ==USB_DT_INTERFACE ) { +struct usb_interface_descriptor *if_desc = + (struct usb_interface_descriptor *) + (buf + offset); + +if (if_desc->iInterface > udc->top_str_id) + udc->top_str_id = if_desc->iInterface; + } + + offset += buf[offset]; + } + +if ((length %8 ) ==0 ) { +/* BIOS can't handle descriptors with size multiple +of xfer size */ + length +=1 ; +/* desc->wTotalLength = cpu_to_le16(length); */ + } + +/* BIOS can only handle configuration 1, +so make sure the config nr is 1 */ + desc->bConfigurationValue =1 ; + +c67x00_ll_write_mem_le16 (udc->sie->dev, +SIEx_CONF_DESC_LOC (udc->sie->sie_num), + req->req.buf, length); + +/* Write vector address to SW interrupt */ +c67x00_ll_set_configuration_descriptor_location ( + udc->sie,SIEx_CONF_DESC_LOC (udc->sie->sie_num)); + +/* String descriptors start behind configuration descriptor */ + udc->string_desc_addr = +SIEx_CONF_DESC_LOC (udc->sie->sie_num) + length; + +/* Make sure the address is even */ +if (udc->string_desc_addr &0x01 ) + udc->string_desc_addr++; + +/* Write string descriptor vector address */ +c67x00_ll_set_string_descriptor_location ( + udc->sie, udc->string_desc_addr); + + retval =1 ; +break ; + } + +case USB_DT_STRING : +/* Write string descriptor */ +c67x00_ll_write_mem_le16 (udc->sie->dev, udc->string_desc_addr, + req->req.buf, req->req.length); + +/* set address to end of this descriptor */ + udc->string_desc_addr += req->req.length; + + retval =1 ; +break ; + } + +return retval; +} + +/* +* done - retire a request; caller blocked irqs +*/ +static void c67x00_udc_done (struct c67x00_udc_ep *ep, +struct c67x00_request *req,int status) +{ +int stopped = ep->stopped; + +list_del_init (&req->queue); + +if (likely (req->req.status == -EINPROGRESS )) + req->req.status = status; +else + status = req->req.status; +/* +if (status && status != -ESHUTDOWN) +DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n", +ep->ep.name, &req->req, status, +req->req.actual, req->req.length); +*/ + +/* don't modify queue heads during completion callback */ + ep->stopped =1 ; + req->req.complete (&ep->ep, &req->req); + ep->stopped = stopped; +} + +/*----------- UDC send/receive functions -------------------------------------*/ + +static void c67x00_udc_start_io_irq (struct c67x00_udc_ep *ep) +{ +struct c67x00_sie *sie = ep->udc->sie; +struct c67x00_request *req = + list_entry(ep->queue.next, struct c67x00_request, queue); + +if (ep->is_ep_in) +c67x00_ll_susb_start_send (sie, ep->ep_num, + req->req.buf, req->req.length); +else +c67x00_ll_susb_start_receive (sie, ep->ep_num, req->req.length); +} + +static void c67x00_udc_io_work (struct work_struct *_udc) +{ +int i =0 ; +struct c67x00_udc *udc = + container_of(_udc, struct c67x00_udc, io_work); + +for (i =0 ; i <C67X00_MAX_NB_END_POINTS ; i++) { +struct c67x00_udc_ep *ep = &udc->ep[i]; +if (ep->start_io) { + ep->start_io =0 ; +c67x00_udc_start_io_irq (ep); + } + } +} + +static void c67x00_udc_schedule_io_irq (struct c67x00_udc_ep *ep) +{ + ep->start_io =1 ; + +/* start work queue */ +schedule_work (&ep->udc->io_work); +} + +static void c67x00_udc_done_irq (struct c67x00_udc_ep *ep,int status) +{ +struct c67x00_request *req; +struct c67x00_sie *sie = ep->udc->sie; +int result; + + result =c67x00_ll_susb_get_transfer_status (sie, ep->ep_num); +if (result <0 ) { +dev_err (sie_dev (sie),"udc_done_irq error (%d)\n" , result); +return ; + } + +if (unlikely (list_empty (&ep->queue))) +return ; + + req = list_entry(ep->queue.next, struct c67x00_request, queue); + + req->req.actual = req->req.length - result; + +if (!ep->is_ep_in && ep->ep_num !=0 ) +c67x00_ll_susb_receive (sie, ep->ep_num, + req->req.buf, req->req.actual); + +c67x00_udc_done (ep, req,0 ); +if (!list_empty (&ep->queue) && !ep->stopped) +/* restart io req */ +c67x00_udc_schedule_io_irq (ep); +} + +/* -------------------------------------------------------------------------- */ +/* endpoints */ + +static const char *c67x00_ep_name[C67X00_MAX_NB_END_POINTS ] = { +"ep0" ,"ep1out-bulk" ,"ep2in-bulk" ,"ep3" ,"ep4" ,"ep5" ,"ep6" ,"ep7" +}; + + +/* -------------------------------------------------------------------------- */ +/* ep opts */ + +/* +* empties entire endpoint queue +*/ +static void c67x00_nuke_ep (struct c67x00_udc_ep *ep,int status) +{ + ep->stopped =1 ; + +while (!list_empty (&ep->queue)) { +struct c67x00_request *req = + list_entry(ep->queue.next, struct c67x00_request, + queue); +c67x00_udc_done (ep, req, status); + } +} + +static int c67x00_ep_enable (struct usb_ep *_ep, +const struct usb_endpoint_descriptor *desc) +{ +struct c67x00_udc_ep *ep; +struct c67x00_sie *sie; +u16 maxpacket; + + ep = container_of(_ep, struct c67x00_udc_ep, ep); + sie = ep->udc->sie; + + maxpacket =le16_to_cpu (desc->wMaxPacketSize); + +c67x00_ll_set_device_ep_status (sie, ep->ep_num,0x0000 ); + +if (desc->bEndpointAddress &USB_DIR_IN ) { + ep->is_ep_in =1 ; +c67x00_ll_set_ep_ctrl_reg (sie, ep->ep_num, +EP_ENABLE |DIR_SEL_IN ); +c67x00_ll_set_ep_packet_size_reg (sie, ep->ep_num, maxpacket); + }else { + ep->is_ep_in =0 ; +c67x00_ll_set_ep_ctrl_reg (sie, ep->ep_num,EP_ENABLE ); +c67x00_ll_set_ep_packet_size_reg (sie, ep->ep_num, maxpacket); + } + + ep->enable =1 ; + ep->stopped =0 ; + ep->ep.maxpacket = maxpacket; + +return 0 ; +} + +static int c67x00_ep_disable (struct usb_ep *_ep) +{ +unsigned long flags; +struct c67x00_udc_ep *ep; + + ep = container_of(_ep, struct c67x00_udc_ep, ep); + +dev_dbg (sie_dev (ep->udc->sie),"ep_disable %s\n" , _ep->name); + +spin_lock_irqsave (&ep->udc->lock, flags); + + ep->enable =0 ; + ep->stopped =1 ; + +c67x00_nuke_ep (ep, -ESHUTDOWN ); + +spin_unlock_irqrestore (&ep->udc->lock, flags); + +return 0 ; +} + + +struct usb_request *c67x00_ep_alloc_request (struct usb_ep *_ep, +gfp_t gfp_flags) +{ +struct c67x00_request *req; + + req =kzalloc (sizeof (struct c67x00_request ), gfp_flags); +if (!req) +return NULL ; + +INIT_LIST_HEAD (&req->queue); +return &req->req; +} + +static void c67x00_ep_free_request (struct usb_ep *_ep, +struct usb_request *_req) +{ +struct c67x00_request *req =NULL ; +struct c67x00_udc_ep *ep; + + ep = container_of(_ep, struct c67x00_udc_ep, ep); +dev_dbg (sie_dev (ep->udc->sie),"free_request %s\n" , _ep->name); + + req = container_of(_req, struct c67x00_request, req); + +if (_req) +kfree (req); +} + +static int c67x00_ep_queue (struct usb_ep *_ep,struct usb_request *_req, +gfp_t gfp_flags) +{ +struct c67x00_udc_ep *ep; +struct c67x00_request *req; +unsigned long flags; +int request =0 ; +struct c67x00_udc *dev; + + req = container_of(_req, struct c67x00_request, req); +if (unlikely + (!_req || !_req->complete || !_req->buf + || !list_empty (&req->queue))) { +printk (KERN_WARNING "bad params\n" ); +return -EINVAL ; + } + + ep = container_of(_ep, struct c67x00_udc_ep, ep); +if (unlikely (!_ep)) { +dev_warn (sie_dev (ep->udc->sie),"bad ep\n" ); +return -EINVAL ; + } + + dev = ep->udc; +if (unlikely + (!dev->driver || dev->gadget.speed ==USB_SPEED_UNKNOWN )) { +dev_warn (sie_dev (ep->udc->sie),"bogus device state\n" ); +return -ESHUTDOWN ; + } + +if (ep->ep_num ==0 ) { +/* The gadget driver returns the descriptors through this way */ +if (!c67x00_udc_parse_descriptor (dev, req)) { + req->req.actual = req->req.length; +return -EINVAL ; + }else { + req->req.status =0 ; + req->req.actual = req->req.length; + req->req.complete (&ep->ep, &req->req); +return 0 ; + } + + } + +spin_lock_irqsave (&dev->lock, flags); + + _req->status = -EINPROGRESS ; + _req->actual =0 ; + +/* Start I/O queue if the list was empty */ +if (list_empty (&ep->queue) && !ep->stopped) + request =1 ; + +/* Add the request to the queue of the endpoint */ +list_add_tail (&req->queue, &ep->queue); + +if (request) +c67x00_udc_schedule_io_irq (ep); + +spin_unlock_irqrestore (&dev->lock, flags); + +return 0 ; +} + + +static int c67x00_ep_dequeue (struct usb_ep *_ep,struct usb_request *_req) +{ +struct c67x00_udc_ep *ep; +struct c67x00_request *req; +unsigned long flags; + + ep = container_of(_ep, struct c67x00_udc_ep, ep); +if (!_ep || ep->ep_num ==0 ) +return -EINVAL ; + +dev_dbg (sie_dev (ep->udc->sie),"dequeue %s\n" , _ep->name); + +spin_lock_irqsave (&ep->udc->lock, flags); + +/* make sure it's actually queued on this endpoint */ +list_for_each_entry (req, &ep->queue, queue) { +if (&req->req == _req) +break ; + } +if (&req->req != _req) { +spin_unlock_irqrestore (&ep->udc->lock, flags); +return -EINVAL ; + } + +c67x00_udc_done (ep, req, -ECONNRESET ); + +spin_unlock_irqrestore (&ep->udc->lock, flags); +return 0 ; +} + + + +static int c67x00_ep_set_halt (struct usb_ep *_ep,int value) +{ +printk (KERN_WARNING "c67x00-udc : ep set_halt %s\n" , _ep->name); +return -ENODEV ; +} + +static void c67x00_ep_fifo_flush (struct usb_ep *_ep) +{ +printk (KERN_WARNING "c67x00-udc : ep fifo_flush %s\n" , _ep->name); +} + + +static const struct usb_ep_ops c67x00_ep_ops = { + .enable = c67x00_ep_enable, + .disable = c67x00_ep_disable, + + .alloc_request = c67x00_ep_alloc_request, + .free_request = c67x00_ep_free_request, + + .queue = c67x00_ep_queue, + .dequeue = c67x00_ep_dequeue, + + .set_halt = c67x00_ep_set_halt, + .fifo_flush = c67x00_ep_fifo_flush, +}; + +/* -------------------------------------------------------------------------- */ + +void c67x00_udc_msg_received (struct c67x00_sie *sie,u16 msg) +{ +struct c67x00_udc *udc = sie->private_data; +u16 EPx_msg_mask =/*SUSB_EP0_MSG | */ SUSB_EP1_MSG + |SUSB_EP2_MSG + |SUSB_EP3_MSG + |SUSB_EP4_MSG + |SUSB_EP5_MSG + |SUSB_EP6_MSG + |SUSB_EP7_MSG ; + +if ((msg & EPx_msg_mask) !=0 ) { +int i, mask =0x01 ; + +for (i =0 ; i <C67X00_MAX_NB_END_POINTS ; i++, mask <<=1 ) +if (msg & mask) +c67x00_udc_done_irq (&udc->ep[i],0 ); + } + +if (msg &SUSB_RST_MSG ) { +int i; +dev_dbg (sie_dev (sie), +"udc_msg_rec (0x%04X) : SUSB_RST_MSG\n" , msg); + +for (i =0 ; i <C67X00_MAX_NB_END_POINTS ; i++) { +struct c67x00_udc_ep *ep = &udc->ep[i]; +if (i !=0 && ep->enable) { + ep->stopped =1 ; +c67x00_nuke_ep (ep, -ESHUTDOWN ); + } + } +if (udc->driver && udc->driver->disconnect) + udc->driver->disconnect (&udc->gadget); + } + +if (msg &SUSB_SOF_MSG ) { +dev_dbg (sie_dev (sie), +"udc_msg_rec (0x%04X) : SUSB_SOF_MSG\n" , msg); + } + +if (msg &SUSB_CFG_MSG ) { +dev_dbg (sie_dev (sie), +"udc_msg_rec (0x%04X) : SUSB_CFG_MSG\n" , msg); +/* the c67x00 BIOS only supports 1 configuration, +so it must be configuration 1 */ +c67x00_udc_set_configuration (sie, udc->config_nr); + } + +if (msg &SUSB_SUS_MSG ) { +dev_dbg (sie_dev (sie), +"udc_msg_rec (0x%04X) : SUSB_SUS_MSG\n" , msg); + } + +if (msg &SUSB_ID_MSG ) { +dev_dbg (sie_dev (sie), +"udc_msg_rec (0x%04X) : SUSB_ID_MSG\n" , msg); + } + +if (msg &SUSB_VBUS_MSG ) { +dev_dbg (sie_dev (sie), +"udc_msg_rec (0x%04X) : SUSB_VBUS_MSG\n" , msg); + } +} + + +/* +* This function is called from the interrupt handler in c67x00-drv.c +*/ +static void c67x00_udc_irq (struct c67x00_sie *sie,u16 int_status,u16 msg) +{ +u16 device_status; + +if (msg) +c67x00_udc_msg_received (sie, msg); + + device_status =c67x00_ll_usb_get_status (sie); + +if (int_status &SOFEOP_FLG (sie->sie_num)) +c67x00_ll_usb_clear_status (sie,SOF_EOP_IRQ_FLG ); + +if (int_status &RESET_FLG (sie->sie_num)) { +dev_info (sie_dev (sie),"sie%d : reset IRQ\n" , + sie->sie_num); + +/* Handle reset here */ +c67x00_ll_usb_clear_status (sie,RESET_IRQ_FLG ); + } + +if (int_status &DONE_FLG (sie->sie_num)) +dev_info (sie_dev (sie),"sie%d : done IRQ -> " +"device status 0x%04X\n" , + sie->sie_num, device_status); +} + + +int usb_gadget_register_driver (struct usb_gadget_driver *driver) +{ +struct c67x00_udc *udc = &controller; +int i, retval; + +if (!driver +/* || driver->speed < USB_SPEED_FULL*/ + || !driver->bind || !driver->setup) { + +printk (KERN_ERR +"c67x00 : invalid gadget driver provided\n" ); +return -EINVAL ; + } + + +spin_lock (&udc->lock); +if (!udc->sie || udc->driver) { +spin_unlock (&udc->lock); +return -EBUSY ; + } + + udc->driver = driver; + udc->gadget.dev.driver = &driver->driver; + udc->config_nr =1 ; + udc->top_str_id =0 ; +device_add (&udc->gadget.dev); + + driver->driver.bus =NULL ; + +dev_dbg (sie_dev (udc->sie),"Binding %s to SIE%d\n" , + driver->function, udc->sie->sie_num); + + retval = driver->bind (&udc->gadget); +if (retval) { +dev_warn (sie_dev (udc->sie),"Driver bind failed\n" ); + goto error; + } + +/* retrieve descriptors from gadget and program them in device */ + udc->driver->setup (&udc->gadget, + (struct usb_ctrlrequest *) + get_descriptor_device); + udc->driver->setup (&udc->gadget, + (struct usb_ctrlrequest *) + get_descriptor_config); + +for (i =0 ; i <= udc->top_str_id; i++) { + get_descriptor_string[2 ] = i; + udc->driver->setup (&udc->gadget, + (struct usb_ctrlrequest *) + get_descriptor_string); + } + +spin_unlock (&udc->lock); + +/* enable device */ +c67x00_ll_susb_init (udc->sie); + +return 0 ; + +error : + udc->driver =NULL ; + udc->gadget.dev.driver =NULL ; +device_del (&udc->gadget.dev); + +spin_unlock (&udc->lock); + +return retval; +} +EXPORT_SYMBOL (usb_gadget_register_driver); + +int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) +{ +struct c67x00_udc *udc = &controller; + +printk (KERN_WARNING "c67x00-udc : usb_gadget_unregister_driver\n" ); + +spin_lock (&udc->lock); +if (udc->driver != driver) { +spin_unlock (&udc->lock); +return -EINVAL ; + } + + udc->driver =NULL ; + + driver->unbind (&udc->gadget); +device_del (&udc->gadget.dev); + +c67x00_ll_susb_disable (udc->sie); + +spin_unlock (&udc->lock); +return 0 ; +} +EXPORT_SYMBOL (usb_gadget_unregister_driver); + +/* -------------------------------------------------------------------------- */ + +int c67x00_udc_probe (struct c67x00_sie *sie) +{ +struct c67x00_udc *udc = &controller; +unsigned long flags; +int i; + +if (udc->sie) { +dev_err (sie_dev (sie), +"Only 1 peripheral port supported, check sie_config\n" ); +return -EBUSY ; + } + +spin_lock_init (&udc->lock); +INIT_WORK (&udc->io_work, c67x00_udc_io_work); +INIT_LIST_HEAD (&udc->gadget.ep_list); + + udc->gadget.ep0 = &udc->ep[0 ].ep; +INIT_LIST_HEAD (&udc->gadget.ep0->ep_list); +for (i =0 ; i <C67X00_MAX_NB_END_POINTS ; i++) { +struct c67x00_udc_ep *ep = &udc->ep[i]; +if (i !=0 ) { +INIT_LIST_HEAD (&udc->ep[i].ep.ep_list); +list_add_tail (&udc->ep[i].ep.ep_list, + &udc->gadget.ep_list); + } + ep->ep.name = c67x00_ep_name[i]; + ep->ep.ops = &c67x00_ep_ops; + ep->enable =0 ; + ep->start_io =0 ; +if (i ==0 ) + ep->ep.maxpacket =8 ; +else +/* Size is set when endpoint is enabled */ + ep->ep.maxpacket =512 ; + ep->ep_num = i; + ep->udc = udc; +INIT_LIST_HEAD (&ep->queue); + } + + udc->sie = sie; + udc->gadget.dev.parent = &sie->dev->pdev->dev; +device_initialize (&udc->gadget.dev); + +spin_lock_irqsave (&sie->lock, flags); + sie->private_data = udc; + sie->irq = c67x00_udc_irq; +spin_unlock_irqrestore (&sie->lock, flags); + +return 0 ; +} + +void c67x00_udc_remove (struct c67x00_sie *sie) +{ +struct c67x00_udc *udc = sie->private_data; + +if (!udc) { +dev_err (sie_dev (sie),"No udc found!\n" ); +return ; + } + +/* gadget driver must not be registered */ +BUG_ON (udc->driver !=NULL ); + +spin_lock (&udc->lock); + sie->private_data =NULL ; + udc->sie =NULL ; +spin_unlock (&udc->lock); +}
drivers/usb/c67x00/c67x00-udc.h new file +50 -0
50 lines not on this page · raw message
drivers/usb/c67x00/c67x00-drv.c +11 -0
@@ -42,6 +42,7 @@#include "c67x00.h" #include "c67x00-hcd.h" +#include "c67x00-udc.h" static void c67x00_probe_sie (struct c67x00_sie *sie,struct c67x00_device *dev,int sie_num)
@@ -56,6 +57,11 @@c67x00_hcd_probe (sie);break ; +case C67X00_SIE_PERIPHERAL_A : +case C67X00_SIE_PERIPHERAL_B : +c67x00_udc_probe (sie); +break ; +case C67X00_SIE_UNUSED :dev_info (sie_dev (sie),"Not using SIE %d as requested\n" , sie->sie_num);
@@ -76,6 +82,11 @@c67x00_hcd_remove (sie);break ; +case C67X00_SIE_PERIPHERAL_A : +case C67X00_SIE_PERIPHERAL_B : +c67x00_udc_remove (sie); +break ; +default :break ; }
drivers/usb/c67x00/c67x00-ll-hpi.c +201 -0
207 lines not on this page · raw message
drivers/usb/c67x00/c67x00.h +21 -0
@@ -284,6 +284,27 @@void c67x00_ll_husb_init_host_port (struct c67x00_sie *sie);void c67x00_ll_husb_reset_port (struct c67x00_sie *sie,int port); +/* Slave specific functions */ +void c67x00_ll_susb_init (struct c67x00_sie *sie); +void c67x00_ll_susb_disable (struct c67x00_sie *sie); +void c67x00_ll_set_ep_ctrl_reg (struct c67x00_sie *sie,int ep_num,u16 val); +void c67x00_ll_set_ep_packet_size_reg (struct c67x00_sie *sie,int ep_num, +u16 val); +u16 c67x00_ll_get_device_ep_status (struct c67x00_sie *sie,int ep); +void c67x00_ll_set_device_ep_status (struct c67x00_sie *sie,int ep,u16 value); +void c67x00_ll_set_device_descriptor_location (struct c67x00_sie *sie, +u16 address); +void c67x00_ll_set_configuration_descriptor_location (struct c67x00_sie *sie, +u16 address); +void c67x00_ll_set_string_descriptor_location (struct c67x00_sie *sie, +u16 address); +int c67x00_ll_susb_start_send (struct c67x00_sie *sie,int ep, +void *data,int len); +int c67x00_ll_susb_start_receive (struct c67x00_sie *sie,int ep,int len); +int c67x00_ll_susb_get_transfer_status (struct c67x00_sie *sie,int ep); +void c67x00_ll_susb_receive (struct c67x00_sie *sie,int ep, +void *data,int len); +/* Called by c67x00_irq to handle lcp interrupts */ void c67x00_ll_irq (struct c67x00_device *dev,u16 int_status);
drivers/usb/c67x00/Makefile +5 -2
@@ -6,6 +6,9 @@ EXTRA_CFLAGS += -DDEBUG endif -obj-$(CONFIG_USB_C67X00_HCD) += c67x00.o +obj-$(CONFIG_USB_C67X00_DRV) += c67x00.o -c67x00-objs := c67x00-drv.o c67x00-ll-hpi.o c67x00-hcd.o c67x00-sched.o +c67x00-y += c67x00-drv.o c67x00-ll-hpi.o + +c67x00-$(CONFIG_USB_C67X00_HCD) += c67x00-hcd.o c67x00-sched.o +c67x00-$(CONFIG_USB_GADGET_C67X00) += c67x00-udc.o
drivers/usb/gadget/Kconfig +7 -0
@@ -324,6 +324,13 @@ depends on USB_GADGET_AT91 default USB_GADGET +config USB_GADGET_C67X00 + boolean "Cypress C67X00 Gadget support" + depends on USB_C67X00_DRV + select USB_GADGET_SELECTED + help + This enables the gadget functionality of the Cypress C67X00. + config USB_GADGET_DUMMY_HCD boolean "Dummy HCD (DEVELOPMENT)" depends on (USB=y || (USB=m && USB_GADGET=m)) && EXPERIMENTAL
drivers/usb/gadget/gadget_chips.h +8 -0
@@ -147,6 +147,12 @@#define gadget_is_m66592 (g) 0#endif +#ifdef CONFIG_USB_GADGET_C67X00 +#define gadget_is_c67x00 (g) !strcmp("c67x00_udc", (g)->name) +#else +#define gadget_is_c67x00 (g) 0 +#endif +// CONFIG_USB_GADGET_SX2 // CONFIG_USB_GADGET_AU1X00
@@ -212,5 +218,7 @@return 0x20 ; else if (gadget_is_m66592(gadget)) return 0x21; + else if (gadget_is_c67x00(gadget)) + return 0x22;return -ENOENT ; }
drivers/usb/host/Kconfig +0 -12
@@ -261,15 +261,3 @@ To compile this driver as a module, choose M here: the module will be called r8a66597-hcd. -config USB_C67X00_HCD - tristate "Cypress C67x00 HCD support" - depends on USB - help - The Cypress C67x00 (EZ-Host/EZ-OTG) chips are dual-role - host/peripheral/OTG USB controllers. - - Enable this option to support this chip in host controller mode. - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called c67x00.
drivers/usb/Makefile +1 -1
@@ -17,7 +17,7 @@ obj-$(CONFIG_USB_U132_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ -obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ +obj-$(CONFIG_USB_C67X00_DRV) += c67x00/ obj-$(CONFIG_USB_ACM) += class/ obj-$(CONFIG_USB_PRINTER) += class/
drivers/usb/Kconfig +2 -0
@@ -91,6 +91,8 @@ source "drivers/usb/host/Kconfig" +source "drivers/usb/c67x00/Kconfig" + source "drivers/usb/class/Kconfig" source "drivers/usb/storage/Kconfig"
drivers/usb/c67x00/Kconfig new file +21 -0
@@ -0,0 +1,21 @@ +# +# Cypress C67x00 USB controller +# +config USB_C67X00_DRV + tristate "Cypress C67x00 support" + # only allowed to be =y if both USB!=m and USB_GADGET!=m + depends on (!USB && USB_GADGET) || (!USB_GADGET && USB) || (USB && USB_GADGET) + help + The Cypress C67x00 (EZ-Host/EZ-OTG) chips are dual-role + host/peripheral USB controllers. + + To compile this driver as a module, choose M here: the + module will be called c67x00. + +config USB_C67X00_HCD + bool "Cypress C67X00 HCD support" + depends on USB && USB_C67X00_DRV + default y + help + Enable this option to support the Cypress C67x00 in host + controller mode.
--
Bye, Peter Korsgaard