From nobody Fri Sep 25 19:16:06 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 85C8155C332 for ; Wed, 9 Sep 2026 12:37:27 +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=1788957450; cv=none; b=r0LGq5CJ0uZly6HapcDlMKgYnKl26jWpvuYYkJAREK9grLVh+6b/puoWSc79Yd92n6eKSKZSnYzMRdLRGMXN+gKRZvC42Y6nNgtXwC4Cs8qSl+wJ7APAZRW+QmZvZL2YYbPBaAyYNpzWRbMA80kgQf/7dqQpIWx6FJjsNhZL9bs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788957450; c=relaxed/simple; bh=Qv2lflH6YpMRmCWBAa2RN4e8yD7dg6y3jZkOloHx9hc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NQQX0A5swYoEHCsPNBk3Wv+zM2O/QZpR4hvkqwiDRgU8PwrrF8+KdbXHtqOqNA1VDZRK5rqkJ/ZphyJI8e8FD2fdxUcxRoLKqVnLaCaHHdQo/oW4cgIpex76wqXeJjfpCWtuUmpO7Gu2xLaTcvLfv4WWx5pGVZzEOWo8qKm7LYQ= 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=Dwm9N7Hg; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=dO8gM31U; 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="Dwm9N7Hg"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="dO8gM31U" Received: from pps.filterd (m0279865.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 689BoV1T2698515 for ; Wed, 9 Sep 2026 12:37:26 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= ffE7yM+yBd+rWRItDOzR+dZhkdj1+8iczv8XL0VmG2Y=; b=Dwm9N7Hg36zec/+W 7oFEqtFHu6E3zU62Bl4C2shKxe4e9bRAlxnnyEfT5264LOWIXQHQUYhuADAbRvWR XEarkoeTSUogn2UMTxcwLg3980to6OuI4GY9ztz2cytBGAMtE+dzrR2P58OHQrOd /QFmBg1tz9NksbPjvYQtAUVIQimwvDNGqvXsw0WBNEBdksXuWifvH5wZ+AW7dHW6 z2/LA7s5P5dNckLYrNvkMCq05kH0HWiWL2w9ebXfwCQvwO6ntIfx9KgGN/rWjjw8 E+L/kx3UaOJurUel40qO+/KHCSMMENH2SRMY064WFGCE7YQ2FgeSEaEq5XhX6KRE vkqDnQ== Received: from mail-vs1-f70.google.com (mail-vs1-f70.google.com [209.85.217.70]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gk6xh059e-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Wed, 09 Sep 2026 12:37:26 +0000 (GMT) Received: by mail-vs1-f70.google.com with SMTP id ada2fe7eead31-78bb7517525so723675137.3 for ; Wed, 09 Sep 2026 05:37:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1788957446; x=1789562246; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=ffE7yM+yBd+rWRItDOzR+dZhkdj1+8iczv8XL0VmG2Y=; b=dO8gM31UBsXSLyp0AFKdY5TvhR+e3nw28vXrxnXkllrAPxlUPtnQcizHq0Jxyuzqzb J+SL/IRC9HkAqTPJYG/+Iap2V72m9JvV6aIqv6SYwq3G2IyfzyEVOAA0ljqcO/CR/HvW 7BS+ZFuPerSypCVsrHlXowFMzCK6CSP3B/fOU0HymOWcFqMXgPHpNyYGhOfMJ04zrHAg K2vtLGMsFpryxfLuU2Uk7fm8mT0x8zZIVojOnGMr6Pa655a/4/70SiOnOctYdTUFLoyP QDHONM6eieD9v7zVPYayhuUUfbvvaS80wfgPnKtrFNB6zy5qEXqHT+yNtwUSaGimth36 lbPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788957446; x=1789562246; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=ffE7yM+yBd+rWRItDOzR+dZhkdj1+8iczv8XL0VmG2Y=; b=NBy6fws0fQbFr2NxuqsRlQ86oKru9Th8FECTuY/umOTiAhEpDjxX0ABCoqOpTAh+yC vqzCbKmh4mvbuS8U7+OM3k67fSPtMwwggpgDRKSBU8haIAFr1bVWbllZ5t+mSuckgdBT 4KZuPLv2uq0+qveRcMCkQWsTofmOzvvumaRq8CoV8ex90T2yX9Yvh1TrxCMq/FlHNag+ qcmayJcl+6sul5eciArBL75O1/F1vEFTrUFtDzjhWISb4W7GjjCf6LYq0WCmWTfzkMAV 1hkMEg6QZNS2HO7wlwbiEipJr0STZRtQ1E7VDKpDlrKA9acdWE9ZDQoXUcQEn9GFfcap 7Mpg== X-Forwarded-Encrypted: i=1; AKwUvBxj7t9DLDDvwWsZ+SCX3nCHvY9QDp0ze7Eg6ejagxkSx/LmpISFSLOqn4+bpeLHtLkZk62Ko9gMpp8aEPE=@vger.kernel.org X-Gm-Message-State: AFuF++kjEc+rKbC/imx3SP7m0RW9SupgKuK3sKn3ncPbiwhVCvwbLMuo poKU5PjKOAz8RhLoFsKAxDXgrWSuCCKA+0/sHzA6gEqIF9ACv6MaZK0dcub2IxkijsVILtBWuGW wKuopl3glhNAU5sx1pKpZ1wND9lpZgxB+GyHkt5u/MGLrkPCl+4BGDX6w/PYOvBMTTqsSyVMFaO 4= X-Gm-Gg: AYBFou3ssSGC0tRkZr1tPLLse9h/OKPPnOMMRftfFoli9eyS/2o2U4lZynhaZwuAAW/ BukCtQjAeBXpzAySHGXDqfFzSNj/1LP6TIO4xdyzE0+DK3xU9qIRP6fwbqFBwZvnJy5kk97hGOJ 6LeJmvhEWd5T1Ra4uREUReuIZE61Y5zpzPa5GpQ+geRXF2B18/NquVco4a98qcoICfvX3UigVkk Z6bePIwQ2EhbJBgwVpq+1oa6rORcn4gjsvoeCkJ5li5Q2HnKqC35GrupREwG52iOHBpesVGkMwt I4osrzOUUHJ16DPpAI2z3kKwtShw0doT5CXGwp8C1FLBft0kQ09ZNo1Dy8xDHF+GMJqdlYmP4fI 7+Qn8pZTMKiD3/46RQws7P+45nYBb X-Received: by 2002:a05:6102:8309:b0:790:322:8a36 with SMTP id ada2fe7eead31-7900331d5c6mr736914137.8.1788957445747; Wed, 09 Sep 2026 05:37:25 -0700 (PDT) X-Received: by 2002:a05:6102:8309:b0:790:322:8a36 with SMTP id ada2fe7eead31-7900331d5c6mr736889137.8.1788957445220; Wed, 09 Sep 2026 05:37:25 -0700 (PDT) Received: from brgl-qcom.local ([2a01:cb1d:dc:7e00:de8b:7fd5:2738:cc2a]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-4859162f354sm36317328f8f.20.2026.09.09.05.37.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 09 Sep 2026 05:37:24 -0700 (PDT) From: Bartosz Golaszewski Date: Wed, 09 Sep 2026 14:37:07 +0200 Subject: [PATCH v2 1/4] power: sequencing: don't call .post_enable() if pwrseq_unit_enable() failed Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-pwrseq-kunit-v2-1-ef496afc89d2@oss.qualcomm.com> References: <20260909-pwrseq-kunit-v2-0-ef496afc89d2@oss.qualcomm.com> In-Reply-To: <20260909-pwrseq-kunit-v2-0-ef496afc89d2@oss.qualcomm.com> To: Bartosz Golaszewski , Manivannan Sadhasivam , Chen-Yu Tsai , Krzysztof Kozlowski Cc: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, Bartosz Golaszewski , stable@vger.kernel.org X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=908; i=bartosz.golaszewski@oss.qualcomm.com; h=from:subject:message-id; bh=Qv2lflH6YpMRmCWBAa2RN4e8yD7dg6y3jZkOloHx9hc=; b=owEBbQKS/ZANAwAKAQWdLsv/NoTDAcsmYgBqoVL8+nCqtB133ENfnVlGNNxCsHBQMrD5YTEX5 axDTV5SkNGJAjMEAAEKAB0WIQSR5RMt5bVGHXuiZfwFnS7L/zaEwwUCaqFS/AAKCRAFnS7L/zaE w+fjD/9GSPt2ty3DyQWiuSJkVfBGl03keOCQsVxAgXFij3/47xs+tjLSWP5cb2ZIjCY+mX04tM2 2R9/vHZRV/zsuqoWBnCQUk7lyATOXqq42Nf+zjzshhY5pv3W55QlbYg2Y7Eu/+Y3z9sRJj7OXEl 4fWFDJNVCYzCYSi8pws5daTYzRTU6wqw6P6jRk/IcxhAUH8yuwVNJTrxoGf8YVJX7DFoVBIqXn1 RLKTgvtoH4ItoCFMNbLNN9OGDAvjl2xv/xakoqgUjZRXJhzpLscqExTDXM343IwNC0xM01sdseB d+0UhK/c84fV4eQe6zWRziRCZObpU3eAZFcXoDaVR3Fon7hoFOi9YRI1Uw3LO0NeVZt8WGBI6iS H9DQvmgKXsOY8glC+HrakrJdBmjPaCtnYJBIAPStaGgsuskm7sywSr/pwZN6pUATnVQ11yIunKY w1Mfp8DIxE/cSmWRxMVXT2h2/zN0zFvUZdEnGrAyNY+HMWPH7HrP+dii9Reyi4Pm+yk5DW9yp7B LL4fjUJdrvUyF9gtFX9JC1C1ismDI+fIr/9tAonDZMMb/9vdPc/UnLZV9eYSI4pH1CYNQwveLJn OwNTut3BGNgIGvxqpwuhWEp5zz1ErgwobeHBr1ggylfVobJvaELTA6uktNaZac8F+OZCw9Ff3cR bmZgMoj3fJaJtnQ== X-Developer-Key: i=bartosz.golaszewski@oss.qualcomm.com; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTA5MDE0MSBTYWx0ZWRfX4dr3NYm84NJT b7Gh/EN8gY7JTg5AaM5HkUW5nP7tQujENzbSuhIB++jEx86mFDt/fpE7XSeYuj34+LWnaiGQbYF MLtlEkQVVKdiaBGBOsTpH+Az0c1hRo++Py1UX5DnX+8nWdUfQ9FBwjp6HJYoeJZjdIwioQWK7F8 qgWN3B10rTdj2ogvbMMKq2C0daJfJnBjOX3OQKEbu0qr7+dFXYaAAoLZPWoWrVxGTiLiJRowAeo HUFvnEL3uMAvhT5rrNA+aXiS1q2xLDQ0HvXFQJwR2sdx924dxXOUIkwnG7/QNgm6D8kJ+XBgJW/ w44ql4PxlMDmgsIm1iau//zsZUG3jO5NdBdqSxIUNlVbyT1jJzGbNKAbUtjSzfOlcn6Myzn8UX6 pa7rl9s7UKV40cCC5uPwF4ckKmFYdoWgOprS04s4KIZUtWzFHJ7gOX1QV+uXIz15AQTTJMu9vsh 4kCty+CvSV//eSAI2fw== X-Proofpoint-Spam-Info: AW1haW4tMjYwOTA5MDE0MSBTYWx0ZWRfX8bO4k5Z7sVKa S7iBi4EFu2XNoLjaMGGRTHblxVy05ZGwS6QDENsPaAQNqf+L+zEgkJyuxNp1q2tby6gRaHKxHy1 8JDz2sr2Ss+1Clq3OaHK0cwvoEqDGjc= X-Proofpoint-GUID: 70wFnuKW0eKhmLEfRPygoaBZqOtItjnm X-Authority-Analysis: v=2.4 cv=KOlqylFo c=1 sm=1 tr=0 ts=6aa15306 cx=c_pps a=N1BjEkVkxJi3uNfLdpvX3g==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=Um2Pa8k9VHT-vaBCBUpS:22 a=VwQbUJbxAAAA:8 a=EUspDBNiAAAA:8 a=Trbc2l1I1fXLbfWOQTAA:9 a=QEXdDO2ut3YA:10 a=crWF4MFLhNY0qMRaF8an:22 X-Proofpoint-ORIG-GUID: 70wFnuKW0eKhmLEfRPygoaBZqOtItjnm 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-08_03,2026-09-09_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 adultscore=0 priorityscore=1501 spamscore=0 impostorscore=0 phishscore=0 lowpriorityscore=0 bulkscore=0 suspectscore=0 clxscore=1015 malwarescore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2609090141 If the call to pwrseq_unit_enable() failed in pwrseq_enable(), bail out instead of calling target->post_enable() which assumes the target was successfully enabled. Fixes: 249ebf3f65f8 ("power: sequencing: implement the pwrseq core") Cc: stable@vger.kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/cor= e.c index 0cb71efbb268b351607ccf610cbf0315f35eb3b7..a26615b727f6fe8e7cdc3ab1d3f= 362ba2f1c5684 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -912,6 +912,8 @@ int pwrseq_enable(struct pwrseq_desc *desc) if (!ret) desc->powered_on =3D true; } + if (ret) + return ret; =20 if (target->post_enable) { ret =3D target->post_enable(pwrseq); --=20 2.47.3 From nobody Fri Sep 25 19:16:06 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 65B6C558209 for ; Wed, 9 Sep 2026 12:37:29 +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=1788957451; cv=none; b=EQnEroXOrjl53otDAieyG1iDxOJ/ecvT4cTETC6/Jeuc/6RCm4wf6wii+R4FQGKqN8dygCsUTqUZKfxl3KSfDg6y9MDTCp/H2wpztxemaUEOa6Wg8THRTOG6nhhKdgzmtDP+vF3B3qiY8WtWHfO0/jvLke0NLYHD7Y38JHHVTnI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788957451; c=relaxed/simple; bh=6repGLUNrgVfsf2TAEMxYryccpKQnbHINlMORO8gKZU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=oXKtOkR3xAOxOnP/BqgEZxE6cs0sV7uyIb/v5vx0bcsQszTqkb3eAvPjwUissAtfWFtq0uvKzXyOt+9PQ7/av4NNyZntHXybNnQ52TaQv3yRjU9ZtlSaUYGWCJwib1gFCuZTQer/jlT/Cer5atGtMyPHd6NJi6AenuJmv+9dQQ0= 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=CabGPaHG; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=fX/Ze5/H; 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="CabGPaHG"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="fX/Ze5/H" Received: from pps.filterd (m0279864.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 689BU7Mk599844 for ; Wed, 9 Sep 2026 12:37:28 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= shxfk64WT7DU4wrhJlkTYBHV8dk9ViK4RzFH9GS22IE=; b=CabGPaHGTUDCL3Xz cwOBMA8vDb4DOl5l3sjvS4V0uB/57lmLjqf/pi+cD1v2likF2Uk+ECHFHIYo/UdO vw1x9gxj3cb4XwnT6XkUPjqQ7IHtC+JhY2qMuVdvFOffwgGvoj4+5vrVPY9NbwKq GQfOpcIiSm5fA1KBkiAGGtQcFy3q9iiky9n3DWJWp3ARm89/VpNoxRsR8qzqsfES H9urF9qWwjFXpdaXfgAYGKD32HPk+jpAz/Ond8Q6FjNUDvBOsNOmD94sj1Tqv6fM sOX8IMzWZzKxXx10er9+dySjWhQz5WYEGnnkN2GQnJx7qmy4HZKUG3LoDKAsGY+q +4M3dw== Received: from mail-ua1-f72.google.com (mail-ua1-f72.google.com [209.85.222.72]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gjywr1xpy-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Wed, 09 Sep 2026 12:37:28 +0000 (GMT) Received: by mail-ua1-f72.google.com with SMTP id a1e0cc1a2514c-97e8d95121cso477704241.0 for ; Wed, 09 Sep 2026 05:37:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1788957447; x=1789562247; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=shxfk64WT7DU4wrhJlkTYBHV8dk9ViK4RzFH9GS22IE=; b=fX/Ze5/HJsWKSmbLvpNeCPuLMyEDzFqeldYi2PrRALter0Q0tuRXdO57IFp6E1PMmQ o6aT248iAbVnSLYOzoGPjchZxII9WoH04HhlFBRJ0JECrxtVBvYPIL1R93F6Ns9PsX/a 7sM1ttlDh3+weC8UxzSY8DiywLz+7gp7RFzJqHA4lyRy0LusA33HKjfAH59Z4PUopd5P slsb/h+ZSxY550hgZZaEfE2r1o/YO3Wwd5y+u+g3Ys2/bGDf3nCmetPIJMrjSbVOa6Rr SuyubYCQ0hZtJxnwbzZWlIkvNK8u5CirtKf8JVnKEHRMLAeZbSyx6pZVCu9nQ3aFR8eG c0ag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788957447; x=1789562247; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=shxfk64WT7DU4wrhJlkTYBHV8dk9ViK4RzFH9GS22IE=; b=ESaov5+QpsGChxL3oWlfXL3iMZ3Fm0e/vpktLGydBRH58yInXazaQdkjznDYv3CTK4 vuMhOPBL3mpHSFGCDEvp7isuMl7ImVJcycDoIRwGR1bQ9j6px5S9+hvJHUalqwOg8Ig4 NaCAZ8jrgwR//X52jEBM7cFsjWIfcVzxFn1V+rwzYXqVYDeWse5LHv6/eLIg9dHWWN2Q GwWsv9m+usK3AWulPE/WWYjAyIaggOI62qHCOLgXkRiuhJY4zwVYcKFawSAAej02/PB+ jDhxIjqaZSXuCuw6DW3V4sEtG8rJA5PjFum8UnFpYflTkTEEdW8hjhGEr1cTLj31+E4L tQuw== X-Forwarded-Encrypted: i=1; AKwUvBxzoWilrcVQVn8o+ljd5Hit0py59VT/ZgpciieMlrow02H3eheiYjvkyU6jANJHHuuAwT/Ks9bRfjYVAH8=@vger.kernel.org X-Gm-Message-State: AFuF++lxHIJ/cD+M8Wg4XpCTeXmqUR0yNcGZ3Zu7DuGYr1f/ffOGgiGB lSIZuItHuCm1BWBUJza+SRKfZgITgrb7hB7dtJQz0PrthINRcHC9iPD0MJkbK99znRKPeMDHfad OP04o4kyp18WFzA0DxbuK+F/ubogLrQsVU31fvlz0oxQLrNEj2ZG7gQHE97g9BgCyZ/Odz31yzz o= X-Gm-Gg: AYBFou2IJdOyYH1WnvYXQm3HL15kZ+ewWf9/X+JMuIrYbdhSFK5mn8DvudkBBNNB32H MBe7LpfjofUUJ9KMaysQQi8Np8uI4LBej2bP+tBT2gKDitzAV/MZGgTyfU4t2aI3F4QC6XrjTOu ZK3mJVcpOUjkY5/hLS1g1HD2Mjhvz0mQEgt+30/aNEs6DWKIRJflQybcxwnzS3T0MIfw1NDyJ6+ unGsiRlaGSC6Meu79qMei8r828S39VImH4gsqT7NFF3Y2bYw5/QalRDQxeuXiWa7cr2dwcw2/5S /FZKiVzy3XIaK48n8ZX6ueQeMCLdwzSx5Gjgn+kpzD85Ee7xYX+5W/9qdbnEskm+IfWI9HxSc79 dqSi8+9iNhpAkC8gU2kXeiwyUzW03 X-Received: by 2002:a05:6102:2907:b0:778:9033:e337 with SMTP id ada2fe7eead31-78a4a7186d7mr13704030137.7.1788957447574; Wed, 09 Sep 2026 05:37:27 -0700 (PDT) X-Received: by 2002:a05:6102:2907:b0:778:9033:e337 with SMTP id ada2fe7eead31-78a4a7186d7mr13704012137.7.1788957447169; Wed, 09 Sep 2026 05:37:27 -0700 (PDT) Received: from brgl-qcom.local ([2a01:cb1d:dc:7e00:de8b:7fd5:2738:cc2a]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-4859162f354sm36317328f8f.20.2026.09.09.05.37.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 09 Sep 2026 05:37:26 -0700 (PDT) From: Bartosz Golaszewski Date: Wed, 09 Sep 2026 14:37:08 +0200 Subject: [PATCH v2 2/4] power: sequencing: fix NULL-pointer dereference in pwrseq_unit_new() Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-pwrseq-kunit-v2-2-ef496afc89d2@oss.qualcomm.com> References: <20260909-pwrseq-kunit-v2-0-ef496afc89d2@oss.qualcomm.com> In-Reply-To: <20260909-pwrseq-kunit-v2-0-ef496afc89d2@oss.qualcomm.com> To: Bartosz Golaszewski , Manivannan Sadhasivam , Chen-Yu Tsai , Krzysztof Kozlowski Cc: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, Bartosz Golaszewski , stable@vger.kernel.org, sashiko-bot X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=1255; i=bartosz.golaszewski@oss.qualcomm.com; h=from:subject:message-id; bh=6repGLUNrgVfsf2TAEMxYryccpKQnbHINlMORO8gKZU=; b=owEBbQKS/ZANAwAKAQWdLsv/NoTDAcsmYgBqoVL98WcjgMDeiyBAjzKFCykewD3nYcFjSqF6I jnCeCBj9DGJAjMEAAEKAB0WIQSR5RMt5bVGHXuiZfwFnS7L/zaEwwUCaqFS/QAKCRAFnS7L/zaE wyxXD/9ml1oNhCbvMnk3AskOyswv+qxkFD8qDg5FTzVRFqdeB1SnzL5dw9ffTRZJESigiwHErkV 66RtiO1BAPE/WKrnClpD02RbO/y2AncvPDWUqGv90vNhGf9aAnRhAFQ/yoqYZa3INaT7OCL6nHV RRCXjUmbHMW1Qk588T+vonQf7Yl3lrsjKp2eB8S5RYOKi4Z0j/KFKEQOk4NYlAldajdaxkqOPBz oDtPIaEK3yMFGcxZ/tgBqSlo2vxnSzWHITFjh3EWMf61Tlk4F8c1ycNQNn646PTjYwqPbKuKZSE rLh/nIb8l8iUg9zZ7UKLiSqCSCMnJ6rDmCS45TdRtPdOgmm5CeD2EZYOTndRcr00vJnbeCw2jPo 3i+vTOeyR3Q4xXqfOG7MEeVOZ153dc3nDhYjP4pQ7FFBSmPQMKqvrtKAMoQ0FlfhrQk6MKEQDyB sCq4nvTI9elQHG6hGzOAkoTjM9Ag8POO+1U1JWuC0+M4kk/f3N5uZFczTbPbw9VBropVLb1CN5k x13LPVrHZjomAxlXGVAwwyqJ3bAl2s3oObKC9UB3RYxEsuMn4RRTmtFDdcArVqAscvU4nvDbH+U bGwEY0fwJummPA4UZPjqLRY1kWpV+tYoNfN0YkbMPBW9lRgIGu63xHzXLwHWM/xN9MgskiFDLzT GjIuBzXESvbcneQ== X-Developer-Key: i=bartosz.golaszewski@oss.qualcomm.com; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 X-Authority-Analysis: v=2.4 cv=a4YAM0SF c=1 sm=1 tr=0 ts=6aa15308 cx=c_pps a=ULNsgckmlI/WJG3HAyAuOQ==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=DJpcGTmdVt4CTyJn9g5Z:22 a=c92rfblmAAAA:8 a=EUspDBNiAAAA:8 a=VwQbUJbxAAAA:8 a=LS90zmZLc4-xERvDOr8A:9 a=QEXdDO2ut3YA:10 a=1WsBpfsz9X-RYQiigVTh:22 a=GvGzcOZaWPEFPQC_NcjD:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTA5MDE0MCBTYWx0ZWRfX1PMObE9Bp5X9 MIMsA92xQzFOk5HpYQVVCyvb6xvsgJmNgk+d06fxL45aCTdcJ3T+tH6yWRKvJ0cqfnpj1JL+zqg 6k/PbObUfmYGUMgqWuqxeb5DsK9d0SQto+QwIV8MeS5YJRyE6CNFRCF2WdaFKaqTyxxLOSfgpOn 3/j2kKIQaUL94KLDQduN9UaBTLbx7hK8ztmsP+6vZdZvIlGjwLUQl020yjIcjy6gzqE2s6g/L4T Xn9rnsKWyHjoxEsePRw7Bnr7dGxHuOwbjq57aD58y9Mx0nC+q+SaFAvonYmf+rRb532/QK/ZemZ HkZpiWzrkjB8rcJkaS0kitsJyZMwt/osZKoIfiXjhbkP3AsvJin42BTlXZAPrWqD00/40Vdm2I5 R2+4CdnV+DV+tx/NgDeSPrKYO0kNkZcenxmQzIyN6TcFMUIzipE8QktaB3USZ72c+BHmd023ZqF tExCspmzuRhnqkcAcJQ== X-Proofpoint-Spam-Info: AW1haW4tMjYwOTA5MDE0MCBTYWx0ZWRfX5A0nO1GN+mq9 mi5teOs8te6tGdJ73moiQB1zSTkL9QOT9VOD6GsvD9pT+3a2JR5LXRgpDSsgLcCJ6MrZtPeHQvQ L6QHpXJXVCemUbI55Sn4gUYCF8uFfts= X-Proofpoint-ORIG-GUID: gURDMQotlCwmtB-bXkn8-fY3bHmOkx33 X-Proofpoint-GUID: gURDMQotlCwmtB-bXkn8-fY3bHmOkx33 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-08_03,2026-09-09_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 phishscore=0 lowpriorityscore=0 bulkscore=0 clxscore=1015 spamscore=0 adultscore=0 impostorscore=0 suspectscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2609090140 If memory allocation fails in pwrseq_unit_setup_deps(), pwrseq_unit_put() is called to release the partially initialized unit. However, we've never initialized unit->list and pwrseq_unit_release() will unconditionally call list_del() on it. Initialize unit->list right after allocating the unit struct. Fixes: 249ebf3f65f8 ("power: sequencing: implement the pwrseq core") Cc: stable@vger.kernel.org Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260903-pwrseq-kunit-v1-0-1f893d2ca= bc2%40oss.qualcomm.com?part=3D1 Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/cor= e.c index a26615b727f6fe8e7cdc3ab1d3f362ba2f1c5684..a092031943397241b2a9970de57= 93a0bbc8d7ecf 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -101,6 +101,7 @@ static struct pwrseq_unit *pwrseq_unit_new(const struct= pwrseq_unit_data *data) } =20 kref_init(&unit->ref); + INIT_LIST_HEAD(&unit->list); INIT_LIST_HEAD(&unit->deps); unit->enable =3D data->enable; unit->disable =3D data->disable; --=20 2.47.3 From nobody Fri Sep 25 19:16:06 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 BE4B7560AA5 for ; Wed, 9 Sep 2026 12:37:31 +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=1788957454; cv=none; b=HemMS8WiKKxzHf5OtHKFg/P2UejrVChP5tD8oQ0p0VoFue7M/Q3kVIXdOYqWk1UIZov3QWfoFxeN53n8siN3uouKp4bhD4PmxLwAgnGS1fIp2HtcShvQvkrlR4OS4njMeNtg/xZuoK6rl3L6j4nZ2BTiXDL6VC3ZVKgmyh589hE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788957454; c=relaxed/simple; bh=aQ4wUnjGc5pmfoIzvGc2IT9dZtMCvDmo5D6QlqxAXbE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=p4YpLfNfW2SbdPj5UIh5FK9zDs2wLJM48bgcL/B6I6419wHjrY41B67DkNwkZOnhEOEN3WvNsYDBcqDO8fp2kVRrMfwZLlInRGXOVqqlE9fwtEL09GynlXoc5SN1m6VNDuE9B/C+7Cxo0fhGxeF2tPJ8AHh2Pkptk5aHd3wqlEE= 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=DQ4r1EO+; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=gk97BU61; 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="DQ4r1EO+"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="gk97BU61" Received: from pps.filterd (m0279865.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 689BoX9G2698594 for ; Wed, 9 Sep 2026 12:37:30 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= tHOk3O4iY8NmPYpem4zaMAQrb1+f8Tv7Kz/g9GavEs4=; b=DQ4r1EO+UlkQjRzh +81BwMd5RWqJLbiNTyDtb0bL8yVim75g4cbxiTk7fypgSRqHo3yoJVE49fWU9Nq9 UCqF5HN/aMvw1PQc4kX7ZhNwe50OVP1yjV7hK8QIw4l6t1c1aJGEiuZUWF987PDm CJNMaqqhNSlX1k9jJEvFk33qtCzJZanlv3h3/DpDD43Te1+py29rRXbNwPRJQcV2 cbvZeYNMAneys1BrkJtuHEELF1F0B0jndxi5zPwpozkhsP8dwNwv0zdPrIt50yMd PL8wfMxwRLE0wsbTvQw4S3mFFQ/y3C6kqS1/dbnfP9P7IWmG+AoMckAA5Xo64Auk vN9cVg== Received: from mail-qk1-f199.google.com (mail-qk1-f199.google.com [209.85.222.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gk6xh059p-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Wed, 09 Sep 2026 12:37:30 +0000 (GMT) Received: by mail-qk1-f199.google.com with SMTP id af79cd13be357-93918756165so986860085a.0 for ; Wed, 09 Sep 2026 05:37:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1788957450; x=1789562250; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=tHOk3O4iY8NmPYpem4zaMAQrb1+f8Tv7Kz/g9GavEs4=; b=gk97BU61qDMkyNcUOQmDPv8EU0pEmkXNW8nk3jh2mpEpVjQ5tI9eR6pVNbb8aDAHRh 17RZC579jy6nDMS6dQMBgBIG7v66VaAIngqq67E8BxDxkeDjBt8LFFDhCIQxSwH2h6eY IcS6RwqDt7e3cuveYJLscHv3tSSOVidyDN/IohRWOCQx2Nrgfjd22hcE9U0KFNBCAsCx vqjkh6ZM0cKkx7jUwyVWoXY0qddNuiM3FcFMbBupVn7rs2qwVtZtr5rYoXdWGtaRuxFN +OD7jOk6BOtH3lqM8iK9S8SJnIqbS/ulcVNSSosfvLD6H37OXeo5iffuirlNxASCmOSr uZ5Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788957450; x=1789562250; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=tHOk3O4iY8NmPYpem4zaMAQrb1+f8Tv7Kz/g9GavEs4=; b=UkGXTWdHdK4uRKktChIc8IRgwvTL1OKQiBTwLacMv7Qsjdcf5GImvhhtv778PoYkx9 O2DdaOWWT5XAebEwmTQ+qpVEOLO5PDnVV5OlkriTQ5WTkLfIESZ7l3dQlkzuSVnJXPkR +do7xkBYT26XiPB+ibOxq1bbhhdL8F46PVzKYH1HrYnPt7KLcDcCyfwrU16g+FGj4CV0 kRcg64NvlxaqDsB+NLi14WoNbNFHsX3a8e+NVFhk8hxU+0ZuMlQN5fHlB9ABoHCmzHAq dmfJoNHjzkJg3t4AZ3TuhHJ9hTPnue9pddrLJdYmPCZ49tiuXAuS5uSmYZd+eTSH9FvF ZeTw== X-Forwarded-Encrypted: i=1; AKwUvBx+Js/o7yzpqnK+Nmkvp4bXWrY8WwaQFz+qHkNMIBHilxyasXi0uwg9p8vChttyx6pcMqIVFbWi5G4X/PY=@vger.kernel.org X-Gm-Message-State: AFuF++nR0JH2LN7e5hDZoRML78Xmkws0CNHzODx31QMA4Qf4sVgn55GI hAscHbtvkNbNe3ZJEdoRhCV6Lcmo4hAJZAVXWhCQyO/d89ahh+evsMauSEnoRdVzg4ASNliphP4 kLYVzHYRwBEjnwEGqaYVQCj29ldDW5b196/S6VZXWXBlA211sU1CJhvjfPpIvcS3crcqXaj8Fdo o= X-Gm-Gg: AYBFou339GZwyP2aAMrouHMpYN21U2Cazoqwqxjt+nmAlMYuMdlb53kcPRxscQQM0kx 6ze+mJEo5jCtPJ97AOrSWJsYfLDKQJMdkd4KUXhk1bswDEKNgN1ZNIYAMX5t5ZA1TQo/MPkdx41 W8FFyj/BFUTUaM7/A5K2VTjpXbAv3D9O4V1Ux0FBJhM51Fyk57LVVId0rH/g5CaYdajFegTRzzR WcemDSCznu/bPwAtY/5uWPWsBhAkqqINAeiD07F+JELTDfKmz1SZqp4AhA2Bpp5Ijvlj+MLzvan 9QWxKmN/GMd3UDvJMhAyozAebbZ4q2yTTkdT97Iit0zVUI61pck+DRTqrReiqQAKkaxntffFsVz EbWVzXtrYO1RMmDIzfGQPa2SG+zyf X-Received: by 2002:a05:620a:4494:b0:939:d440:e530 with SMTP id af79cd13be357-939d492a0b3mr120412585a.9.1788957449701; Wed, 09 Sep 2026 05:37:29 -0700 (PDT) X-Received: by 2002:a05:620a:4494:b0:939:d440:e530 with SMTP id af79cd13be357-939d492a0b3mr120400985a.9.1788957448984; Wed, 09 Sep 2026 05:37:28 -0700 (PDT) Received: from brgl-qcom.local ([2a01:cb1d:dc:7e00:de8b:7fd5:2738:cc2a]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-4859162f354sm36317328f8f.20.2026.09.09.05.37.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 09 Sep 2026 05:37:28 -0700 (PDT) From: Bartosz Golaszewski Date: Wed, 09 Sep 2026 14:37:09 +0200 Subject: [PATCH v2 3/4] power: sequencing: fix NULL-pointer dereference in pwrseq_device_register() Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-pwrseq-kunit-v2-3-ef496afc89d2@oss.qualcomm.com> References: <20260909-pwrseq-kunit-v2-0-ef496afc89d2@oss.qualcomm.com> In-Reply-To: <20260909-pwrseq-kunit-v2-0-ef496afc89d2@oss.qualcomm.com> To: Bartosz Golaszewski , Manivannan Sadhasivam , Chen-Yu Tsai , Krzysztof Kozlowski Cc: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, Bartosz Golaszewski , stable@vger.kernel.org, sashiko-bot X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=1686; i=bartosz.golaszewski@oss.qualcomm.com; h=from:subject:message-id; bh=aQ4wUnjGc5pmfoIzvGc2IT9dZtMCvDmo5D6QlqxAXbE=; b=owEBbQKS/ZANAwAKAQWdLsv/NoTDAcsmYgBqoVL/Sq/tgXU4/X6gqqO38xFKMT5GqgUCVy035 4yRgiUMKpGJAjMEAAEKAB0WIQSR5RMt5bVGHXuiZfwFnS7L/zaEwwUCaqFS/wAKCRAFnS7L/zaE w4w8D/9JI9hjiLlbLoDtubGkshl0wI00tYT8GieAZK85Lpf3rJ0tsRqQsm0Upc9WGH5N+eqK+ce brP1+0Dl8XNaNGsnn3LPyrLRQJ5kt/numaL4MBHj5KcHtAVT+0kE96m/svFzw2pEsqdVEynSOLl 8JJZT5HYeQh5sFdJm1nqfAxE26c18BofBEh5r83X1ztYQZrCgPaSC4Z3t+20nTEjkuqMQojqKqz 272HnjTBbZQ5dk7KnT55uKB56GiEt4ukZKUOhKC3vzuLMzK5H4+E8+AsPNpF47TJ2j9bmoLxKYt cgAoy4V2kG+rQrLDr8Cq6z24pRc4T+lC2Cr/EUuRM7+YokTgJDVpVCpPEj4ReFJ+PaArguB8QgR tmFrdOQhUJdjpoI3tnq/A5X66miBO1WEpaEpeHJetrMUC9XWwJyZnOhfIUlPv+Z4s26dKDnYswH 7WMYfM60lD69clYfHHwhSqjfeVGpOojUh7L8khYtzPADpIFXRdJAeEkdOkflSt+bQ9mWLw3Lf5A xoR+rcCtzAsmjbJu+n6yUzBsJpui8VQC3nevIlVyqw7OvvoIrQKUyjAd8sTs6yDHkLi2ZQLZHUg VqU0ZbIYs6imQ7vPlwNt0axvDRnvup0HE/y2HcNoS5qdE5WNd60ahMPdp7lagcb+8ZuZHOMuIjd YKmmWSFncRYEyPA== X-Developer-Key: i=bartosz.golaszewski@oss.qualcomm.com; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTA5MDE0MSBTYWx0ZWRfX0zx39KGHHpTr MAPQkY7vG6gCedubL4h7Kaf9EUh3gjOxDO+S9S1sJUy3hSaSLmN24xn7CNgD6fUa5tyCrys9QLy KXngOhOD5t+qgKdrS6TSqJX5ChlpI+hVt0LBFraJx4G4RK6Vm1GD6bX480Vq7dn6f/Db189UwLd bucJrSALtaLO5/8l/UTcQUAw7miVtieogILvBZZH2UCxqQOb1kMXv22fqd5gzqzuHM8Xrga6R7w k5TLac67OlQKrhEl9TXt7Vwq//uWN30enBtL707o9utVgqlGxU9e0GKOiwGvqpbO2WcpKZKj/IO TVfG/Xovh/bsJNJPtJzCssLy/8X920T65ZmgoXk20/XEIz/BO7be8SYO+Z+ggXqameUpRG0gufL 81rPVQGujfKDP1KWPLxOtr7ttWQj18N7T6/6N289kBuvHV9se50xWNCttjzmFgmeLWB1HrR7ybW 6KNDnTxSUKh+YjCZIxg== X-Proofpoint-Spam-Info: AW1haW4tMjYwOTA5MDE0MSBTYWx0ZWRfX9NjiBqkfwvI1 GhswJ/tkB8trCB4y7bUO0T9BcK1cv8eiu3vlv5/c3HFa2ICQl8r2H724+RbKkEZ0jxq+gKqOye4 XPP5taVO2YWBzlopfsUUzwQKaE8yxoY= X-Proofpoint-GUID: -6_s64xHLlAFD30GEWEPvc0gSp3nNbrx X-Authority-Analysis: v=2.4 cv=KOlqylFo c=1 sm=1 tr=0 ts=6aa1530a cx=c_pps a=HLyN3IcIa5EE8TELMZ618Q==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=Um2Pa8k9VHT-vaBCBUpS:22 a=c92rfblmAAAA:8 a=EUspDBNiAAAA:8 a=VwQbUJbxAAAA:8 a=lh2aROLewo0y3NVoW6AA:9 a=QEXdDO2ut3YA:10 a=bTQJ7kPSJx9SKPbeHEYW:22 a=GvGzcOZaWPEFPQC_NcjD:22 X-Proofpoint-ORIG-GUID: -6_s64xHLlAFD30GEWEPvc0gSp3nNbrx 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-08_03,2026-09-09_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 adultscore=0 priorityscore=1501 spamscore=0 impostorscore=0 phishscore=0 lowpriorityscore=0 bulkscore=0 suspectscore=0 clxscore=1015 malwarescore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2609090141 If dev_set_name() fails in pwrseq_device_register(), we jump to the err_put_pwrseq label before initializing pwrseq->targets. pwrseq_release() will try to iterate over targets unconditionally and subsequently dereference an invalid pointer. Move the call to dev_set_name() after the list head is initialized. Fixes: 249ebf3f65f8 ("power: sequencing: implement the pwrseq core") Cc: stable@vger.kernel.org Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260903-pwrseq-kunit-v1-0-1f893d2ca= bc2%40oss.qualcomm.com?part=3D2 Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/cor= e.c index a092031943397241b2a9970de5793a0bbc8d7ecf..3076b3879af9e29261ff1f4b133= bacdb74489d7c 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -505,10 +505,6 @@ pwrseq_device_register(const struct pwrseq_config *con= fig) */ device_initialize(&pwrseq->dev); =20 - ret =3D dev_set_name(&pwrseq->dev, "pwrseq.%d", pwrseq->id); - if (ret) - goto err_put_pwrseq; - pwrseq->owner =3D config->owner ?: THIS_MODULE; pwrseq->match =3D config->match; =20 @@ -517,6 +513,10 @@ pwrseq_device_register(const struct pwrseq_config *con= fig) INIT_LIST_HEAD(&pwrseq->targets); INIT_LIST_HEAD(&pwrseq->units); =20 + ret =3D dev_set_name(&pwrseq->dev, "pwrseq.%d", pwrseq->id); + if (ret) + goto err_put_pwrseq; + ret =3D pwrseq_setup_targets(config->targets, pwrseq); if (ret) goto err_put_pwrseq; --=20 2.47.3 From nobody Fri Sep 25 19:16:06 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 CF559560AB4 for ; Wed, 9 Sep 2026 12:37:35 +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=1788957461; cv=none; b=L/GGpQF7+xbnMX6U4nCP4Qx0Nz0xti5qcYvzHDAk0+lSSo5j/DosSTNYaEI8caIaZdvZCXWzgctnbfaiG5vOjQmN6SmW7tnQxB4M5AZKyCpzGh5UVsuSEUB5ZfX7O27/Ig7K2DgEIAYvVycHKVS7NMb/oeltIFy1VXmm+eaOLOY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788957461; c=relaxed/simple; bh=87I7IBOfoO1/OUKB8jLS15WMIS9h1aPeWI6Zuv25XGQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Tar7xiMy3nvikVjrbjqHCzLMBBeFQsHC5kkwW36WeuAITa9O4Sd0hC3oIc2mmr76Hdcu2wXIW9bHfgmKF+cIauIzEqKtagBnngxquvU6oXGne/w7S2fKJgnmLvsOIq6QZzqj1vnGtFtqOcYiHLkHxZtvXPOB+KMj8ZUfl9z2vl0= 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=Tcw8v0n1; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=ghNVRoTK; 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="Tcw8v0n1"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="ghNVRoTK" Received: from pps.filterd (m0279865.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 689BoUjL2698506 for ; Wed, 9 Sep 2026 12:37:34 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= 5IXu4DZ5j3g12muqhUXwDxqYZMQhzBEXoYPVjup8Kq0=; b=Tcw8v0n1a8kDglrL AOK+l7Tf3AZGQ80J8Ghgu7jUjWCRor+mUA3aOW3qsBarEy8JkFjnNblxhwQBlXPj LTN6mLhoqHqn4HX+0fYj/CVXx0Xewk9DwwW/LnaEkyjDDiN5z2gjbcX9KXSavVz3 802Uh6PvnOl04Ui9H9rsb+fbrDYrcYeKohBEW7rGSpK0njQs3VWmq+Hp1cg7zEo3 b3uvXcOU91ILI2QxaNr9vCg9DtPW4XiZGR4M9pKLwDhJKiKaotrOPf6ayospJkPb TyYX2bIKuyLrRAoU1QgObgXYbVQGF6nB9KaytXS/o83pukbLt2z+GiWwK5JLIHGv LWylfQ== Received: from mail-vs1-f72.google.com (mail-vs1-f72.google.com [209.85.217.72]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gk6xh059v-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Wed, 09 Sep 2026 12:37:33 +0000 (GMT) Received: by mail-vs1-f72.google.com with SMTP id ada2fe7eead31-78bb7517525so723709137.3 for ; Wed, 09 Sep 2026 05:37:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1788957453; x=1789562253; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=5IXu4DZ5j3g12muqhUXwDxqYZMQhzBEXoYPVjup8Kq0=; b=ghNVRoTKrEMzM9+l5U33LdMkcenvPVDRNZcZrUJI/8PIuIiHU1tgAe1vgeOyIhfNL2 JSMrk6ct3P6KUP24DkHPY8SgxgZJ0yNRQ/IB5KpDBF8ziky0lpQ0I+a2i2hs8hCVKF0w g4/KZGyokm1pkGHj/8vro5UFZOUuR9m0UBIl4cfmLNkAkKGFrL6wj2e21VMddDonpSd7 S9CTfBe6tcB8BUQkSbeq5nf+LKbATcb4xB4kZd8qCL0dcj/zHtH2PT9NaFa7he48ykiQ 1tMocj+FFuRknaJzvZWiZytePw4HEqNsbuniqCkVIfR4BsorMh4VW4PC2gkMuonG8tFU cSog== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788957453; x=1789562253; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=5IXu4DZ5j3g12muqhUXwDxqYZMQhzBEXoYPVjup8Kq0=; b=dw9yxZca/OAcd+Y/qVTRp6LDYJ/Lrn/xHLUi1vadOpFuPzNeSpvhetCX4IxKOdUQVP 0XkKWzii94JIQQx4oGgnvy2uzOi68dp03eQ2tgIkUH66f98BgGQ28c1LCLgSvmcmOl8H UBPzQ33tvRkE5CUT3R6H9JmSUhOMA5+nkmWl2JGvEi4V/TG2JQDUothHTqW4nz6Y7EyS 1enr+mYD0933keyjQygnpCRmG/tEZSKYc9B3BrakQiTviMAvfAwwkCYW5vhvrcNuynUI 9SQl3rVg6Cj6AZR5f7deNDB9Jc29NyUvCfPoEVNWbhEI5A1RmmYz299aU5WTotZLtRYU gFyw== X-Forwarded-Encrypted: i=1; AKwUvBzYtFVu40lt4R6EcJVtg4fbBv2VZ4uyM0g9rcICyELP4ixRXSRX/kF4mNExx5fb7U1hgqktOK8XUcMnYX8=@vger.kernel.org X-Gm-Message-State: AFuF++ldbnI567Gp1UclFtLjatsN9+lECcguPHE/TKQfhfWg3bOd6WpV UbhSpj/69x2LNv9SG/muXWYgEnVL/0pdT7LT51bsQAqsd6ZnyT8jOiTJMtHpzXgTHmahBvsziZH YyvhL4G+OOIERi1gKP+Vu/LHyNGsg3iMu038bIFdG1R3+QjhlTqBi450yfnwblBa5rkRbBRTpZq Q= X-Gm-Gg: AYBFou0ETZoqGsLXYlO2yK1SmDPhQc9aTPJeU3AGagUS9ws8Q5pU/XRh3Xd/maX2/Ul +gwLd5/NAZ+C1ME0WdJiAK2y0tJwk0SVgojSA5Yu83AUE1Z9XTgYPyigJvK8KdLQbygitY81geX vPua0nZouBU5lnIjkDg1kVDQlVVa5y6XTD5ktniVpGD1xCkiuFeu/WHU/jv2oO+s/vFc7eFk8DA sTUsgNz2CrcQh5Dpbfm6CKlx+mONI298ejAWFBA/TKe8L1ojEkRERDQtDenuU/NxQ2XVwaFllYW ABn8k8LxMe0hWcNsU2GzEAl9opLYxPvs7e4tM4zI1O495nzbA/yljMYPzmw3U/PEsTsumkHIziS c1GZVCm2wddkkmGtr/eHM4Trb9858 X-Received: by 2002:a05:6102:1606:b0:778:9e3c:5760 with SMTP id ada2fe7eead31-78a4a9621d7mr13474340137.11.1788957452080; Wed, 09 Sep 2026 05:37:32 -0700 (PDT) X-Received: by 2002:a05:6102:1606:b0:778:9e3c:5760 with SMTP id ada2fe7eead31-78a4a9621d7mr13474289137.11.1788957451012; Wed, 09 Sep 2026 05:37:31 -0700 (PDT) Received: from brgl-qcom.local ([2a01:cb1d:dc:7e00:de8b:7fd5:2738:cc2a]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-4859162f354sm36317328f8f.20.2026.09.09.05.37.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 09 Sep 2026 05:37:30 -0700 (PDT) From: Bartosz Golaszewski Date: Wed, 09 Sep 2026 14:37:10 +0200 Subject: [PATCH v2 4/4] power: sequencing: add kunit test cases Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-pwrseq-kunit-v2-4-ef496afc89d2@oss.qualcomm.com> References: <20260909-pwrseq-kunit-v2-0-ef496afc89d2@oss.qualcomm.com> In-Reply-To: <20260909-pwrseq-kunit-v2-0-ef496afc89d2@oss.qualcomm.com> To: Bartosz Golaszewski , Manivannan Sadhasivam , Chen-Yu Tsai , Krzysztof Kozlowski Cc: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, Bartosz Golaszewski X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=45947; i=bartosz.golaszewski@oss.qualcomm.com; h=from:subject:message-id; bh=87I7IBOfoO1/OUKB8jLS15WMIS9h1aPeWI6Zuv25XGQ=; b=owEBbQKS/ZANAwAKAQWdLsv/NoTDAcsmYgBqoVMA3cR1v8wAAqLQGR2CdG5lNO/0IJ/F+aLhB stuHbgiVNKJAjMEAAEKAB0WIQSR5RMt5bVGHXuiZfwFnS7L/zaEwwUCaqFTAAAKCRAFnS7L/zaE w0xiD/9EohHc5Ewj0MdEOu64Yic1akOrKQ4mGBFArlJ4jE2j0LiF/lm4Jn89tAqH7VzUPRr2uLC befoQt0inR/MABzmRHH2nP7bHN1rC0P18gooHHN2H2L/GhGEVpw3IG1s6ExV8Zm7v5h+gL4/4zN Ke/Ek1pB7nxkyGIn/ieZOIhBgmi9tEjfJlAybvav0mrW/8HVffF2iuJybBI6DIfmFRcc2q/kXu7 SVFUZBLBOjVJkZj1+TqCTvEy63RMPjAXoW95LzLGadET4Ln72/yPo+3zSSEtY+HyFMHZSG9mxLu HY9/iLvrNrccbh8eF7NZ/Xr/oQbozrVcnHybk6bZeZnD453YlVCvnoZ8TMhUpOq7ZM3s4CVLHAe NclWj8WsFBa5CiiLyYa6C4CJBJzv9i7d/hB9qigsUBlNB8xP2D2cVLIB7ZSUD/zorBEulDAiNVq oGlZ8G86Et01NbApIEyB6++G0Vu7nUbj1ln3/V+19cvaI+6TCdRdTZlENvuRrwWkzsCiVjdUyQA wupYBIE6wwQJGWfakiIlNFhGkFdj53WVqOG346ACqEwDQBGsLqRG22XIKd8OOy/nK+wSunOvjem 6VdlNBEYHiPEnakYnU3hlCnkXTw9bt4ZkH7+2N/bscZHi+GYgogRgMmDqYvuckf/k8V6QtBIgbu HWDTgr5a+7PQGeg== X-Developer-Key: i=bartosz.golaszewski@oss.qualcomm.com; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTA5MDE0MSBTYWx0ZWRfX19n9GU5N0v4c n3rZQOnRYnSIXawSTypNVVtXNMXYQCPeyXumX+TcLdfozlNsOWV0e8AhQzuvzty69GbxAL75ErN vafjWIeIeBVrGjKqSHmzp4wmnoMN/k7HVir2N6Qrez/mTa5jKqrC34jJmTuLr2TYkhNSh0Kt8bk OCyCiEjVw+5TGSD9mb4IdBw5IQM+CcLA3kERUJMIJcFCWOfzZ0TmtQ9ME+xlscM5UAjbEYA66fG jI90VKi9/5x2/kbzXzatsacl8kvl5v10A5hnyn8gwqMV6uufahP8w9AB99F5zsBROFWI134NoLe bh4P5K0Xr9z148kWsa2+hKlL9KqbBwUdJAIRdA5nu+nW/pUhkvF+9tWj8uJbI8fCdCHQNDNXn6F Wj+ILQ4nNyEbD0tb8IJ2P27PRssRZ3jEr74DfZGant2HeMpOymYNXEHnL16RZPa9tW1PqEauj1I 8FsSENPq1u7mmnNbLZA== X-Proofpoint-Spam-Info: AW1haW4tMjYwOTA5MDE0MSBTYWx0ZWRfX1GNA2oRulM8d Adymk44FMKg2vSzWCT1iWJPE0GsoadLci39tPQtCcAsIe9tGZncY0U6STxImNIAGWziNr8DWEn6 xGIXsmvcMPH5Q3q09EcQiIW1TZ4X9Zs= X-Proofpoint-GUID: uE0rBCfMLwvY0zgLf-dNVG45_vRNZ8Fr X-Authority-Analysis: v=2.4 cv=KOlqylFo c=1 sm=1 tr=0 ts=6aa1530d cx=c_pps a=DUEm7b3gzWu7BqY5nP7+9g==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=VdqzKS8jKosA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=Um2Pa8k9VHT-vaBCBUpS:22 a=EUspDBNiAAAA:8 a=Ap85WKxUwIOrhNPdkRgA:9 a=QEXdDO2ut3YA:10 a=-aSRE8QhW-JAV6biHavz:22 X-Proofpoint-ORIG-GUID: uE0rBCfMLwvY0zgLf-dNVG45_vRNZ8Fr 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-08_03,2026-09-09_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 adultscore=0 priorityscore=1501 spamscore=0 impostorscore=0 phishscore=0 lowpriorityscore=0 bulkscore=0 suspectscore=0 clxscore=1015 malwarescore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2609090141 Add a set of kunit test-cases for the power sequencing subsystem. Verify several use-cases such as detecting circural dependencies, tracking of the enable-count, shared dependencies, swnode lookup, missing target on matching provider, etc. Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/Kconfig | 10 + drivers/power/sequencing/Makefile | 1 + drivers/power/sequencing/pwrseq-kunit.c | 1497 +++++++++++++++++++++++++++= ++++ 3 files changed, 1508 insertions(+) diff --git a/drivers/power/sequencing/Kconfig b/drivers/power/sequencing/Kc= onfig index 1c5f5820f5b76479b380df79971ca5e245e62c6b..a09f18b41feafd97833dffe82e1= 03288ffb6ce1b 100644 --- a/drivers/power/sequencing/Kconfig +++ b/drivers/power/sequencing/Kconfig @@ -46,4 +46,14 @@ config POWER_SEQUENCING_PCIE_M2 connectors. This driver handles the power sequencing for the M.2 connectors exposing multiple interfaces like PCIe, SATA, UART, etc... =20 +config POWER_SEQUENCING_KUNIT + tristate "Build power sequencing KUnit test cases" + depends on KUNIT + default KUNIT_ALL_TESTS + help + Say Y here to build the module containing KUnit test cases for the + power sequencing subsystem. + + If unsure, say N. + endif diff --git a/drivers/power/sequencing/Makefile b/drivers/power/sequencing/M= akefile index 0911d461829897c5018e26dbe475b28f6fb6914c..cde06a5115d0c57b02b1a7d66cc= 641b691c5f5e3 100644 --- a/drivers/power/sequencing/Makefile +++ b/drivers/power/sequencing/Makefile @@ -6,3 +6,4 @@ pwrseq-core-y :=3D core.o obj-$(CONFIG_POWER_SEQUENCING_QCOM_WCN) +=3D pwrseq-qcom-wcn.o obj-$(CONFIG_POWER_SEQUENCING_TH1520_GPU) +=3D pwrseq-thead-gpu.o obj-$(CONFIG_POWER_SEQUENCING_PCIE_M2) +=3D pwrseq-pcie-m2.o +obj-$(CONFIG_POWER_SEQUENCING_KUNIT) +=3D pwrseq-kunit.o diff --git a/drivers/power/sequencing/pwrseq-kunit.c b/drivers/power/sequen= cing/pwrseq-kunit.c new file mode 100644 index 0000000000000000000000000000000000000000..c2899f3b16e6450eab7178e58d7= 5e9ddc005d5e5 --- /dev/null +++ b/drivers/power/sequencing/pwrseq-kunit.c @@ -0,0 +1,1497 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Qualcomm Technologies, Inc. and/or its subsidiaries + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define PWRSEQ_TEST_PARENT "pwrseq-test-parent" +#define PWRSEQ_TEST_CONSUMER "pwrseq-test-consumer" + +#define PWRSEQ_SWNODE_TEST_PROVIDER "pwrseq-swnode-test-provider" +#define PWRSEQ_SWNODE_TEST_PROVIDER_2 "pwrseq-swnode-test-provider-2" +#define PWRSEQ_PROBE_ORDER_TEST_CONSUMER "pwrseq-probe-order-test-consumer" +#define PWRSEQ_PROBE_DEFER_TEST_CONSUMER "pwrseq-probe-defer-test-consumer" + +static const struct software_node pwrseq_test_provider_swnode =3D { + .name =3D "pwrseq-test-provider", +}; + +KUNIT_DEFINE_ACTION_WRAPPER(pwrseq_device_unregister_wrapper, + pwrseq_device_unregister, + struct pwrseq_device *); + +static struct pwrseq_device * +kunit_pwrseq_device_register(struct kunit *test, + const struct pwrseq_config *config) +{ + struct pwrseq_device *pwrseq; + int ret; + + pwrseq =3D pwrseq_device_register(config); + if (IS_ERR(pwrseq)) + return pwrseq; + + ret =3D kunit_add_action_or_reset(test, pwrseq_device_unregister_wrapper, + pwrseq); + if (ret) + return ERR_PTR(ret); + + return pwrseq; +} + +KUNIT_DEFINE_ACTION_WRAPPER(pwrseq_put_wrapper, pwrseq_put, + struct pwrseq_desc *); + +static struct pwrseq_desc * +kunit_pwrseq_get(struct kunit *test, struct device *dev, const char *targe= t) +{ + struct pwrseq_desc *desc; + int ret; + + desc =3D pwrseq_get(dev, target); + if (IS_ERR(desc)) + return desc; + + ret =3D kunit_add_action_or_reset(test, pwrseq_put_wrapper, desc); + if (ret) + return ERR_PTR(ret); + + return desc; +} + +struct pwrseq_test_ctx { + const char *consumer_name; + int enable_count_a; + int enable_count_b; + int disable_count_a; + int disable_count_b; + int enable_calls_a; + int post_enable_calls; + bool enable_error_a; + bool enable_error_b; + bool post_enable_error; +}; + +static int pwrseq_test_no_match(struct pwrseq_device *pwrseq, + struct device *dev) +{ + return PWRSEQ_NO_MATCH; +} + +static int pwrseq_test_match_by_name(struct pwrseq_device *pwrseq, + struct device *dev) +{ + struct pwrseq_test_ctx *ctx =3D pwrseq_device_get_drvdata(pwrseq); + + return strcmp(dev_name(dev), ctx->consumer_name) =3D=3D 0 ? + PWRSEQ_MATCH_OK : PWRSEQ_NO_MATCH; +} + +static int pwrseq_test_match_always(struct pwrseq_device *pwrseq, + struct device *dev) +{ + return PWRSEQ_MATCH_OK; +} + +static int pwrseq_test_enable_a(struct pwrseq_device *pwrseq) +{ + struct pwrseq_test_ctx *ctx =3D pwrseq_device_get_drvdata(pwrseq); + + ctx->enable_calls_a++; + + if (ctx->enable_error_a) + return -EIO; + + ctx->enable_count_a++; + + return 0; +} + +static int pwrseq_test_disable_a(struct pwrseq_device *pwrseq) +{ + struct pwrseq_test_ctx *ctx =3D pwrseq_device_get_drvdata(pwrseq); + + ctx->disable_count_a++; + + return 0; +} + +static int pwrseq_test_enable_b(struct pwrseq_device *pwrseq) +{ + struct pwrseq_test_ctx *ctx =3D pwrseq_device_get_drvdata(pwrseq); + + if (ctx->enable_error_b) + return -EIO; + + ctx->enable_count_b++; + + return 0; +} + +static int pwrseq_test_disable_b(struct pwrseq_device *pwrseq) +{ + struct pwrseq_test_ctx *ctx =3D pwrseq_device_get_drvdata(pwrseq); + + ctx->disable_count_b++; + + return 0; +} + +static int pwrseq_test_post_enable(struct pwrseq_device *pwrseq) +{ + struct pwrseq_test_ctx *ctx =3D pwrseq_device_get_drvdata(pwrseq); + + ctx->post_enable_calls++; + + return ctx->post_enable_error ? -EIO : 0; +} + +static int pwrseq_test_parent_probe(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver pwrseq_test_parent_driver =3D { + .probe =3D pwrseq_test_parent_probe, + .driver =3D { + .name =3D PWRSEQ_TEST_PARENT, + }, +}; + +static int pwrseq_test_parent_init(struct kunit *test) +{ + static const struct platform_device_info pdevinfo =3D { + .name =3D PWRSEQ_TEST_PARENT, + .id =3D PLATFORM_DEVID_NONE, + }; + + struct platform_device *pdev; + bool bound; + int ret; + + ret =3D kunit_platform_driver_register(test, &pwrseq_test_parent_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + pdev =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + wait_for_device_probe(); + scoped_guard(device, &pdev->dev) + bound =3D device_is_bound(&pdev->dev); + + KUNIT_ASSERT_TRUE(test, bound); + + test->priv =3D pdev; + + return 0; +} + +/* + * Test that registering a power sequencer whose unit dependency graph + * contains a two-node cycle (A -> B -> A) is rejected with -EINVAL. + */ +static void pwrseq_circular_deps(struct kunit *test) +{ + const struct pwrseq_unit_data *unit_a_deps[2] =3D { }; + const struct pwrseq_unit_data *unit_b_deps[2] =3D { }; + const struct pwrseq_target_data *targets[2] =3D { }; + struct platform_device *pdev =3D test->priv; + struct pwrseq_unit_data unit_a, unit_b; + struct pwrseq_target_data target; + struct pwrseq_device *pwrseq; + struct pwrseq_config config; + + unit_a =3D (struct pwrseq_unit_data){ + .name =3D "unit-a", + .deps =3D unit_a_deps, + }; + + unit_b =3D (struct pwrseq_unit_data){ + .name =3D "unit-b", + .deps =3D unit_b_deps, + }; + + unit_a_deps[0] =3D &unit_b; + unit_b_deps[0] =3D &unit_a; + + target =3D (struct pwrseq_target_data){ + .name =3D "test-target", + .unit =3D &unit_a, + }; + + targets[0] =3D ⌖ + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .match =3D pwrseq_test_no_match, + .targets =3D targets, + }; + + kunit_warning_suppress(test) { + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1); + } + + KUNIT_EXPECT_TRUE(test, IS_ERR(pwrseq)); + KUNIT_EXPECT_EQ(test, PTR_ERR(pwrseq), -EINVAL); +} + +/* + * Test that a longer chain cycle (A -> B -> C -> D -> A) is also rejected + * with -EINVAL. + */ +static void pwrseq_circular_deps_chain(struct kunit *test) +{ + struct pwrseq_unit_data unit_a, unit_b, unit_c, unit_d; + const struct pwrseq_unit_data *unit_a_deps[2] =3D { }; + const struct pwrseq_unit_data *unit_b_deps[2] =3D { }; + const struct pwrseq_unit_data *unit_c_deps[2] =3D { }; + const struct pwrseq_unit_data *unit_d_deps[2] =3D { }; + const struct pwrseq_target_data *targets[2] =3D { }; + struct platform_device *pdev =3D test->priv; + struct pwrseq_target_data target; + struct pwrseq_device *pwrseq; + struct pwrseq_config config; + + unit_a =3D (struct pwrseq_unit_data){ + .name =3D "unit-a", + .deps =3D unit_a_deps, + }; + unit_b =3D (struct pwrseq_unit_data){ + .name =3D "unit-b", + .deps =3D unit_b_deps, + }; + unit_c =3D (struct pwrseq_unit_data){ + .name =3D "unit-c", + .deps =3D unit_c_deps, + }; + unit_d =3D (struct pwrseq_unit_data){ + .name =3D "unit-d", + .deps =3D unit_d_deps, + }; + + unit_a_deps[0] =3D &unit_b; + unit_b_deps[0] =3D &unit_c; + unit_c_deps[0] =3D &unit_d; + unit_d_deps[0] =3D &unit_a; + + target =3D (struct pwrseq_target_data){ + .name =3D "test-target", + .unit =3D &unit_a, + }; + + targets[0] =3D ⌖ + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .match =3D pwrseq_test_no_match, + .targets =3D targets, + }; + + kunit_warning_suppress(test) { + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1); + } + + KUNIT_EXPECT_TRUE(test, IS_ERR(pwrseq)); + KUNIT_EXPECT_EQ(test, PTR_ERR(pwrseq), -EINVAL); +} + +/* + * Test that a valid acyclic dependency graph (A -> B) registers successfu= lly. + */ +static void pwrseq_register_valid(struct kunit *test) +{ + const struct pwrseq_unit_data *unit_a_deps[2] =3D { }; + const struct pwrseq_target_data *targets[2] =3D { }; + struct platform_device *pdev =3D test->priv; + struct pwrseq_unit_data unit_a, unit_b; + struct pwrseq_target_data target; + struct pwrseq_device *pwrseq; + struct pwrseq_config config; + + unit_b =3D (struct pwrseq_unit_data){ + .name =3D "unit-b", + }; + + unit_a_deps[0] =3D &unit_b; + unit_a =3D (struct pwrseq_unit_data){ + .name =3D "unit-a", + .deps =3D unit_a_deps, + }; + + target =3D (struct pwrseq_target_data){ + .name =3D "test-target", + .unit =3D &unit_a, + }; + + targets[0] =3D ⌖ + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .match =3D pwrseq_test_no_match, + .targets =3D targets, + }; + + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pwrseq); +} + +/* + * Test that registration is rejected with -EINVAL when .parent, .match or + * .targets is missing, or when .targets is a non-NULL but empty array. + */ +static void pwrseq_register_invalid_args(struct kunit *test) +{ + const struct pwrseq_target_data *empty_targets[1] =3D { }; + const struct pwrseq_target_data *targets[2] =3D { }; + struct platform_device *pdev =3D test->priv; + struct pwrseq_target_data target; + struct pwrseq_unit_data unit; + struct pwrseq_device *pwrseq; + struct pwrseq_config config; + + unit =3D (struct pwrseq_unit_data){ .name =3D "unit-a" }; + target =3D (struct pwrseq_target_data){ + .name =3D "test-target", + .unit =3D &unit, + }; + targets[0] =3D ⌖ + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .match =3D pwrseq_test_no_match, + .targets =3D targets, + }; + + config.parent =3D NULL; + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_EXPECT_TRUE(test, IS_ERR(pwrseq)); + KUNIT_EXPECT_EQ(test, PTR_ERR(pwrseq), -EINVAL); + config.parent =3D &pdev->dev; + + config.match =3D NULL; + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_EXPECT_TRUE(test, IS_ERR(pwrseq)); + KUNIT_EXPECT_EQ(test, PTR_ERR(pwrseq), -EINVAL); + config.match =3D pwrseq_test_no_match; + + config.targets =3D NULL; + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_EXPECT_TRUE(test, IS_ERR(pwrseq)); + KUNIT_EXPECT_EQ(test, PTR_ERR(pwrseq), -EINVAL); + + config.targets =3D empty_targets; + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_EXPECT_TRUE(test, IS_ERR(pwrseq)); + KUNIT_EXPECT_EQ(test, PTR_ERR(pwrseq), -EINVAL); +} + +/* + * Test that a target without a unit is rejected with -EINVAL. + */ +static void pwrseq_register_target_without_unit(struct kunit *test) +{ + const struct pwrseq_target_data *targets[2] =3D { }; + struct platform_device *pdev =3D test->priv; + struct pwrseq_target_data target; + struct pwrseq_device *pwrseq; + struct pwrseq_config config; + + target =3D (struct pwrseq_target_data){ + .name =3D "test-target", + .unit =3D NULL, + }; + targets[0] =3D ⌖ + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .match =3D pwrseq_test_no_match, + .targets =3D targets, + }; + + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_EXPECT_TRUE(test, IS_ERR(pwrseq)); + KUNIT_EXPECT_EQ(test, PTR_ERR(pwrseq), -EINVAL); +} + +/* + * Test that enabling and disabling a single-unit target increments and + * decrements enable_count correctly and fires the enable/disable callbacks + * exactly once. + */ +static void pwrseq_enable_disable(struct kunit *test) +{ + const struct pwrseq_target_data *targets[2] =3D { }; + struct platform_device *pdev =3D test->priv; + struct platform_device_info pdevinfo; + struct pwrseq_target_data target; + struct pwrseq_test_ctx *ctx; + struct pwrseq_unit_data unit; + struct pwrseq_device *pwrseq; + struct pwrseq_config config; + struct pwrseq_desc *desc; + int ret; + + ctx =3D kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + unit =3D (struct pwrseq_unit_data){ + .name =3D "unit-a", + .enable =3D pwrseq_test_enable_a, + .disable =3D pwrseq_test_disable_a, + }; + + target =3D (struct pwrseq_target_data){ + .name =3D "test-target", + .unit =3D &unit, + }; + + targets[0] =3D ⌖ + + ctx->consumer_name =3D PWRSEQ_TEST_CONSUMER; + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .drvdata =3D ctx, + .match =3D pwrseq_test_match_by_name, + .targets =3D targets, + }; + + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pwrseq); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_TEST_CONSUMER, + .id =3D PLATFORM_DEVID_NONE, + }; + + pdev =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + desc =3D kunit_pwrseq_get(test, &pdev->dev, "test-target"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc); + + ret =3D pwrseq_enable(desc); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->enable_count_a, 1); + KUNIT_EXPECT_EQ(test, ctx->disable_count_a, 0); + + ret =3D pwrseq_disable(desc); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->enable_count_a, 1); + KUNIT_EXPECT_EQ(test, ctx->disable_count_a, 1); +} + +/* + * Test that two consumers sharing a common dependency unit cause it to be + * enabled only once and disabled only once, while enable_count tracks each + * consumer correctly. + */ +static void pwrseq_shared_deps(struct kunit *test) +{ + const struct pwrseq_unit_data *unit_a_deps[2] =3D { }; + const struct pwrseq_unit_data *unit_b_deps[2] =3D { }; + const struct pwrseq_target_data *targets[3] =3D { }; + struct pwrseq_unit_data dep_unit, unit_a, unit_b; + struct pwrseq_target_data target_a, target_b; + struct platform_device *parent =3D test->priv; + struct platform_device *pdev_a, *pdev_b; + struct platform_device_info pdevinfo; + struct pwrseq_desc *desc_a, *desc_b; + struct pwrseq_device *pwrseq; + struct pwrseq_config config; + struct pwrseq_test_ctx *ctx; + int ret; + + ctx =3D kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + dep_unit =3D (struct pwrseq_unit_data){ + .name =3D "dep", + .enable =3D pwrseq_test_enable_a, + .disable =3D pwrseq_test_disable_a, + }; + + unit_a_deps[0] =3D &dep_unit; + unit_a =3D (struct pwrseq_unit_data){ + .name =3D "unit-a", + .deps =3D unit_a_deps, + .enable =3D pwrseq_test_enable_b, + .disable =3D pwrseq_test_disable_b, + }; + + unit_b_deps[0] =3D &dep_unit; + unit_b =3D (struct pwrseq_unit_data){ + .name =3D "unit-b", + .deps =3D unit_b_deps, + }; + + target_a =3D (struct pwrseq_target_data){ + .name =3D "target-a", + .unit =3D &unit_a, + }; + target_b =3D (struct pwrseq_target_data){ + .name =3D "target-b", + .unit =3D &unit_b, + }; + + targets[0] =3D &target_a; + targets[1] =3D &target_b; + + ctx->consumer_name =3D PWRSEQ_TEST_CONSUMER "-a"; + + config =3D (struct pwrseq_config){ + .parent =3D &parent->dev, + .drvdata =3D ctx, + .match =3D pwrseq_test_match_by_name, + .targets =3D targets, + }; + + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pwrseq); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_TEST_CONSUMER "-a", + .id =3D PLATFORM_DEVID_NONE, + }; + pdev_a =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev_a); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_TEST_CONSUMER "-b", + .id =3D PLATFORM_DEVID_NONE, + }; + pdev_b =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev_b); + + /* + * match_by_name() only matches one consumer name at a time, so + * reconfigure ctx->consumer_name between the two pwrseq_get() calls + * below to acquire descriptors for both consumer-a and consumer-b. + */ + desc_a =3D kunit_pwrseq_get(test, &pdev_a->dev, "target-a"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc_a); + + ctx->consumer_name =3D PWRSEQ_TEST_CONSUMER "-b"; + desc_b =3D kunit_pwrseq_get(test, &pdev_b->dev, "target-b"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc_b); + + ret =3D pwrseq_enable(desc_a); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->enable_count_a, 1); + KUNIT_EXPECT_EQ(test, ctx->enable_count_b, 1); + + ret =3D pwrseq_enable(desc_b); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->enable_count_a, 1); + + ret =3D pwrseq_disable(desc_a); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->disable_count_a, 0); + KUNIT_EXPECT_EQ(test, ctx->disable_count_b, 1); + + ret =3D pwrseq_disable(desc_b); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->disable_count_a, 1); +} + +/* + * Test that calling pwrseq_enable() twice on the same descriptor has no + * effect. + */ +static void pwrseq_enable_idempotent(struct kunit *test) +{ + const struct pwrseq_target_data *targets[2] =3D { }; + struct platform_device *pdev =3D test->priv; + struct platform_device_info pdevinfo; + struct pwrseq_target_data target; + struct pwrseq_device *pwrseq; + struct pwrseq_unit_data unit; + struct pwrseq_config config; + struct pwrseq_test_ctx *ctx; + struct pwrseq_desc *desc; + int ret; + + ctx =3D kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + unit =3D (struct pwrseq_unit_data){ + .name =3D "unit-a", + .enable =3D pwrseq_test_enable_a, + .disable =3D pwrseq_test_disable_a, + }; + + target =3D (struct pwrseq_target_data){ + .name =3D "test-target", + .unit =3D &unit, + }; + + targets[0] =3D ⌖ + + ctx->consumer_name =3D PWRSEQ_TEST_CONSUMER; + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .drvdata =3D ctx, + .match =3D pwrseq_test_match_by_name, + .targets =3D targets, + }; + + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pwrseq); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_TEST_CONSUMER, + .id =3D PLATFORM_DEVID_NONE, + }; + + pdev =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + desc =3D kunit_pwrseq_get(test, &pdev->dev, "test-target"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc); + + ret =3D pwrseq_enable(desc); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->enable_count_a, 1); + + /* Second power_on on same descriptor must be a no-op. */ + ret =3D pwrseq_enable(desc); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->enable_count_a, 1); + + ret =3D pwrseq_disable(desc); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->disable_count_a, 1); +} + +/* + * Test that when a dependency unit's enable() callback fails, the error + * propagates to the caller, the top unit's enable callback is never reach= ed, + * and target->post_enable() is never invoked. + */ +static void pwrseq_enable_enable_error(struct kunit *test) +{ + const struct pwrseq_target_data *targets[2] =3D { }; + const struct pwrseq_unit_data *top_deps[2] =3D { }; + struct pwrseq_unit_data dep_unit, top_unit; + struct platform_device *pdev =3D test->priv; + struct platform_device_info pdevinfo; + struct pwrseq_target_data target; + struct pwrseq_device *pwrseq; + struct pwrseq_config config; + struct pwrseq_test_ctx *ctx; + struct pwrseq_desc *desc; + int ret; + + ctx =3D kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + dep_unit =3D (struct pwrseq_unit_data){ + .name =3D "dep", + .enable =3D pwrseq_test_enable_a, + }; + + top_deps[0] =3D &dep_unit; + top_unit =3D (struct pwrseq_unit_data){ + .name =3D "top", + .deps =3D top_deps, + .enable =3D pwrseq_test_enable_b, + }; + + target =3D (struct pwrseq_target_data){ + .name =3D "test-target", + .unit =3D &top_unit, + .post_enable =3D pwrseq_test_post_enable, + }; + + targets[0] =3D ⌖ + + ctx->consumer_name =3D PWRSEQ_TEST_CONSUMER; + ctx->enable_error_a =3D true; + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .drvdata =3D ctx, + .match =3D pwrseq_test_match_by_name, + .targets =3D targets, + }; + + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pwrseq); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_TEST_CONSUMER, + .id =3D PLATFORM_DEVID_NONE, + }; + + pdev =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + desc =3D kunit_pwrseq_get(test, &pdev->dev, "test-target"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc); + + ret =3D pwrseq_enable(desc); + KUNIT_EXPECT_EQ(test, ret, -EIO); + /* + * Dependency's enable() was attempted but failed, so it never + * incremented. + */ + KUNIT_EXPECT_EQ(test, ctx->enable_calls_a, 1); + KUNIT_EXPECT_EQ(test, ctx->enable_count_a, 0); + /* Target unit's enable() was never reached. */ + KUNIT_EXPECT_EQ(test, ctx->enable_count_b, 0); + /* Target's .post_enable() must not run on an unpowered sequencer. */ + KUNIT_EXPECT_EQ(test, ctx->post_enable_calls, 0); +} + +/* + * Test that when the top unit's own enable() callback fails after its + * dependency's enable() already succeeded, the dependency is rolled back. + */ +static void pwrseq_enable_rollback_on_top_failure(struct kunit *test) +{ + const struct pwrseq_target_data *targets[2] =3D { }; + const struct pwrseq_unit_data *top_deps[2] =3D { }; + struct pwrseq_unit_data dep_unit, top_unit; + struct platform_device *pdev =3D test->priv; + struct platform_device_info pdevinfo; + struct pwrseq_target_data target; + struct pwrseq_device *pwrseq; + struct pwrseq_config config; + struct pwrseq_test_ctx *ctx; + struct pwrseq_desc *desc; + int ret; + + ctx =3D kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + dep_unit =3D (struct pwrseq_unit_data){ + .name =3D "dep", + .enable =3D pwrseq_test_enable_a, + .disable =3D pwrseq_test_disable_a, + }; + + top_deps[0] =3D &dep_unit; + top_unit =3D (struct pwrseq_unit_data){ + .name =3D "top", + .deps =3D top_deps, + .enable =3D pwrseq_test_enable_b, + }; + + target =3D (struct pwrseq_target_data){ + .name =3D "test-target", + .unit =3D &top_unit, + }; + + targets[0] =3D ⌖ + + ctx->consumer_name =3D PWRSEQ_TEST_CONSUMER; + ctx->enable_error_b =3D true; + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .drvdata =3D ctx, + .match =3D pwrseq_test_match_by_name, + .targets =3D targets, + }; + + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pwrseq); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_TEST_CONSUMER, + .id =3D PLATFORM_DEVID_NONE, + }; + + pdev =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + desc =3D kunit_pwrseq_get(test, &pdev->dev, "test-target"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc); + + ret =3D pwrseq_enable(desc); + KUNIT_EXPECT_EQ(test, ret, -EIO); + /* + * Dependency was enabled, then rolled back once top's own enable + * failed. + */ + KUNIT_EXPECT_EQ(test, ctx->enable_count_a, 1); + KUNIT_EXPECT_EQ(test, ctx->disable_count_a, 1); + /* + * Target unit's own enable never incremented since it returned an + * error. + */ + KUNIT_EXPECT_EQ(test, ctx->enable_count_b, 0); +} + +/* + * Test target->post_enable() on both the success and failure paths: it mu= st + * run exactly once per pwrseq_enable() call that actually powers the targ= et + * unit on, and a failing post_enable() must roll the unit back and clear + * powered_on so a subsequent pwrseq_enable() is called again. + */ +static void pwrseq_enable_post_enable(struct kunit *test) +{ + const struct pwrseq_target_data *targets[2] =3D { }; + struct platform_device *pdev =3D test->priv; + struct platform_device_info pdevinfo; + struct pwrseq_target_data target; + struct pwrseq_unit_data unit; + struct pwrseq_device *pwrseq; + struct pwrseq_config config; + struct pwrseq_test_ctx *ctx; + struct pwrseq_desc *desc; + int ret; + + ctx =3D kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + unit =3D (struct pwrseq_unit_data){ + .name =3D "unit-a", + .enable =3D pwrseq_test_enable_a, + .disable =3D pwrseq_test_disable_a, + }; + + target =3D (struct pwrseq_target_data){ + .name =3D "test-target", + .unit =3D &unit, + .post_enable =3D pwrseq_test_post_enable, + }; + + targets[0] =3D ⌖ + + ctx->consumer_name =3D PWRSEQ_TEST_CONSUMER; + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .drvdata =3D ctx, + .match =3D pwrseq_test_match_by_name, + .targets =3D targets, + }; + + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pwrseq); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_TEST_CONSUMER, + .id =3D PLATFORM_DEVID_NONE, + }; + + pdev =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + desc =3D kunit_pwrseq_get(test, &pdev->dev, "test-target"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc); + + /* Success path: post_enable() runs once and reports success. */ + ret =3D pwrseq_enable(desc); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->post_enable_calls, 1); + KUNIT_EXPECT_EQ(test, ctx->enable_count_a, 1); + + ret =3D pwrseq_disable(desc); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* Failure path: post_enable() fails, unit is rolled back. */ + ctx->post_enable_error =3D true; + ret =3D pwrseq_enable(desc); + KUNIT_EXPECT_EQ(test, ret, -EIO); + KUNIT_EXPECT_EQ(test, ctx->post_enable_calls, 2); + KUNIT_EXPECT_EQ(test, ctx->enable_count_a, 2); + KUNIT_EXPECT_EQ(test, ctx->disable_count_a, 2); + + /* powered_on must have been cleared. */ + ret =3D pwrseq_enable(desc); + KUNIT_EXPECT_EQ(test, ret, -EIO); + KUNIT_EXPECT_EQ(test, ctx->enable_count_a, 3); +} + +/* + * Test that pwrseq_get() returns -ENOENT when the matched provider does n= ot + * have the requested target name. + */ +static void pwrseq_get_target_not_found(struct kunit *test) +{ + const struct pwrseq_target_data *targets[2] =3D { }; + struct platform_device *pdev =3D test->priv; + struct platform_device_info pdevinfo; + struct pwrseq_target_data target; + struct pwrseq_unit_data unit; + struct pwrseq_device *pwrseq; + struct pwrseq_config config; + struct pwrseq_desc *desc; + + unit =3D (struct pwrseq_unit_data){ + .name =3D "unit-a", + }; + + target =3D (struct pwrseq_target_data){ + .name =3D "real-target", + .unit =3D &unit, + }; + + targets[0] =3D ⌖ + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .match =3D pwrseq_test_match_always, + .targets =3D targets, + }; + + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pwrseq); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_TEST_CONSUMER, + .id =3D PLATFORM_DEVID_NONE, + }; + + pdev =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + desc =3D kunit_pwrseq_get(test, &pdev->dev, "nonexistent-target"); + KUNIT_EXPECT_TRUE(test, IS_ERR(desc)); + KUNIT_EXPECT_EQ(test, PTR_ERR(desc), -ENOENT); +} + +/* + * Test that pwrseq_put() on a descriptor that is still powered on disables + * it as part of tear-down, instead of leaking the power-on state. + */ +static void pwrseq_put_disables_powered_desc(struct kunit *test) +{ + const struct pwrseq_target_data *targets[2] =3D { }; + struct platform_device *pdev =3D test->priv; + struct platform_device_info pdevinfo; + struct pwrseq_target_data target; + struct pwrseq_unit_data unit; + struct pwrseq_device *pwrseq; + struct pwrseq_test_ctx *ctx; + struct pwrseq_config config; + struct pwrseq_desc *desc; + int ret; + + ctx =3D kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + unit =3D (struct pwrseq_unit_data){ + .name =3D "unit-a", + .enable =3D pwrseq_test_enable_a, + .disable =3D pwrseq_test_disable_a, + }; + + target =3D (struct pwrseq_target_data){ + .name =3D "test-target", + .unit =3D &unit, + }; + + targets[0] =3D ⌖ + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .drvdata =3D ctx, + .match =3D pwrseq_test_match_always, + .targets =3D targets, + }; + + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pwrseq); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_TEST_CONSUMER, + .id =3D PLATFORM_DEVID_NONE, + }; + + pdev =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + desc =3D kunit_pwrseq_get(test, &pdev->dev, "test-target"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc); + + ret =3D pwrseq_enable(desc); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, ctx->disable_count_a, 0); + + /* + * Run the deferred pwrseq_put() action now instead of at test + * teardown, so its effect can be asserted below. + */ + kunit_release_action(test, pwrseq_put_wrapper, desc); + KUNIT_EXPECT_EQ(test, ctx->disable_count_a, 1); +} + +/* + * Test that pwrseq_device_unregister() on a target with an active user + * triggers "REMOVING POWER SEQUENCER WITH ACTIVE USERS", and that both + * pwrseq_enable() and pwrseq_disable() start returning -ENODEV for + * previously acquired descriptors afterwards. + */ +static void pwrseq_unregister_with_active_user(struct kunit *test) +{ + struct platform_device *pdev =3D test->priv, *cons_a, *cons_b; + const struct pwrseq_target_data *targets[2] =3D { }; + struct platform_device_info pdevinfo; + struct pwrseq_desc *desc_a, *desc_b; + struct pwrseq_target_data target; + struct pwrseq_unit_data unit; + struct pwrseq_device *pwrseq; + struct pwrseq_test_ctx *ctx; + struct pwrseq_config config; + bool bound; + int ret; + + ctx =3D kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + unit =3D (struct pwrseq_unit_data){ + .name =3D "unit-a", + .enable =3D pwrseq_test_enable_a, + .disable =3D pwrseq_test_disable_a, + }; + + target =3D (struct pwrseq_target_data){ + .name =3D "test-target", + .unit =3D &unit, + }; + + targets[0] =3D ⌖ + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .drvdata =3D ctx, + .match =3D pwrseq_test_match_always, + .targets =3D targets, + }; + + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pwrseq); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_TEST_CONSUMER, + .id =3D 0, + }; + cons_a =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_a); + + wait_for_device_probe(); + scoped_guard(device, &cons_a->dev) + bound =3D device_is_bound(&cons_a->dev); + + KUNIT_ASSERT_FALSE(test, bound); + + pdevinfo.id =3D 1; + cons_b =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_b); + + wait_for_device_probe(); + scoped_guard(device, &cons_b->dev) + bound =3D device_is_bound(&cons_b->dev); + + KUNIT_ASSERT_FALSE(test, bound); + + desc_a =3D kunit_pwrseq_get(test, &cons_a->dev, "test-target"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc_a); + desc_b =3D kunit_pwrseq_get(test, &cons_b->dev, "test-target"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc_b); + + ret =3D pwrseq_enable(desc_a); + KUNIT_ASSERT_EQ(test, ret, 0); + + kunit_warning_suppress(test) { + kunit_release_action(test, pwrseq_device_unregister_wrapper, pwrseq); + KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1); + } + + ret =3D pwrseq_enable(desc_b); + KUNIT_EXPECT_EQ(test, ret, -ENODEV); + + ret =3D pwrseq_disable(desc_a); + KUNIT_EXPECT_EQ(test, ret, -ENODEV); +} + +/* + * Test that pwrseq_to_device() returns the pwrseq provider's own device, + * and NULL for a NULL descriptor. + */ +static void pwrseq_to_device_test(struct kunit *test) +{ + struct platform_device *parent =3D test->priv, *cons; + const struct pwrseq_target_data *targets[2] =3D { }; + struct platform_device_info pdevinfo; + struct pwrseq_target_data target; + struct pwrseq_unit_data unit; + struct pwrseq_device *pwrseq; + struct pwrseq_config config; + struct pwrseq_desc *desc; + struct device *dev; + + unit =3D (struct pwrseq_unit_data){ .name =3D "unit-a" }; + target =3D (struct pwrseq_target_data){ + .name =3D "test-target", + .unit =3D &unit, + }; + targets[0] =3D ⌖ + + config =3D (struct pwrseq_config){ + .parent =3D &parent->dev, + .match =3D pwrseq_test_match_always, + .targets =3D targets, + }; + + pwrseq =3D kunit_pwrseq_device_register(test, &config); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pwrseq); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_TEST_CONSUMER, + .id =3D PLATFORM_DEVID_NONE, + }; + + cons =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons); + + desc =3D kunit_pwrseq_get(test, &cons->dev, "test-target"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc); + + dev =3D pwrseq_to_device(desc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + KUNIT_EXPECT_PTR_EQ(test, dev->parent, &parent->dev); + KUNIT_EXPECT_TRUE(test, device_is_registered(dev)); + + KUNIT_EXPECT_PTR_EQ(test, pwrseq_to_device(NULL), NULL); +} + +static struct kunit_case pwrseq_tests[] =3D { + KUNIT_CASE(pwrseq_circular_deps), + KUNIT_CASE(pwrseq_circular_deps_chain), + KUNIT_CASE(pwrseq_register_valid), + KUNIT_CASE(pwrseq_register_invalid_args), + KUNIT_CASE(pwrseq_register_target_without_unit), + KUNIT_CASE(pwrseq_enable_disable), + KUNIT_CASE(pwrseq_shared_deps), + KUNIT_CASE(pwrseq_enable_idempotent), + KUNIT_CASE(pwrseq_enable_enable_error), + KUNIT_CASE(pwrseq_enable_rollback_on_top_failure), + KUNIT_CASE(pwrseq_enable_post_enable), + KUNIT_CASE(pwrseq_get_target_not_found), + KUNIT_CASE(pwrseq_put_disables_powered_desc), + KUNIT_CASE(pwrseq_unregister_with_active_user), + KUNIT_CASE(pwrseq_to_device_test), + { } +}; + +static struct kunit_suite pwrseq_test_suite =3D { + .name =3D "pwrseq", + .init =3D pwrseq_test_parent_init, + .test_cases =3D pwrseq_tests, +}; + +/* + * Match a consumer to this test provider by resolving its "pwrseq-provide= r" + * software-node reference and comparing it to the provider's own fwnode. + */ +static int pwrseq_test_match_by_swnode_ref(struct pwrseq_device *pwrseq, + struct device *dev) +{ + const struct software_node *provider_swnode; + struct fwnode_handle *provider_fwnode, *ref; + bool match; + + if (!dev_fwnode(dev)) + return PWRSEQ_NO_MATCH; + + ref =3D fwnode_find_reference(dev_fwnode(dev), "pwrseq-provider", 0); + if (IS_ERR_OR_NULL(ref)) + return PWRSEQ_NO_MATCH; + + provider_swnode =3D pwrseq_device_get_drvdata(pwrseq); + provider_fwnode =3D software_node_fwnode(provider_swnode); + match =3D (ref =3D=3D provider_fwnode); + fwnode_handle_put(ref); + + return match ? PWRSEQ_MATCH_OK : PWRSEQ_NO_MATCH; +} + +static const struct pwrseq_unit_data pwrseq_swnode_test_unit =3D { + .name =3D "test-target", +}; + +static const struct pwrseq_target_data pwrseq_swnode_test_target =3D { + .name =3D "test-target", + .unit =3D &pwrseq_swnode_test_unit, +}; + +static const struct pwrseq_target_data *pwrseq_swnode_test_targets[] =3D { + &pwrseq_swnode_test_target, + NULL, +}; + +struct pwrseq_swnode_provider_pdata { + const struct software_node *provider_swnode; +}; + +static int pwrseq_swnode_provider_probe(struct platform_device *pdev) +{ + const struct pwrseq_swnode_provider_pdata *pdata =3D dev_get_platdata(&pd= ev->dev); + struct pwrseq_config config; + + config =3D (struct pwrseq_config){ + .parent =3D &pdev->dev, + .drvdata =3D (void *)pdata->provider_swnode, + .match =3D pwrseq_test_match_by_swnode_ref, + .targets =3D pwrseq_swnode_test_targets, + }; + + return PTR_ERR_OR_ZERO(devm_pwrseq_device_register(&pdev->dev, &config)); +} + +static struct platform_driver pwrseq_swnode_provider_driver =3D { + .probe =3D pwrseq_swnode_provider_probe, + .driver =3D { + .name =3D PWRSEQ_SWNODE_TEST_PROVIDER, + }, +}; + +static struct platform_driver pwrseq_swnode_provider_driver_2 =3D { + .probe =3D pwrseq_swnode_provider_probe, + .driver =3D { + .name =3D PWRSEQ_SWNODE_TEST_PROVIDER_2, + }, +}; + +struct pwrseq_probe_order_pdata { + unsigned int probe_count; + int pwrseq_err; +}; + +static const struct pwrseq_probe_order_pdata pwrseq_probe_order_pdata_temp= late; + +static int pwrseq_probe_order_consumer_probe(struct platform_device *pdev) +{ + struct pwrseq_probe_order_pdata *pdata =3D dev_get_platdata(&pdev->dev); + struct pwrseq_desc *desc; + + pdata->probe_count++; + + desc =3D devm_pwrseq_get(&pdev->dev, "test-target"); + pdata->pwrseq_err =3D PTR_ERR_OR_ZERO(desc); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + return 0; +} + +static struct platform_driver pwrseq_probe_order_consumer_driver =3D { + .probe =3D pwrseq_probe_order_consumer_probe, + .driver =3D { + .name =3D PWRSEQ_PROBE_ORDER_TEST_CONSUMER, + }, +}; + +static struct platform_driver pwrseq_probe_defer_consumer_driver =3D { + .probe =3D pwrseq_probe_order_consumer_probe, + .driver =3D { + .name =3D PWRSEQ_PROBE_DEFER_TEST_CONSUMER, + }, +}; + +/* + * Verify that driver core orders the probe of a pwrseq consumer after its + * provider. The consumer references the provider through a software node = and + * is registered first and we rely on devlink for ordering. + */ +static void pwrseq_swnode_probe_order(struct kunit *test) +{ + struct property_entry properties[2] =3D { }; + struct pwrseq_probe_order_pdata *pdata; + struct platform_device_info pdevinfo; + struct platform_device *prvd, *cons; + struct fwnode_handle *fwnode; + bool bound =3D false; + int ret; + + ret =3D kunit_platform_driver_register(test, &pwrseq_swnode_provider_driv= er); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret =3D kunit_platform_driver_register(test, &pwrseq_probe_order_consumer= _driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + fwnode =3D kunit_software_node_register(test, &pwrseq_test_provider_swnod= e); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode); + + properties[0] =3D PROPERTY_ENTRY_REF("pwrseq-provider", + &pwrseq_test_provider_swnode); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_PROBE_ORDER_TEST_CONSUMER, + .id =3D PLATFORM_DEVID_NONE, + .data =3D &pwrseq_probe_order_pdata_template, + .size_data =3D sizeof(pwrseq_probe_order_pdata_template), + .properties =3D properties, + }; + + cons =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons); + + wait_for_device_probe(); + scoped_guard(device, &cons->dev) + bound =3D device_is_bound(&cons->dev); + + KUNIT_ASSERT_FALSE(test, bound); + + pdata =3D dev_get_platdata(&cons->dev); + KUNIT_ASSERT_EQ(test, pdata->probe_count, 0); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_SWNODE_TEST_PROVIDER, + .id =3D PLATFORM_DEVID_NONE, + .swnode =3D &pwrseq_test_provider_swnode, + .data =3D &(const struct pwrseq_swnode_provider_pdata){ + .provider_swnode =3D &pwrseq_test_provider_swnode, + }, + .size_data =3D sizeof(struct pwrseq_swnode_provider_pdata), + }; + + prvd =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prvd); + + wait_for_device_probe(); + + scoped_guard(device, &prvd->dev) + bound =3D device_is_bound(&prvd->dev); + KUNIT_ASSERT_TRUE(test, bound); + + scoped_guard(device, &cons->dev) + bound =3D device_is_bound(&cons->dev); + KUNIT_ASSERT_TRUE(test, bound); + + pdata =3D dev_get_platdata(&cons->dev); + KUNIT_EXPECT_EQ(test, pdata->probe_count, 1); + KUNIT_EXPECT_EQ(test, pdata->pwrseq_err, 0); +} + +/* + * Verify that a pwrseq consumer referencing a provider whose software nod= e is + * not registered yet, defers its probe instead of failing. + * + * The provider software node is deliberately left unregistered when the + * consumer is added. fw_devlink cannot resolve the reference, so it creat= es no + * supplier link and does not order the consumer - the consumer's probe() = runs + * and calls pwrseq_get(), which finds no matching provider and returns + * -EPROBE_DEFER. Once the provider software node and device appear, the + * deferred consumer probes again and binds. + */ +static void pwrseq_swnode_probe_defer_on_unregistered(struct kunit *test) +{ + struct property_entry properties[2] =3D { }; + struct pwrseq_probe_order_pdata *pdata; + struct platform_device_info pdevinfo; + struct platform_device *prvd, *cons; + struct fwnode_handle *fwnode; + bool bound =3D false; + int ret; + + ret =3D kunit_platform_driver_register(test, &pwrseq_swnode_provider_driv= er_2); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret =3D kunit_platform_driver_register(test, &pwrseq_probe_defer_consumer= _driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + properties[0] =3D PROPERTY_ENTRY_REF("pwrseq-provider", + &pwrseq_test_provider_swnode); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_PROBE_DEFER_TEST_CONSUMER, + .id =3D PLATFORM_DEVID_NONE, + .data =3D &pwrseq_probe_order_pdata_template, + .size_data =3D sizeof(pwrseq_probe_order_pdata_template), + .properties =3D properties, + }; + + cons =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons); + + wait_for_device_probe(); + scoped_guard(device, &cons->dev) + bound =3D device_is_bound(&cons->dev); + + KUNIT_ASSERT_FALSE(test, bound); + + pdata =3D dev_get_platdata(&cons->dev); + KUNIT_ASSERT_GT(test, pdata->probe_count, 0); + KUNIT_ASSERT_EQ(test, pdata->pwrseq_err, -EPROBE_DEFER); + + fwnode =3D kunit_software_node_register(test, &pwrseq_test_provider_swnod= e); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode); + + pdevinfo =3D (struct platform_device_info){ + .name =3D PWRSEQ_SWNODE_TEST_PROVIDER_2, + .id =3D PLATFORM_DEVID_NONE, + .swnode =3D &pwrseq_test_provider_swnode, + .data =3D &(const struct pwrseq_swnode_provider_pdata){ + .provider_swnode =3D &pwrseq_test_provider_swnode, + }, + .size_data =3D sizeof(struct pwrseq_swnode_provider_pdata), + }; + + prvd =3D kunit_platform_device_register_full(test, &pdevinfo); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prvd); + + wait_for_device_probe(); + + scoped_guard(device, &prvd->dev) + bound =3D device_is_bound(&prvd->dev); + KUNIT_ASSERT_TRUE(test, bound); + + scoped_guard(device, &cons->dev) + bound =3D device_is_bound(&cons->dev); + KUNIT_ASSERT_TRUE(test, bound); + + pdata =3D dev_get_platdata(&cons->dev); + KUNIT_EXPECT_EQ(test, pdata->pwrseq_err, 0); +} + +static int pwrseq_swnode_test_init(struct kunit *test) +{ + /* + * A device link teardown from an earlier test case, or from this + * suite's previous module load, may still be queued on device_link_mq. + * Flush it so software_node_register() below doesn't spuriously see + * the about-to-be-reused node name as still taken. + */ + device_link_wait_removal(); + + return 0; +} + +static struct kunit_case pwrseq_swnode_probe_order_tests[] =3D { + KUNIT_CASE(pwrseq_swnode_probe_order), + KUNIT_CASE(pwrseq_swnode_probe_defer_on_unregistered), + { } +}; + +static struct kunit_suite pwrseq_swnode_probe_order_test_suite =3D { + .name =3D "pwrseq-swnode-probe-order", + .init =3D pwrseq_swnode_test_init, + .test_cases =3D pwrseq_swnode_probe_order_tests, +}; + +kunit_test_suites(&pwrseq_test_suite, + &pwrseq_swnode_probe_order_test_suite); + +MODULE_DESCRIPTION("KUnit test cases for the power sequencing subsystem"); +MODULE_AUTHOR("Bartosz Golaszewski "= ); +MODULE_LICENSE("GPL"); --=20 2.47.3