7
7
8
8
#include <linux/bitfield.h>
9
9
#include <linux/iopoll.h>
10
+ #include <linux/mdio.h>
11
+ #include <linux/phy.h>
10
12
#include <linux/oa_tc6.h>
11
13
12
14
/* 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
+
13
19
/* Reset Control and Status Register */
14
20
#define OA_TC6_REG_RESET 0x0003
15
21
#define RESET_SWRESET BIT(0) /* Software Reset */
25
31
#define INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3)
26
32
#define INT_MASK0_TX_PROTOCOL_ERR_MASK BIT(0)
27
33
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
+
28
38
/* Control command header */
29
39
#define OA_TC6_CTRL_HEADER_DATA_NOT_CTRL BIT(31)
30
40
#define OA_TC6_CTRL_HEADER_WRITE_NOT_READ BIT(29)
33
43
#define OA_TC6_CTRL_HEADER_LENGTH GENMASK(7, 1)
34
44
#define OA_TC6_CTRL_HEADER_PARITY BIT(0)
35
45
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
+
36
55
#define OA_TC6_CTRL_HEADER_SIZE 4
37
56
#define OA_TC6_CTRL_REG_VALUE_SIZE 4
38
57
#define OA_TC6_CTRL_IGNORED_SIZE 4
46
65
47
66
/* Internal structure for MAC-PHY drivers */
48
67
struct oa_tc6 {
68
+ struct device * dev ;
69
+ struct net_device * netdev ;
70
+ struct phy_device * phydev ;
71
+ struct mii_bus * mdiobus ;
49
72
struct spi_device * spi ;
50
73
struct mutex spi_ctrl_lock ; /* Protects spi control transfer */
51
74
void * spi_ctrl_tx_buf ;
@@ -298,6 +321,191 @@ int oa_tc6_write_register(struct oa_tc6 *tc6, u32 address, u32 value)
298
321
}
299
322
EXPORT_SYMBOL_GPL (oa_tc6_write_register );
300
323
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
+
301
509
static int oa_tc6_read_status0 (struct oa_tc6 * tc6 )
302
510
{
303
511
u32 regval ;
@@ -354,11 +562,12 @@ static int oa_tc6_unmask_macphy_error_interrupts(struct oa_tc6 *tc6)
354
562
/**
355
563
* oa_tc6_init - allocates and initializes oa_tc6 structure.
356
564
* @spi: device with which data will be exchanged.
565
+ * @netdev: network device interface structure.
357
566
*
358
567
* Return: pointer reference to the oa_tc6 structure if the MAC-PHY
359
568
* initialization is successful otherwise NULL.
360
569
*/
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 )
362
571
{
363
572
struct oa_tc6 * tc6 ;
364
573
int ret ;
@@ -368,6 +577,8 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi)
368
577
return NULL ;
369
578
370
579
tc6 -> spi = spi ;
580
+ tc6 -> netdev = netdev ;
581
+ SET_NETDEV_DEV (netdev , & spi -> dev );
371
582
mutex_init (& tc6 -> spi_ctrl_lock );
372
583
373
584
/* Set the SPI controller to pump at realtime priority */
@@ -400,10 +611,27 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi)
400
611
return NULL ;
401
612
}
402
613
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
+
403
621
return tc6 ;
404
622
}
405
623
EXPORT_SYMBOL_GPL (oa_tc6_init );
406
624
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
+
407
635
MODULE_DESCRIPTION ("OPEN Alliance 10BASE‑T1x MAC‑PHY Serial Interface Lib" );
408
636
MODULE_AUTHOR (
"Parthiban Veerasooran <[email protected] >" );
409
637
MODULE_LICENSE ("GPL" );
0 commit comments