linux/drivers/usb/chipidea/usbmisc_imx.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/err.h>
  15#include <linux/io.h>
  16#include <linux/delay.h>
  17
  18#include "ci_hdrc_imx.h"
  19
  20#define MX25_USB_PHY_CTRL_OFFSET        0x08
  21#define MX25_BM_EXTERNAL_VBUS_DIVIDER   BIT(23)
  22
  23#define MX25_EHCI_INTERFACE_SINGLE_UNI  (2 << 0)
  24#define MX25_EHCI_INTERFACE_DIFF_UNI    (0 << 0)
  25#define MX25_EHCI_INTERFACE_MASK        (0xf)
  26
  27#define MX25_OTG_SIC_SHIFT              29
  28#define MX25_OTG_SIC_MASK               (0x3 << MX25_OTG_SIC_SHIFT)
  29#define MX25_OTG_PM_BIT                 BIT(24)
  30#define MX25_OTG_PP_BIT                 BIT(11)
  31#define MX25_OTG_OCPOL_BIT              BIT(3)
  32
  33#define MX25_H1_SIC_SHIFT               21
  34#define MX25_H1_SIC_MASK                (0x3 << MX25_H1_SIC_SHIFT)
  35#define MX25_H1_PP_BIT                  BIT(18)
  36#define MX25_H1_PM_BIT                  BIT(16)
  37#define MX25_H1_IPPUE_UP_BIT            BIT(7)
  38#define MX25_H1_IPPUE_DOWN_BIT          BIT(6)
  39#define MX25_H1_TLL_BIT                 BIT(5)
  40#define MX25_H1_USBTE_BIT               BIT(4)
  41#define MX25_H1_OCPOL_BIT               BIT(2)
  42
  43#define MX27_H1_PM_BIT                  BIT(8)
  44#define MX27_H2_PM_BIT                  BIT(16)
  45#define MX27_OTG_PM_BIT                 BIT(24)
  46
  47#define MX53_USB_OTG_PHY_CTRL_0_OFFSET  0x08
  48#define MX53_USB_OTG_PHY_CTRL_1_OFFSET  0x0c
  49#define MX53_USB_UH2_CTRL_OFFSET        0x14
  50#define MX53_USB_UH3_CTRL_OFFSET        0x18
  51#define MX53_BM_OVER_CUR_DIS_H1         BIT(5)
  52#define MX53_BM_OVER_CUR_DIS_OTG        BIT(8)
  53#define MX53_BM_OVER_CUR_DIS_UHx        BIT(30)
  54#define MX53_USB_PHYCTRL1_PLLDIV_MASK   0x3
  55#define MX53_USB_PLL_DIV_24_MHZ         0x01
  56
  57#define MX6_BM_OVER_CUR_DIS             BIT(7)
  58#define MX6_BM_WAKEUP_ENABLE            BIT(10)
  59#define MX6_BM_ID_WAKEUP                BIT(16)
  60#define MX6_BM_VBUS_WAKEUP              BIT(17)
  61#define MX6SX_BM_DPDM_WAKEUP_EN         BIT(29)
  62#define MX6_BM_WAKEUP_INTR              BIT(31)
  63#define MX6_USB_OTG1_PHY_CTRL           0x18
  64/* For imx6dql, it is host-only controller, for later imx6, it is otg's */
  65#define MX6_USB_OTG2_PHY_CTRL           0x1c
  66#define MX6SX_USB_VBUS_WAKEUP_SOURCE(v) (v << 8)
  67#define MX6SX_USB_VBUS_WAKEUP_SOURCE_VBUS       MX6SX_USB_VBUS_WAKEUP_SOURCE(0)
  68#define MX6SX_USB_VBUS_WAKEUP_SOURCE_AVALID     MX6SX_USB_VBUS_WAKEUP_SOURCE(1)
  69#define MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID     MX6SX_USB_VBUS_WAKEUP_SOURCE(2)
  70#define MX6SX_USB_VBUS_WAKEUP_SOURCE_SESS_END   MX6SX_USB_VBUS_WAKEUP_SOURCE(3)
  71
  72#define VF610_OVER_CUR_DIS              BIT(7)
  73
  74struct usbmisc_ops {
  75        /* It's called once when probe a usb device */
  76        int (*init)(struct imx_usbmisc_data *data);
  77        /* It's called once after adding a usb device */
  78        int (*post)(struct imx_usbmisc_data *data);
  79        /* It's called when we need to enable/disable usb wakeup */
  80        int (*set_wakeup)(struct imx_usbmisc_data *data, bool enabled);
  81};
  82
  83struct imx_usbmisc {
  84        void __iomem *base;
  85        spinlock_t lock;
  86        const struct usbmisc_ops *ops;
  87};
  88
  89static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
  90{
  91        struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
  92        unsigned long flags;
  93        u32 val = 0;
  94
  95        if (data->index > 1)
  96                return -EINVAL;
  97
  98        spin_lock_irqsave(&usbmisc->lock, flags);
  99        switch (data->index) {
 100        case 0:
 101                val = readl(usbmisc->base);
 102                val &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PP_BIT);
 103                val |= (MX25_EHCI_INTERFACE_DIFF_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT;
 104                val |= (MX25_OTG_PM_BIT | MX25_OTG_OCPOL_BIT);
 105                writel(val, usbmisc->base);
 106                break;
 107        case 1:
 108                val = readl(usbmisc->base);
 109                val &= ~(MX25_H1_SIC_MASK | MX25_H1_PP_BIT |  MX25_H1_IPPUE_UP_BIT);
 110                val |= (MX25_EHCI_INTERFACE_SINGLE_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT;
 111                val |= (MX25_H1_PM_BIT | MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT |
 112                        MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT);
 113
 114                writel(val, usbmisc->base);
 115
 116                break;
 117        }
 118        spin_unlock_irqrestore(&usbmisc->lock, flags);
 119
 120        return 0;
 121}
 122
 123static int usbmisc_imx25_post(struct imx_usbmisc_data *data)
 124{
 125        struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
 126        void __iomem *reg;
 127        unsigned long flags;
 128        u32 val;
 129
 130        if (data->index > 2)
 131                return -EINVAL;
 132
 133        if (data->evdo) {
 134                spin_lock_irqsave(&usbmisc->lock, flags);
 135                reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
 136                val = readl(reg);
 137                writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg);
 138                spin_unlock_irqrestore(&usbmisc->lock, flags);
 139                usleep_range(5000, 10000); /* needed to stabilize voltage */
 140        }
 141
 142        return 0;
 143}
 144
 145static int usbmisc_imx27_init(struct imx_usbmisc_data *data)
 146{
 147        struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
 148        unsigned long flags;
 149        u32 val;
 150
 151        switch (data->index) {
 152        case 0:
 153                val = MX27_OTG_PM_BIT;
 154                break;
 155        case 1:
 156                val = MX27_H1_PM_BIT;
 157                break;
 158        case 2:
 159                val = MX27_H2_PM_BIT;
 160                break;
 161        default:
 162                return -EINVAL;
 163        }
 164
 165        spin_lock_irqsave(&usbmisc->lock, flags);
 166        if (data->disable_oc)
 167                val = readl(usbmisc->base) | val;
 168        else
 169                val = readl(usbmisc->base) & ~val;
 170        writel(val, usbmisc->base);
 171        spin_unlock_irqrestore(&usbmisc->lock, flags);
 172
 173        return 0;
 174}
 175
 176static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
 177{
 178        struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
 179        void __iomem *reg = NULL;
 180        unsigned long flags;
 181        u32 val = 0;
 182
 183        if (data->index > 3)
 184                return -EINVAL;
 185
 186        /* Select a 24 MHz reference clock for the PHY  */
 187        val = readl(usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
 188        val &= ~MX53_USB_PHYCTRL1_PLLDIV_MASK;
 189        val |= MX53_USB_PLL_DIV_24_MHZ;
 190        writel(val, usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
 191
 192        if (data->disable_oc) {
 193                spin_lock_irqsave(&usbmisc->lock, flags);
 194                switch (data->index) {
 195                case 0:
 196                        reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
 197                        val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG;
 198                        break;
 199                case 1:
 200                        reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
 201                        val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1;
 202                        break;
 203                case 2:
 204                        reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
 205                        val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
 206                        break;
 207                case 3:
 208                        reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
 209                        val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
 210                        break;
 211                }
 212                if (reg && val)
 213                        writel(val, reg);
 214                spin_unlock_irqrestore(&usbmisc->lock, flags);
 215        }
 216
 217        return 0;
 218}
 219
 220static int usbmisc_imx6q_set_wakeup
 221        (struct imx_usbmisc_data *data, bool enabled)
 222{
 223        struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
 224        unsigned long flags;
 225        u32 val;
 226        u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE |
 227                MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP);
 228        int ret = 0;
 229
 230        if (data->index > 3)
 231                return -EINVAL;
 232
 233        spin_lock_irqsave(&usbmisc->lock, flags);
 234        val = readl(usbmisc->base + data->index * 4);
 235        if (enabled) {
 236                val |= wakeup_setting;
 237                writel(val, usbmisc->base + data->index * 4);
 238        } else {
 239                if (val & MX6_BM_WAKEUP_INTR)
 240                        pr_debug("wakeup int at ci_hdrc.%d\n", data->index);
 241                val &= ~wakeup_setting;
 242                writel(val, usbmisc->base + data->index * 4);
 243        }
 244        spin_unlock_irqrestore(&usbmisc->lock, flags);
 245
 246        return ret;
 247}
 248
 249static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
 250{
 251        struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
 252        unsigned long flags;
 253        u32 reg;
 254
 255        if (data->index > 3)
 256                return -EINVAL;
 257
 258        if (data->disable_oc) {
 259                spin_lock_irqsave(&usbmisc->lock, flags);
 260                reg = readl(usbmisc->base + data->index * 4);
 261                writel(reg | MX6_BM_OVER_CUR_DIS,
 262                        usbmisc->base + data->index * 4);
 263                spin_unlock_irqrestore(&usbmisc->lock, flags);
 264        }
 265
 266        usbmisc_imx6q_set_wakeup(data, false);
 267
 268        return 0;
 269}
 270
 271static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data)
 272{
 273        void __iomem *reg = NULL;
 274        unsigned long flags;
 275        struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
 276        u32 val;
 277
 278        usbmisc_imx6q_init(data);
 279
 280        if (data->index == 0 || data->index == 1) {
 281                reg = usbmisc->base + MX6_USB_OTG1_PHY_CTRL + data->index * 4;
 282                spin_lock_irqsave(&usbmisc->lock, flags);
 283                /* Set vbus wakeup source as bvalid */
 284                val = readl(reg);
 285                writel(val | MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID, reg);
 286                /*
 287                 * Disable dp/dm wakeup in device mode when vbus is
 288                 * not there.
 289                 */
 290                val = readl(usbmisc->base + data->index * 4);
 291                writel(val & ~MX6SX_BM_DPDM_WAKEUP_EN,
 292                        usbmisc->base + data->index * 4);
 293                spin_unlock_irqrestore(&usbmisc->lock, flags);
 294        }
 295
 296        return 0;
 297}
 298
 299static int usbmisc_vf610_init(struct imx_usbmisc_data *data)
 300{
 301        struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
 302        u32 reg;
 303
 304        /*
 305         * Vybrid only has one misc register set, but in two different
 306         * areas. These is reflected in two instances of this driver.
 307         */
 308        if (data->index >= 1)
 309                return -EINVAL;
 310
 311        if (data->disable_oc) {
 312                reg = readl(usbmisc->base);
 313                writel(reg | VF610_OVER_CUR_DIS, usbmisc->base);
 314        }
 315
 316        return 0;
 317}
 318
 319static const struct usbmisc_ops imx25_usbmisc_ops = {
 320        .init = usbmisc_imx25_init,
 321        .post = usbmisc_imx25_post,
 322};
 323
 324static const struct usbmisc_ops imx27_usbmisc_ops = {
 325        .init = usbmisc_imx27_init,
 326};
 327
 328static const struct usbmisc_ops imx53_usbmisc_ops = {
 329        .init = usbmisc_imx53_init,
 330};
 331
 332static const struct usbmisc_ops imx6q_usbmisc_ops = {
 333        .set_wakeup = usbmisc_imx6q_set_wakeup,
 334        .init = usbmisc_imx6q_init,
 335};
 336
 337static const struct usbmisc_ops vf610_usbmisc_ops = {
 338        .init = usbmisc_vf610_init,
 339};
 340
 341static const struct usbmisc_ops imx6sx_usbmisc_ops = {
 342        .set_wakeup = usbmisc_imx6q_set_wakeup,
 343        .init = usbmisc_imx6sx_init,
 344};
 345
 346int imx_usbmisc_init(struct imx_usbmisc_data *data)
 347{
 348        struct imx_usbmisc *usbmisc;
 349
 350        if (!data)
 351                return 0;
 352
 353        usbmisc = dev_get_drvdata(data->dev);
 354        if (!usbmisc->ops->init)
 355                return 0;
 356        return usbmisc->ops->init(data);
 357}
 358EXPORT_SYMBOL_GPL(imx_usbmisc_init);
 359
 360int imx_usbmisc_init_post(struct imx_usbmisc_data *data)
 361{
 362        struct imx_usbmisc *usbmisc;
 363
 364        if (!data)
 365                return 0;
 366
 367        usbmisc = dev_get_drvdata(data->dev);
 368        if (!usbmisc->ops->post)
 369                return 0;
 370        return usbmisc->ops->post(data);
 371}
 372EXPORT_SYMBOL_GPL(imx_usbmisc_init_post);
 373
 374int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled)
 375{
 376        struct imx_usbmisc *usbmisc;
 377
 378        if (!data)
 379                return 0;
 380
 381        usbmisc = dev_get_drvdata(data->dev);
 382        if (!usbmisc->ops->set_wakeup)
 383                return 0;
 384        return usbmisc->ops->set_wakeup(data, enabled);
 385}
 386EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup);
 387
 388static const struct of_device_id usbmisc_imx_dt_ids[] = {
 389        {
 390                .compatible = "fsl,imx25-usbmisc",
 391                .data = &imx25_usbmisc_ops,
 392        },
 393        {
 394                .compatible = "fsl,imx35-usbmisc",
 395                .data = &imx25_usbmisc_ops,
 396        },
 397        {
 398                .compatible = "fsl,imx27-usbmisc",
 399                .data = &imx27_usbmisc_ops,
 400        },
 401        {
 402                .compatible = "fsl,imx51-usbmisc",
 403                .data = &imx53_usbmisc_ops,
 404        },
 405        {
 406                .compatible = "fsl,imx53-usbmisc",
 407                .data = &imx53_usbmisc_ops,
 408        },
 409        {
 410                .compatible = "fsl,imx6q-usbmisc",
 411                .data = &imx6q_usbmisc_ops,
 412        },
 413        {
 414                .compatible = "fsl,vf610-usbmisc",
 415                .data = &vf610_usbmisc_ops,
 416        },
 417        {
 418                .compatible = "fsl,imx6sx-usbmisc",
 419                .data = &imx6sx_usbmisc_ops,
 420        },
 421        { /* sentinel */ }
 422};
 423MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
 424
 425static int usbmisc_imx_probe(struct platform_device *pdev)
 426{
 427        struct resource *res;
 428        struct imx_usbmisc *data;
 429        struct of_device_id *tmp_dev;
 430
 431        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 432        if (!data)
 433                return -ENOMEM;
 434
 435        spin_lock_init(&data->lock);
 436
 437        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 438        data->base = devm_ioremap_resource(&pdev->dev, res);
 439        if (IS_ERR(data->base))
 440                return PTR_ERR(data->base);
 441
 442        tmp_dev = (struct of_device_id *)
 443                of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
 444        data->ops = (const struct usbmisc_ops *)tmp_dev->data;
 445        platform_set_drvdata(pdev, data);
 446
 447        return 0;
 448}
 449
 450static int usbmisc_imx_remove(struct platform_device *pdev)
 451{
 452        return 0;
 453}
 454
 455static struct platform_driver usbmisc_imx_driver = {
 456        .probe = usbmisc_imx_probe,
 457        .remove = usbmisc_imx_remove,
 458        .driver = {
 459                .name = "usbmisc_imx",
 460                .of_match_table = usbmisc_imx_dt_ids,
 461         },
 462};
 463
 464module_platform_driver(usbmisc_imx_driver);
 465
 466MODULE_ALIAS("platform:usbmisc-imx");
 467MODULE_LICENSE("GPL v2");
 468MODULE_DESCRIPTION("driver for imx usb non-core registers");
 469MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
 470