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