Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | // SPDX-License-Identifier: GPL-2.0+ /* * sl28 extension commands * * Copyright (c) 2020 Kontron Europe GmbH */ #include <command.h> #include <i2c.h> #include <vsprintf.h> #include <linux/delay.h> #include <linux/errno.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 = hextoul(argv[1], &endp); 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; } U_BOOT_LONGHELP(sl28, "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)); |