The board supports 16 configuration bits which can be manipulated with this command. See the board's README for a detailed explanation on each bit.
Signed-off-by: Michael Walle <mich...@walle.cc> --- board/kontron/sl28/Makefile | 2 +- board/kontron/sl28/README | 79 ++++++++++++++++ board/kontron/sl28/cmds.c | 178 ++++++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 board/kontron/sl28/cmds.c diff --git a/board/kontron/sl28/Makefile b/board/kontron/sl28/Makefile index 0f1866c874..74d8012f0f 100644 --- a/board/kontron/sl28/Makefile +++ b/board/kontron/sl28/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ ifndef CONFIG_SPL_BUILD -obj-y += sl28.o +obj-y += sl28.o cmds.o endif obj-y += common.o ddr.o diff --git a/board/kontron/sl28/README b/board/kontron/sl28/README index 3ddd9aeeb8..40f3197716 100644 --- a/board/kontron/sl28/README +++ b/board/kontron/sl28/README @@ -57,6 +57,85 @@ u-boot (yet). But you can use the i2c command to access it: > i2c md 4a 3.1 1 +Non-volatile Board Configuration Bits +===================================== + +The board has 16 configuration bits which are stored in the CPLD and are +non-volatile. These can be changed by the `sl28 nvm` command. + +| Bit | Description | +| --- | --------------------------------------------------------------- | +| 0 | Power-on inhibit | +| 1 | Enable eMMC boot | +| 2 | Enable watchdog by default | +| 3 | Disable failsafe watchdog by default | +| 4 | Clock generator selection bit 0 | +| 5 | Clock generator selection bit 1 | +| 6 | Disable CPU SerDes clock #2 and PCIe-A clock output | +| 7 | Disable PCIe-B and PCIe-C clock output | +| 8 | Keep onboard PHYs in reset | +| 9 | Keep USB hub in reset | +| 10 | Keep eDP-to-LVDS converter in reset | +| 11 | Enable I2C stuck recovery on I2C PM and I2C GP busses | +| 12 | Enable automatic onboard PHY H/W reset | +| 13 | reserved | +| 14 | Used by the RCW to determine boot source | +| 15 | Used by the RCW to determine boot source | + +Please note, that if the board is in failsafe mode, the bits will have the +factory defaults, ie. all bits are off. + +Power-On Inhibit +---------------- + +If this is set, the board doesn't automatically turn on when power is +applied. Instead, the user has to either toggle the `PWR_BTN#` line or +use any other wake-up source such as RTC alarm or Wake-on-LAN. + +eMMC Boot +--------- + +If this is set, the RCW will be fetched from the on-board eMMC at offset +1MiB. For further details, have a look at the [SMARC-sAL28 Reset +Configuration Word documentation][1]. + +Watchdog +-------- + +By default, the CPLD watchdog is enabled in failsafe mode. Using bits 2 and +3, the user can change its mode or disable it altogether. + +| Bit 2 | Bit 3 | Description | +| ----- | ----- | ------------------------------- | +| 0 | 0 | Watchdog enabled, failsafe mode | +| 0 | 1 | Watchdog disabled | +| 1 | 0 | Watchdog enabled, failsafe mode | +| 1 | 1 | Watchdog enabled, normal mode | + +Clock Generator Select +---------------------- + +The board is prepared to supply different SerDes clock speeds. But for now, +only setting 0 is supported, otherwise the CPU will hang because the PLL +will not lock. + +Clock Output Disable And Keep Devices In Reset +---------------------------------------------- + +To safe power, the user might disable different devices and clock output of +the board. It is not supported to disable the "CPU SerDes clock #2" for +now, otherwise the CPU will hang because the PLL will not lock. + +Automatic reset of the onboard PHYs +----------------------------------- + +By default, there is no hardware reset of the onboard PHY. This is because +for Wake-on-LAN, some registers have to retain their values. If you don't +use the WOL feature and a soft reset of the PHY is not enough you can +enable the hardware reset. The onboard PHY hardware reset follows the +power-on reset. + + Vendor Documentation ==================== diff --git a/board/kontron/sl28/cmds.c b/board/kontron/sl28/cmds.c new file mode 100644 index 0000000000..046d3b4903 --- /dev/null +++ b/board/kontron/sl28/cmds.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * sl28 extension commands + * + * Copyright (c) 2020 Kontron Europe GmbH + */ + +#include <common.h> +#include <command.h> +#include <i2c.h> +#include <linux/delay.h> + +#define CPLD_I2C_ADDR 0x4a +#define REG_UFM_CTRL 0x02 +#define UFM_CTRL_DCLK BIT(1) +#define UFM_CTRL_DIN BIT(2) +#define UFM_CTRL_PROGRAM BIT(3) +#define UFM_CTRL_ERASE BIT(4) +#define UFM_CTRL_DSHIFT BIT(5) +#define UFM_CTRL_DOUT BIT(6) +#define UFM_CTRL_BUSY BIT(7) + +static int ufm_shift_data(struct udevice *dev, u16 data_in, u16 *data_out) +{ + int i; + int ret; + u16 data = 0; + + /* latch data */ + ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, 0); + if (ret < 0) + return ret; + ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DCLK); + if (ret < 0) + return ret; + + /* assert drshift */ + ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, + UFM_CTRL_DSHIFT | UFM_CTRL_DCLK); + if (ret < 0) + return ret; + + /* clock 16 data bits, reverse order */ + for (i = 15; i >= 0; i--) { + u8 din = (data_in & (1 << i)) ? UFM_CTRL_DIN : 0; + + ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DSHIFT + | din); + if (ret < 0) + return ret; + if (data_out) { + ret = dm_i2c_reg_read(dev, REG_UFM_CTRL); + if (ret < 0) + return ret; + if (ret & UFM_CTRL_DOUT) + data |= (1 << i); + } + ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, + UFM_CTRL_DSHIFT | UFM_CTRL_DCLK | din); + if (ret < 0) + return ret; + } + + /* deassert drshift */ + ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DCLK); + if (ret < 0) + return ret; + + if (data_out) + *data_out = data; + + return ret; +} + +static int ufm_erase(struct udevice *dev) +{ + int ret; + + /* erase, tEPMX is 500ms */ + ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, + UFM_CTRL_DCLK | UFM_CTRL_ERASE); + if (ret < 0) + return ret; + ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DCLK); + if (ret < 0) + return ret; + mdelay(500); + + return 0; +} + +static int ufm_program(struct udevice *dev) +{ + int ret; + + /* program, tPPMX is 100us */ + ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, + UFM_CTRL_DCLK | UFM_CTRL_PROGRAM); + if (ret < 0) + return ret; + ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DCLK); + if (ret < 0) + return ret; + udelay(100); + + return 0; +} + +static int ufm_write(struct udevice *dev, u16 data) +{ + int ret; + + ret = ufm_shift_data(dev, data, NULL); + if (ret < 0) + return ret; + + ret = ufm_erase(dev); + if (ret < 0) + return ret; + + return ufm_program(dev); +} + +static int ufm_read(struct udevice *dev, u16 *data) +{ + return ufm_shift_data(dev, 0, data); +} + +static int do_sl28_nvm(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + u16 nvm; + int ret; + char *endp; + + if (i2c_get_chip_for_busnum(0, CPLD_I2C_ADDR, 1, &dev)) + return CMD_RET_FAILURE; + + if (argc > 1) { + nvm = simple_strtoul(argv[1], &endp, 16); + if (*endp != '\0') { + printf("ERROR: argument is not a valid number\n"); + ret = -EINVAL; + goto out; + } + + /* + * We swap all bits, because the a zero bit in hardware means the + * feature is enabled. But this is hard for the user. + */ + nvm ^= 0xffff; + + ret = ufm_write(dev, nvm); + if (ret) + goto out; + printf("New settings will be activated after the next power cycle!\n"); + } else { + ret = ufm_read(dev, &nvm); + if (ret) + goto out; + nvm ^= 0xffff; + + printf("%04hx\n", nvm); + } + + return CMD_RET_SUCCESS; + +out: + printf("command failed (%d)\n", ret); + return CMD_RET_FAILURE; +} + +static char sl28_help_text[] = + "nvm [<hex>] - display/set the 16 non-volatile bits\n"; + +U_BOOT_CMD_WITH_SUBCMDS(sl28, "SMARC-sAL28 specific", sl28_help_text, + U_BOOT_SUBCMD_MKENT(nvm, 2, 1, do_sl28_nvm)); -- 2.20.1