| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | | - * omap-usb2.c - USB PHY, talking to musb controller in OMAP. |
|---|
| 3 | + * omap-usb2.c - USB PHY, talking to USB controller on TI SoCs. |
|---|
| 3 | 4 | * |
|---|
| 4 | | - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com |
|---|
| 5 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 6 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 7 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 8 | | - * (at your option) any later version. |
|---|
| 9 | | - * |
|---|
| 5 | + * Copyright (C) 2012-2020 Texas Instruments Incorporated - http://www.ti.com |
|---|
| 10 | 6 | * Author: Kishon Vijay Abraham I <kishon@ti.com> |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 13 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 15 | | - * GNU General Public License for more details. |
|---|
| 16 | | - * |
|---|
| 17 | 7 | */ |
|---|
| 18 | 8 | |
|---|
| 19 | | -#include <linux/module.h> |
|---|
| 20 | | -#include <linux/platform_device.h> |
|---|
| 21 | | -#include <linux/slab.h> |
|---|
| 22 | | -#include <linux/of.h> |
|---|
| 23 | | -#include <linux/io.h> |
|---|
| 24 | | -#include <linux/phy/omap_usb.h> |
|---|
| 25 | | -#include <linux/usb/phy_companion.h> |
|---|
| 26 | 9 | #include <linux/clk.h> |
|---|
| 27 | | -#include <linux/err.h> |
|---|
| 28 | | -#include <linux/pm_runtime.h> |
|---|
| 29 | 10 | #include <linux/delay.h> |
|---|
| 30 | | -#include <linux/phy/omap_control_phy.h> |
|---|
| 31 | | -#include <linux/phy/phy.h> |
|---|
| 11 | +#include <linux/err.h> |
|---|
| 12 | +#include <linux/io.h> |
|---|
| 32 | 13 | #include <linux/mfd/syscon.h> |
|---|
| 33 | | -#include <linux/regmap.h> |
|---|
| 14 | +#include <linux/module.h> |
|---|
| 15 | +#include <linux/of.h> |
|---|
| 34 | 16 | #include <linux/of_platform.h> |
|---|
| 17 | +#include <linux/phy/omap_control_phy.h> |
|---|
| 18 | +#include <linux/phy/omap_usb.h> |
|---|
| 19 | +#include <linux/phy/phy.h> |
|---|
| 20 | +#include <linux/platform_device.h> |
|---|
| 21 | +#include <linux/pm_runtime.h> |
|---|
| 22 | +#include <linux/regmap.h> |
|---|
| 23 | +#include <linux/slab.h> |
|---|
| 24 | +#include <linux/sys_soc.h> |
|---|
| 25 | +#include <linux/usb/phy_companion.h> |
|---|
| 35 | 26 | |
|---|
| 36 | | -#define USB2PHY_DISCON_BYP_LATCH (1 << 31) |
|---|
| 37 | | -#define USB2PHY_ANA_CONFIG1 0x4c |
|---|
| 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 | + |
|---|
| 53 | +struct 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 | + |
|---|
| 72 | +struct usb_phy_data { |
|---|
| 73 | + const char *label; |
|---|
| 74 | + u8 flags; |
|---|
| 75 | + u32 mask; |
|---|
| 76 | + u32 power_on; |
|---|
| 77 | + u32 power_off; |
|---|
| 78 | +}; |
|---|
| 79 | + |
|---|
| 80 | +static inline u32 omap_usb_readl(void __iomem *addr, unsigned int offset) |
|---|
| 81 | +{ |
|---|
| 82 | + return __raw_readl(addr + offset); |
|---|
| 83 | +} |
|---|
| 84 | + |
|---|
| 85 | +static inline void omap_usb_writel(void __iomem *addr, unsigned int offset, |
|---|
| 86 | + u32 data) |
|---|
| 87 | +{ |
|---|
| 88 | + __raw_writel(data, addr + offset); |
|---|
| 89 | +} |
|---|
| 38 | 90 | |
|---|
| 39 | 91 | /** |
|---|
| 40 | | - * omap_usb2_set_comparator - links the comparator present in the sytem with |
|---|
| 92 | + * omap_usb2_set_comparator - links the comparator present in the system with |
|---|
| 41 | 93 | * this phy |
|---|
| 42 | 94 | * @comparator - the companion phy(comparator) for this phy |
|---|
| 43 | 95 | * |
|---|
| .. | .. |
|---|
| 90 | 142 | } |
|---|
| 91 | 143 | |
|---|
| 92 | 144 | static int omap_usb_set_peripheral(struct usb_otg *otg, |
|---|
| 93 | | - struct usb_gadget *gadget) |
|---|
| 145 | + struct usb_gadget *gadget) |
|---|
| 94 | 146 | { |
|---|
| 95 | 147 | otg->gadget = gadget; |
|---|
| 96 | 148 | if (!gadget) |
|---|
| .. | .. |
|---|
| 135 | 187 | |
|---|
| 136 | 188 | static int omap_usb2_disable_clocks(struct omap_usb *phy) |
|---|
| 137 | 189 | { |
|---|
| 138 | | - clk_disable(phy->wkupclk); |
|---|
| 190 | + clk_disable_unprepare(phy->wkupclk); |
|---|
| 139 | 191 | if (!IS_ERR(phy->optclk)) |
|---|
| 140 | | - clk_disable(phy->optclk); |
|---|
| 192 | + clk_disable_unprepare(phy->optclk); |
|---|
| 141 | 193 | |
|---|
| 142 | 194 | return 0; |
|---|
| 143 | 195 | } |
|---|
| .. | .. |
|---|
| 146 | 198 | { |
|---|
| 147 | 199 | int ret; |
|---|
| 148 | 200 | |
|---|
| 149 | | - ret = clk_enable(phy->wkupclk); |
|---|
| 201 | + ret = clk_prepare_enable(phy->wkupclk); |
|---|
| 150 | 202 | if (ret < 0) { |
|---|
| 151 | 203 | dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); |
|---|
| 152 | 204 | goto err0; |
|---|
| 153 | 205 | } |
|---|
| 154 | 206 | |
|---|
| 155 | 207 | if (!IS_ERR(phy->optclk)) { |
|---|
| 156 | | - ret = clk_enable(phy->optclk); |
|---|
| 208 | + ret = clk_prepare_enable(phy->optclk); |
|---|
| 157 | 209 | if (ret < 0) { |
|---|
| 158 | 210 | dev_err(phy->dev, "Failed to enable optclk %d\n", ret); |
|---|
| 159 | 211 | goto err1; |
|---|
| .. | .. |
|---|
| 163 | 215 | return 0; |
|---|
| 164 | 216 | |
|---|
| 165 | 217 | err1: |
|---|
| 166 | | - clk_disable(phy->wkupclk); |
|---|
| 218 | + clk_disable_unprepare(phy->wkupclk); |
|---|
| 167 | 219 | |
|---|
| 168 | 220 | err0: |
|---|
| 169 | 221 | return ret; |
|---|
| .. | .. |
|---|
| 188 | 240 | val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1); |
|---|
| 189 | 241 | val |= USB2PHY_DISCON_BYP_LATCH; |
|---|
| 190 | 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); |
|---|
| 191 | 250 | } |
|---|
| 192 | 251 | |
|---|
| 193 | 252 | return 0; |
|---|
| .. | .. |
|---|
| 245 | 304 | .power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD, |
|---|
| 246 | 305 | }; |
|---|
| 247 | 306 | |
|---|
| 307 | +static 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 | + |
|---|
| 248 | 316 | static const struct of_device_id omap_usb2_id_table[] = { |
|---|
| 249 | 317 | { |
|---|
| 250 | 318 | .compatible = "ti,omap-usb2", |
|---|
| .. | .. |
|---|
| 266 | 334 | .compatible = "ti,am437x-usb2", |
|---|
| 267 | 335 | .data = &am437x_usb2_data, |
|---|
| 268 | 336 | }, |
|---|
| 337 | + { |
|---|
| 338 | + .compatible = "ti,am654-usb2", |
|---|
| 339 | + .data = &am654_usb2_data, |
|---|
| 340 | + }, |
|---|
| 269 | 341 | {}, |
|---|
| 270 | 342 | }; |
|---|
| 271 | 343 | MODULE_DEVICE_TABLE(of, omap_usb2_id_table); |
|---|
| 344 | + |
|---|
| 345 | +static 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 | +} |
|---|
| 272 | 364 | |
|---|
| 273 | 365 | static int omap_usb2_probe(struct platform_device *pdev) |
|---|
| 274 | 366 | { |
|---|
| .. | .. |
|---|
| 307 | 399 | phy->mask = phy_data->mask; |
|---|
| 308 | 400 | phy->power_on = phy_data->power_on; |
|---|
| 309 | 401 | phy->power_off = phy_data->power_off; |
|---|
| 402 | + phy->flags = phy_data->flags; |
|---|
| 310 | 403 | |
|---|
| 311 | | - if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) { |
|---|
| 312 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 313 | | - phy->phy_base = devm_ioremap_resource(&pdev->dev, res); |
|---|
| 314 | | - if (IS_ERR(phy->phy_base)) |
|---|
| 315 | | - return PTR_ERR(phy->phy_base); |
|---|
| 316 | | - phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT; |
|---|
| 317 | | - } |
|---|
| 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); |
|---|
| 318 | 410 | |
|---|
| 319 | 411 | phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node, |
|---|
| 320 | | - "syscon-phy-power"); |
|---|
| 412 | + "syscon-phy-power"); |
|---|
| 321 | 413 | if (IS_ERR(phy->syscon_phy_power)) { |
|---|
| 322 | 414 | dev_dbg(&pdev->dev, |
|---|
| 323 | 415 | "can't get syscon-phy-power, using control device\n"); |
|---|
| .. | .. |
|---|
| 346 | 438 | } |
|---|
| 347 | 439 | } |
|---|
| 348 | 440 | |
|---|
| 349 | | - otg->set_host = omap_usb_set_host; |
|---|
| 350 | | - otg->set_peripheral = omap_usb_set_peripheral; |
|---|
| 441 | + phy->wkupclk = devm_clk_get(phy->dev, "wkupclk"); |
|---|
| 442 | + if (IS_ERR(phy->wkupclk)) { |
|---|
| 443 | + if (PTR_ERR(phy->wkupclk) == -EPROBE_DEFER) |
|---|
| 444 | + return -EPROBE_DEFER; |
|---|
| 445 | + |
|---|
| 446 | + dev_warn(&pdev->dev, "unable to get wkupclk %ld, trying old name\n", |
|---|
| 447 | + PTR_ERR(phy->wkupclk)); |
|---|
| 448 | + phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); |
|---|
| 449 | + |
|---|
| 450 | + if (IS_ERR(phy->wkupclk)) { |
|---|
| 451 | + if (PTR_ERR(phy->wkupclk) != -EPROBE_DEFER) |
|---|
| 452 | + dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); |
|---|
| 453 | + return PTR_ERR(phy->wkupclk); |
|---|
| 454 | + } |
|---|
| 455 | + |
|---|
| 456 | + dev_warn(&pdev->dev, |
|---|
| 457 | + "found usb_phy_cm_clk32k, please fix DTS\n"); |
|---|
| 458 | + } |
|---|
| 459 | + |
|---|
| 460 | + phy->optclk = devm_clk_get(phy->dev, "refclk"); |
|---|
| 461 | + if (IS_ERR(phy->optclk)) { |
|---|
| 462 | + if (PTR_ERR(phy->optclk) == -EPROBE_DEFER) |
|---|
| 463 | + return -EPROBE_DEFER; |
|---|
| 464 | + |
|---|
| 465 | + dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n"); |
|---|
| 466 | + phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); |
|---|
| 467 | + |
|---|
| 468 | + if (IS_ERR(phy->optclk)) { |
|---|
| 469 | + if (PTR_ERR(phy->optclk) != -EPROBE_DEFER) { |
|---|
| 470 | + dev_dbg(&pdev->dev, |
|---|
| 471 | + "unable to get usb_otg_ss_refclk960m\n"); |
|---|
| 472 | + } |
|---|
| 473 | + } else { |
|---|
| 474 | + dev_warn(&pdev->dev, |
|---|
| 475 | + "found usb_otg_ss_refclk960m, please fix DTS\n"); |
|---|
| 476 | + } |
|---|
| 477 | + } |
|---|
| 478 | + |
|---|
| 479 | + otg->set_host = omap_usb_set_host; |
|---|
| 480 | + otg->set_peripheral = omap_usb_set_peripheral; |
|---|
| 351 | 481 | if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS) |
|---|
| 352 | | - otg->set_vbus = omap_usb_set_vbus; |
|---|
| 482 | + otg->set_vbus = omap_usb_set_vbus; |
|---|
| 353 | 483 | if (phy_data->flags & OMAP_USB2_HAS_START_SRP) |
|---|
| 354 | | - otg->start_srp = omap_usb_start_srp; |
|---|
| 355 | | - otg->usb_phy = &phy->phy; |
|---|
| 484 | + otg->start_srp = omap_usb_start_srp; |
|---|
| 485 | + otg->usb_phy = &phy->phy; |
|---|
| 356 | 486 | |
|---|
| 357 | 487 | platform_set_drvdata(pdev, phy); |
|---|
| 358 | 488 | pm_runtime_enable(phy->dev); |
|---|
| .. | .. |
|---|
| 367 | 497 | omap_usb_power_off(generic_phy); |
|---|
| 368 | 498 | |
|---|
| 369 | 499 | phy_provider = devm_of_phy_provider_register(phy->dev, |
|---|
| 370 | | - of_phy_simple_xlate); |
|---|
| 500 | + of_phy_simple_xlate); |
|---|
| 371 | 501 | if (IS_ERR(phy_provider)) { |
|---|
| 372 | 502 | pm_runtime_disable(phy->dev); |
|---|
| 373 | 503 | return PTR_ERR(phy_provider); |
|---|
| 374 | 504 | } |
|---|
| 375 | | - |
|---|
| 376 | | - phy->wkupclk = devm_clk_get(phy->dev, "wkupclk"); |
|---|
| 377 | | - if (IS_ERR(phy->wkupclk)) { |
|---|
| 378 | | - dev_warn(&pdev->dev, "unable to get wkupclk, trying old name\n"); |
|---|
| 379 | | - phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); |
|---|
| 380 | | - if (IS_ERR(phy->wkupclk)) { |
|---|
| 381 | | - dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); |
|---|
| 382 | | - pm_runtime_disable(phy->dev); |
|---|
| 383 | | - return PTR_ERR(phy->wkupclk); |
|---|
| 384 | | - } else { |
|---|
| 385 | | - dev_warn(&pdev->dev, |
|---|
| 386 | | - "found usb_phy_cm_clk32k, please fix DTS\n"); |
|---|
| 387 | | - } |
|---|
| 388 | | - } |
|---|
| 389 | | - clk_prepare(phy->wkupclk); |
|---|
| 390 | | - |
|---|
| 391 | | - phy->optclk = devm_clk_get(phy->dev, "refclk"); |
|---|
| 392 | | - if (IS_ERR(phy->optclk)) { |
|---|
| 393 | | - dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n"); |
|---|
| 394 | | - phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); |
|---|
| 395 | | - if (IS_ERR(phy->optclk)) { |
|---|
| 396 | | - dev_dbg(&pdev->dev, |
|---|
| 397 | | - "unable to get usb_otg_ss_refclk960m\n"); |
|---|
| 398 | | - } else { |
|---|
| 399 | | - dev_warn(&pdev->dev, |
|---|
| 400 | | - "found usb_otg_ss_refclk960m, please fix DTS\n"); |
|---|
| 401 | | - } |
|---|
| 402 | | - } |
|---|
| 403 | | - |
|---|
| 404 | | - if (!IS_ERR(phy->optclk)) |
|---|
| 405 | | - clk_prepare(phy->optclk); |
|---|
| 406 | 505 | |
|---|
| 407 | 506 | usb_add_phy_dev(&phy->phy); |
|---|
| 408 | 507 | |
|---|
| .. | .. |
|---|
| 413 | 512 | { |
|---|
| 414 | 513 | struct omap_usb *phy = platform_get_drvdata(pdev); |
|---|
| 415 | 514 | |
|---|
| 416 | | - clk_unprepare(phy->wkupclk); |
|---|
| 417 | | - if (!IS_ERR(phy->optclk)) |
|---|
| 418 | | - clk_unprepare(phy->optclk); |
|---|
| 419 | 515 | usb_remove_phy(&phy->phy); |
|---|
| 420 | 516 | pm_runtime_disable(phy->dev); |
|---|
| 421 | 517 | |
|---|