[U-Boot] [PATCH v2 3/5] ARM: omap-common: Add standard access for board description EEPROM

Steve Kipisz
2015-11-03T12:22:40Z

From: Lokesh Vutla <lokeshvutla@ti.com>

Several TI EVMs have EEPROM that can contain board description information
such as revision, DDR definition, serial number, etc. In just about all
cases, these EEPROM are on the I2C bus and provides us the opportunity
to centralize the generic operations involved.

The on-board EEPROM on the BeagleBone Black, BeagleBone, AM335x EVM,
AM43x GP EVM, AM57xx-evm, BeagleBoard-X15 share the same format.
However, DRA-7* EVMs, OMAP4SDP use a modified format.

We hence introduce logic which is generic between these platforms
without enforcing any specific format. This allows the boards to use the
relevant format for operations that they might choose.

It is important to note that this logic is fundamental to the board
configuration process such as DDR configuration which is needed in
SPL, hence cannot be part of the standard u-boot driver model (which
is available later in the process). Hence, to aid efficiency, the
eeprom contents are copied over to SRAM scratchpad memory area at the
first invocation to retrieve data.

The follow on patches introduce the use of this library for AM57x
platform support. AM335x/AM43xx cleanups need to first ensure usage of
omap_common prior to switch over to this generic solution.

Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com>
Signed-off-by: Steve Kipisz <s-kipisz2@ti.com>
---
v2 Based on
master a6104737 ARM: at91: sama5: change the environment address to 0x6000

Changes in v2 (since v1)
- make the EEPROM code mor generic for TI EVMs
- rename structures/subroutines to ti_am_xxxxx
- add routines to access the EEPROM data
- redo commit message to be more clear

v1: http://marc.info/?t=144608007900001&r=1&w=2
(mailing list squashed original submission)

arch/arm/cpu/armv7/omap-common/Makefile | 1 +
arch/arm/cpu/armv7/omap-common/ti-i2c-eeprom.c | 148 +++++++++++++++++++++++++
arch/arm/include/asm/omap_common.h | 130 +++++++++++++++++++++-
3 files changed, 278 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/cpu/armv7/omap-common/ti-i2c-eeprom.c

diff --git a/arch/arm/cpu/armv7/omap-common/Makefile b/arch/arm/cpu/armv7/omap-common/Makefile
index 464a5d1d732a..53a9fdb81100 100644
--- a/arch/arm/cpu/armv7/omap-common/Makefile
+++ b/arch/arm/cpu/armv7/omap-common/Makefile
@@ -15,6 +15,7 @@ obj-y += clocks-common.o
obj-y += emif-common.o
obj-y += vc.o
obj-y += abb.o
+obj-$(CONFIG_I2C) += ti-i2c-eeprom.o
endif

ifneq ($(CONFIG_OMAP54XX),)
diff --git a/arch/arm/cpu/armv7/omap-common/ti-i2c-eeprom.c b/arch/arm/cpu/armv7/omap-common/ti-i2c-eeprom.c
new file mode 100644
index 000000000000..f59ebbdb4ee8
--- /dev/null
+++ b/arch/arm/cpu/armv7/omap-common/ti-i2c-eeprom.c
@@ -0,0 +1,148 @@
+/*
+ * Library to support early TI EVM EEPROM handling
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * Lokesh Vutla
+ * Steve Kipisz
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/omap_common.h>
+#include <i2c.h>
+
+/**
+ * ti_i2c_eeprom_init - Initialize an i2c bus and probe for a device
+ * @i2c_bus: i2c bus number to initialize
+ * @dev_addr: Device address to probe for
+ *
+ * Return: 0 on success or corresponding error on failure.
+ */
+static inline int ti_i2c_eeprom_init(int i2c_bus, int dev_addr)
+{
+ int rc;
+
+ rc = i2c_set_bus_num(i2c_bus);
+ if (rc)
+ return rc;
+
+ return i2c_probe(dev_addr);
+}
+
+/**
+ * ti_i2c_eeprom_read - Read data from an EEPROM
+ * @dev_addr: The device address of the EEPROM
+ * @offset: Offset to start reading in the EEPROM
+ * @ep: Pointer to a buffer to read into
+ * @epsize: Size of buffer
+ *
+ * Return: 0 on success or corresponding result of i2c_read
+ */
+static inline int ti_i2c_eeprom_read(int dev_addr, int offset, uchar *ep,
+ int epsize)
+{
+ return i2c_read(dev_addr, offset, 2, ep, epsize);
+}
+
+/**
+ * ti_eeprom_string_cleanup() - Handle eeprom programming errors
+ * @s: eeprom string (should be NULL terminated)
+ *
+ * Some Board manufacturers do not add a NULL termination at the
+ * end of string, instead some binary information is kludged in, hence
+ * convert the string to just printable characters of ASCII chart.
+ */
+static inline void ti_eeprom_string_cleanup(char *s)
+{
+ int i, l;
+
+ l = strlen(s);
+ for (i = 0; i < l; i++, s++)
+ if (*s < ' ' || *s > '~') {
+ *s = 0;
+ break;
+ }
+}
+
+int __maybe_unused ti_i2c_eeprom_am_get(int bus_addr, int dev_addr,
+ struct ti_am_eeprom **epp)
+{
+ int rc;
+ struct ti_am_eeprom *ep;
+
+ if (!epp)
+ return -1;
+
+ ep = TI_AM_EEPROM_DATA;
+ if (ep->header == TI_EEPROM_HEADER_MAGIC)
+ goto already_read;
+
+ /* Initialize with a known bad marker for i2c fails.. */
+ ep->header = 0xADEAD12C;
+
+ gpi2c_init();
+ rc = ti_i2c_eeprom_init(bus_addr, dev_addr);
+ if (rc)
+ return rc;
+ rc = i2c_read(dev_addr, 0x0, 2, (uint8_t *)ep, sizeof(*ep));
+ if (rc)
+ return rc;
+
+ /* Corrupted data??? */
+ if (ep->header != TI_EEPROM_HEADER_MAGIC)
+ return -1;
+
+already_read:
+ *epp = ep;
+
+ return 0;
+}
+
+int __maybe_unused ti_i2c_eeprom_am_get_print(int bus_addr, int dev_addr,
+ struct ti_am_eeprom_printable *p)
+{
+ struct ti_am_eeprom *ep;
+ int rc;
+
+ /* Incase of invalid eeprom contents */
+ p->name[0] = 0x00;
+ p->version[0] = 0x00;
+ p->serial[0] = 0x00;
+
+ rc = ti_i2c_eeprom_am_get(bus_addr, dev_addr, &ep);
+ if (rc)
+ return rc;
+
+ /*
+ * Alas! we have to null terminate and cleanup the strings!
+ */
+ strlcpy(p->name, ep->name, TI_EEPROM_HDR_NAME_LEN);
+ ti_eeprom_string_cleanup(p->name);
+ strlcpy(p->version, ep->version, TI_EEPROM_HDR_NAME_LEN);
+ ti_eeprom_string_cleanup(p->version);
+ strlcpy(p->serial, ep->serial, TI_EEPROM_HDR_NAME_LEN);
+ ti_eeprom_string_cleanup(p->serial);
+ return 0;
+}
+
+void __maybe_unused set_board_info_env(char *name, char *default_name,
+ char *revision, char *serial)
+{
+ char *unknown = "unknown";
+
+ if (name)
+ setenv("board_name", name);
+ else
+ setenv("board_name", default_name);
+
+ if (revision)
+ setenv("board_revision", revision);
+ else
+ setenv("board_revision", unknown);
+
+ if (serial)
+ setenv("board_serial", serial);
+ else
+ setenv("board_serial", unknown);
+}
diff --git a/arch/arm/include/asm/omap_common.h b/arch/arm/include/asm/omap_common.h
index d773b0430ad4..a76c67a85d37 100644
--- a/arch/arm/include/asm/omap_common.h
+++ b/arch/arm/include/asm/omap_common.h
@@ -713,7 +713,9 @@ static inline u8 is_dra72x(void)
#define OMAP_SRAM_SCRATCH_VCORES_PTR (SRAM_SCRATCH_SPACE_ADDR + 0x1C)
#define OMAP_SRAM_SCRATCH_SYS_CTRL (SRAM_SCRATCH_SPACE_ADDR + 0x20)
#define OMAP_SRAM_SCRATCH_BOOT_PARAMS (SRAM_SCRATCH_SPACE_ADDR + 0x24)
-#define OMAP5_SRAM_SCRATCH_SPACE_END (SRAM_SCRATCH_SPACE_ADDR + 0x28)
+#define OMAP_SRAM_SCRATCH_BOARD_EEPROM_START (SRAM_SCRATCH_SPACE_ADDR + 0x28)
+#define OMAP_SRAM_SCRATCH_BOARD_EEPROM_END (SRAM_SCRATCH_SPACE_ADDR + 0x200)
+#define OMAP_SRAM_SCRATCH_SPACE_END (OMAP_SRAM_SCRATCH_BOARD_EEPROM_END)

/* Boot parameters */
#define DEVICE_DATA_OFFSET 0x18
@@ -728,4 +730,130 @@ static inline u8 is_dra72x(void)
u32 omap_sys_boot_device(void);
#endif

+/* Common I2C eeprom definitions */
+#ifndef __ASSEMBLY__
+
+/* TI EEPROM MAGIC Header identifier */
+#define TI_EEPROM_HEADER_MAGIC 0xEE3355AA
+
+#define TI_EEPROM_HDR_NAME_LEN 8
+#define TI_EEPROM_HDR_REV_LEN 12
+#define TI_EEPROM_HDR_SERIAL_LEN 4
+#define TI_EEPROM_HDR_CONFIG_LEN 32
+#define TI_EEPROM_HDR_NO_OF_MAC_ADDR 3
+#define TI_EEPROM_HDR_ETH_ALEN 6
+
+/**
+ * struct ti_am_eeprom - This structure holds data read in from the
+ * AM335x, AM437x, AM57xx TI EVM EEPROMs.
+ * @header: This holds the magic number
+ * @name: The name of the board
+ * @version: Board revision
+ * @serial: Board serial number
+ * @config: Reserved
+ * @mac_addr: Any MAC addresses written in the EEPROM
+ *
+ * The data is this structure is read from the EEPROM on the board.
+ * It is used for board detection which is based on name. It is used
+ * to configure specific TI boards. This allows booting of multiple
+ * TI boards with a single MLO and u-boot.
+ */
+struct ti_am_eeprom {
+ unsigned int header;
+ char name[TI_EEPROM_HDR_NAME_LEN];
+ char version[TI_EEPROM_HDR_REV_LEN];
+ char serial[TI_EEPROM_HDR_SERIAL_LEN];
+ char config[TI_EEPROM_HDR_CONFIG_LEN];
+ char mac_addr[TI_EEPROM_HDR_NO_OF_MAC_ADDR][TI_EEPROM_HDR_ETH_ALEN];
+} __attribute__ ((__packed__));
+
+/**
+ * struct ti_am_eeprom_printable - Null terminated, printable EEPROM contents
+ * @name: NULL terminated name
+ * @version: NULL terminated version
+ * @serial: NULL terminated serial number
+ */
+struct ti_am_eeprom_printable {
+ char name[TI_EEPROM_HDR_NAME_LEN + 1];
+ char version[TI_EEPROM_HDR_REV_LEN + 1];
+ char serial[TI_EEPROM_HDR_SERIAL_LEN + 1];
+};
+#define TI_AM_EEPROM_DATA ((struct ti_am_eeprom *)\
+ OMAP_SRAM_SCRATCH_BOARD_EEPROM_START)
+
+/**
+ * ti_i2c_eeprom_am_get() - Consolidated eeprom data collection for AM* TI EVMs
+ * @bus_addr: I2C bus address
+ * @dev_addr: I2C slave address
+ * @ep: Pointer to eeprom structure
+ *
+ * *ep is populated by the this AM generic function that consolidates
+ * the basic initialization logic common accross all AM* platforms.
+ */
+int ti_i2c_eeprom_am_get(int bus_addr, int dev_addr, struct ti_am_eeprom **epp);
+
+/**
+ * ti_i2c_eeprom_am_get_print() - Get a printable representation of eeprom data
+ * @bus_addr: I2C bus address
+ * @dev_addr: I2C slave address
+ * @p: Pointer to eeprom structure
+ *
+ * This reads the eeprom and converts the data into a printable string for
+ * further processing.
+ */
+int ti_i2c_eeprom_am_get_print(int bus_addr, int dev_addr,
+ struct ti_am_eeprom_printable *p);
+
+/**
+ * set_board_info_env() - Setup commonly used board information environment vars
+ * @name: Name of the board
+ * @default_name: In case of empty string, what name to use?
+ * @revision: Revision of the board
+ * @serial: Serial Number of the board
+ *
+ * In case of NULL revision or serial information "unknown" is setup.
+ * If name is NULL, default_name is used.
+ */
+void set_board_info_env(char *name, char *default_name,
+ char *revision, char *serial);
+
+/**
+ * board_am_is() - Generic Board detection logic
+ * @name_tag: Tag used in eeprom for the board
+ *
+ * Return: false if board information does not match OR eeprom was'nt read.
+ * true otherwise
+ */
+static inline bool board_am_is(char *name_tag)
+{
+ struct ti_am_eeprom *ep = TI_AM_EEPROM_DATA;
+
+ if (ep->header != TI_EEPROM_HEADER_MAGIC)
+ return false;
+ return !strncmp(ep->name, name_tag, TI_EEPROM_HDR_NAME_LEN);
+}
+
+/**
+ * board_am_rev_is() - Compare board revision
+ * @rev_tag: Revision tag to check in eeprom
+ * @cmp_len: How many chars to compare?
+ *
+ * NOTE: revision information is often messed up (hence the str len match) :(
+ *
+ * Return: false if board information does not match OR eeprom was'nt read.
+ * true otherwise
+ */
+static inline bool board_am_rev_is(char *rev_tag, int cmp_len)
+{
+ struct ti_am_eeprom *ep = TI_AM_EEPROM_DATA;
+ int l;
+
+ if (ep->header != TI_EEPROM_HEADER_MAGIC)
+ return false;
+
+ l = cmp_len > TI_EEPROM_HDR_REV_LEN ? TI_EEPROM_HDR_NAME_LEN : cmp_len;
+ return !strncmp(ep->version, rev_tag, l);
+}
+#endif /* __ASSEMBLY__ */
+
#endif /* _OMAP_COMMON_H_ */
--
1.9.1