mirror of
				https://github.com/Ysurac/openmptcprouter.git
				synced 2025-03-09 15:40:20 +00:00 
			
		
		
		
	Refresh kernel 6.6 patches
This commit is contained in:
		
							parent
							
								
									4fed94fcee
								
							
						
					
					
						commit
						a4ee56638a
					
				
					 50 changed files with 11257 additions and 659 deletions
				
			
		
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -0,0 +1,183 @@ | |||
| From e1fbfa4a995d42e02e22b0dff2f8b4fdee1504b3 Mon Sep 17 00:00:00 2001 | ||||
| From: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Date: Tue, 14 Nov 2023 15:08:42 +0100 | ||||
| Subject: [PATCH 2/3] net: phy: aquantia: move MMD_VEND define to header | ||||
| 
 | ||||
| Move MMD_VEND define to header to clean things up and in preparation for | ||||
| firmware loading support that require some define placed in | ||||
| aquantia_main. | ||||
| 
 | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| ---
 | ||||
|  drivers/net/phy/aquantia/aquantia.h       | 69 +++++++++++++++++++++++ | ||||
|  drivers/net/phy/aquantia/aquantia_hwmon.c | 14 ----- | ||||
|  drivers/net/phy/aquantia/aquantia_main.c  | 55 ------------------ | ||||
|  3 files changed, 69 insertions(+), 69 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/net/phy/aquantia/aquantia.h
 | ||||
| +++ b/drivers/net/phy/aquantia/aquantia.h
 | ||||
| @@ -9,6 +9,75 @@
 | ||||
|  #include <linux/device.h> | ||||
|  #include <linux/phy.h> | ||||
|   | ||||
| +/* Vendor specific 1, MDIO_MMD_VEND1 */
 | ||||
| +#define VEND1_GLOBAL_FW_ID			0x0020
 | ||||
| +#define VEND1_GLOBAL_FW_ID_MAJOR		GENMASK(15, 8)
 | ||||
| +#define VEND1_GLOBAL_FW_ID_MINOR		GENMASK(7, 0)
 | ||||
| +
 | ||||
| +/* The following registers all have similar layouts; first the registers... */
 | ||||
| +#define VEND1_GLOBAL_CFG_10M			0x0310
 | ||||
| +#define VEND1_GLOBAL_CFG_100M			0x031b
 | ||||
| +#define VEND1_GLOBAL_CFG_1G			0x031c
 | ||||
| +#define VEND1_GLOBAL_CFG_2_5G			0x031d
 | ||||
| +#define VEND1_GLOBAL_CFG_5G			0x031e
 | ||||
| +#define VEND1_GLOBAL_CFG_10G			0x031f
 | ||||
| +/* ...and now the fields */
 | ||||
| +#define VEND1_GLOBAL_CFG_RATE_ADAPT		GENMASK(8, 7)
 | ||||
| +#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE	0
 | ||||
| +#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX		1
 | ||||
| +#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE	2
 | ||||
| +
 | ||||
| +/* Vendor specific 1, MDIO_MMD_VEND2 */
 | ||||
| +#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL	0xc421
 | ||||
| +#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL	0xc422
 | ||||
| +#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN	0xc423
 | ||||
| +#define VEND1_THERMAL_PROV_LOW_TEMP_WARN	0xc424
 | ||||
| +#define VEND1_THERMAL_STAT1			0xc820
 | ||||
| +#define VEND1_THERMAL_STAT2			0xc821
 | ||||
| +#define VEND1_THERMAL_STAT2_VALID		BIT(0)
 | ||||
| +#define VEND1_GENERAL_STAT1			0xc830
 | ||||
| +#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL	BIT(14)
 | ||||
| +#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL	BIT(13)
 | ||||
| +#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN	BIT(12)
 | ||||
| +#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN	BIT(11)
 | ||||
| +
 | ||||
| +#define VEND1_GLOBAL_GEN_STAT2			0xc831
 | ||||
| +#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG	BIT(15)
 | ||||
| +
 | ||||
| +#define VEND1_GLOBAL_RSVD_STAT1			0xc885
 | ||||
| +#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID	GENMASK(7, 4)
 | ||||
| +#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID		GENMASK(3, 0)
 | ||||
| +
 | ||||
| +#define VEND1_GLOBAL_RSVD_STAT9			0xc88d
 | ||||
| +#define VEND1_GLOBAL_RSVD_STAT9_MODE		GENMASK(7, 0)
 | ||||
| +#define VEND1_GLOBAL_RSVD_STAT9_1000BT2		0x23
 | ||||
| +
 | ||||
| +#define VEND1_GLOBAL_INT_STD_STATUS		0xfc00
 | ||||
| +#define VEND1_GLOBAL_INT_VEND_STATUS		0xfc01
 | ||||
| +
 | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK		0xff00
 | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PMA1		BIT(15)
 | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PMA2		BIT(14)
 | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PCS1		BIT(13)
 | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PCS2		BIT(12)
 | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PCS3		BIT(11)
 | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1	BIT(10)
 | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2	BIT(9)
 | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_AN1		BIT(8)
 | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_AN2		BIT(7)
 | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_GBE		BIT(6)
 | ||||
| +#define VEND1_GLOBAL_INT_STD_MASK_ALL		BIT(0)
 | ||||
| +
 | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK		0xff01
 | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_PMA		BIT(15)
 | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_PCS		BIT(14)
 | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS	BIT(13)
 | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_AN		BIT(12)
 | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_GBE		BIT(11)
 | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1	BIT(2)
 | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2	BIT(1)
 | ||||
| +#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3	BIT(0)
 | ||||
| +
 | ||||
|  #if IS_REACHABLE(CONFIG_HWMON) | ||||
|  int aqr_hwmon_probe(struct phy_device *phydev); | ||||
|  #else | ||||
| --- a/drivers/net/phy/aquantia/aquantia_hwmon.c
 | ||||
| +++ b/drivers/net/phy/aquantia/aquantia_hwmon.c
 | ||||
| @@ -13,20 +13,6 @@
 | ||||
|   | ||||
|  #include "aquantia.h" | ||||
|   | ||||
| -/* Vendor specific 1, MDIO_MMD_VEND2 */
 | ||||
| -#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL	0xc421
 | ||||
| -#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL	0xc422
 | ||||
| -#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN	0xc423
 | ||||
| -#define VEND1_THERMAL_PROV_LOW_TEMP_WARN	0xc424
 | ||||
| -#define VEND1_THERMAL_STAT1			0xc820
 | ||||
| -#define VEND1_THERMAL_STAT2			0xc821
 | ||||
| -#define VEND1_THERMAL_STAT2_VALID		BIT(0)
 | ||||
| -#define VEND1_GENERAL_STAT1			0xc830
 | ||||
| -#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL	BIT(14)
 | ||||
| -#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL	BIT(13)
 | ||||
| -#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN	BIT(12)
 | ||||
| -#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN	BIT(11)
 | ||||
| -
 | ||||
|  #if IS_REACHABLE(CONFIG_HWMON) | ||||
|   | ||||
|  static umode_t aqr_hwmon_is_visible(const void *data, | ||||
| --- a/drivers/net/phy/aquantia/aquantia_main.c
 | ||||
| +++ b/drivers/net/phy/aquantia/aquantia_main.c
 | ||||
| @@ -91,61 +91,6 @@
 | ||||
|  #define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR	0xd31a | ||||
|  #define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES		0xd31b | ||||
|   | ||||
| -/* Vendor specific 1, MDIO_MMD_VEND1 */
 | ||||
| -#define VEND1_GLOBAL_FW_ID			0x0020
 | ||||
| -#define VEND1_GLOBAL_FW_ID_MAJOR		GENMASK(15, 8)
 | ||||
| -#define VEND1_GLOBAL_FW_ID_MINOR		GENMASK(7, 0)
 | ||||
| -
 | ||||
| -#define VEND1_GLOBAL_GEN_STAT2			0xc831
 | ||||
| -#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG	BIT(15)
 | ||||
| -
 | ||||
| -/* The following registers all have similar layouts; first the registers... */
 | ||||
| -#define VEND1_GLOBAL_CFG_10M			0x0310
 | ||||
| -#define VEND1_GLOBAL_CFG_100M			0x031b
 | ||||
| -#define VEND1_GLOBAL_CFG_1G			0x031c
 | ||||
| -#define VEND1_GLOBAL_CFG_2_5G			0x031d
 | ||||
| -#define VEND1_GLOBAL_CFG_5G			0x031e
 | ||||
| -#define VEND1_GLOBAL_CFG_10G			0x031f
 | ||||
| -/* ...and now the fields */
 | ||||
| -#define VEND1_GLOBAL_CFG_RATE_ADAPT		GENMASK(8, 7)
 | ||||
| -#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE	0
 | ||||
| -#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX		1
 | ||||
| -#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE	2
 | ||||
| -
 | ||||
| -#define VEND1_GLOBAL_RSVD_STAT1			0xc885
 | ||||
| -#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID	GENMASK(7, 4)
 | ||||
| -#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID		GENMASK(3, 0)
 | ||||
| -
 | ||||
| -#define VEND1_GLOBAL_RSVD_STAT9			0xc88d
 | ||||
| -#define VEND1_GLOBAL_RSVD_STAT9_MODE		GENMASK(7, 0)
 | ||||
| -#define VEND1_GLOBAL_RSVD_STAT9_1000BT2		0x23
 | ||||
| -
 | ||||
| -#define VEND1_GLOBAL_INT_STD_STATUS		0xfc00
 | ||||
| -#define VEND1_GLOBAL_INT_VEND_STATUS		0xfc01
 | ||||
| -
 | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK		0xff00
 | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PMA1		BIT(15)
 | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PMA2		BIT(14)
 | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PCS1		BIT(13)
 | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PCS2		BIT(12)
 | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PCS3		BIT(11)
 | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1	BIT(10)
 | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2	BIT(9)
 | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_AN1		BIT(8)
 | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_AN2		BIT(7)
 | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_GBE		BIT(6)
 | ||||
| -#define VEND1_GLOBAL_INT_STD_MASK_ALL		BIT(0)
 | ||||
| -
 | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK		0xff01
 | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_PMA		BIT(15)
 | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_PCS		BIT(14)
 | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS	BIT(13)
 | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_AN		BIT(12)
 | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_GBE		BIT(11)
 | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1	BIT(2)
 | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2	BIT(1)
 | ||||
| -#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3	BIT(0)
 | ||||
| -
 | ||||
|  /* Sleep and timeout for checking if the Processor-Intensive | ||||
|   * MDIO operation is finished | ||||
|   */ | ||||
|  | @ -0,0 +1,504 @@ | |||
| From e93984ebc1c82bd34f7a1b3391efaceee0a8ae96 Mon Sep 17 00:00:00 2001 | ||||
| From: Robert Marko <robimarko@gmail.com> | ||||
| Date: Tue, 14 Nov 2023 15:08:43 +0100 | ||||
| Subject: [PATCH 3/3] net: phy: aquantia: add firmware load support | ||||
| 
 | ||||
| Aquantia PHY-s require firmware to be loaded before they start operating. | ||||
| It can be automatically loaded in case when there is a SPI-NOR connected | ||||
| to Aquantia PHY-s or can be loaded from the host via MDIO. | ||||
| 
 | ||||
| This patch adds support for loading the firmware via MDIO as in most cases | ||||
| there is no SPI-NOR being used to save on cost. | ||||
| Firmware loading code itself is ported from mainline U-boot with cleanups. | ||||
| 
 | ||||
| The firmware has mixed values both in big and little endian. | ||||
| PHY core itself is big-endian but it expects values to be in little-endian. | ||||
| The firmware is little-endian but CRC-16 value for it is stored at the end | ||||
| of firmware in big-endian. | ||||
| 
 | ||||
| It seems the PHY does the conversion internally from firmware that is | ||||
| little-endian to the PHY that is big-endian on using the mailbox | ||||
| but mailbox returns a big-endian CRC-16 to verify the written data | ||||
| integrity. | ||||
| 
 | ||||
| Co-developed-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Signed-off-by: Robert Marko <robimarko@gmail.com> | ||||
| Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||
| Reviewed-by: Andrew Lunn <andrew@lunn.ch> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| ---
 | ||||
|  drivers/net/phy/aquantia/Kconfig             |   1 + | ||||
|  drivers/net/phy/aquantia/Makefile            |   2 +- | ||||
|  drivers/net/phy/aquantia/aquantia.h          |  32 ++ | ||||
|  drivers/net/phy/aquantia/aquantia_firmware.c | 370 +++++++++++++++++++ | ||||
|  drivers/net/phy/aquantia/aquantia_main.c     |   6 + | ||||
|  5 files changed, 410 insertions(+), 1 deletion(-) | ||||
|  create mode 100644 drivers/net/phy/aquantia/aquantia_firmware.c | ||||
| 
 | ||||
| --- a/drivers/net/phy/aquantia/Kconfig
 | ||||
| +++ b/drivers/net/phy/aquantia/Kconfig
 | ||||
| @@ -1,5 +1,6 @@
 | ||||
|  # SPDX-License-Identifier: GPL-2.0-only | ||||
|  config AQUANTIA_PHY | ||||
|  	tristate "Aquantia PHYs" | ||||
| +	select CRC_CCITT
 | ||||
|  	help | ||||
|  	  Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 | ||||
| --- a/drivers/net/phy/aquantia/Makefile
 | ||||
| +++ b/drivers/net/phy/aquantia/Makefile
 | ||||
| @@ -1,5 +1,5 @@
 | ||||
|  # SPDX-License-Identifier: GPL-2.0 | ||||
| -aquantia-objs			+= aquantia_main.o
 | ||||
| +aquantia-objs			+= aquantia_main.o aquantia_firmware.o
 | ||||
|  ifdef CONFIG_HWMON | ||||
|  aquantia-objs			+= aquantia_hwmon.o | ||||
|  endif | ||||
| --- a/drivers/net/phy/aquantia/aquantia.h
 | ||||
| +++ b/drivers/net/phy/aquantia/aquantia.h
 | ||||
| @@ -10,10 +10,35 @@
 | ||||
|  #include <linux/phy.h> | ||||
|   | ||||
|  /* Vendor specific 1, MDIO_MMD_VEND1 */ | ||||
| +#define VEND1_GLOBAL_SC				0x0
 | ||||
| +#define VEND1_GLOBAL_SC_SOFT_RESET		BIT(15)
 | ||||
| +#define VEND1_GLOBAL_SC_LOW_POWER		BIT(11)
 | ||||
| +
 | ||||
|  #define VEND1_GLOBAL_FW_ID			0x0020 | ||||
|  #define VEND1_GLOBAL_FW_ID_MAJOR		GENMASK(15, 8) | ||||
|  #define VEND1_GLOBAL_FW_ID_MINOR		GENMASK(7, 0) | ||||
|   | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE1			0x0200
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE		BIT(15)
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE		BIT(14)
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET	BIT(12)
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE1_BUSY		BIT(8)
 | ||||
| +
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE2			0x0201
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE3			0x0202
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK	GENMASK(15, 0)
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(x)	FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK, (u16)((x) >> 16))
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE4			0x0203
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK	GENMASK(15, 2)
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(x)	FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK, (u16)(x))
 | ||||
| +
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE5			0x0204
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK	GENMASK(15, 0)
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(x)	FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK, (u16)((x) >> 16))
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE6			0x0205
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK	GENMASK(15, 0)
 | ||||
| +#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(x)	FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK, (u16)(x))
 | ||||
| +
 | ||||
|  /* The following registers all have similar layouts; first the registers... */ | ||||
|  #define VEND1_GLOBAL_CFG_10M			0x0310 | ||||
|  #define VEND1_GLOBAL_CFG_100M			0x031b | ||||
| @@ -28,6 +53,11 @@
 | ||||
|  #define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE	2 | ||||
|   | ||||
|  /* Vendor specific 1, MDIO_MMD_VEND2 */ | ||||
| +#define VEND1_GLOBAL_CONTROL2			0xc001
 | ||||
| +#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST	BIT(15)
 | ||||
| +#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD	BIT(6)
 | ||||
| +#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL	BIT(0)
 | ||||
| +
 | ||||
|  #define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL	0xc421 | ||||
|  #define VEND1_THERMAL_PROV_LOW_TEMP_FAIL	0xc422 | ||||
|  #define VEND1_THERMAL_PROV_HIGH_TEMP_WARN	0xc423 | ||||
| @@ -83,3 +113,5 @@ int aqr_hwmon_probe(struct phy_device *p
 | ||||
|  #else | ||||
|  static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; } | ||||
|  #endif | ||||
| +
 | ||||
| +int aqr_firmware_load(struct phy_device *phydev);
 | ||||
| --- /dev/null
 | ||||
| +++ b/drivers/net/phy/aquantia/aquantia_firmware.c
 | ||||
| @@ -0,0 +1,370 @@
 | ||||
| +// SPDX-License-Identifier: GPL-2.0
 | ||||
| +
 | ||||
| +#include <linux/bitfield.h>
 | ||||
| +#include <linux/of.h>
 | ||||
| +#include <linux/firmware.h>
 | ||||
| +#include <linux/crc-ccitt.h>
 | ||||
| +#include <linux/nvmem-consumer.h>
 | ||||
| +
 | ||||
| +#include <asm/unaligned.h>
 | ||||
| +
 | ||||
| +#include "aquantia.h"
 | ||||
| +
 | ||||
| +#define UP_RESET_SLEEP		100
 | ||||
| +
 | ||||
| +/* addresses of memory segments in the phy */
 | ||||
| +#define DRAM_BASE_ADDR		0x3FFE0000
 | ||||
| +#define IRAM_BASE_ADDR		0x40000000
 | ||||
| +
 | ||||
| +/* firmware image format constants */
 | ||||
| +#define VERSION_STRING_SIZE		0x40
 | ||||
| +#define VERSION_STRING_OFFSET		0x0200
 | ||||
| +/* primary offset is written at an offset from the start of the fw blob */
 | ||||
| +#define PRIMARY_OFFSET_OFFSET		0x8
 | ||||
| +/* primary offset needs to be then added to a base offset */
 | ||||
| +#define PRIMARY_OFFSET_SHIFT		12
 | ||||
| +#define PRIMARY_OFFSET(x)		((x) << PRIMARY_OFFSET_SHIFT)
 | ||||
| +#define HEADER_OFFSET			0x300
 | ||||
| +
 | ||||
| +struct aqr_fw_header {
 | ||||
| +	u32 padding;
 | ||||
| +	u8 iram_offset[3];
 | ||||
| +	u8 iram_size[3];
 | ||||
| +	u8 dram_offset[3];
 | ||||
| +	u8 dram_size[3];
 | ||||
| +} __packed;
 | ||||
| +
 | ||||
| +enum aqr_fw_src {
 | ||||
| +	AQR_FW_SRC_NVMEM = 0,
 | ||||
| +	AQR_FW_SRC_FS,
 | ||||
| +};
 | ||||
| +
 | ||||
| +static const char * const aqr_fw_src_string[] = {
 | ||||
| +	[AQR_FW_SRC_NVMEM] = "NVMEM",
 | ||||
| +	[AQR_FW_SRC_FS] = "FS",
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* AQR firmware doesn't have fixed offsets for iram and dram section
 | ||||
| + * but instead provide an header with the offset to use on reading
 | ||||
| + * and parsing the firmware.
 | ||||
| + *
 | ||||
| + * AQR firmware can't be trusted and each offset is validated to be
 | ||||
| + * not negative and be in the size of the firmware itself.
 | ||||
| + */
 | ||||
| +static bool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size)
 | ||||
| +{
 | ||||
| +	return offset + get_size <= size;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int aqr_fw_get_be16(const u8 *data, size_t offset, size_t size, u16 *value)
 | ||||
| +{
 | ||||
| +	if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	*value = get_unaligned_be16(data + offset);
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int aqr_fw_get_le16(const u8 *data, size_t offset, size_t size, u16 *value)
 | ||||
| +{
 | ||||
| +	if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	*value = get_unaligned_le16(data + offset);
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int aqr_fw_get_le24(const u8 *data, size_t offset, size_t size, u32 *value)
 | ||||
| +{
 | ||||
| +	if (!aqr_fw_validate_get(size, offset, sizeof(u8) * 3))
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	*value = get_unaligned_le24(data + offset);
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* load data into the phy's memory */
 | ||||
| +static int aqr_fw_load_memory(struct phy_device *phydev, u32 addr,
 | ||||
| +			      const u8 *data, size_t len)
 | ||||
| +{
 | ||||
| +	u16 crc = 0, up_crc;
 | ||||
| +	size_t pos;
 | ||||
| +
 | ||||
| +	/* PHY expect addr in LE */
 | ||||
| +	addr = (__force u32)cpu_to_le32(addr);
 | ||||
| +
 | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1,
 | ||||
| +		      VEND1_GLOBAL_MAILBOX_INTERFACE1,
 | ||||
| +		      VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET);
 | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1,
 | ||||
| +		      VEND1_GLOBAL_MAILBOX_INTERFACE3,
 | ||||
| +		      VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(addr));
 | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1,
 | ||||
| +		      VEND1_GLOBAL_MAILBOX_INTERFACE4,
 | ||||
| +		      VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(addr));
 | ||||
| +
 | ||||
| +	/* We assume and enforce the size to be word aligned.
 | ||||
| +	 * If a firmware that is not word aligned is found, please report upstream.
 | ||||
| +	 */
 | ||||
| +	for (pos = 0; pos < len; pos += sizeof(u32)) {
 | ||||
| +		u32 word;
 | ||||
| +
 | ||||
| +		/* FW data is always stored in little-endian */
 | ||||
| +		word = get_unaligned((const u32 *)(data + pos));
 | ||||
| +
 | ||||
| +		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5,
 | ||||
| +			      VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word));
 | ||||
| +		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE6,
 | ||||
| +			      VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(word));
 | ||||
| +
 | ||||
| +		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE1,
 | ||||
| +			      VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE |
 | ||||
| +			      VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE);
 | ||||
| +
 | ||||
| +		/* calculate CRC as we load data to the mailbox.
 | ||||
| +		 * We convert word to big-endian as PHY is BE and mailbox will
 | ||||
| +		 * return a BE CRC.
 | ||||
| +		 */
 | ||||
| +		word = (__force u32)cpu_to_be32(word);
 | ||||
| +		crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word));
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2);
 | ||||
| +	if (crc != up_crc) {
 | ||||
| +		phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n",
 | ||||
| +			   crc, up_crc);
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int aqr_fw_boot(struct phy_device *phydev, const u8 *data, size_t size,
 | ||||
| +		       enum aqr_fw_src fw_src)
 | ||||
| +{
 | ||||
| +	u16 calculated_crc, read_crc, read_primary_offset;
 | ||||
| +	u32 iram_offset = 0, iram_size = 0;
 | ||||
| +	u32 dram_offset = 0, dram_size = 0;
 | ||||
| +	char version[VERSION_STRING_SIZE];
 | ||||
| +	u32 primary_offset = 0;
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	/* extract saved CRC at the end of the fw
 | ||||
| +	 * CRC is saved in big-endian as PHY is BE
 | ||||
| +	 */
 | ||||
| +	ret = aqr_fw_get_be16(data, size - sizeof(u16), size, &read_crc);
 | ||||
| +	if (ret) {
 | ||||
| +		phydev_err(phydev, "bad firmware CRC in firmware\n");
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +	calculated_crc = crc_ccitt_false(0, data, size - sizeof(u16));
 | ||||
| +	if (read_crc != calculated_crc) {
 | ||||
| +		phydev_err(phydev, "bad firmware CRC: file 0x%04x calculated 0x%04x\n",
 | ||||
| +			   read_crc, calculated_crc);
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* Get the primary offset to extract DRAM and IRAM sections. */
 | ||||
| +	ret = aqr_fw_get_le16(data, PRIMARY_OFFSET_OFFSET, size, &read_primary_offset);
 | ||||
| +	if (ret) {
 | ||||
| +		phydev_err(phydev, "bad primary offset in firmware\n");
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +	primary_offset = PRIMARY_OFFSET(read_primary_offset);
 | ||||
| +
 | ||||
| +	/* Find the DRAM and IRAM sections within the firmware file.
 | ||||
| +	 * Make sure the fw_header is correctly in the firmware.
 | ||||
| +	 */
 | ||||
| +	if (!aqr_fw_validate_get(size, primary_offset + HEADER_OFFSET,
 | ||||
| +				 sizeof(struct aqr_fw_header))) {
 | ||||
| +		phydev_err(phydev, "bad fw_header in firmware\n");
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* offset are in LE and values needs to be converted to cpu endian */
 | ||||
| +	ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
 | ||||
| +			      offsetof(struct aqr_fw_header, iram_offset),
 | ||||
| +			      size, &iram_offset);
 | ||||
| +	if (ret) {
 | ||||
| +		phydev_err(phydev, "bad iram offset in firmware\n");
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +	ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
 | ||||
| +			      offsetof(struct aqr_fw_header, iram_size),
 | ||||
| +			      size, &iram_size);
 | ||||
| +	if (ret) {
 | ||||
| +		phydev_err(phydev, "invalid iram size in firmware\n");
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +	ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
 | ||||
| +			      offsetof(struct aqr_fw_header, dram_offset),
 | ||||
| +			      size, &dram_offset);
 | ||||
| +	if (ret) {
 | ||||
| +		phydev_err(phydev, "bad dram offset in firmware\n");
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +	ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
 | ||||
| +			      offsetof(struct aqr_fw_header, dram_size),
 | ||||
| +			      size, &dram_size);
 | ||||
| +	if (ret) {
 | ||||
| +		phydev_err(phydev, "invalid dram size in firmware\n");
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* Increment the offset with the primary offset.
 | ||||
| +	 * Validate iram/dram offset and size.
 | ||||
| +	 */
 | ||||
| +	iram_offset += primary_offset;
 | ||||
| +	if (iram_size % sizeof(u32)) {
 | ||||
| +		phydev_err(phydev, "iram size if not aligned to word size. Please report this upstream!\n");
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +	if (!aqr_fw_validate_get(size, iram_offset, iram_size)) {
 | ||||
| +		phydev_err(phydev, "invalid iram offset for iram size\n");
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	dram_offset += primary_offset;
 | ||||
| +	if (dram_size % sizeof(u32)) {
 | ||||
| +		phydev_err(phydev, "dram size if not aligned to word size. Please report this upstream!\n");
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +	if (!aqr_fw_validate_get(size, dram_offset, dram_size)) {
 | ||||
| +		phydev_err(phydev, "invalid iram offset for iram size\n");
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	phydev_dbg(phydev, "primary %d IRAM offset=%d size=%d DRAM offset=%d size=%d\n",
 | ||||
| +		   primary_offset, iram_offset, iram_size, dram_offset, dram_size);
 | ||||
| +
 | ||||
| +	if (!aqr_fw_validate_get(size, dram_offset + VERSION_STRING_OFFSET,
 | ||||
| +				 VERSION_STRING_SIZE)) {
 | ||||
| +		phydev_err(phydev, "invalid version in firmware\n");
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +	strscpy(version, (char *)data + dram_offset + VERSION_STRING_OFFSET,
 | ||||
| +		VERSION_STRING_SIZE);
 | ||||
| +	if (version[0] == '\0') {
 | ||||
| +		phydev_err(phydev, "invalid version in firmware\n");
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +	phydev_info(phydev, "loading firmware version '%s' from '%s'\n", version,
 | ||||
| +		    aqr_fw_src_string[fw_src]);
 | ||||
| +
 | ||||
| +	/* stall the microcprocessor */
 | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
 | ||||
| +		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);
 | ||||
| +
 | ||||
| +	phydev_dbg(phydev, "loading DRAM 0x%08x from offset=%d size=%d\n",
 | ||||
| +		   DRAM_BASE_ADDR, dram_offset, dram_size);
 | ||||
| +	ret = aqr_fw_load_memory(phydev, DRAM_BASE_ADDR, data + dram_offset,
 | ||||
| +				 dram_size);
 | ||||
| +	if (ret)
 | ||||
| +		return ret;
 | ||||
| +
 | ||||
| +	phydev_dbg(phydev, "loading IRAM 0x%08x from offset=%d size=%d\n",
 | ||||
| +		   IRAM_BASE_ADDR, iram_offset, iram_size);
 | ||||
| +	ret = aqr_fw_load_memory(phydev, IRAM_BASE_ADDR, data + iram_offset,
 | ||||
| +				 iram_size);
 | ||||
| +	if (ret)
 | ||||
| +		return ret;
 | ||||
| +
 | ||||
| +	/* make sure soft reset and low power mode are clear */
 | ||||
| +	phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_SC,
 | ||||
| +			   VEND1_GLOBAL_SC_SOFT_RESET | VEND1_GLOBAL_SC_LOW_POWER);
 | ||||
| +
 | ||||
| +	/* Release the microprocessor. UP_RESET must be held for 100 usec. */
 | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
 | ||||
| +		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL |
 | ||||
| +		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD |
 | ||||
| +		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST);
 | ||||
| +	usleep_range(UP_RESET_SLEEP, UP_RESET_SLEEP * 2);
 | ||||
| +
 | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
 | ||||
| +		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int aqr_firmware_load_nvmem(struct phy_device *phydev)
 | ||||
| +{
 | ||||
| +	struct nvmem_cell *cell;
 | ||||
| +	size_t size;
 | ||||
| +	u8 *buf;
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	cell = nvmem_cell_get(&phydev->mdio.dev, "firmware");
 | ||||
| +	if (IS_ERR(cell))
 | ||||
| +		return PTR_ERR(cell);
 | ||||
| +
 | ||||
| +	buf = nvmem_cell_read(cell, &size);
 | ||||
| +	if (IS_ERR(buf)) {
 | ||||
| +		ret = PTR_ERR(buf);
 | ||||
| +		goto exit;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	ret = aqr_fw_boot(phydev, buf, size, AQR_FW_SRC_NVMEM);
 | ||||
| +	if (ret)
 | ||||
| +		phydev_err(phydev, "firmware loading failed: %d\n", ret);
 | ||||
| +
 | ||||
| +	kfree(buf);
 | ||||
| +exit:
 | ||||
| +	nvmem_cell_put(cell);
 | ||||
| +
 | ||||
| +	return ret;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int aqr_firmware_load_fs(struct phy_device *phydev)
 | ||||
| +{
 | ||||
| +	struct device *dev = &phydev->mdio.dev;
 | ||||
| +	const struct firmware *fw;
 | ||||
| +	const char *fw_name;
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	ret = of_property_read_string(dev->of_node, "firmware-name",
 | ||||
| +				      &fw_name);
 | ||||
| +	if (ret)
 | ||||
| +		return ret;
 | ||||
| +
 | ||||
| +	ret = request_firmware(&fw, fw_name, dev);
 | ||||
| +	if (ret) {
 | ||||
| +		phydev_err(phydev, "failed to find FW file %s (%d)\n",
 | ||||
| +			   fw_name, ret);
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	ret = aqr_fw_boot(phydev, fw->data, fw->size, AQR_FW_SRC_FS);
 | ||||
| +	if (ret)
 | ||||
| +		phydev_err(phydev, "firmware loading failed: %d\n", ret);
 | ||||
| +
 | ||||
| +	release_firmware(fw);
 | ||||
| +
 | ||||
| +	return ret;
 | ||||
| +}
 | ||||
| +
 | ||||
| +int aqr_firmware_load(struct phy_device *phydev)
 | ||||
| +{
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	/* Check if the firmware is not already loaded by pooling
 | ||||
| +	 * the current version returned by the PHY. If 0 is returned,
 | ||||
| +	 * no firmware is loaded.
 | ||||
| +	 */
 | ||||
| +	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID);
 | ||||
| +	if (ret > 0)
 | ||||
| +		goto exit;
 | ||||
| +
 | ||||
| +	ret = aqr_firmware_load_nvmem(phydev);
 | ||||
| +	if (!ret)
 | ||||
| +		goto exit;
 | ||||
| +
 | ||||
| +	ret = aqr_firmware_load_fs(phydev);
 | ||||
| +	if (ret)
 | ||||
| +		return ret;
 | ||||
| +
 | ||||
| +exit:
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| --- a/drivers/net/phy/aquantia/aquantia_main.c
 | ||||
| +++ b/drivers/net/phy/aquantia/aquantia_main.c
 | ||||
| @@ -658,11 +658,17 @@ static int aqr107_resume(struct phy_devi
 | ||||
|   | ||||
|  static int aqr107_probe(struct phy_device *phydev) | ||||
|  { | ||||
| +	int ret;
 | ||||
| +
 | ||||
|  	phydev->priv = devm_kzalloc(&phydev->mdio.dev, | ||||
|  				    sizeof(struct aqr107_priv), GFP_KERNEL); | ||||
|  	if (!phydev->priv) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
| +	ret = aqr_firmware_load(phydev);
 | ||||
| +	if (ret)
 | ||||
| +		return ret;
 | ||||
| +
 | ||||
|  	return aqr_hwmon_probe(phydev); | ||||
|  } | ||||
|   | ||||
|  | @ -75,7 +75,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | |||
|  		struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash); | ||||
|   | ||||
|  		hwe->ib1 &= ~MTK_FOE_IB1_STATE; | ||||
| @@ -532,7 +533,8 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
 | ||||
| @@ -533,7 +534,8 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
 | ||||
|  	if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW) | ||||
|  		return; | ||||
|   | ||||
|  | @ -85,7 +85,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | |||
|  	kfree(entry); | ||||
|  } | ||||
|   | ||||
| @@ -548,66 +550,55 @@ static int __mtk_foe_entry_idle_time(str
 | ||||
| @@ -549,66 +551,55 @@ static int __mtk_foe_entry_idle_time(str
 | ||||
|  		return now - timestamp; | ||||
|  } | ||||
|   | ||||
|  | @ -181,7 +181,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | |||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -650,7 +641,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
 | ||||
| @@ -651,7 +642,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
 | ||||
|  void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
|  { | ||||
|  	spin_lock_bh(&ppe_lock); | ||||
|  | @ -191,7 +191,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | |||
|  	spin_unlock_bh(&ppe_lock); | ||||
|  } | ||||
|   | ||||
| @@ -697,8 +689,8 @@ mtk_foe_entry_commit_subflow(struct mtk_
 | ||||
| @@ -698,8 +690,8 @@ mtk_foe_entry_commit_subflow(struct mtk_
 | ||||
|  { | ||||
|  	const struct mtk_soc_data *soc = ppe->eth->soc; | ||||
|  	struct mtk_flow_entry *flow_info; | ||||
|  | @ -201,7 +201,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | |||
|  	u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP; | ||||
|  	int type; | ||||
|   | ||||
| @@ -706,30 +698,30 @@ mtk_foe_entry_commit_subflow(struct mtk_
 | ||||
| @@ -707,30 +699,30 @@ mtk_foe_entry_commit_subflow(struct mtk_
 | ||||
|  	if (!flow_info) | ||||
|  		return; | ||||
|   | ||||
|  | @ -242,7 +242,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | |||
|  } | ||||
|   | ||||
|  void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) | ||||
| @@ -739,9 +731,11 @@ void __mtk_ppe_check_skb(struct mtk_ppe
 | ||||
| @@ -740,9 +732,11 @@ void __mtk_ppe_check_skb(struct mtk_ppe
 | ||||
|  	struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash); | ||||
|  	struct mtk_flow_entry *entry; | ||||
|  	struct mtk_foe_bridge key = {}; | ||||
|  | @ -254,7 +254,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | |||
|  	u8 *tag; | ||||
|   | ||||
|  	spin_lock_bh(&ppe_lock); | ||||
| @@ -749,20 +743,14 @@ void __mtk_ppe_check_skb(struct mtk_ppe
 | ||||
| @@ -750,20 +744,14 @@ void __mtk_ppe_check_skb(struct mtk_ppe
 | ||||
|  	if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND) | ||||
|  		goto out; | ||||
|   | ||||
|  | @ -281,7 +281,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> | |||
|  			continue; | ||||
|  		} | ||||
|   | ||||
| @@ -813,9 +801,17 @@ out:
 | ||||
| @@ -814,9 +802,17 @@ out:
 | ||||
|   | ||||
|  int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
|  { | ||||
|  | @ -0,0 +1,343 @@ | |||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Thu, 23 Mar 2023 11:05:22 +0100 | ||||
| Subject: [PATCH] net: ethernet: mediatek: fix ppe flow accounting for L2 | ||||
|  flows | ||||
| 
 | ||||
| For L2 flows, the packet/byte counters should report the sum of the | ||||
| counters of their subflows, both current and expired. | ||||
| In order to make this work, change the way that accounting data is tracked. | ||||
| Reset counters when a flow enters bind. Once it expires (or enters unbind), | ||||
| store the last counter value in struct mtk_flow_entry. | ||||
| 
 | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
 | ||||
| @@ -80,9 +80,9 @@ static int mtk_ppe_mib_wait_busy(struct
 | ||||
|  	int ret; | ||||
|  	u32 val; | ||||
|   | ||||
| -	ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val,
 | ||||
| -				 !(val & MTK_PPE_MIB_SER_CR_ST),
 | ||||
| -				 20, MTK_PPE_WAIT_TIMEOUT_US);
 | ||||
| +	ret = readl_poll_timeout_atomic(ppe->base + MTK_PPE_MIB_SER_CR, val,
 | ||||
| +					!(val & MTK_PPE_MIB_SER_CR_ST),
 | ||||
| +					20, MTK_PPE_WAIT_TIMEOUT_US);
 | ||||
|   | ||||
|  	if (ret) | ||||
|  		dev_err(ppe->dev, "MIB table busy"); | ||||
| @@ -90,17 +90,31 @@ static int mtk_ppe_mib_wait_busy(struct
 | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| -static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
 | ||||
| +static inline struct mtk_foe_accounting *
 | ||||
| +mtk_ppe_acct_data(struct mtk_ppe *ppe, u16 index)
 | ||||
| +{
 | ||||
| +	if (!ppe->acct_table)
 | ||||
| +		return NULL;
 | ||||
| +
 | ||||
| +	return ppe->acct_table + index * sizeof(struct mtk_foe_accounting);
 | ||||
| +}
 | ||||
| +
 | ||||
| +struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index)
 | ||||
|  { | ||||
|  	u32 val, cnt_r0, cnt_r1, cnt_r2; | ||||
| +	struct mtk_foe_accounting *acct;
 | ||||
|  	int ret; | ||||
|   | ||||
|  	val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST; | ||||
|  	ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val); | ||||
|   | ||||
| +	acct = mtk_ppe_acct_data(ppe, index);
 | ||||
| +	if (!acct)
 | ||||
| +		return NULL;
 | ||||
| +
 | ||||
|  	ret = mtk_ppe_mib_wait_busy(ppe); | ||||
|  	if (ret) | ||||
| -		return ret;
 | ||||
| +		return acct;
 | ||||
|   | ||||
|  	cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0); | ||||
|  	cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1); | ||||
| @@ -109,19 +123,19 @@ static int mtk_mib_entry_read(struct mtk
 | ||||
|  	if (mtk_is_netsys_v3_or_greater(ppe->eth)) { | ||||
|  		/* 64 bit for each counter */ | ||||
|  		u32 cnt_r3 = readl(ppe->base + MTK_PPE_MIB_SER_R3); | ||||
| -		*bytes = ((u64)cnt_r1 << 32) | cnt_r0;
 | ||||
| -		*packets = ((u64)cnt_r3 << 32) | cnt_r2;
 | ||||
| +		acct->bytes += ((u64)cnt_r1 << 32) | cnt_r0;
 | ||||
| +		acct->packets += ((u64)cnt_r3 << 32) | cnt_r2;
 | ||||
|  	} else { | ||||
|  		/* 48 bit byte counter, 40 bit packet counter */ | ||||
|  		u32 byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0); | ||||
|  		u32 byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1); | ||||
|  		u32 pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1); | ||||
|  		u32 pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2); | ||||
| -		*bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low;
 | ||||
| -		*packets = ((u64)pkt_cnt_high << 16) | pkt_cnt_low;
 | ||||
| +		acct->bytes += ((u64)byte_cnt_high << 32) | byte_cnt_low;
 | ||||
| +		acct->packets += ((u64)pkt_cnt_high << 16) | pkt_cnt_low;
 | ||||
|  	} | ||||
|   | ||||
| -	return 0;
 | ||||
| +	return acct;
 | ||||
|  } | ||||
|   | ||||
|  static void mtk_ppe_cache_clear(struct mtk_ppe *ppe) | ||||
| @@ -520,14 +534,6 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
 | ||||
|  		hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID); | ||||
|  		dma_wmb(); | ||||
|  		mtk_ppe_cache_clear(ppe); | ||||
| -
 | ||||
| -		if (ppe->accounting) {
 | ||||
| -			struct mtk_foe_accounting *acct;
 | ||||
| -
 | ||||
| -			acct = ppe->acct_table + entry->hash * sizeof(*acct);
 | ||||
| -			acct->packets = 0;
 | ||||
| -			acct->bytes = 0;
 | ||||
| -		}
 | ||||
|  	} | ||||
|  	entry->hash = 0xffff; | ||||
|   | ||||
| @@ -552,11 +558,14 @@ static int __mtk_foe_entry_idle_time(str
 | ||||
|  } | ||||
|   | ||||
|  static bool | ||||
| -mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
 | ||||
| +mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
 | ||||
| +		      u64 *packets, u64 *bytes)
 | ||||
|  { | ||||
| +	struct mtk_foe_accounting *acct;
 | ||||
|  	struct mtk_foe_entry foe = {}; | ||||
|  	struct mtk_foe_entry *hwe; | ||||
|  	u16 hash = entry->hash; | ||||
| +	bool ret = false;
 | ||||
|  	int len; | ||||
|   | ||||
|  	if (hash == 0xffff) | ||||
| @@ -567,18 +576,35 @@ mtk_flow_entry_update(struct mtk_ppe *pp
 | ||||
|  	memcpy(&foe, hwe, len); | ||||
|   | ||||
|  	if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) || | ||||
| -	    FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND)
 | ||||
| -		return false;
 | ||||
| +	    FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) {
 | ||||
| +		acct = mtk_ppe_acct_data(ppe, hash);
 | ||||
| +		if (acct) {
 | ||||
| +			entry->prev_packets += acct->packets;
 | ||||
| +			entry->prev_bytes += acct->bytes;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		goto out;
 | ||||
| +	}
 | ||||
|   | ||||
|  	entry->data.ib1 = foe.ib1; | ||||
| +	acct = mtk_ppe_mib_entry_read(ppe, hash);
 | ||||
| +	ret = true;
 | ||||
| +
 | ||||
| +out:
 | ||||
| +	if (acct) {
 | ||||
| +		*packets += acct->packets;
 | ||||
| +		*bytes += acct->bytes;
 | ||||
| +	}
 | ||||
|   | ||||
| -	return true;
 | ||||
| +	return ret;
 | ||||
|  } | ||||
|   | ||||
|  static void | ||||
|  mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
|  { | ||||
|  	u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth); | ||||
| +	u64 *packets = &entry->packets;
 | ||||
| +	u64 *bytes = &entry->bytes;
 | ||||
|  	struct mtk_flow_entry *cur; | ||||
|  	struct hlist_node *tmp; | ||||
|  	int idle; | ||||
| @@ -587,7 +613,9 @@ mtk_flow_entry_update_l2(struct mtk_ppe
 | ||||
|  	hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) { | ||||
|  		int cur_idle; | ||||
|   | ||||
| -		if (!mtk_flow_entry_update(ppe, cur)) {
 | ||||
| +		if (!mtk_flow_entry_update(ppe, cur, packets, bytes)) {
 | ||||
| +			entry->prev_packets += cur->prev_packets;
 | ||||
| +			entry->prev_bytes += cur->prev_bytes;
 | ||||
|  			__mtk_foe_entry_clear(ppe, entry, false); | ||||
|  			continue; | ||||
|  		} | ||||
| @@ -602,10 +630,29 @@ mtk_flow_entry_update_l2(struct mtk_ppe
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| +void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
 | ||||
| +			     int *idle)
 | ||||
| +{
 | ||||
| +	entry->packets = entry->prev_packets;
 | ||||
| +	entry->bytes = entry->prev_bytes;
 | ||||
| +
 | ||||
| +	spin_lock_bh(&ppe_lock);
 | ||||
| +
 | ||||
| +	if (entry->type == MTK_FLOW_TYPE_L2)
 | ||||
| +		mtk_flow_entry_update_l2(ppe, entry);
 | ||||
| +	else
 | ||||
| +		mtk_flow_entry_update(ppe, entry, &entry->packets, &entry->bytes);
 | ||||
| +
 | ||||
| +	*idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
 | ||||
| +
 | ||||
| +	spin_unlock_bh(&ppe_lock);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static void | ||||
|  __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, | ||||
|  		       u16 hash) | ||||
|  { | ||||
| +	struct mtk_foe_accounting *acct;
 | ||||
|  	struct mtk_eth *eth = ppe->eth; | ||||
|  	u16 timestamp = mtk_eth_timestamp(eth); | ||||
|  	struct mtk_foe_entry *hwe; | ||||
| @@ -636,6 +683,12 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
 | ||||
|   | ||||
|  	dma_wmb(); | ||||
|   | ||||
| +	acct = mtk_ppe_mib_entry_read(ppe, hash);
 | ||||
| +	if (acct) {
 | ||||
| +		acct->packets = 0;
 | ||||
| +		acct->bytes = 0;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	mtk_ppe_cache_clear(ppe); | ||||
|  } | ||||
|   | ||||
| @@ -800,21 +853,6 @@ out:
 | ||||
|  	spin_unlock_bh(&ppe_lock); | ||||
|  } | ||||
|   | ||||
| -int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
 | ||||
| -{
 | ||||
| -	int idle;
 | ||||
| -
 | ||||
| -	spin_lock_bh(&ppe_lock);
 | ||||
| -	if (entry->type == MTK_FLOW_TYPE_L2)
 | ||||
| -		mtk_flow_entry_update_l2(ppe, entry);
 | ||||
| -	else
 | ||||
| -		mtk_flow_entry_update(ppe, entry);
 | ||||
| -	idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
 | ||||
| -	spin_unlock_bh(&ppe_lock);
 | ||||
| -
 | ||||
| -	return idle;
 | ||||
| -}
 | ||||
| -
 | ||||
|  int mtk_ppe_prepare_reset(struct mtk_ppe *ppe) | ||||
|  { | ||||
|  	if (!ppe) | ||||
| @@ -842,32 +880,6 @@ int mtk_ppe_prepare_reset(struct mtk_ppe
 | ||||
|  	return mtk_ppe_wait_busy(ppe); | ||||
|  } | ||||
|   | ||||
| -struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
 | ||||
| -						 struct mtk_foe_accounting *diff)
 | ||||
| -{
 | ||||
| -	struct mtk_foe_accounting *acct;
 | ||||
| -	int size = sizeof(struct mtk_foe_accounting);
 | ||||
| -	u64 bytes, packets;
 | ||||
| -
 | ||||
| -	if (!ppe->accounting)
 | ||||
| -		return NULL;
 | ||||
| -
 | ||||
| -	if (mtk_mib_entry_read(ppe, index, &bytes, &packets))
 | ||||
| -		return NULL;
 | ||||
| -
 | ||||
| -	acct = ppe->acct_table + index * size;
 | ||||
| -
 | ||||
| -	acct->bytes += bytes;
 | ||||
| -	acct->packets += packets;
 | ||||
| -
 | ||||
| -	if (diff) {
 | ||||
| -		diff->bytes = bytes;
 | ||||
| -		diff->packets = packets;
 | ||||
| -	}
 | ||||
| -
 | ||||
| -	return acct;
 | ||||
| -}
 | ||||
| -
 | ||||
|  struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index) | ||||
|  { | ||||
|  	bool accounting = eth->soc->has_accounting; | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.h
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
 | ||||
| @@ -304,6 +304,8 @@ struct mtk_flow_entry {
 | ||||
|  	struct mtk_foe_entry data; | ||||
|  	struct rhash_head node; | ||||
|  	unsigned long cookie; | ||||
| +	u64 prev_packets, prev_bytes;
 | ||||
| +	u64 packets, bytes;
 | ||||
|  }; | ||||
|   | ||||
|  struct mtk_mib_entry { | ||||
| @@ -348,6 +350,7 @@ void mtk_ppe_deinit(struct mtk_eth *eth)
 | ||||
|  void mtk_ppe_start(struct mtk_ppe *ppe); | ||||
|  int mtk_ppe_stop(struct mtk_ppe *ppe); | ||||
|  int mtk_ppe_prepare_reset(struct mtk_ppe *ppe); | ||||
| +struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index);
 | ||||
|   | ||||
|  void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash); | ||||
|   | ||||
| @@ -396,9 +399,8 @@ int mtk_foe_entry_set_queue(struct mtk_e
 | ||||
|  			    unsigned int queue); | ||||
|  int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
|  void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
| -int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
 | ||||
|  int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index); | ||||
| -struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
 | ||||
| -						 struct mtk_foe_accounting *diff);
 | ||||
| +void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
 | ||||
| +			     int *idle);
 | ||||
|   | ||||
|  #endif | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
 | ||||
| @@ -96,7 +96,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file
 | ||||
|  		if (bind && state != MTK_FOE_STATE_BIND) | ||||
|  			continue; | ||||
|   | ||||
| -		acct = mtk_foe_entry_get_mib(ppe, i, NULL);
 | ||||
| +		acct = mtk_ppe_mib_entry_read(ppe, i);
 | ||||
|   | ||||
|  		type = mtk_get_ib1_pkt_type(ppe->eth, entry->ib1); | ||||
|  		seq_printf(m, "%05x %s %7s", i, | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 | ||||
| @@ -501,24 +501,21 @@ static int
 | ||||
|  mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  { | ||||
|  	struct mtk_flow_entry *entry; | ||||
| -	struct mtk_foe_accounting diff;
 | ||||
| -	u32 idle;
 | ||||
| +	u64 packets, bytes;
 | ||||
| +	int idle;
 | ||||
|   | ||||
|  	entry = rhashtable_lookup(ð->flow_table, &f->cookie, | ||||
|  				  mtk_flow_ht_params); | ||||
|  	if (!entry) | ||||
|  		return -ENOENT; | ||||
|   | ||||
| -	idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
 | ||||
| +	packets = entry->packets;
 | ||||
| +	bytes = entry->bytes;
 | ||||
| +	mtk_foe_entry_get_stats(eth->ppe[entry->ppe_index], entry, &idle);
 | ||||
| +	f->stats.pkts += entry->packets - packets;
 | ||||
| +	f->stats.bytes += entry->bytes - bytes;
 | ||||
|  	f->stats.lastused = jiffies - idle * HZ; | ||||
|   | ||||
| -	if (entry->hash != 0xFFFF &&
 | ||||
| -	    mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash,
 | ||||
| -				  &diff)) {
 | ||||
| -		f->stats.pkts += diff.packets;
 | ||||
| -		f->stats.bytes += diff.bytes;
 | ||||
| -	}
 | ||||
| -
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
|  | @ -0,0 +1,29 @@ | |||
| From: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Date: Tue, 12 Sep 2023 10:22:56 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on mtk_pse_port definitions | ||||
|  in mtk_flow_set_output_device | ||||
| 
 | ||||
| Similar to ethernet ports, rely on mtk_pse_port definitions for | ||||
| pse wdma ports as well. | ||||
| 
 | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Reviewed-by: Simon Horman <horms@kernel.org> | ||||
| Link: https://lore.kernel.org/r/b86bdb717e963e3246c1dec5f736c810703cf056.1694506814.git.lorenzo@kernel.org | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 | ||||
| @@ -196,10 +196,10 @@ mtk_flow_set_output_device(struct mtk_et
 | ||||
|  		if (mtk_is_netsys_v2_or_greater(eth)) { | ||||
|  			switch (info.wdma_idx) { | ||||
|  			case 0: | ||||
| -				pse_port = 8;
 | ||||
| +				pse_port = PSE_WDMA0_PORT;
 | ||||
|  				break; | ||||
|  			case 1: | ||||
| -				pse_port = 9;
 | ||||
| +				pse_port = PSE_WDMA1_PORT;
 | ||||
|  				break; | ||||
|  			default: | ||||
|  				return -EINVAL; | ||||
|  | @ -0,0 +1,26 @@ | |||
| From: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Date: Tue, 12 Sep 2023 10:28:00 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: check update_wo_rx_stats in | ||||
|  mtk_wed_update_rx_stats() | ||||
| 
 | ||||
| Check if update_wo_rx_stats function pointer is properly set in | ||||
| mtk_wed_update_rx_stats routine before accessing it. | ||||
| 
 | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Reviewed-by: Simon Horman <horms@kernel.org> | ||||
| Link: https://lore.kernel.org/r/b0d233386e059bccb59f18f69afb79a7806e5ded.1694507226.git.lorenzo@kernel.org | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 | ||||
| @@ -68,6 +68,9 @@ mtk_wed_update_rx_stats(struct mtk_wed_d
 | ||||
|  	struct mtk_wed_wo_rx_stats *stats; | ||||
|  	int i; | ||||
|   | ||||
| +	if (!wed->wlan.update_wo_rx_stats)
 | ||||
| +		return;
 | ||||
| +
 | ||||
|  	if (count * sizeof(*stats) > skb->len - sizeof(u32)) | ||||
|  		return; | ||||
|   | ||||
|  | @ -0,0 +1,68 @@ | |||
| From: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Date: Wed, 13 Sep 2023 20:42:47 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: do not assume offload callbacks are | ||||
|  always set | ||||
| 
 | ||||
| Check if wlan.offload_enable and wlan.offload_disable callbacks are set | ||||
| in mtk_wed_flow_add/mtk_wed_flow_remove since mt7996 will not rely | ||||
| on them. | ||||
| 
 | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Reviewed-by: Simon Horman <horms@kernel.org> | ||||
| Signed-off-by: David S. Miller <davem@davemloft.net> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| @@ -1713,19 +1713,20 @@ mtk_wed_irq_set_mask(struct mtk_wed_devi
 | ||||
|  int mtk_wed_flow_add(int index) | ||||
|  { | ||||
|  	struct mtk_wed_hw *hw = hw_list[index]; | ||||
| -	int ret;
 | ||||
| +	int ret = 0;
 | ||||
|   | ||||
| -	if (!hw || !hw->wed_dev)
 | ||||
| -		return -ENODEV;
 | ||||
| +	mutex_lock(&hw_lock);
 | ||||
|   | ||||
| -	if (hw->num_flows) {
 | ||||
| -		hw->num_flows++;
 | ||||
| -		return 0;
 | ||||
| +	if (!hw || !hw->wed_dev) {
 | ||||
| +		ret = -ENODEV;
 | ||||
| +		goto out;
 | ||||
|  	} | ||||
|   | ||||
| -	mutex_lock(&hw_lock);
 | ||||
| -	if (!hw->wed_dev) {
 | ||||
| -		ret = -ENODEV;
 | ||||
| +	if (!hw->wed_dev->wlan.offload_enable)
 | ||||
| +		goto out;
 | ||||
| +
 | ||||
| +	if (hw->num_flows) {
 | ||||
| +		hw->num_flows++;
 | ||||
|  		goto out; | ||||
|  	} | ||||
|   | ||||
| @@ -1744,14 +1745,15 @@ void mtk_wed_flow_remove(int index)
 | ||||
|  { | ||||
|  	struct mtk_wed_hw *hw = hw_list[index]; | ||||
|   | ||||
| -	if (!hw)
 | ||||
| -		return;
 | ||||
| +	mutex_lock(&hw_lock);
 | ||||
|   | ||||
| -	if (--hw->num_flows)
 | ||||
| -		return;
 | ||||
| +	if (!hw || !hw->wed_dev)
 | ||||
| +		goto out;
 | ||||
|   | ||||
| -	mutex_lock(&hw_lock);
 | ||||
| -	if (!hw->wed_dev)
 | ||||
| +	if (!hw->wed_dev->wlan.offload_disable)
 | ||||
| +		goto out;
 | ||||
| +
 | ||||
| +	if (--hw->num_flows)
 | ||||
|  		goto out; | ||||
|   | ||||
|  	hw->wed_dev->wlan.offload_disable(hw->wed_dev); | ||||
|  | @ -0,0 +1,232 @@ | |||
| From: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Date: Mon, 18 Sep 2023 12:29:05 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: introduce versioning utility routines | ||||
| 
 | ||||
| Similar to mtk_eth_soc, introduce the following wed versioning | ||||
| utility routines: | ||||
| - mtk_wed_is_v1
 | ||||
| - mtk_wed_is_v2
 | ||||
| 
 | ||||
| This is a preliminary patch to introduce WED support for MT7988 SoC | ||||
| 
 | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| @@ -278,7 +278,7 @@ mtk_wed_assign(struct mtk_wed_device *de
 | ||||
|  		if (!hw->wed_dev) | ||||
|  			goto out; | ||||
|   | ||||
| -		if (hw->version == 1)
 | ||||
| +		if (mtk_wed_is_v1(hw))
 | ||||
|  			return NULL; | ||||
|   | ||||
|  		/* MT7986 WED devices do not have any pcie slot restrictions */ | ||||
| @@ -359,7 +359,7 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
 | ||||
|  			desc->buf0 = cpu_to_le32(buf_phys); | ||||
|  			desc->buf1 = cpu_to_le32(buf_phys + txd_size); | ||||
|   | ||||
| -			if (dev->hw->version == 1)
 | ||||
| +			if (mtk_wed_is_v1(dev->hw))
 | ||||
|  				ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | | ||||
|  				       FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, | ||||
|  						  MTK_WED_BUF_SIZE - txd_size) | | ||||
| @@ -498,7 +498,7 @@ mtk_wed_set_ext_int(struct mtk_wed_devic
 | ||||
|  { | ||||
|  	u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; | ||||
|   | ||||
| -	if (dev->hw->version == 1)
 | ||||
| +	if (mtk_wed_is_v1(dev->hw))
 | ||||
|  		mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; | ||||
|  	else | ||||
|  		mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | | ||||
| @@ -577,7 +577,7 @@ mtk_wed_dma_disable(struct mtk_wed_devic
 | ||||
|  		 MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | | ||||
|  		 MTK_WDMA_GLO_CFG_RX_INFO2_PRERES); | ||||
|   | ||||
| -	if (dev->hw->version == 1) {
 | ||||
| +	if (mtk_wed_is_v1(dev->hw)) {
 | ||||
|  		regmap_write(dev->hw->mirror, dev->hw->index * 4, 0); | ||||
|  		wdma_clr(dev, MTK_WDMA_GLO_CFG, | ||||
|  			 MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); | ||||
| @@ -606,7 +606,7 @@ mtk_wed_stop(struct mtk_wed_device *dev)
 | ||||
|  	wdma_w32(dev, MTK_WDMA_INT_GRP2, 0); | ||||
|  	wed_w32(dev, MTK_WED_WPDMA_INT_MASK, 0); | ||||
|   | ||||
| -	if (dev->hw->version == 1)
 | ||||
| +	if (mtk_wed_is_v1(dev->hw))
 | ||||
|  		return; | ||||
|   | ||||
|  	wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0); | ||||
| @@ -625,7 +625,7 @@ mtk_wed_deinit(struct mtk_wed_device *de
 | ||||
|  		MTK_WED_CTRL_WED_TX_BM_EN | | ||||
|  		MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); | ||||
|   | ||||
| -	if (dev->hw->version == 1)
 | ||||
| +	if (mtk_wed_is_v1(dev->hw))
 | ||||
|  		return; | ||||
|   | ||||
|  	wed_clr(dev, MTK_WED_CTRL, | ||||
| @@ -731,7 +731,7 @@ mtk_wed_bus_init(struct mtk_wed_device *
 | ||||
|  static void | ||||
|  mtk_wed_set_wpdma(struct mtk_wed_device *dev) | ||||
|  { | ||||
| -	if (dev->hw->version == 1) {
 | ||||
| +	if (mtk_wed_is_v1(dev->hw)) {
 | ||||
|  		wed_w32(dev, MTK_WED_WPDMA_CFG_BASE,  dev->wlan.wpdma_phys); | ||||
|  	} else { | ||||
|  		mtk_wed_bus_init(dev); | ||||
| @@ -762,7 +762,7 @@ mtk_wed_hw_init_early(struct mtk_wed_dev
 | ||||
|  	      MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY; | ||||
|  	wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set); | ||||
|   | ||||
| -	if (dev->hw->version == 1) {
 | ||||
| +	if (mtk_wed_is_v1(dev->hw)) {
 | ||||
|  		u32 offset = dev->hw->index ? 0x04000400 : 0; | ||||
|   | ||||
|  		wdma_set(dev, MTK_WDMA_GLO_CFG, | ||||
| @@ -935,7 +935,7 @@ mtk_wed_hw_init(struct mtk_wed_device *d
 | ||||
|   | ||||
|  	wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE); | ||||
|   | ||||
| -	if (dev->hw->version == 1) {
 | ||||
| +	if (mtk_wed_is_v1(dev->hw)) {
 | ||||
|  		wed_w32(dev, MTK_WED_TX_BM_TKID, | ||||
|  			FIELD_PREP(MTK_WED_TX_BM_TKID_START, | ||||
|  				   dev->wlan.token_start) | | ||||
| @@ -968,7 +968,7 @@ mtk_wed_hw_init(struct mtk_wed_device *d
 | ||||
|   | ||||
|  	mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); | ||||
|   | ||||
| -	if (dev->hw->version == 1) {
 | ||||
| +	if (mtk_wed_is_v1(dev->hw)) {
 | ||||
|  		wed_set(dev, MTK_WED_CTRL, | ||||
|  			MTK_WED_CTRL_WED_TX_BM_EN | | ||||
|  			MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); | ||||
| @@ -1218,7 +1218,7 @@ mtk_wed_reset_dma(struct mtk_wed_device
 | ||||
|  	} | ||||
|   | ||||
|  	dev->init_done = false; | ||||
| -	if (dev->hw->version == 1)
 | ||||
| +	if (mtk_wed_is_v1(dev->hw))
 | ||||
|  		return; | ||||
|   | ||||
|  	if (!busy) { | ||||
| @@ -1344,7 +1344,7 @@ mtk_wed_configure_irq(struct mtk_wed_dev
 | ||||
|  		MTK_WED_CTRL_WED_TX_BM_EN | | ||||
|  		MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); | ||||
|   | ||||
| -	if (dev->hw->version == 1) {
 | ||||
| +	if (mtk_wed_is_v1(dev->hw)) {
 | ||||
|  		wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, | ||||
|  			MTK_WED_PCIE_INT_TRIGGER_STATUS); | ||||
|   | ||||
| @@ -1417,7 +1417,7 @@ mtk_wed_dma_enable(struct mtk_wed_device
 | ||||
|  		 MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | | ||||
|  		 MTK_WDMA_GLO_CFG_RX_INFO2_PRERES); | ||||
|   | ||||
| -	if (dev->hw->version == 1) {
 | ||||
| +	if (mtk_wed_is_v1(dev->hw)) {
 | ||||
|  		wdma_set(dev, MTK_WDMA_GLO_CFG, | ||||
|  			 MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); | ||||
|  	} else { | ||||
| @@ -1466,7 +1466,7 @@ mtk_wed_start(struct mtk_wed_device *dev
 | ||||
|   | ||||
|  	mtk_wed_set_ext_int(dev, true); | ||||
|   | ||||
| -	if (dev->hw->version == 1) {
 | ||||
| +	if (mtk_wed_is_v1(dev->hw)) {
 | ||||
|  		u32 val = dev->wlan.wpdma_phys | MTK_PCIE_MIRROR_MAP_EN | | ||||
|  			  FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID, | ||||
|  				     dev->hw->index); | ||||
| @@ -1551,7 +1551,7 @@ mtk_wed_attach(struct mtk_wed_device *de
 | ||||
|  	} | ||||
|   | ||||
|  	mtk_wed_hw_init_early(dev); | ||||
| -	if (hw->version == 1) {
 | ||||
| +	if (mtk_wed_is_v1(hw)) {
 | ||||
|  		regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, | ||||
|  				   BIT(hw->index), 0); | ||||
|  	} else { | ||||
| @@ -1619,7 +1619,7 @@ static int
 | ||||
|  mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs) | ||||
|  { | ||||
|  	struct mtk_wed_ring *ring = &dev->txfree_ring; | ||||
| -	int i, index = dev->hw->version == 1;
 | ||||
| +	int i, index = mtk_wed_is_v1(dev->hw);
 | ||||
|   | ||||
|  	/* | ||||
|  	 * For txfree event handling, the same DMA ring is shared between WED | ||||
| @@ -1677,7 +1677,7 @@ mtk_wed_irq_get(struct mtk_wed_device *d
 | ||||
|  { | ||||
|  	u32 val, ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; | ||||
|   | ||||
| -	if (dev->hw->version == 1)
 | ||||
| +	if (mtk_wed_is_v1(dev->hw))
 | ||||
|  		ext_mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; | ||||
|  	else | ||||
|  		ext_mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | | ||||
| @@ -1844,7 +1844,7 @@ mtk_wed_setup_tc(struct mtk_wed_device *
 | ||||
|  { | ||||
|  	struct mtk_wed_hw *hw = wed->hw; | ||||
|   | ||||
| -	if (hw->version < 2)
 | ||||
| +	if (mtk_wed_is_v1(hw))
 | ||||
|  		return -EOPNOTSUPP; | ||||
|   | ||||
|  	switch (type) { | ||||
| @@ -1918,9 +1918,9 @@ void mtk_wed_add_hw(struct device_node *
 | ||||
|  	hw->wdma = wdma; | ||||
|  	hw->index = index; | ||||
|  	hw->irq = irq; | ||||
| -	hw->version = mtk_is_netsys_v1(eth) ? 1 : 2;
 | ||||
| +	hw->version = eth->soc->version;
 | ||||
|   | ||||
| -	if (hw->version == 1) {
 | ||||
| +	if (mtk_wed_is_v1(hw)) {
 | ||||
|  		hw->mirror = syscon_regmap_lookup_by_phandle(eth_np, | ||||
|  				"mediatek,pcie-mirror"); | ||||
|  		hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np, | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.h
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.h
 | ||||
| @@ -40,6 +40,16 @@ struct mtk_wdma_info {
 | ||||
|  }; | ||||
|   | ||||
|  #ifdef CONFIG_NET_MEDIATEK_SOC_WED | ||||
| +static inline bool mtk_wed_is_v1(struct mtk_wed_hw *hw)
 | ||||
| +{
 | ||||
| +	return hw->version == 1;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static inline bool mtk_wed_is_v2(struct mtk_wed_hw *hw)
 | ||||
| +{
 | ||||
| +	return hw->version == 2;
 | ||||
| +}
 | ||||
| +
 | ||||
|  static inline void | ||||
|  wed_w32(struct mtk_wed_device *dev, u32 reg, u32 val) | ||||
|  { | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
 | ||||
| @@ -261,7 +261,7 @@ void mtk_wed_hw_add_debugfs(struct mtk_w
 | ||||
|  	debugfs_create_u32("regidx", 0600, dir, &hw->debugfs_reg); | ||||
|  	debugfs_create_file_unsafe("regval", 0600, dir, hw, &fops_regval); | ||||
|  	debugfs_create_file_unsafe("txinfo", 0400, dir, hw, &wed_txinfo_fops); | ||||
| -	if (hw->version != 1)
 | ||||
| +	if (!mtk_wed_is_v1(hw))
 | ||||
|  		debugfs_create_file_unsafe("rxinfo", 0400, dir, hw, | ||||
|  					   &wed_rxinfo_fops); | ||||
|  } | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 | ||||
| @@ -207,7 +207,7 @@ int mtk_wed_mcu_msg_update(struct mtk_we
 | ||||
|  { | ||||
|  	struct mtk_wed_wo *wo = dev->hw->wed_wo; | ||||
|   | ||||
| -	if (dev->hw->version == 1)
 | ||||
| +	if (mtk_wed_is_v1(dev->hw))
 | ||||
|  		return 0; | ||||
|   | ||||
|  	if (WARN_ON(!wo)) | ||||
|  | @ -0,0 +1,234 @@ | |||
| From: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Date: Mon, 18 Sep 2023 12:29:06 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: do not configure rx offload if not | ||||
|  supported | ||||
| 
 | ||||
| Check if rx offload is supported running mtk_wed_get_rx_capa routine | ||||
| before configuring it. This is a preliminary patch to introduce Wireless | ||||
| Ethernet Dispatcher (WED) support for MT7988 SoC. | ||||
| 
 | ||||
| Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| @@ -606,7 +606,7 @@ mtk_wed_stop(struct mtk_wed_device *dev)
 | ||||
|  	wdma_w32(dev, MTK_WDMA_INT_GRP2, 0); | ||||
|  	wed_w32(dev, MTK_WED_WPDMA_INT_MASK, 0); | ||||
|   | ||||
| -	if (mtk_wed_is_v1(dev->hw))
 | ||||
| +	if (!mtk_wed_get_rx_capa(dev))
 | ||||
|  		return; | ||||
|   | ||||
|  	wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0); | ||||
| @@ -733,16 +733,21 @@ mtk_wed_set_wpdma(struct mtk_wed_device
 | ||||
|  { | ||||
|  	if (mtk_wed_is_v1(dev->hw)) { | ||||
|  		wed_w32(dev, MTK_WED_WPDMA_CFG_BASE,  dev->wlan.wpdma_phys); | ||||
| -	} else {
 | ||||
| -		mtk_wed_bus_init(dev);
 | ||||
| -
 | ||||
| -		wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int);
 | ||||
| -		wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask);
 | ||||
| -		wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx);
 | ||||
| -		wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree);
 | ||||
| -		wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo);
 | ||||
| -		wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx);
 | ||||
| +		return;
 | ||||
|  	} | ||||
| +
 | ||||
| +	mtk_wed_bus_init(dev);
 | ||||
| +
 | ||||
| +	wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int);
 | ||||
| +	wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask);
 | ||||
| +	wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx);
 | ||||
| +	wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree);
 | ||||
| +
 | ||||
| +	if (!mtk_wed_get_rx_capa(dev))
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo);
 | ||||
| +	wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx);
 | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -974,15 +979,17 @@ mtk_wed_hw_init(struct mtk_wed_device *d
 | ||||
|  			MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); | ||||
|  	} else { | ||||
|  		wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE); | ||||
| -		/* rx hw init */
 | ||||
| -		wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
 | ||||
| -			MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
 | ||||
| -			MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
 | ||||
| -		wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
 | ||||
| -
 | ||||
| -		mtk_wed_rx_buffer_hw_init(dev);
 | ||||
| -		mtk_wed_rro_hw_init(dev);
 | ||||
| -		mtk_wed_route_qm_hw_init(dev);
 | ||||
| +		if (mtk_wed_get_rx_capa(dev)) {
 | ||||
| +			/* rx hw init */
 | ||||
| +			wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
 | ||||
| +				MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
 | ||||
| +				MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
 | ||||
| +			wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
 | ||||
| +
 | ||||
| +			mtk_wed_rx_buffer_hw_init(dev);
 | ||||
| +			mtk_wed_rro_hw_init(dev);
 | ||||
| +			mtk_wed_route_qm_hw_init(dev);
 | ||||
| +		}
 | ||||
|  	} | ||||
|   | ||||
|  	wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE); | ||||
| @@ -1354,8 +1361,6 @@ mtk_wed_configure_irq(struct mtk_wed_dev
 | ||||
|   | ||||
|  		wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask); | ||||
|  	} else { | ||||
| -		wdma_mask |= FIELD_PREP(MTK_WDMA_INT_MASK_TX_DONE,
 | ||||
| -					GENMASK(1, 0));
 | ||||
|  		/* initail tx interrupt trigger */ | ||||
|  		wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX, | ||||
|  			MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN | | ||||
| @@ -1374,15 +1379,20 @@ mtk_wed_configure_irq(struct mtk_wed_dev
 | ||||
|  			FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG, | ||||
|  				   dev->wlan.txfree_tbit)); | ||||
|   | ||||
| -		wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RX,
 | ||||
| -			MTK_WED_WPDMA_INT_CTRL_RX0_EN |
 | ||||
| -			MTK_WED_WPDMA_INT_CTRL_RX0_CLR |
 | ||||
| -			MTK_WED_WPDMA_INT_CTRL_RX1_EN |
 | ||||
| -			MTK_WED_WPDMA_INT_CTRL_RX1_CLR |
 | ||||
| -			FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG,
 | ||||
| -				   dev->wlan.rx_tbit[0]) |
 | ||||
| -			FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG,
 | ||||
| -				   dev->wlan.rx_tbit[1]));
 | ||||
| +		if (mtk_wed_get_rx_capa(dev)) {
 | ||||
| +			wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RX,
 | ||||
| +				MTK_WED_WPDMA_INT_CTRL_RX0_EN |
 | ||||
| +				MTK_WED_WPDMA_INT_CTRL_RX0_CLR |
 | ||||
| +				MTK_WED_WPDMA_INT_CTRL_RX1_EN |
 | ||||
| +				MTK_WED_WPDMA_INT_CTRL_RX1_CLR |
 | ||||
| +				FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG,
 | ||||
| +					   dev->wlan.rx_tbit[0]) |
 | ||||
| +				FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG,
 | ||||
| +					   dev->wlan.rx_tbit[1]));
 | ||||
| +
 | ||||
| +			wdma_mask |= FIELD_PREP(MTK_WDMA_INT_MASK_TX_DONE,
 | ||||
| +						GENMASK(1, 0));
 | ||||
| +		}
 | ||||
|   | ||||
|  		wed_w32(dev, MTK_WED_WDMA_INT_CLR, wdma_mask); | ||||
|  		wed_set(dev, MTK_WED_WDMA_INT_CTRL, | ||||
| @@ -1401,6 +1411,8 @@ mtk_wed_configure_irq(struct mtk_wed_dev
 | ||||
|  static void | ||||
|  mtk_wed_dma_enable(struct mtk_wed_device *dev) | ||||
|  { | ||||
| +	int i;
 | ||||
| +
 | ||||
|  	wed_set(dev, MTK_WED_WPDMA_INT_CTRL, MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV); | ||||
|   | ||||
|  	wed_set(dev, MTK_WED_GLO_CFG, | ||||
| @@ -1420,33 +1432,33 @@ mtk_wed_dma_enable(struct mtk_wed_device
 | ||||
|  	if (mtk_wed_is_v1(dev->hw)) { | ||||
|  		wdma_set(dev, MTK_WDMA_GLO_CFG, | ||||
|  			 MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); | ||||
| -	} else {
 | ||||
| -		int i;
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
|   | ||||
| -		wed_set(dev, MTK_WED_WPDMA_CTRL,
 | ||||
| -			MTK_WED_WPDMA_CTRL_SDL1_FIXED);
 | ||||
| +	wed_set(dev, MTK_WED_WPDMA_CTRL,
 | ||||
| +		MTK_WED_WPDMA_CTRL_SDL1_FIXED);
 | ||||
| +	wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
 | ||||
| +		MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
 | ||||
| +		MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
 | ||||
| +	wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
 | ||||
| +		MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP |
 | ||||
| +		MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV);
 | ||||
|   | ||||
| -		wed_set(dev, MTK_WED_WDMA_GLO_CFG,
 | ||||
| -			MTK_WED_WDMA_GLO_CFG_TX_DRV_EN |
 | ||||
| -			MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
 | ||||
| +	if (!mtk_wed_get_rx_capa(dev))
 | ||||
| +		return;
 | ||||
|   | ||||
| -		wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
 | ||||
| -			MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
 | ||||
| -			MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
 | ||||
| -
 | ||||
| -		wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
 | ||||
| -			MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP |
 | ||||
| -			MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV);
 | ||||
| +	wed_set(dev, MTK_WED_WDMA_GLO_CFG,
 | ||||
| +		MTK_WED_WDMA_GLO_CFG_TX_DRV_EN |
 | ||||
| +		MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
 | ||||
|   | ||||
| -		wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
 | ||||
| -			MTK_WED_WPDMA_RX_D_RX_DRV_EN |
 | ||||
| -			FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) |
 | ||||
| -			FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL,
 | ||||
| -				   0x2));
 | ||||
| +	wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
 | ||||
| +		MTK_WED_WPDMA_RX_D_RX_DRV_EN |
 | ||||
| +		FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) |
 | ||||
| +		FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL,
 | ||||
| +			   0x2));
 | ||||
|   | ||||
| -		for (i = 0; i < MTK_WED_RX_QUEUES; i++)
 | ||||
| -			mtk_wed_check_wfdma_rx_fill(dev, i);
 | ||||
| -	}
 | ||||
| +	for (i = 0; i < MTK_WED_RX_QUEUES; i++)
 | ||||
| +		mtk_wed_check_wfdma_rx_fill(dev, i);
 | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -1473,7 +1485,7 @@ mtk_wed_start(struct mtk_wed_device *dev
 | ||||
|   | ||||
|  		val |= BIT(0) | (BIT(1) * !!dev->hw->index); | ||||
|  		regmap_write(dev->hw->mirror, dev->hw->index * 4, val); | ||||
| -	} else {
 | ||||
| +	} else if (mtk_wed_get_rx_capa(dev)) {
 | ||||
|  		/* driver set mid ready and only once */ | ||||
|  		wed_w32(dev, MTK_WED_EXT_INT_MASK1, | ||||
|  			MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY); | ||||
| @@ -1485,7 +1497,6 @@ mtk_wed_start(struct mtk_wed_device *dev
 | ||||
|   | ||||
|  		if (mtk_wed_rro_cfg(dev)) | ||||
|  			return; | ||||
| -
 | ||||
|  	} | ||||
|   | ||||
|  	mtk_wed_set_512_support(dev, dev->wlan.wcid_512); | ||||
| @@ -1551,13 +1562,14 @@ mtk_wed_attach(struct mtk_wed_device *de
 | ||||
|  	} | ||||
|   | ||||
|  	mtk_wed_hw_init_early(dev); | ||||
| -	if (mtk_wed_is_v1(hw)) {
 | ||||
| +	if (mtk_wed_is_v1(hw))
 | ||||
|  		regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, | ||||
|  				   BIT(hw->index), 0); | ||||
| -	} else {
 | ||||
| +	else
 | ||||
|  		dev->rev_id = wed_r32(dev, MTK_WED_REV_ID); | ||||
| +
 | ||||
| +	if (mtk_wed_get_rx_capa(dev))
 | ||||
|  		ret = mtk_wed_wo_init(hw); | ||||
| -	}
 | ||||
|  out: | ||||
|  	if (ret) { | ||||
|  		dev_err(dev->hw->dev, "failed to attach wed device\n"); | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 | ||||
| @@ -207,7 +207,7 @@ int mtk_wed_mcu_msg_update(struct mtk_we
 | ||||
|  { | ||||
|  	struct mtk_wed_wo *wo = dev->hw->wed_wo; | ||||
|   | ||||
| -	if (mtk_wed_is_v1(dev->hw))
 | ||||
| +	if (!mtk_wed_get_rx_capa(dev))
 | ||||
|  		return 0; | ||||
|   | ||||
|  	if (WARN_ON(!wo)) | ||||
|  | @ -0,0 +1,52 @@ | |||
| From: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Date: Mon, 18 Sep 2023 12:29:07 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: rename mtk_rxbm_desc in | ||||
|  mtk_wed_bm_desc | ||||
| 
 | ||||
| Rename mtk_rxbm_desc structure in mtk_wed_bm_desc since it will be used | ||||
| even on tx side by MT7988 SoC. | ||||
| 
 | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| @@ -422,7 +422,7 @@ free_pagelist:
 | ||||
|  static int | ||||
|  mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev) | ||||
|  { | ||||
| -	struct mtk_rxbm_desc *desc;
 | ||||
| +	struct mtk_wed_bm_desc *desc;
 | ||||
|  	dma_addr_t desc_phys; | ||||
|   | ||||
|  	dev->rx_buf_ring.size = dev->wlan.rx_nbuf; | ||||
| @@ -442,7 +442,7 @@ mtk_wed_rx_buffer_alloc(struct mtk_wed_d
 | ||||
|  static void | ||||
|  mtk_wed_free_rx_buffer(struct mtk_wed_device *dev) | ||||
|  { | ||||
| -	struct mtk_rxbm_desc *desc = dev->rx_buf_ring.desc;
 | ||||
| +	struct mtk_wed_bm_desc *desc = dev->rx_buf_ring.desc;
 | ||||
|   | ||||
|  	if (!desc) | ||||
|  		return; | ||||
| --- a/include/linux/soc/mediatek/mtk_wed.h
 | ||||
| +++ b/include/linux/soc/mediatek/mtk_wed.h
 | ||||
| @@ -45,7 +45,7 @@ enum mtk_wed_wo_cmd {
 | ||||
|  	MTK_WED_WO_CMD_WED_END | ||||
|  }; | ||||
|   | ||||
| -struct mtk_rxbm_desc {
 | ||||
| +struct mtk_wed_bm_desc {
 | ||||
|  	__le32 buf0; | ||||
|  	__le32 token; | ||||
|  } __packed __aligned(4); | ||||
| @@ -104,7 +104,7 @@ struct mtk_wed_device {
 | ||||
|   | ||||
|  	struct { | ||||
|  		int size; | ||||
| -		struct mtk_rxbm_desc *desc;
 | ||||
| +		struct mtk_wed_bm_desc *desc;
 | ||||
|  		dma_addr_t desc_phys; | ||||
|  	} rx_buf_ring; | ||||
|   | ||||
|  | @ -0,0 +1,87 @@ | |||
| From: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Date: Mon, 18 Sep 2023 12:29:08 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: introduce mtk_wed_buf structure | ||||
| 
 | ||||
| Introduce mtk_wed_buf structure to store both virtual and physical | ||||
| addresses allocated in mtk_wed_tx_buffer_alloc() routine. This is a | ||||
| preliminary patch to add WED support for MT7988 SoC since it relies on a | ||||
| different dma descriptor layout not storing page dma addresses.
 | ||||
| 
 | ||||
| Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| @@ -300,9 +300,9 @@ out:
 | ||||
|  static int | ||||
|  mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev) | ||||
|  { | ||||
| +	struct mtk_wed_buf *page_list;
 | ||||
|  	struct mtk_wdma_desc *desc; | ||||
|  	dma_addr_t desc_phys; | ||||
| -	void **page_list;
 | ||||
|  	int token = dev->wlan.token_start; | ||||
|  	int ring_size; | ||||
|  	int n_pages; | ||||
| @@ -343,7 +343,8 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
 | ||||
|  			return -ENOMEM; | ||||
|  		} | ||||
|   | ||||
| -		page_list[page_idx++] = page;
 | ||||
| +		page_list[page_idx].p = page;
 | ||||
| +		page_list[page_idx++].phy_addr = page_phys;
 | ||||
|  		dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE, | ||||
|  					DMA_BIDIRECTIONAL); | ||||
|   | ||||
| @@ -387,8 +388,8 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
 | ||||
|  static void | ||||
|  mtk_wed_free_tx_buffer(struct mtk_wed_device *dev) | ||||
|  { | ||||
| +	struct mtk_wed_buf *page_list = dev->tx_buf_ring.pages;
 | ||||
|  	struct mtk_wdma_desc *desc = dev->tx_buf_ring.desc; | ||||
| -	void **page_list = dev->tx_buf_ring.pages;
 | ||||
|  	int page_idx; | ||||
|  	int i; | ||||
|   | ||||
| @@ -400,13 +401,12 @@ mtk_wed_free_tx_buffer(struct mtk_wed_de
 | ||||
|   | ||||
|  	for (i = 0, page_idx = 0; i < dev->tx_buf_ring.size; | ||||
|  	     i += MTK_WED_BUF_PER_PAGE) { | ||||
| -		void *page = page_list[page_idx++];
 | ||||
| -		dma_addr_t buf_addr;
 | ||||
| +		dma_addr_t buf_addr = page_list[page_idx].phy_addr;
 | ||||
| +		void *page = page_list[page_idx++].p;
 | ||||
|   | ||||
|  		if (!page) | ||||
|  			break; | ||||
|   | ||||
| -		buf_addr = le32_to_cpu(desc[i].buf0);
 | ||||
|  		dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE, | ||||
|  			       DMA_BIDIRECTIONAL); | ||||
|  		__free_page(page); | ||||
| --- a/include/linux/soc/mediatek/mtk_wed.h
 | ||||
| +++ b/include/linux/soc/mediatek/mtk_wed.h
 | ||||
| @@ -76,6 +76,11 @@ struct mtk_wed_wo_rx_stats {
 | ||||
|  	__le32 rx_drop_cnt; | ||||
|  }; | ||||
|   | ||||
| +struct mtk_wed_buf {
 | ||||
| +	void *p;
 | ||||
| +	dma_addr_t phy_addr;
 | ||||
| +};
 | ||||
| +
 | ||||
|  struct mtk_wed_device { | ||||
|  #ifdef CONFIG_NET_MEDIATEK_SOC_WED | ||||
|  	const struct mtk_wed_ops *ops; | ||||
| @@ -97,7 +102,7 @@ struct mtk_wed_device {
 | ||||
|   | ||||
|  	struct { | ||||
|  		int size; | ||||
| -		void **pages;
 | ||||
| +		struct mtk_wed_buf *pages;
 | ||||
|  		struct mtk_wdma_desc *desc; | ||||
|  		dma_addr_t desc_phys; | ||||
|  	} tx_buf_ring; | ||||
|  | @ -0,0 +1,88 @@ | |||
| From: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Date: Mon, 18 Sep 2023 12:29:09 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: move mem_region array out of | ||||
|  mtk_wed_mcu_load_firmware | ||||
| 
 | ||||
| Remove mtk_wed_wo_memory_region boot structure in mtk_wed_wo. | ||||
| This is a preliminary patch to introduce WED support for MT7988 SoC. | ||||
| 
 | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 | ||||
| @@ -16,14 +16,30 @@
 | ||||
|  #include "mtk_wed_wo.h" | ||||
|  #include "mtk_wed.h" | ||||
|   | ||||
| +static struct mtk_wed_wo_memory_region mem_region[] = {
 | ||||
| +	[MTK_WED_WO_REGION_EMI] = {
 | ||||
| +		.name = "wo-emi",
 | ||||
| +	},
 | ||||
| +	[MTK_WED_WO_REGION_ILM] = {
 | ||||
| +		.name = "wo-ilm",
 | ||||
| +	},
 | ||||
| +	[MTK_WED_WO_REGION_DATA] = {
 | ||||
| +		.name = "wo-data",
 | ||||
| +		.shared = true,
 | ||||
| +	},
 | ||||
| +	[MTK_WED_WO_REGION_BOOT] = {
 | ||||
| +		.name = "wo-boot",
 | ||||
| +	},
 | ||||
| +};
 | ||||
| +
 | ||||
|  static u32 wo_r32(struct mtk_wed_wo *wo, u32 reg) | ||||
|  { | ||||
| -	return readl(wo->boot.addr + reg);
 | ||||
| +	return readl(mem_region[MTK_WED_WO_REGION_BOOT].addr + reg);
 | ||||
|  } | ||||
|   | ||||
|  static void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val) | ||||
|  { | ||||
| -	writel(val, wo->boot.addr + reg);
 | ||||
| +	writel(val, mem_region[MTK_WED_WO_REGION_BOOT].addr + reg);
 | ||||
|  } | ||||
|   | ||||
|  static struct sk_buff * | ||||
| @@ -294,18 +310,6 @@ next:
 | ||||
|  static int | ||||
|  mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo) | ||||
|  { | ||||
| -	static struct mtk_wed_wo_memory_region mem_region[] = {
 | ||||
| -		[MTK_WED_WO_REGION_EMI] = {
 | ||||
| -			.name = "wo-emi",
 | ||||
| -		},
 | ||||
| -		[MTK_WED_WO_REGION_ILM] = {
 | ||||
| -			.name = "wo-ilm",
 | ||||
| -		},
 | ||||
| -		[MTK_WED_WO_REGION_DATA] = {
 | ||||
| -			.name = "wo-data",
 | ||||
| -			.shared = true,
 | ||||
| -		},
 | ||||
| -	};
 | ||||
|  	const struct mtk_wed_fw_trailer *trailer; | ||||
|  	const struct firmware *fw; | ||||
|  	const char *fw_name; | ||||
| @@ -319,11 +323,6 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
 | ||||
|  			return ret; | ||||
|  	} | ||||
|   | ||||
| -	wo->boot.name = "wo-boot";
 | ||||
| -	ret = mtk_wed_get_memory_region(wo, &wo->boot);
 | ||||
| -	if (ret)
 | ||||
| -		return ret;
 | ||||
| -
 | ||||
|  	/* set dummy cr */ | ||||
|  	wed_w32(wo->hw->wed_dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL, | ||||
|  		wo->hw->index + 1); | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
 | ||||
| @@ -228,7 +228,6 @@ struct mtk_wed_wo_queue {
 | ||||
|   | ||||
|  struct mtk_wed_wo { | ||||
|  	struct mtk_wed_hw *hw; | ||||
| -	struct mtk_wed_wo_memory_region boot;
 | ||||
|   | ||||
|  	struct mtk_wed_wo_queue q_tx; | ||||
|  	struct mtk_wed_wo_queue q_rx; | ||||
|  | @ -0,0 +1,71 @@ | |||
| From: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Date: Mon, 18 Sep 2023 12:29:10 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: make memory region optional | ||||
| 
 | ||||
| Make mtk_wed_wo_memory_region optionals. | ||||
| This is a preliminary patch to introduce Wireless Ethernet Dispatcher | ||||
| support for MT7988 SoC since MT7988 WED fw image will have a different | ||||
| layout. | ||||
| 
 | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
 | ||||
| @@ -234,19 +234,13 @@ int mtk_wed_mcu_msg_update(struct mtk_we
 | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| -mtk_wed_get_memory_region(struct mtk_wed_wo *wo,
 | ||||
| +mtk_wed_get_memory_region(struct mtk_wed_hw *hw, int index,
 | ||||
|  			  struct mtk_wed_wo_memory_region *region) | ||||
|  { | ||||
|  	struct reserved_mem *rmem; | ||||
|  	struct device_node *np; | ||||
| -	int index;
 | ||||
|   | ||||
| -	index = of_property_match_string(wo->hw->node, "memory-region-names",
 | ||||
| -					 region->name);
 | ||||
| -	if (index < 0)
 | ||||
| -		return index;
 | ||||
| -
 | ||||
| -	np = of_parse_phandle(wo->hw->node, "memory-region", index);
 | ||||
| +	np = of_parse_phandle(hw->node, "memory-region", index);
 | ||||
|  	if (!np) | ||||
|  		return -ENODEV; | ||||
|   | ||||
| @@ -258,7 +252,7 @@ mtk_wed_get_memory_region(struct mtk_wed
 | ||||
|   | ||||
|  	region->phy_addr = rmem->base; | ||||
|  	region->size = rmem->size; | ||||
| -	region->addr = devm_ioremap(wo->hw->dev, region->phy_addr, region->size);
 | ||||
| +	region->addr = devm_ioremap(hw->dev, region->phy_addr, region->size);
 | ||||
|   | ||||
|  	return !region->addr ? -EINVAL : 0; | ||||
|  } | ||||
| @@ -271,6 +265,9 @@ mtk_wed_mcu_run_firmware(struct mtk_wed_
 | ||||
|  	const struct mtk_wed_fw_trailer *trailer; | ||||
|  	const struct mtk_wed_fw_region *fw_region; | ||||
|   | ||||
| +	if (!region->phy_addr || !region->size)
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
|  	trailer_ptr = fw->data + fw->size - sizeof(*trailer); | ||||
|  	trailer = (const struct mtk_wed_fw_trailer *)trailer_ptr; | ||||
|  	region_ptr = trailer_ptr - trailer->num_region * sizeof(*fw_region); | ||||
| @@ -318,7 +315,13 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
 | ||||
|   | ||||
|  	/* load firmware region metadata */ | ||||
|  	for (i = 0; i < ARRAY_SIZE(mem_region); i++) { | ||||
| -		ret = mtk_wed_get_memory_region(wo, &mem_region[i]);
 | ||||
| +		int index = of_property_match_string(wo->hw->node,
 | ||||
| +						     "memory-region-names",
 | ||||
| +						     mem_region[i].name);
 | ||||
| +		if (index < 0)
 | ||||
| +			continue;
 | ||||
| +
 | ||||
| +		ret = mtk_wed_get_memory_region(wo->hw, index, &mem_region[i]);
 | ||||
|  		if (ret) | ||||
|  			return ret; | ||||
|  	} | ||||
|  | @ -0,0 +1,217 @@ | |||
| From: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Date: Mon, 18 Sep 2023 12:29:12 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: add mtk_wed_soc_data structure | ||||
| 
 | ||||
| Introduce mtk_wed_soc_data utility structure to contain per-SoC | ||||
| definitions. | ||||
| 
 | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| @@ -49,6 +49,26 @@ struct mtk_wed_flow_block_priv {
 | ||||
|  	struct net_device *dev; | ||||
|  }; | ||||
|   | ||||
| +static const struct mtk_wed_soc_data mt7622_data = {
 | ||||
| +	.regmap = {
 | ||||
| +		.tx_bm_tkid		= 0x088,
 | ||||
| +		.wpdma_rx_ring0		= 0x770,
 | ||||
| +		.reset_idx_tx_mask	= GENMASK(3, 0),
 | ||||
| +		.reset_idx_rx_mask	= GENMASK(17, 16),
 | ||||
| +	},
 | ||||
| +	.wdma_desc_size = sizeof(struct mtk_wdma_desc),
 | ||||
| +};
 | ||||
| +
 | ||||
| +static const struct mtk_wed_soc_data mt7986_data = {
 | ||||
| +	.regmap = {
 | ||||
| +		.tx_bm_tkid		= 0x0c8,
 | ||||
| +		.wpdma_rx_ring0		= 0x770,
 | ||||
| +		.reset_idx_tx_mask	= GENMASK(1, 0),
 | ||||
| +		.reset_idx_rx_mask	= GENMASK(7, 6),
 | ||||
| +	},
 | ||||
| +	.wdma_desc_size = 2 * sizeof(struct mtk_wdma_desc),
 | ||||
| +};
 | ||||
| +
 | ||||
|  static void | ||||
|  wed_m32(struct mtk_wed_device *dev, u32 reg, u32 mask, u32 val) | ||||
|  { | ||||
| @@ -747,7 +767,7 @@ mtk_wed_set_wpdma(struct mtk_wed_device
 | ||||
|  		return; | ||||
|   | ||||
|  	wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo); | ||||
| -	wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx);
 | ||||
| +	wed_w32(dev, dev->hw->soc->regmap.wpdma_rx_ring0, dev->wlan.wpdma_rx);
 | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -941,22 +961,10 @@ mtk_wed_hw_init(struct mtk_wed_device *d
 | ||||
|  	wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE); | ||||
|   | ||||
|  	if (mtk_wed_is_v1(dev->hw)) { | ||||
| -		wed_w32(dev, MTK_WED_TX_BM_TKID,
 | ||||
| -			FIELD_PREP(MTK_WED_TX_BM_TKID_START,
 | ||||
| -				   dev->wlan.token_start) |
 | ||||
| -			FIELD_PREP(MTK_WED_TX_BM_TKID_END,
 | ||||
| -				   dev->wlan.token_start +
 | ||||
| -				   dev->wlan.nbuf - 1));
 | ||||
|  		wed_w32(dev, MTK_WED_TX_BM_DYN_THR, | ||||
|  			FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) | | ||||
|  			MTK_WED_TX_BM_DYN_THR_HI); | ||||
|  	} else { | ||||
| -		wed_w32(dev, MTK_WED_TX_BM_TKID_V2,
 | ||||
| -			FIELD_PREP(MTK_WED_TX_BM_TKID_START,
 | ||||
| -				   dev->wlan.token_start) |
 | ||||
| -			FIELD_PREP(MTK_WED_TX_BM_TKID_END,
 | ||||
| -				   dev->wlan.token_start +
 | ||||
| -				   dev->wlan.nbuf - 1));
 | ||||
|  		wed_w32(dev, MTK_WED_TX_BM_DYN_THR, | ||||
|  			FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO_V2, 0) | | ||||
|  			MTK_WED_TX_BM_DYN_THR_HI_V2); | ||||
| @@ -971,6 +979,11 @@ mtk_wed_hw_init(struct mtk_wed_device *d
 | ||||
|  			MTK_WED_TX_TKID_DYN_THR_HI); | ||||
|  	} | ||||
|   | ||||
| +	wed_w32(dev, dev->hw->soc->regmap.tx_bm_tkid,
 | ||||
| +		FIELD_PREP(MTK_WED_TX_BM_TKID_START, dev->wlan.token_start) |
 | ||||
| +		FIELD_PREP(MTK_WED_TX_BM_TKID_END,
 | ||||
| +			   dev->wlan.token_start + dev->wlan.nbuf - 1));
 | ||||
| +
 | ||||
|  	mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); | ||||
|   | ||||
|  	if (mtk_wed_is_v1(dev->hw)) { | ||||
| @@ -1105,13 +1118,8 @@ mtk_wed_rx_reset(struct mtk_wed_device *
 | ||||
|  	if (ret) { | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WED_RX_DMA); | ||||
|  	} else { | ||||
| -		struct mtk_eth *eth = dev->hw->eth;
 | ||||
| -
 | ||||
| -		if (mtk_is_netsys_v2_or_greater(eth))
 | ||||
| -			wed_set(dev, MTK_WED_RESET_IDX,
 | ||||
| -				MTK_WED_RESET_IDX_RX_V2);
 | ||||
| -		else
 | ||||
| -			wed_set(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_RX);
 | ||||
| +		wed_set(dev, MTK_WED_RESET_IDX,
 | ||||
| +			dev->hw->soc->regmap.reset_idx_rx_mask);
 | ||||
|  		wed_w32(dev, MTK_WED_RESET_IDX, 0); | ||||
|  	} | ||||
|   | ||||
| @@ -1164,7 +1172,8 @@ mtk_wed_reset_dma(struct mtk_wed_device
 | ||||
|  	if (busy) { | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WED_TX_DMA); | ||||
|  	} else { | ||||
| -		wed_w32(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_TX);
 | ||||
| +		wed_w32(dev, MTK_WED_RESET_IDX,
 | ||||
| +			dev->hw->soc->regmap.reset_idx_tx_mask);
 | ||||
|  		wed_w32(dev, MTK_WED_RESET_IDX, 0); | ||||
|  	} | ||||
|   | ||||
| @@ -1256,7 +1265,6 @@ static int
 | ||||
|  mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size, | ||||
|  			   bool reset) | ||||
|  { | ||||
| -	u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
 | ||||
|  	struct mtk_wed_ring *wdma; | ||||
|   | ||||
|  	if (idx >= ARRAY_SIZE(dev->rx_wdma)) | ||||
| @@ -1264,7 +1272,7 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_we
 | ||||
|   | ||||
|  	wdma = &dev->rx_wdma[idx]; | ||||
|  	if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, | ||||
| -					 desc_size, true))
 | ||||
| +					 dev->hw->soc->wdma_desc_size, true))
 | ||||
|  		return -ENOMEM; | ||||
|   | ||||
|  	wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE, | ||||
| @@ -1285,7 +1293,6 @@ static int
 | ||||
|  mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size, | ||||
|  			   bool reset) | ||||
|  { | ||||
| -	u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
 | ||||
|  	struct mtk_wed_ring *wdma; | ||||
|   | ||||
|  	if (idx >= ARRAY_SIZE(dev->tx_wdma)) | ||||
| @@ -1293,7 +1300,7 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_we
 | ||||
|   | ||||
|  	wdma = &dev->tx_wdma[idx]; | ||||
|  	if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, | ||||
| -					 desc_size, true))
 | ||||
| +					 dev->hw->soc->wdma_desc_size, true))
 | ||||
|  		return -ENOMEM; | ||||
|   | ||||
|  	wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE, | ||||
| @@ -1932,7 +1939,12 @@ void mtk_wed_add_hw(struct device_node *
 | ||||
|  	hw->irq = irq; | ||||
|  	hw->version = eth->soc->version; | ||||
|   | ||||
| -	if (mtk_wed_is_v1(hw)) {
 | ||||
| +	switch (hw->version) {
 | ||||
| +	case 2:
 | ||||
| +		hw->soc = &mt7986_data;
 | ||||
| +		break;
 | ||||
| +	default:
 | ||||
| +	case 1:
 | ||||
|  		hw->mirror = syscon_regmap_lookup_by_phandle(eth_np, | ||||
|  				"mediatek,pcie-mirror"); | ||||
|  		hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np, | ||||
| @@ -1946,6 +1958,8 @@ void mtk_wed_add_hw(struct device_node *
 | ||||
|  			regmap_write(hw->mirror, 0, 0); | ||||
|  			regmap_write(hw->mirror, 4, 0); | ||||
|  		} | ||||
| +		hw->soc = &mt7622_data;
 | ||||
| +		break;
 | ||||
|  	} | ||||
|   | ||||
|  	mtk_wed_hw_add_debugfs(hw); | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.h
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.h
 | ||||
| @@ -12,7 +12,18 @@
 | ||||
|  struct mtk_eth; | ||||
|  struct mtk_wed_wo; | ||||
|   | ||||
| +struct mtk_wed_soc_data {
 | ||||
| +	struct {
 | ||||
| +		u32 tx_bm_tkid;
 | ||||
| +		u32 wpdma_rx_ring0;
 | ||||
| +		u32 reset_idx_tx_mask;
 | ||||
| +		u32 reset_idx_rx_mask;
 | ||||
| +	} regmap;
 | ||||
| +	u32 wdma_desc_size;
 | ||||
| +};
 | ||||
| +
 | ||||
|  struct mtk_wed_hw { | ||||
| +	const struct mtk_wed_soc_data *soc;
 | ||||
|  	struct device_node *node; | ||||
|  	struct mtk_eth *eth; | ||||
|  	struct regmap *regs; | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
 | ||||
| @@ -100,8 +100,6 @@ struct mtk_wdma_desc {
 | ||||
|   | ||||
|  #define MTK_WED_TX_BM_BASE				0x084 | ||||
|   | ||||
| -#define MTK_WED_TX_BM_TKID				0x088
 | ||||
| -#define MTK_WED_TX_BM_TKID_V2				0x0c8
 | ||||
|  #define MTK_WED_TX_BM_TKID_START			GENMASK(15, 0) | ||||
|  #define MTK_WED_TX_BM_TKID_END				GENMASK(31, 16) | ||||
|   | ||||
| @@ -160,9 +158,6 @@ struct mtk_wdma_desc {
 | ||||
|  #define MTK_WED_GLO_CFG_RX_2B_OFFSET			BIT(31) | ||||
|   | ||||
|  #define MTK_WED_RESET_IDX				0x20c | ||||
| -#define MTK_WED_RESET_IDX_TX				GENMASK(3, 0)
 | ||||
| -#define MTK_WED_RESET_IDX_RX				GENMASK(17, 16)
 | ||||
| -#define MTK_WED_RESET_IDX_RX_V2				GENMASK(7, 6)
 | ||||
|  #define MTK_WED_RESET_WPDMA_IDX_RX			GENMASK(31, 30) | ||||
|   | ||||
|  #define MTK_WED_TX_MIB(_n)				(0x2a0 + (_n) * 4) | ||||
| @@ -286,7 +281,6 @@ struct mtk_wdma_desc {
 | ||||
|  #define MTK_WED_WPDMA_RX_D_RST_DRV_IDX			GENMASK(25, 24) | ||||
|   | ||||
|  #define MTK_WED_WPDMA_RX_GLO_CFG			0x76c | ||||
| -#define MTK_WED_WPDMA_RX_RING				0x770
 | ||||
|   | ||||
|  #define MTK_WED_WPDMA_RX_D_MIB(_n)			(0x774 + (_n) * 4) | ||||
|  #define MTK_WED_WPDMA_RX_D_PROCESSED_MIB(_n)		(0x784 + (_n) * 4) | ||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -0,0 +1,95 @@ | |||
| From: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Date: Mon, 18 Sep 2023 12:29:14 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: refactor mtk_wed_check_wfdma_rx_fill | ||||
|  routine | ||||
| 
 | ||||
| Refactor mtk_wed_check_wfdma_rx_fill() in order to be reused adding HW | ||||
| receive offload support for MT7988 SoC. | ||||
| 
 | ||||
| Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| @@ -586,22 +586,15 @@ mtk_wed_set_512_support(struct mtk_wed_d
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| -#define MTK_WFMDA_RX_DMA_EN	BIT(2)
 | ||||
| -static void
 | ||||
| -mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev, int idx)
 | ||||
| +static int
 | ||||
| +mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev,
 | ||||
| +			    struct mtk_wed_ring *ring)
 | ||||
|  { | ||||
| -	u32 val;
 | ||||
|  	int i; | ||||
|   | ||||
| -	if (!(dev->rx_ring[idx].flags & MTK_WED_RING_CONFIGURED))
 | ||||
| -		return; /* queue is not configured by mt76 */
 | ||||
| -
 | ||||
|  	for (i = 0; i < 3; i++) { | ||||
| -		u32 cur_idx;
 | ||||
| +		u32 cur_idx = readl(ring->wpdma + MTK_WED_RING_OFS_CPU_IDX);
 | ||||
|   | ||||
| -		cur_idx = wed_r32(dev,
 | ||||
| -				  MTK_WED_WPDMA_RING_RX_DATA(idx) +
 | ||||
| -				  MTK_WED_RING_OFS_CPU_IDX);
 | ||||
|  		if (cur_idx == MTK_WED_RX_RING_SIZE - 1) | ||||
|  			break; | ||||
|   | ||||
| @@ -610,12 +603,10 @@ mtk_wed_check_wfdma_rx_fill(struct mtk_w
 | ||||
|   | ||||
|  	if (i == 3) { | ||||
|  		dev_err(dev->hw->dev, "rx dma enable failed\n"); | ||||
| -		return;
 | ||||
| +		return -ETIMEDOUT;
 | ||||
|  	} | ||||
|   | ||||
| -	val = wifi_r32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base) |
 | ||||
| -	      MTK_WFMDA_RX_DMA_EN;
 | ||||
| -	wifi_w32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base, val);
 | ||||
| +	return 0;
 | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -1546,6 +1537,7 @@ mtk_wed_configure_irq(struct mtk_wed_dev
 | ||||
|  	wed_w32(dev, MTK_WED_INT_MASK, irq_mask); | ||||
|  } | ||||
|   | ||||
| +#define MTK_WFMDA_RX_DMA_EN	BIT(2)
 | ||||
|  static void | ||||
|  mtk_wed_dma_enable(struct mtk_wed_device *dev) | ||||
|  { | ||||
| @@ -1633,8 +1625,26 @@ mtk_wed_dma_enable(struct mtk_wed_device
 | ||||
|  		wdma_set(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN); | ||||
|  	} | ||||
|   | ||||
| -	for (i = 0; i < MTK_WED_RX_QUEUES; i++)
 | ||||
| -		mtk_wed_check_wfdma_rx_fill(dev, i);
 | ||||
| +	for (i = 0; i < MTK_WED_RX_QUEUES; i++) {
 | ||||
| +		struct mtk_wed_ring *ring = &dev->rx_ring[i];
 | ||||
| +		u32 val;
 | ||||
| +
 | ||||
| +		if (!(ring->flags & MTK_WED_RING_CONFIGURED))
 | ||||
| +			continue; /* queue is not configured by mt76 */
 | ||||
| +
 | ||||
| +		if (mtk_wed_check_wfdma_rx_fill(dev, ring)) {
 | ||||
| +			dev_err(dev->hw->dev,
 | ||||
| +				"rx_ring(%d) dma enable failed\n", i);
 | ||||
| +			continue;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		val = wifi_r32(dev,
 | ||||
| +			       dev->wlan.wpdma_rx_glo -
 | ||||
| +			       dev->wlan.phy_base) | MTK_WFMDA_RX_DMA_EN;
 | ||||
| +		wifi_w32(dev,
 | ||||
| +			 dev->wlan.wpdma_rx_glo - dev->wlan.phy_base,
 | ||||
| +			 val);
 | ||||
| +	}
 | ||||
|  } | ||||
|   | ||||
|  static void | ||||
|  | @ -0,0 +1,465 @@ | |||
| From: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 12:29:15 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: introduce partial AMSDU offload | ||||
|  support for MT7988 | ||||
| 
 | ||||
| Introduce partial AMSDU offload support for MT7988 SoC in order to merge | ||||
| in hw packets belonging to the same AMSDU before passing them to the | ||||
| WLAN nic. | ||||
| 
 | ||||
| Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
 | ||||
| @@ -439,7 +439,8 @@ int mtk_foe_entry_set_pppoe(struct mtk_e
 | ||||
|  } | ||||
|   | ||||
|  int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry, | ||||
| -			   int wdma_idx, int txq, int bss, int wcid)
 | ||||
| +			   int wdma_idx, int txq, int bss, int wcid,
 | ||||
| +			   bool amsdu_en)
 | ||||
|  { | ||||
|  	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry); | ||||
|  	u32 *ib2 = mtk_foe_entry_ib2(eth, entry); | ||||
| @@ -451,6 +452,7 @@ int mtk_foe_entry_set_wdma(struct mtk_et
 | ||||
|  			 MTK_FOE_IB2_WDMA_WINFO_V2; | ||||
|  		l2->w3info = FIELD_PREP(MTK_FOE_WINFO_WCID_V3, wcid) | | ||||
|  			     FIELD_PREP(MTK_FOE_WINFO_BSS_V3, bss); | ||||
| +		l2->amsdu = FIELD_PREP(MTK_FOE_WINFO_AMSDU_EN, amsdu_en);
 | ||||
|  		break; | ||||
|  	case 2: | ||||
|  		*ib2 &= ~MTK_FOE_IB2_PORT_MG_V2; | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.h
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
 | ||||
| @@ -88,13 +88,13 @@ enum {
 | ||||
|  #define MTK_FOE_WINFO_BSS_V3		GENMASK(23, 16) | ||||
|  #define MTK_FOE_WINFO_WCID_V3		GENMASK(15, 0) | ||||
|   | ||||
| -#define MTK_FOE_WINFO_PAO_USR_INFO	GENMASK(15, 0)
 | ||||
| -#define MTK_FOE_WINFO_PAO_TID		GENMASK(19, 16)
 | ||||
| -#define MTK_FOE_WINFO_PAO_IS_FIXEDRATE	BIT(20)
 | ||||
| -#define MTK_FOE_WINFO_PAO_IS_PRIOR	BIT(21)
 | ||||
| -#define MTK_FOE_WINFO_PAO_IS_SP		BIT(22)
 | ||||
| -#define MTK_FOE_WINFO_PAO_HF		BIT(23)
 | ||||
| -#define MTK_FOE_WINFO_PAO_AMSDU_EN	BIT(24)
 | ||||
| +#define MTK_FOE_WINFO_AMSDU_USR_INFO	GENMASK(15, 0)
 | ||||
| +#define MTK_FOE_WINFO_AMSDU_TID		GENMASK(19, 16)
 | ||||
| +#define MTK_FOE_WINFO_AMSDU_IS_FIXEDRATE	BIT(20)
 | ||||
| +#define MTK_FOE_WINFO_AMSDU_IS_PRIOR	BIT(21)
 | ||||
| +#define MTK_FOE_WINFO_AMSDU_IS_SP	BIT(22)
 | ||||
| +#define MTK_FOE_WINFO_AMSDU_HF		BIT(23)
 | ||||
| +#define MTK_FOE_WINFO_AMSDU_EN		BIT(24)
 | ||||
|   | ||||
|  enum { | ||||
|  	MTK_FOE_STATE_INVALID, | ||||
| @@ -123,7 +123,7 @@ struct mtk_foe_mac_info {
 | ||||
|   | ||||
|  	/* netsys_v3 */ | ||||
|  	u32 w3info; | ||||
| -	u32 wpao;
 | ||||
| +	u32 amsdu;
 | ||||
|  }; | ||||
|   | ||||
|  /* software-only entry type */ | ||||
| @@ -394,7 +394,8 @@ int mtk_foe_entry_set_vlan(struct mtk_et
 | ||||
|  int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry, | ||||
|  			    int sid); | ||||
|  int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry, | ||||
| -			   int wdma_idx, int txq, int bss, int wcid);
 | ||||
| +			   int wdma_idx, int txq, int bss, int wcid,
 | ||||
| +			   bool amsdu_en);
 | ||||
|  int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry, | ||||
|  			    unsigned int queue); | ||||
|  int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 | ||||
| @@ -111,6 +111,7 @@ mtk_flow_get_wdma_info(struct net_device
 | ||||
|  	info->queue = path->mtk_wdma.queue; | ||||
|  	info->bss = path->mtk_wdma.bss; | ||||
|  	info->wcid = path->mtk_wdma.wcid; | ||||
| +	info->amsdu = path->mtk_wdma.amsdu;
 | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -192,7 +193,7 @@ mtk_flow_set_output_device(struct mtk_et
 | ||||
|   | ||||
|  	if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) { | ||||
|  		mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue, | ||||
| -				       info.bss, info.wcid);
 | ||||
| +				       info.bss, info.wcid, info.amsdu);
 | ||||
|  		if (mtk_is_netsys_v2_or_greater(eth)) { | ||||
|  			switch (info.wdma_idx) { | ||||
|  			case 0: | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| @@ -30,6 +30,8 @@
 | ||||
|  #define MTK_WED_RX_PAGE_BUF_PER_PAGE	(PAGE_SIZE / 128) | ||||
|  #define MTK_WED_RX_RING_SIZE		1536 | ||||
|  #define MTK_WED_RX_PG_BM_CNT		8192 | ||||
| +#define MTK_WED_AMSDU_BUF_SIZE		(PAGE_SIZE << 4)
 | ||||
| +#define MTK_WED_AMSDU_NPAGES		32
 | ||||
|   | ||||
|  #define MTK_WED_TX_RING_SIZE		2048 | ||||
|  #define MTK_WED_WDMA_RING_SIZE		1024 | ||||
| @@ -173,6 +175,23 @@ mtk_wdma_rx_reset(struct mtk_wed_device
 | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| +static u32
 | ||||
| +mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
 | ||||
| +{
 | ||||
| +	return !!(wed_r32(dev, reg) & mask);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int
 | ||||
| +mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
 | ||||
| +{
 | ||||
| +	int sleep = 15000;
 | ||||
| +	int timeout = 100 * sleep;
 | ||||
| +	u32 val;
 | ||||
| +
 | ||||
| +	return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep,
 | ||||
| +				 timeout, false, dev, reg, mask);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static void | ||||
|  mtk_wdma_tx_reset(struct mtk_wed_device *dev) | ||||
|  { | ||||
| @@ -336,6 +355,118 @@ out:
 | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| +mtk_wed_amsdu_buffer_alloc(struct mtk_wed_device *dev)
 | ||||
| +{
 | ||||
| +	struct mtk_wed_hw *hw = dev->hw;
 | ||||
| +	struct mtk_wed_amsdu *wed_amsdu;
 | ||||
| +	int i;
 | ||||
| +
 | ||||
| +	if (!mtk_wed_is_v3_or_greater(hw))
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
| +	wed_amsdu = devm_kcalloc(hw->dev, MTK_WED_AMSDU_NPAGES,
 | ||||
| +				 sizeof(*wed_amsdu), GFP_KERNEL);
 | ||||
| +	if (!wed_amsdu)
 | ||||
| +		return -ENOMEM;
 | ||||
| +
 | ||||
| +	for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) {
 | ||||
| +		void *ptr;
 | ||||
| +
 | ||||
| +		/* each segment is 64K */
 | ||||
| +		ptr = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN |
 | ||||
| +					       __GFP_ZERO | __GFP_COMP |
 | ||||
| +					       GFP_DMA32,
 | ||||
| +					       get_order(MTK_WED_AMSDU_BUF_SIZE));
 | ||||
| +		if (!ptr)
 | ||||
| +			goto error;
 | ||||
| +
 | ||||
| +		wed_amsdu[i].txd = ptr;
 | ||||
| +		wed_amsdu[i].txd_phy = dma_map_single(hw->dev, ptr,
 | ||||
| +						      MTK_WED_AMSDU_BUF_SIZE,
 | ||||
| +						      DMA_TO_DEVICE);
 | ||||
| +		if (dma_mapping_error(hw->dev, wed_amsdu[i].txd_phy))
 | ||||
| +			goto error;
 | ||||
| +	}
 | ||||
| +	dev->hw->wed_amsdu = wed_amsdu;
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +
 | ||||
| +error:
 | ||||
| +	for (i--; i >= 0; i--)
 | ||||
| +		dma_unmap_single(hw->dev, wed_amsdu[i].txd_phy,
 | ||||
| +				 MTK_WED_AMSDU_BUF_SIZE, DMA_TO_DEVICE);
 | ||||
| +	return -ENOMEM;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void
 | ||||
| +mtk_wed_amsdu_free_buffer(struct mtk_wed_device *dev)
 | ||||
| +{
 | ||||
| +	struct mtk_wed_amsdu *wed_amsdu = dev->hw->wed_amsdu;
 | ||||
| +	int i;
 | ||||
| +
 | ||||
| +	if (!wed_amsdu)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) {
 | ||||
| +		dma_unmap_single(dev->hw->dev, wed_amsdu[i].txd_phy,
 | ||||
| +				 MTK_WED_AMSDU_BUF_SIZE, DMA_TO_DEVICE);
 | ||||
| +		free_pages((unsigned long)wed_amsdu[i].txd,
 | ||||
| +			   get_order(MTK_WED_AMSDU_BUF_SIZE));
 | ||||
| +	}
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int
 | ||||
| +mtk_wed_amsdu_init(struct mtk_wed_device *dev)
 | ||||
| +{
 | ||||
| +	struct mtk_wed_amsdu *wed_amsdu = dev->hw->wed_amsdu;
 | ||||
| +	int i, ret;
 | ||||
| +
 | ||||
| +	if (!wed_amsdu)
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
| +	for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++)
 | ||||
| +		wed_w32(dev, MTK_WED_AMSDU_HIFTXD_BASE_L(i),
 | ||||
| +			wed_amsdu[i].txd_phy);
 | ||||
| +
 | ||||
| +	/* init all sta parameter */
 | ||||
| +	wed_w32(dev, MTK_WED_AMSDU_STA_INFO_INIT, MTK_WED_AMSDU_STA_RMVL |
 | ||||
| +		MTK_WED_AMSDU_STA_WTBL_HDRT_MODE |
 | ||||
| +		FIELD_PREP(MTK_WED_AMSDU_STA_MAX_AMSDU_LEN,
 | ||||
| +			   dev->wlan.amsdu_max_len >> 8) |
 | ||||
| +		FIELD_PREP(MTK_WED_AMSDU_STA_MAX_AMSDU_NUM,
 | ||||
| +			   dev->wlan.amsdu_max_subframes));
 | ||||
| +
 | ||||
| +	wed_w32(dev, MTK_WED_AMSDU_STA_INFO, MTK_WED_AMSDU_STA_INFO_DO_INIT);
 | ||||
| +
 | ||||
| +	ret = mtk_wed_poll_busy(dev, MTK_WED_AMSDU_STA_INFO,
 | ||||
| +				MTK_WED_AMSDU_STA_INFO_DO_INIT);
 | ||||
| +	if (ret) {
 | ||||
| +		dev_err(dev->hw->dev, "amsdu initialization failed\n");
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* init partial amsdu offload txd src */
 | ||||
| +	wed_set(dev, MTK_WED_AMSDU_HIFTXD_CFG,
 | ||||
| +		FIELD_PREP(MTK_WED_AMSDU_HIFTXD_SRC, dev->hw->index));
 | ||||
| +
 | ||||
| +	/* init qmem */
 | ||||
| +	wed_set(dev, MTK_WED_AMSDU_PSE, MTK_WED_AMSDU_PSE_RESET);
 | ||||
| +	ret = mtk_wed_poll_busy(dev, MTK_WED_MON_AMSDU_QMEM_STS1, BIT(29));
 | ||||
| +	if (ret) {
 | ||||
| +		pr_info("%s: amsdu qmem initialization failed\n", __func__);
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* eagle E1 PCIE1 tx ring 22 flow control issue */
 | ||||
| +	if (dev->wlan.id == 0x7991)
 | ||||
| +		wed_clr(dev, MTK_WED_AMSDU_FIFO, MTK_WED_AMSDU_IS_PRIOR0_RING);
 | ||||
| +
 | ||||
| +	wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN);
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int
 | ||||
|  mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev) | ||||
|  { | ||||
|  	u32 desc_size = dev->hw->soc->tx_ring_desc_size; | ||||
| @@ -709,6 +840,7 @@ __mtk_wed_detach(struct mtk_wed_device *
 | ||||
|   | ||||
|  	mtk_wdma_rx_reset(dev); | ||||
|  	mtk_wed_reset(dev, MTK_WED_RESET_WED); | ||||
| +	mtk_wed_amsdu_free_buffer(dev);
 | ||||
|  	mtk_wed_free_tx_buffer(dev); | ||||
|  	mtk_wed_free_tx_rings(dev); | ||||
|   | ||||
| @@ -1129,23 +1261,6 @@ mtk_wed_ring_reset(struct mtk_wed_ring *
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| -static u32
 | ||||
| -mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
 | ||||
| -{
 | ||||
| -	return !!(wed_r32(dev, reg) & mask);
 | ||||
| -}
 | ||||
| -
 | ||||
| -static int
 | ||||
| -mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
 | ||||
| -{
 | ||||
| -	int sleep = 15000;
 | ||||
| -	int timeout = 100 * sleep;
 | ||||
| -	u32 val;
 | ||||
| -
 | ||||
| -	return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep,
 | ||||
| -				 timeout, false, dev, reg, mask);
 | ||||
| -}
 | ||||
| -
 | ||||
|  static int | ||||
|  mtk_wed_rx_reset(struct mtk_wed_device *dev) | ||||
|  { | ||||
| @@ -1692,6 +1807,7 @@ mtk_wed_start(struct mtk_wed_device *dev
 | ||||
|  	} | ||||
|   | ||||
|  	mtk_wed_set_512_support(dev, dev->wlan.wcid_512); | ||||
| +	mtk_wed_amsdu_init(dev);
 | ||||
|   | ||||
|  	mtk_wed_dma_enable(dev); | ||||
|  	dev->running = true; | ||||
| @@ -1748,6 +1864,10 @@ mtk_wed_attach(struct mtk_wed_device *de
 | ||||
|  	if (ret) | ||||
|  		goto out; | ||||
|   | ||||
| +	ret = mtk_wed_amsdu_buffer_alloc(dev);
 | ||||
| +	if (ret)
 | ||||
| +		goto out;
 | ||||
| +
 | ||||
|  	if (mtk_wed_get_rx_capa(dev)) { | ||||
|  		ret = mtk_wed_rro_alloc(dev); | ||||
|  		if (ret) | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.h
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.h
 | ||||
| @@ -25,6 +25,11 @@ struct mtk_wed_soc_data {
 | ||||
|  	u32 wdma_desc_size; | ||||
|  }; | ||||
|   | ||||
| +struct mtk_wed_amsdu {
 | ||||
| +	void *txd;
 | ||||
| +	dma_addr_t txd_phy;
 | ||||
| +};
 | ||||
| +
 | ||||
|  struct mtk_wed_hw { | ||||
|  	const struct mtk_wed_soc_data *soc; | ||||
|  	struct device_node *node; | ||||
| @@ -38,6 +43,7 @@ struct mtk_wed_hw {
 | ||||
|  	struct dentry *debugfs_dir; | ||||
|  	struct mtk_wed_device *wed_dev; | ||||
|  	struct mtk_wed_wo *wed_wo; | ||||
| +	struct mtk_wed_amsdu *wed_amsdu;
 | ||||
|  	u32 pcie_base; | ||||
|  	u32 debugfs_reg; | ||||
|  	u32 num_flows; | ||||
| @@ -52,6 +58,7 @@ struct mtk_wdma_info {
 | ||||
|  	u8 queue; | ||||
|  	u16 wcid; | ||||
|  	u8 bss; | ||||
| +	u8 amsdu;
 | ||||
|  }; | ||||
|   | ||||
|  #ifdef CONFIG_NET_MEDIATEK_SOC_WED | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
 | ||||
| @@ -672,6 +672,82 @@ struct mtk_wdma_desc {
 | ||||
|  #define MTK_WED_WOCPU_VIEW_MIOD_BASE			0x8000 | ||||
|  #define MTK_WED_PCIE_INT_MASK				0x0 | ||||
|   | ||||
| +#define MTK_WED_AMSDU_FIFO				0x1800
 | ||||
| +#define MTK_WED_AMSDU_IS_PRIOR0_RING			BIT(10)
 | ||||
| +
 | ||||
| +#define MTK_WED_AMSDU_STA_INFO				0x01810
 | ||||
| +#define MTK_WED_AMSDU_STA_INFO_DO_INIT			BIT(0)
 | ||||
| +#define MTK_WED_AMSDU_STA_INFO_SET_INIT			BIT(1)
 | ||||
| +
 | ||||
| +#define MTK_WED_AMSDU_STA_INFO_INIT			0x01814
 | ||||
| +#define MTK_WED_AMSDU_STA_WTBL_HDRT_MODE		BIT(0)
 | ||||
| +#define MTK_WED_AMSDU_STA_RMVL				BIT(1)
 | ||||
| +#define MTK_WED_AMSDU_STA_MAX_AMSDU_LEN			GENMASK(7, 2)
 | ||||
| +#define MTK_WED_AMSDU_STA_MAX_AMSDU_NUM			GENMASK(11, 8)
 | ||||
| +
 | ||||
| +#define MTK_WED_AMSDU_HIFTXD_BASE_L(_n)			(0x1980 + (_n) * 0x4)
 | ||||
| +
 | ||||
| +#define MTK_WED_AMSDU_PSE				0x1910
 | ||||
| +#define MTK_WED_AMSDU_PSE_RESET				BIT(16)
 | ||||
| +
 | ||||
| +#define MTK_WED_AMSDU_HIFTXD_CFG			0x1968
 | ||||
| +#define MTK_WED_AMSDU_HIFTXD_SRC			GENMASK(16, 15)
 | ||||
| +
 | ||||
| +#define MTK_WED_MON_AMSDU_FIFO_DMAD			0x1a34
 | ||||
| +
 | ||||
| +#define MTK_WED_MON_AMSDU_ENG_DMAD(_n)			(0x1a80 + (_n) * 0x50)
 | ||||
| +#define MTK_WED_MON_AMSDU_ENG_QFPL(_n)			(0x1a84 + (_n) * 0x50)
 | ||||
| +#define MTK_WED_MON_AMSDU_ENG_QENI(_n)			(0x1a88 + (_n) * 0x50)
 | ||||
| +#define MTK_WED_MON_AMSDU_ENG_QENO(_n)			(0x1a8c + (_n) * 0x50)
 | ||||
| +#define MTK_WED_MON_AMSDU_ENG_MERG(_n)			(0x1a90 + (_n) * 0x50)
 | ||||
| +
 | ||||
| +#define MTK_WED_MON_AMSDU_ENG_CNT8(_n)			(0x1a94 + (_n) * 0x50)
 | ||||
| +#define MTK_WED_AMSDU_ENG_MAX_QGPP_CNT			GENMASK(10, 0)
 | ||||
| +#define MTK_WED_AMSDU_ENG_MAX_PL_CNT			GENMASK(27, 16)
 | ||||
| +
 | ||||
| +#define MTK_WED_MON_AMSDU_ENG_CNT9(_n)			(0x1a98 + (_n) * 0x50)
 | ||||
| +#define MTK_WED_AMSDU_ENG_CUR_ENTRY			GENMASK(10, 0)
 | ||||
| +#define MTK_WED_AMSDU_ENG_MAX_BUF_MERGED		GENMASK(20, 16)
 | ||||
| +#define MTK_WED_AMSDU_ENG_MAX_MSDU_MERGED		GENMASK(28, 24)
 | ||||
| +
 | ||||
| +#define MTK_WED_MON_AMSDU_QMEM_STS1			0x1e04
 | ||||
| +
 | ||||
| +#define MTK_WED_MON_AMSDU_QMEM_CNT(_n)			(0x1e0c + (_n) * 0x4)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_FQ_CNT			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_SP_QCNT			GENMASK(11, 0)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID0_QCNT			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID1_QCNT			GENMASK(11, 0)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID2_QCNT			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID3_QCNT			GENMASK(11, 0)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID4_QCNT			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID5_QCNT			GENMASK(11, 0)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID6_QCNT			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID7_QCNT			GENMASK(11, 0)
 | ||||
| +
 | ||||
| +#define MTK_WED_MON_AMSDU_QMEM_PTR(_n)			(0x1e20 + (_n) * 0x4)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_FQ_HEAD			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_SP_QHEAD			GENMASK(11, 0)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID0_QHEAD			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID1_QHEAD			GENMASK(11, 0)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID2_QHEAD			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID3_QHEAD			GENMASK(11, 0)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID4_QHEAD			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID5_QHEAD			GENMASK(11, 0)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID6_QHEAD			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID7_QHEAD			GENMASK(11, 0)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_FQ_TAIL			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_SP_QTAIL			GENMASK(11, 0)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID0_QTAIL			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID1_QTAIL			GENMASK(11, 0)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID2_QTAIL			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID3_QTAIL			GENMASK(11, 0)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID4_QTAIL			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID5_QTAIL			GENMASK(11, 0)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID6_QTAIL			GENMASK(27, 16)
 | ||||
| +#define MTK_WED_AMSDU_QMEM_TID7_QTAIL			GENMASK(11, 0)
 | ||||
| +
 | ||||
| +#define MTK_WED_MON_AMSDU_HIFTXD_FETCH_MSDU(_n)		(0x1ec4 + (_n) * 0x4)
 | ||||
| +
 | ||||
|  #define MTK_WED_PCIE_BASE			0x11280000 | ||||
|  #define MTK_WED_PCIE_BASE0			0x11300000 | ||||
|  #define MTK_WED_PCIE_BASE1			0x11310000 | ||||
| --- a/include/linux/netdevice.h
 | ||||
| +++ b/include/linux/netdevice.h
 | ||||
| @@ -917,6 +917,7 @@ struct net_device_path {
 | ||||
|  			u8 queue; | ||||
|  			u16 wcid; | ||||
|  			u8 bss; | ||||
| +			u8 amsdu;
 | ||||
|  		} mtk_wdma; | ||||
|  	}; | ||||
|  }; | ||||
| --- a/include/linux/soc/mediatek/mtk_wed.h
 | ||||
| +++ b/include/linux/soc/mediatek/mtk_wed.h
 | ||||
| @@ -128,6 +128,7 @@ struct mtk_wed_device {
 | ||||
|  		enum mtk_wed_bus_tye bus_type; | ||||
|  		void __iomem *base; | ||||
|  		u32 phy_base; | ||||
| +		u32 id;
 | ||||
|   | ||||
|  		u32 wpdma_phys; | ||||
|  		u32 wpdma_int; | ||||
| @@ -146,10 +147,12 @@ struct mtk_wed_device {
 | ||||
|  		unsigned int rx_nbuf; | ||||
|  		unsigned int rx_npkt; | ||||
|  		unsigned int rx_size; | ||||
| +		unsigned int amsdu_max_len;
 | ||||
|   | ||||
|  		u8 tx_tbit[MTK_WED_TX_QUEUES]; | ||||
|  		u8 rx_tbit[MTK_WED_RX_QUEUES]; | ||||
|  		u8 txfree_tbit; | ||||
| +		u8 amsdu_max_subframes;
 | ||||
|   | ||||
|  		u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id); | ||||
|  		int (*offload_enable)(struct mtk_wed_device *wed); | ||||
| @@ -223,6 +226,15 @@ static inline bool mtk_wed_get_rx_capa(s
 | ||||
|  #else | ||||
|  	return false; | ||||
|  #endif | ||||
| +}
 | ||||
| +
 | ||||
| +static inline bool mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev)
 | ||||
| +{
 | ||||
| +#ifdef CONFIG_NET_MEDIATEK_SOC_WED
 | ||||
| +	return dev->version == 3;
 | ||||
| +#else
 | ||||
| +	return false;
 | ||||
| +#endif
 | ||||
|  } | ||||
|   | ||||
|  #ifdef CONFIG_NET_MEDIATEK_SOC_WED | ||||
|  | @ -0,0 +1,483 @@ | |||
| From: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 12:29:16 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: introduce hw_rro support for MT7988 | ||||
| 
 | ||||
| MT7988 SoC support 802.11 receive reordering offload in hw while | ||||
| MT7986 SoC implements it through the firmware running on the mcu. | ||||
| 
 | ||||
| Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| @@ -27,7 +27,7 @@
 | ||||
|  #define MTK_WED_BUF_SIZE		2048 | ||||
|  #define MTK_WED_PAGE_BUF_SIZE		128 | ||||
|  #define MTK_WED_BUF_PER_PAGE		(PAGE_SIZE / 2048) | ||||
| -#define MTK_WED_RX_PAGE_BUF_PER_PAGE	(PAGE_SIZE / 128)
 | ||||
| +#define MTK_WED_RX_BUF_PER_PAGE		(PAGE_SIZE / MTK_WED_PAGE_BUF_SIZE)
 | ||||
|  #define MTK_WED_RX_RING_SIZE		1536 | ||||
|  #define MTK_WED_RX_PG_BM_CNT		8192 | ||||
|  #define MTK_WED_AMSDU_BUF_SIZE		(PAGE_SIZE << 4) | ||||
| @@ -597,6 +597,68 @@ free_pagelist:
 | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| +mtk_wed_hwrro_buffer_alloc(struct mtk_wed_device *dev)
 | ||||
| +{
 | ||||
| +	int n_pages = MTK_WED_RX_PG_BM_CNT / MTK_WED_RX_BUF_PER_PAGE;
 | ||||
| +	struct mtk_wed_buf *page_list;
 | ||||
| +	struct mtk_wed_bm_desc *desc;
 | ||||
| +	dma_addr_t desc_phys;
 | ||||
| +	int i, page_idx = 0;
 | ||||
| +
 | ||||
| +	if (!dev->wlan.hw_rro)
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
| +	page_list = kcalloc(n_pages, sizeof(*page_list), GFP_KERNEL);
 | ||||
| +	if (!page_list)
 | ||||
| +		return -ENOMEM;
 | ||||
| +
 | ||||
| +	dev->hw_rro.size = dev->wlan.rx_nbuf & ~(MTK_WED_BUF_PER_PAGE - 1);
 | ||||
| +	dev->hw_rro.pages = page_list;
 | ||||
| +	desc = dma_alloc_coherent(dev->hw->dev,
 | ||||
| +				  dev->wlan.rx_nbuf * sizeof(*desc),
 | ||||
| +				  &desc_phys, GFP_KERNEL);
 | ||||
| +	if (!desc)
 | ||||
| +		return -ENOMEM;
 | ||||
| +
 | ||||
| +	dev->hw_rro.desc = desc;
 | ||||
| +	dev->hw_rro.desc_phys = desc_phys;
 | ||||
| +
 | ||||
| +	for (i = 0; i < MTK_WED_RX_PG_BM_CNT; i += MTK_WED_RX_BUF_PER_PAGE) {
 | ||||
| +		dma_addr_t page_phys, buf_phys;
 | ||||
| +		struct page *page;
 | ||||
| +		int s;
 | ||||
| +
 | ||||
| +		page = __dev_alloc_page(GFP_KERNEL);
 | ||||
| +		if (!page)
 | ||||
| +			return -ENOMEM;
 | ||||
| +
 | ||||
| +		page_phys = dma_map_page(dev->hw->dev, page, 0, PAGE_SIZE,
 | ||||
| +					 DMA_BIDIRECTIONAL);
 | ||||
| +		if (dma_mapping_error(dev->hw->dev, page_phys)) {
 | ||||
| +			__free_page(page);
 | ||||
| +			return -ENOMEM;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		page_list[page_idx].p = page;
 | ||||
| +		page_list[page_idx++].phy_addr = page_phys;
 | ||||
| +		dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE,
 | ||||
| +					DMA_BIDIRECTIONAL);
 | ||||
| +
 | ||||
| +		buf_phys = page_phys;
 | ||||
| +		for (s = 0; s < MTK_WED_RX_BUF_PER_PAGE; s++) {
 | ||||
| +			desc->buf0 = cpu_to_le32(buf_phys);
 | ||||
| +			buf_phys += MTK_WED_PAGE_BUF_SIZE;
 | ||||
| +			desc++;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		dma_sync_single_for_device(dev->hw->dev, page_phys, PAGE_SIZE,
 | ||||
| +					   DMA_BIDIRECTIONAL);
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int
 | ||||
|  mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev) | ||||
|  { | ||||
|  	struct mtk_wed_bm_desc *desc; | ||||
| @@ -613,7 +675,42 @@ mtk_wed_rx_buffer_alloc(struct mtk_wed_d
 | ||||
|  	dev->rx_buf_ring.desc_phys = desc_phys; | ||||
|  	dev->wlan.init_rx_buf(dev, dev->wlan.rx_npkt); | ||||
|   | ||||
| -	return 0;
 | ||||
| +	return mtk_wed_hwrro_buffer_alloc(dev);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void
 | ||||
| +mtk_wed_hwrro_free_buffer(struct mtk_wed_device *dev)
 | ||||
| +{
 | ||||
| +	struct mtk_wed_buf *page_list = dev->hw_rro.pages;
 | ||||
| +	struct mtk_wed_bm_desc *desc = dev->hw_rro.desc;
 | ||||
| +	int i, page_idx = 0;
 | ||||
| +
 | ||||
| +	if (!dev->wlan.hw_rro)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	if (!page_list)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	if (!desc)
 | ||||
| +		goto free_pagelist;
 | ||||
| +
 | ||||
| +	for (i = 0; i < MTK_WED_RX_PG_BM_CNT; i += MTK_WED_RX_BUF_PER_PAGE) {
 | ||||
| +		dma_addr_t buf_addr = page_list[page_idx].phy_addr;
 | ||||
| +		void *page = page_list[page_idx++].p;
 | ||||
| +
 | ||||
| +		if (!page)
 | ||||
| +			break;
 | ||||
| +
 | ||||
| +		dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE,
 | ||||
| +			       DMA_BIDIRECTIONAL);
 | ||||
| +		__free_page(page);
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	dma_free_coherent(dev->hw->dev, dev->hw_rro.size * sizeof(*desc),
 | ||||
| +			  desc, dev->hw_rro.desc_phys);
 | ||||
| +
 | ||||
| +free_pagelist:
 | ||||
| +	kfree(page_list);
 | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -627,6 +724,28 @@ mtk_wed_free_rx_buffer(struct mtk_wed_de
 | ||||
|  	dev->wlan.release_rx_buf(dev); | ||||
|  	dma_free_coherent(dev->hw->dev, dev->rx_buf_ring.size * sizeof(*desc), | ||||
|  			  desc, dev->rx_buf_ring.desc_phys); | ||||
| +
 | ||||
| +	mtk_wed_hwrro_free_buffer(dev);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void
 | ||||
| +mtk_wed_hwrro_init(struct mtk_wed_device *dev)
 | ||||
| +{
 | ||||
| +	if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	wed_set(dev, MTK_WED_RRO_PG_BM_RX_DMAM,
 | ||||
| +		FIELD_PREP(MTK_WED_RRO_PG_BM_RX_SDL0, 128));
 | ||||
| +
 | ||||
| +	wed_w32(dev, MTK_WED_RRO_PG_BM_BASE, dev->hw_rro.desc_phys);
 | ||||
| +
 | ||||
| +	wed_w32(dev, MTK_WED_RRO_PG_BM_INIT_PTR,
 | ||||
| +		MTK_WED_RRO_PG_BM_INIT_SW_TAIL_IDX |
 | ||||
| +		FIELD_PREP(MTK_WED_RRO_PG_BM_SW_TAIL_IDX,
 | ||||
| +			   MTK_WED_RX_PG_BM_CNT));
 | ||||
| +
 | ||||
| +	/* enable rx_page_bm to fetch dmad */
 | ||||
| +	wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_PG_BM_EN);
 | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -640,6 +759,8 @@ mtk_wed_rx_buffer_hw_init(struct mtk_wed
 | ||||
|  	wed_w32(dev, MTK_WED_RX_BM_DYN_ALLOC_TH, | ||||
|  		FIELD_PREP(MTK_WED_RX_BM_DYN_ALLOC_TH_H, 0xffff)); | ||||
|  	wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN); | ||||
| +
 | ||||
| +	mtk_wed_hwrro_init(dev);
 | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -935,6 +1056,8 @@ mtk_wed_bus_init(struct mtk_wed_device *
 | ||||
|  static void | ||||
|  mtk_wed_set_wpdma(struct mtk_wed_device *dev) | ||||
|  { | ||||
| +	int i;
 | ||||
| +
 | ||||
|  	if (mtk_wed_is_v1(dev->hw)) { | ||||
|  		wed_w32(dev, MTK_WED_WPDMA_CFG_BASE,  dev->wlan.wpdma_phys); | ||||
|  		return; | ||||
| @@ -952,6 +1075,15 @@ mtk_wed_set_wpdma(struct mtk_wed_device
 | ||||
|   | ||||
|  	wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo); | ||||
|  	wed_w32(dev, dev->hw->soc->regmap.wpdma_rx_ring0, dev->wlan.wpdma_rx); | ||||
| +
 | ||||
| +	if (!dev->wlan.hw_rro)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	wed_w32(dev, MTK_WED_RRO_RX_D_CFG(0), dev->wlan.wpdma_rx_rro[0]);
 | ||||
| +	wed_w32(dev, MTK_WED_RRO_RX_D_CFG(1), dev->wlan.wpdma_rx_rro[1]);
 | ||||
| +	for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++)
 | ||||
| +		wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING_CFG(i),
 | ||||
| +			dev->wlan.wpdma_rx_pg + i * 0x10);
 | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -1763,6 +1895,165 @@ mtk_wed_dma_enable(struct mtk_wed_device
 | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| +mtk_wed_start_hw_rro(struct mtk_wed_device *dev, u32 irq_mask, bool reset)
 | ||||
| +{
 | ||||
| +	int i;
 | ||||
| +
 | ||||
| +	wed_w32(dev, MTK_WED_WPDMA_INT_MASK, irq_mask);
 | ||||
| +	wed_w32(dev, MTK_WED_INT_MASK, irq_mask);
 | ||||
| +
 | ||||
| +	if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_MSDU_PG_DRV_CLR);
 | ||||
| +	wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
 | ||||
| +		MTK_WED_RRO_MSDU_PG_DRV_CLR);
 | ||||
| +
 | ||||
| +	wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RRO_RX,
 | ||||
| +		MTK_WED_WPDMA_INT_CTRL_RRO_RX0_EN |
 | ||||
| +		MTK_WED_WPDMA_INT_CTRL_RRO_RX0_CLR |
 | ||||
| +		MTK_WED_WPDMA_INT_CTRL_RRO_RX1_EN |
 | ||||
| +		MTK_WED_WPDMA_INT_CTRL_RRO_RX1_CLR |
 | ||||
| +		FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_RX0_DONE_TRIG,
 | ||||
| +			   dev->wlan.rro_rx_tbit[0]) |
 | ||||
| +		FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_RX1_DONE_TRIG,
 | ||||
| +			   dev->wlan.rro_rx_tbit[1]));
 | ||||
| +
 | ||||
| +	wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RRO_MSDU_PG,
 | ||||
| +		MTK_WED_WPDMA_INT_CTRL_RRO_PG0_EN |
 | ||||
| +		MTK_WED_WPDMA_INT_CTRL_RRO_PG0_CLR |
 | ||||
| +		MTK_WED_WPDMA_INT_CTRL_RRO_PG1_EN |
 | ||||
| +		MTK_WED_WPDMA_INT_CTRL_RRO_PG1_CLR |
 | ||||
| +		MTK_WED_WPDMA_INT_CTRL_RRO_PG2_EN |
 | ||||
| +		MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR |
 | ||||
| +		FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG0_DONE_TRIG,
 | ||||
| +			   dev->wlan.rx_pg_tbit[0]) |
 | ||||
| +		FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG1_DONE_TRIG,
 | ||||
| +			   dev->wlan.rx_pg_tbit[1]) |
 | ||||
| +		FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG,
 | ||||
| +			   dev->wlan.rx_pg_tbit[2]));
 | ||||
| +
 | ||||
| +	/* RRO_MSDU_PG_RING2_CFG1_FLD_DRV_EN should be enabled after
 | ||||
| +	 * WM FWDL completed, otherwise RRO_MSDU_PG ring may broken
 | ||||
| +	 */
 | ||||
| +	wed_set(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
 | ||||
| +		MTK_WED_RRO_MSDU_PG_DRV_EN);
 | ||||
| +
 | ||||
| +	for (i = 0; i < MTK_WED_RX_QUEUES; i++) {
 | ||||
| +		struct mtk_wed_ring *ring = &dev->rx_rro_ring[i];
 | ||||
| +
 | ||||
| +		if (!(ring->flags & MTK_WED_RING_CONFIGURED))
 | ||||
| +			continue;
 | ||||
| +
 | ||||
| +		if (mtk_wed_check_wfdma_rx_fill(dev, ring))
 | ||||
| +			dev_err(dev->hw->dev,
 | ||||
| +				"rx_rro_ring(%d) initialization failed\n", i);
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++) {
 | ||||
| +		struct mtk_wed_ring *ring = &dev->rx_page_ring[i];
 | ||||
| +
 | ||||
| +		if (!(ring->flags & MTK_WED_RING_CONFIGURED))
 | ||||
| +			continue;
 | ||||
| +
 | ||||
| +		if (mtk_wed_check_wfdma_rx_fill(dev, ring))
 | ||||
| +			dev_err(dev->hw->dev,
 | ||||
| +				"rx_page_ring(%d) initialization failed\n", i);
 | ||||
| +	}
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void
 | ||||
| +mtk_wed_rro_rx_ring_setup(struct mtk_wed_device *dev, int idx,
 | ||||
| +			  void __iomem *regs)
 | ||||
| +{
 | ||||
| +	struct mtk_wed_ring *ring = &dev->rx_rro_ring[idx];
 | ||||
| +
 | ||||
| +	ring->wpdma = regs;
 | ||||
| +	wed_w32(dev, MTK_WED_RRO_RX_D_RX(idx) + MTK_WED_RING_OFS_BASE,
 | ||||
| +		readl(regs));
 | ||||
| +	wed_w32(dev, MTK_WED_RRO_RX_D_RX(idx) + MTK_WED_RING_OFS_COUNT,
 | ||||
| +		readl(regs + MTK_WED_RING_OFS_COUNT));
 | ||||
| +	ring->flags |= MTK_WED_RING_CONFIGURED;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void
 | ||||
| +mtk_wed_msdu_pg_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
 | ||||
| +{
 | ||||
| +	struct mtk_wed_ring *ring = &dev->rx_page_ring[idx];
 | ||||
| +
 | ||||
| +	ring->wpdma = regs;
 | ||||
| +	wed_w32(dev, MTK_WED_RRO_MSDU_PG_CTRL0(idx) + MTK_WED_RING_OFS_BASE,
 | ||||
| +		readl(regs));
 | ||||
| +	wed_w32(dev, MTK_WED_RRO_MSDU_PG_CTRL0(idx) + MTK_WED_RING_OFS_COUNT,
 | ||||
| +		readl(regs + MTK_WED_RING_OFS_COUNT));
 | ||||
| +	ring->flags |= MTK_WED_RING_CONFIGURED;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int
 | ||||
| +mtk_wed_ind_rx_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
 | ||||
| +{
 | ||||
| +	struct mtk_wed_ring *ring = &dev->ind_cmd_ring;
 | ||||
| +	u32 val = readl(regs + MTK_WED_RING_OFS_COUNT);
 | ||||
| +	int i, count = 0;
 | ||||
| +
 | ||||
| +	ring->wpdma = regs;
 | ||||
| +	wed_w32(dev, MTK_WED_IND_CMD_RX_CTRL1 + MTK_WED_RING_OFS_BASE,
 | ||||
| +		readl(regs) & 0xfffffff0);
 | ||||
| +
 | ||||
| +	wed_w32(dev, MTK_WED_IND_CMD_RX_CTRL1 + MTK_WED_RING_OFS_COUNT,
 | ||||
| +		readl(regs + MTK_WED_RING_OFS_COUNT));
 | ||||
| +
 | ||||
| +	/* ack sn cr */
 | ||||
| +	wed_w32(dev, MTK_WED_RRO_CFG0, dev->wlan.phy_base +
 | ||||
| +		dev->wlan.ind_cmd.ack_sn_addr);
 | ||||
| +	wed_w32(dev, MTK_WED_RRO_CFG1,
 | ||||
| +		FIELD_PREP(MTK_WED_RRO_CFG1_MAX_WIN_SZ,
 | ||||
| +			   dev->wlan.ind_cmd.win_size) |
 | ||||
| +		FIELD_PREP(MTK_WED_RRO_CFG1_PARTICL_SE_ID,
 | ||||
| +			   dev->wlan.ind_cmd.particular_sid));
 | ||||
| +
 | ||||
| +	/* particular session addr element */
 | ||||
| +	wed_w32(dev, MTK_WED_ADDR_ELEM_CFG0,
 | ||||
| +		dev->wlan.ind_cmd.particular_se_phys);
 | ||||
| +
 | ||||
| +	for (i = 0; i < dev->wlan.ind_cmd.se_group_nums; i++) {
 | ||||
| +		wed_w32(dev, MTK_WED_RADDR_ELEM_TBL_WDATA,
 | ||||
| +			dev->wlan.ind_cmd.addr_elem_phys[i] >> 4);
 | ||||
| +		wed_w32(dev, MTK_WED_ADDR_ELEM_TBL_CFG,
 | ||||
| +			MTK_WED_ADDR_ELEM_TBL_WR | (i & 0x7f));
 | ||||
| +
 | ||||
| +		val = wed_r32(dev, MTK_WED_ADDR_ELEM_TBL_CFG);
 | ||||
| +		while (!(val & MTK_WED_ADDR_ELEM_TBL_WR_RDY) && count++ < 100)
 | ||||
| +			val = wed_r32(dev, MTK_WED_ADDR_ELEM_TBL_CFG);
 | ||||
| +		if (count >= 100)
 | ||||
| +			dev_err(dev->hw->dev,
 | ||||
| +				"write ba session base failed\n");
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* pn check init */
 | ||||
| +	for (i = 0; i < dev->wlan.ind_cmd.particular_sid; i++) {
 | ||||
| +		wed_w32(dev, MTK_WED_PN_CHECK_WDATA_M,
 | ||||
| +			MTK_WED_PN_CHECK_IS_FIRST);
 | ||||
| +
 | ||||
| +		wed_w32(dev, MTK_WED_PN_CHECK_CFG, MTK_WED_PN_CHECK_WR |
 | ||||
| +			FIELD_PREP(MTK_WED_PN_CHECK_SE_ID, i));
 | ||||
| +
 | ||||
| +		count = 0;
 | ||||
| +		val = wed_r32(dev, MTK_WED_PN_CHECK_CFG);
 | ||||
| +		while (!(val & MTK_WED_PN_CHECK_WR_RDY) && count++ < 100)
 | ||||
| +			val = wed_r32(dev, MTK_WED_PN_CHECK_CFG);
 | ||||
| +		if (count >= 100)
 | ||||
| +			dev_err(dev->hw->dev,
 | ||||
| +				"session(%d) initialization failed\n", i);
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	wed_w32(dev, MTK_WED_RX_IND_CMD_CNT0, MTK_WED_RX_IND_CMD_DBG_CNT_EN);
 | ||||
| +	wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_IND_CMD_EN);
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void
 | ||||
|  mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) | ||||
|  { | ||||
|  	int i; | ||||
| @@ -2216,6 +2507,10 @@ void mtk_wed_add_hw(struct device_node *
 | ||||
|  		.detach = mtk_wed_detach, | ||||
|  		.ppe_check = mtk_wed_ppe_check, | ||||
|  		.setup_tc = mtk_wed_setup_tc, | ||||
| +		.start_hw_rro = mtk_wed_start_hw_rro,
 | ||||
| +		.rro_rx_ring_setup = mtk_wed_rro_rx_ring_setup,
 | ||||
| +		.msdu_pg_rx_ring_setup = mtk_wed_msdu_pg_rx_ring_setup,
 | ||||
| +		.ind_rx_ring_setup = mtk_wed_ind_rx_ring_setup,
 | ||||
|  	}; | ||||
|  	struct device_node *eth_np = eth->dev->of_node; | ||||
|  	struct platform_device *pdev; | ||||
| --- a/include/linux/soc/mediatek/mtk_wed.h
 | ||||
| +++ b/include/linux/soc/mediatek/mtk_wed.h
 | ||||
| @@ -10,6 +10,7 @@
 | ||||
|   | ||||
|  #define MTK_WED_TX_QUEUES		2 | ||||
|  #define MTK_WED_RX_QUEUES		2 | ||||
| +#define MTK_WED_RX_PAGE_QUEUES		3
 | ||||
|   | ||||
|  #define WED_WO_STA_REC			0x6 | ||||
|   | ||||
| @@ -99,6 +100,9 @@ struct mtk_wed_device {
 | ||||
|  	struct mtk_wed_ring txfree_ring; | ||||
|  	struct mtk_wed_ring tx_wdma[MTK_WED_TX_QUEUES]; | ||||
|  	struct mtk_wed_ring rx_wdma[MTK_WED_RX_QUEUES]; | ||||
| +	struct mtk_wed_ring rx_rro_ring[MTK_WED_RX_QUEUES];
 | ||||
| +	struct mtk_wed_ring rx_page_ring[MTK_WED_RX_PAGE_QUEUES];
 | ||||
| +	struct mtk_wed_ring ind_cmd_ring;
 | ||||
|   | ||||
|  	struct { | ||||
|  		int size; | ||||
| @@ -119,6 +123,13 @@ struct mtk_wed_device {
 | ||||
|  		dma_addr_t fdbk_phys; | ||||
|  	} rro; | ||||
|   | ||||
| +	struct {
 | ||||
| +		int size;
 | ||||
| +		struct mtk_wed_buf *pages;
 | ||||
| +		struct mtk_wed_bm_desc *desc;
 | ||||
| +		dma_addr_t desc_phys;
 | ||||
| +	} hw_rro;
 | ||||
| +
 | ||||
|  	/* filled by driver: */ | ||||
|  	struct { | ||||
|  		union { | ||||
| @@ -137,6 +148,8 @@ struct mtk_wed_device {
 | ||||
|  		u32 wpdma_txfree; | ||||
|  		u32 wpdma_rx_glo; | ||||
|  		u32 wpdma_rx; | ||||
| +		u32 wpdma_rx_rro[MTK_WED_RX_QUEUES];
 | ||||
| +		u32 wpdma_rx_pg;
 | ||||
|   | ||||
|  		bool wcid_512; | ||||
|  		bool hw_rro; | ||||
| @@ -151,9 +164,20 @@ struct mtk_wed_device {
 | ||||
|   | ||||
|  		u8 tx_tbit[MTK_WED_TX_QUEUES]; | ||||
|  		u8 rx_tbit[MTK_WED_RX_QUEUES]; | ||||
| +		u8 rro_rx_tbit[MTK_WED_RX_QUEUES];
 | ||||
| +		u8 rx_pg_tbit[MTK_WED_RX_PAGE_QUEUES];
 | ||||
|  		u8 txfree_tbit; | ||||
|  		u8 amsdu_max_subframes; | ||||
|   | ||||
| +		struct {
 | ||||
| +			u8 se_group_nums;
 | ||||
| +			u16 win_size;
 | ||||
| +			u16 particular_sid;
 | ||||
| +			u32 ack_sn_addr;
 | ||||
| +			dma_addr_t particular_se_phys;
 | ||||
| +			dma_addr_t addr_elem_phys[1024];
 | ||||
| +		} ind_cmd;
 | ||||
| +
 | ||||
|  		u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id); | ||||
|  		int (*offload_enable)(struct mtk_wed_device *wed); | ||||
|  		void (*offload_disable)(struct mtk_wed_device *wed); | ||||
| @@ -192,6 +216,14 @@ struct mtk_wed_ops {
 | ||||
|  	void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask); | ||||
|  	int (*setup_tc)(struct mtk_wed_device *wed, struct net_device *dev, | ||||
|  			enum tc_setup_type type, void *type_data); | ||||
| +	void (*start_hw_rro)(struct mtk_wed_device *dev, u32 irq_mask,
 | ||||
| +			     bool reset);
 | ||||
| +	void (*rro_rx_ring_setup)(struct mtk_wed_device *dev, int ring,
 | ||||
| +				  void __iomem *regs);
 | ||||
| +	void (*msdu_pg_rx_ring_setup)(struct mtk_wed_device *dev, int ring,
 | ||||
| +				      void __iomem *regs);
 | ||||
| +	int (*ind_rx_ring_setup)(struct mtk_wed_device *dev,
 | ||||
| +				 void __iomem *regs);
 | ||||
|  }; | ||||
|   | ||||
|  extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops; | ||||
| @@ -263,6 +295,15 @@ static inline bool mtk_wed_is_amsdu_supp
 | ||||
|  #define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev) | ||||
|  #define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) \ | ||||
|  	(_dev)->ops->setup_tc(_dev, _netdev, _type, _type_data) | ||||
| +#define mtk_wed_device_start_hw_rro(_dev, _mask, _reset) \
 | ||||
| +	(_dev)->ops->start_hw_rro(_dev, _mask, _reset)
 | ||||
| +#define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) \
 | ||||
| +	(_dev)->ops->rro_rx_ring_setup(_dev, _ring, _regs)
 | ||||
| +#define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs) \
 | ||||
| +	(_dev)->ops->msdu_pg_rx_ring_setup(_dev, _ring, _regs)
 | ||||
| +#define mtk_wed_device_ind_rx_ring_setup(_dev, _regs) \
 | ||||
| +	(_dev)->ops->ind_rx_ring_setup(_dev, _regs)
 | ||||
| +
 | ||||
|  #else | ||||
|  static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) | ||||
|  { | ||||
| @@ -282,6 +323,10 @@ static inline bool mtk_wed_device_active
 | ||||
|  #define mtk_wed_device_stop(_dev) do {} while (0) | ||||
|  #define mtk_wed_device_dma_reset(_dev) do {} while (0) | ||||
|  #define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) -EOPNOTSUPP | ||||
| +#define mtk_wed_device_start_hw_rro(_dev, _mask, _reset) do {} while (0)
 | ||||
| +#define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) -ENODEV
 | ||||
| +#define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs) -ENODEV
 | ||||
| +#define mtk_wed_device_ind_rx_ring_setup(_dev, _regs) -ENODEV
 | ||||
|  #endif | ||||
|   | ||||
|  #endif | ||||
|  | @ -0,0 +1,78 @@ | |||
| From: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Date: Mon, 18 Sep 2023 12:29:17 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: debugfs: move wed_v2 specific regs | ||||
|  out of regs array | ||||
| 
 | ||||
| Move specific WED2.0 debugfs entries out of regs array. This is a | ||||
| preliminary patch to introduce WED 3.0 debugfs info. | ||||
| 
 | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
 | ||||
| @@ -151,7 +151,7 @@ DEFINE_SHOW_ATTRIBUTE(wed_txinfo);
 | ||||
|  static int | ||||
|  wed_rxinfo_show(struct seq_file *s, void *data) | ||||
|  { | ||||
| -	static const struct reg_dump regs[] = {
 | ||||
| +	static const struct reg_dump regs_common[] = {
 | ||||
|  		DUMP_STR("WPDMA RX"), | ||||
|  		DUMP_WPDMA_RX_RING(0), | ||||
|  		DUMP_WPDMA_RX_RING(1), | ||||
| @@ -169,7 +169,7 @@ wed_rxinfo_show(struct seq_file *s, void
 | ||||
|  		DUMP_WED_RING(WED_RING_RX_DATA(0)), | ||||
|  		DUMP_WED_RING(WED_RING_RX_DATA(1)), | ||||
|   | ||||
| -		DUMP_STR("WED RRO"),
 | ||||
| +		DUMP_STR("WED WO RRO"),
 | ||||
|  		DUMP_WED_RRO_RING(WED_RROQM_MIOD_CTRL0), | ||||
|  		DUMP_WED(WED_RROQM_MID_MIB), | ||||
|  		DUMP_WED(WED_RROQM_MOD_MIB), | ||||
| @@ -180,17 +180,6 @@ wed_rxinfo_show(struct seq_file *s, void
 | ||||
|  		DUMP_WED(WED_RROQM_FDBK_ANC_MIB), | ||||
|  		DUMP_WED(WED_RROQM_FDBK_ANC2H_MIB), | ||||
|   | ||||
| -		DUMP_STR("WED Route QM"),
 | ||||
| -		DUMP_WED(WED_RTQM_R2H_MIB(0)),
 | ||||
| -		DUMP_WED(WED_RTQM_R2Q_MIB(0)),
 | ||||
| -		DUMP_WED(WED_RTQM_Q2H_MIB(0)),
 | ||||
| -		DUMP_WED(WED_RTQM_R2H_MIB(1)),
 | ||||
| -		DUMP_WED(WED_RTQM_R2Q_MIB(1)),
 | ||||
| -		DUMP_WED(WED_RTQM_Q2H_MIB(1)),
 | ||||
| -		DUMP_WED(WED_RTQM_Q2N_MIB),
 | ||||
| -		DUMP_WED(WED_RTQM_Q2B_MIB),
 | ||||
| -		DUMP_WED(WED_RTQM_PFDBK_MIB),
 | ||||
| -
 | ||||
|  		DUMP_STR("WED WDMA TX"), | ||||
|  		DUMP_WED(WED_WDMA_TX_MIB), | ||||
|  		DUMP_WED_RING(WED_WDMA_RING_TX), | ||||
| @@ -211,11 +200,25 @@ wed_rxinfo_show(struct seq_file *s, void
 | ||||
|  		DUMP_WED(WED_RX_BM_INTF), | ||||
|  		DUMP_WED(WED_RX_BM_ERR_STS), | ||||
|  	}; | ||||
| +	static const struct reg_dump regs_wed_v2[] = {
 | ||||
| +		DUMP_STR("WED Route QM"),
 | ||||
| +		DUMP_WED(WED_RTQM_R2H_MIB(0)),
 | ||||
| +		DUMP_WED(WED_RTQM_R2Q_MIB(0)),
 | ||||
| +		DUMP_WED(WED_RTQM_Q2H_MIB(0)),
 | ||||
| +		DUMP_WED(WED_RTQM_R2H_MIB(1)),
 | ||||
| +		DUMP_WED(WED_RTQM_R2Q_MIB(1)),
 | ||||
| +		DUMP_WED(WED_RTQM_Q2H_MIB(1)),
 | ||||
| +		DUMP_WED(WED_RTQM_Q2N_MIB),
 | ||||
| +		DUMP_WED(WED_RTQM_Q2B_MIB),
 | ||||
| +		DUMP_WED(WED_RTQM_PFDBK_MIB),
 | ||||
| +	};
 | ||||
|  	struct mtk_wed_hw *hw = s->private; | ||||
|  	struct mtk_wed_device *dev = hw->wed_dev; | ||||
|   | ||||
| -	if (dev)
 | ||||
| -		dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
 | ||||
| +	if (dev) {
 | ||||
| +		dump_wed_regs(s, dev, regs_common, ARRAY_SIZE(regs_common));
 | ||||
| +		dump_wed_regs(s, dev, regs_wed_v2, ARRAY_SIZE(regs_wed_v2));
 | ||||
| +	}
 | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
|  | @ -0,0 +1,432 @@ | |||
| From: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 12:29:18 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: debugfs: add WED 3.0 debugfs entries | ||||
| 
 | ||||
| Introduce WED3.0 debugfs entries useful for debugging. | ||||
| 
 | ||||
| Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
 | ||||
| @@ -11,6 +11,7 @@ struct reg_dump {
 | ||||
|  	u16 offset; | ||||
|  	u8 type; | ||||
|  	u8 base; | ||||
| +	u32 mask;
 | ||||
|  }; | ||||
|   | ||||
|  enum { | ||||
| @@ -25,6 +26,8 @@ enum {
 | ||||
|   | ||||
|  #define DUMP_STR(_str) { _str, 0, DUMP_TYPE_STRING } | ||||
|  #define DUMP_REG(_reg, ...) { #_reg, MTK_##_reg, __VA_ARGS__ } | ||||
| +#define DUMP_REG_MASK(_reg, _mask)	\
 | ||||
| +	{ #_mask, MTK_##_reg, DUMP_TYPE_WED, 0, MTK_##_mask }
 | ||||
|  #define DUMP_RING(_prefix, _base, ...)				\ | ||||
|  	{ _prefix " BASE", _base, __VA_ARGS__ },		\ | ||||
|  	{ _prefix " CNT",  _base + 0x4, __VA_ARGS__ },	\ | ||||
| @@ -32,6 +35,7 @@ enum {
 | ||||
|  	{ _prefix " DIDX", _base + 0xc, __VA_ARGS__ } | ||||
|   | ||||
|  #define DUMP_WED(_reg) DUMP_REG(_reg, DUMP_TYPE_WED) | ||||
| +#define DUMP_WED_MASK(_reg, _mask) DUMP_REG_MASK(_reg, _mask)
 | ||||
|  #define DUMP_WED_RING(_base) DUMP_RING(#_base, MTK_##_base, DUMP_TYPE_WED) | ||||
|   | ||||
|  #define DUMP_WDMA(_reg) DUMP_REG(_reg, DUMP_TYPE_WDMA) | ||||
| @@ -212,12 +216,58 @@ wed_rxinfo_show(struct seq_file *s, void
 | ||||
|  		DUMP_WED(WED_RTQM_Q2B_MIB), | ||||
|  		DUMP_WED(WED_RTQM_PFDBK_MIB), | ||||
|  	}; | ||||
| +	static const struct reg_dump regs_wed_v3[] = {
 | ||||
| +		DUMP_STR("WED RX RRO DATA"),
 | ||||
| +		DUMP_WED_RING(WED_RRO_RX_D_RX(0)),
 | ||||
| +		DUMP_WED_RING(WED_RRO_RX_D_RX(1)),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED RX MSDU PAGE"),
 | ||||
| +		DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(0)),
 | ||||
| +		DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(1)),
 | ||||
| +		DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(2)),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED RX IND CMD"),
 | ||||
| +		DUMP_WED(WED_IND_CMD_RX_CTRL1),
 | ||||
| +		DUMP_WED_MASK(WED_IND_CMD_RX_CTRL2, WED_IND_CMD_MAX_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, WED_IND_CMD_PROC_IDX),
 | ||||
| +		DUMP_WED_MASK(RRO_IND_CMD_SIGNATURE, RRO_IND_CMD_DMA_IDX),
 | ||||
| +		DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, WED_IND_CMD_MAGIC_CNT),
 | ||||
| +		DUMP_WED_MASK(RRO_IND_CMD_SIGNATURE, RRO_IND_CMD_MAGIC_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0,
 | ||||
| +			      WED_IND_CMD_PREFETCH_FREE_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_RRO_CFG1, WED_RRO_CFG1_PARTICL_SE_ID),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED ADDR ELEM"),
 | ||||
| +		DUMP_WED(WED_ADDR_ELEM_CFG0),
 | ||||
| +		DUMP_WED_MASK(WED_ADDR_ELEM_CFG1,
 | ||||
| +			      WED_ADDR_ELEM_PREFETCH_FREE_CNT),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED Route QM"),
 | ||||
| +		DUMP_WED(WED_RTQM_ENQ_I2Q_DMAD_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_ENQ_I2N_DMAD_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_ENQ_I2Q_PKT_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_ENQ_I2N_PKT_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_ENQ_USED_ENTRY_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_ENQ_ERR_CNT),
 | ||||
| +
 | ||||
| +		DUMP_WED(WED_RTQM_DEQ_DMAD_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_DEQ_Q2I_DMAD_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_DEQ_PKT_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_DEQ_Q2I_PKT_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_DEQ_USED_PFDBK_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_DEQ_ERR_CNT),
 | ||||
| +	};
 | ||||
|  	struct mtk_wed_hw *hw = s->private; | ||||
|  	struct mtk_wed_device *dev = hw->wed_dev; | ||||
|   | ||||
|  	if (dev) { | ||||
|  		dump_wed_regs(s, dev, regs_common, ARRAY_SIZE(regs_common)); | ||||
| -		dump_wed_regs(s, dev, regs_wed_v2, ARRAY_SIZE(regs_wed_v2));
 | ||||
| +		if (mtk_wed_is_v2(hw))
 | ||||
| +			dump_wed_regs(s, dev,
 | ||||
| +				      regs_wed_v2, ARRAY_SIZE(regs_wed_v2));
 | ||||
| +		else
 | ||||
| +			dump_wed_regs(s, dev,
 | ||||
| +				      regs_wed_v3, ARRAY_SIZE(regs_wed_v3));
 | ||||
|  	} | ||||
|   | ||||
|  	return 0; | ||||
| @@ -225,6 +275,314 @@ wed_rxinfo_show(struct seq_file *s, void
 | ||||
|  DEFINE_SHOW_ATTRIBUTE(wed_rxinfo); | ||||
|   | ||||
|  static int | ||||
| +wed_amsdu_show(struct seq_file *s, void *data)
 | ||||
| +{
 | ||||
| +	static const struct reg_dump regs[] = {
 | ||||
| +		DUMP_STR("WED AMDSU INFO"),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_FIFO_DMAD),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED AMDSU ENG0 INFO"),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_DMAD(0)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QFPL(0)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENI(0)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENO(0)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_MERG(0)),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(0),
 | ||||
| +			      WED_AMSDU_ENG_MAX_PL_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(0),
 | ||||
| +			      WED_AMSDU_ENG_MAX_QGPP_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0),
 | ||||
| +			      WED_AMSDU_ENG_CUR_ENTRY),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0),
 | ||||
| +			      WED_AMSDU_ENG_MAX_BUF_MERGED),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0),
 | ||||
| +			      WED_AMSDU_ENG_MAX_MSDU_MERGED),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED AMDSU ENG1 INFO"),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_DMAD(1)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QFPL(1)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENI(1)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENO(1)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_MERG(1)),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(1),
 | ||||
| +			      WED_AMSDU_ENG_MAX_PL_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(1),
 | ||||
| +			      WED_AMSDU_ENG_MAX_QGPP_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1),
 | ||||
| +			      WED_AMSDU_ENG_CUR_ENTRY),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
 | ||||
| +			      WED_AMSDU_ENG_MAX_BUF_MERGED),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
 | ||||
| +			      WED_AMSDU_ENG_MAX_MSDU_MERGED),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED AMDSU ENG2 INFO"),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_DMAD(2)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QFPL(2)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENI(2)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENO(2)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_MERG(2)),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(2),
 | ||||
| +			      WED_AMSDU_ENG_MAX_PL_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(2),
 | ||||
| +			      WED_AMSDU_ENG_MAX_QGPP_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
 | ||||
| +			      WED_AMSDU_ENG_CUR_ENTRY),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
 | ||||
| +			      WED_AMSDU_ENG_MAX_BUF_MERGED),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
 | ||||
| +			      WED_AMSDU_ENG_MAX_MSDU_MERGED),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED AMDSU ENG3 INFO"),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_DMAD(3)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QFPL(3)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENI(3)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENO(3)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_MERG(3)),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(3),
 | ||||
| +			      WED_AMSDU_ENG_MAX_PL_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(3),
 | ||||
| +			      WED_AMSDU_ENG_MAX_QGPP_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3),
 | ||||
| +			      WED_AMSDU_ENG_CUR_ENTRY),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3),
 | ||||
| +			      WED_AMSDU_ENG_MAX_BUF_MERGED),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3),
 | ||||
| +			      WED_AMSDU_ENG_MAX_MSDU_MERGED),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED AMDSU ENG4 INFO"),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_DMAD(4)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QFPL(4)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENI(4)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENO(4)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_MERG(4)),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(4),
 | ||||
| +			      WED_AMSDU_ENG_MAX_PL_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(4),
 | ||||
| +			      WED_AMSDU_ENG_MAX_QGPP_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4),
 | ||||
| +			      WED_AMSDU_ENG_CUR_ENTRY),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4),
 | ||||
| +			      WED_AMSDU_ENG_MAX_BUF_MERGED),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4),
 | ||||
| +			      WED_AMSDU_ENG_MAX_MSDU_MERGED),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED AMDSU ENG5 INFO"),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_DMAD(5)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QFPL(5)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENI(5)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENO(5)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_MERG(5)),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(5),
 | ||||
| +			      WED_AMSDU_ENG_MAX_PL_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(5),
 | ||||
| +			      WED_AMSDU_ENG_MAX_QGPP_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5),
 | ||||
| +			      WED_AMSDU_ENG_CUR_ENTRY),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5),
 | ||||
| +			      WED_AMSDU_ENG_MAX_BUF_MERGED),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5),
 | ||||
| +			      WED_AMSDU_ENG_MAX_MSDU_MERGED),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED AMDSU ENG6 INFO"),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_DMAD(6)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QFPL(6)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENI(6)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENO(6)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_MERG(6)),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(6),
 | ||||
| +			      WED_AMSDU_ENG_MAX_PL_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(6),
 | ||||
| +			      WED_AMSDU_ENG_MAX_QGPP_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6),
 | ||||
| +			      WED_AMSDU_ENG_CUR_ENTRY),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6),
 | ||||
| +			      WED_AMSDU_ENG_MAX_BUF_MERGED),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6),
 | ||||
| +			      WED_AMSDU_ENG_MAX_MSDU_MERGED),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED AMDSU ENG7 INFO"),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_DMAD(7)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QFPL(7)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENI(7)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENO(7)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_MERG(7)),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(7),
 | ||||
| +			      WED_AMSDU_ENG_MAX_PL_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(7),
 | ||||
| +			      WED_AMSDU_ENG_MAX_QGPP_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7),
 | ||||
| +			      WED_AMSDU_ENG_CUR_ENTRY),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7),
 | ||||
| +			      WED_AMSDU_ENG_MAX_BUF_MERGED),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4),
 | ||||
| +			      WED_AMSDU_ENG_MAX_MSDU_MERGED),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED AMDSU ENG8 INFO"),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_DMAD(8)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QFPL(8)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENI(8)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_QENO(8)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_ENG_MERG(8)),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(8),
 | ||||
| +			      WED_AMSDU_ENG_MAX_PL_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(8),
 | ||||
| +			      WED_AMSDU_ENG_MAX_QGPP_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8),
 | ||||
| +			      WED_AMSDU_ENG_CUR_ENTRY),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8),
 | ||||
| +			      WED_AMSDU_ENG_MAX_BUF_MERGED),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8),
 | ||||
| +			      WED_AMSDU_ENG_MAX_MSDU_MERGED),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED QMEM INFO"),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(0), WED_AMSDU_QMEM_FQ_CNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(0), WED_AMSDU_QMEM_SP_QCNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(1), WED_AMSDU_QMEM_TID0_QCNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(1), WED_AMSDU_QMEM_TID1_QCNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(2), WED_AMSDU_QMEM_TID2_QCNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(2), WED_AMSDU_QMEM_TID3_QCNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(3), WED_AMSDU_QMEM_TID4_QCNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(3), WED_AMSDU_QMEM_TID5_QCNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(4), WED_AMSDU_QMEM_TID6_QCNT),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(4), WED_AMSDU_QMEM_TID7_QCNT),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED QMEM HEAD INFO"),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(0), WED_AMSDU_QMEM_FQ_HEAD),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(0), WED_AMSDU_QMEM_SP_QHEAD),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(1), WED_AMSDU_QMEM_TID0_QHEAD),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(1), WED_AMSDU_QMEM_TID1_QHEAD),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(2), WED_AMSDU_QMEM_TID2_QHEAD),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(2), WED_AMSDU_QMEM_TID3_QHEAD),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(3), WED_AMSDU_QMEM_TID4_QHEAD),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(3), WED_AMSDU_QMEM_TID5_QHEAD),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(4), WED_AMSDU_QMEM_TID6_QHEAD),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(4), WED_AMSDU_QMEM_TID7_QHEAD),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED QMEM TAIL INFO"),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(5), WED_AMSDU_QMEM_FQ_TAIL),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(5), WED_AMSDU_QMEM_SP_QTAIL),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(6), WED_AMSDU_QMEM_TID0_QTAIL),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(6), WED_AMSDU_QMEM_TID1_QTAIL),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(7), WED_AMSDU_QMEM_TID2_QTAIL),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(7), WED_AMSDU_QMEM_TID3_QTAIL),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(8), WED_AMSDU_QMEM_TID4_QTAIL),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(8), WED_AMSDU_QMEM_TID5_QTAIL),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(9), WED_AMSDU_QMEM_TID6_QTAIL),
 | ||||
| +		DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(9), WED_AMSDU_QMEM_TID7_QTAIL),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED HIFTXD MSDU INFO"),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(1)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(2)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(3)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(4)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(5)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(6)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(7)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(8)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(9)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(10)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(11)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(12)),
 | ||||
| +		DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(13)),
 | ||||
| +	};
 | ||||
| +	struct mtk_wed_hw *hw = s->private;
 | ||||
| +	struct mtk_wed_device *dev = hw->wed_dev;
 | ||||
| +
 | ||||
| +	if (dev)
 | ||||
| +		dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +DEFINE_SHOW_ATTRIBUTE(wed_amsdu);
 | ||||
| +
 | ||||
| +static int
 | ||||
| +wed_rtqm_show(struct seq_file *s, void *data)
 | ||||
| +{
 | ||||
| +	static const struct reg_dump regs[] = {
 | ||||
| +		DUMP_STR("WED Route QM IGRS0(N2H + Recycle)"),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS0_I2HW_DMAD_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS0_I2H_DMAD_CNT(0)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS0_I2H_DMAD_CNT(1)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS0_I2HW_PKT_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS0_I2H_PKT_CNT(0)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS0_I2H_PKT_CNT(0)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS0_FDROP_CNT),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED Route QM IGRS1(Legacy)"),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS1_I2HW_DMAD_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS1_I2H_DMAD_CNT(0)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS1_I2H_DMAD_CNT(1)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS1_I2HW_PKT_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS1_I2H_PKT_CNT(0)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS1_I2H_PKT_CNT(1)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS1_FDROP_CNT),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED Route QM IGRS2(RRO3.0)"),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS2_I2HW_DMAD_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS2_I2H_DMAD_CNT(0)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS2_I2H_DMAD_CNT(1)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS2_I2HW_PKT_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS2_I2H_PKT_CNT(0)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS2_I2H_PKT_CNT(1)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS2_FDROP_CNT),
 | ||||
| +
 | ||||
| +		DUMP_STR("WED Route QM IGRS3(DEBUG)"),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS2_I2HW_DMAD_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS3_I2H_DMAD_CNT(0)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS3_I2H_DMAD_CNT(1)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS3_I2HW_PKT_CNT),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS3_I2H_PKT_CNT(0)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS3_I2H_PKT_CNT(1)),
 | ||||
| +		DUMP_WED(WED_RTQM_IGRS3_FDROP_CNT),
 | ||||
| +	};
 | ||||
| +	struct mtk_wed_hw *hw = s->private;
 | ||||
| +	struct mtk_wed_device *dev = hw->wed_dev;
 | ||||
| +
 | ||||
| +	if (dev)
 | ||||
| +		dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +DEFINE_SHOW_ATTRIBUTE(wed_rtqm);
 | ||||
| +
 | ||||
| +static int
 | ||||
| +wed_rro_show(struct seq_file *s, void *data)
 | ||||
| +{
 | ||||
| +	static const struct reg_dump regs[] = {
 | ||||
| +		DUMP_STR("RRO/IND CMD CNT"),
 | ||||
| +		DUMP_WED(WED_RX_IND_CMD_CNT(1)),
 | ||||
| +		DUMP_WED(WED_RX_IND_CMD_CNT(2)),
 | ||||
| +		DUMP_WED(WED_RX_IND_CMD_CNT(3)),
 | ||||
| +		DUMP_WED(WED_RX_IND_CMD_CNT(4)),
 | ||||
| +		DUMP_WED(WED_RX_IND_CMD_CNT(5)),
 | ||||
| +		DUMP_WED(WED_RX_IND_CMD_CNT(6)),
 | ||||
| +		DUMP_WED(WED_RX_IND_CMD_CNT(7)),
 | ||||
| +		DUMP_WED(WED_RX_IND_CMD_CNT(8)),
 | ||||
| +		DUMP_WED_MASK(WED_RX_IND_CMD_CNT(9),
 | ||||
| +			      WED_IND_CMD_MAGIC_CNT_FAIL_CNT),
 | ||||
| +
 | ||||
| +		DUMP_WED(WED_RX_ADDR_ELEM_CNT(0)),
 | ||||
| +		DUMP_WED_MASK(WED_RX_ADDR_ELEM_CNT(1),
 | ||||
| +			      WED_ADDR_ELEM_SIG_FAIL_CNT),
 | ||||
| +		DUMP_WED(WED_RX_MSDU_PG_CNT(1)),
 | ||||
| +		DUMP_WED(WED_RX_MSDU_PG_CNT(2)),
 | ||||
| +		DUMP_WED(WED_RX_MSDU_PG_CNT(3)),
 | ||||
| +		DUMP_WED(WED_RX_MSDU_PG_CNT(4)),
 | ||||
| +		DUMP_WED(WED_RX_MSDU_PG_CNT(5)),
 | ||||
| +		DUMP_WED_MASK(WED_RX_PN_CHK_CNT,
 | ||||
| +			      WED_PN_CHK_FAIL_CNT),
 | ||||
| +	};
 | ||||
| +	struct mtk_wed_hw *hw = s->private;
 | ||||
| +	struct mtk_wed_device *dev = hw->wed_dev;
 | ||||
| +
 | ||||
| +	if (dev)
 | ||||
| +		dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +DEFINE_SHOW_ATTRIBUTE(wed_rro);
 | ||||
| +
 | ||||
| +static int
 | ||||
|  mtk_wed_reg_set(void *data, u64 val) | ||||
|  { | ||||
|  	struct mtk_wed_hw *hw = data; | ||||
| @@ -264,7 +622,16 @@ void mtk_wed_hw_add_debugfs(struct mtk_w
 | ||||
|  	debugfs_create_u32("regidx", 0600, dir, &hw->debugfs_reg); | ||||
|  	debugfs_create_file_unsafe("regval", 0600, dir, hw, &fops_regval); | ||||
|  	debugfs_create_file_unsafe("txinfo", 0400, dir, hw, &wed_txinfo_fops); | ||||
| -	if (!mtk_wed_is_v1(hw))
 | ||||
| +	if (!mtk_wed_is_v1(hw)) {
 | ||||
|  		debugfs_create_file_unsafe("rxinfo", 0400, dir, hw, | ||||
|  					   &wed_rxinfo_fops); | ||||
| +		if (mtk_wed_is_v3_or_greater(hw)) {
 | ||||
| +			debugfs_create_file_unsafe("amsdu", 0400, dir, hw,
 | ||||
| +						   &wed_amsdu_fops);
 | ||||
| +			debugfs_create_file_unsafe("rtqm", 0400, dir, hw,
 | ||||
| +						   &wed_rtqm_fops);
 | ||||
| +			debugfs_create_file_unsafe("rro", 0400, dir, hw,
 | ||||
| +						   &wed_rro_fops);
 | ||||
| +		}
 | ||||
| +	}
 | ||||
|  } | ||||
|  | @ -0,0 +1,587 @@ | |||
| From: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Date: Mon, 18 Sep 2023 12:29:19 +0200 | ||||
| Subject: [PATCH] net: ethernet: mtk_wed: add wed 3.0 reset support | ||||
| 
 | ||||
| Introduce support for resetting Wireless Ethernet Dispatcher 3.0 | ||||
| available on MT988 SoC. | ||||
| 
 | ||||
| Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> | ||||
| Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com> | ||||
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed.c
 | ||||
| @@ -149,6 +149,90 @@ mtk_wdma_read_reset(struct mtk_wed_devic
 | ||||
|  	return wdma_r32(dev, MTK_WDMA_GLO_CFG); | ||||
|  } | ||||
|   | ||||
| +static void
 | ||||
| +mtk_wdma_v3_rx_reset(struct mtk_wed_device *dev)
 | ||||
| +{
 | ||||
| +	u32 status;
 | ||||
| +
 | ||||
| +	if (!mtk_wed_is_v3_or_greater(dev->hw))
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
 | ||||
| +
 | ||||
| +	if (read_poll_timeout(wdma_r32, status,
 | ||||
| +			      !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY),
 | ||||
| +			      0, 10000, false, dev, MTK_WDMA_PREF_TX_CFG))
 | ||||
| +		dev_err(dev->hw->dev, "rx reset failed\n");
 | ||||
| +
 | ||||
| +	if (read_poll_timeout(wdma_r32, status,
 | ||||
| +			      !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY),
 | ||||
| +			      0, 10000, false, dev, MTK_WDMA_PREF_RX_CFG))
 | ||||
| +		dev_err(dev->hw->dev, "rx reset failed\n");
 | ||||
| +
 | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
 | ||||
| +
 | ||||
| +	if (read_poll_timeout(wdma_r32, status,
 | ||||
| +			      !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY),
 | ||||
| +			      0, 10000, false, dev, MTK_WDMA_WRBK_TX_CFG))
 | ||||
| +		dev_err(dev->hw->dev, "rx reset failed\n");
 | ||||
| +
 | ||||
| +	if (read_poll_timeout(wdma_r32, status,
 | ||||
| +			      !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY),
 | ||||
| +			      0, 10000, false, dev, MTK_WDMA_WRBK_RX_CFG))
 | ||||
| +		dev_err(dev->hw->dev, "rx reset failed\n");
 | ||||
| +
 | ||||
| +	/* prefetch FIFO */
 | ||||
| +	wdma_w32(dev, MTK_WDMA_PREF_RX_FIFO_CFG,
 | ||||
| +		 MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR |
 | ||||
| +		 MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_RX_FIFO_CFG,
 | ||||
| +		 MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR |
 | ||||
| +		 MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR);
 | ||||
| +
 | ||||
| +	/* core FIFO */
 | ||||
| +	wdma_w32(dev, MTK_WDMA_XDMA_RX_FIFO_CFG,
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_XDMA_RX_FIFO_CFG,
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR);
 | ||||
| +
 | ||||
| +	/* writeback FIFO */
 | ||||
| +	wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0),
 | ||||
| +		 MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
 | ||||
| +	wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1),
 | ||||
| +		 MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
 | ||||
| +
 | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0),
 | ||||
| +		 MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1),
 | ||||
| +		 MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
 | ||||
| +
 | ||||
| +	/* prefetch ring status */
 | ||||
| +	wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG,
 | ||||
| +		 MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG,
 | ||||
| +		 MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR);
 | ||||
| +
 | ||||
| +	/* writeback ring status */
 | ||||
| +	wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG,
 | ||||
| +		 MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG,
 | ||||
| +		 MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static int | ||||
|  mtk_wdma_rx_reset(struct mtk_wed_device *dev) | ||||
|  { | ||||
| @@ -161,6 +245,7 @@ mtk_wdma_rx_reset(struct mtk_wed_device
 | ||||
|  	if (ret) | ||||
|  		dev_err(dev->hw->dev, "rx reset failed\n"); | ||||
|   | ||||
| +	mtk_wdma_v3_rx_reset(dev);
 | ||||
|  	wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX); | ||||
|  	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); | ||||
|   | ||||
| @@ -193,6 +278,84 @@ mtk_wed_poll_busy(struct mtk_wed_device
 | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| +mtk_wdma_v3_tx_reset(struct mtk_wed_device *dev)
 | ||||
| +{
 | ||||
| +	u32 status;
 | ||||
| +
 | ||||
| +	if (!mtk_wed_is_v3_or_greater(dev->hw))
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
 | ||||
| +
 | ||||
| +	if (read_poll_timeout(wdma_r32, status,
 | ||||
| +			      !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY),
 | ||||
| +			      0, 10000, false, dev, MTK_WDMA_PREF_TX_CFG))
 | ||||
| +		dev_err(dev->hw->dev, "tx reset failed\n");
 | ||||
| +
 | ||||
| +	if (read_poll_timeout(wdma_r32, status,
 | ||||
| +			      !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY),
 | ||||
| +			      0, 10000, false, dev, MTK_WDMA_PREF_RX_CFG))
 | ||||
| +		dev_err(dev->hw->dev, "tx reset failed\n");
 | ||||
| +
 | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
 | ||||
| +
 | ||||
| +	if (read_poll_timeout(wdma_r32, status,
 | ||||
| +			      !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY),
 | ||||
| +			      0, 10000, false, dev, MTK_WDMA_WRBK_TX_CFG))
 | ||||
| +		dev_err(dev->hw->dev, "tx reset failed\n");
 | ||||
| +
 | ||||
| +	if (read_poll_timeout(wdma_r32, status,
 | ||||
| +			      !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY),
 | ||||
| +			      0, 10000, false, dev, MTK_WDMA_WRBK_RX_CFG))
 | ||||
| +		dev_err(dev->hw->dev, "tx reset failed\n");
 | ||||
| +
 | ||||
| +	/* prefetch FIFO */
 | ||||
| +	wdma_w32(dev, MTK_WDMA_PREF_TX_FIFO_CFG,
 | ||||
| +		 MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR |
 | ||||
| +		 MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_TX_FIFO_CFG,
 | ||||
| +		 MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR |
 | ||||
| +		 MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR);
 | ||||
| +
 | ||||
| +	/* core FIFO */
 | ||||
| +	wdma_w32(dev, MTK_WDMA_XDMA_TX_FIFO_CFG,
 | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_XDMA_TX_FIFO_CFG,
 | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR |
 | ||||
| +		 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR);
 | ||||
| +
 | ||||
| +	/* writeback FIFO */
 | ||||
| +	wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0),
 | ||||
| +		 MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
 | ||||
| +	wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1),
 | ||||
| +		 MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
 | ||||
| +
 | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0),
 | ||||
| +		 MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1),
 | ||||
| +		 MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
 | ||||
| +
 | ||||
| +	/* prefetch ring status */
 | ||||
| +	wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG,
 | ||||
| +		 MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG,
 | ||||
| +		 MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR);
 | ||||
| +
 | ||||
| +	/* writeback ring status */
 | ||||
| +	wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG,
 | ||||
| +		 MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR);
 | ||||
| +	wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG,
 | ||||
| +		 MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void
 | ||||
|  mtk_wdma_tx_reset(struct mtk_wed_device *dev) | ||||
|  { | ||||
|  	u32 status, mask = MTK_WDMA_GLO_CFG_TX_DMA_BUSY; | ||||
| @@ -203,6 +366,7 @@ mtk_wdma_tx_reset(struct mtk_wed_device
 | ||||
|  			       !(status & mask), 0, 10000)) | ||||
|  		dev_err(dev->hw->dev, "tx reset failed\n"); | ||||
|   | ||||
| +	mtk_wdma_v3_tx_reset(dev);
 | ||||
|  	wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX); | ||||
|  	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); | ||||
|   | ||||
| @@ -1406,13 +1570,33 @@ mtk_wed_rx_reset(struct mtk_wed_device *
 | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| +	if (dev->wlan.hw_rro) {
 | ||||
| +		wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_IND_CMD_EN);
 | ||||
| +		mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_HW_STS,
 | ||||
| +				  MTK_WED_RX_IND_CMD_BUSY);
 | ||||
| +		mtk_wed_reset(dev, MTK_WED_RESET_RRO_RX_TO_PG);
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RX_DRV_EN); | ||||
|  	ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, | ||||
|  				MTK_WED_WPDMA_RX_D_RX_DRV_BUSY); | ||||
| +	if (!ret && mtk_wed_is_v3_or_greater(dev->hw))
 | ||||
| +		ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
 | ||||
| +					MTK_WED_WPDMA_RX_D_PREF_BUSY);
 | ||||
|  	if (ret) { | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT); | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_D_DRV); | ||||
|  	} else { | ||||
| +		if (mtk_wed_is_v3_or_greater(dev->hw)) {
 | ||||
| +			/* 1.a. disable prefetch HW */
 | ||||
| +			wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
 | ||||
| +				MTK_WED_WPDMA_RX_D_PREF_EN);
 | ||||
| +			mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
 | ||||
| +					  MTK_WED_WPDMA_RX_D_PREF_BUSY);
 | ||||
| +			wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
 | ||||
| +				MTK_WED_WPDMA_RX_D_RST_DRV_IDX_ALL);
 | ||||
| +		}
 | ||||
| +
 | ||||
|  		wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, | ||||
|  			MTK_WED_WPDMA_RX_D_RST_CRX_IDX | | ||||
|  			MTK_WED_WPDMA_RX_D_RST_DRV_IDX); | ||||
| @@ -1440,23 +1624,52 @@ mtk_wed_rx_reset(struct mtk_wed_device *
 | ||||
|  		wed_w32(dev, MTK_WED_RROQM_RST_IDX, 0); | ||||
|  	} | ||||
|   | ||||
| +	if (dev->wlan.hw_rro) {
 | ||||
| +		/* disable rro msdu page drv */
 | ||||
| +		wed_clr(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
 | ||||
| +			MTK_WED_RRO_MSDU_PG_DRV_EN);
 | ||||
| +
 | ||||
| +		/* disable rro data drv */
 | ||||
| +		wed_clr(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_EN);
 | ||||
| +
 | ||||
| +		/* rro msdu page drv reset */
 | ||||
| +		wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
 | ||||
| +			MTK_WED_RRO_MSDU_PG_DRV_CLR);
 | ||||
| +		mtk_wed_poll_busy(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
 | ||||
| +				  MTK_WED_RRO_MSDU_PG_DRV_CLR);
 | ||||
| +
 | ||||
| +		/* rro data drv reset */
 | ||||
| +		wed_w32(dev, MTK_WED_RRO_RX_D_CFG(2),
 | ||||
| +			MTK_WED_RRO_RX_D_DRV_CLR);
 | ||||
| +		mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_D_CFG(2),
 | ||||
| +				  MTK_WED_RRO_RX_D_DRV_CLR);
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	/* reset route qm */ | ||||
|  	wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN); | ||||
|  	ret = mtk_wed_poll_busy(dev, MTK_WED_CTRL, | ||||
|  				MTK_WED_CTRL_RX_ROUTE_QM_BUSY); | ||||
| -	if (ret)
 | ||||
| +	if (ret) {
 | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM); | ||||
| -	else
 | ||||
| -		wed_set(dev, MTK_WED_RTQM_GLO_CFG,
 | ||||
| -			MTK_WED_RTQM_Q_RST);
 | ||||
| +	} else if (mtk_wed_is_v3_or_greater(dev->hw)) {
 | ||||
| +		wed_set(dev, MTK_WED_RTQM_RST, BIT(0));
 | ||||
| +		wed_clr(dev, MTK_WED_RTQM_RST, BIT(0));
 | ||||
| +		mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM);
 | ||||
| +	} else {
 | ||||
| +		wed_set(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
 | ||||
| +	}
 | ||||
|   | ||||
|  	/* reset tx wdma */ | ||||
|  	mtk_wdma_tx_reset(dev); | ||||
|   | ||||
|  	/* reset tx wdma drv */ | ||||
|  	wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_TX_DRV_EN); | ||||
| -	mtk_wed_poll_busy(dev, MTK_WED_CTRL,
 | ||||
| -			  MTK_WED_CTRL_WDMA_INT_AGENT_BUSY);
 | ||||
| +	if (mtk_wed_is_v3_or_greater(dev->hw))
 | ||||
| +		mtk_wed_poll_busy(dev, MTK_WED_WPDMA_STATUS,
 | ||||
| +				  MTK_WED_WPDMA_STATUS_TX_DRV);
 | ||||
| +	else
 | ||||
| +		mtk_wed_poll_busy(dev, MTK_WED_CTRL,
 | ||||
| +				  MTK_WED_CTRL_WDMA_INT_AGENT_BUSY);
 | ||||
|  	mtk_wed_reset(dev, MTK_WED_RESET_WDMA_TX_DRV); | ||||
|   | ||||
|  	/* reset wed rx dma */ | ||||
| @@ -1477,6 +1690,14 @@ mtk_wed_rx_reset(struct mtk_wed_device *
 | ||||
|  			  MTK_WED_CTRL_WED_RX_BM_BUSY); | ||||
|  	mtk_wed_reset(dev, MTK_WED_RESET_RX_BM); | ||||
|   | ||||
| +	if (dev->wlan.hw_rro) {
 | ||||
| +		wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_PG_BM_EN);
 | ||||
| +		mtk_wed_poll_busy(dev, MTK_WED_CTRL,
 | ||||
| +				  MTK_WED_CTRL_WED_RX_PG_BM_BUSY);
 | ||||
| +		wed_set(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM);
 | ||||
| +		wed_clr(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM);
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	/* wo change to enable state */ | ||||
|  	val = MTK_WED_WO_STATE_ENABLE; | ||||
|  	ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, | ||||
| @@ -1494,6 +1715,7 @@ mtk_wed_rx_reset(struct mtk_wed_device *
 | ||||
|  				   false); | ||||
|  	} | ||||
|  	mtk_wed_free_rx_buffer(dev); | ||||
| +	mtk_wed_hwrro_free_buffer(dev);
 | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -1527,15 +1749,41 @@ mtk_wed_reset_dma(struct mtk_wed_device
 | ||||
|   | ||||
|  	/* 2. reset WDMA rx DMA */ | ||||
|  	busy = !!mtk_wdma_rx_reset(dev); | ||||
| -	wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
 | ||||
| +	if (mtk_wed_is_v3_or_greater(dev->hw)) {
 | ||||
| +		val = MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE |
 | ||||
| +		      wed_r32(dev, MTK_WED_WDMA_GLO_CFG);
 | ||||
| +		val &= ~MTK_WED_WDMA_GLO_CFG_RX_DRV_EN;
 | ||||
| +		wed_w32(dev, MTK_WED_WDMA_GLO_CFG, val);
 | ||||
| +	} else {
 | ||||
| +		wed_clr(dev, MTK_WED_WDMA_GLO_CFG,
 | ||||
| +			MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	if (!busy) | ||||
|  		busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_GLO_CFG, | ||||
|  					 MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY); | ||||
| +	if (!busy && mtk_wed_is_v3_or_greater(dev->hw))
 | ||||
| +		busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG,
 | ||||
| +					 MTK_WED_WDMA_RX_PREF_BUSY);
 | ||||
|   | ||||
|  	if (busy) { | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT); | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WDMA_RX_DRV); | ||||
|  	} else { | ||||
| +		if (mtk_wed_is_v3_or_greater(dev->hw)) {
 | ||||
| +			/* 1.a. disable prefetch HW */
 | ||||
| +			wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG,
 | ||||
| +				MTK_WED_WDMA_RX_PREF_EN);
 | ||||
| +			mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG,
 | ||||
| +					  MTK_WED_WDMA_RX_PREF_BUSY);
 | ||||
| +			wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG,
 | ||||
| +				MTK_WED_WDMA_RX_PREF_DDONE2_EN);
 | ||||
| +
 | ||||
| +			/* 2. Reset dma index */
 | ||||
| +			wed_w32(dev, MTK_WED_WDMA_RESET_IDX,
 | ||||
| +				MTK_WED_WDMA_RESET_IDX_RX_ALL);
 | ||||
| +		}
 | ||||
| +
 | ||||
|  		wed_w32(dev, MTK_WED_WDMA_RESET_IDX, | ||||
|  			MTK_WED_WDMA_RESET_IDX_RX | MTK_WED_WDMA_RESET_IDX_DRV); | ||||
|  		wed_w32(dev, MTK_WED_WDMA_RESET_IDX, 0); | ||||
| @@ -1551,8 +1799,13 @@ mtk_wed_reset_dma(struct mtk_wed_device
 | ||||
|  	wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); | ||||
|   | ||||
|  	for (i = 0; i < 100; i++) { | ||||
| -		val = wed_r32(dev, MTK_WED_TX_BM_INTF);
 | ||||
| -		if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40)
 | ||||
| +		if (mtk_wed_is_v1(dev->hw))
 | ||||
| +			val = FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP,
 | ||||
| +					wed_r32(dev, MTK_WED_TX_BM_INTF));
 | ||||
| +		else
 | ||||
| +			val = FIELD_GET(MTK_WED_TX_TKID_INTF_TKFIFO_FDEP,
 | ||||
| +					wed_r32(dev, MTK_WED_TX_TKID_INTF));
 | ||||
| +		if (val == 0x40)
 | ||||
|  			break; | ||||
|  	} | ||||
|   | ||||
| @@ -1574,6 +1827,8 @@ mtk_wed_reset_dma(struct mtk_wed_device
 | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT); | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV); | ||||
|  		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_DRV); | ||||
| +		if (mtk_wed_is_v3_or_greater(dev->hw))
 | ||||
| +			wed_w32(dev, MTK_WED_RX1_CTRL2, 0);
 | ||||
|  	} else { | ||||
|  		wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, | ||||
|  			MTK_WED_WPDMA_RESET_IDX_TX | | ||||
| @@ -1590,7 +1845,14 @@ mtk_wed_reset_dma(struct mtk_wed_device
 | ||||
|  		wed_w32(dev, MTK_WED_RESET_IDX, 0); | ||||
|  	} | ||||
|   | ||||
| -	mtk_wed_rx_reset(dev);
 | ||||
| +	if (mtk_wed_is_v3_or_greater(dev->hw)) {
 | ||||
| +		/* reset amsdu engine */
 | ||||
| +		wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN);
 | ||||
| +		mtk_wed_reset(dev, MTK_WED_RESET_TX_AMSDU);
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	if (mtk_wed_get_rx_capa(dev))
 | ||||
| +		mtk_wed_rx_reset(dev);
 | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| @@ -1842,6 +2104,7 @@ mtk_wed_dma_enable(struct mtk_wed_device
 | ||||
|  			MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4); | ||||
|   | ||||
|  		wdma_set(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN); | ||||
| +		wdma_set(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
 | ||||
|  	} | ||||
|   | ||||
|  	wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, | ||||
| @@ -1905,6 +2168,12 @@ mtk_wed_start_hw_rro(struct mtk_wed_devi
 | ||||
|  	if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro) | ||||
|  		return; | ||||
|   | ||||
| +	if (reset) {
 | ||||
| +		wed_set(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
 | ||||
| +			MTK_WED_RRO_MSDU_PG_DRV_EN);
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_MSDU_PG_DRV_CLR); | ||||
|  	wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, | ||||
|  		MTK_WED_RRO_MSDU_PG_DRV_CLR); | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
 | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
 | ||||
| @@ -28,6 +28,8 @@ struct mtk_wdma_desc {
 | ||||
|  #define MTK_WED_RESET					0x008 | ||||
|  #define MTK_WED_RESET_TX_BM				BIT(0) | ||||
|  #define MTK_WED_RESET_RX_BM				BIT(1) | ||||
| +#define MTK_WED_RESET_RX_PG_BM				BIT(2)
 | ||||
| +#define MTK_WED_RESET_RRO_RX_TO_PG			BIT(3)
 | ||||
|  #define MTK_WED_RESET_TX_FREE_AGENT			BIT(4) | ||||
|  #define MTK_WED_RESET_WPDMA_TX_DRV			BIT(8) | ||||
|  #define MTK_WED_RESET_WPDMA_RX_DRV			BIT(9) | ||||
| @@ -106,6 +108,9 @@ struct mtk_wdma_desc {
 | ||||
|  #define MTK_WED_STATUS					0x060 | ||||
|  #define MTK_WED_STATUS_TX				GENMASK(15, 8) | ||||
|   | ||||
| +#define MTK_WED_WPDMA_STATUS				0x068
 | ||||
| +#define MTK_WED_WPDMA_STATUS_TX_DRV			GENMASK(15, 8)
 | ||||
| +
 | ||||
|  #define MTK_WED_TX_BM_CTRL				0x080 | ||||
|  #define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM			GENMASK(6, 0) | ||||
|  #define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM			GENMASK(22, 16) | ||||
| @@ -140,6 +145,9 @@ struct mtk_wdma_desc {
 | ||||
|  #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM		GENMASK(22, 16) | ||||
|  #define MTK_WED_TX_TKID_CTRL_PAUSE			BIT(28) | ||||
|   | ||||
| +#define MTK_WED_TX_TKID_INTF				0x0dc
 | ||||
| +#define MTK_WED_TX_TKID_INTF_TKFIFO_FDEP		GENMASK(25, 16)
 | ||||
| +
 | ||||
|  #define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3		GENMASK(7, 0) | ||||
|  #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3		GENMASK(23, 16) | ||||
|   | ||||
| @@ -190,6 +198,7 @@ struct mtk_wdma_desc {
 | ||||
|  #define MTK_WED_RING_RX_DATA(_n)			(0x420 + (_n) * 0x10) | ||||
|   | ||||
|  #define MTK_WED_SCR0					0x3c0 | ||||
| +#define MTK_WED_RX1_CTRL2				0x418
 | ||||
|  #define MTK_WED_WPDMA_INT_TRIGGER			0x504 | ||||
|  #define MTK_WED_WPDMA_INT_TRIGGER_RX_DONE		BIT(1) | ||||
|  #define MTK_WED_WPDMA_INT_TRIGGER_TX_DONE		GENMASK(5, 4) | ||||
| @@ -303,6 +312,7 @@ struct mtk_wdma_desc {
 | ||||
|   | ||||
|  #define MTK_WED_WPDMA_RX_D_RST_IDX			0x760 | ||||
|  #define MTK_WED_WPDMA_RX_D_RST_CRX_IDX			GENMASK(17, 16) | ||||
| +#define MTK_WED_WPDMA_RX_D_RST_DRV_IDX_ALL		BIT(20)
 | ||||
|  #define MTK_WED_WPDMA_RX_D_RST_DRV_IDX			GENMASK(25, 24) | ||||
|   | ||||
|  #define MTK_WED_WPDMA_RX_GLO_CFG			0x76c | ||||
| @@ -313,6 +323,7 @@ struct mtk_wdma_desc {
 | ||||
|   | ||||
|  #define MTK_WED_WPDMA_RX_D_PREF_CFG			0x7b4 | ||||
|  #define MTK_WED_WPDMA_RX_D_PREF_EN			BIT(0) | ||||
| +#define MTK_WED_WPDMA_RX_D_PREF_BUSY			BIT(1)
 | ||||
|  #define MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE		GENMASK(12, 8) | ||||
|  #define MTK_WED_WPDMA_RX_D_PREF_LOW_THRES		GENMASK(21, 16) | ||||
|   | ||||
| @@ -334,11 +345,13 @@ struct mtk_wdma_desc {
 | ||||
|   | ||||
|  #define MTK_WED_WDMA_RX_PREF_CFG			0x950 | ||||
|  #define MTK_WED_WDMA_RX_PREF_EN				BIT(0) | ||||
| +#define MTK_WED_WDMA_RX_PREF_BUSY			BIT(1)
 | ||||
|  #define MTK_WED_WDMA_RX_PREF_BURST_SIZE			GENMASK(12, 8) | ||||
|  #define MTK_WED_WDMA_RX_PREF_LOW_THRES			GENMASK(21, 16) | ||||
|  #define MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR		BIT(24) | ||||
|  #define MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR		BIT(25) | ||||
|  #define MTK_WED_WDMA_RX_PREF_DDONE2_EN			BIT(26) | ||||
| +#define MTK_WED_WDMA_RX_PREF_DDONE2_BUSY		BIT(27)
 | ||||
|   | ||||
|  #define MTK_WED_WDMA_RX_PREF_FIFO_CFG			0x95C | ||||
|  #define MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR		BIT(0) | ||||
| @@ -367,6 +380,7 @@ struct mtk_wdma_desc {
 | ||||
|   | ||||
|  #define MTK_WED_WDMA_RESET_IDX				0xa08 | ||||
|  #define MTK_WED_WDMA_RESET_IDX_RX			GENMASK(17, 16) | ||||
| +#define MTK_WED_WDMA_RESET_IDX_RX_ALL			BIT(20)
 | ||||
|  #define MTK_WED_WDMA_RESET_IDX_DRV			GENMASK(25, 24) | ||||
|   | ||||
|  #define MTK_WED_WDMA_INT_CLR				0xa24 | ||||
| @@ -437,21 +451,62 @@ struct mtk_wdma_desc {
 | ||||
|  #define MTK_WDMA_INT_MASK_RX_DELAY			BIT(30) | ||||
|  #define MTK_WDMA_INT_MASK_RX_COHERENT			BIT(31) | ||||
|   | ||||
| +#define MTK_WDMA_XDMA_TX_FIFO_CFG			0x238
 | ||||
| +#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR	BIT(0)
 | ||||
| +#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR	BIT(4)
 | ||||
| +#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR	BIT(8)
 | ||||
| +#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR	BIT(12)
 | ||||
| +
 | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG			0x23c
 | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR	BIT(0)
 | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR	BIT(4)
 | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR	BIT(8)
 | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR	BIT(12)
 | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR	BIT(15)
 | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR	BIT(18)
 | ||||
| +#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR	BIT(21)
 | ||||
| +
 | ||||
|  #define MTK_WDMA_INT_GRP1				0x250 | ||||
|  #define MTK_WDMA_INT_GRP2				0x254 | ||||
|   | ||||
|  #define MTK_WDMA_PREF_TX_CFG				0x2d0 | ||||
|  #define MTK_WDMA_PREF_TX_CFG_PREF_EN			BIT(0) | ||||
| +#define MTK_WDMA_PREF_TX_CFG_PREF_BUSY			BIT(1)
 | ||||
|   | ||||
|  #define MTK_WDMA_PREF_RX_CFG				0x2dc | ||||
|  #define MTK_WDMA_PREF_RX_CFG_PREF_EN			BIT(0) | ||||
| +#define MTK_WDMA_PREF_RX_CFG_PREF_BUSY			BIT(1)
 | ||||
| +
 | ||||
| +#define MTK_WDMA_PREF_RX_FIFO_CFG			0x2e0
 | ||||
| +#define MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR		BIT(0)
 | ||||
| +#define MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR		BIT(16)
 | ||||
| +
 | ||||
| +#define MTK_WDMA_PREF_TX_FIFO_CFG			0x2d4
 | ||||
| +#define MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR		BIT(0)
 | ||||
| +#define MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR		BIT(16)
 | ||||
| +
 | ||||
| +#define MTK_WDMA_PREF_SIDX_CFG				0x2e4
 | ||||
| +#define MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR		GENMASK(3, 0)
 | ||||
| +#define MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR		GENMASK(5, 4)
 | ||||
|   | ||||
|  #define MTK_WDMA_WRBK_TX_CFG				0x300 | ||||
| +#define MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY			BIT(0)
 | ||||
|  #define MTK_WDMA_WRBK_TX_CFG_WRBK_EN			BIT(30) | ||||
|   | ||||
| +#define MTK_WDMA_WRBK_TX_FIFO_CFG(_n)			(0x304 + (_n) * 0x4)
 | ||||
| +#define MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR		BIT(0)
 | ||||
| +
 | ||||
|  #define MTK_WDMA_WRBK_RX_CFG				0x344 | ||||
| +#define MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY			BIT(0)
 | ||||
|  #define MTK_WDMA_WRBK_RX_CFG_WRBK_EN			BIT(30) | ||||
|   | ||||
| +#define MTK_WDMA_WRBK_RX_FIFO_CFG(_n)			(0x348 + (_n) * 0x4)
 | ||||
| +#define MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR		BIT(0)
 | ||||
| +
 | ||||
| +#define MTK_WDMA_WRBK_SIDX_CFG				0x388
 | ||||
| +#define MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR		GENMASK(3, 0)
 | ||||
| +#define MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR		GENMASK(5, 4)
 | ||||
| +
 | ||||
|  #define MTK_PCIE_MIRROR_MAP(n)				((n) ? 0x4 : 0x0) | ||||
|  #define MTK_PCIE_MIRROR_MAP_EN				BIT(0) | ||||
|  #define MTK_PCIE_MIRROR_MAP_WED_ID			BIT(1) | ||||
| @@ -465,6 +520,8 @@ struct mtk_wdma_desc {
 | ||||
|  #define MTK_WED_RTQM_Q_DBG_BYPASS			BIT(5) | ||||
|  #define MTK_WED_RTQM_TXDMAD_FPORT			GENMASK(23, 20) | ||||
|   | ||||
| +#define MTK_WED_RTQM_RST				0xb04
 | ||||
| +
 | ||||
|  #define MTK_WED_RTQM_IGRS0_I2HW_DMAD_CNT		0xb1c | ||||
|  #define MTK_WED_RTQM_IGRS0_I2H_DMAD_CNT(_n)		(0xb20 + (_n) * 0x4) | ||||
|  #define	MTK_WED_RTQM_IGRS0_I2HW_PKT_CNT			0xb28 | ||||
| @@ -653,6 +710,9 @@ struct mtk_wdma_desc {
 | ||||
|  #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR		BIT(17) | ||||
|  #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG	GENMASK(22, 18) | ||||
|   | ||||
| +#define MTK_WED_RRO_RX_HW_STS				0xf00
 | ||||
| +#define MTK_WED_RX_IND_CMD_BUSY				GENMASK(31, 0)
 | ||||
| +
 | ||||
|  #define MTK_WED_RX_IND_CMD_CNT0				0xf20 | ||||
|  #define MTK_WED_RX_IND_CMD_DBG_CNT_EN			BIT(31) | ||||
|   | ||||
|  | @ -0,0 +1,122 @@ | |||
| From 788d30daa8f97f06166b6a63f0e51f2a4c2f036a Mon Sep 17 00:00:00 2001 | ||||
| From: Hayes Wang <hayeswang@realtek.com> | ||||
| Date: Tue, 26 Sep 2023 19:17:14 +0800 | ||||
| Subject: [PATCH] r8152: use napi_gro_frags | ||||
| 
 | ||||
| Use napi_gro_frags() for the skb of fragments when the work_done is less | ||||
| than budget. | ||||
| 
 | ||||
| Signed-off-by: Hayes Wang <hayeswang@realtek.com> | ||||
| Link: https://lore.kernel.org/r/20230926111714.9448-434-nic_swsd@realtek.com | ||||
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> | ||||
| ---
 | ||||
|  drivers/net/usb/r8152.c | 67 ++++++++++++++++++++++++++++++----------- | ||||
|  1 file changed, 50 insertions(+), 17 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/net/usb/r8152.c
 | ||||
| +++ b/drivers/net/usb/r8152.c
 | ||||
| @@ -2584,8 +2584,9 @@ static int rx_bottom(struct r8152 *tp, i
 | ||||
|  		while (urb->actual_length > len_used) { | ||||
|  			struct net_device *netdev = tp->netdev; | ||||
|  			struct net_device_stats *stats = &netdev->stats; | ||||
| -			unsigned int pkt_len, rx_frag_head_sz;
 | ||||
| +			unsigned int pkt_len, rx_frag_head_sz, len;
 | ||||
|  			struct sk_buff *skb; | ||||
| +			bool use_frags;
 | ||||
|   | ||||
|  			WARN_ON_ONCE(skb_queue_len(&tp->rx_queue) >= 1000); | ||||
|   | ||||
| @@ -2598,45 +2599,77 @@ static int rx_bottom(struct r8152 *tp, i
 | ||||
|  				break; | ||||
|   | ||||
|  			pkt_len -= ETH_FCS_LEN; | ||||
| +			len = pkt_len;
 | ||||
|  			rx_data += sizeof(struct rx_desc); | ||||
|   | ||||
| -			if (!agg_free || tp->rx_copybreak > pkt_len)
 | ||||
| -				rx_frag_head_sz = pkt_len;
 | ||||
| +			if (!agg_free || tp->rx_copybreak > len)
 | ||||
| +				use_frags = false;
 | ||||
|  			else | ||||
| -				rx_frag_head_sz = tp->rx_copybreak;
 | ||||
| +				use_frags = true;
 | ||||
| +
 | ||||
| +			if (use_frags) {
 | ||||
| +				/* If the budget is exhausted, the packet
 | ||||
| +				 * would be queued in the driver. That is,
 | ||||
| +				 * napi_gro_frags() wouldn't be called, so
 | ||||
| +				 * we couldn't use napi_get_frags().
 | ||||
| +				 */
 | ||||
| +				if (work_done >= budget) {
 | ||||
| +					rx_frag_head_sz = tp->rx_copybreak;
 | ||||
| +					skb = napi_alloc_skb(napi,
 | ||||
| +							     rx_frag_head_sz);
 | ||||
| +				} else {
 | ||||
| +					rx_frag_head_sz = 0;
 | ||||
| +					skb = napi_get_frags(napi);
 | ||||
| +				}
 | ||||
| +			} else {
 | ||||
| +				rx_frag_head_sz = 0;
 | ||||
| +				skb = napi_alloc_skb(napi, len);
 | ||||
| +			}
 | ||||
|   | ||||
| -			skb = napi_alloc_skb(napi, rx_frag_head_sz);
 | ||||
|  			if (!skb) { | ||||
|  				stats->rx_dropped++; | ||||
|  				goto find_next_rx; | ||||
|  			} | ||||
|   | ||||
|  			skb->ip_summed = r8152_rx_csum(tp, rx_desc); | ||||
| -			memcpy(skb->data, rx_data, rx_frag_head_sz);
 | ||||
| -			skb_put(skb, rx_frag_head_sz);
 | ||||
| -			pkt_len -= rx_frag_head_sz;
 | ||||
| -			rx_data += rx_frag_head_sz;
 | ||||
| -			if (pkt_len) {
 | ||||
| +			rtl_rx_vlan_tag(rx_desc, skb);
 | ||||
| +
 | ||||
| +			if (use_frags) {
 | ||||
| +				if (rx_frag_head_sz) {
 | ||||
| +					memcpy(skb->data, rx_data,
 | ||||
| +					       rx_frag_head_sz);
 | ||||
| +					skb_put(skb, rx_frag_head_sz);
 | ||||
| +					len -= rx_frag_head_sz;
 | ||||
| +					rx_data += rx_frag_head_sz;
 | ||||
| +					skb->protocol = eth_type_trans(skb,
 | ||||
| +								       netdev);
 | ||||
| +				}
 | ||||
| +
 | ||||
|  				skb_add_rx_frag(skb, 0, agg->page, | ||||
|  						agg_offset(agg, rx_data), | ||||
| -						pkt_len,
 | ||||
| -						SKB_DATA_ALIGN(pkt_len));
 | ||||
| +						len, SKB_DATA_ALIGN(len));
 | ||||
|  				get_page(agg->page); | ||||
| +			} else {
 | ||||
| +				memcpy(skb->data, rx_data, len);
 | ||||
| +				skb_put(skb, len);
 | ||||
| +				skb->protocol = eth_type_trans(skb, netdev);
 | ||||
|  			} | ||||
|   | ||||
| -			skb->protocol = eth_type_trans(skb, netdev);
 | ||||
| -			rtl_rx_vlan_tag(rx_desc, skb);
 | ||||
|  			if (work_done < budget) { | ||||
| +				if (use_frags)
 | ||||
| +					napi_gro_frags(napi);
 | ||||
| +				else
 | ||||
| +					napi_gro_receive(napi, skb);
 | ||||
| +
 | ||||
|  				work_done++; | ||||
|  				stats->rx_packets++; | ||||
| -				stats->rx_bytes += skb->len;
 | ||||
| -				napi_gro_receive(napi, skb);
 | ||||
| +				stats->rx_bytes += pkt_len;
 | ||||
|  			} else { | ||||
|  				__skb_queue_tail(&tp->rx_queue, skb); | ||||
|  			} | ||||
|   | ||||
|  find_next_rx: | ||||
| -			rx_data = rx_agg_align(rx_data + pkt_len + ETH_FCS_LEN);
 | ||||
| +			rx_data = rx_agg_align(rx_data + len + ETH_FCS_LEN);
 | ||||
|  			rx_desc = (struct rx_desc *)rx_data; | ||||
|  			len_used = agg_offset(agg, rx_data); | ||||
|  			len_used += sizeof(struct rx_desc); | ||||
|  | @ -0,0 +1,207 @@ | |||
| From a64c3c1357275b1fd61bc9734f638cdb5d8a8bbb Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> | ||||
| Date: Mon, 18 Sep 2023 18:11:02 +0200 | ||||
| Subject: [PATCH 4/6] leds: turris-omnia: Make set_brightness() more efficient | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| Implement caching of the LED color and state values that are sent to MCU | ||||
| in order to make the set_brightness() operation more efficient by | ||||
| avoiding I2C transactions which are not needed. | ||||
| 
 | ||||
| On Turris Omnia's MCU, which acts as the RGB LED controller, each LED | ||||
| has a RGB color, and a ON/OFF state, which are configurable via I2C | ||||
| commands CMD_LED_COLOR and CMD_LED_STATE. | ||||
| 
 | ||||
| The CMD_LED_COLOR command sends 5 bytes and the CMD_LED_STATE command 2 | ||||
| bytes over the I2C bus, which operates at 100 kHz. With I2C overhead | ||||
| this allows ~1670 color changing commands and ~3200 state changing | ||||
| commands per second (or around 1000 color + state changes per second). | ||||
| This may seem more than enough, but the issue is that the I2C bus is | ||||
| shared with another peripheral, the MCU. The MCU exposes an interrupt | ||||
| interface, and it can trigger hundreds of interrupts per second. Each | ||||
| time, we need to read the interrupt state register over this I2C bus. | ||||
| Whenever we are sending a LED color/state changing command, the | ||||
| interrupt reading is waiting. | ||||
| 
 | ||||
| Currently, every time LED brightness or LED multi intensity is changed, | ||||
| we send a CMD_LED_STATE command, and if the computed color (brightness | ||||
| adjusted multi_intensity) is non-zero, we also send a CMD_LED_COLOR | ||||
| command. | ||||
| 
 | ||||
| Consider for example the situation when we have a netdev trigger enabled | ||||
| for a LED. The netdev trigger does not change the LED color, only the | ||||
| brightness (either to 0 or to currently configured brightness), and so | ||||
| there is no need to send the CMD_LED_COLOR command. But each change of | ||||
| brightness to 0 sends one CMD_LED_STATE command, and each change of | ||||
| brightness to max_brightness sends one CMD_LED_STATE command and one | ||||
| CMD_LED_COLOR command: | ||||
|     set_brightness(0)   ->  CMD_LED_STATE | ||||
|     set_brightness(255) ->  CMD_LED_STATE + CMD_LED_COLOR | ||||
|                                             (unnecessary) | ||||
| 
 | ||||
| We can avoid the unnecessary I2C transactions if we cache the values of | ||||
| state and color that are sent to the controller. If the color does not | ||||
| change from the one previously sent, there is no need to do the | ||||
| CMD_LED_COLOR I2C transaction, and if the state does not change, there | ||||
| is no need to do the CMD_LED_STATE transaction. | ||||
| 
 | ||||
| Because we need to make sure that our cached values are consistent with | ||||
| the controller state, add explicit setting of the LED color to white at | ||||
| probe time (this is the default setting when MCU resets, but does not | ||||
| necessarily need to be the case, for example if U-Boot played with the | ||||
| LED colors). | ||||
| 
 | ||||
| Signed-off-by: Marek Behún <kabel@kernel.org> | ||||
| Link: https://lore.kernel.org/r/20230918161104.20860-3-kabel@kernel.org | ||||
| Signed-off-by: Lee Jones <lee@kernel.org> | ||||
| ---
 | ||||
|  drivers/leds/leds-turris-omnia.c | 96 ++++++++++++++++++++++++++------ | ||||
|  1 file changed, 78 insertions(+), 18 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/leds/leds-turris-omnia.c
 | ||||
| +++ b/drivers/leds/leds-turris-omnia.c
 | ||||
| @@ -30,6 +30,8 @@
 | ||||
|  struct omnia_led { | ||||
|  	struct led_classdev_mc mc_cdev; | ||||
|  	struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS]; | ||||
| +	u8 cached_channels[OMNIA_LED_NUM_CHANNELS];
 | ||||
| +	bool on;
 | ||||
|  	int reg; | ||||
|  }; | ||||
|   | ||||
| @@ -72,36 +74,82 @@ static int omnia_cmd_read_u8(const struc
 | ||||
|  		return -EIO; | ||||
|  } | ||||
|   | ||||
| +static int omnia_led_send_color_cmd(const struct i2c_client *client,
 | ||||
| +				    struct omnia_led *led)
 | ||||
| +{
 | ||||
| +	char cmd[5];
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	cmd[0] = CMD_LED_COLOR;
 | ||||
| +	cmd[1] = led->reg;
 | ||||
| +	cmd[2] = led->subled_info[0].brightness;
 | ||||
| +	cmd[3] = led->subled_info[1].brightness;
 | ||||
| +	cmd[4] = led->subled_info[2].brightness;
 | ||||
| +
 | ||||
| +	/* Send the color change command */
 | ||||
| +	ret = i2c_master_send(client, cmd, 5);
 | ||||
| +	if (ret < 0)
 | ||||
| +		return ret;
 | ||||
| +
 | ||||
| +	/* Cache the RGB channel brightnesses */
 | ||||
| +	for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i)
 | ||||
| +		led->cached_channels[i] = led->subled_info[i].brightness;
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* Determine if the computed RGB channels are different from the cached ones */
 | ||||
| +static bool omnia_led_channels_changed(struct omnia_led *led)
 | ||||
| +{
 | ||||
| +	for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i)
 | ||||
| +		if (led->subled_info[i].brightness != led->cached_channels[i])
 | ||||
| +			return true;
 | ||||
| +
 | ||||
| +	return false;
 | ||||
| +}
 | ||||
| +
 | ||||
|  static int omnia_led_brightness_set_blocking(struct led_classdev *cdev, | ||||
|  					     enum led_brightness brightness) | ||||
|  { | ||||
|  	struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); | ||||
|  	struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); | ||||
|  	struct omnia_led *led = to_omnia_led(mc_cdev); | ||||
| -	u8 buf[5], state;
 | ||||
| -	int ret;
 | ||||
| +	int err = 0;
 | ||||
|   | ||||
|  	mutex_lock(&leds->lock); | ||||
|   | ||||
| -	led_mc_calc_color_components(&led->mc_cdev, brightness);
 | ||||
| +	/*
 | ||||
| +	 * Only recalculate RGB brightnesses from intensities if brightness is
 | ||||
| +	 * non-zero. Otherwise we won't be using them and we can save ourselves
 | ||||
| +	 * some software divisions (Omnia's CPU does not implement the division
 | ||||
| +	 * instruction).
 | ||||
| +	 */
 | ||||
| +	if (brightness) {
 | ||||
| +		led_mc_calc_color_components(mc_cdev, brightness);
 | ||||
| +
 | ||||
| +		/*
 | ||||
| +		 * Send color command only if brightness is non-zero and the RGB
 | ||||
| +		 * channel brightnesses changed.
 | ||||
| +		 */
 | ||||
| +		if (omnia_led_channels_changed(led))
 | ||||
| +			err = omnia_led_send_color_cmd(leds->client, led);
 | ||||
| +	}
 | ||||
|   | ||||
| -	buf[0] = CMD_LED_COLOR;
 | ||||
| -	buf[1] = led->reg;
 | ||||
| -	buf[2] = mc_cdev->subled_info[0].brightness;
 | ||||
| -	buf[3] = mc_cdev->subled_info[1].brightness;
 | ||||
| -	buf[4] = mc_cdev->subled_info[2].brightness;
 | ||||
| -
 | ||||
| -	state = CMD_LED_STATE_LED(led->reg);
 | ||||
| -	if (buf[2] || buf[3] || buf[4])
 | ||||
| -		state |= CMD_LED_STATE_ON;
 | ||||
| -
 | ||||
| -	ret = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state);
 | ||||
| -	if (ret >= 0 && (state & CMD_LED_STATE_ON))
 | ||||
| -		ret = i2c_master_send(leds->client, buf, 5);
 | ||||
| +	/* Send on/off state change only if (bool)brightness changed */
 | ||||
| +	if (!err && !brightness != !led->on) {
 | ||||
| +		u8 state = CMD_LED_STATE_LED(led->reg);
 | ||||
| +
 | ||||
| +		if (brightness)
 | ||||
| +			state |= CMD_LED_STATE_ON;
 | ||||
| +
 | ||||
| +		err = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state);
 | ||||
| +		if (!err)
 | ||||
| +			led->on = !!brightness;
 | ||||
| +	}
 | ||||
|   | ||||
|  	mutex_unlock(&leds->lock); | ||||
|   | ||||
| -	return ret;
 | ||||
| +	return err;
 | ||||
|  } | ||||
|   | ||||
|  static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, | ||||
| @@ -129,11 +177,15 @@ static int omnia_led_register(struct i2c
 | ||||
|  	} | ||||
|   | ||||
|  	led->subled_info[0].color_index = LED_COLOR_ID_RED; | ||||
| -	led->subled_info[0].channel = 0;
 | ||||
|  	led->subled_info[1].color_index = LED_COLOR_ID_GREEN; | ||||
| -	led->subled_info[1].channel = 1;
 | ||||
|  	led->subled_info[2].color_index = LED_COLOR_ID_BLUE; | ||||
| -	led->subled_info[2].channel = 2;
 | ||||
| +
 | ||||
| +	/* Initial color is white */
 | ||||
| +	for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) {
 | ||||
| +		led->subled_info[i].intensity = 255;
 | ||||
| +		led->subled_info[i].brightness = 255;
 | ||||
| +		led->subled_info[i].channel = i;
 | ||||
| +	}
 | ||||
|   | ||||
|  	led->mc_cdev.subled_info = led->subled_info; | ||||
|  	led->mc_cdev.num_colors = OMNIA_LED_NUM_CHANNELS; | ||||
| @@ -162,6 +214,14 @@ static int omnia_led_register(struct i2c
 | ||||
|  		return ret; | ||||
|  	} | ||||
|   | ||||
| +	/* Set initial color and cache it */
 | ||||
| +	ret = omnia_led_send_color_cmd(client, led);
 | ||||
| +	if (ret < 0) {
 | ||||
| +		dev_err(dev, "Cannot set LED %pOF initial color: %i\n", np,
 | ||||
| +			ret);
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	ret = devm_led_classdev_multicolor_register_ext(dev, &led->mc_cdev, | ||||
|  							&init_data); | ||||
|  	if (ret < 0) { | ||||
|  | @ -0,0 +1,202 @@ | |||
| From e965e0f6de60874fc0a0caed9a9e0122999e0c7b Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> | ||||
| Date: Mon, 18 Sep 2023 18:11:03 +0200 | ||||
| Subject: [PATCH 5/6] leds: turris-omnia: Support HW controlled mode via | ||||
|  private trigger | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| Add support for enabling MCU controlled mode of the Turris Omnia LEDs | ||||
| via a LED private trigger called "omnia-mcu". Recall that private LED | ||||
| triggers will only be listed in the sysfs trigger file for LEDs that | ||||
| support them (currently there is no user of this mechanism). | ||||
| 
 | ||||
| When in MCU controlled mode, the user can still set LED color, but the | ||||
| blinking is done by MCU, which does different things for different LEDs: | ||||
| - WAN LED is blinked according to the LED[0] pin of the WAN PHY
 | ||||
| - LAN LEDs are blinked according to the LED[0] output of the
 | ||||
|   corresponding port of the LAN switch | ||||
| - PCIe LEDs are blinked according to the logical OR of the MiniPCIe port
 | ||||
|   LED pins | ||||
| 
 | ||||
| In the future I want to make the netdev trigger to transparently offload | ||||
| the blinking to the HW if user sets compatible settings for the netdev | ||||
| trigger (for LEDs associated with network devices). | ||||
| There was some work on this already, and hopefully we will be able to | ||||
| complete it sometime, but for now there are still multiple blockers for | ||||
| this, and even if there weren't, we still would not be able to configure | ||||
| HW controlled mode for the LEDs associated with MiniPCIe ports. | ||||
| 
 | ||||
| In the meantime let's support HW controlled mode via the private LED | ||||
| trigger mechanism. If, in the future, we manage to complete the netdev | ||||
| trigger offloading, we can still keep this private trigger for backwards | ||||
| compatibility, if needed. | ||||
| 
 | ||||
| We also set "omnia-mcu" to cdev->default_trigger, so that the MCU keeps | ||||
| control until the user first wants to take over it. If a different | ||||
| default trigger is specified in device-tree via the | ||||
| 'linux,default-trigger' property, LED class will overwrite | ||||
| cdev->default_trigger, and so the DT property will be respected. | ||||
| 
 | ||||
| Signed-off-by: Marek Behún <kabel@kernel.org> | ||||
| Link: https://lore.kernel.org/r/20230918161104.20860-4-kabel@kernel.org | ||||
| Signed-off-by: Lee Jones <lee@kernel.org> | ||||
| ---
 | ||||
|  drivers/leds/Kconfig             |  1 + | ||||
|  drivers/leds/leds-turris-omnia.c | 98 +++++++++++++++++++++++++++++--- | ||||
|  2 files changed, 91 insertions(+), 8 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/leds/Kconfig
 | ||||
| +++ b/drivers/leds/Kconfig
 | ||||
| @@ -187,6 +187,7 @@ config LEDS_TURRIS_OMNIA
 | ||||
|  	depends on I2C | ||||
|  	depends on MACH_ARMADA_38X || COMPILE_TEST | ||||
|  	depends on OF | ||||
| +	select LEDS_TRIGGERS
 | ||||
|  	help | ||||
|  	  This option enables basic support for the LEDs found on the front | ||||
|  	  side of CZ.NIC's Turris Omnia router. There are 12 RGB LEDs on the | ||||
| --- a/drivers/leds/leds-turris-omnia.c
 | ||||
| +++ b/drivers/leds/leds-turris-omnia.c
 | ||||
| @@ -31,7 +31,7 @@ struct omnia_led {
 | ||||
|  	struct led_classdev_mc mc_cdev; | ||||
|  	struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS]; | ||||
|  	u8 cached_channels[OMNIA_LED_NUM_CHANNELS]; | ||||
| -	bool on;
 | ||||
| +	bool on, hwtrig;
 | ||||
|  	int reg; | ||||
|  }; | ||||
|   | ||||
| @@ -120,12 +120,14 @@ static int omnia_led_brightness_set_bloc
 | ||||
|   | ||||
|  	/* | ||||
|  	 * Only recalculate RGB brightnesses from intensities if brightness is | ||||
| -	 * non-zero. Otherwise we won't be using them and we can save ourselves
 | ||||
| -	 * some software divisions (Omnia's CPU does not implement the division
 | ||||
| -	 * instruction).
 | ||||
| +	 * non-zero (if it is zero and the LED is in HW blinking mode, we use
 | ||||
| +	 * max_brightness as brightness). Otherwise we won't be using them and
 | ||||
| +	 * we can save ourselves some software divisions (Omnia's CPU does not
 | ||||
| +	 * implement the division instruction).
 | ||||
|  	 */ | ||||
| -	if (brightness) {
 | ||||
| -		led_mc_calc_color_components(mc_cdev, brightness);
 | ||||
| +	if (brightness || led->hwtrig) {
 | ||||
| +		led_mc_calc_color_components(mc_cdev, brightness ?:
 | ||||
| +						      cdev->max_brightness);
 | ||||
|   | ||||
|  		/* | ||||
|  		 * Send color command only if brightness is non-zero and the RGB | ||||
| @@ -135,8 +137,11 @@ static int omnia_led_brightness_set_bloc
 | ||||
|  			err = omnia_led_send_color_cmd(leds->client, led); | ||||
|  	} | ||||
|   | ||||
| -	/* Send on/off state change only if (bool)brightness changed */
 | ||||
| -	if (!err && !brightness != !led->on) {
 | ||||
| +	/*
 | ||||
| +	 * Send on/off state change only if (bool)brightness changed and the LED
 | ||||
| +	 * is not being blinked by HW.
 | ||||
| +	 */
 | ||||
| +	if (!err && !led->hwtrig && !brightness != !led->on) {
 | ||||
|  		u8 state = CMD_LED_STATE_LED(led->reg); | ||||
|   | ||||
|  		if (brightness) | ||||
| @@ -152,6 +157,71 @@ static int omnia_led_brightness_set_bloc
 | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| +static struct led_hw_trigger_type omnia_hw_trigger_type;
 | ||||
| +
 | ||||
| +static int omnia_hwtrig_activate(struct led_classdev *cdev)
 | ||||
| +{
 | ||||
| +	struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
 | ||||
| +	struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent);
 | ||||
| +	struct omnia_led *led = to_omnia_led(mc_cdev);
 | ||||
| +	int err = 0;
 | ||||
| +
 | ||||
| +	mutex_lock(&leds->lock);
 | ||||
| +
 | ||||
| +	if (!led->on) {
 | ||||
| +		/*
 | ||||
| +		 * If the LED is off (brightness was set to 0), the last
 | ||||
| +		 * configured color was not necessarily sent to the MCU.
 | ||||
| +		 * Recompute with max_brightness and send if needed.
 | ||||
| +		 */
 | ||||
| +		led_mc_calc_color_components(mc_cdev, cdev->max_brightness);
 | ||||
| +
 | ||||
| +		if (omnia_led_channels_changed(led))
 | ||||
| +			err = omnia_led_send_color_cmd(leds->client, led);
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	if (!err) {
 | ||||
| +		/* Put the LED into MCU controlled mode */
 | ||||
| +		err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE,
 | ||||
| +					 CMD_LED_MODE_LED(led->reg));
 | ||||
| +		if (!err)
 | ||||
| +			led->hwtrig = true;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	mutex_unlock(&leds->lock);
 | ||||
| +
 | ||||
| +	return err;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void omnia_hwtrig_deactivate(struct led_classdev *cdev)
 | ||||
| +{
 | ||||
| +	struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent);
 | ||||
| +	struct omnia_led *led = to_omnia_led(lcdev_to_mccdev(cdev));
 | ||||
| +	int err;
 | ||||
| +
 | ||||
| +	mutex_lock(&leds->lock);
 | ||||
| +
 | ||||
| +	led->hwtrig = false;
 | ||||
| +
 | ||||
| +	/* Put the LED into software mode */
 | ||||
| +	err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE,
 | ||||
| +				 CMD_LED_MODE_LED(led->reg) |
 | ||||
| +				 CMD_LED_MODE_USER);
 | ||||
| +
 | ||||
| +	mutex_unlock(&leds->lock);
 | ||||
| +
 | ||||
| +	if (err < 0)
 | ||||
| +		dev_err(cdev->dev, "Cannot put LED to software mode: %i\n",
 | ||||
| +			err);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static struct led_trigger omnia_hw_trigger = {
 | ||||
| +	.name		= "omnia-mcu",
 | ||||
| +	.activate	= omnia_hwtrig_activate,
 | ||||
| +	.deactivate	= omnia_hwtrig_deactivate,
 | ||||
| +	.trigger_type	= &omnia_hw_trigger_type,
 | ||||
| +};
 | ||||
| +
 | ||||
|  static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, | ||||
|  			      struct device_node *np) | ||||
|  { | ||||
| @@ -195,6 +265,12 @@ static int omnia_led_register(struct i2c
 | ||||
|  	cdev = &led->mc_cdev.led_cdev; | ||||
|  	cdev->max_brightness = 255; | ||||
|  	cdev->brightness_set_blocking = omnia_led_brightness_set_blocking; | ||||
| +	cdev->trigger_type = &omnia_hw_trigger_type;
 | ||||
| +	/*
 | ||||
| +	 * Use the omnia-mcu trigger as the default trigger. It may be rewritten
 | ||||
| +	 * by LED class from the linux,default-trigger property.
 | ||||
| +	 */
 | ||||
| +	cdev->default_trigger = omnia_hw_trigger.name;
 | ||||
|   | ||||
|  	/* put the LED into software mode */ | ||||
|  	ret = omnia_cmd_write_u8(client, CMD_LED_MODE, | ||||
| @@ -308,6 +384,12 @@ static int omnia_leds_probe(struct i2c_c
 | ||||
|   | ||||
|  	mutex_init(&leds->lock); | ||||
|   | ||||
| +	ret = devm_led_trigger_register(dev, &omnia_hw_trigger);
 | ||||
| +	if (ret < 0) {
 | ||||
| +		dev_err(dev, "Cannot register private LED trigger: %d\n", ret);
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	led = &leds->leds[0]; | ||||
|  	for_each_available_child_of_node(np, child) { | ||||
|  		ret = omnia_led_register(client, led, child); | ||||
|  | @ -0,0 +1,244 @@ | |||
| From 0efb3f9609d3de5a7d8c31e3835d7eb3e6adce79 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> | ||||
| Date: Mon, 18 Sep 2023 18:11:04 +0200 | ||||
| Subject: [PATCH 6/6] leds: turris-omnia: Add support for enabling/disabling HW | ||||
|  gamma correction | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| If the MCU on Turris Omnia is running newer firmware versions, the LED | ||||
| controller supports RGB gamma correction (and enables it by default for | ||||
| newer boards). | ||||
| 
 | ||||
| Determine whether the gamma correction setting feature is supported and | ||||
| add the ability to set it via sysfs attribute file. | ||||
| 
 | ||||
| Signed-off-by: Marek Behún <kabel@kernel.org> | ||||
| Link: https://lore.kernel.org/r/20230918161104.20860-5-kabel@kernel.org | ||||
| Signed-off-by: Lee Jones <lee@kernel.org> | ||||
| ---
 | ||||
|  .../sysfs-class-led-driver-turris-omnia       |  14 ++ | ||||
|  drivers/leds/leds-turris-omnia.c              | 137 +++++++++++++++--- | ||||
|  2 files changed, 134 insertions(+), 17 deletions(-) | ||||
| 
 | ||||
| --- a/Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia
 | ||||
| +++ b/Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia
 | ||||
| @@ -12,3 +12,17 @@ Description:	(RW) On the front panel of
 | ||||
|  		able to change this setting from software. | ||||
|   | ||||
|  		Format: %i | ||||
| +
 | ||||
| +What:		/sys/class/leds/<led>/device/gamma_correction
 | ||||
| +Date:		August 2023
 | ||||
| +KernelVersion:	6.6
 | ||||
| +Contact:	Marek Behún <kabel@kernel.org>
 | ||||
| +Description:	(RW) Newer versions of the microcontroller firmware of the
 | ||||
| +		Turris Omnia router support gamma correction for the RGB LEDs.
 | ||||
| +		This feature can be enabled/disabled by writing to this file.
 | ||||
| +
 | ||||
| +		If the feature is not supported because the MCU firmware is too
 | ||||
| +		old, the file always reads as 0, and writing to the file results
 | ||||
| +		in the EOPNOTSUPP error.
 | ||||
| +
 | ||||
| +		Format: %i
 | ||||
| --- a/drivers/leds/leds-turris-omnia.c
 | ||||
| +++ b/drivers/leds/leds-turris-omnia.c
 | ||||
| @@ -15,17 +15,30 @@
 | ||||
|  #define OMNIA_BOARD_LEDS	12 | ||||
|  #define OMNIA_LED_NUM_CHANNELS	3 | ||||
|   | ||||
| -#define CMD_LED_MODE		3
 | ||||
| -#define CMD_LED_MODE_LED(l)	((l) & 0x0f)
 | ||||
| -#define CMD_LED_MODE_USER	0x10
 | ||||
| -
 | ||||
| -#define CMD_LED_STATE		4
 | ||||
| -#define CMD_LED_STATE_LED(l)	((l) & 0x0f)
 | ||||
| -#define CMD_LED_STATE_ON	0x10
 | ||||
| -
 | ||||
| -#define CMD_LED_COLOR		5
 | ||||
| -#define CMD_LED_SET_BRIGHTNESS	7
 | ||||
| -#define CMD_LED_GET_BRIGHTNESS	8
 | ||||
| +/* MCU controller commands at I2C address 0x2a */
 | ||||
| +#define OMNIA_MCU_I2C_ADDR		0x2a
 | ||||
| +
 | ||||
| +#define CMD_GET_STATUS_WORD		0x01
 | ||||
| +#define STS_FEATURES_SUPPORTED		BIT(2)
 | ||||
| +
 | ||||
| +#define CMD_GET_FEATURES		0x10
 | ||||
| +#define FEAT_LED_GAMMA_CORRECTION	BIT(5)
 | ||||
| +
 | ||||
| +/* LED controller commands at I2C address 0x2b */
 | ||||
| +#define CMD_LED_MODE			0x03
 | ||||
| +#define CMD_LED_MODE_LED(l)		((l) & 0x0f)
 | ||||
| +#define CMD_LED_MODE_USER		0x10
 | ||||
| +
 | ||||
| +#define CMD_LED_STATE			0x04
 | ||||
| +#define CMD_LED_STATE_LED(l)		((l) & 0x0f)
 | ||||
| +#define CMD_LED_STATE_ON		0x10
 | ||||
| +
 | ||||
| +#define CMD_LED_COLOR			0x05
 | ||||
| +#define CMD_LED_SET_BRIGHTNESS		0x07
 | ||||
| +#define CMD_LED_GET_BRIGHTNESS		0x08
 | ||||
| +
 | ||||
| +#define CMD_SET_GAMMA_CORRECTION	0x30
 | ||||
| +#define CMD_GET_GAMMA_CORRECTION	0x31
 | ||||
|   | ||||
|  struct omnia_led { | ||||
|  	struct led_classdev_mc mc_cdev; | ||||
| @@ -40,6 +53,7 @@ struct omnia_led {
 | ||||
|  struct omnia_leds { | ||||
|  	struct i2c_client *client; | ||||
|  	struct mutex lock; | ||||
| +	bool has_gamma_correction;
 | ||||
|  	struct omnia_led leds[]; | ||||
|  }; | ||||
|   | ||||
| @@ -50,30 +64,42 @@ static int omnia_cmd_write_u8(const stru
 | ||||
|  	return i2c_master_send(client, buf, sizeof(buf)); | ||||
|  } | ||||
|   | ||||
| -static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)
 | ||||
| +static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd,
 | ||||
| +			      void *reply, size_t len)
 | ||||
|  { | ||||
|  	struct i2c_msg msgs[2]; | ||||
| -	u8 reply;
 | ||||
|  	int ret; | ||||
|   | ||||
| -	msgs[0].addr = client->addr;
 | ||||
| +	msgs[0].addr = addr;
 | ||||
|  	msgs[0].flags = 0; | ||||
|  	msgs[0].len = 1; | ||||
|  	msgs[0].buf = &cmd; | ||||
| -	msgs[1].addr = client->addr;
 | ||||
| +	msgs[1].addr = addr;
 | ||||
|  	msgs[1].flags = I2C_M_RD; | ||||
| -	msgs[1].len = 1;
 | ||||
| -	msgs[1].buf = &reply;
 | ||||
| +	msgs[1].len = len;
 | ||||
| +	msgs[1].buf = reply;
 | ||||
|   | ||||
| -	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
 | ||||
| +	ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
 | ||||
|  	if (likely(ret == ARRAY_SIZE(msgs))) | ||||
| -		return reply;
 | ||||
| +		return len;
 | ||||
|  	else if (ret < 0) | ||||
|  		return ret; | ||||
|  	else | ||||
|  		return -EIO; | ||||
|  } | ||||
|   | ||||
| +static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)
 | ||||
| +{
 | ||||
| +	u8 reply;
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	ret = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1);
 | ||||
| +	if (ret < 0)
 | ||||
| +		return ret;
 | ||||
| +
 | ||||
| +	return reply;
 | ||||
| +}
 | ||||
| +
 | ||||
|  static int omnia_led_send_color_cmd(const struct i2c_client *client, | ||||
|  				    struct omnia_led *led) | ||||
|  { | ||||
| @@ -352,12 +378,74 @@ static ssize_t brightness_store(struct d
 | ||||
|  } | ||||
|  static DEVICE_ATTR_RW(brightness); | ||||
|   | ||||
| +static ssize_t gamma_correction_show(struct device *dev,
 | ||||
| +				     struct device_attribute *a, char *buf)
 | ||||
| +{
 | ||||
| +	struct i2c_client *client = to_i2c_client(dev);
 | ||||
| +	struct omnia_leds *leds = i2c_get_clientdata(client);
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	if (leds->has_gamma_correction) {
 | ||||
| +		ret = omnia_cmd_read_u8(client, CMD_GET_GAMMA_CORRECTION);
 | ||||
| +		if (ret < 0)
 | ||||
| +			return ret;
 | ||||
| +	} else {
 | ||||
| +		ret = 0;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	return sysfs_emit(buf, "%d\n", !!ret);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static ssize_t gamma_correction_store(struct device *dev,
 | ||||
| +				      struct device_attribute *a,
 | ||||
| +				      const char *buf, size_t count)
 | ||||
| +{
 | ||||
| +	struct i2c_client *client = to_i2c_client(dev);
 | ||||
| +	struct omnia_leds *leds = i2c_get_clientdata(client);
 | ||||
| +	bool val;
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	if (!leds->has_gamma_correction)
 | ||||
| +		return -EOPNOTSUPP;
 | ||||
| +
 | ||||
| +	if (kstrtobool(buf, &val) < 0)
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	ret = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val);
 | ||||
| +
 | ||||
| +	return ret < 0 ? ret : count;
 | ||||
| +}
 | ||||
| +static DEVICE_ATTR_RW(gamma_correction);
 | ||||
| +
 | ||||
|  static struct attribute *omnia_led_controller_attrs[] = { | ||||
|  	&dev_attr_brightness.attr, | ||||
| +	&dev_attr_gamma_correction.attr,
 | ||||
|  	NULL, | ||||
|  }; | ||||
|  ATTRIBUTE_GROUPS(omnia_led_controller); | ||||
|   | ||||
| +static int omnia_mcu_get_features(const struct i2c_client *client)
 | ||||
| +{
 | ||||
| +	u16 reply;
 | ||||
| +	int err;
 | ||||
| +
 | ||||
| +	err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR,
 | ||||
| +				 CMD_GET_STATUS_WORD, &reply, sizeof(reply));
 | ||||
| +	if (err < 0)
 | ||||
| +		return err;
 | ||||
| +
 | ||||
| +	/* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */
 | ||||
| +	if (!(le16_to_cpu(reply) & STS_FEATURES_SUPPORTED))
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
| +	err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR,
 | ||||
| +				 CMD_GET_FEATURES, &reply, sizeof(reply));
 | ||||
| +	if (err < 0)
 | ||||
| +		return err;
 | ||||
| +
 | ||||
| +	return le16_to_cpu(reply);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static int omnia_leds_probe(struct i2c_client *client) | ||||
|  { | ||||
|  	struct device *dev = &client->dev; | ||||
| @@ -382,6 +470,21 @@ static int omnia_leds_probe(struct i2c_c
 | ||||
|  	leds->client = client; | ||||
|  	i2c_set_clientdata(client, leds); | ||||
|   | ||||
| +	ret = omnia_mcu_get_features(client);
 | ||||
| +	if (ret < 0) {
 | ||||
| +		dev_err(dev, "Cannot determine MCU supported features: %d\n",
 | ||||
| +			ret);
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	leds->has_gamma_correction = ret & FEAT_LED_GAMMA_CORRECTION;
 | ||||
| +	if (!leds->has_gamma_correction) {
 | ||||
| +		dev_info(dev,
 | ||||
| +			 "Your board's MCU firmware does not support the LED gamma correction feature.\n");
 | ||||
| +		dev_info(dev,
 | ||||
| +			 "Consider upgrading MCU firmware with the omnia-mcutool utility.\n");
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	mutex_init(&leds->lock); | ||||
|   | ||||
|  	ret = devm_led_trigger_register(dev, &omnia_hw_trigger); | ||||
|  | @ -0,0 +1,167 @@ | |||
| From ffec49d391c5f0195360912b216aa24dbc9b53c8 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> | ||||
| Date: Mon, 16 Oct 2023 16:15:38 +0200 | ||||
| Subject: [PATCH] leds: turris-omnia: Fix brightness setting and trigger | ||||
|  activating | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| I have improperly refactored commits | ||||
|   4d5ed2621c24 ("leds: turris-omnia: Make set_brightness() more efficient") | ||||
| and | ||||
|   aaf38273cf76 ("leds: turris-omnia: Support HW controlled mode via private trigger") | ||||
| after Lee requested a change in API semantics of the new functions I | ||||
| introduced in commit | ||||
|   28350bc0ac77 ("leds: turris-omnia: Do not use SMBUS calls"). | ||||
| 
 | ||||
| Before the change, the function omnia_cmd_write_u8() returned 0 on | ||||
| success, and afterwards it returned a positive value (number of bytes | ||||
| written). The latter version was applied, but the following commits did | ||||
| not properly account for this change. | ||||
| 
 | ||||
| This results in non-functional LED's .brightness_set_blocking() and | ||||
| trigger's .activate() methods. | ||||
| 
 | ||||
| The main reasoning behind the semantics change was that read/write | ||||
| methods should return the number of read/written bytes on success. | ||||
| It was pointed to me [1] that this is not always true (for example the | ||||
| regmap API does not do so), and since the driver never uses this number | ||||
| of read/written bytes information, I decided to fix this issue by | ||||
| changing the functions to the original semantics (return 0 on success). | ||||
| 
 | ||||
| [1] https://lore.kernel.org/linux-gpio/ZQnn+Gi0xVlsGCYA@smile.fi.intel.com/ | ||||
| 
 | ||||
| Fixes: 28350bc0ac77 ("leds: turris-omnia: Do not use SMBUS calls") | ||||
| Signed-off-by: Marek Behún <kabel@kernel.org> | ||||
| ---
 | ||||
|  drivers/leds/leds-turris-omnia.c | 37 +++++++++++++++++--------------- | ||||
|  1 file changed, 20 insertions(+), 17 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/leds/leds-turris-omnia.c
 | ||||
| +++ b/drivers/leds/leds-turris-omnia.c
 | ||||
| @@ -60,8 +60,11 @@ struct omnia_leds {
 | ||||
|  static int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd, u8 val) | ||||
|  { | ||||
|  	u8 buf[2] = { cmd, val }; | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	ret = i2c_master_send(client, buf, sizeof(buf));
 | ||||
|   | ||||
| -	return i2c_master_send(client, buf, sizeof(buf));
 | ||||
| +	return ret < 0 ? ret : 0;
 | ||||
|  } | ||||
|   | ||||
|  static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd, | ||||
| @@ -81,7 +84,7 @@ static int omnia_cmd_read_raw(struct i2c
 | ||||
|   | ||||
|  	ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs)); | ||||
|  	if (likely(ret == ARRAY_SIZE(msgs))) | ||||
| -		return len;
 | ||||
| +		return 0;
 | ||||
|  	else if (ret < 0) | ||||
|  		return ret; | ||||
|  	else | ||||
| @@ -91,11 +94,11 @@ static int omnia_cmd_read_raw(struct i2c
 | ||||
|  static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) | ||||
|  { | ||||
|  	u8 reply; | ||||
| -	int ret;
 | ||||
| +	int err;
 | ||||
|   | ||||
| -	ret = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1);
 | ||||
| -	if (ret < 0)
 | ||||
| -		return ret;
 | ||||
| +	err = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1);
 | ||||
| +	if (err)
 | ||||
| +		return err;
 | ||||
|   | ||||
|  	return reply; | ||||
|  } | ||||
| @@ -236,7 +239,7 @@ static void omnia_hwtrig_deactivate(stru
 | ||||
|   | ||||
|  	mutex_unlock(&leds->lock); | ||||
|   | ||||
| -	if (err < 0)
 | ||||
| +	if (err)
 | ||||
|  		dev_err(cdev->dev, "Cannot put LED to software mode: %i\n", | ||||
|  			err); | ||||
|  } | ||||
| @@ -302,7 +305,7 @@ static int omnia_led_register(struct i2c
 | ||||
|  	ret = omnia_cmd_write_u8(client, CMD_LED_MODE, | ||||
|  				 CMD_LED_MODE_LED(led->reg) | | ||||
|  				 CMD_LED_MODE_USER); | ||||
| -	if (ret < 0) {
 | ||||
| +	if (ret) {
 | ||||
|  		dev_err(dev, "Cannot set LED %pOF to software mode: %i\n", np, | ||||
|  			ret); | ||||
|  		return ret; | ||||
| @@ -311,7 +314,7 @@ static int omnia_led_register(struct i2c
 | ||||
|  	/* disable the LED */ | ||||
|  	ret = omnia_cmd_write_u8(client, CMD_LED_STATE, | ||||
|  				 CMD_LED_STATE_LED(led->reg)); | ||||
| -	if (ret < 0) {
 | ||||
| +	if (ret) {
 | ||||
|  		dev_err(dev, "Cannot set LED %pOF brightness: %i\n", np, ret); | ||||
|  		return ret; | ||||
|  	} | ||||
| @@ -364,7 +367,7 @@ static ssize_t brightness_store(struct d
 | ||||
|  { | ||||
|  	struct i2c_client *client = to_i2c_client(dev); | ||||
|  	unsigned long brightness; | ||||
| -	int ret;
 | ||||
| +	int err;
 | ||||
|   | ||||
|  	if (kstrtoul(buf, 10, &brightness)) | ||||
|  		return -EINVAL; | ||||
| @@ -372,9 +375,9 @@ static ssize_t brightness_store(struct d
 | ||||
|  	if (brightness > 100) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| -	ret = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness);
 | ||||
| +	err = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness);
 | ||||
|   | ||||
| -	return ret < 0 ? ret : count;
 | ||||
| +	return err ?: count;
 | ||||
|  } | ||||
|  static DEVICE_ATTR_RW(brightness); | ||||
|   | ||||
| @@ -403,7 +406,7 @@ static ssize_t gamma_correction_store(st
 | ||||
|  	struct i2c_client *client = to_i2c_client(dev); | ||||
|  	struct omnia_leds *leds = i2c_get_clientdata(client); | ||||
|  	bool val; | ||||
| -	int ret;
 | ||||
| +	int err;
 | ||||
|   | ||||
|  	if (!leds->has_gamma_correction) | ||||
|  		return -EOPNOTSUPP; | ||||
| @@ -411,9 +414,9 @@ static ssize_t gamma_correction_store(st
 | ||||
|  	if (kstrtobool(buf, &val) < 0) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| -	ret = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val);
 | ||||
| +	err = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val);
 | ||||
|   | ||||
| -	return ret < 0 ? ret : count;
 | ||||
| +	return err ?: count;
 | ||||
|  } | ||||
|  static DEVICE_ATTR_RW(gamma_correction); | ||||
|   | ||||
| @@ -431,7 +434,7 @@ static int omnia_mcu_get_features(const
 | ||||
|   | ||||
|  	err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, | ||||
|  				 CMD_GET_STATUS_WORD, &reply, sizeof(reply)); | ||||
| -	if (err < 0)
 | ||||
| +	if (err)
 | ||||
|  		return err; | ||||
|   | ||||
|  	/* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */ | ||||
| @@ -440,7 +443,7 @@ static int omnia_mcu_get_features(const
 | ||||
|   | ||||
|  	err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, | ||||
|  				 CMD_GET_FEATURES, &reply, sizeof(reply)); | ||||
| -	if (err < 0)
 | ||||
| +	if (err)
 | ||||
|  		return err; | ||||
|   | ||||
|  	return le16_to_cpu(reply); | ||||
|  | @ -0,0 +1,37 @@ | |||
| From 16724d6ea40a2c9315f5a0d81005dfa4d7a6da24 Mon Sep 17 00:00:00 2001 | ||||
| From: Luca Weiss <luca.weiss@fairphone.com> | ||||
| Date: Fri, 20 Oct 2023 11:55:40 +0100 | ||||
| Subject: [PATCH] nvmem: qfprom: Mark core clk as optional | ||||
| 
 | ||||
| On some platforms like sc7280 on non-ChromeOS devices the core clock | ||||
| cannot be touched by Linux so we cannot provide it. Mark it as optional | ||||
| as accessing qfprom for reading works without it but we still prohibit | ||||
| writing if we cannot provide the clock. | ||||
| 
 | ||||
| Signed-off-by: Luca Weiss <luca.weiss@fairphone.com> | ||||
| Reviewed-by: Douglas Anderson <dianders@chromium.org> | ||||
| Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | ||||
| Link: https://lore.kernel.org/r/20231020105545.216052-2-srinivas.kandagatla@linaro.org | ||||
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||||
| ---
 | ||||
|  drivers/nvmem/qfprom.c | 6 +++--- | ||||
|  1 file changed, 3 insertions(+), 3 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/nvmem/qfprom.c
 | ||||
| +++ b/drivers/nvmem/qfprom.c
 | ||||
| @@ -423,12 +423,12 @@ static int qfprom_probe(struct platform_
 | ||||
|  		if (IS_ERR(priv->vcc)) | ||||
|  			return PTR_ERR(priv->vcc); | ||||
|   | ||||
| -		priv->secclk = devm_clk_get(dev, "core");
 | ||||
| +		priv->secclk = devm_clk_get_optional(dev, "core");
 | ||||
|  		if (IS_ERR(priv->secclk)) | ||||
|  			return dev_err_probe(dev, PTR_ERR(priv->secclk), "Error getting clock\n"); | ||||
|   | ||||
| -		/* Only enable writing if we have SoC data. */
 | ||||
| -		if (priv->soc_data)
 | ||||
| +		/* Only enable writing if we have SoC data and a valid clock */
 | ||||
| +		if (priv->soc_data && priv->secclk)
 | ||||
|  			econfig.reg_write = qfprom_reg_write; | ||||
|  	} | ||||
|   | ||||
|  | @ -0,0 +1,330 @@ | |||
| From 2cc3b37f5b6df8189d55d0e812d9658ce256dfec Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Fri, 20 Oct 2023 11:55:41 +0100 | ||||
| Subject: [PATCH] nvmem: add explicit config option to read old syntax fixed OF | ||||
|  cells | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| Binding for fixed NVMEM cells defined directly as NVMEM device subnodes | ||||
| has been deprecated. It has been replaced by the "fixed-layout" NVMEM | ||||
| layout binding. | ||||
| 
 | ||||
| New syntax is meant to be clearer and should help avoiding imprecise | ||||
| bindings. | ||||
| 
 | ||||
| NVMEM subsystem already supports the new binding. It should be a good | ||||
| idea to limit support for old syntax to existing drivers that actually | ||||
| support & use it (we can't break backward compatibility!). That way we | ||||
| additionally encourage new bindings & drivers to ignore deprecated | ||||
| binding. | ||||
| 
 | ||||
| It wasn't clear (to me) if rtc and w1 code actually uses old syntax | ||||
| fixed cells. I enabled them to don't risk any breakage. | ||||
| 
 | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| [for meson-{efuse,mx-efuse}.c] | ||||
| Acked-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> | ||||
| [for mtk-efuse.c, nvmem/core.c, nvmem-provider.h] | ||||
| Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> | ||||
| [MT8192, MT8195 Chromebooks] | ||||
| Tested-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> | ||||
| [for microchip-otpc.c] | ||||
| Reviewed-by: Claudiu Beznea <claudiu.beznea@microchip.com> | ||||
| [SAMA7G5-EK] | ||||
| Tested-by: Claudiu Beznea <claudiu.beznea@microchip.com> | ||||
| Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com> | ||||
| Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | ||||
| Link: https://lore.kernel.org/r/20231020105545.216052-3-srinivas.kandagatla@linaro.org | ||||
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||||
| ---
 | ||||
|  drivers/mtd/mtdcore.c          | 2 ++ | ||||
|  drivers/nvmem/apple-efuses.c   | 1 + | ||||
|  drivers/nvmem/core.c           | 8 +++++--- | ||||
|  drivers/nvmem/imx-ocotp-scu.c  | 1 + | ||||
|  drivers/nvmem/imx-ocotp.c      | 1 + | ||||
|  drivers/nvmem/meson-efuse.c    | 1 + | ||||
|  drivers/nvmem/meson-mx-efuse.c | 1 + | ||||
|  drivers/nvmem/microchip-otpc.c | 1 + | ||||
|  drivers/nvmem/mtk-efuse.c      | 1 + | ||||
|  drivers/nvmem/qcom-spmi-sdam.c | 1 + | ||||
|  drivers/nvmem/qfprom.c         | 1 + | ||||
|  drivers/nvmem/rave-sp-eeprom.c | 1 + | ||||
|  drivers/nvmem/rockchip-efuse.c | 1 + | ||||
|  drivers/nvmem/sc27xx-efuse.c   | 1 + | ||||
|  drivers/nvmem/sec-qfprom.c     | 1 + | ||||
|  drivers/nvmem/sprd-efuse.c     | 1 + | ||||
|  drivers/nvmem/stm32-romem.c    | 1 + | ||||
|  drivers/nvmem/sunplus-ocotp.c  | 1 + | ||||
|  drivers/nvmem/sunxi_sid.c      | 1 + | ||||
|  drivers/nvmem/uniphier-efuse.c | 1 + | ||||
|  drivers/nvmem/zynqmp_nvmem.c   | 1 + | ||||
|  drivers/rtc/nvmem.c            | 1 + | ||||
|  drivers/w1/slaves/w1_ds250x.c  | 1 + | ||||
|  include/linux/nvmem-provider.h | 2 ++ | ||||
|  24 files changed, 30 insertions(+), 3 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/mtd/mtdcore.c
 | ||||
| +++ b/drivers/mtd/mtdcore.c
 | ||||
| @@ -552,6 +552,7 @@ static int mtd_nvmem_add(struct mtd_info
 | ||||
|  	config.dev = &mtd->dev; | ||||
|  	config.name = dev_name(&mtd->dev); | ||||
|  	config.owner = THIS_MODULE; | ||||
| +	config.add_legacy_fixed_of_cells = of_device_is_compatible(node, "nvmem-cells");
 | ||||
|  	config.reg_read = mtd_nvmem_reg_read; | ||||
|  	config.size = mtd->size; | ||||
|  	config.word_size = 1; | ||||
| @@ -898,6 +899,7 @@ static struct nvmem_device *mtd_otp_nvme
 | ||||
|  	config.name = compatible; | ||||
|  	config.id = NVMEM_DEVID_AUTO; | ||||
|  	config.owner = THIS_MODULE; | ||||
| +	config.add_legacy_fixed_of_cells = true;
 | ||||
|  	config.type = NVMEM_TYPE_OTP; | ||||
|  	config.root_only = true; | ||||
|  	config.ignore_wp = true; | ||||
| --- a/drivers/nvmem/apple-efuses.c
 | ||||
| +++ b/drivers/nvmem/apple-efuses.c
 | ||||
| @@ -36,6 +36,7 @@ static int apple_efuses_probe(struct pla
 | ||||
|  	struct resource *res; | ||||
|  	struct nvmem_config config = { | ||||
|  		.dev = &pdev->dev, | ||||
| +		.add_legacy_fixed_of_cells = true,
 | ||||
|  		.read_only = true, | ||||
|  		.reg_read = apple_efuses_read, | ||||
|  		.stride = sizeof(u32), | ||||
| --- a/drivers/nvmem/core.c
 | ||||
| +++ b/drivers/nvmem/core.c
 | ||||
| @@ -1003,9 +1003,11 @@ struct nvmem_device *nvmem_register(cons
 | ||||
|  	if (rval) | ||||
|  		goto err_remove_cells; | ||||
|   | ||||
| -	rval = nvmem_add_cells_from_legacy_of(nvmem);
 | ||||
| -	if (rval)
 | ||||
| -		goto err_remove_cells;
 | ||||
| +	if (config->add_legacy_fixed_of_cells) {
 | ||||
| +		rval = nvmem_add_cells_from_legacy_of(nvmem);
 | ||||
| +		if (rval)
 | ||||
| +			goto err_remove_cells;
 | ||||
| +	}
 | ||||
|   | ||||
|  	rval = nvmem_add_cells_from_fixed_layout(nvmem); | ||||
|  	if (rval) | ||||
| --- a/drivers/nvmem/imx-ocotp-scu.c
 | ||||
| +++ b/drivers/nvmem/imx-ocotp-scu.c
 | ||||
| @@ -220,6 +220,7 @@ static int imx_scu_ocotp_write(void *con
 | ||||
|   | ||||
|  static struct nvmem_config imx_scu_ocotp_nvmem_config = { | ||||
|  	.name = "imx-scu-ocotp", | ||||
| +	.add_legacy_fixed_of_cells = true,
 | ||||
|  	.read_only = false, | ||||
|  	.word_size = 4, | ||||
|  	.stride = 1, | ||||
| --- a/drivers/nvmem/imx-ocotp.c
 | ||||
| +++ b/drivers/nvmem/imx-ocotp.c
 | ||||
| @@ -615,6 +615,7 @@ static int imx_ocotp_probe(struct platfo
 | ||||
|  		return PTR_ERR(priv->clk); | ||||
|   | ||||
|  	priv->params = of_device_get_match_data(&pdev->dev); | ||||
| +	imx_ocotp_nvmem_config.add_legacy_fixed_of_cells = true;
 | ||||
|  	imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; | ||||
|  	imx_ocotp_nvmem_config.dev = dev; | ||||
|  	imx_ocotp_nvmem_config.priv = priv; | ||||
| --- a/drivers/nvmem/meson-efuse.c
 | ||||
| +++ b/drivers/nvmem/meson-efuse.c
 | ||||
| @@ -93,6 +93,7 @@ static int meson_efuse_probe(struct plat
 | ||||
|   | ||||
|  	econfig->dev = dev; | ||||
|  	econfig->name = dev_name(dev); | ||||
| +	econfig->add_legacy_fixed_of_cells = true;
 | ||||
|  	econfig->stride = 1; | ||||
|  	econfig->word_size = 1; | ||||
|  	econfig->reg_read = meson_efuse_read; | ||||
| --- a/drivers/nvmem/meson-mx-efuse.c
 | ||||
| +++ b/drivers/nvmem/meson-mx-efuse.c
 | ||||
| @@ -210,6 +210,7 @@ static int meson_mx_efuse_probe(struct p
 | ||||
|  	efuse->config.owner = THIS_MODULE; | ||||
|  	efuse->config.dev = &pdev->dev; | ||||
|  	efuse->config.priv = efuse; | ||||
| +	efuse->config.add_legacy_fixed_of_cells = true;
 | ||||
|  	efuse->config.stride = drvdata->word_size; | ||||
|  	efuse->config.word_size = drvdata->word_size; | ||||
|  	efuse->config.size = SZ_512; | ||||
| --- a/drivers/nvmem/microchip-otpc.c
 | ||||
| +++ b/drivers/nvmem/microchip-otpc.c
 | ||||
| @@ -261,6 +261,7 @@ static int mchp_otpc_probe(struct platfo
 | ||||
|  		return ret; | ||||
|   | ||||
|  	mchp_nvmem_config.dev = otpc->dev; | ||||
| +	mchp_nvmem_config.add_legacy_fixed_of_cells = true;
 | ||||
|  	mchp_nvmem_config.size = size; | ||||
|  	mchp_nvmem_config.priv = otpc; | ||||
|  	nvmem = devm_nvmem_register(&pdev->dev, &mchp_nvmem_config); | ||||
| --- a/drivers/nvmem/mtk-efuse.c
 | ||||
| +++ b/drivers/nvmem/mtk-efuse.c
 | ||||
| @@ -83,6 +83,7 @@ static int mtk_efuse_probe(struct platfo
 | ||||
|  		return PTR_ERR(priv->base); | ||||
|   | ||||
|  	pdata = device_get_match_data(dev); | ||||
| +	econfig.add_legacy_fixed_of_cells = true;
 | ||||
|  	econfig.stride = 1; | ||||
|  	econfig.word_size = 1; | ||||
|  	econfig.reg_read = mtk_reg_read; | ||||
| --- a/drivers/nvmem/qcom-spmi-sdam.c
 | ||||
| +++ b/drivers/nvmem/qcom-spmi-sdam.c
 | ||||
| @@ -142,6 +142,7 @@ static int sdam_probe(struct platform_de
 | ||||
|  	sdam->sdam_config.name = "spmi_sdam"; | ||||
|  	sdam->sdam_config.id = NVMEM_DEVID_AUTO; | ||||
|  	sdam->sdam_config.owner = THIS_MODULE; | ||||
| +	sdam->sdam_config.add_legacy_fixed_of_cells = true;
 | ||||
|  	sdam->sdam_config.stride = 1; | ||||
|  	sdam->sdam_config.word_size = 1; | ||||
|  	sdam->sdam_config.reg_read = sdam_read; | ||||
| --- a/drivers/nvmem/qfprom.c
 | ||||
| +++ b/drivers/nvmem/qfprom.c
 | ||||
| @@ -357,6 +357,7 @@ static int qfprom_probe(struct platform_
 | ||||
|  { | ||||
|  	struct nvmem_config econfig = { | ||||
|  		.name = "qfprom", | ||||
| +		.add_legacy_fixed_of_cells = true,
 | ||||
|  		.stride = 1, | ||||
|  		.word_size = 1, | ||||
|  		.id = NVMEM_DEVID_AUTO, | ||||
| --- a/drivers/nvmem/rave-sp-eeprom.c
 | ||||
| +++ b/drivers/nvmem/rave-sp-eeprom.c
 | ||||
| @@ -328,6 +328,7 @@ static int rave_sp_eeprom_probe(struct p
 | ||||
|  	of_property_read_string(np, "zii,eeprom-name", &config.name); | ||||
|  	config.priv		= eeprom; | ||||
|  	config.dev		= dev; | ||||
| +	config.add_legacy_fixed_of_cells	= true;
 | ||||
|  	config.size		= size; | ||||
|  	config.reg_read		= rave_sp_eeprom_reg_read; | ||||
|  	config.reg_write	= rave_sp_eeprom_reg_write; | ||||
| --- a/drivers/nvmem/rockchip-efuse.c
 | ||||
| +++ b/drivers/nvmem/rockchip-efuse.c
 | ||||
| @@ -205,6 +205,7 @@ static int rockchip_rk3399_efuse_read(vo
 | ||||
|   | ||||
|  static struct nvmem_config econfig = { | ||||
|  	.name = "rockchip-efuse", | ||||
| +	.add_legacy_fixed_of_cells = true,
 | ||||
|  	.stride = 1, | ||||
|  	.word_size = 1, | ||||
|  	.read_only = true, | ||||
| --- a/drivers/nvmem/sc27xx-efuse.c
 | ||||
| +++ b/drivers/nvmem/sc27xx-efuse.c
 | ||||
| @@ -247,6 +247,7 @@ static int sc27xx_efuse_probe(struct pla
 | ||||
|  	econfig.reg_read = sc27xx_efuse_read; | ||||
|  	econfig.priv = efuse; | ||||
|  	econfig.dev = &pdev->dev; | ||||
| +	econfig.add_legacy_fixed_of_cells = true;
 | ||||
|  	nvmem = devm_nvmem_register(&pdev->dev, &econfig); | ||||
|  	if (IS_ERR(nvmem)) { | ||||
|  		dev_err(&pdev->dev, "failed to register nvmem config\n"); | ||||
| --- a/drivers/nvmem/sec-qfprom.c
 | ||||
| +++ b/drivers/nvmem/sec-qfprom.c
 | ||||
| @@ -47,6 +47,7 @@ static int sec_qfprom_probe(struct platf
 | ||||
|  { | ||||
|  	struct nvmem_config econfig = { | ||||
|  		.name = "sec-qfprom", | ||||
| +		.add_legacy_fixed_of_cells = true,
 | ||||
|  		.stride = 1, | ||||
|  		.word_size = 1, | ||||
|  		.id = NVMEM_DEVID_AUTO, | ||||
| --- a/drivers/nvmem/sprd-efuse.c
 | ||||
| +++ b/drivers/nvmem/sprd-efuse.c
 | ||||
| @@ -408,6 +408,7 @@ static int sprd_efuse_probe(struct platf
 | ||||
|  	econfig.read_only = false; | ||||
|  	econfig.name = "sprd-efuse"; | ||||
|  	econfig.size = efuse->data->blk_nums * SPRD_EFUSE_BLOCK_WIDTH; | ||||
| +	econfig.add_legacy_fixed_of_cells = true;
 | ||||
|  	econfig.reg_read = sprd_efuse_read; | ||||
|  	econfig.reg_write = sprd_efuse_write; | ||||
|  	econfig.priv = efuse; | ||||
| --- a/drivers/nvmem/stm32-romem.c
 | ||||
| +++ b/drivers/nvmem/stm32-romem.c
 | ||||
| @@ -207,6 +207,7 @@ static int stm32_romem_probe(struct plat
 | ||||
|  	priv->cfg.priv = priv; | ||||
|  	priv->cfg.owner = THIS_MODULE; | ||||
|  	priv->cfg.type = NVMEM_TYPE_OTP; | ||||
| +	priv->cfg.add_legacy_fixed_of_cells = true;
 | ||||
|   | ||||
|  	priv->lower = 0; | ||||
|   | ||||
| --- a/drivers/nvmem/sunplus-ocotp.c
 | ||||
| +++ b/drivers/nvmem/sunplus-ocotp.c
 | ||||
| @@ -145,6 +145,7 @@ disable_clk:
 | ||||
|   | ||||
|  static struct nvmem_config sp_ocotp_nvmem_config = { | ||||
|  	.name = "sp-ocotp", | ||||
| +	.add_legacy_fixed_of_cells = true,
 | ||||
|  	.read_only = true, | ||||
|  	.word_size = 1, | ||||
|  	.size = QAC628_OTP_SIZE, | ||||
| --- a/drivers/nvmem/sunxi_sid.c
 | ||||
| +++ b/drivers/nvmem/sunxi_sid.c
 | ||||
| @@ -153,6 +153,7 @@ static int sunxi_sid_probe(struct platfo
 | ||||
|  	nvmem_cfg->dev = dev; | ||||
|  	nvmem_cfg->name = "sunxi-sid"; | ||||
|  	nvmem_cfg->type = NVMEM_TYPE_OTP; | ||||
| +	nvmem_cfg->add_legacy_fixed_of_cells = true;
 | ||||
|  	nvmem_cfg->read_only = true; | ||||
|  	nvmem_cfg->size = cfg->size; | ||||
|  	nvmem_cfg->word_size = 1; | ||||
| --- a/drivers/nvmem/uniphier-efuse.c
 | ||||
| +++ b/drivers/nvmem/uniphier-efuse.c
 | ||||
| @@ -52,6 +52,7 @@ static int uniphier_efuse_probe(struct p
 | ||||
|  	econfig.size = resource_size(res); | ||||
|  	econfig.priv = priv; | ||||
|  	econfig.dev = dev; | ||||
| +	econfig.add_legacy_fixed_of_cells = true;
 | ||||
|  	nvmem = devm_nvmem_register(dev, &econfig); | ||||
|   | ||||
|  	return PTR_ERR_OR_ZERO(nvmem); | ||||
| --- a/drivers/nvmem/zynqmp_nvmem.c
 | ||||
| +++ b/drivers/nvmem/zynqmp_nvmem.c
 | ||||
| @@ -58,6 +58,7 @@ static int zynqmp_nvmem_probe(struct pla
 | ||||
|   | ||||
|  	priv->dev = dev; | ||||
|  	econfig.dev = dev; | ||||
| +	econfig.add_legacy_fixed_of_cells = true;
 | ||||
|  	econfig.reg_read = zynqmp_nvmem_read; | ||||
|  	econfig.priv = priv; | ||||
|   | ||||
| --- a/drivers/rtc/nvmem.c
 | ||||
| +++ b/drivers/rtc/nvmem.c
 | ||||
| @@ -21,6 +21,7 @@ int devm_rtc_nvmem_register(struct rtc_d
 | ||||
|   | ||||
|  	nvmem_config->dev = dev; | ||||
|  	nvmem_config->owner = rtc->owner; | ||||
| +	nvmem_config->add_legacy_fixed_of_cells = true;
 | ||||
|  	nvmem = devm_nvmem_register(dev, nvmem_config); | ||||
|  	if (IS_ERR(nvmem)) | ||||
|  		dev_err(dev, "failed to register nvmem device for RTC\n"); | ||||
| --- a/drivers/w1/slaves/w1_ds250x.c
 | ||||
| +++ b/drivers/w1/slaves/w1_ds250x.c
 | ||||
| @@ -168,6 +168,7 @@ static int w1_eprom_add_slave(struct w1_
 | ||||
|  	struct nvmem_device *nvmem; | ||||
|  	struct nvmem_config nvmem_cfg = { | ||||
|  		.dev = &sl->dev, | ||||
| +		.add_legacy_fixed_of_cells = true,
 | ||||
|  		.reg_read = w1_nvmem_read, | ||||
|  		.type = NVMEM_TYPE_OTP, | ||||
|  		.read_only = true, | ||||
| --- a/include/linux/nvmem-provider.h
 | ||||
| +++ b/include/linux/nvmem-provider.h
 | ||||
| @@ -82,6 +82,7 @@ struct nvmem_cell_info {
 | ||||
|   * @owner:	Pointer to exporter module. Used for refcounting. | ||||
|   * @cells:	Optional array of pre-defined NVMEM cells. | ||||
|   * @ncells:	Number of elements in cells. | ||||
| + * @add_legacy_fixed_of_cells:	Read fixed NVMEM cells from old OF syntax.
 | ||||
|   * @keepout:	Optional array of keepout ranges (sorted ascending by start). | ||||
|   * @nkeepout:	Number of elements in the keepout array. | ||||
|   * @type:	Type of the nvmem storage | ||||
| @@ -112,6 +113,7 @@ struct nvmem_config {
 | ||||
|  	struct module		*owner; | ||||
|  	const struct nvmem_cell_info	*cells; | ||||
|  	int			ncells; | ||||
| +	bool			add_legacy_fixed_of_cells;
 | ||||
|  	const struct nvmem_keepout *keepout; | ||||
|  	unsigned int		nkeepout; | ||||
|  	enum nvmem_type		type; | ||||
|  | @ -0,0 +1,77 @@ | |||
| From 0720219f4d34a88a9badb4de70cfad7585687d48 Mon Sep 17 00:00:00 2001 | ||||
| From: Rob Herring <robh@kernel.org> | ||||
| Date: Fri, 20 Oct 2023 11:55:45 +0100 | ||||
| Subject: [PATCH] nvmem: Use device_get_match_data() | ||||
| 
 | ||||
| Use preferred device_get_match_data() instead of of_match_device() to | ||||
| get the driver match data. With this, adjust the includes to explicitly | ||||
| include the correct headers. | ||||
| 
 | ||||
| Signed-off-by: Rob Herring <robh@kernel.org> | ||||
| Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | ||||
| Link: https://lore.kernel.org/r/20231020105545.216052-7-srinivas.kandagatla@linaro.org | ||||
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||||
| ---
 | ||||
|  drivers/nvmem/mxs-ocotp.c   | 10 ++++------ | ||||
|  drivers/nvmem/stm32-romem.c |  7 ++++--- | ||||
|  2 files changed, 8 insertions(+), 9 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/nvmem/mxs-ocotp.c
 | ||||
| +++ b/drivers/nvmem/mxs-ocotp.c
 | ||||
| @@ -13,8 +13,9 @@
 | ||||
|  #include <linux/io.h> | ||||
|  #include <linux/module.h> | ||||
|  #include <linux/nvmem-provider.h> | ||||
| -#include <linux/of_device.h>
 | ||||
| +#include <linux/of.h>
 | ||||
|  #include <linux/platform_device.h> | ||||
| +#include <linux/property.h>
 | ||||
|  #include <linux/slab.h> | ||||
|  #include <linux/stmp_device.h> | ||||
|   | ||||
| @@ -140,11 +141,10 @@ static int mxs_ocotp_probe(struct platfo
 | ||||
|  	struct device *dev = &pdev->dev; | ||||
|  	const struct mxs_data *data; | ||||
|  	struct mxs_ocotp *otp; | ||||
| -	const struct of_device_id *match;
 | ||||
|  	int ret; | ||||
|   | ||||
| -	match = of_match_device(dev->driver->of_match_table, dev);
 | ||||
| -	if (!match || !match->data)
 | ||||
| +	data = device_get_match_data(dev);
 | ||||
| +	if (!data)
 | ||||
|  		return -EINVAL; | ||||
|   | ||||
|  	otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL); | ||||
| @@ -169,8 +169,6 @@ static int mxs_ocotp_probe(struct platfo
 | ||||
|  	if (ret) | ||||
|  		return ret; | ||||
|   | ||||
| -	data = match->data;
 | ||||
| -
 | ||||
|  	ocotp_config.size = data->size; | ||||
|  	ocotp_config.priv = otp; | ||||
|  	ocotp_config.dev = dev; | ||||
| --- a/drivers/nvmem/stm32-romem.c
 | ||||
| +++ b/drivers/nvmem/stm32-romem.c
 | ||||
| @@ -10,7 +10,9 @@
 | ||||
|  #include <linux/io.h> | ||||
|  #include <linux/module.h> | ||||
|  #include <linux/nvmem-provider.h> | ||||
| -#include <linux/of_device.h>
 | ||||
| +#include <linux/of.h>
 | ||||
| +#include <linux/platform_device.h>
 | ||||
| +#include <linux/property.h>
 | ||||
|  #include <linux/tee_drv.h> | ||||
|   | ||||
|  #include "stm32-bsec-optee-ta.h" | ||||
| @@ -211,8 +213,7 @@ static int stm32_romem_probe(struct plat
 | ||||
|   | ||||
|  	priv->lower = 0; | ||||
|   | ||||
| -	cfg = (const struct stm32_romem_cfg *)
 | ||||
| -		of_match_device(dev->driver->of_match_table, dev)->data;
 | ||||
| +	cfg = device_get_match_data(dev);
 | ||||
|  	if (!cfg) { | ||||
|  		priv->cfg.read_only = true; | ||||
|  		priv->cfg.size = resource_size(res); | ||||
|  | @ -0,0 +1,77 @@ | |||
| From f4cf4e5db331a5ce69e3f0b21d322cac0f4e4b5d Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Mon, 23 Oct 2023 12:27:59 +0200 | ||||
| Subject: [PATCH] Revert "nvmem: add new config option" | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| This reverts commit 517f14d9cf3533d5ab4fded195ab6f80a92e378f. | ||||
| 
 | ||||
| Config option "no_of_node" is no longer needed since adding a more | ||||
| explicit and targeted option "add_legacy_fixed_of_cells". | ||||
| 
 | ||||
| That "no_of_node" config option was needed *earlier* to help mtd's case. | ||||
| 
 | ||||
| DT nodes of MTD partitions (that are also NVMEM devices) may contain | ||||
| subnodes. Those SHOULD NOT be treated as NVMEM fixed cells. | ||||
| 
 | ||||
| To prevent NVMEM core code from parsing subnodes a "no_of_node" option | ||||
| was added (and set to true in mtd) to make for_each_child_of_node() in | ||||
| NVMEM a no-op. That was a bit hacky because it was messing with | ||||
| "of_node" pointer to achieve some side-effect. | ||||
| 
 | ||||
| With the introduction of "add_legacy_fixed_of_cells" config option | ||||
| things got more explicit. MTD subsystem simply tells NVMEM when to look | ||||
| for fixed cells and there is no need to hack "of_node" pointer anymore. | ||||
| 
 | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | ||||
| Link: https://lore.kernel.org/r/20231023102759.31529-1-zajec5@gmail.com | ||||
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||||
| ---
 | ||||
|  drivers/mtd/mtdcore.c          | 1 - | ||||
|  drivers/nvmem/core.c           | 2 +- | ||||
|  include/linux/nvmem-provider.h | 2 -- | ||||
|  3 files changed, 1 insertion(+), 4 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/mtd/mtdcore.c
 | ||||
| +++ b/drivers/mtd/mtdcore.c
 | ||||
| @@ -560,7 +560,6 @@ static int mtd_nvmem_add(struct mtd_info
 | ||||
|  	config.read_only = true; | ||||
|  	config.root_only = true; | ||||
|  	config.ignore_wp = true; | ||||
| -	config.no_of_node = !of_device_is_compatible(node, "nvmem-cells");
 | ||||
|  	config.priv = mtd; | ||||
|   | ||||
|  	mtd->nvmem = nvmem_register(&config); | ||||
| --- a/drivers/nvmem/core.c
 | ||||
| +++ b/drivers/nvmem/core.c
 | ||||
| @@ -941,7 +941,7 @@ struct nvmem_device *nvmem_register(cons
 | ||||
|  	nvmem->nkeepout = config->nkeepout; | ||||
|  	if (config->of_node) | ||||
|  		nvmem->dev.of_node = config->of_node; | ||||
| -	else if (!config->no_of_node)
 | ||||
| +	else
 | ||||
|  		nvmem->dev.of_node = config->dev->of_node; | ||||
|   | ||||
|  	switch (config->id) { | ||||
| --- a/include/linux/nvmem-provider.h
 | ||||
| +++ b/include/linux/nvmem-provider.h
 | ||||
| @@ -89,7 +89,6 @@ struct nvmem_cell_info {
 | ||||
|   * @read_only:	Device is read-only. | ||||
|   * @root_only:	Device is accessibly to root only. | ||||
|   * @of_node:	If given, this will be used instead of the parent's of_node. | ||||
| - * @no_of_node:	Device should not use the parent's of_node even if it's !NULL.
 | ||||
|   * @reg_read:	Callback to read data. | ||||
|   * @reg_write:	Callback to write data. | ||||
|   * @size:	Device size. | ||||
| @@ -122,7 +121,6 @@ struct nvmem_config {
 | ||||
|  	bool			ignore_wp; | ||||
|  	struct nvmem_layout	*layout; | ||||
|  	struct device_node	*of_node; | ||||
| -	bool			no_of_node;
 | ||||
|  	nvmem_reg_read_t	reg_read; | ||||
|  	nvmem_reg_write_t	reg_write; | ||||
|  	int	size; | ||||
|  | @ -0,0 +1,140 @@ | |||
| From 7f38b70042fcaa49219045bd1a9a2836e27a58ac Mon Sep 17 00:00:00 2001 | ||||
| From: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Date: Fri, 15 Dec 2023 11:15:27 +0000 | ||||
| Subject: [PATCH] of: device: Export of_device_make_bus_id() | ||||
| 
 | ||||
| This helper is really handy to create unique device names based on their | ||||
| device tree path, we may need it outside of the OF core (in the NVMEM | ||||
| subsystem) so let's export it. As this helper has nothing patform | ||||
| specific, let's move it to of/device.c instead of of/platform.c so we | ||||
| can add its prototype to of_device.h. | ||||
| 
 | ||||
| Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Acked-by: Rob Herring <robh@kernel.org> | ||||
| Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | ||||
| Link: https://lore.kernel.org/r/20231215111536.316972-2-srinivas.kandagatla@linaro.org | ||||
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||||
| ---
 | ||||
|  drivers/of/device.c       | 41 +++++++++++++++++++++++++++++++++++++++ | ||||
|  drivers/of/platform.c     | 40 -------------------------------------- | ||||
|  include/linux/of_device.h |  6 ++++++ | ||||
|  3 files changed, 47 insertions(+), 40 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/of/device.c
 | ||||
| +++ b/drivers/of/device.c
 | ||||
| @@ -304,3 +304,44 @@ int of_device_uevent_modalias(const stru
 | ||||
|  	return 0; | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(of_device_uevent_modalias); | ||||
| +
 | ||||
| +/**
 | ||||
| + * of_device_make_bus_id - Use the device node data to assign a unique name
 | ||||
| + * @dev: pointer to device structure that is linked to a device tree node
 | ||||
| + *
 | ||||
| + * This routine will first try using the translated bus address to
 | ||||
| + * derive a unique name. If it cannot, then it will prepend names from
 | ||||
| + * parent nodes until a unique name can be derived.
 | ||||
| + */
 | ||||
| +void of_device_make_bus_id(struct device *dev)
 | ||||
| +{
 | ||||
| +	struct device_node *node = dev->of_node;
 | ||||
| +	const __be32 *reg;
 | ||||
| +	u64 addr;
 | ||||
| +	u32 mask;
 | ||||
| +
 | ||||
| +	/* Construct the name, using parent nodes if necessary to ensure uniqueness */
 | ||||
| +	while (node->parent) {
 | ||||
| +		/*
 | ||||
| +		 * If the address can be translated, then that is as much
 | ||||
| +		 * uniqueness as we need. Make it the first component and return
 | ||||
| +		 */
 | ||||
| +		reg = of_get_property(node, "reg", NULL);
 | ||||
| +		if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
 | ||||
| +			if (!of_property_read_u32(node, "mask", &mask))
 | ||||
| +				dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn",
 | ||||
| +					     addr, ffs(mask) - 1, node, dev_name(dev));
 | ||||
| +
 | ||||
| +			else
 | ||||
| +				dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn",
 | ||||
| +					     addr, node, dev_name(dev));
 | ||||
| +			return;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		/* format arguments only used if dev_name() resolves to NULL */
 | ||||
| +		dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
 | ||||
| +			     kbasename(node->full_name), dev_name(dev));
 | ||||
| +		node = node->parent;
 | ||||
| +	}
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(of_device_make_bus_id);
 | ||||
| --- a/drivers/of/platform.c
 | ||||
| +++ b/drivers/of/platform.c
 | ||||
| @@ -98,46 +98,6 @@ static const struct of_device_id of_skip
 | ||||
|   */ | ||||
|   | ||||
|  /** | ||||
| - * of_device_make_bus_id - Use the device node data to assign a unique name
 | ||||
| - * @dev: pointer to device structure that is linked to a device tree node
 | ||||
| - *
 | ||||
| - * This routine will first try using the translated bus address to
 | ||||
| - * derive a unique name. If it cannot, then it will prepend names from
 | ||||
| - * parent nodes until a unique name can be derived.
 | ||||
| - */
 | ||||
| -static void of_device_make_bus_id(struct device *dev)
 | ||||
| -{
 | ||||
| -	struct device_node *node = dev->of_node;
 | ||||
| -	const __be32 *reg;
 | ||||
| -	u64 addr;
 | ||||
| -	u32 mask;
 | ||||
| -
 | ||||
| -	/* Construct the name, using parent nodes if necessary to ensure uniqueness */
 | ||||
| -	while (node->parent) {
 | ||||
| -		/*
 | ||||
| -		 * If the address can be translated, then that is as much
 | ||||
| -		 * uniqueness as we need. Make it the first component and return
 | ||||
| -		 */
 | ||||
| -		reg = of_get_property(node, "reg", NULL);
 | ||||
| -		if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
 | ||||
| -			if (!of_property_read_u32(node, "mask", &mask))
 | ||||
| -				dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn",
 | ||||
| -					     addr, ffs(mask) - 1, node, dev_name(dev));
 | ||||
| -
 | ||||
| -			else
 | ||||
| -				dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn",
 | ||||
| -					     addr, node, dev_name(dev));
 | ||||
| -			return;
 | ||||
| -		}
 | ||||
| -
 | ||||
| -		/* format arguments only used if dev_name() resolves to NULL */
 | ||||
| -		dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
 | ||||
| -			     kbasename(node->full_name), dev_name(dev));
 | ||||
| -		node = node->parent;
 | ||||
| -	}
 | ||||
| -}
 | ||||
| -
 | ||||
| -/**
 | ||||
|   * of_device_alloc - Allocate and initialize an of_device | ||||
|   * @np: device node to assign to device | ||||
|   * @bus_id: Name to assign to the device.  May be null to use default name. | ||||
| --- a/include/linux/of_device.h
 | ||||
| +++ b/include/linux/of_device.h
 | ||||
| @@ -40,6 +40,9 @@ static inline int of_dma_configure(struc
 | ||||
|  { | ||||
|  	return of_dma_configure_id(dev, np, force_dma, NULL); | ||||
|  } | ||||
| +
 | ||||
| +void of_device_make_bus_id(struct device *dev);
 | ||||
| +
 | ||||
|  #else /* CONFIG_OF */ | ||||
|   | ||||
|  static inline int of_driver_match_device(struct device *dev, | ||||
| @@ -82,6 +85,9 @@ static inline int of_dma_configure(struc
 | ||||
|  { | ||||
|  	return 0; | ||||
|  } | ||||
| +
 | ||||
| +static inline void of_device_make_bus_id(struct device *dev) {}
 | ||||
| +
 | ||||
|  #endif /* CONFIG_OF */ | ||||
|   | ||||
|  #endif /* _LINUX_OF_DEVICE_H */ | ||||
|  | @ -0,0 +1,95 @@ | |||
| From 4a1a40233b4a9fc159a5c7a27dc34c5c7bc5be55 Mon Sep 17 00:00:00 2001 | ||||
| From: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Date: Fri, 15 Dec 2023 11:15:28 +0000 | ||||
| Subject: [PATCH] nvmem: Move of_nvmem_layout_get_container() in another header | ||||
| 
 | ||||
| nvmem-consumer.h is included by consumer devices, extracting data from | ||||
| NVMEM devices whereas nvmem-provider.h is included by devices providing | ||||
| NVMEM content. | ||||
| 
 | ||||
| The only users of of_nvmem_layout_get_container() outside of the core | ||||
| are layout drivers, so better move its prototype to nvmem-provider.h. | ||||
| 
 | ||||
| While we do so, we also move the kdoc associated with the function to | ||||
| the header rather than the .c file. | ||||
| 
 | ||||
| Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | ||||
| Link: https://lore.kernel.org/r/20231215111536.316972-3-srinivas.kandagatla@linaro.org | ||||
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||||
| ---
 | ||||
|  drivers/nvmem/core.c           |  8 -------- | ||||
|  include/linux/nvmem-consumer.h |  7 ------- | ||||
|  include/linux/nvmem-provider.h | 21 +++++++++++++++++++++ | ||||
|  3 files changed, 21 insertions(+), 15 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/nvmem/core.c
 | ||||
| +++ b/drivers/nvmem/core.c
 | ||||
| @@ -847,14 +847,6 @@ static int nvmem_add_cells_from_layout(s
 | ||||
|  } | ||||
|   | ||||
|  #if IS_ENABLED(CONFIG_OF) | ||||
| -/**
 | ||||
| - * of_nvmem_layout_get_container() - Get OF node to layout container.
 | ||||
| - *
 | ||||
| - * @nvmem: nvmem device.
 | ||||
| - *
 | ||||
| - * Return: a node pointer with refcount incremented or NULL if no
 | ||||
| - * container exists. Use of_node_put() on it when done.
 | ||||
| - */
 | ||||
|  struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) | ||||
|  { | ||||
|  	return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout"); | ||||
| --- a/include/linux/nvmem-consumer.h
 | ||||
| +++ b/include/linux/nvmem-consumer.h
 | ||||
| @@ -241,7 +241,6 @@ struct nvmem_cell *of_nvmem_cell_get(str
 | ||||
|  				     const char *id); | ||||
|  struct nvmem_device *of_nvmem_device_get(struct device_node *np, | ||||
|  					 const char *name); | ||||
| -struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
 | ||||
|  #else | ||||
|  static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, | ||||
|  						   const char *id) | ||||
| @@ -254,12 +253,6 @@ static inline struct nvmem_device *of_nv
 | ||||
|  { | ||||
|  	return ERR_PTR(-EOPNOTSUPP); | ||||
|  } | ||||
| -
 | ||||
| -static inline struct device_node *
 | ||||
| -of_nvmem_layout_get_container(struct nvmem_device *nvmem)
 | ||||
| -{
 | ||||
| -	return NULL;
 | ||||
| -}
 | ||||
|  #endif /* CONFIG_NVMEM && CONFIG_OF */ | ||||
|   | ||||
|  #endif  /* ifndef _LINUX_NVMEM_CONSUMER_H */ | ||||
| --- a/include/linux/nvmem-provider.h
 | ||||
| +++ b/include/linux/nvmem-provider.h
 | ||||
| @@ -244,6 +244,27 @@ nvmem_layout_get_match_data(struct nvmem
 | ||||
|   | ||||
|  #endif /* CONFIG_NVMEM */ | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
 | ||||
| +
 | ||||
| +/**
 | ||||
| + * of_nvmem_layout_get_container() - Get OF node of layout container
 | ||||
| + *
 | ||||
| + * @nvmem: nvmem device
 | ||||
| + *
 | ||||
| + * Return: a node pointer with refcount incremented or NULL if no
 | ||||
| + * container exists. Use of_node_put() on it when done.
 | ||||
| + */
 | ||||
| +struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
 | ||||
| +
 | ||||
| +#else  /* CONFIG_NVMEM && CONFIG_OF */
 | ||||
| +
 | ||||
| +static inline struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
 | ||||
| +{
 | ||||
| +	return NULL;
 | ||||
| +}
 | ||||
| +
 | ||||
| +#endif /* CONFIG_NVMEM && CONFIG_OF */
 | ||||
| +
 | ||||
|  #define module_nvmem_layout_driver(__layout_driver)		\ | ||||
|  	module_driver(__layout_driver, nvmem_layout_register,	\ | ||||
|  		      nvmem_layout_unregister) | ||||
|  | @ -0,0 +1,91 @@ | |||
| From ec9c08a1cb8dc5e8e003f95f5f62de41dde235bb Mon Sep 17 00:00:00 2001 | ||||
| From: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Date: Fri, 15 Dec 2023 11:15:29 +0000 | ||||
| Subject: [PATCH] nvmem: Create a header for internal sharing | ||||
| 
 | ||||
| Before adding all the NVMEM layout bus infrastructure to the core, let's | ||||
| move the main nvmem_device structure in an internal header, only | ||||
| available to the core. This way all the additional code can be added in | ||||
| a dedicated file in order to keep the current core file tidy. | ||||
| 
 | ||||
| Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | ||||
| Link: https://lore.kernel.org/r/20231215111536.316972-4-srinivas.kandagatla@linaro.org | ||||
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||||
| ---
 | ||||
|  drivers/nvmem/core.c      | 24 +----------------------- | ||||
|  drivers/nvmem/internals.h | 35 +++++++++++++++++++++++++++++++++++ | ||||
|  2 files changed, 36 insertions(+), 23 deletions(-) | ||||
|  create mode 100644 drivers/nvmem/internals.h | ||||
| 
 | ||||
| --- a/drivers/nvmem/core.c
 | ||||
| +++ b/drivers/nvmem/core.c
 | ||||
| @@ -19,29 +19,7 @@
 | ||||
|  #include <linux/of.h> | ||||
|  #include <linux/slab.h> | ||||
|   | ||||
| -struct nvmem_device {
 | ||||
| -	struct module		*owner;
 | ||||
| -	struct device		dev;
 | ||||
| -	int			stride;
 | ||||
| -	int			word_size;
 | ||||
| -	int			id;
 | ||||
| -	struct kref		refcnt;
 | ||||
| -	size_t			size;
 | ||||
| -	bool			read_only;
 | ||||
| -	bool			root_only;
 | ||||
| -	int			flags;
 | ||||
| -	enum nvmem_type		type;
 | ||||
| -	struct bin_attribute	eeprom;
 | ||||
| -	struct device		*base_dev;
 | ||||
| -	struct list_head	cells;
 | ||||
| -	const struct nvmem_keepout *keepout;
 | ||||
| -	unsigned int		nkeepout;
 | ||||
| -	nvmem_reg_read_t	reg_read;
 | ||||
| -	nvmem_reg_write_t	reg_write;
 | ||||
| -	struct gpio_desc	*wp_gpio;
 | ||||
| -	struct nvmem_layout	*layout;
 | ||||
| -	void *priv;
 | ||||
| -};
 | ||||
| +#include "internals.h"
 | ||||
|   | ||||
|  #define to_nvmem_device(d) container_of(d, struct nvmem_device, dev) | ||||
|   | ||||
| --- /dev/null
 | ||||
| +++ b/drivers/nvmem/internals.h
 | ||||
| @@ -0,0 +1,35 @@
 | ||||
| +/* SPDX-License-Identifier: GPL-2.0 */
 | ||||
| +
 | ||||
| +#ifndef _LINUX_NVMEM_INTERNALS_H
 | ||||
| +#define _LINUX_NVMEM_INTERNALS_H
 | ||||
| +
 | ||||
| +#include <linux/device.h>
 | ||||
| +#include <linux/nvmem-consumer.h>
 | ||||
| +#include <linux/nvmem-provider.h>
 | ||||
| +
 | ||||
| +struct nvmem_device {
 | ||||
| +	struct module		*owner;
 | ||||
| +	struct device		dev;
 | ||||
| +	struct list_head	node;
 | ||||
| +	int			stride;
 | ||||
| +	int			word_size;
 | ||||
| +	int			id;
 | ||||
| +	struct kref		refcnt;
 | ||||
| +	size_t			size;
 | ||||
| +	bool			read_only;
 | ||||
| +	bool			root_only;
 | ||||
| +	int			flags;
 | ||||
| +	enum nvmem_type		type;
 | ||||
| +	struct bin_attribute	eeprom;
 | ||||
| +	struct device		*base_dev;
 | ||||
| +	struct list_head	cells;
 | ||||
| +	const struct nvmem_keepout *keepout;
 | ||||
| +	unsigned int		nkeepout;
 | ||||
| +	nvmem_reg_read_t	reg_read;
 | ||||
| +	nvmem_reg_write_t	reg_write;
 | ||||
| +	struct gpio_desc	*wp_gpio;
 | ||||
| +	struct nvmem_layout	*layout;
 | ||||
| +	void *priv;
 | ||||
| +};
 | ||||
| +
 | ||||
| +#endif  /* ifndef _LINUX_NVMEM_INTERNALS_H */
 | ||||
|  | @ -0,0 +1,79 @@ | |||
| From 1b7c298a4ecbc28cc6ee94005734bff55eb83d22 Mon Sep 17 00:00:00 2001 | ||||
| From: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Date: Fri, 15 Dec 2023 11:15:30 +0000 | ||||
| Subject: [PATCH] nvmem: Simplify the ->add_cells() hook | ||||
| 
 | ||||
| The layout entry is not used and will anyway be made useless by the new | ||||
| layout bus infrastructure coming next, so drop it. While at it, clarify | ||||
| the kdoc entry. | ||||
| 
 | ||||
| Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | ||||
| Link: https://lore.kernel.org/r/20231215111536.316972-5-srinivas.kandagatla@linaro.org | ||||
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||||
| ---
 | ||||
|  drivers/nvmem/core.c             | 2 +- | ||||
|  drivers/nvmem/layouts/onie-tlv.c | 3 +-- | ||||
|  drivers/nvmem/layouts/sl28vpd.c  | 3 +-- | ||||
|  include/linux/nvmem-provider.h   | 8 +++----- | ||||
|  4 files changed, 6 insertions(+), 10 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/nvmem/core.c
 | ||||
| +++ b/drivers/nvmem/core.c
 | ||||
| @@ -816,7 +816,7 @@ static int nvmem_add_cells_from_layout(s
 | ||||
|  	int ret; | ||||
|   | ||||
|  	if (layout && layout->add_cells) { | ||||
| -		ret = layout->add_cells(&nvmem->dev, nvmem, layout);
 | ||||
| +		ret = layout->add_cells(&nvmem->dev, nvmem);
 | ||||
|  		if (ret) | ||||
|  			return ret; | ||||
|  	} | ||||
| --- a/drivers/nvmem/layouts/onie-tlv.c
 | ||||
| +++ b/drivers/nvmem/layouts/onie-tlv.c
 | ||||
| @@ -182,8 +182,7 @@ static bool onie_tlv_crc_is_valid(struct
 | ||||
|  	return true; | ||||
|  } | ||||
|   | ||||
| -static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem,
 | ||||
| -				struct nvmem_layout *layout)
 | ||||
| +static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem)
 | ||||
|  { | ||||
|  	struct onie_tlv_hdr hdr; | ||||
|  	size_t table_len, data_len, hdr_len; | ||||
| --- a/drivers/nvmem/layouts/sl28vpd.c
 | ||||
| +++ b/drivers/nvmem/layouts/sl28vpd.c
 | ||||
| @@ -80,8 +80,7 @@ static int sl28vpd_v1_check_crc(struct d
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem,
 | ||||
| -			     struct nvmem_layout *layout)
 | ||||
| +static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem)
 | ||||
|  { | ||||
|  	const struct nvmem_cell_info *pinfo; | ||||
|  	struct nvmem_cell_info info = {0}; | ||||
| --- a/include/linux/nvmem-provider.h
 | ||||
| +++ b/include/linux/nvmem-provider.h
 | ||||
| @@ -156,9 +156,8 @@ struct nvmem_cell_table {
 | ||||
|   * | ||||
|   * @name:		Layout name. | ||||
|   * @of_match_table:	Open firmware match table. | ||||
| - * @add_cells:		Will be called if a nvmem device is found which
 | ||||
| - *			has this layout. The function will add layout
 | ||||
| - *			specific cells with nvmem_add_one_cell().
 | ||||
| + * @add_cells:		Called to populate the layout using
 | ||||
| + *			nvmem_add_one_cell().
 | ||||
|   * @fixup_cell_info:	Will be called before a cell is added. Can be | ||||
|   *			used to modify the nvmem_cell_info. | ||||
|   * @owner:		Pointer to struct module. | ||||
| @@ -172,8 +171,7 @@ struct nvmem_cell_table {
 | ||||
|  struct nvmem_layout { | ||||
|  	const char *name; | ||||
|  	const struct of_device_id *of_match_table; | ||||
| -	int (*add_cells)(struct device *dev, struct nvmem_device *nvmem,
 | ||||
| -			 struct nvmem_layout *layout);
 | ||||
| +	int (*add_cells)(struct device *dev, struct nvmem_device *nvmem);
 | ||||
|  	void (*fixup_cell_info)(struct nvmem_device *nvmem, | ||||
|  				struct nvmem_layout *layout, | ||||
|  				struct nvmem_cell_info *cell); | ||||
|  | @ -0,0 +1,169 @@ | |||
| From 1172460e716784ac7e1049a537bdca8edbf97360 Mon Sep 17 00:00:00 2001 | ||||
| From: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Date: Fri, 15 Dec 2023 11:15:31 +0000 | ||||
| Subject: [PATCH] nvmem: Move and rename ->fixup_cell_info() | ||||
| 
 | ||||
| This hook is meant to be used by any provider and instantiating a layout | ||||
| just for this is useless. Let's instead move this hook to the nvmem | ||||
| device and add it to the config structure to be easily shared by the | ||||
| providers. | ||||
| 
 | ||||
| While at moving this hook, rename it ->fixup_dt_cell_info() to clarify | ||||
| its main intended purpose. | ||||
| 
 | ||||
| Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | ||||
| Link: https://lore.kernel.org/r/20231215111536.316972-6-srinivas.kandagatla@linaro.org | ||||
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||||
| ---
 | ||||
|  drivers/nvmem/core.c           |  6 +++--- | ||||
|  drivers/nvmem/imx-ocotp.c      | 11 +++-------- | ||||
|  drivers/nvmem/internals.h      |  2 ++ | ||||
|  drivers/nvmem/mtk-efuse.c      | 11 +++-------- | ||||
|  include/linux/nvmem-provider.h |  9 ++++----- | ||||
|  5 files changed, 15 insertions(+), 24 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/nvmem/core.c
 | ||||
| +++ b/drivers/nvmem/core.c
 | ||||
| @@ -675,7 +675,6 @@ static int nvmem_validate_keepouts(struc
 | ||||
|   | ||||
|  static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np) | ||||
|  { | ||||
| -	struct nvmem_layout *layout = nvmem->layout;
 | ||||
|  	struct device *dev = &nvmem->dev; | ||||
|  	struct device_node *child; | ||||
|  	const __be32 *addr; | ||||
| @@ -705,8 +704,8 @@ static int nvmem_add_cells_from_dt(struc
 | ||||
|   | ||||
|  		info.np = of_node_get(child); | ||||
|   | ||||
| -		if (layout && layout->fixup_cell_info)
 | ||||
| -			layout->fixup_cell_info(nvmem, layout, &info);
 | ||||
| +		if (nvmem->fixup_dt_cell_info)
 | ||||
| +			nvmem->fixup_dt_cell_info(nvmem, &info);
 | ||||
|   | ||||
|  		ret = nvmem_add_one_cell(nvmem, &info); | ||||
|  		kfree(info.name); | ||||
| @@ -895,6 +894,7 @@ struct nvmem_device *nvmem_register(cons
 | ||||
|   | ||||
|  	kref_init(&nvmem->refcnt); | ||||
|  	INIT_LIST_HEAD(&nvmem->cells); | ||||
| +	nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info;
 | ||||
|   | ||||
|  	nvmem->owner = config->owner; | ||||
|  	if (!nvmem->owner && config->dev->driver) | ||||
| --- a/drivers/nvmem/imx-ocotp.c
 | ||||
| +++ b/drivers/nvmem/imx-ocotp.c
 | ||||
| @@ -583,17 +583,12 @@ static const struct of_device_id imx_oco
 | ||||
|  }; | ||||
|  MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); | ||||
|   | ||||
| -static void imx_ocotp_fixup_cell_info(struct nvmem_device *nvmem,
 | ||||
| -				      struct nvmem_layout *layout,
 | ||||
| -				      struct nvmem_cell_info *cell)
 | ||||
| +static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem,
 | ||||
| +					 struct nvmem_cell_info *cell)
 | ||||
|  { | ||||
|  	cell->read_post_process = imx_ocotp_cell_pp; | ||||
|  } | ||||
|   | ||||
| -static struct nvmem_layout imx_ocotp_layout = {
 | ||||
| -	.fixup_cell_info = imx_ocotp_fixup_cell_info,
 | ||||
| -};
 | ||||
| -
 | ||||
|  static int imx_ocotp_probe(struct platform_device *pdev) | ||||
|  { | ||||
|  	struct device *dev = &pdev->dev; | ||||
| @@ -619,7 +614,7 @@ static int imx_ocotp_probe(struct platfo
 | ||||
|  	imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; | ||||
|  	imx_ocotp_nvmem_config.dev = dev; | ||||
|  	imx_ocotp_nvmem_config.priv = priv; | ||||
| -	imx_ocotp_nvmem_config.layout = &imx_ocotp_layout;
 | ||||
| +	imx_ocotp_nvmem_config.fixup_dt_cell_info = &imx_ocotp_fixup_dt_cell_info;
 | ||||
|   | ||||
|  	priv->config = &imx_ocotp_nvmem_config; | ||||
|   | ||||
| --- a/drivers/nvmem/internals.h
 | ||||
| +++ b/drivers/nvmem/internals.h
 | ||||
| @@ -23,6 +23,8 @@ struct nvmem_device {
 | ||||
|  	struct bin_attribute	eeprom; | ||||
|  	struct device		*base_dev; | ||||
|  	struct list_head	cells; | ||||
| +	void (*fixup_dt_cell_info)(struct nvmem_device *nvmem,
 | ||||
| +				   struct nvmem_cell_info *cell);
 | ||||
|  	const struct nvmem_keepout *keepout; | ||||
|  	unsigned int		nkeepout; | ||||
|  	nvmem_reg_read_t	reg_read; | ||||
| --- a/drivers/nvmem/mtk-efuse.c
 | ||||
| +++ b/drivers/nvmem/mtk-efuse.c
 | ||||
| @@ -45,9 +45,8 @@ static int mtk_efuse_gpu_speedbin_pp(voi
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem,
 | ||||
| -				      struct nvmem_layout *layout,
 | ||||
| -				      struct nvmem_cell_info *cell)
 | ||||
| +static void mtk_efuse_fixup_dt_cell_info(struct nvmem_device *nvmem,
 | ||||
| +					 struct nvmem_cell_info *cell)
 | ||||
|  { | ||||
|  	size_t sz = strlen(cell->name); | ||||
|   | ||||
| @@ -61,10 +60,6 @@ static void mtk_efuse_fixup_cell_info(st
 | ||||
|  		cell->read_post_process = mtk_efuse_gpu_speedbin_pp; | ||||
|  } | ||||
|   | ||||
| -static struct nvmem_layout mtk_efuse_layout = {
 | ||||
| -	.fixup_cell_info = mtk_efuse_fixup_cell_info,
 | ||||
| -};
 | ||||
| -
 | ||||
|  static int mtk_efuse_probe(struct platform_device *pdev) | ||||
|  { | ||||
|  	struct device *dev = &pdev->dev; | ||||
| @@ -91,7 +86,7 @@ static int mtk_efuse_probe(struct platfo
 | ||||
|  	econfig.priv = priv; | ||||
|  	econfig.dev = dev; | ||||
|  	if (pdata->uses_post_processing) | ||||
| -		econfig.layout = &mtk_efuse_layout;
 | ||||
| +		econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
 | ||||
|  	nvmem = devm_nvmem_register(dev, &econfig); | ||||
|   | ||||
|  	return PTR_ERR_OR_ZERO(nvmem); | ||||
| --- a/include/linux/nvmem-provider.h
 | ||||
| +++ b/include/linux/nvmem-provider.h
 | ||||
| @@ -83,6 +83,8 @@ struct nvmem_cell_info {
 | ||||
|   * @cells:	Optional array of pre-defined NVMEM cells. | ||||
|   * @ncells:	Number of elements in cells. | ||||
|   * @add_legacy_fixed_of_cells:	Read fixed NVMEM cells from old OF syntax. | ||||
| + * @fixup_dt_cell_info: Will be called before a cell is added. Can be
 | ||||
| + *		used to modify the nvmem_cell_info.
 | ||||
|   * @keepout:	Optional array of keepout ranges (sorted ascending by start). | ||||
|   * @nkeepout:	Number of elements in the keepout array. | ||||
|   * @type:	Type of the nvmem storage | ||||
| @@ -113,6 +115,8 @@ struct nvmem_config {
 | ||||
|  	const struct nvmem_cell_info	*cells; | ||||
|  	int			ncells; | ||||
|  	bool			add_legacy_fixed_of_cells; | ||||
| +	void (*fixup_dt_cell_info)(struct nvmem_device *nvmem,
 | ||||
| +				   struct nvmem_cell_info *cell);
 | ||||
|  	const struct nvmem_keepout *keepout; | ||||
|  	unsigned int		nkeepout; | ||||
|  	enum nvmem_type		type; | ||||
| @@ -158,8 +162,6 @@ struct nvmem_cell_table {
 | ||||
|   * @of_match_table:	Open firmware match table. | ||||
|   * @add_cells:		Called to populate the layout using | ||||
|   *			nvmem_add_one_cell(). | ||||
| - * @fixup_cell_info:	Will be called before a cell is added. Can be
 | ||||
| - *			used to modify the nvmem_cell_info.
 | ||||
|   * @owner:		Pointer to struct module. | ||||
|   * @node:		List node. | ||||
|   * | ||||
| @@ -172,9 +174,6 @@ struct nvmem_layout {
 | ||||
|  	const char *name; | ||||
|  	const struct of_device_id *of_match_table; | ||||
|  	int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); | ||||
| -	void (*fixup_cell_info)(struct nvmem_device *nvmem,
 | ||||
| -				struct nvmem_layout *layout,
 | ||||
| -				struct nvmem_cell_info *cell);
 | ||||
|   | ||||
|  	/* private */ | ||||
|  	struct module *owner; | ||||
|  | @ -0,0 +1,763 @@ | |||
| From fc29fd821d9ac2ae3d32a722fac39ce874efb883 Mon Sep 17 00:00:00 2001 | ||||
| From: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Date: Fri, 15 Dec 2023 11:15:32 +0000 | ||||
| Subject: [PATCH] nvmem: core: Rework layouts to become regular devices | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| Current layout support was initially written without modules support in | ||||
| mind. When the requirement for module support rose, the existing base | ||||
| was improved to adopt modularization support, but kind of a design flaw | ||||
| was introduced. With the existing implementation, when a storage device | ||||
| registers into NVMEM, the core tries to hook a layout (if any) and | ||||
| populates its cells immediately. This means, if the hardware description | ||||
| expects a layout to be hooked up, but no driver was provided for that, | ||||
| the storage medium will fail to probe and try later from | ||||
| scratch. Even if we consider that the hardware description shall be | ||||
| correct, we could still probe the storage device (especially if it | ||||
| contains the rootfs). | ||||
| 
 | ||||
| One way to overcome this situation is to consider the layouts as | ||||
| devices, and leverage the native notifier mechanism. When a new NVMEM | ||||
| device is registered, we can populate its nvmem-layout child, if any, | ||||
| and wait for the matching to be done in order to get the cells (the | ||||
| waiting can be easily done with the NVMEM notifiers). If the layout | ||||
| driver is compiled as a module, it should automatically be loaded. This | ||||
| way, there is no strong order to enforce, any NVMEM device creation | ||||
| or NVMEM layout driver insertion will be observed as a new event which | ||||
| may lead to the creation of additional cells, without disturbing the | ||||
| probes with costly (and sometimes endless) deferrals. | ||||
| 
 | ||||
| In order to achieve that goal we create a new bus for the nvmem-layouts | ||||
| with minimal logic to match nvmem-layout devices with nvmem-layout | ||||
| drivers. All this infrastructure code is created in the layouts.c file. | ||||
| 
 | ||||
| Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Tested-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | ||||
| Link: https://lore.kernel.org/r/20231215111536.316972-7-srinivas.kandagatla@linaro.org | ||||
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||||
| ---
 | ||||
|  drivers/nvmem/Kconfig            |   1 + | ||||
|  drivers/nvmem/Makefile           |   2 + | ||||
|  drivers/nvmem/core.c             | 170 ++++++++++---------------- | ||||
|  drivers/nvmem/internals.h        |  21 ++++ | ||||
|  drivers/nvmem/layouts.c          | 201 +++++++++++++++++++++++++++++++ | ||||
|  drivers/nvmem/layouts/Kconfig    |   8 ++ | ||||
|  drivers/nvmem/layouts/onie-tlv.c |  24 +++- | ||||
|  drivers/nvmem/layouts/sl28vpd.c  |  24 +++- | ||||
|  include/linux/nvmem-provider.h   |  38 +++--- | ||||
|  9 files changed, 354 insertions(+), 135 deletions(-) | ||||
|  create mode 100644 drivers/nvmem/layouts.c | ||||
| 
 | ||||
| --- a/drivers/nvmem/Kconfig
 | ||||
| +++ b/drivers/nvmem/Kconfig
 | ||||
| @@ -1,6 +1,7 @@
 | ||||
|  # SPDX-License-Identifier: GPL-2.0-only | ||||
|  menuconfig NVMEM | ||||
|  	bool "NVMEM Support" | ||||
| +	imply NVMEM_LAYOUTS
 | ||||
|  	help | ||||
|  	  Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES... | ||||
|   | ||||
| --- a/drivers/nvmem/Makefile
 | ||||
| +++ b/drivers/nvmem/Makefile
 | ||||
| @@ -5,6 +5,8 @@
 | ||||
|   | ||||
|  obj-$(CONFIG_NVMEM)		+= nvmem_core.o | ||||
|  nvmem_core-y			:= core.o | ||||
| +obj-$(CONFIG_NVMEM_LAYOUTS)	+= nvmem_layouts.o
 | ||||
| +nvmem_layouts-y			:= layouts.o
 | ||||
|  obj-y				+= layouts/ | ||||
|   | ||||
|  # Devices | ||||
| --- a/drivers/nvmem/core.c
 | ||||
| +++ b/drivers/nvmem/core.c
 | ||||
| @@ -55,9 +55,6 @@ static LIST_HEAD(nvmem_lookup_list);
 | ||||
|   | ||||
|  static BLOCKING_NOTIFIER_HEAD(nvmem_notifier); | ||||
|   | ||||
| -static DEFINE_SPINLOCK(nvmem_layout_lock);
 | ||||
| -static LIST_HEAD(nvmem_layouts);
 | ||||
| -
 | ||||
|  static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset, | ||||
|  			    void *val, size_t bytes) | ||||
|  { | ||||
| @@ -740,97 +737,22 @@ static int nvmem_add_cells_from_fixed_la
 | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| -int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner)
 | ||||
| +int nvmem_layout_register(struct nvmem_layout *layout)
 | ||||
|  { | ||||
| -	layout->owner = owner;
 | ||||
| -
 | ||||
| -	spin_lock(&nvmem_layout_lock);
 | ||||
| -	list_add(&layout->node, &nvmem_layouts);
 | ||||
| -	spin_unlock(&nvmem_layout_lock);
 | ||||
| -
 | ||||
| -	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_ADD, layout);
 | ||||
| +	if (!layout->add_cells)
 | ||||
| +		return -EINVAL;
 | ||||
|   | ||||
| -	return 0;
 | ||||
| +	/* Populate the cells */
 | ||||
| +	return layout->add_cells(&layout->nvmem->dev, layout->nvmem);
 | ||||
|  } | ||||
| -EXPORT_SYMBOL_GPL(__nvmem_layout_register);
 | ||||
| +EXPORT_SYMBOL_GPL(nvmem_layout_register);
 | ||||
|   | ||||
|  void nvmem_layout_unregister(struct nvmem_layout *layout) | ||||
|  { | ||||
| -	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_REMOVE, layout);
 | ||||
| -
 | ||||
| -	spin_lock(&nvmem_layout_lock);
 | ||||
| -	list_del(&layout->node);
 | ||||
| -	spin_unlock(&nvmem_layout_lock);
 | ||||
| +	/* Keep the API even with an empty stub in case we need it later */
 | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(nvmem_layout_unregister); | ||||
|   | ||||
| -static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
 | ||||
| -{
 | ||||
| -	struct device_node *layout_np;
 | ||||
| -	struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER);
 | ||||
| -
 | ||||
| -	layout_np = of_nvmem_layout_get_container(nvmem);
 | ||||
| -	if (!layout_np)
 | ||||
| -		return NULL;
 | ||||
| -
 | ||||
| -	/* Fixed layouts don't have a matching driver */
 | ||||
| -	if (of_device_is_compatible(layout_np, "fixed-layout")) {
 | ||||
| -		of_node_put(layout_np);
 | ||||
| -		return NULL;
 | ||||
| -	}
 | ||||
| -
 | ||||
| -	/*
 | ||||
| -	 * In case the nvmem device was built-in while the layout was built as a
 | ||||
| -	 * module, we shall manually request the layout driver loading otherwise
 | ||||
| -	 * we'll never have any match.
 | ||||
| -	 */
 | ||||
| -	of_request_module(layout_np);
 | ||||
| -
 | ||||
| -	spin_lock(&nvmem_layout_lock);
 | ||||
| -
 | ||||
| -	list_for_each_entry(l, &nvmem_layouts, node) {
 | ||||
| -		if (of_match_node(l->of_match_table, layout_np)) {
 | ||||
| -			if (try_module_get(l->owner))
 | ||||
| -				layout = l;
 | ||||
| -
 | ||||
| -			break;
 | ||||
| -		}
 | ||||
| -	}
 | ||||
| -
 | ||||
| -	spin_unlock(&nvmem_layout_lock);
 | ||||
| -	of_node_put(layout_np);
 | ||||
| -
 | ||||
| -	return layout;
 | ||||
| -}
 | ||||
| -
 | ||||
| -static void nvmem_layout_put(struct nvmem_layout *layout)
 | ||||
| -{
 | ||||
| -	if (layout)
 | ||||
| -		module_put(layout->owner);
 | ||||
| -}
 | ||||
| -
 | ||||
| -static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem)
 | ||||
| -{
 | ||||
| -	struct nvmem_layout *layout = nvmem->layout;
 | ||||
| -	int ret;
 | ||||
| -
 | ||||
| -	if (layout && layout->add_cells) {
 | ||||
| -		ret = layout->add_cells(&nvmem->dev, nvmem);
 | ||||
| -		if (ret)
 | ||||
| -			return ret;
 | ||||
| -	}
 | ||||
| -
 | ||||
| -	return 0;
 | ||||
| -}
 | ||||
| -
 | ||||
| -#if IS_ENABLED(CONFIG_OF)
 | ||||
| -struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
 | ||||
| -{
 | ||||
| -	return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
 | ||||
| -}
 | ||||
| -EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
 | ||||
| -#endif
 | ||||
| -
 | ||||
|  const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem, | ||||
|  					struct nvmem_layout *layout) | ||||
|  { | ||||
| @@ -838,7 +760,7 @@ const void *nvmem_layout_get_match_data(
 | ||||
|  	const struct of_device_id *match; | ||||
|   | ||||
|  	layout_np = of_nvmem_layout_get_container(nvmem); | ||||
| -	match = of_match_node(layout->of_match_table, layout_np);
 | ||||
| +	match = of_match_node(layout->dev.driver->of_match_table, layout_np);
 | ||||
|   | ||||
|  	return match ? match->data : NULL; | ||||
|  } | ||||
| @@ -950,19 +872,6 @@ struct nvmem_device *nvmem_register(cons
 | ||||
|  			goto err_put_device; | ||||
|  	} | ||||
|   | ||||
| -	/*
 | ||||
| -	 * If the driver supplied a layout by config->layout, the module
 | ||||
| -	 * pointer will be NULL and nvmem_layout_put() will be a noop.
 | ||||
| -	 */
 | ||||
| -	nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
 | ||||
| -	if (IS_ERR(nvmem->layout)) {
 | ||||
| -		rval = PTR_ERR(nvmem->layout);
 | ||||
| -		nvmem->layout = NULL;
 | ||||
| -
 | ||||
| -		if (rval == -EPROBE_DEFER)
 | ||||
| -			goto err_teardown_compat;
 | ||||
| -	}
 | ||||
| -
 | ||||
|  	if (config->cells) { | ||||
|  		rval = nvmem_add_cells(nvmem, config->cells, config->ncells); | ||||
|  		if (rval) | ||||
| @@ -983,24 +892,24 @@ struct nvmem_device *nvmem_register(cons
 | ||||
|  	if (rval) | ||||
|  		goto err_remove_cells; | ||||
|   | ||||
| -	rval = nvmem_add_cells_from_layout(nvmem);
 | ||||
| -	if (rval)
 | ||||
| -		goto err_remove_cells;
 | ||||
| -
 | ||||
|  	dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); | ||||
|   | ||||
|  	rval = device_add(&nvmem->dev); | ||||
|  	if (rval) | ||||
|  		goto err_remove_cells; | ||||
|   | ||||
| +	rval = nvmem_populate_layout(nvmem);
 | ||||
| +	if (rval)
 | ||||
| +		goto err_remove_dev;
 | ||||
| +
 | ||||
|  	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem); | ||||
|   | ||||
|  	return nvmem; | ||||
|   | ||||
| +err_remove_dev:
 | ||||
| +	device_del(&nvmem->dev);
 | ||||
|  err_remove_cells: | ||||
|  	nvmem_device_remove_all_cells(nvmem); | ||||
| -	nvmem_layout_put(nvmem->layout);
 | ||||
| -err_teardown_compat:
 | ||||
|  	if (config->compat) | ||||
|  		nvmem_sysfs_remove_compat(nvmem, config); | ||||
|  err_put_device: | ||||
| @@ -1022,7 +931,7 @@ static void nvmem_device_release(struct
 | ||||
|  		device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); | ||||
|   | ||||
|  	nvmem_device_remove_all_cells(nvmem); | ||||
| -	nvmem_layout_put(nvmem->layout);
 | ||||
| +	nvmem_destroy_layout(nvmem);
 | ||||
|  	device_unregister(&nvmem->dev); | ||||
|  } | ||||
|   | ||||
| @@ -1324,6 +1233,12 @@ nvmem_cell_get_from_lookup(struct device
 | ||||
|  	return cell; | ||||
|  } | ||||
|   | ||||
| +static void nvmem_layout_module_put(struct nvmem_device *nvmem)
 | ||||
| +{
 | ||||
| +	if (nvmem->layout && nvmem->layout->dev.driver)
 | ||||
| +		module_put(nvmem->layout->dev.driver->owner);
 | ||||
| +}
 | ||||
| +
 | ||||
|  #if IS_ENABLED(CONFIG_OF) | ||||
|  static struct nvmem_cell_entry * | ||||
|  nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np) | ||||
| @@ -1342,6 +1257,18 @@ nvmem_find_cell_entry_by_node(struct nvm
 | ||||
|  	return cell; | ||||
|  } | ||||
|   | ||||
| +static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem)
 | ||||
| +{
 | ||||
| +	if (!nvmem->layout)
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
| +	if (!nvmem->layout->dev.driver ||
 | ||||
| +	    !try_module_get(nvmem->layout->dev.driver->owner))
 | ||||
| +		return -EPROBE_DEFER;
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
|  /** | ||||
|   * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id | ||||
|   * | ||||
| @@ -1404,16 +1331,29 @@ struct nvmem_cell *of_nvmem_cell_get(str
 | ||||
|  		return ERR_CAST(nvmem); | ||||
|  	} | ||||
|   | ||||
| +	ret = nvmem_layout_module_get_optional(nvmem);
 | ||||
| +	if (ret) {
 | ||||
| +		of_node_put(cell_np);
 | ||||
| +		__nvmem_device_put(nvmem);
 | ||||
| +		return ERR_PTR(ret);
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np); | ||||
|  	of_node_put(cell_np); | ||||
|  	if (!cell_entry) { | ||||
|  		__nvmem_device_put(nvmem); | ||||
| -		return ERR_PTR(-ENOENT);
 | ||||
| +		nvmem_layout_module_put(nvmem);
 | ||||
| +		if (nvmem->layout)
 | ||||
| +			return ERR_PTR(-EPROBE_DEFER);
 | ||||
| +		else
 | ||||
| +			return ERR_PTR(-ENOENT);
 | ||||
|  	} | ||||
|   | ||||
|  	cell = nvmem_create_cell(cell_entry, id, cell_index); | ||||
| -	if (IS_ERR(cell))
 | ||||
| +	if (IS_ERR(cell)) {
 | ||||
|  		__nvmem_device_put(nvmem); | ||||
| +		nvmem_layout_module_put(nvmem);
 | ||||
| +	}
 | ||||
|   | ||||
|  	return cell; | ||||
|  } | ||||
| @@ -1527,6 +1467,7 @@ void nvmem_cell_put(struct nvmem_cell *c
 | ||||
|   | ||||
|  	kfree(cell); | ||||
|  	__nvmem_device_put(nvmem); | ||||
| +	nvmem_layout_module_put(nvmem);
 | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(nvmem_cell_put); | ||||
|   | ||||
| @@ -2104,11 +2045,22 @@ EXPORT_SYMBOL_GPL(nvmem_dev_name);
 | ||||
|   | ||||
|  static int __init nvmem_init(void) | ||||
|  { | ||||
| -	return bus_register(&nvmem_bus_type);
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	ret = bus_register(&nvmem_bus_type);
 | ||||
| +	if (ret)
 | ||||
| +		return ret;
 | ||||
| +
 | ||||
| +	ret = nvmem_layout_bus_register();
 | ||||
| +	if (ret)
 | ||||
| +		bus_unregister(&nvmem_bus_type);
 | ||||
| +
 | ||||
| +	return ret;
 | ||||
|  } | ||||
|   | ||||
|  static void __exit nvmem_exit(void) | ||||
|  { | ||||
| +	nvmem_layout_bus_unregister();
 | ||||
|  	bus_unregister(&nvmem_bus_type); | ||||
|  } | ||||
|   | ||||
| --- a/drivers/nvmem/internals.h
 | ||||
| +++ b/drivers/nvmem/internals.h
 | ||||
| @@ -34,4 +34,25 @@ struct nvmem_device {
 | ||||
|  	void *priv; | ||||
|  }; | ||||
|   | ||||
| +#if IS_ENABLED(CONFIG_OF)
 | ||||
| +int nvmem_layout_bus_register(void);
 | ||||
| +void nvmem_layout_bus_unregister(void);
 | ||||
| +int nvmem_populate_layout(struct nvmem_device *nvmem);
 | ||||
| +void nvmem_destroy_layout(struct nvmem_device *nvmem);
 | ||||
| +#else /* CONFIG_OF */
 | ||||
| +static inline int nvmem_layout_bus_register(void)
 | ||||
| +{
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static inline void nvmem_layout_bus_unregister(void) {}
 | ||||
| +
 | ||||
| +static inline int nvmem_populate_layout(struct nvmem_device *nvmem)
 | ||||
| +{
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static inline void nvmem_destroy_layout(struct nvmem_device *nvmem) { }
 | ||||
| +#endif /* CONFIG_OF */
 | ||||
| +
 | ||||
|  #endif  /* ifndef _LINUX_NVMEM_INTERNALS_H */ | ||||
| --- /dev/null
 | ||||
| +++ b/drivers/nvmem/layouts.c
 | ||||
| @@ -0,0 +1,201 @@
 | ||||
| +// SPDX-License-Identifier: GPL-2.0
 | ||||
| +/*
 | ||||
| + * NVMEM layout bus handling
 | ||||
| + *
 | ||||
| + * Copyright (C) 2023 Bootlin
 | ||||
| + * Author: Miquel Raynal <miquel.raynal@bootlin.com
 | ||||
| + */
 | ||||
| +
 | ||||
| +#include <linux/device.h>
 | ||||
| +#include <linux/dma-mapping.h>
 | ||||
| +#include <linux/nvmem-consumer.h>
 | ||||
| +#include <linux/nvmem-provider.h>
 | ||||
| +#include <linux/of.h>
 | ||||
| +#include <linux/of_device.h>
 | ||||
| +#include <linux/of_irq.h>
 | ||||
| +
 | ||||
| +#include "internals.h"
 | ||||
| +
 | ||||
| +#define to_nvmem_layout_driver(drv) \
 | ||||
| +	(container_of((drv), struct nvmem_layout_driver, driver))
 | ||||
| +#define to_nvmem_layout_device(_dev) \
 | ||||
| +	container_of((_dev), struct nvmem_layout, dev)
 | ||||
| +
 | ||||
| +static int nvmem_layout_bus_match(struct device *dev, struct device_driver *drv)
 | ||||
| +{
 | ||||
| +	return of_driver_match_device(dev, drv);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int nvmem_layout_bus_probe(struct device *dev)
 | ||||
| +{
 | ||||
| +	struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver);
 | ||||
| +	struct nvmem_layout *layout = to_nvmem_layout_device(dev);
 | ||||
| +
 | ||||
| +	if (!drv->probe || !drv->remove)
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	return drv->probe(layout);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void nvmem_layout_bus_remove(struct device *dev)
 | ||||
| +{
 | ||||
| +	struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver);
 | ||||
| +	struct nvmem_layout *layout = to_nvmem_layout_device(dev);
 | ||||
| +
 | ||||
| +	return drv->remove(layout);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static struct bus_type nvmem_layout_bus_type = {
 | ||||
| +	.name		= "nvmem-layout",
 | ||||
| +	.match		= nvmem_layout_bus_match,
 | ||||
| +	.probe		= nvmem_layout_bus_probe,
 | ||||
| +	.remove		= nvmem_layout_bus_remove,
 | ||||
| +};
 | ||||
| +
 | ||||
| +int nvmem_layout_driver_register(struct nvmem_layout_driver *drv)
 | ||||
| +{
 | ||||
| +	drv->driver.bus = &nvmem_layout_bus_type;
 | ||||
| +
 | ||||
| +	return driver_register(&drv->driver);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(nvmem_layout_driver_register);
 | ||||
| +
 | ||||
| +void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv)
 | ||||
| +{
 | ||||
| +	driver_unregister(&drv->driver);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(nvmem_layout_driver_unregister);
 | ||||
| +
 | ||||
| +static void nvmem_layout_release_device(struct device *dev)
 | ||||
| +{
 | ||||
| +	struct nvmem_layout *layout = to_nvmem_layout_device(dev);
 | ||||
| +
 | ||||
| +	of_node_put(layout->dev.of_node);
 | ||||
| +	kfree(layout);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int nvmem_layout_create_device(struct nvmem_device *nvmem,
 | ||||
| +				      struct device_node *np)
 | ||||
| +{
 | ||||
| +	struct nvmem_layout *layout;
 | ||||
| +	struct device *dev;
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	layout = kzalloc(sizeof(*layout), GFP_KERNEL);
 | ||||
| +	if (!layout)
 | ||||
| +		return -ENOMEM;
 | ||||
| +
 | ||||
| +	/* Create a bidirectional link */
 | ||||
| +	layout->nvmem = nvmem;
 | ||||
| +	nvmem->layout = layout;
 | ||||
| +
 | ||||
| +	/* Device model registration */
 | ||||
| +	dev = &layout->dev;
 | ||||
| +	device_initialize(dev);
 | ||||
| +	dev->parent = &nvmem->dev;
 | ||||
| +	dev->bus = &nvmem_layout_bus_type;
 | ||||
| +	dev->release = nvmem_layout_release_device;
 | ||||
| +	dev->coherent_dma_mask = DMA_BIT_MASK(32);
 | ||||
| +	dev->dma_mask = &dev->coherent_dma_mask;
 | ||||
| +	device_set_node(dev, of_fwnode_handle(of_node_get(np)));
 | ||||
| +	of_device_make_bus_id(dev);
 | ||||
| +	of_msi_configure(dev, dev->of_node);
 | ||||
| +
 | ||||
| +	ret = device_add(dev);
 | ||||
| +	if (ret) {
 | ||||
| +		put_device(dev);
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static const struct of_device_id of_nvmem_layout_skip_table[] = {
 | ||||
| +	{ .compatible = "fixed-layout", },
 | ||||
| +	{}
 | ||||
| +};
 | ||||
| +
 | ||||
| +static int nvmem_layout_bus_populate(struct nvmem_device *nvmem,
 | ||||
| +				     struct device_node *layout_dn)
 | ||||
| +{
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	/* Make sure it has a compatible property */
 | ||||
| +	if (!of_get_property(layout_dn, "compatible", NULL)) {
 | ||||
| +		pr_debug("%s() - skipping %pOF, no compatible prop\n",
 | ||||
| +			 __func__, layout_dn);
 | ||||
| +		return 0;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* Fixed layouts are parsed manually somewhere else for now */
 | ||||
| +	if (of_match_node(of_nvmem_layout_skip_table, layout_dn)) {
 | ||||
| +		pr_debug("%s() - skipping %pOF node\n", __func__, layout_dn);
 | ||||
| +		return 0;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	if (of_node_check_flag(layout_dn, OF_POPULATED_BUS)) {
 | ||||
| +		pr_debug("%s() - skipping %pOF, already populated\n",
 | ||||
| +			 __func__, layout_dn);
 | ||||
| +
 | ||||
| +		return 0;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* NVMEM layout buses expect only a single device representing the layout */
 | ||||
| +	ret = nvmem_layout_create_device(nvmem, layout_dn);
 | ||||
| +	if (ret)
 | ||||
| +		return ret;
 | ||||
| +
 | ||||
| +	of_node_set_flag(layout_dn, OF_POPULATED_BUS);
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
 | ||||
| +{
 | ||||
| +	return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Returns the number of devices populated, 0 if the operation was not relevant
 | ||||
| + * for this nvmem device, an error code otherwise.
 | ||||
| + */
 | ||||
| +int nvmem_populate_layout(struct nvmem_device *nvmem)
 | ||||
| +{
 | ||||
| +	struct device_node *layout_dn;
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	layout_dn = of_nvmem_layout_get_container(nvmem);
 | ||||
| +	if (!layout_dn)
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
| +	/* Populate the layout device */
 | ||||
| +	device_links_supplier_sync_state_pause();
 | ||||
| +	ret = nvmem_layout_bus_populate(nvmem, layout_dn);
 | ||||
| +	device_links_supplier_sync_state_resume();
 | ||||
| +
 | ||||
| +	of_node_put(layout_dn);
 | ||||
| +	return ret;
 | ||||
| +}
 | ||||
| +
 | ||||
| +void nvmem_destroy_layout(struct nvmem_device *nvmem)
 | ||||
| +{
 | ||||
| +	struct device *dev;
 | ||||
| +
 | ||||
| +	if (!nvmem->layout)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	dev = &nvmem->layout->dev;
 | ||||
| +	of_node_clear_flag(dev->of_node, OF_POPULATED_BUS);
 | ||||
| +	device_unregister(dev);
 | ||||
| +}
 | ||||
| +
 | ||||
| +int nvmem_layout_bus_register(void)
 | ||||
| +{
 | ||||
| +	return bus_register(&nvmem_layout_bus_type);
 | ||||
| +}
 | ||||
| +
 | ||||
| +void nvmem_layout_bus_unregister(void)
 | ||||
| +{
 | ||||
| +	bus_unregister(&nvmem_layout_bus_type);
 | ||||
| +}
 | ||||
| --- a/drivers/nvmem/layouts/Kconfig
 | ||||
| +++ b/drivers/nvmem/layouts/Kconfig
 | ||||
| @@ -1,5 +1,11 @@
 | ||||
|  # SPDX-License-Identifier: GPL-2.0 | ||||
|   | ||||
| +config NVMEM_LAYOUTS
 | ||||
| +	bool
 | ||||
| +	depends on OF
 | ||||
| +
 | ||||
| +if NVMEM_LAYOUTS
 | ||||
| +
 | ||||
|  menu "Layout Types" | ||||
|   | ||||
|  config NVMEM_LAYOUT_SL28_VPD | ||||
| @@ -21,3 +27,5 @@ config NVMEM_LAYOUT_ONIE_TLV
 | ||||
|  	  If unsure, say N. | ||||
|   | ||||
|  endmenu | ||||
| +
 | ||||
| +endif
 | ||||
| --- a/drivers/nvmem/layouts/onie-tlv.c
 | ||||
| +++ b/drivers/nvmem/layouts/onie-tlv.c
 | ||||
| @@ -225,16 +225,32 @@ static int onie_tlv_parse_table(struct d
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int onie_tlv_probe(struct nvmem_layout *layout)
 | ||||
| +{
 | ||||
| +	layout->add_cells = onie_tlv_parse_table;
 | ||||
| +
 | ||||
| +	return nvmem_layout_register(layout);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void onie_tlv_remove(struct nvmem_layout *layout)
 | ||||
| +{
 | ||||
| +	nvmem_layout_unregister(layout);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static const struct of_device_id onie_tlv_of_match_table[] = { | ||||
|  	{ .compatible = "onie,tlv-layout", }, | ||||
|  	{}, | ||||
|  }; | ||||
|  MODULE_DEVICE_TABLE(of, onie_tlv_of_match_table); | ||||
|   | ||||
| -static struct nvmem_layout onie_tlv_layout = {
 | ||||
| -	.name = "ONIE tlv layout",
 | ||||
| -	.of_match_table = onie_tlv_of_match_table,
 | ||||
| -	.add_cells = onie_tlv_parse_table,
 | ||||
| +static struct nvmem_layout_driver onie_tlv_layout = {
 | ||||
| +	.driver = {
 | ||||
| +		.owner = THIS_MODULE,
 | ||||
| +		.name = "onie-tlv-layout",
 | ||||
| +		.of_match_table = onie_tlv_of_match_table,
 | ||||
| +	},
 | ||||
| +	.probe = onie_tlv_probe,
 | ||||
| +	.remove = onie_tlv_remove,
 | ||||
|  }; | ||||
|  module_nvmem_layout_driver(onie_tlv_layout); | ||||
|   | ||||
| --- a/drivers/nvmem/layouts/sl28vpd.c
 | ||||
| +++ b/drivers/nvmem/layouts/sl28vpd.c
 | ||||
| @@ -134,16 +134,32 @@ static int sl28vpd_add_cells(struct devi
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int sl28vpd_probe(struct nvmem_layout *layout)
 | ||||
| +{
 | ||||
| +	layout->add_cells = sl28vpd_add_cells;
 | ||||
| +
 | ||||
| +	return nvmem_layout_register(layout);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static void sl28vpd_remove(struct nvmem_layout *layout)
 | ||||
| +{
 | ||||
| +	nvmem_layout_unregister(layout);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static const struct of_device_id sl28vpd_of_match_table[] = { | ||||
|  	{ .compatible = "kontron,sl28-vpd" }, | ||||
|  	{}, | ||||
|  }; | ||||
|  MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table); | ||||
|   | ||||
| -static struct nvmem_layout sl28vpd_layout = {
 | ||||
| -	.name = "sl28-vpd",
 | ||||
| -	.of_match_table = sl28vpd_of_match_table,
 | ||||
| -	.add_cells = sl28vpd_add_cells,
 | ||||
| +static struct nvmem_layout_driver sl28vpd_layout = {
 | ||||
| +	.driver = {
 | ||||
| +		.owner = THIS_MODULE,
 | ||||
| +		.name = "kontron-sl28vpd-layout",
 | ||||
| +		.of_match_table = sl28vpd_of_match_table,
 | ||||
| +	},
 | ||||
| +	.probe = sl28vpd_probe,
 | ||||
| +	.remove = sl28vpd_remove,
 | ||||
|  }; | ||||
|  module_nvmem_layout_driver(sl28vpd_layout); | ||||
|   | ||||
| --- a/include/linux/nvmem-provider.h
 | ||||
| +++ b/include/linux/nvmem-provider.h
 | ||||
| @@ -9,6 +9,7 @@
 | ||||
|  #ifndef _LINUX_NVMEM_PROVIDER_H | ||||
|  #define _LINUX_NVMEM_PROVIDER_H | ||||
|   | ||||
| +#include <linux/device.h>
 | ||||
|  #include <linux/device/driver.h> | ||||
|  #include <linux/err.h> | ||||
|  #include <linux/errno.h> | ||||
| @@ -158,12 +159,11 @@ struct nvmem_cell_table {
 | ||||
|  /** | ||||
|   * struct nvmem_layout - NVMEM layout definitions | ||||
|   * | ||||
| - * @name:		Layout name.
 | ||||
| - * @of_match_table:	Open firmware match table.
 | ||||
| - * @add_cells:		Called to populate the layout using
 | ||||
| - *			nvmem_add_one_cell().
 | ||||
| - * @owner:		Pointer to struct module.
 | ||||
| - * @node:		List node.
 | ||||
| + * @dev:		Device-model layout device.
 | ||||
| + * @nvmem:		The underlying NVMEM device
 | ||||
| + * @add_cells:		Will be called if a nvmem device is found which
 | ||||
| + *			has this layout. The function will add layout
 | ||||
| + *			specific cells with nvmem_add_one_cell().
 | ||||
|   * | ||||
|   * A nvmem device can hold a well defined structure which can just be | ||||
|   * evaluated during runtime. For example a TLV list, or a list of "name=val" | ||||
| @@ -171,13 +171,15 @@ struct nvmem_cell_table {
 | ||||
|   * cells. | ||||
|   */ | ||||
|  struct nvmem_layout { | ||||
| -	const char *name;
 | ||||
| -	const struct of_device_id *of_match_table;
 | ||||
| +	struct device dev;
 | ||||
| +	struct nvmem_device *nvmem;
 | ||||
|  	int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); | ||||
| +};
 | ||||
|   | ||||
| -	/* private */
 | ||||
| -	struct module *owner;
 | ||||
| -	struct list_head node;
 | ||||
| +struct nvmem_layout_driver {
 | ||||
| +	struct device_driver driver;
 | ||||
| +	int (*probe)(struct nvmem_layout *layout);
 | ||||
| +	void (*remove)(struct nvmem_layout *layout);
 | ||||
|  }; | ||||
|   | ||||
|  #if IS_ENABLED(CONFIG_NVMEM) | ||||
| @@ -194,11 +196,15 @@ void nvmem_del_cell_table(struct nvmem_c
 | ||||
|  int nvmem_add_one_cell(struct nvmem_device *nvmem, | ||||
|  		       const struct nvmem_cell_info *info); | ||||
|   | ||||
| -int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner);
 | ||||
| -#define nvmem_layout_register(layout) \
 | ||||
| -	__nvmem_layout_register(layout, THIS_MODULE)
 | ||||
| +int nvmem_layout_register(struct nvmem_layout *layout);
 | ||||
|  void nvmem_layout_unregister(struct nvmem_layout *layout); | ||||
|   | ||||
| +int nvmem_layout_driver_register(struct nvmem_layout_driver *drv);
 | ||||
| +void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv);
 | ||||
| +#define module_nvmem_layout_driver(__nvmem_layout_driver)		\
 | ||||
| +	module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \
 | ||||
| +		      nvmem_layout_driver_unregister)
 | ||||
| +
 | ||||
|  const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem, | ||||
|  					struct nvmem_layout *layout); | ||||
|   | ||||
| @@ -262,8 +268,4 @@ static inline struct device_node *of_nvm
 | ||||
|   | ||||
|  #endif /* CONFIG_NVMEM && CONFIG_OF */ | ||||
|   | ||||
| -#define module_nvmem_layout_driver(__layout_driver)		\
 | ||||
| -	module_driver(__layout_driver, nvmem_layout_register,	\
 | ||||
| -		      nvmem_layout_unregister)
 | ||||
| -
 | ||||
|  #endif  /* ifndef _LINUX_NVMEM_PROVIDER_H */ | ||||
|  | @ -0,0 +1,240 @@ | |||
| From 0331c611949fffdf486652450901a4dc52bc5cca Mon Sep 17 00:00:00 2001 | ||||
| From: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Date: Fri, 15 Dec 2023 11:15:34 +0000 | ||||
| Subject: [PATCH] nvmem: core: Expose cells through sysfs | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| The binary content of nvmem devices is available to the user so in the | ||||
| easiest cases, finding the content of a cell is rather easy as it is | ||||
| just a matter of looking at a known and fixed offset. However, nvmem | ||||
| layouts have been recently introduced to cope with more advanced | ||||
| situations, where the offset and size of the cells is not known in | ||||
| advance or is dynamic. When using layouts, more advanced parsers are | ||||
| used by the kernel in order to give direct access to the content of each | ||||
| cell, regardless of its position/size in the underlying | ||||
| device. Unfortunately, these information are not accessible by users, | ||||
| unless by fully re-implementing the parser logic in userland. | ||||
| 
 | ||||
| Let's expose the cells and their content through sysfs to avoid these | ||||
| situations. Of course the relevant NVMEM sysfs Kconfig option must be | ||||
| enabled for this support to be available. | ||||
| 
 | ||||
| Not all nvmem devices expose cells. Indeed, the .bin_attrs attribute | ||||
| group member will be filled at runtime only when relevant and will | ||||
| remain empty otherwise. In this case, as the cells attribute group will | ||||
| be empty, it will not lead to any additional folder/file creation. | ||||
| 
 | ||||
| Exposed cells are read-only. There is, in practice, everything in the | ||||
| core to support a write path, but as I don't see any need for that, I | ||||
| prefer to keep the interface simple (and probably safer). The interface | ||||
| is documented as being in the "testing" state which means we can later | ||||
| add a write attribute if though relevant. | ||||
| 
 | ||||
| Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> | ||||
| Tested-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Tested-by: Chen-Yu Tsai <wenst@chromium.org> | ||||
| Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | ||||
| Link: https://lore.kernel.org/r/20231215111536.316972-9-srinivas.kandagatla@linaro.org | ||||
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||||
| ---
 | ||||
|  drivers/nvmem/core.c      | 135 +++++++++++++++++++++++++++++++++++++- | ||||
|  drivers/nvmem/internals.h |   1 + | ||||
|  2 files changed, 135 insertions(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/nvmem/core.c
 | ||||
| +++ b/drivers/nvmem/core.c
 | ||||
| @@ -299,6 +299,43 @@ static umode_t nvmem_bin_attr_is_visible
 | ||||
|  	return nvmem_bin_attr_get_umode(nvmem); | ||||
|  } | ||||
|   | ||||
| +static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry,
 | ||||
| +					    const char *id, int index);
 | ||||
| +
 | ||||
| +static ssize_t nvmem_cell_attr_read(struct file *filp, struct kobject *kobj,
 | ||||
| +				    struct bin_attribute *attr, char *buf,
 | ||||
| +				    loff_t pos, size_t count)
 | ||||
| +{
 | ||||
| +	struct nvmem_cell_entry *entry;
 | ||||
| +	struct nvmem_cell *cell = NULL;
 | ||||
| +	size_t cell_sz, read_len;
 | ||||
| +	void *content;
 | ||||
| +
 | ||||
| +	entry = attr->private;
 | ||||
| +	cell = nvmem_create_cell(entry, entry->name, 0);
 | ||||
| +	if (IS_ERR(cell))
 | ||||
| +		return PTR_ERR(cell);
 | ||||
| +
 | ||||
| +	if (!cell)
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	content = nvmem_cell_read(cell, &cell_sz);
 | ||||
| +	if (IS_ERR(content)) {
 | ||||
| +		read_len = PTR_ERR(content);
 | ||||
| +		goto destroy_cell;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	read_len = min_t(unsigned int, cell_sz - pos, count);
 | ||||
| +	memcpy(buf, content + pos, read_len);
 | ||||
| +	kfree(content);
 | ||||
| +
 | ||||
| +destroy_cell:
 | ||||
| +	kfree_const(cell->id);
 | ||||
| +	kfree(cell);
 | ||||
| +
 | ||||
| +	return read_len;
 | ||||
| +}
 | ||||
| +
 | ||||
|  /* default read/write permissions */ | ||||
|  static struct bin_attribute bin_attr_rw_nvmem = { | ||||
|  	.attr	= { | ||||
| @@ -320,11 +357,21 @@ static const struct attribute_group nvme
 | ||||
|  	.is_bin_visible = nvmem_bin_attr_is_visible, | ||||
|  }; | ||||
|   | ||||
| +/* Cell attributes will be dynamically allocated */
 | ||||
| +static struct attribute_group nvmem_cells_group = {
 | ||||
| +	.name		= "cells",
 | ||||
| +};
 | ||||
| +
 | ||||
|  static const struct attribute_group *nvmem_dev_groups[] = { | ||||
|  	&nvmem_bin_group, | ||||
|  	NULL, | ||||
|  }; | ||||
|   | ||||
| +static const struct attribute_group *nvmem_cells_groups[] = {
 | ||||
| +	&nvmem_cells_group,
 | ||||
| +	NULL,
 | ||||
| +};
 | ||||
| +
 | ||||
|  static struct bin_attribute bin_attr_nvmem_eeprom_compat = { | ||||
|  	.attr	= { | ||||
|  		.name	= "eeprom", | ||||
| @@ -380,6 +427,68 @@ static void nvmem_sysfs_remove_compat(st
 | ||||
|  		device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); | ||||
|  } | ||||
|   | ||||
| +static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
 | ||||
| +{
 | ||||
| +	struct bin_attribute **cells_attrs, *attrs;
 | ||||
| +	struct nvmem_cell_entry *entry;
 | ||||
| +	unsigned int ncells = 0, i = 0;
 | ||||
| +	int ret = 0;
 | ||||
| +
 | ||||
| +	mutex_lock(&nvmem_mutex);
 | ||||
| +
 | ||||
| +	if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) {
 | ||||
| +		nvmem_cells_group.bin_attrs = NULL;
 | ||||
| +		goto unlock_mutex;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* Allocate an array of attributes with a sentinel */
 | ||||
| +	ncells = list_count_nodes(&nvmem->cells);
 | ||||
| +	cells_attrs = devm_kcalloc(&nvmem->dev, ncells + 1,
 | ||||
| +				   sizeof(struct bin_attribute *), GFP_KERNEL);
 | ||||
| +	if (!cells_attrs) {
 | ||||
| +		ret = -ENOMEM;
 | ||||
| +		goto unlock_mutex;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL);
 | ||||
| +	if (!attrs) {
 | ||||
| +		ret = -ENOMEM;
 | ||||
| +		goto unlock_mutex;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* Initialize each attribute to take the name and size of the cell */
 | ||||
| +	list_for_each_entry(entry, &nvmem->cells, node) {
 | ||||
| +		sysfs_bin_attr_init(&attrs[i]);
 | ||||
| +		attrs[i].attr.name = devm_kasprintf(&nvmem->dev, GFP_KERNEL,
 | ||||
| +						    "%s@%x", entry->name,
 | ||||
| +						    entry->offset);
 | ||||
| +		attrs[i].attr.mode = 0444;
 | ||||
| +		attrs[i].size = entry->bytes;
 | ||||
| +		attrs[i].read = &nvmem_cell_attr_read;
 | ||||
| +		attrs[i].private = entry;
 | ||||
| +		if (!attrs[i].attr.name) {
 | ||||
| +			ret = -ENOMEM;
 | ||||
| +			goto unlock_mutex;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		cells_attrs[i] = &attrs[i];
 | ||||
| +		i++;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	nvmem_cells_group.bin_attrs = cells_attrs;
 | ||||
| +
 | ||||
| +	ret = devm_device_add_groups(&nvmem->dev, nvmem_cells_groups);
 | ||||
| +	if (ret)
 | ||||
| +		goto unlock_mutex;
 | ||||
| +
 | ||||
| +	nvmem->sysfs_cells_populated = true;
 | ||||
| +
 | ||||
| +unlock_mutex:
 | ||||
| +	mutex_unlock(&nvmem_mutex);
 | ||||
| +
 | ||||
| +	return ret;
 | ||||
| +}
 | ||||
| +
 | ||||
|  #else /* CONFIG_NVMEM_SYSFS */ | ||||
|   | ||||
|  static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, | ||||
| @@ -739,11 +848,25 @@ static int nvmem_add_cells_from_fixed_la
 | ||||
|   | ||||
|  int nvmem_layout_register(struct nvmem_layout *layout) | ||||
|  { | ||||
| +	int ret;
 | ||||
| +
 | ||||
|  	if (!layout->add_cells) | ||||
|  		return -EINVAL; | ||||
|   | ||||
|  	/* Populate the cells */ | ||||
| -	return layout->add_cells(&layout->nvmem->dev, layout->nvmem);
 | ||||
| +	ret = layout->add_cells(&layout->nvmem->dev, layout->nvmem);
 | ||||
| +	if (ret)
 | ||||
| +		return ret;
 | ||||
| +
 | ||||
| +#ifdef CONFIG_NVMEM_SYSFS
 | ||||
| +	ret = nvmem_populate_sysfs_cells(layout->nvmem);
 | ||||
| +	if (ret) {
 | ||||
| +		nvmem_device_remove_all_cells(layout->nvmem);
 | ||||
| +		return ret;
 | ||||
| +	}
 | ||||
| +#endif
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(nvmem_layout_register); | ||||
|   | ||||
| @@ -902,10 +1025,20 @@ struct nvmem_device *nvmem_register(cons
 | ||||
|  	if (rval) | ||||
|  		goto err_remove_dev; | ||||
|   | ||||
| +#ifdef CONFIG_NVMEM_SYSFS
 | ||||
| +	rval = nvmem_populate_sysfs_cells(nvmem);
 | ||||
| +	if (rval)
 | ||||
| +		goto err_destroy_layout;
 | ||||
| +#endif
 | ||||
| +
 | ||||
|  	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem); | ||||
|   | ||||
|  	return nvmem; | ||||
|   | ||||
| +#ifdef CONFIG_NVMEM_SYSFS
 | ||||
| +err_destroy_layout:
 | ||||
| +	nvmem_destroy_layout(nvmem);
 | ||||
| +#endif
 | ||||
|  err_remove_dev: | ||||
|  	device_del(&nvmem->dev); | ||||
|  err_remove_cells: | ||||
| --- a/drivers/nvmem/internals.h
 | ||||
| +++ b/drivers/nvmem/internals.h
 | ||||
| @@ -32,6 +32,7 @@ struct nvmem_device {
 | ||||
|  	struct gpio_desc	*wp_gpio; | ||||
|  	struct nvmem_layout	*layout; | ||||
|  	void *priv; | ||||
| +	bool			sysfs_cells_populated;
 | ||||
|  }; | ||||
|   | ||||
|  #if IS_ENABLED(CONFIG_OF) | ||||
|  | @ -0,0 +1,65 @@ | |||
| From f0ac5b23039610619ca4a4805528553ecb6bc815 Mon Sep 17 00:00:00 2001 | ||||
| From: Patrick Delaunay <patrick.delaunay@foss.st.com> | ||||
| Date: Fri, 15 Dec 2023 11:15:36 +0000 | ||||
| Subject: [PATCH] nvmem: stm32: add support for STM32MP25 BSEC to control OTP | ||||
|  data | ||||
| 
 | ||||
| On STM32MP25, OTP area may be read/written by using BSEC (boot, security | ||||
| and OTP control). The BSEC internal peripheral is only managed by the | ||||
| secure world. | ||||
| 
 | ||||
| The 12 Kbits of OTP (effective) are organized into the following regions: | ||||
| - lower OTP (OTP0 to OTP127) = 4096 lower OTP bits,
 | ||||
|   bitwise (1-bit) programmable | ||||
| - mid OTP (OTP128 to OTP255) = 4096 middle OTP bits,
 | ||||
|   bulk (32-bit) programmable | ||||
| - upper OTP (OTP256 to OTP383) = 4096 upper OTP bits,
 | ||||
|   bulk (32-bit) programmable, | ||||
|   only accessible when BSEC is in closed state. | ||||
| 
 | ||||
| As HWKEY and ECIES key are only accessible by ROM code; | ||||
| only 368 OTP words are managed in this driver (OTP0 to OTP267). | ||||
| 
 | ||||
| This patch adds the STM32MP25 configuration for reading and writing | ||||
| the OTP data using the OP-TEE BSEC TA services. | ||||
| 
 | ||||
| Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com> | ||||
| Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | ||||
| Link: https://lore.kernel.org/r/20231215111536.316972-11-srinivas.kandagatla@linaro.org | ||||
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||||
| ---
 | ||||
|  drivers/nvmem/stm32-romem.c | 16 ++++++++++++++++ | ||||
|  1 file changed, 16 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/nvmem/stm32-romem.c
 | ||||
| +++ b/drivers/nvmem/stm32-romem.c
 | ||||
| @@ -269,6 +269,19 @@ static const struct stm32_romem_cfg stm3
 | ||||
|  	.ta = true, | ||||
|  }; | ||||
|   | ||||
| +/*
 | ||||
| + * STM32MP25 BSEC OTP: 3 regions of 32-bits data words
 | ||||
| + *   lower OTP (OTP0 to OTP127), bitwise (1-bit) programmable
 | ||||
| + *   mid OTP (OTP128 to OTP255), bulk (32-bit) programmable
 | ||||
| + *   upper OTP (OTP256 to OTP383), bulk (32-bit) programmable
 | ||||
| + *              but no access to HWKEY and ECIES key: limited at OTP367
 | ||||
| + */
 | ||||
| +static const struct stm32_romem_cfg stm32mp25_bsec_cfg = {
 | ||||
| +	.size = 368 * 4,
 | ||||
| +	.lower = 127,
 | ||||
| +	.ta = true,
 | ||||
| +};
 | ||||
| +
 | ||||
|  static const struct of_device_id stm32_romem_of_match[] __maybe_unused = { | ||||
|  	{ .compatible = "st,stm32f4-otp", }, { | ||||
|  		.compatible = "st,stm32mp15-bsec", | ||||
| @@ -276,6 +289,9 @@ static const struct of_device_id stm32_r
 | ||||
|  	}, { | ||||
|  		.compatible = "st,stm32mp13-bsec", | ||||
|  		.data = (void *)&stm32mp13_bsec_cfg, | ||||
| +	}, {
 | ||||
| +		.compatible = "st,stm32mp25-bsec",
 | ||||
| +		.data = (void *)&stm32mp25_bsec_cfg,
 | ||||
|  	}, | ||||
|  	{ /* sentinel */ }, | ||||
|  }; | ||||
|  | @ -0,0 +1,93 @@ | |||
| From edd25a77e69b7c546c28077e5dffe72c54c0afe8 Mon Sep 17 00:00:00 2001 | ||||
| From: Linus Walleij <linus.walleij@linaro.org> | ||||
| Date: Thu, 21 Sep 2023 22:18:12 +0200 | ||||
| Subject: [PATCH 2/4] rtc: rtc7301: Support byte-addressed IO | ||||
| 
 | ||||
| The old RTC7301 driver in OpenWrt used byte access, but the | ||||
| current mainline Linux driver uses 32bit word access. | ||||
| 
 | ||||
| Make this configurable using device properties using the | ||||
| standard property "reg-io-width" in e.g. device tree. | ||||
| 
 | ||||
| This is needed for the USRobotics USR8200 which has the | ||||
| chip connected using byte accesses. | ||||
| 
 | ||||
| Debugging and testing by Howard Harte. | ||||
| 
 | ||||
| Signed-off-by: Linus Walleij <linus.walleij@linaro.org> | ||||
| ---
 | ||||
|  drivers/rtc/rtc-r7301.c | 35 +++++++++++++++++++++++++++++++++-- | ||||
|  1 file changed, 33 insertions(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/rtc/rtc-r7301.c
 | ||||
| +++ b/drivers/rtc/rtc-r7301.c
 | ||||
| @@ -14,6 +14,7 @@
 | ||||
|  #include <linux/module.h> | ||||
|  #include <linux/mod_devicetable.h> | ||||
|  #include <linux/delay.h> | ||||
| +#include <linux/property.h>
 | ||||
|  #include <linux/regmap.h> | ||||
|  #include <linux/platform_device.h> | ||||
|  #include <linux/rtc.h> | ||||
| @@ -55,12 +56,23 @@ struct rtc7301_priv {
 | ||||
|  	u8 bank; | ||||
|  }; | ||||
|   | ||||
| -static const struct regmap_config rtc7301_regmap_config = {
 | ||||
| +/*
 | ||||
| + * When the device is memory-mapped, some platforms pack the registers into
 | ||||
| + * 32-bit access using the lower 8 bits at each 4-byte stride, while others
 | ||||
| + * expose them as simply consecutive bytes.
 | ||||
| + */
 | ||||
| +static const struct regmap_config rtc7301_regmap_32_config = {
 | ||||
|  	.reg_bits = 32, | ||||
|  	.val_bits = 8, | ||||
|  	.reg_stride = 4, | ||||
|  }; | ||||
|   | ||||
| +static const struct regmap_config rtc7301_regmap_8_config = {
 | ||||
| +	.reg_bits = 8,
 | ||||
| +	.val_bits = 8,
 | ||||
| +	.reg_stride = 1,
 | ||||
| +};
 | ||||
| +
 | ||||
|  static u8 rtc7301_read(struct rtc7301_priv *priv, unsigned int reg) | ||||
|  { | ||||
|  	int reg_stride = regmap_get_reg_stride(priv->regmap); | ||||
| @@ -356,7 +368,9 @@ static int __init rtc7301_rtc_probe(stru
 | ||||
|  	void __iomem *regs; | ||||
|  	struct rtc7301_priv *priv; | ||||
|  	struct rtc_device *rtc; | ||||
| +	static const struct regmap_config *mapconf;
 | ||||
|  	int ret; | ||||
| +	u32 val;
 | ||||
|   | ||||
|  	priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); | ||||
|  	if (!priv) | ||||
| @@ -366,8 +380,25 @@ static int __init rtc7301_rtc_probe(stru
 | ||||
|  	if (IS_ERR(regs)) | ||||
|  		return PTR_ERR(regs); | ||||
|   | ||||
| +	ret = device_property_read_u32(&dev->dev, "reg-io-width", &val);
 | ||||
| +	if (ret)
 | ||||
| +		/* Default to 32bit accesses */
 | ||||
| +		val = 4;
 | ||||
| +
 | ||||
| +	switch (val) {
 | ||||
| +	case 1:
 | ||||
| +		mapconf = &rtc7301_regmap_8_config;
 | ||||
| +		break;
 | ||||
| +	case 4:
 | ||||
| +		mapconf = &rtc7301_regmap_32_config;
 | ||||
| +		break;
 | ||||
| +	default:
 | ||||
| +		dev_err(&dev->dev, "invalid reg-io-width %d\n", val);
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	priv->regmap = devm_regmap_init_mmio(&dev->dev, regs, | ||||
| -					     &rtc7301_regmap_config);
 | ||||
| +					     mapconf);
 | ||||
|  	if (IS_ERR(priv->regmap)) | ||||
|  		return PTR_ERR(priv->regmap); | ||||
|   | ||||
|  | @ -0,0 +1,82 @@ | |||
| From 49e5663b505070424e18099841943f34342aa405 Mon Sep 17 00:00:00 2001 | ||||
| From: Linus Walleij <linus.walleij@linaro.org> | ||||
| Date: Sun, 24 Sep 2023 01:09:01 +0200 | ||||
| Subject: [PATCH] net: phy: amd: Support the Altima AMI101L | ||||
| 
 | ||||
| The Altima AC101L is obviously compatible with the AMD PHY, | ||||
| as seen by reading the datasheet. | ||||
| 
 | ||||
| Datasheet: https://docs.broadcom.com/doc/AC101L-DS05-405-RDS.pdf | ||||
| 
 | ||||
| Signed-off-by: Linus Walleij <linus.walleij@linaro.org> | ||||
| ---
 | ||||
|  drivers/net/phy/Kconfig |  4 ++-- | ||||
|  drivers/net/phy/amd.c   | 33 +++++++++++++++++++++++---------- | ||||
|  2 files changed, 25 insertions(+), 12 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/net/phy/Kconfig
 | ||||
| +++ b/drivers/net/phy/Kconfig
 | ||||
| @@ -69,9 +69,9 @@ config SFP
 | ||||
|  comment "MII PHY device drivers" | ||||
|   | ||||
|  config AMD_PHY | ||||
| -	tristate "AMD PHYs"
 | ||||
| +	tristate "AMD and Altima PHYs"
 | ||||
|  	help | ||||
| -	  Currently supports the am79c874
 | ||||
| +	  Currently supports the AMD am79c874 and Altima AC101L.
 | ||||
|   | ||||
|  config MESON_GXL_PHY | ||||
|  	tristate "Amlogic Meson GXL Internal PHY" | ||||
| --- a/drivers/net/phy/amd.c
 | ||||
| +++ b/drivers/net/phy/amd.c
 | ||||
| @@ -13,6 +13,7 @@
 | ||||
|  #include <linux/mii.h> | ||||
|  #include <linux/phy.h> | ||||
|   | ||||
| +#define PHY_ID_AC101L		0x00225520
 | ||||
|  #define PHY_ID_AM79C874		0x0022561b | ||||
|   | ||||
|  #define MII_AM79C_IR		17	/* Interrupt Status/Control Register */ | ||||
| @@ -87,19 +88,31 @@ static irqreturn_t am79c_handle_interrup
 | ||||
|  	return IRQ_HANDLED; | ||||
|  } | ||||
|   | ||||
| -static struct phy_driver am79c_driver[] = { {
 | ||||
| -	.phy_id		= PHY_ID_AM79C874,
 | ||||
| -	.name		= "AM79C874",
 | ||||
| -	.phy_id_mask	= 0xfffffff0,
 | ||||
| -	/* PHY_BASIC_FEATURES */
 | ||||
| -	.config_init	= am79c_config_init,
 | ||||
| -	.config_intr	= am79c_config_intr,
 | ||||
| -	.handle_interrupt = am79c_handle_interrupt,
 | ||||
| -} };
 | ||||
| +static struct phy_driver am79c_drivers[] = {
 | ||||
| +	{
 | ||||
| +		.phy_id		= PHY_ID_AM79C874,
 | ||||
| +		.name		= "AM79C874",
 | ||||
| +		.phy_id_mask	= 0xfffffff0,
 | ||||
| +		/* PHY_BASIC_FEATURES */
 | ||||
| +		.config_init	= am79c_config_init,
 | ||||
| +		.config_intr	= am79c_config_intr,
 | ||||
| +		.handle_interrupt = am79c_handle_interrupt,
 | ||||
| +	},
 | ||||
| +	{
 | ||||
| +		.phy_id		= PHY_ID_AC101L,
 | ||||
| +		.name		= "AC101L",
 | ||||
| +		.phy_id_mask	= 0xfffffff0,
 | ||||
| +		/* PHY_BASIC_FEATURES */
 | ||||
| +		.config_init	= am79c_config_init,
 | ||||
| +		.config_intr	= am79c_config_intr,
 | ||||
| +		.handle_interrupt = am79c_handle_interrupt,
 | ||||
| +	},
 | ||||
| +};
 | ||||
|   | ||||
| -module_phy_driver(am79c_driver);
 | ||||
| +module_phy_driver(am79c_drivers);
 | ||||
|   | ||||
|  static struct mdio_device_id __maybe_unused amd_tbl[] = { | ||||
| +	{ PHY_ID_AC101L, 0xfffffff0 },
 | ||||
|  	{ PHY_ID_AM79C874, 0xfffffff0 }, | ||||
|  	{ } | ||||
|  }; | ||||
|  | @ -0,0 +1,29 @@ | |||
| From a067943129b4ec6b835e02cfd5fbef01093c1471 Mon Sep 17 00:00:00 2001 | ||||
| From: Ondrej Jirman <megi@xff.cz> | ||||
| Date: Sun, 8 Oct 2023 16:40:13 +0200 | ||||
| Subject: [PATCH] leds: core: Add more colors from DT bindings to led_colors | ||||
| 
 | ||||
| The colors are already part of DT bindings. Make sure the kernel is | ||||
| able to convert them to strings. | ||||
| 
 | ||||
| Signed-off-by: Ondrej Jirman <megi@xff.cz> | ||||
| Link: https://lore.kernel.org/r/20231008144014.1180334-1-megi@xff.cz | ||||
| Signed-off-by: Lee Jones <lee@kernel.org> | ||||
| ---
 | ||||
|  drivers/leds/led-core.c | 5 +++++ | ||||
|  1 file changed, 5 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/leds/led-core.c
 | ||||
| +++ b/drivers/leds/led-core.c
 | ||||
| @@ -36,6 +36,11 @@ const char * const led_colors[LED_COLOR_
 | ||||
|  	[LED_COLOR_ID_IR] = "ir", | ||||
|  	[LED_COLOR_ID_MULTI] = "multicolor", | ||||
|  	[LED_COLOR_ID_RGB] = "rgb", | ||||
| +	[LED_COLOR_ID_PURPLE] = "purple",
 | ||||
| +	[LED_COLOR_ID_ORANGE] = "orange",
 | ||||
| +	[LED_COLOR_ID_PINK] = "pink",
 | ||||
| +	[LED_COLOR_ID_CYAN] = "cyan",
 | ||||
| +	[LED_COLOR_ID_LIME] = "lime",
 | ||||
|  }; | ||||
|  EXPORT_SYMBOL_GPL(led_colors); | ||||
|   | ||||
|  | @ -1,120 +0,0 @@ | |||
| From: Birger Koblitz <git@birger-koblitz.de> | ||||
| Date: Sun, 5 Sep 2021 15:13:10 +0200 | ||||
| Subject: [PATCH] kernel: Add AQR113C and AQR813 support | ||||
| 
 | ||||
| This hack adds support for the Aquantia 4th generation, 10GBit | ||||
| PHYs AQR113C and AQR813. | ||||
| 
 | ||||
| Signed-off-by: Birger Koblitz <git@birger-koblitz.de> | ||||
| 
 | ||||
| --- a/drivers/net/phy/aquantia_main.c
 | ||||
| +++ b/drivers/net/phy/aquantia_main.c
 | ||||
| @@ -23,6 +23,7 @@
 | ||||
|  #define PHY_ID_AQCS109	0x03a1b5c2 | ||||
|  #define PHY_ID_AQR405	0x03a1b4b0 | ||||
|  #define PHY_ID_AQR113C	0x31c31c12 | ||||
| +#define PHY_ID_AQR813	0x31c31cb2
 | ||||
|   | ||||
|  #define MDIO_PHYXS_VEND_IF_STATUS		0xe812 | ||||
|  #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK	GENMASK(7, 3) | ||||
| @@ -415,6 +416,49 @@ static int aqr107_read_rate(struct phy_d
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int aqr113c_read_status(struct phy_device *phydev)
 | ||||
| +{
 | ||||
| +	int val, ret;
 | ||||
| +
 | ||||
| +	ret = aqr_read_status(phydev);
 | ||||
| +	if (ret)
 | ||||
| +		return ret;
 | ||||
| +
 | ||||
| +	if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE)
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
| +	// On AQR113C, the speed returned by aqr_read_status is wrong
 | ||||
| +	aqr107_read_rate(phydev);
 | ||||
| +
 | ||||
| +	val = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS);
 | ||||
| +	if (val < 0)
 | ||||
| +		return val;
 | ||||
| +
 | ||||
| +	switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) {
 | ||||
| +	case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR:
 | ||||
| +		phydev->interface = PHY_INTERFACE_MODE_10GKR;
 | ||||
| +		break;
 | ||||
| +	case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI:
 | ||||
| +		phydev->interface = PHY_INTERFACE_MODE_10GBASER;
 | ||||
| +		break;
 | ||||
| +	case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII:
 | ||||
| +		phydev->interface = PHY_INTERFACE_MODE_USXGMII;
 | ||||
| +		break;
 | ||||
| +	case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII:
 | ||||
| +		phydev->interface = PHY_INTERFACE_MODE_SGMII;
 | ||||
| +		break;
 | ||||
| +	case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII:
 | ||||
| +		phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
 | ||||
| +		break;
 | ||||
| +	default:
 | ||||
| +		phydev->interface = PHY_INTERFACE_MODE_NA;
 | ||||
| +		break;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* Read downshifted rate from vendor register */
 | ||||
| +	return aqr107_read_rate(phydev);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static int aqr107_read_status(struct phy_device *phydev) | ||||
|  { | ||||
|  	int val, ret; | ||||
| @@ -554,7 +598,7 @@ static void aqr107_chip_info(struct phy_
 | ||||
|  	build_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID, val); | ||||
|  	prov_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_PROV_ID, val); | ||||
|   | ||||
| -	phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u\n",
 | ||||
| +	phydev_info(phydev, "FW %u.%u, Build %u, Provisioning %u\n",
 | ||||
|  		   fw_major, fw_minor, build_id, prov_id); | ||||
|  } | ||||
|   | ||||
| @@ -809,7 +853,7 @@ static struct phy_driver aqr_driver[] =
 | ||||
|  	.config_aneg    = aqr_config_aneg, | ||||
|  	.config_intr    = aqr_config_intr, | ||||
|  	.handle_interrupt       = aqr_handle_interrupt, | ||||
| -	.read_status    = aqr107_read_status,
 | ||||
| +	.read_status    = aqr113c_read_status,
 | ||||
|  	.get_tunable    = aqr107_get_tunable, | ||||
|  	.set_tunable    = aqr107_set_tunable, | ||||
|  	.suspend        = aqr107_suspend, | ||||
| @@ -819,6 +863,24 @@ static struct phy_driver aqr_driver[] =
 | ||||
|  	.get_stats      = aqr107_get_stats, | ||||
|  	.link_change_notify = aqr107_link_change_notify, | ||||
|  }, | ||||
| +{
 | ||||
| +	PHY_ID_MATCH_MODEL(PHY_ID_AQR813),
 | ||||
| +	.name		= "Aquantia AQR813",
 | ||||
| +	.probe		= aqr107_probe,
 | ||||
| +	.config_init	= aqr107_config_init,
 | ||||
| +	.config_aneg    = aqr_config_aneg,
 | ||||
| +	.config_intr	= aqr_config_intr,
 | ||||
| +	.handle_interrupt = aqr_handle_interrupt,
 | ||||
| +	.read_status	= aqr113c_read_status,
 | ||||
| +	.get_tunable    = aqr107_get_tunable,
 | ||||
| +	.set_tunable    = aqr107_set_tunable,
 | ||||
| +	.suspend	= aqr107_suspend,
 | ||||
| +	.resume		= aqr107_resume,
 | ||||
| +	.get_sset_count	= aqr107_get_sset_count,
 | ||||
| +	.get_strings	= aqr107_get_strings,
 | ||||
| +	.get_stats	= aqr107_get_stats,
 | ||||
| +	.link_change_notify = aqr107_link_change_notify,
 | ||||
| +},
 | ||||
|  }; | ||||
|   | ||||
|  module_phy_driver(aqr_driver); | ||||
| @@ -832,6 +894,7 @@ static struct mdio_device_id __maybe_unu
 | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, | ||||
| +	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR813) },
 | ||||
|  	{ } | ||||
|  }; | ||||
|   | ||||
|  | @ -1,148 +0,0 @@ | |||
| From 5f62951fba63a9f9cfff564209426bdea5fcc371 Mon Sep 17 00:00:00 2001 | ||||
| From: Alex Marginean <alexandru.marginean@nxp.com> | ||||
| Date: Tue, 27 Aug 2019 15:16:56 +0300 | ||||
| Subject: [PATCH] drivers: net: phy: aquantia: enable AQR112 and AQR412 | ||||
| 
 | ||||
| Adds support for AQR112 and AQR412 which is mostly based on existing code | ||||
| with the addition of code configuring the protocol on system side. | ||||
| This allows changing the system side protocol without having to deploy a | ||||
| different firmware on the PHY.
 | ||||
| 
 | ||||
| Signed-off-by: Alex Marginean <alexandru.marginean@nxp.com> | ||||
| ---
 | ||||
|  drivers/net/phy/aquantia_main.c | 88 +++++++++++++++++++++++++++++++++++++++++ | ||||
|  1 file changed, 88 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/net/phy/aquantia_main.c
 | ||||
| +++ b/drivers/net/phy/aquantia_main.c
 | ||||
| @@ -24,6 +24,8 @@
 | ||||
|  #define PHY_ID_AQR405	0x03a1b4b0 | ||||
|  #define PHY_ID_AQR113C	0x31c31c12 | ||||
|  #define PHY_ID_AQR813	0x31c31cb2 | ||||
| +#define PHY_ID_AQR112	0x03a1b662
 | ||||
| +#define PHY_ID_AQR412	0x03a1b712
 | ||||
|   | ||||
|  #define MDIO_PHYXS_VEND_IF_STATUS		0xe812 | ||||
|  #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK	GENMASK(7, 3) | ||||
| @@ -151,6 +153,29 @@
 | ||||
|  #define AQR107_OP_IN_PROG_SLEEP		1000 | ||||
|  #define AQR107_OP_IN_PROG_TIMEOUT	100000 | ||||
|   | ||||
| +/* registers in MDIO_MMD_VEND1 region */
 | ||||
| +#define AQUANTIA_VND1_GLOBAL_SC			0x000
 | ||||
| +#define  AQUANTIA_VND1_GLOBAL_SC_LP		BIT(0xb)
 | ||||
| +
 | ||||
| +/* global start rate, the protocol associated with this speed is used by default
 | ||||
| + * on SI.
 | ||||
| + */
 | ||||
| +#define AQUANTIA_VND1_GSTART_RATE		0x31a
 | ||||
| +#define  AQUANTIA_VND1_GSTART_RATE_OFF		0
 | ||||
| +#define  AQUANTIA_VND1_GSTART_RATE_100M		1
 | ||||
| +#define  AQUANTIA_VND1_GSTART_RATE_1G		2
 | ||||
| +#define  AQUANTIA_VND1_GSTART_RATE_10G		3
 | ||||
| +#define  AQUANTIA_VND1_GSTART_RATE_2_5G		4
 | ||||
| +#define  AQUANTIA_VND1_GSTART_RATE_5G		5
 | ||||
| +
 | ||||
| +/* SYSCFG registers for 100M, 1G, 2.5G, 5G, 10G */
 | ||||
| +#define AQUANTIA_VND1_GSYSCFG_BASE		0x31b
 | ||||
| +#define AQUANTIA_VND1_GSYSCFG_100M		0
 | ||||
| +#define AQUANTIA_VND1_GSYSCFG_1G		1
 | ||||
| +#define AQUANTIA_VND1_GSYSCFG_2_5G		2
 | ||||
| +#define AQUANTIA_VND1_GSYSCFG_5G		3
 | ||||
| +#define AQUANTIA_VND1_GSYSCFG_10G		4
 | ||||
| +
 | ||||
|  struct aqr107_hw_stat { | ||||
|  	const char *name; | ||||
|  	int reg; | ||||
| @@ -282,6 +307,51 @@ static int aqr_config_aneg(struct phy_de
 | ||||
|  	return genphy_c45_check_and_restart_aneg(phydev, changed); | ||||
|  } | ||||
|   | ||||
| +static struct {
 | ||||
| +	u16 syscfg;
 | ||||
| +	int cnt;
 | ||||
| +	u16 start_rate;
 | ||||
| +} aquantia_syscfg[PHY_INTERFACE_MODE_MAX] = {
 | ||||
| +	[PHY_INTERFACE_MODE_SGMII] =      {0x04b, AQUANTIA_VND1_GSYSCFG_1G,
 | ||||
| +					   AQUANTIA_VND1_GSTART_RATE_1G},
 | ||||
| +	[PHY_INTERFACE_MODE_2500BASEX] = {0x144, AQUANTIA_VND1_GSYSCFG_2_5G,
 | ||||
| +					   AQUANTIA_VND1_GSTART_RATE_2_5G},
 | ||||
| +	[PHY_INTERFACE_MODE_XGMII] =      {0x100, AQUANTIA_VND1_GSYSCFG_10G,
 | ||||
| +					   AQUANTIA_VND1_GSTART_RATE_10G},
 | ||||
| +	[PHY_INTERFACE_MODE_USXGMII] =    {0x080, AQUANTIA_VND1_GSYSCFG_10G,
 | ||||
| +					   AQUANTIA_VND1_GSTART_RATE_10G},
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* Sets up protocol on system side before calling aqr_config_aneg */
 | ||||
| +static int aqr_config_aneg_set_prot(struct phy_device *phydev)
 | ||||
| +{
 | ||||
| +	int if_type = phydev->interface;
 | ||||
| +	int i;
 | ||||
| +
 | ||||
| +	if (!aquantia_syscfg[if_type].cnt)
 | ||||
| +		return 0;
 | ||||
| +
 | ||||
| +	/* set PHY in low power mode so we can configure protocols */
 | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC,
 | ||||
| +		      AQUANTIA_VND1_GLOBAL_SC_LP);
 | ||||
| +	mdelay(10);
 | ||||
| +
 | ||||
| +	/* set the default rate to enable the SI link */
 | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE,
 | ||||
| +		      aquantia_syscfg[if_type].start_rate);
 | ||||
| +
 | ||||
| +	for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++)
 | ||||
| +		phy_write_mmd(phydev, MDIO_MMD_VEND1,
 | ||||
| +			      AQUANTIA_VND1_GSYSCFG_BASE + i,
 | ||||
| +			      aquantia_syscfg[if_type].syscfg);
 | ||||
| +
 | ||||
| +	/* wake PHY back up */
 | ||||
| +	phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0);
 | ||||
| +	mdelay(10);
 | ||||
| +
 | ||||
| +	return aqr_config_aneg(phydev);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static int aqr_config_intr(struct phy_device *phydev) | ||||
|  { | ||||
|  	bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED; | ||||
| @@ -881,6 +951,30 @@ static struct phy_driver aqr_driver[] =
 | ||||
|  	.get_stats	= aqr107_get_stats, | ||||
|  	.link_change_notify = aqr107_link_change_notify, | ||||
|  }, | ||||
| +{
 | ||||
| +	PHY_ID_MATCH_MODEL(PHY_ID_AQR112),
 | ||||
| +	.name		= "Aquantia AQR112",
 | ||||
| +	.probe		= aqr107_probe,
 | ||||
| +	.config_aneg	= aqr_config_aneg_set_prot,
 | ||||
| +	.config_intr	= aqr_config_intr,
 | ||||
| +	.handle_interrupt = aqr_handle_interrupt,
 | ||||
| +	.read_status	= aqr107_read_status,
 | ||||
| +	.get_sset_count	= aqr107_get_sset_count,
 | ||||
| +	.get_strings	= aqr107_get_strings,
 | ||||
| +	.get_stats	= aqr107_get_stats,
 | ||||
| +},
 | ||||
| +{
 | ||||
| +	PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
 | ||||
| +	.name		= "Aquantia AQR412",
 | ||||
| +	.probe		= aqr107_probe,
 | ||||
| +	.config_aneg	= aqr_config_aneg_set_prot,
 | ||||
| +	.config_intr	= aqr_config_intr,
 | ||||
| +	.handle_interrupt = aqr_handle_interrupt,
 | ||||
| +	.read_status	= aqr107_read_status,
 | ||||
| +	.get_sset_count	= aqr107_get_sset_count,
 | ||||
| +	.get_strings	= aqr107_get_strings,
 | ||||
| +	.get_stats	= aqr107_get_stats,
 | ||||
| +},
 | ||||
|  }; | ||||
|   | ||||
|  module_phy_driver(aqr_driver); | ||||
| @@ -895,6 +989,8 @@ static struct mdio_device_id __maybe_unu
 | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, | ||||
| +	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
 | ||||
| +	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
 | ||||
|  	{ } | ||||
|  }; | ||||
|   | ||||
|  | @ -1,34 +0,0 @@ | |||
| From 5f008cb22f60da4e10375f22266c1a4e20b1252e Mon Sep 17 00:00:00 2001 | ||||
| From: Alex Marginean <alexandru.marginean@nxp.com> | ||||
| Date: Fri, 20 Sep 2019 18:22:52 +0300 | ||||
| Subject: [PATCH] drivers: net: phy: aquantia: fix system side protocol | ||||
|  misconfiguration | ||||
| 
 | ||||
| Do not set up protocols for speeds that are not supported by FW.  Enabling | ||||
| these protocols leads to link issues on system side. | ||||
| 
 | ||||
| Signed-off-by: Alex Marginean <alexandru.marginean@nxp.com> | ||||
| ---
 | ||||
|  drivers/net/phy/aquantia_main.c | 8 +++++++- | ||||
|  1 file changed, 7 insertions(+), 1 deletion(-) | ||||
| 
 | ||||
| --- a/drivers/net/phy/aquantia_main.c
 | ||||
| +++ b/drivers/net/phy/aquantia_main.c
 | ||||
| @@ -340,10 +340,16 @@ static int aqr_config_aneg_set_prot(stru
 | ||||
|  	phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE, | ||||
|  		      aquantia_syscfg[if_type].start_rate); | ||||
|   | ||||
| -	for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++)
 | ||||
| +	for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) {
 | ||||
| +		u16 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1,
 | ||||
| +				       AQUANTIA_VND1_GSYSCFG_BASE + i);
 | ||||
| +		if (!reg)
 | ||||
| +			continue;
 | ||||
| +
 | ||||
|  		phy_write_mmd(phydev, MDIO_MMD_VEND1, | ||||
|  			      AQUANTIA_VND1_GSYSCFG_BASE + i, | ||||
|  			      aquantia_syscfg[if_type].syscfg); | ||||
| +	}
 | ||||
|   | ||||
|  	/* wake PHY back up */ | ||||
|  	phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0); | ||||
|  | @ -1,43 +0,0 @@ | |||
| From 2e677e4ae8f8330f68013163b060d0fda3a43095 Mon Sep 17 00:00:00 2001 | ||||
| From: "Langer, Thomas" <tlanger@maxlinear.com> | ||||
| Date: Fri, 9 Jul 2021 17:36:46 +0200 | ||||
| Subject: [PATCH] PONRTSYS-8842: aquantia: Add AQR113 driver support | ||||
| 
 | ||||
| Add a new entry for AQR113 PHY_ID | ||||
| ---
 | ||||
|  drivers/net/phy/aquantia_main.c | 10 ++++++++++ | ||||
|  1 file changed, 10 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/net/phy/aquantia_main.c
 | ||||
| +++ b/drivers/net/phy/aquantia_main.c
 | ||||
| @@ -26,6 +26,7 @@
 | ||||
|  #define PHY_ID_AQR813	0x31c31cb2 | ||||
|  #define PHY_ID_AQR112	0x03a1b662 | ||||
|  #define PHY_ID_AQR412	0x03a1b712 | ||||
| +#define PHY_ID_AQR113	0x31c31c40
 | ||||
|   | ||||
|  #define MDIO_PHYXS_VEND_IF_STATUS		0xe812 | ||||
|  #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK	GENMASK(7, 3) | ||||
| @@ -981,6 +982,14 @@ static struct phy_driver aqr_driver[] =
 | ||||
|  	.get_strings	= aqr107_get_strings, | ||||
|  	.get_stats	= aqr107_get_stats, | ||||
|  }, | ||||
| +{
 | ||||
| +	PHY_ID_MATCH_MODEL(PHY_ID_AQR113),
 | ||||
| +	.name		= "Aquantia AQR113",
 | ||||
| +	.config_aneg	= aqr_config_aneg,
 | ||||
| +	.config_intr	= aqr_config_intr,
 | ||||
| +	.handle_interrupt = aqr_handle_interrupt,
 | ||||
| +	.read_status	= aqr107_read_status,
 | ||||
| +},
 | ||||
|  }; | ||||
|   | ||||
|  module_phy_driver(aqr_driver); | ||||
| @@ -997,6 +1006,7 @@ static struct mdio_device_id __maybe_unu
 | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR412) }, | ||||
| +	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
 | ||||
|  	{ } | ||||
|  }; | ||||
|   | ||||
|  | @ -1,63 +0,0 @@ | |||
| From 3b92ee7b7899b6beffb2b484c58326e36612a873 Mon Sep 17 00:00:00 2001 | ||||
| From: Daniel Golle <daniel@makrotopia.org> | ||||
| Date: Thu, 23 Dec 2021 14:52:56 +0000 | ||||
| Subject: [PATCH] net: phy: aquantia: add PHY_ID for AQR112R | ||||
| 
 | ||||
| As advised by Ian Chang this PHY is used in Puzzle devices. | ||||
| 
 | ||||
| Signed-off-by: Daniel Golle <daniel@makrotopia.org> | ||||
| ---
 | ||||
|  drivers/net/phy/aquantia_main.c | 10 ++++++++++ | ||||
|  1 file changed, 10 insertions(+) | ||||
| 
 | ||||
| --- a/drivers/net/phy/aquantia_main.c
 | ||||
| +++ b/drivers/net/phy/aquantia_main.c
 | ||||
| @@ -27,6 +27,8 @@
 | ||||
|  #define PHY_ID_AQR112	0x03a1b662 | ||||
|  #define PHY_ID_AQR412	0x03a1b712 | ||||
|  #define PHY_ID_AQR113	0x31c31c40 | ||||
| +#define PHY_ID_AQR112C	0x03a1b790
 | ||||
| +#define PHY_ID_AQR112R	0x31c31d12
 | ||||
|   | ||||
|  #define MDIO_PHYXS_VEND_IF_STATUS		0xe812 | ||||
|  #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK	GENMASK(7, 3) | ||||
| @@ -990,6 +992,30 @@ static struct phy_driver aqr_driver[] =
 | ||||
|  	.handle_interrupt = aqr_handle_interrupt, | ||||
|  	.read_status	= aqr107_read_status, | ||||
|  }, | ||||
| +{
 | ||||
| +	PHY_ID_MATCH_MODEL(PHY_ID_AQR112C),
 | ||||
| +	.name		= "Aquantia AQR112C",
 | ||||
| +	.probe		= aqr107_probe,
 | ||||
| +	.config_aneg	= aqr_config_aneg_set_prot,
 | ||||
| +	.config_intr	= aqr_config_intr,
 | ||||
| +	.handle_interrupt = aqr_handle_interrupt,
 | ||||
| +	.read_status	= aqr107_read_status,
 | ||||
| +	.get_sset_count	= aqr107_get_sset_count,
 | ||||
| +	.get_strings	= aqr107_get_strings,
 | ||||
| +	.get_stats	= aqr107_get_stats,
 | ||||
| +},
 | ||||
| +{
 | ||||
| +	PHY_ID_MATCH_MODEL(PHY_ID_AQR112R),
 | ||||
| +	.name		= "Aquantia AQR112R",
 | ||||
| +	.probe		= aqr107_probe,
 | ||||
| +	.config_aneg	= aqr_config_aneg_set_prot,
 | ||||
| +	.config_intr	= aqr_config_intr,
 | ||||
| +	.handle_interrupt = aqr_handle_interrupt,
 | ||||
| +	.read_status	= aqr107_read_status,
 | ||||
| +	.get_sset_count	= aqr107_get_sset_count,
 | ||||
| +	.get_strings	= aqr107_get_strings,
 | ||||
| +	.get_stats	= aqr107_get_stats,
 | ||||
| +},
 | ||||
|  }; | ||||
|   | ||||
|  module_phy_driver(aqr_driver); | ||||
| @@ -1007,6 +1033,8 @@ static struct mdio_device_id __maybe_unu
 | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR412) }, | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, | ||||
| +	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR112C) },
 | ||||
| +	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR112R) },
 | ||||
|  	{ } | ||||
|  }; | ||||
|   | ||||
|  | @ -1,110 +0,0 @@ | |||
| Author: Thomas Kupper <thomas.kupper@gmail.com> | ||||
| Date:   Wed May 24 21:14:17 2023 +0200 | ||||
| 
 | ||||
| kernel: phy: add Aquantia PHY AQR111 & AQR111B0 | ||||
| 
 | ||||
| Add the IDs for Aquantia PHY AQR111 and AQR111B0 as found in the GPL sources | ||||
| of the Netgear RAX120v2 firmware v1.2.8.40. | ||||
| 
 | ||||
| This is a 5GbE chip but it reports support for 10G. Implement config_init() | ||||
| to set max speed to 5G. | ||||
| 
 | ||||
| Signed-off-by: Thomas Kupper <thomas.kupper@gmail.com> | ||||
| --- a/drivers/net/phy/aquantia_main.c
 | ||||
| +++ b/drivers/net/phy/aquantia_main.c
 | ||||
| @@ -24,6 +24,8 @@
 | ||||
|  #define PHY_ID_AQR405	0x03a1b4b0 | ||||
|  #define PHY_ID_AQR113C	0x31c31c12 | ||||
|  #define PHY_ID_AQR813	0x31c31cb2 | ||||
| +#define PHY_ID_AQR111	0x03a1b610
 | ||||
| +#define PHY_ID_AQR111B0	0x03a1b612
 | ||||
|  #define PHY_ID_AQR112	0x03a1b662 | ||||
|  #define PHY_ID_AQR412	0x03a1b712 | ||||
|  #define PHY_ID_AQR113	0x31c31c40 | ||||
| @@ -729,6 +731,34 @@ static int aqcs109_config_init(struct ph
 | ||||
|  	return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); | ||||
|  } | ||||
|   | ||||
| +static int aqr111_config_init(struct phy_device *phydev)
 | ||||
| +{
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	/* Check that the PHY interface type is compatible */
 | ||||
| +	if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
 | ||||
| +	    phydev->interface != PHY_INTERFACE_MODE_1000BASEKX &&
 | ||||
| +	    phydev->interface != PHY_INTERFACE_MODE_2500BASEX &&
 | ||||
| +	    phydev->interface != PHY_INTERFACE_MODE_XGMII &&
 | ||||
| +	    phydev->interface != PHY_INTERFACE_MODE_USXGMII &&
 | ||||
| +	    phydev->interface != PHY_INTERFACE_MODE_10GKR &&
 | ||||
| +	    phydev->interface != PHY_INTERFACE_MODE_10GBASER &&
 | ||||
| +	    phydev->interface != PHY_INTERFACE_MODE_XAUI &&
 | ||||
| +	    phydev->interface != PHY_INTERFACE_MODE_RXAUI)
 | ||||
| +		return -ENODEV;
 | ||||
| +
 | ||||
| +	WARN(phydev->interface == PHY_INTERFACE_MODE_XGMII,
 | ||||
| +	     "Your devicetree is out of date, please update it. The AQR107 family doesn't support XGMII, maybe you mean USXGMII.\n");
 | ||||
| +
 | ||||
| +	ret = aqr107_wait_reset_complete(phydev);
 | ||||
| +	if (!ret)
 | ||||
| +		aqr107_chip_info(phydev);
 | ||||
| +
 | ||||
| +	/* AQR111 reports supporting speed up to 10G, however only speeds up to 5G are supported. */
 | ||||
| +	phy_set_max_speed(phydev, SPEED_5000);
 | ||||
| +
 | ||||
| +	return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
 | ||||
| +}
 | ||||
|  static void aqr107_link_change_notify(struct phy_device *phydev) | ||||
|  { | ||||
|  	u8 fw_major, fw_minor; | ||||
| @@ -961,6 +991,42 @@ static struct phy_driver aqr_driver[] =
 | ||||
|  	.link_change_notify = aqr107_link_change_notify, | ||||
|  }, | ||||
|  { | ||||
| +	PHY_ID_MATCH_MODEL(PHY_ID_AQR111),
 | ||||
| +	.name		= "Aquantia AQR111",
 | ||||
| +	.probe		= aqr107_probe,
 | ||||
| +	.config_init	= aqr111_config_init,
 | ||||
| +	.config_aneg    = aqr_config_aneg,
 | ||||
| +	.config_intr	= aqr_config_intr,
 | ||||
| +	.handle_interrupt = aqr_handle_interrupt,
 | ||||
| +	.read_status	= aqr107_read_status,
 | ||||
| +	.get_tunable    = aqr107_get_tunable,
 | ||||
| +	.set_tunable    = aqr107_set_tunable,
 | ||||
| +	.suspend	= aqr107_suspend,
 | ||||
| +	.resume		= aqr107_resume,
 | ||||
| +	.get_sset_count	= aqr107_get_sset_count,
 | ||||
| +	.get_strings	= aqr107_get_strings,
 | ||||
| +	.get_stats	= aqr107_get_stats,
 | ||||
| +	.link_change_notify = aqr107_link_change_notify,
 | ||||
| +},
 | ||||
| +{
 | ||||
| +	PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0),
 | ||||
| +	.name		= "Aquantia AQR111B0",
 | ||||
| +	.probe		= aqr107_probe,
 | ||||
| +	.config_init	= aqr111_config_init,
 | ||||
| +	.config_aneg    = aqr_config_aneg,
 | ||||
| +	.config_intr	= aqr_config_intr,
 | ||||
| +	.handle_interrupt = aqr_handle_interrupt,
 | ||||
| +	.read_status	= aqr107_read_status,
 | ||||
| +	.get_tunable    = aqr107_get_tunable,
 | ||||
| +	.set_tunable    = aqr107_set_tunable,
 | ||||
| +	.suspend	= aqr107_suspend,
 | ||||
| +	.resume		= aqr107_resume,
 | ||||
| +	.get_sset_count	= aqr107_get_sset_count,
 | ||||
| +	.get_strings	= aqr107_get_strings,
 | ||||
| +	.get_stats	= aqr107_get_stats,
 | ||||
| +	.link_change_notify = aqr107_link_change_notify,
 | ||||
| +},
 | ||||
| +{
 | ||||
|  	PHY_ID_MATCH_MODEL(PHY_ID_AQR112), | ||||
|  	.name		= "Aquantia AQR112", | ||||
|  	.probe		= aqr107_probe, | ||||
| @@ -1030,6 +1096,8 @@ static struct mdio_device_id __maybe_unu
 | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, | ||||
| +	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR111) },
 | ||||
| +	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) },
 | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR412) }, | ||||
|  	{ PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, | ||||
|  | @ -1,40 +0,0 @@ | |||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Thu, 13 Jul 2023 17:30:59 +0200 | ||||
| Subject: [PATCH] nvmem: core: fix support for fixed cells NVMEM layout | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| Returning -EPROBE_DEFER for "fixed-layout" makes nvmem_register() always | ||||
| fail (that layout is supported internally with no external module). That | ||||
| makes callers (e.g. mtd_nvmem_add()) fail as well and prevents booting | ||||
| on devices with "fixed-layout" in DT. | ||||
| 
 | ||||
| Add a quick workaround for it. | ||||
| 
 | ||||
| Fixes: 6468a6f45148 ("nvmem: core: handle the absence of expected layouts") | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/nvmem/core.c
 | ||||
| +++ b/drivers/nvmem/core.c
 | ||||
| @@ -798,6 +798,19 @@ static struct nvmem_layout *nvmem_layout
 | ||||
|  		return NULL; | ||||
|   | ||||
|  	/* | ||||
| +	 * We should return -EPROBE_DEFER only when layout driver is expected to
 | ||||
| +	 * become available later. Otherwise NVMEM will never probe successfully
 | ||||
| +	 * for unsupported layouts. There is no known solution for that right
 | ||||
| +	 * now.
 | ||||
| +	 *
 | ||||
| +	 * This problem also affects "fixed-layout". It's supported in NVMEM
 | ||||
| +	 * core code so there never will be layout for it. We shouldn't return
 | ||||
| +	 * -EPROBE_DEFER in such case. Add a quick workaround for that.
 | ||||
| +	 */
 | ||||
| +	if (of_device_is_compatible(layout_np, "fixed-layout"))
 | ||||
| +		return NULL;
 | ||||
| +
 | ||||
| +	/*
 | ||||
|  	 * In case the nvmem device was built-in while the layout was built as a | ||||
|  	 * module, we shall manually request the layout driver loading otherwise | ||||
|  	 * we'll never have any match. | ||||
|  | @ -1,93 +0,0 @@ | |||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Thu, 13 Jul 2023 18:29:19 +0200 | ||||
| Subject: [PATCH] nvmem: core: support "mac-base" fixed layout cells | ||||
| 
 | ||||
| Fixed layout binding allows specifying "mac-base" NVMEM cells. It's used | ||||
| for base MAC address (that can be used for calculating relative | ||||
| addresses). It can be stored in a raw binary format or as an ASCII | ||||
| string. | ||||
| ---
 | ||||
| 
 | ||||
| --- a/drivers/nvmem/Kconfig
 | ||||
| +++ b/drivers/nvmem/Kconfig
 | ||||
| @@ -1,6 +1,7 @@
 | ||||
|  # SPDX-License-Identifier: GPL-2.0-only | ||||
|  menuconfig NVMEM | ||||
|  	bool "NVMEM Support" | ||||
| +	select GENERIC_NET_UTILS
 | ||||
|  	help | ||||
|  	  Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES... | ||||
|   | ||||
| --- a/drivers/nvmem/core.c
 | ||||
| +++ b/drivers/nvmem/core.c
 | ||||
| @@ -7,9 +7,11 @@
 | ||||
|   */ | ||||
|   | ||||
|  #include <linux/device.h> | ||||
| +#include <linux/etherdevice.h>
 | ||||
|  #include <linux/export.h> | ||||
|  #include <linux/fs.h> | ||||
|  #include <linux/idr.h> | ||||
| +#include <linux/if_ether.h>
 | ||||
|  #include <linux/init.h> | ||||
|  #include <linux/kref.h> | ||||
|  #include <linux/module.h> | ||||
| @@ -696,6 +698,37 @@ static int nvmem_validate_keepouts(struc
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int nvmem_mac_base_raw_read(void *context, const char *id, int index, unsigned int offset,
 | ||||
| +				   void *buf, size_t bytes)
 | ||||
| +{
 | ||||
| +	if (WARN_ON(bytes != ETH_ALEN))
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	if (index)
 | ||||
| +		eth_addr_add(buf, index);
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +static int nvmem_mac_base_ascii_read(void *context, const char *id, int index, unsigned int offset,
 | ||||
| +				     void *buf, size_t bytes)
 | ||||
| +{
 | ||||
| +	u8 mac[ETH_ALEN];
 | ||||
| +
 | ||||
| +	if (WARN_ON(bytes != 3 * ETH_ALEN - 1))
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	if (!mac_pton(buf, mac))
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	if (index)
 | ||||
| +		eth_addr_add(mac, index);
 | ||||
| +
 | ||||
| +	ether_addr_copy(buf, mac);
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
|  static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np) | ||||
|  { | ||||
|  	struct nvmem_layout *layout = nvmem->layout; | ||||
| @@ -731,6 +764,20 @@ static int nvmem_add_cells_from_dt(struc
 | ||||
|  		if (layout && layout->fixup_cell_info) | ||||
|  			layout->fixup_cell_info(nvmem, layout, &info); | ||||
|   | ||||
| +		if (of_device_is_compatible(np, "fixed-layout")) {
 | ||||
| +			if (of_device_is_compatible(child, "mac-base")) {
 | ||||
| +				if (info.bytes == 6) {
 | ||||
| +					info.raw_len = info.bytes;
 | ||||
| +					info.bytes = ETH_ALEN;
 | ||||
| +					info.read_post_process = nvmem_mac_base_raw_read;
 | ||||
| +				} else if (info.bytes == 3 * ETH_ALEN - 1) {
 | ||||
| +					info.raw_len = info.bytes;
 | ||||
| +					info.bytes = ETH_ALEN;
 | ||||
| +					info.read_post_process = nvmem_mac_base_ascii_read;
 | ||||
| +				}
 | ||||
| +			}
 | ||||
| +		}
 | ||||
| +
 | ||||
|  		ret = nvmem_add_one_cell(nvmem, &info); | ||||
|  		kfree(info.name); | ||||
|  		if (ret) { | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue