summaryrefslogtreecommitdiffstats
path: root/atapromise.c
blob: d781190b5aeb10bca9263277db75dc39a81d43be (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*
 * This file is part of the flashrom project.
 *
 * Copyright (C) 2015 Joseph C. Lehner <joseph.c.lehner@gmail.com>
 *
 * 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.
 */

#include <string.h>
#include <stdlib.h>
#include "flash.h"
#include "programmer.h"
#include "hwaccess_x86_io.h"
#include "hwaccess_physmap.h"
#include "platform/pci.h"

#define MAX_ROM_DECODE (32 * 1024)
#define ADDR_MASK (MAX_ROM_DECODE - 1)

/*
 * In the absence of any public docs on the PDC2026x family, this programmer was created through a mix of
 * reverse-engineering and trial and error.
 *
 * The only device tested is an Ultra100 controller, but the logic for programming the other 2026x controllers
 * is the same, so it should, in theory, work for those as well.
 *
 * While the tested Ultra100 controller used a 128 kB MX29F001T chip, A16 and A15 showed continuity to ground,
 * thus limiting the the programmer on this card to 32 kB. Without other controllers to test this programmer on,
 * this is currently a hard limit. Note that ROM files for these controllers are 16 kB only.
 *
 * Since flashrom does not support accessing flash chips larger than the size limit of the programmer (the
 * tested Ultra100 uses a 128 kB MX29F001T chip), the chip size is hackishly adjusted in atapromise_limit_chip.
 */

static uint32_t io_base_addr = 0;
static uint32_t rom_base_addr = 0;

static uint8_t *atapromise_bar = NULL;
static size_t rom_size = 0;

static const struct dev_entry ata_promise[] = {
	{0x105a, 0x4d38, NT, "Promise", "PDC20262 (FastTrak66/Ultra66)"},
	{0x105a, 0x0d30, NT, "Promise", "PDC20265 (FastTrak100 Lite/Ultra100)"},
	{0x105a, 0x4d30, OK, "Promise", "PDC20267 (FastTrak100/Ultra100)"},
	{0},
};

static void *atapromise_map(const char *descr, uintptr_t phys_addr, size_t len)
{
	/* In case fallback_map ever returns something other than NULL. */
	return NULL;
}

static void atapromise_limit_chip(struct flashchip *chip)
{
	unsigned int i, size;
	unsigned int usable_erasers = 0;

	size = chip->total_size * 1024;

	/* Chip is small enough or already limited. */
	if (size <= rom_size)
		return;

	/* Undefine all block_erasers that don't operate on the whole chip,
	 * and adjust the eraseblock size of those which do.
	 */
	for (i = 0; i < NUM_ERASEFUNCTIONS; ++i) {
		if (chip->block_erasers[i].eraseblocks[0].size != size) {
			chip->block_erasers[i].eraseblocks[0].count = 0;
			chip->block_erasers[i].block_erase = NULL;
		} else {
			chip->block_erasers[i].eraseblocks[0].size = rom_size;
			usable_erasers++;
		}
	}

	if (usable_erasers) {
		chip->total_size = rom_size / 1024;
		if (chip->page_size > rom_size)
			chip->page_size = rom_size;
	} else {
		msg_pdbg("Failed to adjust size of chip \"%s\" (%d kB).\n", chip->name, chip->total_size);
	}
}

static void atapromise_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr)
{
	uint32_t data;

	atapromise_limit_chip(flash->chip);
	data = (rom_base_addr + (addr & ADDR_MASK)) << 8 | val;
	OUTL(data, io_base_addr + 0x14);
}

static uint8_t atapromise_chip_readb(const struct flashctx *flash, const chipaddr addr)
{
	atapromise_limit_chip(flash->chip);
	return pci_mmio_readb(atapromise_bar + (addr & ADDR_MASK));
}

static const struct par_master par_master_atapromise = {
		.chip_readb		= atapromise_chip_readb,
		.chip_readw		= fallback_chip_readw,
		.chip_readl		= fallback_chip_readl,
		.chip_readn		= fallback_chip_readn,
		.chip_writeb		= atapromise_chip_writeb,
		.chip_writew		= fallback_chip_writew,
		.chip_writel		= fallback_chip_writel,
		.chip_writen		= fallback_chip_writen,
};

static int atapromise_init(void)
{
	struct pci_dev *dev = NULL;

	if (rget_io_perms())
		return 1;

	dev = pcidev_init(ata_promise, PCI_BASE_ADDRESS_4);
	if (!dev)
		return 1;

	io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_4) & 0xfffe;
	if (!io_base_addr) {
		return 1;
	}

	/* Not exactly sure what this does, because flashing seems to work
	 * well without it. However, PTIFLASH does it, so we do it too.
	 */
	OUTB(1, io_base_addr + 0x10);

	rom_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_5);
	if (!rom_base_addr) {
		msg_pdbg("Failed to read BAR5\n");
		return 1;
	}

	rom_size = dev->rom_size > MAX_ROM_DECODE ? MAX_ROM_DECODE : dev->rom_size;
	atapromise_bar = (uint8_t*)rphysmap("Promise", rom_base_addr, rom_size);
	if (atapromise_bar == ERROR_PTR) {
		return 1;
	}

	max_rom_decode.parallel = rom_size;

	msg_pwarn("Do not use this device as a generic programmer. It will leave anything outside\n"
		  "the first %zu kB of the flash chip in an undefined state. It works fine for the\n"
		  "purpose of updating the firmware of this device (padding may necessary).\n",
		  rom_size / 1024);

	return register_par_master(&par_master_atapromise, BUS_PARALLEL, NULL);
}

const struct programmer_entry programmer_atapromise = {
	.name			= "atapromise",
	.type			= PCI,
	.devs.dev		= ata_promise,
	.init			= atapromise_init,
	.map_flash_region	= atapromise_map,
	.unmap_flash_region	= fallback_unmap,
	.delay			= internal_delay,
};