summaryrefslogtreecommitdiffstats
path: root/asm106x.c
blob: 0c9cf111d6bed556b1fba475ef5836cb1c6db4b9 (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
/*
 * This file is part of the flashrom project.
 *
 * Copyright (C) 2023 Alex Badea <vamposdecampos@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 <stdlib.h>
#include "programmer.h"
#include "platform/pci.h"

#define PCI_VENDOR_ID_ASMEDIA	0x1b21

#define ASM106X_REG_DATA	0xf0
#define ASM106X_REG_CTRL	0xf4
#define ASM106X_CTRL_RUN	0x20	/* SPI master is running */
#define ASM106X_CTRL_CSN	0x10	/* CS_n pin output */
#define ASM106X_CTRL_WRITE	0x08	/* 0=read, 1=write */
#define ASM106X_CTRL_MASK	0xc0	/* unknown, untouched */

struct asm106x_data {
	struct pci_dev *pci;
};

static const struct dev_entry asm106x_devs[] = {
	{PCI_VENDOR_ID_ASMEDIA, 0x0612, OK, "ASMedia", "ASM106x"},

	{0},
};

static int asm106x_wait_ready(struct pci_dev *pci, uint8_t *pval)
{
	unsigned int timeout = 100;
	uint8_t val;

	while (timeout) {
		val = pci_read_byte(pci, ASM106X_REG_CTRL);
		msg_pdbg2("asm106x status %#02"PRIx8" tries %d\n", val, timeout);
		if (!(val & ASM106X_CTRL_RUN)) {
			if (pval)
				*pval = val;
			return 0;
		}
		default_delay(10);
		timeout--;
	}

	msg_pdbg("asm106x timed out, ctrl %#02"PRIx8"\n", val);
	return 1;
}


static int asm106x_command(const struct flashctx *flash,
	unsigned int writecnt, unsigned int readcnt,
	const unsigned char *writearr, unsigned char *readarr)
{
	struct asm106x_data *data = flash->mst->spi.data;
	uint8_t ctrl;
	int rv = 1;

	msg_pdbg2("asm106x command: wr %d rd %d\n", writecnt, readcnt);
	if (asm106x_wait_ready(data->pci, &ctrl))
		return 1;
	ctrl &= ASM106X_CTRL_MASK;

	while (writecnt) {
		const unsigned int chunk = min(writecnt, 4);
		uint32_t val = 0;

		for (int k = chunk-1; k >= 0; k--)
			val = (val << 8) | writearr[k];
		msg_pdbg2("asm106x write %#08"PRIx32" chunk %u\n", val, chunk);
		pci_write_long(data->pci, ASM106X_REG_DATA, val);
		pci_write_byte(data->pci, ASM106X_REG_CTRL,
			ctrl | ASM106X_CTRL_RUN | ASM106X_CTRL_WRITE | chunk);
		if (asm106x_wait_ready(data->pci, NULL))
			goto out;
		writecnt -= chunk;
		writearr += chunk;
	}
	while (readcnt) {
		const unsigned int chunk = min(readcnt, 4);

		pci_write_byte(data->pci, ASM106X_REG_CTRL,
			ctrl | ASM106X_CTRL_RUN | chunk);
		if (asm106x_wait_ready(data->pci, NULL))
			goto out;

		uint32_t val = pci_read_long(data->pci, ASM106X_REG_DATA);
		msg_pdbg2("asm106x read %#08"PRIx32" chunk %u\n", val, chunk);
		for (unsigned int k = 0; k < chunk; k++) {
			readarr[k] = val & 0xff;
			val >>= 8;
		}
		readcnt -= chunk;
		readarr += chunk;
	}

	rv = 0;
out:
	pci_write_byte(data->pci, ASM106X_REG_CTRL, ctrl | ASM106X_CTRL_CSN);
	return rv;
}

static int asm106x_shutdown(void *data)
{
	free(data);
	return 0;
}

static const struct spi_master asm106x_spi_master = {
	.features	= SPI_MASTER_4BA,
	.max_data_read	= MAX_DATA_READ_UNLIMITED,
	.max_data_write	= MAX_DATA_WRITE_UNLIMITED,
	.command	= asm106x_command,
	.shutdown	= asm106x_shutdown,
	.read		= default_spi_read,
	.write_256	= default_spi_write_256,
};

static int asm106x_init(const struct programmer_cfg *cfg)
{
	struct pci_dev *pci;
	struct asm106x_data *data;

	/* TODO: no BAR required (just config space) */
	pci = pcidev_init(cfg, asm106x_devs, PCI_ROM_ADDRESS);
	if (!pci)
		return 1;

	data = calloc(1, sizeof(*data));
	if (!data) {
		msg_perr("cannot allocate memory for asm106x_data\n");
		return 1;
	}
	data->pci = pci;
	return register_spi_master(&asm106x_spi_master, data);
}

const struct programmer_entry programmer_asm106x = {
	.name			= "asm106x",
	.type			= PCI,
	.devs.dev		= asm106x_devs,
	.init			= asm106x_init,
};