public inbox for linux-riscv@lists.infradead.org
 help / color / mirror / Atom feed
From: Yao Zi <ziyao@disroot.org>
To: Vinod Koul <vkoul@kernel.org>,
	Kishon Vijay Abraham I <kishon@kernel.org>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Chen Wang <unicorn_wang@outlook.com>,
	Inochi Amaoto <inochiama@outlook.com>,
	Paul Walmsley <paul.walmsley@sifive.com>,
	Palmer Dabbelt <palmer@dabbelt.com>,
	Albert Ou <aou@eecs.berkeley.edu>,
	Jisheng Zhang <jszhang@kernel.org>,
	linux-phy@lists.infradead.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-riscv@lists.infradead.org
Cc: Yao Zi <ziyao@disroot.org>
Subject: [PATCH 3/3] phy: sophgo: add usb phy driver for Sophgo CV1800 SoCs
Date: Mon,  8 Jul 2024 12:08:30 +0000	[thread overview]
Message-ID: <20240708120830.5785-4-ziyao@disroot.org> (raw)
In-Reply-To: <20240708120830.5785-1-ziyao@disroot.org>

This adds a new driver for USB2 phys integrated in Sophgo CV1800 and
SG200x SoCs, which have the same design.

Most CV1800/SG200x boards have broken VBUS/ID detection, so we force
ID status to be specified in the device tree. This phy also supports
charger detection, which could be implemented later.

Unfortunately, there is no description about the phy in the public
datasheet. The driver was written by reading Sophgo GPL kernel source
and dumping registers on actual SG2002 devices.

Signed-off-by: Yao Zi <ziyao@disroot.org>
---
 drivers/phy/Kconfig                 |   1 +
 drivers/phy/Makefile                |   1 +
 drivers/phy/sophgo/Kconfig          |  10 ++
 drivers/phy/sophgo/Makefile         |   2 +
 drivers/phy/sophgo/phy-cv1800-usb.c | 213 ++++++++++++++++++++++++++++
 5 files changed, 227 insertions(+)
 create mode 100644 drivers/phy/sophgo/Kconfig
 create mode 100644 drivers/phy/sophgo/Makefile
 create mode 100644 drivers/phy/sophgo/phy-cv1800-usb.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 787354b849c7..596b37ab3191 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -92,6 +92,7 @@ source "drivers/phy/renesas/Kconfig"
 source "drivers/phy/rockchip/Kconfig"
 source "drivers/phy/samsung/Kconfig"
 source "drivers/phy/socionext/Kconfig"
+source "drivers/phy/sophgo/Kconfig"
 source "drivers/phy/st/Kconfig"
 source "drivers/phy/starfive/Kconfig"
 source "drivers/phy/sunplus/Kconfig"
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 868a220ed0f6..7ff32f0ae08a 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -31,6 +31,7 @@ obj-y					+= allwinner/	\
 					   rockchip/	\
 					   samsung/	\
 					   socionext/	\
+					   sophgo/	\
 					   st/		\
 					   starfive/	\
 					   sunplus/	\
diff --git a/drivers/phy/sophgo/Kconfig b/drivers/phy/sophgo/Kconfig
new file mode 100644
index 000000000000..6feb5795f1fc
--- /dev/null
+++ b/drivers/phy/sophgo/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config PHY_SOPHGO_CV1800_USB
+	tristate "SOPHGO CV1800 USB 2.0 PHY driver"
+	depends on OF && (ARCH_SOPHGO || COMPILE_TEST)
+	select GENERIC_PHY
+	help
+	  Enable this to support the USB 2.0 PHY on Sophgo CV1800
+	  and SG200x SoCs.
+	  If unsure, say N.
diff --git a/drivers/phy/sophgo/Makefile b/drivers/phy/sophgo/Makefile
new file mode 100644
index 000000000000..b4b9de0697e7
--- /dev/null
+++ b/drivers/phy/sophgo/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PHY_SOPHGO_CV1800_USB)	+= phy-cv1800-usb.o
diff --git a/drivers/phy/sophgo/phy-cv1800-usb.c b/drivers/phy/sophgo/phy-cv1800-usb.c
new file mode 100644
index 000000000000..873c72bd95cf
--- /dev/null
+++ b/drivers/phy/sophgo/phy-cv1800-usb.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * USB PHY driver for Sophgo CV1800 SoCs.
+ *
+ * Copyright 2024 Yao Zi <ziyao@disroot.org>
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+
+#define CV1800_REG04				0x04
+#define CV1800_REG14				0x14
+#define  CV1800_REG14_UTMI_OVERRIDE		BIT(0)
+#define  CV1800_REG14_OPMODE_MASK		(0x3 << 1)
+#define  CV1800_REG14_OPMODE_SHIFT		1
+#define  CV1800_REG14_XCVRSEL_MASK		(0x3 << 3)
+#define  CV1800_REG14_XCVRSEL_SHIFT		3
+#define  CV1800_REG14_TERMSEL			BIT(5)
+#define  CV1800_REG14_DPPULLDOWN		BIT(6)
+#define  CV1800_REG14_DMPULLDOWN		BIT(7)
+#define  CV1800_REG14_UTMI_RESET		BIT(8)
+#define CV1800_REG20				0x20
+#define  CV1800_REG20_BC_EN			BIT(0)
+#define  CV1800_REG20_DCD_EN			BIT(1)
+#define  CV1800_REG20_DP_CMP_EN			BIT(2)
+#define  CV1800_REG20_DM_CMP_EN			BIT(3)
+#define  CV1800_REG20_VDP_SRC_EN		BIT(4)
+#define  CV1800_REG20_VDM_SRC_EN		BIT(5)
+#define  CV1800_REG20_CHG_DET			BIT(16)
+#define  CV1800_REG20_DP_DET			BIT(17)
+
+#define CV1800_PIN_ID_OVERWRITE_EN		BIT(6)
+#define CV1800_PIN_ID_OVERWRITE_VALUE(v)	((v) << 7)
+
+enum cv1800_usb_phy_role {
+	CV1800_USB_PHY_HOST	= 0,
+	CV1800_USB_PHY_DEVICE	= 1,
+};
+
+struct cv1800_usb_phy_priv {
+	void __iomem *regs;
+	void __iomem *pinreg;
+	struct clk *clk_apb;
+	struct clk *clk_125m;
+	struct clk *clk_33k;
+	struct clk *clk_12m;
+	enum cv1800_usb_phy_role role;
+};
+
+static void
+cv1800_usb_phy_set_role(struct cv1800_usb_phy_priv *priv,
+			enum cv1800_usb_phy_role role)
+{
+	writel(CV1800_PIN_ID_OVERWRITE_EN | CV1800_PIN_ID_OVERWRITE_VALUE(role),
+	       priv->pinreg);
+}
+
+static int cv1800_usb_phy_init(struct phy *phy)
+{
+	struct cv1800_usb_phy_priv *priv = phy_get_drvdata(phy);
+	int ret = 0;
+
+	ret = clk_prepare_enable(priv->clk_apb);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(priv->clk_125m);
+	if (ret)
+		goto err_clk_125m;
+
+	ret = clk_prepare_enable(priv->clk_33k);
+	if (ret)
+		goto err_clk_33k;
+
+	ret = clk_prepare_enable(priv->clk_12m);
+	if (ret)
+		goto err_clk_12m;
+
+	writel(0xa, priv->regs + CV1800_REG04);		/* magic number	*/
+	writel(0, priv->regs + CV1800_REG14);
+	writel(0, priv->regs + CV1800_REG20);
+
+	cv1800_usb_phy_set_role(priv, priv->role);
+
+	return 0;
+
+err_clk_12m:
+	clk_disable_unprepare(priv->clk_33k);
+err_clk_33k:
+	clk_disable_unprepare(priv->clk_125m);
+err_clk_125m:
+	clk_disable_unprepare(priv->clk_apb);
+
+	return ret;
+}
+
+static int cv1800_usb_phy_exit(struct phy *phy)
+{
+	struct cv1800_usb_phy_priv *priv = phy_get_drvdata(phy);
+
+	clk_disable_unprepare(priv->clk_33k);
+	clk_disable_unprepare(priv->clk_125m);
+	clk_disable_unprepare(priv->clk_apb);
+
+	return 0;
+}
+
+static const struct phy_ops cv1800_usb_phy_ops = {
+	.init = cv1800_usb_phy_init,
+	.exit = cv1800_usb_phy_exit,
+};
+
+static int
+cv1800b_usb_phy_parse_dt(struct cv1800_usb_phy_priv *priv, struct device *dev)
+{
+	const char *role;
+
+	if (!of_property_read_string(dev->of_node, "dr_role", &role)) {
+		if (!strcmp(role, "host")) {
+			priv->role = CV1800_USB_PHY_HOST;
+		} else if (!strcmp(role, "device")) {
+			priv->role = CV1800_USB_PHY_DEVICE;
+		} else {
+			dev_err(dev, "invalid dr_role %s", role);
+			return -EINVAL;
+		}
+	} else {
+		priv->role = CV1800_USB_PHY_DEVICE;
+	}
+
+	return 0;
+}
+
+static int cv1800_usb_phy_probe(struct platform_device *pdev)
+{
+	struct cv1800_usb_phy_priv *priv;
+	struct phy_provider *provider;
+	struct device *dev = &pdev->dev;
+	struct phy *phy;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	ret = cv1800b_usb_phy_parse_dt(priv, dev);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to parse dt");
+
+	priv->regs = devm_platform_ioremap_resource_byname(pdev, "phy");
+	if (IS_ERR(priv->regs))
+		return dev_err_probe(dev, PTR_ERR(priv->regs),
+				     "failed to map phy registers");
+
+	priv->pinreg = devm_platform_ioremap_resource_byname(pdev, "pin");
+	if (IS_ERR(priv->pinreg))
+		return dev_err_probe(dev, PTR_ERR(priv->pinreg),
+				     "failed to map pin register");
+
+	priv->clk_apb = devm_clk_get(dev, "apb");
+	if (IS_ERR(priv->clk_apb))
+		return dev_err_probe(dev, PTR_ERR(priv->clk_apb),
+				     "failed to get apb clock");
+
+	priv->clk_125m = devm_clk_get(dev, "125m");
+	if (IS_ERR(priv->clk_125m))
+		return dev_err_probe(dev, PTR_ERR(priv->clk_125m),
+				     "failed to get 125m clock");
+
+	priv->clk_33k = devm_clk_get(dev, "33k");
+	if (IS_ERR(priv->clk_33k))
+		return dev_err_probe(dev, PTR_ERR(priv->clk_33k),
+				     "failed to get 33k clock");
+
+	priv->clk_12m = devm_clk_get(dev, "12m");
+	if (IS_ERR(priv->clk_12m))
+		return dev_err_probe(dev, PTR_ERR(priv->clk_12m),
+				     "failed to get 12m clock");
+
+	phy = devm_phy_create(dev, NULL, &cv1800_usb_phy_ops);
+	if (IS_ERR(phy))
+		return dev_err_probe(dev, PTR_ERR(phy),
+				     "cannot create phy");
+
+	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+	phy_set_drvdata(phy, priv);
+
+	return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id cv1800_usb_phy_of_match_table[] = {
+	{ .compatible = "sophgo,cv1800-usb-phy" },
+	{ },
+};
+
+static struct platform_driver cv1800_usb_phy_platform_driver = {
+	.driver = {
+		.name = "cv1800-usb-phy",
+		.of_match_table = cv1800_usb_phy_of_match_table,
+	},
+	.probe = cv1800_usb_phy_probe,
+};
+
+module_platform_driver(cv1800_usb_phy_platform_driver);
+
+MODULE_DESCRIPTION("Sophgo CV1800 USB PHY Driver");
+MODULE_AUTHOR("Yao Zi <ziyao@disroot.org>");
+MODULE_LICENSE("GPL");
-- 
2.45.2


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

      parent reply	other threads:[~2024-07-08 12:10 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-07-08 12:08 [PATCH 0/3] Add USB support for Sophgo CV1800/SG200x SoCs Yao Zi
2024-07-08 12:08 ` [PATCH 1/3] dt-bindings: phy: add YAML schema for cv1800-usb-phy driver bindings Yao Zi
2024-07-08 13:40   ` Krzysztof Kozlowski
2024-07-08 22:23   ` Inochi Amaoto
2024-07-08 23:54     ` Chen Wang
2024-07-09  7:07     ` Yao Zi
2024-07-08 12:08 ` [PATCH 2/3] riscv: dts: sophgo: add nodes for USB phy and controller Yao Zi
2024-07-08 13:41   ` Krzysztof Kozlowski
2024-07-08 12:08 ` Yao Zi [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240708120830.5785-4-ziyao@disroot.org \
    --to=ziyao@disroot.org \
    --cc=aou@eecs.berkeley.edu \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=inochiama@outlook.com \
    --cc=jszhang@kernel.org \
    --cc=kishon@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-phy@lists.infradead.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=palmer@dabbelt.com \
    --cc=paul.walmsley@sifive.com \
    --cc=robh@kernel.org \
    --cc=unicorn_wang@outlook.com \
    --cc=vkoul@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox