xref: /openbmc/u-boot/cmd/aspeed/peci.c (revision 110fa72f)
1 #include <common.h>
2 #include <command.h>
3 #include <string.h>
4 #include <asm/io.h>
5 
6 #define AST_SCU				(0x1e6e2000)
7 #if defined(CONFIG_ASPEED_AST2500)
8 #define AST_SCU_SYSRST_CTRL (AST_SCU + 0x04)
9 #elif defined(CONFIG_ASPEED_AST2600)
10 #define AST_SCU_SYSRST_CLR2	(AST_SCU + 0x54)
11 #endif
12 
13 #define AST_PECI			(0x1e78b000)
14 #define AST_PECI_CTRL		(AST_PECI + 0x00)
15 #define AST_PECI_TIMING		(AST_PECI + 0x04)
16 #define AST_PECI_CMD		(AST_PECI + 0x08)
17 #define AST_PECI_RW_LEN		(AST_PECI + 0x0C)
18 #define AST_PECI_EXP_FCS	(AST_PECI + 0x10)
19 #define AST_PECI_CAP_FCS	(AST_PECI + 0x14)
20 #define AST_PECI_INT_CTRL	(AST_PECI + 0x18)
21 #define AST_PECI_INT_STAT	(AST_PECI + 0x1C)
22 #define AST_PECI_WR_DATA0	(AST_PECI + 0x20)
23 #define AST_PECI_WR_DATA1	(AST_PECI + 0x24)
24 #define AST_PECI_WR_DATA2	(AST_PECI + 0x28)
25 #define AST_PECI_WR_DATA3	(AST_PECI + 0x2C)
26 #define AST_PECI_RD_DATA0	(AST_PECI + 0x30)
27 #define AST_PECI_RD_DATA1	(AST_PECI + 0x34)
28 #define AST_PECI_RD_DATA2	(AST_PECI + 0x38)
29 #define AST_PECI_RD_DATA3	(AST_PECI + 0x3C)
30 #define AST_PECI_WR_DATA4	(AST_PECI + 0x40)
31 #define AST_PECI_WR_DATA5	(AST_PECI + 0x44)
32 #define AST_PECI_WR_DATA6	(AST_PECI + 0x48)
33 #define AST_PECI_WR_DATA7	(AST_PECI + 0x4C)
34 #define AST_PECI_RD_DATA4	(AST_PECI + 0x50)
35 #define AST_PECI_RD_DATA5	(AST_PECI + 0x54)
36 #define AST_PECI_RD_DATA6	(AST_PECI + 0x58)
37 #define AST_PECI_RD_DATA7	(AST_PECI + 0x5C)
38 
39 #ifdef CONFIG_ASPEED_AST2500
40 static void ast2500_peci_init(void)
41 {
42 	unsigned int val;
43 	static bool is_init = false;
44 
45 	/* init once only */
46 	if (is_init)
47 		return;
48 
49 	/* release PECI reset */
50 	val = readl(AST_SCU_SYSRST_CTRL);
51 	val &= ~(0x1 << 10);
52 	writel(val, AST_SCU_SYSRST_CTRL);
53 
54 	/*
55 	 * 1. disable PECI
56 	 * 2. set source clock to 24Mhz oscillator
57 	 * 3. set clock divider to 8
58 	 */
59 	writel(0x80301, AST_PECI_CTRL);
60 
61 	/* set negotiate timing */
62 	writel(0x4040, AST_PECI_TIMING);
63 
64 	/* enable interrupt */
65 	writel(0x1f, AST_PECI_INT_CTRL);
66 
67 	/* enable PECI */
68 	writel(0x80311, AST_PECI_CTRL);
69 
70 	is_init = true;
71 }
72 #endif
73 
74 #ifdef CONFIG_ASPEED_AST2600
75 static void ast2600_peci_init(void)
76 {
77 	static bool is_init = false;
78 
79 	/* init once only */
80 	if (is_init)
81 		return;
82 
83 	/* release PECI reset */
84 	writel(0x10, AST_SCU_SYSRST_CLR2);
85 
86 	/*
87 	 * 1. disable PECI
88 	 * 2. set source clock to HCLK
89 	 * 3. set clock divider to 2
90 	 * 4. set 32-byte mode
91 	 */
92 	writel(0x80901, AST_PECI_CTRL);
93 
94 	/* set negotiate timing */
95 	writel(0x0303, AST_PECI_TIMING);
96 
97 	/* enable interrupt */
98 	writel(0x1f, AST_PECI_INT_CTRL);
99 
100 	/* enable PECI */
101 	writel(0x80911, AST_PECI_CTRL);
102 
103 	is_init = true;
104 }
105 #endif
106 
107 static void ast_peci_init(void)
108 {
109 #if defined(CONFIG_ASPEED_AST2500)
110 	return ast2500_peci_init();
111 #elif defined(CONFIG_ASPEED_AST2600)
112 	return ast2600_peci_init();
113 #endif
114 }
115 
116 static int do_ast_peci_ping(unsigned long client_addr)
117 {
118 	unsigned int rec_wfcs;
119 	unsigned int exp_wfcs;
120 
121 	unsigned long val;
122 	unsigned long retry = 10;
123 
124 	ast_peci_init();
125 
126 	/* make PECI no operation */
127 	writel(0x0, AST_PECI_CMD);
128 
129 	/* set client address */
130 	writel((client_addr & 0xff), AST_PECI_RW_LEN);
131 
132 	/* clear interrupt status */
133 	writel(0x1f, AST_PECI_INT_STAT);
134 
135 	/* fire PECI command */
136 	writel(0x1, AST_PECI_CMD);
137 
138 	/* wait PECI done for 10 seconds */
139 	printf("Waiting PECI ... ");
140 	while (retry) {
141 		val = readl(AST_PECI_INT_STAT);
142 		if (val & 0x1)
143 			break;
144 		printf("Retry ... ");
145 		udelay(1000000);
146 		--retry;
147 	}
148 
149 	if (retry == 0) {
150 		printf("Timeout\n");
151 		goto ping_clean_and_exit;
152 	}
153 	printf("Done\n");
154 
155 	/* compare the expected WFCS and the captured one */
156 	exp_wfcs = readl(AST_PECI_EXP_FCS) & 0xFF;
157 	rec_wfcs = readl(AST_PECI_CAP_FCS) & 0xFF;
158 	if (exp_wfcs != rec_wfcs)
159 		printf("Mismatched WFCS: %02x, expected %02x\n", rec_wfcs, exp_wfcs);
160 	else
161 		printf("Ping: ACK\n");
162 
163 ping_clean_and_exit:
164 	writel(0x1f, AST_PECI_INT_STAT);
165 	writel(0x0, AST_PECI_CMD);
166 
167 	return 0;
168 }
169 
170 static int do_ast_peci_getdib(unsigned long client_addr)
171 {
172 	unsigned char dib[8];
173 
174 	unsigned long val;
175 	unsigned long retry = 10;
176 
177 	unsigned int rec_wfcs;
178 	unsigned int exp_wfcs;
179 
180 	unsigned int rec_rfcs;
181 	unsigned int exp_rfcs;
182 
183 	ast_peci_init();
184 
185 	memset(dib, 0, sizeof(dib));
186 
187 	/* make PECI no operation */
188 	writel(0x0, AST_PECI_CMD);
189 
190 	/* prepare GetDIB command code 0xF7 */
191 	writel(0xf7, AST_PECI_WR_DATA0);
192 	writel(0x00, AST_PECI_WR_DATA1);
193 	writel(0x00, AST_PECI_WR_DATA2);
194 	writel(0x00, AST_PECI_WR_DATA3);
195 	writel(0x00, AST_PECI_WR_DATA4);
196 	writel(0x00, AST_PECI_WR_DATA5);
197 	writel(0x00, AST_PECI_WR_DATA6);
198 	writel(0x00, AST_PECI_WR_DATA7);
199 
200 	/*
201 	 * 1. set read length to 8 bytes
202 	 * 2. set write length to 1 bytes
203 	 * 3. set client address
204 	 */
205 	writel((0x80100 | (client_addr & 0xff)), AST_PECI_RW_LEN);
206 
207 	/* clear interrupt status */
208 	writel(0x1f, AST_PECI_INT_STAT);
209 
210 	/* fire PECI command */
211 	writel(0x1, AST_PECI_CMD);
212 
213 	/* wait PECI done for 10 seconds */
214 	printf("Waiting PECI ... ");
215 	while (retry) {
216 		val = readl(AST_PECI_INT_STAT);
217 		if (val & 0x1)
218 			break;
219 		printf("Retry ... ");
220 		udelay(1000000);
221 		--retry;
222 	}
223 
224 	if (retry == 0) {
225 		printf("Timeout\n");
226 		goto getdib_clean_and_exit;
227 	}
228 	printf("Done\n");
229 
230 	/* compare the expected WFCS and the captured one */
231 	exp_wfcs = readl(AST_PECI_EXP_FCS) & 0xFF;
232 	rec_wfcs = readl(AST_PECI_CAP_FCS) & 0xFF;
233 	if (exp_wfcs != rec_wfcs) {
234 		printf("Mismatched WFCS: %02x, expected %02x\n",
235 			   rec_wfcs, exp_wfcs);
236 		goto getdib_clean_and_exit;
237 	}
238 
239 	/* compare the expected RFCS and the captured one */
240 	exp_rfcs = readl(AST_PECI_EXP_FCS) & 0xFF0000;
241 	rec_rfcs = readl(AST_PECI_CAP_FCS) & 0xFF0000;
242 	if (exp_rfcs != rec_rfcs) {
243 		printf("Mismatched RFCS: %02x, expected %02x\n",
244 			   rec_rfcs, exp_rfcs);
245 		goto getdib_clean_and_exit;
246 	}
247 
248 	*((unsigned int*)dib) = readl(AST_PECI_RD_DATA0);
249 	*((unsigned int*)(dib + 4)) = readl(AST_PECI_RD_DATA1);
250 
251 	printf("DIB: %02x %02x %02x %02x %02x %02x %02x %02x\n",
252 		   dib[0], dib[1], dib[2], dib[3],
253 		   dib[4], dib[5], dib[6], dib[7]);
254 
255 getdib_clean_and_exit:
256 	writel(0x1f, AST_PECI_INT_STAT);
257 	writel(0x0, AST_PECI_CMD);
258 
259 	return 0;
260 }
261 
262 static int do_ast_peci(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
263 {
264 	char *peci_cmd;
265 	unsigned long client_addr;
266 
267 	if (argc < 3)
268 		return CMD_RET_USAGE;
269 
270 	peci_cmd = argv[1];
271 	client_addr = strtoul(argv[2], 0, 0);
272 
273 	if (client_addr > 0xFF) {
274 		printf("Invalid client address: %lu\n", client_addr);
275 		return CMD_RET_USAGE;
276 	}
277 
278 	if (!strcmp(peci_cmd, "ping"))
279 		return do_ast_peci_ping(client_addr);
280 
281 	if (!strcmp(peci_cmd, "getdib"))
282 		return do_ast_peci_getdib(client_addr);
283 
284 	return CMD_RET_USAGE;
285 }
286 
287 U_BOOT_CMD(peci, 3, 0, do_ast_peci,
288 		   "ASPEED PECI general bus command test program",
289 		   "ping <client_addr> - Ping to check if the device at the targeted address can respond\n"
290 		   "peci getdib <client_addr> - Get 8-byte Device Information Bytes\n"
291 		   );
292