1 /* SPDX-License-Identifier: GPL-2.0-only */
2
3 #include <build.h>
4 #include <types.h>
5 #include <string.h>
6 #include <device/device.h>
7 #include <device/smbus.h>
8 #include <smbios.h>
9 #include <console/console.h>
10 #include <version.h>
11 #include "lenovo.h"
12
13 #define ERROR_STRING "*INVALID*"
14
at24rf08c_find_bank(u8 bank)15 static struct device *at24rf08c_find_bank(u8 bank)
16 {
17 struct device *dev;
18 dev = dev_find_slot_on_smbus(1, 0x54 | bank);
19 if (!dev)
20 printk(BIOS_WARNING, "EEPROM not found\n");
21 return dev;
22 }
23
at24rf08c_read_byte(struct device * dev,u8 addr)24 static int at24rf08c_read_byte(struct device *dev, u8 addr)
25 {
26 int t = -1;
27 int j;
28
29 /* After a register write AT24RF08C (which we issued in init function)
30 sometimes stops responding. Retry several times in case of failure.
31 */
32 for (j = 0; j < 100; j++) {
33 t = smbus_read_byte(dev, addr);
34 if (t >= 0)
35 return t;
36 }
37
38 return t;
39 }
40
at24rf08c_read_string_dev(struct device * dev,u8 start,u8 len,char * result)41 static void at24rf08c_read_string_dev(struct device *dev, u8 start,
42 u8 len, char *result)
43 {
44 int i;
45 for (i = 0; i < len; i++) {
46 int t = at24rf08c_read_byte(dev, start + i);
47
48 if (t < 0x20 || t > 0x7f) {
49 memcpy(result, ERROR_STRING, sizeof(ERROR_STRING));
50 return;
51 }
52 result[i] = t;
53 }
54 result[len] = '\0';
55 }
56
at24rf08c_read_string(u8 bank,u8 start,u8 len,char * result)57 static void at24rf08c_read_string(u8 bank, u8 start, u8 len, char *result)
58 {
59 struct device *dev;
60
61 dev = at24rf08c_find_bank(bank);
62 if (dev == NULL) {
63 printk(BIOS_WARNING, "EEPROM not found\n");
64 memcpy(result, ERROR_STRING, sizeof(ERROR_STRING));
65 return;
66 }
67
68 at24rf08c_read_string_dev(dev, start, len, result);
69 }
70
smbios_mainboard_serial_number(void)71 const char *smbios_mainboard_serial_number(void)
72 {
73 static char result[12];
74 static int already_read;
75
76 if (already_read)
77 return result;
78
79 memset(result, 0, sizeof(result));
80 at24rf08c_read_string(0, 0x2e, 7, result);
81
82 already_read = 1;
83 return result;
84 }
85
lenovo_mainboard_partnumber(void)86 const char *lenovo_mainboard_partnumber(void)
87 {
88 static char result[12];
89 static int already_read;
90
91 if (already_read)
92 return result;
93
94 memset(result, 0, sizeof(result));
95 at24rf08c_read_string(0, 0x27, 7, result);
96
97 already_read = 1;
98 return result;
99 }
100
smbios_mainboard_product_name(void)101 const char *smbios_mainboard_product_name(void)
102 {
103 return lenovo_mainboard_partnumber();
104 }
105
smbios_system_set_uuid(u8 * uuid)106 void smbios_system_set_uuid(u8 *uuid)
107 {
108 static char result[16];
109 unsigned int i;
110 static int already_read;
111 struct device *dev;
112 const int remap[16] = {
113 /* UUID byteswap. */
114 3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15
115 };
116
117 if (already_read) {
118 memcpy(uuid, result, 16);
119 return;
120 }
121
122 memset(result, 0, sizeof(result));
123
124 dev = dev_find_slot_on_smbus(1, 0x56);
125 if (dev == NULL) {
126 printk(BIOS_WARNING, "EEPROM not found\n");
127 already_read = 1;
128 memset(uuid, 0, 16);
129 return;
130 }
131
132 for (i = 0; i < 16; i++) {
133 int t;
134 int j;
135 /* After a register write AT24RF08C (which we issued in init function) sometimes stops responding.
136 Retry several times in case of failure.
137 */
138 for (j = 0; j < 100; j++) {
139 t = smbus_read_byte(dev, 0x12 + i);
140 if (t >= 0)
141 break;
142 }
143 if (t < 0) {
144 memset(result, 0, sizeof(result));
145 break;
146 }
147 result[remap[i]] = t;
148 }
149
150 already_read = 1;
151
152 memcpy(uuid, result, 16);
153 }
154
smbios_mainboard_version(void)155 const char *smbios_mainboard_version(void)
156 {
157 static char result[100];
158 static int already_read;
159 struct device *dev;
160 int len;
161
162 if (already_read)
163 return result;
164
165 memset(result, 0, sizeof(result));
166
167 dev = at24rf08c_find_bank(2);
168 if (dev == NULL) {
169 memcpy(result, ERROR_STRING, sizeof(ERROR_STRING));
170 return result;
171 }
172
173 len = at24rf08c_read_byte(dev, 0x26) - 2;
174 if (len < 0 || len > sizeof(result) - 1) {
175 memcpy(result, ERROR_STRING, sizeof(ERROR_STRING));
176 return result;
177 }
178
179 at24rf08c_read_string_dev(dev, 0x27, len, result);
180
181 already_read = 1;
182 return result;
183 }
184
smbios_mainboard_bios_version(void)185 const char *smbios_mainboard_bios_version(void)
186 {
187 /* Satisfy thinkpad_acpi. */
188 if (strlen(CONFIG_LOCALVERSION))
189 return "CBET4000 " CONFIG_LOCALVERSION;
190
191 return "CBET4000 " COREBOOT_VERSION;
192 }
193
smbios_mainboard_manufacturer(void)194 const char *smbios_mainboard_manufacturer(void)
195 {
196 return "LENOVO";
197 }
198