lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Mon,  9 May 2022 00:48:48 +0200
From:   Hauke Mehrtens <hauke@...ke-m.de>
To:     davem@...emloft.net, kuba@...nel.org
Cc:     linus.walleij@...aro.org, alsi@...g-olufsen.dk, andrew@...n.ch,
        vivien.didelot@...il.com, f.fainelli@...il.com, olteanv@...il.com,
        netdev@...r.kernel.org, Hauke Mehrtens <hauke@...ke-m.de>
Subject: [PATCH 4/4] net: dsa: realtek: rtl8365mb: Add SGMII and HSGMII support

This adds support for SGMII and HSGMII on RTL8367S switches.
HSGMII is configured using the 2500BASEX mode.
This is baed on the rtl8367c driver found in OpenWrt.

For (H)SGMII mode we have to load a firmware into some memory which gets
executed on the integrated 8051. The firmware binary was added as a
array into the driver with a GPL license notice on top.

This was tested on RTL8367S (ver=0x00a0, opt=0x0001).

Signed-off-by: Hauke Mehrtens <hauke@...ke-m.de>
---
 drivers/net/dsa/realtek/rtl8365mb.c | 356 +++++++++++++++++++++++++++-
 1 file changed, 345 insertions(+), 11 deletions(-)

diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c
index f9b690251155..5504a34fffeb 100644
--- a/drivers/net/dsa/realtek/rtl8365mb.c
+++ b/drivers/net/dsa/realtek/rtl8365mb.c
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2021 Alvin Šipraga <alsi@...g-olufsen.dk>
  * Copyright (C) 2021 Michael Rasmussen <mir@...g-olufsen.dk>
+ * Copyright (C) 2022 Hauke Mehrtens <hauke@...ke-m.de>
  *
  * The RTL8365MB-VC is a 4+1 port 10/100/1000M switch controller. It includes 4
  * integrated PHYs for the user facing ports, and an extension interface which
@@ -98,6 +99,7 @@
 #include <linux/of_irq.h>
 #include <linux/regmap.h>
 #include <linux/if_bridge.h>
+#include <linux/firmware.h>
 
 #include "realtek.h"
 
@@ -135,6 +137,7 @@ static const int rtl8365mb_extint_port_map[]  = { -1, -1, -1, -1, -1, -1, 1, 2,
 
 /* Chip reset register */
 #define RTL8365MB_CHIP_RESET_REG	0x1322
+#define RTL8365MB_CHIP_RESET_DW8051	BIT(4)
 #define RTL8365MB_CHIP_RESET_SW_MASK	0x0002
 #define RTL8365MB_CHIP_RESET_HW_MASK	0x0001
 
@@ -278,6 +281,29 @@ static const int rtl8365mb_extint_port_map[]  = { -1, -1, -1, -1, -1, -1, 1, 2,
 #define   RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK		0x0004
 #define   RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK		0x0003
 
+#define RTL8365MB_SDS_MISC			0x1d11
+#define  RTL8365MB_CFG_SGMII_RXFC		0x4000
+#define  RTL8365MB_CFG_SGMII_TXFC		0x2000
+#define  RTL8365MB_INB_ARB			0x1000
+#define  RTL8365MB_CFG_MAC8_SEL_HSGMII		0x0800
+#define  RTL8365MB_CFG_SGMII_FDUP		0x0400
+#define  RTL8365MB_CFG_SGMII_LINK		0x0200
+#define  RTL8365MB_CFG_SGMII_SPD		0x0180
+#define  RTL8365MB_CFG_MAC8_SEL_SGMII		0x0040
+#define  RTL8365MB_CFG_INB_SEL			0x0038
+#define  RTL8365MB_CFG_SDS_MODE_18C		0x0007
+
+#define RTL8365MB_SDS_INDACS_CMD		0x6600
+#define RTL8365MB_SDS_INDACS_ADR		0x6601
+#define RTL8365MB_SDS_INDACS_DATA		0x6602
+
+#define RTL8365MB_MISC_CFG0			0x130c
+#define  RTL8365MB_MISC_CFG0_DW8051_EN		BIT(5)
+
+#define RTL8365MB_DW8051_RDY			0x1336
+#define  RTL8365MB_DW8051_RDY_IROM_MSB		BIT(2)
+#define  RTL8365MB_DW8051_RDY_ACS_IROM_EN	BIT(1)
+
 /* CPU port mask register - controls which ports are treated as CPU ports */
 #define RTL8365MB_CPU_PORT_MASK_REG	0x1219
 #define   RTL8365MB_CPU_PORT_MASK_MASK	0x07FF
@@ -296,6 +322,8 @@ static const int rtl8365mb_extint_port_map[]  = { -1, -1, -1, -1, -1, -1, 1, 2,
 #define RTL8365MB_CFG0_MAX_LEN_REG	0x088C
 #define   RTL8365MB_CFG0_MAX_LEN_MASK	0x3FFF
 
+#define RTL8365MB_BYPASS_LINE_RATE		0x03f7
+
 /* Port learning limit registers */
 #define RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE		0x0A20
 #define RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(_physport) \
@@ -493,6 +521,39 @@ static const struct rtl8365mb_jam_tbl_entry rtl8365mb_init_jam_common[] = {
 	{ 0x1D32, 0x0002 },
 };
 
+struct rtl8365mb_sds_init {
+	unsigned int data;
+	unsigned int addr;
+};
+
+static const struct rtl8365mb_sds_init redData[] = {
+	{0x04D7, 0x0480}, {0xF994, 0x0481}, {0x21A2, 0x0482}, {0x6960, 0x0483},
+	{0x9728, 0x0484}, {0x9D85, 0x0423}, {0xD810, 0x0424}, {0x83F2, 0x002E}
+};
+
+static const struct rtl8365mb_sds_init redDataSB[] = {
+	{0x04D7, 0x0480}, {0xF994, 0x0481}, {0x31A2, 0x0482}, {0x6960, 0x0483},
+	{0x9728, 0x0484}, {0x9D85, 0x0423}, {0xD810, 0x0424}, {0x83F2, 0x002E}
+};
+
+static const struct rtl8365mb_sds_init redData1_5_6[] = {
+	{0x82F1, 0x0500}, {0xF195, 0x0501}, {0x31A2, 0x0502}, {0x796C, 0x0503},
+	{0x9728, 0x0504}, {0x9D85, 0x0423}, {0xD810, 0x0424}, {0x0F80, 0x0001},
+	{0x83F2, 0x002E}
+};
+
+static const struct rtl8365mb_sds_init redData8_9[] = {
+	{0x82F1, 0x0500}, {0xF995, 0x0501}, {0x31A2, 0x0502}, {0x796C, 0x0503},
+	{0x9728, 0x0504}, {0x9D85, 0x0423}, {0xD810, 0x0424}, {0x0F80, 0x0001},
+	{0x83F2, 0x002E}
+};
+
+static const struct rtl8365mb_sds_init redDataHB[] = {
+	{0x82F0, 0x0500}, {0xF195, 0x0501}, {0x31A2, 0x0502}, {0x7960, 0x0503},
+	{0x9728, 0x0504}, {0x9D85, 0x0423}, {0xD810, 0x0424}, {0x0F80, 0x0001},
+	{0x83F2, 0x002E}
+};
+
 enum rtl8365mb_stp_state {
 	RTL8365MB_STP_STATE_DISABLED = 0,
 	RTL8365MB_STP_STATE_BLOCKING = 1,
@@ -801,6 +862,232 @@ rtl8365mb_get_tag_protocol(struct dsa_switch *ds, int port,
 	return DSA_TAG_PROTO_RTL8_4;
 }
 
+static int rtl8365mb_sds_indacs_write(struct realtek_priv *priv, unsigned int addr,
+				      unsigned int data)
+{
+	int ret;
+
+	ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_DATA, data);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_ADR, addr);
+	if (ret)
+		return ret;
+
+	return regmap_write(priv->map, RTL8365MB_SDS_INDACS_CMD, 0x00C0);
+}
+
+static int rtl8365mb_sds_indacs_read(struct realtek_priv *priv, unsigned int addr,
+				     unsigned int *data)
+{
+	int ret;
+
+	ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_ADR, addr);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_CMD, 0x00C0);
+	if (ret)
+		return ret;
+
+	return regmap_write(priv->map, RTL8365MB_SDS_INDACS_DATA, *data);
+}
+
+static int rtl8365mb_ext_init_sgmii_fw(struct realtek_priv *priv)
+{
+	struct device *dev = priv->dev;
+	const struct firmware *fw;
+	int ret;
+	int i;
+
+	ret = request_firmware(&fw, "rtl_switch/rtl8367s-sgmii.bin", dev);
+	if (ret) {
+		dev_err(dev, "failed to load firmware rtl_switch/rtl8367s-sgmii.bin, error: %i\n",
+			ret);
+		return ret;
+	}
+
+	ret = regmap_update_bits(priv->map, RTL8365MB_CHIP_RESET_REG,
+				 RTL8365MB_CHIP_RESET_DW8051,
+				 FIELD_PREP(RTL8365MB_CHIP_RESET_DW8051, 1));
+	if (ret)
+		goto release_fw;
+
+	ret = regmap_update_bits(priv->map, RTL8365MB_MISC_CFG0,
+				 RTL8365MB_MISC_CFG0_DW8051_EN,
+				 FIELD_PREP(RTL8365MB_MISC_CFG0_DW8051_EN, 1));
+	if (ret)
+		goto release_fw;
+
+	ret = regmap_update_bits(priv->map, RTL8365MB_DW8051_RDY,
+				 RTL8365MB_DW8051_RDY_ACS_IROM_EN,
+				 FIELD_PREP(RTL8365MB_DW8051_RDY_ACS_IROM_EN, 1));
+	if (ret)
+		goto release_fw;
+
+	ret = regmap_update_bits(priv->map, RTL8365MB_DW8051_RDY,
+				 RTL8365MB_DW8051_RDY_IROM_MSB,
+				 FIELD_PREP(RTL8365MB_DW8051_RDY_IROM_MSB, 0));
+	if (ret)
+		goto release_fw;
+
+	for (i = 0; i < fw->size; i++) {
+		ret = regmap_write(priv->map, 0xE000 + i, fw->data[i]);
+		if (ret)
+			goto release_fw;
+	}
+
+	ret = regmap_update_bits(priv->map, RTL8365MB_DW8051_RDY,
+				 RTL8365MB_DW8051_RDY_IROM_MSB,
+				 FIELD_PREP(RTL8365MB_DW8051_RDY_IROM_MSB, 0));
+	if (ret)
+		goto release_fw;
+
+	ret = regmap_update_bits(priv->map, RTL8365MB_DW8051_RDY,
+				 RTL8365MB_DW8051_RDY_ACS_IROM_EN,
+				 FIELD_PREP(RTL8365MB_DW8051_RDY_ACS_IROM_EN, 0));
+	if (ret)
+		goto release_fw;
+
+	ret = regmap_update_bits(priv->map, RTL8365MB_CHIP_RESET_REG,
+				 RTL8365MB_CHIP_RESET_DW8051,
+				 FIELD_PREP(RTL8365MB_CHIP_RESET_DW8051, 0));
+
+release_fw:
+	release_firmware(fw);
+	return ret;
+}
+
+static int rtl8365mb_ext_init_sgmii(struct realtek_priv *priv, int port, phy_interface_t interface)
+{
+	struct rtl8365mb *mb;
+	int interface_mode;
+	int sds_mode;
+	const struct rtl8365mb_sds_init *sds_init;
+	size_t sds_init_len;
+	int ext_int;
+	int ret;
+	int i;
+	int val;
+	int mask;
+
+	mb = priv->chip_data;
+
+	if (mb->chip_id != RTL8365MB_CHIP_ID_8365MB_VC)
+		return -EINVAL;
+
+	ext_int = rtl8365mb_extint_port_map[port];
+	if (ext_int != 1)
+		return -EINVAL;
+
+	if (interface == PHY_INTERFACE_MODE_SGMII) {
+		sds_mode = FIELD_PREP(RTL8365MB_CFG_MAC8_SEL_SGMII, 1);
+		interface_mode = RTL8365MB_EXT_PORT_MODE_SGMII;
+
+		if (mb->chip_option == 0) {
+			sds_init = redData;
+			sds_init_len = ARRAY_SIZE(redData);
+		} else {
+			sds_init = redDataSB;
+			sds_init_len = ARRAY_SIZE(redDataSB);
+		}
+	} else if (interface == PHY_INTERFACE_MODE_2500BASEX) {
+		sds_mode = FIELD_PREP(RTL8365MB_CFG_MAC8_SEL_HSGMII, 1);
+		interface_mode = RTL8365MB_EXT_PORT_MODE_HSGMII;
+
+		if (mb->chip_option == 0) {
+			switch (mb->chip_ver & 0x00F0) {
+			case 0x0010:
+			case 0x0050:
+			case 0x0060:
+				sds_init = redData1_5_6;
+				sds_init_len = ARRAY_SIZE(redData1_5_6);
+				break;
+			case 0x0080:
+			case 0x0090:
+				sds_init = redData8_9;
+				sds_init_len = ARRAY_SIZE(redData8_9);
+				break;
+			default:
+				return -EINVAL;
+			}
+		} else {
+			sds_init = redDataHB;
+			sds_init_len = ARRAY_SIZE(redDataHB);
+		}
+	} else {
+		return -EINVAL;
+	}
+
+	for (i = 0; i < sds_init_len; i++) {
+		ret = rtl8365mb_sds_indacs_write(priv, sds_init[i].addr, sds_init[i].data);
+		if (ret)
+			return ret;
+	}
+
+	mask = RTL8365MB_CFG_MAC8_SEL_SGMII | RTL8365MB_CFG_MAC8_SEL_HSGMII;
+	ret = regmap_update_bits(priv->map,
+				 RTL8365MB_SDS_MISC,
+				 mask,
+				 sds_mode);
+	if (ret)
+		return ret;
+
+	mask = RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(ext_int);
+	val = interface_mode << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(ext_int);
+	ret = regmap_update_bits(priv->map,
+				 RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(ext_int),
+				 mask,
+				 val);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->map, RTL8365MB_BYPASS_LINE_RATE, 0x0);
+	if (ret)
+		return ret;
+
+	/* Serdes not reset */
+	ret = rtl8365mb_sds_indacs_write(priv, 0x0003, 0x7106);
+	if (ret)
+		return ret;
+
+	return rtl8365mb_ext_init_sgmii_fw(priv);
+}
+
+static int rtl8365mb_ext_sgmii_nway(struct realtek_priv *priv, bool state)
+{
+	u32 running;
+	u32 regValue;
+	int ret;
+
+	ret = regmap_read(priv->map, RTL8365MB_MISC_CFG0, &running);
+	if (running & RTL8365MB_MISC_CFG0_DW8051_EN) {
+		ret = regmap_update_bits(priv->map, RTL8365MB_MISC_CFG0,
+					 RTL8365MB_MISC_CFG0_DW8051_EN,
+					 FIELD_PREP(RTL8365MB_MISC_CFG0_DW8051_EN, 0));
+		if (ret)
+			return ret;
+	}
+
+	ret = rtl8365mb_sds_indacs_read(priv, 0x0002, &regValue);
+	if (ret)
+		return ret;
+
+	if (state)
+		regValue |= 0x0200;
+	else
+		regValue &= ~0x0200;
+	regValue |= 0x0100;
+
+	ret = rtl8365mb_sds_indacs_write(priv, 0x0002, regValue);
+	if (ret)
+		return ret;
+	return regmap_update_bits(priv->map, RTL8365MB_MISC_CFG0,
+				  RTL8365MB_MISC_CFG0_DW8051_EN,
+				  FIELD_PREP(RTL8365MB_MISC_CFG0_DW8051_EN, 1));
+}
+
 static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port,
 				      phy_interface_t interface)
 {
@@ -886,6 +1173,7 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port,
 }
 
 static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,
+					  phy_interface_t interface,
 					  bool link, int speed, int duplex,
 					  bool tx_pause, bool rx_pause)
 {
@@ -911,7 +1199,7 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,
 		r_rx_pause = rx_pause ? 1 : 0;
 		r_tx_pause = tx_pause ? 1 : 0;
 
-		if (speed == SPEED_1000) {
+		if (speed == SPEED_1000 || speed == SPEED_2500) {
 			r_speed = RTL8365MB_PORT_SPEED_1000M;
 		} else if (speed == SPEED_100) {
 			r_speed = RTL8365MB_PORT_SPEED_100M;
@@ -941,6 +1229,25 @@ static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port,
 		r_duplex = 0;
 	}
 
+	if (interface == PHY_INTERFACE_MODE_SGMII ||
+	    interface == PHY_INTERFACE_MODE_2500BASEX) {
+		val = FIELD_PREP(RTL8365MB_CFG_SGMII_FDUP, r_duplex) |
+		      FIELD_PREP(RTL8365MB_CFG_SGMII_SPD, r_speed) |
+		      FIELD_PREP(RTL8365MB_CFG_SGMII_LINK, r_link) |
+		      FIELD_PREP(RTL8365MB_CFG_SGMII_TXFC, r_tx_pause) |
+		      FIELD_PREP(RTL8365MB_CFG_SGMII_RXFC, r_rx_pause);
+		ret = regmap_update_bits(priv->map,
+					 RTL8365MB_SDS_MISC,
+					 RTL8365MB_CFG_SGMII_FDUP |
+					 RTL8365MB_CFG_SGMII_SPD |
+					 RTL8365MB_CFG_SGMII_LINK |
+					 RTL8365MB_CFG_SGMII_TXFC |
+					 RTL8365MB_CFG_SGMII_RXFC,
+					 val);
+		if (ret)
+			return ret;
+	}
+
 	val = FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK, 1) |
 	      FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK,
 			 r_tx_pause) |
@@ -972,10 +1279,15 @@ static bool rtl8365mb_phy_mode_supported(struct dsa_switch *ds, int port,
 	     interface == PHY_INTERFACE_MODE_GMII))
 		/* Internal PHY */
 		return true;
-	else if ((ext_int >= 1) &&
-		 phy_interface_mode_is_rgmii(interface))
+	else if ((ext_int == 1) &&
+		 (phy_interface_mode_is_rgmii(interface) ||
+		  interface == PHY_INTERFACE_MODE_SGMII ||
+		  interface == PHY_INTERFACE_MODE_2500BASEX))
 		/* Extension MAC */
 		return true;
+	else if ((ext_int >= 2) &&
+		 phy_interface_mode_is_rgmii(interface))
+		return true;
 
 	return false;
 }
@@ -983,14 +1295,25 @@ static bool rtl8365mb_phy_mode_supported(struct dsa_switch *ds, int port,
 static void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port,
 				       struct phylink_config *config)
 {
-	if (dsa_is_user_port(ds, port))
+	int ext_int = rtl8365mb_extint_port_map[port];
+
+	config->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
+				   MAC_10 | MAC_100 | MAC_1000FD;
+
+	if (dsa_is_user_port(ds, port)) {
 		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
 			  config->supported_interfaces);
-	else if (dsa_is_cpu_port(ds, port))
+	} else if (dsa_is_cpu_port(ds, port)) {
+		if (ext_int == 1) {
+			__set_bit(PHY_INTERFACE_MODE_SGMII,
+				  config->supported_interfaces);
+			__set_bit(PHY_INTERFACE_MODE_2500BASEX,
+				  config->supported_interfaces);
+			config->mac_capabilities |= MAC_2500FD;
+		}
 		phy_interface_set_rgmii(config->supported_interfaces);
+	}
 
-	config->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
-				   MAC_10 | MAC_100 | MAC_1000FD;
 }
 
 static void rtl8365mb_phylink_mac_config(struct dsa_switch *ds, int port,
@@ -1020,6 +1343,10 @@ static void rtl8365mb_phylink_mac_config(struct dsa_switch *ds, int port,
 				"failed to configure RGMII mode on port %d: %d\n",
 				port, ret);
 		return;
+	} else if (state->interface == PHY_INTERFACE_MODE_SGMII ||
+		   state->interface == PHY_INTERFACE_MODE_2500BASEX) {
+		rtl8365mb_ext_init_sgmii(priv, port, state->interface);
+		rtl8365mb_ext_sgmii_nway(priv, false);
 	}
 
 	/* TODO: Implement MII and RMII modes, which the RTL8365MB-VC also
@@ -1040,8 +1367,11 @@ static void rtl8365mb_phylink_mac_link_down(struct dsa_switch *ds, int port,
 	p = &mb->ports[port];
 	cancel_delayed_work_sync(&p->mib_work);
 
-	if (phy_interface_mode_is_rgmii(interface)) {
-		ret = rtl8365mb_ext_config_forcemode(priv, port, false, 0, 0,
+	if (phy_interface_mode_is_rgmii(interface) ||
+	    interface == PHY_INTERFACE_MODE_SGMII ||
+	    interface == PHY_INTERFACE_MODE_2500BASEX) {
+		ret = rtl8365mb_ext_config_forcemode(priv, port, interface,
+						     false, 0, 0,
 						     false, false);
 		if (ret)
 			dev_err(priv->dev,
@@ -1068,8 +1398,11 @@ static void rtl8365mb_phylink_mac_link_up(struct dsa_switch *ds, int port,
 	p = &mb->ports[port];
 	schedule_delayed_work(&p->mib_work, 0);
 
-	if (phy_interface_mode_is_rgmii(interface)) {
-		ret = rtl8365mb_ext_config_forcemode(priv, port, true, speed,
+	if (phy_interface_mode_is_rgmii(interface) ||
+	    interface == PHY_INTERFACE_MODE_SGMII ||
+	    interface == PHY_INTERFACE_MODE_2500BASEX) {
+		ret = rtl8365mb_ext_config_forcemode(priv, port, interface,
+						     true, speed,
 						     duplex, tx_pause,
 						     rx_pause);
 		if (ret)
@@ -2156,6 +2489,7 @@ const struct realtek_variant rtl8365mb_variant = {
 };
 EXPORT_SYMBOL_GPL(rtl8365mb_variant);
 
+MODULE_FIRMWARE("rtl_switch/rtl8367s-sgmii.bin");
 MODULE_AUTHOR("Alvin Šipraga <alsi@...g-olufsen.dk>");
 MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch");
 MODULE_LICENSE("GPL");
-- 
2.30.2

Powered by blists - more mailing lists