Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1455457 > unrolled thread

[RFC PATCH 0/4] Type-C Port Manager

Started byGuenter Roeck <groeck@chromium.org>
First post2016-08-02 22:40 +0200
Last post2016-08-03 20:50 +0200
Articles 6 — 3 participants

Back to article view | Back to linux.kernel


Contents

  [RFC PATCH 0/4] Type-C Port Manager Guenter Roeck <groeck@chromium.org> - 2016-08-02 22:40 +0200
    [RFC PATCH 4/4] usb: typec: Type-C Port Controller Interface driver (tcpci) Guenter Roeck <groeck@chromium.org> - 2016-08-02 22:40 +0200
    Re: [RFC PATCH 3/4] usb: typec: USB Type-C Port Manager (tcpm) Oliver Neukum <oneukum@suse.com> - 2016-08-03 11:30 +0200
      Re: [RFC PATCH 3/4] usb: typec: USB Type-C Port Manager (tcpm) Guenter Roeck <groeck@google.com> - 2016-08-03 20:10 +0200
    Re: [RFC PATCH 3/4] usb: typec: USB Type-C Port Manager (tcpm) Oliver Neukum <oneukum@suse.com> - 2016-08-03 11:40 +0200
      Re: [RFC PATCH 3/4] usb: typec: USB Type-C Port Manager (tcpm) Guenter Roeck <groeck@google.com> - 2016-08-03 20:50 +0200

#1455457 — [RFC PATCH 0/4] Type-C Port Manager

FromGuenter Roeck <groeck@chromium.org>
Date2016-08-02 22:40 +0200
Subject[RFC PATCH 0/4] Type-C Port Manager
Message-ID<s1ODL-16D-3@gated-at.bofh.it>
The following series of patches implements a USB Type-C Port Manager
using the pending USB Type-C class code as basis. The code is still WIP
(I am still waiting for a final decision on locking in the class code),
but I think it is important to get feedback from the community at this point.

There are four patches in the series. The first two patches remove locking from
the Type-C class code. The third patch implements the Type-C Port Manager state
machine. The forth patch is an interface between the Type-C Port Manager and a
TCPCI (Type-C Port Controller Interface) compliant USB Type-C Port Controller.

Patch 4/4 (the interface to a TCPCI compliant chip) is currently untested
since I don't have the necessary hardware available. The port manager code
was tested connecting to an Embedded Controller on a Chromebook, bypassing
the Port Manager implementation in the EC.

Both Source and Sink operation was tested with various Type-C chargers, docks,
and connectors. Alternate mode support is partially implemented (Alternate mode
support is requested from the partner), but alternate modes are actually
selected. Implementing this will require more thought, since the actual
alternate mode support has to be implemented elsewhere, such as in a dedicated
Phy driver. It should be possible to implement the interface between phy driver
and Type-C Port Controller driver using extcon, but I have not further explored
the possibilities, and other options might be possible and/or better.

Why remove locking from the Type-C class code ?

The primary problem is that Type-C state and role changes are triggered by
the Port Manager state machine and quite independent from role change
requests triggered by or requested through the class code. At the same time,
the code handling role change requests triggered from the class code has to
wait for the role change to complete before returning to the class code.
This can result in a deadlock, since the state machine also needs to report
unsolicited role (or status) changes to the class code.

Consider this situation:

- User requests a role change throuch class code.
- Class code acquires mutex, and passes role change request to port manager.
- Port manager attempts to acquire its port mutex. Port state machine is
  active, causing the code to stall.
- Port state machine executes a state change or role change request from the
  port partner. Upon completion, it attempts to inform the class that a role
  or state change occurred. It does so while the port mutex is active to
  prevent state changes while the state machine is still running.
- The callback into the class code waits for the class mutex, which is locked
  because there is also a role change request from the class code pending.

Instead of trying to handle the situation in low level drivers by trying to
avoid deadlocks, it seems better to leave all locking to low level drivers to
start with. Since low level drivers are in control of port changes and port
roles, this appears to be the simpler approach. I am open to listen to other
ideas, though.

[toc] | [next] | [standalone]


#1455458 — [RFC PATCH 4/4] usb: typec: Type-C Port Controller Interface driver (tcpci)

FromGuenter Roeck <groeck@chromium.org>
Date2016-08-02 22:40 +0200
Subject[RFC PATCH 4/4] usb: typec: Type-C Port Controller Interface driver (tcpci)
Message-ID<s1ODM-16D-31@gated-at.bofh.it>
In reply to#1455457
The port controller interface driver interconnects the Type-C Port
Manager with a Type-C Port Controller Interface (TCPCI) compliant
port controller.

Signed-off-by: Guenter Roeck <groeck@chromium.org>
---
 drivers/usb/typec/Kconfig  |   9 +
 drivers/usb/typec/Makefile |   1 +
 drivers/usb/typec/tcpci.c  | 498 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/typec/tcpci.h  | 133 ++++++++++++
 4 files changed, 641 insertions(+)
 create mode 100644 drivers/usb/typec/tcpci.c
 create mode 100644 drivers/usb/typec/tcpci.h

diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index 113bb1b..a92c9d1 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -25,4 +25,13 @@ config TYPEC_TCPM
 	  The Type-C Port Controller Manager provides a USB PD and USB Type-C
 	  state machine for use with Type-C Port Controllers.
 
+if TYPEC_TCPM
+
+config TYPEC_TCPCI
+	tristate "Type-C Port Controller Interface driver"
+	help
+	  Type-C Port Controller driver for TCPCI-compliant controller.
+
+endif
+
 endmenu
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index bbe4572..7dbaf8c 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_TYPEC)		+= typec.o
 obj-$(CONFIG_TYPEC_WCOVE)	+= typec_wcove.o
 obj-$(CONFIG_TYPEC_TCPM)	+= tcpm.o
+obj-$(CONFIG_TYPEC_TCPCI)	+= tcpci.o
diff --git a/drivers/usb/typec/tcpci.c b/drivers/usb/typec/tcpci.c
new file mode 100644
index 0000000..2a98bf4
--- /dev/null
+++ b/drivers/usb/typec/tcpci.c
@@ -0,0 +1,498 @@
+/*
+ * Copyright 2015-2016 Google, Inc
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * USB Type-C Port Controller Interface.
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/typec.h>
+
+#include "tcpci.h"
+#include "tcpm.h"
+
+#define PD_RETRY_COUNT 3
+
+struct tcpci {
+	struct device *dev;
+	struct i2c_client *client;
+
+	struct tcpm_port *port;
+
+	struct regmap *regmap;
+
+	bool controls_vbus;
+
+	struct tcpc_dev tcpc;
+};
+
+static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc)
+{
+	return container_of(tcpc, struct tcpci, tcpc);
+}
+
+static int tcpci_read16(struct tcpci *tcpci, unsigned int reg,
+			unsigned int *val)
+{
+	return regmap_raw_read(tcpci->regmap, reg, val, sizeof(u16));
+}
+
+static int tcpci_write16(struct tcpci *tcpci, unsigned int reg, u16 val)
+{
+	return regmap_raw_write(tcpci->regmap, reg, &val, sizeof(u16));
+}
+
+static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+	unsigned int reg;
+	int ret;
+
+	switch (cc) {
+	case TYPEC_CC_RA:
+		reg = (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC1_SHIFT) |
+			(TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC2_SHIFT);
+		break;
+	case TYPEC_CC_RD:
+		reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
+			(TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
+		break;
+	case TYPEC_CC_RP_DEF:
+		reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
+			(TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
+			(TCPC_ROLE_CTRL_RP_VAL_DEF <<
+			 TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+		break;
+	case TYPEC_CC_RP_1_5:
+		reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
+			(TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
+			(TCPC_ROLE_CTRL_RP_VAL_1_5 <<
+			 TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+		break;
+	case TYPEC_CC_RP_3_0:
+		reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
+			(TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
+			(TCPC_ROLE_CTRL_RP_VAL_1_5 <<
+			 TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+		break;
+	case TYPEC_CC_OPEN:
+	default:
+		reg = (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT) |
+			(TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT);
+		break;
+	}
+
+	ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static enum typec_cc_status tcpci_to_typec_cc(unsigned int cc, bool sink)
+{
+	switch (cc) {
+	case 0x1:
+		return sink ? TYPEC_CC_RP_DEF : TYPEC_CC_RA;
+	case 0x2:
+		return sink ? TYPEC_CC_RP_1_5 : TYPEC_CC_RD;
+	case 0x3:
+		if (sink)
+			return TYPEC_CC_RP_3_0;
+	case 0x0:
+	default:
+		return TYPEC_CC_OPEN;
+	}
+}
+
+static int tcpci_get_cc(struct tcpc_dev *tcpc,
+			enum typec_cc_status *cc1, enum typec_cc_status *cc2)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+	unsigned int reg;
+	int ret;
+
+	ret = regmap_read(tcpci->regmap, TCPC_CC_STATUS, &reg);
+	if (ret < 0)
+		return ret;
+
+	*cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) &
+				 TCPC_CC_STATUS_CC1_MASK,
+				 reg & TCPC_CC_STATUS_TERM);
+	*cc2 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC2_SHIFT) &
+				 TCPC_CC_STATUS_CC2_MASK,
+				 reg & TCPC_CC_STATUS_TERM);
+
+	return 0;
+}
+
+static int tcpci_set_polarity(struct tcpc_dev *tcpc,
+			      enum typec_cc_polarity polarity)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+	int ret;
+
+	ret = regmap_write(tcpci->regmap, TCPC_TCPC_CTRL,
+			   (polarity == TYPEC_POLARITY_CC2) ?
+			   TCPC_TCPC_CTRL_ORIENTATION : 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+	int ret;
+
+	ret = regmap_write(tcpci->regmap, TCPC_POWER_CTRL,
+			   enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int tcpci_set_pd_header(struct tcpc_dev *tcpc, enum typec_role role,
+			       enum typec_data_role data)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+	unsigned int reg;
+	int ret;
+
+	reg = PD_REV20 << TCPC_MSG_HDR_INFO_REV_SHIFT;
+	if (role == TYPEC_SOURCE)
+		reg |= TCPC_MSG_HDR_INFO_PWR_ROLE;
+	if (data == TYPEC_HOST)
+		reg |= TCPC_MSG_HDR_INFO_DATA_ROLE;
+	ret = regmap_write(tcpci->regmap, TCPC_MSG_HDR_INFO, reg);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int tcpci_set_pd_rx(struct tcpc_dev *tcpc, bool enable)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+	unsigned int reg = 0;
+	int ret;
+
+	if (enable)
+		reg = TCPC_RX_DETECT_SOP | TCPC_RX_DETECT_HARD_RESET;
+	ret = regmap_write(tcpci->regmap, TCPC_RX_DETECT, reg);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int tcpci_get_vbus(struct tcpc_dev *tcpc)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+	unsigned int reg;
+	int ret;
+
+	ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, &reg);
+	if (ret < 0)
+		return ret;
+
+	return !!(reg & TCPC_POWER_STATUS_VBUS_PRES);
+}
+
+static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+	int ret;
+
+	if (source) {
+		ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
+				   TCPC_CMD_SRC_VBUS_DEFAULT);
+		if (ret < 0)
+			return ret;
+	} else {
+		ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
+				   TCPC_CMD_DISABLE_SRC_VBUS);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (sink) {
+		ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
+				   TCPC_CMD_SINK_VBUS);
+		if (ret < 0)
+			return ret;
+	} else {
+		ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
+				   TCPC_CMD_DISABLE_SINK_VBUS);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int tcpci_pd_transmit(struct tcpc_dev *tcpc,
+			     enum tcpm_transmit_type type,
+			     const struct pd_message *msg)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+	unsigned int reg, cnt, header;
+	int ret;
+
+	cnt = msg ? pd_header_cnt(msg->header) * 4 : 0;
+	ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt);
+	if (ret < 0)
+		return ret;
+
+	header = msg ? msg->header : 0;
+	ret = tcpci_write16(tcpci, TCPC_TX_HDR, header);
+	if (ret < 0)
+		return ret;
+
+	if (cnt > 0) {
+		ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA,
+				       &msg->payload, cnt);
+		if (ret < 0)
+			return ret;
+	}
+
+	reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) |
+		(type << TCPC_TRANSMIT_TYPE_SHIFT);
+	ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int tcpci_init(struct tcpc_dev *tcpc)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+	unsigned long timeout = jiffies + msecs_to_jiffies(2000); /* XXX */
+	unsigned int reg;
+	int ret;
+
+	while (time_before_eq(jiffies, timeout)) {
+		ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, &reg);
+		if (ret < 0)
+			return ret;
+		if (!(reg & TCPC_POWER_STATUS_UNINIT))
+			break;
+		usleep_range(10000, 20000);
+	}
+	if (time_after(jiffies, timeout))
+		return -ETIMEDOUT;
+
+	/* Clear all events */
+	ret = tcpci_write16(tcpci, TCPC_ALERT, 0xffff);
+	if (ret < 0)
+		return ret;
+
+	if (tcpci->controls_vbus)
+		reg = TCPC_POWER_STATUS_VBUS_PRES;
+	else
+		reg = 0;
+	ret = regmap_write(tcpci->regmap, TCPC_POWER_STATUS_MASK, reg);
+	if (ret < 0)
+		return ret;
+
+	reg = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_FAILED |
+		TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_RX_STATUS |
+		TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS;
+	if (tcpci->controls_vbus)
+		reg |= TCPC_ALERT_POWER_STATUS;
+	return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg);
+}
+
+static irqreturn_t tcpci_irq(int irq, void *dev_id)
+{
+	struct tcpci *tcpci = dev_id;
+	unsigned int status, reg;
+	int ret;
+
+	tcpci_read16(tcpci, TCPC_ALERT, &status);
+
+	/*
+	 * Clear alert status for everything except RX_STATUS, which shouldn't
+	 * be cleared until we have successfully retrieved message.
+	 */
+	if (status & ~TCPC_ALERT_RX_STATUS)
+		tcpci_write16(tcpci, TCPC_ALERT,
+			      status & ~TCPC_ALERT_RX_STATUS);
+
+	if (status & TCPC_ALERT_CC_STATUS) {
+		enum typec_cc_status cc1, cc2;
+
+		ret = tcpci_get_cc(&tcpci->tcpc, &cc1, &cc2);
+		if (ret == 0)
+			tcpm_cc_change(tcpci->port, cc1, cc2);
+	}
+
+	if (status & TCPC_ALERT_POWER_STATUS) {
+		regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, &reg);
+
+		if (reg == 0xff) {
+			/*
+			 * If power status mask has been reset, then the TCPC
+			 * has reset.
+			 */
+			tcpm_tcpc_reset(tcpci->port);
+		} else {
+			regmap_read(tcpci->regmap, TCPC_POWER_STATUS, &reg);
+			if (reg & TCPC_POWER_STATUS_VBUS_PRES)
+				tcpm_vbus_on(tcpci->port);
+			else
+				tcpm_vbus_off(tcpci->port);
+		}
+	}
+
+	if (status & TCPC_ALERT_RX_STATUS) {
+		struct pd_message msg;
+		unsigned int cnt;
+
+		regmap_read(tcpci->regmap, TCPC_RX_BYTE_CNT, &cnt);
+
+		tcpci_read16(tcpci, TCPC_RX_HDR, &reg);
+		msg.header = reg;
+
+		if (WARN_ON(cnt > sizeof(msg.payload)))
+			cnt = sizeof(msg.payload);
+
+		if (cnt > 0)
+			regmap_raw_read(tcpci->regmap, TCPC_RX_DATA,
+					&msg.payload, cnt);
+
+		/* Read complete, clear RX status alert bit */
+		tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS);
+
+		tcpm_pd_receive(tcpci->port, &msg);
+	}
+
+	if (status & TCPC_ALERT_RX_HARD_RST)
+		tcpm_pd_hard_reset(tcpci->port);
+
+	if (status & TCPC_ALERT_TX_SUCCESS)
+		tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_SUCCESS);
+	else if (status & TCPC_ALERT_TX_DISCARDED)
+		tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_DISCARDED);
+	else if (status & TCPC_ALERT_TX_FAILED)
+		tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_FAILED);
+
+	return IRQ_HANDLED;
+}
+
+static const struct regmap_config tcpci_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = 0x7F, /* 0x80 .. 0xFF are vendor defined */
+};
+
+static int tcpci_parse_config(struct tcpci *tcpci)
+{
+	tcpci->controls_vbus = true; /* XXX */
+
+	/* TODO: Populate struct tcpc_config from ACPI/device-tree */
+
+	return 0;
+}
+
+static int tcpci_probe(struct i2c_client *client,
+		       const struct i2c_device_id *i2c_id)
+{
+	struct tcpci *tcpci;
+	int err;
+
+	tcpci = devm_kzalloc(&client->dev, sizeof(*tcpci), GFP_KERNEL);
+	if (!tcpci)
+		return -ENOMEM;
+
+	tcpci->client = client;
+	tcpci->dev = &client->dev;
+	i2c_set_clientdata(client, tcpci);
+	tcpci->regmap = devm_regmap_init_i2c(client, &tcpci_regmap_config);
+	if (IS_ERR(tcpci->regmap))
+		return PTR_ERR(tcpci->regmap);
+
+	tcpci->tcpc.init = tcpci_init;
+	tcpci->tcpc.get_vbus = tcpci_get_vbus;
+	tcpci->tcpc.set_vbus = tcpci_set_vbus;
+	tcpci->tcpc.set_cc = tcpci_set_cc;
+	tcpci->tcpc.get_cc = tcpci_get_cc;
+	tcpci->tcpc.set_polarity = tcpci_set_polarity;
+	tcpci->tcpc.set_vconn = tcpci_set_vconn;
+
+	tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx;
+	tcpci->tcpc.set_pd_header = tcpci_set_pd_header;
+	tcpci->tcpc.pd_transmit = tcpci_pd_transmit;
+
+	err = tcpci_parse_config(tcpci);
+	if (err < 0)
+		return err;
+
+	err = devm_request_threaded_irq(tcpci->dev, client->irq, NULL,
+					tcpci_irq, IRQF_TRIGGER_FALLING,
+					dev_name(tcpci->dev), tcpci);
+	if (err < 0)
+		return err;
+
+	tcpci->port = tcpm_register_port(tcpci->dev, &tcpci->tcpc);
+	if (IS_ERR(tcpci->port))
+		return PTR_ERR(tcpci->port);
+
+	return 0;
+}
+
+static int tcpci_remove(struct i2c_client *client)
+{
+	struct tcpci *tcpci = i2c_get_clientdata(client);
+
+	tcpm_unregister_port(tcpci->port);
+
+	return 0;
+}
+
+static const struct i2c_device_id tcpci_id[] = {
+	{ "tcpci", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tcpci_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id tcpci_of_match[] = {
+	{ .compatible = "usb,tcpci", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, tcpci_of_match);
+#endif
+
+static struct i2c_driver tcpci_i2c_driver = {
+	.driver = {
+		.name = "tcpci",
+		.of_match_table = of_match_ptr(tcpci_of_match),
+	},
+	.probe = tcpci_probe,
+	.remove = tcpci_remove,
+	.id_table = tcpci_id,
+};
+module_i2c_driver(tcpci_i2c_driver);
+
+MODULE_DESCRIPTION("USB Type-C Port Controller Interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/typec/tcpci.h b/drivers/usb/typec/tcpci.h
new file mode 100644
index 0000000..0c996a9
--- /dev/null
+++ b/drivers/usb/typec/tcpci.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2015-2016 Google, Inc
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * USB Type-C Port Controller Interface.
+ */
+
+#ifndef __LINUX_USB_TCPCI_H
+#define __LINUX_USB_TCPCI_H
+
+#define TCPC_VENDOR_ID			0x0
+#define TCPC_PRODUCT_ID			0x2
+#define TCPC_BCD_DEV			0x4
+#define TCPC_TC_REV			0x6
+#define TCPC_PD_REV			0x8
+#define TCPC_PD_INT_REV			0xa
+
+#define TCPC_ALERT			0x10
+#define TCPC_ALERT_VBUS_DISCNCT		BIT(11)
+#define TCPC_ALERT_RX_BUF_OVF		BIT(10)
+#define TCPC_ALERT_FAULT		BIT(9)
+#define TCPC_ALERT_V_ALARM_LO		BIT(8)
+#define TCPC_ALERT_V_ALARM_HI		BIT(7)
+#define TCPC_ALERT_TX_SUCCESS		BIT(6)
+#define TCPC_ALERT_TX_DISCARDED		BIT(5)
+#define TCPC_ALERT_TX_FAILED		BIT(4)
+#define TCPC_ALERT_RX_HARD_RST		BIT(3)
+#define TCPC_ALERT_RX_STATUS		BIT(2)
+#define TCPC_ALERT_POWER_STATUS		BIT(1)
+#define TCPC_ALERT_CC_STATUS		BIT(0)
+
+#define TCPC_ALERT_MASK			0x12
+#define TCPC_POWER_STATUS_MASK		0x14
+#define TCPC_FAULT_STATUS_MASK		0x15
+#define TCPC_CONFIG_STD_OUTPUT		0x18
+
+#define TCPC_TCPC_CTRL			0x19
+#define TCPC_TCPC_CTRL_ORIENTATION	BIT(0)
+
+#define TCPC_ROLE_CTRL			0x1a
+#define TCPC_ROLE_CTRL_DRP		BIT(6)
+#define TCPC_ROLE_CTRL_RP_VAL_SHIFT	4
+#define TCPC_ROLE_CTRL_RP_VAL_MASK	0x3
+#define TCPC_ROLE_CTRL_RP_VAL_DEF	0x0
+#define TCPC_ROLE_CTRL_RP_VAL_1_5	0x1
+#define TCPC_ROLE_CTRL_RP_VAL_3_0	0x2
+#define TCPC_ROLE_CTRL_CC2_SHIFT	2
+#define TCPC_ROLE_CTRL_CC2_MASK		0x3
+#define TCPC_ROLE_CTRL_CC1_SHIFT	0
+#define TCPC_ROLE_CTRL_CC1_MASK		0x3
+#define TCPC_ROLE_CTRL_CC_RA		0x0
+#define TCPC_ROLE_CTRL_CC_RP		0x1
+#define TCPC_ROLE_CTRL_CC_RD		0x2
+#define TCPC_ROLE_CTRL_CC_OPEN		0x3
+
+#define TCPC_FAULT_CTRL			0x1b
+
+#define TCPC_POWER_CTRL			0x1c
+#define TCPC_POWER_CTRL_VCONN_ENABLE	BIT(0)
+
+#define TCPC_CC_STATUS			0x1d
+#define TCPC_CC_STATUS_TERM		BIT(4)
+#define TCPC_CC_STATUS_CC2_SHIFT	2
+#define TCPC_CC_STATUS_CC2_MASK		0x3
+#define TCPC_CC_STATUS_CC1_SHIFT	0
+#define TCPC_CC_STATUS_CC1_MASK		0x3
+
+#define TCPC_POWER_STATUS		0x1e
+#define TCPC_POWER_STATUS_UNINIT	BIT(6)
+#define TCPC_POWER_STATUS_VBUS_DET	BIT(3)
+#define TCPC_POWER_STATUS_VBUS_PRES	BIT(2)
+
+#define TCPC_FAULT_STATUS		0x1f
+
+#define TCPC_COMMAND			0x23
+#define TCPC_CMD_WAKE_I2C		0x11
+#define TCPC_CMD_DISABLE_VBUS_DETECT	0x22
+#define TCPC_CMD_ENABLE_VBUS_DETECT	0x33
+#define TCPC_CMD_DISABLE_SINK_VBUS	0x44
+#define TCPC_CMD_SINK_VBUS		0x55
+#define TCPC_CMD_DISABLE_SRC_VBUS	0x66
+#define TCPC_CMD_SRC_VBUS_DEFAULT	0x77
+#define TCPC_CMD_SRC_VBUS_HIGH		0x88
+#define TCPC_CMD_LOOK4CONNECTION	0x99
+#define TCPC_CMD_RXONEMORE		0xAA
+#define TCPC_CMD_I2C_IDLE		0xFF
+
+#define TCPC_DEV_CAP_1			0x24
+#define TCPC_DEV_CAP_2			0x26
+#define TCPC_STD_INPUT_CAP		0x28
+#define TCPC_STD_OUTPUT_CAP		0x29
+
+#define TCPC_MSG_HDR_INFO		0x2e
+#define TCPC_MSG_HDR_INFO_DATA_ROLE	BIT(3)
+#define TCPC_MSG_HDR_INFO_PWR_ROLE	BIT(0)
+#define TCPC_MSG_HDR_INFO_REV_SHIFT	1
+#define TCPC_MSG_HDR_INFO_REV_MASK	0x3
+
+#define TCPC_RX_DETECT			0x2f
+#define TCPC_RX_DETECT_HARD_RESET	BIT(5)
+#define TCPC_RX_DETECT_SOP		BIT(0)
+
+#define TCPC_RX_BYTE_CNT		0x30
+#define TCPC_RX_BUF_FRAME_TYPE		0x31
+#define TCPC_RX_HDR			0x32
+#define TCPC_RX_DATA			0x34 /* through 0x4f */
+
+#define TCPC_TRANSMIT			0x50
+#define TCPC_TRANSMIT_RETRY_SHIFT	4
+#define TCPC_TRANSMIT_RETRY_MASK	0x3
+#define TCPC_TRANSMIT_TYPE_SHIFT	0
+#define TCPC_TRANSMIT_TYPE_MASK		0x7
+
+#define TCPC_TX_BYTE_CNT		0x51
+#define TCPC_TX_HDR			0x52
+#define TCPC_TX_DATA			0x54 /* through 0x6f */
+
+#define TCPC_VBUS_VOLTAGE			0x70
+#define TCPC_VBUS_SINK_DISCONNECT_THRESH	0x72
+#define TCPC_VBUS_STOP_DISCHARGE_THRESH		0x74
+#define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG		0x76
+#define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG		0x78
+
+#endif /* __LINUX_USB_TCPCI_H */
-- 
2.6.6

[toc] | [prev] | [next] | [standalone]


#1455710 — Re: [RFC PATCH 3/4] usb: typec: USB Type-C Port Manager (tcpm)

FromOliver Neukum <oneukum@suse.com>
Date2016-08-03 11:30 +0200
SubjectRe: [RFC PATCH 3/4] usb: typec: USB Type-C Port Manager (tcpm)
Message-ID<s20EW-Iw-13@gated-at.bofh.it>
In reply to#1455457
On Tue, 2016-08-02 at 13:32 -0700, Guenter Roeck wrote:
> +static int tcpm_set_polarity(struct tcpm_port *port,
> +                            enum typec_cc_polarity polarity)
> +{
> +       tcpm_log(port, "polarity %d", polarity);
> +
> +       port->polarity = polarity;
> +
> +       return port->tcpc->set_polarity(port->tcpc, port->polarity);
> +}

Here you don't care about the result.

> +
> +static int tcpm_set_vconn(struct tcpm_port *port, bool enable)
> +{
> +       int ret = 0;
> +
> +       tcpm_log(port, "vconn:=%d", enable);
> +
> +       ret = port->tcpc->set_vconn(port->tcpc, enable);
> +       if (!ret) {
> +               port->con.vconn_role = enable ? TYPEC_SOURCE
> +                                             : TYPEC_SINK;
> +               typec_set_vconn_role(port->typec_port,
> port->con.vconn_role);
> +       }
> +
> +       return ret;
> +}

But here you do. Which is right?

	Regards
		Oliver

[toc] | [prev] | [next] | [standalone]


#1455933 — Re: [RFC PATCH 3/4] usb: typec: USB Type-C Port Manager (tcpm)

FromGuenter Roeck <groeck@google.com>
Date2016-08-03 20:10 +0200
SubjectRe: [RFC PATCH 3/4] usb: typec: USB Type-C Port Manager (tcpm)
Message-ID<s28Ma-62m-21@gated-at.bofh.it>
In reply to#1455710
On Wed, Aug 3, 2016 at 2:18 AM, Oliver Neukum <oneukum@suse.com> wrote:
> On Tue, 2016-08-02 at 13:32 -0700, Guenter Roeck wrote:
>> +static int tcpm_set_polarity(struct tcpm_port *port,
>> +                            enum typec_cc_polarity polarity)
>> +{
>> +       tcpm_log(port, "polarity %d", polarity);
>> +
>> +       port->polarity = polarity;
>> +
>> +       return port->tcpc->set_polarity(port->tcpc, port->polarity);
>> +}
>
> Here you don't care about the result.

Changed to only set port->polarity if the set_polarity callback was successful.

Thanks,
Guenter

[toc] | [prev] | [next] | [standalone]


#1455712 — Re: [RFC PATCH 3/4] usb: typec: USB Type-C Port Manager (tcpm)

FromOliver Neukum <oneukum@suse.com>
Date2016-08-03 11:40 +0200
SubjectRe: [RFC PATCH 3/4] usb: typec: USB Type-C Port Manager (tcpm)
Message-ID<s20OB-LF-1@gated-at.bofh.it>
In reply to#1455457
On Tue, 2016-08-02 at 13:32 -0700, Guenter Roeck wrote:
> +static bool svdm_consume_svids(struct tcpm_port *port, const u32
> *payload,
> +                              int cnt)
> +{
> +       struct pd_mode_data *pmdata = &port->mode_data;
> +       int i;
> +
> +       for (i = 1; i < cnt; i++) {
> +               u16 svid;
> +
> +               svid = (payload[i] >> 16) & 0xffff;
> +               if (!svid)
> +                       return false;

Hi,

this looks like an endianness bug.

	Regards
		Oliver

[toc] | [prev] | [next] | [standalone]


#1455956 — Re: [RFC PATCH 3/4] usb: typec: USB Type-C Port Manager (tcpm)

FromGuenter Roeck <groeck@google.com>
Date2016-08-03 20:50 +0200
SubjectRe: [RFC PATCH 3/4] usb: typec: USB Type-C Port Manager (tcpm)
Message-ID<s29oS-6hy-21@gated-at.bofh.it>
In reply to#1455712
On Wed, Aug 3, 2016 at 2:28 AM, Oliver Neukum <oneukum@suse.com> wrote:
> On Tue, 2016-08-02 at 13:32 -0700, Guenter Roeck wrote:
>> +static bool svdm_consume_svids(struct tcpm_port *port, const u32
>> *payload,
>> +                              int cnt)
>> +{
>> +       struct pd_mode_data *pmdata = &port->mode_data;
>> +       int i;
>> +
>> +       for (i = 1; i < cnt; i++) {
>> +               u16 svid;
>> +
>> +               svid = (payload[i] >> 16) & 0xffff;
>> +               if (!svid)
>> +                       return false;
>
> Hi,
>
> this looks like an endianness bug.
>

Yes, you are right, and there are lots of those in my code. Thanks for
bringing it up.

Guenter

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web