linux/drivers/phy/ti/phy-omap-usb2.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * omap-usb2.c - USB PHY, talking to USB controller on TI SoCs.
   4 *
   5 * Copyright (C) 2012-2020 Texas Instruments Incorporated - http://www.ti.com
   6 * Author: Kishon Vijay Abraham I <kishon@ti.com>
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/platform_device.h>
  11#include <linux/slab.h>
  12#include <linux/of.h>
  13#include <linux/io.h>
  14#include <linux/phy/omap_usb.h>
  15#include <linux/usb/phy_companion.h>
  16#include <linux/clk.h>
  17#include <linux/err.h>
  18#include <linux/pm_runtime.h>
  19#include <linux/delay.h>
  20#include <linux/phy/omap_control_phy.h>
  21#include <linux/phy/phy.h>
  22#include <linux/mfd/syscon.h>
  23#include <linux/regmap.h>
  24#include <linux/of_platform.h>
  25#include <linux/sys_soc.h>
  26
  27#define USB2PHY_ANA_CONFIG1             0x4c
  28#define USB2PHY_DISCON_BYP_LATCH        BIT(31)
  29
  30#define USB2PHY_CHRG_DET                        0x14
  31#define USB2PHY_CHRG_DET_USE_CHG_DET_REG        BIT(29)
  32#define USB2PHY_CHRG_DET_DIS_CHG_DET            BIT(28)
  33
  34/* SoC Specific USB2_OTG register definitions */
  35#define AM654_USB2_OTG_PD               BIT(8)
  36#define AM654_USB2_VBUS_DET_EN          BIT(5)
  37#define AM654_USB2_VBUSVALID_DET_EN     BIT(4)
  38
  39#define OMAP_DEV_PHY_PD         BIT(0)
  40#define OMAP_USB2_PHY_PD        BIT(28)
  41
  42#define AM437X_USB2_PHY_PD              BIT(0)
  43#define AM437X_USB2_OTG_PD              BIT(1)
  44#define AM437X_USB2_OTGVDET_EN          BIT(19)
  45#define AM437X_USB2_OTGSESSEND_EN       BIT(20)
  46
  47/* Driver Flags */
  48#define OMAP_USB2_HAS_START_SRP                 BIT(0)
  49#define OMAP_USB2_HAS_SET_VBUS                  BIT(1)
  50#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT    BIT(2)
  51#define OMAP_USB2_DISABLE_CHRG_DET              BIT(3)
  52
  53struct omap_usb {
  54        struct usb_phy          phy;
  55        struct phy_companion    *comparator;
  56        void __iomem            *pll_ctrl_base;
  57        void __iomem            *phy_base;
  58        struct device           *dev;
  59        struct device           *control_dev;
  60        struct clk              *wkupclk;
  61        struct clk              *optclk;
  62        u8                      flags;
  63        struct regmap           *syscon_phy_power; /* ctrl. reg. acces */
  64        unsigned int            power_reg; /* power reg. index within syscon */
  65        u32                     mask;
  66        u32                     power_on;
  67        u32                     power_off;
  68};
  69
  70#define phy_to_omapusb(x)       container_of((x), struct omap_usb, phy)
  71
  72struct usb_phy_data {
  73        const char *label;
  74        u8 flags;
  75        u32 mask;
  76        u32 power_on;
  77        u32 power_off;
  78};
  79
  80static inline u32 omap_usb_readl(void __iomem *addr, unsigned int offset)
  81{
  82        return __raw_readl(addr + offset);
  83}
  84
  85static inline void omap_usb_writel(void __iomem *addr, unsigned int offset,
  86                                   u32 data)
  87{
  88        __raw_writel(data, addr + offset);
  89}
  90
  91/**
  92 * omap_usb2_set_comparator - links the comparator present in the sytem with
  93 *      this phy
  94 * @comparator - the companion phy(comparator) for this phy
  95 *
  96 * The phy companion driver should call this API passing the phy_companion
  97 * filled with set_vbus and start_srp to be used by usb phy.
  98 *
  99 * For use by phy companion driver
 100 */
 101int omap_usb2_set_comparator(struct phy_companion *comparator)
 102{
 103        struct omap_usb *phy;
 104        struct usb_phy  *x = usb_get_phy(USB_PHY_TYPE_USB2);
 105
 106        if (IS_ERR(x))
 107                return -ENODEV;
 108
 109        phy = phy_to_omapusb(x);
 110        phy->comparator = comparator;
 111        return 0;
 112}
 113EXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
 114
 115static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
 116{
 117        struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
 118
 119        if (!phy->comparator)
 120                return -ENODEV;
 121
 122        return phy->comparator->set_vbus(phy->comparator, enabled);
 123}
 124
 125static int omap_usb_start_srp(struct usb_otg *otg)
 126{
 127        struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
 128
 129        if (!phy->comparator)
 130                return -ENODEV;
 131
 132        return phy->comparator->start_srp(phy->comparator);
 133}
 134
 135static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
 136{
 137        otg->host = host;
 138        if (!host)
 139                otg->state = OTG_STATE_UNDEFINED;
 140
 141        return 0;
 142}
 143
 144static int omap_usb_set_peripheral(struct usb_otg *otg,
 145                struct usb_gadget *gadget)
 146{
 147        otg->gadget = gadget;
 148        if (!gadget)
 149                otg->state = OTG_STATE_UNDEFINED;
 150
 151        return 0;
 152}
 153
 154static int omap_usb_phy_power(struct omap_usb *phy, int on)
 155{
 156        u32 val;
 157        int ret;
 158
 159        if (!phy->syscon_phy_power) {
 160                omap_control_phy_power(phy->control_dev, on);
 161                return 0;
 162        }
 163
 164        if (on)
 165                val = phy->power_on;
 166        else
 167                val = phy->power_off;
 168
 169        ret = regmap_update_bits(phy->syscon_phy_power, phy->power_reg,
 170                                 phy->mask, val);
 171        return ret;
 172}
 173
 174static int omap_usb_power_off(struct phy *x)
 175{
 176        struct omap_usb *phy = phy_get_drvdata(x);
 177
 178        return omap_usb_phy_power(phy, false);
 179}
 180
 181static int omap_usb_power_on(struct phy *x)
 182{
 183        struct omap_usb *phy = phy_get_drvdata(x);
 184
 185        return omap_usb_phy_power(phy, true);
 186}
 187
 188static int omap_usb2_disable_clocks(struct omap_usb *phy)
 189{
 190        clk_disable_unprepare(phy->wkupclk);
 191        if (!IS_ERR(phy->optclk))
 192                clk_disable_unprepare(phy->optclk);
 193
 194        return 0;
 195}
 196
 197static int omap_usb2_enable_clocks(struct omap_usb *phy)
 198{
 199        int ret;
 200
 201        ret = clk_prepare_enable(phy->wkupclk);
 202        if (ret < 0) {
 203                dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
 204                goto err0;
 205        }
 206
 207        if (!IS_ERR(phy->optclk)) {
 208                ret = clk_prepare_enable(phy->optclk);
 209                if (ret < 0) {
 210                        dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
 211                        goto err1;
 212                }
 213        }
 214
 215        return 0;
 216
 217err1:
 218        clk_disable(phy->wkupclk);
 219
 220err0:
 221        return ret;
 222}
 223
 224static int omap_usb_init(struct phy *x)
 225{
 226        struct omap_usb *phy = phy_get_drvdata(x);
 227        u32 val;
 228
 229        omap_usb2_enable_clocks(phy);
 230
 231        if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
 232                /*
 233                 *
 234                 * Reduce the sensitivity of internal PHY by enabling the
 235                 * DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This
 236                 * resolves issues with certain devices which can otherwise
 237                 * be prone to false disconnects.
 238                 *
 239                 */
 240                val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1);
 241                val |= USB2PHY_DISCON_BYP_LATCH;
 242                omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val);
 243        }
 244
 245        if (phy->flags & OMAP_USB2_DISABLE_CHRG_DET) {
 246                val = omap_usb_readl(phy->phy_base, USB2PHY_CHRG_DET);
 247                val |= USB2PHY_CHRG_DET_USE_CHG_DET_REG |
 248                       USB2PHY_CHRG_DET_DIS_CHG_DET;
 249                omap_usb_writel(phy->phy_base, USB2PHY_CHRG_DET, val);
 250        }
 251
 252        return 0;
 253}
 254
 255static int omap_usb_exit(struct phy *x)
 256{
 257        struct omap_usb *phy = phy_get_drvdata(x);
 258
 259        return omap_usb2_disable_clocks(phy);
 260}
 261
 262static const struct phy_ops ops = {
 263        .init           = omap_usb_init,
 264        .exit           = omap_usb_exit,
 265        .power_on       = omap_usb_power_on,
 266        .power_off      = omap_usb_power_off,
 267        .owner          = THIS_MODULE,
 268};
 269
 270static const struct usb_phy_data omap_usb2_data = {
 271        .label = "omap_usb2",
 272        .flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
 273        .mask = OMAP_DEV_PHY_PD,
 274        .power_off = OMAP_DEV_PHY_PD,
 275};
 276
 277static const struct usb_phy_data omap5_usb2_data = {
 278        .label = "omap5_usb2",
 279        .flags = 0,
 280        .mask = OMAP_DEV_PHY_PD,
 281        .power_off = OMAP_DEV_PHY_PD,
 282};
 283
 284static const struct usb_phy_data dra7x_usb2_data = {
 285        .label = "dra7x_usb2",
 286        .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
 287        .mask = OMAP_DEV_PHY_PD,
 288        .power_off = OMAP_DEV_PHY_PD,
 289};
 290
 291static const struct usb_phy_data dra7x_usb2_phy2_data = {
 292        .label = "dra7x_usb2_phy2",
 293        .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
 294        .mask = OMAP_USB2_PHY_PD,
 295        .power_off = OMAP_USB2_PHY_PD,
 296};
 297
 298static const struct usb_phy_data am437x_usb2_data = {
 299        .label = "am437x_usb2",
 300        .flags =  0,
 301        .mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD |
 302                AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
 303        .power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
 304        .power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD,
 305};
 306
 307static const struct usb_phy_data am654_usb2_data = {
 308        .label = "am654_usb2",
 309        .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
 310        .mask = AM654_USB2_OTG_PD | AM654_USB2_VBUS_DET_EN |
 311                AM654_USB2_VBUSVALID_DET_EN,
 312        .power_on = AM654_USB2_VBUS_DET_EN | AM654_USB2_VBUSVALID_DET_EN,
 313        .power_off = AM654_USB2_OTG_PD,
 314};
 315
 316static const struct of_device_id omap_usb2_id_table[] = {
 317        {
 318                .compatible = "ti,omap-usb2",
 319                .data = &omap_usb2_data,
 320        },
 321        {
 322                .compatible = "ti,omap5-usb2",
 323                .data = &omap5_usb2_data,
 324        },
 325        {
 326                .compatible = "ti,dra7x-usb2",
 327                .data = &dra7x_usb2_data,
 328        },
 329        {
 330                .compatible = "ti,dra7x-usb2-phy2",
 331                .data = &dra7x_usb2_phy2_data,
 332        },
 333        {
 334                .compatible = "ti,am437x-usb2",
 335                .data = &am437x_usb2_data,
 336        },
 337        {
 338                .compatible = "ti,am654-usb2",
 339                .data = &am654_usb2_data,
 340        },
 341        {},
 342};
 343MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
 344
 345static void omap_usb2_init_errata(struct omap_usb *phy)
 346{
 347        static const struct soc_device_attribute am65x_sr10_soc_devices[] = {
 348                { .family = "AM65X", .revision = "SR1.0" },
 349                { /* sentinel */ }
 350        };
 351
 352        /*
 353         * Errata i2075: USB2PHY: USB2PHY Charger Detect is Enabled by
 354         * Default Without VBUS Presence.
 355         *
 356         * AM654x SR1.0 has a silicon bug due to which D+ is pulled high after
 357         * POR, which could cause enumeration failure with some USB hubs.
 358         * Disabling the USB2_PHY Charger Detect function will put D+
 359         * into the normal state.
 360         */
 361        if (soc_device_match(am65x_sr10_soc_devices))
 362                phy->flags |= OMAP_USB2_DISABLE_CHRG_DET;
 363}
 364
 365static int omap_usb2_probe(struct platform_device *pdev)
 366{
 367        struct omap_usb *phy;
 368        struct phy *generic_phy;
 369        struct resource *res;
 370        struct phy_provider *phy_provider;
 371        struct usb_otg *otg;
 372        struct device_node *node = pdev->dev.of_node;
 373        struct device_node *control_node;
 374        struct platform_device *control_pdev;
 375        const struct of_device_id *of_id;
 376        struct usb_phy_data *phy_data;
 377
 378        of_id = of_match_device(omap_usb2_id_table, &pdev->dev);
 379
 380        if (!of_id)
 381                return -EINVAL;
 382
 383        phy_data = (struct usb_phy_data *)of_id->data;
 384
 385        phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
 386        if (!phy)
 387                return -ENOMEM;
 388
 389        otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
 390        if (!otg)
 391                return -ENOMEM;
 392
 393        phy->dev                = &pdev->dev;
 394
 395        phy->phy.dev            = phy->dev;
 396        phy->phy.label          = phy_data->label;
 397        phy->phy.otg            = otg;
 398        phy->phy.type           = USB_PHY_TYPE_USB2;
 399        phy->mask               = phy_data->mask;
 400        phy->power_on           = phy_data->power_on;
 401        phy->power_off          = phy_data->power_off;
 402        phy->flags              = phy_data->flags;
 403
 404        omap_usb2_init_errata(phy);
 405
 406        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 407        phy->phy_base = devm_ioremap_resource(&pdev->dev, res);
 408        if (IS_ERR(phy->phy_base))
 409                return PTR_ERR(phy->phy_base);
 410
 411        phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node,
 412                                                        "syscon-phy-power");
 413        if (IS_ERR(phy->syscon_phy_power)) {
 414                dev_dbg(&pdev->dev,
 415                        "can't get syscon-phy-power, using control device\n");
 416                phy->syscon_phy_power = NULL;
 417
 418                control_node = of_parse_phandle(node, "ctrl-module", 0);
 419                if (!control_node) {
 420                        dev_err(&pdev->dev,
 421                                "Failed to get control device phandle\n");
 422                        return -EINVAL;
 423                }
 424
 425                control_pdev = of_find_device_by_node(control_node);
 426                if (!control_pdev) {
 427                        dev_err(&pdev->dev, "Failed to get control device\n");
 428                        return -EINVAL;
 429                }
 430                phy->control_dev = &control_pdev->dev;
 431        } else {
 432                if (of_property_read_u32_index(node,
 433                                               "syscon-phy-power", 1,
 434                                               &phy->power_reg)) {
 435                        dev_err(&pdev->dev,
 436                                "couldn't get power reg. offset\n");
 437                        return -EINVAL;
 438                }
 439        }
 440
 441
 442        phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
 443        if (IS_ERR(phy->wkupclk)) {
 444                if (PTR_ERR(phy->wkupclk) == -EPROBE_DEFER)
 445                        return -EPROBE_DEFER;
 446
 447                dev_warn(&pdev->dev, "unable to get wkupclk %ld, trying old name\n",
 448                         PTR_ERR(phy->wkupclk));
 449                phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
 450
 451                if (IS_ERR(phy->wkupclk)) {
 452                        if (PTR_ERR(phy->wkupclk) != -EPROBE_DEFER)
 453                                dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
 454                        return PTR_ERR(phy->wkupclk);
 455                } else {
 456                        dev_warn(&pdev->dev,
 457                                 "found usb_phy_cm_clk32k, please fix DTS\n");
 458                }
 459        }
 460
 461        phy->optclk = devm_clk_get(phy->dev, "refclk");
 462        if (IS_ERR(phy->optclk)) {
 463                if (PTR_ERR(phy->optclk) == -EPROBE_DEFER)
 464                        return -EPROBE_DEFER;
 465
 466                dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n");
 467                phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
 468
 469                if (IS_ERR(phy->optclk)) {
 470                        if (PTR_ERR(phy->optclk) != -EPROBE_DEFER) {
 471                                dev_dbg(&pdev->dev,
 472                                        "unable to get usb_otg_ss_refclk960m\n");
 473                        }
 474                } else {
 475                        dev_warn(&pdev->dev,
 476                                 "found usb_otg_ss_refclk960m, please fix DTS\n");
 477                }
 478        }
 479
 480        otg->set_host = omap_usb_set_host;
 481        otg->set_peripheral = omap_usb_set_peripheral;
 482        if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS)
 483                otg->set_vbus = omap_usb_set_vbus;
 484        if (phy_data->flags & OMAP_USB2_HAS_START_SRP)
 485                otg->start_srp = omap_usb_start_srp;
 486        otg->usb_phy = &phy->phy;
 487
 488        platform_set_drvdata(pdev, phy);
 489        pm_runtime_enable(phy->dev);
 490
 491        generic_phy = devm_phy_create(phy->dev, NULL, &ops);
 492        if (IS_ERR(generic_phy)) {
 493                pm_runtime_disable(phy->dev);
 494                return PTR_ERR(generic_phy);
 495        }
 496
 497        phy_set_drvdata(generic_phy, phy);
 498        omap_usb_power_off(generic_phy);
 499
 500        phy_provider = devm_of_phy_provider_register(phy->dev,
 501                                                     of_phy_simple_xlate);
 502        if (IS_ERR(phy_provider)) {
 503                pm_runtime_disable(phy->dev);
 504                return PTR_ERR(phy_provider);
 505        }
 506
 507
 508        usb_add_phy_dev(&phy->phy);
 509
 510        return 0;
 511}
 512
 513static int omap_usb2_remove(struct platform_device *pdev)
 514{
 515        struct omap_usb *phy = platform_get_drvdata(pdev);
 516
 517        usb_remove_phy(&phy->phy);
 518        pm_runtime_disable(phy->dev);
 519
 520        return 0;
 521}
 522
 523static struct platform_driver omap_usb2_driver = {
 524        .probe          = omap_usb2_probe,
 525        .remove         = omap_usb2_remove,
 526        .driver         = {
 527                .name   = "omap-usb2",
 528                .of_match_table = omap_usb2_id_table,
 529        },
 530};
 531
 532module_platform_driver(omap_usb2_driver);
 533
 534MODULE_ALIAS("platform:omap_usb2");
 535MODULE_AUTHOR("Texas Instruments Inc.");
 536MODULE_DESCRIPTION("OMAP USB2 phy driver");
 537MODULE_LICENSE("GPL v2");
 538