From nobody Fri Sep 25 16:53:58 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E07162472B8 for ; Thu, 10 Sep 2026 09:18:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789031891; cv=none; b=CZa7PQdd76OsOJ4DGW0lf3hnsK+VxzLoPXy+bR28s0vkaFF5TviyoXmfIntUrFJjEhAzB9QdzZ21jNEB5S1ghSg7dkU/zVVQwL+HdIBxSC0Qx51zzdftH63tp/f1zVJEMMJAHC0LlIxrqK3p2ecwp+5KZcRSg6vubnliZScmSNY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789031891; c=relaxed/simple; bh=uqXaAfYUFhE/LqQ34mcswk141mw9mtdWD/wtSOx2T40=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EeCFFFFu9Hl0B+55sROkhtwLOEqM0DpN1mr6rXJAxgpmpxd5IUyKzwy4cBuycNDFXxq7EcDvRrx0vyqEo5Zbva0Kct0pz5oS1SpWC3SqzvwUg7GI2v70NtFKmyIxMQl0QMUaHDrFB0CRbnlLtibQkKx1pCaOoXJGgieAdvkPoXs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=oaONKq4k; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=L32Td5LP; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="oaONKq4k"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="L32Td5LP" Received: from pps.filterd (m0279872.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68A8pv7F3998289 for ; Thu, 10 Sep 2026 09:18:04 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=R7VlR3KIJ7C bxcd6Y1OKzAYlMEijNWEnGLFJbn3lQWU=; b=oaONKq4kgSoIWX9DYW6ICey4NDo A7Qs9s+p1M0lndLoniYHtYfIDy3DF+m0tyTmiknvJQAEoza0eAURI/SYXOMQyIE3 YtHfD09oj64vcNbtAdsRoVQ77OfKX6RZU8K+GIIOqnVSJLo23/Hez0PWnFn3z3uO sdhlH82sMEFPDT67RDpUquaOeXnVrSgZxm6xhR8HOmIdoZnruKMG64AXyAwRVmnP NXYr8aPHWJap1Ye+9HgP4diKPkQZySqNIO0OCOfnAi2cMitwmm87v2KVwNEd7cOF rgVwtZUYmtqvtuJaAy2rT7gm4DowDtPzV5oWwg74IZk9TvYbUSmXwnGzrIg== Received: from mail-oi1-f197.google.com (mail-oi1-f197.google.com [209.85.167.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gkcyfk598-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Thu, 10 Sep 2026 09:18:04 +0000 (GMT) Received: by mail-oi1-f197.google.com with SMTP id 5614622812f47-4a7de733fa9so7521531b6e.3 for ; Thu, 10 Sep 2026 02:18:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1789031883; x=1789636683; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=R7VlR3KIJ7Cbxcd6Y1OKzAYlMEijNWEnGLFJbn3lQWU=; b=L32Td5LP5PfRVJu2Q7y5BmNG63LyzMdWYXD6utNn6oKczkWdxiFwgjm5NwXjVTymq/ OoFGE/PPIHzWskk/bOOCfWbE849YgAHeKNFMqJDNGuPO5ELiJIUsLBlmvOLL5W63PHRf K7z93fP6qWcGSYLwazu5MEtpkNJpJWlomFxfJJ/ih9i2ojIKP7uMstrtDQu/fw7sqbNn 2NRYjtLpCXgwAdYo1qHtL9emNcTIZ9qnUA0upnJLfr8SR3XKvYodolJam0cJlOjBUNHE bdtvTKBg+E6NTO5jH2/W8WVCg+HEjsoGeVoyhAvurDr5D/DCn6lOLCGllVXHGrLCSrDn XjSA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789031883; x=1789636683; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=R7VlR3KIJ7Cbxcd6Y1OKzAYlMEijNWEnGLFJbn3lQWU=; b=dKr4/Yw3uYWRyoHHtwptB/DLN8howMN5bSw7ZJFIMaaGM4IeZnr1P8s0kcSb8qFSkK NtY3VQcF+MqXBjLt057UTwC5lJaDJkurPGcfPMi+dOGSfVTaxp2szdvYwflNWjQJnVl0 jXXM2tb20Pvh2PctsDjHimisygDxZrH+C+gfJ4uDo5eQOuwnEMf4G4BcOyMkkRtUrmHV z9US+7J6asQ0/yLU04MFyBFisSNiZoPlcsr5byeGiO+SLCO58k+zI0ZZJmCQTxFOiE8u 9WC5e6mhjYGM75+d8zwOCTeHTRYvPKcysCrYiVbmbNciqjlZOjfx6frtmK3QaDKesoXM KvdA== X-Forwarded-Encrypted: i=1; AKwUvBxBrtMsNLRaJ1UG5EDxLfSa0JsHQ+b/NH70K6Bx1yZgMRAPN9i3Q+2ucNwRq/LmnGOuhPCo1eX2TWbNVDM=@vger.kernel.org X-Gm-Message-State: AFuF++l6TfgCifmuObcCJjfDEFy4u2V8YtggUsxojpesdoHo0QkOWkIH igbjTbajIuo+Dw7lYAwH6NLq5dROCdYmdiUDZ/k9pzsQvZqYPsUooDlc6WP3NjHRKBKcRx3HwTr r0yg74sf7xnE3Wbb0/tIYyD7ZvhNehJmxweniqTYYU63eJztjHdbJZgRNS7OKzO2wn6Y= X-Gm-Gg: AYBFou3Rrvcyle6yivlbB+tA3v3DIO6GLbRJrYB1bl921u4ZK7x1cdoDASjxejSyR06 /s2xi7T7dawNo8s0yCuPAADAtnjj+zjklVOZHLucFU3R88vj1b4gZrpAooiSGNIi1esgWs7/4gZ VNkaDedewdXK7SWaTcKr73QB+3/3rh5pLYGq55V5mwkwwgXQED01KgZuvlqlaF6J4QBVUGrvhDL kZ0EItTY5ecv7MvBMuxwQWUJHUuVYZU2r+7mXtz6etY565y5q3IBXYxiuwU5cdacvBp9bPN+b98 cimH1Engs7eFhUzS3QN4UhDwu4aJfKGUg9XbJ84YZi+pZbNmLbUIuhL2WhsoCk0tpYAKOW40ykY JTO2EAZJmjFr99ExdpMBdo8nEW3eG/doxUaJ3zmr/XU4HWOkVVJJwiwNkFJZ0 X-Received: by 2002:a05:6808:f8c:b0:4b9:a829:f018 with SMTP id 5614622812f47-4b9a829f8f4mr21445248b6e.36.1789031883381; Thu, 10 Sep 2026 02:18:03 -0700 (PDT) X-Received: by 2002:a05:6808:f8c:b0:4b9:a829:f018 with SMTP id 5614622812f47-4b9a829f8f4mr21445217b6e.36.1789031882907; Thu, 10 Sep 2026 02:18:02 -0700 (PDT) Received: from QCOM-aGQu4IUr3Y.qualcomm.com (i-global052.qualcomm.com. [199.106.103.52]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-1433171a97esm57395988c88.12.2026.09.10.02.17.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 10 Sep 2026 02:18:02 -0700 (PDT) From: Shawn Guo To: Greg Kroah-Hartman Cc: Heikki Krogerus , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bartosz Golaszewski , Krishna Kurapati , =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , linux-usb@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, Shawn Guo , Krzysztof Kozlowski Subject: [PATCH v2 1/2] dt-bindings: usb: Add onsemi FUSB15201 Type-C and PD controller Date: Thu, 10 Sep 2026 17:17:50 +0800 Message-ID: <20260910091751.819264-2-shengchao.guo@oss.qualcomm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260910091751.819264-1-shengchao.guo@oss.qualcomm.com> References: <20260910091751.819264-1-shengchao.guo@oss.qualcomm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-Spam-Info: AW1haW4tMjYwOTEwMDEwNCBTYWx0ZWRfX1OVVJJSDoZBq v1RFYIzfjR4gne/dlBW6krZi/wdxPBRytoEOtfPuLlQJRbFvJxMdVJIeAC2A+gCike/AJTs1sAs tagTpfxeP7gBjmn0n2gwYSZF/T025QA= X-Proofpoint-ORIG-GUID: 6gPc8DoD-vo7BzEVjtFCdgrU1lnaAO8O X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTEwMDEwNCBTYWx0ZWRfX/jNKZ/nWWLrX uM/a+OuvNFuMPYNhH7RNRbyW/6JMl/UQzqHX2dEqMuHW3nnfECz3gzuUbN5kQKfHUKUCTCzIxRh ovoVdKn953OdmNmlpS7WLRujtdf2TtWFRIl7HQlv0jyV/unkv3yBfzDqZhN9znjQVbPmlFg3qae DTTAGu8Ad3fqq6t/BFT3sl6FNiRlKBkvzez22qiYfnJ9NeqKCZxwaLw2SsmxXxmrgr7tap2xAuC 6VEQa2VSzYKV8af+Hxm2vEGQnBEblvX29ikDEtjz+uLguhLSeIomGpfcmNKrvdATFYTTFI8YWuX kYabyaZ9ULyy24XrQdIrvSmZ0izjhsk14a1UkZeytUuo3YHNQ5Uhao0MoE3n2+ifTyALLrIWkW4 l0z2lPgGAuDptLNHsQI76x3/QiwxGeVWo2gNb8sNYbcbsqH3sArcS26prWwb7Hds1f+i+tyv5H8 zNxRzcvMxHKx4K67pfQ== X-Authority-Analysis: v=2.4 cv=H5pOUOYi c=1 sm=1 tr=0 ts=6aa275cc cx=c_pps a=WJcna6AvsNCxL/DJwPP1KA==:117 a=b9+bayejhc3NMeqCNyeLQQ==:17 a=VdqzKS8jKosA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=yx91gb_oNiZeI1HMLzn7:22 a=VwQbUJbxAAAA:8 a=EUspDBNiAAAA:8 a=gEfo2CItAAAA:8 a=pw356RtDBc5WmomScbsA:9 a=_Y9Zt4tPzoBS9L09Snn2:22 a=sptkURWiP4Gy88Gu7hUp:22 X-Proofpoint-GUID: 6gPc8DoD-vo7BzEVjtFCdgrU1lnaAO8O X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-09-10_03,2026-09-09_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 clxscore=1015 suspectscore=0 spamscore=0 priorityscore=1501 impostorscore=0 malwarescore=0 bulkscore=0 adultscore=0 lowpriorityscore=0 phishscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609100104 Content-Type: text/plain; charset="utf-8" The FUSB15201 is an autonomous dual-port USB Type-C and Power Delivery controller. It runs the Type-C state machine and the Power Delivery policy engine on an integrated microcontroller, and exposes only a status and command interface over I2C, so it is not a TCPC. Describe the two ports as addressed connector child nodes, so that a board which wires up only one of them can describe just that one. Assisted-by: LLM Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260902045938.184221-2-shengchao.guo@oss.q= ualcomm.com Signed-off-by: Shawn Guo --- .../bindings/usb/onnn,fusb15201.yaml | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/onnn,fusb15201.ya= ml diff --git a/Documentation/devicetree/bindings/usb/onnn,fusb15201.yaml b/Do= cumentation/devicetree/bindings/usb/onnn,fusb15201.yaml new file mode 100644 index 000000000000..09f2e964d3ef --- /dev/null +++ b/Documentation/devicetree/bindings/usb/onnn,fusb15201.yaml @@ -0,0 +1,152 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/onnn,fusb15201.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: onsemi FUSB15201 dual-port Type-C and Power Delivery controller + +maintainers: + - Shawn Guo + +description: + The FUSB15201 is an autonomous dual-port USB Type-C and Power Delivery + controller. It runs the Type-C state machine and the Power Delivery poli= cy + engine on an integrated microcontroller, so the host is not involved in + Power Delivery messaging. Over I2C it reports the attach state, CC + orientation, data and power roles, faults and the VBUS voltage of each of + its two ports, and accepts commands to initiate data and power role swap= s. + + The two ports are described as connector child nodes, addressed 0 for po= rt A + and 1 for port B. A board that wires up only one of the two ports descri= bes + only the corresponding connector. + +properties: + compatible: + const: onnn,fusb15201 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + description: + Interrupt line shared by both ports, driven by the I2C_INT pin. The = pin + is active low and stays asserted until every pending interrupt has b= een + cleared, so it has to be described as level triggered. + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + '^connector@[01]$': + type: object + $ref: /schemas/connector/usb-connector.yaml# + unevaluatedProperties: false + + properties: + compatible: + const: usb-c-connector + + reg: + maximum: 1 + description: + 0 for port A, 1 for port B. + + required: + - reg + +required: + - compatible + - reg + - interrupts + - '#address-cells' + - '#size-cells' + +anyOf: + - required: [connector@0] + - required: [connector@1] + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + typec@50 { + compatible =3D "onnn,fusb15201"; + reg =3D <0x50>; + interrupts-extended =3D <&pmic_gpios 8 IRQ_TYPE_LEVEL_LOW>; + + #address-cells =3D <1>; + #size-cells =3D <0>; + + connector@0 { + compatible =3D "usb-c-connector"; + reg =3D <0>; + label =3D "USB-C0"; + power-role =3D "dual"; + data-role =3D "dual"; + try-power-role =3D "sink"; + + ports { + #address-cells =3D <1>; + #size-cells =3D <0>; + + port@0 { + reg =3D <0>; + + endpoint { + remote-endpoint =3D <&usb_0_dwc3_hs>; + }; + }; + + port@1 { + reg =3D <1>; + + endpoint { + remote-endpoint =3D <&usb_0_qmpphy_ss>; + }; + }; + }; + }; + + connector@1 { + compatible =3D "usb-c-connector"; + reg =3D <1>; + label =3D "USB-C1"; + power-role =3D "dual"; + data-role =3D "dual"; + try-power-role =3D "sink"; + + ports { + #address-cells =3D <1>; + #size-cells =3D <0>; + + port@0 { + reg =3D <0>; + + endpoint { + remote-endpoint =3D <&usb_1_dwc3_hs>; + }; + }; + + port@1 { + reg =3D <1>; + + endpoint { + remote-endpoint =3D <&usb_1_qmpphy_ss>; + }; + }; + }; + }; + }; + }; +... --=20 2.43.0 From nobody Fri Sep 25 16:53:58 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 862743DDDA1 for ; Thu, 10 Sep 2026 09:18:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789031897; cv=none; b=SmvGgmLSJ+YQ8Rm8cVive72c+91umhirhcP5sOya8jRD6ZBJ9BfyrD3OgEmypu77D8gyEzeCIuALvo6DQNVbdfFiDVqEWrklUwrZjPTECfQCqKWSCIRzXYFCpRWYRmxZdTwh+tXUZ//UTFiscM2H7KVsWPqaUJqJZdcZQihMngw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789031897; c=relaxed/simple; bh=Duw11h67AK9mlE2GKnKH+3tCa+KayysGh2MjBEnp7tY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hF3zMQlK80JXJAFNglmEhwSdY44afE+tNjGC1SQE/NjASMRQiu0Ae+dGIqTZ7qiS/rdtADL9ZTkR+F7BAlWNhz2XGwLogSs+9j2d/+u5qfk9ZS5S+PW+TzYytdnKWMJkbDB9NBlWwJ9q6wWS8S3DvrksJVBXV7S4tV6mllPObhc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=FHdR0gqC; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=hM42O5o9; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="FHdR0gqC"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="hM42O5o9" Received: from pps.filterd (m0279862.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68A8q69S4136853 for ; Thu, 10 Sep 2026 09:18:09 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=qr6izYPz4TE z7rNe6D8L7zTFOwcCD9wacaryphTzI/s=; b=FHdR0gqCzNFVa2+p2Q5Vk0+5LfT NMNTKVWE8TkRwO1VEuvu0t3II+E0XlSUSO024C09xMVA1BkIFN5uLn8azZKymXQT Pa8Jk+e6fpCB1zrWbVXbTI5mUCiHIrvqvH769Ilj/skRchN2s4uIRbCA0biRyQUD O0mfDhMuJPvdE8KsgqqFH0c+ov20GOwFdq9XegWaIph2c7W7TjG+zjBhww7SN3ZU sx5YBIo3aeiQa68A1clg0c9pmVr7PWkWSqSAbN4QBCdpTPMq0lggFTY3JQSnouK6 2ZPtPOu4rUzZE9m6fYvqcgvATKTUgTsIG4jqJiQO5rglMbf74/PEnp38e1w== Received: from mail-oi1-f199.google.com (mail-oi1-f199.google.com [209.85.167.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gkcydu296-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Thu, 10 Sep 2026 09:18:09 +0000 (GMT) Received: by mail-oi1-f199.google.com with SMTP id 5614622812f47-4ab4dd46ddeso4152494b6e.3 for ; Thu, 10 Sep 2026 02:18:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1789031888; x=1789636688; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=qr6izYPz4TEz7rNe6D8L7zTFOwcCD9wacaryphTzI/s=; b=hM42O5o9MmEDAg7l6Dn2i9SWCtgz0Od00gm3nGmyzfG+BdH3djlHcW9cSXEkVLmrsH gzzxHzwTBhGhPRUthoZr46Vh7UC6Epp8h0RxUIQBgG0fNloLucfR1G2XS1q72q2DqY9t OEej1ve6SyoKbI7Ph7pRug26MZxDb5iyz25gCHXEi9qcyi5hYdCAAolsriSu4dfEgTs7 Mi+PTyPqOlBdkyPnboyQTUerkKX3/SYecCMP4qvNGIfmIFSt/qjTIB0NbVGftYs5wMwe vk2HnOWom8FaMqTH+Aj3gVPFY02/2HsAwlVPrvHmnk99CH5PVNDkZwQ0z5zrf8h03V2S 5pAA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789031888; x=1789636688; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=qr6izYPz4TEz7rNe6D8L7zTFOwcCD9wacaryphTzI/s=; b=XGPvQxhYCLARVboPKwuMKPv8hILeSs7oIsBd8HdX6mam4wb+YxEztJ36IeK5Hwiq16 Ptj492HV4/ZDsvw0nTyJPaePQ+OvFj8SvMMbK/bo1rbZ7OWHysnZIjNbAM0/mrJKTW9O kW2n6vYABfnnQBtJRCuTipBF0hoKW4/o3RM+pfd8lY210bbOoqaOT3gS+4sgPj2L4hsT E5fOG2dtvHtua90LDB8q5GxmcR7XF+C0qnpogrvHs+on0KR5gZquPgU57eLCc56JCopJ nTJ0i9AmR4MGr6dwMfsUqps5F2owZ2mnSbFfBNdueAJcLHyrmlmwH3PYk3XzKEFp/tlW 5WXg== X-Forwarded-Encrypted: i=1; AKwUvBzUgSYLOtZvuHaJd6Vf9Z9nbrNh0EGcIX8hgR1KcQ2ft5CIG4ggrQxnMCpusFn9UUs3g9KWpucKn38D0ec=@vger.kernel.org X-Gm-Message-State: AFuF++lBeb1CCG5x6EUXBZwavmM1mVNfWM3MwD2ZFrFWX5n19eeAzbXo e8a4ZC3Sd2FrOoJU6ChZsiARYo5yuF0ti97L2lmMzoYsB1MzHV2sYl3+2XdtlGSAnnQjBfeqMGX DuyZOF13ijpXkVGBuXJK9m1K25+4YSPRm1WLdLEIB+1FoWAlXiFSykZjGvxA+r7/EfCA= X-Gm-Gg: AYBFou2Cp0i6KBlxnOX4jnMIL2nSyxMrhXAM7n8i/H83qYELLLKkkAPjHLG0xMqAQID G1V2GCkQ0Krku+BBSY/YQ5u8ronb9x8tdIzLN0PvXi96rVC6Nenxw36YJP5CGhozTNICvZ19vOP XqumXriml5x/zZ3ID8v+dkmPDJsb9eGEkYLXl5v8huYeP5WdtnYMn7aInN3eFho7md0e/DeoSb9 bTgXwP6kiBpsQgisjgF4i46alK9zqPxXg5EnOexgDYErWTLWRLW0jU485NGffjdEUM9H1Hhw4/u SHh/3VM5H221tJ8QSUF0cTVM3rkbLm3ipBLjNKzBsxeb+3wQw4wD/OxWz129NubfMjsq1Bz4Ljk JAqp1mEpbKcW11FfqMjmFjqTlhJ8kW2d6KigM3HIT2LuZ7lk5c2CwABHrQCxM X-Received: by 2002:a05:6808:1c0e:b0:496:34a:d7e3 with SMTP id 5614622812f47-4b961b9154dmr22719691b6e.17.1789031888032; Thu, 10 Sep 2026 02:18:08 -0700 (PDT) X-Received: by 2002:a05:6808:1c0e:b0:496:34a:d7e3 with SMTP id 5614622812f47-4b961b9154dmr22719661b6e.17.1789031887373; Thu, 10 Sep 2026 02:18:07 -0700 (PDT) Received: from QCOM-aGQu4IUr3Y.qualcomm.com (i-global052.qualcomm.com. [199.106.103.52]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-1433171a97esm57395988c88.12.2026.09.10.02.18.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 10 Sep 2026 02:18:07 -0700 (PDT) From: Shawn Guo To: Greg Kroah-Hartman Cc: Heikki Krogerus , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bartosz Golaszewski , Krishna Kurapati , =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , linux-usb@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, Shawn Guo , Bartosz Golaszewski Subject: [PATCH v2 2/2] usb: typec: Add onsemi FUSB15201 driver Date: Thu, 10 Sep 2026 17:17:51 +0800 Message-ID: <20260910091751.819264-3-shengchao.guo@oss.qualcomm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260910091751.819264-1-shengchao.guo@oss.qualcomm.com> References: <20260910091751.819264-1-shengchao.guo@oss.qualcomm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Authority-Analysis: v=2.4 cv=IfsSymqa c=1 sm=1 tr=0 ts=6aa275d1 cx=c_pps a=yymyAM/LQ7lj/HqAiIiKTw==:117 a=b9+bayejhc3NMeqCNyeLQQ==:17 a=VdqzKS8jKosA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=_K5XuSEh1TEqbUxoQ0s3:22 a=VwQbUJbxAAAA:8 a=EUspDBNiAAAA:8 a=-ozyDiMbBtCkaKFn3fsA:9 a=efpaJB4zofY2dbm2aIRb:22 X-Proofpoint-GUID: 9bx87E75HJ1F4uDwwVqUyVc9nqQ6YJzF X-Proofpoint-ORIG-GUID: 9bx87E75HJ1F4uDwwVqUyVc9nqQ6YJzF X-Proofpoint-Spam-Info: AW1haW4tMjYwOTEwMDEwNCBTYWx0ZWRfX+uMYPxpWzllH 7B5Uyuhmy90inXhYGvQqggsT3SpzChGpsSZXCi4m5cli5ojyN4di5gWRLuQ35G7P4C3HqytIWbK JFPc+mAM0O+JTXxTmcpZ4QzsHHYeO3E= X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTEwMDEwNCBTYWx0ZWRfXwPC1vu62wRId ZHiQGhy0m0doTXfPGMPnPWGRk/Uywc8UKSb/46h4YkGH0e2xIBI3VCVGjYf/8/bJpscnayUa1j3 zOSIEJzKzwzqkWX4oc3Cm60n9M/ChSuXJ8h+qc+gkw1uM1TY/Jk+ZimKkd/peLPFkoYPftvPqS9 YnZmDS40ISqYM1kTXbYe5w2hJVum1iUNvcYq43id6HPd4NeDppMiuSDI9sZiJPnXxEk+q96ooQe 6TUpbOSgbht3Tp9u8cwWJ7mvSNgGHltAtvCDHvXMCYztG9pSI9LzVIaL7lAfB2I8cGqF0HtOVuU tBWlFZ1qun9k3x4r+74+oiyp5HFnCqxpYZOG2AebtxLo/0/TFtgtNbRlVdId7fCgTvXZN/4CNdW y7NmqPa5EbV4pdynzBBR6dUbOD5hUlmP1LuHLvpkpAUgUg1kzZvFRGhvO/AY7OwHEHPx4ACxsAy v1Rvwj5C9pdf0OFUAKg== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-09-10_03,2026-09-09_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 phishscore=0 malwarescore=0 priorityscore=1501 lowpriorityscore=0 impostorscore=0 spamscore=0 adultscore=0 suspectscore=0 bulkscore=0 clxscore=1015 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609100104 Content-Type: text/plain; charset="utf-8" The FUSB15201 is a dual-port USB Type-C and Power Delivery controller which runs the Type-C state machine and the PD policy engine on an integrated microcontroller. The host only observes the resulting state and may ask for role swaps, so this is a plain Type-C class driver rather than a TCPC driven by the Type-C Port Manager. Register a Type-C port for each connector described in firmware, report attach and detach, orientation, power and data roles and the PD capability of the partner, and drive the USB role switch of the attached USB controller so that OTG role switching works. Data and power role swaps requested through sysfs are forwarded to the controller, which performs them asynchronously and reports the outcome as a port status interrupt. The controller exposes a per-port VBUS ADC reading, but the Type-C class has no VBUS attribute and nothing needs the value, so it is not read. PD messages, PDOs and alternate modes are not exposed by the hardware at all. The chip-wide MASTER_RESET command would drop both ports at once and has no caller, so it is left alone as well. Assisted-by: LLM Reviewed-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20260902045938.184221-3-shengchao.guo@oss.q= ualcomm.com Signed-off-by: Shawn Guo --- drivers/usb/typec/Kconfig | 17 + drivers/usb/typec/Makefile | 1 + drivers/usb/typec/fusb15201.c | 580 ++++++++++++++++++++++++++++++++++ 3 files changed, 598 insertions(+) create mode 100644 drivers/usb/typec/fusb15201.c diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index 2f80c2792dbd..e21854cb89d4 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -64,6 +64,23 @@ config TYPEC_ANX7411 If you choose to build this driver as a dynamically linked module, the module will be called anx7411.ko. =20 +config TYPEC_FUSB15201 + tristate "onsemi FUSB15201 Type-C and Power Delivery controller driver" + depends on I2C + depends on USB_ROLE_SWITCH + select REGMAP_I2C + help + Say Y or M here if your system has an onsemi FUSB15201 dual-port USB + Type-C and Power Delivery controller. + + The FUSB15201 handles Type-C detection and Power Delivery negotiation + autonomously. This driver reports the resulting state to the Type-C + class, drives the USB role switch of the attached USB controller, and + allows data and power role swaps to be requested. + + If you choose to build this driver as a dynamically linked module, the + module will be called fusb15201.ko. + config TYPEC_RT1719 tristate "Richtek RT1719 Sink Only Type-C controller driver" depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 8a6a1c663eb6..9f3a156d9c9f 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_TYPEC_TCPM) +=3D tcpm/ obj-$(CONFIG_TYPEC_UCSI) +=3D ucsi/ obj-$(CONFIG_TYPEC_TPS6598X) +=3D tipd/ obj-$(CONFIG_TYPEC_ANX7411) +=3D anx7411.o +obj-$(CONFIG_TYPEC_FUSB15201) +=3D fusb15201.o obj-$(CONFIG_TYPEC_HD3SS3220) +=3D hd3ss3220.o obj-$(CONFIG_TYPEC_STUSB160X) +=3D stusb160x.o obj-$(CONFIG_TYPEC_RT1719) +=3D rt1719.o diff --git a/drivers/usb/typec/fusb15201.c b/drivers/usb/typec/fusb15201.c new file mode 100644 index 000000000000..897b2fabc449 --- /dev/null +++ b/drivers/usb/typec/fusb15201.c @@ -0,0 +1,580 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * Driver for the onsemi FUSB15201 dual-port USB Type-C and Power Delivery + * controller. + * + * The FUSB15201 runs the Type-C state machine and the Power Delivery poli= cy + * engine on an integrated microcontroller. The host only observes the + * resulting state and may ask for role swaps, so this is a plain Type-C c= lass + * driver rather than a TCPC on the Type-C Port Manager. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FUSB15201_NUM_PORTS 2 + +#define FUSB15201_REG_VENDID 0x00 +#define FUSB15201_REG_PRODID 0x01 +#define FUSB15201_REG_DEVID 0x02 +#define FUSB15201_REG_HWVER 0x03 +#define FUSB15201_REG_FWVER 0x04 + +#define FUSB15201_VENDID 0xaa +#define FUSB15201_PRODID 0x65 +#define FUSB15201_DEVID 0x01 + +/* Per-port status block, port A at 0x06 and port B at 0x0c */ +#define FUSB15201_PORT_BASE(p) (0x06 + (p) * 0x06) +#define FUSB15201_REG_STATUS(p) (FUSB15201_PORT_BASE(p) + 0x02) +#define FUSB15201_REG_RESET_STATUS(p) (FUSB15201_PORT_BASE(p) + 0x03) +#define FUSB15201_REG_FAULT_STATUS(p) (FUSB15201_PORT_BASE(p) + 0x04) + +#define FUSB15201_STATUS_ATTACHED BIT(0) +#define FUSB15201_STATUS_USBPD BIT(1) +#define FUSB15201_STATUS_CC2 BIT(2) +#define FUSB15201_STATUS_DFP BIT(3) +#define FUSB15201_STATUS_SINK BIT(4) + +#define FUSB15201_RESET_ERR_RECOVERY BIT(2) + +#define FUSB15201_FAULT_VBUS_OVP BIT(0) +#define FUSB15201_FAULT_OTP BIT(1) +#define FUSB15201_FAULT_CC_OVP BIT(2) +#define FUSB15201_FAULT_VCONN_OCP BIT(3) + +/* Per-port command block, port A at 0x11 and port B at 0x13 */ +#define FUSB15201_REG_COMMANDS(p) (0x11 + (p) * 0x02) +#define FUSB15201_REG_INT_MASK(p) (0x12 + (p) * 0x02) + +/* + * The swap command bits are named after the role the port is in when the = swap + * is requested, not the role it ends up in. + */ +#define FUSB15201_CMD_UFP_TO_DFP BIT(4) +#define FUSB15201_CMD_DFP_TO_UFP BIT(5) +#define FUSB15201_CMD_SINK_TO_SOURCE BIT(6) +#define FUSB15201_CMD_SOURCE_TO_SINK BIT(7) + +#define FUSB15201_REG_MASTER_CONTROL 0x15 +#define FUSB15201_MASTER_DRP_DISABLE BIT(1) + +/* Per-port interrupt status, write one to clear */ +#define FUSB15201_REG_INTERRUPT(p) (0x16 + (p)) +#define FUSB15201_INT_FAULT BIT(0) +#define FUSB15201_INT_RESET_STATUS BIT(1) +#define FUSB15201_INT_PORT_STATUS BIT(2) +#define FUSB15201_INT_ALL (FUSB15201_INT_FAULT | \ + FUSB15201_INT_RESET_STATUS | \ + FUSB15201_INT_PORT_STATUS) + +#define FUSB15201_REG_MAX FUSB15201_REG_INTERRUPT(1) + +struct fusb15201; + +struct fusb15201_port { + struct fusb15201 *chip; + unsigned int index; + struct typec_port *port; + struct typec_partner *partner; + struct usb_role_switch *role_sw; + struct typec_capability cap; + unsigned int status; +}; + +struct fusb15201 { + struct device *dev; + struct regmap *regmap; + struct mutex lock; /* serialises access to the shared registers */ + struct fusb15201_port ports[FUSB15201_NUM_PORTS]; +}; + +static enum typec_role fusb15201_default_pwr_role(struct fusb15201_port *p= ort) +{ + switch (port->cap.type) { + case TYPEC_PORT_SRC: + return TYPEC_SOURCE; + case TYPEC_PORT_SNK: + return TYPEC_SINK; + default: + if (port->cap.prefer_role =3D=3D TYPEC_SOURCE) + return TYPEC_SOURCE; + return TYPEC_SINK; + } +} + +static void fusb15201_set_roles(struct fusb15201_port *port, + enum typec_role pwr_role, + enum typec_data_role data_role, bool attached) +{ + typec_set_pwr_role(port->port, pwr_role); + typec_set_vconn_role(port->port, pwr_role); + typec_set_data_role(port->port, data_role); + + if (!port->role_sw) + return; + + if (!attached) + usb_role_switch_set_role(port->role_sw, USB_ROLE_NONE); + else if (data_role =3D=3D TYPEC_HOST) + usb_role_switch_set_role(port->role_sw, USB_ROLE_HOST); + else + usb_role_switch_set_role(port->role_sw, USB_ROLE_DEVICE); +} + +static void fusb15201_hw_update(struct fusb15201_port *port) +{ + struct fusb15201 *chip =3D port->chip; + enum typec_data_role data_role; + enum typec_role pwr_role; + unsigned int status; + int ret; + + ret =3D regmap_read(chip->regmap, FUSB15201_REG_STATUS(port->index), + &status); + if (ret) { + dev_warn(chip->dev, "port%u: failed to read status: %d\n", + port->index, ret); + return; + } + + if (!(status & FUSB15201_STATUS_ATTACHED)) { + if (port->partner) { + typec_unregister_partner(port->partner); + port->partner =3D NULL; + } + + /* + * None of the remaining status bits are valid while detached, + * so fall back to what the port is configured to do. + */ + typec_set_orientation(port->port, TYPEC_ORIENTATION_NONE); + typec_set_pwr_opmode(port->port, TYPEC_PWR_MODE_USB); + + pwr_role =3D fusb15201_default_pwr_role(port); + data_role =3D pwr_role =3D=3D TYPEC_SOURCE ? TYPEC_HOST : TYPEC_DEVICE; + fusb15201_set_roles(port, pwr_role, data_role, false); + + port->status =3D status; + return; + } + + if (!(port->status & FUSB15201_STATUS_ATTACHED)) { + struct typec_partner_desc desc =3D { + .usb_pd =3D !!(status & FUSB15201_STATUS_USBPD), + }; + + port->partner =3D typec_register_partner(port->port, &desc); + if (IS_ERR(port->partner)) { + dev_err(chip->dev, + "port%u: failed to register partner: %pe\n", + port->index, port->partner); + port->partner =3D NULL; + } + } + + typec_set_orientation(port->port, status & FUSB15201_STATUS_CC2 ? + TYPEC_ORIENTATION_REVERSE : + TYPEC_ORIENTATION_NORMAL); + + /* + * The controller only tells us whether the partner is PD capable. The + * Type-C current advertisement is not reported, so 1.5A and 3.0A + * operation cannot be distinguished from the default. + */ + typec_set_pwr_opmode(port->port, status & FUSB15201_STATUS_USBPD ? + TYPEC_PWR_MODE_PD : TYPEC_PWR_MODE_USB); + + pwr_role =3D status & FUSB15201_STATUS_SINK ? TYPEC_SINK : TYPEC_SOURCE; + data_role =3D status & FUSB15201_STATUS_DFP ? TYPEC_HOST : TYPEC_DEVICE; + fusb15201_set_roles(port, pwr_role, data_role, true); + + port->status =3D status; +} + +static void fusb15201_report_faults(struct fusb15201_port *port) +{ + struct fusb15201 *chip =3D port->chip; + unsigned int fault; + int ret; + + ret =3D regmap_read(chip->regmap, FUSB15201_REG_FAULT_STATUS(port->index), + &fault); + if (ret) + return; + + /* + * The controller protects itself, so there is nothing to do here + * beyond making the fault visible. + */ + if (fault & FUSB15201_FAULT_VBUS_OVP) + dev_warn_ratelimited(chip->dev, "port%u: VBUS overvoltage\n", + port->index); + if (fault & FUSB15201_FAULT_OTP) + dev_warn_ratelimited(chip->dev, "port%u: over temperature\n", + port->index); + if (fault & FUSB15201_FAULT_CC_OVP) + dev_warn_ratelimited(chip->dev, "port%u: CC overvoltage\n", + port->index); + if (fault & FUSB15201_FAULT_VCONN_OCP) + dev_warn_ratelimited(chip->dev, "port%u: VCONN overcurrent\n", + port->index); +} + +static void fusb15201_report_reset(struct fusb15201_port *port) +{ + struct fusb15201 *chip =3D port->chip; + unsigned int reset; + int ret; + + ret =3D regmap_read(chip->regmap, FUSB15201_REG_RESET_STATUS(port->index), + &reset); + if (ret) + return; + + dev_dbg(chip->dev, "port%u: reset status 0x%02x\n", port->index, reset); + + if (reset & FUSB15201_RESET_ERR_RECOVERY) + dev_warn_ratelimited(chip->dev, + "port%u: entered error recovery\n", + port->index); +} + +static irqreturn_t fusb15201_irq(int irq, void *data) +{ + struct fusb15201 *chip =3D data; + irqreturn_t ret =3D IRQ_NONE; + unsigned int i; + + guard(mutex)(&chip->lock); + + for (i =3D 0; i < FUSB15201_NUM_PORTS; i++) { + struct fusb15201_port *port =3D &chip->ports[i]; + unsigned int pending; + + if (!port->port) + continue; + + if (regmap_read(chip->regmap, FUSB15201_REG_INTERRUPT(i), + &pending)) + continue; + + pending &=3D FUSB15201_INT_ALL; + if (!pending) + continue; + + /* Write one to clear */ + regmap_write(chip->regmap, FUSB15201_REG_INTERRUPT(i), pending); + + if (pending & FUSB15201_INT_PORT_STATUS) + fusb15201_hw_update(port); + if (pending & FUSB15201_INT_FAULT) + fusb15201_report_faults(port); + if (pending & FUSB15201_INT_RESET_STATUS) + fusb15201_report_reset(port); + + ret =3D IRQ_HANDLED; + } + + return ret; +} + +/* + * Role swaps are asynchronous: the command only asks the controller to st= art + * the swap, and the outcome arrives later as a port status interrupt. + */ +static int fusb15201_command(struct fusb15201_port *port, unsigned int cmd) +{ + struct fusb15201 *chip =3D port->chip; + + guard(mutex)(&chip->lock); + + if (!(port->status & FUSB15201_STATUS_ATTACHED)) + return -ENOTCONN; + + return regmap_write(chip->regmap, FUSB15201_REG_COMMANDS(port->index), + cmd); +} + +static int fusb15201_dr_set(struct typec_port *p, enum typec_data_role rol= e) +{ + struct fusb15201_port *port =3D typec_get_drvdata(p); + unsigned int cmd; + + cmd =3D role =3D=3D TYPEC_HOST ? FUSB15201_CMD_UFP_TO_DFP : + FUSB15201_CMD_DFP_TO_UFP; + + return fusb15201_command(port, cmd); +} + +static int fusb15201_pr_set(struct typec_port *p, enum typec_role role) +{ + struct fusb15201_port *port =3D typec_get_drvdata(p); + unsigned int cmd; + + cmd =3D role =3D=3D TYPEC_SOURCE ? FUSB15201_CMD_SINK_TO_SOURCE : + FUSB15201_CMD_SOURCE_TO_SINK; + + return fusb15201_command(port, cmd); +} + +static const struct typec_operations fusb15201_typec_ops =3D { + .dr_set =3D fusb15201_dr_set, + .pr_set =3D fusb15201_pr_set, +}; + +/* + * Nearly every register is either volatile or write-only, and the interru= pt + * registers are write-one-to-clear, so the register map is not cached. + */ +static const struct regmap_config fusb15201_regmap_config =3D { + .reg_bits =3D 8, + .val_bits =3D 8, + .max_register =3D FUSB15201_REG_MAX, +}; + +static void fusb15201_unregister_port(void *data) +{ + struct fusb15201_port *port =3D data; + + if (port->partner) + typec_unregister_partner(port->partner); + typec_unregister_port(port->port); +} + +static void fusb15201_put_role_sw(void *data) +{ + usb_role_switch_put(data); +} + +static int fusb15201_check_id(struct fusb15201 *chip) +{ + static const struct { + unsigned int reg; + unsigned int expected; + const char *name; + } ids[] =3D { + { FUSB15201_REG_VENDID, FUSB15201_VENDID, "vendor" }, + { FUSB15201_REG_PRODID, FUSB15201_PRODID, "product" }, + { FUSB15201_REG_DEVID, FUSB15201_DEVID, "device" }, + }; + unsigned int val, hwver, fwver; + int ret, i; + + for (i =3D 0; i < ARRAY_SIZE(ids); i++) { + ret =3D regmap_read(chip->regmap, ids[i].reg, &val); + if (ret) + return dev_err_probe(chip->dev, ret, + "failed to read %s id\n", + ids[i].name); + + if (val !=3D ids[i].expected) + return dev_err_probe(chip->dev, -ENODEV, + "bad %s id 0x%02x, expected 0x%02x\n", + ids[i].name, val, ids[i].expected); + } + + /* + * The register semantics are defined by the firmware image, so record + * the versions to make mismatches easier to spot. + */ + if (!regmap_read(chip->regmap, FUSB15201_REG_HWVER, &hwver) && + !regmap_read(chip->regmap, FUSB15201_REG_FWVER, &fwver)) + dev_dbg(chip->dev, "hardware version %u, firmware version %u\n", + hwver, fwver); + + return 0; +} + +static int fusb15201_register_port(struct fusb15201 *chip, + struct fwnode_handle *fwnode) +{ + struct fusb15201_port *port; + unsigned int index; + int ret; + + ret =3D fwnode_property_read_u32(fwnode, "reg", &index); + if (ret) + return dev_err_probe(chip->dev, ret, + "missing reg property of %pfwP\n", fwnode); + + if (index >=3D FUSB15201_NUM_PORTS) + return dev_err_probe(chip->dev, -EINVAL, + "invalid connector index %u\n", index); + + port =3D &chip->ports[index]; + if (port->port) + return dev_err_probe(chip->dev, -EINVAL, + "duplicate connector for port %u\n", index); + + port->chip =3D chip; + port->index =3D index; + + ret =3D typec_get_fw_cap(&port->cap, fwnode); + if (ret) + return dev_err_probe(chip->dev, ret, + "port%u: failed to get capabilities\n", + index); + + port->cap.revision =3D USB_TYPEC_REV_2_0; + port->cap.orientation_aware =3D true; + port->cap.driver_data =3D port; + port->cap.ops =3D &fusb15201_typec_ops; + + port->port =3D typec_register_port(chip->dev, &port->cap); + if (IS_ERR(port->port)) { + ret =3D PTR_ERR(port->port); + port->port =3D NULL; + return dev_err_probe(chip->dev, ret, + "port%u: failed to register\n", index); + } + + ret =3D devm_add_action_or_reset(chip->dev, fusb15201_unregister_port, + port); + if (ret) + return ret; + + port->role_sw =3D fwnode_usb_role_switch_get(fwnode); + if (IS_ERR(port->role_sw)) + return dev_err_probe(chip->dev, PTR_ERR(port->role_sw), + "port%u: failed to get role switch\n", + index); + + if (port->role_sw) { + ret =3D devm_add_action_or_reset(chip->dev, fusb15201_put_role_sw, + port->role_sw); + if (ret) + return ret; + } + + return 0; +} + +static int fusb15201_hw_init(struct fusb15201 *chip) +{ + bool source_only =3D true; + unsigned int i; + int ret; + + for (i =3D 0; i < FUSB15201_NUM_PORTS; i++) { + struct fusb15201_port *port =3D &chip->ports[i]; + + if (!port->port) { + /* Nothing describes this port, keep it quiet. */ + ret =3D regmap_write(chip->regmap, + FUSB15201_REG_INT_MASK(i), + FUSB15201_INT_ALL); + if (ret) + return ret; + + continue; + } + + if (port->cap.type !=3D TYPEC_PORT_SRC) + source_only =3D false; + + ret =3D regmap_write(chip->regmap, FUSB15201_REG_INT_MASK(i), 0); + if (ret) + return ret; + + /* Discard anything that happened before we got here */ + ret =3D regmap_write(chip->regmap, FUSB15201_REG_INTERRUPT(i), + FUSB15201_INT_ALL); + if (ret) + return ret; + } + + /* + * Dual role toggling is the only configurable part of the Type-C state + * machine, and it is chip wide rather than per port, which is why it is + * set up here instead of from a port_type_set() callback. + */ + return regmap_update_bits(chip->regmap, FUSB15201_REG_MASTER_CONTROL, + FUSB15201_MASTER_DRP_DISABLE, + source_only ? FUSB15201_MASTER_DRP_DISABLE : 0); +} + +static int fusb15201_probe(struct i2c_client *client) +{ + struct device *dev =3D &client->dev; + struct fusb15201 *chip; + unsigned int i, nports =3D 0; + int ret; + + chip =3D devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev =3D dev; + i2c_set_clientdata(client, chip); + + ret =3D devm_mutex_init(dev, &chip->lock); + if (ret) + return ret; + + chip->regmap =3D devm_regmap_init_i2c(client, &fusb15201_regmap_config); + if (IS_ERR(chip->regmap)) + return dev_err_probe(dev, PTR_ERR(chip->regmap), + "failed to initialise register map\n"); + + ret =3D fusb15201_check_id(chip); + if (ret) + return ret; + + device_for_each_child_node_scoped(dev, fwnode) { + ret =3D fusb15201_register_port(chip, fwnode); + if (ret) + return ret; + + nports++; + } + + if (!nports) + return dev_err_probe(dev, -ENODEV, "no connector described\n"); + + ret =3D fusb15201_hw_init(chip); + if (ret) + return dev_err_probe(dev, ret, "failed to initialise\n"); + + /* Seed the Type-C class with the current state before arming the IRQ */ + for (i =3D 0; i < FUSB15201_NUM_PORTS; i++) { + if (chip->ports[i].port) + fusb15201_hw_update(&chip->ports[i]); + } + + ret =3D devm_request_threaded_irq(dev, client->irq, NULL, fusb15201_irq, + IRQF_ONESHOT, dev_name(dev), chip); + if (ret) + return dev_err_probe(dev, ret, "failed to request irq\n"); + + return 0; +} + +static const struct of_device_id fusb15201_of_match[] =3D { + { .compatible =3D "onnn,fusb15201" }, + { } +}; +MODULE_DEVICE_TABLE(of, fusb15201_of_match); + +static struct i2c_driver fusb15201_driver =3D { + .driver =3D { + .name =3D "fusb15201", + .of_match_table =3D fusb15201_of_match, + }, + .probe =3D fusb15201_probe, +}; +module_i2c_driver(fusb15201_driver); + +MODULE_AUTHOR("Shawn Guo "); +MODULE_DESCRIPTION("onsemi FUSB15201 Type-C and Power Delivery controller"= ); +MODULE_LICENSE("GPL"); --=20 2.43.0