1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries
4  *
5  * Author: Tudor Ambarus <tudor.ambarus@microchip.com>
6  */
7 
8 #include <common.h>
9 #include <dm.h>
10 #include <env.h>
11 #include <net.h>
12 #include <linux/mtd/spi-nor.h>
13 #include <netdev.h>
14 
15 #define ETH_ADDR_SIZE			6
16 
17 #ifdef CONFIG_SPI_FLASH_SST
18 #define SFDP_MICROCHIP_MANUF_ID		0xbf
19 #define SFDP_MICROCHIP_MEM_TYPE		0x26
20 #define SFDP_MICROCHIP_DEV_ID		0x43
21 
22 #define SFDP_MICROCHIP_EUI_OFFSET	0x60
23 #define SFDP_MICROCHIP_EUI48		0x30
24 
25 struct sst26vf064beui {
26 	u8 manufacturer_id;
27 	u8 memory_type;
28 	u8 device_id;
29 	u8 reserved;
30 };
31 
32 /**
33  * sst26vf064beui_check() - Check the validity of the EUI-48 information from
34  * the sst26vf064beui SPI NOR Microchip SFDP table.
35  * @manufacturer_sfdp:	pointer to the Microchip manufacturer specific SFDP
36  *			table.
37  *
38  * Return: 0 on success, -errno otherwise.
39  */
sst26vf064beui_check(const u8 * manufacturer_sfdp)40 static int sst26vf064beui_check(const u8 *manufacturer_sfdp)
41 {
42 	struct sst26vf064beui *sst26vf064beui =
43 		(struct sst26vf064beui *)manufacturer_sfdp;
44 
45 	if (sst26vf064beui->manufacturer_id != SFDP_MICROCHIP_MANUF_ID)
46 		return -EINVAL;
47 
48 	if (sst26vf064beui->memory_type != SFDP_MICROCHIP_MEM_TYPE)
49 		return -EINVAL;
50 
51 	if (sst26vf064beui->device_id != SFDP_MICROCHIP_DEV_ID)
52 		return -EINVAL;
53 
54 	/*
55 	 * Check if the EUI-48 MAC address is programmed in the next six address
56 	 * locations.
57 	 */
58 	if (manufacturer_sfdp[SFDP_MICROCHIP_EUI_OFFSET] !=
59 	    SFDP_MICROCHIP_EUI48)
60 		return -EINVAL;
61 
62 	return 0;
63 }
64 
65 /**
66  * sst26vf064beui_get_ethaddr() - Get the ethernet address from the
67  * sst26vf064beui SPI NOR Microchip SFDP table.
68  * @manufacturer_sfdp:	pointer to the Microchip manufacturer specific SFDP
69  *			table.
70  * @ethaddr:		pointer where to fill the ethernet address
71  * @size:		size of the ethernet address.
72  *
73  * Return: 0 on success, -errno otherwise.
74  */
sst26vf064beui_get_ethaddr(const u8 * manufacturer_sfdp,u8 * ethaddr,size_t size)75 static int sst26vf064beui_get_ethaddr(const u8 *manufacturer_sfdp,
76 				      u8 *ethaddr, size_t size)
77 {
78 	u64 eui_table[2];
79 	u64 *p = (u64 *)&manufacturer_sfdp[SFDP_MICROCHIP_EUI_OFFSET];
80 	int i, ret;
81 
82 	ret = sst26vf064beui_check(manufacturer_sfdp);
83 	if (ret)
84 		return ret;
85 
86 	for (i = 0; i < 2; i++)
87 		eui_table[i] = le64_to_cpu(p[i]);
88 
89 	/* Ethaddr starts at offset one. */
90 	memcpy(ethaddr, &((u8 *)eui_table)[1], size);
91 
92 	return 0;
93 }
94 #endif
95 
96 /**
97  * at91_spi_nor_set_ethaddr() - Retrieve and set the ethernet address from the
98  * SPI NOR manufacturer specific SFDP table.
99  */
at91_spi_nor_set_ethaddr(void)100 void at91_spi_nor_set_ethaddr(void)
101 {
102 	struct udevice *dev;
103 	struct spi_nor *nor;
104 	const char *ethaddr_name = "ethaddr";
105 	u8 ethaddr[ETH_ADDR_SIZE] = {0};
106 
107 	if (env_get(ethaddr_name))
108 		return;
109 
110 	if (uclass_first_device_err(UCLASS_SPI_FLASH, &dev))
111 		return;
112 
113 	nor = dev_get_uclass_priv(dev);
114 	if (!nor)
115 		return;
116 
117 	if (!nor->manufacturer_sfdp)
118 		return;
119 
120 #ifdef CONFIG_SPI_FLASH_SST
121 	if (sst26vf064beui_get_ethaddr(nor->manufacturer_sfdp, ethaddr,
122 				       ETH_ADDR_SIZE))
123 		return;
124 #endif
125 
126 	if (is_valid_ethaddr(ethaddr))
127 		eth_env_set_enetaddr(ethaddr_name, ethaddr);
128 }
129