1 /*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2010 Uwe Hermann <[email protected]>
5 * Copyright (C) 2011 Jonathan Kollasch <[email protected]>
6 * Copyright (C) 2012-2013 Stefan Tauner
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19 #include <stdlib.h>
20 #include <string.h>
21 #include "flash.h"
22 #include "programmer.h"
23 #include "platform/pci.h"
24
25 #define PCI_VENDOR_ID_VIA 0x1106
26
27 #define VIA_MAX_RETRIES 300
28
29 #define BROM_ADDR 0x60
30
31 #define BROM_DATA 0x64
32
33 #define BROM_ACCESS 0x68
34 #define BROM_TRIGGER 0x80
35 #define BROM_WRITE 0x40
36 #define BROM_SIZE_MASK 0x30
37 #define BROM_SIZE_64K 0x00
38 #define BROM_SIZE_32K 0x10
39 #define BROM_SIZE_16K 0x20
40 #define BROM_SIZE_0K 0x30
41 #define BROM_BYTE_ENABLE_MASK 0x0f
42
43 #define BROM_STATUS 0x69
44 #define BROM_ERROR_STATUS 0x80
45
46 /* Select the byte we want to access. This is done by clearing the bit corresponding to the byte we want to
47 * access, leaving the others set (yes, really). */
48 #define ENABLE_BYTE(address) ((~(1 << ((address) & 3))) & BROM_BYTE_ENABLE_MASK)
49 #define BYTE_OFFSET(address) (((address) & 3) * 8)
50
51 static const struct dev_entry ata_via[] = {
52 {PCI_VENDOR_ID_VIA, 0x3249, DEP, "VIA", "VT6421A"},
53
54 {0},
55 };
56
57 static void *atavia_offset = NULL;
58 static struct pci_dev *dev = NULL;
59
atavia_prettyprint_access(uint8_t access)60 static void atavia_prettyprint_access(uint8_t access)
61 {
62 uint8_t bmask = access & BROM_BYTE_ENABLE_MASK;
63 uint8_t size = access & BROM_SIZE_MASK;
64
65 msg_pspew("Accessing byte(s):%s%s%s%s\n",
66 ((bmask & (1<<3)) == 0) ? " 3" : "",
67 ((bmask & (1<<2)) == 0) ? " 2" : "",
68 ((bmask & (1<<1)) == 0) ? " 1" : "",
69 ((bmask & (1<<0)) == 0) ? " 0" : "");
70 if (size == BROM_SIZE_0K) {
71 msg_pspew("No ROM device found.\n");
72 } else
73 msg_pspew("ROM device with %s kB attached.\n",
74 (size == BROM_SIZE_64K) ? ">=64" :
75 (size == BROM_SIZE_32K) ? "32" : "16");
76 msg_pspew("Access is a %s.\n", (access & BROM_WRITE) ? "write" : "read");
77 msg_pspew("Device is %s.\n", (access & BROM_TRIGGER) ? "busy" : "ready");
78 }
79
atavia_ready(struct pci_dev * pcidev_dev)80 static bool atavia_ready(struct pci_dev *pcidev_dev)
81 {
82 int try;
83 uint8_t access, status;
84 bool ready = false;
85
86 for (try = 0; try < VIA_MAX_RETRIES; try++) {
87 access = pci_read_byte(pcidev_dev, BROM_ACCESS);
88 status = pci_read_byte(pcidev_dev, BROM_STATUS);
89 if (((access & BROM_TRIGGER) == 0) && (status & BROM_ERROR_STATUS) == 0) {
90 ready = true;
91 break;
92 } else {
93 default_delay(1);
94 continue;
95 }
96 }
97
98 msg_pdbg2("\n%s: %s after %d tries (access=0x%02x, status=0x%02x)\n",
99 __func__, ready ? "succeeded" : "failed", try, access, status);
100 atavia_prettyprint_access(access);
101 return ready;
102 }
103
atavia_map(const char * descr,uintptr_t phys_addr,size_t len)104 static void *atavia_map(const char *descr, uintptr_t phys_addr, size_t len)
105 {
106 return (atavia_offset != 0) ? atavia_offset : (void *)phys_addr;
107 }
108
atavia_chip_writeb(const struct flashctx * flash,uint8_t val,const chipaddr addr)109 static void atavia_chip_writeb(const struct flashctx *flash, uint8_t val, const chipaddr addr)
110 {
111 msg_pspew("%s: 0x%02x to 0x%*" PRIxPTR ".\n", __func__, val, PRIxPTR_WIDTH, addr);
112 pci_write_long(dev, BROM_ADDR, (addr & ~3));
113 pci_write_long(dev, BROM_DATA, val << BYTE_OFFSET(addr));
114 pci_write_byte(dev, BROM_ACCESS, BROM_TRIGGER | BROM_WRITE | ENABLE_BYTE(addr));
115
116 if (!atavia_ready(dev)) {
117 msg_perr("not ready after write\n");
118 }
119 }
120
atavia_chip_readb(const struct flashctx * flash,const chipaddr addr)121 static uint8_t atavia_chip_readb(const struct flashctx *flash, const chipaddr addr)
122 {
123 pci_write_long(dev, BROM_ADDR, (addr & ~3));
124 pci_write_byte(dev, BROM_ACCESS, BROM_TRIGGER | ENABLE_BYTE(addr));
125
126 if (!atavia_ready(dev)) {
127 msg_perr("not ready after read\n");
128 }
129
130 uint8_t val = (pci_read_long(dev, BROM_DATA) >> BYTE_OFFSET(addr)) & 0xff;
131 msg_pspew("%s: 0x%02x from 0x%*" PRIxPTR ".\n", __func__, val, PRIxPTR_WIDTH, addr);
132 return val;
133 }
134
135 static const struct par_master lpc_master_atavia = {
136 .map_flash_region = atavia_map,
137 .chip_readb = atavia_chip_readb,
138 .chip_writeb = atavia_chip_writeb,
139 };
140
atavia_init(const struct programmer_cfg * cfg)141 static int atavia_init(const struct programmer_cfg *cfg)
142 {
143 char *arg = extract_programmer_param_str(cfg, "offset");
144 if (arg) {
145 if (strlen(arg) == 0) {
146 msg_perr("Missing argument for offset.\n");
147 free(arg);
148 return ERROR_FLASHROM_FATAL;
149 }
150 char *endptr;
151 atavia_offset = (void *)strtoul(arg, &endptr, 0);
152 if (*endptr) {
153 msg_perr("Error: Invalid offset specified: \"%s\".\n", arg);
154 free(arg);
155 return ERROR_FLASHROM_FATAL;
156 }
157 msg_pinfo("Mapping addresses to base %p.\n", atavia_offset);
158 }
159 free(arg);
160
161 dev = pcidev_init(cfg, ata_via, PCI_ROM_ADDRESS); /* Actually no BAR setup needed at all. */
162 if (!dev)
163 return 1;
164
165 /* Test if a flash chip is attached. */
166 pci_write_long(dev, PCI_ROM_ADDRESS, (uint32_t)PCI_ROM_ADDRESS_MASK);
167 default_delay(90);
168 uint32_t base = pci_read_long(dev, PCI_ROM_ADDRESS);
169 msg_pdbg2("BROM base=0x%08"PRIx32"\n", base);
170 if ((base & PCI_ROM_ADDRESS_MASK) == 0) {
171 msg_pwarn("Controller thinks there is no ROM attached.\n");
172 }
173
174 if (!atavia_ready(dev)) {
175 msg_perr("Controller not ready.\n");
176 return 1;
177 }
178
179 return register_par_master(&lpc_master_atavia, BUS_LPC, NULL);
180 }
181
182 const struct programmer_entry programmer_atavia = {
183 .name = "atavia",
184 .type = PCI,
185 .devs.dev = ata_via,
186 .init = atavia_init,
187 };
188