From patchwork Mon Jul 29 14:34:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= X-Patchwork-Id: 1966056 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=SwURyEuy; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=2604:1380:45d1:ec00::1; helo=ny.mirrors.kernel.org; envelope-from=linux-pwm+bounces-2923-incoming=patchwork.ozlabs.org@vger.kernel.org; receiver=patchwork.ozlabs.org) Received: from ny.mirrors.kernel.org (ny.mirrors.kernel.org [IPv6:2604:1380:45d1:ec00::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WXgvd6SyXz20FY for ; Tue, 30 Jul 2024 00:38:21 +1000 (AEST) Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ny.mirrors.kernel.org (Postfix) with ESMTPS id D012C1C21D85 for ; Mon, 29 Jul 2024 14:38:19 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 844BC15B119; Mon, 29 Jul 2024 14:35:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b="SwURyEuy" X-Original-To: linux-pwm@vger.kernel.org Received: from mail-wr1-f53.google.com (mail-wr1-f53.google.com [209.85.221.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B48EF155A59 for ; Mon, 29 Jul 2024 14:35:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263710; cv=none; b=IZ1Ev/R5cEs8y3OB4ZIZlwRrz59UqjkxZXf4IX0y+KNLkVI1S9U+SlVTF6WP/iSA0qZ4JTJE3X6c4sPK8SC2rejfLoetBIbNv+Xr0wt8Wqre9jmc1ZBy2ihdCMDq6sp+hNctCQGo/bPnwrplaYNaAHMCGQ14Sjhu8kziEfMmiHY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263710; c=relaxed/simple; bh=G8RzSgzqE3b/YQkVUMRKktKZQUWUi0ZOXcwktYDJ6dI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=txOtzg7m9kmkK2W02M7XrzhCTCsmQCPpahonvpkHVKiX2S+MALWIC+Is0eNSVbN2nwNyMCssOA1+Zk/xhdxeO3Hc9kkcnRd5OO+CppwpPOTNASZ8f36VNP47cimTfyKp3RQdCdB5VnSSRzG8r9bFz8ZY8fUCEK3wIUwE9mX03xk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com; spf=pass smtp.mailfrom=baylibre.com; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b=SwURyEuy; arc=none smtp.client-ip=209.85.221.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=baylibre.com Received: by mail-wr1-f53.google.com with SMTP id ffacd0b85a97d-36844375001so1292141f8f.0 for ; Mon, 29 Jul 2024 07:35:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1722263707; x=1722868507; 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; bh=Czqr3VJTl4Kc4YYcH1tRw4LJap/cuiBFRZk+Kq2Jw3s=; b=SwURyEuymjbLAzlJttPloASO9K01ONk4kFJFfdCIbmKs6QjNUw4s6+lC8dye/3emoJ aN90xkkHZ859Z0vzQ5i9qam5YuIudJnTXM92OOgoZibZb6MtqR+bNENww2M06rwbHaCG Tl5ky1DMSsUYs0t1Ay9mE3hqWucu0/sLAUI159OrEx2s8muLk9Zy6zJ/BZq4xA9d3V6o 3Wx6o7xfBiqjCfAqhBT4VQz/NbGEkNfLct1v9rUpGrNYUlDtiqHBY8z7OlRvpbeIzhM0 ChxMD/h9VcALfLBYx1QLgViE/yK4vVaFpVDsOU2bQ+qdlzIlmgvfGkp86lkUM6K7+4EK X95A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722263707; x=1722868507; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Czqr3VJTl4Kc4YYcH1tRw4LJap/cuiBFRZk+Kq2Jw3s=; b=S1CaCFGyEZ9f4tTOCgbGolRzkeYKV/0nCnMtaQc2YkQIw2QYquhPG3vVFPkBJCDXz+ bIzHiUQjvwvPjViwuXSXlkfYxtqDAknk+PFGOKWKDstOuiVoLjNbLbKosZpf6XIx6Dqv PupR0rOfiENekpCyAI9fIG3CxPZO7PwACgwrIKVnRv0WqcjXbHaVYe9/nI/1i5gYS0cG a+jkZJ1yeSFDQZsriUpSqxRqjWTV2BKndl0kKubyPGgoePqS1dwlm3+1W7HU99MnsFNl 1UPdIftEAFUyFXWaytpC6/ZwJlITwMqrFXpjlZwBCiklm/HhGWIXy1APXwXhBX0uXa7Q 3pIw== X-Gm-Message-State: AOJu0YxZhJuP5PaE3RkHB+1taKOSkD/+YRFs4ezc50j0+IsO5OhppTtn RoavmOlBBUxXVpZ/2fK0nnFtv0TS3PvUyoWHxxsd5MqavltOb6YnKItuDS0+vQ6k5ff6VIBhu7d F X-Google-Smtp-Source: AGHT+IHY7O/M+9SVL5JcH0T+2k2tGxsTtXE/FT7uTm1WEYrs1GBs89XK9ESYfBHKG4TQZ6f/8uKQTA== X-Received: by 2002:a5d:5888:0:b0:368:4e86:14cc with SMTP id ffacd0b85a97d-36b5cefd6aemr7502357f8f.10.1722263707002; Mon, 29 Jul 2024 07:35:07 -0700 (PDT) Received: from localhost ([2a02:8071:b783:6940:59dd:510e:47d0:643f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-36b367c093bsm12569716f8f.18.2024.07.29.07.35.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 Jul 2024 07:35:06 -0700 (PDT) From: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= To: linux-pwm@vger.kernel.org Cc: Trevor Gamblin Subject: [PATCH v3 1/8] pwm: Simplify pwm_capture() Date: Mon, 29 Jul 2024 16:34:17 +0200 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pwm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1044; i=u.kleine-koenig@baylibre.com; h=from:subject:message-id; bh=G8RzSgzqE3b/YQkVUMRKktKZQUWUi0ZOXcwktYDJ6dI=; b=owEBbQGS/pANAwAKAY+A+1h9Ev5OAcsmYgBmp6htSFOEhjeD3V/7VA9S3ARvLSX7hiOQRtuob z8MdQI9b9yJATMEAAEKAB0WIQQ/gaxpOnoeWYmt/tOPgPtYfRL+TgUCZqeobQAKCRCPgPtYfRL+ TsPOB/4vxC09yJKuDnTlShoGZN47mTAbKnQt10Z98J25uwJ+qTEDUbPnjOs9c4KD2STNnxpFBiC HvIzW8xC0T4jozo88ATYj0uma8JQK0EL1vO0/G6eQYmOgKRB5XfB1KcScKnfDG5PZ6TtB7aZP27 Bjt4b6AnNjW7jmlUpxZ0lCHBtV7loakAcZFzdgdwa4Ryq3tPHBrUT6ZOocTPQbfrRvJNpcUE0aH j7qsHE88wWoGFBf085pS0v/MBC3sfqglmEHqpQ0q3TmarOZvdG1byUrs7LkHwnkZYEbJHi8Rc7+ UOjFCTXcvDLeEJqE4JfU1U8Xli4oACB+enlkms/suRC8UlZ4 X-Developer-Key: i=u.kleine-koenig@baylibre.com; a=openpgp; fpr=0D2511F322BFAB1C1580266BE2DCDD9132669BD6 When pwm_capture() is called, pwm is valid, so the checks for pwm and pwm->chip->ops being NULL can be dropped. Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 56d91c11f0d4..6e752e148b98 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -328,15 +328,15 @@ EXPORT_SYMBOL_GPL(pwm_adjust_config); static int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result, unsigned long timeout) { - if (!pwm || !pwm->chip->ops) - return -EINVAL; + struct pwm_chip *chip = pwm->chip; + const struct pwm_ops *ops = chip->ops; - if (!pwm->chip->ops->capture) + if (!ops->capture) return -ENOSYS; guard(mutex)(&pwm_lock); - return pwm->chip->ops->capture(pwm->chip, pwm, result, timeout); + return ops->capture(chip, pwm, result, timeout); } static struct pwm_chip *pwmchip_find_by_name(const char *name) From patchwork Mon Jul 29 14:34:18 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= X-Patchwork-Id: 1966052 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=jpuhyiE2; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=139.178.88.99; helo=sv.mirrors.kernel.org; envelope-from=linux-pwm+bounces-2924-incoming=patchwork.ozlabs.org@vger.kernel.org; receiver=patchwork.ozlabs.org) Received: from sv.mirrors.kernel.org (sv.mirrors.kernel.org [139.178.88.99]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WXgvd0SXZz20FY for ; Tue, 30 Jul 2024 00:38:20 +1000 (AEST) Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sv.mirrors.kernel.org (Postfix) with ESMTPS id C04EE283B1E for ; Mon, 29 Jul 2024 14:38:19 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 44B4D15B113; Mon, 29 Jul 2024 14:35:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b="jpuhyiE2" X-Original-To: linux-pwm@vger.kernel.org Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com [209.85.128.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C983315ADB3 for ; Mon, 29 Jul 2024 14:35:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263713; cv=none; b=EPKK3RgMUfxGnf2fOYkdM6IIuEVe1d1wNTsFtq4PkEgEWjx7Z6g46jGrryw/0wy96Uc5mnhL8iwIABWT5mx/BITLV18IsGrqFLqiDVN8ID+8ZRaGKTeZ/UrrWOnAfH04CefFPlNWtMBaE1/QFfXwSJKs2ROqP6iXRjeG8X7teKU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263713; c=relaxed/simple; bh=AaJoUcIX++IpzzWW0vVTaUFC2VwCQNaZTXiOvVfE78k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=uRG6/MsXNTdqshpIWD8b5k3rWLCmEnpFOXaq9ZsvI8ghHGDsg2VZqhmQV2hSsnt94z6H3zQU8kPsVPNem9Jz2drW4xQ6+79cZp1Kuk1X/A0O02hLLhukz7G45Vhr1Mdmi6wx8A911bvlMHfrwbK9g5y1M+Lwji7Nl/qOTc9+jXg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com; spf=pass smtp.mailfrom=baylibre.com; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b=jpuhyiE2; arc=none smtp.client-ip=209.85.128.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=baylibre.com Received: by mail-wm1-f42.google.com with SMTP id 5b1f17b1804b1-428163f7635so15796195e9.2 for ; Mon, 29 Jul 2024 07:35:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1722263709; x=1722868509; 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; bh=xwL3WwnYCVO/qW47jad+fDcEi3a3huqXh2bt+q3kgnQ=; b=jpuhyiE2/Lg7x46HwrYhMpdmK2KN/Sj0HDFsCZdI6OPxi1VORA/u1yB1TO4vtoe2d5 9AUPkfQYtk38O8Hfx3vCM351jT24N7cUdSxZtUbB0zLEQ/bXLv66f9h7Gt064pxmkkko nVjkivP1v46QoPnY6yBLnC/1lDUSFoIyQT4URkIGp1LiMKS9Kh38QA4FSj7RbL1BarGE g9GPO3GJBNzRC2LstYgQB3pJjPbYm36JvIlygUcO8NwbzF1IdEBPjgP1KJwFNUFDdU1J tgU8lTdMGkV7CXJhVi9FtQp2z2wbPz7cSob7VdYwv78fIZBbYYrpGkU9MzU4W8XM+Bok UVkA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722263709; x=1722868509; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=xwL3WwnYCVO/qW47jad+fDcEi3a3huqXh2bt+q3kgnQ=; b=oGU6ZQ+aibim+XM0PzfIQQcpY++3gPA9GZWeIrODDd9IHOMKDEsJLiD0b0bEHtLsK9 +Jsd/Zpia5B5hPNJVRGv2Efb1JfwadO+qcAfil0ENcEOqwBYgkZQAb/MVFLvu0WFRpsu 0HyVvQAHVAJPEO7aGraElmnVWBQpvycZkVvtvc0gx25s/NnSSwi9nvCoFQXUGxJRdntA ChHQBkCDUUeXksD7w/7zTOj3gk4Sd1SlzMy4oC/J6U6Tb0KI7yXUz3G5F2z+oUJzlnrW a2J4/FWW3TLEXbttP4KTPAb9Ft69bdEWHNV/TvWVYSbQl9otVhEHrgKF8aixL3JAwT1y m/bA== X-Gm-Message-State: AOJu0YwJWcgCk4jf0hyLGvGeY1yOWRUvg5otktRt0mliYHmdQIfrdrNf dXtdY6QBjWvnCt1AQZ31CLtQUS1lJOx65QHue8jUJfbQiMA7pObQHsg+yYf6Yep57wusuCPLZQc j X-Google-Smtp-Source: AGHT+IEr/04vIox2C0s7ifJ3PZSh2WTD4CEHgyvvjae51EkLT7+/m5KvKDmgYZoPttdqPdVuEKbzNQ== X-Received: by 2002:a05:600c:3ca9:b0:428:2e9:6573 with SMTP id 5b1f17b1804b1-42811d99617mr51411425e9.17.1722263709162; Mon, 29 Jul 2024 07:35:09 -0700 (PDT) Received: from localhost ([2a02:8071:b783:6940:59dd:510e:47d0:643f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4281e72c21esm27404265e9.40.2024.07.29.07.35.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 Jul 2024 07:35:08 -0700 (PDT) From: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= To: linux-pwm@vger.kernel.org Cc: Trevor Gamblin Subject: [PATCH v3 2/8] pwm: Add more locking Date: Mon, 29 Jul 2024 16:34:18 +0200 Message-ID: <32467e0cbf3563820e6e6494dd593921627d0764.1722261050.git.u.kleine-koenig@baylibre.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pwm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=7509; i=u.kleine-koenig@baylibre.com; h=from:subject:message-id; bh=AaJoUcIX++IpzzWW0vVTaUFC2VwCQNaZTXiOvVfE78k=; b=owEBbQGS/pANAwAKAY+A+1h9Ev5OAcsmYgBmp6hwn/lMVjtivuMwePiBxjETvSYuajDQPBxLC itcNrGViUmJATMEAAEKAB0WIQQ/gaxpOnoeWYmt/tOPgPtYfRL+TgUCZqeocAAKCRCPgPtYfRL+ ThonB/44mihxh2nj5cy8Bac8vSQY8i9BJSJTlb5fXrkfFMYnx2r7sSocPqRtuej+XFeuBKCWan1 GdaDDwlp8yqeTi5rZLRPtamFUJcL632nk2YXFsKnTZeHisRKzf1z36SZFY1r0bVyn3XNpwh2wJB oM9frzQJ0L7c8gHfvC5cjRpq1aBxftMfZYnIempATX3oYZFC1HigehMiGGCFRH1Voa7GQMzuWz+ yer8TLugYtUv4EKLewRhbLdh+/7cCbA90Omuj+b7zqJo6E9lNrgQWBXRN6CRgj1ocv5wxEIQm4P wMV9xOaRTAiP1ECIMvnKPrftv4vLPczr1h4VYY7EJllD/fXT X-Developer-Key: i=u.kleine-koenig@baylibre.com; a=openpgp; fpr=0D2511F322BFAB1C1580266BE2DCDD9132669BD6 This ensures that a pwm_chip that has no corresponding driver isn't used and that a driver doesn't go away while a callback is still running. In the presence of device links this isn't necessary yet (so this is no fix) but for pwm character device support this is needed. To not serialize all pwm_apply_state() calls, this introduces a per chip lock. An additional complication is that for atomic chips a mutex cannot be used (as pwm_apply_atomic() must not sleep) and a spinlock cannot be held while calling an operation for a sleeping chip. So depending on the chip being atomic or not a spinlock or a mutex is used. Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 95 +++++++++++++++++++++++++++++++++++++++++---- include/linux/pwm.h | 13 +++++++ 2 files changed, 100 insertions(+), 8 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 6e752e148b98..9752eb446879 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -31,6 +31,24 @@ static DEFINE_MUTEX(pwm_lock); static DEFINE_IDR(pwm_chips); +static void pwmchip_lock(struct pwm_chip *chip) +{ + if (chip->atomic) + spin_lock(&chip->atomic_lock); + else + mutex_lock(&chip->nonatomic_lock); +} + +static void pwmchip_unlock(struct pwm_chip *chip) +{ + if (chip->atomic) + spin_unlock(&chip->atomic_lock); + else + mutex_unlock(&chip->nonatomic_lock); +} + +DEFINE_GUARD(pwmchip, struct pwm_chip *, pwmchip_lock(_T), pwmchip_unlock(_T)) + static void pwm_apply_debug(struct pwm_device *pwm, const struct pwm_state *state) { @@ -220,6 +238,7 @@ static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state) int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state) { int err; + struct pwm_chip *chip = pwm->chip; /* * Some lowlevel driver's implementations of .apply() make use of @@ -230,7 +249,12 @@ int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state) */ might_sleep(); - if (IS_ENABLED(CONFIG_PWM_DEBUG) && pwm->chip->atomic) { + guard(pwmchip)(chip); + + if (!chip->operational) + return -ENODEV; + + if (IS_ENABLED(CONFIG_PWM_DEBUG) && chip->atomic) { /* * Catch any drivers that have been marked as atomic but * that will sleep anyway. @@ -254,9 +278,16 @@ EXPORT_SYMBOL_GPL(pwm_apply_might_sleep); */ int pwm_apply_atomic(struct pwm_device *pwm, const struct pwm_state *state) { - WARN_ONCE(!pwm->chip->atomic, + struct pwm_chip *chip = pwm->chip; + + WARN_ONCE(!chip->atomic, "sleeping PWM driver used in atomic context\n"); + guard(pwmchip)(chip); + + if (!chip->operational) + return -ENODEV; + return __pwm_apply(pwm, state); } EXPORT_SYMBOL_GPL(pwm_apply_atomic); @@ -336,6 +367,11 @@ static int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result, guard(mutex)(&pwm_lock); + guard(pwmchip)(chip); + + if (!chip->operational) + return -ENODEV; + return ops->capture(chip, pwm, result, timeout); } @@ -368,6 +404,14 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) if (test_bit(PWMF_REQUESTED, &pwm->flags)) return -EBUSY; + /* + * This function is called while holding pwm_lock. As .operational only + * changes while holding this lock, checking it here without holding the + * chip lock is fine. + */ + if (!chip->operational) + return -ENODEV; + if (!try_module_get(chip->owner)) return -ENODEV; @@ -396,7 +440,9 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) */ struct pwm_state state = { 0, }; - err = ops->get_state(chip, pwm, &state); + scoped_guard(pwmchip, chip) + err = ops->get_state(chip, pwm, &state); + trace_pwm_get(pwm, &state, err); if (!err) @@ -1020,6 +1066,7 @@ struct pwm_chip *pwmchip_alloc(struct device *parent, unsigned int npwm, size_t chip->npwm = npwm; chip->uses_pwmchip_alloc = true; + chip->operational = false; pwmchip_dev = &chip->dev; device_initialize(pwmchip_dev); @@ -1125,6 +1172,11 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner) chip->owner = owner; + if (chip->atomic) + spin_lock_init(&chip->atomic_lock); + else + mutex_init(&chip->nonatomic_lock); + guard(mutex)(&pwm_lock); ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL); @@ -1138,6 +1190,9 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner) if (IS_ENABLED(CONFIG_OF)) of_pwmchip_add(chip); + scoped_guard(pwmchip, chip) + chip->operational = true; + ret = device_add(&chip->dev); if (ret) goto err_device_add; @@ -1145,6 +1200,9 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner) return 0; err_device_add: + scoped_guard(pwmchip, chip) + chip->operational = false; + if (IS_ENABLED(CONFIG_OF)) of_pwmchip_remove(chip); @@ -1164,11 +1222,27 @@ void pwmchip_remove(struct pwm_chip *chip) { pwmchip_sysfs_unexport(chip); - if (IS_ENABLED(CONFIG_OF)) - of_pwmchip_remove(chip); + scoped_guard(mutex, &pwm_lock) { + unsigned int i; + + scoped_guard(pwmchip, chip) + chip->operational = false; + + for (i = 0; i < chip->npwm; ++i) { + struct pwm_device *pwm = &chip->pwms[i]; + + if (test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) { + dev_warn(&chip->dev, "Freeing requested PWM #%u\n", i); + if (pwm->chip->ops->free) + pwm->chip->ops->free(pwm->chip, pwm); + } + } + + if (IS_ENABLED(CONFIG_OF)) + of_pwmchip_remove(chip); - scoped_guard(mutex, &pwm_lock) idr_remove(&pwm_chips, chip->id); + } device_del(&chip->dev); } @@ -1538,12 +1612,17 @@ void pwm_put(struct pwm_device *pwm) guard(mutex)(&pwm_lock); - if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) { + /* + * If the chip isn't operational, PWMF_REQUESTED was already cleared. So + * don't warn in this case. This can only happen if a consumer called + * pwm_put() twice. + */ + if (chip->operational && !test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) { pr_warn("PWM device already freed\n"); return; } - if (chip->ops->free) + if (chip->operational && chip->ops->free) pwm->chip->ops->free(pwm->chip, pwm); pwm->label = NULL; diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 8acd60b53f58..3ea73e075abe 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -275,6 +275,9 @@ struct pwm_ops { * @of_xlate: request a PWM device given a device tree PWM specifier * @atomic: can the driver's ->apply() be called in atomic context * @uses_pwmchip_alloc: signals if pwmchip_allow was used to allocate this chip + * @operational: signals if the chip can be used (or is already deregistered) + * @nonatomic_lock: mutex for nonatomic chips + * @atomic_lock: mutex for atomic chips * @pwms: array of PWM devices allocated by the framework */ struct pwm_chip { @@ -290,6 +293,16 @@ struct pwm_chip { /* only used internally by the PWM framework */ bool uses_pwmchip_alloc; + bool operational; + union { + /* + * depending on the chip being atomic or not either the mutex or + * the spinlock is used. It protects .operational and + * synchronizes the callbacks in .ops + */ + struct mutex nonatomic_lock; + spinlock_t atomic_lock; + }; struct pwm_device pwms[] __counted_by(npwm); }; From patchwork Mon Jul 29 14:34:19 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= X-Patchwork-Id: 1966059 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=pggbg53Z; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=2604:1380:4601:e00::3; helo=am.mirrors.kernel.org; envelope-from=linux-pwm+bounces-2925-incoming=patchwork.ozlabs.org@vger.kernel.org; receiver=patchwork.ozlabs.org) Received: from am.mirrors.kernel.org (am.mirrors.kernel.org [IPv6:2604:1380:4601:e00::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WXgvg4c83z20FY for ; Tue, 30 Jul 2024 00:38:23 +1000 (AEST) Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by am.mirrors.kernel.org (Postfix) with ESMTPS id F2D481F22947 for ; Mon, 29 Jul 2024 14:38:20 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5482615ADB3; Mon, 29 Jul 2024 14:35:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b="pggbg53Z" X-Original-To: linux-pwm@vger.kernel.org Received: from mail-wm1-f52.google.com (mail-wm1-f52.google.com [209.85.128.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E7EEF155A59 for ; Mon, 29 Jul 2024 14:35:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263715; cv=none; b=RDc6isWByBb/Ui+NRXME/pKYEa/2SFUQxpqNyqGlcrvkdVWS2QBqxPJzRKWW47r67MA4BIXGAu8aSBYT8oYdYQi3LNF44SI6YME1xUA2xe/HB+YLGCrTitOulDL1VAw7qrQ+khklCd884aTywTjw2LsuqGneEhTTpXnOOXLgHo8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263715; c=relaxed/simple; bh=YLGo1zxeq011mK3nXAuvOgnnvFfP4RyP1ZOR1WemDKk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=lISokEIAXZHKcQWyi6Edf3mSb4YQJ7CN3LUNXxWd0OUZ+sM9m8MAcxPEHkVdJduhWplTRkcICr7q6r2NIP3g3jq3aP1vQTqcJXEt66WquSMn9ZUzbHXjCxjXcZL3hONAD4r4O56bzIbBGjD1ZWZOuPJFxAb9SyriiNcPduBWxFI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com; spf=pass smtp.mailfrom=baylibre.com; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b=pggbg53Z; arc=none smtp.client-ip=209.85.128.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=baylibre.com Received: by mail-wm1-f52.google.com with SMTP id 5b1f17b1804b1-4281c164408so9733745e9.1 for ; Mon, 29 Jul 2024 07:35:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1722263711; x=1722868511; 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; bh=kXu5kBrwgEeGJc7q/nmENSymYvT9rDG8rM/GSXObGnA=; b=pggbg53ZvlxIxejmAMZcUGkZmLuRbHVradd3M8A/WLDw8FkhZqB/lHc6LNHtKEylVz L5LGMcsKMVRFavpw7toQ+riejVczJCuyVH84ldit5qadadY9Q0foJKNXnrJTrgLw0do3 14HEEN+LbX/ui/sARy9ZZ8wRzIw0sSOjtdQW4yeL6/y6qnB35wau1tXL72TZDrFXGbUZ d5FEH+BQXAhopDDtnyn7JgjB5yVaZ6LdcfEyaB1NE3LJxAsvnISpRa48+xHtPAtUY/BW OI95lFgGnzjjGI871qTyhquXAA5XUotiM7+3SYo8UPSh+lXOL77DG9mH6LGY14RWb8CA a9iQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722263711; x=1722868511; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=kXu5kBrwgEeGJc7q/nmENSymYvT9rDG8rM/GSXObGnA=; b=AooFI5mHC250IpqtGJFeBijrLKv1XT1MP2Qc3QPKnH834gykbMw+pp+cC9kUIhSbCn uzO0dPhT7iyr61FFLKsbqeQ6ptVaHuatLpqlhyO3W4YwNpX1Mc3wgV6vdbzPLYtN5qFf RwaohKyki/5moWLYs1joyXE6l/kxP1JH/0PrHF8rfi5TnNTIv5pggoYg7wqWjtVt3Jb8 LvrJxfB3ACMdUGmDrP9qlUAYgpZaaUGd7pFLOa0r3yqe3vR8gotZ+wMVSD8CEM70pwnD WMYF9zM3dBo5TGScuoczpwqoaFU1O8t518uYPjFIO9D1mz4t6knJ4Re4Nr3wNisaMTEH TysA== X-Gm-Message-State: AOJu0YwFZ6sKGg+CtrHjuhe823WvltYkX/bvUgan/EmTqNv+d8TTo7DI BgzEqlpfRfJNCYzdOV1iMhoy5VGWxphhGGirYiBtFs2Uda/8dvoW8Zg89mXIw0gNJ/N2uv9IloQ 9 X-Google-Smtp-Source: AGHT+IH85kGuC70LU/PtjU8sLos4uCMUHwxwenvJ5RLvnyTKjqSTd05HjCOCmJOKHVkmoF+4JKNKwg== X-Received: by 2002:adf:ea0e:0:b0:367:94b8:1df1 with SMTP id ffacd0b85a97d-36b5d0c2e61mr4976865f8f.55.1722263711349; Mon, 29 Jul 2024 07:35:11 -0700 (PDT) Received: from localhost ([2a02:8071:b783:6940:59dd:510e:47d0:643f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-36b367fc445sm12328395f8f.48.2024.07.29.07.35.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 Jul 2024 07:35:11 -0700 (PDT) From: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= To: linux-pwm@vger.kernel.org Cc: Trevor Gamblin Subject: [PATCH v3 3/8] pwm: New abstraction for PWM waveforms Date: Mon, 29 Jul 2024 16:34:19 +0200 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pwm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=12976; i=u.kleine-koenig@baylibre.com; h=from:subject:message-id; bh=YLGo1zxeq011mK3nXAuvOgnnvFfP4RyP1ZOR1WemDKk=; b=kA0DAAoBj4D7WH0S/k4ByyZiAGanqHPIZtQ490lkxoeekHvo1e/IoH4Bxs1LX817j/JUpLV1e IkBMwQAAQoAHRYhBD+BrGk6eh5Zia3+04+A+1h9Ev5OBQJmp6hzAAoJEI+A+1h9Ev5O1iAIAK08 4oq9W/LqEX3erfIIU5GW2/er1qQN0GDo5jobD/3XRaY4hSjxiTHS26e5roVO6f/ZDH4HOpYf5LS 42/JkFYfZaGpOSgXfvs68+dGJC8TYauppFOtkGUXajxdTeZ/ZAWF4uD3Z5AJgp3pomaGOL1BgB7 yZEGp/x0ezBMM0SFh2Ts65iln46kT1mYQuisEXhWHBkgfaTpi+P3dxs9Lo3f5fj14Xi2YxKEddV hdEYBZ5ljYqIC+tpKFuspeiD5gSWoJfWKwbA9KI8JQrEPTR9uet7KLhc3pEhG1ZWDAmrZSGvuT6 64c5gkgoe3b21Syt+aj+aD7XdzJtEaQk68wbPoY= X-Developer-Key: i=u.kleine-koenig@baylibre.com; a=openpgp; fpr=0D2511F322BFAB1C1580266BE2DCDD9132669BD6 Up to now the configuration of a PWM setting is described exclusively by a struct pwm_state which contains information about period, duty_cycle, polarity and if the PWM is enabled. (There is another member usage_power which doesn't completely fit into pwm_state, I ignore it here for simplicity.) Instead of a polarity the new abstraction has a member duty_offset_ns that defines when the rising edge happens after the period start. This is more general, as with a pwm_state the rising edge can only happen at the period's start or such that the falling edge is at the end of the period (i.e. duty_offset_ns == 0 or duty_offset_ns == period_length_ns - duty_length_ns). A disabled PWM is modeled by .period_length_ns = 0. In my eyes this is a nice usage of that otherwise unusable setting, as it doesn't define anything about the future which matches the fact that consumers should consider the state of the output as undefined and it's just there to say "No further requirements about the output, you can save some power.". Further I renamed period and duty_cycle to period_length_ns and duty_length_ns. In the past there was confusion from time to time about duty_cycle being measured in nanoseconds because people expected a percentage of period instead. With "length_ns" as suffix the semantic should be more obvious to people unfamiliar with the pwm subsystem. period is renamed to period_length_ns for consistency. The API for consumers doesn't change yet, but lowlevel drivers can implement callbacks that work with pwm_waveforms instead of pwm_states. A new thing about these callbacks is that the calculation of hardware settings needed to implement a certain waveform is separated from actually writing these settings. The motivation for that is that this allows a consumer to query the hardware capabilities without actually modifying the hardware state. The rounding rules that are expected to be implemented in the round_waveform_tohw() are: First pick the biggest possible period not bigger than wf->period_length_ns. For that period pick the biggest possible duty setting not bigger than wf->duty_length_ns. Third pick the biggest possible offset not bigger than wf->duty_offset_ns. If the requested period is too small for the hardware, it's expected that a setting with the minimal period and duty_length_ns = duty_offset_ns = 0 is returned and this fact is signaled by a return value of 1. Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 225 +++++++++++++++++++++++++++++++++++++++----- include/linux/pwm.h | 36 +++++++ 2 files changed, 240 insertions(+), 21 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 9752eb446879..a8a0c12fef83 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -49,6 +49,102 @@ static void pwmchip_unlock(struct pwm_chip *chip) DEFINE_GUARD(pwmchip, struct pwm_chip *, pwmchip_lock(_T), pwmchip_unlock(_T)) +static void pwm_wf2state(const struct pwm_waveform *wf, struct pwm_state *state) +{ + if (wf->period_length_ns) { + if (wf->duty_length_ns + wf->duty_offset_ns < wf->period_length_ns) + *state = (struct pwm_state){ + .enabled = true, + .polarity = PWM_POLARITY_NORMAL, + .period = wf->period_length_ns, + .duty_cycle = wf->duty_length_ns, + }; + else + *state = (struct pwm_state){ + .enabled = true, + .polarity = PWM_POLARITY_INVERSED, + .period = wf->period_length_ns, + .duty_cycle = wf->period_length_ns - wf->duty_length_ns, + }; + } else { + *state = (struct pwm_state){ + .enabled = false, + }; + } +} + +static void pwm_state2wf(const struct pwm_state *state, struct pwm_waveform *wf) +{ + if (state->enabled) { + if (state->polarity == PWM_POLARITY_NORMAL) + *wf = (struct pwm_waveform){ + .period_length_ns = state->period, + .duty_length_ns = state->duty_cycle, + .duty_offset_ns = 0, + }; + else + *wf = (struct pwm_waveform){ + .period_length_ns = state->period, + .duty_length_ns = state->period - state->duty_cycle, + .duty_offset_ns = state->duty_cycle, + }; + } else { + *wf = (struct pwm_waveform){ + .period_length_ns = 0, + }; + } +} + +static int pwm_check_rounding(const struct pwm_waveform *wf, + const struct pwm_waveform *wf_rounded) +{ + if (!wf->period_length_ns) + return 0; + + if (wf->period_length_ns < wf_rounded->period_length_ns) + return 1; + + if (wf->duty_length_ns < wf_rounded->duty_length_ns) + return 1; + + if (wf->duty_offset_ns < wf_rounded->duty_offset_ns) + return 1; + + return 0; +} + +static int __pwm_round_waveform_tohw(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_waveform *wf, void *wfhw) +{ + const struct pwm_ops *ops = chip->ops; + + return ops->round_waveform_tohw(chip, pwm, wf, wfhw); +} + +static int __pwm_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm, + const void *wfhw, struct pwm_waveform *wf) +{ + const struct pwm_ops *ops = chip->ops; + + return ops->round_waveform_fromhw(chip, pwm, wfhw, wf); +} + +static int __pwm_read_waveform(struct pwm_chip *chip, struct pwm_device *pwm, void *wfhw) +{ + const struct pwm_ops *ops = chip->ops; + + return ops->read_waveform(chip, pwm, wfhw); +} + +static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, const void *wfhw) +{ + const struct pwm_ops *ops = chip->ops; + + return ops->write_waveform(chip, pwm, wfhw); +} + +#define WFHWSIZE 20 + static void pwm_apply_debug(struct pwm_device *pwm, const struct pwm_state *state) { @@ -182,6 +278,7 @@ static bool pwm_state_valid(const struct pwm_state *state) static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state) { struct pwm_chip *chip; + const struct pwm_ops *ops; int err; if (!pwm || !state) @@ -205,6 +302,7 @@ static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state) } chip = pwm->chip; + ops = chip->ops; if (state->period == pwm->state.period && state->duty_cycle == pwm->state.duty_cycle && @@ -213,18 +311,60 @@ static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state) state->usage_power == pwm->state.usage_power) return 0; - err = chip->ops->apply(chip, pwm, state); - trace_pwm_apply(pwm, state, err); - if (err) - return err; + if (ops->write_waveform) { + struct pwm_waveform wf; + char wfhw[WFHWSIZE]; - pwm->state = *state; + BUG_ON(WFHWSIZE < ops->sizeof_wfhw); - /* - * only do this after pwm->state was applied as some - * implementations of .get_state depend on this - */ - pwm_apply_debug(pwm, state); + pwm_state2wf(state, &wf); + + /* + * The rounding is wrong here for states with inverted polarity. + * While .apply() rounds down duty_cycle (which represents the + * time from the start of the period to the inner edge), + * .round_waveform_tohw() rounds down the time the PWM is high. + * Can be fixed if the need arises, until reported otherwise + * let's assume that consumers don't care. + */ + + err = __pwm_round_waveform_tohw(chip, pwm, &wf, &wfhw); + if (err) + return err; + + if (IS_ENABLED(CONFIG_PWM_DEBUG)) { + struct pwm_waveform wf_rounded; + + err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_rounded); + if (err) + return err; + + if (pwm_check_rounding(&wf, &wf_rounded)) + dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n", + wf.duty_length_ns, wf.period_length_ns, wf.duty_offset_ns, + wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns); + } + + err = __pwm_write_waveform(chip, pwm, &wfhw); + if (err) + return err; + + pwm->state = *state; + + } else { + err = ops->apply(chip, pwm, state); + trace_pwm_apply(pwm, state, err); + if (err) + return err; + + pwm->state = *state; + + /* + * only do this after pwm->state was applied as some + * implementations of .get_state depend on this + */ + pwm_apply_debug(pwm, state); + } return 0; } @@ -292,6 +432,41 @@ int pwm_apply_atomic(struct pwm_device *pwm, const struct pwm_state *state) } EXPORT_SYMBOL_GPL(pwm_apply_atomic); +static int pwm_get_state_hw(struct pwm_device *pwm, struct pwm_state *state) +{ + struct pwm_chip *chip = pwm->chip; + const struct pwm_ops *ops = chip->ops; + int ret = -EOPNOTSUPP; + + if (ops->read_waveform) { + char wfhw[WFHWSIZE]; + struct pwm_waveform wf; + + BUG_ON(WFHWSIZE < ops->sizeof_wfhw); + + scoped_guard(pwmchip, chip) { + + ret = __pwm_read_waveform(chip, pwm, &wfhw); + if (ret) + return ret; + + ret = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf); + if (ret) + return ret; + } + + pwm_wf2state(&wf, state); + + } else if (ops->get_state) { + scoped_guard(pwmchip, chip) + ret = ops->get_state(chip, pwm, state); + + trace_pwm_get(pwm, state, ret); + } + + return ret; +} + /** * pwm_adjust_config() - adjust the current PWM config to the PWM arguments * @pwm: PWM device @@ -430,7 +605,7 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) } } - if (ops->get_state) { + if (ops->read_waveform || ops->get_state) { /* * Zero-initialize state because most drivers are unaware of * .usage_power. The other members of state are supposed to be @@ -440,11 +615,7 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) */ struct pwm_state state = { 0, }; - scoped_guard(pwmchip, chip) - err = ops->get_state(chip, pwm, &state); - - trace_pwm_get(pwm, &state, err); - + err = pwm_get_state_hw(pwm, &state); if (!err) pwm->state = state; @@ -1131,12 +1302,24 @@ static bool pwm_ops_check(const struct pwm_chip *chip) { const struct pwm_ops *ops = chip->ops; - if (!ops->apply) - return false; + if (ops->write_waveform) { + if (!ops->round_waveform_tohw || + !ops->round_waveform_fromhw || + !ops->write_waveform) + return false; - if (IS_ENABLED(CONFIG_PWM_DEBUG) && !ops->get_state) - dev_warn(pwmchip_parent(chip), - "Please implement the .get_state() callback\n"); + if (WFHWSIZE < ops->sizeof_wfhw) { + dev_warn(pwmchip_parent(chip), "WFHWSIZE < %zu\n", ops->sizeof_wfhw); + return false; + } + } else { + if (!ops->apply) + return false; + + if (IS_ENABLED(CONFIG_PWM_DEBUG) && !ops->get_state) + dev_warn(pwmchip_parent(chip), + "Please implement the .get_state() callback\n"); + } return true; } diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 3ea73e075abe..6a26a5210dab 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -49,6 +49,31 @@ enum { PWMF_EXPORTED = 1, }; +/* + * struct pwm_waveform - description of a PWM waveform + * @period_length_ns: PWM period + * @duty_length_ns: PWM duty cycle + * @duty_offset_ns: offset of the rising edge from the period's start + * + * This is a representation of a PWM waveform alternative to struct pwm_state + * below. It's more expressive than struct pwm_state as it contains a + * duty_offset_ns and so can represent offsets other than $period - $duty_cycle + * which is done using .polarity = PWM_POLARITY_INVERSED. Note there is no + * explicit bool for enabled. A "disabled" PWM is represented by + * .period_length_ns = 0. + * + * Note that the behaviour of a "disabled" PWM is undefined. Depending on the + * hardware's capabilities it might drive the active or inactive level, go + * high-z or even continue to toggle. + * + * The unit for all three members is nanoseconds. + */ +struct pwm_waveform { + u64 period_length_ns; + u64 duty_length_ns; + u64 duty_offset_ns; +}; + /* * struct pwm_state - state of a PWM channel * @period: PWM period (in nanoseconds) @@ -259,6 +284,17 @@ struct pwm_ops { void (*free)(struct pwm_chip *chip, struct pwm_device *pwm); int (*capture)(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_capture *result, unsigned long timeout); + + size_t sizeof_wfhw; + int (*round_waveform_tohw)(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_waveform *wf, void *wfhw); + int (*round_waveform_fromhw)(struct pwm_chip *chip, struct pwm_device *pwm, + const void *wfhw, struct pwm_waveform *wf); + int (*read_waveform)(struct pwm_chip *chip, struct pwm_device *pwm, + void *wfhw); + int (*write_waveform)(struct pwm_chip *chip, struct pwm_device *pwm, + const void *wfhw); + int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state); int (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, From patchwork Mon Jul 29 14:34:20 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= X-Patchwork-Id: 1966060 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=OlbakB8f; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=147.75.80.249; helo=am.mirrors.kernel.org; envelope-from=linux-pwm+bounces-2926-incoming=patchwork.ozlabs.org@vger.kernel.org; receiver=patchwork.ozlabs.org) Received: from am.mirrors.kernel.org (am.mirrors.kernel.org [147.75.80.249]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WXgvg5fVLz20Fr for ; Tue, 30 Jul 2024 00:38:23 +1000 (AEST) Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by am.mirrors.kernel.org (Postfix) with ESMTPS id 3808D1F22BFF for ; Mon, 29 Jul 2024 14:38:21 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id E58E7155A59; Mon, 29 Jul 2024 14:35:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b="OlbakB8f" X-Original-To: linux-pwm@vger.kernel.org Received: from mail-wm1-f44.google.com (mail-wm1-f44.google.com [209.85.128.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E129C155A58 for ; Mon, 29 Jul 2024 14:35:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263716; cv=none; b=mZtnQ2reFDAQzSZsQihFsydZSd6BYQ3Gczm/MYhzgQG1QeHIGBVgL/+YnzAlounAtRgau/z2kOHIFeZPa8lmmAuvmzswyOBw49pl5lGM9qGczCC2gAzXaOpkQ6DUhzwAUvZ0Y1HNd3yjS1QGSb9VcaGuiFOZHcXsZqaj5IVwVeA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263716; c=relaxed/simple; bh=NSSmYuFik1RNMaX/b0qg9v0ZOSqNyaU9oWMhz1GZ80M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=lAWXB6QJxLFeHw14/te8IWbpdw4rgsxpBpg4/AlawUIx+5I8hic/NRuIAxtFYTgZPvGB28RSAxhiLlohq7Yj3GVlDO1StQgVKBoISB/hq+OSOHI+sbJzgihUfL8O+ET+A3csFBGkP7bL0cwOVryz8uT4H9QrPiv7xxdaA3Ia/CQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com; spf=pass smtp.mailfrom=baylibre.com; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b=OlbakB8f; arc=none smtp.client-ip=209.85.128.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=baylibre.com Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-42819654737so12124135e9.1 for ; Mon, 29 Jul 2024 07:35:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1722263713; x=1722868513; 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; bh=u9treUPuX3jNmcVNf9Xy0X4Wi1t56xuaDc+VIUwuIks=; b=OlbakB8fy1GegjPqiputxgfb9iqMQguWhlLPHyIDfNdFkW91qgTE5UtPnldICWOk+4 mVKKn+yQea0d0afRVfug38QT8lTd2dMSqWhrn20HLROomlC200j3wX95bJjSx9cLU8ve OOwba5fJBpqFs++0S2m/wP0VnZ9DOjEr7gxVtG+/C6ppxNXAn1HDIQvcqLUPY3FiUnpz HKlv0UJWuiF90yKldGwYy8xbatFUFy22/l7/2XbKC0VpvFAkLO2WRyEj647nMv95mpF4 OE8pgGR8NwwMeS92APpDdleGGhemUy/prs0QxdaP9Y/Kfj9G62hPVRYuPyjfTwx/JmL+ 5sOA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722263713; x=1722868513; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=u9treUPuX3jNmcVNf9Xy0X4Wi1t56xuaDc+VIUwuIks=; b=nGHpNIlVL2AYwUJV/wQMKnoVYZIp5bA9qMtwnGAu9W6/XPKs9k+TpnuTLX09hmM2HW ctSzQGWKcQR1TxdpmHx+Y9k6/QP0j5AcsRUHsXxCYKO1u5OzhtjlHbbz1WBbagzLfN2F sE3qTv+Dc0uhdRVnDKQDg2nHSpEMdWXP17b31YKu8ccQE51Qpb4yeAOy08pyFmGubzUI Nxs4xxxMSnkd4ZFtAX8FQhPP8OAwZIeOM9rKAI7YBJDLUTuou9PctOMbuk6ntwDMyfzP X1p4AWaSU2kOCMXXg7mLBgKsExGwvfadmbc2yBue2NcsHMrtuR5JJgT/go4mRXGX48d/ LLDQ== X-Gm-Message-State: AOJu0YwtwmBZUvFE8elRiZEM72LAgDvk3hTMXrEmUuWF4xXexS9PNY4p tegj5tkY+VM8yF3UzEV7UogJpSfW+bVKpuNhElpvv5mD4wpD2LP3gXSiGQipToahNyy8PzCzQCB 2 X-Google-Smtp-Source: AGHT+IFcPVC9U5eA5l7U8iZ35TmvMA+oSGF6fD0vB41zp41BuC2Xf7yE8mM9+Y1fyk1z+eehk2JmmQ== X-Received: by 2002:a05:600c:1390:b0:426:5e91:3ff1 with SMTP id 5b1f17b1804b1-42811dcd2f9mr51586095e9.24.1722263713359; Mon, 29 Jul 2024 07:35:13 -0700 (PDT) Received: from localhost ([2a02:8071:b783:6940:59dd:510e:47d0:643f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4281d805323sm33478625e9.41.2024.07.29.07.35.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 Jul 2024 07:35:13 -0700 (PDT) From: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= To: linux-pwm@vger.kernel.org Cc: Trevor Gamblin Subject: [PATCH v3 4/8] pwm: Provide new consumer API functions for waveforms Date: Mon, 29 Jul 2024 16:34:20 +0200 Message-ID: <015a5fbd5cf449bcb2d8fdf2305d7b6bf7109844.1722261050.git.u.kleine-koenig@baylibre.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pwm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=7930; i=u.kleine-koenig@baylibre.com; h=from:subject:message-id; bh=NSSmYuFik1RNMaX/b0qg9v0ZOSqNyaU9oWMhz1GZ80M=; b=owEBbQGS/pANAwAKAY+A+1h9Ev5OAcsmYgBmp6h2AKPXXPg+K+qzaGPP0q4xP2JxJzqMc77tX yRF18SX0F2JATMEAAEKAB0WIQQ/gaxpOnoeWYmt/tOPgPtYfRL+TgUCZqeodgAKCRCPgPtYfRL+ TvK0CACVvQCBddK+HZSs7Pkwj4qhV+LanHnGj7gG6mBMv0v3NHWDwLrBR8VgS2MkE7FqCb6GXlw Y5OQgaoUhLY2EPKZ0fcxCX4r4BXlNZWb2TN1mfdaX4dhyGTehe1I6++41DerQn4SOMk+GFQ04Sx 8k5gdTOcSq0ANCyvSD+ljd9LCzHqNMjtt+1y7wuBm8p4wmgnY5kXfvSbVLVTgY/JRjsNdFIUYJ8 IohdN1YZ7HqgMuKS4hJqRnj7jEtSObzl257MqOq0ZYJRKb9beJiuK/NyNGk0MCWcQILV/vmGcFY x2L1b4oQHsDNFtBWSpUC/Ds1g42yTF0TVyNlyu+ilwJZ4mOe X-Developer-Key: i=u.kleine-koenig@baylibre.com; a=openpgp; fpr=0D2511F322BFAB1C1580266BE2DCDD9132669BD6 Provide API functions for consumers to work with waveforms. Note that one relevant difference between pwm_get_state() and pwm_get_waveform*() is that the latter yields the actually configured hardware state, while the former yields the last state passed to pwm_apply*() and so doesn't account for hardware specific rounding. Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 201 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/pwm.h | 6 +- 2 files changed, 206 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index a8a0c12fef83..41e620944375 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -49,6 +49,30 @@ static void pwmchip_unlock(struct pwm_chip *chip) DEFINE_GUARD(pwmchip, struct pwm_chip *, pwmchip_lock(_T), pwmchip_unlock(_T)) +static bool pwm_wf_valid(const struct pwm_waveform *wf) +{ + /* + * For now restrict waveforms to period_length_ns <= S64_MAX to provide + * some space for future extensions. One possibility is to simplify + * representing waveforms with inverted polarity using negative values + * somehow. + */ + if (wf->period_length_ns > S64_MAX) + return false; + + if (wf->duty_length_ns > wf->period_length_ns) + return false; + + /* + * .duty_offset_ns is supposed to be smaller than .period_length_ns, apart + * from the corner case .duty_offset_ns = 0 + .period_length_ns = 0. + */ + if (wf->duty_offset_ns && wf->duty_offset_ns >= wf->period_length_ns) + return false; + + return true; +} + static void pwm_wf2state(const struct pwm_waveform *wf, struct pwm_state *state) { if (wf->period_length_ns) { @@ -95,6 +119,29 @@ static void pwm_state2wf(const struct pwm_state *state, struct pwm_waveform *wf) } } +static int pwmwfcmp(const struct pwm_waveform *a, const struct pwm_waveform *b) +{ + if (a->period_length_ns > b->period_length_ns) + return 1; + + if (a->period_length_ns < b->period_length_ns) + return -1; + + if (a->duty_length_ns > b->duty_length_ns) + return 1; + + if (a->duty_length_ns < b->duty_length_ns) + return -1; + + if (a->duty_offset_ns > b->duty_offset_ns) + return 1; + + if (a->duty_offset_ns < b->duty_offset_ns) + return -1; + + return 0; +} + static int pwm_check_rounding(const struct pwm_waveform *wf, const struct pwm_waveform *wf_rounded) { @@ -145,6 +192,160 @@ static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, c #define WFHWSIZE 20 +int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf) +{ + struct pwm_chip *chip = pwm->chip; + const struct pwm_ops *ops = chip->ops; + struct pwm_waveform wf_req = *wf; + char wfhw[WFHWSIZE]; + int ret_tohw, ret_fromhw; + + BUG_ON(WFHWSIZE < ops->sizeof_wfhw); + + if (!pwm_wf_valid(wf)) + return -EINVAL; + + guard(pwmchip)(chip); + + if (!chip->operational) + return -ENODEV; + + ret_tohw = __pwm_round_waveform_tohw(chip, pwm, wf, wfhw); + if (ret_tohw < 0) + return ret_tohw; + + ret_fromhw = __pwm_round_waveform_fromhw(chip, pwm, wfhw, wf); + if (ret_fromhw < 0) + return ret_fromhw; + + if (IS_ENABLED(CONFIG_PWM_DEBUG) && + ret_tohw == 0 && pwm_check_rounding(&wf_req, wf)) + dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n", + wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, + wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns); + + return ret_tohw; +} + +int pwm_get_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf) +{ + struct pwm_chip *chip = pwm->chip; + const struct pwm_ops *ops = chip->ops; + char wfhw[WFHWSIZE]; + int err; + + BUG_ON(WFHWSIZE < ops->sizeof_wfhw); + + guard(pwmchip)(chip); + + if (!chip->operational) + return -ENODEV; + + err = __pwm_read_waveform(chip, pwm, &wfhw); + if (err) + return err; + + return __pwm_round_waveform_fromhw(chip, pwm, &wfhw, wf); +} + +/* Called with the pwmchip lock held */ +static int __pwm_set_waveform(struct pwm_device *pwm, + const struct pwm_waveform *wf, + bool exact) +{ + struct pwm_chip *chip = pwm->chip; + const struct pwm_ops *ops = chip->ops; + char wfhw[WFHWSIZE]; + struct pwm_waveform wf_rounded; + int err; + + BUG_ON(WFHWSIZE < ops->sizeof_wfhw); + + if (!pwm_wf_valid(wf)) + return -EINVAL; + + err = __pwm_round_waveform_tohw(chip, pwm, wf, &wfhw); + if (err) + return err; + + if ((IS_ENABLED(CONFIG_PWM_DEBUG) || exact) && wf->period_length_ns) { + err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_rounded); + if (err) + return err; + + if (IS_ENABLED(CONFIG_PWM_DEBUG) && pwm_check_rounding(wf, &wf_rounded)) + dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n", + wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, + wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns); + + if (exact && pwmwfcmp(wf, &wf_rounded)) { + dev_dbg(&chip->dev, "Requested no rounding, but %llu/%llu [+%llu] -> %llu/%llu [+%llu]\n", + wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, + wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns); + + return 1; + } + } + + err = __pwm_write_waveform(chip, pwm, &wfhw); + if (err) + return err; + + /* update .state */ + pwm_wf2state(wf, &pwm->state); + + if (IS_ENABLED(CONFIG_PWM_DEBUG) && ops->read_waveform && wf->period_length_ns) { + struct pwm_waveform wf_set; + + err = __pwm_read_waveform(chip, pwm, &wfhw); + if (err) + /* maybe ignore? */ + return err; + + err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_set); + if (err) + /* maybe ignore? */ + return err; + + if (pwmwfcmp(&wf_set, &wf_rounded) != 0) + dev_err(&chip->dev, + "Unexpected setting: requested %llu/%llu [+%llu], expected %llu/%llu [+%llu], set %llu/%llu [+%llu]\n", + wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, + wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns, + wf_set.duty_length_ns, wf_set.period_length_ns, wf_set.duty_offset_ns); + } + return 0; +} + +int pwm_set_waveform_might_sleep(struct pwm_device *pwm, + const struct pwm_waveform *wf, bool exact) +{ + struct pwm_chip *chip = pwm->chip; + int err; + + might_sleep(); + + guard(pwmchip)(chip); + + if (!chip->operational) + return -ENODEV; + + if (IS_ENABLED(CONFIG_PWM_DEBUG) && chip->atomic) { + /* + * Catch any drivers that have been marked as atomic but + * that will sleep anyway. + */ + non_block_start(); + err = __pwm_set_waveform(pwm, wf, exact); + non_block_end(); + } else { + err = __pwm_set_waveform(pwm, wf, exact); + } + + return err; +} +EXPORT_SYMBOL_GPL(pwm_set_waveform_might_sleep); + static void pwm_apply_debug(struct pwm_device *pwm, const struct pwm_state *state) { diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 6a26a5210dab..40cef0bc0de7 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -358,7 +358,11 @@ static inline void pwmchip_set_drvdata(struct pwm_chip *chip, void *data) } #if IS_ENABLED(CONFIG_PWM) -/* PWM user APIs */ + +/* PWM consumer APIs */ +int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf); +int pwm_get_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf); +int pwm_set_waveform_might_sleep(struct pwm_device *pwm, const struct pwm_waveform *wf, bool exact); int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state); int pwm_apply_atomic(struct pwm_device *pwm, const struct pwm_state *state); int pwm_adjust_config(struct pwm_device *pwm); From patchwork Mon Jul 29 14:34:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= X-Patchwork-Id: 1966054 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=kY15xT+f; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=147.75.48.161; helo=sy.mirrors.kernel.org; envelope-from=linux-pwm+bounces-2927-incoming=patchwork.ozlabs.org@vger.kernel.org; receiver=patchwork.ozlabs.org) Received: from sy.mirrors.kernel.org (sy.mirrors.kernel.org [147.75.48.161]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WXgvf0NJgz20Fr for ; Tue, 30 Jul 2024 00:38:21 +1000 (AEST) Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sy.mirrors.kernel.org (Postfix) with ESMTPS id 88CA1B22C16 for ; Mon, 29 Jul 2024 14:38:21 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 53B8E15B149; Mon, 29 Jul 2024 14:35:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b="kY15xT+f" X-Original-To: linux-pwm@vger.kernel.org Received: from mail-wm1-f53.google.com (mail-wm1-f53.google.com [209.85.128.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 16F61155A58 for ; Mon, 29 Jul 2024 14:35:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263719; cv=none; b=qF85bcD/gw1d6DsTnqz0MwnkrazQc1hXsfTEn42aunIZ/1SVom4SDaHzHCVhqG486OykT73qswP2tE9XVULzB2jQnefZsva/2caaU4CI1R5cZJLgg17CMpJodE+2zvx9fX72FWKaDuSwS6U+mzO1JLQD50rFrfboQzH5am6MyCY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263719; c=relaxed/simple; bh=eUKbTxtMbAkHYGH1VEMDL0Jsu864b5EiqYXZz5qf1Po=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=kSX5LxiURPNKvKw1WYNRPfuj1NLeTJoPia9UD1FzPxn95AE1B+WO8OQ4PAQkT6Rs4fBAMuCgi+7YJdZfLPiJKdFM0mJ6GNAKLkCzPNIBelJZJGAND6/5ARmjDhNkOe9HHf43zYbahMs1LELmooLl360nw6YzD+TbSxtTlz/t4LE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com; spf=pass smtp.mailfrom=baylibre.com; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b=kY15xT+f; arc=none smtp.client-ip=209.85.128.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=baylibre.com Received: by mail-wm1-f53.google.com with SMTP id 5b1f17b1804b1-428178fc07eso14730925e9.3 for ; Mon, 29 Jul 2024 07:35:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1722263715; x=1722868515; 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; bh=GC73j++Su3zvm0+6kORe9wxatcZtt1vWWK6NmsrDrjM=; b=kY15xT+fYJWDA2t/smN5W55ceBpVWpE6v5YQ8DXnpHJtwRJj7bC/25X5hYM+j+oRGv s0wAf/L0EUYftlyKAlhFOrqWFSopfjsY6nfezzA4Np2h65EnZn1ba47jteBq7x0j8Bxm hmYBKDKORyFsMoHJQ8lxsZxvT642+FKQSTimpFvgDV6hKyC7zJTFhwr0yb27rlOb+pve 6Oit1t1wDiRvM7KUVz3Ctd5v253lfA1G0BNVedKHx/m4rRHs3CKS+hZFrCAd6/QHKzqp 0JwldjHw6dc5uHg6o3xQR6LbVR35umlNl5ijs827f7+9d0ycVZ3DS6IQIAdVJBHdgigc Lcgw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722263715; x=1722868515; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=GC73j++Su3zvm0+6kORe9wxatcZtt1vWWK6NmsrDrjM=; b=L1oN3fAxbTGEhL9YCTxEGS1GP8zXscIv2BPtsVCtd4c1/I9mJm4A7Jc7gnawFkJAuP ruUeRdJNrUxjMYuk3zE2+/r1ePzPAJmZxyjKbSBa5drx80EuAZKYiJ7crTeJj3ec4vmf crzu/LNB6j0N00atC20Uz0dNaOJ07yvf4s6Lg4zt2qav/I7iD2vrtPXIyeM86jCIj6Vz NdNxU0vtzuFyre6hDI2awHr87mmpXDc9Sxuh8t55BcuaC13tni0pZ+jEZ3XYH5A8iStZ FUgp6F2VUlKAH181s9VWGzoy11F4rIJcwDUzzT2rEa1ERPNW7Tjjh5Xwk/JnduQDCHJC knYQ== X-Gm-Message-State: AOJu0YzsmS97cFfWGAjehctNzfl//QC9syoY6yBIkNJ3H9JTgC5dlhnL S5PBbe9qC+RKsDDk+uyAjvG5mYSOHKbfQTZAR5gKOZeNf4n94h9HNSVJK96bb+Wt5rxTKSonv32 P X-Google-Smtp-Source: AGHT+IHgZC0PpiIuqgeg7cEtk9nWyi7qjTzgsvc4CbfqdtXoKLRkiBQ4VJUu/X5xC4EbFopCfhbVZg== X-Received: by 2002:a05:600c:1c0a:b0:427:9dad:17df with SMTP id 5b1f17b1804b1-42811d89a06mr45793835e9.12.1722263715417; Mon, 29 Jul 2024 07:35:15 -0700 (PDT) Received: from localhost ([2a02:8071:b783:6940:59dd:510e:47d0:643f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4280ca10aa6sm129554865e9.26.2024.07.29.07.35.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 Jul 2024 07:35:15 -0700 (PDT) From: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= To: linux-pwm@vger.kernel.org Cc: Trevor Gamblin Subject: [PATCH v3 5/8] pwm: Add support for pwmchip devices for faster and easier userspace access Date: Mon, 29 Jul 2024 16:34:21 +0200 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pwm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=9598; i=u.kleine-koenig@baylibre.com; h=from:subject:message-id; bh=eUKbTxtMbAkHYGH1VEMDL0Jsu864b5EiqYXZz5qf1Po=; b=owEBbQGS/pANAwAKAY+A+1h9Ev5OAcsmYgBmp6h53uAVBleB5DRIoVvTfqwbYPxBquuXaDyoW B2f47bwGRSJATMEAAEKAB0WIQQ/gaxpOnoeWYmt/tOPgPtYfRL+TgUCZqeoeQAKCRCPgPtYfRL+ Tu8cB/0dNui8488TfsRc0fwTagRv5GI54qDoeXt+CfGnoPWhWY/hSouFdHHaF8MoayJHowIeh7Z 7W++ZxpoeUiWlvvTOVgZuphLraMb+1sYvDXfpvMmW+Ngv3pgRYl1e4H+SA3afk68r1cC0BB6+Lb eoLQOQ4M0djR0UasNNIWQLgtA2GzraKhv/SX59WVRY3MBb+Bjv6O+koi6wFzMSFe2SbJL6WQTu+ qp1jfBW+e7H/ExfHASBWJ9VkGubDXHlMrI5UeHhd3n1bHS4ZotJ26ExgNe75fCdqMcsj3BnqgMq EUS+x15cD/SSb775t5MI2gHkN4LCw/m7RhYoWG1pX2fV4DPK X-Developer-Key: i=u.kleine-koenig@baylibre.com; a=openpgp; fpr=0D2511F322BFAB1C1580266BE2DCDD9132669BD6 With this change each pwmchip defining the new-style waveform callbacks can be accessed from userspace via a character device. Compared to the sysfs-API this is faster (on a stm32mp157 applying a new configuration takes approx 25% only) and allows to pass the whole configuration in a single ioctl allowing atomic application. Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 270 ++++++++++++++++++++++++++++++++++++++- include/linux/pwm.h | 3 + include/uapi/linux/pwm.h | 25 ++++ 3 files changed, 296 insertions(+), 2 deletions(-) create mode 100644 include/uapi/linux/pwm.h diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 41e620944375..888cd4f51c6e 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -23,6 +23,8 @@ #include +#include + #define CREATE_TRACE_POINTS #include @@ -1525,6 +1527,257 @@ static bool pwm_ops_check(const struct pwm_chip *chip) return true; } +struct pwm_cdev_data { + struct pwm_chip *chip; + struct pwm_device *pwm[]; +}; + +static int pwm_cdev_open(struct inode *inode, struct file *file) +{ + struct pwm_chip *chip = container_of(inode->i_cdev, struct pwm_chip, cdev); + struct pwm_cdev_data *cdata; + + guard(mutex)(&pwm_lock); + + if (!chip->operational) + return -ENXIO; + + cdata = kzalloc(struct_size(cdata, pwm, chip->npwm), GFP_KERNEL); + if (!cdata) + return -ENOMEM; + + cdata->chip = chip; + + file->private_data = cdata; + + return nonseekable_open(inode, file); +} + +static int pwm_cdev_release(struct inode *inode, struct file *file) +{ + struct pwm_cdev_data *cdata = file->private_data; + unsigned int i; + + for (i = 0; i < cdata->chip->npwm; ++i) { + if (cdata->pwm[i]) + pwm_put(cdata->pwm[i]); + } + kfree(cdata); + + return 0; +} + +static int pwm_cdev_request(struct pwm_cdev_data *cdata, unsigned int hwpwm) +{ + struct pwm_chip *chip = cdata->chip; + + if (hwpwm >= chip->npwm) + return -EINVAL; + + if (!cdata->pwm[hwpwm]) { + struct pwm_device *pwm = &chip->pwms[hwpwm]; + int ret; + + ret = pwm_device_request(pwm, "pwm-cdev"); + if (ret < 0) + return ret; + + cdata->pwm[hwpwm] = pwm; + } + + return 0; +} + +static int pwm_cdev_free(struct pwm_cdev_data *cdata, unsigned int hwpwm) +{ + struct pwm_chip *chip = cdata->chip; + + if (hwpwm >= chip->npwm) + return -EINVAL; + + if (cdata->pwm[hwpwm]) { + struct pwm_device *pwm = cdata->pwm[hwpwm]; + + pwm_put(pwm); + + cdata->pwm[hwpwm] = NULL; + } + + return 0; +} + +static long pwm_cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct pwm_cdev_data *cdata = file->private_data; + struct pwm_chip *chip = cdata->chip; + + guard(mutex)(&pwm_lock); + + if (!chip->operational) + return -ENODEV; + + switch (cmd) { + case PWM_IOCTL_GET_NUM_PWMS: + return chip->npwm; + + case PWM_IOCTL_REQUEST: + { + unsigned int hwpwm; + + ret = get_user(hwpwm, (unsigned int __user *)arg); + if (ret) + return ret; + + return pwm_cdev_request(cdata, hwpwm); + } + break; + + case PWM_IOCTL_FREE: + { + unsigned int hwpwm; + + ret = get_user(hwpwm, (unsigned int __user *)arg); + if (ret) + return ret; + + return pwm_cdev_free(cdata, hwpwm); + } + break; + + case PWM_IOCTL_ROUNDWF: + { + struct pwmchip_waveform cwf; + struct pwm_waveform wf; + struct pwm_device *pwm; + + ret = copy_from_user(&cwf, + (struct pwmchip_waveform __user *)arg, + sizeof(cwf)); + if (ret) + return -EFAULT; + + if (cwf.__pad != 0) + return -EINVAL; + + ret = pwm_cdev_request(cdata, cwf.hwpwm); + if (ret) + return ret; + + pwm = cdata->pwm[cwf.hwpwm]; + + wf = (struct pwm_waveform) { + .period_length_ns = cwf.period_length_ns, + .duty_length_ns = cwf.duty_length_ns, + .duty_offset_ns = cwf.duty_offset_ns, + }; + + ret = pwm_round_waveform_might_sleep(pwm, &wf); + if (ret) + return ret; + + cwf = (struct pwmchip_waveform) { + .hwpwm = cwf.hwpwm, + .period_length_ns = wf.period_length_ns, + .duty_length_ns = wf.duty_length_ns, + .duty_offset_ns = wf.duty_offset_ns, + }; + + return copy_to_user((struct pwmchip_waveform __user *)arg, + &cwf, sizeof(cwf)); + } + break; + + case PWM_IOCTL_GETWF: + { + struct pwmchip_waveform cwf; + struct pwm_waveform wf; + struct pwm_device *pwm; + + ret = copy_from_user(&cwf, + (struct pwmchip_waveform __user *)arg, + sizeof(cwf)); + if (ret) + return -EFAULT; + + if (cwf.__pad != 0) { + pr_warn("huh\n"); + return -EINVAL; + } + + ret = pwm_cdev_request(cdata, cwf.hwpwm); + if (ret) + return ret; + + pwm = cdata->pwm[cwf.hwpwm]; + + ret = pwm_get_waveform_might_sleep(pwm, &wf); + if (ret) + return ret; + + cwf.period_length_ns = wf.period_length_ns; + cwf.duty_length_ns = wf.duty_length_ns; + cwf.duty_offset_ns = wf.duty_offset_ns; + + return copy_to_user((struct pwmchip_waveform __user *)arg, + &cwf, sizeof(cwf)); + } + break; + + case PWM_IOCTL_SETROUNDEDWF: + case PWM_IOCTL_SETEXACTWF: + { + struct pwmchip_waveform cwf; + struct pwm_waveform wf; + struct pwm_device *pwm; + + ret = copy_from_user(&cwf, + (struct pwmchip_waveform __user *)arg, + sizeof(cwf)); + if (ret) + return -EFAULT; + if (cwf.__pad != 0) { + pr_warn("huh\n"); + return -EINVAL; + } + + if (cwf.period_length_ns > 0 && + (cwf.duty_length_ns > cwf.period_length_ns || + cwf.duty_offset_ns >= cwf.period_length_ns)) + return -EINVAL; + + ret = pwm_cdev_request(cdata, cwf.hwpwm); + if (ret) + return ret; + + pwm = cdata->pwm[cwf.hwpwm]; + + wf = (struct pwm_waveform){ + .period_length_ns = cwf.period_length_ns, + .duty_length_ns = cwf.duty_length_ns, + .duty_offset_ns = cwf.duty_offset_ns, + }; + + return pwm_set_waveform_might_sleep(pwm, &wf, + cmd == PWM_IOCTL_SETEXACTWF); + } + break; + + default: + return -ENOTTY; + } +} + +static const struct file_operations pwm_cdev_fileops = { + .open = pwm_cdev_open, + .release = pwm_cdev_release, + .owner = THIS_MODULE, + .llseek = no_llseek, + .unlocked_ioctl = pwm_cdev_ioctl, +}; + +static dev_t pwm_devt; + /** * __pwmchip_add() - register a new PWM chip * @chip: the PWM chip to add @@ -1577,7 +1830,13 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner) scoped_guard(pwmchip, chip) chip->operational = true; - ret = device_add(&chip->dev); + if (chip->id < 256 && chip->ops->write_waveform) + chip->dev.devt = MKDEV(MAJOR(pwm_devt), chip->id); + + cdev_init(&chip->cdev, &pwm_cdev_fileops); + chip->cdev.owner = owner; + + ret = cdev_device_add(&chip->cdev, &chip->dev); if (ret) goto err_device_add; @@ -1628,7 +1887,7 @@ void pwmchip_remove(struct pwm_chip *chip) idr_remove(&pwm_chips, chip->id); } - device_del(&chip->dev); + cdev_device_del(&chip->cdev, &chip->dev); } EXPORT_SYMBOL_GPL(pwmchip_remove); @@ -2172,9 +2431,16 @@ static int __init pwm_init(void) { int ret; + ret = alloc_chrdev_region(&pwm_devt, 0, 256, "pwm"); + if (ret) { + pr_warn("Failed to initialize chrdev region for PWM usage\n"); + return ret; + } + ret = class_register(&pwm_class); if (ret) { pr_err("Failed to initialize PWM class (%pe)\n", ERR_PTR(ret)); + unregister_chrdev_region(pwm_devt, 256); return ret; } diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 40cef0bc0de7..b12ca9531e32 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -2,6 +2,7 @@ #ifndef __LINUX_PWM_H #define __LINUX_PWM_H +#include #include #include #include @@ -304,6 +305,7 @@ struct pwm_ops { /** * struct pwm_chip - abstract a PWM controller * @dev: device providing the PWMs + * @cdev: &struct cdev for this device * @ops: callbacks for this PWM controller * @owner: module providing this chip * @id: unique number of this PWM chip @@ -318,6 +320,7 @@ struct pwm_ops { */ struct pwm_chip { struct device dev; + struct cdev cdev; const struct pwm_ops *ops; struct module *owner; unsigned int id; diff --git a/include/uapi/linux/pwm.h b/include/uapi/linux/pwm.h new file mode 100644 index 000000000000..c89ba3e3def8 --- /dev/null +++ b/include/uapi/linux/pwm.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ + +#ifndef _UAPI_PWM_H_ +#define _UAPI_PWM_H_ + +#include +#include + +struct pwmchip_waveform { + unsigned int hwpwm; + unsigned int __pad; /* padding, must be zero */ + __u64 period_length_ns; + __u64 duty_length_ns; + __u64 duty_offset_ns; +}; + +#define PWM_IOCTL_GET_NUM_PWMS _IO(0x75, 0) +#define PWM_IOCTL_REQUEST _IOW(0x75, 1, unsigned int) +#define PWM_IOCTL_FREE _IOW(0x75, 2, unsigned int) +#define PWM_IOCTL_ROUNDWF _IOWR(0x75, 3, struct pwmchip_waveform) +#define PWM_IOCTL_GETWF _IOWR(0x75, 4, struct pwmchip_waveform) +#define PWM_IOCTL_SETROUNDEDWF _IOW(0x75, 5, struct pwmchip_waveform) +#define PWM_IOCTL_SETEXACTWF _IOW(0x75, 6, struct pwmchip_waveform) + +#endif /* _UAPI_PWM_H_ */ From patchwork Mon Jul 29 14:34:22 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= X-Patchwork-Id: 1966055 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=Q9mNZuGb; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=2604:1380:45e3:2400::1; helo=sv.mirrors.kernel.org; envelope-from=linux-pwm+bounces-2928-incoming=patchwork.ozlabs.org@vger.kernel.org; receiver=patchwork.ozlabs.org) Received: from sv.mirrors.kernel.org (sv.mirrors.kernel.org [IPv6:2604:1380:45e3:2400::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WXgvf07rwz20Fq for ; Tue, 30 Jul 2024 00:38:22 +1000 (AEST) Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sv.mirrors.kernel.org (Postfix) with ESMTPS id BD0ED283AD3 for ; Mon, 29 Jul 2024 14:38:20 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id F093715AD9B; Mon, 29 Jul 2024 14:35:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b="Q9mNZuGb" X-Original-To: linux-pwm@vger.kernel.org Received: from mail-wr1-f46.google.com (mail-wr1-f46.google.com [209.85.221.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3C03F15B14C for ; Mon, 29 Jul 2024 14:35:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263721; cv=none; b=jGR8u/xcfANgGlcMSaT1VWSjABPrUetlzs74H+fppqwLjSl+mrIZFq61+0KJOGFso+2HGVYOSVPjg3Eivx5+HmeYCpXUag1fa0zrBI0zR4edJlw3rjb/2DUF/lKUi8wUWNkwtHOnAr/htTtHG94dNRGLCRuYhtoeMBk6a88RRtA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263721; c=relaxed/simple; bh=suh8lj5U75UfHLAikpWSAju24qSZjPOfB+HigRwu20c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Dnf/a4Rgacb13+wnVLJj+6q7iwCZMvGzZbeUc7mfcPLAR2bzay5U+gGqgXXTseEfPiIT+cRBEDsSi8fWZTrXPGWB7VaZpz8HpKyUJteCLQdhejCS+laVYlUH2mDKc8UeXzMyysE0asmPNF/0/wgnN7F4lKCm768WCv4Z9yaKipE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com; spf=pass smtp.mailfrom=baylibre.com; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b=Q9mNZuGb; arc=none smtp.client-ip=209.85.221.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=baylibre.com Received: by mail-wr1-f46.google.com with SMTP id ffacd0b85a97d-36868fcb919so1421202f8f.2 for ; Mon, 29 Jul 2024 07:35:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1722263718; x=1722868518; 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; bh=5wvUKWDbbhCXQe3vpeoWnQvXqGR+nLQUfmDvJ2uq4cw=; b=Q9mNZuGbzJeEUICCYpHFKp3DFYoPBXLfz/vQCUnACG5Ucv2DYENh9e4atVGHvW9iA9 UXXkW6xXr1/DbJztT5VQ7NOvywj31V0fY+oPfDuzWGySh2ZvBWEOI+XKpSg7OcVT1XKN r21dA0HFTGl1bLZpXMPWrwN7iI1TyYW0/iLSV5QOcZ4sNlg4TGmLnHktBDVjsEvQK0c5 BIFMhDoV9ArhesIjf83AgbYTOn8Vv0xNQftH8s4+kdhtxroBZXD9ZmraK3r2uC8d7yJL kwpp+VnhRWcna+ghBZdsnVAox/yOmbnK0YTljOPmBfR0VjiyRpMIJkZYcNgwLWPB3QCj HoWQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722263718; x=1722868518; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5wvUKWDbbhCXQe3vpeoWnQvXqGR+nLQUfmDvJ2uq4cw=; b=vZkibIfJHFbwjCRsSe+13S8iPjSem0wPfjP0WJD3zENnQx+AIMl8uXhouX1jTwp+q9 4EEhVCu/s1f6zMAjqNBL+7U0QWWmEyMqmHTlqBa4TusjsF708WQgMzss0lWVsBmyJoma l5mJnp77oNQptxeRT4sufIDf81G15aqYq90rHT7E1hWeTIwzUTKrVR94bPBNonYdV82D /DcPiqEG0/w+8wr+xcmQmo0kwKjl99q8BcWS31mHhPLWndazEjmQozyZoHkv9ziA4xZY amV3EkWCuDKpKcHB7xDBLoVtc1N3QmOlLN1SefTSDn/ro8pMZGm/H8fZJ2ujrd1sK79m UGZQ== X-Forwarded-Encrypted: i=1; AJvYcCUZMaMN6/yO1B0kBxknR82ChWbUvj7MxBZfvyvbeorWd+yw5jkVtOkrICBVUBKYJtLcu0yhASoCZKiTd4ATU2Nhj6Xsb8/JBj6q X-Gm-Message-State: AOJu0Yz/q6wnw3CZD6lCAswMcfhs5YYGEHeDjzqiNJ6vs0IohIUppWCc gZl7rTcalprRt7eUs2tS2dk8eCSS6MBhVckkQFxx0RYYoJ6TQ49XFICpT9FFPgnCw4BZJSgbFD/ O X-Google-Smtp-Source: AGHT+IEkgWMlUsc544E5LVvkbpdFtBtCkGuAxc3whEOFDWg38/1P2KS0HyrYffvmWz30E0pcqetJ4w== X-Received: by 2002:adf:cd0e:0:b0:367:8875:dd4c with SMTP id ffacd0b85a97d-36b5d0c447fmr5179528f8f.23.1722263717674; Mon, 29 Jul 2024 07:35:17 -0700 (PDT) Received: from localhost ([2a02:8071:b783:6940:59dd:510e:47d0:643f]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-36b367c0829sm12380354f8f.17.2024.07.29.07.35.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 Jul 2024 07:35:17 -0700 (PDT) From: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= To: Steven Rostedt , Masami Hiramatsu , linux-pwm@vger.kernel.org Cc: Mathieu Desnoyers , linux-trace-kernel@vger.kernel.org, Trevor Gamblin Subject: [PATCH v3 6/8] pwm: Add tracing for waveform callbacks Date: Mon, 29 Jul 2024 16:34:22 +0200 Message-ID: <7b9c9ee490df1df1de3bbfafd501f45c6cb2ec4c.1722261050.git.u.kleine-koenig@baylibre.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pwm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=6305; i=u.kleine-koenig@baylibre.com; h=from:subject:message-id; bh=suh8lj5U75UfHLAikpWSAju24qSZjPOfB+HigRwu20c=; b=owEBbQGS/pANAwAKAY+A+1h9Ev5OAcsmYgBmp6h8+2aKe76vTcEkVhxflMS6tCD98MRU23Ao+ 8oDdHvgR6yJATMEAAEKAB0WIQQ/gaxpOnoeWYmt/tOPgPtYfRL+TgUCZqeofAAKCRCPgPtYfRL+ TsWtCACKvnjYofCuiycqmy9MFrD8NFZ6FwGaNaLQwc0rKaf/+ivXoME+L3JwVoNAjLagosyu3+P 87U5esZJdBxp5bx4VkixuySJ4U8Fg1JYtVAuU/Cei/l7KZzXp2BkhBCBeIG1cHAD03FxHQI9u0v 4A0yAyB+1qfQdCn2aH0iHli4deXMzkMcIMB6t8SFwaOIB1I+qN5xEhLWU/HDdF20WG7mcX113Tj IISZ224m7dZaZ/6zZSJA6tqnATtyzLu/I7O5hKYp1v7V+z5PZ+dbfolqX4eCDMlfqWfAqCquCxC tg5vUYJ7TT/xjmcZsI7P1WMpB3JwmS0H2zrB2ejCBXrzPFYQ X-Developer-Key: i=u.kleine-koenig@baylibre.com; a=openpgp; fpr=0D2511F322BFAB1C1580266BE2DCDD9132669BD6 This adds trace events for the recently introduced waveform callbacks. With the introduction of some helper macros consistency among the different events is ensured. Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 24 +++++-- include/trace/events/pwm.h | 134 ++++++++++++++++++++++++++++++++++--- 2 files changed, 146 insertions(+), 12 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 888cd4f51c6e..bad0c8e65f56 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -166,30 +166,46 @@ static int __pwm_round_waveform_tohw(struct pwm_chip *chip, struct pwm_device *p const struct pwm_waveform *wf, void *wfhw) { const struct pwm_ops *ops = chip->ops; + int ret; - return ops->round_waveform_tohw(chip, pwm, wf, wfhw); + ret = ops->round_waveform_tohw(chip, pwm, wf, wfhw); + trace_pwm_round_waveform_tohw(pwm, wf, wfhw, ret); + + return ret; } static int __pwm_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm, const void *wfhw, struct pwm_waveform *wf) { const struct pwm_ops *ops = chip->ops; + int ret; - return ops->round_waveform_fromhw(chip, pwm, wfhw, wf); + ret = ops->round_waveform_fromhw(chip, pwm, wfhw, wf); + trace_pwm_round_waveform_fromhw(pwm, wfhw, wf, ret); + + return ret; } static int __pwm_read_waveform(struct pwm_chip *chip, struct pwm_device *pwm, void *wfhw) { const struct pwm_ops *ops = chip->ops; + int ret; - return ops->read_waveform(chip, pwm, wfhw); + ret = ops->read_waveform(chip, pwm, wfhw); + trace_pwm_read_waveform(pwm, wfhw, ret); + + return ret; } static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, const void *wfhw) { const struct pwm_ops *ops = chip->ops; + int ret; - return ops->write_waveform(chip, pwm, wfhw); + ret = ops->write_waveform(chip, pwm, wfhw); + trace_pwm_write_waveform(pwm, wfhw, ret); + + return ret; } #define WFHWSIZE 20 diff --git a/include/trace/events/pwm.h b/include/trace/events/pwm.h index 8022701c446d..8ba898fd335c 100644 --- a/include/trace/events/pwm.h +++ b/include/trace/events/pwm.h @@ -8,15 +8,135 @@ #include #include +#define TP_PROTO_pwm(args...) \ + TP_PROTO(struct pwm_device *pwm, args) + +#define TP_ARGS_pwm(args...) \ + TP_ARGS(pwm, args) + +#define TP_STRUCT__entry_pwm(args...) \ + TP_STRUCT__entry( \ + __field(unsigned int, chipid) \ + __field(unsigned int, hwpwm) \ + args) + +#define TP_fast_assign_pwm(args...) \ + TP_fast_assign( \ + __entry->chipid = pwm->chip->id; \ + __entry->hwpwm = pwm->hwpwm; \ + args) + +#define TP_printk_pwm(fmt, args...) \ + TP_printk("pwmchip%u.%u: " fmt, __entry->chipid, __entry->hwpwm, args) + +#define __field_pwmwf(wf) \ + __field(u64, wf ## _period_length_ns) \ + __field(u64, wf ## _duty_length_ns) \ + __field(u64, wf ## _duty_offset_ns) \ + +#define fast_assign_pwmwf(wf) \ + __entry->wf ## _period_length_ns = wf->period_length_ns; \ + __entry->wf ## _duty_length_ns = wf->duty_length_ns; \ + __entry->wf ## _duty_offset_ns = wf->duty_offset_ns + +#define printk_pwmwf_format(wf) \ + "%lld/%lld [+%lld]" + +#define printk_pwmwf_formatargs(wf) \ + __entry->wf ## _duty_length_ns, __entry->wf ## _period_length_ns, __entry->wf ## _duty_offset_ns + +TRACE_EVENT(pwm_round_waveform_tohw, + + TP_PROTO_pwm(const struct pwm_waveform *wf, void *wfhw, int err), + + TP_ARGS_pwm(wf, wfhw, err), + + TP_STRUCT__entry_pwm( + __field_pwmwf(wf) + __field(void *, wfhw) + __field(int, err) + ), + + TP_fast_assign_pwm( + fast_assign_pwmwf(wf); + __entry->wfhw = wfhw; + __entry->err = err; + ), + + TP_printk_pwm(printk_pwmwf_format(wf) " > %p err=%d", + printk_pwmwf_formatargs(wf), __entry->wfhw, __entry->err) +); + +TRACE_EVENT(pwm_round_waveform_fromhw, + + TP_PROTO_pwm(const void *wfhw, struct pwm_waveform *wf, int err), + + TP_ARGS_pwm(wfhw, wf, err), + + TP_STRUCT__entry_pwm( + __field(const void *, wfhw) + __field_pwmwf(wf) + __field(int, err) + ), + + TP_fast_assign_pwm( + __entry->wfhw = wfhw; + fast_assign_pwmwf(wf); + __entry->err = err; + ), + + TP_printk_pwm("%p > " printk_pwmwf_format(wf) " err=%d", + __entry->wfhw, printk_pwmwf_formatargs(wf), __entry->err) +); + +TRACE_EVENT(pwm_read_waveform, + + TP_PROTO_pwm(void *wfhw, int err), + + TP_ARGS_pwm(wfhw, err), + + TP_STRUCT__entry_pwm( + __field(void *, wfhw) + __field(int, err) + ), + + TP_fast_assign_pwm( + __entry->wfhw = wfhw; + __entry->err = err; + ), + + TP_printk_pwm("%p err=%d", + __entry->wfhw, __entry->err) +); + +TRACE_EVENT(pwm_write_waveform, + + TP_PROTO_pwm(const void *wfhw, int err), + + TP_ARGS_pwm(wfhw, err), + + TP_STRUCT__entry_pwm( + __field(const void *, wfhw) + __field(int, err) + ), + + TP_fast_assign_pwm( + __entry->wfhw = wfhw; + __entry->err = err; + ), + + TP_printk_pwm("%p err=%d", + __entry->wfhw, __entry->err) +); + + DECLARE_EVENT_CLASS(pwm, TP_PROTO(struct pwm_device *pwm, const struct pwm_state *state, int err), TP_ARGS(pwm, state, err), - TP_STRUCT__entry( - __field(unsigned int, chipid) - __field(unsigned int, hwpwm) + TP_STRUCT__entry_pwm( __field(u64, period) __field(u64, duty_cycle) __field(enum pwm_polarity, polarity) @@ -24,9 +144,7 @@ DECLARE_EVENT_CLASS(pwm, __field(int, err) ), - TP_fast_assign( - __entry->chipid = pwm->chip->id; - __entry->hwpwm = pwm->hwpwm; + TP_fast_assign_pwm( __entry->period = state->period; __entry->duty_cycle = state->duty_cycle; __entry->polarity = state->polarity; @@ -34,8 +152,8 @@ DECLARE_EVENT_CLASS(pwm, __entry->err = err; ), - TP_printk("pwmchip%u.%u: period=%llu duty_cycle=%llu polarity=%d enabled=%d err=%d", - __entry->chipid, __entry->hwpwm, __entry->period, __entry->duty_cycle, + TP_printk_pwm("period=%llu duty_cycle=%llu polarity=%d enabled=%d err=%d", + __entry->period, __entry->duty_cycle, __entry->polarity, __entry->enabled, __entry->err) ); From patchwork Mon Jul 29 14:34:23 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= X-Patchwork-Id: 1966057 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=3YIdM1lB; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=2604:1380:45d1:ec00::1; helo=ny.mirrors.kernel.org; envelope-from=linux-pwm+bounces-2929-incoming=patchwork.ozlabs.org@vger.kernel.org; receiver=patchwork.ozlabs.org) Received: from ny.mirrors.kernel.org (ny.mirrors.kernel.org [IPv6:2604:1380:45d1:ec00::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WXgvg0bxvz1yYq for ; Tue, 30 Jul 2024 00:38:23 +1000 (AEST) Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ny.mirrors.kernel.org (Postfix) with ESMTPS id 47FD01C20E42 for ; Mon, 29 Jul 2024 14:38:21 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 8730715B561; Mon, 29 Jul 2024 14:35:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b="3YIdM1lB" X-Original-To: linux-pwm@vger.kernel.org Received: from mail-wm1-f49.google.com (mail-wm1-f49.google.com [209.85.128.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6CF4F15B546 for ; Mon, 29 Jul 2024 14:35:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263723; cv=none; b=ixV2yBXBeKEN2YD2/8fK+gZZeQxgPnhEBEfB1qIysB94rExC9Ie+xvIZ0nbSUoWbi/TAuuMNDIdwgPPeTtTqPhs7wOYBzK89Gq+zMPWnQXj3lBf4bFV97MsMmI1TGE9SNZMS2UbCzAPK4XofvRPbSc1RpC/ksv9cH5U6JGjnrf8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263723; c=relaxed/simple; bh=rd3iGZlags0xCY2CaNWV3yMfgo7/ugpYl4/3RZTxNCI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=lSmRNgd1qA46Zhfkv++sCAX8Jy5Z3Msg0dxcTJefguyt8nHT7vz6F5kPBJVYA1qj1BsjH8Qw2hvm2c2r3ogBiXiw5zMq1b9nNSOGZ2s9DiFETIl1mXo4yieJADl5NS4m2xDrqRFGlvQITT7BVlvzoXexjEemq3Za1eH03Gtrv6Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com; spf=pass smtp.mailfrom=baylibre.com; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b=3YIdM1lB; arc=none smtp.client-ip=209.85.128.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=baylibre.com Received: by mail-wm1-f49.google.com with SMTP id 5b1f17b1804b1-428119da952so17023235e9.0 for ; Mon, 29 Jul 2024 07:35:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1722263720; x=1722868520; 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; bh=U2QznIvsNt9N3skUO5kcGaDAxTNGh853gMo3fAl40As=; b=3YIdM1lBu8RYZnW3Ejbs1XHZV9WeQ9zm/aNTGkmCXFvlkBD+8+iwJ+ypHx0yWGKWG+ BGF9kIYuZRTqSpsXHCiSYnUAKIvDBo4kZ8P9vj2+s6UY6xr6rbAvtIeBbscwWnBUVBh3 w88TIL1JCpB3LsnkKwefPSolR4n8/ep81SI9JUGEhDVNFgBw1xH76daO1c1IUFG4Rr3r xS2s3u3Hi0GOkZyot0N+jnGnGOF1tNTr5vLRsKrRszVx61SDHtRSulccqSHNjvFL+hCP WAmmpPyWjr2GsaJBQ3j4/C+ROBabnvf7i+PrtfTh8eZOrvUexwJhHIoxsxNfSQ10iePI mmeA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722263720; x=1722868520; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=U2QznIvsNt9N3skUO5kcGaDAxTNGh853gMo3fAl40As=; b=YN+uDrbGPQqS+X6hpIa6klDOARDyaLxAb01BRQgZqen8K1jH1FyllJabWLznqSxOlA tPECbUktL0G12+UAPooVUG2V66BsycF6WvK+Yngkr6qamwUCcTNBKJu0ksrYYVSJh/lw i6ZgHleekiAhmHHDZgNz0ovWtgBmZea5DUVCtHJUenFzbtudOfOUVommJe58I4nxJhs6 JP89a8RRM7tRCyD8tly4/sXkORl3b18CT4Jm/EtAR7ksruLHvqBDdO35y2a5w9ORFgg2 52WFZkHOHgftGEHlhRJ5y5WOw/Pz5eMUY3mU7gNdomL0/xhuUFoOUo9Yt0yd0aNCuz3M Uyzw== X-Gm-Message-State: AOJu0YxjK6A/u7j4R1ufG8Zbf9cZola6F8OH63ZrzNyv0VsZAkp+zTW+ k3+lIhdQr4/D9Lol4grzN32scb6v/6yAo/5soffPqhar6DSqsPbQEb0tpORqTJ5hE/asdHOSIx0 2 X-Google-Smtp-Source: AGHT+IG+iyVvwEXIl6cK3wbWccWykjy0xrJGHpaxtzblwflZ0pF7T7Dwgeyzb9tzUtmEq3va3SMCRg== X-Received: by 2002:a05:600c:1c83:b0:426:622d:9e6b with SMTP id 5b1f17b1804b1-42811dd79fbmr51440925e9.23.1722263719843; Mon, 29 Jul 2024 07:35:19 -0700 (PDT) Received: from localhost ([2a02:8071:b783:6940:59dd:510e:47d0:643f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4280f027411sm119125305e9.33.2024.07.29.07.35.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 Jul 2024 07:35:19 -0700 (PDT) From: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= To: linux-pwm@vger.kernel.org Cc: Michael Hennerich , =?utf-8?q?Nuno_S=C3=A1?= , Trevor Gamblin Subject: [PATCH v3 7/8] pwm: axi-pwmgen: Implementation of the waveform callbacks Date: Mon, 29 Jul 2024 16:34:23 +0200 Message-ID: <0ad43a94b89f8a298217dbbd376abd802dd98cbd.1722261050.git.u.kleine-koenig@baylibre.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pwm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=6920; i=u.kleine-koenig@baylibre.com; h=from:subject:message-id; bh=rd3iGZlags0xCY2CaNWV3yMfgo7/ugpYl4/3RZTxNCI=; b=owEBbQGS/pANAwAKAY+A+1h9Ev5OAcsmYgBmp6h+hX8q65hCZu2tXobl1AaTDedrIOh2BsKm6 sU6Hsh739SJATMEAAEKAB0WIQQ/gaxpOnoeWYmt/tOPgPtYfRL+TgUCZqeofgAKCRCPgPtYfRL+ TjGVB/96QmoAevMluMZVbXC//fL41UpOzdrv74lNmayoz7gOv+TRx1Yn/feVlxAYTliH26Y2EQA cSjdOdMbVGg3XeN4DlYTqopDsfaEk4xT08S+4KcUCs26uXRDYL1CTU/Zyre8Vt61a8tB2eE102I KpQH6JAJ8qM6jRUgvvYy5nhRkPu5jktFjhmWKLGjd4hg2Is+k9UboXVXsp73yBb6LKTKffg74nA pnJQuOIqqXYnn9LK0NPMjdmzcGjuFMZxG7u9mhsvNrohaer1UJI+nc80fwIZo89LPtgP18kcDDN lD/1Z1nNiDXisCaLXD5okmpxW/AUh1rqN1A/uNviTrJPP8n2 X-Developer-Key: i=u.kleine-koenig@baylibre.com; a=openpgp; fpr=0D2511F322BFAB1C1580266BE2DCDD9132669BD6 Convert the axi-pwmgen driver to use the new callbacks for hardware programming. Tested-by: Trevor Gamblin Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-axi-pwmgen.c | 154 ++++++++++++++++++++++++----------- 1 file changed, 108 insertions(+), 46 deletions(-) diff --git a/drivers/pwm/pwm-axi-pwmgen.c b/drivers/pwm/pwm-axi-pwmgen.c index 3ad60edf20a5..14c3274b551b 100644 --- a/drivers/pwm/pwm-axi-pwmgen.c +++ b/drivers/pwm/pwm-axi-pwmgen.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -54,81 +55,142 @@ static const struct regmap_config axi_pwmgen_regmap_config = { .max_register = 0xFC, }; -static int axi_pwmgen_apply(struct pwm_chip *chip, struct pwm_device *pwm, - const struct pwm_state *state) +/* This represents a hardware configuration for one channel */ +struct axi_pwmgen_waveform { + u32 period_cnt; + u32 duty_cycle_cnt; + u32 duty_offset_cnt; +}; + +static int axi_pwmgen_round_waveform_tohw(struct pwm_chip *chip, + struct pwm_device *pwm, + const struct pwm_waveform *wf, + void *_wfhw) { + struct axi_pwmgen_waveform *wfhw = _wfhw; + struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip); + + if (wf->period_length_ns == 0) { + *wfhw = (struct axi_pwmgen_waveform){ + .period_cnt = 0, + .duty_cycle_cnt = 0, + .duty_offset_cnt = 0, + }; + } else { + /* With ddata->clk_rate_hz < NSEC_PER_SEC this won't overflow. */ + wfhw->period_cnt = min_t(u64, + mul_u64_u32_div(wf->period_length_ns, ddata->clk_rate_hz, NSEC_PER_SEC), + U32_MAX); + + if (wfhw->period_cnt == 0) { + /* + * The specified period is too short for the hardware. + * Let's round .duty_cycle down to 0 to get a (somewhat) + * valid result. + */ + wfhw->period_cnt = 1; + wfhw->duty_cycle_cnt = 0; + wfhw->duty_offset_cnt = 0; + } else { + wfhw->duty_cycle_cnt = min_t(u64, + mul_u64_u32_div(wf->duty_length_ns, ddata->clk_rate_hz, NSEC_PER_SEC), + U32_MAX); + wfhw->duty_offset_cnt = min_t(u64, + mul_u64_u32_div(wf->duty_offset_ns, ddata->clk_rate_hz, NSEC_PER_SEC), + U32_MAX); + } + } + + dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] @%lu -> PERIOD: %08x, DUTY: %08x, OFFSET: %08x\n", + pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, + ddata->clk_rate_hz, wfhw->period_cnt, wfhw->duty_cycle_cnt, wfhw->duty_offset_cnt); + + return 0; +} + +static int axi_pwmgen_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm, + const void *_wfhw, struct pwm_waveform *wf) +{ + const struct axi_pwmgen_waveform *wfhw = _wfhw; + struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip); + + wf->period_length_ns = DIV64_U64_ROUND_UP((u64)wfhw->period_cnt * NSEC_PER_SEC, + ddata->clk_rate_hz); + + wf->duty_length_ns = DIV64_U64_ROUND_UP((u64)wfhw->duty_cycle_cnt * NSEC_PER_SEC, + ddata->clk_rate_hz); + + wf->duty_offset_ns = DIV64_U64_ROUND_UP((u64)wfhw->duty_offset_cnt * NSEC_PER_SEC, + ddata->clk_rate_hz); + + return 0; +} + +static int axi_pwmgen_write_waveform(struct pwm_chip *chip, + struct pwm_device *pwm, + const void *_wfhw) +{ + const struct axi_pwmgen_waveform *wfhw = _wfhw; struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip); - unsigned int ch = pwm->hwpwm; struct regmap *regmap = ddata->regmap; - u64 period_cnt, duty_cnt; + unsigned int ch = pwm->hwpwm; int ret; - if (state->polarity != PWM_POLARITY_NORMAL) - return -EINVAL; + ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), wfhw->period_cnt); + if (ret) + return ret; - if (state->enabled) { - period_cnt = mul_u64_u64_div_u64(state->period, ddata->clk_rate_hz, NSEC_PER_SEC); - if (period_cnt > UINT_MAX) - period_cnt = UINT_MAX; + ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), wfhw->duty_cycle_cnt); + if (ret) + return ret; - if (period_cnt == 0) - return -EINVAL; - - ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), period_cnt); - if (ret) - return ret; - - duty_cnt = mul_u64_u64_div_u64(state->duty_cycle, ddata->clk_rate_hz, NSEC_PER_SEC); - if (duty_cnt > UINT_MAX) - duty_cnt = UINT_MAX; - - ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), duty_cnt); - if (ret) - return ret; - } else { - ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), 0); - if (ret) - return ret; - - ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), 0); - if (ret) - return ret; - } + ret = regmap_write(regmap, AXI_PWMGEN_CHX_OFFSET(ch), wfhw->duty_offset_cnt); + if (ret) + return ret; return regmap_write(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_LOAD_CONFIG); } -static int axi_pwmgen_get_state(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +static int axi_pwmgen_read_waveform(struct pwm_chip *chip, + struct pwm_device *pwm, + void *_wfhw) { + struct axi_pwmgen_waveform *wfhw = _wfhw; struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip); struct regmap *regmap = ddata->regmap; unsigned int ch = pwm->hwpwm; - u32 cnt; int ret; - ret = regmap_read(regmap, AXI_PWMGEN_CHX_PERIOD(ch), &cnt); + ret = regmap_read(regmap, AXI_PWMGEN_CHX_PERIOD(ch), &wfhw->period_cnt); if (ret) return ret; - state->enabled = cnt != 0; - - state->period = DIV_ROUND_UP_ULL((u64)cnt * NSEC_PER_SEC, ddata->clk_rate_hz); - - ret = regmap_read(regmap, AXI_PWMGEN_CHX_DUTY(ch), &cnt); + ret = regmap_read(regmap, AXI_PWMGEN_CHX_DUTY(ch), &wfhw->duty_cycle_cnt); if (ret) return ret; - state->duty_cycle = DIV_ROUND_UP_ULL((u64)cnt * NSEC_PER_SEC, ddata->clk_rate_hz); + ret = regmap_read(regmap, AXI_PWMGEN_CHX_OFFSET(ch), &wfhw->duty_offset_cnt); + if (ret) + return ret; - state->polarity = PWM_POLARITY_NORMAL; + if (wfhw->duty_cycle_cnt > wfhw->period_cnt) + wfhw->duty_cycle_cnt = wfhw->period_cnt; + + /* XXX: is this the actual behaviour of the hardware? */ + if (wfhw->duty_offset_cnt >= wfhw->period_cnt) { + wfhw->duty_cycle_cnt = 0; + wfhw->duty_offset_cnt = 0; + } return 0; } static const struct pwm_ops axi_pwmgen_pwm_ops = { - .apply = axi_pwmgen_apply, - .get_state = axi_pwmgen_get_state, + .sizeof_wfhw = sizeof(struct axi_pwmgen_waveform), + .round_waveform_tohw = axi_pwmgen_round_waveform_tohw, + .round_waveform_fromhw = axi_pwmgen_round_waveform_fromhw, + .read_waveform = axi_pwmgen_read_waveform, + .write_waveform = axi_pwmgen_write_waveform, }; static int axi_pwmgen_setup(struct regmap *regmap, struct device *dev) From patchwork Mon Jul 29 14:34:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= X-Patchwork-Id: 1966058 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=3RkoRbgP; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=2604:1380:40f1:3f00::1; helo=sy.mirrors.kernel.org; envelope-from=linux-pwm+bounces-2930-incoming=patchwork.ozlabs.org@vger.kernel.org; receiver=patchwork.ozlabs.org) Received: from sy.mirrors.kernel.org (sy.mirrors.kernel.org [IPv6:2604:1380:40f1:3f00::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WXgvg5bQ2z20Fq for ; Tue, 30 Jul 2024 00:38:23 +1000 (AEST) Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sy.mirrors.kernel.org (Postfix) with ESMTPS id 2981DB22E54 for ; Mon, 29 Jul 2024 14:38:23 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 4B20A15B13C; Mon, 29 Jul 2024 14:35:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b="3RkoRbgP" X-Original-To: linux-pwm@vger.kernel.org Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E3982155A58 for ; Mon, 29 Jul 2024 14:35:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263726; cv=none; b=SdckEzW2PE+NDkkOtsp0djGUL4TVAPGkpYQxOq34dzYOPFEvWiJmmQQwweJkXOsrYkL+HQ+t1/cpFSUC+6GvkX4ZaTx3woEyl6uNOOxNGf0YXgsmeZLskQ1RDGjetEO4Rqh25myapkVBVvKUJE4MHOgDjSIWZpHFuBn+ML0s0MA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722263726; c=relaxed/simple; bh=p3etr4V6tRF3RoLSJnJV32PLW+PXYWtkQtcV3LE/fYg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=RjrkI4ALv/HlyYSzupLWakMscB/xx5JijusWfsfc7e0HZ1n3YE0Aw3UyaSKf0KiNNyV6HCowQ9q4LaArwk8AD8vuo6fXLzKKeM4Bo1dJkSpS/z+iG7cBN4C5lLNIYbZczaS1/Sn4WKd8w0gAqc+g8EiOfEYJuBA+mWmD2l4AkgE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com; spf=pass smtp.mailfrom=baylibre.com; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b=3RkoRbgP; arc=none smtp.client-ip=209.85.128.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=baylibre.com Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-4281e3b2f72so7276375e9.1 for ; Mon, 29 Jul 2024 07:35:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1722263722; x=1722868522; 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; bh=6pfF4deIEab/vxZmoh05/o96Mjb32dnv5FG3n9I9/lg=; b=3RkoRbgPgZen0295gJ9fLfmjq2L2bpD5ZY5oinwkQyRSKojuF4AuhnoxSkzGRglwqu AndBvw+Ti5KgVq5QX0BaH/FLjnCdbQ7K8hrXReZGFhFOJ5bPtgnnuAXyhttHdfd3yWCG 97Y87UuzBL4qxg8R1C4Js2Fb62KmRqqGsYFjs17fyfW/qqNHvn8ddTr0SnpJfooy0Zgj SSeDjzjWCgZWiqhnR5TLK7Sf2AGiCg4w5CrHKgFxp8z+ZwAXclj8oCfbZTESsBRZbUE/ plR30QgpwR+hD9ftCXlTPX6fJ9reqzQ91ovZ0oNxGOVfgkop79+qAve6Jb4xiOhxQ98q OlLQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722263722; x=1722868522; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=6pfF4deIEab/vxZmoh05/o96Mjb32dnv5FG3n9I9/lg=; b=t4Ii5GYiWDnACPBy6ChERx9K7dQIjT9z8gwQYe6FpmzgzjOoRxt5I+8hbLTP8WI/mQ +BAWs5jiLMHHysSmHnPL8RbU7S9bs2GZyZYXglxG3bE0r82rzNTTYgftEBdXxpyQrYLz 1OELbUv9Y/KVclOGXq/lttZ51EeFRpP8Ua6Cpx7EhgitdcXgoGks6BvOo7qbKxqmjMwo ggZvIC7sjRg5BwxdHIagS4qoNyt5tgYx5QFMVfOXKqueVHQomIUXuxtwIo0FA/FDPafP dw32ki9JjJUZOTCLsolPfhX3bE9Xx5WyKGDnAljG3yW5+7gZnu4IsA6D767qtw5mokxE oq2g== X-Gm-Message-State: AOJu0YwuTNnzztPVepuEZnhtySnwMbONzo98zPXaG0AiDiSW5Jm/myzt YtB1chs3O0uD+V4CkV1RZxu/7sR5fmp7sS+gYqM9v2/5L9H319OwjuJrwMQNJXTL2xNjcRu964B 2 X-Google-Smtp-Source: AGHT+IE7yxPHidyiqrJwesEVlv6DnjKIHipLAQCs288B62eXtX7ZPDmBtZrtBufv8T0r4fbGYML9lQ== X-Received: by 2002:a05:600c:5126:b0:428:eb6:2e73 with SMTP id 5b1f17b1804b1-42811dd103fmr46209995e9.29.1722263722333; Mon, 29 Jul 2024 07:35:22 -0700 (PDT) Received: from localhost ([2a02:8071:b783:6940:59dd:510e:47d0:643f]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-42813e5067dsm90015485e9.16.2024.07.29.07.35.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 Jul 2024 07:35:22 -0700 (PDT) From: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= To: linux-pwm@vger.kernel.org Cc: Fabrice Gasnier , Maxime Coquelin , Alexandre Torgue , linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, Trevor Gamblin Subject: [PATCH v3 8/8] pwm: stm32: Implementation of the waveform callbacks Date: Mon, 29 Jul 2024 16:34:24 +0200 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pwm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=17104; i=u.kleine-koenig@baylibre.com; h=from:subject:message-id; bh=p3etr4V6tRF3RoLSJnJV32PLW+PXYWtkQtcV3LE/fYg=; b=owEBbQGS/pANAwAKAY+A+1h9Ev5OAcsmYgBmp6iBT8LYQ4vQSPDY6q7L3zurXNXT7CEerL/vk agz+Uh1H+SJATMEAAEKAB0WIQQ/gaxpOnoeWYmt/tOPgPtYfRL+TgUCZqeogQAKCRCPgPtYfRL+ Tm4BCACt2YwihcMDhE0bYBrwwXtC44fOLKYKORkzS7Lzrui4eQnGIJLW3mq8BqIl7g5IDV1ZU6L YrWQhrU1FDyPOLLzVMnXeY8hzz+p/fEGMzE4zogQNkKWyt+qsGLtPmfFDURM3el5Aq91OzjYYVV SQx9XmIQaoRZmYBxmcnwc9Zh8G8YgzG2pEwtxIHFXZ98BXp+Ox78PnH77tQsm3N/hmriwlZFnVa g2AsQ2vhdK/2oZsPWgyx7Z2gmPIwFQ0OM/XCrUiIlNlNpxRyoD/gra71X0CkYeUOCgCe/FKLz0q ZA4kascV4CJtyZOVJtq+9pcFJkia7dLgDtta8WubfLOFjIOx X-Developer-Key: i=u.kleine-koenig@baylibre.com; a=openpgp; fpr=0D2511F322BFAB1C1580266BE2DCDD9132669BD6 Convert the stm32 pwm driver to use the new callbacks for hardware programming. Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-stm32.c | 607 +++++++++++++++++++++++++--------------- 1 file changed, 386 insertions(+), 221 deletions(-) diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index fd754a99cf2e..846da265bbfe 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -51,6 +51,386 @@ static u32 active_channels(struct stm32_pwm *dev) return ccer & TIM_CCER_CCXE; } +struct stm32_pwm_waveform { + u32 ccer; + u32 psc; + u32 arr; + u32 ccr; +}; + +static int stm32_pwm_round_waveform_tohw(struct pwm_chip *chip, + struct pwm_device *pwm, + const struct pwm_waveform *wf, + void *_wfhw) +{ + struct stm32_pwm_waveform *wfhw = _wfhw; + struct stm32_pwm *priv = to_stm32_pwm_dev(chip); + unsigned int ch = pwm->hwpwm; + unsigned long rate; + u64 ccr, duty; + int ret; + + if (wf->period_length_ns == 0) { + *wfhw = (struct stm32_pwm_waveform){ + .ccer = 0, + }; + + return 0; + } + + ret = clk_enable(priv->clk); + if (ret) + return ret; + + wfhw->ccer = TIM_CCER_CCxE(ch + 1); + if (priv->have_complementary_output) + wfhw->ccer = TIM_CCER_CCxNE(ch); + + rate = clk_get_rate(priv->clk); + + if (active_channels(priv) & ~(1 << ch * 4)) { + u64 arr; + + /* + * Other channels are already enabled, so the configured PSC and + * ARR must be used for this channel, too. + */ + ret = regmap_read(priv->regmap, TIM_PSC, &wfhw->psc); + if (ret) + goto out; + + ret = regmap_read(priv->regmap, TIM_ARR, &wfhw->arr); + if (ret) + goto out; + + /* + * calculate the best value for ARR for the given PSC, refuse if + * the resulting period gets bigger than the requested one. + */ + arr = mul_u64_u64_div_u64(wf->period_length_ns, rate, + (u64)NSEC_PER_SEC * (wfhw->psc + 1)); + if (arr <= wfhw->arr) { + /* + * requested period is small than the currently + * configured and unchangable period, report back the smallest + * possible period, i.e. the current state; Initialize + * ccr to anything valid. + */ + wfhw->ccr = 0; + ret = 1; + goto out; + } + + } else { + /* + * .probe() asserted that clk_get_rate() is not bigger than 1 GHz, so + * the calculations here won't overflow. + * First we need to find the minimal value for prescaler such that + * + * period_ns * clkrate + * ------------------------------ < max_arr + 1 + * NSEC_PER_SEC * (prescaler + 1) + * + * This equation is equivalent to + * + * period_ns * clkrate + * ---------------------------- < prescaler + 1 + * NSEC_PER_SEC * (max_arr + 1) + * + * Using integer division and knowing that the right hand side is + * integer, this is further equivalent to + * + * (period_ns * clkrate) // (NSEC_PER_SEC * (max_arr + 1)) ≤ prescaler + */ + u64 psc = mul_u64_u64_div_u64(wf->period_length_ns, rate, + (u64)NSEC_PER_SEC * ((u64)priv->max_arr + 1)); + u64 arr; + + wfhw->psc = min_t(u64, psc, MAX_TIM_PSC); + + arr = mul_u64_u64_div_u64(wf->period_length_ns, rate, + (u64)NSEC_PER_SEC * (wfhw->psc + 1)); + if (!arr) { + /* + * requested period is too small, report back the smallest + * possible period, i.e. ARR = 0. The only valid CCR + * value is then zero, too. + */ + wfhw->arr = 0; + wfhw->ccr = 0; + ret = 1; + goto out; + } + + /* + * ARR is limited intentionally to values less than + * priv->max_arr to allow 100% duty cycle. + */ + wfhw->arr = min_t(u64, arr, priv->max_arr) - 1; + } + + duty = mul_u64_u64_div_u64(wf->duty_length_ns, rate, + (u64)NSEC_PER_SEC * (wfhw->psc + 1)); + duty = min_t(u64, duty, wfhw->arr + 1); + + if (wf->duty_length_ns && wf->duty_offset_ns && + wf->duty_length_ns + wf->duty_offset_ns >= wf->period_length_ns) { + wfhw->ccer |= TIM_CCER_CCxP(ch + 1); + if (priv->have_complementary_output) + wfhw->ccer |= TIM_CCER_CCxNP(ch + 1); + + ccr = wfhw->arr + 1 - duty; + } else { + ccr = duty; + } + + wfhw->ccr = min_t(u64, ccr, wfhw->arr + 1); + + dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] @%lu -> CCER: %08x, PSC: %08x, ARR: %08x, CCR: %08x\n", + pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, + rate, wfhw->ccer, wfhw->psc, wfhw->arr, wfhw->ccr); + +out: + clk_disable(priv->clk); + + return ret; +} + +/* + * This should be moved to lib/math/div64.c. Currently there are some changes + * pending to mul_u64_u64_div_u64. Uwe will care for that when the dust settles. + */ +static u64 stm32_pwm_mul_u64_u64_div_u64_roundup(u64 a, u64 b, u64 c) +{ + u64 res = mul_u64_u64_div_u64(a, b, c); + /* Those multiplications might overflow but it doesn't matter */ + u64 rem = a * b - c * res; + + if (rem) + res += 1; + + return res; +} + +static int stm32_pwm_round_waveform_fromhw(struct pwm_chip *chip, + struct pwm_device *pwm, + const void *_wfhw, + struct pwm_waveform *wf) +{ + const struct stm32_pwm_waveform *wfhw = _wfhw; + struct stm32_pwm *priv = to_stm32_pwm_dev(chip); + unsigned int ch = pwm->hwpwm; + + if (wfhw->ccer & TIM_CCER_CCxE(ch + 1)) { + unsigned long rate = clk_get_rate(priv->clk); + u64 ccr_ns; + + /* The result doesn't overflow for rate >= 15259 */ + wf->period_length_ns = stm32_pwm_mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * (wfhw->arr + 1), + NSEC_PER_SEC, rate); + + ccr_ns = stm32_pwm_mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * wfhw->ccr, + NSEC_PER_SEC, rate); + + if (wfhw->ccer & TIM_CCER_CCxP(ch + 1)) { + wf->duty_length_ns = + stm32_pwm_mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * (wfhw->arr + 1 - wfhw->ccr), + NSEC_PER_SEC, rate); + + wf->duty_offset_ns = ccr_ns; + } else { + wf->duty_length_ns = ccr_ns; + wf->duty_offset_ns = 0; + } + } else { + *wf = (struct pwm_waveform){ + .period_length_ns = 0, + }; + } + + return 0; +} + +static int stm32_pwm_read_waveform(struct pwm_chip *chip, + struct pwm_device *pwm, + void *_wfhw) +{ + struct stm32_pwm_waveform *wfhw = _wfhw; + struct stm32_pwm *priv = to_stm32_pwm_dev(chip); + unsigned int ch = pwm->hwpwm; + int ret; + + ret = clk_enable(priv->clk); + if (ret) + return ret; + + ret = regmap_read(priv->regmap, TIM_CCER, &wfhw->ccer); + if (ret) + goto out; + + if (wfhw->ccer & TIM_CCER_CCxE(ch + 1)) { + ret = regmap_read(priv->regmap, TIM_PSC, &wfhw->psc); + if (ret) + goto out; + + ret = regmap_read(priv->regmap, TIM_ARR, &wfhw->arr); + if (ret) + goto out; + + if (wfhw->arr == U32_MAX) + wfhw->arr -= 1; + + ret = regmap_read(priv->regmap, TIM_CCRx(ch + 1), &wfhw->ccr); + if (ret) + goto out; + + if (wfhw->ccr > wfhw->arr + 1) + wfhw->ccr = wfhw->arr + 1; + } + +out: + clk_disable(priv->clk); + + return ret; +} + +static int stm32_pwm_write_waveform(struct pwm_chip *chip, + struct pwm_device *pwm, + const void *_wfhw) +{ + const struct stm32_pwm_waveform *wfhw = _wfhw; + struct stm32_pwm *priv = to_stm32_pwm_dev(chip); + unsigned int ch = pwm->hwpwm; + int ret; + + ret = clk_enable(priv->clk); + if (ret) + return ret; + + if (wfhw->ccer & TIM_CCER_CCxE(ch + 1)) { + u32 ccer, mask; + unsigned int shift; + u32 ccmr; + + ret = regmap_read(priv->regmap, TIM_CCER, &ccer); + if (ret) + goto out; + + /* If there are other channels enabled, don't update PSC and ARR */ + if (ccer & ~TIM_CCER_CCxE(ch + 1) & TIM_CCER_CCXE) { + u32 psc, arr; + + ret = regmap_read(priv->regmap, TIM_PSC, &psc); + if (ret) + goto out; + + if (psc != wfhw->psc) { + ret = -EBUSY; + goto out; + } + + regmap_read(priv->regmap, TIM_ARR, &arr); + if (ret) + goto out; + + if (arr != wfhw->arr) { + ret = -EBUSY; + goto out; + } + } else { + ret = regmap_write(priv->regmap, TIM_PSC, wfhw->psc); + if (ret) + goto out; + + ret = regmap_write(priv->regmap, TIM_ARR, wfhw->arr); + if (ret) + goto out; + + ret = regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE); + if (ret) + goto out; + + } + + /* set polarity */ + mask = TIM_CCER_CCxP(ch + 1) | TIM_CCER_CCxNP(ch + 1); + ret = regmap_update_bits(priv->regmap, TIM_CCER, mask, wfhw->ccer); + if (ret) + goto out; + + ret = regmap_write(priv->regmap, TIM_CCRx(ch + 1), wfhw->ccr); + if (ret) + goto out; + + /* Configure output mode */ + shift = (ch & 0x1) * CCMR_CHANNEL_SHIFT; + ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift; + mask = CCMR_CHANNEL_MASK << shift; + + if (ch < 2) + ret = regmap_update_bits(priv->regmap, TIM_CCMR1, mask, ccmr); + else + ret = regmap_update_bits(priv->regmap, TIM_CCMR2, mask, ccmr); + if (ret) + goto out; + + ret = regmap_set_bits(priv->regmap, TIM_BDTR, TIM_BDTR_MOE); + if (ret) + goto out; + + if (!(ccer & TIM_CCER_CCxE(ch + 1))) { + mask = TIM_CCER_CCxE(ch + 1) | TIM_CCER_CCxNE(ch + 1); + + ret = clk_enable(priv->clk); + if (ret) + goto out; + + ccer = (ccer & ~mask) | (wfhw->ccer & mask); + regmap_write(priv->regmap, TIM_CCER, ccer); + + /* Make sure that registers are updated */ + regmap_set_bits(priv->regmap, TIM_EGR, TIM_EGR_UG); + + /* Enable controller */ + regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); + } + + } else { + /* disable channel */ + u32 mask, ccer; + + mask = TIM_CCER_CCxE(ch + 1); + if (priv->have_complementary_output) + mask |= TIM_CCER_CCxNE(ch + 1); + + ret = regmap_read(priv->regmap, TIM_CCER, &ccer); + if (ret) + goto out; + + if (ccer & mask) { + ccer = ccer & ~mask; + + ret = regmap_write(priv->regmap, TIM_CCER, ccer); + if (ret) + goto out; + + if (!(ccer & TIM_CCER_CCXE)) { + /* When all channels are disabled, we can disable the controller */ + ret = regmap_clear_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); + if (ret) + goto out; + } + + clk_disable(priv->clk); + } + } + +out: + clk_disable(priv->clk); + + return ret; +} + #define TIM_CCER_CC12P (TIM_CCER_CC1P | TIM_CCER_CC2P) #define TIM_CCER_CC12E (TIM_CCER_CC1E | TIM_CCER_CC2E) #define TIM_CCER_CC34P (TIM_CCER_CC3P | TIM_CCER_CC4P) @@ -308,228 +688,13 @@ static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm, return ret; } -static int stm32_pwm_config(struct stm32_pwm *priv, unsigned int ch, - u64 duty_ns, u64 period_ns) -{ - unsigned long long prd, dty; - unsigned long long prescaler; - u32 ccmr, mask, shift; - - /* - * .probe() asserted that clk_get_rate() is not bigger than 1 GHz, so - * the calculations here won't overflow. - * First we need to find the minimal value for prescaler such that - * - * period_ns * clkrate - * ------------------------------ < max_arr + 1 - * NSEC_PER_SEC * (prescaler + 1) - * - * This equation is equivalent to - * - * period_ns * clkrate - * ---------------------------- < prescaler + 1 - * NSEC_PER_SEC * (max_arr + 1) - * - * Using integer division and knowing that the right hand side is - * integer, this is further equivalent to - * - * (period_ns * clkrate) // (NSEC_PER_SEC * (max_arr + 1)) ≤ prescaler - */ - - prescaler = mul_u64_u64_div_u64(period_ns, clk_get_rate(priv->clk), - (u64)NSEC_PER_SEC * ((u64)priv->max_arr + 1)); - if (prescaler > MAX_TIM_PSC) - return -EINVAL; - - prd = mul_u64_u64_div_u64(period_ns, clk_get_rate(priv->clk), - (u64)NSEC_PER_SEC * (prescaler + 1)); - if (!prd) - return -EINVAL; - - /* - * All channels share the same prescaler and counter so when two - * channels are active at the same time we can't change them - */ - if (active_channels(priv) & ~(1 << ch * 4)) { - u32 psc, arr; - - regmap_read(priv->regmap, TIM_PSC, &psc); - regmap_read(priv->regmap, TIM_ARR, &arr); - - if ((psc != prescaler) || (arr != prd - 1)) - return -EBUSY; - } - - regmap_write(priv->regmap, TIM_PSC, prescaler); - regmap_write(priv->regmap, TIM_ARR, prd - 1); - regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE); - - /* Calculate the duty cycles */ - dty = mul_u64_u64_div_u64(duty_ns, clk_get_rate(priv->clk), - (u64)NSEC_PER_SEC * (prescaler + 1)); - - regmap_write(priv->regmap, TIM_CCRx(ch + 1), dty); - - /* Configure output mode */ - shift = (ch & 0x1) * CCMR_CHANNEL_SHIFT; - ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift; - mask = CCMR_CHANNEL_MASK << shift; - - if (ch < 2) - regmap_update_bits(priv->regmap, TIM_CCMR1, mask, ccmr); - else - regmap_update_bits(priv->regmap, TIM_CCMR2, mask, ccmr); - - regmap_set_bits(priv->regmap, TIM_BDTR, TIM_BDTR_MOE); - - return 0; -} - -static int stm32_pwm_set_polarity(struct stm32_pwm *priv, unsigned int ch, - enum pwm_polarity polarity) -{ - u32 mask; - - mask = TIM_CCER_CCxP(ch + 1); - if (priv->have_complementary_output) - mask |= TIM_CCER_CCxNP(ch + 1); - - regmap_update_bits(priv->regmap, TIM_CCER, mask, - polarity == PWM_POLARITY_NORMAL ? 0 : mask); - - return 0; -} - -static int stm32_pwm_enable(struct stm32_pwm *priv, unsigned int ch) -{ - u32 mask; - int ret; - - ret = clk_enable(priv->clk); - if (ret) - return ret; - - /* Enable channel */ - mask = TIM_CCER_CCxE(ch + 1); - if (priv->have_complementary_output) - mask |= TIM_CCER_CCxNE(ch); - - regmap_set_bits(priv->regmap, TIM_CCER, mask); - - /* Make sure that registers are updated */ - regmap_set_bits(priv->regmap, TIM_EGR, TIM_EGR_UG); - - /* Enable controller */ - regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); - - return 0; -} - -static void stm32_pwm_disable(struct stm32_pwm *priv, unsigned int ch) -{ - u32 mask; - - /* Disable channel */ - mask = TIM_CCER_CCxE(ch + 1); - if (priv->have_complementary_output) - mask |= TIM_CCER_CCxNE(ch + 1); - - regmap_clear_bits(priv->regmap, TIM_CCER, mask); - - /* When all channels are disabled, we can disable the controller */ - if (!active_channels(priv)) - regmap_clear_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); - - clk_disable(priv->clk); -} - -static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, - const struct pwm_state *state) -{ - bool enabled; - struct stm32_pwm *priv = to_stm32_pwm_dev(chip); - int ret; - - enabled = pwm->state.enabled; - - if (!state->enabled) { - if (enabled) - stm32_pwm_disable(priv, pwm->hwpwm); - return 0; - } - - if (state->polarity != pwm->state.polarity) - stm32_pwm_set_polarity(priv, pwm->hwpwm, state->polarity); - - ret = stm32_pwm_config(priv, pwm->hwpwm, - state->duty_cycle, state->period); - if (ret) - return ret; - - if (!enabled && state->enabled) - ret = stm32_pwm_enable(priv, pwm->hwpwm); - - return ret; -} - -static int stm32_pwm_apply_locked(struct pwm_chip *chip, struct pwm_device *pwm, - const struct pwm_state *state) -{ - struct stm32_pwm *priv = to_stm32_pwm_dev(chip); - int ret; - - /* protect common prescaler for all active channels */ - mutex_lock(&priv->lock); - ret = stm32_pwm_apply(chip, pwm, state); - mutex_unlock(&priv->lock); - - return ret; -} - -static int stm32_pwm_get_state(struct pwm_chip *chip, - struct pwm_device *pwm, struct pwm_state *state) -{ - struct stm32_pwm *priv = to_stm32_pwm_dev(chip); - int ch = pwm->hwpwm; - unsigned long rate; - u32 ccer, psc, arr, ccr; - u64 dty, prd; - int ret; - - mutex_lock(&priv->lock); - - ret = regmap_read(priv->regmap, TIM_CCER, &ccer); - if (ret) - goto out; - - state->enabled = ccer & TIM_CCER_CCxE(ch + 1); - state->polarity = (ccer & TIM_CCER_CCxP(ch + 1)) ? - PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL; - ret = regmap_read(priv->regmap, TIM_PSC, &psc); - if (ret) - goto out; - ret = regmap_read(priv->regmap, TIM_ARR, &arr); - if (ret) - goto out; - ret = regmap_read(priv->regmap, TIM_CCRx(ch + 1), &ccr); - if (ret) - goto out; - - rate = clk_get_rate(priv->clk); - - prd = (u64)NSEC_PER_SEC * (psc + 1) * (arr + 1); - state->period = DIV_ROUND_UP_ULL(prd, rate); - dty = (u64)NSEC_PER_SEC * (psc + 1) * ccr; - state->duty_cycle = DIV_ROUND_UP_ULL(dty, rate); - -out: - mutex_unlock(&priv->lock); - return ret; -} - static const struct pwm_ops stm32pwm_ops = { - .apply = stm32_pwm_apply_locked, - .get_state = stm32_pwm_get_state, + .sizeof_wfhw = sizeof(struct stm32_pwm_waveform), + .round_waveform_tohw = stm32_pwm_round_waveform_tohw, + .round_waveform_fromhw = stm32_pwm_round_waveform_fromhw, + .read_waveform = stm32_pwm_read_waveform, + .write_waveform = stm32_pwm_write_waveform, + .capture = IS_ENABLED(CONFIG_DMA_ENGINE) ? stm32_pwm_capture : NULL, };