Re: [PATCH 1/3] net: ax88796c: ASIX AX88796C SPI Ethernet Adapter Driver

Lukasz Stelmach
2020-09-08T17:49:54Z

[-- Attachment #1: Type: text/plain, Size: 7680 bytes --]

It was <2020-09-07 pon 20:18>, when Andrew Lunn wrote:
>> > On Tue, Aug 25, 2020 at 07:03:09PM +0200, Łukasz Stelmach wrote:
>> >> +++ b/drivers/net/ethernet/asix/ax88796c_ioctl.c
>> >
>> > This is an odd filename. The ioctl code is wrong anyway, but there is
>> > a lot more than ioctl in here. I suggest you give it a new name.
>> >
>>
>> Sure, any suggestions?
>
> Sorry, i have forgotten what is actually contained.

IOCTL handler (.ndo_do_ioctl), ethtool ops, and a bunch of hw control
functions.

> Does it even need to be a separate file?

It doesn't need, but I think it makes sense to keep ioctl and ethtool
stuff in a separate file. Some of the hw control function look like they
might change after using phylib.

>> >> +u8 ax88796c_check_power(struct ax88796c_device *ax_local)
>> >
>> > bool ?
>>
>> OK.
>>
>> It appears, however, that 0 means OK and 1 !OK. Do you think changing to
>> TRUE and FALSE (or FALSE and TRUE) is required?
>
> Or change the name, ax88796c_check_power_off()? I don't really care,
> so long as it is logical and not surprising.
>

Good idea, thanks.

>> >> + AX_READ_STATUS(&ax_local->ax_spi, &ax_status);
>> >> + if (!(ax_status.status & AX_STATUS_READY)) {
>> >> +
>> >> + /* AX88796C in power saving mode */
>> >> + AX_WAKEUP(&ax_local->ax_spi);
>> >> +
>> >> + /* Check status */
>> >> + start_time = jiffies;
>> >> + do {
>> >> + if (time_after(jiffies, start_time + HZ/2)) {
>> >> + netdev_err(ax_local->ndev,
>> >> + "timeout waiting for wakeup"
>> >> + " from power saving\n");
>> >> + break;
>> >> + }
>> >> +
>> >> + AX_READ_STATUS(&ax_local->ax_spi, &ax_status);
>> >> +
>> >> + } while (!(ax_status.status & AX_STATUS_READY));
>> >
>> > include/linux/iopoll.h
>> >
>>
>> Done. The result seems only slightly more elegant since the generic
>> read_poll_timeout() needs to be employed.
>
> Often code like this has bugs in it, not correctly handling the
> scheduler sleeping longer than expected. That is why i point people at
> iopoll, no bugs, not elegance.
>
>> The manufacturer says
>>
>> The AX88796C integrates on-chip Fast Ethernet MAC and PHY, […]
>>
>> There is a single integrated PHY in this chip and no possiblity to
>> connect external one. Do you think it makes sense in such case to
>> introduce the additional layer of abstraction?
>
> Yes it does, because it then uses all the standard phylib code to
> drive the PHY which many people understand, is well tested, etc. It
> will make the MAC driver smaller and probably less buggy.
>

Good point. I need to figure out how to do it. Can you point (from the
top fou your head) a driver which does it for a simmilarly integrated
device?

>> >> +static char *macaddr;
>> >> +module_param(macaddr, charp, 0);
>> >> +MODULE_PARM_DESC(macaddr, "MAC address");
>> >
>> > No Module parameters. You can get the MAC address from DT.
>>
>> What about systems without DT? Not every bootloader is sophisicated
>> enough to edit DT before starting kernel. AX88786C is a chip that can be
>> used in a variety of systems and I'd like to avoid too strong
>> assumptions.
>
> There is also a standardised way to read it from ACPI. And you can set
> it using ip link set. DaveM will likely NACK a module parameter.
>

I am not arguing to keep the parameter at any cost, but I would really
like to know if there is a viable alternative for DT and ACPI. This chip
is for smaller systems which not necessarily implement advanced
bootloaders (and DT).

>> >> +MODULE_AUTHOR("ASIX");
>> >
>> > Do you expect ASIX to support this?
>>
>> No.
>>
>> > You probably want to put your name here.
>>
>> I don't want to be considered as the only author and as far as I can
>> tell being mentioned as an author does not imply being a
>> maintainer. Do you think two MODULE_AUTHOR()s be OK?
>
> Can you have two? One with two names listed is O.K.
>

According to module.h

/*
* Author(s), use "Name <email>" or just "Name", for multiple
* authors use multiple MODULE_AUTHOR() statements/lines.
*/

>> >> +
>> >> + phy_status = AX_READ(&ax_local->ax_spi, P0_PSCR);
>> >> + if (phy_status & PSCR_PHYLINK) {
>> >> +
>> >> + ax_local->w_state = ax_nop;
>> >> + time_to_chk = 0;
>> >> +
>> >> + } else if (!(phy_status & PSCR_PHYCOFF)) {
>> >> + /* The ethernet cable has been plugged */
>> >> + if (ax_local->w_state == chk_cable) {
>> >> + if (netif_msg_timer(ax_local))
>> >> + netdev_info(ndev, "Cable connected\n");
>> >> +
>> >> + ax_local->w_state = chk_link;
>> >> + ax_local->w_ticks = 0;
>> >> + } else {
>> >> + if (netif_msg_timer(ax_local))
>> >> + netdev_info(ndev, "Check media status\n");
>> >> +
>> >> + if (++ax_local->w_ticks == AX88796C_WATCHDOG_RESTART) {
>> >> + if (netif_msg_timer(ax_local))
>> >> + netdev_info(ndev, "Restart autoneg\n");
>> >> + ax88796c_mdio_write(ndev,
>> >> + ax_local->mii.phy_id, MII_BMCR,
>> >> + (BMCR_SPEED100 | BMCR_ANENABLE |
>> >> + BMCR_ANRESTART));
>> >> +
>> >> + if (netif_msg_hw(ax_local))
>> >> + ax88796c_dump_phy_regs(ax_local);
>> >> + ax_local->w_ticks = 0;
>> >> + }
>> >> + }
>> >> + } else {
>> >> + if (netif_msg_timer(ax_local))
>> >> + netdev_info(ndev, "Check cable status\n");
>> >> +
>> >> + ax_local->w_state = chk_cable;
>> >> + }
>> >> +
>> >> + ax88796c_set_power_saving(ax_local, ax_local->ps_level);
>> >> +
>> >> + if (time_to_chk)
>> >> + mod_timer(&ax_local->watchdog, jiffies + time_to_chk);
>> >> +}
>> >
>> > This is not the normal use of a watchdog in network drivers. The
>> > normal case is the network stack as asked the driver to do something,
>> > normally a TX, and the driver has not reported the action has
>> > completed. The state of the cable should not make any
>> > difference. This does not actually appear to do anything useful, like
>> > kick the hardware to bring it back to life.
>> >
>>
>> Maybe it's the naming that is a problem. Yes, it is not a watchdog, but
>> rather a periodic housekeeping and it kicks hw if it can't negotiate
>> the connection. The question is: should the settings be reset in such case.
>
> Let see what is left once you convert to phylib.
>

OK.

>> >> + struct net_device *ndev = ax_local->ndev;
>> >> + int status;
>> >> +
>> >> + do {
>> >> + if (!(ax_local->checksum & AX_RX_CHECKSUM))
>> >> + break;
>> >> +
>> >> + /* checksum error bit is set */
>> >> + if ((rxhdr->flags & RX_HDR3_L3_ERR) ||
>> >> + (rxhdr->flags & RX_HDR3_L4_ERR))
>> >> + break;
>> >> +
>> >> + if ((rxhdr->flags & RX_HDR3_L4_TYPE_TCP) ||
>> >> + (rxhdr->flags & RX_HDR3_L4_TYPE_UDP)) {
>> >> + skb->ip_summed = CHECKSUM_UNNECESSARY;
>> >> + }
>> >> + } while (0);
>> >
>> >
>> > ??
>> >
>>
>> if() break; Should I use goto?
>
> Sorry, i was too ambiguous. Why:
>
> do {
> } while (0);
>
> It is an odd construct.

As to "why" — you have correctly spotted, this is a vendor driver I am
porting. Although it's not like I am trying to avoid any changes, but
because this driver worked for us on older kernels (v3.10.9) I am trying
not to touch pieces which IMHO are good enough. Of course I don't mind
suggestions from more experienced developers.

To avoid using do{}while(0) it requires either goto (instead of breaks),
nesting those if()s in one another or a humongous single if(). Neither
looks pretty and the last one is even less readable than
do()while.

--
Łukasz Stelmach
Samsung R&D Institute Poland
Samsung Electronics

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 487 bytes --]