blob: b8086605c3aeeccdcdfb206be019189da1722d72 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2020 SolidRun
*/
#include <compiler.h>
#include <tlv_eeprom.h>
#include <linux/kernel.h>
#include "tlv_data.h"
#define SR_TLV_CODE_RAM_SIZE 0x81
static void store_product_name(struct tlvinfo_tlv *tlv_entry,
struct tlv_data *td)
{
int len;
char *dest;
if (strlen(td->tlv_product_name[0]) == 0)
dest = td->tlv_product_name[0];
else if (strlen(td->tlv_product_name[1]) == 0)
dest = td->tlv_product_name[1];
else
return;
len = min_t(unsigned int, tlv_entry->length,
sizeof(td->tlv_product_name[0]) - 1);
memcpy(dest, tlv_entry->value, len);
}
static void parse_tlv_vendor_ext(struct tlvinfo_tlv *tlv_entry,
struct tlv_data *td)
{
u8 *val = tlv_entry->value;
u32 pen; /* IANA Private Enterprise Numbers */
if (tlv_entry->length < 5) /* 4 bytes PEN + at least 1 byte type */
return;
/* PEN is big endian */
pen = (val[0] << 24) | (val[1] << 16) | (val[2] << 8) | val[3];
/* Not a real PEN */
if (pen != 0xffffffff)
return;
if (val[4] != SR_TLV_CODE_RAM_SIZE)
return;
if (tlv_entry->length < 6)
return;
td->ram_size = val[5];
/* extension with additional data field for number of ddr channels */
if (tlv_entry->length >= 7) {
td->ram_channels = val[6];
}
}
static void parse_tlv_data(u8 *eeprom, struct tlvinfo_header *hdr,
struct tlvinfo_tlv *entry, struct tlv_data *td)
{
unsigned int tlv_offset, tlv_len;
tlv_offset = sizeof(struct tlvinfo_header);
tlv_len = sizeof(struct tlvinfo_header) + be16_to_cpu(hdr->totallen);
while (tlv_offset < tlv_len) {
entry = (struct tlvinfo_tlv *)&eeprom[tlv_offset];
switch (entry->type) {
case TLV_CODE_PRODUCT_NAME:
store_product_name(entry, td);
break;
case TLV_CODE_VENDOR_EXT:
parse_tlv_vendor_ext(entry, td);
break;
default:
break;
}
tlv_offset += sizeof(struct tlvinfo_tlv) + entry->length;
}
}
void read_tlv_data(struct tlv_data *td)
{
u8 eeprom_data[TLV_TOTAL_LEN_MAX];
struct tlvinfo_header *tlv_hdr;
struct tlvinfo_tlv *tlv_entry;
int ret, i;
for (i = 0; i < 2; i++) {
ret = read_tlvinfo_tlv_eeprom(eeprom_data, &tlv_hdr,
&tlv_entry, i);
if (ret < 0)
continue;
parse_tlv_data(eeprom_data, tlv_hdr, tlv_entry, td);
}
}
bool sr_product_is(const struct tlv_data *td, const char *product)
{
/* Allow prefix sub-string match */
if (strncmp(td->tlv_product_name[0], product, strlen(product)) == 0)
return true;
if (strncmp(td->tlv_product_name[1], product, strlen(product)) == 0)
return true;
return false;
}