* [PATCH 2/2] usb: typec: add PD sink port support for Intel Whiskey Cove PMIC Typc-C PHY driver
@ 2016-07-15 2:16 Bin Gao
0 siblings, 0 replies; only message in thread
From: Bin Gao @ 2016-07-15 2:16 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman, linux-usb, linux-kernel
Cc: Bin Gao, Chandra Sekhar Anagani
From: Chandra Sekhar Anagani <chandra.sekhar.anagani@intel.com>
This adds PD sink port support for the USB Type-C PHY on Intel WhiskeyCove
PMIC which is available on some of the Intel Broxton SoC based platforms.
This patch depends on these two patches:
https://lkml.org/lkml/2016/6/29/349
https://lkml.org/lkml/2016/6/29/350
Signed-off-by: Chandra Sekhar Anagani <chandra.sekhar.anagani@intel.com>
---
drivers/usb/typec/typec_wcove.c | 289 ++++++++++++++++++++++++++++++++++++----
1 file changed, 263 insertions(+), 26 deletions(-)
diff --git a/drivers/usb/typec/typec_wcove.c b/drivers/usb/typec/typec_wcove.c
index c7c2d28..a4250ba 100644
--- a/drivers/usb/typec/typec_wcove.c
+++ b/drivers/usb/typec/typec_wcove.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2016 Intel Corporation
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ * Author: Chandra Sekhar Anagani <chandra.sekhar.anagani@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -10,9 +11,11 @@
*/
#include <linux/acpi.h>
+#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/usb/typec.h>
+#include <linux/usb/pd_sink.h>
#include <linux/platform_device.h>
#include <linux/mfd/intel_soc_pmic.h>
@@ -25,6 +28,7 @@
#define USBC_CONTROL3 0x7003
#define USBC_CC1_CTRL 0x7004
#define USBC_CC2_CTRL 0x7005
+#define USBC_CC_SEL 0x7006
#define USBC_STATUS1 0x7007
#define USBC_STATUS2 0x7008
#define USBC_STATUS3 0x7009
@@ -32,7 +36,16 @@
#define USBC_IRQ2 0x7016
#define USBC_IRQMASK1 0x7017
#define USBC_IRQMASK2 0x7018
-
+#define USBC_PD_CFG1 0x7019
+#define USBC_PD_CFG2 0x701a
+#define USBC_PD_CFG3 0x701b
+#define USBC_PD_STATUS 0x701c
+#define USBC_RX_STATUS 0x701d
+#define USBC_RX_INFO 0x701e
+#define USBC_TX_CMD 0x701f
+#define USBC_TX_INFO 0x7020
+#define USBC_RX_DATA_START 0x7028
+#define USBC_TX_DATA_START 0x7047
/* Register bits */
#define USBC_CONTROL1_MODE_DRP(r) ((r & ~0x7) | 4)
@@ -44,7 +57,9 @@
#define USBC_CONTROL3_PD_DIS BIT(1)
#define USBC_CC_CTRL_VCONN_EN BIT(1)
+#define USBC_CC_CTRL_TX_EN BIT(2)
+#define USBC_CC_SEL_CCSEL (BIT(0) | BIT(1))
#define USBC_STATUS1_DET_ONGOING BIT(6)
#define USBC_STATUS1_RSLT(r) (r & 0xf)
#define USBC_RSLT_NOTHING 0
@@ -79,11 +94,44 @@
USBC_IRQ2_RX_HR | USBC_IRQ2_RX_CR | \
USBC_IRQ2_TX_SUCCESS | USBC_IRQ2_TX_FAIL)
+#define USBC_PD_CFG1_ID_FILL BIT(7)
+
+#define USBC_PD_CFG2_SOP_RX BIT(0)
+
+#define USBC_PD_CFG3_SR_SOP2 (BIT(7) | BIT(6))
+#define USBC_PD_CFG3_SR_SOP1 (BIT(5) | BIT(4))
+#define USBC_PD_CFG3_SR_SOP0 (BIT(3) | BIT(2))
+#define USBC_PD_CFG3_DATAROLE BIT(1)
+#define USBC_PD_CFG3_PWRROLE BIT(0)
+
+#define USBC_TX_CMD_TXBUF_RDY BIT(0)
+#define USBC_TX_CMD_TX_START BIT(1)
+#define USBC_TX_CMD_TXBUF_CMD(r) ((r >> 5) & 0x7)
+
+#define USBC_TX_INFO_TX_SOP (BIT(0) | BIT(1) | BIT(2))
+#define USBC_TX_INFO_TX_RETRIES (BIT(3) | BIT(4) | BIT(5))
+
+#define USBC_RX_STATUS_RX_DATA BIT(7)
+#define USBC_RX_STATUS_RX_OVERRUN BIT(6)
+#define USBC_RX_STATUS_RX_CLEAR BIT(0)
+
+#define USBC_PD_STATUS_RX_RSLT(r) ((r >> 3) & 0x7)
+#define USBC_PD_STATUS_TX_RSLT(r) (r & 0x7)
+
+#define USBC_RX_INFO_RXBYTES(r) ((r >> 3) & 0x1f)
+#define USBC_RX_INFO_RX_SOP(r) (r & 0x7)
+
+#define USBC_PD_RX_BUF_LEN 30
+#define USBC_PD_TX_BUF_LEN 30
+
struct wcove_typec {
+ int pd_port_num;
struct mutex lock; /* device lock */
struct device *dev;
struct regmap *regmap;
struct typec_port *port;
+ struct pd_sink_port pd_port;
+ struct completion complete;
struct typec_capability cap;
struct typec_connection con;
struct typec_partner partner;
@@ -106,6 +154,50 @@ enum wcove_typec_role {
WCOVE_ROLE_DEVICE,
};
+static struct sink_ps profiles[] = {
+
+ {
+ .ps_type = PS_TYPE_FIXED,
+ .ps_fixed = {
+ .voltage_fixed = 100, /* 5V/50mV = 100 */
+ .current_default = 90, /* 900mA/10mA = 90 */
+ .current_max = 90, /* 900mA/10mA = 90 */
+ },
+
+ },
+
+ {
+ .ps_type = PS_TYPE_FIXED,
+ .ps_fixed = {
+ .voltage_fixed = 100,
+ .current_default = 300,
+ .current_max = 300,
+ },
+ },
+
+ {
+ .ps_type = PS_TYPE_FIXED,
+ .ps_fixed = {
+ .voltage_fixed = 240,
+ .current_default = 300,
+ .current_max = 300,
+ },
+ },
+
+};
+
+static struct pd_sink_profile profile = {
+ .hw_goodcrc_tx = true,
+ .hw_goodcrc_rx = true,
+ .gotomin = true,
+ .usb_comm = true,
+ .spec = USB_SPEC_3X,
+ .pd_rev = PD_REVISION_2,
+ .ps = profiles,
+ .nr_ps = ARRAY_SIZE(profiles),
+ .active_ps = 2, /* voltage = 5V, current = 3A */
+};
+
static uuid_le uuid = UUID_LE(0x482383f0, 0x2876, 0x4e49,
0x86, 0x85, 0xdb, 0x66, 0x21, 0x1a, 0xf0, 0x37);
@@ -140,7 +232,111 @@ static void wcove_typec_device_mode(struct wcove_typec *wcove)
typec_connect(wcove->port, &wcove->con);
}
-static irqreturn_t wcove_typec_irq(int irq, void *data)
+static int wcove_typec_pd_recv_pkt_handler(struct wcove_typec *wcove)
+{
+ unsigned int rx_status;
+ unsigned int rx_info;
+ unsigned int temp;
+ int len;
+ int ret, i;
+ struct pd_sink_msg *msg;
+ char buf[USBC_PD_RX_BUF_LEN];
+
+ ret = regmap_read(wcove->regmap, USBC_RX_STATUS, &rx_status);
+ if (ret)
+ goto err;
+
+ while (rx_status & USBC_RX_STATUS_RX_DATA) {
+ ret = regmap_read(wcove->regmap, USBC_RX_INFO, &rx_info);
+ if (ret)
+ goto err;
+
+ len = (USBC_RX_INFO_RXBYTES(rx_info));
+
+ for (i = 0; i < len; i++) {
+ ret = regmap_read(wcove->regmap, USBC_RX_DATA_START + i,
+ &temp);
+ buf[i] = (char)temp;
+ if (ret)
+ goto err;
+ }
+
+ msg = pd_sink_alloc_msg(wcove->pd_port_num, len);
+ memcpy(msg->buf, buf, len);
+
+ switch (USBC_RX_INFO_RX_SOP(rx_info)) {
+ case SOP:
+ msg->sop_type = SOP;
+ break;
+ case SOP_P:
+ msg->sop_type = SOP_P;
+ break;
+ case SOP_PP:
+ msg->sop_type = SOP_PP;
+ break;
+ default:
+ pr_warn("Packet type not supported\n");
+ }
+
+ pd_sink_queue_msg(msg);
+
+ /* Clear RX status */
+ regmap_update_bits(wcove->regmap, USBC_RX_STATUS,
+ USBC_RX_STATUS_RX_CLEAR, USBC_RX_STATUS_RX_CLEAR);
+
+ ret = regmap_read(wcove->regmap, USBC_RX_STATUS, &rx_status);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int wcove_typec_pd_tx_pkt_handler(int port_num, void *data,
+ void *buf, int len, enum sop_type pkt_type)
+{
+ unsigned int tx_cmd;
+ unsigned int val;
+ int ret, i;
+ char *buf1 = buf;
+ struct wcove_typec *wcove = data;
+
+ ret = regmap_read(wcove->regmap, USBC_TX_CMD, &tx_cmd);
+ if (ret)
+ goto err;
+
+ if (!(tx_cmd & USBC_TX_CMD_TXBUF_RDY))
+ return -EBUSY;
+
+ for (i = 0; i < len; i++)
+ ret = regmap_write(wcove->regmap, USBC_TX_DATA_START + i,
+ buf1[i]);
+ if (ret)
+ goto err;
+
+ regmap_read(wcove->regmap, USBC_TX_INFO, &val);
+ ret = regmap_write(wcove->regmap, USBC_TX_INFO, 0x71);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(wcove->regmap, USBC_TX_CMD,
+ USBC_TX_CMD_TX_START | (1 << 5));
+ if (ret)
+ goto err;
+
+ ret = regmap_read(wcove->regmap, USBC_TX_CMD, &tx_cmd);
+ if (ret)
+ goto err;
+
+err:
+ kfree(buf1);
+ return ret;
+}
+
+static irqreturn_t wcove_typec_irq(int irq, void *data)
{
struct wcove_typec *wcove = data;
unsigned int cc1_ctrl;
@@ -149,10 +345,10 @@ static irqreturn_t wcove_typec_irq(int irq, void *data)
unsigned int cc_irq2;
unsigned int status1;
unsigned int status2;
+ unsigned int rx_status;
int ret;
mutex_lock(&wcove->lock);
-
ret = regmap_read(wcove->regmap, USBC_IRQ1, &cc_irq1);
if (ret)
goto err;
@@ -177,6 +373,10 @@ static irqreturn_t wcove_typec_irq(int irq, void *data)
if (ret)
goto err;
+ ret = regmap_read(wcove->regmap, USBC_RX_STATUS, &rx_status);
+ if (ret)
+ goto err;
+
if (cc_irq1) {
if (cc_irq1 & USBC_IRQ1_OVERTEMP)
dev_err(wcove->dev, "VCONN Switch Over Temperature!\n");
@@ -185,18 +385,6 @@ static irqreturn_t wcove_typec_irq(int irq, void *data)
regmap_write(wcove->regmap, USBC_IRQ1, cc_irq1);
}
- if (cc_irq2) {
- regmap_write(wcove->regmap, USBC_IRQ2, cc_irq2);
- /*
- * Ingoring any PD communication interrupts until the PD stack
- * is in place
- */
- if (cc_irq2 & ~USBC_IRQ2_CC_CHANGE) {
- dev_WARN(wcove->dev, "USB PD handling missing\n");
- goto err;
- }
- }
-
if (status1 & USBC_STATUS1_DET_ONGOING)
goto out;
@@ -211,24 +399,51 @@ static irqreturn_t wcove_typec_irq(int irq, void *data)
WCOVE_ORIENTATION_NORMAL);
/* Host mode by default */
wcove_typec_func(wcove, WCOVE_FUNC_ROLE, WCOVE_ROLE_HOST);
- goto out;
- }
- if (wcove->con.partner)
+ /* reset the pd sink state */
+ if (wcove->pd_port_num >= 0)
+ pd_sink_reset_state(wcove->pd_port_num);
+
goto out;
+ }
switch (USBC_STATUS1_ORIENT(status1)) {
case USBC_ORIENT_NORMAL:
wcove_typec_func(wcove, WCOVE_FUNC_ORIENTATION,
WCOVE_ORIENTATION_NORMAL);
+ regmap_update_bits(wcove->regmap, USBC_CC_SEL,
+ USBC_CC_SEL_CCSEL, 0x1);
break;
case USBC_ORIENT_REVERSE:
wcove_typec_func(wcove, WCOVE_FUNC_ORIENTATION,
WCOVE_ORIENTATION_REVERSE);
+ regmap_update_bits(wcove->regmap, USBC_CC_SEL,
+ USBC_CC_SEL_CCSEL, 0x2);
default:
break;
}
+ if (cc_irq2 & USBC_IRQ2_RX_PD ||
+ rx_status & USBC_RX_STATUS_RX_DATA)
+ wcove_typec_pd_recv_pkt_handler(wcove);
+
+ if (cc_irq2 & USBC_IRQ2_RX_HR)
+ pr_debug("RX HR not implemented\n");
+
+ if (cc_irq2 & USBC_IRQ2_RX_CR)
+ pr_debug("RX CR not implemented\n");
+
+ if (cc_irq2 & USBC_IRQ2_TX_SUCCESS) {
+ pd_sink_tx_complete(wcove->pd_port_num);
+ pr_debug("TX_SENT\n");
+ }
+
+ if (cc_irq2 & USBC_IRQ2_TX_FAIL)
+ pr_debug("TX_FAIL\n");
+
+ if (wcove->con.partner)
+ goto out;
+
switch (USBC_STATUS1_RSLT(status1)) {
case USBC_RSLT_SRC_DEFAULT:
wcove->con.pwr_opmode = TYPEC_PWR_MODE_USB;
@@ -267,6 +482,9 @@ static irqreturn_t wcove_typec_irq(int irq, void *data)
dev_WARN(wcove->dev, "%s Undefined result\n", __func__);
goto err;
}
+
+ if (!completion_done(&wcove->complete))
+ complete(&wcove->complete);
out:
/* If either CC pins is requesting VCONN, we turn it on */
if ((cc1_ctrl & USBC_CC_CTRL_VCONN_EN) ||
@@ -280,9 +498,12 @@ out:
!!(status2 & USBC_STATUS2_VBUS_REQ));
err:
/* REVISIT: Clear WhiskeyCove CHGR Type-C interrupt */
- regmap_write(wcove->regmap, WCOVE_CHGRIRQ0, BIT(5));
-
+ regmap_write(wcove->regmap, WCOVE_CHGRIRQ0,
+ BIT(5) | BIT(4) | BIT(3) | BIT(0));
+ regmap_write(wcove->regmap, USBC_IRQ1, cc_irq1);
+ regmap_write(wcove->regmap, USBC_IRQ2, cc_irq2);
mutex_unlock(&wcove->lock);
+
return IRQ_HANDLED;
}
@@ -297,6 +518,7 @@ static int wcove_typec_probe(struct platform_device *pdev)
if (!wcove)
return -ENOMEM;
+ init_completion(&wcove->complete);
mutex_init(&wcove->lock);
wcove->dev = &pdev->dev;
wcove->regmap = pmic->regmap;
@@ -307,26 +529,29 @@ static int wcove_typec_probe(struct platform_device *pdev)
return ret;
ret = devm_request_threaded_irq(&pdev->dev, ret, NULL,
- wcove_typec_irq, IRQF_ONESHOT,
+ wcove_typec_irq, IRQF_ONESHOT,
"wcove_typec", wcove);
if (ret)
return ret;
wcove->cap.type = TYPEC_PORT_DRP;
-
wcove->port = typec_register_port(&pdev->dev, &wcove->cap);
if (IS_ERR(wcove->port))
return PTR_ERR(wcove->port);
+ /* PD receive packet handler */
+ wcove->pd_port_num = pd_sink_register_port(&profile,
+ wcove_typec_pd_tx_pkt_handler, wcove);
+ if (wcove->pd_port_num) {
+ pr_err("Register pd sink port failed\n");
+ return -EIO;
+ }
+
if (!acpi_check_dsm(ACPI_HANDLE(&pdev->dev), uuid.b, 0, 0x1f)) {
dev_err(&pdev->dev, "Missing _DSM functions\n");
return -ENODEV;
}
- /* Make sure the PD PHY is disabled until PD stack is ready */
- regmap_read(wcove->regmap, USBC_CONTROL3, &val);
- regmap_write(wcove->regmap, USBC_CONTROL3, val | USBC_CONTROL3_PD_DIS);
-
/* DRP mode without accessory support */
regmap_read(wcove->regmap, USBC_CONTROL1, &val);
regmap_write(wcove->regmap, USBC_CONTROL1, USBC_CONTROL1_MODE_DRP(val));
@@ -337,6 +562,17 @@ static int wcove_typec_probe(struct platform_device *pdev)
regmap_read(wcove->regmap, USBC_IRQMASK2, &val);
regmap_write(wcove->regmap, USBC_IRQMASK2, val & ~USBC_IRQMASK2_ALL);
+ /*Set HW control the ID of outgoing messages*/
+ regmap_write(wcove->regmap, USBC_PD_CFG1, BIT(7));
+
+ /* Enable SOP messages for now */
+ regmap_write(wcove->regmap, USBC_PD_CFG2, BIT(0));
+
+ /*Set the PD revision */
+ regmap_read(wcove->regmap, USBC_PD_CFG3, &val);
+ val = 0x14;
+ regmap_write(wcove->regmap, USBC_PD_CFG3, val);
+
platform_set_drvdata(pdev, wcove);
return 0;
}
@@ -353,6 +589,7 @@ static int wcove_typec_remove(struct platform_device *pdev)
regmap_write(wcove->regmap, USBC_IRQMASK2, val | USBC_IRQMASK2_ALL);
typec_unregister_port(wcove->port);
+ pd_sink_unregister_port(wcove->pd_port_num);
return 0;
}
--
1.9.1
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2016-07-15 2:13 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-07-15 2:16 [PATCH 2/2] usb: typec: add PD sink port support for Intel Whiskey Cove PMIC Typc-C PHY driver Bin Gao
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.