smi.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Marvell 88E6xxx System Management Interface (SMI) support
  4. *
  5. * Copyright (c) 2008 Marvell Semiconductor
  6. *
  7. * Copyright (c) 2019 Vivien Didelot <[email protected]>
  8. */
  9. #include "chip.h"
  10. #include "smi.h"
  11. /* The switch ADDR[4:1] configuration pins define the chip SMI device address
  12. * (ADDR[0] is always zero, thus only even SMI addresses can be strapped).
  13. *
  14. * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it
  15. * is the only device connected to the SMI master. In this mode it responds to
  16. * all 32 possible SMI addresses, and thus maps directly the internal devices.
  17. *
  18. * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing
  19. * multiple devices to share the SMI interface. In this mode it responds to only
  20. * 2 registers, used to indirectly access the internal SMI devices.
  21. *
  22. * Some chips use a different scheme: Only the ADDR4 pin is used for
  23. * configuration, and the device responds to 16 of the 32 SMI
  24. * addresses, allowing two to coexist on the same SMI interface.
  25. */
  26. static int mv88e6xxx_smi_direct_read(struct mv88e6xxx_chip *chip,
  27. int dev, int reg, u16 *data)
  28. {
  29. int ret;
  30. ret = mdiobus_read_nested(chip->bus, dev, reg);
  31. if (ret < 0)
  32. return ret;
  33. *data = ret & 0xffff;
  34. return 0;
  35. }
  36. static int mv88e6xxx_smi_direct_write(struct mv88e6xxx_chip *chip,
  37. int dev, int reg, u16 data)
  38. {
  39. int ret;
  40. ret = mdiobus_write_nested(chip->bus, dev, reg, data);
  41. if (ret < 0)
  42. return ret;
  43. return 0;
  44. }
  45. static int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip,
  46. int dev, int reg, int bit, int val)
  47. {
  48. const unsigned long timeout = jiffies + msecs_to_jiffies(50);
  49. u16 data;
  50. int err;
  51. int i;
  52. /* Even if the initial poll takes longer than 50ms, always do
  53. * at least one more attempt.
  54. */
  55. for (i = 0; time_before(jiffies, timeout) || (i < 2); i++) {
  56. err = mv88e6xxx_smi_direct_read(chip, dev, reg, &data);
  57. if (err)
  58. return err;
  59. if (!!(data & BIT(bit)) == !!val)
  60. return 0;
  61. if (i < 2)
  62. cpu_relax();
  63. else
  64. usleep_range(1000, 2000);
  65. }
  66. return -ETIMEDOUT;
  67. }
  68. static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_direct_ops = {
  69. .read = mv88e6xxx_smi_direct_read,
  70. .write = mv88e6xxx_smi_direct_write,
  71. };
  72. static int mv88e6xxx_smi_dual_direct_read(struct mv88e6xxx_chip *chip,
  73. int dev, int reg, u16 *data)
  74. {
  75. return mv88e6xxx_smi_direct_read(chip, chip->sw_addr + dev, reg, data);
  76. }
  77. static int mv88e6xxx_smi_dual_direct_write(struct mv88e6xxx_chip *chip,
  78. int dev, int reg, u16 data)
  79. {
  80. return mv88e6xxx_smi_direct_write(chip, chip->sw_addr + dev, reg, data);
  81. }
  82. static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_dual_direct_ops = {
  83. .read = mv88e6xxx_smi_dual_direct_read,
  84. .write = mv88e6xxx_smi_dual_direct_write,
  85. };
  86. /* Offset 0x00: SMI Command Register
  87. * Offset 0x01: SMI Data Register
  88. */
  89. static int mv88e6xxx_smi_indirect_read(struct mv88e6xxx_chip *chip,
  90. int dev, int reg, u16 *data)
  91. {
  92. int err;
  93. err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
  94. MV88E6XXX_SMI_CMD,
  95. MV88E6XXX_SMI_CMD_BUSY |
  96. MV88E6XXX_SMI_CMD_MODE_22 |
  97. MV88E6XXX_SMI_CMD_OP_22_READ |
  98. (dev << 5) | reg);
  99. if (err)
  100. return err;
  101. err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
  102. MV88E6XXX_SMI_CMD, 15, 0);
  103. if (err)
  104. return err;
  105. return mv88e6xxx_smi_direct_read(chip, chip->sw_addr,
  106. MV88E6XXX_SMI_DATA, data);
  107. }
  108. static int mv88e6xxx_smi_indirect_write(struct mv88e6xxx_chip *chip,
  109. int dev, int reg, u16 data)
  110. {
  111. int err;
  112. err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
  113. MV88E6XXX_SMI_DATA, data);
  114. if (err)
  115. return err;
  116. err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
  117. MV88E6XXX_SMI_CMD,
  118. MV88E6XXX_SMI_CMD_BUSY |
  119. MV88E6XXX_SMI_CMD_MODE_22 |
  120. MV88E6XXX_SMI_CMD_OP_22_WRITE |
  121. (dev << 5) | reg);
  122. if (err)
  123. return err;
  124. return mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
  125. MV88E6XXX_SMI_CMD, 15, 0);
  126. }
  127. static int mv88e6xxx_smi_indirect_init(struct mv88e6xxx_chip *chip)
  128. {
  129. /* Ensure that the chip starts out in the ready state. As both
  130. * reads and writes always ensure this on return, they can
  131. * safely depend on the chip not being busy on entry.
  132. */
  133. return mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
  134. MV88E6XXX_SMI_CMD, 15, 0);
  135. }
  136. static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_indirect_ops = {
  137. .read = mv88e6xxx_smi_indirect_read,
  138. .write = mv88e6xxx_smi_indirect_write,
  139. .init = mv88e6xxx_smi_indirect_init,
  140. };
  141. int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
  142. struct mii_bus *bus, int sw_addr)
  143. {
  144. if (chip->info->dual_chip)
  145. chip->smi_ops = &mv88e6xxx_smi_dual_direct_ops;
  146. else if (sw_addr == 0)
  147. chip->smi_ops = &mv88e6xxx_smi_direct_ops;
  148. else if (chip->info->multi_chip)
  149. chip->smi_ops = &mv88e6xxx_smi_indirect_ops;
  150. else
  151. return -EINVAL;
  152. chip->bus = bus;
  153. chip->sw_addr = sw_addr;
  154. if (chip->smi_ops->init)
  155. return chip->smi_ops->init(chip);
  156. return 0;
  157. }