1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Motorola Mapphone MDM6600 modem GPIO controlled USB PHY driver 4 * Copyright (C) 2018 Tony Lindgren <tony@atomide.com> 5 */ 6 7 #include <linux/delay.h> 8 #include <linux/err.h> 9 #include <linux/io.h> 10 #include <linux/interrupt.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 #include <linux/platform_device.h> 14 #include <linux/slab.h> 15 16 #include <linux/gpio/consumer.h> 17 #include <linux/of_platform.h> 18 #include <linux/phy/phy.h> 19 20 #define PHY_MDM6600_PHY_DELAY_MS 4000 /* PHY enable 2.2s to 3.5s */ 21 #define PHY_MDM6600_ENABLED_DELAY_MS 8000 /* 8s more total for MDM6600 */ 22 23 enum phy_mdm6600_ctrl_lines { 24 PHY_MDM6600_ENABLE, /* USB PHY enable */ 25 PHY_MDM6600_POWER, /* Device power */ 26 PHY_MDM6600_RESET, /* Device reset */ 27 PHY_MDM6600_NR_CTRL_LINES, 28 }; 29 30 enum phy_mdm6600_bootmode_lines { 31 PHY_MDM6600_MODE0, /* out USB mode0 and OOB wake */ 32 PHY_MDM6600_MODE1, /* out USB mode1, in OOB wake */ 33 PHY_MDM6600_NR_MODE_LINES, 34 }; 35 36 enum phy_mdm6600_cmd_lines { 37 PHY_MDM6600_CMD0, 38 PHY_MDM6600_CMD1, 39 PHY_MDM6600_CMD2, 40 PHY_MDM6600_NR_CMD_LINES, 41 }; 42 43 enum phy_mdm6600_status_lines { 44 PHY_MDM6600_STATUS0, 45 PHY_MDM6600_STATUS1, 46 PHY_MDM6600_STATUS2, 47 PHY_MDM6600_NR_STATUS_LINES, 48 }; 49 50 /* 51 * MDM6600 command codes. These are based on Motorola Mapphone Linux 52 * kernel tree. 53 */ 54 enum phy_mdm6600_cmd { 55 PHY_MDM6600_CMD_BP_PANIC_ACK, 56 PHY_MDM6600_CMD_DATA_ONLY_BYPASS, /* Reroute USB to CPCAP PHY */ 57 PHY_MDM6600_CMD_FULL_BYPASS, /* Reroute USB to CPCAP PHY */ 58 PHY_MDM6600_CMD_NO_BYPASS, /* Request normal USB mode */ 59 PHY_MDM6600_CMD_BP_SHUTDOWN_REQ, /* Request device power off */ 60 PHY_MDM6600_CMD_BP_UNKNOWN_5, 61 PHY_MDM6600_CMD_BP_UNKNOWN_6, 62 PHY_MDM6600_CMD_UNDEFINED, 63 }; 64 65 /* 66 * MDM6600 status codes. These are based on Motorola Mapphone Linux 67 * kernel tree. 68 */ 69 enum phy_mdm6600_status { 70 PHY_MDM6600_STATUS_PANIC, /* Seems to be really off */ 71 PHY_MDM6600_STATUS_PANIC_BUSY_WAIT, 72 PHY_MDM6600_STATUS_QC_DLOAD, 73 PHY_MDM6600_STATUS_RAM_DOWNLOADER, /* MDM6600 USB flashing mode */ 74 PHY_MDM6600_STATUS_PHONE_CODE_AWAKE, /* MDM6600 normal USB mode */ 75 PHY_MDM6600_STATUS_PHONE_CODE_ASLEEP, 76 PHY_MDM6600_STATUS_SHUTDOWN_ACK, 77 PHY_MDM6600_STATUS_UNDEFINED, 78 }; 79 80 static const char * const 81 phy_mdm6600_status_name[] = { 82 "off", "busy", "qc_dl", "ram_dl", "awake", 83 "asleep", "shutdown", "undefined", 84 }; 85 86 struct phy_mdm6600 { 87 struct device *dev; 88 struct phy *generic_phy; 89 struct phy_provider *phy_provider; 90 struct gpio_desc *ctrl_gpios[PHY_MDM6600_NR_CTRL_LINES]; 91 struct gpio_descs *mode_gpios; 92 struct gpio_descs *status_gpios; 93 struct gpio_descs *cmd_gpios; 94 struct delayed_work bootup_work; 95 struct delayed_work status_work; 96 struct completion ack; 97 bool enabled; /* mdm6600 phy enabled */ 98 bool running; /* mdm6600 boot done */ 99 int status; 100 }; 101 102 static int phy_mdm6600_init(struct phy *x) 103 { 104 struct phy_mdm6600 *ddata = phy_get_drvdata(x); 105 struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE]; 106 107 if (!ddata->enabled) 108 return -EPROBE_DEFER; 109 110 gpiod_set_value_cansleep(enable_gpio, 0); 111 112 return 0; 113 } 114 115 static int phy_mdm6600_power_on(struct phy *x) 116 { 117 struct phy_mdm6600 *ddata = phy_get_drvdata(x); 118 struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE]; 119 120 if (!ddata->enabled) 121 return -ENODEV; 122 123 gpiod_set_value_cansleep(enable_gpio, 1); 124 125 return 0; 126 } 127 128 static int phy_mdm6600_power_off(struct phy *x) 129 { 130 struct phy_mdm6600 *ddata = phy_get_drvdata(x); 131 struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE]; 132 133 if (!ddata->enabled) 134 return -ENODEV; 135 136 gpiod_set_value_cansleep(enable_gpio, 0); 137 138 return 0; 139 } 140 141 static const struct phy_ops gpio_usb_ops = { 142 .init = phy_mdm6600_init, 143 .power_on = phy_mdm6600_power_on, 144 .power_off = phy_mdm6600_power_off, 145 .owner = THIS_MODULE, 146 }; 147 148 /** 149 * phy_mdm6600_cmd() - send a command request to mdm6600 150 * @ddata: device driver data 151 * 152 * Configures the three command request GPIOs to the specified value. 153 */ 154 static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val) 155 { 156 int values[PHY_MDM6600_NR_CMD_LINES]; 157 int i; 158 159 val &= (1 << PHY_MDM6600_NR_CMD_LINES) - 1; 160 for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++) 161 values[i] = (val & BIT(i)) >> i; 162 163 gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES, 164 ddata->cmd_gpios->desc, values); 165 } 166 167 /** 168 * phy_mdm6600_status() - read mdm6600 status lines 169 * @ddata: device driver data 170 */ 171 static void phy_mdm6600_status(struct work_struct *work) 172 { 173 struct phy_mdm6600 *ddata; 174 struct device *dev; 175 int values[PHY_MDM6600_NR_STATUS_LINES]; 176 int error, i, val = 0; 177 178 ddata = container_of(work, struct phy_mdm6600, status_work.work); 179 dev = ddata->dev; 180 181 error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES, 182 ddata->status_gpios->desc, 183 values); 184 if (error) 185 return; 186 187 for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++) { 188 val |= values[i] << i; 189 dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n", 190 __func__, i, values[i], val); 191 } 192 ddata->status = val; 193 194 dev_info(dev, "modem status: %i %s\n", 195 ddata->status, 196 phy_mdm6600_status_name[ddata->status & 7]); 197 complete(&ddata->ack); 198 } 199 200 static irqreturn_t phy_mdm6600_irq_thread(int irq, void *data) 201 { 202 struct phy_mdm6600 *ddata = data; 203 204 schedule_delayed_work(&ddata->status_work, msecs_to_jiffies(10)); 205 206 return IRQ_HANDLED; 207 } 208 209 /** 210 * phy_mdm6600_wakeirq_thread - handle mode1 line OOB wake after booting 211 * @irq: interrupt 212 * @data: interrupt handler data 213 * 214 * GPIO mode1 is used initially as output to configure the USB boot 215 * mode for mdm6600. After booting it is used as input for OOB wake 216 * signal from mdm6600 to the SoC. Just use it for debug info only 217 * for now. 218 */ 219 static irqreturn_t phy_mdm6600_wakeirq_thread(int irq, void *data) 220 { 221 struct phy_mdm6600 *ddata = data; 222 struct gpio_desc *mode_gpio1; 223 224 mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1]; 225 dev_dbg(ddata->dev, "OOB wake on mode_gpio1: %i\n", 226 gpiod_get_value(mode_gpio1)); 227 228 return IRQ_HANDLED; 229 } 230 231 /** 232 * phy_mdm6600_init_irq() - initialize mdm6600 status IRQ lines 233 * @ddata: device driver data 234 */ 235 static void phy_mdm6600_init_irq(struct phy_mdm6600 *ddata) 236 { 237 struct device *dev = ddata->dev; 238 int i, error, irq; 239 240 for (i = PHY_MDM6600_STATUS0; 241 i <= PHY_MDM6600_STATUS2; i++) { 242 struct gpio_desc *gpio = ddata->status_gpios->desc[i]; 243 244 irq = gpiod_to_irq(gpio); 245 if (irq <= 0) 246 continue; 247 248 error = devm_request_threaded_irq(dev, irq, NULL, 249 phy_mdm6600_irq_thread, 250 IRQF_TRIGGER_RISING | 251 IRQF_TRIGGER_FALLING | 252 IRQF_ONESHOT, 253 "mdm6600", 254 ddata); 255 if (error) 256 dev_warn(dev, "no modem status irq%i: %i\n", 257 irq, error); 258 } 259 } 260 261 struct phy_mdm6600_map { 262 const char *name; 263 int direction; 264 }; 265 266 static const struct phy_mdm6600_map 267 phy_mdm6600_ctrl_gpio_map[PHY_MDM6600_NR_CTRL_LINES] = { 268 { "enable", GPIOD_OUT_LOW, }, /* low = phy disabled */ 269 { "power", GPIOD_OUT_LOW, }, /* low = off */ 270 { "reset", GPIOD_OUT_HIGH, }, /* high = reset */ 271 }; 272 273 /** 274 * phy_mdm6600_init_lines() - initialize mdm6600 GPIO lines 275 * @ddata: device driver data 276 */ 277 static int phy_mdm6600_init_lines(struct phy_mdm6600 *ddata) 278 { 279 struct device *dev = ddata->dev; 280 int i; 281 282 /* MDM6600 control lines */ 283 for (i = 0; i < ARRAY_SIZE(phy_mdm6600_ctrl_gpio_map); i++) { 284 const struct phy_mdm6600_map *map = 285 &phy_mdm6600_ctrl_gpio_map[i]; 286 struct gpio_desc **gpio = &ddata->ctrl_gpios[i]; 287 288 *gpio = devm_gpiod_get(dev, map->name, map->direction); 289 if (IS_ERR(*gpio)) { 290 dev_info(dev, "gpio %s error %li\n", 291 map->name, PTR_ERR(*gpio)); 292 return PTR_ERR(*gpio); 293 } 294 } 295 296 /* MDM6600 USB start-up mode output lines */ 297 ddata->mode_gpios = devm_gpiod_get_array(dev, "motorola,mode", 298 GPIOD_OUT_LOW); 299 if (IS_ERR(ddata->mode_gpios)) 300 return PTR_ERR(ddata->mode_gpios); 301 302 if (ddata->mode_gpios->ndescs != PHY_MDM6600_NR_MODE_LINES) 303 return -EINVAL; 304 305 /* MDM6600 status input lines */ 306 ddata->status_gpios = devm_gpiod_get_array(dev, "motorola,status", 307 GPIOD_IN); 308 if (IS_ERR(ddata->status_gpios)) 309 return PTR_ERR(ddata->status_gpios); 310 311 if (ddata->status_gpios->ndescs != PHY_MDM6600_NR_STATUS_LINES) 312 return -EINVAL; 313 314 /* MDM6600 cmd output lines */ 315 ddata->cmd_gpios = devm_gpiod_get_array(dev, "motorola,cmd", 316 GPIOD_OUT_LOW); 317 if (IS_ERR(ddata->cmd_gpios)) 318 return PTR_ERR(ddata->cmd_gpios); 319 320 if (ddata->cmd_gpios->ndescs != PHY_MDM6600_NR_CMD_LINES) 321 return -EINVAL; 322 323 return 0; 324 } 325 326 /** 327 * phy_mdm6600_device_power_on() - power on mdm6600 device 328 * @ddata: device driver data 329 * 330 * To get the integrated USB phy in MDM6600 takes some hoops. We must ensure 331 * the shared USB bootmode GPIOs are configured, then request modem start-up, 332 * reset and power-up.. And then we need to recycle the shared USB bootmode 333 * GPIOs as they are also used for Out of Band (OOB) wake for the USB and 334 * TS 27.010 serial mux. 335 */ 336 static int phy_mdm6600_device_power_on(struct phy_mdm6600 *ddata) 337 { 338 struct gpio_desc *mode_gpio0, *mode_gpio1, *reset_gpio, *power_gpio; 339 int error = 0, wakeirq; 340 341 mode_gpio0 = ddata->mode_gpios->desc[PHY_MDM6600_MODE0]; 342 mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1]; 343 reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET]; 344 power_gpio = ddata->ctrl_gpios[PHY_MDM6600_POWER]; 345 346 /* 347 * Shared GPIOs must be low for normal USB mode. After booting 348 * they are used for OOB wake signaling. These can be also used 349 * to configure USB flashing mode later on based on a module 350 * parameter. 351 */ 352 gpiod_set_value_cansleep(mode_gpio0, 0); 353 gpiod_set_value_cansleep(mode_gpio1, 0); 354 355 /* Request start-up mode */ 356 phy_mdm6600_cmd(ddata, PHY_MDM6600_CMD_NO_BYPASS); 357 358 /* Request a reset first */ 359 gpiod_set_value_cansleep(reset_gpio, 0); 360 msleep(100); 361 362 /* Toggle power GPIO to request mdm6600 to start */ 363 gpiod_set_value_cansleep(power_gpio, 1); 364 msleep(100); 365 gpiod_set_value_cansleep(power_gpio, 0); 366 367 /* 368 * Looks like the USB PHY needs between 2.2 to 4 seconds. 369 * If we try to use it before that, we will get L3 errors 370 * from omap-usb-host trying to access the PHY. See also 371 * phy_mdm6600_init() for -EPROBE_DEFER. 372 */ 373 msleep(PHY_MDM6600_PHY_DELAY_MS); 374 ddata->enabled = true; 375 376 /* Booting up the rest of MDM6600 will take total about 8 seconds */ 377 dev_info(ddata->dev, "Waiting for power up request to complete..\n"); 378 if (wait_for_completion_timeout(&ddata->ack, 379 msecs_to_jiffies(PHY_MDM6600_ENABLED_DELAY_MS))) { 380 if (ddata->status > PHY_MDM6600_STATUS_PANIC && 381 ddata->status < PHY_MDM6600_STATUS_SHUTDOWN_ACK) 382 dev_info(ddata->dev, "Powered up OK\n"); 383 } else { 384 ddata->enabled = false; 385 error = -ETIMEDOUT; 386 dev_err(ddata->dev, "Timed out powering up\n"); 387 } 388 389 /* Reconfigure mode1 GPIO as input for OOB wake */ 390 gpiod_direction_input(mode_gpio1); 391 392 wakeirq = gpiod_to_irq(mode_gpio1); 393 if (wakeirq <= 0) 394 return wakeirq; 395 396 error = devm_request_threaded_irq(ddata->dev, wakeirq, NULL, 397 phy_mdm6600_wakeirq_thread, 398 IRQF_TRIGGER_RISING | 399 IRQF_TRIGGER_FALLING | 400 IRQF_ONESHOT, 401 "mdm6600-wake", 402 ddata); 403 if (error) 404 dev_warn(ddata->dev, "no modem wakeirq irq%i: %i\n", 405 wakeirq, error); 406 407 ddata->running = true; 408 409 return error; 410 } 411 412 /** 413 * phy_mdm6600_device_power_off() - power off mdm6600 device 414 * @ddata: device driver data 415 */ 416 static void phy_mdm6600_device_power_off(struct phy_mdm6600 *ddata) 417 { 418 struct gpio_desc *reset_gpio = 419 ddata->ctrl_gpios[PHY_MDM6600_RESET]; 420 421 ddata->enabled = false; 422 phy_mdm6600_cmd(ddata, PHY_MDM6600_CMD_BP_SHUTDOWN_REQ); 423 msleep(100); 424 425 gpiod_set_value_cansleep(reset_gpio, 1); 426 427 dev_info(ddata->dev, "Waiting for power down request to complete.. "); 428 if (wait_for_completion_timeout(&ddata->ack, 429 msecs_to_jiffies(5000))) { 430 if (ddata->status == PHY_MDM6600_STATUS_PANIC) 431 dev_info(ddata->dev, "Powered down OK\n"); 432 } else { 433 dev_err(ddata->dev, "Timed out powering down\n"); 434 } 435 } 436 437 static void phy_mdm6600_deferred_power_on(struct work_struct *work) 438 { 439 struct phy_mdm6600 *ddata; 440 int error; 441 442 ddata = container_of(work, struct phy_mdm6600, bootup_work.work); 443 444 error = phy_mdm6600_device_power_on(ddata); 445 if (error) 446 dev_err(ddata->dev, "Device not functional\n"); 447 } 448 449 static const struct of_device_id phy_mdm6600_id_table[] = { 450 { .compatible = "motorola,mapphone-mdm6600", }, 451 {}, 452 }; 453 MODULE_DEVICE_TABLE(of, phy_mdm6600_id_table); 454 455 static int phy_mdm6600_probe(struct platform_device *pdev) 456 { 457 struct phy_mdm6600 *ddata; 458 int error; 459 460 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 461 if (!ddata) 462 return -ENOMEM; 463 464 INIT_DELAYED_WORK(&ddata->bootup_work, 465 phy_mdm6600_deferred_power_on); 466 INIT_DELAYED_WORK(&ddata->status_work, phy_mdm6600_status); 467 init_completion(&ddata->ack); 468 469 ddata->dev = &pdev->dev; 470 platform_set_drvdata(pdev, ddata); 471 472 error = phy_mdm6600_init_lines(ddata); 473 if (error) 474 return error; 475 476 phy_mdm6600_init_irq(ddata); 477 478 ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops); 479 if (IS_ERR(ddata->generic_phy)) { 480 error = PTR_ERR(ddata->generic_phy); 481 goto cleanup; 482 } 483 484 phy_set_drvdata(ddata->generic_phy, ddata); 485 486 ddata->phy_provider = 487 devm_of_phy_provider_register(ddata->dev, 488 of_phy_simple_xlate); 489 if (IS_ERR(ddata->phy_provider)) { 490 error = PTR_ERR(ddata->phy_provider); 491 goto cleanup; 492 } 493 494 schedule_delayed_work(&ddata->bootup_work, 0); 495 496 /* 497 * See phy_mdm6600_device_power_on(). We should be able 498 * to remove this eventually when ohci-platform can deal 499 * with -EPROBE_DEFER. 500 */ 501 msleep(PHY_MDM6600_PHY_DELAY_MS + 500); 502 503 return 0; 504 505 cleanup: 506 phy_mdm6600_device_power_off(ddata); 507 return error; 508 } 509 510 static int phy_mdm6600_remove(struct platform_device *pdev) 511 { 512 struct phy_mdm6600 *ddata = platform_get_drvdata(pdev); 513 struct gpio_desc *reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET]; 514 515 if (!ddata->running) 516 wait_for_completion_timeout(&ddata->ack, 517 msecs_to_jiffies(PHY_MDM6600_ENABLED_DELAY_MS)); 518 519 gpiod_set_value_cansleep(reset_gpio, 1); 520 phy_mdm6600_device_power_off(ddata); 521 522 cancel_delayed_work_sync(&ddata->bootup_work); 523 cancel_delayed_work_sync(&ddata->status_work); 524 525 return 0; 526 } 527 528 static struct platform_driver phy_mdm6600_driver = { 529 .probe = phy_mdm6600_probe, 530 .remove = phy_mdm6600_remove, 531 .driver = { 532 .name = "phy-mapphone-mdm6600", 533 .of_match_table = of_match_ptr(phy_mdm6600_id_table), 534 }, 535 }; 536 537 module_platform_driver(phy_mdm6600_driver); 538 539 MODULE_ALIAS("platform:gpio_usb"); 540 MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); 541 MODULE_DESCRIPTION("mdm6600 gpio usb phy driver"); 542 MODULE_LICENSE("GPL v2"); 543