linux/drivers/usb/chipidea/usbmisc_imx6q.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Freescale Semiconductor, Inc.
   3 *
   4 * The code contained herein is licensed under the GNU General Public
   5 * License. You may obtain a copy of the GNU General Public License
   6 * Version 2 or later at the following locations:
   7 *
   8 * http://www.opensource.org/licenses/gpl-license.html
   9 * http://www.gnu.org/copyleft/gpl.html
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/of_platform.h>
  14#include <linux/clk.h>
  15#include <linux/err.h>
  16#include <linux/io.h>
  17
  18#include "ci13xxx_imx.h"
  19
  20#define USB_DEV_MAX 4
  21
  22#define BM_OVER_CUR_DIS         BIT(7)
  23
  24struct imx6q_usbmisc {
  25        void __iomem *base;
  26        spinlock_t lock;
  27        struct clk *clk;
  28        struct usbmisc_usb_device usbdev[USB_DEV_MAX];
  29};
  30
  31static struct imx6q_usbmisc *usbmisc;
  32
  33static struct usbmisc_usb_device *get_usbdev(struct device *dev)
  34{
  35        int i, ret;
  36
  37        for (i = 0; i < USB_DEV_MAX; i++) {
  38                if (usbmisc->usbdev[i].dev == dev)
  39                        return &usbmisc->usbdev[i];
  40                else if (!usbmisc->usbdev[i].dev)
  41                        break;
  42        }
  43
  44        if (i >= USB_DEV_MAX)
  45                return ERR_PTR(-EBUSY);
  46
  47        ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]);
  48        if (ret)
  49                return ERR_PTR(ret);
  50
  51        return &usbmisc->usbdev[i];
  52}
  53
  54static int usbmisc_imx6q_init(struct device *dev)
  55{
  56
  57        struct usbmisc_usb_device *usbdev;
  58        unsigned long flags;
  59        u32 reg;
  60
  61        usbdev = get_usbdev(dev);
  62        if (IS_ERR(usbdev))
  63                return PTR_ERR(usbdev);
  64
  65        if (usbdev->disable_oc) {
  66                spin_lock_irqsave(&usbmisc->lock, flags);
  67                reg = readl(usbmisc->base + usbdev->index * 4);
  68                writel(reg | BM_OVER_CUR_DIS,
  69                        usbmisc->base + usbdev->index * 4);
  70                spin_unlock_irqrestore(&usbmisc->lock, flags);
  71        }
  72
  73        return 0;
  74}
  75
  76static const struct usbmisc_ops imx6q_usbmisc_ops = {
  77        .init = usbmisc_imx6q_init,
  78};
  79
  80static const struct of_device_id usbmisc_imx6q_dt_ids[] = {
  81        { .compatible = "fsl,imx6q-usbmisc"},
  82        { /* sentinel */ }
  83};
  84
  85static int usbmisc_imx6q_probe(struct platform_device *pdev)
  86{
  87        struct resource *res;
  88        struct imx6q_usbmisc *data;
  89        int ret;
  90
  91        if (usbmisc)
  92                return -EBUSY;
  93
  94        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
  95        if (!data)
  96                return -ENOMEM;
  97
  98        spin_lock_init(&data->lock);
  99
 100        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 101        data->base = devm_ioremap_resource(&pdev->dev, res);
 102        if (IS_ERR(data->base))
 103                return PTR_ERR(data->base);
 104
 105        data->clk = devm_clk_get(&pdev->dev, NULL);
 106        if (IS_ERR(data->clk)) {
 107                dev_err(&pdev->dev,
 108                        "failed to get clock, err=%ld\n", PTR_ERR(data->clk));
 109                return PTR_ERR(data->clk);
 110        }
 111
 112        ret = clk_prepare_enable(data->clk);
 113        if (ret) {
 114                dev_err(&pdev->dev,
 115                        "clk_prepare_enable failed, err=%d\n", ret);
 116                return ret;
 117        }
 118
 119        ret = usbmisc_set_ops(&imx6q_usbmisc_ops);
 120        if (ret) {
 121                clk_disable_unprepare(data->clk);
 122                return ret;
 123        }
 124
 125        usbmisc = data;
 126
 127        return 0;
 128}
 129
 130static int usbmisc_imx6q_remove(struct platform_device *pdev)
 131{
 132        usbmisc_unset_ops(&imx6q_usbmisc_ops);
 133        clk_disable_unprepare(usbmisc->clk);
 134        return 0;
 135}
 136
 137static struct platform_driver usbmisc_imx6q_driver = {
 138        .probe = usbmisc_imx6q_probe,
 139        .remove = usbmisc_imx6q_remove,
 140        .driver = {
 141                .name = "usbmisc_imx6q",
 142                .owner = THIS_MODULE,
 143                .of_match_table = usbmisc_imx6q_dt_ids,
 144         },
 145};
 146
 147int __init usbmisc_imx6q_drv_init(void)
 148{
 149        return platform_driver_register(&usbmisc_imx6q_driver);
 150}
 151subsys_initcall(usbmisc_imx6q_drv_init);
 152
 153void __exit usbmisc_imx6q_drv_exit(void)
 154{
 155        platform_driver_unregister(&usbmisc_imx6q_driver);
 156}
 157module_exit(usbmisc_imx6q_drv_exit);
 158
 159MODULE_ALIAS("platform:usbmisc-imx6q");
 160MODULE_LICENSE("GPL v2");
 161MODULE_DESCRIPTION("driver for imx6q usb non-core registers");
 162MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
 163