adm6996.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. /*
  2. * ADM6996 switch driver
  3. *
  4. * Copyright (c) 2008 Felix Fietkau <[email protected]>
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License v2 as published by the
  8. * Free Software Foundation
  9. */
  10. #include <linux/kernel.h>
  11. #include <linux/string.h>
  12. #include <linux/errno.h>
  13. #include <linux/unistd.h>
  14. #include <linux/slab.h>
  15. #include <linux/interrupt.h>
  16. #include <linux/init.h>
  17. #include <linux/delay.h>
  18. #include <linux/netdevice.h>
  19. #include <linux/etherdevice.h>
  20. #include <linux/skbuff.h>
  21. #include <linux/spinlock.h>
  22. #include <linux/mm.h>
  23. #include <linux/module.h>
  24. #include <linux/mii.h>
  25. #include <linux/ethtool.h>
  26. #include <linux/phy.h>
  27. #include <asm/io.h>
  28. #include <asm/irq.h>
  29. #include <asm/uaccess.h>
  30. #include "adm6996.h"
  31. MODULE_DESCRIPTION("Infineon ADM6996 Switch");
  32. MODULE_AUTHOR("Felix Fietkau");
  33. MODULE_LICENSE("GPL");
  34. struct adm6996_priv {
  35. /* use abstraction for regops, we want to add gpio support in the future */
  36. u16 (*read)(struct phy_device *phydev, enum admreg reg);
  37. void (*write)(struct phy_device *phydev, enum admreg reg, u16 val);
  38. };
  39. #define to_adm(_phy) ((struct adm6996_priv *) (_phy)->priv)
  40. static inline u16
  41. r16(struct phy_device *pdev, enum admreg reg)
  42. {
  43. return to_adm(pdev)->read(pdev, reg);
  44. }
  45. static inline void
  46. w16(struct phy_device *pdev, enum admreg reg, u16 val)
  47. {
  48. to_adm(pdev)->write(pdev, reg, val);
  49. }
  50. static u16
  51. adm6996_read_mii_reg(struct phy_device *phydev, enum admreg reg)
  52. {
  53. return phydev->bus->read(phydev->bus, PHYADDR(reg));
  54. }
  55. static void
  56. adm6996_write_mii_reg(struct phy_device *phydev, enum admreg reg, u16 val)
  57. {
  58. phydev->bus->write(phydev->bus, PHYADDR(reg), val);
  59. }
  60. static int adm6996_config_init(struct phy_device *pdev)
  61. {
  62. int i;
  63. printk("%s: ADM6996 PHY driver attached.\n", pdev->attached_dev->name);
  64. pdev->supported = ADVERTISED_100baseT_Full;
  65. pdev->advertising = ADVERTISED_100baseT_Full;
  66. /* initialize port and vlan settings */
  67. for (i = 0; i < ADM_PHY_PORTS; i++) {
  68. w16(pdev, adm_portcfg[i], ADM_PORTCFG_INIT |
  69. ADM_PORTCFG_PVID((i == ADM_WAN_PORT) ? 1 : 0));
  70. }
  71. w16(pdev, adm_portcfg[5], ADM_PORTCFG_CPU);
  72. /* reset all ports */
  73. for (i = 0; i < ADM_PHY_PORTS; i++) {
  74. w16(pdev, ADM_PHY_PORT(i), ADM_PHYCFG_INIT);
  75. }
  76. return 0;
  77. }
  78. static int adm6996_read_status(struct phy_device *phydev)
  79. {
  80. phydev->speed = SPEED_100;
  81. phydev->duplex = DUPLEX_FULL;
  82. phydev->state = PHY_UP;
  83. return 0;
  84. }
  85. static int adm6996_config_aneg(struct phy_device *phydev)
  86. {
  87. return 0;
  88. }
  89. static int adm6996_probe(struct phy_device *pdev)
  90. {
  91. struct adm6996_priv *priv;
  92. priv = kzalloc(sizeof(struct adm6996_priv), GFP_KERNEL);
  93. if (priv == NULL)
  94. return -ENOMEM;
  95. priv->read = adm6996_read_mii_reg;
  96. priv->write = adm6996_write_mii_reg;
  97. pdev->priv = priv;
  98. return 0;
  99. }
  100. static void adm6996_remove(struct phy_device *pdev)
  101. {
  102. kfree(pdev->priv);
  103. }
  104. static bool adm6996_detect(struct mii_bus *bus, int addr)
  105. {
  106. u16 reg;
  107. /* we only attach to phy id 0 */
  108. if (addr != 0)
  109. return false;
  110. /* look for the switch on the bus */
  111. reg = bus->read(bus, PHYADDR(ADM_SIG0)) & ADM_SIG0_MASK;
  112. if (reg != ADM_SIG0_VAL)
  113. return false;
  114. reg = bus->read(bus, PHYADDR(ADM_SIG1)) & ADM_SIG1_MASK;
  115. if (reg != ADM_SIG1_VAL)
  116. return false;
  117. return true;
  118. }
  119. static struct phy_driver adm6996_driver = {
  120. .name = "Infineon ADM6996",
  121. .features = PHY_BASIC_FEATURES,
  122. .detect = adm6996_detect,
  123. .probe = adm6996_probe,
  124. .remove = adm6996_remove,
  125. .config_init = &adm6996_config_init,
  126. .config_aneg = &adm6996_config_aneg,
  127. .read_status = &adm6996_read_status,
  128. .driver = { .owner = THIS_MODULE,},
  129. };
  130. static int __init adm6996_init(void)
  131. {
  132. return phy_driver_register(&adm6996_driver);
  133. }
  134. static void __exit adm6996_exit(void)
  135. {
  136. phy_driver_unregister(&adm6996_driver);
  137. }
  138. module_init(adm6996_init);
  139. module_exit(adm6996_exit);