All Projects

IDCategoryTask TypePrioritySeveritySummaryReported InStatus
2685Base systemBug ReportVery LowLowColdplug too soon; hotplug.d scripts not run for some e...openwrt-19.07Unconfirmed Task Description

OpenWrt 19.07.0-rc2, latest openwrt-packages.

Occurs on at least ath79 but looking at the code this is not device-specific.

1. Have USB devices for which you have hotplug scripts (e.g. from p910d and sane-backends)
2. Boot (or reboot) with usb devices already plugged in.
3. Observe that the actions in the hotplug script (e.g. setting permissions / group ownership that don’t belong in the base system (i.e. hotplug.json)) as it’d be bloat to detect product:vendor ip pairs and take appropriate action for all the various packages and devices hotplug handles – if hotplug.json could have hotplug.json.d or somesuch it would alleviate most of this pain.

Looking at the procd code coldplug (udevtrigger) is called during ‘early’ init, which means that hotplug.d scripts are not yet present. Because /etc/init.d/boot (or any other initscript) no longer does a later call to udevtrigger, the events get missed).

If udevtrigger were called during regular init this wouldn’t be an issue (but probably the coldplug is needed earlier *as well* for devices needed to boot the system.

See also #1903 and #996 (they are likely the same root cause).

2495Base systemBug ReportVery LowLowucert rebuild within SDK segfaults on Debian 10 and Ubu...TrunkNew Task Description

Using the 19.07-SNAPSHOT SDK build Sept 12 (last successful build by the buildbots it looks like), and current openwrt-19.07 branch on the feeds, if doing signed build, the build fails due to ucert segfaulting (this is on Debian 10).

ldd staging_dir/host/bin/ucert shows:

      linux-vdso.so.1 (0x00007ffd03d63000)
      libubox.so => /home/daniel/Build/sdk-19.07-ath79/staging_dir/host/lib/libubox.so (0x00007ff0db3b0000)
      libblobmsg_json.so => /home/daniel/Build/sdk-19.07-ath79/staging_dir/host/lib/libblobmsg_json.so (0x00007ff0db3a8000)
      libjson-c.so.2 => /home/daniel/Build/sdk-19.07-ath79/staging_dir/host/lib/libjson-c.so.2 (0x00007ff0db398000)
      libc.so.6 => /home/daniel/Build/sdk-19.07-ath79/staging_dir/host/lib/libc.so.6 (0x00007ff0daff8000)
      /lib64/ld-linux-x86-64.so.2 (0x00007ff0db3c8000)

I’m wondering if the problem is due to using host instead SDK libraries.


 2485 Base systemBug ReportVery LowLow [19.07-SNAPSHOT SDK] libustream-openssl links against m ...TrunkClosed Task Description
 
Using the Ath79 19.07-SNAPSHOT SDK built Sep 6, I get the following with a clean build:

Package libustream-openssl is missing dependencies for the following libraries:
libmbedcrypto.so.3
libmbedtls.so.12
libmbedx509.so.0
make[3]: *** [Makefile:79: /home/daniel/Build/openwrt-sdk-19.07-SNAPSHOT-ath79-generic_gcc-7.4.0_musl.Linux-x86_64/bin/packages/mips_24kc/base/libustream-openssl20150806_2019-08-17-e8f9c22d-1_mips_24kc.ipk] Error 1
time: package/feeds/base/ustream-ssl/openssl/compile#0.52#0.19#1.28

I have all libustream packages enabled for build as I sometimes use mbedtls and sometimes openssl.

2421Base systemBug ReportVery LowLowspidev_test fails to build under SDKTrunkUnconfirmed Task Description

Using July 31, 2019 snapshot SDK (and quite few before that, just got around to reporting) spidev_test fails to build under the ath79 SDK (which causes the entire build to fail). Log message is:

cp: cannot stat ‘/home/daniel/Build/openwrt-sdk-ath79-generic_gcc-7.4.0_musl.Linux-x86_64/build_dir/target-mips_24kc_musl/linux-ath79_generic/linux-4.19.62/tools/spi/*’: No such file or directory
make[3]: *** [Makefile:64: /home/daniel/Build/openwrt-sdk-ath79-generic_gcc-7.4.0_musl.Linux-x86_64/build_dir/target-mips_24kc_musl/linux-ath79_generic/linux-4.19.62/tools/spi-target-mips_24kc_musl/.prepared_de019ecc69c7e706dce2af75912fda36_6664517399ebbbc92a37c5bb081b5c53] Error 1
time: package/feeds/base/spidev_test/compile#0.20#0.05#0.50

base feed is standard, openwrt-packages has modes to p910nd and saned-backends, otherwise current master, all other feeds standard.

 


 2294 Base systemBug ReportVery LowLow ath79: dts firmware partition size also used as paritio ...TrunkClosed Task Description

PCS CAP324 (ar9344, ath79)

NAME="OpenWrt"
VERSION="SNAPSHOT"
PRETTY_NAME="OpenWrt SNAPSHOT"
VERSION_ID="snapshot"
BUILD_ID="r10075-ace2410"
LEDE_BOARD="ath79/generic"
LEDE_ARCH="mips_24kc"
LEDE_RELEASE="OpenWrt SNAPSHOT r10075-ace2410"

partitions ending at 0xfa0000 *should* end at 0xfe0000.
It looks suspiciously like the *size* of the firmware partition is being used as the *end* of the rootfs partition.

[    0.395749] m25p80 spi0.0: mx25l12805d (16384 Kbytes)
[    0.401067] 4 fixed-partitions partitions found on MTD device spi0.0
[    0.407631] Creating 4 MTD partitions on "spi0.0":
[    0.412604] 0x000000000000-0x000000040000 : "u-boot"
[    0.418554] 0x000000040000-0x000000050000 : "u-boot-env"
[    0.424883] 0x000000050000-0x000000ff0000 : "firmware"
[    0.433582] 2 uimage-fw partitions found on MTD device firmware
[    0.439700] Creating 2 MTD partitions on "firmware":
[    0.444877] 0x000000000000-0x0000001a0000 : "kernel"
[    0.450862] 0x0000001a0000-0x000000fa0000 : "rootfs"
[    0.456702] mtd: device 4 (rootfs) set to be root filesystem
[    0.462629] 1 squashfs-split partitions found on MTD device rootfs
[    0.469018] 0x0000004d0000-0x000000fa0000 : "rootfs_data"
[    0.475393] 0x000000ff0000-0x000001000000 : "art"

https://github.com/openwrt/openwrt/blob/master/target/linux/ath79/dts/ar9344_pcs_cap324.dts

flash@0 {
		compatible = "jedec,spi-nor";
		reg = <0>;
		spi-max-frequency = <25000000>;

		partitions {
			compatible = "fixed-partitions";
			#address-cells = <1>;
			#size-cells = <1>;

			uboot: partition@0 {
				label = "u-boot";
				reg = <0x000000 0x040000>;
				read-only;
			};

			partition@40000 {
				label = "u-boot-env";
				reg = <0x040000 0x010000>;
				read-only;
			};

			partition@50000 {
				compatible = "denx,uimage";
				label = "firmware";
				reg = <0x050000 0x0fa0000>;
			};

			art: partition@7f0000 {
				label = "art";
				reg = <0xff0000 0x010000>;
				read-only;
			};
		};
};

Also occurs similarly with CR5000 – rootfs ends at 0x7a0000 instead of 0x7e0000

https://github.com/openwrt/openwrt/blob/master/target/linux/ath79/dts/ar9344_pcs_cr5000.dts

	flash@0 {
		compatible = "jedec,spi-nor";
		reg = <0>;
		spi-max-frequency = <25000000>;

		partitions {
			compatible = "fixed-partitions";
			#address-cells = <1>;
			#size-cells = <1>;

			uboot: partition@0 {
				label = "u-boot";
				reg = <0x000000 0x040000>;
				read-only;
			};

			partition@40000 {
				label = "u-boot-env";
				reg = <0x040000 0x010000>;
				read-only;
			};

			partition@50000 {
				compatible = "denx,uimage";
				label = "firmware";
				reg = <0x050000 0x07a0000>;
			};

			art: partition@7f0000 {
				label = "art";
				reg = <0x7f0000 0x010000>;
				read-only;
			};
};

/proc/cmdline is

console=ttyS0,115200 rootfstype=squashfs,jffs2

so it’s not reading from e.g. mtdparts

 


 2058 Base systemBug ReportVery LowLow ath79: valgrind fails to build for mip32 TrunkClosed Task Description

Building the core system (current master)

tip commit:

commit 0e8d5ff0fc1cd8eb5236e6e497c340ceb21340fe
Author: Felix Fietkau <nbd@nbd.name>
Date:   Fri Jan 11 17:23:29 2019 +0100

    mt76: fix typo in version number

    Signed-off-by: Felix Fietkau <nbd@nbd.name>

( only for mip32 (specifically ath79 for a variety of boards) with CONFIG_ALL=y fails on building valgrind with:

{standard input}:91723: Error: opcode not supported on this processor: mips32 (mips32) `floor.l.s $f24,$f24'                    
{standard input}:91740: Error: `fp=64' used with a 32-bit fpu
{standard input}:91744: Error: opcode not supported on this processor: mips32 (mips32) `floor.l.d $f24,$f24'                    
{standard input}:91761: Error: `fp=64' used with a 32-bit fpu
{standard input}:91765: Error: opcode not supported on this processor: mips32 (mips32) `round.l.s $f24,$f24'                    
{standard input}:91782: Error: `fp=64' used with a 32-bit fpu
{standard input}:91786: Error: opcode not supported on this processor: mips32 (mips32) `round.l.d $f24,$f24'                    
{standard input}:91803: Error: `fp=64' used with a 32-bit fpu
{standard input}:91807: Error: opcode not supported on this processor: mips32 (mips32) `trunc.l.s $f24,$f24'                    
{standard input}:91824: Error: `fp=64' used with a 32-bit fpu
{standard input}:91828: Error: opcode not supported on this processor: mips32 (mips32) `trunc.l.d $f24,$f24'                    
lto-wrapper: fatal error: mips-openwrt-linux-musl-gcc returned 1 exit status                                                    
compilation terminated.
/home/daniel/Build/openwrt-ath79/staging_dir/toolchain-mips_24kc_gcc-7.4.0_musl/lib/gcc/mips-openwrt-linux-musl/7.4.0/../../../../mips-openwrt-linux-musl/bin/ld: error: lto-wrapper failed
collect2: error: ld returned 1 exit status
 1990 Base systemBug ReportVery LowLow iptables fails to build for ath79 TrunkClosed Task Description

Even after make distclean, since commit 1286c55302696381e67cc25043710ced7e8682e2 iptables: bump to 1.8.2

Compile fails for ath79 (even after make distclean):

  CC       libebt_vlan.oo
In file included from /home/daniel/Build/openwrt-ath79/staging_dir/toolchain-mips_24kc_gcc-7.3.0_musl/include/net/ethernet.h:10:0,
                 from ../iptables/nft-bridge.h:8,
                 from libebt_vlan.c:18:
/home/daniel/Build/openwrt-ath79/staging_dir/toolchain-mips_24kc_gcc-7.3.0_musl/include/netinet/if_ether.h:111:8: error: redefinition of 'struct ethhdr'
 struct ethhdr {
        ^~~~~~
In file included from libebt_vlan.c:16:0:
/home/daniel/Build/openwrt-ath79/build_dir/target-mips_24kc_musl/linux-ath79_generic/linux-4.14.82/user_headers/include/linux/if_ether.h:155:8: note: originally defined here
 struct ethhdr {
        ^~~~~~
make[6]: *** [GNUmakefile:127: libebt_vlan.oo] Error 1
make[6]: Leaving directory '/home/daniel/Build/openwrt-ath79/build_dir/target-mips_24kc_musl/linux-ath79_generic/iptables-1.8.2/extensions'
make[5]: *** [Makefile:506: all-recursive] Error 1
 1704 Base systemBug ReportVery LowLow ath79: ag71xx (ar7240/ar934x builtin) switch does not d ...TrunkClosed Task Description

Ok, I’m tired of typing the description because FS doesn’t like tags (results in DB error; column too long in ‘tag_name’);

Adding support for some devices in ath79 arch.
At least on AR9341 with phy4-mii-enable (fifth port acting as separate ethX device, not ‘just’ a a VLAN from the user perspective) the ‘normal’ switch ethY device only detects plug/unplug on one port and ignores the rest.

This hack gets that working (but needs to be turned into a an actual ag91xx PHY driver, which ends up meaning splitting up ag91xx driver up to be done properly, hence not submitting to list yet; also needs more testing).

Will update as a I test more devices/scenarios.

.../net/ethernet/atheros/ag71xx/ag71xx.h | 5 +
.../ethernet/atheros/ag71xx/ag71xx_ar7240.c | 180 ++++++++++++++++++
.../net/ethernet/atheros/ag71xx/ag71xx_main.c | 16 ++
3 files changed, 201 insertions(+)

diff –git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h
index 22b22522a3..2bebec4ac5 100644
— a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h
@@ -179,6 +179,7 @@ struct ag71xx {
struct phy_device *phy_dev;
void *phy_priv;
int phy_if_mode;
+ bool is_switch;

unsigned int link;
unsigned int speed;
@@ -450,4 +451,8 @@ int ag71xx_mdio_mii_write(struct mii_bus *bus, int addr, int reg, u16 val);
int ar7240sw_phy_read(struct mii_bus *mii, int addr, int reg);
int ar7240sw_phy_write(struct mii_bus *mii, int addr, int reg, u16 val);

+int ar7240sw_update_link(struct phy_device *phydev);
+int ar7240sw_read_status(struct phy_device *phydev);
+int ar7240sw_aneg_done(struct phy_device *phydev);
+
#endif /* _AG71XX_H */
diff –git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
index 52c0f964cf..12c62660b4 100644
— a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
@@ -194,6 +194,7 @@
#define AR7240_PORT_CPU 0
#define AR7240_NUM_PORTS 6
#define AR7240_NUM_PHYS 5
+#define AR7240_PHY_POLL_MASK 0x3e

#define AR7240_PHY_ID1 0x004d
#define AR7240_PHY_ID2 0xd041
@@ -294,6 +295,7 @@ struct ar7240sw {
struct ag71xx_switch_platform_data *swdata;
struct switch_dev swdev;
int num_ports;
+ u32 phy_poll_mask;
u8 ver;
bool vlan;
u16 vlan_id[AR7240_MAX_VLANS];
@@ -1245,10 +1247,20 @@ ar7240_probe(struct ag71xx *ag, struct device_node *np)
as→ver = (ctrl » AR7240_MASK_CTRL_VERSION_S) &
AR7240_MASK_CTRL_VERSION_M;

+ if (of_property_read_u32(np, “phy-poll-mask”, &as→phy_poll_mask))
+ as→phy_poll_mask = AR7240_PHY_POLL_MASK;
+ else
+ as→phy_poll_mask &= AR7240_PHY_POLL_MASK;
+
+ pr_info(”phy-poll-mask %02x”, as→phy_poll_mask);
+
+ ag→is_switch = false;
if (sw_is_ar7240(as)) {
swdev→name = “AR7240/AR9330 built-in switch”;
swdev→ports = AR7240_NUM_PORTS - 1;
+ ag→is_switch = true;
} else if (sw_is_ar934x(as)) {
+ ag→is_switch = true;
swdev→name = “AR934X built-in switch”;

if (ag→phy_if_mode == PHY_INTERFACE_MODE_GMII) {
@@ -1267,6 +1279,8 @@ ar7240_probe(struct ag71xx *ag, struct device_node *np)
ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE1,
AR934X_REG_OPER_MODE1_PHY4_MII_EN);
swdev→ports = AR7240_NUM_PORTS - 1;
+ /* Don’t include port 5 as it’s now a separate PHY */
+ as→phy_poll_mask &= 0x1f;
} else {
swdev→ports = AR7240_NUM_PORTS;
}
@@ -1305,6 +1319,9 @@ void ag71xx_ar7240_start(struct ag71xx *ag)
if (!as)
return;

+ if (ag→phy_dev)
+ ag→phy_dev→priv = ag→phy_priv;
+
ar7240sw_reset(as);

ar7240_set_addr(as, ag→dev→dev_addr);
@@ -1322,6 +1339,9 @@ int ag71xx_ar7240_init(struct ag71xx *ag, struct device_node *np)
ag→phy_priv = as;
ar7240sw_reset(as);

+ if (ag→phy_dev)
+ ag→phy_dev→priv = ag→phy_priv;
+
rwlock_init(&as→stats_lock);

return 0;
@@ -1330,6 +1350,7 @@ int ag71xx_ar7240_init(struct ag71xx *ag, struct device_node *np)
void ag71xx_ar7240_cleanup(struct ag71xx *ag)
{
struct ar7240sw *as = ag→phy_priv;
+ ag→phy_dev→priv = NULL;

if (!as)
return;
@@ -1338,3 +1359,162 @@ void ag71xx_ar7240_cleanup(struct ag71xx *ag)
kfree(as);
ag→phy_priv = NULL;
}
+
+/* Copy of genphy_update_link *without* the call to driver hook for update_link
+ * else we get an infinite loop; from kernel drivers/net/phy/phy_device.c
+ */
+int ar7240sw_copy_genphy_update_link(struct phy_device *phydev) {
+ int status;
+
+ /* Do a fake read */
+ status = phy_read(phydev, MII_BMSR);
+ if (status < 0)
+ return status;
+
+ /* Read link and autonegotiation status */
+ status = phy_read(phydev, MII_BMSR);
+ if (status < 0)
+ return status;
+
+ if 1)) {
+ continue;
+ }
+
+ status = ar7240_get_port_link(swdev, i, &link);
+ if (status < 0) {
+ return status;
+ }
+
+ /* If any non-masked port is active, consider
+ * device to be ‘up’ + */
+ if (link.link) {
+ phydev→link = 1;
+ return 0;
+ }
+ }
+ /* otherwise, device is ‘down’ */
+ phydev→link = 0;
+ return 0;
+}
+
+int ar7240sw_read_status(struct phy_device *phydev) {
+ struct ar7240sw *as = NULL;
+ struct switch_dev *swdev = NULL;
+ int status;
+ int i;
+ struct switch_port_link link;
+
+ if (!phydev→priv)
+ return genphy_read_status(phydev);
+
+ status = ar7240sw_update_link(phydev);
+ if (status)
+ return status;
+
+ as = phydev→priv;
+ swdev = &(as→swdev);
+
+ if (!swdev)
+ return genphy_read_status(phydev);
+
+ /* Set link status for base device */
+ if (phydev→link) {
+ /* While down set link speed duplex lower, but valid */
+ phydev→speed = SPEED_10;
+ phydev→duplex = DUPLEX_HALF;
+
+ for (i = 0; i < swdev→ports; i++) {
+ /* We report the speed and duplex as the
+ * highest any non-masked port has
+ * negotiated.
+ */
+ if (i == swdev→cpu_port)
+ continue;
+
+ if (!(as→phy_poll_mask & BIT(i))) {
+ continue;
+ }
+
+ status = ar7240_get_port_link(swdev, i, &link);
+ if (status < 0) {
+ return status;
+ }
+
+ switch(link.speed) {
+ case SPEED_100:
+ if (phydev→speed != SPEED_1000)
+ phydev→speed = SPEED_100;
+ break;
+ case SPEED_1000:
+ phydev→speed = SPEED_1000;
+ break;
+ default:
+ /* We never reduce detected speed */
+ break;
+ }
+ switch(link.duplex) {
+ case DUPLEX_FULL:
+ phydev→duplex = DUPLEX_FULL;
+ break;
+ default:
+ /* We never reduce detected duplex */
+ break;
+ }
+ }
+
+ phydev→pause = 0;
+ phydev→asym_pause = 0;
+ }
+ return 0;
+}
+
+int ar7240sw_aneg_done (struct phy_device *phydev) {
+ int status;
+
+ if (!phydev→priv) {
+ return genphy_aneg_done(phydev);
+ }
+
+ status = ar7240sw_read_status(phydev);
+ if (status)
+ return status;
+
+ /* If we were able to get a status we’ve
+ * autonegotiated on the port(s) already.
+ */
+ return 1;
+}
diff –git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
index 635c3b9127..f33e148e2e 100644
— a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
@@ -1509,6 +1509,22 @@ static int ag71xx_probe(struct platform_device *pdev)

platform_set_drvdata(pdev, dev);

+ ag→phy_dev→priv = NULL;
+
+ if (ag→phy_priv)
+ if (ag→is_switch)
+ ag→phy_dev→priv = ag→phy_priv;
+
+ /* NB: This replaces update_link for *all* instances of the
+ * the driver (so all ‘Generic PHY’ instances are affected)
+ */
+
+ if (ag→phy_dev→drv) {
+ ag→phy_dev→drv→update_link = ar7240sw_update_link;
+ ag→phy_dev→drv→read_status = ar7240sw_read_status;
+ ag→phy_dev→drv→aneg_done = ar7240sw_aneg_done;
+ }
+
err = register_netdev(dev);
if (err) {
dev_err(&pdev→dev, “unable to register net device\n”);

 


1) status & BMSR_LSTATUS) == 0)
+ phydev→link = 0;
+ else
+ phydev→link = 1;
+
+ return 0;
+}
+
+
+int ar7240sw_update_link (struct phy_device *phydev) {
+ struct ar7240sw *as = NULL;
+ struct switch_dev *swdev = NULL;
+ int i;
+ int status = 0;
+ struct switch_port_link link;
+
+ if (!phydev→priv) {
+ return ar7240sw_copy_genphy_update_link(phydev);
+ }
+
+ as = phydev→priv;
+ swdev = &(as→swdev);
+
+ if (!swdev) {
+ return ar7240sw_copy_genphy_update_link(phydev);
+ }
+
+ /* Don’t touch link state until we actully know the state */
+ for (i = 0; i < swdev→ports; i++) {
+
+ if (i == swdev→cpu_port)
+ continue;
+
+ if (!(as→phy_poll_mask & BIT(i
 1703 Base systemBug ReportVery LowLow ath79: ag71xx (ar7240/ar934x builtin) switch does not d ...TrunkClosed Task Description

I’m adding support for some device in ath79 arch and found the following issue:

At least for AR9341 with phy4-mii-enable (fifth port configured as a separate ethX device rather than ‘just’ as a VLAN, using SoC capabilities), the ‘normal’ switch (i.e. not the fifth port) only detects link status on a single port of the switch.

I have a hack (hence not on list yet; real solution involves ag71xx-phy driver which ends up meaning also ag71xx-mdio etc) below which gets this working for me.

I will update whether this is more than just AR9341 as I test more devices without and with this patch.

.../net/ethernet/atheros/ag71xx/ag71xx.h | 5 +
.../ethernet/atheros/ag71xx/ag71xx_ar7240.c | 180 ++++++++++++++++++
.../net/ethernet/atheros/ag71xx/ag71xx_main.c | 16 ++
3 files changed, 201 insertions(+)

diff –git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h
index 22b22522a3..2bebec4ac5 100644
— a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h
@@ -179,6 +179,7 @@ struct ag71xx {
struct phy_device *phy_dev;
void *phy_priv;
int phy_if_mode;
+ bool is_switch;

unsigned int link;
unsigned int speed;
@@ -450,4 +451,8 @@ int ag71xx_mdio_mii_write(struct mii_bus *bus, int addr, int reg, u16 val);
int ar7240sw_phy_read(struct mii_bus *mii, int addr, int reg);
int ar7240sw_phy_write(struct mii_bus *mii, int addr, int reg, u16 val);

+int ar7240sw_update_link(struct phy_device *phydev);
+int ar7240sw_read_status(struct phy_device *phydev);
+int ar7240sw_aneg_done(struct phy_device *phydev);
+
#endif /* _AG71XX_H */
diff –git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
index 52c0f964cf..12c62660b4 100644
— a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
@@ -194,6 +194,7 @@
#define AR7240_PORT_CPU 0
#define AR7240_NUM_PORTS 6
#define AR7240_NUM_PHYS 5
+#define AR7240_PHY_POLL_MASK 0x3e

#define AR7240_PHY_ID1 0x004d
#define AR7240_PHY_ID2 0xd041
@@ -294,6 +295,7 @@ struct ar7240sw {
struct ag71xx_switch_platform_data *swdata;
struct switch_dev swdev;
int num_ports;
+ u32 phy_poll_mask;
u8 ver;
bool vlan;
u16 vlan_id[AR7240_MAX_VLANS];
@@ -1245,10 +1247,20 @@ ar7240_probe(struct ag71xx *ag, struct device_node *np)
as→ver = (ctrl » AR7240_MASK_CTRL_VERSION_S) &
AR7240_MASK_CTRL_VERSION_M;

+ if (of_property_read_u32(np, “phy-poll-mask”, &as→phy_poll_mask))
+ as→phy_poll_mask = AR7240_PHY_POLL_MASK;
+ else
+ as→phy_poll_mask &= AR7240_PHY_POLL_MASK;
+
+ pr_info(”phy-poll-mask %02x”, as→phy_poll_mask);
+
+ ag→is_switch = false;
if (sw_is_ar7240(as)) {
swdev→name = “AR7240/AR9330 built-in switch”;
swdev→ports = AR7240_NUM_PORTS - 1;
+ ag→is_switch = true;
} else if (sw_is_ar934x(as)) {
+ ag→is_switch = true;
swdev→name = “AR934X built-in switch”;

if (ag→phy_if_mode == PHY_INTERFACE_MODE_GMII) {
@@ -1267,6 +1279,8 @@ ar7240_probe(struct ag71xx *ag, struct device_node *np)
ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE1,
AR934X_REG_OPER_MODE1_PHY4_MII_EN);
swdev→ports = AR7240_NUM_PORTS - 1;
+ /* Don’t include port 5 as it’s now a separate PHY */
+ as→phy_poll_mask &= 0x1f;
} else {
swdev→ports = AR7240_NUM_PORTS;
}
@@ -1305,6 +1319,9 @@ void ag71xx_ar7240_start(struct ag71xx *ag)
if (!as)
return;

+ if (ag→phy_dev)
+ ag→phy_dev→priv = ag→phy_priv;
+
ar7240sw_reset(as);

ar7240_set_addr(as, ag→dev→dev_addr);
@@ -1322,6 +1339,9 @@ int ag71xx_ar7240_init(struct ag71xx *ag, struct device_node *np)
ag→phy_priv = as;
ar7240sw_reset(as);

+ if (ag→phy_dev)
+ ag→phy_dev→priv = ag→phy_priv;
+
rwlock_init(&as→stats_lock);

return 0;
@@ -1330,6 +1350,7 @@ int ag71xx_ar7240_init(struct ag71xx *ag, struct device_node *np)
void ag71xx_ar7240_cleanup(struct ag71xx *ag)
{
struct ar7240sw *as = ag→phy_priv;
+ ag→phy_dev→priv = NULL;

if (!as)
return;
@@ -1338,3 +1359,162 @@ void ag71xx_ar7240_cleanup(struct ag71xx *ag)
kfree(as);
ag→phy_priv = NULL;
}
+
+/* Copy of genphy_update_link *without* the call to driver hook for update_link
+ * else we get an infinite loop; from kernel drivers/net/phy/phy_device.c
+ */
+int ar7240sw_copy_genphy_update_link(struct phy_device *phydev) {
+ int status;
+
+ /* Do a fake read */
+ status = phy_read(phydev, MII_BMSR);
+ if (status < 0)
+ return status;
+
+ /* Read link and autonegotiation status */
+ status = phy_read(phydev, MII_BMSR);
+ if (status < 0)
+ return status;
+
+ if 1)) {
+ continue;
+ }
+
+ status = ar7240_get_port_link(swdev, i, &link);
+ if (status < 0) {
+ return status;
+ }
+
+ /* If any non-masked port is active, consider
+ * device to be ‘up’ + */
+ if (link.link) {
+ phydev→link = 1;
+ return 0;
+ }
+ }
+ /* otherwise, device is ‘down’ */
+ phydev→link = 0;
+ return 0;
+}
+
+int ar7240sw_read_status(struct phy_device *phydev) {
+ struct ar7240sw *as = NULL;
+ struct switch_dev *swdev = NULL;
+ int status;
+ int i;
+ struct switch_port_link link;
+
+ if (!phydev→priv)
+ return genphy_read_status(phydev);
+
+ status = ar7240sw_update_link(phydev);
+ if (status)
+ return status;
+
+ as = phydev→priv;
+ swdev = &(as→swdev);
+
+ if (!swdev)
+ return genphy_read_status(phydev);
+
+ /* Set link status for base device */
+ if (phydev→link) {
+ /* While down set link speed duplex lower, but valid */
+ phydev→speed = SPEED_10;
+ phydev→duplex = DUPLEX_HALF;
+
+ for (i = 0; i < swdev→ports; i++) {
+ /* We report the speed and duplex as the
+ * highest any non-masked port has
+ * negotiated.
+ */
+ if (i == swdev→cpu_port)
+ continue;
+
+ if (!(as→phy_poll_mask & BIT(i))) {
+ continue;
+ }
+
+ status = ar7240_get_port_link(swdev, i, &link);
+ if (status < 0) {
+ return status;
+ }
+
+ switch(link.speed) {
+ case SPEED_100:
+ if (phydev→speed != SPEED_1000)
+ phydev→speed = SPEED_100;
+ break;
+ case SPEED_1000:
+ phydev→speed = SPEED_1000;
+ break;
+ default:
+ /* We never reduce detected speed */
+ break;
+ }
+ switch(link.duplex) {
+ case DUPLEX_FULL:
+ phydev→duplex = DUPLEX_FULL;
+ break;
+ default:
+ /* We never reduce detected duplex */
+ break;
+ }
+ }
+
+ phydev→pause = 0;
+ phydev→asym_pause = 0;
+ }
+ return 0;
+}
+
+int ar7240sw_aneg_done (struct phy_device *phydev) {
+ int status;
+
+ if (!phydev→priv) {
+ return genphy_aneg_done(phydev);
+ }
+
+ status = ar7240sw_read_status(phydev);
+ if (status)
+ return status;
+
+ /* If we were able to get a status we’ve
+ * autonegotiated on the port(s) already.
+ */
+ return 1;
+}
diff –git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
index 635c3b9127..f33e148e2e 100644
— a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
@@ -1509,6 +1509,22 @@ static int ag71xx_probe(struct platform_device *pdev)

platform_set_drvdata(pdev, dev);

+ ag→phy_dev→priv = NULL;
+
+ if (ag→phy_priv)
+ if (ag→is_switch)
+ ag→phy_dev→priv = ag→phy_priv;
+
+ /* NB: This replaces update_link for *all* instances of the
+ * the driver (so all ‘Generic PHY’ instances are affected)
+ */
+
+ if (ag→phy_dev→drv) {
+ ag→phy_dev→drv→update_link = ar7240sw_update_link;
+ ag→phy_dev→drv→read_status = ar7240sw_read_status;
+ ag→phy_dev→drv→aneg_done = ar7240sw_aneg_done;
+ }
+
err = register_netdev(dev);
if (err) {
dev_err(&pdev→dev, “unable to register net device\n”);


1) status & BMSR_LSTATUS) == 0)
+ phydev→link = 0;
+ else
+ phydev→link = 1;
+
+ return 0;
+}
+
+
+int ar7240sw_update_link (struct phy_device *phydev) {
+ struct ar7240sw *as = NULL;
+ struct switch_dev *swdev = NULL;
+ int i;
+ int status = 0;
+ struct switch_port_link link;
+
+ if (!phydev→priv) {
+ return ar7240sw_copy_genphy_update_link(phydev);
+ }
+
+ as = phydev→priv;
+ swdev = &(as→swdev);
+
+ if (!swdev) {
+ return ar7240sw_copy_genphy_update_link(phydev);
+ }
+
+ /* Don’t touch link state until we actully know the state */
+ for (i = 0; i < swdev→ports; i++) {
+
+ if (i == swdev→cpu_port)
+ continue;
+
+ if (!(as→phy_poll_mask & BIT(i
 1702 KernelBug ReportVery LowLow ath79: ag71xx (ar7240/ar934x builtin) switch does not d ...TrunkClosed Task Description

Supply the following if possible:
- Device problem occurs on
- Software versions of OpenWrt/LEDE release, packages, etc.
- Steps to reproduce

I’m adding ath79 arch support for some devices, and discovered the following:

At least on Atheros 9341 SoC with phy4-mii-enable enabled (i.e. fifth port acts as a separate ethX device (rather than being configured as a VLAN)), the link status of the the switch (i.e. the ports acting as a ‘normal’ ar7240 switch) is only detected on a single port of the switch and all other ports are ignored for cable plug/unplug.

This seems to be because the default is to use a ‘Generic PHY’ device and the SoC only responds to a single port on the switch.

I have a hack (not submitted as a patch to the list yet because to be done properly I really need to create an ag71xx-phy device (which means also ag71xx-mdio, etc for the drivers to work properly) to replace certain functions (which you’ll see below) in the ‘Generic PHY’ so that link detection works correctly) which I include here for reference, which gets this working for me.

.../net/ethernet/atheros/ag71xx/ag71xx.h | 5 +
.../ethernet/atheros/ag71xx/ag71xx_ar7240.c | 180 ++++++++++++++++++
.../net/ethernet/atheros/ag71xx/ag71xx_main.c | 16 ++
3 files changed, 201 insertions(+)

diff –git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h
index 22b22522a3..2bebec4ac5 100644
— a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx.h
@@ -179,6 +179,7 @@ struct ag71xx {
struct phy_device *phy_dev;
void *phy_priv;
int phy_if_mode;
+ bool is_switch;

unsigned int link;
unsigned int speed;
@@ -450,4 +451,8 @@ int ag71xx_mdio_mii_write(struct mii_bus *bus, int addr, int reg, u16 val);
int ar7240sw_phy_read(struct mii_bus *mii, int addr, int reg);
int ar7240sw_phy_write(struct mii_bus *mii, int addr, int reg, u16 val);

+int ar7240sw_update_link(struct phy_device *phydev);
+int ar7240sw_read_status(struct phy_device *phydev);
+int ar7240sw_aneg_done(struct phy_device *phydev);
+
#endif /* _AG71XX_H */
diff –git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
index 52c0f964cf..12c62660b4 100644
— a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
@@ -194,6 +194,7 @@
#define AR7240_PORT_CPU 0
#define AR7240_NUM_PORTS 6
#define AR7240_NUM_PHYS 5
+#define AR7240_PHY_POLL_MASK 0x3e

#define AR7240_PHY_ID1 0x004d
#define AR7240_PHY_ID2 0xd041
@@ -294,6 +295,7 @@ struct ar7240sw {
struct ag71xx_switch_platform_data *swdata;
struct switch_dev swdev;
int num_ports;
+ u32 phy_poll_mask;
u8 ver;
bool vlan;
u16 vlan_id[AR7240_MAX_VLANS];
@@ -1245,10 +1247,20 @@ ar7240_probe(struct ag71xx *ag, struct device_node *np)
as→ver = (ctrl » AR7240_MASK_CTRL_VERSION_S) &
AR7240_MASK_CTRL_VERSION_M;

+ if (of_property_read_u32(np, “phy-poll-mask”, &as→phy_poll_mask))
+ as→phy_poll_mask = AR7240_PHY_POLL_MASK;
+ else
+ as→phy_poll_mask &= AR7240_PHY_POLL_MASK;
+
+ pr_info(”phy-poll-mask %02x”, as→phy_poll_mask);
+
+ ag→is_switch = false;
if (sw_is_ar7240(as)) {
swdev→name = “AR7240/AR9330 built-in switch”;
swdev→ports = AR7240_NUM_PORTS - 1;
+ ag→is_switch = true;
} else if (sw_is_ar934x(as)) {
+ ag→is_switch = true;
swdev→name = “AR934X built-in switch”;

if (ag→phy_if_mode == PHY_INTERFACE_MODE_GMII) {
@@ -1267,6 +1279,8 @@ ar7240_probe(struct ag71xx *ag, struct device_node *np)
ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE1,
AR934X_REG_OPER_MODE1_PHY4_MII_EN);
swdev→ports = AR7240_NUM_PORTS - 1;
+ /* Don’t include port 5 as it’s now a separate PHY */
+ as→phy_poll_mask &= 0x1f;
} else {
swdev→ports = AR7240_NUM_PORTS;
}
@@ -1305,6 +1319,9 @@ void ag71xx_ar7240_start(struct ag71xx *ag)
if (!as)
return;

+ if (ag→phy_dev)
+ ag→phy_dev→priv = ag→phy_priv;
+
ar7240sw_reset(as);

ar7240_set_addr(as, ag→dev→dev_addr);
@@ -1322,6 +1339,9 @@ int ag71xx_ar7240_init(struct ag71xx *ag, struct device_node *np)
ag→phy_priv = as;
ar7240sw_reset(as);

+ if (ag→phy_dev)
+ ag→phy_dev→priv = ag→phy_priv;
+
rwlock_init(&as→stats_lock);

return 0;
@@ -1330,6 +1350,7 @@ int ag71xx_ar7240_init(struct ag71xx *ag, struct device_node *np)
void ag71xx_ar7240_cleanup(struct ag71xx *ag)
{
struct ar7240sw *as = ag→phy_priv;
+ ag→phy_dev→priv = NULL;

if (!as)
return;
@@ -1338,3 +1359,162 @@ void ag71xx_ar7240_cleanup(struct ag71xx *ag)
kfree(as);
ag→phy_priv = NULL;
}
+
+/* Copy of genphy_update_link *without* the call to driver hook for update_link
+ * else we get an infinite loop; from kernel drivers/net/phy/phy_device.c
+ */
+int ar7240sw_copy_genphy_update_link(struct phy_device *phydev) {
+ int status;
+
+ /* Do a fake read */
+ status = phy_read(phydev, MII_BMSR);
+ if (status < 0)
+ return status;
+
+ /* Read link and autonegotiation status */
+ status = phy_read(phydev, MII_BMSR);
+ if (status < 0)
+ return status;
+
+ if 1)) {
+ continue;
+ }
+
+ status = ar7240_get_port_link(swdev, i, &link);
+ if (status < 0) {
+ return status;
+ }
+
+ /* If any non-masked port is active, consider
+ * device to be ‘up’ + */
+ if (link.link) {
+ phydev→link = 1;
+ return 0;
+ }
+ }
+ /* otherwise, device is ‘down’ */
+ phydev→link = 0;
+ return 0;
+}
+
+int ar7240sw_read_status(struct phy_device *phydev) {
+ struct ar7240sw *as = NULL;
+ struct switch_dev *swdev = NULL;
+ int status;
+ int i;
+ struct switch_port_link link;
+
+ if (!phydev→priv)
+ return genphy_read_status(phydev);
+
+ status = ar7240sw_update_link(phydev);
+ if (status)
+ return status;
+
+ as = phydev→priv;
+ swdev = &(as→swdev);
+
+ if (!swdev)
+ return genphy_read_status(phydev);
+
+ /* Set link status for base device */
+ if (phydev→link) {
+ /* While down set link speed duplex lower, but valid */
+ phydev→speed = SPEED_10;
+ phydev→duplex = DUPLEX_HALF;
+
+ for (i = 0; i < swdev→ports; i++) {
+ /* We report the speed and duplex as the
+ * highest any non-masked port has
+ * negotiated.
+ */
+ if (i == swdev→cpu_port)
+ continue;
+
+ if (!(as→phy_poll_mask & BIT(i))) {
+ continue;
+ }
+
+ status = ar7240_get_port_link(swdev, i, &link);
+ if (status < 0) {
+ return status;
+ }
+
+ switch(link.speed) {
+ case SPEED_100:
+ if (phydev→speed != SPEED_1000)
+ phydev→speed = SPEED_100;
+ break;
+ case SPEED_1000:
+ phydev→speed = SPEED_1000;
+ break;
+ default:
+ /* We never reduce detected speed */
+ break;
+ }
+ switch(link.duplex) {
+ case DUPLEX_FULL:
+ phydev→duplex = DUPLEX_FULL;
+ break;
+ default:
+ /* We never reduce detected duplex */
+ break;
+ }
+ }
+
+ phydev→pause = 0;
+ phydev→asym_pause = 0;
+ }
+ return 0;
+}
+
+int ar7240sw_aneg_done (struct phy_device *phydev) {
+ int status;
+
+ if (!phydev→priv) {
+ return genphy_aneg_done(phydev);
+ }
+
+ status = ar7240sw_read_status(phydev);
+ if (status)
+ return status;
+
+ /* If we were able to get a status we’ve
+ * autonegotiated on the port(s) already.
+ */
+ return 1;
+}
diff –git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
index 635c3b9127..f33e148e2e 100644
— a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
@@ -1509,6 +1509,22 @@ static int ag71xx_probe(struct platform_device *pdev)

platform_set_drvdata(pdev, dev);

+ ag→phy_dev→priv = NULL;
+
+ if (ag→phy_priv)
+ if (ag→is_switch)
+ ag→phy_dev→priv = ag→phy_priv;
+
+ /* NB: This replaces update_link for *all* instances of the
+ * the driver (so all ‘Generic PHY’ instances are affected)
+ */
+
+ if (ag→phy_dev→drv) {
+ ag→phy_dev→drv→update_link = ar7240sw_update_link;
+ ag→phy_dev→drv→read_status = ar7240sw_read_status;
+ ag→phy_dev→drv→aneg_done = ar7240sw_aneg_done;
+ }
+
err = register_netdev(dev);
if (err) {
dev_err(&pdev→dev, “unable to register net device\n”);


1) status & BMSR_LSTATUS) == 0)
+ phydev→link = 0;
+ else
+ phydev→link = 1;
+
+ return 0;
+}
+
+
+int ar7240sw_update_link (struct phy_device *phydev) {
+ struct ar7240sw *as = NULL;
+ struct switch_dev *swdev = NULL;
+ int i;
+ int status = 0;
+ struct switch_port_link link;
+
+ if (!phydev→priv) {
+ return ar7240sw_copy_genphy_update_link(phydev);
+ }
+
+ as = phydev→priv;
+ swdev = &(as→swdev);
+
+ if (!swdev) {
+ return ar7240sw_copy_genphy_update_link(phydev);
+ }
+
+ /* Don’t touch link state until we actully know the state */
+ for (i = 0; i < swdev→ports; i++) {
+
+ if (i == swdev→cpu_port)
+ continue;
+
+ if (!(as→phy_poll_mask & BIT(i
Showing tasks 1 - 10 of 10 Page 1 of 1

Available keyboard shortcuts

Tasklist

Task Details

Task Editing