blob: 32572481ce9cc63d929df0d409d5f5e8fcf03c01 [file] [log] [blame]
Marek Behún7ca07fc2024-06-18 17:34:31 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2024 Marek Behún <kabel@kernel.org>
4 */
5
6#include <asm/unaligned.h>
7#include <ctype.h>
8#include <linux/compiler.h>
9#include <linux/kernel.h>
10#include <eeprom_field.h>
11#include <eeprom_layout.h>
12#include <u-boot/crc.h>
13
14#define _DEF_FIELD(_n, _s, _t) \
15 { _n, _s, NULL, eeprom_field_print_ ## _t, eeprom_field_update_ ## _t }
16
17static void eeprom_field_print_ramsz(const struct eeprom_field *field)
18{
19 printf(PRINT_FIELD_SEGMENT, field->name);
20 printf("%u\n", get_unaligned_le32(field->buf));
21}
22
23static int eeprom_field_update_ramsz(struct eeprom_field *field, char *value)
24{
25 u32 sz;
26
27 if (value[0] == '1' || value[0] == '2' || value[0] == '4')
28 sz = value[0] - '0';
29 else
30 return -1;
31
32 if (value[1] != '\0')
33 return -1;
34
35 put_unaligned_le32(sz, field->buf);
36
37 return 0;
38}
39
40static void eeprom_field_print_region(const struct eeprom_field *field)
41{
42 eeprom_field_print_ascii(field);
43}
44
45static int eeprom_field_update_region(struct eeprom_field *field, char *value)
46{
47 if (strlen(value) != 2) {
48 printf("%s: has to be 2 characters\n", field->name);
49 return -1;
50 }
51
52 memcpy(field->buf, value, 2);
53 memset(&field->buf[2], '\0', 2);
54
55 return 0;
56}
57
Marek Behún9535f792024-06-18 17:34:34 +020058static void eeprom_field_print_ddr_speed(const struct eeprom_field *field)
59{
60 printf(PRINT_FIELD_SEGMENT, field->name);
61
62 if (field->buf[0] == '\0' || field->buf[0] == 0xff)
63 puts("(empty, defaults to 1600K)\n");
64 else
65 printf("%.5s\n", field->buf);
66}
67
68bool omnia_valid_ddr_speed(const char *name);
69void omnia_print_ddr_speeds(void);
70
71static int eeprom_field_update_ddr_speed(struct eeprom_field *field,
72 char *value)
73{
74 if (value[0] == '\0') {
75 /* setting default value */
76 memset(field->buf, 0xff, field->size);
77
78 return 0;
79 }
80
81 if (!omnia_valid_ddr_speed(value)) {
82 printf("%s: invalid setting, supported values are:\n ",
83 field->name);
84 omnia_print_ddr_speeds();
85
86 return -1;
87 }
88
89 strncpy(field->buf, value, field->size);
90
91 return 0;
92}
93
Marek Behún7ca07fc2024-06-18 17:34:31 +020094static struct eeprom_field omnia_layout[] = {
95 _DEF_FIELD("Magic constant", 4, bin),
96 _DEF_FIELD("RAM size in GB", 4, ramsz),
97 _DEF_FIELD("Wi-Fi Region", 4, region),
98 _DEF_FIELD("CRC32 checksum", 4, bin),
Marek Behún9535f792024-06-18 17:34:34 +020099 _DEF_FIELD("DDR speed", 5, ddr_speed),
100 _DEF_FIELD("Extended reserved fields", 39, reserved),
Marek Behúnc7548162024-06-18 17:34:33 +0200101 _DEF_FIELD("Extended CRC32 checksum", 4, bin),
Marek Behún7ca07fc2024-06-18 17:34:31 +0200102};
103
104static struct eeprom_field *crc_field = &omnia_layout[3];
Marek Behúnc7548162024-06-18 17:34:33 +0200105static struct eeprom_field *ext_crc_field =
106 &omnia_layout[ARRAY_SIZE(omnia_layout) - 1];
Marek Behún7ca07fc2024-06-18 17:34:31 +0200107
108static int omnia_update_field(struct eeprom_layout *layout, char *field_name,
109 char *new_data)
110{
111 struct eeprom_field *field;
112 int err;
113
114 if (!new_data)
115 return 0;
116
117 if (!field_name)
118 return -1;
119
120 field = eeprom_layout_find_field(layout, field_name, true);
121 if (!field)
122 return -1;
123
124 err = field->update(field, new_data);
125 if (err) {
126 printf("Invalid data for field %s\n", field_name);
127 return err;
128 }
129
130 if (field < crc_field) {
131 u32 crc = crc32(0, layout->data, 12);
132 put_unaligned_le32(crc, crc_field->buf);
133 }
134
Marek Behúnc7548162024-06-18 17:34:33 +0200135 if (field < ext_crc_field) {
Marek Behún9535f792024-06-18 17:34:34 +0200136 u32 crc = crc32(0, layout->data, 60);
Marek Behúnc7548162024-06-18 17:34:33 +0200137 put_unaligned_le32(crc, ext_crc_field->buf);
138 }
139
Marek Behún7ca07fc2024-06-18 17:34:31 +0200140 return 0;
141}
142
143void eeprom_layout_assign(struct eeprom_layout *layout, int)
144{
145 layout->fields = omnia_layout;
146 layout->num_of_fields = ARRAY_SIZE(omnia_layout);
147 layout->update = omnia_update_field;
Marek Behúnc7548162024-06-18 17:34:33 +0200148 layout->data_size = 64;
Marek Behún7ca07fc2024-06-18 17:34:31 +0200149}
150
151int eeprom_layout_detect(unsigned char *)
152{
153 /* Turris Omnia has only one version of EEPROM layout */
154 return 0;
155}