mscc_miim.c 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
  2. /*
  3. * Copyright (c) 2018 Microsemi Corporation
  4. */
  5. #include <miiphy.h>
  6. #include <wait_bit.h>
  7. #include <linux/bitops.h>
  8. #include "mscc_miim.h"
  9. #define MIIM_STATUS 0x0
  10. #define MIIM_STAT_BUSY BIT(3)
  11. #define MIIM_CMD 0x8
  12. #define MIIM_CMD_SCAN BIT(0)
  13. #define MIIM_CMD_OPR_WRITE BIT(1)
  14. #define MIIM_CMD_OPR_READ BIT(2)
  15. #define MIIM_CMD_SINGLE_SCAN BIT(3)
  16. #define MIIM_CMD_WRDATA(x) ((x) << 4)
  17. #define MIIM_CMD_REGAD(x) ((x) << 20)
  18. #define MIIM_CMD_PHYAD(x) ((x) << 25)
  19. #define MIIM_CMD_VLD BIT(31)
  20. #define MIIM_DATA 0xC
  21. #define MIIM_DATA_ERROR (0x2 << 16)
  22. static int mscc_miim_wait_ready(struct mscc_miim_dev *miim)
  23. {
  24. return wait_for_bit_le32(miim->regs + MIIM_STATUS, MIIM_STAT_BUSY,
  25. false, 250, false);
  26. }
  27. int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg)
  28. {
  29. struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
  30. u32 val;
  31. int ret;
  32. ret = mscc_miim_wait_ready(miim);
  33. if (ret)
  34. goto out;
  35. writel(MIIM_CMD_VLD | MIIM_CMD_PHYAD(addr) |
  36. MIIM_CMD_REGAD(reg) | MIIM_CMD_OPR_READ,
  37. miim->regs + MIIM_CMD);
  38. ret = mscc_miim_wait_ready(miim);
  39. if (ret)
  40. goto out;
  41. val = readl(miim->regs + MIIM_DATA);
  42. if (val & MIIM_DATA_ERROR) {
  43. ret = -EIO;
  44. goto out;
  45. }
  46. ret = val & 0xFFFF;
  47. out:
  48. return ret;
  49. }
  50. int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg,
  51. u16 val)
  52. {
  53. struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
  54. int ret;
  55. ret = mscc_miim_wait_ready(miim);
  56. if (ret < 0)
  57. goto out;
  58. writel(MIIM_CMD_VLD | MIIM_CMD_PHYAD(addr) |
  59. MIIM_CMD_REGAD(reg) | MIIM_CMD_WRDATA(val) |
  60. MIIM_CMD_OPR_WRITE, miim->regs + MIIM_CMD);
  61. out:
  62. return ret;
  63. }
  64. struct mii_dev *mscc_mdiobus_init(struct mscc_miim_dev *miim, int *miim_count,
  65. phys_addr_t miim_base,
  66. unsigned long miim_size)
  67. {
  68. struct mii_dev *bus;
  69. bus = mdio_alloc();
  70. if (!bus)
  71. return NULL;
  72. *miim_count += 1;
  73. sprintf(bus->name, "miim-bus%d", *miim_count);
  74. miim[*miim_count].regs = ioremap(miim_base, miim_size);
  75. miim[*miim_count].miim_base = miim_base;
  76. miim[*miim_count].miim_size = miim_size;
  77. bus->priv = &miim[*miim_count];
  78. bus->read = mscc_miim_read;
  79. bus->write = mscc_miim_write;
  80. if (mdio_register(bus))
  81. return NULL;
  82. miim[*miim_count].bus = bus;
  83. return bus;
  84. }