ALT Linux kernel packages development
 help / color / mirror / Atom feed
From: Daniil Gnusarev <gnusarevda@basealt.ru>
To: devel-kernel@lists.altlinux.org
Subject: [d-kernel] [PATCH 03/39] USB: Add support for Baikal USB PHY
Date: Mon, 14 Oct 2024 18:01:44 +0400
Message-ID: <20241014140221.535985-4-gnusarevda@basealt.ru> (raw)
In-Reply-To: <20241014140221.535985-1-gnusarevda@basealt.ru>

Add support for USB PHY for Baikal BE-M1000 with firmware
from SDK-ARM64-2403-6.6

Signed-off-by: Daniil Gnusarev <gnusarevda@basealt.ru>
Co-developed-by: Baikal Electronics <info@baikalelectronics.ru>
---
 drivers/phy/Kconfig                 |   1 +
 drivers/phy/Makefile                |   1 +
 drivers/phy/baikal/Kconfig          |  10 +
 drivers/phy/baikal/Makefile         |   3 +
 drivers/phy/baikal/baikal-usb-phy.c | 305 ++++++++++++++++++++++++++++
 5 files changed, 320 insertions(+)
 create mode 100644 drivers/phy/baikal/Kconfig
 create mode 100644 drivers/phy/baikal/Makefile
 create mode 100644 drivers/phy/baikal/baikal-usb-phy.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index e4502958fd62d..b590354e7f66c 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -74,6 +74,7 @@ config PHY_CAN_TRANSCEIVER
 
 source "drivers/phy/allwinner/Kconfig"
 source "drivers/phy/amlogic/Kconfig"
+source "drivers/phy/baikal/Kconfig"
 source "drivers/phy/broadcom/Kconfig"
 source "drivers/phy/cadence/Kconfig"
 source "drivers/phy/freescale/Kconfig"
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index fb3dc9de61115..624f6689d9f92 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_PHY_PISTACHIO_USB)		+= phy-pistachio-usb.o
 obj-$(CONFIG_USB_LGM_PHY)		+= phy-lgm-usb.o
 obj-y					+= allwinner/	\
 					   amlogic/	\
+					   baikal/	\
 					   broadcom/	\
 					   cadence/	\
 					   freescale/	\
diff --git a/drivers/phy/baikal/Kconfig b/drivers/phy/baikal/Kconfig
new file mode 100644
index 0000000000000..b8b598d6019e1
--- /dev/null
+++ b/drivers/phy/baikal/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config USB_PHY_BAIKAL
+	tristate "Baikal USB PHY driver"
+	depends on USB_SUPPORT
+	select GENERIC_PHY
+	select USB_PHY
+	help
+	  Enable this to support the USB PHY on Baikal SoCs.
+	  This driver controls both the USB2 PHY and the USB3 PHY.
\ No newline at end of file
diff --git a/drivers/phy/baikal/Makefile b/drivers/phy/baikal/Makefile
new file mode 100644
index 0000000000000..54fcd11988681
--- /dev/null
+++ b/drivers/phy/baikal/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_USB_PHY_BAIKAL)		+= baikal-usb-phy.o
diff --git a/drivers/phy/baikal/baikal-usb-phy.c b/drivers/phy/baikal/baikal-usb-phy.c
new file mode 100644
index 0000000000000..72e7b747914d3
--- /dev/null
+++ b/drivers/phy/baikal/baikal-usb-phy.c
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Baikal USB PHY driver
+ *
+ * Copyright (C) 2022-2023 Baikal Electronics, JSC
+ */
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <dt-bindings/phy/phy.h>
+
+struct phy_baikal_desc {
+	struct phy		*phy;
+	int			index;
+	bool			enable;
+};
+
+struct phy_baikal_priv {
+	struct phy_baikal_desc	**phys;
+	int			nphys;
+	struct clk_bulk_data	*clocks;
+	unsigned int		nclocks;
+};
+
+#ifdef CONFIG_ACPI
+static int phy_baikal_acpi_get_info(struct fwnode_handle *fwnode,
+				    bool *is_usb3, const char **ref_name)
+{
+	struct acpi_device *adev = to_acpi_device_node(fwnode);
+	struct acpi_device *ref_adev;
+	struct device *ref_dev;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	union acpi_object *obj;
+	acpi_status status;
+	int ret = 0;
+
+	*is_usb3 = fwnode_property_read_bool(fwnode, "usb3");
+	status = acpi_evaluate_object_typed(adev->handle, "CTRL", NULL,
+					    &buffer, ACPI_TYPE_PACKAGE);
+	if (ACPI_FAILURE(status)) {
+		dev_err(&adev->dev, "failed to get CTRL data\n");
+		return -ENODEV;
+	}
+
+	obj = buffer.pointer;
+	if (obj->package.count != 1) {
+		dev_err(&adev->dev, "invalid CTRL data\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	obj = &obj->package.elements[0];
+	if (obj->type != ACPI_TYPE_LOCAL_REFERENCE || !obj->reference.handle) {
+		dev_err(&adev->dev, "invalid CTRL reference\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ref_adev = acpi_fetch_acpi_dev(obj->reference.handle);
+	if (!ref_adev) {
+		dev_err(&adev->dev, "failed to process CTRL reference\n");
+		ret = -ENODEV;
+		goto err;
+	}
+
+	ref_dev = bus_find_device_by_fwnode(&platform_bus_type,
+					    acpi_fwnode_handle(ref_adev));
+	if (!ref_dev) {
+		dev_err(&adev->dev, "failed to get referenced device\n");
+		ret = -ENODEV;
+		goto err;
+	}
+	*ref_name = dev_name(ref_dev);
+
+err:
+	acpi_os_free(buffer.pointer);
+	return ret;
+}
+
+static int phy_baikal_acpi_add(struct device *dev)
+{
+	struct phy_baikal_priv *priv = dev_get_drvdata(dev);
+	struct fwnode_handle *child;
+	const char **ref_name;
+	bool *is_usb3;
+	int count = 0, i, ret;
+
+	ref_name = kcalloc(priv->nphys, sizeof(*ref_name), GFP_KERNEL);
+	if (!ref_name)
+		return -ENOMEM;
+
+	is_usb3 = kcalloc(priv->nphys, sizeof(*is_usb3), GFP_KERNEL);
+	if (!is_usb3) {
+		kfree(ref_name);
+		return -ENOMEM;
+	}
+
+	device_for_each_child_node(dev, child) {
+		ret = phy_baikal_acpi_get_info(child, &is_usb3[count],
+					       &ref_name[count]);
+		if (ret)
+			goto err;
+
+		ret = phy_create_lookup(priv->phys[count]->phy,
+					is_usb3[count] ? "usb3-phy" : "usb2-phy",
+					ref_name[count]);
+		if (ret)
+			goto err;
+
+		++count;
+	}
+
+err:
+	if (ret) {
+		for (i = 0; i < count; ++i)
+			phy_remove_lookup(priv->phys[i]->phy,
+					  is_usb3[i] ? "usb3-phy" : "usb2-phy",
+					  ref_name[i]);
+	}
+
+	kfree(ref_name);
+	kfree(is_usb3);
+	return ret;
+}
+
+static void phy_baikal_acpi_remove(struct device *dev)
+{
+	struct phy_baikal_priv *priv = dev_get_drvdata(dev);
+	struct fwnode_handle *child;
+	const char *ref_name;
+	bool is_usb3;
+	int i, ret;
+
+	device_for_each_child_node(dev, child) {
+		ret = phy_baikal_acpi_get_info(child, &is_usb3, &ref_name);
+		if (ret) {
+			++i;
+			continue;
+		}
+
+		phy_remove_lookup(priv->phys[i++]->phy,
+				  is_usb3 ? "usb3-phy" : "usb2-phy",
+				  ref_name);
+	}
+}
+
+static void phy_baikal_acpi_init_clk(struct phy_baikal_desc *desc)
+{
+	struct device *dev = desc->phy->dev.parent;
+	struct phy_baikal_priv *priv = dev_get_drvdata(dev);
+	struct fwnode_handle *child;
+	int i = 0;
+
+	device_for_each_child_node(dev, child) {
+		if (i++ == desc->index) {
+			priv->clocks[2 * desc->index].clk =
+				devm_clk_get_optional(&to_acpi_device_node(child)->dev, "phy0_clk");
+			priv->clocks[2 * desc->index + 1].clk =
+				devm_clk_get_optional(&to_acpi_device_node(child)->dev, "phy1_clk");
+			break;
+		}
+	}
+}
+#else /* CONFIG_ACPI */
+static inline int phy_baikal_acpi_add(struct device *dev)
+{
+	return 0;
+}
+
+static inline void phy_baikal_acpi_remove(struct device *dev)
+{
+}
+
+static inline void phy_baikal_acpi_init_clk(struct phy_baikal_desc *desc)
+{
+}
+#endif
+
+static int phy_baikal_init(struct phy *phy)
+{
+	struct phy_baikal_priv *priv = dev_get_drvdata(phy->dev.parent);
+	struct phy_baikal_desc *desc = phy_get_drvdata(phy);
+	int n = 2 * desc->index;
+
+	if (!acpi_disabled)
+		phy_baikal_acpi_init_clk(desc);
+
+	if (desc->enable) {
+		clk_prepare_enable(priv->clocks[n + 0].clk);
+		clk_prepare_enable(priv->clocks[n + 1].clk);
+		return 0;
+	} else {
+		clk_disable_unprepare(priv->clocks[n + 0].clk);
+		clk_disable_unprepare(priv->clocks[n + 1].clk);
+		return -1;
+	}
+}
+
+static const struct phy_ops phy_baikal_ops = {
+	.init	= phy_baikal_init,
+	.owner	= THIS_MODULE,
+};
+
+static struct phy *phy_baikal_xlate(struct device *dev, struct of_phandle_args *args)
+{
+	int i;
+	struct phy_baikal_priv *priv = dev_get_drvdata(dev);
+
+	for (i = 0; i < priv->nphys; i++) {
+		if (priv->phys[i]->index == args->args[0])
+			break;
+	}
+
+	return priv->phys[i]->phy;
+}
+
+static int phy_baikal_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct fwnode_handle *child;
+	struct phy *phy;
+	struct phy_baikal_priv *priv;
+	struct phy_baikal_desc *phy_desc;
+	int index;
+	int i = 0;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->nphys = device_get_child_node_count(dev);
+	priv->phys = devm_kcalloc(dev, priv->nphys, sizeof(*priv->phys), GFP_KERNEL);
+	if (!priv->phys)
+		return -ENOMEM;
+
+	if (acpi_disabled) {
+		priv->nclocks = devm_clk_bulk_get_all(dev, &priv->clocks);
+	} else {
+		priv->nclocks = 2 * priv->nphys;
+		priv->clocks = devm_kcalloc(dev, priv->nclocks,
+					    sizeof(*priv->clocks), GFP_KERNEL);
+		if (!priv->clocks)
+			return -ENOMEM;
+	}
+
+	dev_set_drvdata(dev, priv);
+	device_for_each_child_node(dev, child) {
+		fwnode_property_read_u32(child, "reg", &index);
+		phy = devm_phy_create(dev, NULL, &phy_baikal_ops);
+		if (!phy)
+			return -ENOMEM;
+
+		phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
+		if (!phy_desc)
+			return -ENOMEM;
+
+		phy_desc->phy = phy;
+		phy_desc->index = index;
+		phy_desc->enable = fwnode_property_read_bool(child, "enable");
+		priv->phys[i++] = phy_desc;
+		phy_set_drvdata(phy, phy_desc);
+	}
+
+	if (acpi_disabled)
+		return PTR_ERR_OR_ZERO(devm_of_phy_provider_register(dev, phy_baikal_xlate));
+	else
+		return phy_baikal_acpi_add(dev);
+}
+
+static int phy_baikal_remove(struct platform_device *pdev)
+{
+	if (!acpi_disabled)
+		phy_baikal_acpi_remove(&pdev->dev);
+
+	return 0;
+}
+
+static const struct of_device_id phy_baikal_table[] = {
+	{ .compatible = "baikal,bm1000-usb-phy" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, phy_baikal_table);
+
+static struct platform_driver phy_baikal_driver = {
+	.probe = phy_baikal_probe,
+	.remove = phy_baikal_remove,
+	.driver = {
+		.name = "baikal,bm1000-usb-phy",
+		.of_match_table = phy_baikal_table,
+	},
+};
+module_platform_driver(phy_baikal_driver);
+
+MODULE_DESCRIPTION("Baikal USB PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
2.42.2



  parent reply	other threads:[~2024-10-14 14:01 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-10-14 14:01 [d-kernel] [PATCH 00/39] Support Baikal-M in un-def in p11 Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 01/39] Baikal Electronics SoC family Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 02/39] Clk: Add clock drivers for Baikal BE-M1000 with new firmware Daniil Gnusarev
2024-10-14 14:01 ` Daniil Gnusarev [this message]
2024-10-14 14:01 ` [d-kernel] [PATCH 04/39] PCI: Add support for PCIe controller for Baikal BE-M1000 Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 05/39] AHCI SATA: Add support " Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 06/39] UART: Add support for UART " Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 07/39] cpufreq-dt: don't load on Baikal-M SoC Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 08/39] Sound: add support for Baikal BE-M1000 I2S Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 09/39] sound: baikal-i2s: paper over RX overrun warnings on Baikal-M Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 10/39] net: stmmac: support of Baikal-BE1000 SoCs GMAC Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 11/39] net: fwnode_get_phy_id: consider all compatible strings Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 12/39] hwmon: bt1-pvt: access registers via pvt_{readl, writel} helpers Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 13/39] hwmon: bt1-pvt: define pvt_readl/pvt_writel for Baikal-M SoC Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 14/39] hwmon: bt1-pvt: adjusted probing " Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 15/39] hwmon: bt1-pvt: added compatible baikal, pvt Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 16/39] PVT: support register addressing with new firmware Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 17/39] drm: add Baikal-M SoC video display unit driver Daniil Gnusarev
2024-10-14 14:01 ` [d-kernel] [PATCH 18/39] drm/bridge: dw-hdmi: support ahb audio hw revision 0x2a Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 19/39] dt-bindings: dw-hdmi: added ahb-audio-regshift Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 20/39] drm/bridge: dw-hdmi: force ahb audio register offset for Baikal-M Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 21/39] drm/panfrost: forcibly set dma-coherent on Baikal-M Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 22/39] drm/panfrost: disable devfreq " Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 23/39] bmc: add board management controller driver Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 24/39] pm: disable all sleep states on Baikal-M based boards Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 25/39] sound: dwc-i2s: paper over RX overrun warnings on Baikal-M Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 26/39] sound: dwc-i2s: request all IRQs specified in device tree Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 27/39] usb: dwc3: of-simple: added compatible string for Baikal-M SoC Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 28/39] serial: 8250_dw: verify clock rate in dw8250_set_termios Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 29/39] clk: use "cmu-id" if there is no "reg" in devicetree Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 30/39] pci: baikal-pcie: driver compatibility with SDK earlier than 5.7 Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 31/39] pci: baikal-pcie: driver compatibility with SDK versions 5.4 Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 32/39] dw-pcie: refuse to load on Baikal-M with recent firmware Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 33/39] drm: baikal-vdu: driver compatibility with SDK earlier than 5.9 Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 34/39] input: new driver - serdev-serio Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 35/39] input: added TF307 serio PS/2 emulator driver Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 36/39] input: tp_serio: catch up API changes Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 37/39] drm: baikal-m: add vblank events, fix mode switching Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 38/39] drm: baikal-vdu: disable backlight driver loading Daniil Gnusarev
2024-10-14 14:02 ` [d-kernel] [PATCH 39/39] config-aarch64: enable more configs for baikal-m support Daniil Gnusarev
2024-10-14 14:28 ` [d-kernel] [PATCH 00/39] Support Baikal-M in un-def in p11 Vitaly Chikunov

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=20241014140221.535985-4-gnusarevda@basealt.ru \
    --to=gnusarevda@basealt.ru \
    --cc=devel-kernel@lists.altlinux.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

ALT Linux kernel packages development

This inbox may be cloned and mirrored by anyone:

	git clone --mirror http://lore.altlinux.org/devel-kernel/0 devel-kernel/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 devel-kernel devel-kernel/ http://lore.altlinux.org/devel-kernel \
		devel-kernel@altlinux.org devel-kernel@altlinux.ru devel-kernel@altlinux.com
	public-inbox-index devel-kernel

Example config snippet for mirrors.
Newsgroup available over NNTP:
	nntp://lore.altlinux.org/org.altlinux.lists.devel-kernel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git