xref: /openbmc/linux/drivers/phy/phy-pistachio-usb.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*75a6faf6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
257991ebaSAndrew Bresticker /*
357991ebaSAndrew Bresticker  * IMG Pistachio USB PHY driver
457991ebaSAndrew Bresticker  *
557991ebaSAndrew Bresticker  * Copyright (C) 2015 Google, Inc.
657991ebaSAndrew Bresticker  */
757991ebaSAndrew Bresticker 
857991ebaSAndrew Bresticker #include <linux/clk.h>
957991ebaSAndrew Bresticker #include <linux/delay.h>
1057991ebaSAndrew Bresticker #include <linux/io.h>
1157991ebaSAndrew Bresticker #include <linux/kernel.h>
1257991ebaSAndrew Bresticker #include <linux/mfd/syscon.h>
1357991ebaSAndrew Bresticker #include <linux/module.h>
1457991ebaSAndrew Bresticker #include <linux/of.h>
1557991ebaSAndrew Bresticker #include <linux/phy/phy.h>
1657991ebaSAndrew Bresticker #include <linux/platform_device.h>
1757991ebaSAndrew Bresticker #include <linux/regmap.h>
1857991ebaSAndrew Bresticker 
1957991ebaSAndrew Bresticker #include <dt-bindings/phy/phy-pistachio-usb.h>
2057991ebaSAndrew Bresticker 
2157991ebaSAndrew Bresticker #define USB_PHY_CONTROL1				0x04
2257991ebaSAndrew Bresticker #define USB_PHY_CONTROL1_FSEL_SHIFT			2
2357991ebaSAndrew Bresticker #define USB_PHY_CONTROL1_FSEL_MASK			0x7
2457991ebaSAndrew Bresticker 
2557991ebaSAndrew Bresticker #define USB_PHY_STRAP_CONTROL				0x10
2657991ebaSAndrew Bresticker #define USB_PHY_STRAP_CONTROL_REFCLK_SHIFT		4
2757991ebaSAndrew Bresticker #define USB_PHY_STRAP_CONTROL_REFCLK_MASK		0x3
2857991ebaSAndrew Bresticker 
2957991ebaSAndrew Bresticker #define USB_PHY_STATUS					0x14
3057991ebaSAndrew Bresticker #define USB_PHY_STATUS_RX_PHY_CLK			BIT(9)
3157991ebaSAndrew Bresticker #define USB_PHY_STATUS_RX_UTMI_CLK			BIT(8)
3257991ebaSAndrew Bresticker #define USB_PHY_STATUS_VBUS_FAULT			BIT(7)
3357991ebaSAndrew Bresticker 
3457991ebaSAndrew Bresticker struct pistachio_usb_phy {
3557991ebaSAndrew Bresticker 	struct device *dev;
3657991ebaSAndrew Bresticker 	struct regmap *cr_top;
3757991ebaSAndrew Bresticker 	struct clk *phy_clk;
3857991ebaSAndrew Bresticker 	unsigned int refclk;
3957991ebaSAndrew Bresticker };
4057991ebaSAndrew Bresticker 
4157991ebaSAndrew Bresticker static const unsigned long fsel_rate_map[] = {
4257991ebaSAndrew Bresticker 	9600000,
4357991ebaSAndrew Bresticker 	10000000,
4457991ebaSAndrew Bresticker 	12000000,
4557991ebaSAndrew Bresticker 	19200000,
4657991ebaSAndrew Bresticker 	20000000,
4757991ebaSAndrew Bresticker 	24000000,
4857991ebaSAndrew Bresticker 	0,
4957991ebaSAndrew Bresticker 	50000000,
5057991ebaSAndrew Bresticker };
5157991ebaSAndrew Bresticker 
pistachio_usb_phy_power_on(struct phy * phy)5257991ebaSAndrew Bresticker static int pistachio_usb_phy_power_on(struct phy *phy)
5357991ebaSAndrew Bresticker {
5457991ebaSAndrew Bresticker 	struct pistachio_usb_phy *p_phy = phy_get_drvdata(phy);
5557991ebaSAndrew Bresticker 	unsigned long timeout, rate;
5657991ebaSAndrew Bresticker 	unsigned int i;
5757991ebaSAndrew Bresticker 	int ret;
5857991ebaSAndrew Bresticker 
5957991ebaSAndrew Bresticker 	ret = clk_prepare_enable(p_phy->phy_clk);
6057991ebaSAndrew Bresticker 	if (ret < 0) {
6157991ebaSAndrew Bresticker 		dev_err(p_phy->dev, "Failed to enable PHY clock: %d\n", ret);
6257991ebaSAndrew Bresticker 		return ret;
6357991ebaSAndrew Bresticker 	}
6457991ebaSAndrew Bresticker 
6557991ebaSAndrew Bresticker 	regmap_update_bits(p_phy->cr_top, USB_PHY_STRAP_CONTROL,
6657991ebaSAndrew Bresticker 			   USB_PHY_STRAP_CONTROL_REFCLK_MASK <<
6757991ebaSAndrew Bresticker 			   USB_PHY_STRAP_CONTROL_REFCLK_SHIFT,
6857991ebaSAndrew Bresticker 			   p_phy->refclk << USB_PHY_STRAP_CONTROL_REFCLK_SHIFT);
6957991ebaSAndrew Bresticker 
7057991ebaSAndrew Bresticker 	rate = clk_get_rate(p_phy->phy_clk);
7157991ebaSAndrew Bresticker 	if (p_phy->refclk == REFCLK_XO_CRYSTAL && rate != 12000000) {
7257991ebaSAndrew Bresticker 		dev_err(p_phy->dev, "Unsupported rate for XO crystal: %ld\n",
7357991ebaSAndrew Bresticker 			rate);
7457991ebaSAndrew Bresticker 		ret = -EINVAL;
7557991ebaSAndrew Bresticker 		goto disable_clk;
7657991ebaSAndrew Bresticker 	}
7757991ebaSAndrew Bresticker 
7857991ebaSAndrew Bresticker 	for (i = 0; i < ARRAY_SIZE(fsel_rate_map); i++) {
7957991ebaSAndrew Bresticker 		if (rate == fsel_rate_map[i])
8057991ebaSAndrew Bresticker 			break;
8157991ebaSAndrew Bresticker 	}
8257991ebaSAndrew Bresticker 	if (i == ARRAY_SIZE(fsel_rate_map)) {
8357991ebaSAndrew Bresticker 		dev_err(p_phy->dev, "Unsupported clock rate: %lu\n", rate);
8457991ebaSAndrew Bresticker 		ret = -EINVAL;
8557991ebaSAndrew Bresticker 		goto disable_clk;
8657991ebaSAndrew Bresticker 	}
8757991ebaSAndrew Bresticker 
8857991ebaSAndrew Bresticker 	regmap_update_bits(p_phy->cr_top, USB_PHY_CONTROL1,
8957991ebaSAndrew Bresticker 			   USB_PHY_CONTROL1_FSEL_MASK <<
9057991ebaSAndrew Bresticker 			   USB_PHY_CONTROL1_FSEL_SHIFT,
9157991ebaSAndrew Bresticker 			   i << USB_PHY_CONTROL1_FSEL_SHIFT);
9257991ebaSAndrew Bresticker 
9357991ebaSAndrew Bresticker 	timeout = jiffies + msecs_to_jiffies(200);
9457991ebaSAndrew Bresticker 	while (time_before(jiffies, timeout)) {
9557991ebaSAndrew Bresticker 		unsigned int val;
9657991ebaSAndrew Bresticker 
9757991ebaSAndrew Bresticker 		regmap_read(p_phy->cr_top, USB_PHY_STATUS, &val);
9857991ebaSAndrew Bresticker 		if (val & USB_PHY_STATUS_VBUS_FAULT) {
9957991ebaSAndrew Bresticker 			dev_err(p_phy->dev, "VBUS fault detected\n");
10057991ebaSAndrew Bresticker 			ret = -EIO;
10157991ebaSAndrew Bresticker 			goto disable_clk;
10257991ebaSAndrew Bresticker 		}
10357991ebaSAndrew Bresticker 		if ((val & USB_PHY_STATUS_RX_PHY_CLK) &&
10457991ebaSAndrew Bresticker 		    (val & USB_PHY_STATUS_RX_UTMI_CLK))
10557991ebaSAndrew Bresticker 			return 0;
10657991ebaSAndrew Bresticker 		usleep_range(1000, 1500);
10757991ebaSAndrew Bresticker 	}
10857991ebaSAndrew Bresticker 
10957991ebaSAndrew Bresticker 	dev_err(p_phy->dev, "Timed out waiting for PHY to power on\n");
11057991ebaSAndrew Bresticker 	ret = -ETIMEDOUT;
11157991ebaSAndrew Bresticker 
11257991ebaSAndrew Bresticker disable_clk:
11357991ebaSAndrew Bresticker 	clk_disable_unprepare(p_phy->phy_clk);
11457991ebaSAndrew Bresticker 	return ret;
11557991ebaSAndrew Bresticker }
11657991ebaSAndrew Bresticker 
pistachio_usb_phy_power_off(struct phy * phy)11757991ebaSAndrew Bresticker static int pistachio_usb_phy_power_off(struct phy *phy)
11857991ebaSAndrew Bresticker {
11957991ebaSAndrew Bresticker 	struct pistachio_usb_phy *p_phy = phy_get_drvdata(phy);
12057991ebaSAndrew Bresticker 
12157991ebaSAndrew Bresticker 	clk_disable_unprepare(p_phy->phy_clk);
12257991ebaSAndrew Bresticker 
12357991ebaSAndrew Bresticker 	return 0;
12457991ebaSAndrew Bresticker }
12557991ebaSAndrew Bresticker 
12657991ebaSAndrew Bresticker static const struct phy_ops pistachio_usb_phy_ops = {
12757991ebaSAndrew Bresticker 	.power_on = pistachio_usb_phy_power_on,
12857991ebaSAndrew Bresticker 	.power_off = pistachio_usb_phy_power_off,
12957991ebaSAndrew Bresticker 	.owner = THIS_MODULE,
13057991ebaSAndrew Bresticker };
13157991ebaSAndrew Bresticker 
pistachio_usb_phy_probe(struct platform_device * pdev)13257991ebaSAndrew Bresticker static int pistachio_usb_phy_probe(struct platform_device *pdev)
13357991ebaSAndrew Bresticker {
13457991ebaSAndrew Bresticker 	struct pistachio_usb_phy *p_phy;
13557991ebaSAndrew Bresticker 	struct phy_provider *provider;
13657991ebaSAndrew Bresticker 	struct phy *phy;
13757991ebaSAndrew Bresticker 	int ret;
13857991ebaSAndrew Bresticker 
13957991ebaSAndrew Bresticker 	p_phy = devm_kzalloc(&pdev->dev, sizeof(*p_phy), GFP_KERNEL);
14057991ebaSAndrew Bresticker 	if (!p_phy)
14157991ebaSAndrew Bresticker 		return -ENOMEM;
14257991ebaSAndrew Bresticker 	p_phy->dev = &pdev->dev;
14357991ebaSAndrew Bresticker 	platform_set_drvdata(pdev, p_phy);
14457991ebaSAndrew Bresticker 
14557991ebaSAndrew Bresticker 	p_phy->cr_top = syscon_regmap_lookup_by_phandle(p_phy->dev->of_node,
14657991ebaSAndrew Bresticker 							"img,cr-top");
14757991ebaSAndrew Bresticker 	if (IS_ERR(p_phy->cr_top)) {
14857991ebaSAndrew Bresticker 		dev_err(p_phy->dev, "Failed to get CR_TOP registers: %ld\n",
14957991ebaSAndrew Bresticker 			PTR_ERR(p_phy->cr_top));
15057991ebaSAndrew Bresticker 		return PTR_ERR(p_phy->cr_top);
15157991ebaSAndrew Bresticker 	}
15257991ebaSAndrew Bresticker 
15357991ebaSAndrew Bresticker 	p_phy->phy_clk = devm_clk_get(p_phy->dev, "usb_phy");
15457991ebaSAndrew Bresticker 	if (IS_ERR(p_phy->phy_clk)) {
15557991ebaSAndrew Bresticker 		dev_err(p_phy->dev, "Failed to get usb_phy clock: %ld\n",
15657991ebaSAndrew Bresticker 			PTR_ERR(p_phy->phy_clk));
15757991ebaSAndrew Bresticker 		return PTR_ERR(p_phy->phy_clk);
15857991ebaSAndrew Bresticker 	}
15957991ebaSAndrew Bresticker 
16057991ebaSAndrew Bresticker 	ret = of_property_read_u32(p_phy->dev->of_node, "img,refclk",
16157991ebaSAndrew Bresticker 				   &p_phy->refclk);
16257991ebaSAndrew Bresticker 	if (ret < 0) {
16357991ebaSAndrew Bresticker 		dev_err(p_phy->dev, "No reference clock selector specified\n");
16457991ebaSAndrew Bresticker 		return ret;
16557991ebaSAndrew Bresticker 	}
16657991ebaSAndrew Bresticker 
16757991ebaSAndrew Bresticker 	phy = devm_phy_create(p_phy->dev, NULL, &pistachio_usb_phy_ops);
16857991ebaSAndrew Bresticker 	if (IS_ERR(phy)) {
16957991ebaSAndrew Bresticker 		dev_err(p_phy->dev, "Failed to create PHY: %ld\n",
17057991ebaSAndrew Bresticker 			PTR_ERR(phy));
17157991ebaSAndrew Bresticker 		return PTR_ERR(phy);
17257991ebaSAndrew Bresticker 	}
17357991ebaSAndrew Bresticker 	phy_set_drvdata(phy, p_phy);
17457991ebaSAndrew Bresticker 
17557991ebaSAndrew Bresticker 	provider = devm_of_phy_provider_register(p_phy->dev,
17657991ebaSAndrew Bresticker 						 of_phy_simple_xlate);
17757991ebaSAndrew Bresticker 	if (IS_ERR(provider)) {
17857991ebaSAndrew Bresticker 		dev_err(p_phy->dev, "Failed to register PHY provider: %ld\n",
17957991ebaSAndrew Bresticker 			PTR_ERR(provider));
18057991ebaSAndrew Bresticker 		return PTR_ERR(provider);
18157991ebaSAndrew Bresticker 	}
18257991ebaSAndrew Bresticker 
18357991ebaSAndrew Bresticker 	return 0;
18457991ebaSAndrew Bresticker }
18557991ebaSAndrew Bresticker 
18657991ebaSAndrew Bresticker static const struct of_device_id pistachio_usb_phy_of_match[] = {
18757991ebaSAndrew Bresticker 	{ .compatible = "img,pistachio-usb-phy", },
18857991ebaSAndrew Bresticker 	{ },
18957991ebaSAndrew Bresticker };
19057991ebaSAndrew Bresticker MODULE_DEVICE_TABLE(of, pistachio_usb_phy_of_match);
19157991ebaSAndrew Bresticker 
19257991ebaSAndrew Bresticker static struct platform_driver pistachio_usb_phy_driver = {
19357991ebaSAndrew Bresticker 	.probe		= pistachio_usb_phy_probe,
19457991ebaSAndrew Bresticker 	.driver		= {
19557991ebaSAndrew Bresticker 		.name	= "pistachio-usb-phy",
19657991ebaSAndrew Bresticker 		.of_match_table = pistachio_usb_phy_of_match,
19757991ebaSAndrew Bresticker 	},
19857991ebaSAndrew Bresticker };
19957991ebaSAndrew Bresticker module_platform_driver(pistachio_usb_phy_driver);
20057991ebaSAndrew Bresticker 
20157991ebaSAndrew Bresticker MODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>");
20257991ebaSAndrew Bresticker MODULE_DESCRIPTION("IMG Pistachio USB2.0 PHY driver");
20357991ebaSAndrew Bresticker MODULE_LICENSE("GPL v2");
204