linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 1/2] phy: Add a driver for simple phy
@ 2016-01-28 19:52 Alban Bedel
  2016-01-28 19:52 ` [PATCH v3 2/2] phy: Add a driver for the ATH79 USB phy Alban Bedel
  2016-02-01 11:42 ` [PATCH v3 1/2] phy: Add a driver for simple phy Kishon Vijay Abraham I
  0 siblings, 2 replies; 9+ messages in thread
From: Alban Bedel @ 2016-01-28 19:52 UTC (permalink / raw)
  To: linux-kernel
  Cc: Kishon Vijay Abraham I, Andrew Morton, Greg Kroah-Hartman,
	David S. Miller, Mauro Carvalho Chehab, Joe Perches, Jiri Slaby,
	Alban Bedel

This driver is meant to take care of all trivial phys that don't need
any special configuration, it just enable a regulator, a clock and
deassert a reset. A public API is also included to allow re-using the
code in other drivers.

Signed-off-by: Alban Bedel <albeu@free.fr>
---
 drivers/phy/Kconfig        |  12 +++
 drivers/phy/Makefile       |   1 +
 drivers/phy/phy-simple.c   | 204 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/phy/simple.h |  39 +++++++++
 4 files changed, 256 insertions(+)
 create mode 100644 drivers/phy/phy-simple.c
 create mode 100644 include/linux/phy/simple.h

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index e7e117d..efbaee4 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -125,6 +125,18 @@ config PHY_RCAR_GEN3_USB2
 	help
 	  Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs.
 
+config PHY_SIMPLE
+	tristate
+	select GENERIC_PHY
+	help
+
+config PHY_SIMPLE_PDEV
+	tristate "Simple PHY driver"
+	select PHY_SIMPLE
+	help
+	  A PHY driver for simple devices that only need a regulator, clock
+	  and reset for power up and shutdown.
+
 config OMAP_CONTROL_PHY
 	tristate "OMAP CONTROL PHY Driver"
 	depends on ARCH_OMAP2PLUS || COMPILE_TEST
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index c80f09d..1cc7268 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_PHY_EXYNOS5250_SATA)	+= phy-exynos5250-sata.o
 obj-$(CONFIG_PHY_HIX5HD2_SATA)		+= phy-hix5hd2-sata.o
 obj-$(CONFIG_PHY_HI6220_USB)		+= phy-hi6220-usb.o
 obj-$(CONFIG_PHY_MT65XX_USB3)		+= phy-mt65xx-usb3.o
+obj-$(CONFIG_PHY_SIMPLE)		+= phy-simple.o
 obj-$(CONFIG_PHY_SUN4I_USB)		+= phy-sun4i-usb.o
 obj-$(CONFIG_PHY_SUN9I_USB)		+= phy-sun9i-usb.o
 obj-$(CONFIG_PHY_SAMSUNG_USB2)		+= phy-exynos-usb2.o
diff --git a/drivers/phy/phy-simple.c b/drivers/phy/phy-simple.c
new file mode 100644
index 0000000..013f846
--- /dev/null
+++ b/drivers/phy/phy-simple.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2015 Alban Bedel <albeu@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/simple.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+int simple_phy_power_on(struct phy *phy)
+{
+	struct simple_phy *sphy = phy_get_drvdata(phy);
+	int err;
+
+	if (sphy->regulator) {
+		err = regulator_enable(sphy->regulator);
+		if (err)
+			return err;
+	}
+
+	if (sphy->clk) {
+		err = clk_prepare_enable(sphy->clk);
+		if (err)
+			goto regulator_disable;
+	}
+
+	if (sphy->reset) {
+		err = reset_control_deassert(sphy->reset);
+		if (err)
+			goto clock_disable;
+	}
+
+	return 0;
+
+clock_disable:
+	if (sphy->clk)
+		clk_disable_unprepare(sphy->clk);
+regulator_disable:
+	if (sphy->regulator)
+		WARN_ON(regulator_disable(sphy->regulator));
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(simple_phy_power_on);
+
+int simple_phy_power_off(struct phy *phy)
+{
+	struct simple_phy *sphy = phy_get_drvdata(phy);
+	int err;
+
+	if (sphy->reset) {
+		err = reset_control_assert(sphy->reset);
+		if (err)
+			return err;
+	}
+
+	if (sphy->clk)
+		clk_disable_unprepare(sphy->clk);
+
+	if (sphy->regulator) {
+		err = regulator_disable(sphy->regulator);
+		if (err)
+			goto clock_enable;
+	}
+
+	return 0;
+
+clock_enable:
+	if (sphy->clk)
+		WARN_ON(clk_prepare_enable(sphy->clk));
+	if (sphy->reset)
+		WARN_ON(reset_control_deassert(sphy->reset));
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(simple_phy_power_off);
+
+static const struct phy_ops simple_phy_ops = {
+	.power_on	= simple_phy_power_on,
+	.power_off	= simple_phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+struct phy *devm_simple_phy_create(struct device *dev,
+				const struct simple_phy_desc *desc,
+				struct simple_phy *sphy)
+{
+	struct phy *phy;
+
+	if (!dev || !desc)
+		return ERR_PTR(-EINVAL);
+
+	if (!sphy) {
+		sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL);
+		if (!sphy)
+			return ERR_PTR(-ENOMEM);
+	}
+
+	if (!IS_ERR_OR_NULL(desc->regulator)) {
+		sphy->regulator = devm_regulator_get(dev, desc->regulator);
+		if (IS_ERR(sphy->regulator)) {
+			if (PTR_ERR(sphy->regulator) == -ENOENT)
+				sphy->regulator = NULL;
+			else
+				return ERR_PTR(PTR_ERR(sphy->regulator));
+		}
+	}
+
+	if (!IS_ERR(desc->clk)) {
+		sphy->clk = devm_clk_get(dev, desc->clk);
+		if (IS_ERR(sphy->clk)) {
+			if (PTR_ERR(sphy->clk) == -ENOENT)
+				sphy->clk = NULL;
+			else
+				return ERR_PTR(PTR_ERR(sphy->clk));
+		}
+	}
+
+	if (!IS_ERR(desc->reset)) {
+		sphy->reset = devm_reset_control_get(dev, desc->reset);
+		if (IS_ERR(sphy->reset)) {
+			int err = PTR_ERR(sphy->reset);
+
+			if (err == -ENOENT || err == -ENOTSUPP)
+				sphy->reset = NULL;
+			else
+				return ERR_PTR(err);
+		}
+	}
+
+	phy = devm_phy_create(dev, NULL, desc->ops ?: &simple_phy_ops);
+	if (IS_ERR(phy))
+		return ERR_PTR(PTR_ERR(phy));
+
+	phy_set_drvdata(phy, sphy);
+
+	return phy;
+}
+EXPORT_SYMBOL_GPL(devm_simple_phy_create);
+
+#ifdef CONFIG_PHY_SIMPLE_PDEV
+#ifdef CONFIG_OF
+/* Default config, no regulator, default clock and reset if any */
+static const struct simple_phy_desc simple_phy_default_desc = {};
+
+static const struct of_device_id simple_phy_of_match[] = {
+	{ .compatible = "simple-phy", .data = &simple_phy_default_desc },
+	{}
+};
+MODULE_DEVICE_TABLE(of, simple_phy_of_match);
+
+const struct simple_phy_desc *simple_phy_get_of_desc(struct device *dev)
+{
+	const struct of_device_id *match;
+
+	match = of_match_device(simple_phy_of_match, dev);
+
+	return match ? match->data : NULL;
+}
+#else
+const struct simple_phy_desc *simple_phy_get_of_desc(struct device *dev)
+{
+	return NULL;
+}
+#endif
+
+static int simple_phy_probe(struct platform_device *pdev)
+{
+	const struct simple_phy_desc *desc = pdev->dev.platform_data;
+	struct phy *phy;
+
+	if (!desc)
+		desc = simple_phy_get_of_desc(&pdev->dev);
+	if (!desc)
+		return -EINVAL;
+
+	phy = devm_simple_phy_create(&pdev->dev, desc, NULL);
+	if (IS_ERR(phy))
+		return PTR_ERR(phy);
+
+	return PTR_ERR_OR_ZERO(devm_of_phy_provider_register(
+				&pdev->dev, of_phy_simple_xlate));
+}
+
+static struct platform_driver simple_phy_driver = {
+	.probe	= simple_phy_probe,
+	.driver = {
+		.of_match_table	= of_match_ptr(simple_phy_of_match),
+		.name		= "phy-simple",
+	}
+};
+module_platform_driver(simple_phy_driver);
+
+#endif /* CONFIG_PHY_SIMPLE_PDEV */
+
+MODULE_DESCRIPTION("Simple PHY driver");
+MODULE_AUTHOR("Alban Bedel <albeu@free.fr>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/phy/simple.h b/include/linux/phy/simple.h
new file mode 100644
index 0000000..f368b57
--- /dev/null
+++ b/include/linux/phy/simple.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 Alban Bedel <albeu@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __LINUX_PHY_SIMPLE__
+#define __LINUX_PHY_SIMPLE__
+
+#include <linux/phy/phy.h>
+
+struct reset_control;
+struct clk;
+
+struct simple_phy {
+	struct regulator *regulator;
+	struct reset_control *reset;
+	struct clk *clk;
+};
+
+struct simple_phy_desc {
+	const struct phy_ops *ops;
+	const char *regulator;
+	const char *reset;
+	const char *clk;
+};
+
+struct phy *devm_simple_phy_create(struct device *dev,
+				const struct simple_phy_desc *desc,
+				struct simple_phy *sphy);
+
+int simple_phy_power_on(struct phy *phy);
+
+int simple_phy_power_off(struct phy *phy);
+
+#endif /* __LINUX_PHY_SIMPLE__ */
-- 
2.0.0

^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2016-10-13  8:51 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-01-28 19:52 [PATCH v3 1/2] phy: Add a driver for simple phy Alban Bedel
2016-01-28 19:52 ` [PATCH v3 2/2] phy: Add a driver for the ATH79 USB phy Alban Bedel
2016-02-02  6:11   ` Kishon Vijay Abraham I
2016-02-17 14:34     ` Alban
2016-02-01 11:42 ` [PATCH v3 1/2] phy: Add a driver for simple phy Kishon Vijay Abraham I
2016-02-17 14:21   ` Alban
2016-10-07  8:27     ` Vivek Gautam
2016-10-12 22:17       ` Alban
2016-10-13  8:26         ` Vivek Gautam

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).