Commit be2808c3 authored by Ian Ray's avatar Ian Ray Committed by Stefano Babic
Browse files

board: ge: bx50v3: set eth0 MAC address



Define i2c mux configuration.  Add new vpd_reader which is used to read
vital product data.  Read VPD from EEPROM and set eth0 MAC address.
Signed-off-by: Ian Ray's avatarIan Ray <ian.ray@ge.com>
Signed-off-by: default avatarJose Alarcon <jose.alarcon@ge.com>
parent b1e8512e
......@@ -5,4 +5,4 @@
# SPDX-License-Identifier: GPL-2.0+
#
obj-y := bx50v3.o
obj-y := bx50v3.o vpd_reader.o
......@@ -26,8 +26,19 @@
#include <asm/arch/sys_proto.h>
#include <i2c.h>
#include <pwm.h>
#include <stdlib.h>
#include "vpd_reader.h"
DECLARE_GLOBAL_DATA_PTR;
#ifndef CONFIG_SYS_I2C_EEPROM_ADDR
# define CONFIG_SYS_I2C_EEPROM_ADDR 0x50
# define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 1
#endif
#ifndef CONFIG_SYS_I2C_EEPROM_BUS
#define CONFIG_SYS_I2C_EEPROM_BUS 2
#endif
#define NC_PAD_CTRL (PAD_CTL_PUS_100K_UP | \
PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | \
PAD_CTL_HYS)
......@@ -528,6 +539,102 @@ int overwrite_console(void)
return 1;
}
#define VPD_TYPE_INVALID 0x00
#define VPD_BLOCK_NETWORK 0x20
#define VPD_BLOCK_HWID 0x44
#define VPD_PRODUCT_B850 1
#define VPD_PRODUCT_B650 2
#define VPD_PRODUCT_B450 3
struct vpd_cache {
uint8_t product_id;
uint8_t macbits;
unsigned char mac1[6];
};
/*
* Extracts MAC and product information from the VPD.
*/
static int vpd_callback(
void *userdata,
uint8_t id,
uint8_t version,
uint8_t type,
size_t size,
uint8_t const *data)
{
struct vpd_cache *vpd = (struct vpd_cache *)userdata;
if ( id == VPD_BLOCK_HWID
&& version == 1
&& type != VPD_TYPE_INVALID
&& size >= 1) {
vpd->product_id = data[0];
} else if ( id == VPD_BLOCK_NETWORK
&& version == 1
&& type != VPD_TYPE_INVALID
&& size >= 6) {
vpd->macbits |= 1;
memcpy(vpd->mac1, data, 6);
}
return 0;
}
static void set_eth0_mac_address(unsigned char * mac)
{
uint32_t *ENET_TCR = (uint32_t*)0x21880c4;
uint32_t *ENET_PALR = (uint32_t*)0x21880e4;
uint32_t *ENET_PAUR = (uint32_t*)0x21880e8;
*ENET_TCR |= 0x100; /* ADDINS */
*ENET_PALR |= (mac[0] << 24) | (mac[1] << 16) | (mac[2] << 8) | mac[3];
*ENET_PAUR |= (mac[4] << 24) | (mac[5] << 16);
}
static void process_vpd(struct vpd_cache *vpd)
{
if ( vpd->product_id == VPD_PRODUCT_B850
|| vpd->product_id == VPD_PRODUCT_B650
|| vpd->product_id == VPD_PRODUCT_B450) {
if (vpd->macbits & 1) {
set_eth0_mac_address(vpd->mac1);
}
}
}
static int read_vpd(uint eeprom_bus)
{
struct vpd_cache vpd;
int res;
int size = 1024;
uint8_t *data;
unsigned int current_i2c_bus = i2c_get_bus_num();
res = i2c_set_bus_num(eeprom_bus);
if (res < 0)
return res;
data = (uint8_t *)malloc(size);
if (!data)
return -ENOMEM;
res = i2c_read(CONFIG_SYS_I2C_EEPROM_ADDR, 0,
CONFIG_SYS_I2C_EEPROM_ADDR_LEN, data, size);
if (res == 0) {
memset(&vpd, 0, sizeof(vpd));
vpd_reader(size, data, &vpd, vpd_callback);
process_vpd(&vpd);
}
free(data);
i2c_set_bus_num(current_i2c_bus);
return res;
}
int board_eth_init(bd_t *bis)
{
setup_iomux_enet();
......@@ -586,6 +693,8 @@ int board_init(void)
setup_i2c(2, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info2);
setup_i2c(3, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info3);
read_vpd(CONFIG_SYS_I2C_EEPROM_BUS);
return 0;
}
......
/*
* Copyright 2016 General Electric Company
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include "vpd_reader.h"
#include <linux/bch.h>
#include <stdlib.h>
/* BCH configuration */
const struct {
int header_ecc_capability_bits;
int data_ecc_capability_bits;
unsigned int prim_poly;
struct {
int min;
int max;
} galois_field_order;
} bch_configuration = {
.header_ecc_capability_bits = 4,
.data_ecc_capability_bits = 16,
.prim_poly = 0,
.galois_field_order = {
.min = 5,
.max = 15,
},
};
static int calculate_galois_field_order(size_t source_length)
{
int gfo = bch_configuration.galois_field_order.min;
for (; gfo < bch_configuration.galois_field_order.max &&
((((1 << gfo) - 1) - ((int)source_length * 8)) < 0);
gfo++) {
}
if (gfo == bch_configuration.galois_field_order.max) {
return -1;
}
return gfo + 1;
}
static int verify_bch(int ecc_bits, unsigned int prim_poly,
uint8_t * data, size_t data_length,
const uint8_t * ecc, size_t ecc_length)
{
int gfo = calculate_galois_field_order(data_length);
if (gfo < 0) {
return -1;
}
struct bch_control * bch = init_bch(gfo, ecc_bits, prim_poly);
if (!bch) {
return -1;
}
if (bch->ecc_bytes != ecc_length) {
free_bch(bch);
return -1;
}
unsigned * errloc = (unsigned *)calloc(data_length, sizeof(unsigned));
int errors = decode_bch(
bch, data, data_length, ecc, NULL, NULL, errloc);
free_bch(bch);
if (errors < 0) {
free(errloc);
return -1;
}
if (errors > 0) {
for (int n = 0; n < errors; n++) {
if (errloc[n] >= 8 * data_length) {
/* n-th error located in ecc (no need for data correction) */
} else {
/* n-th error located in data */
data[errloc[n] / 8] ^= 1 << (errloc[n] % 8);
}
}
}
free(errloc);
return 0;
}
static const int ID = 0;
static const int LEN = 1;
static const int VER = 2;
static const int TYP = 3;
static const int BLOCK_SIZE = 4;
static const uint8_t HEADER_BLOCK_ID = 0x00;
static const uint8_t HEADER_BLOCK_LEN = 18;
static const uint32_t HEADER_BLOCK_MAGIC = 0xca53ca53;
static const size_t HEADER_BLOCK_VERIFY_LEN = 14;
static const size_t HEADER_BLOCK_ECC_OFF = 14;
static const size_t HEADER_BLOCK_ECC_LEN = 4;
static const uint8_t ECC_BLOCK_ID = 0xFF;
int vpd_reader(
size_t size,
uint8_t * data,
void * userdata,
int (*fn)(
void * userdata,
uint8_t id,
uint8_t version,
uint8_t type,
size_t size,
uint8_t const * data))
{
if ( size < HEADER_BLOCK_LEN
|| data == NULL
|| fn == NULL) {
return -EINVAL;
}
/*
* +--------------------+--------------------+--//--+--------------------+
* | header block | data block | ... | ecc block |
* +--------------------+--------------------+--//--+--------------------+
* : : :
* +------+-------+-----+ +------+-------------+
* | id | magic | ecc | | ... | ecc |
* | len | off | | +------+-------------+
* | ver | size | | :
* | type | | | :
* +------+-------+-----+ :
* : : : :
* <----- [1] ----> <----------- [2] ----------->
*
* Repair (if necessary) the contents of header block [1] by using a
* 4 byte ECC located at the end of the header block. A successful
* return value means that we can trust the header.
*/
int ret = verify_bch(
bch_configuration.header_ecc_capability_bits,
bch_configuration.prim_poly,
data,
HEADER_BLOCK_VERIFY_LEN,
&data[HEADER_BLOCK_ECC_OFF],
HEADER_BLOCK_ECC_LEN);
if (ret < 0) {
return ret;
}
/* Validate header block { id, length, version, type }. */
if ( data[ID] != HEADER_BLOCK_ID
|| data[LEN] != HEADER_BLOCK_LEN
|| data[VER] != 0
|| data[TYP] != 0
|| ntohl(*(uint32_t *)(&data[4])) != HEADER_BLOCK_MAGIC) {
return -EINVAL;
}
uint32_t offset = ntohl(*(uint32_t *)(&data[8]));
uint16_t size_bits = ntohs(*(uint16_t *)(&data[12]));
/* Check that ECC header fits. */
if (offset + 3 >= size) {
return -EINVAL;
}
/* Validate ECC block. */
uint8_t * ecc = &data[offset];
if ( ecc[ID] != ECC_BLOCK_ID
|| ecc[LEN] < BLOCK_SIZE
|| ecc[LEN] + offset > size
|| ecc[LEN] - BLOCK_SIZE != size_bits / 8
|| ecc[VER] != 1
|| ecc[TYP] != 1) {
return -EINVAL;
}
/*
* Use the header block to locate the ECC block and verify the data
* blocks [2] against the ecc block ECC.
*/
ret = verify_bch(
bch_configuration.data_ecc_capability_bits,
bch_configuration.prim_poly,
&data[data[LEN]],
offset - data[LEN],
&data[offset + BLOCK_SIZE],
ecc[LEN] - BLOCK_SIZE);
if (ret < 0) {
return ret;
}
/* Stop after ECC. Ignore possible zero padding. */
size = offset;
for (;;) {
/* Move to next block. */
size -= data[LEN];
data += data[LEN];
if (size == 0) {
/* Finished iterating through blocks. */
return 0;
}
if ( size < BLOCK_SIZE
|| data[LEN] < BLOCK_SIZE) {
/* Not enough data for a header, or short header. */
return -EINVAL;
}
ret = fn(
userdata,
data[ID],
data[VER],
data[TYP],
data[LEN] - BLOCK_SIZE,
&data[BLOCK_SIZE]);
if (ret) {
return ret;
}
}
}
/*
* Copyright 2016 General Electric Company
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include "common.h"
/*
* Read VPD from given data, verify content, and call callback
* for each vital product data block.
*
* Returns Non-zero on error. Negative numbers encode errno.
*/
int vpd_reader(
size_t size,
uint8_t * data,
void * userdata,
int (*fn)(
void * userdata,
uint8_t id,
uint8_t version,
uint8_t type,
size_t size,
uint8_t const * data));
......@@ -299,4 +299,19 @@
#define CONFIG_SYS_I2C_MXC_I2C2
#define CONFIG_SYS_I2C_MXC_I2C3
#define CONFIG_SYS_NUM_I2C_BUSES 9
#define CONFIG_SYS_I2C_MAX_HOPS 1
#define CONFIG_SYS_I2C_BUSES { {0, {I2C_NULL_HOP} }, \
{0, {{I2C_MUX_PCA9547, 0x70, 0} } }, \
{0, {{I2C_MUX_PCA9547, 0x70, 1} } }, \
{0, {{I2C_MUX_PCA9547, 0x70, 2} } }, \
{0, {{I2C_MUX_PCA9547, 0x70, 3} } }, \
{0, {{I2C_MUX_PCA9547, 0x70, 4} } }, \
{0, {{I2C_MUX_PCA9547, 0x70, 5} } }, \
{0, {{I2C_MUX_PCA9547, 0x70, 6} } }, \
{0, {{I2C_MUX_PCA9547, 0x70, 7} } }, \
}
#define CONFIG_BCH
#endif /* __GE_BX50V3_CONFIG_H */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment