source: trunk/hwaccess.c @ 1511

Revision 1511, 6.6 KB checked in by hailfinger, 3 months ago (diff)

Fix parallel-style programmer access from ITE IT87/Winbond W83627 SPI

The ITE IT87 SPI driver uses a trick to speed up reading and writing:
If a flash chip is 512 kByte or less, the flash chip can be completely
mapped in memory and both read and write accesses are faster that way.
The current IT87 SPI code did use the parallel programmer interface for
memory mapped reads and writes, but that's the wrong abstraction. It has
been fixed to use mmio_read*/mmio_write* for that purpose.

The Winbond W83627 SPI driver uses the same trick in its read path for
all supported chip sizes. Fix it the same way.

Switch internal_chip_readn to use mmio_readn as proper abstraction.

Kudos to Michael Karcher for spotting the bugs.

Reported-by: Johan Svensson <flashrom.js@…>
Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@…>
Acked-by: Michael Karcher <flashrom@…>
Tested-by: Johan Svensson <flashrom.js@…>

Line 
1/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19 */
20
21#include <stdint.h>
22#include <string.h>
23#include <stdlib.h>
24#include <sys/types.h>
25#if !defined (__DJGPP__) && !defined(__LIBPAYLOAD__)
26#include <unistd.h>
27#include <fcntl.h>
28#endif
29#if !defined (__DJGPP__)
30#include <errno.h>
31#endif
32#include "flash.h"
33
34#if defined(__i386__) || defined(__x86_64__)
35
36/* sync primitive is not needed because x86 uses uncached accesses
37 * which have a strongly ordered memory model.
38 */
39static inline void sync_primitive(void)
40{
41}
42
43#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
44int io_fd;
45#endif
46
47void get_io_perms(void)
48{
49#if defined(__DJGPP__) || defined(__LIBPAYLOAD__)
50        /* We have full permissions by default. */
51        return;
52#else
53#if defined (__sun) && (defined(__i386) || defined(__amd64))
54        if (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) != 0) {
55#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined (__DragonFly__)
56        if ((io_fd = open("/dev/io", O_RDWR)) < 0) {
57#else
58        if (iopl(3) != 0) {
59#endif
60                msg_perr("ERROR: Could not get I/O privileges (%s).\n"
61                        "You need to be root.\n", strerror(errno));
62#if defined (__OpenBSD__)
63                msg_perr("Please set securelevel=-1 in /etc/rc.securelevel "
64                           "and reboot, or reboot into \n");
65                msg_perr("single user mode.\n");
66#endif
67                exit(1);
68        }
69#endif
70}
71
72void release_io_perms(void)
73{
74#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
75        close(io_fd);
76#endif
77}
78
79#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__)
80
81static inline void sync_primitive(void)
82{
83        /* Prevent reordering and/or merging of reads/writes to hardware.
84         * Such reordering and/or merging would break device accesses which
85         * depend on the exact access order.
86         */
87        asm("eieio" : : : "memory");
88}
89
90/* PCI port I/O is not yet implemented on PowerPC. */
91void get_io_perms(void)
92{
93}
94
95/* PCI port I/O is not yet implemented on PowerPC. */
96void release_io_perms(void)
97{
98}
99
100#elif defined (__mips) || defined (__mips__) || defined (_mips) || defined (mips)
101
102/* sync primitive is not needed because /dev/mem on MIPS uses uncached accesses
103 * in mode 2 which has a strongly ordered memory model.
104 */
105static inline void sync_primitive(void)
106{
107}
108
109/* PCI port I/O is not yet implemented on MIPS. */
110void get_io_perms(void)
111{
112}
113
114/* PCI port I/O is not yet implemented on MIPS. */
115void release_io_perms(void)
116{
117}
118
119#elif defined (__arm__)
120
121static inline void sync_primitive(void)
122{
123}
124
125void get_io_perms(void)
126{
127}
128
129void release_io_perms(void)
130{
131}
132
133#else
134
135#error Unknown architecture
136
137#endif
138
139void mmio_writeb(uint8_t val, void *addr)
140{
141        *(volatile uint8_t *) addr = val;
142        sync_primitive();
143}
144
145void mmio_writew(uint16_t val, void *addr)
146{
147        *(volatile uint16_t *) addr = val;
148        sync_primitive();
149}
150
151void mmio_writel(uint32_t val, void *addr)
152{
153        *(volatile uint32_t *) addr = val;
154        sync_primitive();
155}
156
157uint8_t mmio_readb(void *addr)
158{
159        return *(volatile uint8_t *) addr;
160}
161
162uint16_t mmio_readw(void *addr)
163{
164        return *(volatile uint16_t *) addr;
165}
166
167uint32_t mmio_readl(void *addr)
168{
169        return *(volatile uint32_t *) addr;
170}
171
172void mmio_readn(void *addr, uint8_t *buf, size_t len)
173{
174        memcpy(buf, addr, len);
175        return;
176}
177
178void mmio_le_writeb(uint8_t val, void *addr)
179{
180        mmio_writeb(cpu_to_le8(val), addr);
181}
182
183void mmio_le_writew(uint16_t val, void *addr)
184{
185        mmio_writew(cpu_to_le16(val), addr);
186}
187
188void mmio_le_writel(uint32_t val, void *addr)
189{
190        mmio_writel(cpu_to_le32(val), addr);
191}
192
193uint8_t mmio_le_readb(void *addr)
194{
195        return le_to_cpu8(mmio_readb(addr));
196}
197
198uint16_t mmio_le_readw(void *addr)
199{
200        return le_to_cpu16(mmio_readw(addr));
201}
202
203uint32_t mmio_le_readl(void *addr)
204{
205        return le_to_cpu32(mmio_readl(addr));
206}
207
208enum mmio_write_type {
209        mmio_write_type_b,
210        mmio_write_type_w,
211        mmio_write_type_l,
212};
213
214struct undo_mmio_write_data {
215        void *addr;
216        int reg;
217        enum mmio_write_type type;
218        union {
219                uint8_t bdata;
220                uint16_t wdata;
221                uint32_t ldata;
222        };
223};
224
225int undo_mmio_write(void *p)
226{
227        struct undo_mmio_write_data *data = p;
228        msg_pdbg("Restoring MMIO space at %p\n", data->addr);
229        switch (data->type) {
230        case mmio_write_type_b:
231                mmio_writeb(data->bdata, data->addr);
232                break;
233        case mmio_write_type_w:
234                mmio_writew(data->wdata, data->addr);
235                break;
236        case mmio_write_type_l:
237                mmio_writel(data->ldata, data->addr);
238                break;
239        }
240        /* p was allocated in register_undo_mmio_write. */
241        free(p);
242        return 0;
243}
244
245#define register_undo_mmio_write(a, c)                                  \
246{                                                                       \
247        struct undo_mmio_write_data *undo_mmio_write_data;              \
248        undo_mmio_write_data = malloc(sizeof(struct undo_mmio_write_data)); \
249        if (!undo_mmio_write_data) {                                    \
250                msg_gerr("Out of memory!\n");                           \
251                exit(1);                                                \
252        }                                                               \
253        undo_mmio_write_data->addr = a;                                 \
254        undo_mmio_write_data->type = mmio_write_type_##c;               \
255        undo_mmio_write_data->c##data = mmio_read##c(a);                \
256        register_shutdown(undo_mmio_write, undo_mmio_write_data);       \
257}
258
259#define register_undo_mmio_writeb(a) register_undo_mmio_write(a, b)
260#define register_undo_mmio_writew(a) register_undo_mmio_write(a, w)
261#define register_undo_mmio_writel(a) register_undo_mmio_write(a, l)
262
263void rmmio_writeb(uint8_t val, void *addr)
264{
265        register_undo_mmio_writeb(addr);
266        mmio_writeb(val, addr);
267}
268
269void rmmio_writew(uint16_t val, void *addr)
270{
271        register_undo_mmio_writew(addr);
272        mmio_writew(val, addr);
273}
274
275void rmmio_writel(uint32_t val, void *addr)
276{
277        register_undo_mmio_writel(addr);
278        mmio_writel(val, addr);
279}
280
281void rmmio_le_writeb(uint8_t val, void *addr)
282{
283        register_undo_mmio_writeb(addr);
284        mmio_le_writeb(val, addr);
285}
286
287void rmmio_le_writew(uint16_t val, void *addr)
288{
289        register_undo_mmio_writew(addr);
290        mmio_le_writew(val, addr);
291}
292
293void rmmio_le_writel(uint32_t val, void *addr)
294{
295        register_undo_mmio_writel(addr);
296        mmio_le_writel(val, addr);
297}
298
299void rmmio_valb(void *addr)
300{
301        register_undo_mmio_writeb(addr);
302}
303
304void rmmio_valw(void *addr)
305{
306        register_undo_mmio_writew(addr);
307}
308
309void rmmio_vall(void *addr)
310{
311        register_undo_mmio_writel(addr);
312}
Note: See TracBrowser for help on using the repository browser.