xref: /aosp_15_r20/external/flashrom/pony_spi.c (revision 0d6140be3aa665ecc836e8907834fcd3e3b018fc)
1 /*
2  * This file is part of the flashrom project.
3  *
4  * Copyright (C) 2012 Virgil-Adrian Teaca
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; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  */
15 
16 /* Driver for serial programmers compatible with SI-Prog or AJAWe.
17  *
18  * See http://www.lancos.com/siprogsch.html for SI-Prog schematics and instructions.
19  * See http://www.ajawe.pl/ajawe0208.htm for AJAWe serial programmer documentation.
20  *
21  * Pin layout for SI-Prog-like hardware:
22  *
23  * MOSI <-------< DTR
24  * MISO >-------> CTS
25  * SCK  <---+---< RTS
26  *          +---> DSR
27  * CS#  <-------< TXD
28  *
29  * and for the AJAWe serial programmer:
30  *
31  * MOSI <-------< DTR
32  * MISO >-------> CTS
33  * SCK  <-------< RTS
34  * CS#  <-------< TXD
35  *
36  * DCE  >-------> DSR
37  */
38 
39 #include <stdbool.h>
40 #include <stdlib.h>
41 #include <strings.h>
42 #include <string.h>
43 
44 #include "flash.h"
45 #include "programmer.h"
46 
47 enum pony_type {
48 	TYPE_SI_PROG,
49 	TYPE_SERBANG,
50 	TYPE_AJAWE
51 };
52 
53 struct pony_spi_data {
54 	/* Pins for master->slave direction */
55 	bool negate_cs;
56 	bool negate_sck;
57 	bool negate_mosi;
58 	/* Pins for slave->master direction */
59 	bool negate_miso;
60 };
61 
pony_bitbang_set_cs(int val,void * spi_data)62 static void pony_bitbang_set_cs(int val, void *spi_data)
63 {
64 	struct pony_spi_data *data = spi_data;
65 
66 	if (data->negate_cs)
67 		val ^=  1;
68 
69 	sp_set_pin(PIN_TXD, val);
70 }
71 
pony_bitbang_set_sck(int val,void * spi_data)72 static void pony_bitbang_set_sck(int val, void *spi_data)
73 {
74 	struct pony_spi_data *data = spi_data;
75 
76 	if (data->negate_sck)
77 		val ^=  1;
78 
79 	sp_set_pin(PIN_RTS, val);
80 }
81 
pony_bitbang_set_mosi(int val,void * spi_data)82 static void pony_bitbang_set_mosi(int val, void *spi_data)
83 {
84 	struct pony_spi_data *data = spi_data;
85 
86 	if (data->negate_mosi)
87 		val ^=  1;
88 
89 	sp_set_pin(PIN_DTR, val);
90 }
91 
pony_bitbang_get_miso(void * spi_data)92 static int pony_bitbang_get_miso(void *spi_data)
93 {
94 	struct pony_spi_data *data = spi_data;
95 	int tmp = sp_get_pin(PIN_CTS);
96 
97 	if (data->negate_miso)
98 		tmp ^= 1;
99 
100 	return tmp;
101 }
102 
103 static const struct bitbang_spi_master bitbang_spi_master_pony = {
104 	.set_cs		= pony_bitbang_set_cs,
105 	.set_sck	= pony_bitbang_set_sck,
106 	.set_mosi	= pony_bitbang_set_mosi,
107 	.get_miso	= pony_bitbang_get_miso,
108 	.half_period	= 0,
109 };
110 
pony_spi_shutdown(void * data)111 static int pony_spi_shutdown(void *data)
112 {
113 	/* Shut down serial port communication */
114 	int ret = serialport_shutdown(NULL);
115 	if (ret)
116 		msg_pdbg("Pony SPI shutdown failed.\n");
117 	else
118 		msg_pdbg("Pony SPI shutdown completed.\n");
119 
120 	free(data);
121 	return ret;
122 }
123 
get_params(const struct programmer_cfg * cfg,enum pony_type * type,bool * have_device)124 static int get_params(const struct programmer_cfg *cfg, enum pony_type *type, bool *have_device)
125 {
126 	char *arg = NULL;
127 	int ret = 0;
128 
129 	/* defaults */
130 	*type = TYPE_SI_PROG;
131 	*have_device = false;
132 
133 	/* The parameter is in format "dev=/dev/device,type=serbang" */
134 	arg = extract_programmer_param_str(cfg, "dev");
135 	if (arg && strlen(arg)) {
136 		sp_fd = sp_openserport(arg, 9600);
137 		if (sp_fd == SER_INV_FD)
138 			ret = 1;
139 		else
140 			*have_device = true;
141 	}
142 	free(arg);
143 
144 	arg = extract_programmer_param_str(cfg, "type");
145 	if (arg && !strcasecmp(arg, "serbang")) {
146 		*type = TYPE_SERBANG;
147 	} else if (arg && !strcasecmp(arg, "si_prog")) {
148 		*type = TYPE_SI_PROG;
149 	} else if (arg && !strcasecmp( arg, "ajawe")) {
150 		*type = TYPE_AJAWE;
151 	} else if (arg && !strlen(arg)) {
152 		msg_perr("Error: Missing argument for programmer type.\n");
153 		ret = 1;
154 	} else if (arg) {
155 		msg_perr("Error: Invalid programmer type specified.\n");
156 		ret = 1;
157 	}
158 	free(arg);
159 
160 	return ret;
161 }
162 
pony_spi_init(const struct programmer_cfg * cfg)163 static int pony_spi_init(const struct programmer_cfg *cfg)
164 {
165 	int i, data_out;
166 	enum pony_type type;
167 	const char *name;
168 	bool have_device;
169 	bool have_prog = false;
170 
171 	if (get_params(cfg, &type, &have_device)) {
172 		serialport_shutdown(NULL);
173 		return 1;
174 	}
175 	if (!have_device) {
176 		msg_perr("Error: No valid device specified.\n"
177 			 "Use flashrom -p pony_spi:dev=/dev/device[,type=name]\n");
178 		serialport_shutdown(NULL);
179 		return 1;
180 	}
181 
182 	struct pony_spi_data *data = calloc(1, sizeof(*data));
183 	if (!data) {
184 		msg_perr("Unable to allocate space for SPI master data\n");
185 		serialport_shutdown(NULL);
186 		return 1;
187 	}
188 	data->negate_cs = true;
189 	data->negate_sck = false;
190 	data->negate_mosi = false;
191 	data->negate_miso = false;
192 
193 	if (register_shutdown(pony_spi_shutdown, data) != 0) {
194 		free(data);
195 		serialport_shutdown(NULL);
196 		return 1;
197 	}
198 
199 	/*
200 	 * Configure the serial port pins, depending on the used programmer.
201 	 */
202 	switch (type) {
203 	case TYPE_AJAWE:
204 		data->negate_cs = true;
205 		data->negate_sck = true;
206 		data->negate_mosi = true;
207 		data->negate_miso = true;
208 		name = "AJAWe";
209 		break;
210 	case TYPE_SERBANG:
211 		data->negate_cs = false;
212 		data->negate_sck = false;
213 		data->negate_mosi = false;
214 		data->negate_miso = true;
215 		name = "serbang";
216 		break;
217 	default:
218 	case TYPE_SI_PROG:
219 		data->negate_cs = true;
220 		data->negate_sck = false;
221 		data->negate_mosi = false;
222 		data->negate_miso = false;
223 		name = "SI-Prog";
224 		break;
225 	}
226 	msg_pdbg("Using %s programmer pinout.\n", name);
227 
228 	/*
229 	 * Detect if there is a compatible hardware programmer connected.
230 	 */
231 	pony_bitbang_set_cs(1, data);
232 	pony_bitbang_set_sck(1, data);
233 	pony_bitbang_set_mosi(1, data);
234 
235 	switch (type) {
236 	case TYPE_AJAWE:
237 		have_prog = true;
238 		break;
239 	case TYPE_SI_PROG:
240 	case TYPE_SERBANG:
241 	default:
242 		have_prog = true;
243 		/* We toggle RTS/SCK a few times and see if DSR changes too. */
244 		for (i = 1; i <= 10; i++) {
245 			data_out = i & 1;
246 			sp_set_pin(PIN_RTS, data_out);
247 			default_delay(1000);
248 
249 			/* If DSR does not change, we are not connected to what we think */
250 			if (data_out != sp_get_pin(PIN_DSR)) {
251 				have_prog = false;
252 				break;
253 			}
254 		}
255 		break;
256 	}
257 
258 	if (!have_prog) {
259 		msg_perr("No programmer compatible with %s detected.\n", name);
260 		return 1;
261 	}
262 
263 	if (register_spi_bitbang_master(&bitbang_spi_master_pony, data))
264 		return 1;
265 
266 	return 0;
267 }
268 
269 const struct programmer_entry programmer_pony_spi = {
270 	.name			= "pony_spi",
271 	.type			= OTHER,
272 				/* FIXME */
273 	.devs.note		= "Programmers compatible with SI-Prog, serbang or AJAWe\n",
274 	.init			= pony_spi_init,
275 };
276