linux/drivers/usb/host/ohci-jz4740.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
   3 *
   4 *  This program is free software; you can redistribute it and/or modify it
   5 *  under  the terms of the GNU General  Public License as published by the
   6 *  Free Software Foundation;  either version 2 of the License, or (at your
   7 *  option) any later version.
   8 *
   9 *  You should have received a copy of the  GNU General Public License along
  10 *  with this program; if not, write  to the Free Software Foundation, Inc.,
  11 *  675 Mass Ave, Cambridge, MA 02139, USA.
  12 *
  13 */
  14
  15#include <linux/platform_device.h>
  16#include <linux/clk.h>
  17#include <linux/regulator/consumer.h>
  18
  19struct jz4740_ohci_hcd {
  20        struct ohci_hcd ohci_hcd;
  21
  22        struct regulator *vbus;
  23        bool vbus_enabled;
  24        struct clk *clk;
  25};
  26
  27static inline struct jz4740_ohci_hcd *hcd_to_jz4740_hcd(struct usb_hcd *hcd)
  28{
  29        return (struct jz4740_ohci_hcd *)(hcd->hcd_priv);
  30}
  31
  32static inline struct usb_hcd *jz4740_hcd_to_hcd(struct jz4740_ohci_hcd *jz4740_ohci)
  33{
  34        return container_of((void *)jz4740_ohci, struct usb_hcd, hcd_priv);
  35}
  36
  37static int ohci_jz4740_start(struct usb_hcd *hcd)
  38{
  39        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
  40        int     ret;
  41
  42        ret = ohci_init(ohci);
  43        if (ret < 0)
  44                return ret;
  45
  46        ohci->num_ports = 1;
  47
  48        ret = ohci_run(ohci);
  49        if (ret < 0) {
  50                dev_err(hcd->self.controller, "Can not start %s",
  51                        hcd->self.bus_name);
  52                ohci_stop(hcd);
  53                return ret;
  54        }
  55        return 0;
  56}
  57
  58static int ohci_jz4740_set_vbus_power(struct jz4740_ohci_hcd *jz4740_ohci,
  59        bool enabled)
  60{
  61        int ret = 0;
  62
  63        if (!jz4740_ohci->vbus)
  64                return 0;
  65
  66        if (enabled && !jz4740_ohci->vbus_enabled) {
  67                ret = regulator_enable(jz4740_ohci->vbus);
  68                if (ret)
  69                        dev_err(jz4740_hcd_to_hcd(jz4740_ohci)->self.controller,
  70                                "Could not power vbus\n");
  71        } else if (!enabled && jz4740_ohci->vbus_enabled) {
  72                ret = regulator_disable(jz4740_ohci->vbus);
  73        }
  74
  75        if (ret == 0)
  76                jz4740_ohci->vbus_enabled = enabled;
  77
  78        return ret;
  79}
  80
  81static int ohci_jz4740_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
  82        u16 wIndex, char *buf, u16 wLength)
  83{
  84        struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
  85        int ret;
  86
  87        switch (typeReq) {
  88        case SetHubFeature:
  89                if (wValue == USB_PORT_FEAT_POWER)
  90                        ret = ohci_jz4740_set_vbus_power(jz4740_ohci, true);
  91                break;
  92        case ClearHubFeature:
  93                if (wValue == USB_PORT_FEAT_POWER)
  94                        ret = ohci_jz4740_set_vbus_power(jz4740_ohci, false);
  95                break;
  96        }
  97
  98        if (ret)
  99                return ret;
 100
 101        return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
 102}
 103
 104
 105static const struct hc_driver ohci_jz4740_hc_driver = {
 106        .description =          hcd_name,
 107        .product_desc =         "JZ4740 OHCI",
 108        .hcd_priv_size =        sizeof(struct jz4740_ohci_hcd),
 109
 110        /*
 111         * generic hardware linkage
 112         */
 113        .irq =                  ohci_irq,
 114        .flags =                HCD_USB11 | HCD_MEMORY,
 115
 116        /*
 117         * basic lifecycle operations
 118         */
 119        .start =                ohci_jz4740_start,
 120        .stop =                 ohci_stop,
 121        .shutdown =             ohci_shutdown,
 122
 123        /*
 124         * managing i/o requests and associated device resources
 125         */
 126        .urb_enqueue =          ohci_urb_enqueue,
 127        .urb_dequeue =          ohci_urb_dequeue,
 128        .endpoint_disable =     ohci_endpoint_disable,
 129
 130        /*
 131         * scheduling support
 132         */
 133        .get_frame_number =     ohci_get_frame,
 134
 135        /*
 136         * root hub support
 137         */
 138        .hub_status_data =      ohci_hub_status_data,
 139        .hub_control =          ohci_jz4740_hub_control,
 140#ifdef  CONFIG_PM
 141        .bus_suspend =          ohci_bus_suspend,
 142        .bus_resume =           ohci_bus_resume,
 143#endif
 144        .start_port_reset =     ohci_start_port_reset,
 145};
 146
 147
 148static __devinit int jz4740_ohci_probe(struct platform_device *pdev)
 149{
 150        int ret;
 151        struct usb_hcd *hcd;
 152        struct jz4740_ohci_hcd *jz4740_ohci;
 153        struct resource *res;
 154        int irq;
 155
 156        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 157
 158        if (!res) {
 159                dev_err(&pdev->dev, "Failed to get platform resource\n");
 160                return -ENOENT;
 161        }
 162
 163        irq = platform_get_irq(pdev, 0);
 164        if (irq < 0) {
 165                dev_err(&pdev->dev, "Failed to get platform irq\n");
 166                return irq;
 167        }
 168
 169        hcd = usb_create_hcd(&ohci_jz4740_hc_driver, &pdev->dev, "jz4740");
 170        if (!hcd) {
 171                dev_err(&pdev->dev, "Failed to create hcd.\n");
 172                return -ENOMEM;
 173        }
 174
 175        jz4740_ohci = hcd_to_jz4740_hcd(hcd);
 176
 177        res = request_mem_region(res->start, resource_size(res), hcd_name);
 178        if (!res) {
 179                dev_err(&pdev->dev, "Failed to request mem region.\n");
 180                ret = -EBUSY;
 181                goto err_free;
 182        }
 183
 184        hcd->rsrc_start = res->start;
 185        hcd->rsrc_len = resource_size(res);
 186        hcd->regs = ioremap(res->start, resource_size(res));
 187
 188        if (!hcd->regs) {
 189                dev_err(&pdev->dev, "Failed to ioremap registers.\n");
 190                ret = -EBUSY;
 191                goto err_release_mem;
 192        }
 193
 194        jz4740_ohci->clk = clk_get(&pdev->dev, "uhc");
 195        if (IS_ERR(jz4740_ohci->clk)) {
 196                ret = PTR_ERR(jz4740_ohci->clk);
 197                dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
 198                goto err_iounmap;
 199        }
 200
 201        jz4740_ohci->vbus = regulator_get(&pdev->dev, "vbus");
 202        if (IS_ERR(jz4740_ohci->vbus))
 203                jz4740_ohci->vbus = NULL;
 204
 205
 206        clk_set_rate(jz4740_ohci->clk, 48000000);
 207        clk_enable(jz4740_ohci->clk);
 208        if (jz4740_ohci->vbus)
 209                ohci_jz4740_set_vbus_power(jz4740_ohci, true);
 210
 211        platform_set_drvdata(pdev, hcd);
 212
 213        ohci_hcd_init(hcd_to_ohci(hcd));
 214
 215        ret = usb_add_hcd(hcd, irq, 0);
 216        if (ret) {
 217                dev_err(&pdev->dev, "Failed to add hcd: %d\n", ret);
 218                goto err_disable;
 219        }
 220
 221        return 0;
 222
 223err_disable:
 224        platform_set_drvdata(pdev, NULL);
 225        if (jz4740_ohci->vbus) {
 226                regulator_disable(jz4740_ohci->vbus);
 227                regulator_put(jz4740_ohci->vbus);
 228        }
 229        clk_disable(jz4740_ohci->clk);
 230
 231        clk_put(jz4740_ohci->clk);
 232err_iounmap:
 233        iounmap(hcd->regs);
 234err_release_mem:
 235        release_mem_region(res->start, resource_size(res));
 236err_free:
 237        usb_put_hcd(hcd);
 238
 239        return ret;
 240}
 241
 242static __devexit int jz4740_ohci_remove(struct platform_device *pdev)
 243{
 244        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 245        struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
 246
 247        usb_remove_hcd(hcd);
 248
 249        platform_set_drvdata(pdev, NULL);
 250
 251        if (jz4740_ohci->vbus) {
 252                regulator_disable(jz4740_ohci->vbus);
 253                regulator_put(jz4740_ohci->vbus);
 254        }
 255
 256        clk_disable(jz4740_ohci->clk);
 257        clk_put(jz4740_ohci->clk);
 258
 259        iounmap(hcd->regs);
 260        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 261
 262        usb_put_hcd(hcd);
 263
 264        return 0;
 265}
 266
 267static struct platform_driver ohci_hcd_jz4740_driver = {
 268        .probe = jz4740_ohci_probe,
 269        .remove = __devexit_p(jz4740_ohci_remove),
 270        .driver = {
 271                .name = "jz4740-ohci",
 272                .owner = THIS_MODULE,
 273        },
 274};
 275
 276MODULE_ALIAS("platform:jz4740-ohci");
 277