LKML Archive on lore.kernel.org help / color / mirror / Atom feed
* [PATCH v2 0/2] add TJA1102 support @ 2020-03-09 7:40 Oleksij Rempel 2020-03-09 7:40 ` [PATCH v2 1/2] net: phy: tja11xx: " Oleksij Rempel 2020-03-09 7:40 ` [PATCH v2 2/2] net: phy: tja11xx: add delayed registration of TJA1102 PHY1 Oleksij Rempel 0 siblings, 2 replies; 8+ messages in thread From: Oleksij Rempel @ 2020-03-09 7:40 UTC (permalink / raw) To: Andrew Lunn, Florian Fainelli, Heiner Kallweit Cc: Oleksij Rempel, Pengutronix Kernel Team, linux-kernel, David S. Miller, netdev, Marek Vasut, David Jander changes v2: - use .match_phy_device - add irq support - add add delayed registration for PHY1 Oleksij Rempel (2): net: phy: tja11xx: add TJA1102 support net: phy: tja11xx: add delayed registration of TJA1102 PHY1 drivers/net/phy/nxp-tja11xx.c | 216 +++++++++++++++++++++++++++++++++- 1 file changed, 210 insertions(+), 6 deletions(-) -- 2.25.1 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 1/2] net: phy: tja11xx: add TJA1102 support 2020-03-09 7:40 [PATCH v2 0/2] add TJA1102 support Oleksij Rempel @ 2020-03-09 7:40 ` Oleksij Rempel 2020-03-09 17:14 ` Andrew Lunn 2020-03-09 19:45 ` Heiner Kallweit 2020-03-09 7:40 ` [PATCH v2 2/2] net: phy: tja11xx: add delayed registration of TJA1102 PHY1 Oleksij Rempel 1 sibling, 2 replies; 8+ messages in thread From: Oleksij Rempel @ 2020-03-09 7:40 UTC (permalink / raw) To: Andrew Lunn, Florian Fainelli, Heiner Kallweit Cc: Oleksij Rempel, Pengutronix Kernel Team, linux-kernel, David S. Miller, netdev, Marek Vasut, David Jander TJA1102 is an dual T1 PHY chip. Both PHYs are separately addressable. PHY 0 can be identified by PHY ID. PHY 1 has no PHY ID and can be configured in device tree by setting compatible = "ethernet-phy-id0180.dc81". PHY 1 has less supported registers and functionality. For current driver it will affect only the HWMON support. Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de> --- drivers/net/phy/nxp-tja11xx.c | 102 ++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/drivers/net/phy/nxp-tja11xx.c b/drivers/net/phy/nxp-tja11xx.c index b705d0bd798b..f79c9aa051ed 100644 --- a/drivers/net/phy/nxp-tja11xx.c +++ b/drivers/net/phy/nxp-tja11xx.c @@ -15,6 +15,7 @@ #define PHY_ID_MASK 0xfffffff0 #define PHY_ID_TJA1100 0x0180dc40 #define PHY_ID_TJA1101 0x0180dd00 +#define PHY_ID_TJA1102 0x0180dc80 #define MII_ECTRL 17 #define MII_ECTRL_LINK_CONTROL BIT(15) @@ -40,6 +41,10 @@ #define MII_INTSRC_TEMP_ERR BIT(1) #define MII_INTSRC_UV_ERR BIT(3) +#define MII_INTEN 22 +#define MII_INTEN_LINK_FAIL BIT(10) +#define MII_INTEN_LINK_UP BIT(9) + #define MII_COMMSTAT 23 #define MII_COMMSTAT_LINK_UP BIT(15) @@ -190,6 +195,7 @@ static int tja11xx_config_init(struct phy_device *phydev) return ret; break; case PHY_ID_TJA1101: + case PHY_ID_TJA1102: ret = phy_set_bits(phydev, MII_COMMCFG, MII_COMMCFG_AUTO_OP); if (ret) return ret; @@ -354,6 +360,66 @@ static int tja11xx_probe(struct phy_device *phydev) return PTR_ERR_OR_ZERO(priv->hwmon_dev); } +static int tja1102_match_phy_device(struct phy_device *phydev, bool port0) +{ + int ret; + + if ((phydev->phy_id & PHY_ID_MASK) != PHY_ID_TJA1102) + return 0; + + ret = phy_read(phydev, MII_PHYSID2); + if (ret < 0) + return ret; + + /* TJA1102 Port 1 has phyid 0 and doesn't support temperature + * and undervoltage alarms. + */ + if (port0) + return ret ? 1 : 0; + + return !ret; +} + +static int tja1102_p0_match_phy_device(struct phy_device *phydev) +{ + return tja1102_match_phy_device(phydev, true); +} + +static int tja1102_p1_match_phy_device(struct phy_device *phydev) +{ + return tja1102_match_phy_device(phydev, false); +} + +static int tja11xx_ack_interrupt(struct phy_device *phydev) +{ + int ret; + + ret = phy_read(phydev, MII_INTSRC); + + return (ret < 0) ? ret : 0; +} + +static int tja11xx_config_intr(struct phy_device *phydev) +{ + int value; + int ret; + + value = phy_read(phydev, MII_INTEN); + if (value < 0) + return value; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + value |= MII_INTEN_LINK_FAIL; + value |= MII_INTEN_LINK_UP; + + ret = phy_write(phydev, MII_INTEN, value); + } + else + ret = phy_write(phydev, MII_INTEN, 0); + + return ret; +} + static struct phy_driver tja11xx_driver[] = { { PHY_ID_MATCH_MODEL(PHY_ID_TJA1100), @@ -385,6 +451,41 @@ static struct phy_driver tja11xx_driver[] = { .get_sset_count = tja11xx_get_sset_count, .get_strings = tja11xx_get_strings, .get_stats = tja11xx_get_stats, + }, { + .name = "NXP TJA1102 Port 0", + .features = PHY_BASIC_T1_FEATURES, + .probe = tja11xx_probe, + .soft_reset = tja11xx_soft_reset, + .config_init = tja11xx_config_init, + .read_status = tja11xx_read_status, + .match_phy_device = tja1102_p0_match_phy_device, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + /* Statistics */ + .get_sset_count = tja11xx_get_sset_count, + .get_strings = tja11xx_get_strings, + .get_stats = tja11xx_get_stats, + .ack_interrupt = tja11xx_ack_interrupt, + .config_intr = tja11xx_config_intr, + + }, { + .name = "NXP TJA1102 Port 1", + .features = PHY_BASIC_T1_FEATURES, + /* currently no probe for Port 1 is need */ + .soft_reset = tja11xx_soft_reset, + .config_init = tja11xx_config_init, + .read_status = tja11xx_read_status, + .match_phy_device = tja1102_p1_match_phy_device, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + /* Statistics */ + .get_sset_count = tja11xx_get_sset_count, + .get_strings = tja11xx_get_strings, + .get_stats = tja11xx_get_stats, + .ack_interrupt = tja11xx_ack_interrupt, + .config_intr = tja11xx_config_intr, } }; @@ -393,6 +494,7 @@ module_phy_driver(tja11xx_driver); static struct mdio_device_id __maybe_unused tja11xx_tbl[] = { { PHY_ID_MATCH_MODEL(PHY_ID_TJA1100) }, { PHY_ID_MATCH_MODEL(PHY_ID_TJA1101) }, + { PHY_ID_MATCH_MODEL(PHY_ID_TJA1102) }, { } }; -- 2.25.1 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 1/2] net: phy: tja11xx: add TJA1102 support 2020-03-09 7:40 ` [PATCH v2 1/2] net: phy: tja11xx: " Oleksij Rempel @ 2020-03-09 17:14 ` Andrew Lunn 2020-03-09 19:45 ` Heiner Kallweit 1 sibling, 0 replies; 8+ messages in thread From: Andrew Lunn @ 2020-03-09 17:14 UTC (permalink / raw) To: Oleksij Rempel Cc: Florian Fainelli, Heiner Kallweit, Pengutronix Kernel Team, linux-kernel, David S. Miller, netdev, Marek Vasut, David Jander On Mon, Mar 09, 2020 at 08:40:43AM +0100, Oleksij Rempel wrote: > TJA1102 is an dual T1 PHY chip. Both PHYs are separately addressable. > PHY 0 can be identified by PHY ID. PHY 1 has no PHY ID and can be > configured in device tree by setting compatible = "ethernet-phy-id0180.dc81". Hi Oleksij Given what the second patch does, there is no need to set the compatible. So please reword this. Andrew ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 1/2] net: phy: tja11xx: add TJA1102 support 2020-03-09 7:40 ` [PATCH v2 1/2] net: phy: tja11xx: " Oleksij Rempel 2020-03-09 17:14 ` Andrew Lunn @ 2020-03-09 19:45 ` Heiner Kallweit 2020-03-11 13:12 ` Oleksij Rempel 1 sibling, 1 reply; 8+ messages in thread From: Heiner Kallweit @ 2020-03-09 19:45 UTC (permalink / raw) To: Oleksij Rempel, Andrew Lunn, Florian Fainelli Cc: Pengutronix Kernel Team, linux-kernel, David S. Miller, netdev, Marek Vasut, David Jander On 09.03.2020 08:40, Oleksij Rempel wrote: > TJA1102 is an dual T1 PHY chip. Both PHYs are separately addressable. > PHY 0 can be identified by PHY ID. PHY 1 has no PHY ID and can be > configured in device tree by setting compatible = "ethernet-phy-id0180.dc81". > > PHY 1 has less supported registers and functionality. For current driver > it will affect only the HWMON support. > > Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de> > --- > drivers/net/phy/nxp-tja11xx.c | 102 ++++++++++++++++++++++++++++++++++ > 1 file changed, 102 insertions(+) > > diff --git a/drivers/net/phy/nxp-tja11xx.c b/drivers/net/phy/nxp-tja11xx.c > index b705d0bd798b..f79c9aa051ed 100644 > --- a/drivers/net/phy/nxp-tja11xx.c > +++ b/drivers/net/phy/nxp-tja11xx.c > @@ -15,6 +15,7 @@ > #define PHY_ID_MASK 0xfffffff0 > #define PHY_ID_TJA1100 0x0180dc40 > #define PHY_ID_TJA1101 0x0180dd00 > +#define PHY_ID_TJA1102 0x0180dc80 > > #define MII_ECTRL 17 > #define MII_ECTRL_LINK_CONTROL BIT(15) > @@ -40,6 +41,10 @@ > #define MII_INTSRC_TEMP_ERR BIT(1) > #define MII_INTSRC_UV_ERR BIT(3) > > +#define MII_INTEN 22 > +#define MII_INTEN_LINK_FAIL BIT(10) > +#define MII_INTEN_LINK_UP BIT(9) > + > #define MII_COMMSTAT 23 > #define MII_COMMSTAT_LINK_UP BIT(15) > > @@ -190,6 +195,7 @@ static int tja11xx_config_init(struct phy_device *phydev) > return ret; > break; > case PHY_ID_TJA1101: > + case PHY_ID_TJA1102: > ret = phy_set_bits(phydev, MII_COMMCFG, MII_COMMCFG_AUTO_OP); > if (ret) > return ret; > @@ -354,6 +360,66 @@ static int tja11xx_probe(struct phy_device *phydev) > return PTR_ERR_OR_ZERO(priv->hwmon_dev); > } > > +static int tja1102_match_phy_device(struct phy_device *phydev, bool port0) > +{ > + int ret; > + > + if ((phydev->phy_id & PHY_ID_MASK) != PHY_ID_TJA1102) For port 1 you rely on DT forcing the appropriate phy_id (else it would be 0 and port 1 wouldn't be matched). This is worth a describing comment. > + return 0; > + > + ret = phy_read(phydev, MII_PHYSID2); > + if (ret < 0) > + return ret; > + > + /* TJA1102 Port 1 has phyid 0 and doesn't support temperature > + * and undervoltage alarms. > + */ > + if (port0) > + return ret ? 1 : 0; > + > + return !ret; > +} > + > +static int tja1102_p0_match_phy_device(struct phy_device *phydev) > +{ > + return tja1102_match_phy_device(phydev, true); > +} > + > +static int tja1102_p1_match_phy_device(struct phy_device *phydev) > +{ > + return tja1102_match_phy_device(phydev, false); > +} > + > +static int tja11xx_ack_interrupt(struct phy_device *phydev) > +{ > + int ret; > + > + ret = phy_read(phydev, MII_INTSRC); > + > + return (ret < 0) ? ret : 0; > +} > + > +static int tja11xx_config_intr(struct phy_device *phydev) > +{ > + int value; > + int ret; > + > + value = phy_read(phydev, MII_INTEN); > + if (value < 0) > + return value; > + > + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { > + value |= MII_INTEN_LINK_FAIL; > + value |= MII_INTEN_LINK_UP; > + This may leave unwanted interrupt sources active. Why not simply setting a fixed value like in the else clause? > + ret = phy_write(phydev, MII_INTEN, value); > + } > + else Kernel style: Closing brace and else belong to one line. And the else clause needs braces too. checkpatch.pl should complain here. > + ret = phy_write(phydev, MII_INTEN, 0); > + > + return ret; > +} > + > static struct phy_driver tja11xx_driver[] = { > { > PHY_ID_MATCH_MODEL(PHY_ID_TJA1100), > @@ -385,6 +451,41 @@ static struct phy_driver tja11xx_driver[] = { > .get_sset_count = tja11xx_get_sset_count, > .get_strings = tja11xx_get_strings, > .get_stats = tja11xx_get_stats, > + }, { > + .name = "NXP TJA1102 Port 0", > + .features = PHY_BASIC_T1_FEATURES, > + .probe = tja11xx_probe, > + .soft_reset = tja11xx_soft_reset, > + .config_init = tja11xx_config_init, > + .read_status = tja11xx_read_status, > + .match_phy_device = tja1102_p0_match_phy_device, > + .suspend = genphy_suspend, > + .resume = genphy_resume, > + .set_loopback = genphy_loopback, > + /* Statistics */ > + .get_sset_count = tja11xx_get_sset_count, > + .get_strings = tja11xx_get_strings, > + .get_stats = tja11xx_get_stats, > + .ack_interrupt = tja11xx_ack_interrupt, > + .config_intr = tja11xx_config_intr, > + > + }, { > + .name = "NXP TJA1102 Port 1", > + .features = PHY_BASIC_T1_FEATURES, > + /* currently no probe for Port 1 is need */ > + .soft_reset = tja11xx_soft_reset, > + .config_init = tja11xx_config_init, > + .read_status = tja11xx_read_status, > + .match_phy_device = tja1102_p1_match_phy_device, > + .suspend = genphy_suspend, > + .resume = genphy_resume, > + .set_loopback = genphy_loopback, > + /* Statistics */ > + .get_sset_count = tja11xx_get_sset_count, > + .get_strings = tja11xx_get_strings, > + .get_stats = tja11xx_get_stats, > + .ack_interrupt = tja11xx_ack_interrupt, > + .config_intr = tja11xx_config_intr, > } > }; > > @@ -393,6 +494,7 @@ module_phy_driver(tja11xx_driver); > static struct mdio_device_id __maybe_unused tja11xx_tbl[] = { > { PHY_ID_MATCH_MODEL(PHY_ID_TJA1100) }, > { PHY_ID_MATCH_MODEL(PHY_ID_TJA1101) }, > + { PHY_ID_MATCH_MODEL(PHY_ID_TJA1102) }, > { } > }; > > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 1/2] net: phy: tja11xx: add TJA1102 support 2020-03-09 19:45 ` Heiner Kallweit @ 2020-03-11 13:12 ` Oleksij Rempel 0 siblings, 0 replies; 8+ messages in thread From: Oleksij Rempel @ 2020-03-11 13:12 UTC (permalink / raw) To: Heiner Kallweit Cc: Andrew Lunn, Florian Fainelli, Marek Vasut, netdev, linux-kernel, Pengutronix Kernel Team, David Jander, David S. Miller [-- Attachment #1: Type: text/plain, Size: 6286 bytes --] On Mon, Mar 09, 2020 at 08:45:50PM +0100, Heiner Kallweit wrote: > On 09.03.2020 08:40, Oleksij Rempel wrote: > > TJA1102 is an dual T1 PHY chip. Both PHYs are separately addressable. > > PHY 0 can be identified by PHY ID. PHY 1 has no PHY ID and can be > > configured in device tree by setting compatible = "ethernet-phy-id0180.dc81". > > > > PHY 1 has less supported registers and functionality. For current driver > > it will affect only the HWMON support. > > > > Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de> > > --- > > drivers/net/phy/nxp-tja11xx.c | 102 ++++++++++++++++++++++++++++++++++ > > 1 file changed, 102 insertions(+) > > > > diff --git a/drivers/net/phy/nxp-tja11xx.c b/drivers/net/phy/nxp-tja11xx.c > > index b705d0bd798b..f79c9aa051ed 100644 > > --- a/drivers/net/phy/nxp-tja11xx.c > > +++ b/drivers/net/phy/nxp-tja11xx.c > > @@ -15,6 +15,7 @@ > > #define PHY_ID_MASK 0xfffffff0 > > #define PHY_ID_TJA1100 0x0180dc40 > > #define PHY_ID_TJA1101 0x0180dd00 > > +#define PHY_ID_TJA1102 0x0180dc80 > > > > #define MII_ECTRL 17 > > #define MII_ECTRL_LINK_CONTROL BIT(15) > > @@ -40,6 +41,10 @@ > > #define MII_INTSRC_TEMP_ERR BIT(1) > > #define MII_INTSRC_UV_ERR BIT(3) > > > > +#define MII_INTEN 22 > > +#define MII_INTEN_LINK_FAIL BIT(10) > > +#define MII_INTEN_LINK_UP BIT(9) > > + > > #define MII_COMMSTAT 23 > > #define MII_COMMSTAT_LINK_UP BIT(15) > > > > @@ -190,6 +195,7 @@ static int tja11xx_config_init(struct phy_device *phydev) > > return ret; > > break; > > case PHY_ID_TJA1101: > > + case PHY_ID_TJA1102: > > ret = phy_set_bits(phydev, MII_COMMCFG, MII_COMMCFG_AUTO_OP); > > if (ret) > > return ret; > > @@ -354,6 +360,66 @@ static int tja11xx_probe(struct phy_device *phydev) > > return PTR_ERR_OR_ZERO(priv->hwmon_dev); > > } > > > > +static int tja1102_match_phy_device(struct phy_device *phydev, bool port0) > > +{ > > + int ret; > > + > > + if ((phydev->phy_id & PHY_ID_MASK) != PHY_ID_TJA1102) > > For port 1 you rely on DT forcing the appropriate phy_id > (else it would be 0 and port 1 wouldn't be matched). > This is worth a describing comment. There is a second patch which will do it automatically, no need to force the PHY ID in the devicetree. > > + return 0; > > + > > + ret = phy_read(phydev, MII_PHYSID2); > > + if (ret < 0) > > + return ret; > > + > > + /* TJA1102 Port 1 has phyid 0 and doesn't support temperature > > + * and undervoltage alarms. > > + */ > > + if (port0) > > + return ret ? 1 : 0; > > + > > + return !ret; > > +} > > + > > +static int tja1102_p0_match_phy_device(struct phy_device *phydev) > > +{ > > + return tja1102_match_phy_device(phydev, true); > > +} > > + > > +static int tja1102_p1_match_phy_device(struct phy_device *phydev) > > +{ > > + return tja1102_match_phy_device(phydev, false); > > +} > > + > > +static int tja11xx_ack_interrupt(struct phy_device *phydev) > > +{ > > + int ret; > > + > > + ret = phy_read(phydev, MII_INTSRC); > > + > > + return (ret < 0) ? ret : 0; > > +} > > + > > +static int tja11xx_config_intr(struct phy_device *phydev) > > +{ > > + int value; > > + int ret; > > + > > + value = phy_read(phydev, MII_INTEN); > > + if (value < 0) > > + return value; > > + > > + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { > > + value |= MII_INTEN_LINK_FAIL; > > + value |= MII_INTEN_LINK_UP; > > + > > This may leave unwanted interrupt sources active. Why not > simply setting a fixed value like in the else clause? done > > + ret = phy_write(phydev, MII_INTEN, value); > > + } > > + else > > Kernel style: > Closing brace and else belong to one line. And the else clause > needs braces too. checkpatch.pl should complain here. done > > + ret = phy_write(phydev, MII_INTEN, 0); > > + > > + return ret; > > +} > > + > > static struct phy_driver tja11xx_driver[] = { > > { > > PHY_ID_MATCH_MODEL(PHY_ID_TJA1100), > > @@ -385,6 +451,41 @@ static struct phy_driver tja11xx_driver[] = { > > .get_sset_count = tja11xx_get_sset_count, > > .get_strings = tja11xx_get_strings, > > .get_stats = tja11xx_get_stats, > > + }, { > > + .name = "NXP TJA1102 Port 0", > > + .features = PHY_BASIC_T1_FEATURES, > > + .probe = tja11xx_probe, > > + .soft_reset = tja11xx_soft_reset, > > + .config_init = tja11xx_config_init, > > + .read_status = tja11xx_read_status, > > + .match_phy_device = tja1102_p0_match_phy_device, > > + .suspend = genphy_suspend, > > + .resume = genphy_resume, > > + .set_loopback = genphy_loopback, > > + /* Statistics */ > > + .get_sset_count = tja11xx_get_sset_count, > > + .get_strings = tja11xx_get_strings, > > + .get_stats = tja11xx_get_stats, > > + .ack_interrupt = tja11xx_ack_interrupt, > > + .config_intr = tja11xx_config_intr, > > + > > + }, { > > + .name = "NXP TJA1102 Port 1", > > + .features = PHY_BASIC_T1_FEATURES, > > + /* currently no probe for Port 1 is need */ > > + .soft_reset = tja11xx_soft_reset, > > + .config_init = tja11xx_config_init, > > + .read_status = tja11xx_read_status, > > + .match_phy_device = tja1102_p1_match_phy_device, > > + .suspend = genphy_suspend, > > + .resume = genphy_resume, > > + .set_loopback = genphy_loopback, > > + /* Statistics */ > > + .get_sset_count = tja11xx_get_sset_count, > > + .get_strings = tja11xx_get_strings, > > + .get_stats = tja11xx_get_stats, > > + .ack_interrupt = tja11xx_ack_interrupt, > > + .config_intr = tja11xx_config_intr, > > } > > }; > > > > @@ -393,6 +494,7 @@ module_phy_driver(tja11xx_driver); > > static struct mdio_device_id __maybe_unused tja11xx_tbl[] = { > > { PHY_ID_MATCH_MODEL(PHY_ID_TJA1100) }, > > { PHY_ID_MATCH_MODEL(PHY_ID_TJA1101) }, > > + { PHY_ID_MATCH_MODEL(PHY_ID_TJA1102) }, > > { } > > }; > > > > > > > -- Pengutronix e.K. | | Steuerwalder Str. 21 | http://www.pengutronix.de/ | 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 2/2] net: phy: tja11xx: add delayed registration of TJA1102 PHY1 2020-03-09 7:40 [PATCH v2 0/2] add TJA1102 support Oleksij Rempel 2020-03-09 7:40 ` [PATCH v2 1/2] net: phy: tja11xx: " Oleksij Rempel @ 2020-03-09 7:40 ` Oleksij Rempel 2020-03-09 16:58 ` David Miller 2020-03-09 17:28 ` Andrew Lunn 1 sibling, 2 replies; 8+ messages in thread From: Oleksij Rempel @ 2020-03-09 7:40 UTC (permalink / raw) To: Andrew Lunn, Florian Fainelli, Heiner Kallweit Cc: Oleksij Rempel, Pengutronix Kernel Team, linux-kernel, David S. Miller, netdev, Marek Vasut, David Jander TJA1102 is a dual PHY package with PHY0 having proper PHYID and PHY1 having no ID. On one hand it is possible to for PHY detection by compatible, on other hand we should be able to reset complete chip before PHY1 configured it, and we need to define dependencies for proper power management. We can solve it by defining PHY1 as child of PHY0: tja1102_phy0: ethernet-phy@4 { reg = <0x4>; interrupts-extended = <&gpio5 8 IRQ_TYPE_LEVEL_LOW>; reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>; reset-assert-us = <20>; reset-deassert-us = <2000>; tja1102_phy1: ethernet-phy@5 { reg = <0x5>; interrupts-extended = <&gpio5 8 IRQ_TYPE_LEVEL_LOW>; }; }; The PHY1 should be a subnode of PHY0 and registered only after PHY0 was completely reset and initialized. Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de> --- drivers/net/phy/nxp-tja11xx.c | 116 ++++++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/nxp-tja11xx.c b/drivers/net/phy/nxp-tja11xx.c index f79c9aa051ed..53e9e0aa9b5b 100644 --- a/drivers/net/phy/nxp-tja11xx.c +++ b/drivers/net/phy/nxp-tja11xx.c @@ -6,11 +6,14 @@ #include <linux/delay.h> #include <linux/ethtool.h> #include <linux/kernel.h> +#include <linux/mdio.h> #include <linux/mii.h> #include <linux/module.h> #include <linux/phy.h> #include <linux/hwmon.h> #include <linux/bitfield.h> +#include <linux/of_mdio.h> +#include <linux/of_irq.h> #define PHY_ID_MASK 0xfffffff0 #define PHY_ID_TJA1100 0x0180dc40 @@ -57,6 +60,8 @@ struct tja11xx_priv { char *hwmon_name; struct device *hwmon_dev; + struct phy_device *phydev; + struct work_struct phy_register_work; }; struct tja11xx_phy_stats { @@ -333,16 +338,12 @@ static const struct hwmon_chip_info tja11xx_hwmon_chip_info = { .info = tja11xx_hwmon_info, }; -static int tja11xx_probe(struct phy_device *phydev) +static int tja11xx_hwmon_register(struct phy_device *phydev, + struct tja11xx_priv *priv) { struct device *dev = &phydev->mdio.dev; - struct tja11xx_priv *priv; int i; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - priv->hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); if (!priv->hwmon_name) return -ENOMEM; @@ -360,6 +361,107 @@ static int tja11xx_probe(struct phy_device *phydev) return PTR_ERR_OR_ZERO(priv->hwmon_dev); } +static int tja11xx_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct tja11xx_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->phydev = phydev; + + return tja11xx_hwmon_register(phydev, priv); +} + +static void tja1102_p1_register(struct work_struct *work) +{ + struct tja11xx_priv *priv = container_of(work, struct tja11xx_priv, + phy_register_work); + + struct phy_device *phydev_phy0 = priv->phydev; + struct mii_bus *bus = phydev_phy0->mdio.bus; + struct device *dev = &phydev_phy0->mdio.dev; + struct device_node *np = dev->of_node; + struct device_node *child; + int ret; + + for_each_available_child_of_node(np, child) { + struct phy_device *phy; + int addr; + + addr = of_mdio_parse_addr(dev, child); + if (addr < 0) { + dev_err(dev, "Can't parse addr\n"); + continue; + } + + /* skip already registered PHYs */ + if (mdiobus_is_registered_device(bus, addr)) { + dev_err(dev, "device is already registred \n"); + continue; + } + + phy = phy_device_create(bus, addr, PHY_ID_TJA1102, + false, NULL); + if (IS_ERR(phy)) { + dev_err(dev, "Can't register Port : %i\n", addr); + continue; + } + + ret = of_irq_get(child, 0); + /* can we be deferred here? */ + if (ret > 0) { + phy->irq = ret; + bus->irq[addr] = ret; + } else { + phy->irq = bus->irq[addr]; + } + + /* overwrite parent phy_device_create() set parent to the + * mii_bus->dev + */ + phy->mdio.dev.parent = dev; + + /* Associate the OF node with the device structure so it + * can be looked up later */ + of_node_get(child); + phy->mdio.dev.of_node = child; + phy->mdio.dev.fwnode = of_fwnode_handle(child); + + /* All data is now stored in the phy struct; + * register it */ + ret = phy_device_register(phy); + if (ret) { + phy_device_free(phy); + of_node_put(child); + } + } +} + +static int tja1102_p0_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct tja11xx_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->phydev = phydev; + INIT_WORK(&priv->phy_register_work, tja1102_p1_register); + + ret = tja11xx_hwmon_register(phydev, priv); + if (ret) + return ret; + + schedule_work(&priv->phy_register_work); + + return 0; +} + static int tja1102_match_phy_device(struct phy_device *phydev, bool port0) { int ret; @@ -454,7 +556,7 @@ static struct phy_driver tja11xx_driver[] = { }, { .name = "NXP TJA1102 Port 0", .features = PHY_BASIC_T1_FEATURES, - .probe = tja11xx_probe, + .probe = tja1102_p0_probe, .soft_reset = tja11xx_soft_reset, .config_init = tja11xx_config_init, .read_status = tja11xx_read_status, -- 2.25.1 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 2/2] net: phy: tja11xx: add delayed registration of TJA1102 PHY1 2020-03-09 7:40 ` [PATCH v2 2/2] net: phy: tja11xx: add delayed registration of TJA1102 PHY1 Oleksij Rempel @ 2020-03-09 16:58 ` David Miller 2020-03-09 17:28 ` Andrew Lunn 1 sibling, 0 replies; 8+ messages in thread From: David Miller @ 2020-03-09 16:58 UTC (permalink / raw) To: o.rempel Cc: andrew, f.fainelli, hkallweit1, kernel, linux-kernel, netdev, marex, david From: Oleksij Rempel <o.rempel@pengutronix.de> Date: Mon, 9 Mar 2020 08:40:44 +0100 > + > +static void tja1102_p1_register(struct work_struct *work) > +{ > + struct tja11xx_priv *priv = container_of(work, struct tja11xx_priv, > + phy_register_work); > + > + struct phy_device *phydev_phy0 = priv->phydev; > + struct mii_bus *bus = phydev_phy0->mdio.bus; > + struct device *dev = &phydev_phy0->mdio.dev; Please fix the indentation of the 'bus' variable declaration. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 2/2] net: phy: tja11xx: add delayed registration of TJA1102 PHY1 2020-03-09 7:40 ` [PATCH v2 2/2] net: phy: tja11xx: add delayed registration of TJA1102 PHY1 Oleksij Rempel 2020-03-09 16:58 ` David Miller @ 2020-03-09 17:28 ` Andrew Lunn 1 sibling, 0 replies; 8+ messages in thread From: Andrew Lunn @ 2020-03-09 17:28 UTC (permalink / raw) To: Oleksij Rempel Cc: Florian Fainelli, Heiner Kallweit, Pengutronix Kernel Team, linux-kernel, David S. Miller, netdev, Marek Vasut, David Jander On Mon, Mar 09, 2020 at 08:40:44AM +0100, Oleksij Rempel wrote: > TJA1102 is a dual PHY package with PHY0 having proper PHYID and PHY1 > having no ID. On one hand it is possible to for PHY detection by > compatible, on other hand we should be able to reset complete chip > before PHY1 configured it, and we need to define dependencies for proper > power management. > > We can solve it by defining PHY1 as child of PHY0: > tja1102_phy0: ethernet-phy@4 { > reg = <0x4>; > > interrupts-extended = <&gpio5 8 IRQ_TYPE_LEVEL_LOW>; > > reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>; > reset-assert-us = <20>; > reset-deassert-us = <2000>; > > tja1102_phy1: ethernet-phy@5 { > reg = <0x5>; > > interrupts-extended = <&gpio5 8 IRQ_TYPE_LEVEL_LOW>; > }; > }; > > The PHY1 should be a subnode of PHY0 and registered only after PHY0 was > completely reset and initialized. Hi Oleksij Please add a binding document for this. > +static void tja1102_p1_register(struct work_struct *work) > +{ > + struct tja11xx_priv *priv = container_of(work, struct tja11xx_priv, > + phy_register_work); > + > + struct phy_device *phydev_phy0 = priv->phydev; > + struct mii_bus *bus = phydev_phy0->mdio.bus; > + struct device *dev = &phydev_phy0->mdio.dev; > + struct device_node *np = dev->of_node; > + struct device_node *child; > + int ret; > + > + for_each_available_child_of_node(np, child) { > + struct phy_device *phy; > + int addr; > + > + addr = of_mdio_parse_addr(dev, child); > + if (addr < 0) { > + dev_err(dev, "Can't parse addr\n"); > + continue; > + } It would also be good to check that addr is one more than the parent device. That seems to be a silicon constraint. > + > + /* skip already registered PHYs */ > + if (mdiobus_is_registered_device(bus, addr)) { > + dev_err(dev, "device is already registred \n"); > + continue; > + } > + > + phy = phy_device_create(bus, addr, PHY_ID_TJA1102, > + false, NULL); > + if (IS_ERR(phy)) { > + dev_err(dev, "Can't register Port : %i\n", addr); You are not registering at this step, just allocating. > + continue; > + } > + > + ret = of_irq_get(child, 0); > + /* can we be deferred here? */ Yes. commit 66bdede495c71da9c5ce18542976fae53642880b Author: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed Oct 18 13:54:03 2017 +0200 of_mdio: Fix broken PHY IRQ in case of probe deferral If an Ethernet PHY is initialized before the interrupt controller it is connected to, a message like the following is printed: irq: no irq domain found for /interrupt-controller@e61c0000 ! > + if (ret > 0) { > + phy->irq = ret; > + bus->irq[addr] = ret; > + } else { > + phy->irq = bus->irq[addr]; > + } > + > + /* overwrite parent phy_device_create() set parent to the > + * mii_bus->dev > + */ > + phy->mdio.dev.parent = dev; > + > + /* Associate the OF node with the device structure so it > + * can be looked up later */ > + of_node_get(child); > + phy->mdio.dev.of_node = child; > + phy->mdio.dev.fwnode = of_fwnode_handle(child); > + > + /* All data is now stored in the phy struct; > + * register it */ > + ret = phy_device_register(phy); > + if (ret) { > + phy_device_free(phy); > + of_node_put(child); > + } > + } > +} This is a lot of of_mdiobus_register_phy(). I think it would be better to refactor this code a bit, maybe make a helper out of of_mdiobus_register_phy() for all the shared code. Andrew ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2020-03-11 13:12 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-03-09 7:40 [PATCH v2 0/2] add TJA1102 support Oleksij Rempel 2020-03-09 7:40 ` [PATCH v2 1/2] net: phy: tja11xx: " Oleksij Rempel 2020-03-09 17:14 ` Andrew Lunn 2020-03-09 19:45 ` Heiner Kallweit 2020-03-11 13:12 ` Oleksij Rempel 2020-03-09 7:40 ` [PATCH v2 2/2] net: phy: tja11xx: add delayed registration of TJA1102 PHY1 Oleksij Rempel 2020-03-09 16:58 ` David Miller 2020-03-09 17:28 ` Andrew Lunn
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).