From patchwork Thu Feb 15 12:41:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Claudiu X-Patchwork-Id: 1899308 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; secure) header.d=tuxon.dev header.i=@tuxon.dev header.a=rsa-sha256 header.s=google header.b=jk5EEVfh; 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-gpio+bounces-3345-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 4TbF7B2TRtz23hm for ; Thu, 15 Feb 2024 23:41:42 +1100 (AEDT) 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 D6A9F2814C8 for ; Thu, 15 Feb 2024 12:41:40 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A6A5C12DD8F; Thu, 15 Feb 2024 12:41:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=tuxon.dev header.i=@tuxon.dev header.b="jk5EEVfh" X-Original-To: linux-gpio@vger.kernel.org Received: from mail-wm1-f41.google.com (mail-wm1-f41.google.com [209.85.128.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B0E6E12D74F for ; Thu, 15 Feb 2024 12:41:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708000893; cv=none; b=NzHHv4hJM1+M6EoRMo0Q7BzSn/Y69KYqFkz29Jut+6byYORMH0Td+5pDg3eEigFdsoEnlsx7i+xH3YGKlt2z7tCBINyMDa7MDpBHIbbxznTcDQlUoIXfMFJEUw8mfXm0zFylSaM+5vnzJEGJ0c16Y1OYGK76qbgpudwIRlkeESw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708000893; c=relaxed/simple; bh=gHnyFq4f1pDYyCf0BWfNJKuDKgh/PzyoaOwfFn6k2Rk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=CpwhvIcAeoKYvrQ1cjBrp7n8s7jXijIumyb/Gboej0I/G88zvcZW9a5n5xSM6fm6VuwJY6SJ1fLtiVlg4OQyTnbbUbnBHGR0T6qpHdAhOdVHwoN5qJ+auyUJkOimQlnNG+uiYLOHvtvluSuqo9r73IQ88iR516/d3PGaQVGeAE0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=tuxon.dev; spf=pass smtp.mailfrom=tuxon.dev; dkim=pass (2048-bit key) header.d=tuxon.dev header.i=@tuxon.dev header.b=jk5EEVfh; arc=none smtp.client-ip=209.85.128.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=tuxon.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=tuxon.dev Received: by mail-wm1-f41.google.com with SMTP id 5b1f17b1804b1-40efcb37373so6324235e9.2 for ; Thu, 15 Feb 2024 04:41:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tuxon.dev; s=google; t=1708000890; x=1708605690; 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=dfSE0heYPzlfo04ucdVX3fOnYjllc+qDYIJqJxlkX8U=; b=jk5EEVfhjoVO2j4mPq4QK7/ydyYUzQiUiVGl4rF982HSBW3uv+BFU/JOx3c0HOBHIb kg19ZhqTBctVmBbV73BV6qwmhQIdnXV1ZiwrBAfpWe7al+O9ck+lNk5tOahDHRukAv2N WAHuepMhHyh6v3g0t/M3pqdljCEcfK4PelDlWPlHVfVL/QKUATDfHlvoGGhrqJwDlHZW h0q//NptmR6fZ8alb0Udsw4zWVr2gR+jdY2SYLdNgVBE5kkjfTLdbi1sXP9MhPnpuy71 yHEbPNcLoUGCvGIA9P6AHt3hx1nfB8H65vwvlUoJ60/vVw/7M+AnCFD8faPOEp2bBAU9 P/0g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1708000890; x=1708605690; 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=dfSE0heYPzlfo04ucdVX3fOnYjllc+qDYIJqJxlkX8U=; b=i6RO5fSph083VUwuPJ+1YxhF2Kwu22+q0rH0zE//MjjC203vItGTvyhTYPY8l+1x2K 0d1UhEhy2hKOzdO7T02g1NwVLDm9jasVBMUOUxWYzvo3PktmQvgPj/JvwrUBDlb1yOCv 1XD29MsalYKAt3K4exhOdSz42GdPcseAhk8qVtJ2d2hkOig0Uyreunn53mbrsg/9sMsA 9ypytdCRSaef/UQ6jOD44SIIe8Gi68CPJ8wehTC5Omc1j8eElqLVA0hgj3XyaF9OYbHM NcpzyMCVCsY5/XsCXbcg0e9ndzObgmse0t8lZp13JNXeBCWDZijafBEYXTUVnBRCD9Rc U/6g== X-Forwarded-Encrypted: i=1; AJvYcCUaqwaZ7yvuMADvAJ7ywN8bciQ7Y1E2NylkiWG2O/ZoH1Z7lKFEpHgfieSoBWLNkposnZhpu9wqjTREPA3bX2xNmUJT0AjXkM/RXQ== X-Gm-Message-State: AOJu0YyprecMo2WfeMnqABqFm2ViA1zXqxthskJR0tRk0shqUzmN76Na rLtY7io9coTed2re3iw6ip5IT9MLaIaypU48bFzYTW5TEwQBo5cjOi14fv8Sls4= X-Google-Smtp-Source: AGHT+IGikOwkuOPFBTsMbTZdEAAonvNOhcu6PDAwcbmJos4zHQ93Q6qK8wgIe3B2ezllABgdj7icJQ== X-Received: by 2002:a05:600c:43c3:b0:40f:dd4a:11f5 with SMTP id f3-20020a05600c43c300b0040fdd4a11f5mr1497192wmn.34.1708000889857; Thu, 15 Feb 2024 04:41:29 -0800 (PST) Received: from claudiu-X670E-Pro-RS.. ([82.78.167.20]) by smtp.gmail.com with ESMTPSA id n16-20020a5d51d0000000b0033cefb84b16sm1674931wrv.52.2024.02.15.04.41.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Feb 2024 04:41:28 -0800 (PST) From: Claudiu X-Google-Original-From: Claudiu To: geert+renesas@glider.be, linus.walleij@linaro.org Cc: linux-renesas-soc@vger.kernel.org, linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org, claudiu.beznea@tuxon.dev, Claudiu Beznea Subject: [PATCH v2 1/2] pinctrl: renesas: Select CONFIG_IRQ_DOMAIN_HIERARCHY for pinctrl-rzg2l Date: Thu, 15 Feb 2024 14:41:11 +0200 Message-Id: <20240215124112.2259103-2-claudiu.beznea.uj@bp.renesas.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240215124112.2259103-1-claudiu.beznea.uj@bp.renesas.com> References: <20240215124112.2259103-1-claudiu.beznea.uj@bp.renesas.com> Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Claudiu Beznea The pinctrl-rzg2l driver uses APIs that are defined only if CONFIG_IRQ_DOMAIN_HIERARCHY=y (irq_chip_*_parent() APIs). On the ARCH_RZG2L CONFIG_IRQ_DOMAIN_HIERARCHY is selected anyway, e.g., by the CONFIG_ARM_GIC_V3. Even so, make it explicit at the driver level for a clearer view of the dependencies. Signed-off-by: Claudiu Beznea Reviewed-by: Geert Uytterhoeven --- Changes in v2: - this patch is introduced in v2 drivers/pinctrl/renesas/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/renesas/Kconfig b/drivers/pinctrl/renesas/Kconfig index c8d519ca53eb..a1e57939e54b 100644 --- a/drivers/pinctrl/renesas/Kconfig +++ b/drivers/pinctrl/renesas/Kconfig @@ -190,6 +190,7 @@ config PINCTRL_RZG2L select GENERIC_PINCTRL_GROUPS select GENERIC_PINMUX_FUNCTIONS select GENERIC_PINCONF + select IRQ_DOMAIN_HIERARCHY help This selects GPIO and pinctrl driver for Renesas RZ/{G2L,G2UL,V2L} platforms. From patchwork Thu Feb 15 12:41:12 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Claudiu X-Patchwork-Id: 1899309 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; secure) header.d=tuxon.dev header.i=@tuxon.dev header.a=rsa-sha256 header.s=google header.b=VD2WyMdv; 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-gpio+bounces-3346-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 4TbF7S1sl6z23hm for ; Thu, 15 Feb 2024 23:41:56 +1100 (AEDT) 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 7055D1C23589 for ; Thu, 15 Feb 2024 12:41:54 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3C51712E1FF; Thu, 15 Feb 2024 12:41:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=tuxon.dev header.i=@tuxon.dev header.b="VD2WyMdv" X-Original-To: linux-gpio@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 BC2C512DD9A for ; Thu, 15 Feb 2024 12:41:33 +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=1708000896; cv=none; b=uTzQq+2uXa3BbEnfQC3WenQhXPmuXwm01gTA6UeBOhfRRAlEN5SzCHg/nfcK5iQ1oa+qK9BqUbbMWxVy+BF/ljY2Qmcykjgs8/sBC7MTiYawx77V5kjxwmp1v28cXLSxclXXKftv3xoV2d2VqXIjZhai9rRhCewiXUZPsZx529s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708000896; c=relaxed/simple; bh=3r2hp3/Q7jGc/O04zu9NxGou7pHcI3g1mgV5Mmn8eOQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=FxBnBg/HFyPn1NMFNCBrRfADOFhatWQsIL3/F2JYK3kHRa0At8FOGxkJeiLEIXjmp7JGVepN3mHnMniIZY/KHNxIyCr6Q29WHCfVPHB4aukTtnuyFVMQcFg4BcuFUvLUAuf9EHOF+NzI46w6JbQ36dZ26fjSVnpwc70N5jcZz1o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=tuxon.dev; spf=pass smtp.mailfrom=tuxon.dev; dkim=pass (2048-bit key) header.d=tuxon.dev header.i=@tuxon.dev header.b=VD2WyMdv; arc=none smtp.client-ip=209.85.221.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=tuxon.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=tuxon.dev Received: by mail-wr1-f46.google.com with SMTP id ffacd0b85a97d-33d0a7f2424so273739f8f.0 for ; Thu, 15 Feb 2024 04:41:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tuxon.dev; s=google; t=1708000892; x=1708605692; 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=zY03LI9eTwIaXczCnv2uGINoKlZdRzt+YmyGrzkFxsw=; b=VD2WyMdvOLUsiyiW3zmvyIaWFY7mggswlmE4chZXMJOXzaBv/NWS9yKQSL8YZ9kZys 78csu++oqYcHwAqT8FST5P60tG5G93D2Fs1X9Hq3e2kaQIGkyW/maKCi67kUXqymtUk7 dKdQvrpD0GK5JERxwPSfscwnrgsugbu3rRMUOnqWSYRJLKdTbgZdcqKLnXXUX3cCYtkp JvG5lmkLn4uapg8Y7lKfiEUkGva/TQU5cNwT7Sr8NJDEB+5wpZS5Mf2san0tNxSB5Joh dzBcBsebqdi/MKg0RE+/EtsIQWqr/58RMXQTQkGhOd91YTpmUpnMz9LouA/tPTZ9xxz/ aZzA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1708000892; x=1708605692; 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=zY03LI9eTwIaXczCnv2uGINoKlZdRzt+YmyGrzkFxsw=; b=chUhMvE4aGa75b1hyZZFeazdddSicLfUMDQA/7E9scTu26IDL8pjKIL8WhuCb76hvR yAH/5FAfAU/XyA+QoHVCBaUnCsCrOn4xHzYTAV2q8mpju9auebqs/VwSdttATci+0SGp ogIrb1DWhnnglLR7bKCqNvVM47Kw8p5nXM/oaLf6iJc8SonONUcFrPCFYCqyAXuX7bSa UlTPDwAVZoCV/ADlEQ+LOgKHKWx/DnMDeU0l5UDxuLXWyTYJCsEGcaztq+/GSzhAHuAJ 1s0D182+PTSGMpko0b0QXgRk/uFXPqub+egLE2gZ6NuxP7YGCcvLxjUEZmECNXI/NPW5 wf2Q== X-Forwarded-Encrypted: i=1; AJvYcCVZDZ6Okr6mwFaTOs0vKVuIj11+YUS7ULACIk5Nb/erlaK50vZbYlgoSb5qcPbykwZ1UtzjUHKS0O03loYoB1YR6nJnMo8Pr3qcYA== X-Gm-Message-State: AOJu0YwcbIaGkH8OQUwoJsQsvV9+J9qL57uFdI8VrGKkvoV35foqhuey xDbA/acieE/M8Nuepesq+NPZqhHDQ8wbAme2pdnrrz3OSGhAEoeuKrtcnuImw7k= X-Google-Smtp-Source: AGHT+IEtHZSHyxI98o4xHKyMrMLi7hDQUknqMVsNqFSY9aFx+v8g9HARaFSq9dMPiKl20rIy8bdNpg== X-Received: by 2002:adf:a344:0:b0:33b:60ba:d990 with SMTP id d4-20020adfa344000000b0033b60bad990mr1150689wrb.19.1708000891892; Thu, 15 Feb 2024 04:41:31 -0800 (PST) Received: from claudiu-X670E-Pro-RS.. ([82.78.167.20]) by smtp.gmail.com with ESMTPSA id n16-20020a5d51d0000000b0033cefb84b16sm1674931wrv.52.2024.02.15.04.41.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Feb 2024 04:41:30 -0800 (PST) From: Claudiu X-Google-Original-From: Claudiu To: geert+renesas@glider.be, linus.walleij@linaro.org Cc: linux-renesas-soc@vger.kernel.org, linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org, claudiu.beznea@tuxon.dev, Claudiu Beznea Subject: [PATCH v2 2/2] pinctrl: renesas: rzg2l: Add suspend/resume support Date: Thu, 15 Feb 2024 14:41:12 +0200 Message-Id: <20240215124112.2259103-3-claudiu.beznea.uj@bp.renesas.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240215124112.2259103-1-claudiu.beznea.uj@bp.renesas.com> References: <20240215124112.2259103-1-claudiu.beznea.uj@bp.renesas.com> Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Claudiu Beznea pinctrl-rzg2l driver is used on RZ/G3S which support deep sleep states where power to most of the SoC components is turned off. For this add suspend/resume support. This involves saving and restoring configured registers along with disabling clock in case there is no pin configured as wakeup sources. To save/restore registers 2 caches were allocated: one for GPIO pins and one for dedicated pins. On suspend path the pin controller registers are saved and if none of the pins are configured as wakeup sources the pinctrl clock is disabled. Otherwise it remains on. On resume path the configuration is done as follows: 1/ setup PFCs by writing to registers on pin based accesses 2/ setup GPIOs by writing to registers on port based accesses and following configuration steps specified in hardware manual 3/ setup dedicated pins by writing to registers on port based accesses 4/ setup interrupts. Because interrupt signals are routed to IA55 interrupt controller and IA55 interrupt controller resumes before pin controller, patch restores also the configured interrupts just after pin settings are restored to avoid invalid interrupts while resuming. Signed-off-by: Claudiu Beznea Reviewed-by: Geert Uytterhoeven --- Changes in v2: - use u8 for sd_ch, eth_poc, eth_mode, qspi members of struct rzg2l_pinctrl_reg_cache and readb()/writeb() where necessary - s/wakeup_source/wakeup_path/g - call device_set_wakeup_path() on suspend - call irq_chip_set_wake_parent() on rzg2l_gpio_irq_set_wake() drivers/pinctrl/renesas/pinctrl-rzg2l.c | 408 +++++++++++++++++++++++- 1 file changed, 404 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 818dccdd70da..9903739ba75a 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -149,6 +149,33 @@ #define RZG2L_TINT_IRQ_START_INDEX 9 #define RZG2L_PACK_HWIRQ(t, i) (((t) << 16) | (i)) +/* Read/write 8 bits register */ +#define RZG2L_PCTRL_REG_ACCESS8(_read, _addr, _val) \ + do { \ + if (_read) \ + _val = readb(_addr); \ + else \ + writeb(_val, _addr); \ + } while (0) + +/* Read/write 16 bits register */ +#define RZG2L_PCTRL_REG_ACCESS16(_read, _addr, _val) \ + do { \ + if (_read) \ + _val = readw(_addr); \ + else \ + writew(_val, _addr); \ + } while (0) + +/* Read/write 32 bits register */ +#define RZG2L_PCTRL_REG_ACCESS32(_read, _addr, _val) \ + do { \ + if (_read) \ + _val = readl(_addr); \ + else \ + writel(_val, _addr); \ + } while (0) + /** * struct rzg2l_register_offsets - specific register offsets * @pwpr: PWPR register offset @@ -241,6 +268,32 @@ struct rzg2l_pinctrl_pin_settings { u16 drive_strength_ua; }; +/** + * struct rzg2l_pinctrl_reg_cache - register cache structure (to be used in suspend/resume) + * @p: P registers cache + * @pm: PM registers cache + * @pmc: PMC registers cache + * @pfc: PFC registers cache + * @iolh: IOLH registers cache + * @ien: IEN registers cache + * @sd_ch: SD_CH registers cache + * @eth_poc: ET_POC registers cache + * @eth_mode: ETH_MODE register cache + * @qspi: QSPI registers cache + */ +struct rzg2l_pinctrl_reg_cache { + u8 *p; + u16 *pm; + u8 *pmc; + u32 *pfc; + u32 *iolh[2]; + u32 *ien[2]; + u8 sd_ch[2]; + u8 eth_poc[2]; + u8 eth_mode; + u8 qspi; +}; + struct rzg2l_pinctrl { struct pinctrl_dev *pctl; struct pinctrl_desc desc; @@ -250,6 +303,8 @@ struct rzg2l_pinctrl { void __iomem *base; struct device *dev; + struct clk *clk; + struct gpio_chip gpio_chip; struct pinctrl_gpio_range gpio_range; DECLARE_BITMAP(tint_slot, RZG2L_TINT_MAX_INTERRUPT); @@ -260,6 +315,9 @@ struct rzg2l_pinctrl { struct mutex mutex; /* serialize adding groups and functions */ struct rzg2l_pinctrl_pin_settings *settings; + struct rzg2l_pinctrl_reg_cache *cache; + struct rzg2l_pinctrl_reg_cache *dedicated_cache; + atomic_t wakeup_path; }; static const u16 available_ps[] = { 1800, 2500, 3300 }; @@ -1880,6 +1938,28 @@ static void rzg2l_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p) seq_printf(p, dev_name(gc->parent)); } +static int rzg2l_gpio_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + struct rzg2l_pinctrl *pctrl = container_of(gc, struct rzg2l_pinctrl, gpio_chip); + int ret; + + /* It should not happen. */ + if (!data->parent_data) + return -EOPNOTSUPP; + + ret = irq_chip_set_wake_parent(data, on); + if (ret) + return ret; + + if (on) + atomic_inc(&pctrl->wakeup_path); + else + atomic_dec(&pctrl->wakeup_path); + + return 0; +} + static const struct irq_chip rzg2l_gpio_irqchip = { .name = "rzg2l-gpio", .irq_disable = rzg2l_gpio_irq_disable, @@ -1890,6 +1970,7 @@ static const struct irq_chip rzg2l_gpio_irqchip = { .irq_eoi = rzg2l_gpio_irqc_eoi, .irq_print_chip = rzg2l_gpio_irq_print_chip, .irq_set_affinity = irq_chip_set_affinity_parent, + .irq_set_wake = rzg2l_gpio_irq_set_wake, .flags = IRQCHIP_IMMUTABLE, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; @@ -1937,6 +2018,35 @@ static int rzg2l_gpio_populate_parent_fwspec(struct gpio_chip *chip, return 0; } +static void rzg2l_gpio_irq_restore(struct rzg2l_pinctrl *pctrl) +{ + struct irq_domain *domain = pctrl->gpio_chip.irq.domain; + + for (unsigned int i = 0; i < RZG2L_TINT_MAX_INTERRUPT; i++) { + struct irq_data *data; + unsigned int virq; + + if (!pctrl->hwirq[i]) + continue; + + virq = irq_find_mapping(domain, pctrl->hwirq[i]); + if (!virq) { + dev_crit(pctrl->dev, "Failed to find IRQ mapping for hwirq %u\n", + pctrl->hwirq[i]); + continue; + } + + data = irq_domain_get_irq_data(domain, virq); + if (!data) { + dev_crit(pctrl->dev, "Failed to get IRQ data for virq=%u\n", virq); + continue; + } + + if (!irqd_irq_disabled(data)) + rzg2l_gpio_irq_enable(data); + } +} + static void rzg2l_gpio_irq_domain_free(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs) { @@ -1985,6 +2095,68 @@ static void rzg2l_init_irq_valid_mask(struct gpio_chip *gc, } } +static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl) +{ + u32 nports = pctrl->data->n_port_pins / RZG2L_PINS_PER_PORT; + struct rzg2l_pinctrl_reg_cache *cache, *dedicated_cache; + + cache = devm_kzalloc(pctrl->dev, sizeof(*cache), GFP_KERNEL); + if (!cache) + return -ENOMEM; + + dedicated_cache = devm_kzalloc(pctrl->dev, sizeof(*dedicated_cache), GFP_KERNEL); + if (!dedicated_cache) + return -ENOMEM; + + cache->p = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->p), GFP_KERNEL); + if (!cache->p) + return -ENOMEM; + + cache->pm = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->pm), GFP_KERNEL); + if (!cache->pm) + return -ENOMEM; + + cache->pmc = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->pmc), GFP_KERNEL); + if (!cache->pmc) + return -ENOMEM; + + cache->pfc = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->pfc), GFP_KERNEL); + if (!cache->pfc) + return -ENOMEM; + + for (u8 i = 0; i < 2; i++) { + u32 n_dedicated_pins = pctrl->data->n_dedicated_pins; + + cache->iolh[i] = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->iolh[i]), + GFP_KERNEL); + if (!cache->iolh[i]) + return -ENOMEM; + + cache->ien[i] = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->ien[i]), + GFP_KERNEL); + if (!cache->ien[i]) + return -ENOMEM; + + /* Allocate dedicated cache. */ + dedicated_cache->iolh[i] = devm_kcalloc(pctrl->dev, n_dedicated_pins, + sizeof(*dedicated_cache->iolh[i]), + GFP_KERNEL); + if (!dedicated_cache->iolh[i]) + return -ENOMEM; + + dedicated_cache->ien[i] = devm_kcalloc(pctrl->dev, n_dedicated_pins, + sizeof(*dedicated_cache->ien[i]), + GFP_KERNEL); + if (!dedicated_cache->ien[i]) + return -ENOMEM; + } + + pctrl->cache = cache; + pctrl->dedicated_cache = dedicated_cache; + + return 0; +} + static int rzg2l_gpio_register(struct rzg2l_pinctrl *pctrl) { struct device_node *np = pctrl->dev->of_node; @@ -2125,6 +2297,10 @@ static int rzg2l_pinctrl_register(struct rzg2l_pinctrl *pctrl) } } + ret = rzg2l_pinctrl_reg_cache_alloc(pctrl); + if (ret) + return ret; + ret = devm_pinctrl_register_and_init(pctrl->dev, &pctrl->desc, pctrl, &pctrl->pctl); if (ret) { @@ -2150,7 +2326,6 @@ static int rzg2l_pinctrl_register(struct rzg2l_pinctrl *pctrl) static int rzg2l_pinctrl_probe(struct platform_device *pdev) { struct rzg2l_pinctrl *pctrl; - struct clk *clk; int ret; BUILD_BUG_ON(ARRAY_SIZE(r9a07g044_gpio_configs) * RZG2L_PINS_PER_PORT > @@ -2176,14 +2351,16 @@ static int rzg2l_pinctrl_probe(struct platform_device *pdev) if (IS_ERR(pctrl->base)) return PTR_ERR(pctrl->base); - clk = devm_clk_get_enabled(pctrl->dev, NULL); - if (IS_ERR(clk)) - return dev_err_probe(pctrl->dev, PTR_ERR(clk), + pctrl->clk = devm_clk_get_enabled(pctrl->dev, NULL); + if (IS_ERR(pctrl->clk)) { + return dev_err_probe(pctrl->dev, PTR_ERR(pctrl->clk), "failed to enable GPIO clk\n"); + } spin_lock_init(&pctrl->lock); spin_lock_init(&pctrl->bitmap_lock); mutex_init(&pctrl->mutex); + atomic_set(&pctrl->wakeup_path, 0); platform_set_drvdata(pdev, pctrl); @@ -2195,6 +2372,224 @@ static int rzg2l_pinctrl_probe(struct platform_device *pdev) return 0; } +static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspend) +{ + u32 nports = pctrl->data->n_port_pins / RZG2L_PINS_PER_PORT; + struct rzg2l_pinctrl_reg_cache *cache = pctrl->cache; + + for (u32 port = 0; port < nports; port++) { + bool has_iolh, has_ien; + u32 off, caps; + u8 pincnt; + u64 cfg; + + cfg = pctrl->data->port_pin_configs[port]; + off = RZG2L_PIN_CFG_TO_PORT_OFFSET(cfg); + pincnt = hweight8(FIELD_GET(PIN_CFG_PIN_MAP_MASK, cfg)); + + caps = FIELD_GET(PIN_CFG_MASK, cfg); + has_iolh = !!(caps & (PIN_CFG_IOLH_A | PIN_CFG_IOLH_B | PIN_CFG_IOLH_C)); + has_ien = !!(caps & PIN_CFG_IEN); + + if (suspend) + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PFC(off), cache->pfc[port]); + + /* + * Now cache the registers or set them in the order suggested by + * HW manual (section "Operation for GPIO Function"). + */ + RZG2L_PCTRL_REG_ACCESS8(suspend, pctrl->base + PMC(off), cache->pmc[port]); + if (has_iolh) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + IOLH(off), + cache->iolh[0][port]); + if (pincnt >= 4) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + IOLH(off) + 4, + cache->iolh[1][port]); + } + } + + RZG2L_PCTRL_REG_ACCESS16(suspend, pctrl->base + PM(off), cache->pm[port]); + RZG2L_PCTRL_REG_ACCESS8(suspend, pctrl->base + P(off), cache->p[port]); + + if (has_ien) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + IEN(off), + cache->ien[0][port]); + if (pincnt >= 4) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + IEN(off) + 4, + cache->ien[1][port]); + } + } + } +} + +static void rzg2l_pinctrl_pm_setup_dedicated_regs(struct rzg2l_pinctrl *pctrl, bool suspend) +{ + struct rzg2l_pinctrl_reg_cache *cache = pctrl->dedicated_cache; + + /* + * Make sure entries in pctrl->data->n_dedicated_pins[] having the same + * port offset are close together. + */ + for (u32 i = 0, caps = 0; i < pctrl->data->n_dedicated_pins; i++) { + bool has_iolh, has_ien; + u32 off, next_off = 0; + u64 cfg, next_cfg; + u8 pincnt; + + cfg = pctrl->data->dedicated_pins[i].config; + off = RZG2L_PIN_CFG_TO_PORT_OFFSET(cfg); + if (i + 1 < pctrl->data->n_dedicated_pins) { + next_cfg = pctrl->data->dedicated_pins[i + 1].config; + next_off = RZG2L_PIN_CFG_TO_PORT_OFFSET(next_cfg); + } + + if (off == next_off) { + /* Gather caps of all port pins. */ + caps |= FIELD_GET(PIN_CFG_MASK, cfg); + continue; + } + + /* And apply them in a single shot. */ + has_iolh = !!(caps & (PIN_CFG_IOLH_A | PIN_CFG_IOLH_B | PIN_CFG_IOLH_C)); + has_ien = !!(caps & PIN_CFG_IEN); + pincnt = hweight8(FIELD_GET(RZG2L_SINGLE_PIN_BITS_MASK, cfg)); + + if (has_iolh) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + IOLH(off), + cache->iolh[0][i]); + } + if (has_ien) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + IEN(off), + cache->ien[0][i]); + } + + if (pincnt >= 4) { + if (has_iolh) { + RZG2L_PCTRL_REG_ACCESS32(suspend, + pctrl->base + IOLH(off) + 4, + cache->iolh[1][i]); + } + if (has_ien) { + RZG2L_PCTRL_REG_ACCESS32(suspend, + pctrl->base + IEN(off) + 4, + cache->ien[1][i]); + } + } + caps = 0; + } +} + +static void rzg2l_pinctrl_pm_setup_pfc(struct rzg2l_pinctrl *pctrl) +{ + u32 nports = pctrl->data->n_port_pins / RZG2L_PINS_PER_PORT; + const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg; + const struct rzg2l_register_offsets *regs = &hwcfg->regs; + + /* Set the PWPR register to allow PFC register to write. */ + writel(0x0, pctrl->base + regs->pwpr); /* B0WI=0, PFCWE=0 */ + writel(PWPR_PFCWE, pctrl->base + regs->pwpr); /* B0WI=0, PFCWE=1 */ + + /* Restore port registers. */ + for (u32 port = 0; port < nports; port++) { + unsigned long pinmap; + u8 pmc = 0, max_pin; + u32 off, pfc = 0; + u64 cfg; + u16 pm; + u8 pin; + + cfg = pctrl->data->port_pin_configs[port]; + off = RZG2L_PIN_CFG_TO_PORT_OFFSET(cfg); + pinmap = FIELD_GET(PIN_CFG_PIN_MAP_MASK, cfg); + max_pin = fls(pinmap); + + pm = readw(pctrl->base + PM(off)); + for_each_set_bit(pin, &pinmap, max_pin) { + struct rzg2l_pinctrl_reg_cache *cache = pctrl->cache; + + /* Nothing to do if PFC was not configured before. */ + if (!(cache->pmc[port] & BIT(pin))) + continue; + + /* Set pin to 'Non-use (Hi-Z input protection)' */ + pm &= ~(PM_MASK << (pin * 2)); + writew(pm, pctrl->base + PM(off)); + + /* Temporarily switch to GPIO mode with PMC register */ + pmc &= ~BIT(pin); + writeb(pmc, pctrl->base + PMC(off)); + + /* Select Pin function mode. */ + pfc &= ~(PFC_MASK << (pin * 4)); + pfc |= (cache->pfc[port] & (PFC_MASK << (pin * 4))); + writel(pfc, pctrl->base + PFC(off)); + + /* Switch to Peripheral pin function. */ + pmc |= BIT(pin); + writeb(pmc, pctrl->base + PMC(off)); + } + } + + /* Set the PWPR register to be write-protected. */ + writel(0x0, pctrl->base + regs->pwpr); /* B0WI=0, PFCWE=0 */ + writel(PWPR_B0WI, pctrl->base + regs->pwpr); /* B0WI=1, PFCWE=0 */ +} + +static int rzg2l_pinctrl_suspend_noirq(struct device *dev) +{ + struct rzg2l_pinctrl *pctrl = dev_get_drvdata(dev); + const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg; + const struct rzg2l_register_offsets *regs = &hwcfg->regs; + struct rzg2l_pinctrl_reg_cache *cache = pctrl->cache; + + rzg2l_pinctrl_pm_setup_regs(pctrl, true); + rzg2l_pinctrl_pm_setup_dedicated_regs(pctrl, true); + + for (u8 i = 0; i < 2; i++) { + cache->sd_ch[i] = readb(pctrl->base + SD_CH(regs->sd_ch, i)); + cache->eth_poc[i] = readb(pctrl->base + ETH_POC(regs->eth_poc, i)); + } + + cache->qspi = readb(pctrl->base + QSPI); + cache->eth_mode = readb(pctrl->base + ETH_MODE); + + if (!atomic_read(&pctrl->wakeup_path)) + clk_disable_unprepare(pctrl->clk); + else + device_set_wakeup_path(dev); + + return 0; +} + +static int rzg2l_pinctrl_resume_noirq(struct device *dev) +{ + struct rzg2l_pinctrl *pctrl = dev_get_drvdata(dev); + const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg; + const struct rzg2l_register_offsets *regs = &hwcfg->regs; + struct rzg2l_pinctrl_reg_cache *cache = pctrl->cache; + int ret; + + if (!atomic_read(&pctrl->wakeup_path)) { + ret = clk_prepare_enable(pctrl->clk); + if (ret) + return ret; + } + + writeb(cache->qspi, pctrl->base + QSPI); + writeb(cache->eth_mode, pctrl->base + ETH_MODE); + for (u8 i = 0; i < 2; i++) { + writeb(cache->sd_ch[i], pctrl->base + SD_CH(regs->sd_ch, i)); + writeb(cache->eth_poc[i], pctrl->base + ETH_POC(regs->eth_poc, i)); + } + + rzg2l_pinctrl_pm_setup_pfc(pctrl); + rzg2l_pinctrl_pm_setup_regs(pctrl, false); + rzg2l_pinctrl_pm_setup_dedicated_regs(pctrl, false); + rzg2l_gpio_irq_restore(pctrl); + + return 0; +} + static const struct rzg2l_hwcfg rzg2l_hwcfg = { .regs = { .pwpr = 0x3014, @@ -2291,10 +2686,15 @@ static const struct of_device_id rzg2l_pinctrl_of_table[] = { { /* sentinel */ } }; +static const struct dev_pm_ops rzg2l_pinctrl_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(rzg2l_pinctrl_suspend_noirq, rzg2l_pinctrl_resume_noirq) +}; + static struct platform_driver rzg2l_pinctrl_driver = { .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(rzg2l_pinctrl_of_table), + .pm = pm_sleep_ptr(&rzg2l_pinctrl_pm_ops), }, .probe = rzg2l_pinctrl_probe, };