162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/module.h>
362306a36Sopenharmony_ci#include <linux/platform_device.h>
462306a36Sopenharmony_ci#include <linux/err.h>
562306a36Sopenharmony_ci#include <linux/of.h>
662306a36Sopenharmony_ci#include <linux/io.h>
762306a36Sopenharmony_ci#include <linux/delay.h>
862306a36Sopenharmony_ci#include <linux/usb/otg.h>
962306a36Sopenharmony_ci#include "phy-am335x-control.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistruct am335x_control_usb {
1262306a36Sopenharmony_ci	struct device *dev;
1362306a36Sopenharmony_ci	void __iomem *phy_reg;
1462306a36Sopenharmony_ci	void __iomem *wkup;
1562306a36Sopenharmony_ci	spinlock_t lock;
1662306a36Sopenharmony_ci	struct phy_control phy_ctrl;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define AM335X_USB0_CTRL		0x0
2062306a36Sopenharmony_ci#define AM335X_USB1_CTRL		0x8
2162306a36Sopenharmony_ci#define AM335x_USB_WKUP			0x0
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define USBPHY_CM_PWRDN		(1 << 0)
2462306a36Sopenharmony_ci#define USBPHY_OTG_PWRDN	(1 << 1)
2562306a36Sopenharmony_ci#define USBPHY_OTGVDET_EN	(1 << 19)
2662306a36Sopenharmony_ci#define USBPHY_OTGSESSEND_EN	(1 << 20)
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define AM335X_PHY0_WK_EN	(1 << 0)
2962306a36Sopenharmony_ci#define AM335X_PHY1_WK_EN	(1 << 8)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void am335x_phy_wkup(struct  phy_control *phy_ctrl, u32 id, bool on)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct am335x_control_usb *usb_ctrl;
3462306a36Sopenharmony_ci	u32 val;
3562306a36Sopenharmony_ci	u32 reg;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	usb_ctrl = container_of(phy_ctrl, struct am335x_control_usb, phy_ctrl);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	switch (id) {
4062306a36Sopenharmony_ci	case 0:
4162306a36Sopenharmony_ci		reg = AM335X_PHY0_WK_EN;
4262306a36Sopenharmony_ci		break;
4362306a36Sopenharmony_ci	case 1:
4462306a36Sopenharmony_ci		reg = AM335X_PHY1_WK_EN;
4562306a36Sopenharmony_ci		break;
4662306a36Sopenharmony_ci	default:
4762306a36Sopenharmony_ci		WARN_ON(1);
4862306a36Sopenharmony_ci		return;
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	spin_lock(&usb_ctrl->lock);
5262306a36Sopenharmony_ci	val = readl(usb_ctrl->wkup);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (on)
5562306a36Sopenharmony_ci		val |= reg;
5662306a36Sopenharmony_ci	else
5762306a36Sopenharmony_ci		val &= ~reg;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	writel(val, usb_ctrl->wkup);
6062306a36Sopenharmony_ci	spin_unlock(&usb_ctrl->lock);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void am335x_phy_power(struct phy_control *phy_ctrl, u32 id,
6462306a36Sopenharmony_ci				enum usb_dr_mode dr_mode, bool on)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct am335x_control_usb *usb_ctrl;
6762306a36Sopenharmony_ci	u32 val;
6862306a36Sopenharmony_ci	u32 reg;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	usb_ctrl = container_of(phy_ctrl, struct am335x_control_usb, phy_ctrl);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	switch (id) {
7362306a36Sopenharmony_ci	case 0:
7462306a36Sopenharmony_ci		reg = AM335X_USB0_CTRL;
7562306a36Sopenharmony_ci		break;
7662306a36Sopenharmony_ci	case 1:
7762306a36Sopenharmony_ci		reg = AM335X_USB1_CTRL;
7862306a36Sopenharmony_ci		break;
7962306a36Sopenharmony_ci	default:
8062306a36Sopenharmony_ci		WARN_ON(1);
8162306a36Sopenharmony_ci		return;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	val = readl(usb_ctrl->phy_reg + reg);
8562306a36Sopenharmony_ci	if (on) {
8662306a36Sopenharmony_ci		if (dr_mode == USB_DR_MODE_HOST) {
8762306a36Sopenharmony_ci			val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN |
8862306a36Sopenharmony_ci					USBPHY_OTGVDET_EN);
8962306a36Sopenharmony_ci			val |= USBPHY_OTGSESSEND_EN;
9062306a36Sopenharmony_ci		} else {
9162306a36Sopenharmony_ci			val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN);
9262306a36Sopenharmony_ci			val |= USBPHY_OTGVDET_EN | USBPHY_OTGSESSEND_EN;
9362306a36Sopenharmony_ci		}
9462306a36Sopenharmony_ci	} else {
9562306a36Sopenharmony_ci		val |= USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	writel(val, usb_ctrl->phy_reg + reg);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/*
10162306a36Sopenharmony_ci	 * Give the PHY ~1ms to complete the power up operation.
10262306a36Sopenharmony_ci	 * Tests have shown unstable behaviour if other USB PHY related
10362306a36Sopenharmony_ci	 * registers are written too shortly after such a transition.
10462306a36Sopenharmony_ci	 */
10562306a36Sopenharmony_ci	if (on)
10662306a36Sopenharmony_ci		mdelay(1);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic const struct phy_control ctrl_am335x = {
11062306a36Sopenharmony_ci	.phy_power = am335x_phy_power,
11162306a36Sopenharmony_ci	.phy_wkup = am335x_phy_wkup,
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic const struct of_device_id omap_control_usb_id_table[] = {
11562306a36Sopenharmony_ci	{ .compatible = "ti,am335x-usb-ctrl-module", .data = &ctrl_am335x },
11662306a36Sopenharmony_ci	{}
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic struct platform_driver am335x_control_driver;
12162306a36Sopenharmony_cistatic int match(struct device *dev, const void *data)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	const struct device_node *node = (const struct device_node *)data;
12462306a36Sopenharmony_ci	return dev->of_node == node &&
12562306a36Sopenharmony_ci		dev->driver == &am335x_control_driver.driver;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistruct phy_control *am335x_get_phy_control(struct device *dev)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct device_node *node;
13162306a36Sopenharmony_ci	struct am335x_control_usb *ctrl_usb;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	node = of_parse_phandle(dev->of_node, "ti,ctrl_mod", 0);
13462306a36Sopenharmony_ci	if (!node)
13562306a36Sopenharmony_ci		return NULL;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	dev = bus_find_device(&platform_bus_type, NULL, node, match);
13862306a36Sopenharmony_ci	of_node_put(node);
13962306a36Sopenharmony_ci	if (!dev)
14062306a36Sopenharmony_ci		return NULL;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	ctrl_usb = dev_get_drvdata(dev);
14362306a36Sopenharmony_ci	put_device(dev);
14462306a36Sopenharmony_ci	if (!ctrl_usb)
14562306a36Sopenharmony_ci		return NULL;
14662306a36Sopenharmony_ci	return &ctrl_usb->phy_ctrl;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(am335x_get_phy_control);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int am335x_control_usb_probe(struct platform_device *pdev)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	struct am335x_control_usb *ctrl_usb;
15362306a36Sopenharmony_ci	const struct of_device_id *of_id;
15462306a36Sopenharmony_ci	const struct phy_control *phy_ctrl;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	of_id = of_match_node(omap_control_usb_id_table, pdev->dev.of_node);
15762306a36Sopenharmony_ci	if (!of_id)
15862306a36Sopenharmony_ci		return -EINVAL;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	phy_ctrl = of_id->data;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	ctrl_usb = devm_kzalloc(&pdev->dev, sizeof(*ctrl_usb), GFP_KERNEL);
16362306a36Sopenharmony_ci	if (!ctrl_usb)
16462306a36Sopenharmony_ci		return -ENOMEM;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	ctrl_usb->dev = &pdev->dev;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	ctrl_usb->phy_reg = devm_platform_ioremap_resource_byname(pdev, "phy_ctrl");
16962306a36Sopenharmony_ci	if (IS_ERR(ctrl_usb->phy_reg))
17062306a36Sopenharmony_ci		return PTR_ERR(ctrl_usb->phy_reg);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	ctrl_usb->wkup = devm_platform_ioremap_resource_byname(pdev, "wakeup");
17362306a36Sopenharmony_ci	if (IS_ERR(ctrl_usb->wkup))
17462306a36Sopenharmony_ci		return PTR_ERR(ctrl_usb->wkup);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	spin_lock_init(&ctrl_usb->lock);
17762306a36Sopenharmony_ci	ctrl_usb->phy_ctrl = *phy_ctrl;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	dev_set_drvdata(ctrl_usb->dev, ctrl_usb);
18062306a36Sopenharmony_ci	return 0;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic struct platform_driver am335x_control_driver = {
18462306a36Sopenharmony_ci	.probe		= am335x_control_usb_probe,
18562306a36Sopenharmony_ci	.driver		= {
18662306a36Sopenharmony_ci		.name	= "am335x-control-usb",
18762306a36Sopenharmony_ci		.of_match_table = omap_control_usb_id_table,
18862306a36Sopenharmony_ci	},
18962306a36Sopenharmony_ci};
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cimodule_platform_driver(am335x_control_driver);
19262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
193