diff mbox series

[net-next,v2,6/6] net: phy: microchip_t1s: add support for Microchip LAN865x Rev.B0 PHYs

Message ID 20230522113331.36872-7-Parthiban.Veerasooran@microchip.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series microchip_t1s: Update on Microchip 10BASE-T1S PHY driver | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 8 this patch: 8
netdev/cc_maintainers success CCed 8 of 8 maintainers
netdev/build_clang success Errors and warnings before: 8 this patch: 8
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 8 this patch: 8
netdev/checkpatch warning WARNING: line length of 81 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Parthiban Veerasooran May 22, 2023, 11:33 a.m. UTC
Add support for the Microchip LAN865x Rev.B0 10BASE-T1S Internal PHYs
(LAN8650/1). The LAN865x combines a Media Access Controller (MAC) and an
internal 10BASE-T1S Ethernet PHY to access 10BASE‑T1S networks. As
LAN867X and LAN865X are using the same function for the read_status,
rename the function as lan86xx_read_status.

Signed-off-by: Parthiban Veerasooran <Parthiban.Veerasooran@microchip.com>
---
 drivers/net/phy/microchip_t1s.c | 184 +++++++++++++++++++++++++++++++-
 1 file changed, 181 insertions(+), 3 deletions(-)

Comments

Andrew Lunn May 22, 2023, 12:56 p.m. UTC | #1
> +static int lan865x_setup_cfgparam(struct phy_device *phydev)
> +{
> +	u16 cfg_results[5];
> +	u16 cfg_params[ARRAY_SIZE(lan865x_revb0_fixup_cfg_regs)];
> +	s8 offsets[2];
> +	int ret;

Reverse Christmas tree please.

> +
> +	ret = lan865x_generate_cfg_offsets(phydev, offsets);
> +	if (ret)
> +		return ret;
> +
> +	ret = lan865x_read_cfg_params(phydev, cfg_params);

Is this doing a read from fuses? Is anything documented about this?
What the values mean? Would a board designer ever need to use
different values? Or is this just a case of 'trust us', you don't need
to understand this magic.

> +	if (ret)
> +		return ret;
> +
> +	cfg_results[0] = (cfg_params[0] & 0x000F) |
> +			  FIELD_PREP(GENMASK(15, 10), 9 + offsets[0]) |
> +			  FIELD_PREP(GENMASK(15, 4), 14 + offsets[0]);
> +	cfg_results[1] = (cfg_params[1] & 0x03FF) |
> +			  FIELD_PREP(GENMASK(15, 10), 40 + offsets[1]);
> +	cfg_results[2] = (cfg_params[2] & 0xC0C0) |
> +			  FIELD_PREP(GENMASK(15, 8), 5 + offsets[0]) |
> +			  (9 + offsets[0]);
> +	cfg_results[3] = (cfg_params[3] & 0xC0C0) |
> +			  FIELD_PREP(GENMASK(15, 8), 9 + offsets[0]) |
> +			  (14 + offsets[0]);
> +	cfg_results[4] = (cfg_params[4] & 0xC0C0) |
> +			  FIELD_PREP(GENMASK(15, 8), 17 + offsets[0]) |
> +			  (22 + offsets[0]);
> +
> +	return lan865x_write_cfg_params(phydev, cfg_results);
> +}


	Andrew
Simon Horman May 22, 2023, 3:02 p.m. UTC | #2
On Mon, May 22, 2023 at 05:03:31PM +0530, Parthiban Veerasooran wrote:
> Add support for the Microchip LAN865x Rev.B0 10BASE-T1S Internal PHYs
> (LAN8650/1). The LAN865x combines a Media Access Controller (MAC) and an
> internal 10BASE-T1S Ethernet PHY to access 10BASE‑T1S networks. As
> LAN867X and LAN865X are using the same function for the read_status,
> rename the function as lan86xx_read_status.
> 
> Signed-off-by: Parthiban Veerasooran <Parthiban.Veerasooran@microchip.com>

Hi Parthiban,

thanks for your patch.
Some minor nits from my side.

...

> +/* This is pulled straigt from AN1760 from 'calulation of offset 1' &
> + * 'calculation of offset 2'
> + */

nit: s/straigt/straight/

> +static int lan865x_generate_cfg_offsets(struct phy_device *phydev, s8 offsets[2])
> +{
> +	const u16 fixup_regs[2] = {0x0004, 0x0008};
> +	int ret;
> +
> +	for (int i = 0; i < ARRAY_SIZE(fixup_regs); i++) {
> +		ret = lan865x_revb0_indirect_read(phydev, fixup_regs[i]);
> +		if (ret < 0)
> +			return ret;
> +		if (ret & BIT(4))
> +			offsets[i] = ret | 0xE0;
> +		else
> +			offsets[i] = ret;
> +	}
> +
> +	return 0;
> +}

...

> +static int lan865x_setup_cfgparam(struct phy_device *phydev)
> +{
> +	u16 cfg_results[5];
> +	u16 cfg_params[ARRAY_SIZE(lan865x_revb0_fixup_cfg_regs)];
> +	s8 offsets[2];
> +	int ret;

nit: Please use reverse xmas tree order - longest line to shortest -
     for local variable declarations in networking code.

...
Parthiban Veerasooran May 23, 2023, 5:33 a.m. UTC | #3
Hi Simon,

Thanks for your interest in reviewing my patches.

On 22/05/23 8:32 pm, Simon Horman wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> On Mon, May 22, 2023 at 05:03:31PM +0530, Parthiban Veerasooran wrote:
>> Add support for the Microchip LAN865x Rev.B0 10BASE-T1S Internal PHYs
>> (LAN8650/1). The LAN865x combines a Media Access Controller (MAC) and an
>> internal 10BASE-T1S Ethernet PHY to access 10BASE‑T1S networks. As
>> LAN867X and LAN865X are using the same function for the read_status,
>> rename the function as lan86xx_read_status.
>>
>> Signed-off-by: Parthiban Veerasooran <Parthiban.Veerasooran@microchip.com>
> 
> Hi Parthiban,
> 
> thanks for your patch.
> Some minor nits from my side.
> 
> ...
> 
>> +/* This is pulled straigt from AN1760 from 'calulation of offset 1' &
>> + * 'calculation of offset 2'
>> + */
> 
> nit: s/straigt/straight/
Ah yes, surely will correct it in the next version.
> 
>> +static int lan865x_generate_cfg_offsets(struct phy_device *phydev, s8 offsets[2])
>> +{
>> +     const u16 fixup_regs[2] = {0x0004, 0x0008};
>> +     int ret;
>> +
>> +     for (int i = 0; i < ARRAY_SIZE(fixup_regs); i++) {
>> +             ret = lan865x_revb0_indirect_read(phydev, fixup_regs[i]);
>> +             if (ret < 0)
>> +                     return ret;
>> +             if (ret & BIT(4))
>> +                     offsets[i] = ret | 0xE0;
>> +             else
>> +                     offsets[i] = ret;
>> +     }
>> +
>> +     return 0;
>> +}
> 
> ...
> 
>> +static int lan865x_setup_cfgparam(struct phy_device *phydev)
>> +{
>> +     u16 cfg_results[5];
>> +     u16 cfg_params[ARRAY_SIZE(lan865x_revb0_fixup_cfg_regs)];
>> +     s8 offsets[2];
>> +     int ret;
> 
> nit: Please use reverse xmas tree order - longest line to shortest -
>       for local variable declarations in networking code.
Yes understood, surely will correct it in the next version.

Best Regards,
Parthiban V
> 
> ...
Parthiban Veerasooran May 23, 2023, 6:13 a.m. UTC | #4
Hi Andrew,

On 22/05/23 6:26 pm, Andrew Lunn wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
>> +static int lan865x_setup_cfgparam(struct phy_device *phydev)
>> +{
>> +     u16 cfg_results[5];
>> +     u16 cfg_params[ARRAY_SIZE(lan865x_revb0_fixup_cfg_regs)];
>> +     s8 offsets[2];
>> +     int ret;
> 
> Reverse Christmas tree please.
Ah yes, surely will correct it in the next version.
> 
>> +
>> +     ret = lan865x_generate_cfg_offsets(phydev, offsets);
>> +     if (ret)
>> +             return ret;
>> +
>> +     ret = lan865x_read_cfg_params(phydev, cfg_params);
> 
> Is this doing a read from fuses? Is anything documented about this?
> What the values mean? Would a board designer ever need to use
> different values? Or is this just a case of 'trust us', you don't need
> to understand this magic.
Yes, it is a read from fuses and those values are specific/unique for 
each PHY chip. Those values are calculated based on some characteristics 
of the PHY chip behavior for optimal performance and they are fused in 
the PHY chip for the driver to configure it during the initialization. 
This is done in the production/testing stage of the PHY chip. As it is 
specific to PHY chip, a board designer doesn't have any influence on 
this and need not to worry about it. Unfortunately they can't be 
documented anywhere as they are design specific. So simply 'trust us'.

Best Regards,
Parthiban V
> 
>> +     if (ret)
>> +             return ret;
>> +
>> +     cfg_results[0] = (cfg_params[0] & 0x000F) |
>> +                       FIELD_PREP(GENMASK(15, 10), 9 + offsets[0]) |
>> +                       FIELD_PREP(GENMASK(15, 4), 14 + offsets[0]);
>> +     cfg_results[1] = (cfg_params[1] & 0x03FF) |
>> +                       FIELD_PREP(GENMASK(15, 10), 40 + offsets[1]);
>> +     cfg_results[2] = (cfg_params[2] & 0xC0C0) |
>> +                       FIELD_PREP(GENMASK(15, 8), 5 + offsets[0]) |
>> +                       (9 + offsets[0]);
>> +     cfg_results[3] = (cfg_params[3] & 0xC0C0) |
>> +                       FIELD_PREP(GENMASK(15, 8), 9 + offsets[0]) |
>> +                       (14 + offsets[0]);
>> +     cfg_results[4] = (cfg_params[4] & 0xC0C0) |
>> +                       FIELD_PREP(GENMASK(15, 8), 17 + offsets[0]) |
>> +                       (22 + offsets[0]);
>> +
>> +     return lan865x_write_cfg_params(phydev, cfg_results);
>> +}
> 
> 
>          Andrew
>
Andrew Lunn May 23, 2023, 12:29 p.m. UTC | #5
> > Is this doing a read from fuses? Is anything documented about this?
> > What the values mean? Would a board designer ever need to use
> > different values? Or is this just a case of 'trust us', you don't need
> > to understand this magic.
> Yes, it is a read from fuses and those values are specific/unique for 
> each PHY chip. Those values are calculated based on some characteristics 
> of the PHY chip behavior for optimal performance and they are fused in 
> the PHY chip for the driver to configure it during the initialization. 
> This is done in the production/testing stage of the PHY chip. As it is 
> specific to PHY chip, a board designer doesn't have any influence on 
> this and need not to worry about it. Unfortunately they can't be 
> documented anywhere as they are design specific. So simply 'trust us'.

O.K. Please consider for future generations that you move all this
magic into the PHY firmware. There does not seem to be any reason the
OS needs to know about this.

     Andrew
Parthiban Veerasooran May 24, 2023, 7:25 a.m. UTC | #6
Hi Andrew,

On 23/05/23 5:59 pm, Andrew Lunn wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
>>> Is this doing a read from fuses? Is anything documented about this?
>>> What the values mean? Would a board designer ever need to use
>>> different values? Or is this just a case of 'trust us', you don't need
>>> to understand this magic.
>> Yes, it is a read from fuses and those values are specific/unique for
>> each PHY chip. Those values are calculated based on some characteristics
>> of the PHY chip behavior for optimal performance and they are fused in
>> the PHY chip for the driver to configure it during the initialization.
>> This is done in the production/testing stage of the PHY chip. As it is
>> specific to PHY chip, a board designer doesn't have any influence on
>> this and need not to worry about it. Unfortunately they can't be
>> documented anywhere as they are design specific. So simply 'trust us'.
> 
> O.K. Please consider for future generations that you move all this
> magic into the PHY firmware. There does not seem to be any reason the
> OS needs to know about this.
Thanks for the feedback.

Best Regards,
Parthiban V
> 
>       Andrew
diff mbox series

Patch

diff --git a/drivers/net/phy/microchip_t1s.c b/drivers/net/phy/microchip_t1s.c
index a612f7867df8..385d97d47cb4 100644
--- a/drivers/net/phy/microchip_t1s.c
+++ b/drivers/net/phy/microchip_t1s.c
@@ -4,6 +4,7 @@ 
  *
  * Support: Microchip Phys:
  *  lan8670/1/2 Rev.B1
+ *  lan8650/1 Rev.B0 Internal PHYs
  */
 
 #include <linux/kernel.h>
@@ -11,11 +12,19 @@ 
 #include <linux/phy.h>
 
 #define PHY_ID_LAN867X_REVB1 0x0007C162
+#define PHY_ID_LAN865X_REVB0 0x0007C1B3
 
 #define LAN867X_REG_STS2 0x0019
 
 #define LAN867x_RESET_COMPLETE_STS BIT(11)
 
+#define LAN865X_REG_CFGPARAM_ADDR 0x00D8
+#define LAN865X_REG_CFGPARAM_DATA 0x00D9
+#define LAN865X_REG_CFGPARAM_CTRL 0x00DA
+#define LAN865X_REG_STS2 0x0019
+
+#define LAN865X_CFGPARAM_READ_ENABLE BIT(1)
+
 /* The arrays below are pulled from the following table from AN1699
  * Access MMD Address Value Mask
  * RMW 0x1F 0x00D0 0x0002 0x0E03
@@ -50,6 +59,164 @@  static const u16 lan867x_revb1_fixup_masks[12] = {
 	0x0600, 0x7F00, 0x2000, 0xFFFF,
 };
 
+/* LAN865x Rev.B0 configuration parameters from AN1760 */
+static const u32 lan865x_revb0_fixup_registers[28] = {
+	0x0091, 0x0081, 0x0043, 0x0044,
+	0x0045, 0x0053, 0x0054, 0x0055,
+	0x0040, 0x0050, 0x00D0, 0x00E9,
+	0x00F5, 0x00F4, 0x00F8, 0x00F9,
+	0x00B0, 0x00B1, 0x00B2, 0x00B3,
+	0x00B4, 0x00B5, 0x00B6, 0x00B7,
+	0x00B8, 0x00B9, 0x00BA, 0x00BB,
+};
+
+static const u16 lan865x_revb0_fixup_values[28] = {
+	0x9660, 0x00C0, 0x00FF, 0xFFFF,
+	0x0000, 0x00FF, 0xFFFF, 0x0000,
+	0x0002, 0x0002, 0x5F21, 0x9E50,
+	0x1CF8, 0xC020, 0x9B00, 0x4E53,
+	0x0103, 0x0910, 0x1D26, 0x002A,
+	0x0103, 0x070D, 0x1720, 0x0027,
+	0x0509, 0x0E13, 0x1C25, 0x002B,
+};
+
+static const u16 lan865x_revb0_fixup_cfg_regs[5] = {
+	0x0084, 0x008A, 0x00AD, 0x00AE, 0x00AF
+};
+
+/* Pulled from AN1760 describing 'indirect read'
+ *
+ * write_register(0x4, 0x00D8, addr)
+ * write_register(0x4, 0x00DA, 0x2)
+ * return (int8)(read_register(0x4, 0x00D9))
+ *
+ * 0x4 refers to memory map selector 4, which maps to MDIO_MMD_VEND2
+ */
+static int lan865x_revb0_indirect_read(struct phy_device *phydev, u16 addr)
+{
+	int ret;
+
+	ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LAN865X_REG_CFGPARAM_ADDR,
+			    addr);
+	if (ret)
+		return ret;
+
+	ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LAN865X_REG_CFGPARAM_CTRL,
+			    LAN865X_CFGPARAM_READ_ENABLE);
+	if (ret)
+		return ret;
+
+	return phy_read_mmd(phydev, MDIO_MMD_VEND2, LAN865X_REG_CFGPARAM_DATA);
+}
+
+/* This is pulled straigt from AN1760 from 'calulation of offset 1' &
+ * 'calculation of offset 2'
+ */
+static int lan865x_generate_cfg_offsets(struct phy_device *phydev, s8 offsets[2])
+{
+	const u16 fixup_regs[2] = {0x0004, 0x0008};
+	int ret;
+
+	for (int i = 0; i < ARRAY_SIZE(fixup_regs); i++) {
+		ret = lan865x_revb0_indirect_read(phydev, fixup_regs[i]);
+		if (ret < 0)
+			return ret;
+		if (ret & BIT(4))
+			offsets[i] = ret | 0xE0;
+		else
+			offsets[i] = ret;
+	}
+
+	return 0;
+}
+
+static int lan865x_read_cfg_params(struct phy_device *phydev, u16 cfg_params[])
+{
+	int ret;
+
+	for (int i = 0; i < ARRAY_SIZE(lan865x_revb0_fixup_cfg_regs); i++) {
+		ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+				   lan865x_revb0_fixup_cfg_regs[i]);
+		if (ret < 0)
+			return ret;
+		cfg_params[i] = (u16)ret;
+	}
+
+	return 0;
+}
+
+static int lan865x_write_cfg_params(struct phy_device *phydev, u16 cfg_params[])
+{
+	int ret;
+
+	for (int i = 0; i < ARRAY_SIZE(lan865x_revb0_fixup_cfg_regs); i++) {
+		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+				    lan865x_revb0_fixup_cfg_regs[i],
+				    cfg_params[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int lan865x_setup_cfgparam(struct phy_device *phydev)
+{
+	u16 cfg_results[5];
+	u16 cfg_params[ARRAY_SIZE(lan865x_revb0_fixup_cfg_regs)];
+	s8 offsets[2];
+	int ret;
+
+	ret = lan865x_generate_cfg_offsets(phydev, offsets);
+	if (ret)
+		return ret;
+
+	ret = lan865x_read_cfg_params(phydev, cfg_params);
+	if (ret)
+		return ret;
+
+	cfg_results[0] = (cfg_params[0] & 0x000F) |
+			  FIELD_PREP(GENMASK(15, 10), 9 + offsets[0]) |
+			  FIELD_PREP(GENMASK(15, 4), 14 + offsets[0]);
+	cfg_results[1] = (cfg_params[1] & 0x03FF) |
+			  FIELD_PREP(GENMASK(15, 10), 40 + offsets[1]);
+	cfg_results[2] = (cfg_params[2] & 0xC0C0) |
+			  FIELD_PREP(GENMASK(15, 8), 5 + offsets[0]) |
+			  (9 + offsets[0]);
+	cfg_results[3] = (cfg_params[3] & 0xC0C0) |
+			  FIELD_PREP(GENMASK(15, 8), 9 + offsets[0]) |
+			  (14 + offsets[0]);
+	cfg_results[4] = (cfg_params[4] & 0xC0C0) |
+			  FIELD_PREP(GENMASK(15, 8), 17 + offsets[0]) |
+			  (22 + offsets[0]);
+
+	return lan865x_write_cfg_params(phydev, cfg_results);
+}
+
+static int lan865x_revb0_config_init(struct phy_device *phydev)
+{
+	int ret;
+
+	/* Reference to AN1760
+	 * https://ww1.microchip.com/downloads/aemDocuments/documents/AIS/ProductDocuments/SupportingCollateral/AN-LAN8650-1-Configuration-60001760.pdf
+	 */
+	for (int i = 0; i < ARRAY_SIZE(lan865x_revb0_fixup_registers); i++) {
+		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+				    lan865x_revb0_fixup_registers[i],
+				    lan865x_revb0_fixup_values[i]);
+		if (ret)
+			return ret;
+	}
+	/* Function to calculate and write the configuration parameters in the
+	 * 0x0084, 0x008A, 0x00AD, 0x00AE and 0x00AF registers (from AN1760)
+	 */
+	ret = lan865x_setup_cfgparam(phydev);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
 static int lan867x_revb1_config_init(struct phy_device *phydev)
 {
 	/* HW quirk: Microchip states in the application note (AN1699) for the phy
@@ -105,7 +272,7 @@  static int lan867x_revb1_config_init(struct phy_device *phydev)
 	return 0;
 }
 
-static int lan867x_read_status(struct phy_device *phydev)
+static int lan86xx_read_status(struct phy_device *phydev)
 {
 	/* The phy has some limitations, namely:
 	 *  - always reports link up
@@ -126,17 +293,28 @@  static struct phy_driver microchip_t1s_driver[] = {
 		.name               = "LAN867X Rev.B1",
 		.features           = PHY_BASIC_T1S_P2MP_FEATURES,
 		.config_init        = lan867x_revb1_config_init,
-		.read_status        = lan867x_read_status,
+		.read_status        = lan86xx_read_status,
 		.get_plca_cfg	    = genphy_c45_plca_get_cfg,
 		.set_plca_cfg	    = genphy_c45_plca_set_cfg,
 		.get_plca_status    = genphy_c45_plca_get_status,
-	}
+	},
+	{
+		PHY_ID_MATCH_EXACT(PHY_ID_LAN865X_REVB0),
+		.name               = "LAN865X Rev.B0 Internal Phy",
+		.features           = PHY_BASIC_T1S_P2MP_FEATURES,
+		.config_init        = lan865x_revb0_config_init,
+		.read_status        = lan86xx_read_status,
+		.get_plca_cfg	    = genphy_c45_plca_get_cfg,
+		.set_plca_cfg	    = genphy_c45_plca_set_cfg,
+		.get_plca_status    = genphy_c45_plca_get_status,
+	},
 };
 
 module_phy_driver(microchip_t1s_driver);
 
 static struct mdio_device_id __maybe_unused tbl[] = {
 	{ PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVB1) },
+	{ PHY_ID_MATCH_EXACT(PHY_ID_LAN865X_REVB0) },
 	{ }
 };