blob: b447a9461c0d5c1875cd3bf2f1c711a4abf5b067 [file] [log] [blame]
Nico Huberdd4744e2026-03-22 15:47:25 +01001/*
2 * This file is part of the flashprog project.
3 *
4 * Copyright (C) 2024 Nico Huber <nico.h@gmx.de>
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
17#include <stdint.h>
18#include <stdlib.h>
19#include <flash.h>
20#include <hwaccess_x86_io.h>
21#include <hwaccess_physmap.h>
22#include <platform/pci.h>
23#include <programmer.h>
24
25#define PCI_VENDOR_ID_AMD 0x1022
26
27static const struct dev_entry nics_amd[] = {
28 {PCI_VENDOR_ID_AMD, 0x2000, OK, "AMD", "79C97x [PCnet32 LANCE]"},
29
30 {0},
31};
32
33static uint8_t readb(const struct par_master *, chipaddr);
34static void writeb(const struct par_master *, uint8_t val, chipaddr);
35static int shutdown(void *);
36
37static const struct par_master par_master = {
38 .chip_readb = readb,
39 .chip_readw = fallback_chip_readw,
40 .chip_readl = fallback_chip_readl,
41 .chip_readn = fallback_chip_readn,
42 .chip_writeb = writeb,
43 .chip_writew = fallback_chip_writew,
44 .chip_writel = fallback_chip_writel,
45 .chip_writen = fallback_chip_writen,
46 .shutdown = shutdown,
47};
48
49#define ROM_BAR_SIZE (1*MiB)
50
51/* ----- i/o space ----- */
52
53#define ANY_IO_RDP 0x10 /* Register Data Port */
54#define WORD_IO_RAP 0x12
55#define WORD_IO_RESET 0x14
56#define WORD_IO_BDP 0x16
57
58#define DWORD_IO_RAP 0x14 /* Register Address Port */
59#define DWORD_IO_RESET 0x18
60#define DWORD_IO_BDP 0x1c /* BCR (Bus Configuration Register) Data Port */
61
62/* ----- BCR space ----- */
63
64#define EXP_BUS_ADDR_LOWER 28
65#define EXP_BUS_ADDR_UPPER 29
66#define EBADDRU_FLASH (1 << 15)
67#define EXP_BUS_DATA_PORT 30
68
69/* all BCR registers are 16-bit wide */
70
71static uint16_t bcr_read16(unsigned int io_base, unsigned int reg)
72{
73 OUTL(reg, io_base + DWORD_IO_RAP);
74 return INL(io_base + DWORD_IO_BDP);
75}
76
77static void bcr_write16(unsigned int io_base, unsigned int reg, uint16_t val)
78{
79 OUTL(reg, io_base + DWORD_IO_RAP);
80 OUTL(val, io_base + DWORD_IO_BDP);
81}
82
83static int init(struct flashprog_programmer *const prog)
84{
85 size_t max_decode = 0;
86 char *const decode_override = extract_programmer_param("max_decode_kb");
87 if (decode_override) {
88 char *endptr;
89 max_decode = strtoul(decode_override, &endptr, 10);
90 if (*endptr || max_decode < 16 || max_decode > 1024 || (max_decode & (max_decode - 1))) {
91 msg_perr("Error: Invalid ROM decode override: \"%s\".\n"
92 "Valid values are powers of 2 from 16 through 1024 (KiB).\n",
93 decode_override);
94 free(decode_override);
95 return 1;
96 }
97 max_decode *= KiB;
98 }
99 free(decode_override);
100
101 if (rget_io_perms())
102 return 1;
103
104 struct pci_dev *const dev = pcidev_init(nics_amd, PCI_BASE_ADDRESS_0);
105 if (!dev)
106 return 1;
107
108 uintptr_t io_base = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
109 if (!io_base)
110 return 1;
111
112 if (!max_decode) {
113 msg_pinfo("Trying to guess addressable size based on read contents and patterns. If this\n"
114 "doesn't work, you can override probing with `-p nicamd:max_decode_kb=<size>`.\n");
115
116 const uint16_t command = pci_read_word(dev, PCI_COMMAND);
117 if (!(command & PCI_COMMAND_MEMORY)) {
118 msg_perr("Error: PCI memory access is disabled.\n");
119 return 1;
120 }
121
122 const uint32_t rom_base = pci_read_long(dev, PCI_ROM_ADDRESS);
123 if (!rom_base) {
124 msg_perr("Error: PCI ROM base is not configured.\n");
125 return 1;
126 }
127
128 void *physmap(const char *descr, uintptr_t phys_addr, size_t len);
129 void *const rom = physmap("ROM BAR", rom_base & ~1u, ROM_BAR_SIZE);
130 if (rom == ERROR_PTR)
131 return 1;
132
133 if (!(rom_base & 1u))
134 pci_write_long(dev, PCI_ROM_ADDRESS, rom_base | 1);
135
136 max_decode = estimate_addressable_size(rom, ROM_BAR_SIZE);
137
138 if (!(rom_base & 1u))
139 pci_write_long(dev, PCI_ROM_ADDRESS, rom_base);
140
141 physunmap(rom, ROM_BAR_SIZE);
142
143 if (!max_decode) {
144 max_decode = 256*KiB;
145 msg_pwarn("Estimating addressable size failed. Using default of %zuKiB.\n",
146 max_decode / KiB);
147 }
148 }
149
150 /*
151 * Now comes the fun part: This controller knows two i/o modes,
152 * word and dword. Writing with the wrong width is illegal, and
153 * it's impossible to tell the current mode without writing.
154 *
155 * The datasheet was reasoned with and we concluded:
156 * * A soft reset can be performed with read requests only.
157 * * Trying a dword-i/o reset in word i/o mode is only a read
158 * from a reserved register and shouldn't hurt.
159 * * Trying a word-i/o reset in dword i/o mode is only a read
160 * with the wrong length.
161 * * After reset we still don't know the mode (it is decided
162 * by EEPROM bits), though we can switch to dword i/o mode,
163 * blindly.
164 */
165 INW(io_base + WORD_IO_RESET);
166 INL(io_base + DWORD_IO_RESET);
167 OUTL(0, io_base + ANY_IO_RDP); /* supposed to enable dword i/o mode */
168
169 OUTL(18, io_base + DWORD_IO_RAP);
170 msg_pdbg2("BBCR: 0x%04x\n", INL(io_base + DWORD_IO_BDP) & 0xffff);
171 OUTL(0x98e1, io_base + DWORD_IO_BDP);
172
173 msg_pdbg2("BCR25: 0x%04x\n", bcr_read16(io_base, 25));
174 bcr_write16(io_base, 25, 0);
175
176 return register_par_master(&par_master, BUS_PARALLEL, 0, max_decode, (void *)io_base);
177}
178
179static uint8_t readb(const struct par_master *par, chipaddr addr)
180{
181 uintptr_t io_base = (uintptr_t)par->data;
182
183 bcr_write16(io_base, EXP_BUS_ADDR_UPPER, EBADDRU_FLASH | addr >> 16);
184 bcr_write16(io_base, EXP_BUS_ADDR_LOWER, addr);
185 return bcr_read16(io_base, EXP_BUS_DATA_PORT);
186}
187
188static void writeb(const struct par_master *par, uint8_t val, chipaddr addr)
189{
190 uintptr_t io_base = (uintptr_t)par->data;
191
192 bcr_write16(io_base, EXP_BUS_ADDR_UPPER, EBADDRU_FLASH | addr >> 16);
193 bcr_write16(io_base, EXP_BUS_ADDR_LOWER, addr);
194 bcr_write16(io_base, EXP_BUS_DATA_PORT, val);
195}
196
197static int shutdown(void *data)
198{
199 uintptr_t io_base = (uintptr_t)data;
200
201 /* Reset just in case. */
202 INL(io_base + DWORD_IO_RESET);
203
204 return 0;
205}
206
207const struct programmer_entry programmer_nicamd = {
208 .name = "nicamd",
209 .type = PCI,
210 .devs.dev = nics_amd,
211 .init = init,
212};