aboutsummaryrefslogtreecommitdiff
path: root/board/CZ.NIC/turris_omnia/eeprom.c
blob: 32572481ce9cc63d929df0d409d5f5e8fcf03c01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2024 Marek Behún <kabel@kernel.org>
 */

#include <asm/unaligned.h>
#include <ctype.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <eeprom_field.h>
#include <eeprom_layout.h>
#include <u-boot/crc.h>

#define _DEF_FIELD(_n, _s, _t) \
	{ _n, _s, NULL, eeprom_field_print_ ## _t, eeprom_field_update_ ## _t }

static void eeprom_field_print_ramsz(const struct eeprom_field *field)
{
	printf(PRINT_FIELD_SEGMENT, field->name);
	printf("%u\n", get_unaligned_le32(field->buf));
}

static int eeprom_field_update_ramsz(struct eeprom_field *field, char *value)
{
	u32 sz;

	if (value[0] == '1' || value[0] == '2' || value[0] == '4')
		sz = value[0] - '0';
	else
		return -1;

	if (value[1] != '\0')
		return -1;

	put_unaligned_le32(sz, field->buf);

	return 0;
}

static void eeprom_field_print_region(const struct eeprom_field *field)
{
	eeprom_field_print_ascii(field);
}

static int eeprom_field_update_region(struct eeprom_field *field, char *value)
{
	if (strlen(value) != 2) {
		printf("%s: has to be 2 characters\n", field->name);
		return -1;
	}

	memcpy(field->buf, value, 2);
	memset(&field->buf[2], '\0', 2);

	return 0;
}

static void eeprom_field_print_ddr_speed(const struct eeprom_field *field)
{
	printf(PRINT_FIELD_SEGMENT, field->name);

	if (field->buf[0] == '\0' || field->buf[0] == 0xff)
		puts("(empty, defaults to 1600K)\n");
	else
		printf("%.5s\n", field->buf);
}

bool omnia_valid_ddr_speed(const char *name);
void omnia_print_ddr_speeds(void);

static int eeprom_field_update_ddr_speed(struct eeprom_field *field,
					 char *value)
{
	if (value[0] == '\0') {
		/* setting default value */
		memset(field->buf, 0xff, field->size);

		return 0;
	}

	if (!omnia_valid_ddr_speed(value)) {
		printf("%s: invalid setting, supported values are:\n  ",
		       field->name);
		omnia_print_ddr_speeds();

		return -1;
	}

	strncpy(field->buf, value, field->size);

	return 0;
}

static struct eeprom_field omnia_layout[] = {
	_DEF_FIELD("Magic constant", 4, bin),
	_DEF_FIELD("RAM size in GB", 4, ramsz),
	_DEF_FIELD("Wi-Fi Region", 4, region),
	_DEF_FIELD("CRC32 checksum", 4, bin),
	_DEF_FIELD("DDR speed", 5, ddr_speed),
	_DEF_FIELD("Extended reserved fields", 39, reserved),
	_DEF_FIELD("Extended CRC32 checksum", 4, bin),
};

static struct eeprom_field *crc_field = &omnia_layout[3];
static struct eeprom_field *ext_crc_field =
	&omnia_layout[ARRAY_SIZE(omnia_layout) - 1];

static int omnia_update_field(struct eeprom_layout *layout, char *field_name,
			      char *new_data)
{
	struct eeprom_field *field;
	int err;

	if (!new_data)
		return 0;

	if (!field_name)
		return -1;

	field = eeprom_layout_find_field(layout, field_name, true);
	if (!field)
		return -1;

	err = field->update(field, new_data);
	if (err) {
		printf("Invalid data for field %s\n", field_name);
		return err;
	}

	if (field < crc_field) {
		u32 crc = crc32(0, layout->data, 12);
		put_unaligned_le32(crc, crc_field->buf);
	}

	if (field < ext_crc_field) {
		u32 crc = crc32(0, layout->data, 60);
		put_unaligned_le32(crc, ext_crc_field->buf);
	}

	return 0;
}

void eeprom_layout_assign(struct eeprom_layout *layout, int)
{
	layout->fields = omnia_layout;
	layout->num_of_fields = ARRAY_SIZE(omnia_layout);
	layout->update = omnia_update_field;
	layout->data_size = 64;
}

int eeprom_layout_detect(unsigned char *)
{
	/* Turris Omnia has only one version of EEPROM layout */
	return 0;
}