]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/cbus/tahvo-usb.c
Merge ../linux-2.6
[linux-2.6-omap-h63xx.git] / drivers / cbus / tahvo-usb.c
index a7d36797cb3fca934fa46f70cf41623893792d3e..cfab6c5c3a4ad147c516e2efa4a0ff69346e5be9 100644 (file)
@@ -3,7 +3,11 @@
  *
  * 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
@@ -104,13 +105,13 @@ static struct platform_device tahvo_usb_device;
  * 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;
@@ -130,14 +131,12 @@ static irqreturn_t omap_otg_irq(int irq, void *arg, struct pt_regs *regs)
        } 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;
@@ -155,6 +154,8 @@ static int omap_otg_init(void)
                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
@@ -187,14 +188,15 @@ static int omap_otg_probe(struct device *dev)
        }
 
        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;
 }
 
@@ -212,7 +214,8 @@ struct device_driver omap_otg_driver = {
  * 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);
@@ -221,6 +224,47 @@ static DEVICE_ATTR(vbus_state, 0444, vbus_state_show, NULL);
 
 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;
@@ -242,50 +286,47 @@ static void check_vbus_state(struct tahvo_usb *tu)
                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 |
@@ -297,8 +338,7 @@ static void tahvo_usb_become_host(struct tahvo_usb *tu)
 
 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;
 }
 
@@ -308,7 +348,7 @@ static void tahvo_usb_become_peripheral(struct tahvo_usb *tu)
         * 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 */
@@ -329,24 +369,29 @@ static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu)
 
 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);
@@ -356,6 +401,10 @@ static void tahvo_usb_power_off(struct tahvo_usb *tu)
 
 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? */
        }
@@ -364,8 +413,11 @@ static int tahvo_usb_set_power(struct otg_transceiver *dev, unsigned mA)
 
 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;
@@ -381,6 +433,8 @@ static int tahvo_usb_start_srp(struct otg_transceiver *dev)
        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;
 
@@ -393,13 +447,14 @@ static int tahvo_usb_start_srp(struct otg_transceiver *dev)
        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
@@ -410,30 +465,34 @@ static int tahvo_usb_set_host(struct otg_transceiver *otg, struct usb_bus *host)
 {
        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;
@@ -446,18 +505,20 @@ static int tahvo_usb_set_peripheral(struct otg_transceiver *otg, struct usb_gadg
 {
        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;
        }
 
@@ -465,7 +526,7 @@ static int tahvo_usb_set_peripheral(struct otg_transceiver *otg, struct usb_gadg
        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;
@@ -474,13 +535,13 @@ static int tahvo_usb_set_peripheral(struct otg_transceiver *otg, struct usb_gadg
        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)
@@ -494,7 +555,8 @@ 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) {
@@ -506,13 +568,15 @@ static ssize_t otg_mode_show(struct device *device, char *buf)
        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);
@@ -538,7 +602,7 @@ static ssize_t otg_mode_store(struct device *device, const char *buf, size_t cou
        } else
                r = -EINVAL;
 
-       up(&tu->serialize);
+       mutex_unlock(&tu->serialize);
        return r;
 }
 
@@ -550,6 +614,8 @@ static int tahvo_usb_probe(struct device *dev)
        struct tahvo_usb *tu;
        int ret;
 
+       dev_dbg(dev, "probe\n");
+
        /* Create driver data */
        tu = kmalloc(sizeof(*tu), GFP_KERNEL);
        if (!tu)
@@ -565,8 +631,8 @@ static int tahvo_usb_probe(struct device *dev)
 #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 */
@@ -582,10 +648,12 @@ static int tahvo_usb_probe(struct device *dev)
        }
 
        /* 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);
@@ -616,6 +684,8 @@ static int tahvo_usb_probe(struct device *dev)
 
 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);