From patchwork Mon Feb 7 04:39:06 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Badhri Jagan Sridharan X-Patchwork-Id: 12736903 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A9992C433EF for ; Mon, 7 Feb 2022 06:14:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244668AbiBGGNU (ORCPT ); Mon, 7 Feb 2022 01:13:20 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48406 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1351822AbiBGEjQ (ORCPT ); Sun, 6 Feb 2022 23:39:16 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 97A23C043181 for ; Sun, 6 Feb 2022 20:39:13 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id z15-20020a25bb0f000000b00613388c7d99so26043798ybg.8 for ; Sun, 06 Feb 2022 20:39:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:message-id:mime-version:subject:from:to:cc; bh=ZGBcS7JWV4jEm41Ieg0bh/DWDMa+jid9L2vZwRwLPsQ=; b=hTi2MIRuc/XCPJdsJ8EBR8xE/nfUcnqO/pcV2L0/Vx4Muy05JpQDhrmhezGgIVhPV8 G6etzRvhkLJ/XS074ODg978oyePc62d5Hwywtt24jXa5rivawKxCYakQnlJOdTZdMWbp S1bO7TV0OaXcoTVKm6WBLjTfYvhKrZ3lQJMEZw1+59aZhZR8ogcuHZpwn7zAWGo0nvJj D1mF1Ch86HwaduTjHGN9jlHC3MYC/bkEeWtDbmwzRmpqZGCLbiAnL5xPJcFQ2qpE8ksY WWs2wyHZ5LKrq0VY5TefuETmU0Ui72bJJ4aoXZY/WeoF6WUFzkHYqaRQleJ7vKTeu2Q1 x5aA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:message-id:mime-version:subject:from:to:cc; bh=ZGBcS7JWV4jEm41Ieg0bh/DWDMa+jid9L2vZwRwLPsQ=; b=5go+JEigJ/Vd5q4jhplbcQp4g/BHzZcZqniB4PFA7dlsjCkW0JRPhYMH1IKOMdQqKf zSrNXVF4jZ6y8lY5xtIJChxnLCzPpjAIhkftTNbkRGF3gzixRkVXge3J120E62z5TMqs bjtibNBFNexRDBAPR9yfEQLMlBwBTZuFDJSvmzvX7asdCShtbrQafxOsnVyboZhFUYdN 2c47Py+fJ4iX7f5p/ZzEWfyJNGppMGR2a8TAtMRgENcxMR1JouvS8Qm23rBQAUEg92lj SP15mFAS9iuz/EmG/HX0v+AF2Nr45We91eH7LV8s2luJl931Lzxk76BftT4C9jPa6XeG +hfw== X-Gm-Message-State: AOAM5306m09BoBgI5Aa+7VhBeS8LdCWZkb2FluwTZPGlb1Pmd8uHYjhx oUlhX5vNRvi3HP2wMuU6fwTckXLg5zg= X-Google-Smtp-Source: ABdhPJyKen1NvGRKRRF5JJSfuC0xiMhzV6FqmDa7uRHf2FY/+bd89jMaurQm8wBzbpZx9FYb+xTlwVIwGvs= X-Received: from badhri.mtv.corp.google.com ([2620:15c:211:201:55d1:a045:9816:1433]) (user=badhri job=sendgmr) by 2002:a5b:bcd:: with SMTP id c13mr251353ybr.303.1644208752853; Sun, 06 Feb 2022 20:39:12 -0800 (PST) Date: Sun, 6 Feb 2022 20:39:06 -0800 Message-Id: <20220207043907.2758424-1-badhri@google.com> Mime-Version: 1.0 X-Mailer: git-send-email 2.35.0.263.gb82422642f-goog Subject: [PATCH v1 1/2] usb: typec: Introduce typec attributes for limiting source current From: Badhri Jagan Sridharan To: Guenter Roeck , Heikki Krogerus , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Kyle Tso , Badhri Jagan Sridharan Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org This change introduces the following two attributes to the typec sysfs class which allows userspace to limit the power advertised while acting as source. This is useful to mitigate battery drain in portable devices when the battery SOC is low. New attibutes introduced: 1. limit_src_current_active 2. limit_src_current_ma The port while in PD contract and acting as source would only advertise vSafe5V fixed PDO with current set through limit_src_current_ma when limit_src_current_active is set to 1. When limit_src_current_active is set to 0, the port would publish the default source capabilities. limit_src_current_ma would limit the current to the Maximum current published by vSafe5V fixed pdo of the default source capabilities of the port. Signed-off-by: Badhri Jagan Sridharan --- Documentation/ABI/testing/sysfs-class-typec | 25 ++++++ drivers/usb/typec/class.c | 99 +++++++++++++++++++++ drivers/usb/typec/class.h | 5 ++ include/linux/usb/typec.h | 4 + 4 files changed, 133 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index 75088ecad202..dd2240632172 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec @@ -141,6 +141,31 @@ Description: - "reverse": CC2 orientation - "unknown": Orientation cannot be determined. +What: /sys/class/typec//limit_src_current_active +Date: February 2022 +Contact: Badhri Jagan Sridharan +Description: + This attribute can be used to make the port only publish + vSafe5V fixed pdo with Maximum current limited to the + current limit set by limit_src_current_ma when the port + is acting as source. + Valid values: + - write(2) "1" limits source capabilities to vSafe5V + with max current specified by limit_src_current_ma + - write(2) "0" publishes the default source capabilities + of the port. + +What: /sys/class/typec//limit_src_current_ma +Date: February 2022 +Contact: Badhri Jagan Sridharan +Description: + This attribute allows write(2) to set the Maximum + current published when limit_src_current_active is set + to 1. When limit_src_current_active is already set + to 1, if the port is already acting as source with + explicit contract in place, write(2) will make the port + renegotiate the pd contract. + USB Type-C partner devices (eg. /sys/class/typec/port0-partner/) What: /sys/class/typec/-partner/accessory_mode diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 45a6f0c807cb..3b3c7b080ad1 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1403,6 +1403,102 @@ port_type_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RW(port_type); +static ssize_t +limit_src_current_active_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t size) +{ + struct typec_port *port = to_typec_port(dev); + int ret; + u8 active; + + if (port->cap->type == TYPEC_PORT_SNK || !port->ops || !port->ops->limit_src_current_set || + !port->cap->pd_revision) { + dev_dbg(dev, "Limiting source current not supported\n"); + return -EOPNOTSUPP; + } + + if (kstrtou8(buf, 0, &active)) + return -EINVAL; + + if (active != 1 && active != 0) + return -EINVAL; + + mutex_lock(&port->limit_src_current_lock); + + if (port->limit_src_current_active == (bool)active) { + ret = size; + goto unlock_and_ret; + } + + ret = port->ops->limit_src_current_set(port, port->limit_src_current_ma, active); + if (ret) + goto unlock_and_ret; + + port->limit_src_current_active = active; + ret = size; + +unlock_and_ret: + mutex_unlock(&port->limit_src_current_lock); + return ret; +} + +static ssize_t +limit_src_current_active_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct typec_port *port = to_typec_port(dev); + + return sysfs_emit(buf, "%d\n", port->limit_src_current_active ? 1 : 0); +} +static DEVICE_ATTR_RW(limit_src_current_active); + +static ssize_t +limit_src_current_ma_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t size) +{ + struct typec_port *port = to_typec_port(dev); + int ret; + u32 src_current_ma; + + if (port->cap->type == TYPEC_PORT_SNK || !port->ops || !port->ops->limit_src_current_set || + !port->cap->pd_revision) { + dev_dbg(dev, "Limiting source current not supported\n"); + return -EOPNOTSUPP; + } + + if (kstrtou32(buf, 0, &src_current_ma)) + return -EINVAL; + + mutex_lock(&port->limit_src_current_lock); + + if (port->limit_src_current_ma == src_current_ma) { + ret = size; + goto unlock_and_ret; + } + + if (port->limit_src_current_active) { + ret = port->ops->limit_src_current_set(port, src_current_ma, + port->limit_src_current_active); + if (ret) + goto unlock_and_ret; + } + + port->limit_src_current_ma = src_current_ma; + ret = size; + +unlock_and_ret: + mutex_unlock(&port->limit_src_current_lock); + return ret; +} + +static ssize_t +limit_src_current_ma_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct typec_port *port = to_typec_port(dev); + + return sysfs_emit(buf, "%u\n", port->limit_src_current_ma); +} +static DEVICE_ATTR_RW(limit_src_current_ma); + static const char * const typec_pwr_opmodes[] = { [TYPEC_PWR_MODE_USB] = "default", [TYPEC_PWR_MODE_1_5A] = "1.5A", @@ -1536,6 +1632,8 @@ static struct attribute *typec_attrs[] = { &dev_attr_vconn_source.attr, &dev_attr_port_type.attr, &dev_attr_orientation.attr, + &dev_attr_limit_src_current_active.attr, + &dev_attr_limit_src_current_ma.attr, NULL, }; @@ -2039,6 +2137,7 @@ struct typec_port *typec_register_port(struct device *parent, ida_init(&port->mode_ids); mutex_init(&port->port_type_lock); + mutex_init(&port->limit_src_current_lock); port->id = id; port->ops = cap->ops; diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h index 0f1bd6d19d67..3856bc058444 100644 --- a/drivers/usb/typec/class.h +++ b/drivers/usb/typec/class.h @@ -54,6 +54,11 @@ struct typec_port { const struct typec_capability *cap; const struct typec_operations *ops; + + /* lock to protect limit_src_current_*_store operation */ + struct mutex limit_src_current_lock; + u32 limit_src_current_ma; + bool limit_src_current_active; }; #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev) diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 7ba45a97eeae..1b1958ae4c16 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -213,6 +213,8 @@ struct typec_partner_desc { * @pr_set: Set Power Role * @vconn_set: Source VCONN * @port_type_set: Set port type + * @limit_src_current_set: Used to limit source current advertisement while + * acting as source */ struct typec_operations { int (*try_role)(struct typec_port *port, int role); @@ -221,6 +223,8 @@ struct typec_operations { int (*vconn_set)(struct typec_port *port, enum typec_role role); int (*port_type_set)(struct typec_port *port, enum typec_port_type type); + int (*limit_src_current_set)(struct typec_port *port, u32 limit_src_current_ma, + bool enable); }; enum usb_pd_svdm_ver { From patchwork Mon Feb 7 04:39:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Badhri Jagan Sridharan X-Patchwork-Id: 12736902 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9A39BC433F5 for ; Mon, 7 Feb 2022 06:14:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239294AbiBGGNU (ORCPT ); Mon, 7 Feb 2022 01:13:20 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48426 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1351844AbiBGEjR (ORCPT ); Sun, 6 Feb 2022 23:39:17 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0F600C043186 for ; Sun, 6 Feb 2022 20:39:16 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id 3-20020a250103000000b0061d99b7d0b8so5342145ybb.13 for ; Sun, 06 Feb 2022 20:39:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=fvjMeqmS7YAThM14Nr6hWHQ1rky2oX1fyAm+Zs01zKU=; b=dX+tFNmXNa6mZWGOfqwibUN9WltV9vHGJJRY5AFwriAxK+mtAijgyVAGd0wre//BOS 049UmTSvQrgxrV6f1Gw26FOXVLXemOunLaJPXkgxBlbrvuJwNo//EYmCh6DAJol6Hlf3 8TTqRMfAQUtoTugTw5TjfCq4L/gc9I9DQCHuHHLVKXMqFmUEExPCnsuyUfQ551ajcY/D 2vGc0XSaOKJSRZGHNGWBPn6UBiHQ1NVjWvUFY6O66yRsFa5MDLZpw0rFZbmc/a6bvKX9 ZZl7yup1nNZGj8jiswbWSaDBNLApFxAepHC8Afl6F6t3EaNw6Or4N8Lp4zQXng+oB9tc lHGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=fvjMeqmS7YAThM14Nr6hWHQ1rky2oX1fyAm+Zs01zKU=; b=CDiRdKLUJUFtbShQBrZD3TB+IJE9W48xYBCVs3CSTd8jX3iUBXEPLhwJLX+M2V3nBv J/hTmednfsUaizqPbDQYRqPHy+1Mg7kuX51lWynPWYbghRRYcZ8ze5OmlUK9URt0DIhW Xoi6RKLciQIg+96odvyWi065FM4t7k8d34MLhiMoGPWvmDkVxzptSC+hT0j3428k3ZsZ kM03Fjwp1LqEDMWTBme8xgb//chGsho+e0TVuQru1fG0figM63aopQ1Pbn/YDsMFYTa/ wCJL55UVrEmSmIMhM9xZMkFlBxBUdNgD9KUn7DRYuS70s04j0dIyWVkEc3UNAU+CaqkB B+Lg== X-Gm-Message-State: AOAM530ZS3U/g5ju745HC4xCn08VU/fFLJQXPX64fAsyMR3qZiQzI1/J HFmKQqV65892NehnoCsve+BMEXbMg5U= X-Google-Smtp-Source: ABdhPJzrC2+QNecsePbDSgJYh4Pk9KseHmfPtb16eAYPVWEGR/YoKcDueljCvUXSLi7OZ9RXXf2u/XBfEeo= X-Received: from badhri.mtv.corp.google.com ([2620:15c:211:201:55d1:a045:9816:1433]) (user=badhri job=sendgmr) by 2002:a25:7d45:: with SMTP id y66mr8077570ybc.376.1644208755165; Sun, 06 Feb 2022 20:39:15 -0800 (PST) Date: Sun, 6 Feb 2022 20:39:07 -0800 In-Reply-To: <20220207043907.2758424-1-badhri@google.com> Message-Id: <20220207043907.2758424-2-badhri@google.com> Mime-Version: 1.0 References: <20220207043907.2758424-1-badhri@google.com> X-Mailer: git-send-email 2.35.0.263.gb82422642f-goog Subject: [PATCH v1 2/2] usb: typec: tcpm: Enable limit_src_current_set callback From: Badhri Jagan Sridharan To: Guenter Roeck , Heikki Krogerus , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Kyle Tso , Badhri Jagan Sridharan Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org This change allows TCPM to support limit_src_current_set callback. When limit_src_current_set is enabled, tcpm updates the local source capabilities to only publish vSafe5V fixed pdo with the current limit passed through limit_src_current_set callback. When limit_src_current_set is disabled, tcpm revert back to publishing the default source caps. This patch is co-authored with kyletso@google.com and also uses some of parts of the code that was reverted by c17c7cf147ac56312156eaaaf8b2e19c9a59a71a. Signed-off-by: Badhri Jagan Sridharan --- drivers/usb/typec/tcpm/tcpm.c | 108 ++++++++++++++++++++++++++++++---- include/linux/usb/tcpm.h | 4 ++ 2 files changed, 102 insertions(+), 10 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 5fce795b69c7..491ad6621671 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -475,6 +475,15 @@ struct tcpm_port { * SNK_READY for non-pd link. */ bool slow_charger_loop; + + /* + * Max current published in vSafe5V fixed pdo when limit_src_current_enabled + * is active. + */ + u32 limit_src_current_ma; + /* True when source current limiting is enabled */ + bool limit_src_current_enabled; + #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -5907,12 +5916,99 @@ static int tcpm_port_type_set(struct typec_port *p, enum typec_port_type type) return 0; } +int tcpm_update_source_capabilities_locked(struct tcpm_port *port) +{ + int ret = 0; + + switch (port->state) { + case SRC_UNATTACHED: + case SRC_ATTACH_WAIT: + case SRC_TRYWAIT: + tcpm_set_cc(port, tcpm_rp_cc(port)); + break; + case SRC_SEND_CAPABILITIES: + case SRC_NEGOTIATE_CAPABILITIES: + case SRC_READY: + case SRC_WAIT_NEW_CAPABILITIES: + port->upcoming_state = SRC_SEND_CAPABILITIES; + ret = tcpm_ams_start(port, POWER_NEGOTIATION); + if (ret == -EAGAIN) { + port->upcoming_state = INVALID_STATE; + break; + } + break; + default: + break; + } + return ret; +} + +static int tcpm_fw_get_src_pdo(struct tcpm_port *port, struct fwnode_handle *fwnode, u32 *src_pdo, + unsigned int *nr_src_pdo) +{ + int ret; + + ret = fwnode_property_count_u32(fwnode, "source-pdos"); + if (ret == 0) + return -EINVAL; + else if (ret < 0) + return ret; + + *nr_src_pdo = min(ret, PDO_MAX_OBJECTS); + ret = fwnode_property_read_u32_array(fwnode, "source-pdos", src_pdo, *nr_src_pdo); + + return ret; +} + +static int tcpm_limit_src_current_set(struct typec_port *p, u32 limit_src_current_ma, bool enable) +{ + struct tcpm_port *port = typec_get_drvdata(p); + int ret = 0; + + mutex_lock(&port->lock); + if (limit_src_current_ma == port->limit_src_current_ma && + enable == port->limit_src_current_enabled) + goto port_unlock; + + ret = tcpm_fw_get_src_pdo(port, port->tcpc->fwnode, port->src_pdo, &port->nr_src_pdo); + if (ret) + return ret; + + if (enable) { + u32 current_vSafe5V_max_current_ma = + ((port->src_pdo[0] & (PDO_CURR_MASK << PDO_FIXED_CURR_SHIFT)) >> + PDO_FIXED_CURR_SHIFT) * 10; + /* + * Check to see if limited source current does not exceed the + * max current published in default source cap. + */ + if (limit_src_current_ma > current_vSafe5V_max_current_ma) { + ret = -EINVAL; + goto port_unlock; + } + port->src_pdo[0] &= ~(PDO_CURR_MASK << PDO_FIXED_CURR_SHIFT); + port->src_pdo[0] |= PDO_FIXED_CURR(limit_src_current_ma); + port->nr_src_pdo = 1; + } + + ret = tcpm_update_source_capabilities_locked(port); + if (!ret) { + port->limit_src_current_ma = limit_src_current_ma; + port->limit_src_current_enabled = enable; + } + +port_unlock: + mutex_unlock(&port->lock); + return ret; +} + static const struct typec_operations tcpm_ops = { .try_role = tcpm_try_role, .dr_set = tcpm_dr_set, .pr_set = tcpm_pr_set, .vconn_set = tcpm_vconn_set, - .port_type_set = tcpm_port_type_set + .port_type_set = tcpm_port_type_set, + .limit_src_current_set = tcpm_limit_src_current_set }; void tcpm_tcpc_reset(struct tcpm_port *port) @@ -5970,15 +6066,7 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, /* Get Source PDOs for the PD port or Source Rp value for the non-PD port */ if (port->pd_supported) { - ret = fwnode_property_count_u32(fwnode, "source-pdos"); - if (ret == 0) - return -EINVAL; - else if (ret < 0) - return ret; - - port->nr_src_pdo = min(ret, PDO_MAX_OBJECTS); - ret = fwnode_property_read_u32_array(fwnode, "source-pdos", - port->src_pdo, port->nr_src_pdo); + ret = tcpm_fw_get_src_pdo(port, fwnode, port->src_pdo, &port->nr_src_pdo); if (ret) return ret; ret = tcpm_validate_caps(port, port->src_pdo, port->nr_src_pdo); diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index bffc8d3e14ad..18372d34a9f4 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -165,5 +165,9 @@ void tcpm_pd_transmit_complete(struct tcpm_port *port, enum tcpm_transmit_status status); void tcpm_pd_hard_reset(struct tcpm_port *port); void tcpm_tcpc_reset(struct tcpm_port *port); +bool tcpm_is_debouncing(struct tcpm_port *tcpm); +bool tcpm_is_toggling(struct tcpm_port *port); +int tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo); #endif /* __LINUX_USB_TCPM_H */