Received: by 10.223.185.116 with SMTP id b49csp3264110wrg; Mon, 5 Mar 2018 17:52:10 -0800 (PST) X-Google-Smtp-Source: AG47ELsQ5lC3FH9QS24yFpSB0q/4iKtJrN4WmnwHFpEH5DUXvqLE6LUnSSDW6kHC8+MoimLdgw6V X-Received: by 10.99.126.17 with SMTP id z17mr13747411pgc.218.1520301130175; Mon, 05 Mar 2018 17:52:10 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1520301130; cv=none; d=google.com; s=arc-20160816; b=vqF0fsDYXBafkBZ9icZP9aG61Trv1KMPfMVw5cqqmcaTojOFy4Lp6mEPUUP+uq3SKt rTD8WQcAQubFqjjeUWyEP5nyd33QZdy0eUlY2ZgN/uwn/2xZ8IMElut9WXXZhOkLCu2z yT8RcWYMAvCZw9tBHpWA3wKLnTaheSFOVB3Ps65di5N7OTWs3XGZZqb+ArpaKnVgTMei 4zBQxu4A2paY1qMK5UtjyBNPBNiyqvb0154kMIuDX5LX0m9PxGRQZXVQgcRd7ZhNC/Wt uruxY2IfDOPdOyyHNYqrF9vJOI3I1IJS4jWaYTpjOl+hH7DWkCpJG/W4Ay1XZ5gtU8hn kr/Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:message-id:date:subject:cc:to:from :dkim-signature:arc-authentication-results; bh=fHCDCI04fS/pwsmmoZ2ZbmixhGTcHGGwnwH4rVT1oU4=; b=o5paQqcv9wYs+F8ZzMAlxWwGMqLolYw0DyDpPM6dUqjF1CqKfKqZgpdDeORclWS1k7 VtMhVSvDGzw3PaubYqwIwSJH3NPOkBKvECfjR+sqWRmYC8sY7w3qnaJKBIp0n+R380Zw pZJvgPDEowBrzYnfpn/TxC08aaGW/OeqR/VGgEsL97ZzeuclPkuyjtbhqAW45lFpc4Hg LWpFsLs0hEpzrQ9yS1M6L6NF9RMp8eYlrVJUahWmWoIX3t3stNIJBm1mR7CLGSB9icrn OKkDIvnawwyl0Rpx7ay+u+AHKbVQwU3guPU363K6VSdurLXtukfGET1/WpHddoEGIJWh SDqw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=vTM/FvzM; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id k4-v6si10130597pls.277.2018.03.05.17.51.55; Mon, 05 Mar 2018 17:52:10 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=vTM/FvzM; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932287AbeCFBu5 (ORCPT + 99 others); Mon, 5 Mar 2018 20:50:57 -0500 Received: from mail-pf0-f196.google.com ([209.85.192.196]:43565 "EHLO mail-pf0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752017AbeCFBu4 (ORCPT ); Mon, 5 Mar 2018 20:50:56 -0500 Received: by mail-pf0-f196.google.com with SMTP id j2so5952692pff.10; Mon, 05 Mar 2018 17:50:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=fHCDCI04fS/pwsmmoZ2ZbmixhGTcHGGwnwH4rVT1oU4=; b=vTM/FvzMCDpyJUgEz4okfKGb5ocJAbOEE1WHtBnC8L+ZXvlOsOLec69Wl+O52zTwHa 4cIzU/ih3b1MoqQgkLs5+nSJ1jcM44xlGFCTLyokx7WaIQULVXapFMOezoDgrEUn/Mlh rbbdjz6coTbB56mINxNK6RcksHDkXz1s+w1XiXRVl6ghCjU65myp8hZ02iLoKfauPpKU 87n+Z+hMoUBkgFnSaaAsIHekWDtOEEv1LxS5Ng71gse0t6C3jS2kIecFkZ+KpAsG/MdT YOKwhJyoJQR7JsApaZ60OmkrpmeEsICa0tmzE3qBaIZUTr9XtaaptjVQe5XvVQpFI/3n RUWg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=fHCDCI04fS/pwsmmoZ2ZbmixhGTcHGGwnwH4rVT1oU4=; b=HzMcZpBTQ8739ZSU0+XAQu6yn5HwwW2jQIGAAoxh3OdEe1ZudY4tSqaDs5oC/GqZR4 CRpTPtFdVh796n99p/hv7EwUUmZV9pPmWxO7oR/wg9w3YSEkyMOBSRSxjA+znvAi1A9j cwXIzaFDQZmR2gle1QHprFPGujUyREqr3ADUBuus3Czo+HvNbeSH8NBwS+nPcIQhxA3S jMpNORcIAVm1zIaWjx0GDIgocF+sBU3cRS5UG5m+LvNRGOvWHt+ChuxoKeuHr3xOWW+z 9L5jrOaDYOa/A8zK4ueqqKNQxoSPMXK0kDK/aHLlKLUoT9YrPY2/7tgcQ+sz6t7a9cPb /4pA== X-Gm-Message-State: APf1xPC4txr8rIipjAQPQYsg4Gqqt6ufYPwW7hJWfoTAJhET/NuFsCtd qUHPzJx+1LkM/zuKHmmyahA= X-Received: by 10.99.110.70 with SMTP id j67mr13741853pgc.202.1520301055712; Mon, 05 Mar 2018 17:50:55 -0800 (PST) Received: from localhost.localdomain (42-72-178-43.emome-ip.hinet.net. [42.72.178.43]) by smtp.gmail.com with ESMTPSA id b68sm21758994pfg.59.2018.03.05.17.50.53 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 05 Mar 2018 17:50:54 -0800 (PST) From: ShuFan Lee To: heikki.krogerus@linux.intel.com, linux@roeck-us.net, greg@kroah.com Cc: shufan_lee@richtek.com, cy_huang@richtek.com, jun.li@nxp.com, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Subject: [PATCH v7] staging: typec: handle vendor defined part and modify drp toggling flow Date: Tue, 6 Mar 2018 09:50:32 +0800 Message-Id: <1520301032-10109-1-git-send-email-leechu729@gmail.com> X-Mailer: git-send-email 1.9.1 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: ShuFan Lee Handle vendor defined behavior in tcpci_init, tcpci_set_vconn, tcpci_start_drp_toggling and export tcpci_irq. More operations can be extended in tcpci_data if needed. According to TCPCI specification, 4.4.5.2 ROLE_CONTROL, TCPC shall not start DRP toggling until subsequently the TCPM writes to the COMMAND register to start DRP toggling. DRP toggling flow is changed as following: - Write DRP = 1, Rp level and RC.CCx to Rd/Rd or Rp/Rp - Set LOOK4CONNECTION command Signed-off-by: ShuFan Lee --- drivers/staging/typec/tcpci.c | 127 +++++++++++++++++++++++++++++++++--------- drivers/staging/typec/tcpci.h | 15 +++++ 2 files changed, 116 insertions(+), 26 deletions(-) patch changelogs between v1 & v2 - Remove unnecessary i2c_client in the structure of tcpci. - Rename structure of tcpci_vendor_data to tcpci_data. - Not exporting tcpci read/write wrappers but register i2c regmap in glue driver. - Add set_vconn ops in tcpci_data. (It is necessary for RT1711H to enable/disable idle mode before disabling/enabling vconn) - Export tcpci_irq so that vendor can call it in their own IRQ handler. patch changelogs between v2 & v3 - Change the return type of tcpci_irq from int to irqreturn_t. patch changelogs between v3 & v4 - Directly return regmap_write at the end of drp_toggling function. - Because tcpci_irq is called in _tcpci_irq, move _tcpci_irq to the place after tcpci_irq. patch changelogs between v4 & v5 - Add a space between my first & last name, from ShuFanLee to ShuFan Lee. patch changelogs between v5 & v6 - Add start_drp_toggling in tcpci_data and call it at the beginning of tcpci_start_drp_toggling. - Modify the flow of tcpci_start_drp_toggling as following: - Set Rp level, RC.CCx to Rd/Rd or Rp/Rp and DRP = 1 - Set LOOK4CONNECTION command patch changelogs between v6 & v7 - Remove checking whether tcpci->data is NULL because it is guaranteed to be available. diff --git a/drivers/staging/typec/tcpci.c b/drivers/staging/typec/tcpci.c index 9bd4412..8043740 100644 --- a/drivers/staging/typec/tcpci.c +++ b/drivers/staging/typec/tcpci.c @@ -21,7 +21,6 @@ struct tcpci { struct device *dev; - struct i2c_client *client; struct tcpm_port *port; @@ -30,6 +29,12 @@ struct tcpci { bool controls_vbus; struct tcpc_dev tcpc; + struct tcpci_data *data; +}; + +struct tcpci_chip { + struct tcpci *tcpci; + struct tcpci_data data; }; static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc) @@ -37,8 +42,7 @@ 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, - u16 *val) +static int tcpci_read16(struct tcpci *tcpci, unsigned int reg, u16 *val) { return regmap_raw_read(tcpci->regmap, reg, val, sizeof(u16)); } @@ -98,9 +102,17 @@ static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc) static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc, enum typec_cc_status cc) { + int ret; struct tcpci *tcpci = tcpc_to_tcpci(tcpc); unsigned int reg = TCPC_ROLE_CTRL_DRP; + /* Handle vendor drp toggling */ + if (tcpci->data->start_drp_toggling) { + ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc); + if (ret < 0) + return ret; + } + switch (cc) { default: case TYPEC_CC_RP_DEF: @@ -117,7 +129,17 @@ static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc, break; } - return regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); + if (cc == TYPEC_CC_RD) + reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); + else + reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT); + ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); + if (ret < 0) + return ret; + return regmap_write(tcpci->regmap, TCPC_COMMAND, + TCPC_CMD_LOOK4CONNECTION); } static enum typec_cc_status tcpci_to_typec_cc(unsigned int cc, bool sink) @@ -178,6 +200,13 @@ static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable) struct tcpci *tcpci = tcpc_to_tcpci(tcpc); int ret; + /* Handle vendor set vconn */ + if (tcpci->data->set_vconn) { + ret = tcpci->data->set_vconn(tcpci, tcpci->data, enable); + if (ret < 0) + return ret; + } + ret = regmap_write(tcpci->regmap, TCPC_POWER_CTRL, enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0); if (ret < 0) @@ -323,6 +352,13 @@ static int tcpci_init(struct tcpc_dev *tcpc) if (time_after(jiffies, timeout)) return -ETIMEDOUT; + /* Handle vendor init */ + if (tcpci->data->init) { + ret = tcpci->data->init(tcpci, tcpci->data); + if (ret < 0) + return ret; + } + /* Clear all events */ ret = tcpci_write16(tcpci, TCPC_ALERT, 0xffff); if (ret < 0) @@ -344,9 +380,8 @@ static int tcpci_init(struct tcpc_dev *tcpc) return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg); } -static irqreturn_t tcpci_irq(int irq, void *dev_id) +irqreturn_t tcpci_irq(struct tcpci *tcpci) { - struct tcpci *tcpci = dev_id; u16 status; tcpci_read16(tcpci, TCPC_ALERT, &status); @@ -412,6 +447,14 @@ static irqreturn_t tcpci_irq(int irq, void *dev_id) return IRQ_HANDLED; } +EXPORT_SYMBOL_GPL(tcpci_irq); + +static irqreturn_t _tcpci_irq(int irq, void *dev_id) +{ + struct tcpci *tcpci = dev_id; + + return tcpci_irq(tcpci); +} static const struct regmap_config tcpci_regmap_config = { .reg_bits = 8, @@ -435,22 +478,18 @@ static int tcpci_parse_config(struct tcpci *tcpci) return 0; } -static int tcpci_probe(struct i2c_client *client, - const struct i2c_device_id *i2c_id) +struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) { struct tcpci *tcpci; int err; - tcpci = devm_kzalloc(&client->dev, sizeof(*tcpci), GFP_KERNEL); + tcpci = devm_kzalloc(dev, sizeof(*tcpci), GFP_KERNEL); if (!tcpci) - return -ENOMEM; + return ERR_PTR(-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->dev = dev; + tcpci->data = data; + tcpci->regmap = data->regmap; tcpci->tcpc.init = tcpci_init; tcpci->tcpc.get_vbus = tcpci_get_vbus; @@ -467,27 +506,63 @@ static int tcpci_probe(struct i2c_client *client, err = tcpci_parse_config(tcpci); if (err < 0) - return err; + return ERR_PTR(err); + + tcpci->port = tcpm_register_port(tcpci->dev, &tcpci->tcpc); + if (PTR_ERR_OR_ZERO(tcpci->port)) + return ERR_CAST(tcpci->port); + + return tcpci; +} +EXPORT_SYMBOL_GPL(tcpci_register_port); + +void tcpci_unregister_port(struct tcpci *tcpci) +{ + tcpm_unregister_port(tcpci->port); +} +EXPORT_SYMBOL_GPL(tcpci_unregister_port); + +static int tcpci_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + struct tcpci_chip *chip; + int err; + u16 val = 0; - /* Disable chip interrupts */ - tcpci_write16(tcpci, TCPC_ALERT_MASK, 0); + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->data.regmap = devm_regmap_init_i2c(client, &tcpci_regmap_config); + if (IS_ERR(chip->data.regmap)) + return PTR_ERR(chip->data.regmap); - err = devm_request_threaded_irq(tcpci->dev, client->irq, NULL, - tcpci_irq, + /* Disable chip interrupts before requesting irq */ + err = regmap_raw_write(chip->data.regmap, TCPC_ALERT_MASK, &val, + sizeof(u16)); + if (err < 0) + return err; + + err = devm_request_threaded_irq(&client->dev, client->irq, NULL, + _tcpci_irq, IRQF_ONESHOT | IRQF_TRIGGER_LOW, - dev_name(tcpci->dev), tcpci); + dev_name(&client->dev), chip); if (err < 0) return err; - tcpci->port = tcpm_register_port(tcpci->dev, &tcpci->tcpc); - return PTR_ERR_OR_ZERO(tcpci->port); + chip->tcpci = tcpci_register_port(&client->dev, &chip->data); + if (PTR_ERR_OR_ZERO(chip->tcpci)) + return PTR_ERR(chip->tcpci); + + i2c_set_clientdata(client, chip); + return 0; } static int tcpci_remove(struct i2c_client *client) { - struct tcpci *tcpci = i2c_get_clientdata(client); + struct tcpci_chip *chip = i2c_get_clientdata(client); - tcpm_unregister_port(tcpci->port); + tcpci_unregister_port(chip->tcpci); return 0; } diff --git a/drivers/staging/typec/tcpci.h b/drivers/staging/typec/tcpci.h index fdfb06c..a2c1754 100644 --- a/drivers/staging/typec/tcpci.h +++ b/drivers/staging/typec/tcpci.h @@ -59,6 +59,7 @@ #define TCPC_POWER_CTRL_VCONN_ENABLE BIT(0) #define TCPC_CC_STATUS 0x1d +#define TCPC_CC_STATUS_DRPRST BIT(5) #define TCPC_CC_STATUS_TERM BIT(4) #define TCPC_CC_STATUS_CC2_SHIFT 2 #define TCPC_CC_STATUS_CC2_MASK 0x3 @@ -121,4 +122,18 @@ #define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG 0x76 #define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG 0x78 +struct tcpci; +struct tcpci_data { + struct regmap *regmap; + int (*init)(struct tcpci *tcpci, struct tcpci_data *data); + int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data, + bool enable); + int (*start_drp_toggling)(struct tcpci *tcpci, struct tcpci_data *data, + enum typec_cc_status cc); +}; + +struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data); +void tcpci_unregister_port(struct tcpci *tcpci); +irqreturn_t tcpci_irq(struct tcpci *tcpci); + #endif /* __LINUX_USB_TCPCI_H */ -- 1.9.1