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