[RFC,4/4] phy: lantiq: Add PEF2256 PHY support
Commit Message
The Lantiq PEF2256, is a framer and line interface component designed to
fulfill all required interfacing between an analog E1/T1/J1 line and the
digital PCM system highway/H.100 bus.
The PHY support allows to provide the PEF2556 as a generic PHY and to
use the PHY API to retrieve the E1 line carrier status from the PHY
consumer.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
drivers/phy/lantiq/Kconfig | 15 +++
drivers/phy/lantiq/Makefile | 1 +
drivers/phy/lantiq/phy-lantiq-pef2256.c | 131 ++++++++++++++++++++++++
3 files changed, 147 insertions(+)
create mode 100644 drivers/phy/lantiq/phy-lantiq-pef2256.c
@@ -2,6 +2,21 @@
#
# Phy drivers for Lantiq / Intel platforms
#
+config PHY_LANTIQ_PEF2256
+ tristate "Lantiq PEF2256 PHY"
+ depends on MFD_PEF2256
+ select GENERIC_PHY
+ help
+ Enable support for the Lantiq PEF2256 (FALC56) PHY.
+ The PEF2256 is a framer and line interface between analog E1/T1/J1
+ line and a digital PCM bus.
+ This PHY support allows to consider the PEF2256 as a PHY.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called phy-lantiq-pef2256.
+
config PHY_LANTIQ_VRX200_PCIE
tristate "Lantiq VRX200/ARX300 PCIe PHY"
depends on SOC_TYPE_XWAY || COMPILE_TEST
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PHY_LANTIQ_PEF2256) += phy-lantiq-pef2256.o
obj-$(CONFIG_PHY_LANTIQ_RCU_USB2) += phy-lantiq-rcu-usb2.o
obj-$(CONFIG_PHY_LANTIQ_VRX200_PCIE) += phy-lantiq-vrx200-pcie.o
new file mode 100644
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PEF2256 phy support
+ *
+ * Copyright 2023 CS GROUP France
+ *
+ * Author: Herve Codina <herve.codina@bootlin.com>
+ */
+
+#include <linux/phy/phy.h>
+#include <linux/mfd/pef2256.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct pef2256_phy {
+ struct phy *phy;
+ struct pef2256 *pef2256;
+ struct device *dev;
+ struct atomic_notifier_head event_notifier_list;
+ struct notifier_block nb;
+};
+
+static int pef2256_carrier_notifier(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct pef2256_phy *pef2256 = container_of(nb, struct pef2256_phy, nb);
+
+ switch (action) {
+ case PEF2256_EVENT_CARRIER:
+ return atomic_notifier_call_chain(&pef2256->event_notifier_list,
+ PHY_EVENT_STATUS,
+ NULL);
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int pef2256_phy_atomic_notifier_register(struct phy *phy, struct notifier_block *nb)
+{
+ struct pef2256_phy *pef2256 = phy_get_drvdata(phy);
+
+ return atomic_notifier_chain_register(&pef2256->event_notifier_list, nb);
+}
+
+static int pef2256_phy_atomic_notifier_unregister(struct phy *phy, struct notifier_block *nb)
+{
+ struct pef2256_phy *pef2256 = phy_get_drvdata(phy);
+
+ return atomic_notifier_chain_unregister(&pef2256->event_notifier_list, nb);
+}
+
+static int pef2256_phy_init(struct phy *phy)
+{
+ struct pef2256_phy *pef2256 = phy_get_drvdata(phy);
+
+ ATOMIC_INIT_NOTIFIER_HEAD(&pef2256->event_notifier_list);
+
+ pef2256->nb.notifier_call = pef2256_carrier_notifier;
+ return pef2256_register_event_notifier(pef2256->pef2256, &pef2256->nb);
+}
+
+static int pef2256_phy_exit(struct phy *phy)
+{
+ struct pef2256_phy *pef2256 = phy_get_drvdata(phy);
+
+ return pef2256_unregister_event_notifier(pef2256->pef2256, &pef2256->nb);
+}
+
+static int pef2256_phy_get_status(struct phy *phy, union phy_status *status)
+{
+ struct pef2256_phy *pef2256 = phy_get_drvdata(phy);
+
+ status->basic.link_is_on = pef2256_get_carrier(pef2256->pef2256);
+ return 0;
+}
+
+static const struct phy_ops pef2256_phy_ops = {
+ .owner = THIS_MODULE,
+ .init = pef2256_phy_init,
+ .exit = pef2256_phy_exit,
+ .get_status = pef2256_phy_get_status,
+ .atomic_notifier_register = pef2256_phy_atomic_notifier_register,
+ .atomic_notifier_unregister = pef2256_phy_atomic_notifier_unregister,
+};
+
+static int pef2256_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *provider;
+ struct pef2256_phy *pef2256;
+
+ pef2256 = devm_kzalloc(&pdev->dev, sizeof(*pef2256), GFP_KERNEL);
+ if (!pef2256)
+ return -ENOMEM;
+
+ pef2256->dev = &pdev->dev;
+ pef2256->pef2256 = dev_get_drvdata(pef2256->dev->parent);
+
+ pef2256->phy = devm_phy_create(pef2256->dev, NULL, &pef2256_phy_ops);
+ if (IS_ERR(pef2256->phy))
+ return PTR_ERR(pef2256->phy);
+
+ phy_set_drvdata(pef2256->phy, pef2256);
+ pef2256->phy->attrs.mode = PHY_MODE_BASIC;
+
+ provider = devm_of_phy_provider_register(pef2256->dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id pef2256_phy_of_match[] = {
+ { .compatible = "lantiq,pef2256-phy" },
+ {} /* sentinel */
+};
+MODULE_DEVICE_TABLE(of, pef2256_phy_of_match);
+
+static struct platform_driver pef2256_phy_driver = {
+ .driver = {
+ .name = "lantiq-pef2256-phy",
+ .of_match_table = pef2256_phy_of_match,
+ },
+ .probe = pef2256_phy_probe,
+};
+module_platform_driver(pef2256_phy_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("PEF2256 PHY driver");
+MODULE_LICENSE("GPL");