/* * This file is part of the flashrom project. * * Copyright (C) 2018 Alexander Lund�n * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** * nicamd.c - flashrom programmer module for AMD PCnet/Lance network controllers. * * ---- Theory of operation ---- * The AMD Am79C971 exposes two ways of accessing its expansion ROM/Flash memory. * Method 1 maps the ROM into host memory, but only supports read access. * Method 2, "Direct Flash Access", uses a set of registers to give read and * write access to the ROM. * In either mode the Am79C971 supports up to 1 Mbyte of Flash memory. * This module uses method 2. * ([1] pp. 84-89) * * Configuration Registers: * The Am79C971 has three types of configuration registers: PCI Configuration * Registers, Control and Status Registers (CSR) and Bus Control Registers (BCR). * CSR and BCR are used to configure and control the device's operation. * To select a CSR or BCR, write its number to the Register Address Port (RAP), * then read or write to RDP (for CSR) or BDP (for BCR) to access the register. * ([1] p. 103, pp. 114-180) * * I/O modes: * The Am79C971 offers 16-bit I/O (WIO, legacy) and 32-bit I/O (DWIO). * WIO and DWIO use different memory offsets to the I/O Base Address. * One can switch from WIO to DWIO, but not the other way around. Switching from * WIO to DWIO is performed by a DWORD write operation to RDP. IO mode is checked * by reading bit 7 of BCR18 (0 = WIO, 1 = DWIO). Use 32-bit I/O. * ([1] pp. 104-105) * * STOP mode: * The controller must be in STOP mode to allow access to its expansion ROM. * STOP mode is activated by setting bit 2 of CSR0. * ([1] p. 102, p. 117) * * Direct Flash Access: * The Am79C971 offers a direct data path to the flash chip through three BCR's. * BCR29 (EPADDRU[3:0]) is loaded with the upper 4 bits of the ROM address, and * the FLASH bit (bit 15). Then, BCR28 (EPADDRL[15:0]) is loaded with the lower * 16 bits of the ROM address. The Flash is accessed by a reading or writing * to BCR30 (EBD). * ([1] p. 86) * * ---- References ---- * [1] Am79C971, PCnet�-FAST Single-Chip Full-Duplex 10/100 Mbps Ethernet * Controller for PCI Local Bus (Publication# 20550, Rev: E, Issued May 2000) * https://datasheet.octopart.com/AM79C971AKC-W-AMD-datasheet-5389196.pdf * * ---- Glossary ---- * WIO: WORD I/O (16-bit) * DWIO: DWORD I/O (32-bit) * RAP: Register Address Port * CSR: Control and Status Register * BCR: Bus Control Register * RDP: Register Data Port (for CSR) * BDP: Bus register Data Port (for BCR) * EPADDRU: Expansion Port Address Upper * EPADDRU: Expansion Port Address Lower * EBD: Expansion Bus Data port */ #if defined(__i386__) || defined(__x86_64__) #include #include "flash.h" #include "programmer.h" #include "hwaccess.h" #define PCI_VENDOR_ID_AMD 0x1022 /* Supports up to 1 Megabyte (Mbyte) optional Boot PROM and Flash for diskless node application. (p.2) */ #define MAX_ROM_SIZE (1024 * 1024) /* RAP offsets - 16-bit I/0 (DWIO = 0). (pp. 104-105) */ #define WIO_RAP 0x0012 /* Register Address Port */ #define WIO_RDP 0x0010 /* CSR Data Port */ #define WIO_BDP 0x0016 /* BCR Data Port */ /* RAP offsets - 32-bit I/0 (DWIO = 1). (p. 106) */ #define DWIO_RAP 0x0014 /* Register Address Port */ #define DWIO_RDP 0x0010 /* CSR Data Port */ #define DWIO_BDP 0x001C /* BCR Data Port */ /* Control and Status Registers - offsets and bit definitions. (p. 115) */ #define CSR0 0 /* Controller Status and Control Register */ #define CSR0_STOP 0x0004 /* Stop controller */ #define CSR0_START 0x0002 /* Start controller */ #define CSR0_INIT 0x0001 /* Initialize controller */ /* Bus Control Registers - offsets and bit definitions. (p.148) */ #define BCR18 18 /* Burst and Bus Control */ #define BCR28 28 /* Expansion Bus Address Lower (EBADDRL) */ #define BCR29 29 /* Expansion Bus Address Upper (EBADDRU) */ #define BCR30 30 /* Expansion Bus Data Port (EBD) */ #define BCR18_DWIO 0x0080 /* Bit 7 (32-bit I/O) */ #define BCR29_FLASH 0x8000 /* Bit 15 (flash cycle, not SRAM) */ static uint32_t io_base_addr; const struct dev_entry nics_amd[] = { /* 79C971 */ {PCI_VENDOR_ID_AMD, 0x2000, NT, "AMD", "PCNET32: Am79C971 PCI 10/100 Mbps"}, {0}, }; static void nicamd_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr); static uint8_t nicamd_chip_readb(const struct flashctx *flash, const chipaddr addr); static const struct par_master par_master_nicamd = { .chip_readb = nicamd_chip_readb, .chip_readw = fallback_chip_readw, .chip_readl = fallback_chip_readl, .chip_readn = fallback_chip_readn, .chip_writeb = nicamd_chip_writeb, .chip_writew = fallback_chip_writew, .chip_writel = fallback_chip_writel, .chip_writen = fallback_chip_writen, }; /* Functions preparing the controller for ROM access. */ static unsigned int nicamd_enable_32bit_io(); static unsigned int nicamd_enter_stop_mode(); /* Function loading ROM address before read/write. */ static void nicamd_load_rom_addr(uint32_t addr); /* Functions for reading and writing CSR and BCR. */ static void nicamd_write_rap(uint32_t val); static uint16_t nicamd_read_csr(uint32_t csr); static void nicamd_write_csr(uint32_t csr, uint16_t val); static uint16_t nicamd_read_bcr(uint32_t bcr); static void nicamd_write_bcr(uint32_t bcr, uint16_t val); static int nicamd_shutdown(void *data) { /* Start the device again. */ nicamd_write_csr(CSR0, nicamd_read_csr(CSR0) | CSR0_START); return 0; } void *nicamd_map(const char *descr, uintptr_t phys_addr, size_t len) { /* In case fallback_map ever returns something other than NULL. */ return NULL; } int nicamd_init(void) { static struct pci_dev *dev = NULL; if (rget_io_perms()) { return 1; } /* Get reference to device. */ dev = pcidev_init(nics_amd, PCI_BASE_ADDRESS_0); if (!dev) { return 1; } /* Get I/O base address. */ io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); if (!io_base_addr) { return 1; } if (!nicamd_enable_32bit_io()) { msg_perr("[nicamd] Failed enabling 32-bit I/O.\n"); return 1; } if (!nicamd_enter_stop_mode()) { msg_perr("[nicamd] Failed entering STOP mode.\n"); return 1; } if (register_shutdown(nicamd_shutdown, NULL)) { return 1; } max_rom_decode.parallel = MAX_ROM_SIZE; register_par_master(&par_master_nicamd, BUS_PARALLEL); return 0; } static void nicamd_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr) { nicamd_load_rom_addr(addr); /* Write byte to Expansion bus Data Port. */ nicamd_write_bcr(BCR30, val); } static uint8_t nicamd_chip_readb(const struct flashctx *flash, const chipaddr addr) { nicamd_load_rom_addr(addr); /* Read byte from Expansion bus Data Port. */ return (uint8_t) (nicamd_read_bcr(BCR30) & 0xFF); } static void nicamd_load_rom_addr(uint32_t addr) { /* Set FLASH bit and load BCR29[3:0] with upper 4 bits of address. */ nicamd_write_bcr(BCR29, ((addr & 0x000F0000) >> 16) | BCR29_FLASH); /* Load BCR28[15:0] with lower 16 bits of address. */ nicamd_write_bcr(BCR28, addr & 0xFFFF); } static unsigned int nicamd_enable_32bit_io() { /* * Switch to/verify 32-bit I/O mode. * Assume we're in 16-bit mode. Write to WIO_RAP, read back, and * compare to original value. If they're equal, enable 32-bit mode. * If we read back garble, we're probably already in 32-bit mode. */ msg_pdbg("[nicamd] Enabling/verifying 32-bit I/O.\n"); OUTW(88, io_base_addr + WIO_RAP); if (INW(io_base_addr + WIO_RAP) == 88) { /* We're probably in 16-bit mode. Switch to 32-bit. */ msg_pdbg("[nicamd] Probably in 16-bit mode. Switching to 32-bit.\n"); /* Select CSR0. */ OUTW(CSR0, io_base_addr + WIO_RAP); /* Switch to 32-bit I/O by writing a DWORD to RDP. */ OUTL(CSR0_INIT, io_base_addr + WIO_RDP); } /* * Either we were already in 32-bit mode, or we just entered 32-bit mode. * Verify by reading bit 7 (DWIO) from BCR18. */ return (nicamd_read_bcr(BCR18) & BCR18_DWIO) == BCR18_DWIO; } static unsigned int nicamd_enter_stop_mode() { /* Set STOP bit in CSR0, read back and verify. */ msg_pdbg("[nicamd] Entering STOP mode.\n"); nicamd_write_csr(CSR0, nicamd_read_csr(CSR0) | CSR0_STOP); return (nicamd_read_csr(CSR0) & CSR0_STOP) == CSR0_STOP; } static void nicamd_write_rap(uint32_t reg) { /* Select a specified CSR or BCR. */ OUTL(reg, io_base_addr + DWIO_RAP); } static uint16_t nicamd_read_csr(uint32_t csr) { /* Select specified CSR. */ nicamd_write_rap(csr); /* Read CSR data from RDP. */ return INL(io_base_addr + DWIO_RDP) & 0xFFFF; } static void nicamd_write_csr(uint32_t csr, uint16_t val) { /* Select specified CSR. */ nicamd_write_rap(csr); /* Write to CSR through RDP. */ OUTL(val & 0x0000FFFF, io_base_addr + DWIO_RDP); } static uint16_t nicamd_read_bcr(uint32_t bcr) { /* Select specified BCR. */ nicamd_write_rap(bcr); /* Read CSR data from BDP. */ return INL(io_base_addr + DWIO_BDP) & 0xFFFF; } static void nicamd_write_bcr(uint32_t bcr, uint16_t val) { /* Select specified CSR. */ nicamd_write_rap(bcr); /* Write to CSR through BDP. */ OUTL(val & 0x0000FFFF, io_base_addr + DWIO_BDP); } #else #error PCI port I/O access is not supported on this architecture yet. #endif