1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * sl28 extension commands
4 *
5 * Copyright (c) 2020 Kontron Europe GmbH
6 */
7
8 #include <common.h>
9 #include <command.h>
10 #include <i2c.h>
11 #include <linux/delay.h>
12
13 #define CPLD_I2C_ADDR 0x4a
14 #define REG_UFM_CTRL 0x02
15 #define UFM_CTRL_DCLK BIT(1)
16 #define UFM_CTRL_DIN BIT(2)
17 #define UFM_CTRL_PROGRAM BIT(3)
18 #define UFM_CTRL_ERASE BIT(4)
19 #define UFM_CTRL_DSHIFT BIT(5)
20 #define UFM_CTRL_DOUT BIT(6)
21 #define UFM_CTRL_BUSY BIT(7)
22
ufm_shift_data(struct udevice * dev,u16 data_in,u16 * data_out)23 static int ufm_shift_data(struct udevice *dev, u16 data_in, u16 *data_out)
24 {
25 int i;
26 int ret;
27 u16 data = 0;
28
29 /* latch data */
30 ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, 0);
31 if (ret < 0)
32 return ret;
33 ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DCLK);
34 if (ret < 0)
35 return ret;
36
37 /* assert drshift */
38 ret = dm_i2c_reg_write(dev, REG_UFM_CTRL,
39 UFM_CTRL_DSHIFT | UFM_CTRL_DCLK);
40 if (ret < 0)
41 return ret;
42
43 /* clock 16 data bits, reverse order */
44 for (i = 15; i >= 0; i--) {
45 u8 din = (data_in & (1 << i)) ? UFM_CTRL_DIN : 0;
46
47 ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DSHIFT
48 | din);
49 if (ret < 0)
50 return ret;
51 if (data_out) {
52 ret = dm_i2c_reg_read(dev, REG_UFM_CTRL);
53 if (ret < 0)
54 return ret;
55 if (ret & UFM_CTRL_DOUT)
56 data |= (1 << i);
57 }
58 ret = dm_i2c_reg_write(dev, REG_UFM_CTRL,
59 UFM_CTRL_DSHIFT | UFM_CTRL_DCLK | din);
60 if (ret < 0)
61 return ret;
62 }
63
64 /* deassert drshift */
65 ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DCLK);
66 if (ret < 0)
67 return ret;
68
69 if (data_out)
70 *data_out = data;
71
72 return ret;
73 }
74
ufm_erase(struct udevice * dev)75 static int ufm_erase(struct udevice *dev)
76 {
77 int ret;
78
79 /* erase, tEPMX is 500ms */
80 ret = dm_i2c_reg_write(dev, REG_UFM_CTRL,
81 UFM_CTRL_DCLK | UFM_CTRL_ERASE);
82 if (ret < 0)
83 return ret;
84 ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DCLK);
85 if (ret < 0)
86 return ret;
87 mdelay(500);
88
89 return 0;
90 }
91
ufm_program(struct udevice * dev)92 static int ufm_program(struct udevice *dev)
93 {
94 int ret;
95
96 /* program, tPPMX is 100us */
97 ret = dm_i2c_reg_write(dev, REG_UFM_CTRL,
98 UFM_CTRL_DCLK | UFM_CTRL_PROGRAM);
99 if (ret < 0)
100 return ret;
101 ret = dm_i2c_reg_write(dev, REG_UFM_CTRL, UFM_CTRL_DCLK);
102 if (ret < 0)
103 return ret;
104 udelay(100);
105
106 return 0;
107 }
108
ufm_write(struct udevice * dev,u16 data)109 static int ufm_write(struct udevice *dev, u16 data)
110 {
111 int ret;
112
113 ret = ufm_shift_data(dev, data, NULL);
114 if (ret < 0)
115 return ret;
116
117 ret = ufm_erase(dev);
118 if (ret < 0)
119 return ret;
120
121 return ufm_program(dev);
122 }
123
ufm_read(struct udevice * dev,u16 * data)124 static int ufm_read(struct udevice *dev, u16 *data)
125 {
126 return ufm_shift_data(dev, 0, data);
127 }
128
do_sl28_nvm(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])129 static int do_sl28_nvm(struct cmd_tbl *cmdtp, int flag, int argc,
130 char *const argv[])
131 {
132 struct udevice *dev;
133 u16 nvm;
134 int ret;
135 char *endp;
136
137 if (i2c_get_chip_for_busnum(0, CPLD_I2C_ADDR, 1, &dev))
138 return CMD_RET_FAILURE;
139
140 if (argc > 1) {
141 nvm = simple_strtoul(argv[1], &endp, 16);
142 if (*endp != '\0') {
143 printf("ERROR: argument is not a valid number\n");
144 ret = -EINVAL;
145 goto out;
146 }
147
148 /*
149 * We swap all bits, because the a zero bit in hardware means the
150 * feature is enabled. But this is hard for the user.
151 */
152 nvm ^= 0xffff;
153
154 ret = ufm_write(dev, nvm);
155 if (ret)
156 goto out;
157 printf("New settings will be activated after the next power cycle!\n");
158 } else {
159 ret = ufm_read(dev, &nvm);
160 if (ret)
161 goto out;
162 nvm ^= 0xffff;
163
164 printf("%04hx\n", nvm);
165 }
166
167 return CMD_RET_SUCCESS;
168
169 out:
170 printf("command failed (%d)\n", ret);
171 return CMD_RET_FAILURE;
172 }
173
174 static char sl28_help_text[] =
175 "nvm [<hex>] - display/set the 16 non-volatile bits\n";
176
177 U_BOOT_CMD_WITH_SUBCMDS(sl28, "SMARC-sAL28 specific", sl28_help_text,
178 U_BOOT_SUBCMD_MKENT(nvm, 2, 1, do_sl28_nvm));
179