mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-28 15:13:31 +08:00
board: sl28: add board specific nvm command
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 <michael@walle.cc> Tested-by: Heiko Thiery <heiko.thiery@gmail.com> Reviewed-by: Priyanka Jain <priyanka.jain@nxp.com>
This commit is contained in:
parent
4ceb5c6db2
commit
66d00ebdea
@ -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
|
||||
|
178
board/kontron/sl28/cmds.c
Normal file
178
board/kontron/sl28/cmds.c
Normal file
@ -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));
|
Loading…
Reference in New Issue
Block a user