usb: typec: add PD sink port support for Intel Whiskey Cove PMIC Typc-C PHY driver
Subsystems (2)
PATCH 2/2 usb: typec: add PD sink port support for Intel Whiskey Cove PMIC Typc-C PHY driver
From: Chandra Sekhar Anagani <[email protected]>
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 <[email protected]>
--- drivers/usb/typec/typec_wcove.c | 289 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 263 insertions(+), 26 deletions(-)
drivers/usb/typec/typec_wcove.c +263 -26
@@ -3,6 +3,7 @@* * Copyright (C) 2016 Intel Corporation * Author: Heikki Krogerus <[email protected]> +* Author: Chandra Sekhar Anagani <[email protected]> * * 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 ; }
Signature · 1 line
1.9.1