Baruch Siach | 4417ff2 | 2020-01-20 14:20:11 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Copyright 2020 SolidRun |
| 4 | */ |
| 5 | |
| 6 | #include <common.h> |
Simon Glass | 3ba929a | 2020-10-30 21:38:53 -0600 | [diff] [blame] | 7 | #include <compiler.h> |
Baruch Siach | 4417ff2 | 2020-01-20 14:20:11 +0200 | [diff] [blame] | 8 | #include <tlv_eeprom.h> |
| 9 | #include "tlv_data.h" |
| 10 | |
| 11 | #define SR_TLV_CODE_RAM_SIZE 0x81 |
| 12 | |
| 13 | static void store_product_name(struct tlvinfo_tlv *tlv_entry, |
| 14 | struct tlv_data *td) |
| 15 | { |
| 16 | int len; |
| 17 | char *dest; |
| 18 | |
| 19 | if (strlen(td->tlv_product_name[0]) == 0) |
| 20 | dest = td->tlv_product_name[0]; |
| 21 | else if (strlen(td->tlv_product_name[1]) == 0) |
| 22 | dest = td->tlv_product_name[1]; |
| 23 | else |
| 24 | return; |
| 25 | |
| 26 | len = min_t(unsigned int, tlv_entry->length, |
| 27 | sizeof(td->tlv_product_name[0]) - 1); |
| 28 | memcpy(dest, tlv_entry->value, len); |
| 29 | } |
| 30 | |
| 31 | static void parse_tlv_vendor_ext(struct tlvinfo_tlv *tlv_entry, |
| 32 | struct tlv_data *td) |
| 33 | { |
| 34 | u8 *val = tlv_entry->value; |
| 35 | u32 pen; /* IANA Private Enterprise Numbers */ |
| 36 | |
| 37 | if (tlv_entry->length < 5) /* 4 bytes PEN + at least 1 byte type */ |
| 38 | return; |
| 39 | |
| 40 | /* PEN is big endian */ |
| 41 | pen = (val[0] << 24) | (val[1] << 16) | (val[2] << 8) | val[3]; |
| 42 | /* Not a real PEN */ |
| 43 | if (pen != 0xffffffff) |
| 44 | return; |
| 45 | |
| 46 | if (val[4] != SR_TLV_CODE_RAM_SIZE) |
| 47 | return; |
Josua Mayer | e528366 | 2023-10-08 16:58:03 +0200 | [diff] [blame] | 48 | if (tlv_entry->length < 6) |
Baruch Siach | 4417ff2 | 2020-01-20 14:20:11 +0200 | [diff] [blame] | 49 | return; |
| 50 | td->ram_size = val[5]; |
Josua Mayer | e528366 | 2023-10-08 16:58:03 +0200 | [diff] [blame] | 51 | |
| 52 | /* extension with additional data field for number of ddr channels */ |
| 53 | if (tlv_entry->length >= 7) { |
| 54 | td->ram_channels = val[6]; |
| 55 | } |
Baruch Siach | 4417ff2 | 2020-01-20 14:20:11 +0200 | [diff] [blame] | 56 | } |
| 57 | |
| 58 | static void parse_tlv_data(u8 *eeprom, struct tlvinfo_header *hdr, |
| 59 | struct tlvinfo_tlv *entry, struct tlv_data *td) |
| 60 | { |
| 61 | unsigned int tlv_offset, tlv_len; |
| 62 | |
| 63 | tlv_offset = sizeof(struct tlvinfo_header); |
| 64 | tlv_len = sizeof(struct tlvinfo_header) + be16_to_cpu(hdr->totallen); |
| 65 | while (tlv_offset < tlv_len) { |
| 66 | entry = (struct tlvinfo_tlv *)&eeprom[tlv_offset]; |
| 67 | |
| 68 | switch (entry->type) { |
| 69 | case TLV_CODE_PRODUCT_NAME: |
| 70 | store_product_name(entry, td); |
| 71 | break; |
| 72 | case TLV_CODE_VENDOR_EXT: |
| 73 | parse_tlv_vendor_ext(entry, td); |
| 74 | break; |
| 75 | default: |
| 76 | break; |
| 77 | } |
| 78 | |
| 79 | tlv_offset += sizeof(struct tlvinfo_tlv) + entry->length; |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | void read_tlv_data(struct tlv_data *td) |
| 84 | { |
| 85 | u8 eeprom_data[TLV_TOTAL_LEN_MAX]; |
| 86 | struct tlvinfo_header *tlv_hdr; |
| 87 | struct tlvinfo_tlv *tlv_entry; |
| 88 | int ret, i; |
| 89 | |
| 90 | for (i = 0; i < 2; i++) { |
| 91 | ret = read_tlvinfo_tlv_eeprom(eeprom_data, &tlv_hdr, |
| 92 | &tlv_entry, i); |
| 93 | if (ret < 0) |
| 94 | continue; |
| 95 | parse_tlv_data(eeprom_data, tlv_hdr, tlv_entry, td); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | bool sr_product_is(const struct tlv_data *td, const char *product) |
| 100 | { |
| 101 | /* Allow prefix sub-string match */ |
| 102 | if (strncmp(td->tlv_product_name[0], product, strlen(product)) == 0) |
| 103 | return true; |
| 104 | if (strncmp(td->tlv_product_name[1], product, strlen(product)) == 0) |
| 105 | return true; |
| 106 | |
| 107 | return false; |
| 108 | } |