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