Thread (18 messages) 18 messages, 7 authors, 2016-06-20

[v2 PATCH 2/4] phy: Add USB Type-C PHY driver for rk3399

From: Kishon Vijay Abraham I <hidden>
Date: 2016-06-17 12:54:45
Also in: linux-rockchip, lkml


On Monday 13 June 2016 03:09 PM, Chris Zhong wrote:
quoted hunk ↗ jump to hunk
Add a PHY provider driver for the rk3399 SoC Type-c PHY. The USB
Type-C PHY is designed to support the USB3 and DP applications. The
PHY basically has two main components: USB3 and DisplyPort. USB3
operates in SuperSpeed mode and the DP can operate at RBR, HBR and
HBR2 data rates.

Signed-off-by: Chris Zhong <redacted>
Signed-off-by: Kever Yang <redacted>

---

Changes in v2:
- select RESET_CONTROLLER
- alphabetic order
- modify some spelling mistakes
- make mode cleaner
- use bool for enable/disable
- check all of the return value
- return a better err number
- use more readx_poll_timeout()
- clk_disable_unprepare(tcphy->clk_ref);
- remove unuse functions, rockchip_typec_phy_power_on/off
- remove unnecessary typecast from void *
- use dts node to distinguish between phys.

Changes in v1:
- update the licence note
- init core clock to 50MHz
- use extcon API
- remove unused global
- add some comments for magic num
- change usleep_range(1000, 2000) tousleep_range(1000, 1050)
- remove __func__ from dev_err
- return err number when get clk failed
- remove ADDR_ADJ define
- use devm_clk_get(&pdev->dev, "tcpdcore")

 drivers/phy/Kconfig                    |   8 +
 drivers/phy/Makefile                   |   1 +
 drivers/phy/phy-rockchip-typec.c       | 952 +++++++++++++++++++++++++++++++++
 include/linux/phy/phy-rockchip-typec.h |  20 +
 4 files changed, 981 insertions(+)
 create mode 100644 drivers/phy/phy-rockchip-typec.c
 create mode 100644 include/linux/phy/phy-rockchip-typec.h
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 26566db..ec87b3a 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -351,6 +351,14 @@ config PHY_ROCKCHIP_DP
 	help
 	  Enable this to support the Rockchip Display Port PHY.
 
+config PHY_ROCKCHIP_TYPEC
+	tristate "Rockchip TYPEC PHY Driver"
+	depends on ARCH_ROCKCHIP && OF
+	select GENERIC_PHY
+	select RESET_CONTROLLER
+	help
+	  Enable this to support the Rockchip USB TYPEC PHY.
+
 config PHY_ST_SPEAR1310_MIPHY
 	tristate "ST SPEAR1310-MIPHY driver"
 	select GENERIC_PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 24596a9..91fa413 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
 obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
 obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
+obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
diff --git a/drivers/phy/phy-rockchip-typec.c b/drivers/phy/phy-rockchip-typec.c
new file mode 100644
index 0000000..230e074
--- /dev/null
+++ b/drivers/phy/phy-rockchip-typec.c
<snip>
+static const struct phy_ops rockchip_tcphy_ops = {
+	.owner          = THIS_MODULE,
no ops?
+};
+
+static int tcphy_pd_event(struct notifier_block *nb,
+			  unsigned long event, void *priv)
+{
+	struct rockchip_typec_phy *tcphy;
+	struct extcon_dev *edev = priv;
+	int value = edev->state;
+	int mode;
+	u8 is_plugged, dfp;
+
+	tcphy = container_of(nb, struct rockchip_typec_phy, event_nb);
+
+	is_plugged = GET_PLUGGED(value);
+	tcphy->flip = GET_FLIP(value);
+	dfp = GET_DFP(value);
+	tcphy->map = GET_PIN_MAP(value);
+
+	if (is_plugged) {
+		if (!dfp)
+			mode = MODE_UFP_USB;
+		else if (tcphy->map & (PIN_MAP_B | PIN_MAP_D | PIN_MAP_F))
+			mode = MODE_DFP_USB | MODE_DFP_DP;
+		else if (tcphy->map & (PIN_MAP_A | PIN_MAP_C | PIN_MAP_E))
+			mode = MODE_DFP_DP;
+		else
+			mode = MODE_DFP_USB;
+	} else {
+		mode = MODE_DISCONNECT;
+	}
+
+	if (tcphy->mode != mode) {
+		tcphy->mode = mode;
+		schedule_delayed_work_on(0, &tcphy->event_wq, 0);
+	}
+
+	return 0;
+}
+
+static void tcphy_event_wq(struct work_struct *work)
+{
+	struct rockchip_typec_phy *tcphy;
+	int ret;
+
+	tcphy = container_of(work, struct rockchip_typec_phy, event_wq.work);
+
+	if (tcphy->mode == MODE_DISCONNECT) {
+		tcphy_phy_deinit(tcphy);
+		/* remove hpd sign for DP */
+		if (tcphy->hpd_status) {
+			regmap_write(tcphy->grf_regs, GRF_SOC_CON26,
+				     DPTX_HPD_SEL_MASK | DPTX_HPD_DEL);
+			tcphy->hpd_status = 0;
+		}
+	} else {
+		ret = tcphy_phy_init(tcphy);
+		if (ret)
+			return;
+
+		if (tcphy->mode & (MODE_UFP_USB | MODE_DFP_USB))
+			tcphy_usb3_init(tcphy);
+
+		if (tcphy->mode & MODE_DFP_DP) {
+			ret = tcphy_dp_init(tcphy);
+
+			/* set hpd sign for DP, if DP phy is ready */
+			if (!ret) {
+				regmap_write(tcphy->grf_regs, GRF_SOC_CON26,
+					     DPTX_HPD_SEL_MASK | DPTX_HPD_SEL);
+				tcphy->hpd_status = 1;
+			}
+		}
+	}
+}
+
+static int tcphy_get_param(struct device *dev,
+			   struct usb3phy_reg *reg,
+			   const char *name)
+{
+	int ret, buffer[3];
+
+	ret = of_property_read_u32_array(dev->of_node, name, buffer, 3);
+	if (ret) {
+		dev_err(dev, "Can not parse %s\n", name);
+		return ret;
+	}
+
+	reg->offset = buffer[0];
+	reg->enable_bit = buffer[1];
+	reg->write_enable = buffer[2];
+	return 0;
+}
+
+static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
+			  struct device *dev)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	int ret;
+
+	ret = tcphy_get_param(dev, &cfg->typec_conn_dir,
+			      "rockchip,typec_conn_dir");
+	if (ret)
+		return ret;
+
+	ret = tcphy_get_param(dev, &cfg->usb3tousb2_en,
+			      "rockchip,usb3tousb2_en");
+	if (ret)
+		return ret;
+
+	ret = tcphy_get_param(dev, &cfg->external_psm,
+			      "rockchip,external_psm");
+	if (ret)
+		return ret;
+
+	ret = tcphy_get_param(dev, &cfg->pipe_status,
+			      "rockchip,pipe_status");
+	if (ret)
+		return ret;
+
+	ret = tcphy_get_param(dev, &cfg->uphy_dp_sel,
+			      "rockchip,uphy_dp_sel");
+	if (ret)
+		return ret;
+
+	tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
+							  "rockchip,grf");
+	if (IS_ERR(tcphy->grf_regs)) {
+		dev_err(dev, "could not find grf dt node\n");
+		return PTR_ERR(tcphy->grf_regs);
+	}
+
+	tcphy->clk_core = devm_clk_get(dev, "tcpdcore");
+	if (IS_ERR(tcphy->clk_core)) {
+		dev_err(dev, "could not get uphy core clock\n");
+		return PTR_ERR(tcphy->clk_core);
+	}
+
+	tcphy->clk_ref = devm_clk_get(dev, "tcpdphy_ref");
+	if (IS_ERR(tcphy->clk_ref)) {
+		dev_err(dev, "could not get uphy ref clock\n");
+		return PTR_ERR(tcphy->clk_ref);
+	}
+
+	tcphy->phy_rst = devm_reset_control_get(dev, "tcphy");
+	if (IS_ERR(tcphy->phy_rst)) {
+		dev_err(dev, "no phy_rst reset control found\n");
+		return PTR_ERR(tcphy->phy_rst);
+	}
+
+	tcphy->pipe_rst = devm_reset_control_get(dev, "tcphy_pipe");
+	if (IS_ERR(tcphy->pipe_rst)) {
+		dev_err(dev, "no pipe_rst reset control found\n");
+		return PTR_ERR(tcphy->pipe_rst);
+	}
+
+	tcphy->uphy_rst = devm_reset_control_get(dev, "uphy_tcphy");
+	if (IS_ERR(tcphy->uphy_rst)) {
+		dev_err(dev, "no uphy_rst reset control found\n");
+		return PTR_ERR(tcphy->uphy_rst);
+	}
+
+	return 0;
+}
+
+static int rockchip_typec_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_typec_phy *tcphy;
+	struct resource *res;
+	struct phy_provider *phy_provider;
+	int ret;
+
+	tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
+	if (!tcphy)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	tcphy->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(tcphy->base)) {
+		dev_err(dev, "failed to remap phy regs\n");
+		return PTR_ERR(tcphy->base);
+	}
+
+	tcphy_parse_dt(tcphy, dev);
+
+	tcphy->dev = dev;
+	platform_set_drvdata(pdev, tcphy);
+
+	tcphy->mode = MODE_DISCONNECT;
+
+	tcphy->phy = devm_phy_create(dev, NULL, &rockchip_tcphy_ops);

your phy_ops doesn't have any ops populated. Is there a reason to use the phy
framework?

Thanks
Kishon
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help