[patch 2/4] Base support for the Cypress c67x00 multi-role USB controllers
Peter Korsgaard <[email protected]>
| Newsgroups | gmane.linux.usb.devel |
|---|---|
| Message-ID | <[email protected]> |
usb-c67x00-base.patch
(text/plain, 36.4 KB)
Base infrastucture support for the Cypress c67x00 host/peripheral USB controllers. The controllers contain a 16bit microcontroller, which handles the lowlevel USB signalling. Communication with the controller is done through the link control protocol (LCP) over either HPI, SPI or HSS; Sofar only HPI is supported. Signed-off-by: Peter Korsgaard <[email protected]> --- drivers/usb/Kconfig | 2 drivers/usb/Makefile | 2 drivers/usb/c67x00/Kconfig | 13 drivers/usb/c67x00/Makefile | 13 drivers/usb/c67x00/c67x00-drv.c | 267 +++++++++++++++++++ drivers/usb/c67x00/c67x00-drv.h | 95 ++++++ drivers/usb/c67x00/c67x00-ll-hpi.c | 509 +++++++++++++++++++++++++++++++++++++ drivers/usb/c67x00/c67x00-ll-hpi.h | 98 +++++++ drivers/usb/c67x00/c67x00-memmap.h | 34 ++ drivers/usb/c67x00/c67x00.h | 93 ++++++ include/linux/usb/c67x00.h | 39 ++ 11 files changed, 1165 insertions(+) Index: linux/drivers/usb/c67x00/Kconfig =================================================================== --- /dev/null +++ linux/drivers/usb/c67x00/Kconfig @@ -0,0 +1,13 @@ +# +# Cypress C67x00 USB controller +# +config USB_C67X00_DRV + tristate "Cypress C67x00 support" + default n + 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. + Index: linux/drivers/usb/c67x00/Makefile =================================================================== --- /dev/null +++ linux/drivers/usb/c67x00/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for Cypress C67X00 USB Controller +# + +ifeq ($(CONFIG_USB_DEBUG),y) + EXTRA_CFLAGS += -DDEBUG +endif + +obj-$(CONFIG_USB_C67X00_DRV) += c67x00.o + +c67x00-y += c67x00-drv.o +c67x00-y += c67x00-ll-hpi.o + Index: linux/drivers/usb/c67x00/c67x00-drv.c =================================================================== --- /dev/null +++ linux/drivers/usb/c67x00/c67x00-drv.c @@ -0,0 +1,267 @@ +/* + * c67x00-drv.c: Cypress C67X00 USB Common infrastructure + * + * Copyright (C) 2006-2007 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 (setup_sie) + * and the corresponding subdriver is initialized (probe_sie). + */ + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/usb.h> +#include <asm/io.h> + +#include "c67x00.h" +#include "c67x00-drv.h" +#include "c67x00-ll-hpi.h" + +static struct platform_driver c67x00_driver; + +/* -------------------------------------------------------------------------- */ + +static void setup_sie(struct c67x00_sie *sie, + struct c67x00_drv *drv, int sie_num) +{ + static unsigned int id = 0; + + /* Fill in needed attributes */ + sie->pdev = platform_device_register_simple("c67x00_sie", + id++, NULL, 0); + /* driver used in hub.c: hub_port_init */ + sie->pdev->dev.driver = &c67x00_driver.driver; + spin_lock_init(&sie->lock); + sie->drv = drv; + sie->sie_num = sie_num; + sie->mode = c67x00_sie_config(drv->pdata->sie_config, sie_num); +} + +static void teardown_sie(struct c67x00_sie *sie) +{ + sie->pdev->dev.driver = NULL; + platform_device_unregister(sie->pdev); +} + +/* -------------------------------------------------------------------------- */ + +static void probe_sie(struct c67x00_sie *sie) +{ + switch (c67x00_sie_config(sie->drv->pdata->sie_config, sie->sie_num)) { + 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", + c67x00_sie_config(sie->drv->pdata->sie_config, + sie->sie_num), sie->sie_num); + break; + } +} + +static void remove_sie(struct c67x00_sie *sie) +{ + switch (c67x00_sie_config(sie->drv->pdata->sie_config, sie->sie_num)) { + default: + break; + } +} + +/* -------------------------------------------------------------------------- */ + +static irqreturn_t c67x00_irq(int irq, void *__drv) +{ + struct c67x00_drv *drv = __drv; + int i, count = 8; + + drv->int_status = c67x00_hpi_status(drv); + if (!drv->int_status) + return IRQ_NONE; + + while (drv->int_status != 0 && (count-- >= 0)) { + c67x00_ll_irq(drv); + for (i = 0; i < C67X00_SIES; i++) { + spin_lock(&drv->sie[i].lock); + if (drv->int_status & SIEMSG_FLAG(i)) { + u16 msg; + msg = c67x00_ll_get_siemsg(drv, i); + if (drv->sie[i].msg_received) + drv->sie[i].msg_received(&drv->sie[i], + msg); + } + if (drv->sie[i].irq) + drv->sie[i].irq(irq, &drv->sie[i]); + spin_unlock(&drv->sie[i].lock); + } + drv->int_status = c67x00_hpi_status(drv); + } + + if (drv->int_status) + dev_warn(&drv->pdev->dev, "Not all interrupts handled! " + "status = 0x%04x\n", drv->int_status); + + return IRQ_HANDLED; +} + +/* -------------------------------------------------------------------------- */ + +static int __devinit usb_c67x00_drv_probe(struct platform_device *pdev) +{ + struct c67x00_drv *drv; + 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 = (struct c67x00_platform_data*)pdev->dev.platform_data; + if (!pdata) + return -ENODEV; + + drv = kzalloc(sizeof(*drv), GFP_KERNEL); + if (!drv) + 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; + } + drv->hpi.base = ioremap(res->start, res->end - res->start + 1); + if (!drv->hpi.base) { + dev_err(&pdev->dev, "Unable to map HPI registers\n"); + ret = -EIO; + goto map_failed; + } + + spin_lock_init(&drv->hw_lock); + drv->hpi.regstep = pdata->hpi_regstep; + drv->pdata = pdev->dev.platform_data; + drv->pdev = pdev; + + for (i = 0; i < C67X00_SIES; i++) + setup_sie(&drv->sie[i], drv, i); + + c67x00_ll_init(drv); + c67x00_ll_hpi_reg_init(drv); + + ret = request_irq(res2->start, c67x00_irq, 0, pdev->name, drv); + if (ret) { + dev_err(&pdev->dev, "Cannot claim IRQ\n"); + goto request_irq_failed; + } + + c67x00_ll_reset(drv); + + for (i = 0; i < C67X00_SIES; i++) + probe_sie(&drv->sie[i]); + + platform_set_drvdata(pdev, drv); + + return 0; + +request_irq_failed: + iounmap(drv->hpi.base); +map_failed: + release_mem_region(res->start, res->end - res->start + 1); +request_mem_failed: + kfree(drv); + + return ret; +} + +static int __devexit usb_c67x00_drv_remove(struct platform_device *pdev) +{ + struct c67x00_drv *drv = platform_get_drvdata(pdev); + struct resource *res; + int i; + + for (i = 0; i < C67X00_SIES; i++) { + remove_sie(&drv->sie[i]); + teardown_sie(&drv->sie[i]); + } + + c67x00_ll_release(drv); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res) + free_irq(res->start, drv); + + iounmap(drv->hpi.base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + release_mem_region(res->start, res->end - res->start + 1); + + kfree(drv); + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static struct platform_driver c67x00_driver = { + .probe = usb_c67x00_drv_probe, + .remove = usb_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"); +MODULE_DESCRIPTION("Cypress C67X00 USB Controller Driver"); +MODULE_LICENSE("GPL"); Index: linux/drivers/usb/c67x00/c67x00-drv.h =================================================================== --- /dev/null +++ linux/drivers/usb/c67x00/c67x00-drv.h @@ -0,0 +1,95 @@ +/* + * c67x00-drv.h: Cypress C67X00 USB Common infrastructure API + * + * Copyright (C) 2006-2007 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_DRV_H +#define _USB_C67X00_DRV_H + +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/platform_device.h> +#include <linux/usb/c67x00.h> +#include <linux/workqueue.h> +#include "c67x00-ll-hpi.h" + +struct c67x00_drv; + +/** + * struct c67x00_sie - Common data associated with an SIE + * @lock: lock to protect this struct + * @private_data: subdriver dependent data + * @pdev: platform device associated with this SIE, created in c67x00-drv.c + * @irq: subdriver depenent irq handler, set NULL when not used + * @msg_received: called when an SIEmsg has been received + * @drv: link to common driver structure + * @sie_num: SIE number on chip, starting from 0 + * @mode: SIE mode (host/peripheral/otg/not used) + * + * Each SIE has a separate platform_device associated with it, because the + * hcd needs such a device for itself (TODO: use a struct device instead + * of a new platform device). + */ +struct c67x00_sie { + /* Entries to be used by the subdrivers */ + spinlock_t lock; /* protect this structure */ + void *private_data; + struct platform_device *pdev; + irqreturn_t(*irq) (int irq, struct c67x00_sie * sie); + void (*msg_received) (struct c67x00_sie * sie, u16 msg); + + /* Read only: */ + struct c67x00_drv *drv; + int sie_num; + int mode; +}; + +#define sie_dev(s) (&(s)->pdev->dev) + +struct c67x00_hpi { + void __iomem *base; + int regstep; +}; + +#define C67X00_SIES 2 +#define C67X00_PORTS 2 + +/** + * struct c67x00_drv - Common data structure associated with a c67x00 instance + * @hpi: hpi addresses + * @sie: array of sie's on this chip + * @pdata: configuration provided by the platform + * @hw_lock: hardware lock + * @int_status: interrupt status register, only valid in_interrupt() + * @lcp: lcp dependent data + */ +struct c67x00_drv { + struct c67x00_hpi hpi; + struct c67x00_sie sie[C67X00_SIES]; + struct platform_device *pdev; + struct c67x00_platform_data *pdata; + spinlock_t hw_lock; + + u16 int_status; + struct lcp lcp; +}; + +#endif /* _USB_C67X00_DRV_H */ Index: linux/include/linux/usb/c67x00.h =================================================================== --- /dev/null +++ linux/include/linux/usb/c67x00.h @@ -0,0 +1,39 @@ +/* + * usb_c67x00.h: platform definitions for the Cypress C67X00 USB chip + * + * Copyright (C) 2006-2007 Barco N.V. + * + * 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 _LINUX_USB_C67X00_H +#define _LINUX_USB_C67X00_H + +/* SIE configuration */ +#define C67X00_SIE_UNUSED 0 + +#define c67x00_sie_config(config, n) (((config)>>(4*(n)))&0x3) + +#define C67X00_SIE1_UNUSED (C67X00_SIE_UNUSED << 0) + +#define C67X00_SIE2_UNUSED (C67X00_SIE_UNUSED << 4) + +struct c67x00_platform_data { + int sie_config; /* SIEs config (C67X00_SIEx_*) */ + unsigned long hpi_regstep; /* Step between HPI registers */ +}; + +#endif /* _LINUX_USB_C67X00_H */ Index: linux/drivers/usb/Kconfig =================================================================== --- linux.orig/drivers/usb/Kconfig +++ linux/drivers/usb/Kconfig @@ -86,6 +86,8 @@ source "drivers/usb/host/Kconfig" +source "drivers/usb/c67x00/Kconfig" + source "drivers/usb/class/Kconfig" source "drivers/usb/storage/Kconfig" Index: linux/drivers/usb/Makefile =================================================================== --- linux.orig/drivers/usb/Makefile +++ linux/drivers/usb/Makefile @@ -18,6 +18,8 @@ obj-$(CONFIG_ETRAX_USB_HOST) += host/ obj-$(CONFIG_USB_OHCI_AT91) += host/ +obj-$(CONFIG_USB_C67X00_DRV) += c67x00/ + obj-$(CONFIG_USB_ACM) += class/ obj-$(CONFIG_USB_PRINTER) += class/ Index: linux/drivers/usb/c67x00/c67x00-ll-hpi.c =================================================================== --- /dev/null +++ linux/drivers/usb/c67x00/c67x00-ll-hpi.c @@ -0,0 +1,509 @@ +/* + * c67x00-ll-hpi.c: Cypress C67X00 USB Low level interface using HPI + * + * Copyright (C) 2006-2007 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 <asm/io.h> +#include <asm/byteorder.h> +#include "c67x00-drv.h" +#include "c67x00.h" + +/* -------------------------------------------------------------------------- */ +/* Interface definitions */ + +#define COMM_ACK 0x0FED +#define COMM_NAK 0xDEAD + +#define COMM_CTRL_REG_ADDR 0x01BC +#define COMM_CTRL_REG_DATA 0x01BE +#define COMM_CTRL_REG_LOGIC 0x01C0 +#define COMM_WRITE_CTRL_REG 0xCE03 +#define COMM_READ_CTRL_REG 0xCE02 + +#define COMM_RESET 0xFA50 +#define COMM_EXEC_INT 0xCE01 +#define COMM_INT_NUM 0x01C2 +/* Registers 0 to COMM_REGS-1 */ +#define COMM_R(x) (0x01C4 + 2 * (x)) + +#define HUSB_SIE_pCurrentTDPtr(x) ( (x) ? 0x01B2 : 0x01B0 ) +#define HUSB_SIE_pTDListDone_Sem(x) ( (x) ? 0x01B8 : 0x01B6 ) +#define HUSB_pEOT 0x01B4 + +/* Software interrupts */ +/* 114, 115: */ +#define HUSB_SIE_INIT_INT(x) ( (x) ? 0x0073 : 0x0072 ) +#define HUSB_RESET_INT 0x0074 /* 116 */ + +#define SUSB_INIT_INT 0x0071 +/* -------------------------------------------------------------------------- */ +/* HPI implementation */ + +/* HPI registers */ +#define HPI_DATA 0 +#define HPI_MAILBOX 1 +#define HPI_ADDR 2 +#define HPI_STATUS 3 + +/* These functions could also be implemented with SPI of HSS. + * This is currently not supported */ +static inline u16 hpi_read_reg(struct c67x00_drv *drv, int reg) +{ + return __raw_readw(drv->hpi.base + reg * drv->hpi.regstep); +} + +static inline void hpi_write_reg(struct c67x00_drv *drv, int reg, u16 value) +{ + __raw_writew(value, drv->hpi.base + reg * drv->hpi.regstep); +} + +static inline u16 hpi_read_word_nolock(struct c67x00_drv *drv, u16 reg) +{ + hpi_write_reg(drv, HPI_ADDR, reg); + return hpi_read_reg(drv, HPI_DATA); +} + +static inline u16 hpi_read_word(struct c67x00_drv *drv, u16 reg) +{ + u16 value; + unsigned long flags; + + spin_lock_irqsave(&drv->hw_lock, flags); + value = hpi_read_word_nolock(drv, reg); + spin_unlock_irqrestore(&drv->hw_lock, flags); + + return value; +} + +static inline void hpi_write_word_nolock(struct c67x00_drv *drv, u16 reg, + u16 value) +{ + hpi_write_reg(drv, HPI_ADDR, reg); + hpi_write_reg(drv, HPI_DATA, value); +} + +static inline void hpi_write_word(struct c67x00_drv *drv, u16 reg, u16 value) +{ + unsigned long flags; + + spin_lock_irqsave(&drv->hw_lock, flags); + hpi_write_word_nolock(drv, reg, value); + spin_unlock_irqrestore(&drv->hw_lock, flags); +} + +/* + * Only data is little endian, addr has cpu endianess + */ +static inline void hpi_write_words_le16(struct c67x00_drv *drv, u16 addr, + u16 * data, u16 count) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&drv->hw_lock, flags); + + hpi_write_reg(drv, HPI_ADDR, addr); + for (i = 0; i < count; i++) + hpi_write_reg(drv, HPI_DATA, cpu_to_le16(*data++)); + + spin_unlock_irqrestore(&drv->hw_lock, flags); +} + +/* + * Only data is little endian, addr has cpu endianess + */ +static inline void hpi_read_words_le16(struct c67x00_drv *drv, u16 addr, + u16 * data, u16 count) +{ + unsigned long flags; + int i; + spin_lock_irqsave(&drv->hw_lock, flags); + hpi_write_reg(drv, HPI_ADDR, addr); + for (i = 0; i < count; i++) + *data++ = le16_to_cpu(hpi_read_reg(drv, HPI_DATA)); + + spin_unlock_irqrestore(&drv->hw_lock, flags); +} + +static inline void hpi_set_bits(struct c67x00_drv *drv, u16 reg, u16 mask) +{ + u16 value; + unsigned long flags; + + spin_lock_irqsave(&drv->hw_lock, flags); + value = hpi_read_word_nolock(drv, reg); + hpi_write_word_nolock(drv, reg, value | mask); + spin_unlock_irqrestore(&drv->hw_lock, flags); +} + +static inline void hpi_clear_bits(struct c67x00_drv *drv, u16 reg, u16 mask) +{ + u16 value; + unsigned long flags; + + spin_lock_irqsave(&drv->hw_lock, flags); + value = hpi_read_word_nolock(drv, reg); + hpi_write_word_nolock(drv, reg, value & ~mask); + spin_unlock_irqrestore(&drv->hw_lock, flags); +} + +static inline u16 hpi_recv_mbox(struct c67x00_drv *drv) +{ + u16 value; + unsigned long flags; + + spin_lock_irqsave(&drv->hw_lock, flags); + value = hpi_read_reg(drv, HPI_MAILBOX); + spin_unlock_irqrestore(&drv->hw_lock, flags); + + return value; +} + +static inline u16 hpi_send_mbox(struct c67x00_drv *drv, u16 value) +{ + unsigned long flags; + + spin_lock_irqsave(&drv->hw_lock, flags); + hpi_write_reg(drv, HPI_MAILBOX, value); + spin_unlock_irqrestore(&drv->hw_lock, flags); + + return value; +} + +u16 c67x00_hpi_status(struct c67x00_drv * drv) +{ + u16 value; + unsigned long flags; + + spin_lock_irqsave(&drv->hw_lock, flags); + value = hpi_read_reg(drv, HPI_STATUS); + spin_unlock_irqrestore(&drv->hw_lock, flags); + + return value; +} + +void c67x00_ll_hpi_reg_init(struct c67x00_drv *drv) +{ + int i; + + hpi_recv_mbox(drv); + c67x00_hpi_status(drv); + hpi_write_word(drv, HPI_IRQ_ROUTING_REG, 0); + + for (i=0; i<C67X00_SIES; i++) { + hpi_write_word(drv, SIEMSG_REG(i), 0); + hpi_read_word(drv, SIEMSG_REG(i)); + } +} + +void c67x00_ll_hpi_enable_sofeop(struct c67x00_sie *sie) +{ + hpi_set_bits(sie->drv, HPI_IRQ_ROUTING_REG, + SOFEOP_TO_HPI_EN(sie->sie_num)); +} + +void c67x00_ll_hpi_disable_sofeop(struct c67x00_sie *sie) +{ + hpi_clear_bits(sie->drv, HPI_IRQ_ROUTING_REG, + SOFEOP_TO_HPI_EN(sie->sie_num)); +} + +/* -------------------------------------------------------------------------- */ +/* Transactions */ + +static inline void ll_start(struct c67x00_drv *drv) +{ + INIT_COMPLETION(drv->lcp.msg_received); + mutex_lock(&drv->lcp.mutex); +} + +static inline u16 ll_recv_msg(struct c67x00_drv *drv) +{ + u16 res; + + res = wait_for_completion_timeout(&drv->lcp.msg_received, 5 * HZ); + INIT_COMPLETION(drv->lcp.msg_received); + WARN_ON(!res); + + return res; +} + +static inline void ll_release(struct c67x00_drv *drv) +{ + mutex_unlock(&drv->lcp.mutex); +} + +/* -------------------------------------------------------------------------- */ +/* General functions */ + +u16 c67x00_ll_get_siemsg(struct c67x00_drv *drv, int sie) +{ + return hpi_read_word(drv, SIEMSG_REG(sie)); +} + +void c67x00_ll_set_siemsg(struct c67x00_drv *drv, int sie, u16 val) +{ + hpi_write_word(drv, SIEMSG_REG(sie), val); +} + +u16 c67x00_ll_get_usb_ctl(struct c67x00_sie *sie) +{ + return hpi_read_word(sie->drv, USB_CTL_REG(sie->sie_num)); +} + +/* -------------------------------------------------------------------------- */ +/* Host specific functions */ + +void c67x00_ll_set_husb_eot(struct c67x00_drv *drv, u16 value) +{ + ll_start(drv); + hpi_write_word(drv, HUSB_pEOT, value); + ll_release(drv); +} + +static inline void c67x00_ll_husb_sie_init(struct c67x00_sie *sie) +{ + struct c67x00_drv *drv = sie->drv; + struct lcp_int_data data; + + c67x00_comm_exec_int(drv, HUSB_SIE_INIT_INT(sie->sie_num), &data); +} + +void c67x00_ll_husb_reset(struct c67x00_sie *sie, int port) +{ + struct c67x00_drv *drv = sie->drv; + struct lcp_int_data data; + + data.regs[0] = 50; /* Reset USB port for 50ms */ + data.regs[1] = port | (sie->sie_num << 1); + c67x00_comm_exec_int(drv, HUSB_RESET_INT, &data); +} + +void c67x00_ll_husb_set_current_td(struct c67x00_sie *sie, u16 addr) +{ + hpi_write_word(sie->drv, HUSB_SIE_pCurrentTDPtr(sie->sie_num), addr); +} + +u16 c67x00_ll_husb_get_current_td(struct c67x00_sie *sie) +{ + return hpi_read_word(sie->drv, HUSB_SIE_pCurrentTDPtr(sie->sie_num)); +} + +/** + * c67x00_ll_husb_clear_status - clear the host status bits + */ +void c67x00_ll_husb_clear_status(struct c67x00_sie *sie, u16 bits) +{ + hpi_write_word(sie->drv, HOST_STAT_REG(sie->sie_num), bits); +} + +u16 c67x00_ll_husb_get_status(struct c67x00_sie *sie) +{ + return hpi_read_word(sie->drv, HOST_STAT_REG(sie->sie_num)); +} + +u16 c67x00_ll_husb_get_frame(struct c67x00_sie * sie) +{ + return hpi_read_word(sie->drv, HOST_FRAME_REG(sie->sie_num)); +} + +void c67x00_ll_husb_init_host_port(struct c67x00_sie *sie) +{ + /* Set port into host mode */ + hpi_set_bits(sie->drv, USB_CTL_REG(sie->sie_num), HOST_MODE); + c67x00_ll_husb_sie_init(sie); + /* Clear interrupts */ + c67x00_ll_husb_clear_status(sie, HOST_STAT_MASK); + /* Check */ + if (!(hpi_read_word(sie->drv, USB_CTL_REG(sie->sie_num)) & HOST_MODE)) + dev_warn(sie_dev(sie), + "SIE %d not set to host mode\n", sie->sie_num); +} + +void c67x00_ll_husb_reset_port(struct c67x00_sie *sie, int port) +{ + /* Clear connect change */ + c67x00_ll_husb_clear_status(sie, PORT_CONNECT_CHANGE(port)); + + /* Enable interrupts */ + hpi_set_bits(sie->drv, HPI_IRQ_ROUTING_REG, + SOFEOP_TO_CPU_EN(sie->sie_num)); + hpi_set_bits(sie->drv, HOST_IRQ_EN_REG(sie->sie_num), + SOF_EOP_IRQ_EN | DONE_IRQ_EN); + + /* Enable pull down transistors */ + hpi_set_bits(sie->drv, USB_CTL_REG(sie->sie_num), PORT_RES_EN(port)); +} + +/* -------------------------------------------------------------------------- */ +void c67x00_ll_susb_init(struct c67x00_sie *sie) +{ + struct c67x00_drv *drv = sie->drv; + struct lcp_int_data data; + + data.regs[1] = 1; /* full speed */ + data.regs[2] = sie->sie_num + 1; + c67x00_comm_exec_int(drv, SUSB_INIT_INT, &data); + + hpi_clear_bits(drv, HPI_IRQ_ROUTING_REG, + SOFEOP_TO_HPI_EN(sie->sie_num)); + hpi_set_bits(drv, HPI_IRQ_ROUTING_REG, SOFEOP_TO_CPU_EN(sie->sie_num)); +} + +/* -------------------------------------------------------------------------- */ + +void c67x00_ll_irq(struct c67x00_drv *drv) +{ + if ((drv->int_status & MBX_OUT_FLG) == 0) + return; + + drv->lcp.last_msg = hpi_recv_mbox(drv); + complete(&drv->lcp.msg_received); +} + +/* -------------------------------------------------------------------------- */ + +u16 c67x00_comm_read_ctrl_reg(struct c67x00_drv * drv, u16 addr) +{ + unsigned long msg, res; + + ll_start(drv); + do { + hpi_write_word(drv, COMM_CTRL_REG_ADDR, addr); + hpi_send_mbox(drv, COMM_READ_CTRL_REG); + } while (!ll_recv_msg(drv)); + msg = drv->lcp.last_msg; + if (msg != COMM_ACK) { + dev_warn(&drv->pdev->dev, "COMM_READ_CTRL_REG didn't ACK!\n"); + res = 0; + } else + res = hpi_read_word(drv, COMM_CTRL_REG_DATA); + ll_release(drv); + return res; +} + +void c67x00_comm_exec_int(struct c67x00_drv *drv, u16 nr, + struct lcp_int_data *data) +{ + ll_start(drv); + do { + int i; + hpi_write_word(drv, COMM_INT_NUM, nr); + for (i = 0; i < COMM_REGS; i++) + hpi_write_word(drv, COMM_R(i), data->regs[i]); + hpi_send_mbox(drv, COMM_EXEC_INT); + } while (!ll_recv_msg(drv)); + ll_release(drv); +} + +/* -------------------------------------------------------------------------- */ + +void c67x00_ll_reset(struct c67x00_drv *drv) +{ + ll_start(drv); + do { + hpi_send_mbox(drv, COMM_RESET); + } while (!ll_recv_msg(drv)); + ll_release(drv); +} + +/* -------------------------------------------------------------------------- */ + +/** + * c67x00_write_mem_le16 - write into c67x00 memory + * Only data is little endian, addr has cpu endianess. + */ +void c67x00_write_mem_le16(struct c67x00_drv *drv, u16 addr, + int len, char *data) +{ + /* Sanity check */ + if (addr + len > 0xffff) { + dev_err(&drv->pdev->dev, + "Trying to write beyond writable region!\n"); + return; + } + + if (addr & 0x01) { + /* unaligned access */ + u16 tmp; + tmp = hpi_read_word(drv, addr - 1); + tmp = (tmp & 0x00ff) | (*data++ << 8); + hpi_write_word(drv, addr - 1, tmp); + addr++; + len--; + } + + hpi_write_words_le16(drv, addr, (u16 *) data, len / 2); + data += len & ~0x01; + addr += len & ~0x01; + len &= 0x01; + + if (len) { + u16 tmp; + tmp = hpi_read_word(drv, addr); + tmp = (tmp & 0xff00) | (*data++); + hpi_write_word(drv, addr, tmp); + addr++; + len--; + } + +} + +/** + * c67x00_read_mem_le16 - read from c67x00 memory + * Only data is little endian, addr has cpu endianess. + */ +void c67x00_read_mem_le16(struct c67x00_drv *drv, u16 addr, int len, char *data) +{ + if (addr & 0x01) { + /* unaligned access */ + u16 tmp; + tmp = hpi_read_word(drv, addr - 1); + *data++ = (tmp >> 8) & 0x00ff; + addr++; + len--; + } + + hpi_read_words_le16(drv, addr, (u16 *) data, len / 2); + data += len & ~0x01; + addr += len & ~0x01; + len &= 0x01; + + if (len) { + u16 tmp; + tmp = hpi_read_word(drv, addr); + *data++ = tmp & 0x00ff; + addr++; + len--; + } +} + +/* -------------------------------------------------------------------------- */ + +void c67x00_ll_init(struct c67x00_drv *drv) +{ + mutex_init(&drv->lcp.mutex); + init_completion(&drv->lcp.msg_received); +} + +void c67x00_ll_release(struct c67x00_drv *drv) +{ +} Index: linux/drivers/usb/c67x00/c67x00-ll-hpi.h =================================================================== --- /dev/null +++ linux/drivers/usb/c67x00/c67x00-ll-hpi.h @@ -0,0 +1,98 @@ +/* + * c67x00-ll-hpi.h: Cypress C67X00 USB Low level interface using HPI + * + * Copyright (C) 2006-2007 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_LL_HPI_H +#define _USB_C67X00_LL_HPI_H + +#include <linux/completion.h> +#include <linux/mutex.h> + +struct c67x00_sie; +struct c67x00_drv; + +struct lcp { + /* Internal use only */ + struct mutex mutex; + struct completion msg_received; + u16 last_msg; +}; + +#define COMM_REGS 14 + +struct lcp_int_data { + u16 regs[COMM_REGS]; +}; + +/* ------------------------------------------------------------------------- */ +/* HPI */ + +u16 c67x00_hpi_status(struct c67x00_drv *drv); +void c67x00_ll_hpi_reg_init(struct c67x00_drv *drv); +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_drv *drv, int sie); +void c67x00_ll_set_siemsg(struct c67x00_drv *drv, int sie, u16 val); +u16 c67x00_ll_get_usb_ctl(struct c67x00_sie *sie); + +/* ------------------------------------------------------------------------- */ +/* Host specific functions */ + +void c67x00_ll_set_husb_eot(struct c67x00_drv *drv, 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); +void c67x00_ll_husb_clear_status(struct c67x00_sie *sie, u16 bits); +u16 c67x00_ll_husb_get_status(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); + +/* ------------------------------------------------------------------------- */ +/* Slave specific functions */ + +void c67x00_ll_susb_init(struct c67x00_sie *sie); + +/* ------------------------------------------------------------------------- */ + +void c67x00_write_mem_le16(struct c67x00_drv *drv, u16 addr, + int len, char *data); +void c67x00_read_mem_le16(struct c67x00_drv *drv, u16 addr, + int len, char *data); + +u16 c67x00_comm_read_ctrl_reg(struct c67x00_drv *drv, u16 addr); + +void c67x00_comm_exec_int(struct c67x00_drv *drv, u16 nr, + struct lcp_int_data *data); + +/* Called by c67x00_irq to handle lcp interrupts */ +void c67x00_ll_irq(struct c67x00_drv *drv); + +void c67x00_ll_init(struct c67x00_drv *drv); +void c67x00_ll_release(struct c67x00_drv *drv); +void c67x00_ll_reset(struct c67x00_drv *drv); + +#endif /* _USB_C67X00_LL_HPI_H */ Index: linux/drivers/usb/c67x00/c67x00.h =================================================================== --- /dev/null +++ linux/drivers/usb/c67x00/c67x00.h @@ -0,0 +1,93 @@ +/* + * c67x00.h: Cypress C67X00 USB register and field definitions + * + * Copyright (C) 2006-2007 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_C67X00_H +#define _USB_C67X00_C67X00_H + +/* Processor control registers */ +/* =========================== */ + +/* 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 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 DONE_IRQ_EN 0x0001 /* Done Interrupt Enable */ + +/* Host n status register */ +#define HOST_STAT_REG(x) ( (x) ? 0xC0B0 : 0xC090 ) + +#define HOST_STAT_MASK 0x02FD +#define SOF_EOP_IRQ_FLG 0x0200 +#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 + +/* HPI registers */ +/* ============= */ + +/* HPI Status register */ +#define SOFEOP_FLG(x) (1 << ( (x) ? 12 : 10 )) +#define SIEMSG_FLAG(x) (1 << (4 + (x))) +#define MBX_OUT_FLG 0x0001 /* Message out available */ + +/* Interrupt routing register */ +#define HPI_IRQ_ROUTING_REG 0x0142 + +#define SOFEOP_TO_HPI_EN(x) ( (x) ? 0x2000 : 0x0800 ) +#define SOFEOP_TO_CPU_EN(x) ( (x) ? 0x1000 : 0x0400 ) + +/* SIE msg registers */ +#define SIEMSG_REG(x) ( (x) ? 0x0148 : 0x0144 ) + +#define HUSB_TDListDone 0x1000 + +#endif /* _USB_C67X00_C67X00_H */ Index: linux/drivers/usb/c67x00/c67x00-memmap.h =================================================================== --- /dev/null +++ linux/drivers/usb/c67x00/c67x00-memmap.h @@ -0,0 +1,34 @@ +/* + * c67x00-memmap.h: Cypress C67X00 Internal memory map + * + * Copyright (C) 2006-2007 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_MEMMAP_H +#define _USB_C67X00_MEMMAP_H + +#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) + +#endif /* _USB_C67X00_MEMMAP_H */ -- Bye, Peter Korsgaard ------------------------------------------------------------------------- This SF.net email is sponsored by DB2 Express Download DB2 Express C - the FREE version of DB2 express and take control of your XML. No limits. Just data. Click to get it now. http://sourceforge.net/powerbar/db2/ _______________________________________________ [email protected] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel