Merge tag 'u-boot-atmel-fixes-2020.07-a' of https://gitlab.denx.de/u-boot/custodians...
[oweals/u-boot.git] / board / atmel / common / mac-spi-nor.c
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  */
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  */
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  */
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 }