18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2015 Linaro Ltd.
48c2ecf20Sopenharmony_ci * Copyright (c) 2015 Hisilicon Limited.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
108c2ecf20Sopenharmony_ci#include <linux/phy/phy.h>
118c2ecf20Sopenharmony_ci#include <linux/regmap.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define SC_PERIPH_CTRL4			0x00c
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define CTRL4_PICO_SIDDQ		BIT(6)
168c2ecf20Sopenharmony_ci#define CTRL4_PICO_OGDISABLE		BIT(8)
178c2ecf20Sopenharmony_ci#define CTRL4_PICO_VBUSVLDEXT		BIT(10)
188c2ecf20Sopenharmony_ci#define CTRL4_PICO_VBUSVLDEXTSEL	BIT(11)
198c2ecf20Sopenharmony_ci#define CTRL4_OTG_PHY_SEL		BIT(21)
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define SC_PERIPH_CTRL5			0x010
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define CTRL5_USBOTG_RES_SEL		BIT(3)
248c2ecf20Sopenharmony_ci#define CTRL5_PICOPHY_ACAENB		BIT(4)
258c2ecf20Sopenharmony_ci#define CTRL5_PICOPHY_BC_MODE		BIT(5)
268c2ecf20Sopenharmony_ci#define CTRL5_PICOPHY_CHRGSEL		BIT(6)
278c2ecf20Sopenharmony_ci#define CTRL5_PICOPHY_VDATSRCEND	BIT(7)
288c2ecf20Sopenharmony_ci#define CTRL5_PICOPHY_VDATDETENB	BIT(8)
298c2ecf20Sopenharmony_ci#define CTRL5_PICOPHY_DCDENB		BIT(9)
308c2ecf20Sopenharmony_ci#define CTRL5_PICOPHY_IDDIG		BIT(10)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define SC_PERIPH_CTRL8			0x018
338c2ecf20Sopenharmony_ci#define SC_PERIPH_RSTEN0		0x300
348c2ecf20Sopenharmony_ci#define SC_PERIPH_RSTDIS0		0x304
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define RST0_USBOTG_BUS			BIT(4)
378c2ecf20Sopenharmony_ci#define RST0_POR_PICOPHY		BIT(5)
388c2ecf20Sopenharmony_ci#define RST0_USBOTG			BIT(6)
398c2ecf20Sopenharmony_ci#define RST0_USBOTG_32K			BIT(7)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define EYE_PATTERN_PARA		0x7053348c
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct hi6220_priv {
448c2ecf20Sopenharmony_ci	struct regmap *reg;
458c2ecf20Sopenharmony_ci	struct device *dev;
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic void hi6220_phy_init(struct hi6220_priv *priv)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct regmap *reg = priv->reg;
518c2ecf20Sopenharmony_ci	u32 val, mask;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	val = RST0_USBOTG_BUS | RST0_POR_PICOPHY |
548c2ecf20Sopenharmony_ci	      RST0_USBOTG | RST0_USBOTG_32K;
558c2ecf20Sopenharmony_ci	mask = val;
568c2ecf20Sopenharmony_ci	regmap_update_bits(reg, SC_PERIPH_RSTEN0, mask, val);
578c2ecf20Sopenharmony_ci	regmap_update_bits(reg, SC_PERIPH_RSTDIS0, mask, val);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int hi6220_phy_setup(struct hi6220_priv *priv, bool on)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct regmap *reg = priv->reg;
638c2ecf20Sopenharmony_ci	u32 val, mask;
648c2ecf20Sopenharmony_ci	int ret;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (on) {
678c2ecf20Sopenharmony_ci		val = CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB;
688c2ecf20Sopenharmony_ci		mask = val | CTRL5_PICOPHY_BC_MODE;
698c2ecf20Sopenharmony_ci		ret = regmap_update_bits(reg, SC_PERIPH_CTRL5, mask, val);
708c2ecf20Sopenharmony_ci		if (ret)
718c2ecf20Sopenharmony_ci			goto out;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		val =  CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL |
748c2ecf20Sopenharmony_ci		       CTRL4_OTG_PHY_SEL;
758c2ecf20Sopenharmony_ci		mask = val | CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE;
768c2ecf20Sopenharmony_ci		ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
778c2ecf20Sopenharmony_ci		if (ret)
788c2ecf20Sopenharmony_ci			goto out;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci		ret = regmap_write(reg, SC_PERIPH_CTRL8, EYE_PATTERN_PARA);
818c2ecf20Sopenharmony_ci		if (ret)
828c2ecf20Sopenharmony_ci			goto out;
838c2ecf20Sopenharmony_ci	} else {
848c2ecf20Sopenharmony_ci		val = CTRL4_PICO_SIDDQ;
858c2ecf20Sopenharmony_ci		mask = val;
868c2ecf20Sopenharmony_ci		ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
878c2ecf20Sopenharmony_ci		if (ret)
888c2ecf20Sopenharmony_ci			goto out;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ciout:
938c2ecf20Sopenharmony_ci	dev_err(priv->dev, "failed to setup phy ret: %d\n", ret);
948c2ecf20Sopenharmony_ci	return ret;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int hi6220_phy_start(struct phy *phy)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct hi6220_priv *priv = phy_get_drvdata(phy);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return hi6220_phy_setup(priv, true);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int hi6220_phy_exit(struct phy *phy)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct hi6220_priv *priv = phy_get_drvdata(phy);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return hi6220_phy_setup(priv, false);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic const struct phy_ops hi6220_phy_ops = {
1128c2ecf20Sopenharmony_ci	.init		= hi6220_phy_start,
1138c2ecf20Sopenharmony_ci	.exit		= hi6220_phy_exit,
1148c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1158c2ecf20Sopenharmony_ci};
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic int hi6220_phy_probe(struct platform_device *pdev)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct phy_provider *phy_provider;
1208c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
1218c2ecf20Sopenharmony_ci	struct phy *phy;
1228c2ecf20Sopenharmony_ci	struct hi6220_priv *priv;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
1258c2ecf20Sopenharmony_ci	if (!priv)
1268c2ecf20Sopenharmony_ci		return -ENOMEM;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	priv->dev = dev;
1298c2ecf20Sopenharmony_ci	priv->reg = syscon_regmap_lookup_by_phandle(dev->of_node,
1308c2ecf20Sopenharmony_ci					"hisilicon,peripheral-syscon");
1318c2ecf20Sopenharmony_ci	if (IS_ERR(priv->reg)) {
1328c2ecf20Sopenharmony_ci		dev_err(dev, "no hisilicon,peripheral-syscon\n");
1338c2ecf20Sopenharmony_ci		return PTR_ERR(priv->reg);
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	hi6220_phy_init(priv);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	phy = devm_phy_create(dev, NULL, &hi6220_phy_ops);
1398c2ecf20Sopenharmony_ci	if (IS_ERR(phy))
1408c2ecf20Sopenharmony_ci		return PTR_ERR(phy);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	phy_set_drvdata(phy, priv);
1438c2ecf20Sopenharmony_ci	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
1448c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(phy_provider);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic const struct of_device_id hi6220_phy_of_match[] = {
1488c2ecf20Sopenharmony_ci	{.compatible = "hisilicon,hi6220-usb-phy",},
1498c2ecf20Sopenharmony_ci	{ },
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, hi6220_phy_of_match);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic struct platform_driver hi6220_phy_driver = {
1548c2ecf20Sopenharmony_ci	.probe	= hi6220_phy_probe,
1558c2ecf20Sopenharmony_ci	.driver = {
1568c2ecf20Sopenharmony_ci		.name	= "hi6220-usb-phy",
1578c2ecf20Sopenharmony_ci		.of_match_table	= hi6220_phy_of_match,
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci};
1608c2ecf20Sopenharmony_cimodule_platform_driver(hi6220_phy_driver);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver");
1638c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:hi6220-usb-phy");
1648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
165