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