ksz_spi.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /*
  2. * Microchip KSZ series register access through SPI
  3. *
  4. * Copyright (C) 2017
  5. *
  6. * Permission to use, copy, modify, and/or distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. #include <asm/unaligned.h>
  19. #include <linux/delay.h>
  20. #include <linux/kernel.h>
  21. #include <linux/module.h>
  22. #include <linux/spi/spi.h>
  23. #include "ksz_priv.h"
  24. /* SPI frame opcodes */
  25. #define KS_SPIOP_RD 3
  26. #define KS_SPIOP_WR 2
  27. #define SPI_ADDR_SHIFT 24
  28. #define SPI_ADDR_MASK (BIT(SPI_ADDR_SHIFT) - 1)
  29. #define SPI_TURNAROUND_SHIFT 5
  30. static int ksz_spi_read_reg(struct spi_device *spi, u32 reg, u8 *val,
  31. unsigned int len)
  32. {
  33. u32 txbuf;
  34. int ret;
  35. txbuf = reg & SPI_ADDR_MASK;
  36. txbuf |= KS_SPIOP_RD << SPI_ADDR_SHIFT;
  37. txbuf <<= SPI_TURNAROUND_SHIFT;
  38. txbuf = cpu_to_be32(txbuf);
  39. ret = spi_write_then_read(spi, &txbuf, 4, val, len);
  40. return ret;
  41. }
  42. static int ksz_spi_read(struct ksz_device *dev, u32 reg, u8 *data,
  43. unsigned int len)
  44. {
  45. struct spi_device *spi = dev->priv;
  46. return ksz_spi_read_reg(spi, reg, data, len);
  47. }
  48. static int ksz_spi_read8(struct ksz_device *dev, u32 reg, u8 *val)
  49. {
  50. return ksz_spi_read(dev, reg, val, 1);
  51. }
  52. static int ksz_spi_read16(struct ksz_device *dev, u32 reg, u16 *val)
  53. {
  54. int ret = ksz_spi_read(dev, reg, (u8 *)val, 2);
  55. if (!ret)
  56. *val = be16_to_cpu(*val);
  57. return ret;
  58. }
  59. static int ksz_spi_read24(struct ksz_device *dev, u32 reg, u32 *val)
  60. {
  61. int ret;
  62. *val = 0;
  63. ret = ksz_spi_read(dev, reg, (u8 *)val, 3);
  64. if (!ret) {
  65. *val = be32_to_cpu(*val);
  66. /* convert to 24bit */
  67. *val >>= 8;
  68. }
  69. return ret;
  70. }
  71. static int ksz_spi_read32(struct ksz_device *dev, u32 reg, u32 *val)
  72. {
  73. int ret = ksz_spi_read(dev, reg, (u8 *)val, 4);
  74. if (!ret)
  75. *val = be32_to_cpu(*val);
  76. return ret;
  77. }
  78. static int ksz_spi_write_reg(struct spi_device *spi, u32 reg, u8 *val,
  79. unsigned int len)
  80. {
  81. u32 txbuf;
  82. u8 data[12];
  83. int i;
  84. txbuf = reg & SPI_ADDR_MASK;
  85. txbuf |= (KS_SPIOP_WR << SPI_ADDR_SHIFT);
  86. txbuf <<= SPI_TURNAROUND_SHIFT;
  87. txbuf = cpu_to_be32(txbuf);
  88. data[0] = txbuf & 0xFF;
  89. data[1] = (txbuf & 0xFF00) >> 8;
  90. data[2] = (txbuf & 0xFF0000) >> 16;
  91. data[3] = (txbuf & 0xFF000000) >> 24;
  92. for (i = 0; i < len; i++)
  93. data[i + 4] = val[i];
  94. return spi_write(spi, &data, 4 + len);
  95. }
  96. static int ksz_spi_write8(struct ksz_device *dev, u32 reg, u8 value)
  97. {
  98. struct spi_device *spi = dev->priv;
  99. return ksz_spi_write_reg(spi, reg, &value, 1);
  100. }
  101. static int ksz_spi_write16(struct ksz_device *dev, u32 reg, u16 value)
  102. {
  103. struct spi_device *spi = dev->priv;
  104. value = cpu_to_be16(value);
  105. return ksz_spi_write_reg(spi, reg, (u8 *)&value, 2);
  106. }
  107. static int ksz_spi_write24(struct ksz_device *dev, u32 reg, u32 value)
  108. {
  109. struct spi_device *spi = dev->priv;
  110. /* make it to big endian 24bit from MSB */
  111. value <<= 8;
  112. value = cpu_to_be32(value);
  113. return ksz_spi_write_reg(spi, reg, (u8 *)&value, 3);
  114. }
  115. static int ksz_spi_write32(struct ksz_device *dev, u32 reg, u32 value)
  116. {
  117. struct spi_device *spi = dev->priv;
  118. value = cpu_to_be32(value);
  119. return ksz_spi_write_reg(spi, reg, (u8 *)&value, 4);
  120. }
  121. static const struct ksz_io_ops ksz_spi_ops = {
  122. .read8 = ksz_spi_read8,
  123. .read16 = ksz_spi_read16,
  124. .read24 = ksz_spi_read24,
  125. .read32 = ksz_spi_read32,
  126. .write8 = ksz_spi_write8,
  127. .write16 = ksz_spi_write16,
  128. .write24 = ksz_spi_write24,
  129. .write32 = ksz_spi_write32,
  130. };
  131. static int ksz_spi_probe(struct spi_device *spi)
  132. {
  133. struct ksz_device *dev;
  134. int ret;
  135. dev = ksz_switch_alloc(&spi->dev, &ksz_spi_ops, spi);
  136. if (!dev)
  137. return -ENOMEM;
  138. if (spi->dev.platform_data)
  139. dev->pdata = spi->dev.platform_data;
  140. ret = ksz_switch_register(dev);
  141. if (ret)
  142. return ret;
  143. spi_set_drvdata(spi, dev);
  144. return 0;
  145. }
  146. static int ksz_spi_remove(struct spi_device *spi)
  147. {
  148. struct ksz_device *dev = spi_get_drvdata(spi);
  149. if (dev)
  150. ksz_switch_remove(dev);
  151. return 0;
  152. }
  153. static const struct of_device_id ksz_dt_ids[] = {
  154. { .compatible = "microchip,ksz9477" },
  155. { .compatible = "microchip,ksz9897" },
  156. {},
  157. };
  158. MODULE_DEVICE_TABLE(of, ksz_dt_ids);
  159. static struct spi_driver ksz_spi_driver = {
  160. .driver = {
  161. .name = "ksz9477-switch",
  162. .owner = THIS_MODULE,
  163. .of_match_table = of_match_ptr(ksz_dt_ids),
  164. },
  165. .probe = ksz_spi_probe,
  166. .remove = ksz_spi_remove,
  167. };
  168. module_spi_driver(ksz_spi_driver);
  169. MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>");
  170. MODULE_DESCRIPTION("Microchip KSZ Series Switch SPI access Driver");
  171. MODULE_LICENSE("GPL");