1/* 2 * Copyright 2012-2014 Freescale Semiconductor, Inc. 3 * Copyright (C) 2012 Marek Vasut <marex@denx.de> 4 * on behalf of DENX Software Engineering GmbH 5 * 6 * The code contained herein is licensed under the GNU General Public 7 * License. You may obtain a copy of the GNU General Public License 8 * Version 2 or later at the following locations: 9 * 10 * http://www.opensource.org/licenses/gpl-license.html 11 * http://www.gnu.org/copyleft/gpl.html 12 */ 13 14#include <linux/module.h> 15#include <linux/kernel.h> 16#include <linux/platform_device.h> 17#include <linux/clk.h> 18#include <linux/usb/otg.h> 19#include <linux/stmp_device.h> 20#include <linux/delay.h> 21#include <linux/err.h> 22#include <linux/io.h> 23#include <linux/of_device.h> 24#include <linux/regmap.h> 25#include <linux/mfd/syscon.h> 26 27#define DRIVER_NAME "mxs_phy" 28 29#define HW_USBPHY_PWD 0x00 30#define HW_USBPHY_CTRL 0x30 31#define HW_USBPHY_CTRL_SET 0x34 32#define HW_USBPHY_CTRL_CLR 0x38 33 34#define HW_USBPHY_DEBUG_SET 0x54 35#define HW_USBPHY_DEBUG_CLR 0x58 36 37#define HW_USBPHY_IP 0x90 38#define HW_USBPHY_IP_SET 0x94 39#define HW_USBPHY_IP_CLR 0x98 40 41#define BM_USBPHY_CTRL_SFTRST BIT(31) 42#define BM_USBPHY_CTRL_CLKGATE BIT(30) 43#define BM_USBPHY_CTRL_OTG_ID_VALUE BIT(27) 44#define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS BIT(26) 45#define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE BIT(25) 46#define BM_USBPHY_CTRL_ENVBUSCHG_WKUP BIT(23) 47#define BM_USBPHY_CTRL_ENIDCHG_WKUP BIT(22) 48#define BM_USBPHY_CTRL_ENDPDMCHG_WKUP BIT(21) 49#define BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD BIT(20) 50#define BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE BIT(19) 51#define BM_USBPHY_CTRL_ENAUTO_PWRON_PLL BIT(18) 52#define BM_USBPHY_CTRL_ENUTMILEVEL3 BIT(15) 53#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14) 54#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) 55 56#define BM_USBPHY_IP_FIX (BIT(17) | BIT(18)) 57 58#define BM_USBPHY_DEBUG_CLKGATE BIT(30) 59 60/* Anatop Registers */ 61#define ANADIG_ANA_MISC0 0x150 62#define ANADIG_ANA_MISC0_SET 0x154 63#define ANADIG_ANA_MISC0_CLR 0x158 64 65#define ANADIG_USB1_VBUS_DET_STAT 0x1c0 66#define ANADIG_USB2_VBUS_DET_STAT 0x220 67 68#define ANADIG_USB1_LOOPBACK_SET 0x1e4 69#define ANADIG_USB1_LOOPBACK_CLR 0x1e8 70#define ANADIG_USB2_LOOPBACK_SET 0x244 71#define ANADIG_USB2_LOOPBACK_CLR 0x248 72 73#define ANADIG_USB1_MISC 0x1f0 74#define ANADIG_USB2_MISC 0x250 75 76#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG BIT(12) 77#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11) 78 79#define BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID BIT(3) 80#define BM_ANADIG_USB2_VBUS_DET_STAT_VBUS_VALID BIT(3) 81 82#define BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 BIT(2) 83#define BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN BIT(5) 84#define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 BIT(2) 85#define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN BIT(5) 86 87#define BM_ANADIG_USB1_MISC_RX_VPIN_FS BIT(29) 88#define BM_ANADIG_USB1_MISC_RX_VMIN_FS BIT(28) 89#define BM_ANADIG_USB2_MISC_RX_VPIN_FS BIT(29) 90#define BM_ANADIG_USB2_MISC_RX_VMIN_FS BIT(28) 91 92#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) 93 94/* Do disconnection between PHY and controller without vbus */ 95#define MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS BIT(0) 96 97/* 98 * The PHY will be in messy if there is a wakeup after putting 99 * bus to suspend (set portsc.suspendM) but before setting PHY to low 100 * power mode (set portsc.phcd). 101 */ 102#define MXS_PHY_ABNORMAL_IN_SUSPEND BIT(1) 103 104/* 105 * The SOF sends too fast after resuming, it will cause disconnection 106 * between host and high speed device. 107 */ 108#define MXS_PHY_SENDING_SOF_TOO_FAST BIT(2) 109 110/* 111 * IC has bug fixes logic, they include 112 * MXS_PHY_ABNORMAL_IN_SUSPEND and MXS_PHY_SENDING_SOF_TOO_FAST 113 * which are described at above flags, the RTL will handle it 114 * according to different versions. 115 */ 116#define MXS_PHY_NEED_IP_FIX BIT(3) 117 118struct mxs_phy_data { 119 unsigned int flags; 120}; 121 122static const struct mxs_phy_data imx23_phy_data = { 123 .flags = MXS_PHY_ABNORMAL_IN_SUSPEND | MXS_PHY_SENDING_SOF_TOO_FAST, 124}; 125 126static const struct mxs_phy_data imx6q_phy_data = { 127 .flags = MXS_PHY_SENDING_SOF_TOO_FAST | 128 MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | 129 MXS_PHY_NEED_IP_FIX, 130}; 131 132static const struct mxs_phy_data imx6sl_phy_data = { 133 .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | 134 MXS_PHY_NEED_IP_FIX, 135}; 136 137static const struct mxs_phy_data vf610_phy_data = { 138 .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | 139 MXS_PHY_NEED_IP_FIX, 140}; 141 142static const struct mxs_phy_data imx6sx_phy_data = { 143 .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS, 144}; 145 146static const struct mxs_phy_data imx6ul_phy_data = { 147 .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS, 148}; 149 150static const struct of_device_id mxs_phy_dt_ids[] = { 151 { .compatible = "fsl,imx6sx-usbphy", .data = &imx6sx_phy_data, }, 152 { .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, }, 153 { .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, }, 154 { .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, }, 155 { .compatible = "fsl,vf610-usbphy", .data = &vf610_phy_data, }, 156 { .compatible = "fsl,imx6ul-usbphy", .data = &imx6ul_phy_data, }, 157 { /* sentinel */ } 158}; 159MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); 160 161struct mxs_phy { 162 struct usb_phy phy; 163 struct clk *clk; 164 const struct mxs_phy_data *data; 165 struct regmap *regmap_anatop; 166 int port_id; 167}; 168 169static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy) 170{ 171 return mxs_phy->data == &imx6q_phy_data; 172} 173 174static inline bool is_imx6sl_phy(struct mxs_phy *mxs_phy) 175{ 176 return mxs_phy->data == &imx6sl_phy_data; 177} 178 179/* 180 * PHY needs some 32K cycles to switch from 32K clock to 181 * bus (such as AHB/AXI, etc) clock. 182 */ 183static void mxs_phy_clock_switch_delay(void) 184{ 185 usleep_range(300, 400); 186} 187 188static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) 189{ 190 int ret; 191 void __iomem *base = mxs_phy->phy.io_priv; 192 193 ret = stmp_reset_block(base + HW_USBPHY_CTRL); 194 if (ret) 195 return ret; 196 197 /* Power up the PHY */ 198 writel(0, base + HW_USBPHY_PWD); 199 200 /* 201 * USB PHY Ctrl Setting 202 * - Auto clock/power on 203 * - Enable full/low speed support 204 */ 205 writel(BM_USBPHY_CTRL_ENAUTOSET_USBCLKS | 206 BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE | 207 BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD | 208 BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE | 209 BM_USBPHY_CTRL_ENAUTO_PWRON_PLL | 210 BM_USBPHY_CTRL_ENUTMILEVEL2 | 211 BM_USBPHY_CTRL_ENUTMILEVEL3, 212 base + HW_USBPHY_CTRL_SET); 213 214 if (mxs_phy->data->flags & MXS_PHY_NEED_IP_FIX) 215 writel(BM_USBPHY_IP_FIX, base + HW_USBPHY_IP_SET); 216 217 return 0; 218} 219 220/* Return true if the vbus is there */ 221static bool mxs_phy_get_vbus_status(struct mxs_phy *mxs_phy) 222{ 223 unsigned int vbus_value; 224 225 if (!mxs_phy->regmap_anatop) 226 return false; 227 228 if (mxs_phy->port_id == 0) 229 regmap_read(mxs_phy->regmap_anatop, 230 ANADIG_USB1_VBUS_DET_STAT, 231 &vbus_value); 232 else if (mxs_phy->port_id == 1) 233 regmap_read(mxs_phy->regmap_anatop, 234 ANADIG_USB2_VBUS_DET_STAT, 235 &vbus_value); 236 237 if (vbus_value & BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID) 238 return true; 239 else 240 return false; 241} 242 243static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect) 244{ 245 void __iomem *base = mxs_phy->phy.io_priv; 246 u32 reg; 247 248 if (disconnect) 249 writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, 250 base + HW_USBPHY_DEBUG_CLR); 251 252 if (mxs_phy->port_id == 0) { 253 reg = disconnect ? ANADIG_USB1_LOOPBACK_SET 254 : ANADIG_USB1_LOOPBACK_CLR; 255 regmap_write(mxs_phy->regmap_anatop, reg, 256 BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 | 257 BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN); 258 } else if (mxs_phy->port_id == 1) { 259 reg = disconnect ? ANADIG_USB2_LOOPBACK_SET 260 : ANADIG_USB2_LOOPBACK_CLR; 261 regmap_write(mxs_phy->regmap_anatop, reg, 262 BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 | 263 BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN); 264 } 265 266 if (!disconnect) 267 writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, 268 base + HW_USBPHY_DEBUG_SET); 269 270 /* Delay some time, and let Linestate be SE0 for controller */ 271 if (disconnect) 272 usleep_range(500, 1000); 273} 274 275static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy) 276{ 277 void __iomem *base = mxs_phy->phy.io_priv; 278 u32 phyctrl = readl(base + HW_USBPHY_CTRL); 279 280 if (IS_ENABLED(CONFIG_USB_OTG) && 281 !(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE)) 282 return true; 283 284 return false; 285} 286 287static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) 288{ 289 bool vbus_is_on = false; 290 291 /* If the SoCs don't need to disconnect line without vbus, quit */ 292 if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS)) 293 return; 294 295 /* If the SoCs don't have anatop, quit */ 296 if (!mxs_phy->regmap_anatop) 297 return; 298 299 vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); 300 301 if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy)) 302 __mxs_phy_disconnect_line(mxs_phy, true); 303 else 304 __mxs_phy_disconnect_line(mxs_phy, false); 305 306} 307 308static int mxs_phy_init(struct usb_phy *phy) 309{ 310 int ret; 311 struct mxs_phy *mxs_phy = to_mxs_phy(phy); 312 313 mxs_phy_clock_switch_delay(); 314 ret = clk_prepare_enable(mxs_phy->clk); 315 if (ret) 316 return ret; 317 318 return mxs_phy_hw_init(mxs_phy); 319} 320 321static void mxs_phy_shutdown(struct usb_phy *phy) 322{ 323 struct mxs_phy *mxs_phy = to_mxs_phy(phy); 324 u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP | 325 BM_USBPHY_CTRL_ENDPDMCHG_WKUP | 326 BM_USBPHY_CTRL_ENIDCHG_WKUP | 327 BM_USBPHY_CTRL_ENAUTOSET_USBCLKS | 328 BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE | 329 BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD | 330 BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE | 331 BM_USBPHY_CTRL_ENAUTO_PWRON_PLL; 332 333 writel(value, phy->io_priv + HW_USBPHY_CTRL_CLR); 334 writel(0xffffffff, phy->io_priv + HW_USBPHY_PWD); 335 336 writel(BM_USBPHY_CTRL_CLKGATE, 337 phy->io_priv + HW_USBPHY_CTRL_SET); 338 339 clk_disable_unprepare(mxs_phy->clk); 340} 341 342static bool mxs_phy_is_low_speed_connection(struct mxs_phy *mxs_phy) 343{ 344 unsigned int line_state; 345 /* bit definition is the same for all controllers */ 346 unsigned int dp_bit = BM_ANADIG_USB1_MISC_RX_VPIN_FS, 347 dm_bit = BM_ANADIG_USB1_MISC_RX_VMIN_FS; 348 unsigned int reg = ANADIG_USB1_MISC; 349 350 /* If the SoCs don't have anatop, quit */ 351 if (!mxs_phy->regmap_anatop) 352 return false; 353 354 if (mxs_phy->port_id == 0) 355 reg = ANADIG_USB1_MISC; 356 else if (mxs_phy->port_id == 1) 357 reg = ANADIG_USB2_MISC; 358 359 regmap_read(mxs_phy->regmap_anatop, reg, &line_state); 360 361 if ((line_state & (dp_bit | dm_bit)) == dm_bit) 362 return true; 363 else 364 return false; 365} 366 367static int mxs_phy_suspend(struct usb_phy *x, int suspend) 368{ 369 int ret; 370 struct mxs_phy *mxs_phy = to_mxs_phy(x); 371 bool low_speed_connection, vbus_is_on; 372 373 low_speed_connection = mxs_phy_is_low_speed_connection(mxs_phy); 374 vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); 375 376 if (suspend) { 377 /* 378 * FIXME: Do not power down RXPWD1PT1 bit for low speed 379 * connect. The low speed connection will have problem at 380 * very rare cases during usb suspend and resume process. 381 */ 382 if (low_speed_connection & vbus_is_on) { 383 /* 384 * If value to be set as pwd value is not 0xffffffff, 385 * several 32Khz cycles are needed. 386 */ 387 mxs_phy_clock_switch_delay(); 388 writel(0xffbfffff, x->io_priv + HW_USBPHY_PWD); 389 } else { 390 writel(0xffffffff, x->io_priv + HW_USBPHY_PWD); 391 } 392 writel(BM_USBPHY_CTRL_CLKGATE, 393 x->io_priv + HW_USBPHY_CTRL_SET); 394 clk_disable_unprepare(mxs_phy->clk); 395 } else { 396 mxs_phy_clock_switch_delay(); 397 ret = clk_prepare_enable(mxs_phy->clk); 398 if (ret) 399 return ret; 400 writel(BM_USBPHY_CTRL_CLKGATE, 401 x->io_priv + HW_USBPHY_CTRL_CLR); 402 writel(0, x->io_priv + HW_USBPHY_PWD); 403 } 404 405 return 0; 406} 407 408static int mxs_phy_set_wakeup(struct usb_phy *x, bool enabled) 409{ 410 struct mxs_phy *mxs_phy = to_mxs_phy(x); 411 u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP | 412 BM_USBPHY_CTRL_ENDPDMCHG_WKUP | 413 BM_USBPHY_CTRL_ENIDCHG_WKUP; 414 if (enabled) { 415 mxs_phy_disconnect_line(mxs_phy, true); 416 writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_SET); 417 } else { 418 writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_CLR); 419 mxs_phy_disconnect_line(mxs_phy, false); 420 } 421 422 return 0; 423} 424 425static int mxs_phy_on_connect(struct usb_phy *phy, 426 enum usb_device_speed speed) 427{ 428 dev_dbg(phy->dev, "%s device has connected\n", 429 (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS"); 430 431 if (speed == USB_SPEED_HIGH) 432 writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, 433 phy->io_priv + HW_USBPHY_CTRL_SET); 434 435 return 0; 436} 437 438static int mxs_phy_on_disconnect(struct usb_phy *phy, 439 enum usb_device_speed speed) 440{ 441 dev_dbg(phy->dev, "%s device has disconnected\n", 442 (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS"); 443 444 /* Sometimes, the speed is not high speed when the error occurs */ 445 if (readl(phy->io_priv + HW_USBPHY_CTRL) & 446 BM_USBPHY_CTRL_ENHOSTDISCONDETECT) 447 writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, 448 phy->io_priv + HW_USBPHY_CTRL_CLR); 449 450 return 0; 451} 452 453static int mxs_phy_probe(struct platform_device *pdev) 454{ 455 struct resource *res; 456 void __iomem *base; 457 struct clk *clk; 458 struct mxs_phy *mxs_phy; 459 int ret; 460 const struct of_device_id *of_id; 461 struct device_node *np = pdev->dev.of_node; 462 463 of_id = of_match_device(mxs_phy_dt_ids, &pdev->dev); 464 if (!of_id) 465 return -ENODEV; 466 467 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 468 base = devm_ioremap_resource(&pdev->dev, res); 469 if (IS_ERR(base)) 470 return PTR_ERR(base); 471 472 clk = devm_clk_get(&pdev->dev, NULL); 473 if (IS_ERR(clk)) { 474 dev_err(&pdev->dev, 475 "can't get the clock, err=%ld", PTR_ERR(clk)); 476 return PTR_ERR(clk); 477 } 478 479 mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL); 480 if (!mxs_phy) 481 return -ENOMEM; 482 483 /* Some SoCs don't have anatop registers */ 484 if (of_get_property(np, "fsl,anatop", NULL)) { 485 mxs_phy->regmap_anatop = syscon_regmap_lookup_by_phandle 486 (np, "fsl,anatop"); 487 if (IS_ERR(mxs_phy->regmap_anatop)) { 488 dev_dbg(&pdev->dev, 489 "failed to find regmap for anatop\n"); 490 return PTR_ERR(mxs_phy->regmap_anatop); 491 } 492 } 493 494 ret = of_alias_get_id(np, "usbphy"); 495 if (ret < 0) 496 dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret); 497 mxs_phy->port_id = ret; 498 499 mxs_phy->phy.io_priv = base; 500 mxs_phy->phy.dev = &pdev->dev; 501 mxs_phy->phy.label = DRIVER_NAME; 502 mxs_phy->phy.init = mxs_phy_init; 503 mxs_phy->phy.shutdown = mxs_phy_shutdown; 504 mxs_phy->phy.set_suspend = mxs_phy_suspend; 505 mxs_phy->phy.notify_connect = mxs_phy_on_connect; 506 mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; 507 mxs_phy->phy.type = USB_PHY_TYPE_USB2; 508 mxs_phy->phy.set_wakeup = mxs_phy_set_wakeup; 509 510 mxs_phy->clk = clk; 511 mxs_phy->data = of_id->data; 512 513 platform_set_drvdata(pdev, mxs_phy); 514 515 device_set_wakeup_capable(&pdev->dev, true); 516 517 return usb_add_phy_dev(&mxs_phy->phy); 518} 519 520static int mxs_phy_remove(struct platform_device *pdev) 521{ 522 struct mxs_phy *mxs_phy = platform_get_drvdata(pdev); 523 524 usb_remove_phy(&mxs_phy->phy); 525 526 return 0; 527} 528 529#ifdef CONFIG_PM_SLEEP 530static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on) 531{ 532 unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR; 533 534 /* If the SoCs don't have anatop, quit */ 535 if (!mxs_phy->regmap_anatop) 536 return; 537 538 if (is_imx6q_phy(mxs_phy)) 539 regmap_write(mxs_phy->regmap_anatop, reg, 540 BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG); 541 else if (is_imx6sl_phy(mxs_phy)) 542 regmap_write(mxs_phy->regmap_anatop, 543 reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL); 544} 545 546static int mxs_phy_system_suspend(struct device *dev) 547{ 548 struct mxs_phy *mxs_phy = dev_get_drvdata(dev); 549 550 if (device_may_wakeup(dev)) 551 mxs_phy_enable_ldo_in_suspend(mxs_phy, true); 552 553 return 0; 554} 555 556static int mxs_phy_system_resume(struct device *dev) 557{ 558 struct mxs_phy *mxs_phy = dev_get_drvdata(dev); 559 560 if (device_may_wakeup(dev)) 561 mxs_phy_enable_ldo_in_suspend(mxs_phy, false); 562 563 return 0; 564} 565#endif /* CONFIG_PM_SLEEP */ 566 567static SIMPLE_DEV_PM_OPS(mxs_phy_pm, mxs_phy_system_suspend, 568 mxs_phy_system_resume); 569 570static struct platform_driver mxs_phy_driver = { 571 .probe = mxs_phy_probe, 572 .remove = mxs_phy_remove, 573 .driver = { 574 .name = DRIVER_NAME, 575 .of_match_table = mxs_phy_dt_ids, 576 .pm = &mxs_phy_pm, 577 }, 578}; 579 580static int __init mxs_phy_module_init(void) 581{ 582 return platform_driver_register(&mxs_phy_driver); 583} 584postcore_initcall(mxs_phy_module_init); 585 586static void __exit mxs_phy_module_exit(void) 587{ 588 platform_driver_unregister(&mxs_phy_driver); 589} 590module_exit(mxs_phy_module_exit); 591 592MODULE_ALIAS("platform:mxs-usb-phy"); 593MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 594MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>"); 595MODULE_DESCRIPTION("Freescale MXS USB PHY driver"); 596MODULE_LICENSE("GPL"); 597