blob: 9182af52acafc0855bce066c531d6c8ff5f2163a [file] [log] [blame]
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +00001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
5 * Copyright (C) 2011 Jonathan Kollasch <jakllsch@kollasch.net>
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.
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +000017 */
18
19#include <stdlib.h>
20#include <string.h>
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +000021#include "programmer.h"
Thomas Heijligend96c97c2021-11-02 21:03:00 +010022#include "platform/pci.h"
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +000023
24#define PCI_VENDOR_ID_VIA 0x1106
25
26#define VIA_MAX_RETRIES 300
27
28#define BROM_ADDR 0x60
29
30#define BROM_DATA 0x64
31
32#define BROM_ACCESS 0x68
33#define BROM_TRIGGER 0x80
34#define BROM_WRITE 0x40
35#define BROM_SIZE_MASK 0x30
36#define BROM_SIZE_64K 0x00
37#define BROM_SIZE_32K 0x10
38#define BROM_SIZE_16K 0x20
39#define BROM_SIZE_0K 0x30
40#define BROM_BYTE_ENABLE_MASK 0x0f
41
42#define BROM_STATUS 0x69
43#define BROM_ERROR_STATUS 0x80
44
45/* Select the byte we want to access. This is done by clearing the bit corresponding to the byte we want to
46 * access, leaving the others set (yes, really). */
47#define ENABLE_BYTE(address) ((~(1 << ((address) & 3))) & BROM_BYTE_ENABLE_MASK)
Alexander Goncharovc50f4ae2022-06-17 08:32:47 +030048#define BYTE_OFFSET(address) (((address) & 3) * 8)
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +000049
Thomas Heijligencc853d82021-05-04 15:32:17 +020050static const struct dev_entry ata_via[] = {
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +000051 {PCI_VENDOR_ID_VIA, 0x3249, DEP, "VIA", "VT6421A"},
52
Evgeny Zinoviev83c56b82019-11-05 17:47:43 +030053 {0},
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +000054};
55
Nico Huberdd6e07a2026-02-21 17:55:26 +010056static void atavia_chip_writeb(const struct par_master *, uint8_t val, chipaddr);
57static uint8_t atavia_chip_readb(const struct par_master *, chipaddr);
Nico Huber0e76d992023-01-12 20:22:55 +010058static void *atavia_map(const char *descr, uintptr_t phys_addr, size_t len);
Carl-Daniel Hailfingera5bcbce2014-07-19 22:03:29 +000059static const struct par_master lpc_master_atavia = {
Thomas Heijligen43040f22022-06-23 14:38:35 +020060 .chip_readb = atavia_chip_readb,
61 .chip_readw = fallback_chip_readw,
62 .chip_readl = fallback_chip_readl,
63 .chip_readn = fallback_chip_readn,
64 .chip_writeb = atavia_chip_writeb,
65 .chip_writew = fallback_chip_writew,
66 .chip_writel = fallback_chip_writel,
67 .chip_writen = fallback_chip_writen,
Nico Huber0e76d992023-01-12 20:22:55 +010068 .map_flash = atavia_map,
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +000069};
70
71static void *atavia_offset = NULL;
Stefan Tauner4f094752014-06-01 22:36:30 +000072static struct pci_dev *dev = NULL;
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +000073
74static void atavia_prettyprint_access(uint8_t access)
75{
76 uint8_t bmask = access & BROM_BYTE_ENABLE_MASK;
77 uint8_t size = access & BROM_SIZE_MASK;
78
79 msg_pspew("Accessing byte(s):%s%s%s%s\n",
80 ((bmask & (1<<3)) == 0) ? " 3" : "",
81 ((bmask & (1<<2)) == 0) ? " 2" : "",
82 ((bmask & (1<<1)) == 0) ? " 1" : "",
83 ((bmask & (1<<0)) == 0) ? " 0" : "");
84 if (size == BROM_SIZE_0K) {
85 msg_pspew("No ROM device found.\n");
86 } else
87 msg_pspew("ROM device with %s kB attached.\n",
88 (size == BROM_SIZE_64K) ? ">=64" :
89 (size == BROM_SIZE_32K) ? "32" : "16");
90 msg_pspew("Access is a %s.\n", (access & BROM_WRITE) ? "write" : "read");
91 msg_pspew("Device is %s.\n", (access & BROM_TRIGGER) ? "busy" : "ready");
92}
93
94static bool atavia_ready(struct pci_dev *pcidev_dev)
95{
96 int try;
97 uint8_t access, status;
98 bool ready = false;
99
100 for (try = 0; try < VIA_MAX_RETRIES; try++) {
101 access = pci_read_byte(pcidev_dev, BROM_ACCESS);
102 status = pci_read_byte(pcidev_dev, BROM_STATUS);
103 if (((access & BROM_TRIGGER) == 0) && (status & BROM_ERROR_STATUS) == 0) {
104 ready = true;
105 break;
106 } else {
107 programmer_delay(1);
108 continue;
109 }
110 }
111
112 msg_pdbg2("\n%s: %s after %d tries (access=0x%02x, status=0x%02x)\n",
Stefan Tauner6697f712014-08-06 15:09:15 +0000113 __func__, ready ? "succeeded" : "failed", try, access, status);
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +0000114 atavia_prettyprint_access(access);
115 return ready;
116}
117
Thomas Heijligencc853d82021-05-04 15:32:17 +0200118static void *atavia_map(const char *descr, uintptr_t phys_addr, size_t len)
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +0000119{
120 return (atavia_offset != 0) ? atavia_offset : (void *)phys_addr;
121}
122
Nico Hubere3a26882023-01-11 21:45:51 +0100123static int atavia_init(struct flashprog_programmer *const prog)
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +0000124{
125 char *arg = extract_programmer_param("offset");
126 if (arg) {
127 if (strlen(arg) == 0) {
128 msg_perr("Missing argument for offset.\n");
129 free(arg);
130 return ERROR_FATAL;
131 }
132 char *endptr;
133 atavia_offset = (void *)strtoul(arg, &endptr, 0);
134 if (*endptr) {
135 msg_perr("Error: Invalid offset specified: \"%s\".\n", arg);
136 free(arg);
137 return ERROR_FATAL;
138 }
139 msg_pinfo("Mapping addresses to base %p.\n", atavia_offset);
140 }
141 free(arg);
142
Carl-Daniel Hailfingere4c2b482020-01-20 11:22:41 +0100143 dev = pcidev_init(ata_via, PCI_ROM_ADDRESS); /* Actually no BAR setup needed at all. */
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +0000144 if (!dev)
145 return 1;
146
147 /* Test if a flash chip is attached. */
148 pci_write_long(dev, PCI_ROM_ADDRESS, (uint32_t)PCI_ROM_ADDRESS_MASK);
149 programmer_delay(90);
150 uint32_t base = pci_read_long(dev, PCI_ROM_ADDRESS);
151 msg_pdbg2("BROM base=0x%08x\n", base);
152 if ((base & PCI_ROM_ADDRESS_MASK) == 0) {
153 msg_pwarn("Controller thinks there is no ROM attached.\n");
154 }
155
156 if (!atavia_ready(dev)) {
157 msg_perr("Controller not ready.\n");
158 return 1;
159 }
160
Nico Huber47aa85c2026-02-21 14:57:20 +0100161 return register_par_master(&lpc_master_atavia, BUS_LPC, 0, 0, NULL);
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +0000162}
163
Nico Huberdd6e07a2026-02-21 17:55:26 +0100164static void atavia_chip_writeb(const struct par_master *par, uint8_t val, const chipaddr addr)
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +0000165{
Stefan Tauner4f094752014-06-01 22:36:30 +0000166 msg_pspew("%s: 0x%02x to 0x%*" PRIxPTR ".\n", __func__, val, PRIxPTR_WIDTH, addr);
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +0000167 pci_write_long(dev, BROM_ADDR, (addr & ~3));
168 pci_write_long(dev, BROM_DATA, val << BYTE_OFFSET(addr));
169 pci_write_byte(dev, BROM_ACCESS, BROM_TRIGGER | BROM_WRITE | ENABLE_BYTE(addr));
170
171 if (!atavia_ready(dev)) {
172 msg_perr("not ready after write\n");
173 }
174}
175
Nico Huberdd6e07a2026-02-21 17:55:26 +0100176static uint8_t atavia_chip_readb(const struct par_master *par, const chipaddr addr)
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +0000177{
178 pci_write_long(dev, BROM_ADDR, (addr & ~3));
179 pci_write_byte(dev, BROM_ACCESS, BROM_TRIGGER | ENABLE_BYTE(addr));
180
181 if (!atavia_ready(dev)) {
182 msg_perr("not ready after read\n");
183 }
184
185 uint8_t val = (pci_read_long(dev, BROM_DATA) >> BYTE_OFFSET(addr)) & 0xff;
Stefan Tauner4f094752014-06-01 22:36:30 +0000186 msg_pspew("%s: 0x%02x from 0x%*" PRIxPTR ".\n", __func__, val, PRIxPTR_WIDTH, addr);
Jonathan Kollasch7f0f3fa2014-06-01 10:26:23 +0000187 return val;
188}
Thomas Heijligencc853d82021-05-04 15:32:17 +0200189
190const struct programmer_entry programmer_atavia = {
191 .name = "atavia",
192 .type = PCI,
193 .devs.dev = ata_via,
194 .init = atavia_init,
Thomas Heijligencc853d82021-05-04 15:32:17 +0200195};