162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2015 Linaro Ltd.
462306a36Sopenharmony_ci * Copyright (c) 2015 HiSilicon Limited.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
862306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/platform_device.h>
1162306a36Sopenharmony_ci#include <linux/phy/phy.h>
1262306a36Sopenharmony_ci#include <linux/regmap.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define SC_PERIPH_CTRL4			0x00c
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define CTRL4_PICO_SIDDQ		BIT(6)
1762306a36Sopenharmony_ci#define CTRL4_PICO_OGDISABLE		BIT(8)
1862306a36Sopenharmony_ci#define CTRL4_PICO_VBUSVLDEXT		BIT(10)
1962306a36Sopenharmony_ci#define CTRL4_PICO_VBUSVLDEXTSEL	BIT(11)
2062306a36Sopenharmony_ci#define CTRL4_OTG_PHY_SEL		BIT(21)
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define SC_PERIPH_CTRL5			0x010
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define CTRL5_USBOTG_RES_SEL		BIT(3)
2562306a36Sopenharmony_ci#define CTRL5_PICOPHY_ACAENB		BIT(4)
2662306a36Sopenharmony_ci#define CTRL5_PICOPHY_BC_MODE		BIT(5)
2762306a36Sopenharmony_ci#define CTRL5_PICOPHY_CHRGSEL		BIT(6)
2862306a36Sopenharmony_ci#define CTRL5_PICOPHY_VDATSRCEND	BIT(7)
2962306a36Sopenharmony_ci#define CTRL5_PICOPHY_VDATDETENB	BIT(8)
3062306a36Sopenharmony_ci#define CTRL5_PICOPHY_DCDENB		BIT(9)
3162306a36Sopenharmony_ci#define CTRL5_PICOPHY_IDDIG		BIT(10)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define SC_PERIPH_CTRL8			0x018
3462306a36Sopenharmony_ci#define SC_PERIPH_RSTEN0		0x300
3562306a36Sopenharmony_ci#define SC_PERIPH_RSTDIS0		0x304
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define RST0_USBOTG_BUS			BIT(4)
3862306a36Sopenharmony_ci#define RST0_POR_PICOPHY		BIT(5)
3962306a36Sopenharmony_ci#define RST0_USBOTG			BIT(6)
4062306a36Sopenharmony_ci#define RST0_USBOTG_32K			BIT(7)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define EYE_PATTERN_PARA		0x7053348c
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct hi6220_priv {
4562306a36Sopenharmony_ci	struct regmap *reg;
4662306a36Sopenharmony_ci	struct device *dev;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void hi6220_phy_init(struct hi6220_priv *priv)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct regmap *reg = priv->reg;
5262306a36Sopenharmony_ci	u32 val, mask;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	val = RST0_USBOTG_BUS | RST0_POR_PICOPHY |
5562306a36Sopenharmony_ci	      RST0_USBOTG | RST0_USBOTG_32K;
5662306a36Sopenharmony_ci	mask = val;
5762306a36Sopenharmony_ci	regmap_update_bits(reg, SC_PERIPH_RSTEN0, mask, val);
5862306a36Sopenharmony_ci	regmap_update_bits(reg, SC_PERIPH_RSTDIS0, mask, val);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int hi6220_phy_setup(struct hi6220_priv *priv, bool on)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct regmap *reg = priv->reg;
6462306a36Sopenharmony_ci	u32 val, mask;
6562306a36Sopenharmony_ci	int ret;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (on) {
6862306a36Sopenharmony_ci		val = CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB;
6962306a36Sopenharmony_ci		mask = val | CTRL5_PICOPHY_BC_MODE;
7062306a36Sopenharmony_ci		ret = regmap_update_bits(reg, SC_PERIPH_CTRL5, mask, val);
7162306a36Sopenharmony_ci		if (ret)
7262306a36Sopenharmony_ci			goto out;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		val =  CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL |
7562306a36Sopenharmony_ci		       CTRL4_OTG_PHY_SEL;
7662306a36Sopenharmony_ci		mask = val | CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE;
7762306a36Sopenharmony_ci		ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
7862306a36Sopenharmony_ci		if (ret)
7962306a36Sopenharmony_ci			goto out;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci		ret = regmap_write(reg, SC_PERIPH_CTRL8, EYE_PATTERN_PARA);
8262306a36Sopenharmony_ci		if (ret)
8362306a36Sopenharmony_ci			goto out;
8462306a36Sopenharmony_ci	} else {
8562306a36Sopenharmony_ci		val = CTRL4_PICO_SIDDQ;
8662306a36Sopenharmony_ci		mask = val;
8762306a36Sopenharmony_ci		ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
8862306a36Sopenharmony_ci		if (ret)
8962306a36Sopenharmony_ci			goto out;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return 0;
9362306a36Sopenharmony_ciout:
9462306a36Sopenharmony_ci	dev_err(priv->dev, "failed to setup phy ret: %d\n", ret);
9562306a36Sopenharmony_ci	return ret;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int hi6220_phy_start(struct phy *phy)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct hi6220_priv *priv = phy_get_drvdata(phy);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return hi6220_phy_setup(priv, true);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic int hi6220_phy_exit(struct phy *phy)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct hi6220_priv *priv = phy_get_drvdata(phy);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return hi6220_phy_setup(priv, false);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic const struct phy_ops hi6220_phy_ops = {
11362306a36Sopenharmony_ci	.init		= hi6220_phy_start,
11462306a36Sopenharmony_ci	.exit		= hi6220_phy_exit,
11562306a36Sopenharmony_ci	.owner		= THIS_MODULE,
11662306a36Sopenharmony_ci};
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int hi6220_phy_probe(struct platform_device *pdev)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct phy_provider *phy_provider;
12162306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
12262306a36Sopenharmony_ci	struct phy *phy;
12362306a36Sopenharmony_ci	struct hi6220_priv *priv;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
12662306a36Sopenharmony_ci	if (!priv)
12762306a36Sopenharmony_ci		return -ENOMEM;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	priv->dev = dev;
13062306a36Sopenharmony_ci	priv->reg = syscon_regmap_lookup_by_phandle(dev->of_node,
13162306a36Sopenharmony_ci					"hisilicon,peripheral-syscon");
13262306a36Sopenharmony_ci	if (IS_ERR(priv->reg)) {
13362306a36Sopenharmony_ci		dev_err(dev, "no hisilicon,peripheral-syscon\n");
13462306a36Sopenharmony_ci		return PTR_ERR(priv->reg);
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	hi6220_phy_init(priv);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	phy = devm_phy_create(dev, NULL, &hi6220_phy_ops);
14062306a36Sopenharmony_ci	if (IS_ERR(phy))
14162306a36Sopenharmony_ci		return PTR_ERR(phy);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	phy_set_drvdata(phy, priv);
14462306a36Sopenharmony_ci	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
14562306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(phy_provider);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic const struct of_device_id hi6220_phy_of_match[] = {
14962306a36Sopenharmony_ci	{.compatible = "hisilicon,hi6220-usb-phy",},
15062306a36Sopenharmony_ci	{ },
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, hi6220_phy_of_match);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic struct platform_driver hi6220_phy_driver = {
15562306a36Sopenharmony_ci	.probe	= hi6220_phy_probe,
15662306a36Sopenharmony_ci	.driver = {
15762306a36Sopenharmony_ci		.name	= "hi6220-usb-phy",
15862306a36Sopenharmony_ci		.of_match_table	= hi6220_phy_of_match,
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_cimodule_platform_driver(hi6220_phy_driver);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ciMODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver");
16462306a36Sopenharmony_ciMODULE_ALIAS("platform:hi6220-usb-phy");
16562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
166