*
* Tahvo USB transeiver
*
- * Copyright (C) 2005 Nokia Corporation
+ * Copyright (C) 2005-2006 Nokia Corporation
+ *
+ * Parts copied from drivers/i2c/chips/isp1301_omap.c
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2004 David Brownell
*
* Written by Juha Yrjölä <juha.yrjola@nokia.com>,
* Tony Lindgren <tony@atomide.com>, and
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/usb_ch9.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
#include <linux/usb_gadget.h>
#include <linux/usb.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
#include <linux/i2c.h>
#include <linux/workqueue.h>
-#include <linux/kobject_uevent.h>
+#include <linux/kobject.h>
#include <linux/clk.h>
+#include <linux/mutex.h>
#include <asm/irq.h>
-#include <asm/semaphore.h>
#include <asm/arch/usb.h>
#include "cbus.h"
#define TAHVO_MODE(tu) TAHVO_MODE_HOST
#endif
-extern int ohci_omap_host_enable(struct usb_bus *host, int enable);
-
struct tahvo_usb {
struct platform_device *pt_dev;
struct otg_transceiver otg;
int vbus_state;
struct work_struct irq_work;
- struct semaphore serialize;
+ struct mutex serialize;
#ifdef CONFIG_USB_OTG
int tahvo_mode;
#endif
* OTG related functions
*
* These shoud be separated into omap-otg.c driver module, as they are used
- * by various transceivers. These functions are needed in the UDC-only case
+ * by various transceivers. These functions are needed in the UDC-only case
* as well. These functions are copied from GPL isp1301_omap.c
* ---------------------------------------------------------------------------
*/
static struct platform_device *tahvo_otg_dev;
-static irqreturn_t omap_otg_irq(int irq, void *arg, struct pt_regs *regs)
+static irqreturn_t omap_otg_irq(int irq, void *arg)
{
struct platform_device *otg_dev = (struct platform_device *) arg;
struct tahvo_usb *tu = (struct tahvo_usb *) otg_dev->dev.driver_data;
} else if (otg_irq & A_VBUS_ERR) {
OTG_IRQ_SRC_REG = A_VBUS_ERR;
} else if (otg_irq & DRIVER_SWITCH) {
-
if ((!(OTG_CTRL_REG & OTG_DRIVER_SEL)) &&
tu->otg.host && tu->otg.state == OTG_STATE_A_HOST) {
/* role is host */
usb_bus_start_enum(tu->otg.host,
tu->otg.host->otg_port);
}
-
OTG_IRQ_SRC_REG = DRIVER_SWITCH;
} else
return IRQ_NONE;
return -ENODEV;
}
#endif
+ OTG_SYSCON_1_REG &= ~OTG_IDLE_EN;
+ udelay(100);
/* some of these values are board-specific... */
OTG_SYSCON_2_REG |= OTG_EN
}
return request_irq(tahvo_otg_dev->resource[1].start,
- omap_otg_irq, SA_INTERRUPT, DRIVER_NAME,
+ omap_otg_irq, IRQF_DISABLED, DRIVER_NAME,
&tahvo_usb_device);
}
static int omap_otg_remove(struct device *dev)
{
- tahvo_otg_dev = NULL;
free_irq(tahvo_otg_dev->resource[1].start, &tahvo_usb_device);
+ tahvo_otg_dev = NULL;
+
return 0;
}
* which are copied from isp1301.c
* ---------------------------------------------------------------------------
*/
-static ssize_t vbus_state_show(struct device *device, char *buf)
+static ssize_t vbus_state_show(struct device *device,
+ struct device_attribute *attr, char *buf)
{
struct tahvo_usb *tu = (struct tahvo_usb*) device->driver_data;
return sprintf(buf, "%d\n", tu->vbus_state);
int vbus_active = 0;
+#if 0
+
+static int host_suspend(struct tahvo_usb *tu)
+{
+ struct device *dev;
+
+ if (!tu->otg.host)
+ return -ENODEV;
+
+ /* Currently ASSUMES only the OTG port matters;
+ * other ports could be active...
+ */
+ dev = tu->otg.host->controller;
+ return dev->driver->suspend(dev, PMSG_SUSPEND);
+}
+
+static int host_resume(struct tahvo_usb *tu)
+{
+ struct device *dev;
+
+ if (!tu->otg.host)
+ return -ENODEV;
+
+ dev = tu->otg.host->controller;
+ return dev->driver->resume(dev);
+}
+
+#else
+
+static int host_suspend(struct tahvo_usb *tu)
+{
+ return 0;
+}
+
+static int host_resume(struct tahvo_usb *tu)
+{
+ return 0;
+}
+
+#endif
+
static void check_vbus_state(struct tahvo_usb *tu)
{
int reg, prev_state;
case OTG_STATE_A_IDLE:
/* Session is now valid assuming the USB hub is driving Vbus */
tu->otg.state = OTG_STATE_A_HOST;
- if (tu->otg.host)
- ohci_omap_host_enable(tu->otg.host, 1);
+ host_resume(tu);
break;
default:
break;
}
printk("USB cable connected\n");
} else {
- vbus_active = 0;
switch (tu->otg.state) {
case OTG_STATE_B_PERIPHERAL:
- OTG_CTRL_REG = (OTG_CTRL_REG & ~OTG_BSESSVLD) | OTG_BSESSEND;
if (tu->otg.gadget)
usb_gadget_vbus_disconnect(tu->otg.gadget);
tu->otg.state = OTG_STATE_B_IDLE;
break;
case OTG_STATE_A_HOST:
tu->otg.state = OTG_STATE_A_IDLE;
- if (tu->otg.host)
- ohci_omap_host_enable(tu->otg.host, 0);
-
break;
default:
break;
}
printk("USB cable disconnected\n");
+ vbus_active = 0;
}
prev_state = tu->vbus_state;
tu->vbus_state = reg & 0x01;
- if (prev_state != tu->vbus_state) {
- kobject_uevent_atomic(&tu->pt_dev->dev.kobj,
- KOBJ_CHANGE,
- &dev_attr_vbus_state.attr);
- }
+ if (prev_state != tu->vbus_state)
+ sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state");
}
static void tahvo_usb_become_host(struct tahvo_usb *tu)
{
+ u32 l;
+
/* Clear system and transceiver controlled bits
* also mark the A-session is always valid */
omap_otg_init();
- OTG_CTRL_REG = (OTG_CTRL_REG & ~(OTG_CTRL_XCVR_MASK|OTG_CTRL_SYS_MASK))
- | OTG_ASESSVLD;
+
+ l = OTG_CTRL_REG;
+ l &= ~(OTG_CTRL_XCVR_MASK|OTG_CTRL_SYS_MASK);
+ l |= OTG_ASESSVLD;
+ OTG_CTRL_REG = l;
/* Power up the transceiver in USB host mode */
tahvo_write_reg(TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND |
static void tahvo_usb_stop_host(struct tahvo_usb *tu)
{
- if (tu->otg.host)
- ohci_omap_host_enable(tu->otg.host, 0);
+ host_suspend(tu);
tu->otg.state = OTG_STATE_A_IDLE;
}
* and enable ID to mark peripheral mode and
* BSESSEND to mark no Vbus */
omap_otg_init();
- OTG_CTRL_REG = (OTG_CTRL_REG & ~(OTG_CTRL_XCVR_MASK|OTG_CTRL_SYS_MASK))
+ OTG_CTRL_REG = (OTG_CTRL_REG & ~(OTG_CTRL_XCVR_MASK|OTG_CTRL_SYS_MASK|OTG_BSESSVLD))
| OTG_ID | OTG_BSESSEND;
/* Power up transceiver and set it in USB perhiperal mode */
static void tahvo_usb_power_off(struct tahvo_usb *tu)
{
+ u32 l;
int id;
/* Disable gadget controller if any */
if (tu->otg.gadget)
usb_gadget_vbus_disconnect(tu->otg.gadget);
- if (tu->otg.host)
- ohci_omap_host_enable(tu->otg.host, 0);
+ host_suspend(tu);
/* Disable OTG and interrupts */
if (TAHVO_MODE(tu) == TAHVO_MODE_PERIPHERAL)
id = OTG_ID;
- else id = 0;
- OTG_CTRL_REG = (OTG_CTRL_REG & ~(OTG_CTRL_XCVR_MASK|OTG_CTRL_SYS_MASK)) | id;
+ else
+ id = 0;
+ l = OTG_CTRL_REG;
+ l &= ~(OTG_CTRL_XCVR_MASK | OTG_CTRL_SYS_MASK | OTG_BSESSVLD);
+ l |= id | OTG_BSESSEND;
+ OTG_CTRL_REG = l;
OTG_IRQ_EN_REG = 0;
-#if 0
+
OTG_SYSCON_2_REG &= ~OTG_EN;
-#endif
+
+ OTG_SYSCON_1_REG |= OTG_IDLE_EN;
/* Power off transceiver */
tahvo_write_reg(TAHVO_REG_USBR, 0);
static int tahvo_usb_set_power(struct otg_transceiver *dev, unsigned mA)
{
+ struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, otg);
+
+ dev_dbg(&tu->pt_dev->dev, "set_power %d mA\n", mA);
+
if (dev->state == OTG_STATE_B_PERIPHERAL) {
/* REVISIT: Can Tahvo charge battery from VBUS? */
}
static int tahvo_usb_set_suspend(struct otg_transceiver *dev, int suspend)
{
+ struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, otg);
u16 w;
+ dev_dbg(&tu->pt_dev->dev, "set_suspend\n");
+
w = tahvo_read_reg(TAHVO_REG_USBR);
if (suspend)
w &= ~USBR_NSUSPEND;
struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, otg);
u32 otg_ctrl;
+ dev_dbg(&tu->pt_dev->dev, "start_srp\n");
+
if (!dev || tu->otg.state != OTG_STATE_B_IDLE)
return -ENODEV;
OTG_CTRL_REG = otg_ctrl;
tu->otg.state = OTG_STATE_B_SRP_INIT;
- pr_debug("otg: SRP, %s ... %06x\n", state_name(tu), OTG_CTRL_REG);
-
return 0;
}
-static int tahvo_usb_start_hnp(struct otg_transceiver *dev)
+static int tahvo_usb_start_hnp(struct otg_transceiver *otg)
{
+ struct tahvo_usb *tu = container_of(otg, struct tahvo_usb, otg);
+
+ dev_dbg(&tu->pt_dev->dev, "start_hnp\n");
#ifdef CONFIG_USB_OTG
/* REVISIT: Add this for OTG */
#endif
{
struct tahvo_usb *tu = container_of(otg, struct tahvo_usb, otg);
- if (!otg)
+ dev_dbg(&tu->pt_dev->dev, "set_host %p\n", host);
+
+ if (otg == NULL)
return -ENODEV;
#if defined(CONFIG_USB_OTG) || !defined(CONFIG_USB_GADGET_OMAP)
- down(&tu->serialize);
+ mutex_lock(&tu->serialize);
- if (!host) {
+ if (host == NULL) {
if (TAHVO_MODE(tu) == TAHVO_MODE_HOST)
tahvo_usb_power_off(tu);
- tu->otg.host = 0;
- up(&tu->serialize);
+ tu->otg.host = NULL;
+ mutex_unlock(&tu->serialize);
return 0;
}
+ OTG_SYSCON_1_REG &= ~(OTG_IDLE_EN | HST_IDLE_EN | DEV_IDLE_EN);
+
if (TAHVO_MODE(tu) == TAHVO_MODE_HOST) {
- tu->otg.host = 0;
+ tu->otg.host = NULL;
tahvo_usb_become_host(tu);
} else
- ohci_omap_host_enable(host, 0);
+ host_suspend(tu);
tu->otg.host = host;
- up(&tu->serialize);
+ mutex_unlock(&tu->serialize);
#else
/* No host mode configured, so do not allow host controlled to be set */
return -EINVAL;
{
struct tahvo_usb *tu = container_of(otg, struct tahvo_usb, otg);
+ dev_dbg(&tu->pt_dev->dev, "set_peripheral %p\n", gadget);
+
if (!otg)
return -ENODEV;
#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_GADGET_OMAP)
- down(&tu->serialize);
+ mutex_lock(&tu->serialize);
if (!gadget) {
if (TAHVO_MODE(tu) == TAHVO_MODE_PERIPHERAL)
tahvo_usb_power_off(tu);
- tu->otg.gadget = 0;
- up(&tu->serialize);
+ tu->otg.gadget = NULL;
+ mutex_unlock(&tu->serialize);
return 0;
}
if (TAHVO_MODE(tu) == TAHVO_MODE_PERIPHERAL)
tahvo_usb_become_peripheral(tu);
- up(&tu->serialize);
+ mutex_unlock(&tu->serialize);
#else
/* No gadget mode configured, so do not allow host controlled to be set */
return -EINVAL;
return 0;
}
-static void tahvo_usb_irq_work(void *data)
+static void tahvo_usb_irq_work(struct work_struct *work)
{
- struct tahvo_usb *tu = (struct tahvo_usb *)data;
+ struct tahvo_usb *tu = container_of(work, struct tahvo_usb, irq_work);
- down(&tu->serialize);
+ mutex_lock(&tu->serialize);
check_vbus_state(tu);
- up(&tu->serialize);
+ mutex_unlock(&tu->serialize);
}
static void tahvo_usb_vbus_interrupt(unsigned long arg)
}
#ifdef CONFIG_USB_OTG
-static ssize_t otg_mode_show(struct device *device, char *buf)
+static ssize_t otg_mode_show(struct device *device,
+ struct device_attribute *attr, char *buf)
{
struct tahvo_usb *tu = (struct tahvo_usb*) device->driver_data;
switch (tu->tahvo_mode) {
return sprintf(buf, "unknown\n");
}
-static ssize_t otg_mode_store(struct device *device, const char *buf, size_t count)
+static ssize_t otg_mode_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct tahvo_usb *tu = (struct tahvo_usb*) device->driver_data;
int r;
r = strlen(buf);
- down(&tu->serialize);
+ mutex_lock(&tu->serialize);
if (strncmp(buf, "host", 4) == 0) {
if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
tahvo_usb_stop_peripheral(tu);
} else
r = -EINVAL;
- up(&tu->serialize);
+ mutex_unlock(&tu->serialize);
return r;
}
struct tahvo_usb *tu;
int ret;
+ dev_dbg(dev, "probe\n");
+
/* Create driver data */
tu = kmalloc(sizeof(*tu), GFP_KERNEL);
if (!tu)
#endif
#endif
- INIT_WORK(&tu->irq_work, tahvo_usb_irq_work, tu);
- init_MUTEX(&tu->serialize);
+ INIT_WORK(&tu->irq_work, tahvo_usb_irq_work);
+ mutex_init(&tu->serialize);
/* Set initial state, so that we generate kevents only on
* state changes */
}
/* Attributes */
- device_create_file(dev, &dev_attr_vbus_state);
+ ret = device_create_file(dev, &dev_attr_vbus_state);
#ifdef CONFIG_USB_OTG
- device_create_file(dev, &dev_attr_otg_mode);
+ ret |= device_create_file(dev, &dev_attr_otg_mode);
#endif
+ if (ret)
+ printk(KERN_ERR "attribute creation failed: %d\n", ret);
/* Create OTG interface */
tahvo_usb_power_off(tu);
static int tahvo_usb_remove(struct device *dev)
{
+ dev_dbg(dev, "remove\n");
+
tahvo_free_irq(TAHVO_INT_VBUSON);
flush_scheduled_work();
otg_set_transceiver(0);