sl28vpd.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/crc8.h>
  3. #include <linux/etherdevice.h>
  4. #include <linux/nvmem-consumer.h>
  5. #include <linux/nvmem-provider.h>
  6. #include <linux/of.h>
  7. #include <uapi/linux/if_ether.h>
  8. #define SL28VPD_MAGIC 'V'
  9. struct sl28vpd_header {
  10. u8 magic;
  11. u8 version;
  12. } __packed;
  13. struct sl28vpd_v1 {
  14. struct sl28vpd_header header;
  15. char serial_number[15];
  16. u8 base_mac_address[ETH_ALEN];
  17. u8 crc8;
  18. } __packed;
  19. static int sl28vpd_mac_address_pp(void *priv, const char *id, int index,
  20. unsigned int offset, void *buf,
  21. size_t bytes)
  22. {
  23. if (bytes != ETH_ALEN)
  24. return -EINVAL;
  25. if (index < 0)
  26. return -EINVAL;
  27. if (!is_valid_ether_addr(buf))
  28. return -EINVAL;
  29. eth_addr_add(buf, index);
  30. return 0;
  31. }
  32. static const struct nvmem_cell_info sl28vpd_v1_entries[] = {
  33. {
  34. .name = "serial-number",
  35. .offset = offsetof(struct sl28vpd_v1, serial_number),
  36. .bytes = sizeof_field(struct sl28vpd_v1, serial_number),
  37. },
  38. {
  39. .name = "base-mac-address",
  40. .offset = offsetof(struct sl28vpd_v1, base_mac_address),
  41. .bytes = sizeof_field(struct sl28vpd_v1, base_mac_address),
  42. .read_post_process = sl28vpd_mac_address_pp,
  43. },
  44. };
  45. static int sl28vpd_v1_check_crc(struct device *dev, struct nvmem_device *nvmem)
  46. {
  47. struct sl28vpd_v1 data_v1;
  48. u8 table[CRC8_TABLE_SIZE];
  49. int ret;
  50. u8 crc;
  51. crc8_populate_msb(table, 0x07);
  52. ret = nvmem_device_read(nvmem, 0, sizeof(data_v1), &data_v1);
  53. if (ret < 0)
  54. return ret;
  55. else if (ret != sizeof(data_v1))
  56. return -EIO;
  57. crc = crc8(table, (void *)&data_v1, sizeof(data_v1) - 1, 0);
  58. if (crc != data_v1.crc8) {
  59. dev_err(dev,
  60. "Checksum is invalid (got %02x, expected %02x).\n",
  61. crc, data_v1.crc8);
  62. return -EINVAL;
  63. }
  64. return 0;
  65. }
  66. static int sl28vpd_add_cells(struct nvmem_layout *layout)
  67. {
  68. struct nvmem_device *nvmem = layout->nvmem;
  69. struct device *dev = &layout->dev;
  70. const struct nvmem_cell_info *pinfo;
  71. struct nvmem_cell_info info = {0};
  72. struct device_node *layout_np;
  73. struct sl28vpd_header hdr;
  74. int ret, i;
  75. /* check header */
  76. ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
  77. if (ret < 0)
  78. return ret;
  79. else if (ret != sizeof(hdr))
  80. return -EIO;
  81. if (hdr.magic != SL28VPD_MAGIC) {
  82. dev_err(dev, "Invalid magic value (%02x)\n", hdr.magic);
  83. return -EINVAL;
  84. }
  85. if (hdr.version != 1) {
  86. dev_err(dev, "Version %d is unsupported.\n", hdr.version);
  87. return -EINVAL;
  88. }
  89. ret = sl28vpd_v1_check_crc(dev, nvmem);
  90. if (ret)
  91. return ret;
  92. layout_np = of_nvmem_layout_get_container(nvmem);
  93. if (!layout_np)
  94. return -ENOENT;
  95. for (i = 0; i < ARRAY_SIZE(sl28vpd_v1_entries); i++) {
  96. pinfo = &sl28vpd_v1_entries[i];
  97. info.name = pinfo->name;
  98. info.offset = pinfo->offset;
  99. info.bytes = pinfo->bytes;
  100. info.read_post_process = pinfo->read_post_process;
  101. info.np = of_get_child_by_name(layout_np, pinfo->name);
  102. ret = nvmem_add_one_cell(nvmem, &info);
  103. if (ret) {
  104. of_node_put(layout_np);
  105. return ret;
  106. }
  107. }
  108. of_node_put(layout_np);
  109. return 0;
  110. }
  111. static int sl28vpd_probe(struct nvmem_layout *layout)
  112. {
  113. layout->add_cells = sl28vpd_add_cells;
  114. return nvmem_layout_register(layout);
  115. }
  116. static void sl28vpd_remove(struct nvmem_layout *layout)
  117. {
  118. nvmem_layout_unregister(layout);
  119. }
  120. static const struct of_device_id sl28vpd_of_match_table[] = {
  121. { .compatible = "kontron,sl28-vpd" },
  122. {},
  123. };
  124. MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
  125. static struct nvmem_layout_driver sl28vpd_layout = {
  126. .driver = {
  127. .name = "kontron-sl28vpd-layout",
  128. .of_match_table = sl28vpd_of_match_table,
  129. },
  130. .probe = sl28vpd_probe,
  131. .remove = sl28vpd_remove,
  132. };
  133. module_nvmem_layout_driver(sl28vpd_layout);
  134. MODULE_LICENSE("GPL");
  135. MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
  136. MODULE_DESCRIPTION("NVMEM layout driver for the VPD of Kontron sl28 boards");