Received: by 10.223.185.116 with SMTP id b49csp6959313wrg; Wed, 28 Feb 2018 19:49:01 -0800 (PST) X-Google-Smtp-Source: AG47ELtHdY8d7G9gcz840s4TAQgg3pjv8IcvnA04l3VS4xqS7BlrXDxSBa1Xeg4/fU1LLnJbdVkE X-Received: by 2002:a17:902:a981:: with SMTP id bh1-v6mr495893plb.298.1519876140934; Wed, 28 Feb 2018 19:49:00 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1519876140; cv=none; d=google.com; s=arc-20160816; b=Ca4foisgv2cinO1TQrHWDzc+EwlizJcmvPBCNfQXq0onncOZMjmgLd1bjlIvRxcRTp UAZU35TyGaqaFqufVAktUqEjHcaj8ZSbh10qrGS3ZC73N001n+RDT7TU7Se2JUu/9kmM 0pQRAiWMMT5WE+Zcw1tkgOvBkCBdIxjBskHdi/Y8TyhasGV1nunBKaq2N11WeVFb3KJk /Hlb1n9isohjllXPHlTmFf+TApVX2E+qYwF6hiSn+hyJBP/8mvtrp3zKzhflDIrN63WJ fdu8gqO9/qkwbgwRjvw5EhF6jwV9ihfe/176iq/NFW0Bq1qaGfk6i5EGQlkSwztUJJYE Xp9A== 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=kxd8y8T67QiscEaskYnY+vOvQdXkdCYqEtpVWRBAeL4=; b=ZCFMI5TPlvYk1Wf4JOY8UMspXu9xpGbAHSoFzsA51o+ENyxB2QsPQkzF0mWBpcajw9 XdLfWWn5gJpPQCo7QLSeXfvUnhX6eZ/iWkm7xM7GD2nVCje6Rfzi7FtqWVxYG1AwZNne ihRvKjRlP76msPPFSABb7BkjNsy3vhe6rAdsBphNU+dzY5hXOlGVl9zk4VsrcCxZ8E4c vPoqS72hn1TzO8bwHX5GXZaYwTbztHrhh5g9Ghu44VWBRujdNszruJ1I1mHdH4qnXjGn /9wfE2b8FYxJGgzpYlV38BssA9KTt1nK6uyUcvOGUMIT4GR4NifwMuzaUBg2xjopS0BC dVNQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=qPHf/iTf; 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 j5si2280969pfe.334.2018.02.28.19.48.45; Wed, 28 Feb 2018 19:49:00 -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=qPHf/iTf; 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 S965787AbeCADrp (ORCPT + 99 others); Wed, 28 Feb 2018 22:47:45 -0500 Received: from mail-pg0-f67.google.com ([74.125.83.67]:38126 "EHLO mail-pg0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965714AbeCADrm (ORCPT ); Wed, 28 Feb 2018 22:47:42 -0500 Received: by mail-pg0-f67.google.com with SMTP id l24so1821085pgc.5; Wed, 28 Feb 2018 19:47:42 -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=kxd8y8T67QiscEaskYnY+vOvQdXkdCYqEtpVWRBAeL4=; b=qPHf/iTfOgMmoheqM1NyHTEbwRXGK1kVuo4/Ib0eOC06FxqQLB2StamSa87kvPszqg h/Chv6lMdSt+Yvfoh2c4ppR7WjEvJ1+XgJts51QTAT3FMxP/SW4UJRQl9OuYa4EUW5eq JpOgPfxKOQ5TkibY9qmu/ZEVJYovnE6Rll7Xvl9MBXA8zJTuaJZYepiMDaiYx8QNCPKd IUYHqn6q21unzVB1Jg0v0G2VD+Nk6I3uB1zgDm+YsKvw+Qr/eOtrPSA/e+tvr13Sv0mv sxZCUJyocvvb7diM2CWMZXywHtlCtX+ttTb3SAUo+Gwjqi3DHdOw6xOfsu00WvG2A0Kl j5ew== 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=kxd8y8T67QiscEaskYnY+vOvQdXkdCYqEtpVWRBAeL4=; b=pZTj3K14jz16XFZUI1JHz9vITr0QP7O+UC1XmWLPO8XWdoe5eQWPKhru0Y/+i8MMtf CowQmfU9ETDmbQ9QiqJ1o9Q+9myO2JSuI3IrMU+oVe6gtoHO6jSJEnJCfrQcnD4is4jt 3nhBOOeTt+wqJ0GsTLVk4f+bfH7h6OI7eneU0s2dlQTGw/TTHV+VmrlztEe8PniYsiHH BcrBv2efwdHvIF7nRoeDxr4U2mY3F9dmsSR21MvO1rdCKhIRgZCUCNZnvuAbwLqwNEEB +/vPLSs63VYx84cOwJEJnJkFKc+SlgAp30yJP+7jkMtVXLj0z3602t3WcLNYqsuM+Tg1 VYnQ== X-Gm-Message-State: APf1xPDSV5ki5/e45Q1BQvkPKHr1KGlul4B27OOXi4r3I/0StQzxFRA4 Pf1iy4X+cr/VuuLnCUOFn4Y= X-Received: by 10.98.70.198 with SMTP id o67mr463699pfi.173.1519876061944; Wed, 28 Feb 2018 19:47:41 -0800 (PST) Received: from localhost.localdomain (114-137-136-40.emome-ip.hinet.net. [114.137.136.40]) by smtp.gmail.com with ESMTPSA id q65sm6998581pfd.140.2018.02.28.19.47.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 28 Feb 2018 19:47:41 -0800 (PST) From: ShuFanLee 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 v4] staging: typec: handle vendor defined part and modify drp toggling flow Date: Thu, 1 Mar 2018 11:47:16 +0800 Message-Id: <1519876036-13630-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: ShuFanLee Handle vendor defined behavior in tcpci_init, tcpci_set_vconn 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 chagned as following: - Write DRP = 0 & Rd/Rd - Write DRP = 1 - Set LOOK4CONNECTION command Signed-off-by: ShuFanLee --- drivers/staging/typec/tcpci.c | 127 +++++++++++++++++++++++++++++++++--------- drivers/staging/typec/tcpci.h | 13 +++++ 2 files changed, 113 insertions(+), 27 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 diff --git a/drivers/staging/typec/tcpci.c b/drivers/staging/typec/tcpci.c index 9bd4412..5bafcc4 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,8 +102,10 @@ 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; + unsigned int reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); switch (cc) { default: @@ -117,7 +123,16 @@ static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc, break; } - return regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); + ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); + if (ret < 0) + return ret; + usleep_range(500, 1000); + reg |= TCPC_ROLE_CTRL_DRP; + 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 +193,16 @@ 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) { + 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 +348,15 @@ static int tcpci_init(struct tcpc_dev *tcpc) if (time_after(jiffies, timeout)) return -ETIMEDOUT; + /* Handle vendor init */ + if (tcpci->data) { + 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 +378,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 +445,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 +476,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 +504,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); - /* Disable chip interrupts */ - tcpci_write16(tcpci, TCPC_ALERT_MASK, 0); + 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); - err = devm_request_threaded_irq(tcpci->dev, client->irq, NULL, - tcpci_irq, +static int tcpci_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + struct tcpci_chip *chip; + int err; + u16 val = 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); + + /* 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..40025b2 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,16 @@ #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); +}; + +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