linux/drivers/usb/host/fsl-mph-dr-of.c
<<
>>
Prefs
   1/*
   2 * Setup platform devices needed by the Freescale multi-port host
   3 * and/or dual-role USB controller modules based on the description
   4 * in flat device tree.
   5 *
   6 * This program is free software; you can redistribute  it and/or modify it
   7 * under  the terms of  the GNU General  Public License as published by the
   8 * Free Software Foundation;  either version 2 of the  License, or (at your
   9 * option) any later version.
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/platform_device.h>
  14#include <linux/fsl_devices.h>
  15#include <linux/err.h>
  16#include <linux/io.h>
  17#include <linux/of_platform.h>
  18#include <linux/clk.h>
  19#include <linux/module.h>
  20
  21struct fsl_usb2_dev_data {
  22        char *dr_mode;          /* controller mode */
  23        char *drivers[3];       /* drivers to instantiate for this mode */
  24        enum fsl_usb2_operating_modes op_mode;  /* operating mode */
  25};
  26
  27static struct fsl_usb2_dev_data dr_mode_data[] = {
  28        {
  29                .dr_mode = "host",
  30                .drivers = { "fsl-ehci", NULL, NULL, },
  31                .op_mode = FSL_USB2_DR_HOST,
  32        },
  33        {
  34                .dr_mode = "otg",
  35                .drivers = { "fsl-usb2-otg", "fsl-ehci", "fsl-usb2-udc", },
  36                .op_mode = FSL_USB2_DR_OTG,
  37        },
  38        {
  39                .dr_mode = "peripheral",
  40                .drivers = { "fsl-usb2-udc", NULL, NULL, },
  41                .op_mode = FSL_USB2_DR_DEVICE,
  42        },
  43};
  44
  45static struct fsl_usb2_dev_data *get_dr_mode_data(struct device_node *np)
  46{
  47        const unsigned char *prop;
  48        int i;
  49
  50        prop = of_get_property(np, "dr_mode", NULL);
  51        if (prop) {
  52                for (i = 0; i < ARRAY_SIZE(dr_mode_data); i++) {
  53                        if (!strcmp(prop, dr_mode_data[i].dr_mode))
  54                                return &dr_mode_data[i];
  55                }
  56        }
  57        pr_warn("%s: Invalid 'dr_mode' property, fallback to host mode\n",
  58                np->full_name);
  59        return &dr_mode_data[0]; /* mode not specified, use host */
  60}
  61
  62static enum fsl_usb2_phy_modes determine_usb_phy(const char *phy_type)
  63{
  64        if (!phy_type)
  65                return FSL_USB2_PHY_NONE;
  66        if (!strcasecmp(phy_type, "ulpi"))
  67                return FSL_USB2_PHY_ULPI;
  68        if (!strcasecmp(phy_type, "utmi"))
  69                return FSL_USB2_PHY_UTMI;
  70        if (!strcasecmp(phy_type, "utmi_wide"))
  71                return FSL_USB2_PHY_UTMI_WIDE;
  72        if (!strcasecmp(phy_type, "utmi_dual"))
  73                return FSL_USB2_PHY_UTMI_DUAL;
  74        if (!strcasecmp(phy_type, "serial"))
  75                return FSL_USB2_PHY_SERIAL;
  76
  77        return FSL_USB2_PHY_NONE;
  78}
  79
  80static struct platform_device *fsl_usb2_device_register(
  81                                        struct platform_device *ofdev,
  82                                        struct fsl_usb2_platform_data *pdata,
  83                                        const char *name, int id)
  84{
  85        struct platform_device *pdev;
  86        const struct resource *res = ofdev->resource;
  87        unsigned int num = ofdev->num_resources;
  88        int retval;
  89
  90        pdev = platform_device_alloc(name, id);
  91        if (!pdev) {
  92                retval = -ENOMEM;
  93                goto error;
  94        }
  95
  96        pdev->dev.parent = &ofdev->dev;
  97
  98        pdev->dev.coherent_dma_mask = ofdev->dev.coherent_dma_mask;
  99        *pdev->dev.dma_mask = *ofdev->dev.dma_mask;
 100
 101        retval = platform_device_add_data(pdev, pdata, sizeof(*pdata));
 102        if (retval)
 103                goto error;
 104
 105        if (num) {
 106                retval = platform_device_add_resources(pdev, res, num);
 107                if (retval)
 108                        goto error;
 109        }
 110
 111        retval = platform_device_add(pdev);
 112        if (retval)
 113                goto error;
 114
 115        return pdev;
 116
 117error:
 118        platform_device_put(pdev);
 119        return ERR_PTR(retval);
 120}
 121
 122static const struct of_device_id fsl_usb2_mph_dr_of_match[];
 123
 124static enum fsl_usb2_controller_ver usb_get_ver_info(struct device_node *np)
 125{
 126        enum fsl_usb2_controller_ver ver = FSL_USB_VER_NONE;
 127
 128        /*
 129         * returns 1 for usb controller version 1.6
 130         * returns 2 for usb controller version 2.2
 131         * returns 3 for usb controller version 2.4
 132         * returns 4 for usb controller version 2.5
 133         * returns 0 otherwise
 134         */
 135        if (of_device_is_compatible(np, "fsl-usb2-dr")) {
 136                if (of_device_is_compatible(np, "fsl-usb2-dr-v1.6"))
 137                        ver = FSL_USB_VER_1_6;
 138                else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.2"))
 139                        ver = FSL_USB_VER_2_2;
 140                else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.4"))
 141                        ver = FSL_USB_VER_2_4;
 142                else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.5"))
 143                        ver = FSL_USB_VER_2_5;
 144                else /* for previous controller versions */
 145                        ver = FSL_USB_VER_OLD;
 146
 147                if (ver > FSL_USB_VER_NONE)
 148                        return ver;
 149        }
 150
 151        if (of_device_is_compatible(np, "fsl,mpc5121-usb2-dr"))
 152                return FSL_USB_VER_OLD;
 153
 154        if (of_device_is_compatible(np, "fsl-usb2-mph")) {
 155                if (of_device_is_compatible(np, "fsl-usb2-mph-v1.6"))
 156                        ver = FSL_USB_VER_1_6;
 157                else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.2"))
 158                        ver = FSL_USB_VER_2_2;
 159                else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.4"))
 160                        ver = FSL_USB_VER_2_4;
 161                else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.5"))
 162                        ver = FSL_USB_VER_2_5;
 163                else /* for previous controller versions */
 164                        ver = FSL_USB_VER_OLD;
 165        }
 166
 167        return ver;
 168}
 169
 170static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev)
 171{
 172        struct device_node *np = ofdev->dev.of_node;
 173        struct platform_device *usb_dev;
 174        struct fsl_usb2_platform_data data, *pdata;
 175        struct fsl_usb2_dev_data *dev_data;
 176        const struct of_device_id *match;
 177        const unsigned char *prop;
 178        static unsigned int idx;
 179        int i;
 180
 181        if (!of_device_is_available(np))
 182                return -ENODEV;
 183
 184        match = of_match_device(fsl_usb2_mph_dr_of_match, &ofdev->dev);
 185        if (!match)
 186                return -ENODEV;
 187
 188        pdata = &data;
 189        if (match->data)
 190                memcpy(pdata, match->data, sizeof(data));
 191        else
 192                memset(pdata, 0, sizeof(data));
 193
 194        dev_data = get_dr_mode_data(np);
 195
 196        if (of_device_is_compatible(np, "fsl-usb2-mph")) {
 197                if (of_get_property(np, "port0", NULL))
 198                        pdata->port_enables |= FSL_USB2_PORT0_ENABLED;
 199
 200                if (of_get_property(np, "port1", NULL))
 201                        pdata->port_enables |= FSL_USB2_PORT1_ENABLED;
 202
 203                pdata->operating_mode = FSL_USB2_MPH_HOST;
 204        } else {
 205                if (of_get_property(np, "fsl,invert-drvvbus", NULL))
 206                        pdata->invert_drvvbus = 1;
 207
 208                if (of_get_property(np, "fsl,invert-pwr-fault", NULL))
 209                        pdata->invert_pwr_fault = 1;
 210
 211                /* setup mode selected in the device tree */
 212                pdata->operating_mode = dev_data->op_mode;
 213        }
 214
 215        prop = of_get_property(np, "phy_type", NULL);
 216        pdata->phy_mode = determine_usb_phy(prop);
 217        pdata->controller_ver = usb_get_ver_info(np);
 218
 219        /* Activate Erratum by reading property in device tree */
 220        if (of_get_property(np, "fsl,usb-erratum-a007792", NULL))
 221                pdata->has_fsl_erratum_a007792 = 1;
 222        else
 223                pdata->has_fsl_erratum_a007792 = 0;
 224        if (of_get_property(np, "fsl,usb-erratum-a005275", NULL))
 225                pdata->has_fsl_erratum_a005275 = 1;
 226        else
 227                pdata->has_fsl_erratum_a005275 = 0;
 228
 229        /*
 230         * Determine whether phy_clk_valid needs to be checked
 231         * by reading property in device tree
 232         */
 233        if (of_get_property(np, "phy-clk-valid", NULL))
 234                pdata->check_phy_clk_valid = 1;
 235        else
 236                pdata->check_phy_clk_valid = 0;
 237
 238        if (pdata->have_sysif_regs) {
 239                if (pdata->controller_ver == FSL_USB_VER_NONE) {
 240                        dev_warn(&ofdev->dev, "Could not get controller version\n");
 241                        return -ENODEV;
 242                }
 243        }
 244
 245        for (i = 0; i < ARRAY_SIZE(dev_data->drivers); i++) {
 246                if (!dev_data->drivers[i])
 247                        continue;
 248                usb_dev = fsl_usb2_device_register(ofdev, pdata,
 249                                        dev_data->drivers[i], idx);
 250                if (IS_ERR(usb_dev)) {
 251                        dev_err(&ofdev->dev, "Can't register usb device\n");
 252                        return PTR_ERR(usb_dev);
 253                }
 254        }
 255        idx++;
 256        return 0;
 257}
 258
 259static int __unregister_subdev(struct device *dev, void *d)
 260{
 261        platform_device_unregister(to_platform_device(dev));
 262        return 0;
 263}
 264
 265static int fsl_usb2_mph_dr_of_remove(struct platform_device *ofdev)
 266{
 267        device_for_each_child(&ofdev->dev, NULL, __unregister_subdev);
 268        return 0;
 269}
 270
 271#ifdef CONFIG_PPC_MPC512x
 272
 273#define USBGENCTRL              0x200           /* NOTE: big endian */
 274#define GC_WU_INT_CLR           (1 << 5)        /* Wakeup int clear */
 275#define GC_ULPI_SEL             (1 << 4)        /* ULPI i/f select (usb0 only)*/
 276#define GC_PPP                  (1 << 3)        /* Inv. Port Power Polarity */
 277#define GC_PFP                  (1 << 2)        /* Inv. Power Fault Polarity */
 278#define GC_WU_ULPI_EN           (1 << 1)        /* Wakeup on ULPI event */
 279#define GC_WU_IE                (1 << 1)        /* Wakeup interrupt enable */
 280
 281#define ISIPHYCTRL              0x204           /* NOTE: big endian */
 282#define PHYCTRL_PHYE            (1 << 4)        /* On-chip UTMI PHY enable */
 283#define PHYCTRL_BSENH           (1 << 3)        /* Bit Stuff Enable High */
 284#define PHYCTRL_BSEN            (1 << 2)        /* Bit Stuff Enable */
 285#define PHYCTRL_LSFE            (1 << 1)        /* Line State Filter Enable */
 286#define PHYCTRL_PXE             (1 << 0)        /* PHY oscillator enable */
 287
 288int fsl_usb2_mpc5121_init(struct platform_device *pdev)
 289{
 290        struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
 291        struct clk *clk;
 292        int err;
 293
 294        clk = devm_clk_get(pdev->dev.parent, "ipg");
 295        if (IS_ERR(clk)) {
 296                dev_err(&pdev->dev, "failed to get clk\n");
 297                return PTR_ERR(clk);
 298        }
 299        err = clk_prepare_enable(clk);
 300        if (err) {
 301                dev_err(&pdev->dev, "failed to enable clk\n");
 302                return err;
 303        }
 304        pdata->clk = clk;
 305
 306        if (pdata->phy_mode == FSL_USB2_PHY_UTMI_WIDE) {
 307                u32 reg = 0;
 308
 309                if (pdata->invert_drvvbus)
 310                        reg |= GC_PPP;
 311
 312                if (pdata->invert_pwr_fault)
 313                        reg |= GC_PFP;
 314
 315                out_be32(pdata->regs + ISIPHYCTRL, PHYCTRL_PHYE | PHYCTRL_PXE);
 316                out_be32(pdata->regs + USBGENCTRL, reg);
 317        }
 318        return 0;
 319}
 320
 321static void fsl_usb2_mpc5121_exit(struct platform_device *pdev)
 322{
 323        struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
 324
 325        pdata->regs = NULL;
 326
 327        if (pdata->clk)
 328                clk_disable_unprepare(pdata->clk);
 329}
 330
 331static struct fsl_usb2_platform_data fsl_usb2_mpc5121_pd = {
 332        .big_endian_desc = 1,
 333        .big_endian_mmio = 1,
 334        .es = 1,
 335        .have_sysif_regs = 0,
 336        .le_setup_buf = 1,
 337        .init = fsl_usb2_mpc5121_init,
 338        .exit = fsl_usb2_mpc5121_exit,
 339};
 340#endif /* CONFIG_PPC_MPC512x */
 341
 342static struct fsl_usb2_platform_data fsl_usb2_mpc8xxx_pd = {
 343        .have_sysif_regs = 1,
 344};
 345
 346static const struct of_device_id fsl_usb2_mph_dr_of_match[] = {
 347        { .compatible = "fsl-usb2-mph", .data = &fsl_usb2_mpc8xxx_pd, },
 348        { .compatible = "fsl-usb2-dr", .data = &fsl_usb2_mpc8xxx_pd, },
 349#ifdef CONFIG_PPC_MPC512x
 350        { .compatible = "fsl,mpc5121-usb2-dr", .data = &fsl_usb2_mpc5121_pd, },
 351#endif
 352        {},
 353};
 354MODULE_DEVICE_TABLE(of, fsl_usb2_mph_dr_of_match);
 355
 356static struct platform_driver fsl_usb2_mph_dr_driver = {
 357        .driver = {
 358                .name = "fsl-usb2-mph-dr",
 359                .of_match_table = fsl_usb2_mph_dr_of_match,
 360        },
 361        .probe  = fsl_usb2_mph_dr_of_probe,
 362        .remove = fsl_usb2_mph_dr_of_remove,
 363};
 364
 365module_platform_driver(fsl_usb2_mph_dr_driver);
 366
 367MODULE_DESCRIPTION("FSL MPH DR OF devices driver");
 368MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
 369MODULE_LICENSE("GPL");
 370