* [PATCH 2/5] USB: Add Cypress c67x00 low level interface code
From: Grant Likely @ 2007-11-24 0:24 UTC (permalink / raw)
To: linux-usb-devel, akpm, dbrownell, gregkh, linuxppc-dev
In-Reply-To: <20071124001203.25361.99294.stgit@trillian.cg.shawcable.net>
From: Grant Likely <grant.likely@secretlab.ca>
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 c67x00 device.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
drivers/usb/c67x00/c67x00-ll-hpi.c | 512 ++++++++++++++++++++++++++++++++++++
drivers/usb/c67x00/c67x00.h | 236 +++++++++++++++++
2 files changed, 748 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/c67x00/c67x00-ll-hpi.c b/drivers/usb/c67x00/c67x00-ll-hpi.c
new file mode 100644
index 0000000..5e1bd73
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-ll-hpi.c
@@ -0,0 +1,512 @@
+/*
+ * 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.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_device *dev, int reg)
+{
+ return __raw_readw(dev->hpi.base + reg * dev->hpi.regstep);
+}
+
+static inline void hpi_write_reg(struct c67x00_device *dev, int reg, u16 value)
+{
+ __raw_writew(value, dev->hpi.base + reg * dev->hpi.regstep);
+}
+
+static inline u16 hpi_read_word_nolock(struct c67x00_device *dev, u16 reg)
+{
+ hpi_write_reg(dev, HPI_ADDR, reg);
+ return hpi_read_reg(dev, HPI_DATA);
+}
+
+static inline u16 hpi_read_word(struct c67x00_device *dev, u16 reg)
+{
+ u16 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ value = hpi_read_word_nolock(dev, reg);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ return value;
+}
+
+static inline void hpi_write_word_nolock(struct c67x00_device *dev, u16 reg,
+ u16 value)
+{
+ hpi_write_reg(dev, HPI_ADDR, reg);
+ hpi_write_reg(dev, HPI_DATA, value);
+}
+
+static inline void hpi_write_word(struct c67x00_device *dev, u16 reg, u16 value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ hpi_write_word_nolock(dev, reg, value);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+/*
+ * Only data is little endian, addr has cpu endianess
+ */
+static inline void hpi_write_words_le16(struct c67x00_device *dev, u16 addr,
+ u16 * data, u16 count)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+
+ hpi_write_reg(dev, HPI_ADDR, addr);
+ for (i = 0; i < count; i++)
+ hpi_write_reg(dev, HPI_DATA, cpu_to_le16(*data++));
+
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+/*
+ * Only data is little endian, addr has cpu endianess
+ */
+static inline void hpi_read_words_le16(struct c67x00_device *dev, u16 addr,
+ u16 * data, u16 count)
+{
+ unsigned long flags;
+ int i;
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ hpi_write_reg(dev, HPI_ADDR, addr);
+ for (i = 0; i < count; i++)
+ *data++ = le16_to_cpu(hpi_read_reg(dev, HPI_DATA));
+
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+static inline void hpi_set_bits(struct c67x00_device *dev, u16 reg, u16 mask)
+{
+ u16 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ value = hpi_read_word_nolock(dev, reg);
+ hpi_write_word_nolock(dev, reg, value | mask);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+static inline void hpi_clear_bits(struct c67x00_device *dev, u16 reg, u16 mask)
+{
+ u16 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ value = hpi_read_word_nolock(dev, reg);
+ hpi_write_word_nolock(dev, reg, value & ~mask);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+static inline u16 hpi_recv_mbox(struct c67x00_device *dev)
+{
+ u16 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ value = hpi_read_reg(dev, HPI_MAILBOX);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ return value;
+}
+
+static inline u16 hpi_send_mbox(struct c67x00_device *dev, u16 value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ hpi_write_reg(dev, HPI_MAILBOX, value);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ return value;
+}
+
+u16 c67x00_ll_hpi_status(struct c67x00_device * dev)
+{
+ u16 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ value = hpi_read_reg(dev, HPI_STATUS);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ return value;
+}
+
+void c67x00_ll_hpi_reg_init(struct c67x00_device *dev)
+{
+ int i;
+
+ hpi_recv_mbox(dev);
+ c67x00_ll_hpi_status(dev);
+ hpi_write_word(dev, HPI_IRQ_ROUTING_REG, 0);
+
+ for (i=0; i<C67X00_SIES; i++) {
+ hpi_write_word(dev, SIEMSG_REG(i), 0);
+ hpi_read_word(dev, SIEMSG_REG(i));
+ }
+}
+
+void c67x00_ll_hpi_enable_sofeop(struct c67x00_sie *sie)
+{
+ hpi_set_bits(sie->dev, 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->dev, HPI_IRQ_ROUTING_REG,
+ SOFEOP_TO_HPI_EN(sie->sie_num));
+}
+
+/* -------------------------------------------------------------------------- */
+/* Transactions */
+
+static inline u16 ll_recv_msg(struct c67x00_device *dev)
+{
+ u16 res;
+
+ res = wait_for_completion_timeout(&dev->lcp.msg_received, 5 * HZ);
+ WARN_ON(!res);
+
+ return (res == 0) ? -EIO : 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+u16 c67x00_comm_read_ctrl_reg(struct c67x00_device * dev, u16 addr)
+{
+ unsigned long msg, res;
+ int rc;
+
+ mutex_lock(&dev->lcp.mutex);
+ hpi_write_word(dev, COMM_CTRL_REG_ADDR, addr);
+ hpi_send_mbox(dev, COMM_READ_CTRL_REG);
+ rc = ll_recv_msg(dev);
+
+ BUG_ON(rc); /* No return path for error code; crash spectacularly */
+
+ msg = dev->lcp.last_msg;
+ if (msg != COMM_ACK) {
+ dev_warn(&dev->pdev->dev, "COMM_READ_CTRL_REG didn't ACK!\n");
+ res = 0;
+ } else {
+ res = hpi_read_word(dev, COMM_CTRL_REG_DATA);
+ }
+ mutex_unlock(&dev->lcp.mutex);
+ return res;
+}
+
+int c67x00_comm_exec_int(struct c67x00_device *dev, u16 nr,
+ struct c67x00_lcp_int_data *data)
+{
+ int i, rc;
+
+ mutex_lock(&dev->lcp.mutex);
+ hpi_write_word(dev, COMM_INT_NUM, nr);
+ for (i = 0; i < COMM_REGS; i++)
+ hpi_write_word(dev, COMM_R(i), data->regs[i]);
+ hpi_send_mbox(dev, COMM_EXEC_INT);
+ rc = ll_recv_msg(dev);
+ mutex_unlock(&dev->lcp.mutex);
+
+ return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+/* General functions */
+
+u16 c67x00_ll_get_siemsg(struct c67x00_device *dev, int sie)
+{
+ return hpi_read_word(dev, SIEMSG_REG(sie));
+}
+
+void c67x00_ll_set_siemsg(struct c67x00_device *dev, int sie, u16 val)
+{
+ hpi_write_word(dev, SIEMSG_REG(sie), val);
+}
+
+u16 c67x00_ll_get_usb_ctl(struct c67x00_sie *sie)
+{
+ return hpi_read_word(sie->dev, USB_CTL_REG(sie->sie_num));
+}
+
+/* -------------------------------------------------------------------------- */
+/* Host specific functions */
+
+void c67x00_ll_set_husb_eot(struct c67x00_device *dev, u16 value)
+{
+ mutex_lock(&dev->lcp.mutex);
+ hpi_write_word(dev, HUSB_pEOT, value);
+ mutex_unlock(&dev->lcp.mutex);
+}
+
+static inline void c67x00_ll_husb_sie_init(struct c67x00_sie *sie)
+{
+ struct c67x00_device *dev = sie->dev;
+ struct c67x00_lcp_int_data data;
+ int rc;
+
+ rc = c67x00_comm_exec_int(dev, HUSB_SIE_INIT_INT(sie->sie_num), &data);
+ BUG_ON(rc); /* No return path for error code; crash spectacularly */
+}
+
+void c67x00_ll_husb_reset(struct c67x00_sie *sie, int port)
+{
+ struct c67x00_device *dev = sie->dev;
+ struct c67x00_lcp_int_data data;
+ int rc;
+
+ data.regs[0] = 50; /* Reset USB port for 50ms */
+ data.regs[1] = port | (sie->sie_num << 1);
+ rc = c67x00_comm_exec_int(dev, HUSB_RESET_INT, &data);
+ BUG_ON(rc); /* No return path for error code; crash spectacularly */
+}
+
+void c67x00_ll_husb_set_current_td(struct c67x00_sie *sie, u16 addr)
+{
+ hpi_write_word(sie->dev, HUSB_SIE_pCurrentTDPtr(sie->sie_num), addr);
+}
+
+u16 c67x00_ll_husb_get_current_td(struct c67x00_sie *sie)
+{
+ return hpi_read_word(sie->dev, 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->dev, HOST_STAT_REG(sie->sie_num), bits);
+}
+
+u16 c67x00_ll_husb_get_status(struct c67x00_sie *sie)
+{
+ return hpi_read_word(sie->dev, HOST_STAT_REG(sie->sie_num));
+}
+
+u16 c67x00_ll_husb_get_frame(struct c67x00_sie * sie)
+{
+ return hpi_read_word(sie->dev, 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->dev, 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->dev, 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->dev, HPI_IRQ_ROUTING_REG,
+ SOFEOP_TO_CPU_EN(sie->sie_num));
+ hpi_set_bits(sie->dev, HOST_IRQ_EN_REG(sie->sie_num),
+ SOF_EOP_IRQ_EN | DONE_IRQ_EN);
+
+ /* Enable pull down transistors */
+ hpi_set_bits(sie->dev, USB_CTL_REG(sie->sie_num), PORT_RES_EN(port));
+}
+
+/* -------------------------------------------------------------------------- */
+void c67x00_ll_susb_init(struct c67x00_sie *sie)
+{
+ struct c67x00_device *dev = sie->dev;
+ struct c67x00_lcp_int_data data;
+ int rc;
+
+ data.regs[1] = 1; /* full speed */
+ data.regs[2] = sie->sie_num + 1;
+ rc = c67x00_comm_exec_int(dev, SUSB_INIT_INT, &data);
+ BUG_ON(rc); /* No return path for error code; crash spectacularly */
+
+ hpi_clear_bits(dev, HPI_IRQ_ROUTING_REG,
+ SOFEOP_TO_HPI_EN(sie->sie_num));
+ hpi_set_bits(dev, HPI_IRQ_ROUTING_REG, SOFEOP_TO_CPU_EN(sie->sie_num));
+}
+
+/* -------------------------------------------------------------------------- */
+
+void c67x00_ll_irq(struct c67x00_device *dev)
+{
+ if ((dev->int_status & MBX_OUT_FLG) == 0)
+ return;
+
+ dev->lcp.last_msg = hpi_recv_mbox(dev);
+ complete(&dev->lcp.msg_received);
+}
+
+/* -------------------------------------------------------------------------- */
+
+int c67x00_ll_reset(struct c67x00_device *dev)
+{
+ int rc;
+
+ mutex_lock(&dev->lcp.mutex);
+ hpi_send_mbox(dev, COMM_RESET);
+ rc = ll_recv_msg(dev);
+ mutex_unlock(&dev->lcp.mutex);
+
+ return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_write_mem_le16 - write into c67x00 memory
+ * Only data is little endian, addr has cpu endianess.
+ */
+void c67x00_ll_hpi_write_mem_le16(struct c67x00_device *dev, u16 addr, int len,
+ char *data)
+{
+ /* Sanity check */
+ if (addr + len > 0xffff) {
+ dev_err(&dev->pdev->dev,
+ "Trying to write beyond writable region!\n");
+ return;
+ }
+
+ if (addr & 0x01) {
+ /* unaligned access */
+ u16 tmp;
+ tmp = hpi_read_word(dev, addr - 1);
+ tmp = (tmp & 0x00ff) | (*data++ << 8);
+ hpi_write_word(dev, addr - 1, tmp);
+ addr++;
+ len--;
+ }
+
+ hpi_write_words_le16(dev, addr, (u16 *) data, len / 2);
+ data += len & ~0x01;
+ addr += len & ~0x01;
+ len &= 0x01;
+
+ if (len) {
+ u16 tmp;
+ tmp = hpi_read_word(dev, addr);
+ tmp = (tmp & 0xff00) | (*data++);
+ hpi_write_word(dev, addr, tmp);
+ addr++;
+ len--;
+ }
+
+}
+
+/**
+ * c67x00_ll_hpi_read_mem_le16 - read from c67x00 memory
+ * Only data is little endian, addr has cpu endianess.
+ */
+void c67x00_ll_hpi_read_mem_le16(struct c67x00_device *dev, u16 addr, int len,
+ char *data)
+{
+ if (addr & 0x01) {
+ /* unaligned access */
+ u16 tmp;
+ tmp = hpi_read_word(dev, addr - 1);
+ *data++ = (tmp >> 8) & 0x00ff;
+ addr++;
+ len--;
+ }
+
+ hpi_read_words_le16(dev, addr, (u16 *) data, len / 2);
+ data += len & ~0x01;
+ addr += len & ~0x01;
+ len &= 0x01;
+
+ if (len) {
+ u16 tmp;
+ tmp = hpi_read_word(dev, addr);
+ *data++ = tmp & 0x00ff;
+ addr++;
+ len--;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+void c67x00_ll_init(struct c67x00_device *dev)
+{
+ mutex_init(&dev->lcp.mutex);
+ init_completion(&dev->lcp.msg_received);
+}
+
+void c67x00_ll_release(struct c67x00_device *dev)
+{
+}
diff --git a/drivers/usb/c67x00/c67x00.h b/drivers/usb/c67x00/c67x00.h
new file mode 100644
index 0000000..ca8ed69
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00.h
@@ -0,0 +1,236 @@
+/*
+ * 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_H
+#define _USB_C67X00_H
+
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+
+/* ---------------------------------------------------------------------
+ * Cypress C67x00 register definitions
+ */
+
+/* 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
+
+#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)
+
+/* ---------------------------------------------------------------------
+ * Driver data structures
+ */
+
+struct c67x00_device;
+
+/**
+ * struct c67x00_lcp
+ */
+struct c67x00_lcp {
+ /* Internal use only */
+ struct mutex mutex;
+ struct completion msg_received;
+ u16 last_msg;
+};
+
+/**
+ * struct c67x00_lcp_data
+ */
+#define COMM_REGS 14
+struct c67x00_lcp_int_data {
+ u16 regs[COMM_REGS];
+};
+
+/**
+ * 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
+ * @dev: 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_device *dev;
+ 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_device - Common data structure for 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: link control protocol dependent data
+ */
+struct c67x00_device {
+ 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 c67x00_lcp lcp;
+};
+
+/* ---------------------------------------------------------------------
+ * 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_device *dev, int sie);
+void c67x00_ll_set_siemsg(struct c67x00_device *dev, 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_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);
+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);
+
+/* Read and write to memory */
+void c67x00_ll_hpi_write_mem_le16(struct c67x00_device *dev, u16 addr,
+ int len, char *data);
+void c67x00_ll_hpi_read_mem_le16(struct c67x00_device *dev, u16 addr,
+ int len, char *data);
+
+/* Called by c67x00_irq to handle lcp interrupts */
+void c67x00_ll_irq(struct c67x00_device *dev);
+
+/* 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 */
^ permalink raw reply related
* [PATCH 0/5] Review request: Cypress c67x00 OTG controller
From: Grant Likely @ 2007-11-24 0:24 UTC (permalink / raw)
To: linux-usb-devel, akpm, dbrownell, gregkh, linuxppc-dev
This patch series is based on the c67x00 work done by Peter Korsgaard and
posted back in April this year.
The Cypress c67x00 is an OTG controller so it can behave as either a host
or gadget controller. This series implements the HCD behaviour.
The c67x00 is found on a number of the Xilinx MLxxx series of boards.
I've been performing my testing on a board derived from the ML403.
My goal is to get this driver queued up for inclusion in 2.6.25, but I've
been looking at this code for so long that I'm going crosseyed and missing
obvious issues. Plus, I am by no means a USB expert and I'm not sure that
I'm using the USB HCD API correctly.
So, this email is a request for review. I've got some time cleared away
to work on this over the next week so I should be able to address comments
quickly with the goal of getting it picked up for the next merge window.
Thanks,
g.
--
Grant Likely, B.Sc. P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* [PATCH 1/5] USB: Make usb_hcd_irq work for multi-role USB controllers w/ shared irq
From: Grant Likely @ 2007-11-24 0:24 UTC (permalink / raw)
To: linux-usb-devel, akpm, dbrownell, gregkh, linuxppc-dev
In-Reply-To: <20071124001203.25361.99294.stgit@trillian.cg.shawcable.net>
From: Peter Korsgaard <peter.korsgaard@barco.com>
Some multi-role (host/peripheral) USB controllers use a shared interrupt
line for all parts of the chip. Export usb_hcd_irq so drivers can call it
from their interrupt handler instead of duplicating code.
Drivers pass an irqnum of 0 to usb_add_hcd to signal that the interrupt handler
shouldn't be registerred by the core.
Signed-off-by: Peter Korsgaard <peter.korsgaard@barco.com>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
drivers/usb/core/hcd.c | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index fea8256..6ce305c 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1562,6 +1562,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
usb_hc_died (hcd);
return IRQ_HANDLED;
}
+EXPORT_SYMBOL_GPL (usb_hcd_irq);
/*-------------------------------------------------------------------------*/
@@ -1670,6 +1671,9 @@ EXPORT_SYMBOL (usb_put_hcd);
* Finish the remaining parts of generic HCD initialization: allocate the
* buffers of consistent memory, register the bus, request the IRQ line,
* and call the driver's reset() and start() routines.
+ *
+ * If irqnum is 0, the irq will not be requested. The caller is responsible
+ * for calling usb_hcd_irq at the correct time.
*/
int usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags)
@@ -1723,7 +1727,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
/* enable irqs just before we start the controller */
- if (hcd->driver->irq) {
+ if (hcd->driver->irq && irqnum) {
snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
hcd->driver->description, hcd->self.busnum);
if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
^ permalink raw reply related
* [PATCH 4/5] USB: add Cypress c67x00 OTG controller HCD driver
From: Grant Likely @ 2007-11-24 0:24 UTC (permalink / raw)
To: linux-usb-devel, akpm, dbrownell, gregkh, linuxppc-dev
In-Reply-To: <20071124001203.25361.99294.stgit@trillian.cg.shawcable.net>
From: Grant Likely <grant.likely@secretlab.ca>
This patch adds HDC support for the Cypress c67x00 family of devices.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
drivers/usb/c67x00/c67x00-hcd.c | 390 ++++++++++++
drivers/usb/c67x00/c67x00-hcd.h | 163 +++++
drivers/usb/c67x00/c67x00-sched.c | 1215 +++++++++++++++++++++++++++++++++++++
3 files changed, 1768 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/c67x00/c67x00-hcd.c b/drivers/usb/c67x00/c67x00-hcd.c
new file mode 100644
index 0000000..6ffca60
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-hcd.c
@@ -0,0 +1,390 @@
+/*
+ * c67x00-hcd.c: Cypress C67X00 USB Host Controller Driver
+ *
+ * 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 <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 ** */
+};
+
+#define OK(x) len = (x); break
+
+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_husb_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_husb_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_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_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_dev(c67x00),
+ "ClearPortFeature (%d): C_ENABLE\n", port);
+ len = 0;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ dev_dbg(c67x00_dev(c67x00),
+ "ClearPortFeature (%d): SUSPEND\n", port);
+ len = 0;
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ dev_dbg(c67x00_dev(c67x00),
+ "ClearPortFeature (%d): C_SUSPEND\n", port);
+ len = 0;
+ break;
+ case USB_PORT_FEAT_POWER:
+ dev_dbg(c67x00_dev(c67x00),
+ "ClearPortFeature (%d): POWER\n", port);
+ return -EPIPE;
+ case USB_PORT_FEAT_C_CONNECTION:
+ c67x00_ll_husb_clear_status(sie,
+ PORT_CONNECT_CHANGE(port));
+ len = 0;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ dev_dbg(c67x00_dev(c67x00),
+ "ClearPortFeature (%d): OVER_CURRENT\n", port);
+ len = 0;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ dev_dbg(c67x00_dev(c67x00),
+ "ClearPortFeature (%d): C_RESET\n", port);
+ len = 0;
+ break;
+ default:
+ dev_dbg(c67x00_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_dev(c67x00), "%s: unknown\n", __FUNCTION__);
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * Main part of host controller driver
+ */
+
+/**
+ * c67x00_hcd_drv_irq
+ *
+ * This function is called from the interrupt handler in c67x00-drv.c
+ * We can't have the hcd register the irq (it has already been registerd
+ * by the common driver), so we must fake it through this call.
+ */
+static irqreturn_t c67x00_hcd_drv_irq(int irq, struct c67x00_sie *sie)
+{
+ struct c67x00_hcd *c67x00 = sie->private_data;
+ return usb_hcd_irq(irq, c67x00_hcd_to_hcd(c67x00));
+}
+
+static irqreturn_t c67x00_hcd_irq(struct usb_hcd *hcd)
+{
+ struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+ struct c67x00_sie *sie = c67x00->sie;
+
+ if (sie->dev->int_status & SOFEOP_FLG(sie->sie_num)) {
+ c67x00_ll_husb_clear_status(sie, SOF_EOP_IRQ_FLG);
+ c67x00_sched_kick(c67x00);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+/**
+ * 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_start: Host Controller stop hook
+ */
+static void c67x00_hcd_stop(struct usb_hcd *hcd)
+{
+ /* Nothing todo */
+}
+
+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_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),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = c67x00_hcd_irq,
+ .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)
+{
+ int retval;
+ struct usb_hcd *hcd = NULL;
+ struct c67x00_hcd *c67x00;
+
+ 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;
+
+ spin_lock(&sie->lock);
+ sie->private_data = c67x00;
+ sie->irq = c67x00_hcd_drv_irq;
+ sie->msg_received = c67x00_hcd_msg_received;
+ spin_unlock(&sie->lock);
+
+ 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;
+ }
+
+ 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 usb_hcd *hcd;
+ struct c67x00_hcd *c67x00;
+
+ c67x00 = sie->private_data;
+ hcd = c67x00_hcd_to_hcd(c67x00);
+ c67x00_sched_stop_scheduler(c67x00);
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+}
diff --git a/drivers/usb/c67x00/c67x00-hcd.h b/drivers/usb/c67x00/c67x00-hcd.h
new file mode 100644
index 0000000..02efa17
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-hcd.h
@@ -0,0 +1,163 @@
+/*
+ * c67x00-hcd.h: Cypress C67X00 USB HCD
+ *
+ * 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_HCD_H
+#define _USB_C67X00_HCD_H
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include "../core/hcd.h"
+#include "c67x00.h"
+
+/*
+ * The following parameters depend on the CPU speed, bus speed, ...
+ * These can be tuned for specific use cases, e.g. if isochronous transfers
+ * are very important, bandwith can be sacrificed to guarantee that the
+ * 1ms deadline will be met.
+ * If bulk transfers are important, the MAX_FRAME_BW can be increased,
+ * but some (or many) isochronous deadlines might not be met.
+ *
+ * The values are specified in bittime.
+ */
+
+/*
+ * The current implementation switches between _STD (default) and _ISO (when
+ * isochronous transfers are scheduled), in order to optimize the throughput
+ * in normal cicrumstances, but also provide good isochronous behaviour.
+ *
+ * Bandwidth is described in bit time so with a 12MHz USB clock and 1ms
+ * frames; there are 12000 bit times per frame.
+ */
+
+#define TOTAL_FRAME_BW 12000
+#define DEFAULT_EOT 2250
+
+#define MAX_FRAME_BW_STD (TOTAL_FRAME_BW - DEFAULT_EOT)
+#define MAX_FRAME_BW_ISO 2400
+
+/*
+ * Periodic transfers may only use 90% of the full frame, but as
+ * we currently don't even use 90% of the full frame, we may
+ * use the full usable time for periodic transfers.
+ */
+#define MAX_PERIODIC_BW(full_bw) full_bw
+
+/* -------------------------------------------------------------------------- */
+
+struct c67x00_hcd {
+ spinlock_t lock;
+ struct c67x00_sie *sie;
+ /* Requirement:
+ * All enabled bits in ports must have a position <= MAX_NB_HCD_PORTS
+ * (position starts counting from 1!)
+ * ( ports & ~((1<<MAX_NB_HCD_PORTS)-1) ) == 0
+ *
+ * This might be relaxed if needed by using an other indexing scheme
+ * for port[] (e.g. use wIndex instead of real port number)
+ * */
+#define MAX_NB_HCD_PORTS 2
+ unsigned int low_speed_ports; /* bitmask of low speed ports */
+ unsigned int urb_count;
+ unsigned int urb_iso_count;
+
+ struct list_head list[4]; /* iso, int, ctrl, bulk */
+#if PIPE_BULK != 3
+#error "Sanity check failed, this code presumes PIPE_... to range from 0 to 3"
+#endif
+
+ /* USB bandwidth allocated to td_list */
+ int bandwidth_allocated;
+ /* USB bandwidth allocated for isoc/int transfer */
+ int periodic_bw_allocated;
+ struct list_head td_list;
+ struct list_head done_list;
+ int max_frame_bw;
+
+ u16 td_base_addr;
+ u16 buf_base_addr;
+ u16 next_td_addr;
+ u16 next_buf_addr;
+
+ struct tasklet_struct tasklet;
+ struct tasklet_struct done_tasklet;
+
+ struct completion endpoint_disable;
+
+ u16 current_frame;
+ u16 last_frame;
+};
+
+static inline struct c67x00_hcd *hcd_to_c67x00_hcd(struct usb_hcd *hcd)
+{
+ return (struct c67x00_hcd *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *c67x00_hcd_to_hcd(struct c67x00_hcd *c67x00)
+{
+ return container_of((void *)c67x00, struct usb_hcd, hcd_priv);
+}
+
+/* ---------------------------------------------------------------------
+ * Transfer Descriptor scheduling functions
+ */
+int c67x00_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);
+int c67x00_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
+void c67x00_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep);
+
+void c67x00_hcd_msg_received(struct c67x00_sie *sie, u16 msg);
+void c67x00_sched_kick(struct c67x00_hcd *c67x00);
+int c67x00_sched_start_scheduler(struct c67x00_hcd *c67x00);
+void c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00);
+
+
+/* ---------------------------------------------------------------------
+ * Setup and teardown functions
+ */
+#ifdef CONFIG_USB_C67X00_HCD
+/* Functions used by drv */
+int c67x00_hcd_probe(struct c67x00_sie *sie);
+void c67x00_hcd_remove(struct c67x00_sie *sie);
+#else
+static inline int c67x00_hcd_probe(struct c67x00_sie *sie)
+{
+ printk(KERN_ERR "hcd requested but CONFIG_USB_C67X00_HCD "
+ "not enabled!\n");
+ return -ENODEV;
+}
+
+static inline void c67x00_hcd_remove(struct c67x00_sie *sie)
+{
+}
+
+static int usb_disabled(void)
+{
+ return 0;
+}
+#endif /* CONFIG_USB_C67X00_HCD */
+
+#define c67x00_dev(x) (c67x00_hcd_to_hcd(x)->self.controller)
+
+#endif /* _USB_C67X00_HCD_H */
diff --git a/drivers/usb/c67x00/c67x00-sched.c b/drivers/usb/c67x00/c67x00-sched.c
new file mode 100644
index 0000000..484e291
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-sched.c
@@ -0,0 +1,1215 @@
+/*
+ * c67x00-sched.c: Cypress C67X00 USB Host Controller Driver - TD scheduling
+ *
+ * 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 <linux/kthread.h>
+
+#include "c67x00.h"
+#include "c67x00-hcd.h"
+
+/*
+ * These are the stages for a control urb, they are kept
+ * in both urb->interval and td->privdata.
+ */
+#define SETUP_STAGE 0
+#define DATA_STAGE 1
+#define STATUS_STAGE 2
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * struct c67x00_ep_data: Host endpoint data structure
+ */
+struct c67x00_ep_data {
+ struct list_head queue;
+ struct list_head node;
+ struct usb_host_endpoint *hep;
+ struct usb_device *dev;
+ u16 next_frame; /* For int/isoc transactions */
+};
+
+/**
+ * struct c67x00_td
+ *
+ * Hardware parts are little endiannes, SW in CPU endianess.
+ */
+struct c67x00_td {
+ /* HW specific part */
+ __le16 ly_base_addr; /* Bytes 0-1 */
+ __le16 port_length; /* Bytes 2-3 */
+ u8 pid_ep; /* Byte 4 */
+ u8 dev_addr; /* Byte 5 */
+ u8 ctrl_reg; /* Byte 6 */
+ u8 status; /* Byte 7 */
+ u8 retry_cnt; /* Byte 8 */
+#define TT_OFFSET 2
+#define TT_CONTROL 0
+#define TT_ISOCHRONOUS 1
+#define TT_BULK 2
+#define TT_INTERRUPT 3
+ u8 residue; /* Byte 9 */
+ __le16 next_td_addr; /* Bytes 10-11 */
+ /* SW part */
+ struct list_head td_list;
+ u16 td_addr;
+ char *data;
+ struct urb *urb;
+ unsigned long privdata;
+
+ /* These are needed for handling the toggle bits:
+ * an urb can be dequeued while a td is in progress
+ * after checking the td, the toggle bit might need to
+ * be fixed */
+ struct c67x00_ep_data *ep_data;
+ unsigned int pipe;
+};
+
+struct c67x00_urb_priv {
+ struct list_head hep_node;
+ struct urb *urb;
+ int port;
+ int cnt; /* packet number for isoc */
+ int status;
+ struct c67x00_ep_data *ep_data;
+};
+
+#define td_udev(td) ((td)->ep_data->dev)
+
+#define CY_TD_SIZE 12
+
+#define TD_PIDEP_OFFSET 0x04
+#define TD_PIDEPMASK_PID 0xF0
+#define TD_PIDEPMASK_EP 0x0F
+#define TD_PORTLENMASK_DL 0x02FF
+#define TD_PORTLENMASK_PN 0xC000
+
+#define TD_STATUS_OFFSET 0x07
+#define TD_STATUSMASK_ACK 0x01
+#define TD_STATUSMASK_ERR 0x02
+#define TD_STATUSMASK_TMOUT 0x04
+#define TD_STATUSMASK_SEQ 0x08
+#define TD_STATUSMASK_SETUP 0x10
+#define TD_STATUSMASK_OVF 0x20
+#define TD_STATUSMASK_NAK 0x40
+#define TD_STATUSMASK_STALL 0x80
+
+#define TD_ERROR_MASK (TD_STATUSMASK_ERR | TD_STATUSMASK_TMOUT | \
+ TD_STATUSMASK_STALL )
+
+#define TD_RETRYCNT_OFFSET 0x08
+#define TD_RETRYCNTMASK_ACT_FLG 0x10
+#define TD_RETRYCNTMASK_TX_TYPE 0x0C
+#define TD_RETRYCNTMASK_RTY_CNT 0x03
+
+#define TD_RESIDUE_OVERFLOW 0x80
+
+#define TD_PID_IN 0x90
+
+/* Residue: signed 8bits, neg -> OVERFLOW, pos -> UNDERFLOW */
+#define td_residue(td) ((__s8)(td->residue))
+#define td_ly_base_addr(td) (__le16_to_cpu((td)->ly_base_addr))
+#define td_port_length(td) (__le16_to_cpu((td)->port_length))
+#define td_next_td_addr(td) (__le16_to_cpu((td)->next_td_addr))
+
+#define td_active(td) ((td)->retry_cnt & TD_RETRYCNTMASK_ACT_FLG)
+#define td_length(td) (td_port_length(td) & TD_PORTLENMASK_DL)
+
+#define td_sequence_ok(td) (!td->status || \
+ ( !(td->status & TD_STATUSMASK_SEQ) == \
+ !(td->ctrl_reg & SEQ_SEL) ))
+
+#define td_acked(td) (!td->status || \
+ (td->status & TD_STATUSMASK_ACK))
+#define td_actual_bytes(td) (td_length(td) - td_residue(td))
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef DEBUG
+/*
+ * These patterns are written into the c67x00 internal memory and the
+ * urb->transfer_buffer respectively in order to simplify debugging.
+ */
+#define NON_RECEIVED_PATTERN 0xac
+#define UNREAD_PATTERN 0x0c
+/* #define DEBUG_PATTERN */
+
+/**
+ * dbg_td - Dump the contents of the TD
+ */
+static void dbg_td(struct c67x00_hcd *c67x00, struct c67x00_td *td, char *msg)
+{
+ struct device *dev = c67x00_dev(c67x00);
+ int i, len = td_length(td);
+ dev_dbg(dev, "### %s at 0x%04x\n", msg, td->td_addr);
+ dev_dbg(dev, "urb: 0x%p\n", td->urb);
+ dev_dbg(dev, "endpoint: %4d\n", usb_pipeendpoint(td->pipe));
+ dev_dbg(dev, "pipeout: %4d\n", usb_pipeout(td->pipe));
+ dev_dbg(dev, "ly_base_addr: 0x%04x\n", td_ly_base_addr(td));
+ dev_dbg(dev, "port_length: 0x%04x\n", td_port_length(td));
+ dev_dbg(dev, "pid_ep: 0x%02x\n", td->pid_ep);
+ dev_dbg(dev, "dev_addr: 0x%02x\n", td->dev_addr);
+ dev_dbg(dev, "ctrl_reg: 0x%02x\n", td->ctrl_reg);
+ dev_dbg(dev, "status: 0x%02x\n", td->status);
+ dev_dbg(dev, "retry_cnt: 0x%02x\n", td->retry_cnt);
+ dev_dbg(dev, "residue: 0x%02x\n", td->residue);
+ dev_dbg(dev, "next_td_addr: 0x%04x\n", td_next_td_addr(td));
+ dev_dbg(dev, "data:");
+ for (i = 0; i < len; i++) {
+ if (!(i % 8))
+ printk("\n ");
+ printk(" 0x%02x", td->data[i]);
+ }
+ printk("\n");
+}
+#else /* DEBUG */
+
+static inline void
+dbg_td(struct c67x00_hcd *c67x00, struct c67x00_td *td, char *msg) { }
+
+#endif /* DEBUG */
+
+/* -------------------------------------------------------------------------- */
+/* Helper functions */
+
+/* -------------------------------------------------------------------------- */
+
+static inline u16 c67x00_get_current_frame_number(struct c67x00_hcd *c67x00)
+{
+ u16 temp_val;
+ temp_val = c67x00_ll_husb_get_frame(c67x00->sie);
+ temp_val &= HOST_FRAME_MASK;
+ return temp_val;
+}
+
+/**
+ * frame_add
+ * Software wraparound for framenumbers.
+ */
+static inline u16 frame_add(u16 a, u16 b)
+{
+ return (a + b) & HOST_FRAME_MASK;
+}
+
+/**
+ * frame_after - is frame a after frame b
+ */
+static inline int frame_after(u16 a, u16 b)
+{
+ return ((HOST_FRAME_MASK + a - b) & HOST_FRAME_MASK) <
+ (HOST_FRAME_MASK / 2);
+}
+
+/**
+ * frame_after_eq - is frame a after or equal to frame b
+ */
+static inline int frame_after_eq(u16 a, u16 b)
+{
+ return ((HOST_FRAME_MASK + 1 + a - b) & HOST_FRAME_MASK) <
+ (HOST_FRAME_MASK / 2);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_release_urb - remove link from all tds to this urb
+ * Disconnects the urb from it's tds, so that it can be given back.
+ * pre: urb->hcpriv != NULL
+ */
+static void c67x00_release_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+ struct c67x00_td *td;
+ struct c67x00_urb_priv *urbp;
+
+ BUG_ON(!urb);
+
+ c67x00->urb_count--;
+
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ c67x00->urb_iso_count--;
+ if (c67x00->urb_iso_count == 0)
+ c67x00->max_frame_bw = MAX_FRAME_BW_STD;
+ }
+
+ /* TODO this might be not so efficient when we've got many urbs!
+ * Alternatives:
+ * * only clear when needed
+ * * keep a list of tds with earch urbp
+ */
+ list_for_each_entry(td, &c67x00->td_list, td_list) {
+ if (urb == td->urb)
+ td->urb = NULL;
+ }
+
+ /* Discard the urb private data */
+ urbp = urb->hcpriv;
+ urb->hcpriv = NULL;
+ list_del(&urbp->hep_node);
+ kfree(urbp);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static struct c67x00_ep_data *
+c67x00_ep_data_alloc(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+ //struct usb_device *udev = urb->dev;
+ struct usb_host_endpoint *hep = urb->ep;
+ struct c67x00_ep_data *ep_data;
+ struct c67x00_ep_data *prev;
+ int type;
+
+ c67x00->current_frame = c67x00_get_current_frame_number(c67x00);
+
+ /* Check if endpoint already has a c67x00_ep_data struct allocated */
+ if (hep->hcpriv) {
+ ep_data = hep->hcpriv;
+ if (frame_after(c67x00->current_frame, ep_data->next_frame))
+ ep_data->next_frame =
+ frame_add(c67x00->current_frame, 1);
+ return hep->hcpriv;
+ }
+
+ /* Allocate and initialize a new c67x00 endpoint data structure */
+ ep_data = kzalloc(sizeof(*ep_data), GFP_ATOMIC);
+ if (!ep_data)
+ return NULL;
+
+ INIT_LIST_HEAD(&ep_data->queue);
+ INIT_LIST_HEAD(&ep_data->node);
+ ep_data->hep = hep;
+
+ /* hold a reference to udev as long as this endpoint lives,
+ * this is needed to possibly fix the data toggle */
+ ep_data->dev = usb_get_dev(urb->dev);
+ hep->hcpriv = ep_data;
+
+ /* For ISOC and INT endpoints, start ASAP: */
+ ep_data->next_frame = frame_add(c67x00->current_frame, 1);
+
+ /* Add the endpoint data to one of the pipe lists; must be added
+ * in order of endpoint address */
+ type = usb_pipetype(urb->pipe);
+ if (list_empty(&ep_data->node)) {
+ list_add(&ep_data->node, &c67x00->list[type]);
+ } else {
+ list_for_each_entry(prev, &c67x00->list[type], node) {
+ if (prev->hep->desc.bEndpointAddress >
+ hep->desc.bEndpointAddress) {
+ list_add(&ep_data->node, prev->node.prev);
+ break;
+ }
+ }
+ }
+
+ return ep_data;
+}
+
+static int c67x00_ep_data_free(struct usb_host_endpoint *hep)
+{
+ struct c67x00_ep_data *ep_data = hep->hcpriv;
+ if (!ep_data)
+ return 0;
+
+ if (!list_empty(&ep_data->queue))
+ return -EBUSY;
+
+ usb_put_dev(ep_data->dev);
+ list_del(&ep_data->queue);
+ list_del(&ep_data->node);
+
+ kfree(ep_data);
+ hep->hcpriv = NULL;
+
+ return 0;
+}
+
+void c67x00_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+ unsigned long flags;
+
+ if (!list_empty(&ep->urb_list))
+ dev_warn(c67x00_dev(c67x00), "error: urb list not empty\n");
+
+ spin_lock_irqsave(&c67x00->lock, flags);
+
+ /* Loop waiting for all transfers in the endpoint queue to complete */
+ while (c67x00_ep_data_free(ep)) {
+ /* Drop the lock so we can sleep waiting for the hardware */
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+
+ /* it could happen that we reinitialize this completion, while
+ * somebody was waiting for that completion. The timeout and
+ * while loop handle such cases, but this might be improved */
+ INIT_COMPLETION(c67x00->endpoint_disable);
+ c67x00_sched_kick(c67x00);
+ wait_for_completion_timeout(&c67x00->endpoint_disable, 1 * HZ);
+
+ spin_lock_irqsave(&c67x00->lock, flags);
+ }
+
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static inline int get_root_port(struct usb_device *dev)
+{
+ while (dev->parent->parent)
+ dev = dev->parent;
+ return dev->portnum;
+}
+
+int c67x00_urb_enqueue(struct usb_hcd *hcd,
+ struct urb *urb, gfp_t mem_flags)
+{
+ int ret;
+ unsigned long flags;
+ struct c67x00_urb_priv *urbp = NULL;
+ struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+ int port = get_root_port(urb->dev)-1;
+
+ spin_lock_irqsave(&c67x00->lock, flags);
+
+ /* Make sure host controller is running */
+ if (!HC_IS_RUNNING(hcd->state)) {
+ ret = -ENODEV;
+ goto err_not_linked;
+ }
+
+ ret = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (ret)
+ goto err_not_linked;
+
+ /* Allocate and initialize urb private data */
+ urbp = kzalloc(sizeof(*urbp), mem_flags);
+ if (!urbp) {
+ ret = -ENOMEM;
+ goto err_urbp;
+ }
+ INIT_LIST_HEAD(&urbp->hep_node);
+ urbp->urb = urb;
+ urbp->port = port;
+
+ urbp->ep_data = c67x00_ep_data_alloc(c67x00, urb);
+ if (!urbp->ep_data) {
+ ret = -ENOMEM;
+ goto err_epdata;
+ }
+
+ /* TODO claim bandwidth with usb_claim_bandwidth?
+ * also release it somewhere! */
+
+ urb->hcpriv = urbp;
+
+ urb->actual_length = 0; /* Nothing received/transmitted yet */
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ urb->interval = SETUP_STAGE;
+ break;
+ case PIPE_INTERRUPT:
+ break;
+ case PIPE_BULK:
+ break;
+ case PIPE_ISOCHRONOUS:
+ if (c67x00->urb_iso_count == 0)
+ c67x00->max_frame_bw = MAX_FRAME_BW_ISO;
+ c67x00->urb_iso_count++;
+ /* Assume always URB_ISO_ASAP, FIXME */
+ if (list_empty(&urbp->ep_data->queue))
+ urb->start_frame = urbp->ep_data->next_frame;
+ else {
+ /* Go right after the last one */
+ struct urb *last_urb;
+
+ last_urb = list_entry(urbp->ep_data->queue.prev,
+ struct c67x00_urb_priv, hep_node)->urb;
+ urb->start_frame =
+ frame_add(last_urb->start_frame,
+ last_urb->number_of_packets *
+ last_urb->interval);
+ }
+ urbp->cnt = 0;
+ break;
+ }
+
+ /* Add the URB to the endpoint queue */
+ list_add_tail(&urbp->hep_node, &urbp->ep_data->queue);
+
+ /* If this is the only urb, kick start the controller */
+ if (!c67x00->urb_count++)
+ c67x00_ll_hpi_enable_sofeop(c67x00->sie);
+
+ c67x00_sched_kick(c67x00);
+
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+ return 0;
+
+ /* Something went wrong; unwind the allocations */
+ err_epdata:
+ kfree(urbp);
+ err_urbp:
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ err_not_linked:
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+ return ret;
+}
+
+int c67x00_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&c67x00->lock, flags);
+ rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (rc)
+ goto done;
+
+ c67x00_release_urb(c67x00, urb);
+ //gcl//usb_hcd_giveback_urb(c67x00_hcd_to_hcd(c67x00), urb, status);
+
+done:
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+ return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * pre: urb != NULL and c67x00 locked, urb unlocked
+ */
+static inline void
+c67x00_giveback_urb(struct c67x00_hcd *c67x00, struct urb *urb, int status)
+{
+ struct c67x00_urb_priv *urbp;
+ if (!urb)
+ return;
+
+ urbp = urb->hcpriv;
+ urbp->status = status;
+
+ list_del_init(&urbp->hep_node);
+ list_add_tail(&urbp->hep_node, &c67x00->done_list);
+ tasklet_schedule(&c67x00->done_tasklet);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int claim_frame_bw(struct c67x00_hcd *c67x00, struct urb *urb,
+ int len, int periodic)
+{
+ struct c67x00_urb_priv *urbp = urb->hcpriv;
+ int bit_time;
+
+ /* According to the C67x00 BIOS user manual, page 3-18,19, the
+ * following calculations provide the full speed bit times for
+ * a transaction.
+ *
+ * FS(in) = 112.5 + 9.36*BC + HOST_DELAY
+ * FS(in,iso) = 90.5 + 9.36*BC + HOST_DELAY
+ * FS(out) = 112.5 + 9.36*BC + HOST_DELAY
+ * FS(out,iso) = 78.4 + 9.36*BC + HOST_DELAY
+ * LS(in) = 802.4 + 75.78*BC + HOST_DELAY
+ * LS(out) = 802.6 + 74.67*BC + HOST_DELAY
+ *
+ * HOST_DELAY == 106 for the c67200 and c67300.
+ */
+
+ /* make calculations in 1/100 bit times to maintain resolution */
+ if (urbp->ep_data->dev->speed == USB_SPEED_LOW) {
+ /* Low speed pipe */
+ if (usb_pipein(urb->pipe))
+ bit_time = 80240 + 7578*len;
+ else
+ bit_time = 80260 + 7467*len;
+ } else {
+ /* FS pipes */
+ if (usb_pipeisoc(urb->pipe))
+ bit_time = usb_pipein(urb->pipe) ? 9050 : 7840;
+ else
+ bit_time = 11250;
+ bit_time += 936*len;
+ }
+
+ /* Scale back down to integer bit times. Use a host delay of 106.
+ * (this is the only place it is used) */
+ bit_time = ((bit_time+50) / 100) + 106;
+
+ if (unlikely(bit_time + c67x00->bandwidth_allocated >=
+ c67x00->max_frame_bw))
+ return -EMSGSIZE;
+
+ if (unlikely(c67x00->next_td_addr + CY_TD_SIZE >=
+ c67x00->td_base_addr + SIE_TD_SIZE))
+ return -EMSGSIZE;
+
+ if (unlikely(c67x00->next_buf_addr + len >=
+ c67x00->buf_base_addr + SIE_TD_BUF_SIZE))
+ return -EMSGSIZE;
+
+ if (periodic) {
+ if (unlikely(bit_time + c67x00->periodic_bw_allocated >=
+ MAX_PERIODIC_BW(c67x00->max_frame_bw)))
+ return -EMSGSIZE;
+ c67x00->periodic_bw_allocated += bit_time;
+ }
+
+ c67x00->bandwidth_allocated += bit_time;
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * td_addr and buf_addr must be word aligned
+ */
+static int create_td(struct c67x00_hcd *c67x00,
+ struct urb *urb,
+ char *data,
+ int len, int pid, int toggle, unsigned long privdata)
+{
+ struct c67x00_td *td;
+ struct c67x00_urb_priv *urbp = urb->hcpriv;
+ const __u8 active_flag = 1, retry_cnt = 1;
+ __u8 cmd = 0;
+ int tt = 0;
+
+ if (claim_frame_bw(c67x00, urb, len,
+ usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)))
+ return -EMSGSIZE; /* Not really an error, but expected */
+
+ td = kzalloc(sizeof(*td), GFP_ATOMIC);
+ if (!td)
+ return -ENOMEM;
+
+ td->pipe = urb->pipe;
+ td->ep_data = urbp->ep_data;
+
+ if ((td_udev(td)->speed == USB_SPEED_LOW) &&
+ !(c67x00->low_speed_ports & (1 << urbp->port)))
+ cmd |= PREAMBLE_EN;
+
+ switch (usb_pipetype(td->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ tt = TT_ISOCHRONOUS;
+ cmd |= ISO_EN;
+ break;
+ case PIPE_CONTROL:
+ tt = TT_CONTROL;
+ break;
+ case PIPE_BULK:
+ tt = TT_BULK;
+ break;
+ case PIPE_INTERRUPT:
+ tt = TT_INTERRUPT;
+ break;
+ }
+
+ if (toggle)
+ cmd |= SEQ_SEL;
+
+ cmd |= ARM_EN;
+
+ /* SW part */
+ td->td_addr = c67x00->next_td_addr;
+ c67x00->next_td_addr = c67x00->next_td_addr + CY_TD_SIZE;
+
+ /* HW part */
+ td->ly_base_addr = __cpu_to_le16(c67x00->next_buf_addr);
+ td->port_length = __cpu_to_le16((c67x00->sie->sie_num << 15) |
+ (urbp->port << 14) | (len & 0x3FF));
+ td->pid_ep = ((pid & 0xF) << TD_PIDEP_OFFSET) |
+ (usb_pipeendpoint(td->pipe) & 0xF);
+ td->dev_addr = usb_pipedevice(td->pipe) & 0x7F;
+ td->ctrl_reg = cmd;
+ td->status = 0;
+ td->retry_cnt = (tt << TT_OFFSET) | (active_flag << 4) | retry_cnt;
+ td->residue = 0;
+ td->next_td_addr = __cpu_to_le16(c67x00->next_td_addr);
+
+ /* SW part */
+ td->data = data;
+ td->urb = urb;
+ td->privdata = privdata;
+
+ c67x00->next_buf_addr += (len + 1) & ~0x01; /* properly align */
+
+ list_add_tail(&td->td_list, &c67x00->td_list);
+ return 0;
+}
+
+static inline void release_td(struct c67x00_td *td)
+{
+ list_del_init(&td->td_list);
+ kfree(td);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int add_data_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+ int remaining;
+ int toggle;
+ int pid;
+ int ret = 0;
+ int maxps;
+ int need_empty;
+
+ toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe));
+ remaining = urb->transfer_buffer_length - urb->actual_length;
+
+ maxps = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+ need_empty = (urb->transfer_flags & URB_ZERO_PACKET) &&
+ usb_pipeout(urb->pipe) && !(remaining % maxps);
+
+ while (remaining || need_empty) {
+ int len;
+ char *td_buf;
+
+ len = (remaining > maxps) ? maxps : remaining;
+ if (!len)
+ need_empty = 0;
+
+ pid = usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN;
+ td_buf = urb->transfer_buffer + urb->transfer_buffer_length -
+ remaining;
+ ret = create_td(c67x00, urb, td_buf, len, pid, toggle,
+ DATA_STAGE);
+ if (ret)
+ goto out; /* td wasn't created */
+ toggle ^= 1;
+ remaining -= len;
+ if (usb_pipecontrol(urb->pipe))
+ break;
+ }
+ out:
+ return ret;
+}
+
+/**
+ *
+ * return 0 in case more bandwidth is available, else errorcode
+ */
+static int add_ctrl_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+ int ret;
+ int pid;
+
+ switch (urb->interval) {
+ default:
+ case SETUP_STAGE:
+ ret = create_td(c67x00, urb,
+ urb->setup_packet,
+ 8, USB_PID_SETUP, 0, SETUP_STAGE);
+ if (ret)
+ return ret;
+ urb->interval = SETUP_STAGE;
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), 1);
+ break;
+ case DATA_STAGE:
+ if (urb->transfer_buffer_length) {
+ ret = add_data_urb(c67x00, urb);
+ if (ret)
+ return ret;
+ break;
+ } /* else fallthrough */
+ case STATUS_STAGE:
+ pid = !usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN;
+ ret = create_td(c67x00, urb, NULL, 0, pid, 1, STATUS_STAGE);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * return 0 in case more bandwidth is available, else errorcode
+ */
+static int add_int_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+ struct c67x00_urb_priv *urbp = urb->hcpriv;
+
+ if (frame_after_eq(c67x00->current_frame, urbp->ep_data->next_frame)) {
+ urbp->ep_data->next_frame =
+ frame_add(urbp->ep_data->next_frame, urb->interval);
+ return add_data_urb(c67x00, urb);
+ }
+ return 0;
+}
+
+static int add_iso_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+ struct c67x00_urb_priv *urbp = urb->hcpriv;
+
+ if (frame_after_eq(c67x00->current_frame, urbp->ep_data->next_frame)) {
+ char *td_buf;
+ int len, pid, ret;
+
+ BUG_ON(urbp->cnt >= urb->number_of_packets);
+
+ td_buf = urb->transfer_buffer +
+ urb->iso_frame_desc[urbp->cnt].offset;
+ len = urb->iso_frame_desc[urbp->cnt].length;
+ pid = usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN;
+
+ ret = create_td(c67x00, urb, td_buf, len, pid, 0, urbp->cnt);
+ if (ret) {
+ printk(KERN_DEBUG "create failed: %d\n", ret);
+ urb->iso_frame_desc[urbp->cnt].actual_length = 0;
+ urb->iso_frame_desc[urbp->cnt].status = ret;
+ if (urbp->cnt + 1 == urb->number_of_packets)
+ c67x00_giveback_urb(c67x00, urb, 0);
+ }
+
+ urbp->ep_data->next_frame =
+ frame_add(urbp->ep_data->next_frame, urb->interval);
+ urbp->cnt++;
+ }
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void c67x00_fill_from_list(struct c67x00_hcd *c67x00, int type,
+ int (*add)(struct c67x00_hcd *, struct urb *))
+{
+ struct c67x00_ep_data *ep_data;
+ struct urb *urb;
+
+ /* traverse every endpoint on the list */
+ list_for_each_entry(ep_data, &c67x00->list[type], node) {
+ if (!list_empty(&ep_data->queue)) {
+ /* and add the first urb */
+ /* isochronous transfer rely on this */
+ urb = list_entry(ep_data->queue.next, struct c67x00_urb_priv,
+ hep_node)->urb;
+ add(c67x00, urb);
+ }
+ }
+}
+
+static void c67x00_fill_frame(struct c67x00_hcd *c67x00)
+{
+ struct c67x00_td *td, *ttd;
+
+ /* Check if we can proceed */
+ if (!list_empty(&c67x00->td_list)) {
+ dev_warn(c67x00_dev(c67x00),
+ "TD list not empty! This should not happen!\n");
+ list_for_each_entry_safe(td, ttd, &c67x00->td_list, td_list) {
+ dbg_td(c67x00, td, "Unprocessed td");
+ release_td(td);
+ }
+ }
+
+ /* Reinitialize variables */
+ c67x00->bandwidth_allocated = 0;
+ c67x00->periodic_bw_allocated = 0;
+
+ c67x00->next_td_addr = c67x00->td_base_addr;
+ c67x00->next_buf_addr = c67x00->buf_base_addr;
+
+ /* Fill the list */
+ c67x00_fill_from_list(c67x00, PIPE_ISOCHRONOUS, add_iso_urb);
+ c67x00_fill_from_list(c67x00, PIPE_INTERRUPT, add_int_urb);
+ c67x00_fill_from_list(c67x00, PIPE_CONTROL, add_ctrl_urb);
+ c67x00_fill_from_list(c67x00, PIPE_BULK, add_data_urb);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * Get TD from C67X00
+ */
+static inline void
+c67x00_parse_td(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+ c67x00_ll_hpi_read_mem_le16(c67x00->sie->dev, td->td_addr, CY_TD_SIZE,
+ (char *)td);
+
+ if (usb_pipein(td->pipe) && td_actual_bytes(td))
+ c67x00_ll_hpi_read_mem_le16(c67x00->sie->dev,
+ td_ly_base_addr(td),
+ td_actual_bytes(td), td->data);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int
+c67x00_td_to_error(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+ if (td->status & TD_STATUSMASK_ERR) {
+ dbg_td(c67x00, td, "ERROR_FLAG");
+ return -EILSEQ;
+ }
+ if (td->status & TD_STATUSMASK_STALL) {
+ /* dbg_td(c67x00, td, "STALL"); */
+ return -EPIPE;
+ }
+ if (td->status & TD_STATUSMASK_TMOUT) {
+ dbg_td(c67x00, td, "TIMEOUT");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static inline int c67x00_end_of_data(struct c67x00_td *td)
+{
+ int maxps, need_empty, remaining;
+ struct urb *urb = td->urb;
+ int act_bytes;
+
+ act_bytes = td_actual_bytes(td);
+
+ if (unlikely(!act_bytes))
+ return 1; /* This was an empty packet */
+
+ maxps = usb_maxpacket(td_udev(td), td->pipe, usb_pipeout(td->pipe));
+
+ if (unlikely(act_bytes < maxps))
+ return 1; /* Smaller then full packet */
+
+ remaining = urb->transfer_buffer_length - urb->actual_length;
+ need_empty = (urb->transfer_flags & URB_ZERO_PACKET) &&
+ usb_pipeout(urb->pipe) && !(remaining % maxps);
+
+ if (unlikely(!remaining && !need_empty))
+ return 1;
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Remove all td's from the list which come
+ * after last_td and are meant for the same pipe.
+ * This is used when a short packet has occured */
+static inline void
+c67x00_clear_pipe(struct c67x00_hcd *c67x00, struct c67x00_td *last_td)
+{
+ struct c67x00_td *td, *tmp;
+ td = last_td;
+ tmp = last_td;
+ while (td->td_list.next != &c67x00->td_list) {
+ td = list_entry(td->td_list.next, struct c67x00_td, td_list);
+ if (td->pipe == last_td->pipe) {
+ release_td(td);
+ td = tmp;
+ }
+ tmp = td;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void c67x00_handle_successful_td(struct c67x00_hcd *c67x00,
+ struct c67x00_td *td)
+{
+ struct urb *urb = td->urb;
+
+ if (!urb)
+ return;
+
+ urb->actual_length += td_actual_bytes(td);
+
+ switch (usb_pipetype(td->pipe)) {
+ /* isochronous tds are handled separately */
+ case PIPE_CONTROL:
+ switch (td->privdata) {
+ case SETUP_STAGE:
+ urb->interval =
+ urb->transfer_buffer_length ?
+ DATA_STAGE : STATUS_STAGE;
+ /* Don't count setup_packet with normal data: */
+ urb->actual_length = 0;
+ break;
+ case DATA_STAGE:
+ if (c67x00_end_of_data(td)) {
+ urb->interval = STATUS_STAGE;
+ c67x00_clear_pipe(c67x00, td);
+ }
+ break;
+ case STATUS_STAGE:
+ urb->interval = 0;
+ c67x00_giveback_urb(c67x00, urb, 0);
+ break;
+ }
+ break;
+ case PIPE_INTERRUPT:
+ case PIPE_BULK:
+ if (unlikely(c67x00_end_of_data(td))) {
+ c67x00_clear_pipe(c67x00, td);
+ c67x00_giveback_urb(c67x00, urb, 0);
+ }
+ break;
+ }
+}
+
+static void c67x00_handle_isoc(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+ struct urb *urb = td->urb;
+ struct c67x00_urb_priv *urbp;
+ int cnt;
+
+ if (!urb)
+ return;
+
+ urbp = urb->hcpriv;
+ cnt = td->privdata;
+
+ if (td->status & TD_ERROR_MASK)
+ urb->error_count++;
+
+ urb->iso_frame_desc[cnt].actual_length = td_actual_bytes(td);
+ urb->iso_frame_desc[cnt].status = c67x00_td_to_error(c67x00, td);
+ if (cnt + 1 == urb->number_of_packets) /* Last packet */
+ c67x00_giveback_urb(c67x00, urb, 0);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_check_td_list - handle tds which have been processed by the c67x00
+ * pre: current_td == 0
+ */
+static inline void c67x00_check_td_list(struct c67x00_hcd *c67x00)
+{
+ struct c67x00_td *td, *tmp;
+ struct urb *urb;
+ int ack_ok;
+ int clear_endpoint;
+
+ list_for_each_entry_safe(td, tmp, &c67x00->td_list, td_list) {
+ /* get the TD */
+ c67x00_parse_td(c67x00, td);
+ urb = td->urb; /* urb can be NULL! */
+ ack_ok = 0;
+ clear_endpoint = 1;
+
+ /* Handle isochronous transfers separately */
+ if (usb_pipeisoc(td->pipe)) {
+ clear_endpoint = 0;
+ c67x00_handle_isoc(c67x00, td);
+ goto cont;
+ }
+
+ /* When an error occurs, all td's for that pipe go into an
+ * inactive state. This state matches successful transfers so
+ * we must make sure not to service them. */
+ if (td->status & TD_ERROR_MASK) {
+ c67x00_giveback_urb(c67x00, urb, c67x00_td_to_error(c67x00, td));
+ goto cont;
+ }
+
+ if ((td->status & TD_STATUSMASK_NAK) || !td_sequence_ok(td) ||
+ !td_acked(td))
+ goto cont;
+
+ /* Sequence ok and acked, don't need to fix toggle */
+ ack_ok = 1;
+
+ if (unlikely(td->status & TD_STATUSMASK_OVF)) {
+ if (td_residue(td) & TD_RESIDUE_OVERFLOW) {
+ /* Overflow */
+ c67x00_giveback_urb(c67x00, urb, -EOVERFLOW);
+ goto cont;
+ }
+ }
+
+ clear_endpoint = 0;
+ c67x00_handle_successful_td(c67x00, td);
+
+ cont:
+ if (clear_endpoint)
+ c67x00_clear_pipe(c67x00, td);
+ if (ack_ok)
+ usb_settoggle(td_udev(td), usb_pipeendpoint(td->pipe),
+ usb_pipeout(td->pipe),
+ !(td->ctrl_reg & SEQ_SEL));
+ /* next in list could have been removed, due to clear_pipe! */
+ tmp = list_entry(td->td_list.next, typeof(*td), td_list);
+ release_td(td);
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+void c67x00_hcd_msg_received(struct c67x00_sie *sie, u16 msg)
+{
+ struct c67x00_hcd *c67x00 = sie->private_data;
+ if (msg & HUSB_TDListDone) {
+ msg &= ~HUSB_TDListDone;
+ c67x00_sched_kick(c67x00);
+ }
+ if (msg)
+ dev_warn(c67x00_dev(c67x00),
+ "Unknown SIE msg flag(s): 0x%04x\n", msg);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static inline int c67x00_all_tds_processed(struct c67x00_hcd *c67x00)
+{
+ /* If all tds are processed, we can check the previous frame (if
+ * there was any) and start our next frame.
+ */
+ return !c67x00_ll_husb_get_current_td(c67x00->sie);
+}
+
+/**
+ * Send td to C67X00
+ */
+static void c67x00_send_td(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+ int len = td_length(td);
+
+ if (len && ((td->pid_ep & TD_PIDEPMASK_PID) != TD_PID_IN))
+ c67x00_ll_hpi_write_mem_le16(c67x00->sie->dev,
+ td_ly_base_addr(td),
+ len, td->data);
+
+ c67x00_ll_hpi_write_mem_le16(c67x00->sie->dev, td->td_addr,
+ CY_TD_SIZE, (char *)td);
+}
+
+static void c67x00_send_frame(struct c67x00_hcd *c67x00)
+{
+ struct c67x00_td *td;
+
+ if (list_empty(&c67x00->td_list))
+ dev_warn(c67x00_dev(c67x00),
+ "%s: td list should not be empty here!\n",
+ __FUNCTION__);
+
+ list_for_each_entry(td, &c67x00->td_list, td_list) {
+ if (td->td_list.next == &c67x00->td_list)
+ td->next_td_addr = 0; /* Last td in list */
+
+ c67x00_send_td(c67x00, td);
+ }
+
+ c67x00_ll_husb_set_current_td(c67x00->sie, c67x00->td_base_addr);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_do_work - Schedulers state machine
+ */
+static void c67x00_do_work(struct c67x00_hcd *c67x00)
+{
+ spin_lock(&c67x00->lock);
+ /* Make sure all tds are processed */
+ if (!c67x00_all_tds_processed(c67x00))
+ goto out;
+
+ c67x00_check_td_list(c67x00);
+
+ /* no td's are being processed (current == 0)
+ * and all have been "checked" */
+ complete(&c67x00->endpoint_disable);
+
+ if (!list_empty(&c67x00->td_list))
+ goto out;
+
+ c67x00->current_frame = c67x00_get_current_frame_number(c67x00);
+ if (c67x00->current_frame == c67x00->last_frame)
+ goto out; /* Don't send tds in same frame */
+ c67x00->last_frame = c67x00->current_frame;
+
+ /* If no urbs are scheduled, our work is done */
+ if (!c67x00->urb_count) {
+ c67x00_ll_hpi_disable_sofeop(c67x00->sie);
+ goto out;
+ }
+
+ c67x00_fill_frame(c67x00);
+ if (list_empty(&c67x00->td_list))
+ /* URBs aren't for this frame */
+ goto out;
+ else {
+ /* TD's have been added to the frame */
+ c67x00_send_frame(c67x00);
+ goto out;
+ }
+ out:
+ spin_unlock(&c67x00->lock);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void c67x00_sched_tasklet(unsigned long __c67x00)
+{
+ struct c67x00_hcd *c67x00 = (struct c67x00_hcd *)__c67x00;
+ c67x00_do_work(c67x00);
+}
+
+static void c67x00_sched_done(unsigned long __c67x00)
+{
+ struct c67x00_hcd *c67x00 = (struct c67x00_hcd *)__c67x00;
+ struct c67x00_urb_priv *urbp, *tmp;
+ struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
+ struct urb *urb;
+ int status;
+
+ spin_lock(&c67x00->lock);
+
+ /* Loop over the done list and give back all the urbs */
+ list_for_each_entry_safe(urbp, tmp, &c67x00->done_list, hep_node) {
+ urb = urbp->urb;
+ status = urbp->status;
+
+ c67x00_release_urb(c67x00, urb);
+
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ usb_hcd_giveback_urb(hcd, urb, status);
+ }
+ spin_unlock(&c67x00->lock);
+}
+
+void c67x00_sched_kick(struct c67x00_hcd *c67x00)
+{
+ tasklet_hi_schedule(&c67x00->tasklet);
+}
+
+int c67x00_sched_start_scheduler(struct c67x00_hcd *c67x00)
+{
+ tasklet_init(&c67x00->tasklet, c67x00_sched_tasklet,
+ (unsigned long)c67x00);
+ tasklet_init(&c67x00->done_tasklet, c67x00_sched_done,
+ (unsigned long)c67x00);
+ return 0;
+}
+
+void c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00)
+{
+ tasklet_kill(&c67x00->tasklet);
+ tasklet_kill(&c67x00->done_tasklet);
+}
^ permalink raw reply related
* [PATCH 3/5] USB: add Cypress c67x00 OTG controller core driver
From: Grant Likely @ 2007-11-24 0:24 UTC (permalink / raw)
To: linux-usb-devel, akpm, dbrownell, gregkh, linuxppc-dev
In-Reply-To: <20071124001203.25361.99294.stgit@trillian.cg.shawcable.net>
From: Grant Likely <grant.likely@secretlab.ca>
This 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.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
drivers/usb/c67x00/c67x00-drv.c | 301 +++++++++++++++++++++++++++++++++++++++
include/linux/usb/c67x00.h | 45 ++++++
2 files changed, 346 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c
new file mode 100644
index 0000000..e1b1db1
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-drv.c
@@ -0,0 +1,301 @@
+/*
+ * 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 (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"
+#include "c67x00-hcd.h"
+#include "c67x00-udc.h"
+
+static struct platform_driver c67x00_driver;
+
+static void
+c67x00_setup_sie(struct c67x00_sie *sie, struct c67x00_device *dev, 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->dev = dev;
+ sie->sie_num = sie_num;
+ sie->mode = c67x00_sie_config(dev->pdata->sie_config, sie_num);
+}
+
+static void c67x00_teardown_sie(struct c67x00_sie *sie)
+{
+ sie->pdev->dev.driver = NULL;
+ platform_device_unregister(sie->pdev);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void c67x00_probe_sie(struct c67x00_sie *sie)
+{
+ switch (c67x00_sie_config(sie->dev->pdata->sie_config, sie->sie_num)) {
+ case C67X00_SIE_HOST:
+ c67x00_hcd_probe(sie);
+ break;
+
+ case C67X00_SIE_PERIPHERAL:
+ c67x00_udc_probe(sie);
+ break;
+
+ 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->dev->pdata->sie_config,
+ sie->sie_num), sie->sie_num);
+ break;
+ }
+}
+
+static void c67x00_remove_sie(struct c67x00_sie *sie)
+{
+ switch (c67x00_sie_config(sie->dev->pdata->sie_config, sie->sie_num)) {
+ case C67X00_SIE_HOST:
+ c67x00_hcd_remove(sie);
+ break;
+
+ case C67X00_SIE_PERIPHERAL:
+ c67x00_udc_remove(sie);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* ------------------------------------------------------------------ */
+
+static void
+c67x00_sie_irq(struct c67x00_device *c67x00, int sie_idx, int irq)
+{
+ struct c67x00_sie *sie = &c67x00->sie[sie_idx];
+
+ spin_lock(&sie->lock);
+ if (c67x00->int_status & SIEMSG_FLAG(sie_idx)) {
+ u16 msg = c67x00_ll_get_siemsg(c67x00, sie_idx);
+ if (sie->msg_received)
+ sie->msg_received(sie, msg);
+ }
+ if (sie->irq)
+ sie->irq(irq, sie);
+ spin_unlock(&sie->lock);
+}
+
+static irqreturn_t c67x00_irq(int irq, void *__dev)
+{
+ struct c67x00_device *c67x00 = __dev;
+ int i, count = 8;
+
+ c67x00->int_status = c67x00_ll_hpi_status(c67x00);
+ if (!c67x00->int_status)
+ return IRQ_NONE;
+
+ while (c67x00->int_status != 0 && (count-- >= 0)) {
+ c67x00_ll_irq(c67x00);
+ for (i = 0; i < C67X00_SIES; i++)
+ c67x00_sie_irq(c67x00, i, irq);
+ c67x00->int_status = c67x00_ll_hpi_status(c67x00);
+ }
+
+ if (c67x00->int_status)
+ dev_warn(&c67x00->pdev->dev, "Not all interrupts handled! "
+ "status = 0x%04x\n", c67x00->int_status);
+
+ return IRQ_HANDLED;
+}
+
+/* ---------------------------------------------------------------------
+ * Platform bus binding
+ */
+
+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 = (struct c67x00_platform_data*)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->hw_lock);
+ c67x00->hpi.regstep = pdata->hpi_regstep;
+ c67x00->pdata = pdev->dev.platform_data;
+ c67x00->pdev = pdev;
+
+ for (i = 0; i < C67X00_SIES; i++)
+ c67x00_setup_sie(&c67x00->sie[i], c67x00, i);
+
+ c67x00_ll_init(c67x00);
+ c67x00_ll_hpi_reg_init(c67x00);
+
+ dev_info(&pdev->dev, "USB OTG controller, p:0x%x, v:0x%p, irq:%i\n",
+ res->start, c67x00->hpi.base, res2->start);
+
+ 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]);
+
+ 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_teardown_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 = c67x00_drv_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "c67x00",
+ },
+};
+
+static int __init c67x00_init(void)
+{
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ ret = c67x00_udc_init();
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&c67x00_driver);
+}
+module_init(c67x00_init);
+
+static void __exit c67x00_exit(void)
+{
+ c67x00_udc_cleanup();
+ platform_driver_unregister(&c67x00_driver);
+}
+module_exit(c67x00_exit);
+
+MODULE_AUTHOR("Peter Korsgaard, Jan Veldeman, Grant Likely");
+MODULE_DESCRIPTION("Cypress C67X00 USB Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/usb/c67x00.h b/include/linux/usb/c67x00.h
new file mode 100644
index 0000000..80b1a87
--- /dev/null
+++ b/include/linux/usb/c67x00.h
@@ -0,0 +1,45 @@
+/*
+ * 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_HOST 1
+#define C67X00_SIE_PERIPHERAL 2
+
+#define c67x00_sie_config(config, n) (((config)>>(4*(n)))&0x3)
+
+#define C67X00_SIE1_UNUSED (C67X00_SIE_UNUSED << 0)
+#define C67X00_SIE1_HOST (C67X00_SIE_HOST << 0)
+#define C67X00_SIE1_PERIPHERAL (C67X00_SIE_PERIPHERAL << 0)
+
+#define C67X00_SIE2_UNUSED (C67X00_SIE_UNUSED << 4)
+#define C67X00_SIE2_HOST (C67X00_SIE_HOST << 4)
+#define C67X00_SIE2_PERIPHERAL (C67X00_SIE_PERIPHERAL << 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 */
^ permalink raw reply related
* Re: Linux on ML310
From: narendra sisodiya @ 2007-11-24 0:09 UTC (permalink / raw)
To: Grant Likely; +Cc: linuxppc-embedded
In-Reply-To: <fa686aa40711231517x2f71d8dcv43c58860a052b52c@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2812 bytes --]
Thanks
after getting driver from your site http://git.secretlab.ca/ will the
2.6kernel work fine completely?
What will be the advantage of 2.6 in context to embedded systems.
Actually
I am trying to design is a Video Streaming over RF. I need to transfer
320x240 @ 60 fps video from machine to one / many display systems.
By some method I will try to add a CMOS digital camera to ML310 board (any
suggestion) and Probability i need to build a small system for frame
buffering and then I heard about the Darwin Streaming server. So i will try
to install a tiny Darwin Streaming server on Linux system. Probably I need
to write a small application (or may be driver ) also which will fetch
frames from frame buffer and handle over to server.
For RF interface i want to go with WUSB2.0 ( wireless USB 2.0 ) but due to
some problem my adviser told me to think about blue tooth interface and
perhaps i need to work with blueZ.
I am unable to think for the whole setup and connectivity between various
module. Actually I need to do this my own but i am not getting the whole
picture on which i should work. Can anybody help me out ?? I will be highly
Thankful
Thanks and Regards
On Nov 24, 2007 4:47 AM, Grant Likely <grant.likely@secretlab.ca> wrote:
> On 11/23/07, narendra sisodiya <narendra.sisodiya@gmail.com> wrote:
> > Hi, I am working ML310 XUP virtex2Pro board. My task is to port a Linux
> > 2.4.26 on PowerPC405. I am on the mid way
> > I have following question.
> >
> >
> > Why in every document Linux kernel 2.4.26 is used, can i use 2.6.xxversion.
> > If yes what will be the modifications,
>
> Mainline 2.6.x has support for the virtex device but it is missing
> some drivers for EMAC, TEMAC and some other devices.
>
> You can get the drivers from my tree at http://git.secretlab.ca/ or
> from the xilinx git server http://git.xilinx.com
>
> > In document some are using busybox for making root file system and in
> some
> > Yellow Dog Linux. so what are the such possible options?
>
> Yellow Dog isn't a very good choice for the ppc405 with no floating
> point unit. Use an embedded distro like ELDK, or build your own using
> buildroot or openembedded
>
> > If any body has done some project then Please share your experience with
> me.
> >
> >
> > --
> > Narendra Sisodiya
> > MTech (Computer Technology), IIT Delhi
> > +91-9999232792
> > http://www.techfandu.org/index.html
> > _______________________________________________
> > Linuxppc-embedded mailing list
> > Linuxppc-embedded@ozlabs.org
> > https://ozlabs.org/mailman/listinfo/linuxppc-embedded
> >
>
>
> --
> Grant Likely, B.Sc., P.Eng.
> Secret Lab Technologies Ltd.
> grant.likely@secretlab.ca
> (403) 399-0195
>
--
Narendra Sisodiya
MTech (Computer Technology), IIT Delhi
+91-9999232792
http://www.techfandu.org/index.html
[-- Attachment #2: Type: text/html, Size: 3882 bytes --]
^ permalink raw reply
* Re: [PATCH resend 3/4] powerpc: Add EXPORT_SYMBOL for symbols required by fs_enet and cpm_uart
From: Vitaly Bordug @ 2007-11-23 23:46 UTC (permalink / raw)
To: Jochen Friedrich; +Cc: linuxppc-dev
In-Reply-To: <4745B42B.8050201@scram.de>
On Thu, 22 Nov 2007 17:54:03 +0100
Jochen Friedrich wrote:
> fs_enet and cpm_uart need symbols from commproc.c (for CPM1) or
> cpm2_common.c. Add EXPORT_SYMBOL for cpmp, cpm_setbrg and cpm2_immr,
> so the drivers can be compiled as modules.
>
I think this is not required. We are having very gross setup_immap
function, where just using global area pointer - this should be replaced
with parent node traverse to figure out proper immr reg.
I will likely have a look at this problem. Additionally, I must say what we are having
is unpleasant enough, and EXPORT_...ing global memmapped pointers, we'll open the way to ugly
hacks which nobody want to. Let's cure the disease, not its effects.
> Building modules, stage 2.
> MODPOST 5 modules
> ERROR: "cpm2_immr" [drivers/net/fs_enet/fs_enet.ko] undefined!
> ERROR: "cpmp" [drivers/net/fs_enet/fs_enet.ko] undefined!
> make[1]: *** [__modpost] Error 1
> make: *** [modules] Error 2
>
> Signed-off-by: Jochen Friedrich <jochen@scram.de>
> Acked-by: Scott Wood <scottwood@freescale.com>
> ---
> arch/powerpc/sysdev/commproc.c | 3 +++
> arch/powerpc/sysdev/cpm2_common.c | 3 +++
> 2 files changed, 6 insertions(+), 0 deletions(-)
>
> diff --git a/arch/powerpc/sysdev/commproc.c
> b/arch/powerpc/sysdev/commproc.c index f6a6378..d5a0dcf 100644
> --- a/arch/powerpc/sysdev/commproc.c
> +++ b/arch/powerpc/sysdev/commproc.c
> @@ -51,6 +51,8 @@ static void m8xx_cpm_dpinit(void);
> static uint host_buffer; /* One page of host buffer */
> static uint host_end; /* end + 1 */
> cpm8xx_t __iomem *cpmp; /* Pointer to comm processor space */
> +EXPORT_SYMBOL(cpmp);
> +
> immap_t __iomem *mpc8xx_immr;
> static cpic8xx_t __iomem *cpic_reg;
>
> @@ -302,6 +304,7 @@ cpm_setbrg(uint brg, uint rate)
> out_be32(bp, (((BRG_UART_CLK_DIV16 / rate) - 1) <<
> 1) | CPM_BRG_EN | CPM_BRG_DIV16);
> }
> +EXPORT_SYMBOL(cpm_setbrg);
>
> #ifndef CONFIG_PPC_CPM_NEW_BINDING
> /*
> diff --git a/arch/powerpc/sysdev/cpm2_common.c
> b/arch/powerpc/sysdev/cpm2_common.c index 859362f..4ed5df6 100644
> --- a/arch/powerpc/sysdev/cpm2_common.c
> +++ b/arch/powerpc/sysdev/cpm2_common.c
> @@ -51,11 +51,13 @@ static void cpm2_dpinit(void);
> #endif
>
> cpm_cpm2_t __iomem *cpmp; /* Pointer to comm processor space */
> +EXPORT_SYMBOL(cpmp);
>
> /* We allocate this here because it is used almost exclusively for
> * the communication processor devices.
> */
> cpm2_map_t __iomem *cpm2_immr;
> +EXPORT_SYMBOL(cpm2_immr);
>
> #define CPM_MAP_SIZE (0x40000) /* 256k - the PQ3
> reserve this amount of space for CPM as it is larger
> @@ -117,6 +119,7 @@ cpm_setbrg(uint brg, uint rate)
>
> cpm2_unmap(bp);
> }
> +EXPORT_SYMBOL(cpm_setbrg);
>
> /* This function is used to set high speed synchronous baud rate
> * clocks.
--
Sincerely, Vitaly
^ permalink raw reply
* Re: How to map addresses beyond 4GB
From: Dan Malek @ 2007-11-23 23:23 UTC (permalink / raw)
To: Dell Query; +Cc: linuxppc-embedded
In-Reply-To: <348656.85285.qm@web45609.mail.sp1.yahoo.com>
On Nov 22, 2007, at 2:42 AM, Dell Query wrote:
> May I know how to map this one?
Just call ioremap() as usual. It will understand the > 4G
physical address and return a useful virtual address
to you.
-- Dan
^ permalink raw reply
* Re: Linux on ML310
From: Grant Likely @ 2007-11-23 23:17 UTC (permalink / raw)
To: narendra sisodiya; +Cc: linuxppc-embedded
In-Reply-To: <e3fb97f30711231452t473f7949x901e4921c20ec699@mail.gmail.com>
On 11/23/07, narendra sisodiya <narendra.sisodiya@gmail.com> wrote:
> Hi, I am working ML310 XUP virtex2Pro board. My task is to port a Linux
> 2.4.26 on PowerPC405. I am on the mid way
> I have following question.
>
>
> Why in every document Linux kernel 2.4.26 is used, can i use 2.6.xx version.
> If yes what will be the modifications,
Mainline 2.6.x has support for the virtex device but it is missing
some drivers for EMAC, TEMAC and some other devices.
You can get the drivers from my tree at http://git.secretlab.ca/ or
from the xilinx git server http://git.xilinx.com
> In document some are using busybox for making root file system and in some
> Yellow Dog Linux. so what are the such possible options?
Yellow Dog isn't a very good choice for the ppc405 with no floating
point unit. Use an embedded distro like ELDK, or build your own using
buildroot or openembedded
> If any body has done some project then Please share your experience with me.
>
>
> --
> Narendra Sisodiya
> MTech (Computer Technology), IIT Delhi
> +91-9999232792
> http://www.techfandu.org/index.html
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
>
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
grant.likely@secretlab.ca
(403) 399-0195
^ permalink raw reply
* Linux on ML310
From: narendra sisodiya @ 2007-11-23 22:52 UTC (permalink / raw)
To: linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 606 bytes --]
Hi, I am working ML310 XUP virtex2Pro board. My task is to port a Linux
2.4.26 on PowerPC405. I am on the mid way
I have following question.
1. Why in every document Linux kernel 2.4.26 is used, can i use
2.6.xxversion. If yes what will be the modifications,
2. In document some are using busybox for making root file system and
in some Yellow Dog Linux. so what are the such possible options?
3. If any body has done some project then Please share your experience
with me.
--
Narendra Sisodiya
MTech (Computer Technology), IIT Delhi
+91-9999232792
http://www.techfandu.org/index.html
[-- Attachment #2: Type: text/html, Size: 713 bytes --]
^ permalink raw reply
* Re: time accounting problem (powerpc only?)
From: Johannes Berg @ 2007-11-23 20:48 UTC (permalink / raw)
To: Jörg Sommer; +Cc: linuxppc-dev
In-Reply-To: <1195850245.4149.170.camel@johannes.berg>
[-- Attachment #1: Type: text/plain, Size: 908 bytes --]
On Fri, 2007-11-23 at 21:37 +0100, Johannes Berg wrote:
> Hi,
> > - 25: 18063968 MPIC 1 Level VIA-PMU
> > + 25: 18064241 MPIC 1 Level VIA-PMU
> > - 42: 1415066 MPIC 1 Level keywest i2c
> > - 47: 2075159 MPIC 1 Level GPIO1 ADB
> > - 48: 6686659 MPIC 1 Level radeon@pci:0000:00:10.0
> > + 42: 1415084 MPIC 1 Level keywest i2c
> > + 47: 2075193 MPIC 1 Level GPIO1 ADB
> > + 48: 6686778 MPIC 1 Level radeon@pci:0000:00:10.0
> Are you sure about that? I'm fairly sure that I always had rather high
> numbers of interrupt here. And the system isn't sluggish or unresponsive
> as you'd expect if the IRQs actually did take 90% of the CPU time!
Also, I'm pretty sure I see the same on my powermac (haven't tested but
I did see the same symptoms) and it has neither a PMU nor radeon :)
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]
^ permalink raw reply
* Re: time accounting problem (powerpc only?)
From: Johannes Berg @ 2007-11-23 20:37 UTC (permalink / raw)
To: Jörg Sommer; +Cc: linuxppc-dev
In-Reply-To: <slrnfkdnma.681.joerg@alea.gnuu.de>
[-- Attachment #1: Type: text/plain, Size: 1533 bytes --]
Hi,
> % diff /proc/interrupts <(sleep 2; cat /proc/interrupts)
> --- /proc/interrupts 2007-11-23 15:04:06.004846901 +0100
> +++ /proc/self/fd/11 2007-11-23 15:04:05.952841422 +0100
> @@ -1,15 +1,15 @@
> CPU0
> 21: 5 MPIC 1 Edge PMac Output
> 24: 68 MPIC 1 Level ide1
> - 25: 18063968 MPIC 1 Level VIA-PMU
> + 25: 18064241 MPIC 1 Level VIA-PMU
> 26: 2426 MPIC 1 Level keywest i2c
> 29: 1 MPIC 1 Level ohci_hcd:usb2
> 30: 1 MPIC 1 Edge PMac Input
> 39: 843910 MPIC 1 Level ide0
> 41: 990592 MPIC 1 Level eth0
> - 42: 1415066 MPIC 1 Level keywest i2c
> - 47: 2075159 MPIC 1 Level GPIO1 ADB
> - 48: 6686659 MPIC 1 Level radeon@pci:0000:00:10.0
> + 42: 1415084 MPIC 1 Level keywest i2c
> + 47: 2075193 MPIC 1 Level GPIO1 ADB
> + 48: 6686778 MPIC 1 Level radeon@pci:0000:00:10.0
> 61: 0 MPIC 1 Edge Sound Headphone Detection
> 63: 94238 MPIC 1 Level ehci_hcd:usb1, ohci_hcd:usb3, ohci_hcd:usb4
> BAD: 19
>
> I don't know where they come from, but that's the cause of the high IRQ
> time.
Are you sure about that? I'm fairly sure that I always had rather high
numbers of interrupt here. And the system isn't sluggish or unresponsive
as you'd expect if the IRQs actually did take 90% of the CPU time!
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]
^ permalink raw reply
* Re: Hardware debuggers for PPC74xx G4 CPUs
From: Grant Likely @ 2007-11-23 20:30 UTC (permalink / raw)
To: Jon Smirl; +Cc: Olof Johansson, linuxppc-dev
In-Reply-To: <9e4733910711231226p44541777n4a1190c0ecf5bb55@mail.gmail.com>
On 11/23/07, Jon Smirl <jonsmirl@gmail.com> wrote:
> On 11/23/07, Jon Smirl <jonsmirl@gmail.com> wrote:
> > On 11/22/07, Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
> > >
> > > On Thu, 2007-11-22 at 19:00 -0500, Jon Smirl wrote:
> > > > > It seems like AMCC does provide the necessary infos for 4xx
> > > > processors
> > > > > though. Jon, do you think what they provide is enough to use an open
> > > > > source debugger ?
> > > >
> > > > Do you have a link to the info?
> > >
> > > Not at hand, but easy to find from AMCC main product page, you can then
> > > go to the various eval board download pages, and some of the d/l are
> > > infos for JTAG debugger manufacturers.
>
> Grant, I heard back from the Amontec (a JTAG company) developers. They
> are interested in doing an open JTAG for the Xilinx Microblaze. Is
> that the FPGA you've been working with?
Yes, the FPGA I'm using can host a Microblaze design; except my work
has focused on the ppc405 hard IP core instead of the microblaze core.
Regardless, this sounds interesting. Can you get JTAG debug info out of Xilinx?
Cheers,
g.
.
>
> --
> Jon Smirl
> jonsmirl@gmail.com
>
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
grant.likely@secretlab.ca
(403) 399-0195
^ permalink raw reply
* Re: Hardware debuggers for PPC74xx G4 CPUs
From: Jon Smirl @ 2007-11-23 20:26 UTC (permalink / raw)
To: Grant Likely; +Cc: Olof Johansson, linuxppc-dev
In-Reply-To: <9e4733910711230703w3a8ba108ybfdd0a9f2bf6f537@mail.gmail.com>
On 11/23/07, Jon Smirl <jonsmirl@gmail.com> wrote:
> On 11/22/07, Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
> >
> > On Thu, 2007-11-22 at 19:00 -0500, Jon Smirl wrote:
> > > > It seems like AMCC does provide the necessary infos for 4xx
> > > processors
> > > > though. Jon, do you think what they provide is enough to use an open
> > > > source debugger ?
> > >
> > > Do you have a link to the info?
> >
> > Not at hand, but easy to find from AMCC main product page, you can then
> > go to the various eval board download pages, and some of the d/l are
> > infos for JTAG debugger manufacturers.
Grant, I heard back from the Amontec (a JTAG company) developers. They
are interested in doing an open JTAG for the Xilinx Microblaze. Is
that the FPGA you've been working with?
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply
* [PATCH 3/3] PowerPC: ibm_newemac call dev_set_drvdata() before tah_reset()
From: Valentine Barshak @ 2007-11-23 19:14 UTC (permalink / raw)
To: linuxppc-dev; +Cc: jgarzik, netdev
In-Reply-To: <20071123190747.GA25654@ru.mvista.com>
The patch moves dev_set_drvdata(&ofdev->dev, dev) up before tah_reset(ofdev)
is called to avoid a NULL pointer dereference, since tah_reset uses drvdata.
Signed-off-by: Valentine Barshak <vbarshak@ru.mvista.com>
---
drivers/net/ibm_newemac/tah.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletion(-)
diff -pruN linux-2.6.orig/drivers/net/ibm_newemac/tah.c linux-2.6/drivers/net/ibm_newemac/tah.c
--- linux-2.6.orig/drivers/net/ibm_newemac/tah.c 2007-11-23 21:27:57.000000000 +0300
+++ linux-2.6/drivers/net/ibm_newemac/tah.c 2007-11-23 21:35:12.000000000 +0300
@@ -116,13 +116,14 @@ static int __devinit tah_probe(struct of
goto err_free;
}
+ dev_set_drvdata(&ofdev->dev, dev);
+
/* Initialize TAH and enable IPv4 checksum verification, no TSO yet */
tah_reset(ofdev);
printk(KERN_INFO
"TAH %s initialized\n", ofdev->node->full_name);
wmb();
- dev_set_drvdata(&ofdev->dev, dev);
return 0;
^ permalink raw reply
* [PATCH 2/3] PowerPC: ibm_newemac tah_ph typo fix
From: Valentine Barshak @ 2007-11-23 19:11 UTC (permalink / raw)
To: linuxppc-dev; +Cc: jgarzik, netdev
In-Reply-To: <20071123190747.GA25654@ru.mvista.com>
This patch fixes a typo in ibm_newemac/core.c
(tah_port should be used instead of tah_ph)
Signed-off-by: Valentine Barshak <vbarshak@ru.mvista.com>
---
drivers/net/ibm_newemac/core.c | 2 +-
1 files changed, 1 insertion(+), 1 deletion(-)
diff -pruN linux-2.6.orig/drivers/net/ibm_newemac/core.c linux-2.6/drivers/net/ibm_newemac/core.c
--- linux-2.6.orig/drivers/net/ibm_newemac/core.c 2007-11-23 21:27:57.000000000 +0300
+++ linux-2.6/drivers/net/ibm_newemac/core.c 2007-11-23 21:36:00.000000000 +0300
@@ -2427,7 +2427,7 @@ static int __devinit emac_init_config(st
if (emac_read_uint_prop(np, "tah-device", &dev->tah_ph, 0))
dev->tah_ph = 0;
if (emac_read_uint_prop(np, "tah-channel", &dev->tah_port, 0))
- dev->tah_ph = 0;
+ dev->tah_port = 0;
if (emac_read_uint_prop(np, "mdio-device", &dev->mdio_ph, 0))
dev->mdio_ph = 0;
if (emac_read_uint_prop(np, "zmii-device", &dev->zmii_ph, 0))
^ permalink raw reply
* [PATCH 1/3] PowerPC: ibm_newemac correct opb_bus_freq value
From: Valentine Barshak @ 2007-11-23 19:10 UTC (permalink / raw)
To: linuxppc-dev; +Cc: jgarzik, netdev
In-Reply-To: <20071123190747.GA25654@ru.mvista.com>
The EMAC4_MR1_OBCI(freq) macro expects freg in MHz,
while opb_bus_freq is kept in Hz. Correct this.
Signed-off-by: Valentine Barshak <vbarshak@ru.mvista.com>
---
drivers/net/ibm_newemac/core.c | 2 +-
1 files changed, 1 insertion(+), 1 deletion(-)
diff -pruN linux-2.6.orig/drivers/net/ibm_newemac/core.c linux-2.6/drivers/net/ibm_newemac/core.c
--- linux-2.6.orig/drivers/net/ibm_newemac/core.c 2007-11-23 21:27:57.000000000 +0300
+++ linux-2.6/drivers/net/ibm_newemac/core.c 2007-11-23 21:47:53.000000000 +0300
@@ -402,7 +402,7 @@ static u32 __emac_calc_base_mr1(struct e
static u32 __emac4_calc_base_mr1(struct emac_instance *dev, int tx_size, int rx_size)
{
u32 ret = EMAC_MR1_VLE | EMAC_MR1_IST | EMAC4_MR1_TR |
- EMAC4_MR1_OBCI(dev->opb_bus_freq);
+ EMAC4_MR1_OBCI(dev->opb_bus_freq / 1000000);
DBG2(dev, "__emac4_calc_base_mr1" NL);
^ permalink raw reply
* [PATCH 0/3] PowerPC: ibm_newemac minor fixes.
From: Valentine Barshak @ 2007-11-23 19:07 UTC (permalink / raw)
To: linuxppc-dev; +Cc: jgarzik, netdev
These patches have some minor ibm_newemac fixes.
Thanks,
Valentine.
^ permalink raw reply
* How to map addresses beyond 4GB
From: Dell Query @ 2007-11-22 10:42 UTC (permalink / raw)
To: linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 352 bytes --]
Hi,
I would like to ask if anyone knows how to map addresses beyond 4GB. I am trying to access the GPIO on PPC440EPx by mapping it but the address is 0x1EF600B00, which is way beyond 4GB.
May I know how to map this one?
Thanks,
dell
---------------------------------
Get easy, one-click access to your favorites. Make Yahoo! your homepage.
[-- Attachment #2: Type: text/html, Size: 436 bytes --]
^ permalink raw reply
* [PATCH 3/3] [POWERPC] MPC8349E-mITX: introduce pata node, make use pata_of_platform driver
From: Anton Vorontsov @ 2007-11-23 17:53 UTC (permalink / raw)
To: linuxppc-dev, linux-ide
In-Reply-To: <20071123175229.GA27143@localhost.localdomain>
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
arch/powerpc/boot/dts/mpc8349emitx.dts | 9 +++++++--
arch/powerpc/platforms/83xx/mpc834x_itx.c | 17 +++++++++++++++++
2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/boot/dts/mpc8349emitx.dts b/arch/powerpc/boot/dts/mpc8349emitx.dts
index 5072f6d..898c294 100644
--- a/arch/powerpc/boot/dts/mpc8349emitx.dts
+++ b/arch/powerpc/boot/dts/mpc8349emitx.dts
@@ -249,6 +249,11 @@
device_type = "pci";
};
-
-
+ pata@f0000000 {
+ compatible = "fsl,mpc8349emitx", "pata-platform";
+ reg = <f0000000 10 f000020c 4>;
+ ioport-shift = <1>;
+ interrupts = <17 8>;
+ interrupt-parent = <&ipic>;
+ };
};
diff --git a/arch/powerpc/platforms/83xx/mpc834x_itx.c b/arch/powerpc/platforms/83xx/mpc834x_itx.c
index aa76819..150fafb 100644
--- a/arch/powerpc/platforms/83xx/mpc834x_itx.c
+++ b/arch/powerpc/platforms/83xx/mpc834x_itx.c
@@ -23,6 +23,7 @@
#include <linux/delay.h>
#include <linux/seq_file.h>
#include <linux/root_dev.h>
+#include <linux/of_platform.h>
#include <asm/system.h>
#include <asm/atomic.h>
@@ -37,6 +38,22 @@
#include "mpc83xx.h"
+static struct of_device_id mpc834x_ids[] = {
+ { .compatible = "pata-platform", },
+ {},
+};
+
+static int __init mpc834x_declare_of_platform_devices(void)
+{
+ if (!machine_is(mpc834x_itx))
+ return 0;
+
+ of_platform_bus_probe(NULL, mpc834x_ids, NULL);
+
+ return 0;
+}
+device_initcall(mpc834x_declare_of_platform_devices);
+
/* ************************************************************************
*
* Setup the architecture
--
1.5.2.2
^ permalink raw reply related
* [PATCH 2/3] [libata] pata_of_platform: OF-Platform PATA device driver
From: Anton Vorontsov @ 2007-11-23 17:53 UTC (permalink / raw)
To: linuxppc-dev, linux-ide
In-Reply-To: <20071123175229.GA27143@localhost.localdomain>
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
drivers/ata/Kconfig | 10 +++++
drivers/ata/Makefile | 1 +
drivers/ata/pata_of_platform.c | 88 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 99 insertions(+), 0 deletions(-)
create mode 100644 drivers/ata/pata_of_platform.c
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index ba63619..5a492fa 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -614,6 +614,16 @@ config PATA_PLATFORM
If unsure, say N.
+config PATA_OF_PLATFORM
+ tristate "OpenFirmware platform device PATA support"
+ depends on PATA_PLATFORM && PPC_OF
+ help
+ This option enables support for generic directly connected ATA
+ devices commonly found on embedded systems with OpenFirmware
+ bindings.
+
+ If unsure, say N.
+
config PATA_ICSIDE
tristate "Acorn ICS PATA support"
depends on ARM && ARCH_ACORN
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index b13feb2..ebcee64 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_PATA_IXP4XX_CF) += pata_ixp4xx_cf.o
obj-$(CONFIG_PATA_SCC) += pata_scc.o
obj-$(CONFIG_PATA_BF54X) += pata_bf54x.o
obj-$(CONFIG_PATA_PLATFORM) += pata_platform.o
+obj-$(CONFIG_PATA_OF_PLATFORM) += pata_of_platform.o
obj-$(CONFIG_PATA_ICSIDE) += pata_icside.o
# Should be last but two libata driver
obj-$(CONFIG_PATA_ACPI) += pata_acpi.o
diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c
new file mode 100644
index 0000000..b4eca70
--- /dev/null
+++ b/drivers/ata/pata_of_platform.c
@@ -0,0 +1,88 @@
+/*
+ * OF-platform PATA driver
+ *
+ * Copyright (c) 2007 MontaVista Software, Inc.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include "pata_platform.h"
+
+static int __devinit pata_of_platform_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ int ret;
+ struct device_node *dn = ofdev->node;
+ struct resource io_res;
+ struct resource ctl_res;
+ struct resource irq_res;
+ unsigned int ioport_shift = 0;
+ uint32_t *prop;
+
+ ret = of_address_to_resource(dn, 0, &io_res);
+ if (ret) {
+ dev_err(&ofdev->dev, "can't get IO address from "
+ "device tree\n");
+ return -EINVAL;
+ }
+
+ ret = of_address_to_resource(dn, 1, &ctl_res);
+ if (ret) {
+ dev_err(&ofdev->dev, "can't get CTL address from "
+ "device tree\n");
+ return -EINVAL;
+ }
+
+ ret = of_irq_to_resource(dn, 0, &irq_res);
+ if (ret == NO_IRQ)
+ irq_res.start = irq_res.end = -1;
+ else
+ irq_res.flags = 0;
+
+ prop = (uint32_t *)of_get_property(dn, "ioport-shift", NULL);
+ if (prop)
+ ioport_shift = *prop;
+
+ return __pata_platform_probe(&ofdev->dev, &io_res, &ctl_res, &irq_res,
+ ioport_shift);
+}
+
+static int __devexit pata_of_platform_remove(struct of_device *ofdev)
+{
+ return __pata_platform_remove(&ofdev->dev);
+}
+
+static struct of_device_id pata_of_platform_match[] = {
+ {
+ .compatible = "pata-platform",
+ },
+};
+
+static struct of_platform_driver pata_of_platform_driver = {
+ .name = "pata_of_platform",
+ .match_table = pata_of_platform_match,
+ .probe = pata_of_platform_probe,
+ .remove = __devexit_p(pata_of_platform_remove),
+};
+
+static int __init pata_of_platform_init(void)
+{
+ return of_register_platform_driver(&pata_of_platform_driver);
+}
+module_init(pata_of_platform_init);
+
+static void __exit pata_platform_exit(void)
+{
+ of_unregister_platform_driver(&pata_of_platform_driver);
+}
+module_exit(pata_platform_exit);
+
+MODULE_DESCRIPTION("OF-platform PATA driver");
+MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
+MODULE_LICENSE("GPL");
--
1.5.2.2
^ permalink raw reply related
* [PATCH 1/3] [libata] pata_platform: make probe and remove functions device type neutral
From: Anton Vorontsov @ 2007-11-23 17:53 UTC (permalink / raw)
To: linuxppc-dev, linux-ide
In-Reply-To: <20071123175229.GA27143@localhost.localdomain>
Split pata_platform_{probe,remove} into two pieces:
1. pata_platform_{probe,remove} -- platform_device-dependant bits
2. __ptata_platform_{probe,remove} -- device type neutral bits.
This is done to not duplicate code for the OF-platform driver.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
drivers/ata/pata_platform.c | 139 +++++++++++++++++++++++++------------------
drivers/ata/pata_platform.h | 12 ++++
2 files changed, 94 insertions(+), 57 deletions(-)
create mode 100644 drivers/ata/pata_platform.h
diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c
index ac03a90..6436c38 100644
--- a/drivers/ata/pata_platform.c
+++ b/drivers/ata/pata_platform.c
@@ -93,14 +93,9 @@ static struct ata_port_operations pata_platform_port_ops = {
};
static void pata_platform_setup_port(struct ata_ioports *ioaddr,
- struct pata_platform_info *info)
+ unsigned int shift)
{
- unsigned int shift = 0;
-
/* Fixup the port shift for platforms that need it */
- if (info && info->ioport_shift)
- shift = info->ioport_shift;
-
ioaddr->data_addr = ioaddr->cmd_addr + (ATA_REG_DATA << shift);
ioaddr->error_addr = ioaddr->cmd_addr + (ATA_REG_ERR << shift);
ioaddr->feature_addr = ioaddr->cmd_addr + (ATA_REG_FEATURE << shift);
@@ -114,8 +109,12 @@ static void pata_platform_setup_port(struct ata_ioports *ioaddr,
}
/**
- * pata_platform_probe - attach a platform interface
- * @pdev: platform device
+ * __pata_platform_probe - attach a platform interface
+ * @dev: device
+ * @io_res: Resource representing I/O base
+ * @ctl_res: Resource representing CTL base
+ * @irq_res: Resource representing IRQ and its flags
+ * @ioport_shift: I/O port shift
*
* Register a platform bus IDE interface. Such interfaces are PIO and we
* assume do not support IRQ sharing.
@@ -135,42 +134,17 @@ static void pata_platform_setup_port(struct ata_ioports *ioaddr,
*
* If no IRQ resource is present, PIO polling mode is used instead.
*/
-static int __devinit pata_platform_probe(struct platform_device *pdev)
+int __devinit __pata_platform_probe(struct device *dev,
+ struct resource *io_res,
+ struct resource *ctl_res,
+ struct resource *irq_res,
+ unsigned int ioport_shift)
{
- struct resource *io_res, *ctl_res;
struct ata_host *host;
struct ata_port *ap;
- struct pata_platform_info *pp_info;
unsigned int mmio;
- int irq;
-
- /*
- * Simple resource validation ..
- */
- if ((pdev->num_resources != 3) && (pdev->num_resources != 2)) {
- dev_err(&pdev->dev, "invalid number of resources\n");
- return -EINVAL;
- }
-
- /*
- * Get the I/O base first
- */
- io_res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (io_res == NULL) {
- io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (unlikely(io_res == NULL))
- return -EINVAL;
- }
-
- /*
- * Then the CTL base
- */
- ctl_res = platform_get_resource(pdev, IORESOURCE_IO, 1);
- if (ctl_res == NULL) {
- ctl_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (unlikely(ctl_res == NULL))
- return -EINVAL;
- }
+ int irq = 0;
+ int irq_flags = 0;
/*
* Check for MMIO
@@ -181,14 +155,15 @@ static int __devinit pata_platform_probe(struct platform_device *pdev)
/*
* And the IRQ
*/
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- irq = 0; /* no irq */
+ if (irq_res && irq_res->start > 0) {
+ irq = irq_res->start;
+ irq_flags = irq_res->flags;
+ }
/*
* Now that that's out of the way, wire up the port..
*/
- host = ata_host_alloc(&pdev->dev, 1);
+ host = ata_host_alloc(dev, 1);
if (!host)
return -ENOMEM;
ap = host->ports[0];
@@ -209,25 +184,24 @@ static int __devinit pata_platform_probe(struct platform_device *pdev)
* Handle the MMIO case
*/
if (mmio) {
- ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, io_res->start,
+ ap->ioaddr.cmd_addr = devm_ioremap(dev, io_res->start,
io_res->end - io_res->start + 1);
- ap->ioaddr.ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start,
+ ap->ioaddr.ctl_addr = devm_ioremap(dev, ctl_res->start,
ctl_res->end - ctl_res->start + 1);
} else {
- ap->ioaddr.cmd_addr = devm_ioport_map(&pdev->dev, io_res->start,
+ ap->ioaddr.cmd_addr = devm_ioport_map(dev, io_res->start,
io_res->end - io_res->start + 1);
- ap->ioaddr.ctl_addr = devm_ioport_map(&pdev->dev, ctl_res->start,
+ ap->ioaddr.ctl_addr = devm_ioport_map(dev, ctl_res->start,
ctl_res->end - ctl_res->start + 1);
}
if (!ap->ioaddr.cmd_addr || !ap->ioaddr.ctl_addr) {
- dev_err(&pdev->dev, "failed to map IO/CTL base\n");
+ dev_err(dev, "failed to map IO/CTL base\n");
return -ENOMEM;
}
ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
- pp_info = pdev->dev.platform_data;
- pata_platform_setup_port(&ap->ioaddr, pp_info);
+ pata_platform_setup_port(&ap->ioaddr, ioport_shift);
ata_port_desc(ap, "%s cmd 0x%llx ctl 0x%llx", mmio ? "mmio" : "ioport",
(unsigned long long)io_res->start,
@@ -235,26 +209,77 @@ static int __devinit pata_platform_probe(struct platform_device *pdev)
/* activate */
return ata_host_activate(host, irq, irq ? ata_interrupt : NULL,
- pp_info ? pp_info->irq_flags : 0,
- &pata_platform_sht);
+ irq_flags, &pata_platform_sht);
}
+EXPORT_SYMBOL_GPL(__pata_platform_probe);
/**
- * pata_platform_remove - unplug a platform interface
- * @pdev: platform device
+ * __pata_platform_remove - unplug a platform interface
+ * @dev: device
*
* A platform bus ATA device has been unplugged. Perform the needed
* cleanup. Also called on module unload for any active devices.
*/
-static int __devexit pata_platform_remove(struct platform_device *pdev)
+int __devexit __pata_platform_remove(struct device *dev)
{
- struct device *dev = &pdev->dev;
struct ata_host *host = dev_get_drvdata(dev);
ata_host_detach(host);
return 0;
}
+EXPORT_SYMBOL_GPL(__pata_platform_remove);
+
+static int __devinit pata_platform_probe(struct platform_device *pdev)
+{
+ struct resource *io_res;
+ struct resource *ctl_res;
+ struct resource *irq_res;
+ struct pata_platform_info *pp_info = pdev->dev.platform_data;
+
+ /*
+ * Simple resource validation ..
+ */
+ if ((pdev->num_resources != 3) && (pdev->num_resources != 2)) {
+ dev_err(&pdev->dev, "invalid number of resources\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Get the I/O base first
+ */
+ io_res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (io_res == NULL) {
+ io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(io_res == NULL))
+ return -EINVAL;
+ }
+
+ /*
+ * Then the CTL base
+ */
+ ctl_res = platform_get_resource(pdev, IORESOURCE_IO, 1);
+ if (ctl_res == NULL) {
+ ctl_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (unlikely(ctl_res == NULL))
+ return -EINVAL;
+ }
+
+ /*
+ * And the IRQ
+ */
+ irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (irq_res)
+ irq_res->flags = pp_info ? pp_info->irq_flags : 0;
+
+ return __pata_platform_probe(&pdev->dev, io_res, ctl_res, irq_res,
+ pp_info ? pp_info->ioport_shift : 0);
+}
+
+static int __devexit pata_platform_remove(struct platform_device *pdev)
+{
+ return __pata_platform_remove(&pdev->dev);
+}
static struct platform_driver pata_platform_driver = {
.probe = pata_platform_probe,
diff --git a/drivers/ata/pata_platform.h b/drivers/ata/pata_platform.h
new file mode 100644
index 0000000..9752a42
--- /dev/null
+++ b/drivers/ata/pata_platform.h
@@ -0,0 +1,12 @@
+#ifndef __DRIVERS_ATA_PATA_PLATFORM_H
+#define __DRIVERS_ATA_PATA_PLATFORM_H
+
+extern int __devinit __pata_platform_probe(struct device *dev,
+ struct resource *io_res,
+ struct resource *ctl_res,
+ struct resource *irq_res,
+ unsigned int ioport_shift);
+
+extern int __devexit __pata_platform_remove(struct device *dev);
+
+#endif /* __DRIVERS_ATA_PATA_PLATFORM_H */
--
1.5.2.2
^ permalink raw reply related
* [RFC][PATCH 0/3] OF-platform PATA driver
From: Anton Vorontsov @ 2007-11-23 17:52 UTC (permalink / raw)
To: linuxppc-dev, linux-ide
Hi all,
Here is the PATA Platform driver using OF infrastructure.
Mostly it's just a wrapper around a bit modified pata_platform
driver.
Patches are well split for the easier review:
First one factors out platform_device specific bits and modifies
pata_platform to be a library-alike driver (with platform_device
default binding).
Second patch is OF-driver itself which is using pata_platform
"library".
Third patch is PowerPC specific, but I'm still Cc'ing linux-ide,
just to show how we're using it.
As an alternative approach we can use plain pata_platform
driver, but I'm not sure how Linux OF bindings' ideologists will
or will not like it.
So, these patches are strongly Request For Comments. Feel free
to train your nitpicking skills ;-), and/or vote for the option
you most pleased about (or suggest another?).
Thanks.
---
Down here is "alternative approach".
Probably board-neutral version may be placed somewhere in
the drivers/of/...? But who will call it: board file, or
device_initcall for all boards?
diff --git a/arch/powerpc/platforms/83xx/mpc834x_itx.c b/arch/powerpc/platforms/83xx/mpc834x_itx.c
index 150fafb..4caa90d 100644
--- a/arch/powerpc/platforms/83xx/mpc834x_itx.c
+++ b/arch/powerpc/platforms/83xx/mpc834x_itx.c
@@ -24,6 +24,7 @@
#include <linux/seq_file.h>
#include <linux/root_dev.h>
#include <linux/of_platform.h>
+#include <linux/pata_platform.h>
#include <asm/system.h>
#include <asm/atomic.h>
@@ -102,6 +103,78 @@ static int __init mpc834x_itx_probe(void)
return of_flat_dt_is_compatible(root, "MPC834xMITX");
}
+static int __init mpc834x_itx_pata_init(void)
+{
+ struct device_node *np;
+ unsigned int i;
+
+ if (!machine_is(mpc834x_itx))
+ return 0;
+
+ for (np = NULL, i = 0;
+ (np = of_find_compatible_node(np, NULL, "pata-platform"));
+ i++) {
+ int ret;
+ struct resource res[3];
+ const u32 *ioport_shift;
+ struct platform_device *pdev;
+ struct pata_platform_info pdata = {};
+
+ memset(res, 0, sizeof(res));
+
+ ret = of_address_to_resource(np, 0, &res[0]);
+ if (ret) {
+ printk(KERN_ERR "pata.%d: unable to get IO address "
+ "from the device tree\n", i);
+ goto err0;
+ }
+
+ ret = of_address_to_resource(np, 1, &res[1]);
+ if (ret) {
+ printk(KERN_ERR "pata.%d: unable to get CTL address "
+ "from the device tree\n", i);
+ goto err0;
+ }
+
+ ret = of_irq_to_resource(np, 0, &res[2]);
+ if (ret == NO_IRQ) {
+ printk(KERN_ERR "pata.%d: no IRQ\n", i);
+ goto err0;
+ }
+
+ ioport_shift = of_get_property(np, "ioport-shift", NULL);
+ if (ioport_shift)
+ pdata.ioport_shift = *ioport_shift;
+
+ pdev = platform_device_alloc("pata_platform", i);
+ if (!pdev)
+ goto err0;
+
+ ret = platform_device_add_data(pdev, &pdata, sizeof(pdata));
+ if (ret)
+ goto err1;
+
+ ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
+ if (ret)
+ goto err1;
+
+ ret = platform_device_register(pdev);
+ if (ret)
+ goto err1;
+
+ continue;
+err1:
+ printk(KERN_ERR "pata.%d: registration failed\n", i);
+ platform_device_del(pdev); /* will free everything */
+err0:
+ /* Even if some device failed, try others */
+ continue;
+ }
+
+ return 0;
+}
+device_initcall(mpc834x_itx_pata_init);
+
define_machine(mpc834x_itx) {
.name = "MPC834x ITX",
.probe = mpc834x_itx_probe,
^ permalink raw reply related
* MontaVista 4.0.1 on Xilinx ML405 board
From: gsk19 @ 2007-11-23 16:58 UTC (permalink / raw)
To: linuxppc-embedded
We built a design for ML405 using EDK 9.1.02. EDK also
creates linux drivers for MontaVista Pro 4.0.1 (2.6.10).
The drivers were copied to a clean copy of Pro 4.0.1 and the
kernel was built. When we downloaded the kernel to the target
and ran it, we got exception. We didn't get any messages on
the hyper terminal.
We used Tri-Mode Ethernet and UART lite.
MontaVista checked the integration of EDK 8.2 with ML403.
Can you help ?
^ permalink raw reply
* sys_mpc83xx spi driver not probed
From: Joel Rouch @ 2007-11-23 17:02 UTC (permalink / raw)
To: linuxppc-dev
Hello
I have a custom 8349 ppc board. I have defined the spi master in the
configuration.
While booting my board, I driver is well added and linked to platform
bus, however I don't see that the probe entry point is reached.
I don't succeed to understand who is triggering the probe. Can someone
point me to the right link or explain me how to force it ?
Thanks
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox