blob: 1aa224beab0276dd1c33f1edb8570e5f4946582d [file] [log] [blame]
Norman James6a58a272015-10-07 14:34:16 -05001#define _GNU_SOURCE /* for strcasestr */
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <fcntl.h>
6#include <sys/mman.h>
7#include <sys/types.h>
8#include <sys/stat.h>
9#include <unistd.h>
10#include <byteswap.h>
11#include <stdint.h>
12#include <stdbool.h>
13#include <getopt.h>
14#include <limits.h>
15#include <arpa/inet.h>
16#include <assert.h>
17
18#include "io.h"
19
20/* Big endian warning/note:
21 *
22 * The register accessors return byteswapped data for registers
23 */
24uint32_t (*ast_ahb_readl)(uint32_t offset);
25void (*ast_ahb_writel)(uint32_t val, uint32_t offset);
26int (*ast_copy_to_ahb)(uint32_t reg, const void *src, uint32_t len);
27int (*ast_copy_from_ahb)(void *dst, uint32_t reg, uint32_t len);
28
29static enum ppc_platform {
30 plat_unknown,
31 plat_rhesus,
32 plat_ast_bmc,
33} ppc_platform;
34
35static int lpc_io_fd = -1, lpc_fw_fd = -1;
36static uint32_t lpc_old_flash_reg;
37static uint32_t ahb_flash_base, ahb_flash_size, lpc_flash_offset;
38
39static void lpc_outb(uint8_t val, uint16_t port)
40{
41 int rc;
42
43 lseek(lpc_io_fd, port, SEEK_SET);
44 rc = write(lpc_io_fd, &val, 1);
45 if (rc != 1) {
46 perror("Can't write to LPC IO");
47 exit(1);
48 }
49}
50
51static uint8_t lpc_inb(uint16_t port)
52{
53 uint8_t val;
54 int rc;
55
56 lseek(lpc_io_fd, port, SEEK_SET);
57 rc = read(lpc_io_fd, &val, 1);
58 if (rc != 1) {
59 perror("Can't read from LPC IO");
60 exit(1);
61 }
62 return val;
63}
64
65int lpc_fw_write32(uint32_t val, uint32_t addr)
66{
67 int rc;
68
69 /* The value passed in is in big endian always */
70 lseek(lpc_fw_fd, addr, SEEK_SET);
71 rc = write(lpc_fw_fd, &val, 4);
72 if (rc != 4) {
73 perror("Can't write to LPC FW");
74 exit(1);
75 }
76 return 0;
77}
78
79int lpc_fw_read32(uint32_t *val, uint32_t addr)
80{
81 int rc;
82
83 lseek(lpc_fw_fd, addr, SEEK_SET);
84 rc = read(lpc_fw_fd, val, 4);
85 if (rc != 4) {
86 perror("Can't read from LPC FW");
87 exit(1);
88 }
89 return 0;
90}
91
92static void lpc_sio_outb(uint8_t val, uint8_t reg)
93{
94 lpc_outb(reg, 0x2e);
95 lpc_outb(val, 0x2f);
96}
97
98static uint8_t lpc_sio_inb(uint8_t reg)
99{
100 lpc_outb(reg, 0x2e);
101 return lpc_inb(0x2f);
102}
103
104static void lpc_ahb_prep(uint32_t reg, uint8_t type)
105{
106 /* Address */
107 lpc_sio_outb((reg >> 24) & 0xff, 0xf0);
108 lpc_sio_outb((reg >> 16) & 0xff, 0xf1);
109 lpc_sio_outb((reg >> 8) & 0xff, 0xf2);
110 lpc_sio_outb((reg ) & 0xff, 0xf3);
111
112 /* 4 bytes cycle */
113 lpc_sio_outb(type, 0xf8);
114}
115
116static void lpc_ahb_writel(uint32_t val, uint32_t reg)
117{
118 lpc_ahb_prep(reg, 2);
119
120 /* Write data */
121 lpc_sio_outb(val >> 24, 0xf4);
122 lpc_sio_outb(val >> 16, 0xf5);
123 lpc_sio_outb(val >> 8, 0xf6);
124 lpc_sio_outb(val , 0xf7);
125
126 /* Trigger */
127 lpc_sio_outb(0xcf, 0xfe);
128}
129
130static uint32_t lpc_ahb_readl(uint32_t reg)
131{
132 uint32_t val = 0;
133
134 lpc_ahb_prep(reg, 2);
135
136 /* Trigger */
137 lpc_sio_inb(0xfe);
138
139 /* Read results */
140 val = (val << 8) | lpc_sio_inb(0xf4);
141 val = (val << 8) | lpc_sio_inb(0xf5);
142 val = (val << 8) | lpc_sio_inb(0xf6);
143 val = (val << 8) | lpc_sio_inb(0xf7);
144
145 return val;
146}
147
148static void lpc_ahb_init(bool bmc_flash)
149{
150 uint32_t b;
151
152 /* Send SuperIO password */
153 lpc_outb(0xa5, 0x2e);
154 lpc_outb(0xa5, 0x2e);
155
156 /* Select logical dev d */
157 lpc_sio_outb(0x0d, 0x07);
158
159 /* Enable iLPC->AHB */
160 lpc_sio_outb(0x01, 0x30);
161
162 /* Save flash base */
163 lpc_old_flash_reg = b = lpc_ahb_readl(LPC_CTRL_BASE + 0x88);
164 /* Upate flash base */
165 if (bmc_flash) {
166 ahb_flash_base = BMC_FLASH_BASE;
167 ahb_flash_size = BMC_FLASH_SIZE;
168 } else {
169 ahb_flash_base = PNOR_FLASH_BASE;
170 ahb_flash_size = PNOR_FLASH_SIZE;
171 }
172 lpc_flash_offset = 0x0e000000;
173 b = (b & 0x0000ffff) | ahb_flash_base;
174 lpc_ahb_writel(b, LPC_CTRL_BASE + 0x88);
175 b = lpc_ahb_readl(LPC_CTRL_BASE + 0x88);
176}
177
178static int lpc_ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len)
179{
180 int rc;
181
182 if (reg < ahb_flash_base ||
183 (reg + len) > (ahb_flash_base + ahb_flash_size))
184 return -1;
185 reg = (reg - ahb_flash_base) + lpc_flash_offset;
186
187 lseek(lpc_fw_fd, reg, SEEK_SET);
188 rc = read(lpc_fw_fd, dst, len);
189 if (rc != len) {
190 perror("Can't read bulk from LPC FW");
191 exit(1);
192 }
193 return 0;
194}
195
196static int lpc_ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len)
197{
198 int rc;
199
200 if (reg < ahb_flash_base ||
201 (reg + len) > (ahb_flash_base + ahb_flash_size))
202 return -1;
203 reg = (reg - ahb_flash_base) + lpc_flash_offset;
204
205 lseek(lpc_fw_fd, reg, SEEK_SET);
206 rc = write(lpc_fw_fd, src, len);
207 if (rc != len) {
208 perror("Can't write bulk from LPC FW");
209 exit(1);
210 }
211 return 0;
212}
213
214/*
215 * Write protect: TODO use custom IPMI to control lock from BMC
216 */
217static uint32_t lpc_gpio_ctl_readl(uint32_t offset)
218{
219 return lpc_ahb_readl(GPIO_CTRL_BASE + offset);
220}
221
222static void lpc_gpio_ctl_writel(uint32_t val, uint32_t offset)
223{
224 lpc_ahb_writel(val, GPIO_CTRL_BASE + offset);
225}
226
227bool set_wrprotect(bool protect)
228{
229 uint32_t reg;
230 bool was_protected;
231
232 if (ppc_platform != plat_ast_bmc)
233 return false;
234
235 reg = lpc_gpio_ctl_readl(0x20);
236 was_protected = !!(reg & 0x00004000);
237 if (protect)
238 reg |= 0x00004000; /* GPIOF[6] value */
239 else
240 reg &= ~0x00004000; /* GPIOF[6] value */
241 lpc_gpio_ctl_writel(reg, 0x20);
242 reg = lpc_gpio_ctl_readl(0x24);
243 reg |= 0x00004000; /* GPIOF[6] direction */
244 lpc_gpio_ctl_writel(reg, 0x24);
245
246 return was_protected;
247}
248
249static void open_lpc(bool bmc_flash)
250{
251 lpc_fw_fd = open("/sys/kernel/debug/powerpc/lpc/fw", O_RDWR);
252 if (lpc_fw_fd < 0) {
253 perror("can't open LPC MEM");
254 exit(1);
255 }
256
257 if (ppc_platform != plat_ast_bmc)
258 return;
259
260 lpc_io_fd = open("/sys/kernel/debug/powerpc/lpc/io", O_RDWR);
261 if (lpc_io_fd < 0) {
262 perror("can't open LPC IO");
263 exit(1);
264 }
265
266 ast_ahb_readl = lpc_ahb_readl;
267 ast_ahb_writel = lpc_ahb_writel;
268 ast_copy_to_ahb = lpc_ast_copy_to_ahb;
269 ast_copy_from_ahb = lpc_ast_copy_from_ahb;
270
271 lpc_ahb_init(bmc_flash);
272}
273
274void close_devs(void)
275{
276 if (lpc_io_fd < 0 || lpc_fw_fd < 0)
277 return;
278
279 if (ppc_platform != plat_ast_bmc)
280 return;
281
282 /* Restore flash base */
283 lpc_ahb_writel(lpc_old_flash_reg, LPC_CTRL_BASE + 0x88);
284}
285
286static void open_pci(bool bmc_flash)
287{
288 /* XXX */
289 fprintf(stderr, "WARNING: PCI access method not implemented !\n");
290 fprintf(stderr, " Use -l or --lpc\n");
291 exit(1);
292}
293
294static void identify_platform(void)
295{
296 FILE *cpuinfo;
297 char *lptr = NULL;
298 size_t lsize = 0;
299 bool found = false;
300
301 ppc_platform = plat_unknown;
302
303 cpuinfo = fopen("/proc/cpuinfo", "r");
304 if (!cpuinfo) {
305 perror("Can't open /proc/cpuinfo");
306 exit(1);
307 }
308 while(!found && getline(&lptr, &lsize, cpuinfo) >= 0) {
309 if (!strncmp(lptr, "model", 5)) {
310 if (strcasestr(lptr, "rhesus"))
311 ppc_platform = plat_rhesus;
312 else if (strcasestr(lptr, "palmetto"))
313 ppc_platform = plat_ast_bmc;
314 found = true;
315 }
316 free(lptr);
317 lptr = NULL;
318 lsize = 0;
319 }
320}
321
322void open_devs(bool use_lpc, bool bmc_flash)
323{
324 if (ppc_platform == plat_unknown) {
325 fprintf(stderr, "Unsupported platform !\n");
326 exit(1);
327 }
328
329 if (use_lpc)
330 open_lpc(bmc_flash);
331 else
332 open_pci(bmc_flash);
333}
334
335void check_platform(bool *has_sfc, bool *has_ast)
336{
337 identify_platform();
338
339 *has_sfc = ppc_platform == plat_rhesus;
340 *has_ast = ppc_platform == plat_ast_bmc;
341}