]> Git Repo - u-boot.git/blob - board/kontron/sl28/cmds.c
global: Convert simple_strtoul() with hex to hextoul()
[u-boot.git] / board / kontron / sl28 / cmds.c
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
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
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
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
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
124 static int ufm_read(struct udevice *dev, u16 *data)
125 {
126         return ufm_shift_data(dev, 0, data);
127 }
128
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 = hextoul(argv[1], &endp);
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));
This page took 0.034899 seconds and 4 git commands to generate.