1 /*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2023 Alex Badea <[email protected]>
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 <stdlib.h>
18 #include "programmer.h"
19 #include "platform/pci.h"
20
21 #define PCI_VENDOR_ID_ASMEDIA 0x1b21
22
23 #define ASM106X_REG_DATA 0xf0
24 #define ASM106X_REG_CTRL 0xf4
25 #define ASM106X_CTRL_RUN 0x20 /* SPI master is running */
26 #define ASM106X_CTRL_CSN 0x10 /* CS_n pin output */
27 #define ASM106X_CTRL_WRITE 0x08 /* 0=read, 1=write */
28 #define ASM106X_CTRL_MASK 0xc0 /* unknown, untouched */
29
30 struct asm106x_data {
31 struct pci_dev *pci;
32 };
33
34 static const struct dev_entry asm106x_devs[] = {
35 {PCI_VENDOR_ID_ASMEDIA, 0x0612, OK, "ASMedia", "ASM106x"},
36
37 {0},
38 };
39
asm106x_wait_ready(struct pci_dev * pci,uint8_t * pval)40 static int asm106x_wait_ready(struct pci_dev *pci, uint8_t *pval)
41 {
42 unsigned int timeout = 100;
43 uint8_t val;
44
45 while (timeout) {
46 val = pci_read_byte(pci, ASM106X_REG_CTRL);
47 msg_pdbg2("asm106x status %#02"PRIx8" tries %d\n", val, timeout);
48 if (!(val & ASM106X_CTRL_RUN)) {
49 if (pval)
50 *pval = val;
51 return 0;
52 }
53 default_delay(10);
54 timeout--;
55 }
56
57 msg_pdbg("asm106x timed out, ctrl %#02"PRIx8"\n", val);
58 return 1;
59 }
60
61
asm106x_command(const struct flashctx * flash,unsigned int writecnt,unsigned int readcnt,const unsigned char * writearr,unsigned char * readarr)62 static int asm106x_command(const struct flashctx *flash,
63 unsigned int writecnt, unsigned int readcnt,
64 const unsigned char *writearr, unsigned char *readarr)
65 {
66 struct asm106x_data *data = flash->mst->spi.data;
67 uint8_t ctrl;
68 int rv = 1;
69
70 msg_pdbg2("asm106x command: wr %d rd %d\n", writecnt, readcnt);
71 if (asm106x_wait_ready(data->pci, &ctrl))
72 return 1;
73 ctrl &= ASM106X_CTRL_MASK;
74
75 while (writecnt) {
76 const unsigned int chunk = min(writecnt, 4);
77 uint32_t val = 0;
78
79 for (int k = chunk-1; k >= 0; k--)
80 val = (val << 8) | writearr[k];
81 msg_pdbg2("asm106x write %#08"PRIx32" chunk %u\n", val, chunk);
82 pci_write_long(data->pci, ASM106X_REG_DATA, val);
83 pci_write_byte(data->pci, ASM106X_REG_CTRL,
84 ctrl | ASM106X_CTRL_RUN | ASM106X_CTRL_WRITE | chunk);
85 if (asm106x_wait_ready(data->pci, NULL))
86 goto out;
87 writecnt -= chunk;
88 writearr += chunk;
89 }
90 while (readcnt) {
91 const unsigned int chunk = min(readcnt, 4);
92
93 pci_write_byte(data->pci, ASM106X_REG_CTRL,
94 ctrl | ASM106X_CTRL_RUN | chunk);
95 if (asm106x_wait_ready(data->pci, NULL))
96 goto out;
97
98 uint32_t val = pci_read_long(data->pci, ASM106X_REG_DATA);
99 msg_pdbg2("asm106x read %#08"PRIx32" chunk %u\n", val, chunk);
100 for (unsigned int k = 0; k < chunk; k++) {
101 readarr[k] = val & 0xff;
102 val >>= 8;
103 }
104 readcnt -= chunk;
105 readarr += chunk;
106 }
107
108 rv = 0;
109 out:
110 pci_write_byte(data->pci, ASM106X_REG_CTRL, ctrl | ASM106X_CTRL_CSN);
111 return rv;
112 }
113
asm106x_shutdown(void * data)114 static int asm106x_shutdown(void *data)
115 {
116 free(data);
117 return 0;
118 }
119
120 static const struct spi_master asm106x_spi_master = {
121 .features = SPI_MASTER_4BA,
122 .max_data_read = MAX_DATA_READ_UNLIMITED,
123 .max_data_write = MAX_DATA_WRITE_UNLIMITED,
124 .command = asm106x_command,
125 .shutdown = asm106x_shutdown,
126 .read = default_spi_read,
127 .write_256 = default_spi_write_256,
128 };
129
asm106x_init(const struct programmer_cfg * cfg)130 static int asm106x_init(const struct programmer_cfg *cfg)
131 {
132 struct pci_dev *pci;
133 struct asm106x_data *data;
134
135 /* TODO: no BAR required (just config space) */
136 pci = pcidev_init(cfg, asm106x_devs, PCI_ROM_ADDRESS);
137 if (!pci)
138 return 1;
139
140 data = calloc(1, sizeof(*data));
141 if (!data) {
142 msg_perr("cannot allocate memory for asm106x_data\n");
143 return 1;
144 }
145 data->pci = pci;
146 return register_spi_master(&asm106x_spi_master, data);
147 }
148
149 const struct programmer_entry programmer_asm106x = {
150 .name = "asm106x",
151 .type = PCI,
152 .devs.dev = asm106x_devs,
153 .init = asm106x_init,
154 };
155