[PATCH 2/2] phy: intel: Add driver support for combo phy

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Combo phy subsystem provides PHYs for various
controllers like PCIe, SATA and EMAC.

Signed-off-by: Dilip Kota <eswara.kota@xxxxxxxxxxxxxxx>
---
 drivers/phy/Kconfig                 |   1 +
 drivers/phy/Makefile                |   1 +
 drivers/phy/intel/Kconfig           |  15 +
 drivers/phy/intel/Makefile          |   2 +
 drivers/phy/intel/phy-intel-combo.c | 732 ++++++++++++++++++++++++++++++++++++
 5 files changed, 751 insertions(+)
 create mode 100644 drivers/phy/intel/Kconfig
 create mode 100644 drivers/phy/intel/Makefile
 create mode 100644 drivers/phy/intel/phy-intel-combo.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 0263db2ac874..cde11d1e57f4 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -55,6 +55,7 @@ source "drivers/phy/broadcom/Kconfig"
 source "drivers/phy/cadence/Kconfig"
 source "drivers/phy/freescale/Kconfig"
 source "drivers/phy/hisilicon/Kconfig"
+source "drivers/phy/intel/Kconfig"
 source "drivers/phy/lantiq/Kconfig"
 source "drivers/phy/marvell/Kconfig"
 source "drivers/phy/mediatek/Kconfig"
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index c96a1afc95bd..310c149a9df5 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -18,6 +18,7 @@ obj-y					+= broadcom/	\
 					   cadence/	\
 					   freescale/	\
 					   hisilicon/	\
+					   intel/	\
 					   lantiq/	\
 					   marvell/	\
 					   motorola/	\
diff --git a/drivers/phy/intel/Kconfig b/drivers/phy/intel/Kconfig
new file mode 100644
index 000000000000..cfe7fb89f08b
--- /dev/null
+++ b/drivers/phy/intel/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Phy drivers for Intel X86 LGM platform
+#
+config PHY_INTEL_COMBO
+	bool "Intel Combo PHY driver"
+	depends on OF && HAS_IOMEM
+	select MFD_SYSCON
+	select GENERIC_PHY
+	help
+	  Enable this to support Intel combo phy.
+
+	  This driver configures combo phy subsystem on Intel gateway
+	  chipsets which provides PHYs for various controllers, EMAC,
+	  SATA and PCIe.
diff --git a/drivers/phy/intel/Makefile b/drivers/phy/intel/Makefile
new file mode 100644
index 000000000000..960218133d84
--- /dev/null
+++ b/drivers/phy/intel/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PHY_INTEL_COMBO)		+= phy-intel-combo.o
diff --git a/drivers/phy/intel/phy-intel-combo.c b/drivers/phy/intel/phy-intel-combo.c
new file mode 100644
index 000000000000..3fee02ab5e53
--- /dev/null
+++ b/drivers/phy/intel/phy-intel-combo.c
@@ -0,0 +1,732 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Combo-PHY driver
+ *
+ * Copyright (C) 2019 Intel Corporation.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#define PCIE_PHY_GEN_CTRL	0x00
+#define PCIE_PHY_CLK_PAD	BIT(17)
+#define PCIE_PHY_MPLLA_CTRL	0x10
+#define PCIE_PHY_MPLLB_CTRL	0x14
+
+#define REG_COMBO_MODE(x)	((x) * 0x200)
+#define REG_CLK_DISABLE(x)	((x) * 0x200 + 0x124)
+
+#define CLK_100MHZ		100000000
+#define CLK_156_25MHZ		156250000
+
+#define COMBO_PHY_ID(x)	((x)->parent->id)
+#define PHY_ID(x)	((x)->id)
+
+static const char *const intel_iphy_names[] = {"pcie", "xpcs", "sata"};
+static const unsigned long intel_iphy_clk_rate[] = {
+	CLK_100MHZ, CLK_156_25MHZ, CLK_100MHZ
+};
+
+enum {
+	PHY_0 = 0,
+	PHY_1,
+	PHY_MAX_NUM,
+};
+
+enum intel_phy_mode {
+	PHY_PCIE_MODE = 0,
+	PHY_XPCS_MODE,
+	PHY_SATA_MODE,
+	PHY_MAX_MODE,
+};
+
+enum intel_combo_mode {
+	PCIE0_PCIE1_MODE = 0,
+	PCIE_DL_MODE,
+	RXAUI_MODE,
+	XPCS0_XPCS1_MODE,
+	SATA0_SATA1_MODE,
+	COMBO_PHY_MODE_MAX,
+};
+
+enum aggregated_mode {
+	PHY_SL_MODE = 0,
+	PHY_DL_MODE,
+};
+
+enum intel_phy_role {
+	PHY_INDIVIDUAL = 0,	/* Not aggr phy mode */
+	PHY_MASTER,		/* DL mode, PHY 0 */
+	PHY_SLAVE,		/* DL mode, PHY 1 */
+};
+
+#define	PHY_PCIE_CAP	BIT(PHY_PCIE_MODE)
+#define	PHY_XPCS_CAP	BIT(PHY_XPCS_MODE)
+#define	PHY_SATA_CAP	BIT(PHY_SATA_MODE)
+
+struct intel_combo_phy;
+
+struct intel_cbphy_iphy {
+	u32 id; /* Internal PHY idx 0 - 1 */
+	bool enable;
+	struct intel_combo_phy *parent;
+	struct platform_device *pdev;
+	struct device_node *np;
+	struct device	*dev;
+	struct phy *phy;
+	struct reset_control *app_rst;
+	void __iomem *app_base;
+	struct clk *freq_clk;
+	unsigned long clk_rate;
+	enum intel_phy_mode phy_mode;
+	enum intel_phy_role phy_role;
+	bool power_en;
+};
+
+struct intel_combo_phy {
+	u32 id; /* Physical COMBO PHY Index */
+	u32 phy_cap;
+	u32 bid; /* Bus ID */
+	struct device *dev;
+	struct regmap *syscfg;
+	struct regmap *hsiocfg;
+	struct reset_control *phy_rst;
+	struct reset_control *core_rst;
+	struct intel_cbphy_iphy iphy[PHY_MAX_NUM];
+	enum intel_combo_mode mode;
+	enum aggregated_mode aggr_mode;
+};
+
+static void intel_cbphy_iphy_enable(struct intel_cbphy_iphy *iphy, bool set)
+{
+	struct intel_combo_phy *cbphy = iphy->parent;
+	u32 val, bitn;
+
+	bitn = iphy->phy_mode * 2 + iphy->id;
+
+	/* Register: 0 is enable, 1 is disable */
+	val =  set ? 0 : BIT(bitn);
+
+	regmap_update_bits(cbphy->hsiocfg, REG_CLK_DISABLE(cbphy->bid),
+			   BIT(bitn), val);
+}
+
+static void intel_cbphy_pcie_refclk_cfg(struct intel_cbphy_iphy *iphy, bool set)
+{
+	struct intel_combo_phy *cbphy = iphy->parent;
+	const u32 pad_dis_cfg_off = 0x174;
+	u32 val, bitn;
+
+	bitn = cbphy->id * 2 + iphy->id;
+
+	/* Register: 0 is enable, 1 is disable */
+	val = set ? 0 : BIT(bitn);
+
+	regmap_update_bits(cbphy->syscfg, pad_dis_cfg_off, BIT(bitn), val);
+}
+
+static ssize_t intel_cbphy_info_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct intel_combo_phy *cbphy;
+	int i, off;
+
+	cbphy = dev_get_drvdata(dev);
+
+	off = sprintf(buf, "mode: %u\n", cbphy->mode);
+
+	off += sprintf(buf + off, "aggr mode: %s\n",
+		      cbphy->aggr_mode == PHY_DL_MODE ? "Yes" : "No");
+
+	off += sprintf(buf + off, "capability: ");
+	for (i = PHY_PCIE_MODE; i < PHY_MAX_MODE; i++) {
+		if (BIT(i) & cbphy->phy_cap)
+			off += sprintf(buf + off, "%s ", intel_iphy_names[i]);
+	}
+
+	off += sprintf(buf + off, "\n");
+
+	for (i = 0; i < PHY_MAX_NUM; i++) {
+		off += sprintf(buf + off, "PHY%d mode: %s, enable: %s\n",
+			       i, intel_iphy_names[cbphy->iphy[i].phy_mode],
+			       cbphy->iphy[i].enable ? "Yes" : "No");
+	}
+
+	return off;
+}
+
+static DEVICE_ATTR_RO(intel_cbphy_info);
+
+static struct attribute *intel_cbphy_attrs[] = {
+	&dev_attr_intel_cbphy_info.attr,
+	NULL,
+};
+
+ATTRIBUTE_GROUPS(intel_cbphy);
+
+static int intel_cbphy_sysfs_init(struct intel_combo_phy *cbphy)
+{
+	return devm_device_add_groups(cbphy->dev, intel_cbphy_groups);
+}
+
+static inline void combo_phy_w32_off_mask(void __iomem *base,
+					  u32 mask, u32 set, unsigned int reg)
+{
+	u32 val;
+
+	val = readl(base + reg);
+	val &= ~mask;
+	val |= FIELD_PREP(mask, set);
+	writel(val, base + reg);
+}
+
+static int intel_cbphy_iphy_cfg(struct intel_cbphy_iphy *iphy,
+				int (*phy_cfg)(struct intel_cbphy_iphy *))
+{
+	struct intel_combo_phy *cbphy = iphy->parent;
+	struct intel_cbphy_iphy *sphy;
+	int ret;
+
+	ret = phy_cfg(iphy);
+	if (ret)
+		return ret;
+
+	if (cbphy->aggr_mode == PHY_DL_MODE) {
+		sphy = &cbphy->iphy[PHY_1];
+		ret =  phy_cfg(sphy);
+	}
+
+	return ret;
+}
+
+static int intel_cbphy_pcie_en_pad_refclk(struct intel_cbphy_iphy *iphy)
+{
+	intel_cbphy_pcie_refclk_cfg(iphy, true);
+	combo_phy_w32_off_mask(iphy->app_base, PCIE_PHY_CLK_PAD,
+			       0, PCIE_PHY_GEN_CTRL);
+	/*
+	 * NB, no way to identify gen1/2/3/4 for mppla and mpplb, just delay
+	 * for stable plla(gen1/gen2) or pllb(gen3/gen4)
+	 */
+	usleep_range(50, 100);
+
+	return 0;
+}
+
+static int intel_cbphy_pcie_dis_pad_refclk(struct intel_cbphy_iphy *iphy)
+{
+	intel_cbphy_pcie_refclk_cfg(iphy, false);
+	combo_phy_w32_off_mask(iphy->app_base, PCIE_PHY_CLK_PAD,
+			       1, PCIE_PHY_GEN_CTRL);
+
+	return 0;
+}
+
+static int intel_cbphy_iphy_power_on(struct intel_cbphy_iphy *iphy)
+{
+	struct device *dev = iphy->dev;
+	int ret;
+
+	ret = reset_control_deassert(iphy->app_rst);
+	if (ret) {
+		dev_err(dev, "PHY(%u:%u) phy deassert failed!\n",
+			COMBO_PHY_ID(iphy), PHY_ID(iphy));
+		return ret;
+	}
+
+	ret = clk_prepare_enable(iphy->freq_clk);
+	if (ret) {
+		dev_err(dev, "PHY(%u:%u) freq clock enable failed!\n",
+			COMBO_PHY_ID(iphy), PHY_ID(iphy));
+		return ret;
+	}
+
+	ret = clk_set_rate(iphy->freq_clk, iphy->clk_rate);
+	if (ret) {
+		dev_err(dev, "PHY(%u:%u) clock frequency set to %lu failed!\n",
+			COMBO_PHY_ID(iphy), PHY_ID(iphy),
+			iphy->clk_rate);
+		goto clk_err;
+	}
+
+	intel_cbphy_iphy_enable(iphy, true);
+	iphy->power_en = true;
+
+	return ret;
+
+clk_err:
+	clk_disable_unprepare(iphy->freq_clk);
+
+	return ret;
+}
+
+static int intel_cbphy_iphy_power_off(struct intel_cbphy_iphy *iphy)
+{
+	struct device *dev = iphy->dev;
+	int ret;
+
+	ret = reset_control_assert(iphy->app_rst);
+	if (ret) {
+		dev_err(dev, "PHY(%u:%u) core assert failed!\n",
+			COMBO_PHY_ID(iphy), PHY_ID(iphy));
+		return ret;
+	}
+
+	intel_cbphy_iphy_enable(iphy, false);
+	clk_disable_unprepare(iphy->freq_clk);
+	iphy->power_en = false;
+
+	return 0;
+}
+
+static int intel_cbphy_init(struct phy *phy)
+{
+	struct intel_cbphy_iphy *iphy;
+	int ret = 0;
+
+	iphy = phy_get_drvdata(phy);
+
+	if (iphy->phy_mode == PHY_PCIE_MODE) {
+		ret = intel_cbphy_iphy_cfg(iphy,
+					   intel_cbphy_pcie_en_pad_refclk);
+	}
+
+	if (!ret)
+		ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_on);
+
+	return ret;
+}
+
+static int intel_cbphy_exit(struct phy *phy)
+{
+	struct intel_cbphy_iphy *iphy;
+	int ret = 0;
+
+	iphy = phy_get_drvdata(phy);
+
+	if (iphy->power_en)
+		ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_off);
+
+	if (!ret && iphy->phy_mode == PHY_PCIE_MODE)
+		ret = intel_cbphy_iphy_cfg(iphy,
+					   intel_cbphy_pcie_dis_pad_refclk);
+
+	return ret;
+}
+
+static int intel_cbphy_power_on(struct phy *phy)
+{
+	struct intel_cbphy_iphy *iphy;
+
+	iphy = phy_get_drvdata(phy);
+
+	if (iphy->power_en)
+		return 0;
+
+	return intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_on);
+}
+
+static int intel_cbphy_power_off(struct phy *phy)
+{
+	struct intel_cbphy_iphy *iphy;
+
+	iphy = phy_get_drvdata(phy);
+
+	if (!iphy->power_en)
+		return 0;
+
+	return intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_off);
+}
+
+static int intel_cbphy_iphy_mem_resource(struct intel_cbphy_iphy *iphy)
+{
+	void __iomem *base;
+
+	base = devm_platform_ioremap_resource(iphy->pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	iphy->app_base = base;
+
+	return 0;
+}
+
+static int intel_cbphy_iphy_get_clks(struct intel_cbphy_iphy *iphy)
+{
+	enum intel_phy_mode mode = iphy->phy_mode;
+	struct device *dev = iphy->dev;
+	int ret = 0;
+
+	iphy->freq_clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(iphy->freq_clk)) {
+		ret = PTR_ERR(iphy->freq_clk);
+		if (ret != -EPROBE_DEFER) {
+			dev_err(dev, "PHY[%u:%u] No %s freq clock\n",
+				COMBO_PHY_ID(iphy), PHY_ID(iphy),
+				intel_iphy_names[mode]);
+		}
+
+		return ret;
+	}
+
+	iphy->clk_rate = intel_iphy_clk_rate[mode];
+
+	return ret;
+}
+
+static int intel_cbphy_iphy_get_reset(struct intel_cbphy_iphy *iphy)
+{
+	enum intel_phy_mode mode = iphy->phy_mode;
+	struct device *dev = iphy->dev;
+	int ret = 0;
+
+	iphy->app_rst = devm_reset_control_get_optional(dev, NULL);
+	if (IS_ERR(iphy->app_rst)) {
+		ret = PTR_ERR(iphy->app_rst);
+		if (ret != -EPROBE_DEFER) {
+			dev_err(dev, "PHY[%u:%u] Get %s reset ctrl Err!\n",
+				COMBO_PHY_ID(iphy),
+				PHY_ID(iphy), intel_iphy_names[mode]);
+		}
+
+		return ret;
+	}
+
+	return ret;
+}
+
+static int intel_cbphy_iphy_dt_parse(struct intel_combo_phy *cbphy,
+				     struct fwnode_handle *fwn, int idx)
+{
+	struct intel_cbphy_iphy *iphy = &cbphy->iphy[idx];
+	struct platform_device *pdev;
+	struct device *dev;
+	int ret = 0;
+	u32 prop;
+
+	iphy->id = idx;
+	iphy->enable = false;
+	iphy->power_en = false;
+	iphy->parent = cbphy;
+	iphy->np = to_of_node(fwn);
+
+	pdev = of_find_device_by_node(iphy->np);
+	if (!pdev) {
+		dev_warn(cbphy->dev, "Combo-PHY%u: PHY device: %d disabled!\n",
+			 cbphy->id, idx);
+		return 0;
+	}
+
+	dev = &pdev->dev;
+	iphy->pdev = pdev;
+	iphy->dev = dev;
+	platform_set_drvdata(pdev, iphy);
+
+	if (!device_property_read_u32(dev, "intel,phy-mode", &prop)) {
+		iphy->phy_mode = prop;
+		if (iphy->phy_mode >= PHY_MAX_MODE) {
+			dev_err(dev, "PHY mode: %u is invalid\n",
+				iphy->phy_mode);
+			return -EINVAL;
+		}
+	}
+
+	if (!(BIT(iphy->phy_mode) & cbphy->phy_cap)) {
+		dev_err(dev,
+			"PHY mode %u is not supported by CbPhy(%u) cap: 0x%x\n",
+			iphy->phy_mode, PHY_ID(iphy), cbphy->phy_cap);
+		return -EINVAL;
+	}
+
+	cbphy->aggr_mode = PHY_SL_MODE;
+	if (device_property_read_bool(dev, "intel,aggregation"))
+		cbphy->aggr_mode = PHY_DL_MODE;
+
+	if (cbphy->aggr_mode == PHY_DL_MODE)
+		iphy->phy_role = iphy->id ? PHY_SLAVE : PHY_MASTER;
+	else
+		iphy->phy_role = PHY_INDIVIDUAL;
+
+	ret = intel_cbphy_iphy_mem_resource(iphy);
+	if (ret)
+		return ret;
+
+	ret = intel_cbphy_iphy_get_clks(iphy);
+	if (ret)
+		return ret;
+
+	ret = intel_cbphy_iphy_get_reset(iphy);
+	if (ret)
+		return ret;
+
+	iphy->enable = fwnode_device_is_available(fwn);
+
+	dev_dbg(dev, "PHY(%u:%u) mode: %u, role: %u, enable %u\n",
+		COMBO_PHY_ID(iphy), PHY_ID(iphy),
+		iphy->phy_mode, iphy->phy_role, iphy->enable);
+
+	return ret;
+}
+
+static int intel_cbphy_dt_sanity_check(struct intel_combo_phy *cbphy)
+{
+	struct intel_cbphy_iphy *iphy0, *iphy1;
+
+	iphy0 = &cbphy->iphy[PHY_0];
+	iphy1 = &cbphy->iphy[PHY_1];
+
+	if (!iphy0->enable && !iphy1->enable) {
+		dev_err(cbphy->dev, "CBPHY%u both PHY disabled!\n", cbphy->id);
+		return -EINVAL;
+	}
+
+	if (cbphy->aggr_mode == PHY_DL_MODE) {
+		if (!iphy0->enable || !iphy1->enable) {
+			dev_err(cbphy->dev,
+				"Dual lane mode but lane0: %s, lane1: %s\n",
+				iphy0->enable ? "on" : "off",
+				iphy1->enable ? "on" : "off");
+			return -EINVAL;
+		}
+
+		if (iphy0->phy_mode != iphy1->phy_mode) {
+			dev_err(cbphy->dev,
+				" Mode mismatch lane0 : %u, lane1 : %u\n",
+				iphy0->phy_mode, iphy1->phy_mode);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int intel_cbphy_dt_parse(struct intel_combo_phy *cbphy)
+{
+	struct device *dev = cbphy->dev;
+	struct device_node *np = dev->of_node;
+	struct fwnode_handle *fwn;
+	int i = 0, ret = 0;
+
+	cbphy->phy_rst = devm_reset_control_get_optional(dev, "phy");
+	if (IS_ERR(cbphy->phy_rst)) {
+		ret = PTR_ERR(cbphy->phy_rst);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "get phy reset err: %d!\n", ret);
+		return ret;
+	}
+
+	cbphy->core_rst = devm_reset_control_get_optional(dev, "core");
+	if (IS_ERR(cbphy->core_rst)) {
+		ret = PTR_ERR(cbphy->core_rst);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "get combo reset err: %d!\n", ret);
+		return ret;
+	}
+
+	cbphy->syscfg = syscon_regmap_lookup_by_phandle(np, "intel,syscfg");
+	if (IS_ERR(cbphy->syscfg)) {
+		dev_err(dev, "No syscon phandle for chip cfg registers\n");
+		return PTR_ERR(cbphy->syscfg);
+	}
+
+	cbphy->hsiocfg = syscon_regmap_lookup_by_phandle(np, "intel,hsio");
+	if (IS_ERR(cbphy->hsiocfg)) {
+		dev_err(dev, "No syscon phandle for hsio\n");
+		return PTR_ERR(cbphy->hsiocfg);
+	}
+
+	ret = device_property_read_u32(dev, "intel,bid", &cbphy->bid);
+	if (ret) {
+		dev_err(dev, "NO intel,bid provided!\n");
+		return ret;
+	}
+
+	device_for_each_child_node(dev, fwn) {
+		if (i >= PHY_MAX_NUM) {
+			fwnode_handle_put(fwn);
+			dev_err(dev, "Error: DT child number larger than %d\n",
+				PHY_MAX_NUM);
+			return -EINVAL;
+		}
+
+		ret = intel_cbphy_iphy_dt_parse(cbphy, fwn, i);
+		if (ret) {
+			fwnode_handle_put(fwn);
+			return ret;
+		}
+
+		i++;
+	}
+
+	return intel_cbphy_dt_sanity_check(cbphy);
+}
+
+static void intel_cbphy_deassert(struct intel_combo_phy *cbphy)
+{
+	reset_control_deassert(cbphy->core_rst);
+	reset_control_deassert(cbphy->phy_rst);
+	usleep_range(10, 20);
+}
+
+static int intel_cbphy_set_mode(struct intel_combo_phy *cbphy)
+{
+	enum intel_combo_mode cb_mode = COMBO_PHY_MODE_MAX;
+	struct device *dev = cbphy->dev;
+	struct intel_cbphy_iphy *iphy0, *iphy1;
+	enum intel_phy_mode mode;
+	enum aggregated_mode aggr = cbphy->aggr_mode;
+
+	iphy0 = &cbphy->iphy[PHY_0];
+	iphy1 = &cbphy->iphy[PHY_1];
+
+	mode = iphy0->enable ? iphy0->phy_mode : iphy1->phy_mode;
+
+	switch (mode) {
+	case PHY_PCIE_MODE:
+		cb_mode = (aggr == PHY_DL_MODE) ?
+			  PCIE_DL_MODE : PCIE0_PCIE1_MODE;
+		break;
+
+	case PHY_XPCS_MODE:
+		cb_mode = (aggr == PHY_DL_MODE) ? RXAUI_MODE : XPCS0_XPCS1_MODE;
+		break;
+
+	case PHY_SATA_MODE:
+		if (aggr == PHY_DL_MODE) {
+			dev_err(dev, "CBPHY%u mode:%u not support dual lane!\n",
+				cbphy->id, mode);
+			return -EINVAL;
+		}
+
+		cb_mode = SATA0_SATA1_MODE;
+		break;
+
+	default:
+		dev_err(dev, "CBPHY%u mode:%u not supported!\n",
+			cbphy->id, mode);
+		return -EINVAL;
+	}
+
+	cbphy->mode = cb_mode;
+	regmap_write(cbphy->hsiocfg, REG_COMBO_MODE(cbphy->bid), cb_mode);
+
+	return 0;
+}
+
+static const struct phy_ops intel_cbphy_ops = {
+	.init =		intel_cbphy_init,
+	.exit =		intel_cbphy_exit,
+	.power_on =	intel_cbphy_power_on,
+	.power_off =	intel_cbphy_power_off,
+};
+
+static int intel_cbphy_iphy_create(struct intel_cbphy_iphy *iphy)
+{
+	struct device *dev = iphy->dev;
+	struct phy_provider *phy_provider;
+
+	/* No phy instance required for slave */
+	if (iphy->phy_role == PHY_SLAVE)
+		return 0;
+
+	iphy->phy = devm_phy_create(dev, iphy->np, &intel_cbphy_ops);
+	if (IS_ERR(iphy->phy)) {
+		dev_err(dev, "PHY[%u:%u]: create PHY instance failed!\n",
+			COMBO_PHY_ID(iphy), PHY_ID(iphy));
+		return PTR_ERR(iphy->phy);
+	}
+
+	phy_set_drvdata(iphy->phy, iphy);
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(phy_provider)) {
+		dev_err(dev, "PHY[%u:%u]: register phy provider failed!\n",
+			COMBO_PHY_ID(iphy), PHY_ID(iphy));
+		return PTR_ERR(phy_provider);
+	}
+
+	return 0;
+}
+
+static int intel_cbphy_create(struct intel_combo_phy *cbphy)
+{
+	struct intel_cbphy_iphy *iphy;
+	int i, ret = 0;
+
+	for (i = 0; i < PHY_MAX_NUM; i++) {
+		iphy = &cbphy->iphy[i];
+		if (iphy->enable) {
+			ret = intel_cbphy_iphy_create(iphy);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int intel_cbphy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct intel_combo_phy *cbphy;
+	int id, ret;
+
+	ret = of_property_read_u32(dev->of_node, "cell-index", &id);
+	if (ret) {
+		dev_err(dev, "cell index not specified:%d\n", ret);
+		return ret;
+	}
+
+	cbphy = devm_kzalloc(dev, sizeof(*cbphy), GFP_KERNEL);
+	if (!cbphy)
+		return -ENOMEM;
+
+	cbphy->phy_cap = PHY_PCIE_CAP;
+	if (!device_property_read_bool(dev, "intel,cap-pcie-only"))
+		cbphy->phy_cap |= PHY_XPCS_CAP | PHY_SATA_CAP;
+
+	cbphy->id = id;
+	cbphy->dev = dev;
+	platform_set_drvdata(pdev, cbphy);
+
+	ret = intel_cbphy_dt_parse(cbphy);
+	if (ret)
+		return ret;
+
+	intel_cbphy_deassert(cbphy);
+	ret = intel_cbphy_set_mode(cbphy);
+	if (ret)
+		return ret;
+
+	ret = intel_cbphy_create(cbphy);
+	if (ret)
+		return ret;
+
+	ret = intel_cbphy_sysfs_init(cbphy);
+
+	return ret;
+}
+
+static const struct of_device_id of_intel_cbphy_match[] = {
+	{ .compatible = "intel,combo-phy" },
+	{}
+};
+
+static struct platform_driver intel_cbphy_driver = {
+	.probe = intel_cbphy_probe,
+	.driver = {
+		.name = "intel-combo-phy",
+		.of_match_table = of_intel_cbphy_match,
+	}
+};
+
+builtin_platform_driver(intel_cbphy_driver);
-- 
2.11.0




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux