Skip to content

Commit 8f9bf85

Browse files
ParthibanI17164kuba-moo
authored andcommitted
net: ethernet: oa_tc6: implement internal PHY initialization
Internal PHY is initialized as per the PHY register capability supported by the MAC-PHY. Direct PHY Register Access Capability indicates if PHY registers are directly accessible within the SPI register memory space. Indirect PHY Register Access Capability indicates if PHY registers are indirectly accessible through the MDIO/MDC registers MDIOACCn defined in OPEN Alliance specification. Currently the direct register access is only supported. Reviewed-by: Andrew Lunn <[email protected]> Signed-off-by: Parthiban Veerasooran <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 86c03a0 commit 8f9bf85

File tree

3 files changed

+233
-2
lines changed

3 files changed

+233
-2
lines changed

drivers/net/ethernet/oa_tc6.c

Lines changed: 229 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@
77

88
#include <linux/bitfield.h>
99
#include <linux/iopoll.h>
10+
#include <linux/mdio.h>
11+
#include <linux/phy.h>
1012
#include <linux/oa_tc6.h>
1113

1214
/* OPEN Alliance TC6 registers */
15+
/* Standard Capabilities Register */
16+
#define OA_TC6_REG_STDCAP 0x0002
17+
#define STDCAP_DIRECT_PHY_REG_ACCESS BIT(8)
18+
1319
/* Reset Control and Status Register */
1420
#define OA_TC6_REG_RESET 0x0003
1521
#define RESET_SWRESET BIT(0) /* Software Reset */
@@ -25,6 +31,10 @@
2531
#define INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3)
2632
#define INT_MASK0_TX_PROTOCOL_ERR_MASK BIT(0)
2733

34+
/* PHY Clause 22 registers base address and mask */
35+
#define OA_TC6_PHY_STD_REG_ADDR_BASE 0xFF00
36+
#define OA_TC6_PHY_STD_REG_ADDR_MASK 0x1F
37+
2838
/* Control command header */
2939
#define OA_TC6_CTRL_HEADER_DATA_NOT_CTRL BIT(31)
3040
#define OA_TC6_CTRL_HEADER_WRITE_NOT_READ BIT(29)
@@ -33,6 +43,15 @@
3343
#define OA_TC6_CTRL_HEADER_LENGTH GENMASK(7, 1)
3444
#define OA_TC6_CTRL_HEADER_PARITY BIT(0)
3545

46+
/* PHY – Clause 45 registers memory map selector (MMS) as per table 6 in the
47+
* OPEN Alliance specification.
48+
*/
49+
#define OA_TC6_PHY_C45_PCS_MMS2 2 /* MMD 3 */
50+
#define OA_TC6_PHY_C45_PMA_PMD_MMS3 3 /* MMD 1 */
51+
#define OA_TC6_PHY_C45_VS_PLCA_MMS4 4 /* MMD 31 */
52+
#define OA_TC6_PHY_C45_AUTO_NEG_MMS5 5 /* MMD 7 */
53+
#define OA_TC6_PHY_C45_POWER_UNIT_MMS6 6 /* MMD 13 */
54+
3655
#define OA_TC6_CTRL_HEADER_SIZE 4
3756
#define OA_TC6_CTRL_REG_VALUE_SIZE 4
3857
#define OA_TC6_CTRL_IGNORED_SIZE 4
@@ -46,6 +65,10 @@
4665

4766
/* Internal structure for MAC-PHY drivers */
4867
struct oa_tc6 {
68+
struct device *dev;
69+
struct net_device *netdev;
70+
struct phy_device *phydev;
71+
struct mii_bus *mdiobus;
4972
struct spi_device *spi;
5073
struct mutex spi_ctrl_lock; /* Protects spi control transfer */
5174
void *spi_ctrl_tx_buf;
@@ -298,6 +321,191 @@ int oa_tc6_write_register(struct oa_tc6 *tc6, u32 address, u32 value)
298321
}
299322
EXPORT_SYMBOL_GPL(oa_tc6_write_register);
300323

324+
static int oa_tc6_check_phy_reg_direct_access_capability(struct oa_tc6 *tc6)
325+
{
326+
u32 regval;
327+
int ret;
328+
329+
ret = oa_tc6_read_register(tc6, OA_TC6_REG_STDCAP, &regval);
330+
if (ret)
331+
return ret;
332+
333+
if (!(regval & STDCAP_DIRECT_PHY_REG_ACCESS))
334+
return -ENODEV;
335+
336+
return 0;
337+
}
338+
339+
static void oa_tc6_handle_link_change(struct net_device *netdev)
340+
{
341+
phy_print_status(netdev->phydev);
342+
}
343+
344+
static int oa_tc6_mdiobus_read(struct mii_bus *bus, int addr, int regnum)
345+
{
346+
struct oa_tc6 *tc6 = bus->priv;
347+
u32 regval;
348+
bool ret;
349+
350+
ret = oa_tc6_read_register(tc6, OA_TC6_PHY_STD_REG_ADDR_BASE |
351+
(regnum & OA_TC6_PHY_STD_REG_ADDR_MASK),
352+
&regval);
353+
if (ret)
354+
return ret;
355+
356+
return regval;
357+
}
358+
359+
static int oa_tc6_mdiobus_write(struct mii_bus *bus, int addr, int regnum,
360+
u16 val)
361+
{
362+
struct oa_tc6 *tc6 = bus->priv;
363+
364+
return oa_tc6_write_register(tc6, OA_TC6_PHY_STD_REG_ADDR_BASE |
365+
(regnum & OA_TC6_PHY_STD_REG_ADDR_MASK),
366+
val);
367+
}
368+
369+
static int oa_tc6_get_phy_c45_mms(int devnum)
370+
{
371+
switch (devnum) {
372+
case MDIO_MMD_PCS:
373+
return OA_TC6_PHY_C45_PCS_MMS2;
374+
case MDIO_MMD_PMAPMD:
375+
return OA_TC6_PHY_C45_PMA_PMD_MMS3;
376+
case MDIO_MMD_VEND2:
377+
return OA_TC6_PHY_C45_VS_PLCA_MMS4;
378+
case MDIO_MMD_AN:
379+
return OA_TC6_PHY_C45_AUTO_NEG_MMS5;
380+
case MDIO_MMD_POWER_UNIT:
381+
return OA_TC6_PHY_C45_POWER_UNIT_MMS6;
382+
default:
383+
return -EOPNOTSUPP;
384+
}
385+
}
386+
387+
static int oa_tc6_mdiobus_read_c45(struct mii_bus *bus, int addr, int devnum,
388+
int regnum)
389+
{
390+
struct oa_tc6 *tc6 = bus->priv;
391+
u32 regval;
392+
int ret;
393+
394+
ret = oa_tc6_get_phy_c45_mms(devnum);
395+
if (ret < 0)
396+
return ret;
397+
398+
ret = oa_tc6_read_register(tc6, (ret << 16) | regnum, &regval);
399+
if (ret)
400+
return ret;
401+
402+
return regval;
403+
}
404+
405+
static int oa_tc6_mdiobus_write_c45(struct mii_bus *bus, int addr, int devnum,
406+
int regnum, u16 val)
407+
{
408+
struct oa_tc6 *tc6 = bus->priv;
409+
int ret;
410+
411+
ret = oa_tc6_get_phy_c45_mms(devnum);
412+
if (ret < 0)
413+
return ret;
414+
415+
return oa_tc6_write_register(tc6, (ret << 16) | regnum, val);
416+
}
417+
418+
static int oa_tc6_mdiobus_register(struct oa_tc6 *tc6)
419+
{
420+
int ret;
421+
422+
tc6->mdiobus = mdiobus_alloc();
423+
if (!tc6->mdiobus) {
424+
netdev_err(tc6->netdev, "MDIO bus alloc failed\n");
425+
return -ENOMEM;
426+
}
427+
428+
tc6->mdiobus->priv = tc6;
429+
tc6->mdiobus->read = oa_tc6_mdiobus_read;
430+
tc6->mdiobus->write = oa_tc6_mdiobus_write;
431+
/* OPEN Alliance 10BASE-T1x compliance MAC-PHYs will have both C22 and
432+
* C45 registers space. If the PHY is discovered via C22 bus protocol it
433+
* assumes it uses C22 protocol and always uses C22 registers indirect
434+
* access to access C45 registers. This is because, we don't have a
435+
* clean separation between C22/C45 register space and C22/C45 MDIO bus
436+
* protocols. Resulting, PHY C45 registers direct access can't be used
437+
* which can save multiple SPI bus access. To support this feature, PHY
438+
* drivers can set .read_mmd/.write_mmd in the PHY driver to call
439+
* .read_c45/.write_c45. Ex: drivers/net/phy/microchip_t1s.c
440+
*/
441+
tc6->mdiobus->read_c45 = oa_tc6_mdiobus_read_c45;
442+
tc6->mdiobus->write_c45 = oa_tc6_mdiobus_write_c45;
443+
tc6->mdiobus->name = "oa-tc6-mdiobus";
444+
tc6->mdiobus->parent = tc6->dev;
445+
446+
snprintf(tc6->mdiobus->id, ARRAY_SIZE(tc6->mdiobus->id), "%s",
447+
dev_name(&tc6->spi->dev));
448+
449+
ret = mdiobus_register(tc6->mdiobus);
450+
if (ret) {
451+
netdev_err(tc6->netdev, "Could not register MDIO bus\n");
452+
mdiobus_free(tc6->mdiobus);
453+
return ret;
454+
}
455+
456+
return 0;
457+
}
458+
459+
static void oa_tc6_mdiobus_unregister(struct oa_tc6 *tc6)
460+
{
461+
mdiobus_unregister(tc6->mdiobus);
462+
mdiobus_free(tc6->mdiobus);
463+
}
464+
465+
static int oa_tc6_phy_init(struct oa_tc6 *tc6)
466+
{
467+
int ret;
468+
469+
ret = oa_tc6_check_phy_reg_direct_access_capability(tc6);
470+
if (ret) {
471+
netdev_err(tc6->netdev,
472+
"Direct PHY register access is not supported by the MAC-PHY\n");
473+
return ret;
474+
}
475+
476+
ret = oa_tc6_mdiobus_register(tc6);
477+
if (ret)
478+
return ret;
479+
480+
tc6->phydev = phy_find_first(tc6->mdiobus);
481+
if (!tc6->phydev) {
482+
netdev_err(tc6->netdev, "No PHY found\n");
483+
oa_tc6_mdiobus_unregister(tc6);
484+
return -ENODEV;
485+
}
486+
487+
tc6->phydev->is_internal = true;
488+
ret = phy_connect_direct(tc6->netdev, tc6->phydev,
489+
&oa_tc6_handle_link_change,
490+
PHY_INTERFACE_MODE_INTERNAL);
491+
if (ret) {
492+
netdev_err(tc6->netdev, "Can't attach PHY to %s\n",
493+
tc6->mdiobus->id);
494+
oa_tc6_mdiobus_unregister(tc6);
495+
return ret;
496+
}
497+
498+
phy_attached_info(tc6->netdev->phydev);
499+
500+
return 0;
501+
}
502+
503+
static void oa_tc6_phy_exit(struct oa_tc6 *tc6)
504+
{
505+
phy_disconnect(tc6->phydev);
506+
oa_tc6_mdiobus_unregister(tc6);
507+
}
508+
301509
static int oa_tc6_read_status0(struct oa_tc6 *tc6)
302510
{
303511
u32 regval;
@@ -354,11 +562,12 @@ static int oa_tc6_unmask_macphy_error_interrupts(struct oa_tc6 *tc6)
354562
/**
355563
* oa_tc6_init - allocates and initializes oa_tc6 structure.
356564
* @spi: device with which data will be exchanged.
565+
* @netdev: network device interface structure.
357566
*
358567
* Return: pointer reference to the oa_tc6 structure if the MAC-PHY
359568
* initialization is successful otherwise NULL.
360569
*/
361-
struct oa_tc6 *oa_tc6_init(struct spi_device *spi)
570+
struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev)
362571
{
363572
struct oa_tc6 *tc6;
364573
int ret;
@@ -368,6 +577,8 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi)
368577
return NULL;
369578

370579
tc6->spi = spi;
580+
tc6->netdev = netdev;
581+
SET_NETDEV_DEV(netdev, &spi->dev);
371582
mutex_init(&tc6->spi_ctrl_lock);
372583

373584
/* Set the SPI controller to pump at realtime priority */
@@ -400,10 +611,27 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi)
400611
return NULL;
401612
}
402613

614+
ret = oa_tc6_phy_init(tc6);
615+
if (ret) {
616+
dev_err(&tc6->spi->dev,
617+
"MAC internal PHY initialization failed: %d\n", ret);
618+
return NULL;
619+
}
620+
403621
return tc6;
404622
}
405623
EXPORT_SYMBOL_GPL(oa_tc6_init);
406624

625+
/**
626+
* oa_tc6_exit - exit function.
627+
* @tc6: oa_tc6 struct.
628+
*/
629+
void oa_tc6_exit(struct oa_tc6 *tc6)
630+
{
631+
oa_tc6_phy_exit(tc6);
632+
}
633+
EXPORT_SYMBOL_GPL(oa_tc6_exit);
634+
407635
MODULE_DESCRIPTION("OPEN Alliance 10BASE‑T1x MAC‑PHY Serial Interface Lib");
408636
MODULE_AUTHOR("Parthiban Veerasooran <[email protected]>");
409637
MODULE_LICENSE("GPL");

include/linux/oa_tc6.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
* Author: Parthiban Veerasooran <[email protected]>
88
*/
99

10+
#include <linux/etherdevice.h>
1011
#include <linux/spi/spi.h>
1112

1213
struct oa_tc6;
1314

14-
struct oa_tc6 *oa_tc6_init(struct spi_device *spi);
15+
struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev);
16+
void oa_tc6_exit(struct oa_tc6 *tc6);
1517
int oa_tc6_write_register(struct oa_tc6 *tc6, u32 address, u32 value);
1618
int oa_tc6_write_registers(struct oa_tc6 *tc6, u32 address, u32 value[],
1719
u8 length);

include/uapi/linux/mdio.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#define MDIO_MMD_DTEXS 5 /* DTE Extender Sublayer */
2424
#define MDIO_MMD_TC 6 /* Transmission Convergence */
2525
#define MDIO_MMD_AN 7 /* Auto-Negotiation */
26+
#define MDIO_MMD_POWER_UNIT 13 /* PHY Power Unit */
2627
#define MDIO_MMD_C22EXT 29 /* Clause 22 extension */
2728
#define MDIO_MMD_VEND1 30 /* Vendor specific 1 */
2829
#define MDIO_MMD_VEND2 31 /* Vendor specific 2 */

0 commit comments

Comments
 (0)