* Re: [PATCH 3/6] [POWERPC] QE: prepare QE PIO code for GPIO LIB support
From: Timur Tabi @ 2008-04-29 20:10 UTC (permalink / raw)
To: Anton Vorontsov; +Cc: linuxppc-dev
In-Reply-To: <20080429190035.GC21203@polina.dev.rtsoft.ru>
Anton Vorontsov wrote:
> - split and export __par_io_config_pin() out of par_io_config_pin(), so we
> could use the prefixed version with GPIO LIB API;
> - rename struct port_regs to qe_pio_regs, and place it into qe.h;
> - rename #define NUM_OF_PINS to QE_PIO_PINS, and place it into qe.h.
>
> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Acked-By: Timur Tabi <timur@freescale.com>
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* Re: [patch v11 1/4] USB: add Cypress c67x00 low level interface code
From: Grant Likely @ 2008-04-29 20:24 UTC (permalink / raw)
To: Peter Korsgaard; +Cc: linuxppc-dev, dbrownell, linux-usb, greg
In-Reply-To: <20080427070231.581107724@sunsite.dk>
On Sun, Apr 27, 2008 at 12:59 AM, Peter Korsgaard <jacmet@sunsite.dk> wrote:
> This patch adds the low level support code for the Cypress c67x00 family of
> OTG controllers. The low level code is responsible for register access and
> implements the software protocol for communicating with the 16bit
> microcontroller inside the c67x00 device.
>
> Communication is done over the HPI interface (16bit SRAM-like parallel bus).
>
> Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
> Acked-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
> ---
> drivers/usb/c67x00/c67x00-ll-hpi.c | 405 +++++++++++++++++++++++++++++++++++++
> drivers/usb/c67x00/c67x00.h | 285 ++++++++++++++++++++++++++
> 2 files changed, 690 insertions(+)
>
> Index: linux-2.6/drivers/usb/c67x00/c67x00.h
> ===================================================================
> --- /dev/null
> +++ linux-2.6/drivers/usb/c67x00/c67x00.h
> @@ -0,0 +1,285 @@
> +/*
> + * c67x00.h: Cypress C67X00 USB register and field definitions
> + *
> + * Copyright (C) 2006-2008 Barco N.V.
> + * Derived from the Cypress cy7c67200/300 ezusb linux driver and
> + * based on multiple host controller drivers inside the linux kernel.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> + * MA 02110-1301 USA.
> + */
> +
> +#ifndef _USB_C67X00_H
> +#define _USB_C67X00_H
> +
> +#include <linux/spinlock.h>
> +#include <linux/platform_device.h>
> +#include <linux/completion.h>
> +#include <linux/mutex.h>
> +
> +/* ---------------------------------------------------------------------
> + * Cypress C67x00 register definitions
> + */
> +
> +/* Hardware Revision Register */
> +#define HW_REV_REG 0xC004
> +
> +/* General USB registers */
> +/* ===================== */
> +
> +/* USB Control Register */
> +#define USB_CTL_REG(x) ((x) ? 0xC0AA : 0xC08A)
> +
> +#define LOW_SPEED_PORT(x) ((x) ? 0x0800 : 0x0400)
> +#define HOST_MODE 0x0200
> +#define PORT_RES_EN(x) ((x) ? 0x0100 : 0x0080)
> +#define SOF_EOP_EN(x) ((x) ? 0x0002 : 0x0001)
> +
> +/* USB status register - Notice it has different content in hcd/udc mode */
> +#define USB_STAT_REG(x) ((x) ? 0xC0B0 : 0xC090)
> +
> +#define EP0_IRQ_FLG 0x0001
> +#define EP1_IRQ_FLG 0x0002
> +#define EP2_IRQ_FLG 0x0004
> +#define EP3_IRQ_FLG 0x0008
> +#define EP4_IRQ_FLG 0x0010
> +#define EP5_IRQ_FLG 0x0020
> +#define EP6_IRQ_FLG 0x0040
> +#define EP7_IRQ_FLG 0x0080
> +#define RESET_IRQ_FLG 0x0100
> +#define SOF_EOP_IRQ_FLG 0x0200
> +#define ID_IRQ_FLG 0x4000
> +#define VBUS_IRQ_FLG 0x8000
> +
> +/* USB Host only registers */
> +/* ======================= */
> +
> +/* Host n Control Register */
> +#define HOST_CTL_REG(x) ((x) ? 0xC0A0 : 0xC080)
> +
> +#define PREAMBLE_EN 0x0080 /* Preamble enable */
> +#define SEQ_SEL 0x0040 /* Data Toggle Sequence Bit Select */
> +#define ISO_EN 0x0010 /* Isochronous enable */
> +#define ARM_EN 0x0001 /* Arm operation */
> +
> +/* Host n Interrupt Enable Register */
> +#define HOST_IRQ_EN_REG(x) ((x) ? 0xC0AC : 0xC08C)
> +
> +#define SOF_EOP_IRQ_EN 0x0200 /* SOF/EOP Interrupt Enable */
> +#define SOF_EOP_TMOUT_IRQ_EN 0x0800 /* SOF/EOP Timeout Interrupt Enable */
> +#define ID_IRQ_EN 0x4000 /* ID interrupt enable */
> +#define VBUS_IRQ_EN 0x8000 /* VBUS interrupt enable */
> +#define DONE_IRQ_EN 0x0001 /* Done Interrupt Enable */
> +
> +/* USB status register */
> +#define HOST_STAT_MASK 0x02FD
> +#define PORT_CONNECT_CHANGE(x) ((x) ? 0x0020 : 0x0010)
> +#define PORT_SE0_STATUS(x) ((x) ? 0x0008 : 0x0004)
> +
> +/* Host Frame Register */
> +#define HOST_FRAME_REG(x) ((x) ? 0xC0B6 : 0xC096)
> +
> +#define HOST_FRAME_MASK 0x07FF
> +
> +/* USB Peripheral only registers */
> +/* ============================= */
> +
> +/* Device n Port Sel reg */
> +#define DEVICE_N_PORT_SEL(x) ((x) ? 0xC0A4 : 0xC084)
> +
> +/* Device n Interrupt Enable Register */
> +#define DEVICE_N_IRQ_EN_REG(x) ((x) ? 0xC0AC : 0xC08C)
> +
> +#define DEVICE_N_ENDPOINT_N_CTL_REG(dev, ep) ((dev) \
> + ? (0x0280 + (ep << 4)) \
> + : (0x0200 + (ep << 4)))
> +#define DEVICE_N_ENDPOINT_N_STAT_REG(dev, ep) ((dev) \
> + ? (0x0286 + (ep << 4)) \
> + : (0x0206 + (ep << 4)))
> +
> +#define DEVICE_N_ADDRESS(dev) ((dev) ? (0xC0AE) : (0xC08E))
> +
> +/* HPI registers */
> +/* ============= */
> +
> +/* HPI Status register */
> +#define SOFEOP_FLG(x) (1 << ((x) ? 12 : 10))
> +#define SIEMSG_FLG(x) (1 << (4 + (x)))
> +#define RESET_FLG(x) ((x) ? 0x0200 : 0x0002)
> +#define DONE_FLG(x) (1 << (2 + (x)))
> +#define RESUME_FLG(x) (1 << (6 + (x)))
> +#define MBX_OUT_FLG 0x0001 /* Message out available */
> +#define MBX_IN_FLG 0x0100
> +#define ID_FLG 0x4000
> +#define VBUS_FLG 0x8000
> +
> +/* Interrupt routing register */
> +#define HPI_IRQ_ROUTING_REG 0x0142
> +
> +#define HPI_SWAP_ENABLE(x) ((x) ? 0x0100 : 0x0001)
> +#define RESET_TO_HPI_ENABLE(x) ((x) ? 0x0200 : 0x0002)
> +#define DONE_TO_HPI_ENABLE(x) ((x) ? 0x0008 : 0x0004)
> +#define RESUME_TO_HPI_ENABLE(x) ((x) ? 0x0080 : 0x0040)
> +#define SOFEOP_TO_HPI_EN(x) ((x) ? 0x2000 : 0x0800)
> +#define SOFEOP_TO_CPU_EN(x) ((x) ? 0x1000 : 0x0400)
> +#define ID_TO_HPI_ENABLE 0x4000
> +#define VBUS_TO_HPI_ENABLE 0x8000
> +
> +/* SIE msg registers */
> +#define SIEMSG_REG(x) ((x) ? 0x0148 : 0x0144)
> +
> +#define HUSB_TDListDone 0x1000
> +
> +#define SUSB_EP0_MSG 0x0001
> +#define SUSB_EP1_MSG 0x0002
> +#define SUSB_EP2_MSG 0x0004
> +#define SUSB_EP3_MSG 0x0008
> +#define SUSB_EP4_MSG 0x0010
> +#define SUSB_EP5_MSG 0x0020
> +#define SUSB_EP6_MSG 0x0040
> +#define SUSB_EP7_MSG 0x0080
> +#define SUSB_RST_MSG 0x0100
> +#define SUSB_SOF_MSG 0x0200
> +#define SUSB_CFG_MSG 0x0400
> +#define SUSB_SUS_MSG 0x0800
> +#define SUSB_ID_MSG 0x4000
> +#define SUSB_VBUS_MSG 0x8000
> +
> +/* BIOS interrupt routines */
> +
> +#define SUSBx_RECEIVE_INT(x) ((x) ? 97 : 81)
> +#define SUSBx_SEND_INT(x) ((x) ? 96 : 80)
> +
> +#define SUSBx_DEV_DESC_VEC(x) ((x) ? 0x00D4 : 0x00B4)
> +#define SUSBx_CONF_DESC_VEC(x) ((x) ? 0x00D6 : 0x00B6)
> +#define SUSBx_STRING_DESC_VEC(x) ((x) ? 0x00D8 : 0x00B8)
> +
> +#define CY_HCD_BUF_ADDR 0x500 /* Base address for host */
> +#define SIE_TD_SIZE 0x200 /* size of the td list */
> +#define SIE_TD_BUF_SIZE 0x400 /* size of the data buffer */
> +
> +#define SIE_TD_OFFSET(host) ((host) ? (SIE_TD_SIZE+SIE_TD_BUF_SIZE) : 0)
> +#define SIE_BUF_OFFSET(host) (SIE_TD_OFFSET(host) + SIE_TD_SIZE)
> +
> +/* Base address of HCD + 2 x TD_SIZE + 2 x TD_BUF_SIZE */
> +#define CY_UDC_REQ_HEADER_BASE 0x1100
> +/* 8- byte request headers for IN/OUT transfers */
> +#define CY_UDC_REQ_HEADER_SIZE 8
> +
> +#define CY_UDC_REQ_HEADER_ADDR(ep_num) (CY_UDC_REQ_HEADER_BASE + \
> + ((ep_num) * CY_UDC_REQ_HEADER_SIZE))
> +#define CY_UDC_DESC_BASE_ADDRESS (CY_UDC_REQ_HEADER_ADDR(8))
> +
> +#define CY_UDC_BIOS_REPLACE_BASE 0x1800
> +#define CY_UDC_REQ_BUFFER_BASE 0x2000
> +#define CY_UDC_REQ_BUFFER_SIZE 0x0400
> +#define CY_UDC_REQ_BUFFER_ADDR(ep_num) (CY_UDC_REQ_BUFFER_BASE + \
> + ((ep_num) * CY_UDC_REQ_BUFFER_SIZE))
> +
> +/* ---------------------------------------------------------------------
> + * Driver data structures
> + */
> +
> +struct c67x00_device;
> +
> +/**
> + * struct c67x00_sie - Common data associated with a SIE
> + * @lock: lock to protect this struct and the associated chip registers
> + * @private_data: subdriver dependent data
> + * @irq: subdriver dependent irq handler, set NULL when not used
> + * @dev: link to common driver structure
> + * @sie_num: SIE number on chip, starting from 0
> + * @mode: SIE mode (host/peripheral/otg/not used)
> + */
> +struct c67x00_sie {
> + /* Entries to be used by the subdrivers */
> + spinlock_t lock; /* protect this structure */
> + void *private_data;
> + void (*irq) (struct c67x00_sie *sie, u16 int_status, u16 msg);
> +
> + /* Read only: */
> + struct c67x00_device *dev;
> + int sie_num;
> + int mode;
> +};
> +
> +#define sie_dev(s) (&(s)->dev->pdev->dev)
> +
> +/**
> + * struct c67x00_lcp
> + */
> +struct c67x00_lcp {
> + /* Internal use only */
> + struct mutex mutex;
> + struct completion msg_received;
> + u16 last_msg;
> +};
> +
> +/*
> + * struct c67x00_hpi
> + */
> +struct c67x00_hpi {
> + void __iomem *base;
> + int regstep;
> + spinlock_t lock;
> + struct c67x00_lcp lcp;
> +};
> +
> +#define C67X00_SIES 2
> +#define C67X00_PORTS 2
> +
> +/**
> + * struct c67x00_device - Common data associated with a c67x00 instance
> + * @hpi: hpi addresses
> + * @sie: array of sie's on this chip
> + * @pdev: platform device of instance
> + * @pdata: configuration provided by the platform
> + */
> +struct c67x00_device {
> + struct c67x00_hpi hpi;
> + struct c67x00_sie sie[C67X00_SIES];
> + struct platform_device *pdev;
> + struct c67x00_platform_data *pdata;
> +};
> +
> +/* ---------------------------------------------------------------------
> + * Low level interface functions
> + */
> +
> +/* Host Port Interface (HPI) functions */
> +u16 c67x00_ll_hpi_status(struct c67x00_device *dev);
> +void c67x00_ll_hpi_reg_init(struct c67x00_device *dev);
> +void c67x00_ll_hpi_enable_sofeop(struct c67x00_sie *sie);
> +void c67x00_ll_hpi_disable_sofeop(struct c67x00_sie *sie);
> +
> +/* General functions */
> +u16 c67x00_ll_fetch_siemsg(struct c67x00_device *dev, int sie_num);
> +u16 c67x00_ll_get_usb_ctl(struct c67x00_sie *sie);
> +void c67x00_ll_usb_clear_status(struct c67x00_sie *sie, u16 bits);
> +u16 c67x00_ll_usb_get_status(struct c67x00_sie *sie);
> +void c67x00_ll_write_mem_le16(struct c67x00_device *dev, u16 addr,
> + void *data, int len);
> +void c67x00_ll_read_mem_le16(struct c67x00_device *dev, u16 addr,
> + void *data, int len);
> +
> +/* Called by c67x00_irq to handle lcp interrupts */
> +void c67x00_ll_irq(struct c67x00_device *dev, u16 int_status);
> +
> +/* Setup and teardown */
> +void c67x00_ll_init(struct c67x00_device *dev);
> +void c67x00_ll_release(struct c67x00_device *dev);
> +int c67x00_ll_reset(struct c67x00_device *dev);
> +
> +#endif /* _USB_C67X00_H */
> Index: linux-2.6/drivers/usb/c67x00/c67x00-ll-hpi.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6/drivers/usb/c67x00/c67x00-ll-hpi.c
> @@ -0,0 +1,405 @@
> +/*
> + * c67x00-ll-hpi.c: Cypress C67X00 USB Low level interface using HPI
> + *
> + * Copyright (C) 2006-2008 Barco N.V.
> + * Derived from the Cypress cy7c67200/300 ezusb linux driver and
> + * based on multiple host controller drivers inside the linux kernel.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> + * MA 02110-1301 USA.
> + */
> +
> +#include <asm/byteorder.h>
> +#include <linux/io.h>
> +#include <linux/usb/c67x00.h>
> +#include "c67x00.h"
> +
> +#define COMM_REGS 14
> +
> +struct c67x00_lcp_int_data {
> + u16 regs[COMM_REGS];
> +};
> +
> +/* -------------------------------------------------------------------------- */
> +/* Interface definitions */
> +
> +#define COMM_ACK 0x0FED
> +#define COMM_NAK 0xDEAD
> +
> +#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
> +
> +#define SUSB_INIT_INT 0x0071
> +#define SUSB_INIT_INT_LOC (SUSB_INIT_INT * 2)
> +
> +/* -----------------------------------------------------------------------
> + * HPI implementation
> + *
> + * The c67x00 chip also support control via SPI or HSS serial
> + * interfaces. However, this driver assumes that register access can
> + * be performed from IRQ context. While this is a safe assuption with
> + * the HPI interface, it is not true for the serial interfaces.
> + */
> +
> +/* HPI registers */
> +#define HPI_DATA 0
> +#define HPI_MAILBOX 1
> +#define HPI_ADDR 2
> +#define HPI_STATUS 3
> +
> +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 u16 hpi_read_word(struct c67x00_device *dev, u16 reg)
> +{
> + u16 value;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dev->hpi.lock, flags);
> + value = hpi_read_word_nolock(dev, reg);
> + spin_unlock_irqrestore(&dev->hpi.lock, flags);
> +
> + return value;
> +}
> +
> +static 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 void hpi_write_word(struct c67x00_device *dev, u16 reg, u16 value)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dev->hpi.lock, flags);
> + hpi_write_word_nolock(dev, reg, value);
> + spin_unlock_irqrestore(&dev->hpi.lock, flags);
> +}
> +
> +/*
> + * Only data is little endian, addr has cpu endianess
> + */
> +static void hpi_write_words_le16(struct c67x00_device *dev, u16 addr,
> + u16 *data, u16 count)
> +{
> + unsigned long flags;
> + int i;
> +
> + spin_lock_irqsave(&dev->hpi.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->hpi.lock, flags);
> +}
> +
> +/*
> + * Only data is little endian, addr has cpu endianess
> + */
> +static void hpi_read_words_le16(struct c67x00_device *dev, u16 addr,
> + u16 *data, u16 count)
> +{
> + unsigned long flags;
> + int i;
> +
> + spin_lock_irqsave(&dev->hpi.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->hpi.lock, flags);
> +}
> +
> +static void hpi_set_bits(struct c67x00_device *dev, u16 reg, u16 mask)
> +{
> + u16 value;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dev->hpi.lock, flags);
> + value = hpi_read_word_nolock(dev, reg);
> + hpi_write_word_nolock(dev, reg, value | mask);
> + spin_unlock_irqrestore(&dev->hpi.lock, flags);
> +}
> +
> +static void hpi_clear_bits(struct c67x00_device *dev, u16 reg, u16 mask)
> +{
> + u16 value;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dev->hpi.lock, flags);
> + value = hpi_read_word_nolock(dev, reg);
> + hpi_write_word_nolock(dev, reg, value & ~mask);
> + spin_unlock_irqrestore(&dev->hpi.lock, flags);
> +}
> +
> +static u16 hpi_recv_mbox(struct c67x00_device *dev)
> +{
> + u16 value;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dev->hpi.lock, flags);
> + value = hpi_read_reg(dev, HPI_MAILBOX);
> + spin_unlock_irqrestore(&dev->hpi.lock, flags);
> +
> + return value;
> +}
> +
> +static u16 hpi_send_mbox(struct c67x00_device *dev, u16 value)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dev->hpi.lock, flags);
> + hpi_write_reg(dev, HPI_MAILBOX, value);
> + spin_unlock_irqrestore(&dev->hpi.lock, flags);
> +
> + return value;
> +}
> +
> +u16 c67x00_ll_hpi_status(struct c67x00_device *dev)
> +{
> + u16 value;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dev->hpi.lock, flags);
> + value = hpi_read_reg(dev, HPI_STATUS);
> + spin_unlock_irqrestore(&dev->hpi.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->hpi.lcp.msg_received, 5 * HZ);
> + WARN_ON(!res);
> +
> + return (res == 0) ? -EIO : 0;
> +}
> +
> +/* -------------------------------------------------------------------------- */
> +/* General functions */
> +
> +u16 c67x00_ll_fetch_siemsg(struct c67x00_device *dev, int sie_num)
> +{
> + u16 val;
> +
> + val = hpi_read_word(dev, SIEMSG_REG(sie_num));
> + /* clear register to allow next message */
> + hpi_write_word(dev, SIEMSG_REG(sie_num), 0);
> +
> + return val;
> +}
> +
> +u16 c67x00_ll_get_usb_ctl(struct c67x00_sie *sie)
> +{
> + return hpi_read_word(sie->dev, USB_CTL_REG(sie->sie_num));
> +}
> +
> +/**
> + * c67x00_ll_usb_clear_status - clear the USB status bits
> + */
> +void c67x00_ll_usb_clear_status(struct c67x00_sie *sie, u16 bits)
> +{
> + hpi_write_word(sie->dev, USB_STAT_REG(sie->sie_num), bits);
> +}
> +
> +u16 c67x00_ll_usb_get_status(struct c67x00_sie *sie)
> +{
> + return hpi_read_word(sie->dev, USB_STAT_REG(sie->sie_num));
> +}
> +
> +/* -------------------------------------------------------------------------- */
> +
> +static int c67x00_comm_exec_int(struct c67x00_device *dev, u16 nr,
> + struct c67x00_lcp_int_data *data)
> +{
> + int i, rc;
> +
> + mutex_lock(&dev->hpi.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->hpi.lcp.mutex);
> +
> + return rc;
> +}
> +
> +/* -------------------------------------------------------------------------- */
> +
> +void c67x00_ll_irq(struct c67x00_device *dev, u16 int_status)
> +{
> + if ((int_status & MBX_OUT_FLG) == 0)
> + return;
> +
> + dev->hpi.lcp.last_msg = hpi_recv_mbox(dev);
> + complete(&dev->hpi.lcp.msg_received);
> +}
> +
> +/* -------------------------------------------------------------------------- */
> +
> +int c67x00_ll_reset(struct c67x00_device *dev)
> +{
> + int rc;
> +
> + mutex_lock(&dev->hpi.lcp.mutex);
> + hpi_send_mbox(dev, COMM_RESET);
> + rc = ll_recv_msg(dev);
> + mutex_unlock(&dev->hpi.lcp.mutex);
> +
> + return rc;
> +}
> +
> +/* -------------------------------------------------------------------------- */
> +
> +/**
> + * c67x00_ll_write_mem_le16 - write into c67x00 memory
> + * Only data is little endian, addr has cpu endianess.
> + */
> +void c67x00_ll_write_mem_le16(struct c67x00_device *dev, u16 addr,
> + void *data, int len)
> +{
> + u8 *buf = 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) | (*buf++ << 8);
> + hpi_write_word(dev, addr - 1, tmp);
> + addr++;
> + len--;
> + }
> +
> + hpi_write_words_le16(dev, addr, (u16 *)buf, len / 2);
> + buf += len & ~0x01;
> + addr += len & ~0x01;
> + len &= 0x01;
> +
> + if (len) {
> + u16 tmp;
> + tmp = hpi_read_word(dev, addr);
> + tmp = (tmp & 0xff00) | *buf;
> + hpi_write_word(dev, addr, tmp);
> + }
> +}
> +
> +/**
> + * c67x00_ll_read_mem_le16 - read from c67x00 memory
> + * Only data is little endian, addr has cpu endianess.
> + */
> +void c67x00_ll_read_mem_le16(struct c67x00_device *dev, u16 addr,
> + void *data, int len)
> +{
> + u8 *buf = data;
> +
> + if (addr & 0x01) {
> + /* unaligned access */
> + u16 tmp;
> + tmp = hpi_read_word(dev, addr - 1);
> + *buf++ = (tmp >> 8) & 0x00ff;
> + addr++;
> + len--;
> + }
> +
> + hpi_read_words_le16(dev, addr, (u16 *)buf, len / 2);
> + buf += len & ~0x01;
> + addr += len & ~0x01;
> + len &= 0x01;
> +
> + if (len) {
> + u16 tmp;
> + tmp = hpi_read_word(dev, addr);
> + *buf = tmp & 0x00ff;
> + }
> +}
> +
> +/* -------------------------------------------------------------------------- */
> +
> +void c67x00_ll_init(struct c67x00_device *dev)
> +{
> + mutex_init(&dev->hpi.lcp.mutex);
> + init_completion(&dev->hpi.lcp.msg_received);
> +}
> +
> +void c67x00_ll_release(struct c67x00_device *dev)
> +{
> +}
>
> --
> Bye, Peter Korsgaard
>
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [patch v11 2/4] USB: add Cypress c67x00 OTG controller core driver
From: Grant Likely @ 2008-04-29 20:25 UTC (permalink / raw)
To: Peter Korsgaard; +Cc: linuxppc-dev, dbrownell, linux-usb, greg
In-Reply-To: <20080427070231.764077242@sunsite.dk>
On Sun, Apr 27, 2008 at 12:59 AM, Peter Korsgaard <jacmet@sunsite.dk> wrote:
> 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: Peter Korsgaard <jacmet@sunsite.dk>
> Acked-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
> ---
> MAINTAINERS | 6 +
> drivers/usb/c67x00/c67x00-drv.c | 230 ++++++++++++++++++++++++++++++++++++++++
> include/linux/usb/c67x00.h | 48 ++++++++
> 3 files changed, 284 insertions(+)
>
> Index: linux-2.6/drivers/usb/c67x00/c67x00-drv.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6/drivers/usb/c67x00/c67x00-drv.c
> @@ -0,0 +1,230 @@
> +/*
> + * c67x00-drv.c: Cypress C67X00 USB Common infrastructure
> + *
> + * Copyright (C) 2006-2008 Barco N.V.
> + * Derived from the Cypress cy7c67200/300 ezusb linux driver and
> + * based on multiple host controller drivers inside the linux kernel.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> + * MA 02110-1301 USA.
> + */
> +
> +/*
> + * This file implements the common infrastructure for using the c67x00.
> + * It is both the link between the platform configuration and subdrivers and
> + * the link between the common hardware parts and the subdrivers (e.g.
> + * interrupt handling).
> + *
> + * The c67x00 has 2 SIE's (serial interface engine) wich can be configured
> + * to be host, device or OTG (with some limitations, E.G. only SIE1 can be OTG).
> + *
> + * Depending on the platform configuration, the SIE's are created and
> + * the corresponding subdriver is initialized (c67x00_probe_sie).
> + */
> +
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/list.h>
> +#include <linux/usb.h>
> +#include <linux/usb/c67x00.h>
> +
> +#include "c67x00.h"
> +
> +static void c67x00_probe_sie(struct c67x00_sie *sie,
> + struct c67x00_device *dev, int sie_num)
> +{
> + spin_lock_init(&sie->lock);
> + sie->dev = dev;
> + sie->sie_num = sie_num;
> + sie->mode = c67x00_sie_config(dev->pdata->sie_config, sie_num);
> +
> + switch (sie->mode) {
> + case C67X00_SIE_UNUSED:
> + dev_info(sie_dev(sie),
> + "Not using SIE %d as requested\n", sie->sie_num);
> + break;
> +
> + default:
> + dev_err(sie_dev(sie),
> + "Unsupported configuration: 0x%x for SIE %d\n",
> + sie->mode, sie->sie_num);
> + break;
> + }
> +}
> +
> +static void c67x00_remove_sie(struct c67x00_sie *sie)
> +{
> +}
> +
> +static irqreturn_t c67x00_irq(int irq, void *__dev)
> +{
> + struct c67x00_device *c67x00 = __dev;
> + struct c67x00_sie *sie;
> + u16 msg, int_status;
> + int i, count = 8;
> +
> + int_status = c67x00_ll_hpi_status(c67x00);
> + if (!int_status)
> + return IRQ_NONE;
> +
> + while (int_status != 0 && (count-- >= 0)) {
> + c67x00_ll_irq(c67x00, int_status);
> + for (i = 0; i < C67X00_SIES; i++) {
> + sie = &c67x00->sie[i];
> + msg = 0;
> + if (int_status & SIEMSG_FLG(i))
> + msg = c67x00_ll_fetch_siemsg(c67x00, i);
> + if (sie->irq)
> + sie->irq(sie, int_status, msg);
> + }
> + int_status = c67x00_ll_hpi_status(c67x00);
> + }
> +
> + if (int_status)
> + dev_warn(&c67x00->pdev->dev, "Not all interrupts handled! "
> + "status = 0x%04x\n", int_status);
> +
> + return IRQ_HANDLED;
> +}
> +
> +/* ------------------------------------------------------------------------- */
> +
> +static int __devinit c67x00_drv_probe(struct platform_device *pdev)
> +{
> + struct c67x00_device *c67x00;
> + struct c67x00_platform_data *pdata;
> + struct resource *res, *res2;
> + int ret, i;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res)
> + return -ENODEV;
> +
> + res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + if (!res2)
> + return -ENODEV;
> +
> + pdata = pdev->dev.platform_data;
> + if (!pdata)
> + return -ENODEV;
> +
> + c67x00 = kzalloc(sizeof(*c67x00), GFP_KERNEL);
> + if (!c67x00)
> + return -ENOMEM;
> +
> + if (!request_mem_region(res->start, res->end - res->start + 1,
> + pdev->name)) {
> + dev_err(&pdev->dev, "Memory region busy\n");
> + ret = -EBUSY;
> + goto request_mem_failed;
> + }
> + c67x00->hpi.base = ioremap(res->start, res->end - res->start + 1);
> + if (!c67x00->hpi.base) {
> + dev_err(&pdev->dev, "Unable to map HPI registers\n");
> + ret = -EIO;
> + goto map_failed;
> + }
> +
> + spin_lock_init(&c67x00->hpi.lock);
> + c67x00->hpi.regstep = pdata->hpi_regstep;
> + c67x00->pdata = pdev->dev.platform_data;
> + c67x00->pdev = pdev;
> +
> + c67x00_ll_init(c67x00);
> + c67x00_ll_hpi_reg_init(c67x00);
> +
> + ret = request_irq(res2->start, c67x00_irq, 0, pdev->name, c67x00);
> + if (ret) {
> + dev_err(&pdev->dev, "Cannot claim IRQ\n");
> + goto request_irq_failed;
> + }
> +
> + ret = c67x00_ll_reset(c67x00);
> + if (ret) {
> + dev_err(&pdev->dev, "Device reset failed\n");
> + goto reset_failed;
> + }
> +
> + for (i = 0; i < C67X00_SIES; i++)
> + c67x00_probe_sie(&c67x00->sie[i], c67x00, i);
> +
> + platform_set_drvdata(pdev, c67x00);
> +
> + return 0;
> +
> + reset_failed:
> + free_irq(res2->start, c67x00);
> + request_irq_failed:
> + iounmap(c67x00->hpi.base);
> + map_failed:
> + release_mem_region(res->start, res->end - res->start + 1);
> + request_mem_failed:
> + kfree(c67x00);
> +
> + return ret;
> +}
> +
> +static int __devexit c67x00_drv_remove(struct platform_device *pdev)
> +{
> + struct c67x00_device *c67x00 = platform_get_drvdata(pdev);
> + struct resource *res;
> + int i;
> +
> + for (i = 0; i < C67X00_SIES; i++)
> + c67x00_remove_sie(&c67x00->sie[i]);
> +
> + c67x00_ll_release(c67x00);
> +
> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + if (res)
> + free_irq(res->start, c67x00);
> +
> + iounmap(c67x00->hpi.base);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (res)
> + release_mem_region(res->start, res->end - res->start + 1);
> +
> + kfree(c67x00);
> +
> + return 0;
> +}
> +
> +static struct platform_driver c67x00_driver = {
> + .probe = c67x00_drv_probe,
> + .remove = __devexit_p(c67x00_drv_remove),
> + .driver = {
> + .owner = THIS_MODULE,
> + .name = "c67x00",
> + },
> +};
> +MODULE_ALIAS("platform:c67x00");
> +
> +static int __init c67x00_init(void)
> +{
> + return platform_driver_register(&c67x00_driver);
> +}
> +
> +static void __exit c67x00_exit(void)
> +{
> + platform_driver_unregister(&c67x00_driver);
> +}
> +
> +module_init(c67x00_init);
> +module_exit(c67x00_exit);
> +
> +MODULE_AUTHOR("Peter Korsgaard, Jan Veldeman, Grant Likely");
> +MODULE_DESCRIPTION("Cypress C67X00 USB Controller Driver");
> +MODULE_LICENSE("GPL");
> Index: linux-2.6/include/linux/usb/c67x00.h
> ===================================================================
> --- /dev/null
> +++ linux-2.6/include/linux/usb/c67x00.h
> @@ -0,0 +1,48 @@
> +/*
> + * usb_c67x00.h: platform definitions for the Cypress C67X00 USB chip
> + *
> + * Copyright (C) 2006-2008 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_A 2 /* peripheral on A port */
> +#define C67X00_SIE_PERIPHERAL_B 3 /* peripheral on B port */
> +
> +#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_A (C67X00_SIE_PERIPHERAL_A << 0)
> +#define C67X00_SIE1_PERIPHERAL_B (C67X00_SIE_PERIPHERAL_B << 0)
> +
> +#define C67X00_SIE2_UNUSED (C67X00_SIE_UNUSED << 4)
> +#define C67X00_SIE2_HOST (C67X00_SIE_HOST << 4)
> +#define C67X00_SIE2_PERIPHERAL_A (C67X00_SIE_PERIPHERAL_A << 4)
> +#define C67X00_SIE2_PERIPHERAL_B (C67X00_SIE_PERIPHERAL_B << 4)
> +
> +struct c67x00_platform_data {
> + int sie_config; /* SIEs config (C67X00_SIEx_*) */
> + unsigned long hpi_regstep; /* Step between HPI registers */
> +};
> +
> +#endif /* _LINUX_USB_C67X00_H */
> Index: linux-2.6/MAINTAINERS
> ===================================================================
> --- linux-2.6.orig/MAINTAINERS
> +++ linux-2.6/MAINTAINERS
> @@ -4010,6 +4010,12 @@
> S: Maintained
> W: http://www.kroah.com/linux-usb/
>
> +USB CYPRESS C67X00 DRIVER
> +P: Peter Korsgaard
> +M: jacmet@sunsite.dk
> +L: linux-usb@vger.kernel.org
> +S: Maintained
> +
> USB DAVICOM DM9601 DRIVER
> P: Peter Korsgaard
> M: jacmet@sunsite.dk
>
> --
> Bye, Peter Korsgaard
>
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [patch v11 3/4] USB: add Cypress c67x00 OTG controller HCD driver
From: Grant Likely @ 2008-04-29 20:26 UTC (permalink / raw)
To: Peter Korsgaard; +Cc: linuxppc-dev, dbrownell, linux-usb, greg
In-Reply-To: <20080427070231.906264382@sunsite.dk>
On Sun, Apr 27, 2008 at 12:59 AM, Peter Korsgaard <jacmet@sunsite.dk> wrote:
> This patch adds HCD support for the Cypress c67x00 family of devices.
>
> Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
> ---
> drivers/usb/Makefile | 2
> drivers/usb/c67x00/Makefile | 9
> drivers/usb/c67x00/c67x00-drv.c | 13
> drivers/usb/c67x00/c67x00-hcd.c | 412 +++++++++++++
> drivers/usb/c67x00/c67x00-hcd.h | 133 ++++
> drivers/usb/c67x00/c67x00-ll-hpi.c | 75 ++
> drivers/usb/c67x00/c67x00-sched.c | 1170 +++++++++++++++++++++++++++++++++++++
> drivers/usb/c67x00/c67x00.h | 9
> drivers/usb/host/Kconfig | 13
> 9 files changed, 1836 insertions(+)
>
> Index: linux-2.6/drivers/usb/c67x00/c67x00-hcd.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6/drivers/usb/c67x00/c67x00-hcd.c
> @@ -0,0 +1,412 @@
> +/*
> + * c67x00-hcd.c: Cypress C67X00 USB Host Controller Driver
> + *
> + * Copyright (C) 2006-2008 Barco N.V.
> + * Derived from the Cypress cy7c67200/300 ezusb linux driver and
> + * based on multiple host controller drivers inside the linux kernel.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> + * MA 02110-1301 USA.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/usb.h>
> +
> +#include "c67x00.h"
> +#include "c67x00-hcd.h"
> +
> +/* --------------------------------------------------------------------------
> + * Root Hub Support
> + */
> +
> +static __u8 c67x00_hub_des[] = {
> + 0x09, /* __u8 bLength; */
> + 0x29, /* __u8 bDescriptorType; Hub-descriptor */
> + 0x02, /* __u8 bNbrPorts; */
> + 0x00, /* __u16 wHubCharacteristics; */
> + 0x00, /* (per-port OC, no power switching) */
> + 0x32, /* __u8 bPwrOn2pwrGood; 2ms */
> + 0x00, /* __u8 bHubContrCurrent; 0 mA */
> + 0x00, /* __u8 DeviceRemovable; ** 7 Ports max ** */
> + 0xff, /* __u8 PortPwrCtrlMask; ** 7 ports max ** */
> +};
> +
> +static void c67x00_hub_reset_host_port(struct c67x00_sie *sie, int port)
> +{
> + struct c67x00_hcd *c67x00 = sie->private_data;
> + unsigned long flags;
> +
> + c67x00_ll_husb_reset(sie, port);
> +
> + spin_lock_irqsave(&c67x00->lock, flags);
> + c67x00_ll_husb_reset_port(sie, port);
> + spin_unlock_irqrestore(&c67x00->lock, flags);
> +
> + c67x00_ll_set_husb_eot(sie->dev, DEFAULT_EOT);
> +}
> +
> +static int c67x00_hub_status_data(struct usb_hcd *hcd, char *buf)
> +{
> + struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
> + struct c67x00_sie *sie = c67x00->sie;
> + u16 status;
> + int i;
> +
> + *buf = 0;
> + status = c67x00_ll_usb_get_status(sie);
> + for (i = 0; i < C67X00_PORTS; i++)
> + if (status & PORT_CONNECT_CHANGE(i))
> + *buf |= (1 << i);
> +
> + /* bit 0 denotes hub change, b1..n port change */
> + *buf <<= 1;
> +
> + return !!*buf;
> +}
> +
> +static int c67x00_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
> + u16 wIndex, char *buf, u16 wLength)
> +{
> + struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
> + struct c67x00_sie *sie = c67x00->sie;
> + u16 status, usb_status;
> + int len = 0;
> + unsigned int port = wIndex-1;
> + u16 wPortChange, wPortStatus;
> +
> + switch (typeReq) {
> +
> + case GetHubStatus:
> + *(__le32 *) buf = cpu_to_le32(0);
> + len = 4; /* hub power */
> + break;
> +
> + case GetPortStatus:
> + if (wIndex > C67X00_PORTS)
> + return -EPIPE;
> +
> + status = c67x00_ll_usb_get_status(sie);
> + usb_status = c67x00_ll_get_usb_ctl(sie);
> +
> + wPortChange = 0;
> + if (status & PORT_CONNECT_CHANGE(port))
> + wPortChange |= USB_PORT_STAT_C_CONNECTION;
> +
> + wPortStatus = USB_PORT_STAT_POWER;
> + if (!(status & PORT_SE0_STATUS(port)))
> + wPortStatus |= USB_PORT_STAT_CONNECTION;
> + if (usb_status & LOW_SPEED_PORT(port)) {
> + wPortStatus |= USB_PORT_STAT_LOW_SPEED;
> + c67x00->low_speed_ports |= (1 << port);
> + } else
> + c67x00->low_speed_ports &= ~(1 << port);
> +
> + if (usb_status & SOF_EOP_EN(port))
> + wPortStatus |= USB_PORT_STAT_ENABLE;
> +
> + *(__le16 *) buf = cpu_to_le16(wPortStatus);
> + *(__le16 *) (buf + 2) = cpu_to_le16(wPortChange);
> + len = 4;
> + break;
> +
> + case SetHubFeature: /* We don't implement these */
> + case ClearHubFeature:
> + switch (wValue) {
> + case C_HUB_OVER_CURRENT:
> + case C_HUB_LOCAL_POWER:
> + len = 0;
> + break;
> +
> + default:
> + return -EPIPE;
> + }
> + break;
> +
> + case SetPortFeature:
> + if (wIndex > C67X00_PORTS)
> + return -EPIPE;
> +
> + switch (wValue) {
> + case USB_PORT_FEAT_SUSPEND:
> + dev_dbg(c67x00_hcd_dev(c67x00),
> + "SetPortFeature %d (SUSPEND)\n", port);
> + len = 0;
> + break;
> +
> + case USB_PORT_FEAT_RESET:
> + c67x00_hub_reset_host_port(sie, port);
> + len = 0;
> + break;
> +
> + case USB_PORT_FEAT_POWER:
> + /* Power always enabled */
> + len = 0;
> + break;
> +
> + default:
> + dev_dbg(c67x00_hcd_dev(c67x00),
> + "%s: SetPortFeature %d (0x%04x) Error!\n",
> + __func__, port, wValue);
> + return -EPIPE;
> + }
> + break;
> +
> + case ClearPortFeature:
> + if (wIndex > C67X00_PORTS)
> + return -EPIPE;
> +
> + switch (wValue) {
> + case USB_PORT_FEAT_ENABLE:
> + /* Reset the port so that the c67x00 also notices the
> + * disconnect */
> + c67x00_hub_reset_host_port(sie, port);
> + len = 0;
> + break;
> +
> + case USB_PORT_FEAT_C_ENABLE:
> + dev_dbg(c67x00_hcd_dev(c67x00),
> + "ClearPortFeature (%d): C_ENABLE\n", port);
> + len = 0;
> + break;
> +
> + case USB_PORT_FEAT_SUSPEND:
> + dev_dbg(c67x00_hcd_dev(c67x00),
> + "ClearPortFeature (%d): SUSPEND\n", port);
> + len = 0;
> + break;
> +
> + case USB_PORT_FEAT_C_SUSPEND:
> + dev_dbg(c67x00_hcd_dev(c67x00),
> + "ClearPortFeature (%d): C_SUSPEND\n", port);
> + len = 0;
> + break;
> +
> + case USB_PORT_FEAT_POWER:
> + dev_dbg(c67x00_hcd_dev(c67x00),
> + "ClearPortFeature (%d): POWER\n", port);
> + return -EPIPE;
> +
> + case USB_PORT_FEAT_C_CONNECTION:
> + c67x00_ll_usb_clear_status(sie,
> + PORT_CONNECT_CHANGE(port));
> + len = 0;
> + break;
> +
> + case USB_PORT_FEAT_C_OVER_CURRENT:
> + dev_dbg(c67x00_hcd_dev(c67x00),
> + "ClearPortFeature (%d): OVER_CURRENT\n", port);
> + len = 0;
> + break;
> +
> + case USB_PORT_FEAT_C_RESET:
> + dev_dbg(c67x00_hcd_dev(c67x00),
> + "ClearPortFeature (%d): C_RESET\n", port);
> + len = 0;
> + break;
> +
> + default:
> + dev_dbg(c67x00_hcd_dev(c67x00),
> + "%s: ClearPortFeature %d (0x%04x) Error!\n",
> + __func__, port, wValue);
> + return -EPIPE;
> + }
> + break;
> +
> + case GetHubDescriptor:
> + len = min_t(unsigned int, sizeof(c67x00_hub_des), wLength);
> + memcpy(buf, c67x00_hub_des, len);
> + break;
> +
> + default:
> + dev_dbg(c67x00_hcd_dev(c67x00), "%s: unknown\n", __func__);
> + return -EPIPE;
> + }
> +
> + return 0;
> +}
> +
> +/* ---------------------------------------------------------------------
> + * Main part of host controller driver
> + */
> +
> +/**
> + * c67x00_hcd_irq
> + *
> + * This function is called from the interrupt handler in c67x00-drv.c
> + */
> +static void c67x00_hcd_irq(struct c67x00_sie *sie, u16 int_status, u16 msg)
> +{
> + struct c67x00_hcd *c67x00 = sie->private_data;
> + struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
> +
> + /* Handle sie message flags */
> + if (msg) {
> + if (msg & HUSB_TDListDone)
> + c67x00_sched_kick(c67x00);
> + else
> + dev_warn(c67x00_hcd_dev(c67x00),
> + "Unknown SIE msg flag(s): 0x%04x\n", msg);
> + }
> +
> + if (unlikely(hcd->state == HC_STATE_HALT))
> + return;
> +
> + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
> + return;
> +
> + /* Handle Start of frame events */
> + if (int_status & SOFEOP_FLG(sie->sie_num)) {
> + c67x00_ll_usb_clear_status(sie, SOF_EOP_IRQ_FLG);
> + c67x00_sched_kick(c67x00);
> + set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
> + }
> +}
> +
> +/**
> + * c67x00_hcd_start: Host controller start hook
> + */
> +static int c67x00_hcd_start(struct usb_hcd *hcd)
> +{
> + hcd->uses_new_polling = 1;
> + hcd->state = HC_STATE_RUNNING;
> + hcd->poll_rh = 1;
> +
> + return 0;
> +}
> +
> +/**
> + * c67x00_hcd_stop: Host controller stop hook
> + */
> +static void c67x00_hcd_stop(struct usb_hcd *hcd)
> +{
> + /* Nothing to do */
> +}
> +
> +static int c67x00_hcd_get_frame(struct usb_hcd *hcd)
> +{
> + struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
> + u16 temp_val;
> +
> + dev_dbg(c67x00_hcd_dev(c67x00), "%s\n", __func__);
> + temp_val = c67x00_ll_husb_get_frame(c67x00->sie);
> + temp_val &= HOST_FRAME_MASK;
> + return temp_val ? (temp_val - 1) : HOST_FRAME_MASK;
> +}
> +
> +static struct hc_driver c67x00_hc_driver = {
> + .description = "c67x00-hcd",
> + .product_desc = "Cypress C67X00 Host Controller",
> + .hcd_priv_size = sizeof(struct c67x00_hcd),
> + .flags = HCD_USB11 | HCD_MEMORY,
> +
> + /*
> + * basic lifecycle operations
> + */
> + .start = c67x00_hcd_start,
> + .stop = c67x00_hcd_stop,
> +
> + /*
> + * managing i/o requests and associated device resources
> + */
> + .urb_enqueue = c67x00_urb_enqueue,
> + .urb_dequeue = c67x00_urb_dequeue,
> + .endpoint_disable = c67x00_endpoint_disable,
> +
> + /*
> + * scheduling support
> + */
> + .get_frame_number = c67x00_hcd_get_frame,
> +
> + /*
> + * root hub support
> + */
> + .hub_status_data = c67x00_hub_status_data,
> + .hub_control = c67x00_hub_control,
> +};
> +
> +/* ---------------------------------------------------------------------
> + * Setup/Teardown routines
> + */
> +
> +int c67x00_hcd_probe(struct c67x00_sie *sie)
> +{
> + struct c67x00_hcd *c67x00;
> + struct usb_hcd *hcd;
> + unsigned long flags;
> + int retval;
> +
> + if (usb_disabled())
> + return -ENODEV;
> +
> + 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);
> + c67x00->td_base_addr = CY_HCD_BUF_ADDR + SIE_TD_OFFSET(sie->sie_num);
> + c67x00->buf_base_addr = CY_HCD_BUF_ADDR + SIE_BUF_OFFSET(sie->sie_num);
> + c67x00->max_frame_bw = MAX_FRAME_BW_STD;
> +
> + c67x00_ll_husb_init_host_port(sie);
> +
> + init_completion(&c67x00->endpoint_disable);
> + retval = c67x00_sched_start_scheduler(c67x00);
> + if (retval)
> + goto err1;
> +
> + retval = usb_add_hcd(hcd, 0, 0);
> + if (retval) {
> + dev_dbg(sie_dev(sie), "%s: usb_add_hcd returned %d\n",
> + __func__, retval);
> + goto err2;
> + }
> +
> + spin_lock_irqsave(&sie->lock, flags);
> + sie->private_data = c67x00;
> + sie->irq = c67x00_hcd_irq;
> + spin_unlock_irqrestore(&sie->lock, flags);
> +
> + return retval;
> +
> + err2:
> + c67x00_sched_stop_scheduler(c67x00);
> + err1:
> + usb_put_hcd(hcd);
> + err0:
> + return retval;
> +}
> +
> +/* may be called with controller, bus, and devices active */
> +void c67x00_hcd_remove(struct c67x00_sie *sie)
> +{
> + struct c67x00_hcd *c67x00 = sie->private_data;
> + struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
> +
> + c67x00_sched_stop_scheduler(c67x00);
> + usb_remove_hcd(hcd);
> + usb_put_hcd(hcd);
> +}
> Index: linux-2.6/drivers/usb/c67x00/c67x00-hcd.h
> ===================================================================
> --- /dev/null
> +++ linux-2.6/drivers/usb/c67x00/c67x00-hcd.h
> @@ -0,0 +1,133 @@
> +/*
> + * c67x00-hcd.h: Cypress C67X00 USB HCD
> + *
> + * Copyright (C) 2006-2008 Barco N.V.
> + * Derived from the Cypress cy7c67200/300 ezusb linux driver and
> + * based on multiple host controller drivers inside the linux kernel.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> + * MA 02110-1301 USA.
> + */
> +
> +#ifndef _USB_C67X00_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;
> + 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;
> + 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 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);
> +}
> +
> +/* ---------------------------------------------------------------------
> + * Functions used by c67x00-drv
> + */
> +
> +int c67x00_hcd_probe(struct c67x00_sie *sie);
> +void c67x00_hcd_remove(struct c67x00_sie *sie);
> +
> +/* ---------------------------------------------------------------------
> + * 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);
> +
> +#define c67x00_hcd_dev(x) (c67x00_hcd_to_hcd(x)->self.controller)
> +
> +#endif /* _USB_C67X00_HCD_H */
> Index: linux-2.6/drivers/usb/c67x00/c67x00-sched.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6/drivers/usb/c67x00/c67x00-sched.c
> @@ -0,0 +1,1170 @@
> +/*
> + * c67x00-sched.c: Cypress C67X00 USB Host Controller Driver - TD scheduling
> + *
> + * Copyright (C) 2006-2008 Barco N.V.
> + * Derived from the Cypress cy7c67200/300 ezusb linux driver and
> + * based on multiple host controller drivers inside the linux kernel.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> + * MA 02110-1301 USA.
> + */
> +
> +#include <linux/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;
> + void *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
> +
> +/**
> + * 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_hcd_dev(c67x00);
> +
> + 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:");
> + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1,
> + td->data, td_length(td), 1);
> +}
> +#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)
> +{
> + return c67x00_ll_husb_get_frame(c67x00->sie) & HOST_FRAME_MASK;
> +}
> +
> +/**
> + * 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 each urbp
> + */
> + list_for_each_entry(td, &c67x00->td_list, td_list)
> + if (urb == td->urb)
> + td->urb = NULL;
> +
> + 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_host_endpoint *hep = urb->ep;
> + struct c67x00_ep_data *ep_data;
> + 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 {
> + struct c67x00_ep_data *prev;
> +
> + 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_hcd_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;
> + 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;
> +
> +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);
> + usb_hcd_unlink_urb_from_ep(hcd, urb);
> +
> + spin_unlock(&c67x00->lock);
> + usb_hcd_giveback_urb(hcd, urb, status);
> + spin_lock(&c67x00->lock);
> +
> + spin_unlock_irqrestore(&c67x00->lock, flags);
> +
> + return 0;
> +
> + done:
> + spin_unlock_irqrestore(&c67x00->lock, flags);
> + return rc;
> +}
> +
> +/* -------------------------------------------------------------------------- */
> +
> +/*
> + * pre: c67x00 locked, urb unlocked
> + */
> +static 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);
> +
> + c67x00_release_urb(c67x00, urb);
> + usb_hcd_unlink_urb_from_ep(c67x00_hcd_to_hcd(c67x00), urb);
> + spin_unlock(&c67x00->lock);
> + usb_hcd_giveback_urb(c67x00_hcd_to_hcd(c67x00), urb, urbp->status);
> + spin_lock(&c67x00->lock);
> +}
> +
> +/* -------------------------------------------------------------------------- */
> +
> +static int c67x00_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 c67x00_create_td(struct c67x00_hcd *c67x00, struct urb *urb,
> + void *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 (c67x00_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 c67x00_release_td(struct c67x00_td *td)
> +{
> + list_del_init(&td->td_list);
> + kfree(td);
> +}
> +
> +/* -------------------------------------------------------------------------- */
> +
> +static int c67x00_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 = c67x00_create_td(c67x00, urb, td_buf, len, pid, toggle,
> + DATA_STAGE);
> + if (ret)
> + return ret; /* td wasn't created */
> +
> + toggle ^= 1;
> + remaining -= len;
> + if (usb_pipecontrol(urb->pipe))
> + break;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * return 0 in case more bandwidth is available, else errorcode
> + */
> +static int c67x00_add_ctrl_urb(struct c67x00_hcd *c67x00, struct urb *urb)
> +{
> + int ret;
> + int pid;
> +
> + switch (urb->interval) {
> + default:
> + case SETUP_STAGE:
> + ret = c67x00_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 = c67x00_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 = c67x00_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 c67x00_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 c67x00_add_data_urb(c67x00, urb);
> + }
> + return 0;
> +}
> +
> +static int c67x00_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 = c67x00_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_hcd_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");
> + c67x00_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, c67x00_add_iso_urb);
> + c67x00_fill_from_list(c67x00, PIPE_INTERRUPT, c67x00_add_int_urb);
> + c67x00_fill_from_list(c67x00, PIPE_CONTROL, c67x00_add_ctrl_urb);
> + c67x00_fill_from_list(c67x00, PIPE_BULK, c67x00_add_data_urb);
> +}
> +
> +/* -------------------------------------------------------------------------- */
> +
> +/**
> + * Get TD from C67X00
> + */
> +static inline void
> +c67x00_parse_td(struct c67x00_hcd *c67x00, struct c67x00_td *td)
> +{
> + c67x00_ll_read_mem_le16(c67x00->sie->dev,
> + td->td_addr, td, CY_TD_SIZE);
> +
> + if (usb_pipein(td->pipe) && td_actual_bytes(td))
> + c67x00_ll_read_mem_le16(c67x00->sie->dev, td_ly_base_addr(td),
> + td->data, td_actual_bytes(td));
> +}
> +
> +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) {
> + c67x00_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);
> + c67x00_release_td(td);
> + }
> +}
> +
> +/* -------------------------------------------------------------------------- */
> +
> +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_write_mem_le16(c67x00->sie->dev, td_ly_base_addr(td),
> + td->data, len);
> +
> + c67x00_ll_write_mem_le16(c67x00->sie->dev,
> + td->td_addr, td, CY_TD_SIZE);
> +}
> +
> +static void c67x00_send_frame(struct c67x00_hcd *c67x00)
> +{
> + struct c67x00_td *td;
> +
> + if (list_empty(&c67x00->td_list))
> + dev_warn(c67x00_hcd_dev(c67x00),
> + "%s: td list should not be empty here!\n",
> + __func__);
> +
> + 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))
> + /* TD's have been added to the frame */
> + c67x00_send_frame(c67x00);
> +
> + 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);
> +}
> +
> +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);
> + return 0;
> +}
> +
> +void c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00)
> +{
> + tasklet_kill(&c67x00->tasklet);
> +}
> Index: linux-2.6/drivers/usb/c67x00/Makefile
> ===================================================================
> --- /dev/null
> +++ linux-2.6/drivers/usb/c67x00/Makefile
> @@ -0,0 +1,9 @@
> +#
> +# Makefile for Cypress C67X00 USB Controller
> +#
> +
> +ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG
> +
> +obj-$(CONFIG_USB_C67X00_HCD) += c67x00.o
> +
> +c67x00-objs := c67x00-drv.o c67x00-ll-hpi.o c67x00-hcd.o c67x00-sched.o
> Index: linux-2.6/drivers/usb/Makefile
> ===================================================================
> --- linux-2.6.orig/drivers/usb/Makefile
> +++ linux-2.6/drivers/usb/Makefile
> @@ -17,6 +17,8 @@
> obj-$(CONFIG_USB_U132_HCD) += host/
> obj-$(CONFIG_USB_R8A66597_HCD) += host/
>
> +obj-$(CONFIG_USB_C67X00_HCD) += c67x00/
> +
> obj-$(CONFIG_USB_ACM) += class/
> obj-$(CONFIG_USB_PRINTER) += class/
>
> Index: linux-2.6/drivers/usb/c67x00/c67x00-drv.c
> ===================================================================
> --- linux-2.6.orig/drivers/usb/c67x00/c67x00-drv.c
> +++ linux-2.6/drivers/usb/c67x00/c67x00-drv.c
> @@ -41,6 +41,7 @@
> #include <linux/usb/c67x00.h>
>
> #include "c67x00.h"
> +#include "c67x00-hcd.h"
>
> static void c67x00_probe_sie(struct c67x00_sie *sie,
> struct c67x00_device *dev, int sie_num)
> @@ -51,6 +52,10 @@
> sie->mode = c67x00_sie_config(dev->pdata->sie_config, sie_num);
>
> switch (sie->mode) {
> + case C67X00_SIE_HOST:
> + c67x00_hcd_probe(sie);
> + break;
> +
> case C67X00_SIE_UNUSED:
> dev_info(sie_dev(sie),
> "Not using SIE %d as requested\n", sie->sie_num);
> @@ -66,6 +71,14 @@
>
> static void c67x00_remove_sie(struct c67x00_sie *sie)
> {
> + switch (sie->mode) {
> + case C67X00_SIE_HOST:
> + c67x00_hcd_remove(sie);
> + break;
> +
> + default:
> + break;
> + }
> }
>
> static irqreturn_t c67x00_irq(int irq, void *__dev)
> Index: linux-2.6/drivers/usb/c67x00/c67x00.h
> ===================================================================
> --- linux-2.6.orig/drivers/usb/c67x00/c67x00.h
> +++ linux-2.6/drivers/usb/c67x00/c67x00.h
> @@ -274,6 +274,15 @@
> void c67x00_ll_read_mem_le16(struct c67x00_device *dev, u16 addr,
> void *data, int len);
>
> +/* Host specific functions */
> +void c67x00_ll_set_husb_eot(struct c67x00_device *dev, u16 value);
> +void c67x00_ll_husb_reset(struct c67x00_sie *sie, int port);
> +void c67x00_ll_husb_set_current_td(struct c67x00_sie *sie, u16 addr);
> +u16 c67x00_ll_husb_get_current_td(struct c67x00_sie *sie);
> +u16 c67x00_ll_husb_get_frame(struct c67x00_sie *sie);
> +void c67x00_ll_husb_init_host_port(struct c67x00_sie *sie);
> +void c67x00_ll_husb_reset_port(struct c67x00_sie *sie, int port);
> +
> /* Called by c67x00_irq to handle lcp interrupts */
> void c67x00_ll_irq(struct c67x00_device *dev, u16 int_status);
>
> Index: linux-2.6/drivers/usb/c67x00/c67x00-ll-hpi.c
> ===================================================================
> --- linux-2.6.orig/drivers/usb/c67x00/c67x00-ll-hpi.c
> +++ linux-2.6/drivers/usb/c67x00/c67x00-ll-hpi.c
> @@ -297,6 +297,81 @@
> }
>
> /* -------------------------------------------------------------------------- */
> +/* Host specific functions */
> +
> +void c67x00_ll_set_husb_eot(struct c67x00_device *dev, u16 value)
> +{
> + mutex_lock(&dev->hpi.lcp.mutex);
> + hpi_write_word(dev, HUSB_pEOT, value);
> + mutex_unlock(&dev->hpi.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));
> +}
> +
> +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_usb_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_usb_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_irq(struct c67x00_device *dev, u16 int_status)
> {
> Index: linux-2.6/drivers/usb/host/Kconfig
> ===================================================================
> --- linux-2.6.orig/drivers/usb/host/Kconfig
> +++ linux-2.6/drivers/usb/host/Kconfig
> @@ -4,6 +4,19 @@
> comment "USB Host Controller Drivers"
> depends on USB
>
> +config USB_C67X00_HCD
> + tristate "Cypress C67x00 HCD support"
> + depends on USB
> + help
> + The Cypress C67x00 (EZ-Host/EZ-OTG) chips are dual-role
> + host/peripheral/OTG USB controllers.
> +
> + Enable this option to support this chip in host controller mode.
> + If unsure, say N.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called c67x00.
> +
> config USB_EHCI_HCD
> tristate "EHCI HCD (USB 2.0) support"
> depends on USB && USB_ARCH_HAS_EHCI
>
> --
> Bye, Peter Korsgaard
>
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* [PATCH v3] sysdev,mv64x60: MV64x60 device bus
From: Remi Machet @ 2008-04-29 20:26 UTC (permalink / raw)
To: Paul Mackerras, Dale Farnsworth, Stephen Rothwell; +Cc: linuxppc-dev
Follow up of my email of 4/16/2008 titled "MV64x60 device bus".
For each mv64360 entry in the OpenFirmware database, add the
registration of an of_bus to take care of devices connected to
the MV64x60 asynchronous devices controller.
Signed-off-by: Remi Machet (rmachet@slac.stanford.edu)
---
Use the compatible field to detect devices to register (on Dale Farnsworth
suggestion).
Added __initdata to variable of_mv64x60_devices (thanks to Stephen Rothwell
for pointing that out).
arch/powerpc/sysdev/mv64x60_dev.c | 10 ++++++++++
1 files changed, 10 insertions(+)
diff --git a/arch/powerpc/sysdev/mv64x60_dev.c b/arch/powerpc/sysdev/mv64x60_dev.c
index 41af122..c38695e 100644
--- a/arch/powerpc/sysdev/mv64x60_dev.c
+++ b/arch/powerpc/sysdev/mv64x60_dev.c
@@ -15,6 +15,7 @@
#include <linux/console.h>
#include <linux/mv643xx.h>
#include <linux/platform_device.h>
+#include <linux/of_platform.h>
#include <asm/prom.h>
@@ -25,6 +26,11 @@
* PowerPC of_platform_bus_type. They support platform_bus_type instead.
*/
+static struct of_device_id __initdata of_mv64x60_devices[] = {
+ { .compatible = "marvell,mv64306-devctrl", },
+ {}
+};
+
/*
* Create MPSC platform devices
*/
@@ -482,6 +488,10 @@ static int __init mv64x60_device_setup(void)
of_node_put(np);
}
+ /* Now add every node that is on the device bus (type is devicectrl */
+ for_each_compatible_node(np, NULL, "marvell,mv64360")
+ of_platform_bus_probe(np, of_mv64x60_devices, NULL);
+
return 0;
}
arch_initcall(mv64x60_device_setup);
^ permalink raw reply related
* Re: [PATCH 4/6] [POWERPC] QE: implement support for the GPIO LIB API
From: Timur Tabi @ 2008-04-29 20:29 UTC (permalink / raw)
To: Anton Vorontsov; +Cc: linuxppc-dev
In-Reply-To: <20080429190045.GD21203@polina.dev.rtsoft.ru>
Anton Vorontsov wrote:
> This is needed to access QE GPIOs via Linux GPIO API.
>
> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
> ---
> Documentation/powerpc/booting-without-of.txt | 37 ++++---
> arch/powerpc/sysdev/qe_lib/Kconfig | 9 ++
> arch/powerpc/sysdev/qe_lib/Makefile | 1 +
> arch/powerpc/sysdev/qe_lib/gpio.c | 145 ++++++++++++++++++++++++++
> include/asm-powerpc/qe.h | 1 +
> 5 files changed, 180 insertions(+), 13 deletions(-)
> create mode 100644 arch/powerpc/sysdev/qe_lib/gpio.c
>
> diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt
> index fc7a235..4fefc44 100644
> --- a/Documentation/powerpc/booting-without-of.txt
> +++ b/Documentation/powerpc/booting-without-of.txt
> @@ -1723,24 +1723,35 @@ platforms are moved over to use the flattened-device-tree model.
> information.
>
> Required properties:
> - - device_type : should be "par_io".
> + - #gpio-cells : should be "2".
> + - compatible : should be "fsl,<chip>-qe-pario-bank",
> + "fsl,mpc8323-qe-pario-bank".
> - reg : offset to the register set and its length.
> - - num-ports : number of Parallel I/O ports
> + - gpio-controller : node to identify gpio controllers.
>
> - Example:
> - par_io@1400 {
> - reg = <1400 100>;
> - #address-cells = <1>;
> - #size-cells = <0>;
> - device_type = "par_io";
> - num-ports = <7>;
> - ucc_pin@01 {
> - ......
> - };
> + For example, two QE Par I/O banks:
> + qe_pio_a: gpio-controller@1400 {
I think this change will break a number of boards, because a lot of them do this:
if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) {
par_io_init(np);
So if you're going to change the par_io nodes, you need to change the code as well.
A patch that changes the documentation should also change the code. And if
you're code changes the device tree, it should also maintain compatibility for
older device trees.
> + #gpio-cells = <2>;
> + compatible = "fsl,mpc8360-qe-pario-bank",
> + "fsl,mpc8323-qe-pario-bank";
> + reg = <0x1400 0x18>;
> + gpio-controller;
> + };
>
> + qe_pio_e: gpio-controller@1460 {
> + #gpio-cells = <2>;
> + compatible = "fsl,mpc8360-qe-pario-bank",
> + "fsl,mpc8323-qe-pario-bank";
> + reg = <0x1460 0x18>;
> + gpio-controller;
> + };
>
> vi) Pin configuration nodes
>
> + NOTE: pin configuration nodes are obsolete. Usually, their existance
> + is an evidence of the firmware shortcomings. Such fixups are
> + better handled by the Linux board file, not the device tree.
You can't just delete the par_io documentation without updating the code and
planning for feature removal. Almost all of the existing code out there for QE
boards expects a par_io node, and the device trees still have them.
> +static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio)
> +{
> + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
> + struct qe_pio_regs __iomem *regs = mm_gc->regs;
> + u32 pin_mask = 1 << (QE_PIO_PINS - 1 - gpio);
> +
> + return !!(in_be32(®s->cpdata) & pin_mask);
Do we need to do "!!"? I thought as long as the result was non-zero, it didn't
matter what the actual value is. "!!" converts non-zero to 1.
> +static int qe_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
> +{
> + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
> + struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&qe_gc->lock, flags);
> +
> + __par_io_config_pin(mm_gc->regs, gpio, 2, 0, 0, 0);
No magic numbers, please.
> +void __init qe_add_gpiochips(void)
> +{
> + int ret;
> + struct device_node *np;
> +
> + for_each_compatible_node(np, NULL, "fsl,mpc8323-qe-pario-bank") {
> + struct qe_gpio_chip *qe_gc;
> + struct of_mm_gpio_chip *mm_gc;
> + struct of_gpio_chip *of_gc;
> + struct gpio_chip *gc;
> +
> + qe_gc = kzalloc(sizeof(*qe_gc), GFP_KERNEL);
> + if (!qe_gc) {
> + ret = -ENOMEM;
> + goto err;
> + }
> +
> + spin_lock_init(&qe_gc->lock);
> +
> + mm_gc = &qe_gc->mm_gc;
> + of_gc = &mm_gc->of_gc;
> + gc = &of_gc->gc;
> +
> + mm_gc->save_regs = qe_gpio_save_regs;
> + of_gc->gpio_cells = 2;
> + gc->ngpio = QE_PIO_PINS;
> + gc->direction_input = qe_gpio_dir_in;
> + gc->direction_output = qe_gpio_dir_out;
> + gc->get = qe_gpio_get;
> + gc->set = qe_gpio_set;
> +
> + ret = of_mm_gpiochip_add(np, mm_gc);
> + if (ret)
> + goto err;
> + }
> +
> + return;
> +err:
> + pr_err("%s: registration failed with status %d\n", np->full_name, ret);
> + of_node_put(np);
Memory leak here. If of_mm_gpiochip_add() fails or if the 2nd call to kzalloc()
fails, the already-allocated qe_gc objects won't be released.
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* MPC5200b problem configuring PSC6 as a serial port
From: Nick @ 2008-04-29 20:22 UTC (permalink / raw)
To: linuxppc-dev
Hi Everyone,
Has anyone setup and used the MPC5200b PSC6 port as a serial port? I am
having trouble getting mine to work. I am using PSC1, 2 and 3 already,
and they work fine. In my DTS file, I have PSC6 configured like this:
serial@2c00 { // PSC6
device_type = "serial";
compatible = "mpc5200b-psc-uart","mpc5200-psc-uart";
port-number = <5>;
reg = <2c00 100>;
interrupts = <2 4 0>.;
interrupt-parent = <&mpc5200_pic>;
};
Is there anything special I need to do to get this port working?
Thanks,
Nick
^ permalink raw reply
* Re: [PATCH v3] sysdev,mv64x60: MV64x60 device bus
From: Dale Farnsworth @ 2008-04-29 20:46 UTC (permalink / raw)
To: Remi Machet; +Cc: Stephen Rothwell, Paul Mackerras, linuxppc-dev
In-Reply-To: <1209500798.14407.32.camel@pcds-ts102.slac.stanford.edu>
On Tue, Apr 29, 2008 at 01:26:38PM -0700, Remi Machet wrote:
> Follow up of my email of 4/16/2008 titled "MV64x60 device bus".
> For each mv64360 entry in the OpenFirmware database, add the
> registration of an of_bus to take care of devices connected to
> the MV64x60 asynchronous devices controller.
>
> Signed-off-by: Remi Machet (rmachet@slac.stanford.edu)
Acked-by: Dale Farnsworth <dale@farnsworth.org>
^ permalink raw reply
* Re: [PATCH] [IDE] alim15x3: disable init_hwif_ali15x3 for PowerPC
From: Bartlomiej Zolnierkiewicz @ 2008-04-29 21:01 UTC (permalink / raw)
To: Anton Vorontsov; +Cc: linux-ide, linuxppc-dev
In-Reply-To: <20080429161914.GA4980@polina.dev.rtsoft.ru>
On Tuesday 29 April 2008, Anton Vorontsov wrote:
> We don't need init_hwif_ali15x3() on the PowerPC systems either.
>
> Before:
>
> ALI15X3: IDE controller (0x10b9:0x5229 rev 0xc8) at PCI slot 0001:03:1f.0
> ALI15X3: 100% native mode on irq 19
> ide0: BM-DMA at 0x1120-0x1127
> ide1: BM-DMA at 0x1128-0x112f
> hda: SONY DVD RW AW-Q170A, ATAPI CD/DVD-ROM drive
> hda: UDMA/66 mode selected
> ide0: Disabled unable to get IRQ 14.
> ide0: failed to initialize IDE interface
> ide1: Disabled unable to get IRQ 15.
> ide1: failed to initialize IDE interface
>
> After:
>
> ALI15X3: IDE controller (0x10b9:0x5229 rev 0xc8) at PCI slot 0001:03:1f.0
> ALI15X3: 100% native mode on irq 19
> ide0: BM-DMA at 0x1120-0x1127
> ide1: BM-DMA at 0x1128-0x112f
> hda: SONY DVD RW AW-Q170A, ATAPI CD/DVD-ROM drive
> hda: UDMA/66 mode selected
> ide0 at 0x1100-0x1107,0x110a on irq 19
> ide1 at 0x1110-0x1117,0x111a on irq 19
> hda: ATAPI 48X DVD-ROM DVD-R CD-R/RW drive, 2048kB Cache
>
> ide0 works well, though I can't test ide1, it isn't traced out on
> the board.
>
> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
applied, thanks
^ permalink raw reply
* Re: [PATCH] Add fast little-endian switch system call
From: Paul Mackerras @ 2008-04-29 21:16 UTC (permalink / raw)
To: Wolfgang Denk; +Cc: linux-arch, linuxppc-dev, Christoph Hellwig, mtk.manpages
In-Reply-To: <20080429184047.47CB7247B4@gemini.denx.de>
Wolfgang Denk writes:
> This probably depends a bit on the performance of the system in
> question. Did you measure it - for example - on a 50 MHz MPC850 ?
The patch only affects arch/powerpc/kernel/entry_64.S. So no, I
didn't measure it on a 50MHz MPC850, or indeed any 32-bit system. :)
Paul.
^ permalink raw reply
* Re: [PATCH 2/6] [POWERPC] QE: add support for QE USB clocks routing
From: Anton Vorontsov @ 2008-04-29 21:22 UTC (permalink / raw)
To: Timur Tabi; +Cc: linuxppc-dev
In-Reply-To: <48177DAE.7030807@freescale.com>
On Tue, Apr 29, 2008 at 02:57:34PM -0500, Timur Tabi wrote:
> Anton Vorontsov wrote:
> > This patch adds a function to the qe_lib to setup QE USB clocks routing.
> > To setup clocks safely, cmxgcr register needs locking, so I just reused
> > ucc_lock since it was used only to protect cmxgcr.
> >
> > The idea behind placing clocks routing functions into the qe_lib is that
> > later we'll hopefully switch to the generic Linux Clock API, thus, for
> > example, FHCI driver may be used for QE and CPM chips without nasty #ifdefs.
> >
> > This patch also fixes QE_USB_RESTART_TX command definition in the qe.h.
> >
> > Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
>
> Acked-By: Timur Tabi <timur@freescale.com>
Much thanks for the review and Acks.
> Anton, please be sure to CC: me on any QE library patches you send out.
Sorry about that. I actually know that QE changes should be Cc: you,
but I always forget this. :-/ I should automate Ccing, someday.
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: [PATCH 4/6] [POWERPC] QE: implement support for the GPIO LIB API
From: Anton Vorontsov @ 2008-04-29 21:23 UTC (permalink / raw)
To: Timur Tabi; +Cc: linuxppc-dev
In-Reply-To: <4817850C.20201@freescale.com>
On Tue, Apr 29, 2008 at 03:29:00PM -0500, Timur Tabi wrote:
> Anton Vorontsov wrote:
> > This is needed to access QE GPIOs via Linux GPIO API.
> >
> > Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
> > ---
> > Documentation/powerpc/booting-without-of.txt | 37 ++++---
> > arch/powerpc/sysdev/qe_lib/Kconfig | 9 ++
> > arch/powerpc/sysdev/qe_lib/Makefile | 1 +
> > arch/powerpc/sysdev/qe_lib/gpio.c | 145 ++++++++++++++++++++++++++
> > include/asm-powerpc/qe.h | 1 +
> > 5 files changed, 180 insertions(+), 13 deletions(-)
> > create mode 100644 arch/powerpc/sysdev/qe_lib/gpio.c
> >
> > diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt
> > index fc7a235..4fefc44 100644
> > --- a/Documentation/powerpc/booting-without-of.txt
> > +++ b/Documentation/powerpc/booting-without-of.txt
> > @@ -1723,24 +1723,35 @@ platforms are moved over to use the flattened-device-tree model.
> > information.
> >
> > Required properties:
> > - - device_type : should be "par_io".
> > + - #gpio-cells : should be "2".
> > + - compatible : should be "fsl,<chip>-qe-pario-bank",
> > + "fsl,mpc8323-qe-pario-bank".
> > - reg : offset to the register set and its length.
> > - - num-ports : number of Parallel I/O ports
> > + - gpio-controller : node to identify gpio controllers.
> >
> > - Example:
> > - par_io@1400 {
> > - reg = <1400 100>;
> > - #address-cells = <1>;
> > - #size-cells = <0>;
> > - device_type = "par_io";
> > - num-ports = <7>;
> > - ucc_pin@01 {
> > - ......
> > - };
> > + For example, two QE Par I/O banks:
> > + qe_pio_a: gpio-controller@1400 {
>
> I think this change will break a number of boards, because a lot of them do this:
>
> if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) {
> par_io_init(np);
>
> So if you're going to change the par_io nodes, you need to change the code as well.
>
> A patch that changes the documentation should also change the code. And if
> you're code changes the device tree, it should also maintain compatibility for
> older device trees.
Well, I'm indeed removing [further] support for the par_io nodes,
overwriting it with gpio nodes. That way we'll stop new use cases of
these nodes.
Eventually I plan to adjust the existing code/device trees accordingly.
If you think that code should be adjusted at the same time as
documentation, I'm fine with it as well. I'll just add new documentation
instead of replacing the old.
> > + #gpio-cells = <2>;
> > + compatible = "fsl,mpc8360-qe-pario-bank",
> > + "fsl,mpc8323-qe-pario-bank";
> > + reg = <0x1400 0x18>;
> > + gpio-controller;
> > + };
> >
> > + qe_pio_e: gpio-controller@1460 {
> > + #gpio-cells = <2>;
> > + compatible = "fsl,mpc8360-qe-pario-bank",
> > + "fsl,mpc8323-qe-pario-bank";
> > + reg = <0x1460 0x18>;
> > + gpio-controller;
> > + };
> >
> > vi) Pin configuration nodes
> >
> > + NOTE: pin configuration nodes are obsolete. Usually, their existance
> > + is an evidence of the firmware shortcomings. Such fixups are
> > + better handled by the Linux board file, not the device tree.
>
> You can't just delete the par_io documentation without updating the code and
> planning for feature removal. Almost all of the existing code out there for QE
> boards expects a par_io node, and the device trees still have them.
Yup, exactly. Old device tree still use them, but don't we want to get
rid of this? If so, we should remove documentation, or someone will
use it for the new device trees. ;-)
> > +static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio)
> > +{
> > + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
> > + struct qe_pio_regs __iomem *regs = mm_gc->regs;
> > + u32 pin_mask = 1 << (QE_PIO_PINS - 1 - gpio);
> > +
> > + return !!(in_be32(®s->cpdata) & pin_mask);
>
> Do we need to do "!!"? I thought as long as the result was non-zero, it didn't
> matter what the actual value is. "!!" converts non-zero to 1.
Just checked with the Documentation/gpio.txt... yup we don't need this.
"The values are boolean, zero for low, nonzero for high."
> > +static int qe_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
> > +{
> > + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
> > + struct qe_gpio_chip *qe_gc = to_qe_gpio_chip(mm_gc);
> > + unsigned long flags;
> > +
> > + spin_lock_irqsave(&qe_gc->lock, flags);
> > +
> > + __par_io_config_pin(mm_gc->regs, gpio, 2, 0, 0, 0);
>
> No magic numbers, please.
Ok.
> > +void __init qe_add_gpiochips(void)
> > +{
> > + int ret;
> > + struct device_node *np;
> > +
> > + for_each_compatible_node(np, NULL, "fsl,mpc8323-qe-pario-bank") {
> > + struct qe_gpio_chip *qe_gc;
> > + struct of_mm_gpio_chip *mm_gc;
> > + struct of_gpio_chip *of_gc;
> > + struct gpio_chip *gc;
> > +
> > + qe_gc = kzalloc(sizeof(*qe_gc), GFP_KERNEL);
> > + if (!qe_gc) {
> > + ret = -ENOMEM;
> > + goto err;
> > + }
> > +
> > + spin_lock_init(&qe_gc->lock);
> > +
> > + mm_gc = &qe_gc->mm_gc;
> > + of_gc = &mm_gc->of_gc;
> > + gc = &of_gc->gc;
> > +
> > + mm_gc->save_regs = qe_gpio_save_regs;
> > + of_gc->gpio_cells = 2;
> > + gc->ngpio = QE_PIO_PINS;
> > + gc->direction_input = qe_gpio_dir_in;
> > + gc->direction_output = qe_gpio_dir_out;
> > + gc->get = qe_gpio_get;
> > + gc->set = qe_gpio_set;
> > +
> > + ret = of_mm_gpiochip_add(np, mm_gc);
> > + if (ret)
> > + goto err;
> > + }
> > +
> > + return;
> > +err:
> > + pr_err("%s: registration failed with status %d\n", np->full_name, ret);
> > + of_node_put(np);
>
> Memory leak here. If of_mm_gpiochip_add() fails or if the 2nd call to kzalloc()
> fails, the already-allocated qe_gc objects won't be released.
Thanks for catching this.
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: [PATCH] [POWERPC] convert transfer_to_handler into a macro
From: Paul Mackerras @ 2008-04-29 21:28 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev
In-Reply-To: <Pine.LNX.4.64.0804290856210.9230@blarg.am.freescale.net>
Kumar Gala writes:
> We need to have unique transfer_to_handler paths for each exception level
> that is supported. We need to use the proper xSRR0/1 depending on which
> exception level the interrupt was from. The macro conversion lets up
> templatize this code path.
It seems to me that this implies you are assuming that you will never
ever get a synchronous normal interrupt such as a TLB miss while you
are in a critical or machine check handler.
Wouldn't it be better and safer to have the exception prolog for
critical interrupts save SRR0/1 in the stack frame, and have the
prolog for machine checks save SRR0/1 and CSRR0/1 likewise?
Paul.
^ permalink raw reply
* Re: [PATCH 4/6] [POWERPC] QE: implement support for the GPIO LIB API
From: Timur Tabi @ 2008-04-29 21:29 UTC (permalink / raw)
To: cbouatmailru; +Cc: linuxppc-dev
In-Reply-To: <20080429212304.GA8103@zarina>
Anton Vorontsov wrote:
> Well, I'm indeed removing [further] support for the par_io nodes,
> overwriting it with gpio nodes. That way we'll stop new use cases of
> these nodes.
I have no problem with replacing par_io nodes with gpio nodes.
> Eventually I plan to adjust the existing code/device trees accordingly.
> If you think that code should be adjusted at the same time as
> documentation, I'm fine with it as well. I'll just add new documentation
> instead of replacing the old.
Yes, please.
>> You can't just delete the par_io documentation without updating the code and
>> planning for feature removal. Almost all of the existing code out there for QE
>> boards expects a par_io node, and the device trees still have them.
>
> Yup, exactly. Old device tree still use them, but don't we want to get
> rid of this? If so, we should remove documentation, or someone will
> use it for the new device trees. ;-)
It should still be documented, but marked as deprecated. The documentation can
be removed only when all the DTS files *and* the code are scrubbed of those
kinds of nodes. And you can't remove backwards compatibility for those from the
code until at least the next major revision of the kernel.
Look at function of_get_mac_address() for an example. "mac-address" was removed
mid last year, but we still support them. Try to isolate the code that support
the newer gpio nodes and the older par_io nodes into one function, and it'll be
easier to deal with.
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* Re: filenames with spaces in /sys?
From: Christian Kujau @ 2008-04-29 21:54 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev, dwmw2
In-Reply-To: <20080428153127.GA9849@ld0162-tx32.am.freescale.net>
On Mon, 28 Apr 2008, Scott Wood wrote:
>> Wouldn't ../pmu-battery.0/power_supply/PMU_battery_0/power be feasible too?
>
> Feasible, yes, but uglier
I did not want to argue about "ugliness", as this is a matter of
personal taste.
> If userspace tools make it awkward to deal with filenames with spaces,
> let's fix the userspace tools.
Usually I would agree. I was only puzzled that filenames in /sys have
spaces at all - I was under the impression spaces should be omitted in
/sys (and /proc, too) for ease of handling (userspace has to specialcase
spaces). And looking at these directories only very few filenames contain
spaces, so it looked like ../PMU battery 0/ somehow "slipped through", as
there's no point in having spaces when almost everything else was using
an unserscore.
> /sys is certainly not the only place they'll be found, and waging war
> against them is a battle best left for a couple decades ago.
Hm, I guess the real solution would be a statement about special
characters in special filesystems in CodingStyle ....and a travel back in
time to have this rule applied of course :)
C.
--
BOFH excuse #190:
Proprietary Information.
^ permalink raw reply
* [PATCH] [POWERPC] mpc5200: Allow for fixed speed MII configurations
From: Grant Likely @ 2008-04-29 23:06 UTC (permalink / raw)
To: wg, linuxppc-dev, paulus, netdev, wd, domen.puncer
From: Grant Likely <grant.likely@secretlab.ca>
Various improvements for configuring the MPC5200 MII link from the
device tree:
* Look for 'current-speed' property for fixed speed MII links
* Look for 'fsl,7-wire-mode' property for boards using the 7 wire mode
* move definition of private data structure out of the header file
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
.../powerpc/mpc52xx-device-tree-bindings.txt | 11 ++
drivers/net/fec_mpc52xx.c | 96 +++++++++++++++-----
drivers/net/fec_mpc52xx.h | 19 ----
3 files changed, 85 insertions(+), 41 deletions(-)
diff --git a/Documentation/powerpc/mpc52xx-device-tree-bindings.txt b/Documentation/powerpc/mpc52xx-device-tree-bindings.txt
index cda7a7d..6f12f1c 100644
--- a/Documentation/powerpc/mpc52xx-device-tree-bindings.txt
+++ b/Documentation/powerpc/mpc52xx-device-tree-bindings.txt
@@ -237,6 +237,17 @@ Each GPIO controller node should have the empty property gpio-controller and
according to the bit numbers in the GPIO control registers. The second cell
is for flags which is currently unsused.
+8) FEC nodes
+The FEC node can specify one of the following properties to configure
+the MII link:
+"fsl,7-wire-mode" - An empty property that specifies the link uses 7-wire
+ mode instead of MII
+"current-speed" - Specifies that the MII should be configured for a fixed
+ speed. This property should contain two cells. The
+ first cell specifies the speed in Mbps and the second
+ should be '0' for half duplex and '1' for full duplex
+"phy-handle" - Contains a phandle to an Ethernet PHY.
+
IV - Extra Notes
================
diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c
index d21b7ab..eeb4433 100644
--- a/drivers/net/fec_mpc52xx.c
+++ b/drivers/net/fec_mpc52xx.c
@@ -43,6 +43,29 @@
#define DRIVER_NAME "mpc52xx-fec"
+#define FEC5200_PHYADDR_NONE (-1)
+#define FEC5200_PHYADDR_7WIRE (-2)
+
+/* Private driver data structure */
+struct mpc52xx_fec_priv {
+ int duplex;
+ int speed;
+ int r_irq;
+ int t_irq;
+ struct mpc52xx_fec __iomem *fec;
+ struct bcom_task *rx_dmatsk;
+ struct bcom_task *tx_dmatsk;
+ spinlock_t lock;
+ int msg_enable;
+
+ /* MDIO link details */
+ int phy_addr;
+ unsigned int phy_speed;
+ struct phy_device *phydev;
+ enum phy_state link;
+};
+
+
static irqreturn_t mpc52xx_fec_interrupt(int, void *);
static irqreturn_t mpc52xx_fec_rx_interrupt(int, void *);
static irqreturn_t mpc52xx_fec_tx_interrupt(int, void *);
@@ -223,7 +246,7 @@ static int mpc52xx_fec_phy_start(struct net_device *dev)
struct mpc52xx_fec_priv *priv = netdev_priv(dev);
int err;
- if (!priv->has_phy)
+ if (priv->phy_addr < 0)
return 0;
err = mpc52xx_fec_init_phy(dev);
@@ -243,7 +266,7 @@ static void mpc52xx_fec_phy_stop(struct net_device *dev)
{
struct mpc52xx_fec_priv *priv = netdev_priv(dev);
- if (!priv->has_phy)
+ if (!priv->phydev)
return;
phy_disconnect(priv->phydev);
@@ -255,7 +278,7 @@ static void mpc52xx_fec_phy_stop(struct net_device *dev)
static int mpc52xx_fec_phy_mii_ioctl(struct mpc52xx_fec_priv *priv,
struct mii_ioctl_data *mii_data, int cmd)
{
- if (!priv->has_phy)
+ if (!priv->phydev)
return -ENOTSUPP;
return phy_mii_ioctl(priv->phydev, mii_data, cmd);
@@ -265,7 +288,7 @@ static void mpc52xx_fec_phy_hw_init(struct mpc52xx_fec_priv *priv)
{
struct mpc52xx_fec __iomem *fec = priv->fec;
- if (!priv->has_phy)
+ if (priv->phydev)
return;
out_be32(&fec->mii_speed, priv->phy_speed);
@@ -704,7 +727,7 @@ static void mpc52xx_fec_start(struct net_device *dev)
rcntrl = FEC_RX_BUFFER_SIZE << 16; /* max frame length */
rcntrl |= FEC_RCNTRL_FCE;
- if (priv->has_phy)
+ if (priv->phy_addr != FEC5200_PHYADDR_7WIRE)
rcntrl |= FEC_RCNTRL_MII_MODE;
if (priv->duplex == DUPLEX_FULL)
@@ -864,7 +887,10 @@ mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
struct net_device *ndev;
struct mpc52xx_fec_priv *priv = NULL;
struct resource mem;
- const phandle *ph;
+ struct device_node *phy_node;
+ const phandle *phy_handle;
+ const u32 *prop;
+ int prop_size;
phys_addr_t rx_fifo;
phys_addr_t tx_fifo;
@@ -950,24 +976,36 @@ mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
priv->msg_enable = netif_msg_init(debug, MPC52xx_MESSAGES_DEFAULT);
priv->duplex = DUPLEX_FULL;
- /* is the phy present in device tree? */
- ph = of_get_property(op->node, "phy-handle", NULL);
- if (ph) {
- const unsigned int *prop;
- struct device_node *phy_dn;
- priv->has_phy = 1;
-
- phy_dn = of_find_node_by_phandle(*ph);
- prop = of_get_property(phy_dn, "reg", NULL);
- priv->phy_addr = *prop;
+ /*
+ * Link mode configuration
+ */
- of_node_put(phy_dn);
+ /* Start with safe defaults for link connection */
+ priv->phy_addr = FEC5200_PHYADDR_NONE;
+ priv->speed = 100;
+ priv->duplex = 0;
+ priv->phy_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1;
+
+ /* the 7-wire property means don't use MII mode */
+ if (of_find_property(op->node, "fsl,7-wire-mode", NULL))
+ priv->phy_addr = FEC5200_PHYADDR_7WIRE;
+
+ /* The current speed preconfigures the speed of the MII link */
+ prop = of_get_property(op->node, "current-speed", &prop_size);
+ if (prop && (prop_size >= sizeof(u32) * 2)) {
+ priv->speed = prop[0];
+ priv->duplex = prop[1] ? DUPLEX_FULL : DUPLEX_HALF;
+ }
- /* Phy speed */
- priv->phy_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1;
- } else {
- dev_info(&ndev->dev, "can't find \"phy-handle\" in device"
- " tree, using 7-wire mode\n");
+ /* If there is a phy handle, setup link to that phy */
+ phy_handle = of_get_property(op->node, "phy-handle", &prop_size);
+ if (phy_handle && (prop_size >= sizeof(phandle))) {
+ phy_node = of_find_node_by_phandle(*phy_handle);
+ prop = of_get_property(phy_node, "reg", &prop_size);
+ if (prop && (prop_size >= sizeof(u32)))
+ if ((*prop >= 0) && (*prop < PHY_MAX_ADDR))
+ priv->phy_addr = *prop;
+ of_node_put(phy_node);
}
/* Hardware init */
@@ -982,6 +1020,20 @@ mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
if (rv < 0)
goto probe_error;
+ /* Now report the link setup */
+ switch (priv->phy_addr) {
+ case FEC5200_PHYADDR_NONE:
+ dev_info(&ndev->dev, "Fixed speed MII link: %i%cD\n",
+ priv->speed, priv->duplex ? 'F' : 'H');
+ break;
+ case FEC5200_PHYADDR_7WIRE:
+ dev_info(&ndev->dev, "using 7-wire PHY mode\n");
+ break;
+ default:
+ dev_info(&ndev->dev, "Using PHY at MDIO address %i\n",
+ priv->phy_addr);
+ }
+
/* We're done ! */
dev_set_drvdata(&op->dev, ndev);
diff --git a/drivers/net/fec_mpc52xx.h b/drivers/net/fec_mpc52xx.h
index 8b1f753..a227a52 100644
--- a/drivers/net/fec_mpc52xx.h
+++ b/drivers/net/fec_mpc52xx.h
@@ -26,25 +26,6 @@
#define FEC_WATCHDOG_TIMEOUT ((400*HZ)/1000)
-struct mpc52xx_fec_priv {
- int duplex;
- int r_irq;
- int t_irq;
- struct mpc52xx_fec __iomem *fec;
- struct bcom_task *rx_dmatsk;
- struct bcom_task *tx_dmatsk;
- spinlock_t lock;
- int msg_enable;
-
- int has_phy;
- unsigned int phy_speed;
- unsigned int phy_addr;
- struct phy_device *phydev;
- enum phy_state link;
- int speed;
-};
-
-
/* ======================================================================== */
/* Hardware register sets & bits */
/* ======================================================================== */
^ permalink raw reply related
* Re: [PATCH] [POWERPC] convert transfer_to_handler into a macro
From: Kumar Gala @ 2008-04-29 23:52 UTC (permalink / raw)
To: Paul Mackerras; +Cc: linuxppc-dev
In-Reply-To: <18455.37613.709102.30668@cargo.ozlabs.ibm.com>
On Apr 29, 2008, at 4:28 PM, Paul Mackerras wrote:
> Kumar Gala writes:
>
>> We need to have unique transfer_to_handler paths for each exception
>> level
>> that is supported. We need to use the proper xSRR0/1 depending on
>> which
>> exception level the interrupt was from. The macro conversion lets up
>> templatize this code path.
>
> It seems to me that this implies you are assuming that you will never
> ever get a synchronous normal interrupt such as a TLB miss while you
> are in a critical or machine check handler.
Grr.. one more thing to fix :)
> Wouldn't it be better and safer to have the exception prolog for
> critical interrupts save SRR0/1 in the stack frame, and have the
> prolog for machine checks save SRR0/1 and CSRR0/1 likewise?
If we do this I guess we can use SRR0/1 regardless of which level we
came from.
- k
^ permalink raw reply
* Re: [PATCH] [POWERPC] Add struct iommu_table argument to iommu_map_sg()
From: Stephen Rothwell @ 2008-04-29 23:53 UTC (permalink / raw)
To: Olof Johansson; +Cc: Mark Nelson, linuxppc-dev
In-Reply-To: <20080429175413.GA10269@lixom.net>
[-- Attachment #1: Type: text/plain, Size: 583 bytes --]
Hi Olof,
On Tue, 29 Apr 2008 12:54:17 -0500 Olof Johansson <olof@lixom.net> wrote:
>
> Consistency is good, but I wonder if the opposite wouldn't be the better
> way to go here: always just pass down just the dev pointer instead. The
> table can be reached from it.
I made the changes that passed the iommu_table so that e.g. iSeries could
call iommu_alloc_coherent without a dev pointer at all so that the
generic powerpc code could be cleaned up quite a bit.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: [PATCH] [POWERPC] Add struct iommu_table argument to iommu_map_sg()
From: Mark Nelson @ 2008-04-30 0:37 UTC (permalink / raw)
To: Olof Johansson; +Cc: linuxppc-dev
In-Reply-To: <20080429175413.GA10269@lixom.net>
Olof Johansson wrote:
> On Tue, Apr 29, 2008 at 03:17:45PM +1000, Mark Nelson wrote:
>> Make iommu_map_sg take a struct iommu_table. It did so before commit
>> 740c3ce66700640a6e6136ff679b067e92125794 (iommu sg merging: ppc: make
>> iommu respect the segment size limits).
>>
>> This stops the function looking in the archdata.dma_data for the iommu
>> table because in the future it will be called with a device that has
>> no table there.
>
> The logical thing would be to add the archdata.dma_data to said device
> instead, no? Without seeing the rest of the code that makes use of it
> it's hard to tell anyway, so please post that.
I'll post the follow on series of patches as an RFC to give you a better
understanding of what I'm trying to do; but basically I'm building on
the new dma_*map*_attrs() interfaces that Andrew Morton sent to Linus
last night and implementing them for powerpc (in particular the Cell
platform). To cut a longish story short, the fixed linear mapping for
the Cell's IOMMU can be set to either be weakly or strongly ordered,
and so a device that has been identified as being able to use the
fixed mapping can later request a mapping that cannot be satisfied using
the fixed mapping (eg: it may request a weakly ordered mapping when
the fixed mapping is strongly ordered, or vice versa). Devices that
use the fixed mapping use the dma_direct_ops with their dma_data being
the necessary offset, but for the case above they have to use the
iommu_map*() functions that the dma_iommu_ops use. So that's how we end
up calling iommu_map_sg() with a device that doesn't have the table
in the dma_data (and already has something useful in dma_data).
I'll try to get the follow on series of patches cleaned up today and
send them out asap, so if my ramblings above turned out to be too
obscure the code will hopefully clear things up.
Thanks!
Mark.
>
>> This also has the nice side effect of making iommu_map_sg() match the
>> other map functions.
>
> Consistency is good, but I wonder if the opposite wouldn't be the better
> way to go here: always just pass down just the dev pointer instead. The
> table can be reached from it.
>
>
> -Olof
>
^ permalink raw reply
* Re: [RESEND][PATCH][POWERPC] PIKA Warp: Update platform code to supportRev B boards
From: Josh Boyer @ 2008-04-30 0:48 UTC (permalink / raw)
To: Sean MacLennan; +Cc: Scott Wood, linuxppc-dev
In-Reply-To: <20080428180717.4e06187a@lappy.seanm.ca>
On Mon, 28 Apr 2008 18:07:17 -0400
Sean MacLennan <seanm@seanm.ca> wrote:
> On Mon, 28 Apr 2008 16:54:24 -0500
> Scott Wood <scottwood@freescale.com> wrote:
>
> > Why can't the existing partition binding be used with NAND? It's
> > what we do with Freescale FCM NAND.
>
> I guess I could put the partitions in the dts. But I would have to
> read them and dynamically create an array to pass to the ndfc driver.
>
> It seems simpler to just statically initialize the array. Once the ndfc
> is modified to use the dts, I will switch to that method.
Right. It's a limitation of NDFC, which isn't WARP specific and needs
fixing in general.
josh
^ permalink raw reply
* [PATCH] Make emergency stack safe for current_thread_info() use
From: Michael Ellerman @ 2008-04-30 3:21 UTC (permalink / raw)
To: linuxppc-dev
The current_thread_info() macro, used by preempt_count(), assumes the
stack is THREAD_SIZE aligned. It should probably also be THREAD_SIZE
in size.
The emergency stack currently isn't either of these things, which could
potentially cause problems anytime we're running on the emergency
stack. That includes when we detect a bad kernel stack pointer, and also
during early_setup_secondary().
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
Booted on P6 LPAR and QS21.
arch/powerpc/kernel/setup_64.c | 9 ++++++---
1 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index dff6308..25e3fd8 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -487,9 +487,12 @@ static void __init emergency_stack_init(void)
*/
limit = min(0x10000000UL, lmb.rmo_size);
- for_each_possible_cpu(i)
- paca[i].emergency_sp =
- __va(lmb_alloc_base(HW_PAGE_SIZE, 128, limit)) + HW_PAGE_SIZE;
+ for_each_possible_cpu(i) {
+ unsigned long sp;
+ sp = lmb_alloc_base(THREAD_SIZE, THREAD_SIZE, limit);
+ sp += THREAD_SIZE;
+ paca[i].emergency_sp = __va(sp);
+ }
}
/*
--
1.5.5
^ permalink raw reply related
* Re: [PATCH] [POWERPC] convert transfer_to_handler into a macro
From: Paul Mackerras @ 2008-04-30 3:24 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev
In-Reply-To: <73CD5806-5CE4-40A7-957B-8F164E26FAFB@kernel.crashing.org>
Kumar Gala writes:
> > Wouldn't it be better and safer to have the exception prolog for
> > critical interrupts save SRR0/1 in the stack frame, and have the
> > prolog for machine checks save SRR0/1 and CSRR0/1 likewise?
>
> If we do this I guess we can use SRR0/1 regardless of which level we
> came from.
That's the idea. I actually thought the booke exception prologs
already did that, but it seems I'm wrong.
What sort of things do you expect critical and machine-check handlers
to be needing to do?
Paul.
^ permalink raw reply
* Re: [PATCH v3] sysdev,mv64x60: MV64x60 device bus
From: Paul Mackerras @ 2008-04-30 4:35 UTC (permalink / raw)
To: Remi Machet; +Cc: linuxppc-dev, Stephen Rothwell
In-Reply-To: <1209500798.14407.32.camel@pcds-ts102.slac.stanford.edu>
Remi Machet writes:
> Follow up of my email of 4/16/2008 titled "MV64x60 device bus".
> For each mv64360 entry in the OpenFirmware database, add the
> registration of an of_bus to take care of devices connected to
> the MV64x60 asynchronous devices controller.
Your patch description should also explain what good things will come
of doing this, or what bad things will be avoided. Also please put
the "Follow up..." sentence below the 3 dashes so I don't have to edit
it out.
If there are no further review comments, and if you resend with a
better patch description, I'll queue this up for the next merge window
(for 2.6.27).
Paul.
^ permalink raw reply
* [PATCH] Fix crashkernel= handling when no crashkernel= specified
From: Michael Ellerman @ 2008-04-30 4:47 UTC (permalink / raw)
To: linuxppc-dev; +Cc: bwalle
Commit edd8ce67436851a62f99f1d9707b40ea6a8e5323 (Use extended crashkernel
command line on ppc64), changed the logic in reserve_crashkernel()
which deals with the crashkernel= command line option.
This introduced a bug in the case when there is no crashkernel= option,
or it is incorrect. We would fallthrough and calculate the crash_size
based on the existing values in crashk_res. If both start and end are 0,
the default, we calculate the crash_size as 1 byte - which is wrong.
Rework the logic so that we use crashk_res, regardless of whether it's
set by the command line or via the device tree (see prom.c). Then check
if we have an empty range (end == start), and if so make sure to set
both end and start to zero (this is checked in machine_kexec_64.c). Then
we calculate the crash_size once we know we have a non-zero range.
Finally we always want to warn the user if they specify a base != 32MB,
so remove the special case for that in the command line parsing case.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
arch/powerpc/kernel/machine_kexec.c | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c
index 2d202f2..29a0e03 100644
--- a/arch/powerpc/kernel/machine_kexec.c
+++ b/arch/powerpc/kernel/machine_kexec.c
@@ -74,20 +74,20 @@ void __init reserve_crashkernel(void)
ret = parse_crashkernel(boot_command_line, lmb_phys_mem_size(),
&crash_size, &crash_base);
if (ret == 0 && crash_size > 0) {
- if (crash_base == 0)
- crash_base = KDUMP_KERNELBASE;
crashk_res.start = crash_base;
- } else {
- /* handle the device tree */
- crash_size = crashk_res.end - crashk_res.start + 1;
+ crashk_res.end = crash_base + crash_size - 1;
}
- if (crash_size == 0)
+ if (crashk_res.end == crashk_res.start) {
+ crashk_res.start = crashk_res.end = 0;
return;
+ }
/* We might have got these values via the command line or the
* device tree, either way sanitise them now. */
+ crash_size = crashk_res.end - crashk_res.start + 1;
+
if (crashk_res.start != KDUMP_KERNELBASE)
printk("Crash kernel location must be 0x%x\n",
KDUMP_KERNELBASE);
--
1.5.5
^ permalink raw reply related
* [RFC] [PATCH] vmemmap fixes to use smaller pages
From: Benjamin Herrenschmidt @ 2008-04-30 5:41 UTC (permalink / raw)
To: linuxppc-dev list
This patch changes vmemmap to use a different region (region 0xf) of the
address space whose page size can be dynamically configured at boot.
The problem with the current approach of always using 16M pages is that
it's not well suited to machines that have small amounts of memory such
as small partitions on pseries, or PS3's.
In fact, on the PS3, failure to allocate the 16M page backing vmmemmap
tends to prevent hotplugging the HV's "additional" memory, thus limiting
the available memory even more, from my experience down to something
like 80M total, which makes it really not very useable.
The logic used by my match to choose the vmemmap page size is:
- If 16M pages are available and there's 1G or more RAM at boot, use that size.
- Else if 64K pages are available, use that
- Else use 4K pages
I've tested on a POWER6 (16M pages) and on an iSeries POWER3 (4K pages)
and it seems to work fine.
However, when attempting to test on a PS3, it didn't boot.
In fact, it doesn't boot without my patch with current upstream. I tried
booting 2.6.25 with a ps3_defconfig and that doesn't work neither
(though at least when doing the later, I do get a black screen & no
sync, like of ps3fb failed monitor detection, while with current
upstream, I just get the last kexec messages and nothing happens).
Since the PS3 boot failures are impossible to debug unless your email is
@sony* and you have the special magic tools, I'll let Geoff try the
patch out.
Note that I intend to change the way we organize the kernel regions &
SLBs so the actual region will change from 0xf back to something else at
one point, as I simplify the SLB miss handler, but that will be for a
later patch.
Index: linux-work/arch/powerpc/mm/hash_utils_64.c
===================================================================
--- linux-work.orig/arch/powerpc/mm/hash_utils_64.c 2008-04-30 15:00:54.000000000 +1000
+++ linux-work/arch/powerpc/mm/hash_utils_64.c 2008-04-30 15:01:00.000000000 +1000
@@ -94,6 +94,9 @@ unsigned long htab_hash_mask;
int mmu_linear_psize = MMU_PAGE_4K;
int mmu_virtual_psize = MMU_PAGE_4K;
int mmu_vmalloc_psize = MMU_PAGE_4K;
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+int mmu_vmemmap_psize = MMU_PAGE_4K;
+#endif
int mmu_io_psize = MMU_PAGE_4K;
int mmu_kernel_ssize = MMU_SEGSIZE_256M;
int mmu_highuser_ssize = MMU_SEGSIZE_256M;
@@ -387,11 +390,32 @@ static void __init htab_init_page_sizes(
}
#endif /* CONFIG_PPC_64K_PAGES */
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+ /* We try to use 16M pages for vmemmap if that is supported
+ * and we have at least 1G of RAM at boot
+ */
+ if (mmu_psize_defs[MMU_PAGE_16M].shift &&
+ lmb_phys_mem_size() >= 0x40000000)
+ mmu_vmemmap_psize = MMU_PAGE_16M;
+ else if (mmu_psize_defs[MMU_PAGE_64K].shift)
+ mmu_vmemmap_psize = MMU_PAGE_64K;
+ else
+ mmu_vmemmap_psize = MMU_PAGE_4K;
+#endif /* CONFIG_SPARSEMEM_VMEMMAP */
+
printk(KERN_DEBUG "Page orders: linear mapping = %d, "
- "virtual = %d, io = %d\n",
+ "virtual = %d, io = %d"
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+ ", vmemmap = %d"
+#endif
+ "\n",
mmu_psize_defs[mmu_linear_psize].shift,
mmu_psize_defs[mmu_virtual_psize].shift,
- mmu_psize_defs[mmu_io_psize].shift);
+ mmu_psize_defs[mmu_io_psize].shift
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+ ,mmu_psize_defs[mmu_vmemmap_psize].shift
+#endif
+ );
#ifdef CONFIG_HUGETLB_PAGE
/* Init large page size. Currently, we pick 16M or 1M depending
Index: linux-work/arch/powerpc/mm/init_64.c
===================================================================
--- linux-work.orig/arch/powerpc/mm/init_64.c 2008-04-30 15:00:54.000000000 +1000
+++ linux-work/arch/powerpc/mm/init_64.c 2008-04-30 15:05:02.000000000 +1000
@@ -19,6 +19,8 @@
*
*/
+#undef DEBUG
+
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -208,12 +210,12 @@ int __meminit vmemmap_populated(unsigned
}
int __meminit vmemmap_populate(struct page *start_page,
- unsigned long nr_pages, int node)
+ unsigned long nr_pages, int node)
{
unsigned long mode_rw;
unsigned long start = (unsigned long)start_page;
unsigned long end = (unsigned long)(start_page + nr_pages);
- unsigned long page_size = 1 << mmu_psize_defs[mmu_linear_psize].shift;
+ unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift;
mode_rw = _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_COHERENT | PP_RWXX;
@@ -235,11 +237,11 @@ int __meminit vmemmap_populate(struct pa
start, p, __pa(p));
mapped = htab_bolt_mapping(start, start + page_size,
- __pa(p), mode_rw, mmu_linear_psize,
+ __pa(p), mode_rw, mmu_vmemmap_psize,
mmu_kernel_ssize);
BUG_ON(mapped < 0);
}
return 0;
}
-#endif
+#endif /* CONFIG_SPARSEMEM_VMEMMAP */
Index: linux-work/arch/powerpc/mm/slb.c
===================================================================
--- linux-work.orig/arch/powerpc/mm/slb.c 2008-04-30 15:00:54.000000000 +1000
+++ linux-work/arch/powerpc/mm/slb.c 2008-04-30 15:01:14.000000000 +1000
@@ -28,7 +28,7 @@
#include <asm/udbg.h>
#ifdef DEBUG
-#define DBG(fmt...) udbg_printf(fmt)
+#define DBG(fmt...) printk(fmt)
#else
#define DBG(fmt...)
#endif
@@ -263,13 +263,19 @@ void slb_initialize(void)
extern unsigned int *slb_miss_kernel_load_linear;
extern unsigned int *slb_miss_kernel_load_io;
extern unsigned int *slb_compare_rr_to_size;
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+ extern unsigned int *slb_miss_kernel_load_vmemmap;
+ unsigned long vmemmap_llp;
+#endif
/* Prepare our SLB miss handler based on our page size */
linear_llp = mmu_psize_defs[mmu_linear_psize].sllp;
io_llp = mmu_psize_defs[mmu_io_psize].sllp;
vmalloc_llp = mmu_psize_defs[mmu_vmalloc_psize].sllp;
get_paca()->vmalloc_sllp = SLB_VSID_KERNEL | vmalloc_llp;
-
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+ vmemmap_llp = mmu_psize_defs[mmu_vmemmap_psize].sllp;
+#endif
if (!slb_encoding_inited) {
slb_encoding_inited = 1;
patch_slb_encoding(slb_miss_kernel_load_linear,
@@ -279,8 +285,14 @@ void slb_initialize(void)
patch_slb_encoding(slb_compare_rr_to_size,
mmu_slb_size);
- DBG("SLB: linear LLP = %04x\n", linear_llp);
- DBG("SLB: io LLP = %04x\n", io_llp);
+ DBG("SLB: linear LLP = %04lx\n", linear_llp);
+ DBG("SLB: io LLP = %04lx\n", io_llp);
+
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+ patch_slb_encoding(slb_miss_kernel_load_vmemmap,
+ SLB_VSID_KERNEL | vmemmap_llp);
+ DBG("SLB: vmemmap LLP = %04lx\n", vmemmap_llp);
+#endif
}
get_paca()->stab_rr = SLB_NUM_BOLTED;
Index: linux-work/arch/powerpc/mm/slb_low.S
===================================================================
--- linux-work.orig/arch/powerpc/mm/slb_low.S 2008-04-30 15:00:54.000000000 +1000
+++ linux-work/arch/powerpc/mm/slb_low.S 2008-04-30 15:01:00.000000000 +1000
@@ -47,8 +47,7 @@ _GLOBAL(slb_allocate_realmode)
* it to VSID 0, which is reserved as a bad VSID - one which
* will never have any pages in it. */
- /* Check if hitting the linear mapping of the vmalloc/ioremap
- * kernel space
+ /* Check if hitting the linear mapping or some other kernel space
*/
bne cr7,1f
@@ -62,7 +61,18 @@ BEGIN_FTR_SECTION
END_FTR_SECTION_IFCLR(CPU_FTR_1T_SEGMENT)
b slb_finish_load_1T
-1: /* vmalloc/ioremap mapping encoding bits, the "li" instructions below
+1:
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+ /* Check virtual memmap region. To be patches at kernel boot */
+ cmpldi cr0,r9,0xf
+ bne 1f
+_GLOBAL(slb_miss_kernel_load_vmemmap)
+ li r11,0
+ b 6f
+1:
+#endif /* CONFIG_SPARSEMEM_VMEMMAP */
+
+ /* vmalloc/ioremap mapping encoding bits, the "li" instructions below
* will be patched by the kernel at boot
*/
BEGIN_FTR_SECTION
Index: linux-work/include/asm-powerpc/mmu-hash64.h
===================================================================
--- linux-work.orig/include/asm-powerpc/mmu-hash64.h 2008-04-30 15:00:54.000000000 +1000
+++ linux-work/include/asm-powerpc/mmu-hash64.h 2008-04-30 15:01:00.000000000 +1000
@@ -177,6 +177,7 @@ extern struct mmu_psize_def mmu_psize_de
extern int mmu_linear_psize;
extern int mmu_virtual_psize;
extern int mmu_vmalloc_psize;
+extern int mmu_vmemmap_psize;
extern int mmu_io_psize;
extern int mmu_kernel_ssize;
extern int mmu_highuser_ssize;
Index: linux-work/include/asm-powerpc/pgtable-ppc64.h
===================================================================
--- linux-work.orig/include/asm-powerpc/pgtable-ppc64.h 2008-04-30 15:00:54.000000000 +1000
+++ linux-work/include/asm-powerpc/pgtable-ppc64.h 2008-04-30 15:01:00.000000000 +1000
@@ -65,15 +65,15 @@
#define VMALLOC_REGION_ID (REGION_ID(VMALLOC_START))
#define KERNEL_REGION_ID (REGION_ID(PAGE_OFFSET))
+#define VMEMMAP_REGION_ID (0xfUL)
#define USER_REGION_ID (0UL)
/*
- * Defines the address of the vmemap area, in the top 16th of the
- * kernel region.
+ * Defines the address of the vmemap area, in its own region
*/
-#define VMEMMAP_BASE (ASM_CONST(CONFIG_KERNEL_START) + \
- (0xfUL << (REGION_SHIFT - 4)))
-#define vmemmap ((struct page *)VMEMMAP_BASE)
+#define VMEMMAP_BASE (VMEMMAP_REGION_ID << REGION_SHIFT)
+#define vmemmap ((struct page *)VMEMMAP_BASE)
+
/*
* Common bits in a linux-style PTE. These match the bits in the
^ 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