From nobody Mon Jun 29 23:25:21 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 56AA5C433EF for ; Tue, 1 Feb 2022 11:19:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237225AbiBALTy (ORCPT ); Tue, 1 Feb 2022 06:19:54 -0500 Received: from esa.microchip.iphmx.com ([68.232.154.123]:10723 "EHLO esa.microchip.iphmx.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236928AbiBALTw (ORCPT ); Tue, 1 Feb 2022 06:19:52 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1643714392; x=1675250392; h=from:to:cc:subject:date:message-id:mime-version: content-transfer-encoding; bh=IIB5rDXBtoZRTwes/NhdX9oReKXVwSPkBaGh5bykTbw=; b=cOXFVlhb1/W8ipIjwJm/0f0QppyJe5fGkGvGIOYR0qojjFurPJ0KSBLz z1kegsbqvmD1AEIat6GHbQoHRRJ9HggXz6V2I2iA07erEMpo8Dty3Chns 2IiZ4PpvPEHz+7nXJpKWgrxSEUm3RmCU9AlresdL2AZmqmkpXWCTqMqLO 3lEN2RZvSF9YM/CKqfj1++6zg0odY0RZGyhudobgB+nMBdtVUnxBtrRef vC3agYgFPRtsuUQZXsPijb0KOuTt7HHltijvKVTSOP9KZaDw7nFr4SvpW nAMkmGfu8lg9IhiRR6mmG5r3MOLilzd7OABs35HYpGHel9q3DGvcjBZWI g==; IronPort-SDR: KGT9rOB5LDUXaUsVeNaVcNugp4wR5GIgeBXB6VplMT9NRYmndFU5cbymqIfwhEYac3sSVlkwx/ PB6I+0dt5F4u0N2ZPn1JgO7DB33StHwa0UaHyyXGzpjyedhvk3FMsO4pqyWg0fADbI7XqgKBgo 0dx4dya7rwVUgBsytdWbiOxZvPkYO2dFuTFcbV4O0mT65OaLXzMK58rk1Ksg5z33ahjg0Iksdf W3HuysYKK4BpExjgrDVlvzLp024j8N/MCL/yEfs8x+w+t/gfYdB4O4AqJnqMlIgryLzmzU/VS7 4LA6ENHHCpsn9oDoIFh8+a7S X-IronPort-AV: E=Sophos;i="5.88,333,1635231600"; d="scan'208";a="147244652" Received: from smtpout.microchip.com (HELO email.microchip.com) ([198.175.253.82]) by esa2.microchip.iphmx.com with ESMTP/TLS/AES256-SHA256; 01 Feb 2022 04:19:51 -0700 Received: from chn-vm-ex03.mchp-main.com (10.10.85.151) by chn-vm-ex04.mchp-main.com (10.10.85.152) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.17; Tue, 1 Feb 2022 04:19:51 -0700 Received: from localhost.localdomain (10.10.115.15) by chn-vm-ex03.mchp-main.com (10.10.85.151) with Microsoft SMTP Server id 15.1.2375.17 via Frontend Transport; Tue, 1 Feb 2022 04:19:49 -0700 From: Claudiu Beznea To: , CC: , Claudiu Beznea Subject: [RESEND][PATCH] mfd: syscon: allow mapping a range of addresses Date: Tue, 1 Feb 2022 13:20:50 +0200 Message-ID: <20220201112050.3466927-1-claudiu.beznea@microchip.com> X-Mailer: git-send-email 2.33.0 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Allow the mapping of a range of addresses for syscon. This is done through regmap_config::wr_table, regmap_config::rd_table. Previously, only one range could have been allocated to a syscon via regmap_config::max_register. The code is needed for SAMA7G5 whose reset controller contains a register (at offset 0xE001D0E4, see below) that is used to control the USB PHYs but that register is not located continuously in reset controller address space, as follows: Controller's name Memory space Offset ... ... ... +------------+ 0xE001D000 Reset controller | | +------------+ 0xE001D010 Shutdown controller | | +------------+ 0xE001D020 RTT controller | | +------------+ 0xE001D050 ... ... ... +------------+ 0xE001D0E4 Reset controller | | +------------+ 0xE001D0E8 ... ... ... To control the PHYs the proper syscon is retrieved from proper driver. With previous approach only the first range passed though device tree was taken into account by syscon. With the code in this patch the following DT bindings: reset_controller: rstc@e001d000 { compatible =3D "microchip,sama7g5-rstc", "microchip,sam9x60-rstc", "syscon"; #address-cells =3D <1>; #size-cells =3D <1>; reg =3D <0xe001d000 0x8>, <0xe001d0e4 0x4>; clocks =3D <&clk32k 0>; }; Are translated into a syscon regmap with: - allowed ranges: [0x0, 0xC) u [0xE4, 0xE8) - invalid ranges: [0xC, 0xE0] Signed-off-by: Claudiu Beznea --- drivers/mfd/syscon.c | 158 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 143 insertions(+), 15 deletions(-) diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 191fdb87c424..fe80e6150d95 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -20,9 +20,36 @@ #include #include #include +#include #include #include =20 +#define syscon_alloc(_dev, args...) ({ \ + void *_ptr; \ + if (_dev) \ + _ptr =3D devm_kzalloc((_dev), args); \ + else \ + _ptr =3D kzalloc(args); \ + (_ptr); \ +}) + +#define syscon_get_resource(_pdev, _np, _idx, _res) ({ \ + int _ret =3D -EINVAL; \ + if (_pdev) { \ + struct resource *_r; \ + _r =3D platform_get_resource((_pdev), IORESOURCE_MEM, (_idx));\ + if (!_r) { \ + _ret =3D -ENOMEM; \ + } else { \ + (_res) =3D *_r; \ + _ret =3D 0; \ + } \ + } else if (_np) { \ + _ret =3D of_address_to_resource((_np), (_idx), &(_res)); \ + } \ + (_ret); \ +}) + static struct platform_driver syscon_driver; =20 static DEFINE_SPINLOCK(syscon_list_slock); @@ -40,14 +67,93 @@ static const struct regmap_config syscon_regmap_config = =3D { .reg_stride =3D 4, }; =20 +static const struct regmap_access_table * +syscon_prepare_regmap_access_table(struct platform_device *pdev, + struct device_node *np, u32 reg_io_width, + int entries) +{ + struct regmap_access_table *at; + struct regmap_range *yes_ranges, *no_ranges =3D NULL; + struct device *dev =3D pdev ? &pdev->dev : NULL; + struct resource res; + resource_size_t base_offset, offset; + int i, ret; + + /* Allocate memory for access table. */ + at =3D syscon_alloc(dev, sizeof(*at), GFP_KERNEL); + if (!at) + return ERR_PTR(-ENOMEM); + + /* Allocate memory for allowed ranges. */ + yes_ranges =3D syscon_alloc(dev, entries * sizeof(*yes_ranges), + GFP_KERNEL); + if (!yes_ranges) { + ret =3D -ENOMEM; + goto free; + } + + /* Allocate memory for invalid ranges. */ + if (entries > 1) { + no_ranges =3D syscon_alloc(dev, + (entries - 1) * sizeof(*no_ranges), + GFP_KERNEL); + if (!no_ranges) { + ret =3D -ENOMEM; + goto free; + } + } + + /* Populate allowed and invalid ranges. */ + ret =3D syscon_get_resource(pdev, np, 0, res); + if (ret) + goto free; + + base_offset =3D res.start; + yes_ranges[0].range_max =3D resource_size(&res) - reg_io_width; + if (entries > 1) + no_ranges[0].range_min =3D resource_size(&res); + + for (i =3D 1; i < entries; i++) { + ret =3D syscon_get_resource(pdev, np, i, res); + if (ret) + goto free; + + offset =3D res.start - base_offset; + yes_ranges[i].range_min =3D offset; + yes_ranges[i].range_max =3D offset + resource_size(&res) - + reg_io_width; + if (i !=3D entries - 1) + no_ranges[i].range_min =3D offset + resource_size(&res); + no_ranges[i - 1].range_max =3D offset - reg_io_width; + } + + /* Store them to access table. */ + at->yes_ranges =3D yes_ranges; + at->n_yes_ranges =3D entries; + at->no_ranges =3D no_ranges; + at->n_no_ranges =3D entries > 1 ? entries - 1 : 0; + + return at; + +free: + if (!dev) { + kfree(no_ranges); + kfree(yes_ranges); + kfree(at); + } + + return ERR_PTR(ret); +} + static struct syscon *of_syscon_register(struct device_node *np, bool chec= k_clk) { struct clk *clk; struct syscon *syscon; struct regmap *regmap; void __iomem *base; + const struct regmap_access_table *at; u32 reg_io_width; - int ret; + int ret, n_res =3D 0; struct regmap_config syscon_config =3D syscon_regmap_config; struct resource res; =20 @@ -55,11 +161,29 @@ static struct syscon *of_syscon_register(struct device= _node *np, bool check_clk) if (!syscon) return ERR_PTR(-ENOMEM); =20 - if (of_address_to_resource(np, 0, &res)) { + /* Count the number of resources. */ + while (of_address_to_resource(np, n_res, &res) =3D=3D 0) + n_res++; + if (!n_res) { ret =3D -ENOMEM; goto err_map; } =20 + /* + * search for reg-io-width property in DT. If it is not provided, + * default to 4 bytes. regmap_init_mmio will return an error if values + * are invalid so there is no need to check them here. + */ + ret =3D of_property_read_u32(np, "reg-io-width", ®_io_width); + if (ret) + reg_io_width =3D 4; + + at =3D syscon_prepare_regmap_access_table(NULL, np, reg_io_width, n_res); + if (IS_ERR(at)) { + ret =3D PTR_ERR(at); + goto err_map; + } + base =3D of_iomap(np, 0); if (!base) { ret =3D -ENOMEM; @@ -74,15 +198,6 @@ static struct syscon *of_syscon_register(struct device_= node *np, bool check_clk) else if (of_property_read_bool(np, "native-endian")) syscon_config.val_format_endian =3D REGMAP_ENDIAN_NATIVE; =20 - /* - * search for reg-io-width property in DT. If it is not provided, - * default to 4 bytes. regmap_init_mmio will return an error if values - * are invalid so there is no need to check them here. - */ - ret =3D of_property_read_u32(np, "reg-io-width", ®_io_width); - if (ret) - reg_io_width =3D 4; - ret =3D of_hwspin_lock_get_id(np, 0); if (ret > 0 || (IS_ENABLED(CONFIG_HWSPINLOCK) && ret =3D=3D 0)) { syscon_config.use_hwlock =3D true; @@ -105,7 +220,8 @@ static struct syscon *of_syscon_register(struct device_= node *np, bool check_clk) (u64)res.start); syscon_config.reg_stride =3D reg_io_width; syscon_config.val_bits =3D reg_io_width * 8; - syscon_config.max_register =3D resource_size(&res) - reg_io_width; + syscon_config.wr_table =3D at; + syscon_config.rd_table =3D at; =20 regmap =3D regmap_init_mmio(NULL, base, &syscon_config); kfree(syscon_config.name); @@ -147,6 +263,9 @@ static struct syscon *of_syscon_register(struct device_= node *np, bool check_clk) iounmap(base); err_map: kfree(syscon); + kfree(at->no_ranges); + kfree(at->yes_ranges); + kfree(at); return ERR_PTR(ret); } =20 @@ -279,22 +398,31 @@ static int syscon_probe(struct platform_device *pdev) struct syscon_platform_data *pdata =3D dev_get_platdata(dev); struct syscon *syscon; struct regmap_config syscon_config =3D syscon_regmap_config; + const struct regmap_access_table *at; struct resource *res; void __iomem *base; + int n_res =3D 0; =20 syscon =3D devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL); if (!syscon) return -ENOMEM; =20 - res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) + /* Count the number of resources. */ + while (platform_get_resource(pdev, IORESOURCE_MEM, n_res)) + n_res++; + if (!n_res) return -ENOENT; =20 base =3D devm_ioremap(dev, res->start, resource_size(res)); if (!base) return -ENOMEM; =20 - syscon_config.max_register =3D resource_size(res) - 4; + at =3D syscon_prepare_regmap_access_table(pdev, NULL, 4, n_res); + if (IS_ERR(at)) + return PTR_ERR(at); + + syscon_config.wr_table =3D at; + syscon_config.rd_table =3D at; if (pdata) syscon_config.name =3D pdata->label; syscon->regmap =3D devm_regmap_init_mmio(dev, base, &syscon_config); --=20 2.32.0