Re: [PATCH net-next 5/6] net: dsa: realtek-smi: add rtl8365mb subdriver for RTL8365MB-VC
From: Alvin Šipraga
Date: Tue Oct 12 2021 - 10:30:59 EST
On 10/12/21 4:03 PM, Vladimir Oltean wrote:
> On Tue, Oct 12, 2021 at 01:50:44PM +0000, Alvin Šipraga wrote:
>>>>> +static int rtl8365mb_ext_config_rgmii(struct realtek_smi *smi, int
>>>>> port,
>>>>> + phy_interface_t interface)
>>>>> +{
>>>>> + int tx_delay = 0;
>>>>> + int rx_delay = 0;
>>>>> + int ext_port;
>>>>> + int ret;
>>>>> +
>>>>> + if (port == smi->cpu_port) {
>>>>> + ext_port = PORT_NUM_L2E(port);
>>>>> + } else {
>>>>> + dev_err(smi->dev, "only one EXT port is currently
>>>>> supported\n");
>>>>> + return -EINVAL;
>>>>> + }
>>>>> +
>>>>> + /* Set the RGMII TX/RX delay
>>>>> + *
>>>>> + * The Realtek vendor driver indicates the following possible
>>>>> + * configuration settings:
>>>>> + *
>>>>> + * TX delay:
>>>>> + * 0 = no delay, 1 = 2 ns delay
>>>>> + * RX delay:
>>>>> + * 0 = no delay, 7 = maximum delay
>>>>> + * No units are specified, but there are a total of 8 steps.
>>>>> + *
>>>>> + * The vendor driver also states that this must be configured
>>>>> *before*
>>>>> + * forcing the external interface into a particular mode, which
>>>>> is done
>>>>> + * in the rtl8365mb_phylink_mac_link_{up,down} functions.
>>>>> + *
>>>>> + * NOTE: For now this is hardcoded to tx_delay = 1, rx_delay = 4.
>>>>> + */
>>>>> + if (interface == PHY_INTERFACE_MODE_RGMII_ID ||
>>>>> + interface == PHY_INTERFACE_MODE_RGMII_TXID)
>>>>> + tx_delay = 1; /* 2 ns */
>>>>> +
>>>>> + if (interface == PHY_INTERFACE_MODE_RGMII_ID ||
>>>>> + interface == PHY_INTERFACE_MODE_RGMII_RXID)
>>>>> + rx_delay = 4;
>>>>
>>>> There is this ongoing discussion that we have been interpreting the
>>>> meaning of "phy-mode" incorrectly for RGMII all along. The conclusion
>>>> seems to be that for a PHY driver, it is okay to configure its internal
>>>> delay lines based on the value of the phy-mode string, but for a MAC
>>>> driver it is not. The only viable option for a MAC driver to configure
>>>> its internal delays is based on parsing some new device tree properties
>>>> called rx-internal-delay-ps and tx-internal-delay-ps.
>>>> Since you do not seem to have any baggage to support here (new driver),
>>>> could you please just accept any PHY_INTERFACE_MODE_RGMII* value and
>>>> apply delays (or not) based on those other OF properties?
>>>> https://patchwork.kernel.org/project/netdevbpf/patch/20210723173108.459770-6-prasanna.vengateshan@xxxxxxxxxxxxx/>>>>
>>>
>>> Ugh, I remember my head spinning when I first looked into this. But OK,
>>> I can do as you suggest.
>>>
>>> Just to clarify: if the *-internal-delay-ps property is missing, you are
>>> saying that I should set the delay to 0 rather than my defaults (tx=1,
>>> rx=4), right?
>
> Yes, I think so.
>
>> Another problem is that for the RX delay, I have no idea what the actual
>> unit of measurement is. See the comment I left in
>> rtl8365mb_ext_config_rgmii().
>>
>> So I guess I could "reinterpret" rx-internal-delay-ps to mean these
>> magic step values, or otherwise I don't know what might be the best
>> practice.
>
> I think what could work is you could accept only the 0 or 2000 ps values.
> For the TX delay you say it is clear that you should program "1" to hardware.
> For the RX delay I guess that the value of "4" is simply your best guess
> of what would correspond to 2 ns. So you could just transform the 2000 ps
> value into a "4" for the RX delay and make no other guesses otherwise.
OK, this is also the most obvious way to deal with it. Will address in v2.
>
>>>>> + ret = regmap_update_bits(
>>>>> + smi->map, RTL8365MB_EXT_RGMXF_REG(ext_port),
>>>>> + RTL8365MB_EXT_RGMXF_TXDELAY_MASK |
>>>>> + RTL8365MB_EXT_RGMXF_RXDELAY_MASK,
>>>>> + FIELD_PREP(RTL8365MB_EXT_RGMXF_TXDELAY_MASK, tx_delay) |
>>>>> + FIELD_PREP(RTL8365MB_EXT_RGMXF_RXDELAY_MASK, rx_delay));
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = regmap_update_bits(
>>>>> + smi->map, RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(ext_port),
>>>>> + RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(ext_port),
>>>>> + RTL8365MB_EXT_PORT_MODE_RGMII
>>>>> + << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(
>>>>> + ext_port));
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>
>>>>> +static void rtl8365mb_phylink_mac_config(struct dsa_switch *ds, int
>>>>> port,
>>>>> + unsigned int mode,
>>>>> + const struct phylink_link_state *state)
>>>>> +{
>>>>> + struct realtek_smi *smi = ds->priv;
>>>>> + int ret;
>>>>> +
>>>>> + if (!rtl8365mb_phy_mode_supported(ds, port, state->interface)) {
>>>>> + dev_err(smi->dev, "phy mode %s is unsupported on port %d\n",
>>>>> + phy_modes(state->interface), port);
>>>>> + return;
>>>>> + }
>>>>> +
>>>>> + /* If port MAC is connected to an internal PHY, we have nothing
>>>>> to do */
>>>>> + if (dsa_is_user_port(ds, port))
>>>>> + return;
>>>>> +
>>>>> + if (mode != MLO_AN_PHY && mode != MLO_AN_FIXED) {
>>>>> + dev_err(smi->dev,
>>>>> + "port %d supports only conventional PHY or fixed-link\n",
>>>>> + port);
>>>>> + return;
>>>>> + }
>>>>> +
>>>>> + if (phy_interface_mode_is_rgmii(state->interface)) {
>>>>> + ret = rtl8365mb_ext_config_rgmii(smi, port, state->interface);
>>>>> + if (ret)
>>>>> + dev_err(smi->dev,
>>>>> + "failed to configure RGMII mode on port %d: %d\n",
>>>>> + port, ret);
>>>>> + return;
>>>>> + }
>>>>> +
>>>>> + /* TODO: Implement MII and RMII modes, which the RTL8365MB-VC also
>>>>> + * supports
>>>>> + */
>>>>> +}
>>>>> +
>>>>> +static void rtl8365mb_phylink_mac_link_down(struct dsa_switch *ds,
>>>>> int port,
>>>>> + unsigned int mode,
>>>>> + phy_interface_t interface)
>>>>> +{
>>>>> + struct realtek_smi *smi = ds->priv;
>>>>> + int ret;
>>>>> +
>>>>> + if (dsa_is_cpu_port(ds, port)) {
>>>>
>>>> I assume the "dsa_is_cpu_port()" check here can also be replaced with
>>>> "phy_interface_mode_is_rgmii(interface)"? Can you please do that for
>>>> consistency?
>>>
>>> Consistency with what exactly?
>
> I was going to say with rtl8365mb_phylink_mac_config() where you do have
> a specific check for phy_interface_mode_is_rgmii(), but now I notice
> that it is further guarded by a "dsa_is_user_port()" check. So, with nothing.
>
>>> All I'm saying with this code is that for CPU ports, we have to
>>> force some mode on it in response to mac_link_up. This doesn't
>>> apply to user ports because the PHY is always internal to the switch
>>> (this appears to be the case for all switches in the rtl8365mb-like
>>> family). Or are you wondering about a scenario where the port is
>>> treated as a DSA port?
>
> Understood that the code is functionally correct, but you're not forcing
> the link because it's a CPU port, you're forcing the link because it's
> an RGMII port. Semantically, a CPU port means something entirely
> different: pass DSA-tagged frames to a host. Nothing at the physical link level.
> On your switch it is basically a coincidence that all user ports have
> internal PHYs, and the CPU port is RGMII. All I'm saying is to remove
> the assumptions based on port roles from your MAC configuration logic.
I see your point. However I would still like to keep the
dsa_is_{user,cpu}_port() checks in rtl8365mb_phy_mode_supported(), just
so that somebody doesn't unwittingly misconfigure the chip via device
tree. But I'll remove the port type checks in
.phylink_mac_{config,link_down,link_up}.
>
> For somebody searching the git tree for .phylink_mac_link_up implementations
> and sleepwalking into your code, it will be deeply confusing to see such
> logic, even if there is a drawing at the top of the file.
>
> Why do you need these checks anyway and cannot simply distinguish based
> on PHY_INTERFACE_MODE_INTERNAL vs PHY_INTERFACE_MODE_RGMII*?
Even this might not be necessary, but I'll check it out for v2.