Received: by 10.223.185.116 with SMTP id b49csp796371wrg; Wed, 21 Feb 2018 07:05:00 -0800 (PST) X-Google-Smtp-Source: AH8x227OCxIUcNbJngSWXxsSVCm6TFaGTXuY6yOZO4sbWidjzzr6/DIeOv9GSZ350KugJdK1/g1L X-Received: by 10.101.69.13 with SMTP id n13mr3004135pgq.62.1519225499960; Wed, 21 Feb 2018 07:04:59 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1519225499; cv=none; d=google.com; s=arc-20160816; b=LIgLdTn5i9lsLCiwJyvvb7PoxMt1w6Evh+l7DyVZ2Nxev9u9EJ8CLVzL32sGn/IpT1 NHkcJkPWUUJ3dVRm6qBtT46yNVvSHvEpMOI6Fuh/EQVspuCpgnFSE3OEIgApMBI/K4wq PAj8QvE/S81/ksLNUXVQI/+cyAZTsZx05sTXMB2YBw0zMSCJ0Wda2vInk5gaFSlUVlTo KULF6VFGBwB+VYPmDoFWdpLOrIXDzaLIj4RSQOD3q2gFNCklNIXyxjQimEZyHr9d4McE hwIIgdOj0fwcmH7gDPYZX7EOyhLSmxIVjiobf+4oRo669uqepYT9Z4vdjMW533h7zM/5 s9Bw== 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=/rZmRk6YgaBF762eSlidPHlobwsLampqY4hcYjyBUa4=; b=iILWt9u/yFiV/vEDYzaqdmc2lhmj5B+UResszoZhvbvj/pce6fWJeIWjvf+DT47sU5 ytAlXwvnSCQPkx7HW8lCNjHuDt33fwGb30PDahF1EKtvS0TjFXx8nMJrWB7RdBSyW9/o lRD0PxFedQ3xg3fqVoRzRQIu+Tgac8NQGhSxdaO0d3OhJXvV9GPGDeKPhAoW+eevkWOq ccSM+JDYejCeiI6r+k4WfakV0EKXHTMwYxFEV8JZ6qvGf9QdxjqlPQV7GEIarS8pr8EH 3NPe5rMq/G4c6bsLvMbomXupW5/JDcLjS7N/sDVKTf6456G9H9GgXs10AnngtLXIon/o BXpQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=jkpX3bdI; 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 q25si6816484pge.457.2018.02.21.07.04.37; Wed, 21 Feb 2018 07:04:59 -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=jkpX3bdI; 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 S965941AbeBUPCf (ORCPT + 99 others); Wed, 21 Feb 2018 10:02:35 -0500 Received: from mail-pg0-f65.google.com ([74.125.83.65]:33023 "EHLO mail-pg0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965878AbeBUPCc (ORCPT ); Wed, 21 Feb 2018 10:02:32 -0500 Received: by mail-pg0-f65.google.com with SMTP id g12so733381pgs.0; Wed, 21 Feb 2018 07:02:32 -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=/rZmRk6YgaBF762eSlidPHlobwsLampqY4hcYjyBUa4=; b=jkpX3bdIf/s9/6POg6BKGhLPOEhzHbdr5kft8hlkQb9vJQDYOIBXrmnUXWG06QlHCc vODXrZk/75dfOBe6iR5cWwMADmTR3IKyxPCEQ9wX1jnhKXczkqtzX8CCca/NGs/OjNaG qQIgdGcRnG09Y2K2jZZj8+gIIIuUELNC50FV+4IkIIAkC/NjlLj/Wyk9LK9HBgMgN8q0 aTsqy4TgMfU+DGjN+/YpcgiXhahuYPQk0b6kAE3CbioctkjybiaqiPrfw3Ku8FzseIBC OMD0L54qPuXFEv9jVdhj7vZ/Lt0fgwnt/8ueO42Dxzn71bsuZiC2Hw65bl7sx30NacCn PkbA== 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=/rZmRk6YgaBF762eSlidPHlobwsLampqY4hcYjyBUa4=; b=Y8ZtA3DkqnA4taR6h/qvWxIJG/VRUk/Rs8nkJ0ONlEu/xbIme6ZkP91iod8Nsc8OS4 7LTZwMYL6gtX1AR8kimTEHnAOQQy1UnxBMXk+G995HHMwwcKoCYY/+r2ncwteiIR0b+l MZgtNkSl0zS7pq/M0ZIvci6OmPTEwAyILLFsG3Jf6oP6hpwJn7Rm+gpkuND6AWQdCNPk eSHqGzBWADSpe+/JVFRVFlNNZ/R1KyDB3XZm/A+10/qhB0Cj/e/BTNu2p6qeRTjzT5re RxOL9UM5B8V7rBp6zAe+LBDuYpvBbYsIcF0ADziRL4ZADouoE9ptucU/pjg3yxhDFFov Sdyg== X-Gm-Message-State: APf1xPCG0/MGznVBSa5bPNOyJweliU6XRO74BRjzv6gqYO+dzCovmXBJ YPQrTQlYgYI0eBh52c7FCbeDAmYZyT8= X-Received: by 10.99.141.200 with SMTP id z191mr2931320pgd.418.1519225351774; Wed, 21 Feb 2018 07:02:31 -0800 (PST) Received: from localhost.localdomain (114-32-235-24.HINET-IP.hinet.net. [114.32.235.24]) by smtp.gmail.com with ESMTPSA id y7sm65450309pfy.128.2018.02.21.07.02.29 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 21 Feb 2018 07:02:31 -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, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Subject: [PATCH] staging: typec: handle vendor defined part and modify drp toggling flow Date: Wed, 21 Feb 2018 23:02:23 +0800 Message-Id: <1519225343-2929-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 | 128 +++++++++++++++++++++++++++++++++--------- drivers/staging/typec/tcpci.h | 13 +++++ 2 files changed, 115 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 diff --git a/drivers/staging/typec/tcpci.c b/drivers/staging/typec/tcpci.c index 9bd4412..4959c69 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,19 @@ 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; + ret = regmap_write(tcpci->regmap, TCPC_COMMAND, + TCPC_CMD_LOOK4CONNECTION); + if (ret < 0) + return ret; + return 0; } static enum typec_cc_status tcpci_to_typec_cc(unsigned int cc, bool sink) @@ -178,6 +196,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 +351,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 +381,15 @@ 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) +static irqreturn_t _tcpci_irq(int irq, void *dev_id) { struct tcpci *tcpci = dev_id; + + return tcpci_irq(tcpci); +} + +irqreturn_t tcpci_irq(struct tcpci *tcpci) +{ u16 status; tcpci_read16(tcpci, TCPC_ALERT, &status); @@ -412,6 +455,7 @@ static irqreturn_t tcpci_irq(int irq, void *dev_id) return IRQ_HANDLED; } +EXPORT_SYMBOL_GPL(tcpci_irq); static const struct regmap_config tcpci_regmap_config = { .reg_bits = 8, @@ -435,22 +479,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 +507,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