xref: /openbmc/linux/drivers/usb/phy/phy-generic.c (revision e47d9254)
13fa4d734SSebastian Andrzej Siewior /*
23fa4d734SSebastian Andrzej Siewior  * drivers/usb/otg/nop-usb-xceiv.c
33fa4d734SSebastian Andrzej Siewior  *
43fa4d734SSebastian Andrzej Siewior  * NOP USB transceiver for all USB transceiver which are either built-in
53fa4d734SSebastian Andrzej Siewior  * into USB IP or which are mostly autonomous.
63fa4d734SSebastian Andrzej Siewior  *
73fa4d734SSebastian Andrzej Siewior  * Copyright (C) 2009 Texas Instruments Inc
83fa4d734SSebastian Andrzej Siewior  * Author: Ajay Kumar Gupta <ajay.gupta@ti.com>
93fa4d734SSebastian Andrzej Siewior  *
103fa4d734SSebastian Andrzej Siewior  * This program is free software; you can redistribute it and/or modify
113fa4d734SSebastian Andrzej Siewior  * it under the terms of the GNU General Public License as published by
123fa4d734SSebastian Andrzej Siewior  * the Free Software Foundation; either version 2 of the License, or
133fa4d734SSebastian Andrzej Siewior  * (at your option) any later version.
143fa4d734SSebastian Andrzej Siewior  *
153fa4d734SSebastian Andrzej Siewior  * This program is distributed in the hope that it will be useful,
163fa4d734SSebastian Andrzej Siewior  * but WITHOUT ANY WARRANTY; without even the implied warranty of
173fa4d734SSebastian Andrzej Siewior  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
183fa4d734SSebastian Andrzej Siewior  * GNU General Public License for more details.
193fa4d734SSebastian Andrzej Siewior  *
203fa4d734SSebastian Andrzej Siewior  * You should have received a copy of the GNU General Public License
213fa4d734SSebastian Andrzej Siewior  * along with this program; if not, write to the Free Software
223fa4d734SSebastian Andrzej Siewior  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
233fa4d734SSebastian Andrzej Siewior  *
243fa4d734SSebastian Andrzej Siewior  * Current status:
253fa4d734SSebastian Andrzej Siewior  *	This provides a "nop" transceiver for PHYs which are
263fa4d734SSebastian Andrzej Siewior  *	autonomous such as isp1504, isp1707, etc.
273fa4d734SSebastian Andrzej Siewior  */
283fa4d734SSebastian Andrzej Siewior 
293fa4d734SSebastian Andrzej Siewior #include <linux/module.h>
303fa4d734SSebastian Andrzej Siewior #include <linux/platform_device.h>
313fa4d734SSebastian Andrzej Siewior #include <linux/dma-mapping.h>
323fa4d734SSebastian Andrzej Siewior #include <linux/usb/otg.h>
33d7078df6SFelipe Balbi #include <linux/usb/usb_phy_generic.h>
343fa4d734SSebastian Andrzej Siewior #include <linux/slab.h>
353fa4d734SSebastian Andrzej Siewior #include <linux/clk.h>
363fa4d734SSebastian Andrzej Siewior #include <linux/regulator/consumer.h>
373fa4d734SSebastian Andrzej Siewior #include <linux/of.h>
38bd27fa44SRoger Quadros #include <linux/of_gpio.h>
39bd27fa44SRoger Quadros #include <linux/gpio.h>
40bd27fa44SRoger Quadros #include <linux/delay.h>
413fa4d734SSebastian Andrzej Siewior 
4253b6fc28SSebastian Andrzej Siewior #include "phy-generic.h"
433fa4d734SSebastian Andrzej Siewior 
442f36ff69SFelipe Balbi struct platform_device *usb_phy_generic_register(void)
453fa4d734SSebastian Andrzej Siewior {
462f36ff69SFelipe Balbi 	return platform_device_register_simple("usb_phy_generic",
472f36ff69SFelipe Balbi 			PLATFORM_DEVID_AUTO, NULL, 0);
483fa4d734SSebastian Andrzej Siewior }
494525beebSFelipe Balbi EXPORT_SYMBOL_GPL(usb_phy_generic_register);
503fa4d734SSebastian Andrzej Siewior 
512f36ff69SFelipe Balbi void usb_phy_generic_unregister(struct platform_device *pdev)
523fa4d734SSebastian Andrzej Siewior {
532f36ff69SFelipe Balbi 	platform_device_unregister(pdev);
543fa4d734SSebastian Andrzej Siewior }
554525beebSFelipe Balbi EXPORT_SYMBOL_GPL(usb_phy_generic_unregister);
563fa4d734SSebastian Andrzej Siewior 
573fa4d734SSebastian Andrzej Siewior static int nop_set_suspend(struct usb_phy *x, int suspend)
583fa4d734SSebastian Andrzej Siewior {
593fa4d734SSebastian Andrzej Siewior 	return 0;
603fa4d734SSebastian Andrzej Siewior }
613fa4d734SSebastian Andrzej Siewior 
624525beebSFelipe Balbi static void nop_reset_set(struct usb_phy_generic *nop, int asserted)
63bd27fa44SRoger Quadros {
64bd27fa44SRoger Quadros 	int value;
65bd27fa44SRoger Quadros 
66bd27fa44SRoger Quadros 	if (!gpio_is_valid(nop->gpio_reset))
67bd27fa44SRoger Quadros 		return;
68bd27fa44SRoger Quadros 
69bd27fa44SRoger Quadros 	value = asserted;
70bd27fa44SRoger Quadros 	if (nop->reset_active_low)
71bd27fa44SRoger Quadros 		value = !value;
72bd27fa44SRoger Quadros 
73bd27fa44SRoger Quadros 	gpio_set_value_cansleep(nop->gpio_reset, value);
74bd27fa44SRoger Quadros 
75bd27fa44SRoger Quadros 	if (!asserted)
76bd27fa44SRoger Quadros 		usleep_range(10000, 20000);
77bd27fa44SRoger Quadros }
78bd27fa44SRoger Quadros 
7953b6fc28SSebastian Andrzej Siewior int usb_gen_phy_init(struct usb_phy *phy)
803fa4d734SSebastian Andrzej Siewior {
814525beebSFelipe Balbi 	struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
823fa4d734SSebastian Andrzej Siewior 
833fa4d734SSebastian Andrzej Siewior 	if (!IS_ERR(nop->vcc)) {
843fa4d734SSebastian Andrzej Siewior 		if (regulator_enable(nop->vcc))
853fa4d734SSebastian Andrzej Siewior 			dev_err(phy->dev, "Failed to enable power\n");
863fa4d734SSebastian Andrzej Siewior 	}
873fa4d734SSebastian Andrzej Siewior 
883fa4d734SSebastian Andrzej Siewior 	if (!IS_ERR(nop->clk))
894d175f34SMark Brown 		clk_prepare_enable(nop->clk);
903fa4d734SSebastian Andrzej Siewior 
913fa4d734SSebastian Andrzej Siewior 	/* De-assert RESET */
92bd27fa44SRoger Quadros 	nop_reset_set(nop, 0);
933fa4d734SSebastian Andrzej Siewior 
943fa4d734SSebastian Andrzej Siewior 	return 0;
953fa4d734SSebastian Andrzej Siewior }
9653b6fc28SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_gen_phy_init);
973fa4d734SSebastian Andrzej Siewior 
9853b6fc28SSebastian Andrzej Siewior void usb_gen_phy_shutdown(struct usb_phy *phy)
993fa4d734SSebastian Andrzej Siewior {
1004525beebSFelipe Balbi 	struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
1013fa4d734SSebastian Andrzej Siewior 
1023fa4d734SSebastian Andrzej Siewior 	/* Assert RESET */
103bd27fa44SRoger Quadros 	nop_reset_set(nop, 1);
1043fa4d734SSebastian Andrzej Siewior 
1053fa4d734SSebastian Andrzej Siewior 	if (!IS_ERR(nop->clk))
1064d175f34SMark Brown 		clk_disable_unprepare(nop->clk);
1073fa4d734SSebastian Andrzej Siewior 
1083fa4d734SSebastian Andrzej Siewior 	if (!IS_ERR(nop->vcc)) {
1093fa4d734SSebastian Andrzej Siewior 		if (regulator_disable(nop->vcc))
1103fa4d734SSebastian Andrzej Siewior 			dev_err(phy->dev, "Failed to disable power\n");
1113fa4d734SSebastian Andrzej Siewior 	}
1123fa4d734SSebastian Andrzej Siewior }
11353b6fc28SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_gen_phy_shutdown);
1143fa4d734SSebastian Andrzej Siewior 
1153fa4d734SSebastian Andrzej Siewior static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
1163fa4d734SSebastian Andrzej Siewior {
1173fa4d734SSebastian Andrzej Siewior 	if (!otg)
1183fa4d734SSebastian Andrzej Siewior 		return -ENODEV;
1193fa4d734SSebastian Andrzej Siewior 
1203fa4d734SSebastian Andrzej Siewior 	if (!gadget) {
1213fa4d734SSebastian Andrzej Siewior 		otg->gadget = NULL;
1223fa4d734SSebastian Andrzej Siewior 		return -ENODEV;
1233fa4d734SSebastian Andrzej Siewior 	}
1243fa4d734SSebastian Andrzej Siewior 
1253fa4d734SSebastian Andrzej Siewior 	otg->gadget = gadget;
126e47d9254SAntoine Tenart 	otg->state = OTG_STATE_B_IDLE;
1273fa4d734SSebastian Andrzej Siewior 	return 0;
1283fa4d734SSebastian Andrzej Siewior }
1293fa4d734SSebastian Andrzej Siewior 
1303fa4d734SSebastian Andrzej Siewior static int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
1313fa4d734SSebastian Andrzej Siewior {
1323fa4d734SSebastian Andrzej Siewior 	if (!otg)
1333fa4d734SSebastian Andrzej Siewior 		return -ENODEV;
1343fa4d734SSebastian Andrzej Siewior 
1353fa4d734SSebastian Andrzej Siewior 	if (!host) {
1363fa4d734SSebastian Andrzej Siewior 		otg->host = NULL;
1373fa4d734SSebastian Andrzej Siewior 		return -ENODEV;
1383fa4d734SSebastian Andrzej Siewior 	}
1393fa4d734SSebastian Andrzej Siewior 
1403fa4d734SSebastian Andrzej Siewior 	otg->host = host;
1413fa4d734SSebastian Andrzej Siewior 	return 0;
1423fa4d734SSebastian Andrzej Siewior }
1433fa4d734SSebastian Andrzej Siewior 
1444525beebSFelipe Balbi int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
1454525beebSFelipe Balbi 		struct usb_phy_generic_platform_data *pdata)
14653b6fc28SSebastian Andrzej Siewior {
147af9f51c5SFelipe Balbi 	enum usb_phy_type type = USB_PHY_TYPE_USB2;
14853b6fc28SSebastian Andrzej Siewior 	int err;
14953b6fc28SSebastian Andrzej Siewior 
150af9f51c5SFelipe Balbi 	u32 clk_rate = 0;
151af9f51c5SFelipe Balbi 	bool needs_vcc = false;
152af9f51c5SFelipe Balbi 
153af9f51c5SFelipe Balbi 	nop->reset_active_low = true;	/* default behaviour */
154af9f51c5SFelipe Balbi 
155af9f51c5SFelipe Balbi 	if (dev->of_node) {
156af9f51c5SFelipe Balbi 		struct device_node *node = dev->of_node;
15737cfbc42SHeikki Krogerus 		enum of_gpio_flags flags = 0;
158af9f51c5SFelipe Balbi 
159af9f51c5SFelipe Balbi 		if (of_property_read_u32(node, "clock-frequency", &clk_rate))
160af9f51c5SFelipe Balbi 			clk_rate = 0;
161af9f51c5SFelipe Balbi 
162af9f51c5SFelipe Balbi 		needs_vcc = of_property_read_bool(node, "vcc-supply");
163af9f51c5SFelipe Balbi 		nop->gpio_reset = of_get_named_gpio_flags(node, "reset-gpios",
164af9f51c5SFelipe Balbi 								0, &flags);
165af9f51c5SFelipe Balbi 		if (nop->gpio_reset == -EPROBE_DEFER)
166af9f51c5SFelipe Balbi 			return -EPROBE_DEFER;
167af9f51c5SFelipe Balbi 
168af9f51c5SFelipe Balbi 		nop->reset_active_low = flags & OF_GPIO_ACTIVE_LOW;
169af9f51c5SFelipe Balbi 
170af9f51c5SFelipe Balbi 	} else if (pdata) {
171af9f51c5SFelipe Balbi 		type = pdata->type;
172af9f51c5SFelipe Balbi 		clk_rate = pdata->clk_rate;
173af9f51c5SFelipe Balbi 		needs_vcc = pdata->needs_vcc;
174af9f51c5SFelipe Balbi 		nop->gpio_reset = pdata->gpio_reset;
175dc52c574SAaro Koskinen 	} else {
176dc52c574SAaro Koskinen 		nop->gpio_reset = -1;
177af9f51c5SFelipe Balbi 	}
178af9f51c5SFelipe Balbi 
17953b6fc28SSebastian Andrzej Siewior 	nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg),
18053b6fc28SSebastian Andrzej Siewior 			GFP_KERNEL);
18153b6fc28SSebastian Andrzej Siewior 	if (!nop->phy.otg)
18253b6fc28SSebastian Andrzej Siewior 		return -ENOMEM;
18353b6fc28SSebastian Andrzej Siewior 
18453b6fc28SSebastian Andrzej Siewior 	nop->clk = devm_clk_get(dev, "main_clk");
18553b6fc28SSebastian Andrzej Siewior 	if (IS_ERR(nop->clk)) {
18653b6fc28SSebastian Andrzej Siewior 		dev_dbg(dev, "Can't get phy clock: %ld\n",
18753b6fc28SSebastian Andrzej Siewior 					PTR_ERR(nop->clk));
18853b6fc28SSebastian Andrzej Siewior 	}
18953b6fc28SSebastian Andrzej Siewior 
19053b6fc28SSebastian Andrzej Siewior 	if (!IS_ERR(nop->clk) && clk_rate) {
19153b6fc28SSebastian Andrzej Siewior 		err = clk_set_rate(nop->clk, clk_rate);
19253b6fc28SSebastian Andrzej Siewior 		if (err) {
19353b6fc28SSebastian Andrzej Siewior 			dev_err(dev, "Error setting clock rate\n");
19453b6fc28SSebastian Andrzej Siewior 			return err;
19553b6fc28SSebastian Andrzej Siewior 		}
19653b6fc28SSebastian Andrzej Siewior 	}
19753b6fc28SSebastian Andrzej Siewior 
19853b6fc28SSebastian Andrzej Siewior 	nop->vcc = devm_regulator_get(dev, "vcc");
19953b6fc28SSebastian Andrzej Siewior 	if (IS_ERR(nop->vcc)) {
20053b6fc28SSebastian Andrzej Siewior 		dev_dbg(dev, "Error getting vcc regulator: %ld\n",
20153b6fc28SSebastian Andrzej Siewior 					PTR_ERR(nop->vcc));
20253b6fc28SSebastian Andrzej Siewior 		if (needs_vcc)
20353b6fc28SSebastian Andrzej Siewior 			return -EPROBE_DEFER;
20453b6fc28SSebastian Andrzej Siewior 	}
20553b6fc28SSebastian Andrzej Siewior 
206bd27fa44SRoger Quadros 	if (gpio_is_valid(nop->gpio_reset)) {
207bd27fa44SRoger Quadros 		unsigned long gpio_flags;
208bd27fa44SRoger Quadros 
209bd27fa44SRoger Quadros 		/* Assert RESET */
210bd27fa44SRoger Quadros 		if (nop->reset_active_low)
211bd27fa44SRoger Quadros 			gpio_flags = GPIOF_OUT_INIT_LOW;
212bd27fa44SRoger Quadros 		else
213bd27fa44SRoger Quadros 			gpio_flags = GPIOF_OUT_INIT_HIGH;
214bd27fa44SRoger Quadros 
215bd27fa44SRoger Quadros 		err = devm_gpio_request_one(dev, nop->gpio_reset,
216bd27fa44SRoger Quadros 						gpio_flags, dev_name(dev));
217bd27fa44SRoger Quadros 		if (err) {
218bd27fa44SRoger Quadros 			dev_err(dev, "Error requesting RESET GPIO %d\n",
219bd27fa44SRoger Quadros 					nop->gpio_reset);
220bd27fa44SRoger Quadros 			return err;
221bd27fa44SRoger Quadros 		}
22253b6fc28SSebastian Andrzej Siewior 	}
22353b6fc28SSebastian Andrzej Siewior 
22453b6fc28SSebastian Andrzej Siewior 	nop->dev		= dev;
22553b6fc28SSebastian Andrzej Siewior 	nop->phy.dev		= nop->dev;
22653b6fc28SSebastian Andrzej Siewior 	nop->phy.label		= "nop-xceiv";
22753b6fc28SSebastian Andrzej Siewior 	nop->phy.set_suspend	= nop_set_suspend;
22853b6fc28SSebastian Andrzej Siewior 	nop->phy.type		= type;
22953b6fc28SSebastian Andrzej Siewior 
230e47d9254SAntoine Tenart 	nop->phy.otg->state		= OTG_STATE_UNDEFINED;
23153b6fc28SSebastian Andrzej Siewior 	nop->phy.otg->phy		= &nop->phy;
23253b6fc28SSebastian Andrzej Siewior 	nop->phy.otg->set_host		= nop_set_host;
23353b6fc28SSebastian Andrzej Siewior 	nop->phy.otg->set_peripheral	= nop_set_peripheral;
23453b6fc28SSebastian Andrzej Siewior 
23553b6fc28SSebastian Andrzej Siewior 	return 0;
23653b6fc28SSebastian Andrzej Siewior }
23753b6fc28SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);
23853b6fc28SSebastian Andrzej Siewior 
2394525beebSFelipe Balbi static int usb_phy_generic_probe(struct platform_device *pdev)
2403fa4d734SSebastian Andrzej Siewior {
2413fa4d734SSebastian Andrzej Siewior 	struct device *dev = &pdev->dev;
2424525beebSFelipe Balbi 	struct usb_phy_generic	*nop;
2433fa4d734SSebastian Andrzej Siewior 	int err;
2443fa4d734SSebastian Andrzej Siewior 
24553b6fc28SSebastian Andrzej Siewior 	nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL);
24653b6fc28SSebastian Andrzej Siewior 	if (!nop)
24753b6fc28SSebastian Andrzej Siewior 		return -ENOMEM;
2483fa4d734SSebastian Andrzej Siewior 
249af9f51c5SFelipe Balbi 	err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev));
25053b6fc28SSebastian Andrzej Siewior 	if (err)
2513fa4d734SSebastian Andrzej Siewior 		return err;
2523fa4d734SSebastian Andrzej Siewior 
25353b6fc28SSebastian Andrzej Siewior 	nop->phy.init		= usb_gen_phy_init;
25453b6fc28SSebastian Andrzej Siewior 	nop->phy.shutdown	= usb_gen_phy_shutdown;
2553fa4d734SSebastian Andrzej Siewior 
2563fa4d734SSebastian Andrzej Siewior 	err = usb_add_phy_dev(&nop->phy);
2573fa4d734SSebastian Andrzej Siewior 	if (err) {
2583fa4d734SSebastian Andrzej Siewior 		dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
2593fa4d734SSebastian Andrzej Siewior 			err);
2604d175f34SMark Brown 		return err;
2613fa4d734SSebastian Andrzej Siewior 	}
2623fa4d734SSebastian Andrzej Siewior 
2633fa4d734SSebastian Andrzej Siewior 	platform_set_drvdata(pdev, nop);
2643fa4d734SSebastian Andrzej Siewior 
2653fa4d734SSebastian Andrzej Siewior 	return 0;
2663fa4d734SSebastian Andrzej Siewior }
2673fa4d734SSebastian Andrzej Siewior 
2684525beebSFelipe Balbi static int usb_phy_generic_remove(struct platform_device *pdev)
2693fa4d734SSebastian Andrzej Siewior {
2704525beebSFelipe Balbi 	struct usb_phy_generic *nop = platform_get_drvdata(pdev);
2713fa4d734SSebastian Andrzej Siewior 
2723fa4d734SSebastian Andrzej Siewior 	usb_remove_phy(&nop->phy);
2733fa4d734SSebastian Andrzej Siewior 
2743fa4d734SSebastian Andrzej Siewior 	return 0;
2753fa4d734SSebastian Andrzej Siewior }
2763fa4d734SSebastian Andrzej Siewior 
2773fa4d734SSebastian Andrzej Siewior static const struct of_device_id nop_xceiv_dt_ids[] = {
2783fa4d734SSebastian Andrzej Siewior 	{ .compatible = "usb-nop-xceiv" },
2793fa4d734SSebastian Andrzej Siewior 	{ }
2803fa4d734SSebastian Andrzej Siewior };
2813fa4d734SSebastian Andrzej Siewior 
2823fa4d734SSebastian Andrzej Siewior MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids);
2833fa4d734SSebastian Andrzej Siewior 
2844525beebSFelipe Balbi static struct platform_driver usb_phy_generic_driver = {
2854525beebSFelipe Balbi 	.probe		= usb_phy_generic_probe,
2864525beebSFelipe Balbi 	.remove		= usb_phy_generic_remove,
2873fa4d734SSebastian Andrzej Siewior 	.driver		= {
2884525beebSFelipe Balbi 		.name	= "usb_phy_generic",
2893fa4d734SSebastian Andrzej Siewior 		.owner	= THIS_MODULE,
2903fa4d734SSebastian Andrzej Siewior 		.of_match_table = nop_xceiv_dt_ids,
2913fa4d734SSebastian Andrzej Siewior 	},
2923fa4d734SSebastian Andrzej Siewior };
2933fa4d734SSebastian Andrzej Siewior 
2944525beebSFelipe Balbi static int __init usb_phy_generic_init(void)
2953fa4d734SSebastian Andrzej Siewior {
2964525beebSFelipe Balbi 	return platform_driver_register(&usb_phy_generic_driver);
2973fa4d734SSebastian Andrzej Siewior }
2984525beebSFelipe Balbi subsys_initcall(usb_phy_generic_init);
2993fa4d734SSebastian Andrzej Siewior 
3004525beebSFelipe Balbi static void __exit usb_phy_generic_exit(void)
3013fa4d734SSebastian Andrzej Siewior {
3024525beebSFelipe Balbi 	platform_driver_unregister(&usb_phy_generic_driver);
3033fa4d734SSebastian Andrzej Siewior }
3044525beebSFelipe Balbi module_exit(usb_phy_generic_exit);
3053fa4d734SSebastian Andrzej Siewior 
3064525beebSFelipe Balbi MODULE_ALIAS("platform:usb_phy_generic");
3073fa4d734SSebastian Andrzej Siewior MODULE_AUTHOR("Texas Instruments Inc");
3083fa4d734SSebastian Andrzej Siewior MODULE_DESCRIPTION("NOP USB Transceiver driver");
3093fa4d734SSebastian Andrzej Siewior MODULE_LICENSE("GPL");
310