From patchwork Tue Apr 16 02:20:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Baryshkov X-Patchwork-Id: 789748 Received: from mail-lf1-f50.google.com (mail-lf1-f50.google.com [209.85.167.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 988E5101E2 for ; Tue, 16 Apr 2024 02:20:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713234058; cv=none; b=EzlE8PK3eehZdlY3BlbGKRy42v1TB63JO31KkkrkGy+feVQgmqWki+RUf8F8lV72yluNt4G4Zfx0rv32XEOuffX4EByZ1FVytwUM32cHbqclezk3G5RRX1H5/X1CWgcMPQdSg19y5DYuRkIhdP+N7EPVdgBhw+evn5gLKNUeR5w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713234058; c=relaxed/simple; bh=bln5CYO6vQ5ETso/plaD6OhVP1kj5DATMhTuIWkFahI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Rp+AM+/lfr4DkQU1W8SdY7zNQvLGUqMQ47IW2DwH1Zeym1LPLtlu5YPxBdxAD6aIZ6kRUHg0gILigFBny7ybxm0IWOvyHw/r/+oNJUZGSwBnhPwBHfrRXUwrpX8+8W6qUQCS5m4sK5GggMTVJLGufNjav8LZGeHAAysjuuD6cmc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=lqPhXNsJ; arc=none smtp.client-ip=209.85.167.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="lqPhXNsJ" Received: by mail-lf1-f50.google.com with SMTP id 2adb3069b0e04-516d2600569so4885752e87.0 for ; Mon, 15 Apr 2024 19:20:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1713234055; x=1713838855; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=QKVAbmtACrYJY5KlEwDa6TSGBQh+ZqsL8dIuknwyyFk=; b=lqPhXNsJzZgIPDmfmQsopZEEj2yomJLxtE1f8ecdoJZusnbazWm8AsO6sDQ2/Cpd4n /mTnx+C9Rie0FcSSW0/xP0dzo6+ZmaA8YiWrB9B8rSjURhEinPf2rALZ/v1cAH4slYsa tCEehKD7GIB/Jm71urLF6rJfOoVKoHdtyRq7OYVM4wtR4QVXGwKgPuqj3OBXAvrShuKX kb+njDP/Wa566ho3y3dNL2jd/zDum7qIfymZ4dvtG5HpfKD6b77cQwfcRUc+VEmKqpW0 zyaMSU+b+idUVNTWujv80V1Bb8bmLmjlRKSnhN8nMthTUKJN3Cfc89E71RYrfcwiM66q 7qGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713234055; x=1713838855; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=QKVAbmtACrYJY5KlEwDa6TSGBQh+ZqsL8dIuknwyyFk=; b=olcrh4+iXSW+CQ8ge7BNjYOloNXRdI9WLv6pF6aq13BLmQRyn0psu9Hr13D+o1/UBQ dSVy+iBODZSFxfXFb6brMQdybRJwXQ2Y+ny49g9NT1YEHsiIR87ZF9sbjmgjCLCiqd04 TcnoQdDxmxYfOO66TaF01QTognxwycAMHjk1tOr/IAqFpXeqPEbOcI+oqF80kWVenITw Lc1h3DEk/5soSE8gi6guW5eQxxUsMCNf+v2RVpaLD93Mbr+T5jaPMohGRh4GOZG1r8Gq 6aQNrKt/Kcqp5znrRVI77pXTaXQsnd8GoE24nKU50rbL/OuFBgp++VmJAVPWji3fQ/KN K6MQ== X-Gm-Message-State: AOJu0YxJijW2tjFCHdvyUaBZGv1tZBSlbnqE2mVl6MEYR9njGLGqYC6N //ACIVOPcTWO/lQqBB7fvxs8W7Cwpqdpo/NqJMLhdgwmG0AMVYmtNhsJVqW7iGTtyP2HWZ/YoIk 7 X-Google-Smtp-Source: AGHT+IGR5VnTd32ybFeiDtNMe8qSkLBT4/ZrvIZEN4DmOvOuPHsnWIwOElz8WWzbN+5Zgycqafp5BA== X-Received: by 2002:a05:6512:4805:b0:518:b58c:5234 with SMTP id eo5-20020a056512480500b00518b58c5234mr4839392lfb.33.1713234054894; Mon, 15 Apr 2024 19:20:54 -0700 (PDT) Received: from umbar.lan ([192.130.178.91]) by smtp.gmail.com with ESMTPSA id bi30-20020a0565120e9e00b0051925dd92dbsm27716lfb.214.2024.04.15.19.20.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 15 Apr 2024 19:20:54 -0700 (PDT) From: Dmitry Baryshkov Date: Tue, 16 Apr 2024 05:20:51 +0300 Subject: [PATCH 2/8] usb: typec: altmode: add low level altmode configuration helper Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240416-ucsi-glink-altmode-v1-2-890db00877ac@linaro.org> References: <20240416-ucsi-glink-altmode-v1-0-890db00877ac@linaro.org> In-Reply-To: <20240416-ucsi-glink-altmode-v1-0-890db00877ac@linaro.org> To: Heikki Krogerus , Greg Kroah-Hartman , Neil Armstrong , Bjorn Andersson , Konrad Dybcio Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Dmitry Baryshkov X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2826; i=dmitry.baryshkov@linaro.org; h=from:subject:message-id; bh=bln5CYO6vQ5ETso/plaD6OhVP1kj5DATMhTuIWkFahI=; b=owEBbQGS/pANAwAKAYs8ij4CKSjVAcsmYgBmHeCDGotF2VpWSvjEJ9OObzVO7tPWMtSWgBLNT CUCFBuOzTeJATMEAAEKAB0WIQRMcISVXLJjVvC4lX+LPIo+Aiko1QUCZh3ggwAKCRCLPIo+Aiko 1YkQB/98zz+RFNpPS3kNEKTEDiKdaBjW5F40eDsVbpJ0BztJwrgCv7nmgpQiJe3pSHIsSR6iIyh SJVB7fCdSNnkF44FJQdSi1czYeQH8ay3wrrHnJh4TsbSLIb2XyPgRWP4sgKyO7z18hrNAv/Zzeg g5EKCO+UamTC9JK7v88KXus/FHSZJkg32MbUIVB567gF3nwPK6luS+3pYw51fkwmOAtQpLppReT wvrwJ1Ozomg3m7wj8xWdEX2mdWI9EArUgdrhLeMSQ7pMx6+vT3gh2GHRUfaiVXWsaThfOJhXLds L6wOmPbenqXkj8+6Vu3ipbos8GQ+PLsJ6J9gBVhLGXE6Ekld X-Developer-Key: i=dmitry.baryshkov@linaro.org; a=openpgp; fpr=8F88381DD5C873E4AE487DA5199BF1243632046A In some obscure cases (Qualcomm PMIC Glink) altmode is completely handled by the firmware. Linux does not get proper partner altmode info. Instead we get the notification once the altmode is negotiated and entered (or left). However even in such a case the driver has to switch board components (muxes, switches and retimers) according to the altmode selected by the hardware. We can not use existing typec_altmode_enter() / typec_altmode_exit() / typec_altmode_notify() functions in such a case, since there is no corresponding partner's altmode instance. Signed-off-by: Dmitry Baryshkov --- drivers/usb/typec/bus.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/usb/typec_altmode.h | 3 +++ 2 files changed, 37 insertions(+) diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index 6ea103e1abae..68f3908401c6 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -67,6 +67,40 @@ static int typec_altmode_set_state(struct typec_altmode *adev, return typec_altmode_set_switches(port_altmode, conf, data); } +/** + * typec_altmode_set_port - set the altmode configuration + * @conf: Alternate mode specific configuration value + * @dVata: Alternate mode specific data + * + * This function allows configuring muxes and retimer for the selected altmode. + * This function may only be used by the special case drivers, that handle + * the altmode negotiation by the alternative means and thus have no + * corresponding typec_altmode instance for the parnter. + */ +int typec_altmode_set_port(struct typec_altmode *adev, + unsigned long conf, void *data) +{ + bool is_port; + struct altmode *altmode; + int ret; + + if (!adev) + return 0; + + altmode = to_altmode(adev); + is_port = is_typec_port(adev->dev.parent); + + if (altmode->partner || !is_port) + return -EINVAL; + + ret = typec_altmode_set_switches(altmode, conf, data); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(typec_altmode_set_port); + /* -------------------------------------------------------------------------- */ /* Common API */ diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index b3c0866ea70f..d78a9618bedf 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -77,6 +77,9 @@ int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf, const struct typec_altmode * typec_altmode_get_partner(struct typec_altmode *altmode); +int typec_altmode_set_port(struct typec_altmode *altmode, unsigned long conf, + void *data); + /** * struct typec_cable_ops - Cable alternate mode operations vector * @enter: Operations to be executed with Enter Mode Command From patchwork Tue Apr 16 02:20:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Baryshkov X-Patchwork-Id: 789747 Received: from mail-lf1-f41.google.com (mail-lf1-f41.google.com [209.85.167.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A611314A8F for ; Tue, 16 Apr 2024 02:20:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713234060; cv=none; b=Zv+2Fc7VZFMAgUE6Ricq0WFSWDYbzq/Q8u3MwlLZrPGij/RYc5oPOdBrbaaiV66AdR4309fbmaiLSjTbzXv/d+RCCXcxkmGDk7Ti82Csk0XPGPutmu020aty7PGT+yP+lEKbjpgNDfEXPDUowKrfIS+3L5j3nLl1wJh/eUQA3CA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713234060; c=relaxed/simple; bh=AvWzBq0+Nx+u5rcERgaRzwmrpy+PRkTWudggCntMNQI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=qfZ8piTggSQ8eRyVrPZSqHgKj8K190jhzy8lCoHf9cgNX/fMqrY723tParxpMn9qOPOOCMU2hr2bhquBdygJun9Yo+IUvAy8Hvmwl28uom/g0IhZr8YZVqwkfW+45G9FxDhGd2OGfPDZ7pSikzZLGvxvTVbXn3ERcy3sJ/BXNl8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=CPn8kpPc; arc=none smtp.client-ip=209.85.167.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="CPn8kpPc" Received: by mail-lf1-f41.google.com with SMTP id 2adb3069b0e04-516ef30b16eso4564029e87.3 for ; Mon, 15 Apr 2024 19:20:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1713234057; x=1713838857; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=FmQc/tP4sqYRlf+bPIXpAY4Oec6ZydToP6BQl/vWpTI=; b=CPn8kpPcYrRDxRd+itb8AoNmNkz/0qxxD0SjdfSBOEUSxreTBz3a0eu3dgyYPlP9oW uyL6D+59SrieQHCzeljJPLRvOl5eLeIGYy+WdlhV5ihmGOP9wbRWkrd2pVLIddUsr/vV uuE7f90dqy48z/ZT/UifXBC/9fo5KahOEsA9qOI7yPBP1DpzDpVO8NrDIBo3UUqkdpiy CwlhyyOUmdPWh18eymzEjJ82dQVTNWQRFWv9+V9G5stga0ilDqfzkkwrNFnxVOUiqgnq 2LQ1zcUerMabvdQr+lultXjeALTdK31Eu2vuoxqvVMt6NIDjw1x4D5hAOFSgtsystqxO SwcQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713234057; x=1713838857; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=FmQc/tP4sqYRlf+bPIXpAY4Oec6ZydToP6BQl/vWpTI=; b=DFqO+Ejt73QMNFqqfEEMLIYNQGcDSS76MAUJlz5GxuEhS5Gq7yW9T3yyCZecTJJvjc jTS4AFkb//hGuFLi8M5k35Q5eI29KwnDhdBj4I0qH0dmBQ9l+exIipwpsO4yVrhhQKR7 3h5eklV+srP34nwclwf8UmwEkXb5I4t/Y+d0uUr7fG2UyYeKeEqGoC2jxOHnDTmOQLzE q3/+Jmg/gebZ6ZiIL/WX6a/0qZ0b371+XAJjal5B6+Tp3bbHyLAg/UAz2iCNi/LoWe++ kRR8AzcqpkBtAnkz/hP0KkTmxkiB71IKijrr9cr9XrvS74eSLcrG9OT7ZbKv06ivPVN0 1cEg== X-Gm-Message-State: AOJu0YxBBjdRnIDluKdT9diQS4Ds3O9zj6305UyuzLvAWC3YtQcaqL/2 n8ixM4gyZ6peZCshXLJNNgsSa494jMBhlCt+MgAKZXc812bTkiPqApJHCrO8glM= X-Google-Smtp-Source: AGHT+IETOuKLKc04tqgGWAuHh3sieieMjw48KKxtIuyneyvtzCAA1gzcT/OKN/HP7Nz5FhFgSAUGmg== X-Received: by 2002:a05:6512:2506:b0:518:9ab5:a987 with SMTP id be6-20020a056512250600b005189ab5a987mr7455064lfb.18.1713234056966; Mon, 15 Apr 2024 19:20:56 -0700 (PDT) Received: from umbar.lan ([192.130.178.91]) by smtp.gmail.com with ESMTPSA id bi30-20020a0565120e9e00b0051925dd92dbsm27716lfb.214.2024.04.15.19.20.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 15 Apr 2024 19:20:56 -0700 (PDT) From: Dmitry Baryshkov Date: Tue, 16 Apr 2024 05:20:53 +0300 Subject: [PATCH 4/8] usb: typec: ucsi: glink: use le32 for message data Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240416-ucsi-glink-altmode-v1-4-890db00877ac@linaro.org> References: <20240416-ucsi-glink-altmode-v1-0-890db00877ac@linaro.org> In-Reply-To: <20240416-ucsi-glink-altmode-v1-0-890db00877ac@linaro.org> To: Heikki Krogerus , Greg Kroah-Hartman , Neil Armstrong , Bjorn Andersson , Konrad Dybcio Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Dmitry Baryshkov X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1651; i=dmitry.baryshkov@linaro.org; h=from:subject:message-id; bh=AvWzBq0+Nx+u5rcERgaRzwmrpy+PRkTWudggCntMNQI=; b=owEBbQGS/pANAwAKAYs8ij4CKSjVAcsmYgBmHeCENu1BW+Smad9t6wMjMLuof3RywYFmVx90u eqnm9pRNi2JATMEAAEKAB0WIQRMcISVXLJjVvC4lX+LPIo+Aiko1QUCZh3ghAAKCRCLPIo+Aiko 1SH0B/0bf6Uf94w1RJgq/zOccS0iRZrF5QtsXGrxUqPNTbrxjvk8QpBMsCkiXPy4emoHaLAKFkU njuZOawG7lC5VLRn2JX6ROCiwe3T0rtXc1dauChQ92q3opEf06dRF1gkvr8BzXMp+B3Wqz5Trsu NEgDZ3WfN3e2ztot12Y5S9JXeMxkx4Ro/+WGngmiymfkDZYbu6t1JqTjO1mLIzHUPn2t7SSYGBz 3dIiW0STD7jRf4S6hiGbNSk9ZtPfbtWDSQxie/zmLaKv34+wz8JU7F5oxaaUwLCAiaExgzuiUrS 7CPNFXSBtHK280wKdRhRtKlh0G51aybIXaYujkl+eUadWek0 X-Developer-Key: i=dmitry.baryshkov@linaro.org; a=openpgp; fpr=8F88381DD5C873E4AE487DA5199BF1243632046A The message structures as transferred by the PMIC_GLINK use le32 for data encoding. Correct struct accessors to follow the lead of the main pmic_glink.c driver. Fixes: 62b5412b1f4a ("usb: typec: ucsi: add PMIC Glink UCSI driver") Signed-off-by: Dmitry Baryshkov Reviewed-by: Neil Armstrong Reviewed-by: Heikki Krogerus --- drivers/usb/typec/ucsi/ucsi_glink.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 6be9d89d4a28..d029cc9d82e3 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -33,7 +33,7 @@ struct ucsi_read_buf_req_msg { struct ucsi_read_buf_resp_msg { struct pmic_glink_hdr hdr; u8 buf[UCSI_BUF_SIZE]; - u32 ret_code; + __le32 ret_code; }; struct ucsi_write_buf_req_msg { @@ -44,13 +44,13 @@ struct ucsi_write_buf_req_msg { struct ucsi_write_buf_resp_msg { struct pmic_glink_hdr hdr; - u32 ret_code; + __le32 ret_code; }; struct ucsi_notify_ind_msg { struct pmic_glink_hdr hdr; - u32 notification; - u32 receiver; + __le32 notification; + __le32 receiver; u32 reserved; }; @@ -255,7 +255,7 @@ static void pmic_glink_ucsi_write_ack(struct pmic_glink_ucsi *ucsi, const void * if (resp->ret_code) return; - ucsi->sync_val = resp->ret_code; + ucsi->sync_val = le32_to_cpu(resp->ret_code); complete(&ucsi->write_ack); } From patchwork Tue Apr 16 02:20:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Baryshkov X-Patchwork-Id: 789746 Received: from mail-lj1-f177.google.com (mail-lj1-f177.google.com [209.85.208.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4C8A8210EC for ; Tue, 16 Apr 2024 02:21:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713234062; cv=none; b=gfTgEjPaaC2Qs0zZBD8KeTKjfKzfNaEbnOhUBHq5JITu2ef8GMbtqLSQ/r+QSn6CZriZtn11RNUf3U3TUTUquJV1zHi4+limKbVSO/SiSdnE6Cj6YraPB+Geh5XhlHajamUk8mwIvmj/xjCcXQivCiVRi1Zw/Aovjuiq+yG9/S8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713234062; c=relaxed/simple; bh=SToAHymtfQGBKZLpbR0Oic69HFLPWamTvWh5j26sXtY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=VZIGTVaFMxf6EXWqzyn96PGC6cYpD2LW+TOCXyN2NcKOMNyEjHRAqW2gsce6n/H1nGnYI5nflSjWITomftFz3gPxFnv/BiVAg28EA3f3bxKSNkAu6m9SATzkFoW6ZeaWquy3881HBNL041nQcmHhSuvJTy19e1FBkCfsVsj8x48= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=x+/HNUs8; arc=none smtp.client-ip=209.85.208.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="x+/HNUs8" Received: by mail-lj1-f177.google.com with SMTP id 38308e7fff4ca-2da88833109so22605441fa.0 for ; Mon, 15 Apr 2024 19:21:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1713234058; x=1713838858; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=WU7kkHqmeD8WLGIOtmqDI6a+4uwK8HkBVPKBTEJy0EU=; b=x+/HNUs8dqMsD5di6b2GsS+M2svZJIXiS8gmeHeEw3+FPOrOy6Z0jjEdxiukxkTkYp p7XpH+tDD+3rJ1YDAdCGJsZPG0IMXTIguKrmr6xz6AR9LU3ikW0Knwo/IqFwL4OLqIj0 h9J+4IfzuMS/0bSl/ycr3OYQnA/9eFKWRUE0K+nZt1kNYjCM9a5t7jKqTDl2gNUyDsbU jF0YbDgeqUBLftO0fT2Te5P7PLUWvaNJz0Uv1GrQlVvyoGK/kAhHUQTdxqro4VV/whZU nbApmUj/WeyBVVRlYaIfuNyJlnbjsHTO6PDS+iCDUYm8xt0tAzgMsCLG48BV8w6x7nuX jlzQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713234058; x=1713838858; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=WU7kkHqmeD8WLGIOtmqDI6a+4uwK8HkBVPKBTEJy0EU=; b=KUlkULtavVy3xuiXmXfZsWjvULrM8fWPe3n7fR3Brrb7ky1WstYlLPc1KnFvuuSpxZ a3ZkezAmXRfGLIVP8eixGOTS/XLMUmgJIxYsvgqgx3rq5P+dypAMKO5B9dkULvh4ctNt uz6Ae+XeiUmdjg2CY0TGOgS9vyfABuEjTxnYktxIbtLRHPrI7e2Q/HCPEVrJxcY+aacL m6wbgLHcJCYTEej3EG0IAZ+lgGqHOz4GhEYWEAZPMv8nbylnI843FdAJ1K3BwfjqpXgc zQllVi8c+XcfU2DLYn/atuN75kbgIDPm6anibNbRskpGLfB6jSl0bJ85HUoNfsd6sk55 wqpA== X-Gm-Message-State: AOJu0YxKvd+MnM5Xb/hil1oeSy1pDmupOdYXqdnTq0Mt2OvhHVcYrs9i OKUPu07mja6sy/Y8Rr+GO1uvjWaWb2qAzbvp143ACs3YF67Sj2Bihvdw4GoNvPg= X-Google-Smtp-Source: AGHT+IHOVwyxCsFGxzMB7VCkX79Ri++G8vkHqYJkw7PDN/FaNvbEMg6uvmiAAxAardy8Z0daJuJWcQ== X-Received: by 2002:ac2:5931:0:b0:515:99f6:2ca4 with SMTP id v17-20020ac25931000000b0051599f62ca4mr7434912lfi.36.1713234058533; Mon, 15 Apr 2024 19:20:58 -0700 (PDT) Received: from umbar.lan ([192.130.178.91]) by smtp.gmail.com with ESMTPSA id bi30-20020a0565120e9e00b0051925dd92dbsm27716lfb.214.2024.04.15.19.20.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 15 Apr 2024 19:20:58 -0700 (PDT) From: Dmitry Baryshkov Date: Tue, 16 Apr 2024 05:20:55 +0300 Subject: [PATCH 6/8] usb: typec: ucsi: add ucsi_registered() callback Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240416-ucsi-glink-altmode-v1-6-890db00877ac@linaro.org> References: <20240416-ucsi-glink-altmode-v1-0-890db00877ac@linaro.org> In-Reply-To: <20240416-ucsi-glink-altmode-v1-0-890db00877ac@linaro.org> To: Heikki Krogerus , Greg Kroah-Hartman , Neil Armstrong , Bjorn Andersson , Konrad Dybcio Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Dmitry Baryshkov X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1914; i=dmitry.baryshkov@linaro.org; h=from:subject:message-id; bh=SToAHymtfQGBKZLpbR0Oic69HFLPWamTvWh5j26sXtY=; b=owEBbQGS/pANAwAKAYs8ij4CKSjVAcsmYgBmHeCEZyNdqdYdy5k8fktQr8KpTHz9ht3CfWxcR qepO/zIpF6JATMEAAEKAB0WIQRMcISVXLJjVvC4lX+LPIo+Aiko1QUCZh3ghAAKCRCLPIo+Aiko 1ZrzB/0ZMtTNXuCJ9qcQq7KFdJhCrQYMdE0BtbgFMtTDhBe7lE42wRn06wEkhA43KUWPV6YgyV8 E0Ov94SXw2ICzurfz+SU3dkMOIMiIBcDdWw6eUcHL2TbrKs43knPvLabUVty+8z55DIH0OtK/bX cpLQrX+TWz29PzjS+Cg4qWtLqZInpCRmAZhgQLKWMtU0/5yJMViLXok0OlHg8NQBAvRcxtWngB1 eV74DpLPKHikHjbUdMZjKGBEQ39pwslBTFyScRt6FxKZ0meiZt87PeQnmaev7LT2VKyaMQ1AJHf POyvzEWkd5XEcFYrzRTudraH9xZXQW3PUgVojHbLLGm6TG5O X-Developer-Key: i=dmitry.baryshkov@linaro.org; a=openpgp; fpr=8F88381DD5C873E4AE487DA5199BF1243632046A As the registration of the UCSI device is performed from the scheduled worker, the glue driver isn't notified when the UCSI registration succeeds. The ucsi_glink driver needs this event to be able to manually register DisplayPort altmodes. Signed-off-by: Dmitry Baryshkov --- drivers/usb/typec/ucsi/ucsi.c | 3 +++ drivers/usb/typec/ucsi/ucsi.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index cb52e7b0a2c5..ae89c4c8341d 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1755,6 +1755,9 @@ static int ucsi_init(struct ucsi *ucsi) if (UCSI_CCI_CONNECTOR(cci)) ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci)); + if (ucsi->ops->ucsi_registered) + ucsi->ops->ucsi_registered(ucsi); + return 0; err_unregister: diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index c4d103db9d0f..37ee1b1d8c31 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -62,6 +62,7 @@ struct dentry; * @update_altmodes: Squashes duplicate DP altmodes * @update_connector: Update connector capabilities before registering * @connector_status: Updates connector status, called holding connector lock + * @ucsi_registered: notify host driver when the UCSI interface is registered * * Read and write routines for UCSI interface. @sync_write must wait for the * Command Completion Event from the PPM before returning, and @async_write must @@ -78,6 +79,7 @@ struct ucsi_operations { struct ucsi_altmode *updated); void (*update_connector)(struct ucsi_connector *con); void (*connector_status)(struct ucsi_connector *con); + void (*ucsi_registered)(struct ucsi *ucsi); }; struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops); From patchwork Tue Apr 16 02:20:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Baryshkov X-Patchwork-Id: 789745 Received: from mail-lf1-f51.google.com (mail-lf1-f51.google.com [209.85.167.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 32FBC374EB for ; Tue, 16 Apr 2024 02:21:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713234065; cv=none; b=oVoMtXXox1xVV/JgSt35uiWqDGE75RFVqQCDC+ORs4DvG8BUVgOhFioSOZJ1SOjncZtEA+m1Q9QAOWRcVep2xLJztIkUJu7/6RIQQKUYUuvPa7pFzsuS6BR5yspRH010YCJ7+DbwJTwcNaQPWtmxziT4b+brv2k7T1/cXZgRIh4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713234065; c=relaxed/simple; bh=yJeAlayWU742uIOS1+Qex7ZD6Uw85hAJ5t34KTJ2nEg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=GLwNNLAdaN2o3My4Zvvjn7iWR9/BgOPa16kHNaOqETV5+RVh21K9t2pU9p9bqYMANfS2KPylXdha3RqZxBTzsHikxUf5qnwZcve1ty8XNcY4ybptLYSYxiSExUfHPmt59OtUg3NYzndJlsFhr9ETeGL5AQJUdQb6z02ycgjaGjY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=cxF/vkhv; arc=none smtp.client-ip=209.85.167.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="cxF/vkhv" Received: by mail-lf1-f51.google.com with SMTP id 2adb3069b0e04-516d264d0e4so4914929e87.0 for ; Mon, 15 Apr 2024 19:21:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1713234059; x=1713838859; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=QlPc7pTPuON7kRU6W7J6GqxYpvFAb8d0DJbeeg0HRuY=; b=cxF/vkhvb9Uy83yQNGeYYf0gf0+Qthb39fy6u+MurPGoWL/9CRKITBhPi1wKJijnvK a8eBbJfN8GOeWARKYhVO6Mhl444SjDFYeDLOSMPDiG0odVfL/Xj9LCWa95ghVCG5e5/U DqjBd/A/2G8VJO4yaOtBntK9Sf+JT4y7igkAQ6GW8PwL8VmQ5WuRveib6QZRDe1iNzKh BRM25y920toD1qxT841syYKpxy2KQ7nLsoenycJBa6JeIUVgHfFbJXrClUCq1pJZwhuB 6mN96Ocj0OZGggyrVXvUqbAA6fFJuh9uZRmx1jKSRLhBty0uyo7uXoPnvZnM/5Ly5Zmn 235w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713234059; x=1713838859; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=QlPc7pTPuON7kRU6W7J6GqxYpvFAb8d0DJbeeg0HRuY=; b=NgHdQB0w/6pPtZLAUBL/72sKKKgeQlLziv12LEnwU0HTKL/vq7fiCn5tYAiXDRQHaC JNs9/UlkF1A3uorgwYNlLIrbsebYDBnAjwg2LaK5GUZ6pBMnbRFucFNYa9ZMEr6AI/uL 8c2ut7RMjZPqfUZvrErM4TOVR2OkhYJf4rQ7vftM0Jc+wyRkj4bbpDLrdSE0C0hOVSlJ BFxj2dR+7xrdfhdOOAzA2Mum1NDxYx+1jTEnU7J8IgYJvWjEgdtO1m5Lq2FWQefBHSl3 8+M9FbJPVdxo5Rvy9+7T6IIEdPZZy3p+yX9LUu/toEXd5eKWxQ2a5OmRO8VnYduB+0Ge BRRA== X-Gm-Message-State: AOJu0YxCXq3yM3i/YEtBfnPqKzN2nxW8bPzIuRHP9GgYEw1/Ywv/hnwq PhxkGf8JnT8HcTMjItXcvYNVesdDenfeagIb+McP4nH5mxvqcye2Z5Kec4NF9uE= X-Google-Smtp-Source: AGHT+IG0Aujt5pjTKjVXbznsH4RV+YvZWYPpGXrdwYlIVufe4pyhYrT2mp9wYcK7ESwfTUVltzw3Og== X-Received: by 2002:a05:6512:e89:b0:513:dcd2:1267 with SMTP id bi9-20020a0565120e8900b00513dcd21267mr514554lfb.23.1713234059396; Mon, 15 Apr 2024 19:20:59 -0700 (PDT) Received: from umbar.lan ([192.130.178.91]) by smtp.gmail.com with ESMTPSA id bi30-20020a0565120e9e00b0051925dd92dbsm27716lfb.214.2024.04.15.19.20.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 15 Apr 2024 19:20:58 -0700 (PDT) From: Dmitry Baryshkov Date: Tue, 16 Apr 2024 05:20:56 +0300 Subject: [PATCH 7/8] usb: typec: ucsi: glink: merge pmic_glink_altmode driver Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240416-ucsi-glink-altmode-v1-7-890db00877ac@linaro.org> References: <20240416-ucsi-glink-altmode-v1-0-890db00877ac@linaro.org> In-Reply-To: <20240416-ucsi-glink-altmode-v1-0-890db00877ac@linaro.org> To: Heikki Krogerus , Greg Kroah-Hartman , Neil Armstrong , Bjorn Andersson , Konrad Dybcio Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, Dmitry Baryshkov X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=35052; i=dmitry.baryshkov@linaro.org; h=from:subject:message-id; bh=yJeAlayWU742uIOS1+Qex7ZD6Uw85hAJ5t34KTJ2nEg=; b=owEBbQGS/pANAwAKAYs8ij4CKSjVAcsmYgBmHeCE3B4iMkJyiM/yo7kmaZCzCplGuOi0k9WM2 qFB7JxQki2JATMEAAEKAB0WIQRMcISVXLJjVvC4lX+LPIo+Aiko1QUCZh3ghAAKCRCLPIo+Aiko 1aiOCACUcftTPMFb/QxgX7eDi2FbhXEzbA1ypRl4gUtnEQ5L6A/AXxuLGQNt+bFkR5/XwTBTjQF dURvwhOdSdcJU5k6NdV+Tg9xuTP4AqnSwppUw5WwHCYAkuGgHaK+eYfaQyNx099tEcZKP5Yvuhj 2yTP0HeC7ce9TJYxzJdwmLbFasPqA+Q2HCbtP/cVQ93m5En6g71Z2jw3KEaPMwErpfTSfmg9GpF qwt+Ramnz/Pvep4ehba8O+zpDuMix78wYsPW3BS0tfCKrV1CVnOiK7Wz/wHIUTDAXSnCzo1EmMk LRPgkw/PF/at3N7ezhrEMPNOcotZF18L2qq+2q02f3OJT/m9 X-Developer-Key: i=dmitry.baryshkov@linaro.org; a=openpgp; fpr=8F88381DD5C873E4AE487DA5199BF1243632046A Move handling of USB Altmode to the ucsi_glink driver. This way the altmode is properly registered in the Type-C framework, the altmode handlers can use generic typec calls, the UCSI driver can use orientation information from altmode messages and vice versa, the altmode handlers can use GPIO-based orientation inormation from UCSI GLINK driver. Signed-off-by: Dmitry Baryshkov --- drivers/soc/qcom/Makefile | 1 - drivers/soc/qcom/pmic_glink_altmode.c | 546 ---------------------------------- drivers/usb/typec/ucsi/ucsi_glink.c | 495 ++++++++++++++++++++++++++++-- 3 files changed, 475 insertions(+), 567 deletions(-) diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index ca0bece0dfff..d43d2b444634 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_OCMEM) += ocmem.o obj-$(CONFIG_QCOM_PDR_HELPERS) += pdr_interface.o obj-$(CONFIG_QCOM_PMIC_GLINK) += pmic_glink.o -obj-$(CONFIG_QCOM_PMIC_GLINK) += pmic_glink_altmode.o obj-$(CONFIG_QCOM_PMIC_PDCHARGER_ULOG) += pmic_pdcharger_ulog.o CFLAGS_pmic_pdcharger_ulog.o := -I$(src) obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c deleted file mode 100644 index b3808fc24c69..000000000000 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ /dev/null @@ -1,546 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. - * Copyright (c) 2022, Linaro Ltd - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#define PMIC_GLINK_MAX_PORTS 2 - -#define USBC_SC8180X_NOTIFY_IND 0x13 -#define USBC_CMD_WRITE_REQ 0x15 -#define USBC_NOTIFY_IND 0x16 - -#define ALTMODE_PAN_EN 0x10 -#define ALTMODE_PAN_ACK 0x11 - -struct usbc_write_req { - struct pmic_glink_hdr hdr; - __le32 cmd; - __le32 arg; - __le32 reserved; -}; - -#define NOTIFY_PAYLOAD_SIZE 16 -struct usbc_notify { - struct pmic_glink_hdr hdr; - char payload[NOTIFY_PAYLOAD_SIZE]; - u32 reserved; -}; - -struct usbc_sc8180x_notify { - struct pmic_glink_hdr hdr; - __le32 notification; - __le32 reserved[2]; -}; - -enum pmic_glink_altmode_pin_assignment { - DPAM_HPD_OUT, - DPAM_HPD_A, - DPAM_HPD_B, - DPAM_HPD_C, - DPAM_HPD_D, - DPAM_HPD_E, - DPAM_HPD_F, -}; - -struct pmic_glink_altmode; - -#define work_to_altmode_port(w) container_of((w), struct pmic_glink_altmode_port, work) - -struct pmic_glink_altmode_port { - struct pmic_glink_altmode *altmode; - unsigned int index; - - struct typec_switch *typec_switch; - struct typec_mux *typec_mux; - struct typec_mux_state state; - struct typec_retimer *typec_retimer; - struct typec_retimer_state retimer_state; - struct typec_altmode dp_alt; - - struct work_struct work; - - struct auxiliary_device *bridge; - - enum typec_orientation orientation; - u16 svid; - u8 dp_data; - u8 mode; - u8 hpd_state; - u8 hpd_irq; -}; - -#define work_to_altmode(w) container_of((w), struct pmic_glink_altmode, enable_work) - -struct pmic_glink_altmode { - struct device *dev; - - unsigned int owner_id; - - /* To synchronize WRITE_REQ acks */ - struct mutex lock; - - struct completion pan_ack; - struct pmic_glink_client *client; - - struct work_struct enable_work; - - struct pmic_glink_altmode_port ports[PMIC_GLINK_MAX_PORTS]; -}; - -static int pmic_glink_altmode_request(struct pmic_glink_altmode *altmode, u32 cmd, u32 arg) -{ - struct usbc_write_req req = {}; - unsigned long left; - int ret; - - /* - * The USBC_CMD_WRITE_REQ ack doesn't identify the request, so wait for - * one ack at a time. - */ - mutex_lock(&altmode->lock); - - req.hdr.owner = cpu_to_le32(altmode->owner_id); - req.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP); - req.hdr.opcode = cpu_to_le32(USBC_CMD_WRITE_REQ); - req.cmd = cpu_to_le32(cmd); - req.arg = cpu_to_le32(arg); - - ret = pmic_glink_send(altmode->client, &req, sizeof(req)); - if (ret) { - dev_err(altmode->dev, "failed to send altmode request: %#x (%d)\n", cmd, ret); - goto out_unlock; - } - - left = wait_for_completion_timeout(&altmode->pan_ack, 5 * HZ); - if (!left) { - dev_err(altmode->dev, "timeout waiting for altmode request ack for: %#x\n", cmd); - ret = -ETIMEDOUT; - } - -out_unlock: - mutex_unlock(&altmode->lock); - return ret; -} - -static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode, - struct pmic_glink_altmode_port *port, - u8 mode, bool hpd_state, - bool hpd_irq) -{ - struct typec_displayport_data dp_data = {}; - int ret; - - dp_data.status = DP_STATUS_ENABLED; - if (hpd_state) - dp_data.status |= DP_STATUS_HPD_STATE; - if (hpd_irq) - dp_data.status |= DP_STATUS_IRQ_HPD; - dp_data.conf = DP_CONF_SET_PIN_ASSIGN(mode); - - port->state.alt = &port->dp_alt; - port->state.data = &dp_data; - port->state.mode = TYPEC_MODAL_STATE(mode); - - ret = typec_mux_set(port->typec_mux, &port->state); - if (ret) - dev_err(altmode->dev, "failed to switch mux to DP: %d\n", ret); - - port->retimer_state.alt = &port->dp_alt; - port->retimer_state.data = &dp_data; - port->retimer_state.mode = TYPEC_MODAL_STATE(mode); - - ret = typec_retimer_set(port->typec_retimer, &port->retimer_state); - if (ret) - dev_err(altmode->dev, "failed to setup retimer to DP: %d\n", ret); -} - -static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode, - struct pmic_glink_altmode_port *port) -{ - int ret; - - port->state.alt = NULL; - port->state.data = NULL; - port->state.mode = TYPEC_STATE_USB; - - ret = typec_mux_set(port->typec_mux, &port->state); - if (ret) - dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret); - - port->retimer_state.alt = NULL; - port->retimer_state.data = NULL; - port->retimer_state.mode = TYPEC_STATE_USB; - - ret = typec_retimer_set(port->typec_retimer, &port->retimer_state); - if (ret) - dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret); -} - -static void pmic_glink_altmode_safe(struct pmic_glink_altmode *altmode, - struct pmic_glink_altmode_port *port) -{ - int ret; - - port->state.alt = NULL; - port->state.data = NULL; - port->state.mode = TYPEC_STATE_SAFE; - - ret = typec_mux_set(port->typec_mux, &port->state); - if (ret) - dev_err(altmode->dev, "failed to switch mux to safe mode: %d\n", ret); - - port->retimer_state.alt = NULL; - port->retimer_state.data = NULL; - port->retimer_state.mode = TYPEC_STATE_SAFE; - - ret = typec_retimer_set(port->typec_retimer, &port->retimer_state); - if (ret) - dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret); -} - -static void pmic_glink_altmode_worker(struct work_struct *work) -{ - struct pmic_glink_altmode_port *alt_port = work_to_altmode_port(work); - struct pmic_glink_altmode *altmode = alt_port->altmode; - - typec_switch_set(alt_port->typec_switch, alt_port->orientation); - - if (alt_port->svid == USB_TYPEC_DP_SID && alt_port->mode == 0xff) - pmic_glink_altmode_safe(altmode, alt_port); - else if (alt_port->svid == USB_TYPEC_DP_SID) - pmic_glink_altmode_enable_dp(altmode, alt_port, alt_port->mode, - alt_port->hpd_state, alt_port->hpd_irq); - else - pmic_glink_altmode_enable_usb(altmode, alt_port); - - drm_aux_hpd_bridge_notify(&alt_port->bridge->dev, - alt_port->hpd_state ? - connector_status_connected : - connector_status_disconnected); - - pmic_glink_altmode_request(altmode, ALTMODE_PAN_ACK, alt_port->index); -} - -static enum typec_orientation pmic_glink_altmode_orientation(unsigned int orientation) -{ - if (orientation == 0) - return TYPEC_ORIENTATION_NORMAL; - else if (orientation == 1) - return TYPEC_ORIENTATION_REVERSE; - else - return TYPEC_ORIENTATION_NONE; -} - -#define SC8180X_PORT_MASK 0x000000ff -#define SC8180X_ORIENTATION_MASK 0x0000ff00 -#define SC8180X_MUX_MASK 0x00ff0000 -#define SC8180X_MODE_MASK 0x3f000000 -#define SC8180X_HPD_STATE_MASK 0x40000000 -#define SC8180X_HPD_IRQ_MASK 0x80000000 - -static void pmic_glink_altmode_sc8180xp_notify(struct pmic_glink_altmode *altmode, - const void *data, size_t len) -{ - struct pmic_glink_altmode_port *alt_port; - const struct usbc_sc8180x_notify *msg; - u32 notification; - u8 orientation; - u8 hpd_state; - u8 hpd_irq; - u16 svid; - u8 port; - u8 mode; - u8 mux; - - if (len != sizeof(*msg)) { - dev_warn(altmode->dev, "invalid length of USBC_NOTIFY indication: %zd\n", len); - return; - } - - msg = data; - notification = le32_to_cpu(msg->notification); - port = FIELD_GET(SC8180X_PORT_MASK, notification); - orientation = FIELD_GET(SC8180X_ORIENTATION_MASK, notification); - mux = FIELD_GET(SC8180X_MUX_MASK, notification); - mode = FIELD_GET(SC8180X_MODE_MASK, notification); - hpd_state = FIELD_GET(SC8180X_HPD_STATE_MASK, notification); - hpd_irq = FIELD_GET(SC8180X_HPD_IRQ_MASK, notification); - - svid = mux == 2 ? USB_TYPEC_DP_SID : 0; - - if (port >= ARRAY_SIZE(altmode->ports) || !altmode->ports[port].altmode) { - dev_dbg(altmode->dev, "notification on undefined port %d\n", port); - return; - } - - alt_port = &altmode->ports[port]; - alt_port->orientation = pmic_glink_altmode_orientation(orientation); - alt_port->svid = svid; - alt_port->mode = mode; - alt_port->hpd_state = hpd_state; - alt_port->hpd_irq = hpd_irq; - schedule_work(&alt_port->work); -} - -#define SC8280XP_DPAM_MASK 0x3f -#define SC8280XP_HPD_STATE_MASK BIT(6) -#define SC8280XP_HPD_IRQ_MASK BIT(7) - -static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmode, - u16 svid, const void *data, size_t len) -{ - struct pmic_glink_altmode_port *alt_port; - const struct usbc_notify *notify; - u8 orientation; - u8 hpd_state; - u8 hpd_irq; - u8 mode; - u8 port; - - if (len != sizeof(*notify)) { - dev_warn(altmode->dev, "invalid length USBC_NOTIFY_IND: %zd\n", - len); - return; - } - - notify = data; - - port = notify->payload[0]; - orientation = notify->payload[1]; - mode = FIELD_GET(SC8280XP_DPAM_MASK, notify->payload[8]) - DPAM_HPD_A; - hpd_state = FIELD_GET(SC8280XP_HPD_STATE_MASK, notify->payload[8]); - hpd_irq = FIELD_GET(SC8280XP_HPD_IRQ_MASK, notify->payload[8]); - - if (port >= ARRAY_SIZE(altmode->ports) || !altmode->ports[port].altmode) { - dev_dbg(altmode->dev, "notification on undefined port %d\n", port); - return; - } - - alt_port = &altmode->ports[port]; - alt_port->orientation = pmic_glink_altmode_orientation(orientation); - alt_port->svid = svid; - alt_port->mode = mode; - alt_port->hpd_state = hpd_state; - alt_port->hpd_irq = hpd_irq; - schedule_work(&alt_port->work); -} - -static void pmic_glink_altmode_callback(const void *data, size_t len, void *priv) -{ - struct pmic_glink_altmode *altmode = priv; - const struct pmic_glink_hdr *hdr = data; - u16 opcode; - u16 svid; - - opcode = le32_to_cpu(hdr->opcode) & 0xff; - svid = le32_to_cpu(hdr->opcode) >> 16; - - switch (opcode) { - case USBC_CMD_WRITE_REQ: - complete(&altmode->pan_ack); - break; - case USBC_NOTIFY_IND: - pmic_glink_altmode_sc8280xp_notify(altmode, svid, data, len); - break; - case USBC_SC8180X_NOTIFY_IND: - pmic_glink_altmode_sc8180xp_notify(altmode, data, len); - break; - } -} - -static void pmic_glink_altmode_put_retimer(void *data) -{ - typec_retimer_put(data); -} - -static void pmic_glink_altmode_put_mux(void *data) -{ - typec_mux_put(data); -} - -static void pmic_glink_altmode_put_switch(void *data) -{ - typec_switch_put(data); -} - -static void pmic_glink_altmode_enable_worker(struct work_struct *work) -{ - struct pmic_glink_altmode *altmode = work_to_altmode(work); - int ret; - - ret = pmic_glink_altmode_request(altmode, ALTMODE_PAN_EN, 0); - if (ret) - dev_err(altmode->dev, "failed to request altmode notifications: %d\n", ret); -} - -static void pmic_glink_altmode_pdr_notify(void *priv, int state) -{ - struct pmic_glink_altmode *altmode = priv; - - if (state == SERVREG_SERVICE_STATE_UP) - schedule_work(&altmode->enable_work); -} - -static const struct of_device_id pmic_glink_altmode_of_quirks[] = { - { .compatible = "qcom,sc8180x-pmic-glink", .data = (void *)PMIC_GLINK_OWNER_USBC }, - {} -}; - -static int pmic_glink_altmode_probe(struct auxiliary_device *adev, - const struct auxiliary_device_id *id) -{ - struct pmic_glink_altmode_port *alt_port; - struct pmic_glink_altmode *altmode; - const struct of_device_id *match; - struct fwnode_handle *fwnode; - struct device *dev = &adev->dev; - u32 port; - int ret; - - altmode = devm_kzalloc(dev, sizeof(*altmode), GFP_KERNEL); - if (!altmode) - return -ENOMEM; - - altmode->dev = dev; - - match = of_match_device(pmic_glink_altmode_of_quirks, dev->parent); - if (match) - altmode->owner_id = (unsigned long)match->data; - else - altmode->owner_id = PMIC_GLINK_OWNER_USBC_PAN; - - INIT_WORK(&altmode->enable_work, pmic_glink_altmode_enable_worker); - init_completion(&altmode->pan_ack); - mutex_init(&altmode->lock); - - device_for_each_child_node(dev, fwnode) { - ret = fwnode_property_read_u32(fwnode, "reg", &port); - if (ret < 0) { - dev_err(dev, "missing reg property of %pOFn\n", fwnode); - fwnode_handle_put(fwnode); - return ret; - } - - if (port >= ARRAY_SIZE(altmode->ports)) { - dev_warn(dev, "invalid connector number, ignoring\n"); - continue; - } - - if (altmode->ports[port].altmode) { - dev_err(dev, "multiple connector definition for port %u\n", port); - fwnode_handle_put(fwnode); - return -EINVAL; - } - - alt_port = &altmode->ports[port]; - alt_port->altmode = altmode; - alt_port->index = port; - INIT_WORK(&alt_port->work, pmic_glink_altmode_worker); - - alt_port->bridge = devm_drm_dp_hpd_bridge_alloc(dev, to_of_node(fwnode)); - if (IS_ERR(alt_port->bridge)) { - fwnode_handle_put(fwnode); - return PTR_ERR(alt_port->bridge); - } - - alt_port->dp_alt.svid = USB_TYPEC_DP_SID; - alt_port->dp_alt.mode = USB_TYPEC_DP_MODE; - alt_port->dp_alt.active = 1; - - alt_port->typec_mux = fwnode_typec_mux_get(fwnode); - if (IS_ERR(alt_port->typec_mux)) { - fwnode_handle_put(fwnode); - return dev_err_probe(dev, PTR_ERR(alt_port->typec_mux), - "failed to acquire mode-switch for port: %d\n", - port); - } - - ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_mux, - alt_port->typec_mux); - if (ret) { - fwnode_handle_put(fwnode); - return ret; - } - - alt_port->typec_retimer = fwnode_typec_retimer_get(fwnode); - if (IS_ERR(alt_port->typec_retimer)) { - fwnode_handle_put(fwnode); - return dev_err_probe(dev, PTR_ERR(alt_port->typec_retimer), - "failed to acquire retimer-switch for port: %d\n", - port); - } - - ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_retimer, - alt_port->typec_retimer); - if (ret) { - fwnode_handle_put(fwnode); - return ret; - } - - alt_port->typec_switch = fwnode_typec_switch_get(fwnode); - if (IS_ERR(alt_port->typec_switch)) { - fwnode_handle_put(fwnode); - return dev_err_probe(dev, PTR_ERR(alt_port->typec_switch), - "failed to acquire orientation-switch for port: %d\n", - port); - } - - ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_switch, - alt_port->typec_switch); - if (ret) { - fwnode_handle_put(fwnode); - return ret; - } - } - - for (port = 0; port < ARRAY_SIZE(altmode->ports); port++) { - alt_port = &altmode->ports[port]; - if (!alt_port->bridge) - continue; - - ret = devm_drm_dp_hpd_bridge_add(dev, alt_port->bridge); - if (ret) - return ret; - } - - altmode->client = devm_pmic_glink_register_client(dev, - altmode->owner_id, - pmic_glink_altmode_callback, - pmic_glink_altmode_pdr_notify, - altmode); - return PTR_ERR_OR_ZERO(altmode->client); -} - -static const struct auxiliary_device_id pmic_glink_altmode_id_table[] = { - { .name = "pmic_glink.altmode", }, - {}, -}; -MODULE_DEVICE_TABLE(auxiliary, pmic_glink_altmode_id_table); - -static struct auxiliary_driver pmic_glink_altmode_driver = { - .name = "pmic_glink_altmode", - .probe = pmic_glink_altmode_probe, - .id_table = pmic_glink_altmode_id_table, -}; - -module_auxiliary_driver(pmic_glink_altmode_driver); - -MODULE_DESCRIPTION("Qualcomm PMIC GLINK Altmode driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 40fcda055b05..1ef638d5fd79 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -10,9 +10,14 @@ #include #include #include +#include +#include #include #include #include + +#include + #include "ucsi.h" #define PMIC_GLINK_MAX_PORTS 2 @@ -27,6 +32,16 @@ #define UC_UCSI_WRITE_BUF_REQ 0x12 #define UC_UCSI_USBC_NOTIFY_IND 0x13 +/* + * On sc8180x these requests use UCSI owner, + * on other platforms they use USBC_PAN. + */ +#define USBC_CMD_WRITE_REQ 0x15 +#define USBC_PAN_NOTIFY_IND 0x16 + +#define ALTMODE_PAN_EN 0x10 +#define ALTMODE_PAN_ACK 0x11 + struct ucsi_read_buf_req_msg { struct pmic_glink_hdr hdr; }; @@ -55,17 +70,89 @@ struct ucsi_notify_ind_msg { u32 reserved; }; +struct usbc_write_req_msg { + struct pmic_glink_hdr hdr; + __le32 cmd; + __le32 arg; + u32 reserved; +}; + +#define USBC_NOTIFY_PAYLOAD_SIZE 16 +struct usbc_pan_notify_ind_msg { + struct pmic_glink_hdr hdr; + char payload[USBC_NOTIFY_PAYLOAD_SIZE]; + u32 reserved; +}; + +enum pmic_glink_ucsi_orientation { + USBC_ORIENTATION_NORMAL, + USBC_ORIENTATION_REVERSE, + USBC_ORIENTATION_NONE, +}; + +enum pmic_glink_ucsi_mux { + USBC_MUX_NONE, + USBC_MUX_USB_2L, + USBC_MUX_DP_4L, + USBC_MUX_USB_DP, +}; + +enum pmic_glink_altmode_pin_assignment { + DPAM_HPD_OUT, + DPAM_HPD_A, + DPAM_HPD_B, + DPAM_HPD_C, + DPAM_HPD_D, + DPAM_HPD_E, + DPAM_HPD_F, +}; + +#define SC8180X_PORT_MASK GENMASK(7, 0) +#define SC8180X_ORIENTATION_MASK GENMASK(15, 8) +#define SC8180X_MUX_MASK GENMASK(23, 16) +#define SC8180X_MODE_MASK GENMASK(29, 24) +#define SC8180X_HPD_STATE_MASK BIT(30) +#define SC8180X_HPD_IRQ_MASK BIT(31) + +#define SC8280XP_DPAM_MASK GENMASK(5, 0) +#define SC8280XP_HPD_STATE_MASK BIT(6) +#define SC8280XP_HPD_IRQ_MASK BIT(7) + +struct pmic_glink_ucsi_port { + spinlock_t lock; + + struct work_struct altmode_work; + + struct pmic_glink_ucsi *ucsi; + struct gpio_desc *port_orientation; + struct auxiliary_device *bridge; + + int idx; + + enum typec_orientation orientation; + + enum pmic_glink_ucsi_mux mux; + unsigned int mode; + + u16 svid; + u8 hpd_state; + u8 hpd_irq; +}; + struct pmic_glink_ucsi { struct device *dev; - struct gpio_desc *port_orientation[PMIC_GLINK_MAX_PORTS]; + struct pmic_glink_ucsi_port ports[PMIC_GLINK_MAX_PORTS]; + unsigned int altmode_id; + struct pmic_glink_client *altmode_client; struct pmic_glink_client *client; struct ucsi *ucsi; struct completion read_ack; struct completion write_ack; struct completion sync_ack; + struct completion pan_ack; bool sync_pending; struct mutex lock; /* protects concurrent access to PMIC Glink interface */ @@ -193,27 +280,128 @@ static void pmic_glink_ucsi_update_connector(struct ucsi_connector *con) int i; for (i = 0; i < PMIC_GLINK_MAX_PORTS; i++) { - if (ucsi->port_orientation[i]) + if (ucsi->ports[i].port_orientation) con->typec_cap.orientation_aware = true; } } +static int pmic_glink_altmode_request(struct pmic_glink_ucsi *ucsi, u32 cmd, u32 arg) +{ + struct usbc_write_req_msg req = {}; + unsigned long left; + int ret; + + /* + * The USBC_CMD_WRITE_REQ ack doesn't identify the request, so wait for + * one ack at a time. + */ + mutex_lock(&ucsi->lock); + + req.hdr.owner = cpu_to_le32(ucsi->altmode_id); + req.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP); + req.hdr.opcode = cpu_to_le32(USBC_CMD_WRITE_REQ); + req.cmd = cpu_to_le32(cmd); + req.arg = cpu_to_le32(arg); + + reinit_completion(&ucsi->pan_ack); + ret = pmic_glink_send(ucsi->altmode_client, &req, sizeof(req)); + if (ret) { + dev_err(ucsi->dev, "failed to send altmode request: %#x (%d)\n", cmd, ret); + goto out_unlock; + } + + left = wait_for_completion_timeout(&ucsi->pan_ack, 5 * HZ); + if (!left) { + dev_err(ucsi->dev, "timeout waiting for altmode request ack for: %#x\n", cmd); + ret = -ETIMEDOUT; + } + +out_unlock: + mutex_unlock(&ucsi->lock); + return ret; +} + +static void pmic_glink_ucsi_set_orientation(struct ucsi_connector *con, + struct pmic_glink_ucsi_port *port) +{ + enum typec_orientation orientation; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + orientation = port->orientation; + spin_unlock_irqrestore(&port->lock, flags); + + if (port->port_orientation) { + int val = gpiod_get_value(port->port_orientation); + if (val >= 0) + orientation = val ? + TYPEC_ORIENTATION_REVERSE : + TYPEC_ORIENTATION_NORMAL; + } + + typec_set_orientation(con->port, orientation); +} + static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con) { struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(con->ucsi); - int orientation; - if (con->num >= PMIC_GLINK_MAX_PORTS || - !ucsi->port_orientation[con->num - 1]) + if (con->num >= PMIC_GLINK_MAX_PORTS) return; - orientation = gpiod_get_value(ucsi->port_orientation[con->num - 1]); - if (orientation >= 0) { - typec_set_orientation(con->port, - orientation ? - TYPEC_ORIENTATION_REVERSE : - TYPEC_ORIENTATION_NORMAL); - } + pmic_glink_ucsi_set_orientation(con, &ucsi->ports[con->num - 1]); +} + +static void pmic_glink_ucsi_register_altmode(struct ucsi_connector *con) +{ + static const u8 all_assignments = BIT(DP_PIN_ASSIGN_C) | BIT(DP_PIN_ASSIGN_D) | + BIT(DP_PIN_ASSIGN_E); + struct typec_altmode_desc desc; + struct typec_altmode *alt; + + mutex_lock(&con->lock); + + if (con->port_altmode[0]) + goto out; + + memset(&desc, 0, sizeof(desc)); + desc.svid = USB_TYPEC_DP_SID; + desc.mode = USB_TYPEC_DP_MODE; + + desc.vdo = DP_CAP_CAPABILITY(DP_CAP_DFP_D); + + /* We can't rely on the firmware with the capabilities. */ + desc.vdo |= DP_CAP_DP_SIGNALLING(0) | DP_CAP_RECEPTACLE; + + /* Claiming that we support all pin assignments */ + desc.vdo |= all_assignments << 8; + desc.vdo |= all_assignments << 16; + + alt = typec_port_register_altmode(con->port, &desc); + if (IS_ERR(alt)) + goto out; + + alt->desc = "DisplayPort"; + + con->port_altmode[0] = alt; + +out: + mutex_unlock(&con->lock); +} + +static void pmic_glink_ucsi_registered(struct ucsi *ucsi) +{ + int i, ret; + + if (!ucsi->connector) + return; + + for (i = 0; i < ucsi->cap.num_connectors; i++) + pmic_glink_ucsi_register_altmode(&ucsi->connector[i]); + + ret = pmic_glink_altmode_request(ucsi_get_drvdata(ucsi), ALTMODE_PAN_EN, 0); + if (ret) + dev_err(ucsi->dev, "failed to request altmode notification: %d\n", ret); } static const struct ucsi_operations pmic_glink_ucsi_ops = { @@ -222,6 +410,7 @@ static const struct ucsi_operations pmic_glink_ucsi_ops = { .async_write = pmic_glink_ucsi_async_write, .update_connector = pmic_glink_ucsi_update_connector, .connector_status = pmic_glink_ucsi_connector_status, + .ucsi_registered = pmic_glink_ucsi_registered, }; static void pmic_glink_ucsi_notify_handle(struct pmic_glink_ucsi *ucsi, u32 cci) @@ -289,6 +478,206 @@ static void pmic_glink_ucsi_notify_ind(struct pmic_glink_ucsi *ucsi, const void pmic_glink_ucsi_notify_handle(ucsi, le32_to_cpu(msg->notification)); } +static enum typec_orientation pmic_glink_altmode_orientation(unsigned int orientation) +{ + if (orientation == USBC_ORIENTATION_NORMAL) + return TYPEC_ORIENTATION_NORMAL; + else if (orientation == USBC_ORIENTATION_REVERSE) + return TYPEC_ORIENTATION_REVERSE; + + WARN_ON(orientation != USBC_ORIENTATION_NONE); + return TYPEC_ORIENTATION_NONE; +} + +static void pmic_glink_ucsi_notify_ind_sc8180x(struct pmic_glink_ucsi *ucsi, const void *data, size_t len) +{ + const struct ucsi_notify_ind_msg *msg; + struct pmic_glink_ucsi_port *port; + unsigned long flags; + u32 notification; + unsigned int idx; + unsigned int orientation; + + if (len != sizeof (*msg)) { + dev_err_ratelimited(ucsi->dev, "Unexpected altmode notify struct size %zd\n", len); + return; + } + + msg = data; + + notification = le32_to_cpu(msg->notification); + + idx = FIELD_GET(SC8180X_PORT_MASK, notification); + if (idx == 0x80) { + schedule_work(&ucsi->notify_work_sc8180x); + return; + } + + if (idx > ARRAY_SIZE(ucsi->ports)) { + dev_err_ratelimited(ucsi->dev, "notification port index out of range (%d)\n", idx); + return; + } + + port = &ucsi->ports[idx]; + + spin_lock_irqsave(&port->lock, flags); + + orientation = FIELD_GET(SC8180X_ORIENTATION_MASK, notification); + port->orientation = pmic_glink_altmode_orientation(orientation); + port->mux = FIELD_GET(SC8180X_MUX_MASK, notification); + port->mode = FIELD_GET(SC8180X_MODE_MASK, notification); + port->hpd_state = FIELD_GET(SC8180X_HPD_STATE_MASK, notification); + port->hpd_irq = FIELD_GET(SC8180X_HPD_IRQ_MASK, notification); + + if (port->mux == USBC_MUX_DP_4L || + port->mux == USBC_MUX_USB_DP) + port->svid = USB_TYPEC_DP_SID; + else + port->svid = USB_SID_PD; + + spin_unlock_irqrestore(&port->lock, flags); + + schedule_work(&port->altmode_work); +} + +/* used everywhere except sc8180x */ +static void pmic_glink_ucsi_altmode_notify_ind(struct pmic_glink_ucsi *ucsi, u16 svid, const void *data, size_t len) +{ + const struct usbc_pan_notify_ind_msg *msg; + struct pmic_glink_ucsi_port *port; + unsigned long flags; + unsigned int idx; + + if (len != sizeof (*msg)) { + dev_err_ratelimited(ucsi->dev, "Unexpected altmode notify struct size %zd\n", len); + return; + } + + msg = data; + + idx = msg->payload[0]; + if (idx > ARRAY_SIZE(ucsi->ports)) { + dev_err_ratelimited(ucsi->dev, "notification port index out of range (%d)\n", idx); + return; + } + + port = &ucsi->ports[idx]; + + spin_lock_irqsave(&port->lock, flags); + + port->orientation = pmic_glink_altmode_orientation(msg->payload[1]); + port->mux = msg->payload[2]; + port->svid = svid; + port->mode = FIELD_GET(SC8280XP_DPAM_MASK, msg->payload[8]); + port->hpd_state = FIELD_GET(SC8280XP_HPD_STATE_MASK, msg->payload[8]); + port->hpd_irq = FIELD_GET(SC8280XP_HPD_IRQ_MASK, msg->payload[8]); + + spin_unlock_irqrestore(&port->lock, flags); + + schedule_work(&port->altmode_work); +} + +static struct typec_altmode *find_altmode(struct ucsi_connector *con, + u16 svid) +{ + int i; + + for (i = 0; con->port_altmode[i]; i++) { + if (con->port_altmode[i]->svid == svid) + return con->port_altmode[i]; + } + + return NULL; +} + +static void pmic_glink_ucsi_set_state(struct ucsi_connector *con, + struct pmic_glink_ucsi_port *port) +{ + struct typec_displayport_data dp_data = {}; + struct typec_altmode *altmode = NULL; + unsigned long flags; + void *data = NULL; + int mode; + + spin_lock_irqsave(&port->lock, flags); + + if (port->svid == USB_SID_PD) { + mode = TYPEC_STATE_USB; + } else if (port->svid == USB_TYPEC_DP_SID && port->mode == DPAM_HPD_OUT) { + mode = TYPEC_STATE_SAFE; + } else if (port->svid == USB_TYPEC_DP_SID) { + altmode = find_altmode(con, port->svid); + if (!altmode) { + dev_err(con->ucsi->dev, "altmode woth SVID 0x%04x not found\n", + port->svid); + spin_unlock_irqrestore(&port->lock, flags); + return; + } + + mode = TYPEC_MODAL_STATE(port->mode - DPAM_HPD_A); + + dp_data.status = DP_STATUS_ENABLED; + dp_data.status |= DP_STATUS_CON_DFP_D; + if (port->hpd_state) + dp_data.status |= DP_STATUS_HPD_STATE; + if (port->hpd_irq) + dp_data.status |= DP_STATUS_IRQ_HPD; + dp_data.conf = DP_CONF_SET_PIN_ASSIGN(port->mode - DPAM_HPD_A); + + data = &dp_data; + } else { + dev_err(con->ucsi->dev, "Unsupported SVID 0x%04x\n", port->svid); + spin_unlock_irqrestore(&port->lock, flags); + return; + } + + spin_unlock_irqrestore(&port->lock, flags); + + if (altmode) + typec_altmode_set_port(altmode, mode, data); + else + typec_set_mode(con->port, mode); + + if (port->bridge) + drm_aux_hpd_bridge_notify(&port->bridge->dev, + port->hpd_state ? + connector_status_connected : + connector_status_disconnected); + +} + +static void pmic_glink_ucsi_handle_altmode(struct pmic_glink_ucsi_port *port) +{ + struct pmic_glink_ucsi *ucsi = port->ucsi; + struct ucsi_connector *con; + int idx = port->idx; + + if (idx > ucsi->ucsi->cap.num_connectors) { + dev_warn_ratelimited(ucsi->ucsi->dev, "altmode port out of range: %d\n", idx); + goto out; + } + + con = &ucsi->ucsi->connector[idx]; + + mutex_lock(&con->lock); + + pmic_glink_ucsi_set_orientation(con, port); + + pmic_glink_ucsi_set_state(con, port); + + mutex_unlock(&con->lock); + +out: + pmic_glink_altmode_request(ucsi, ALTMODE_PAN_ACK, idx); +} + +static void pmic_glink_ucsi_altmode_work(struct work_struct *work) +{ + struct pmic_glink_ucsi_port *port = container_of(work, struct pmic_glink_ucsi_port, altmode_work); + + pmic_glink_ucsi_handle_altmode(port); +} + static void pmic_glink_ucsi_notify_work_sc8180x(struct work_struct *work) { struct pmic_glink_ucsi *ucsi = container_of(work, struct pmic_glink_ucsi, notify_work_sc8180x); @@ -324,7 +713,10 @@ static void pmic_glink_ucsi_callback_sc8180x(const void *data, size_t len, void pmic_glink_ucsi_write_ack(ucsi, data, len); break; case UC_UCSI_USBC_NOTIFY_IND: - schedule_work(&ucsi->notify_work_sc8180x); + pmic_glink_ucsi_notify_ind_sc8180x(ucsi, data, len); + break; + case USBC_CMD_WRITE_REQ: + complete(&ucsi->pan_ack); break; }; } @@ -347,6 +739,26 @@ static void pmic_glink_ucsi_callback(const void *data, size_t len, void *priv) }; } +static void pmic_glink_ucsi_altmode_callback(const void *data, size_t len, void *priv) +{ + struct pmic_glink_ucsi *ucsi = priv; + const struct pmic_glink_hdr *hdr = data; + u16 opcode; + u16 svid; + + opcode = le32_to_cpu(hdr->opcode) & 0xff; + svid = le32_to_cpu(hdr->opcode) >> 16; + + switch (opcode) { + case USBC_CMD_WRITE_REQ: + complete(&ucsi->pan_ack); + break; + case USBC_PAN_NOTIFY_IND: + pmic_glink_ucsi_altmode_notify_ind(ucsi, svid, data, len); + break; + }; +} + static void pmic_glink_ucsi_pdr_notify(void *priv, int state) { struct pmic_glink_ucsi *ucsi = priv; @@ -357,6 +769,11 @@ static void pmic_glink_ucsi_pdr_notify(void *priv, int state) ucsi_unregister(ucsi->ucsi); } +static void pmic_glink_ucsi_altmode_pdr_notify(void *priv, int state) +{ + /* do nothing */ +} + static void pmic_glink_ucsi_destroy(void *data) { struct pmic_glink_ucsi *ucsi = data; @@ -389,7 +806,7 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev, const struct of_device_id *match; struct fwnode_handle *fwnode; bool sc8180x_glink; - int ret; + int i, ret; ucsi = devm_kzalloc(dev, sizeof(*ucsi), GFP_KERNEL); if (!ucsi) @@ -403,6 +820,7 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev, init_completion(&ucsi->read_ack); init_completion(&ucsi->write_ack); init_completion(&ucsi->sync_ack); + init_completion(&ucsi->pan_ack); mutex_init(&ucsi->lock); ucsi->ucsi = ucsi_create(dev, &pmic_glink_ucsi_ops); @@ -436,27 +854,64 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev, } desc = devm_gpiod_get_index_optional(&adev->dev, "orientation", port, GPIOD_IN); - - /* If GPIO isn't found, continue */ - if (!desc) - continue; - if (IS_ERR(desc)) return dev_err_probe(dev, PTR_ERR(desc), "unable to acquire orientation gpio\n"); - ucsi->port_orientation[port] = desc; + ucsi->ports[port].port_orientation = desc; + + ucsi->ports[port].bridge = devm_drm_dp_hpd_bridge_alloc(ucsi->dev, + to_of_node(fwnode)); + if (IS_ERR(ucsi->ports[port].bridge)) { + fwnode_handle_put(fwnode); + return PTR_ERR(ucsi->ports[port].bridge); + } + } + + for (i = 0; i < PMIC_GLINK_MAX_PORTS; i++) { + if (!ucsi->ports[i].bridge) + continue; + + ret = devm_drm_dp_hpd_bridge_add(dev, ucsi->ports[i].bridge); + if (ret) + return ret; } sc8180x_glink = of_device_is_compatible(dev->parent->of_node, "qcom,sc8180x-pmic-glink"); + for (i = 0; i < PMIC_GLINK_MAX_PORTS; i++) { + ucsi->ports[i].idx = i; + ucsi->ports[i].ucsi = ucsi; + spin_lock_init(&ucsi->ports[i].lock); + INIT_WORK(&ucsi->ports[i].altmode_work, + pmic_glink_ucsi_altmode_work); + } + if (sc8180x_glink) { + ucsi->altmode_id = PMIC_GLINK_OWNER_USBC; + + /* + * We don't need another instance of USBC client, both altmode + * and UCSI are handled via the same client. + */ ucsi->client = devm_pmic_glink_register_client(dev, PMIC_GLINK_OWNER_USBC, pmic_glink_ucsi_callback_sc8180x, pmic_glink_ucsi_pdr_notify, ucsi); + ucsi->altmode_client = ucsi->client; } else { + ucsi->altmode_id = PMIC_GLINK_OWNER_USBC_PAN; + + ucsi->altmode_client = + devm_pmic_glink_register_client(dev, + ucsi->altmode_id, + pmic_glink_ucsi_altmode_callback, + pmic_glink_ucsi_altmode_pdr_notify, + ucsi); + if (IS_ERR(ucsi->altmode_client)) + return PTR_ERR(ucsi->altmode_client); + ucsi->client = devm_pmic_glink_register_client(dev, PMIC_GLINK_OWNER_USBC, pmic_glink_ucsi_callback,