xref: /aosp_15_r20/external/flashrom/developerbox_spi.c (revision 0d6140be3aa665ecc836e8907834fcd3e3b018fc)
1 /*
2  * This file is part of the flashrom project.
3  *
4  * Copyright (C) 2018 Linaro Limited
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 /*
18  * Bit bang driver for the 96Boards Developerbox (a.k.a. Synquacer E-series)
19  * on-board debug UART.  The Developerbox implements its debug UART using a
20  * CP2102N, a USB to UART bridge which also provides four GPIO pins. On
21  * Developerbox these can be hooked up to the onboard SPI NOR FLASH and used
22  * for emergency de-brick without any additional hardware programmer. Bit
23  * banging over USB is extremely slow compared to a proper SPI programmer so
24  * this is only practical as a de-brick tool.
25  *
26  * Schematic is available here:
27  * https://www.96boards.org/documentation/enterprise/developerbox/hardware-docs/
28  *
29  * To prepare a Developerbox for programming via the debug UART, DSW4 must be
30  * changed from the default 00000000 to 10001000 (i.e. DSW4-1 and DSW4-5
31  * should be turned on).
32  */
33 
34 #include <stdlib.h>
35 #include <libusb.h>
36 #include "programmer.h"
37 #include "spi.h"
38 
39 /* Bit positions for each pin. */
40 #define DEVELOPERBOX_SPI_SCK	0
41 #define DEVELOPERBOX_SPI_CS	1
42 #define DEVELOPERBOX_SPI_MISO	2
43 #define DEVELOPERBOX_SPI_MOSI	3
44 
45 /* Config request types */
46 #define REQTYPE_HOST_TO_DEVICE  0x40
47 #define REQTYPE_DEVICE_TO_HOST  0xc0
48 
49 /* Config request codes */
50 #define CP210X_VENDOR_SPECIFIC  0xff
51 
52 /* CP210X_VENDOR_SPECIFIC */
53 #define CP210X_WRITE_LATCH      0x37e1
54 #define CP210X_READ_LATCH       0x00c2
55 
56 static const struct dev_entry devs_developerbox_spi[] = {
57 	{0x10c4, 0xea60, OK, "Silicon Labs", "CP2102N USB to UART Bridge Controller"},
58 	{0},
59 };
60 
61 struct devbox_spi_data {
62 	struct libusb_context *usb_ctx;
63 	libusb_device_handle *cp210x_handle;
64 };
65 
cp210x_gpio_get(void * spi_data)66 static int cp210x_gpio_get(void *spi_data)
67 {
68 	int res;
69 	uint8_t gpio;
70 	struct devbox_spi_data *data = spi_data;
71 
72 	res = libusb_control_transfer(data->cp210x_handle, REQTYPE_DEVICE_TO_HOST,
73 			CP210X_VENDOR_SPECIFIC, CP210X_READ_LATCH,
74 			0, &gpio, 1, 0);
75 	if (res < 0) {
76 		msg_perr("Failed to read GPIO pins (%s)\n", libusb_error_name(res));
77 		return 0;
78 	}
79 
80 	return gpio;
81 }
82 
cp210x_gpio_set(uint8_t val,uint8_t mask,void * spi_data)83 static void cp210x_gpio_set(uint8_t val, uint8_t mask, void *spi_data)
84 {
85 	int res;
86 	uint16_t gpio;
87 	struct devbox_spi_data *data = spi_data;
88 
89 	gpio = ((val & 0xf) << 8) | (mask & 0xf);
90 
91 	/* Set relay state on the card */
92 	res = libusb_control_transfer(data->cp210x_handle, REQTYPE_HOST_TO_DEVICE,
93 			CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH,
94 			gpio, NULL, 0, 0);
95 	if (res < 0)
96 		msg_perr("Failed to read GPIO pins (%s)\n", libusb_error_name(res));
97 }
98 
cp210x_bitbang_set_cs(int val,void * spi_data)99 static void cp210x_bitbang_set_cs(int val, void *spi_data)
100 {
101 	cp210x_gpio_set(val << DEVELOPERBOX_SPI_CS, 1 << DEVELOPERBOX_SPI_CS, spi_data);
102 }
103 
cp210x_bitbang_set_sck(int val,void * spi_data)104 static void cp210x_bitbang_set_sck(int val, void *spi_data)
105 {
106 	cp210x_gpio_set(val << DEVELOPERBOX_SPI_SCK, 1 << DEVELOPERBOX_SPI_SCK, spi_data);
107 }
108 
cp210x_bitbang_set_mosi(int val,void * spi_data)109 static void cp210x_bitbang_set_mosi(int val, void *spi_data)
110 {
111 	cp210x_gpio_set(val << DEVELOPERBOX_SPI_MOSI, 1 << DEVELOPERBOX_SPI_MOSI, spi_data);
112 }
113 
cp210x_bitbang_get_miso(void * spi_data)114 static int cp210x_bitbang_get_miso(void *spi_data)
115 {
116 	return !!(cp210x_gpio_get(spi_data) & (1 << DEVELOPERBOX_SPI_MISO));
117 }
118 
cp210x_bitbang_set_sck_set_mosi(int sck,int mosi,void * spi_data)119 static void cp210x_bitbang_set_sck_set_mosi(int sck, int mosi, void *spi_data)
120 {
121 	cp210x_gpio_set(sck << DEVELOPERBOX_SPI_SCK | mosi << DEVELOPERBOX_SPI_MOSI,
122 			  1 << DEVELOPERBOX_SPI_SCK |    1 << DEVELOPERBOX_SPI_MOSI,
123 			  spi_data);
124 }
125 
126 static const struct bitbang_spi_master bitbang_spi_master_cp210x = {
127 	.set_cs			= cp210x_bitbang_set_cs,
128 	.set_sck		= cp210x_bitbang_set_sck,
129 	.set_mosi		= cp210x_bitbang_set_mosi,
130 	.get_miso		= cp210x_bitbang_get_miso,
131 	.set_sck_set_mosi	= cp210x_bitbang_set_sck_set_mosi,
132 };
133 
developerbox_spi_shutdown(void * spi_data)134 static int developerbox_spi_shutdown(void *spi_data)
135 {
136 	struct devbox_spi_data *data = spi_data;
137 
138 	libusb_close(data->cp210x_handle);
139 	libusb_exit(data->usb_ctx);
140 
141 	free(data);
142 	return 0;
143 }
144 
developerbox_spi_init(const struct programmer_cfg * cfg)145 static int developerbox_spi_init(const struct programmer_cfg *cfg)
146 {
147 	struct libusb_context *usb_ctx;
148 	libusb_device_handle *cp210x_handle;
149 
150 	if (libusb_init(&usb_ctx)) {
151 		msg_perr("Could not initialize libusb!\n");
152 		return 1;
153 	}
154 
155 	char *serialno = extract_programmer_param_str(cfg, "serial");
156 	if (serialno)
157 		msg_pdbg("Looking for serial number commencing %s\n", serialno);
158 	cp210x_handle = usb_dev_get_by_vid_pid_serial(usb_ctx,
159 			devs_developerbox_spi[0].vendor_id, devs_developerbox_spi[0].device_id, serialno);
160 	free(serialno);
161 	if (!cp210x_handle) {
162 		msg_perr("Could not find a Developerbox programmer on USB.\n");
163 		goto err_exit;
164 	}
165 
166 	struct devbox_spi_data *data = calloc(1, sizeof(*data));
167 	if (!data) {
168 		msg_perr("Unable to allocate space for SPI master data\n");
169 		goto err_exit;
170 	}
171 	data->usb_ctx = usb_ctx;
172 	data->cp210x_handle = cp210x_handle;
173 
174 	if (register_shutdown(developerbox_spi_shutdown, data)) {
175 		free(data);
176 		goto err_exit;
177 	}
178 
179 	if (register_spi_bitbang_master(&bitbang_spi_master_cp210x, data))
180 		return 1; /* shutdown function does the cleanup */
181 
182 	return 0;
183 
184 err_exit:
185 	if (cp210x_handle)
186 		libusb_close(cp210x_handle);
187 	libusb_exit(usb_ctx);
188 	return 1;
189 }
190 
191 const struct programmer_entry programmer_developerbox = {
192 	.name			= "developerbox",
193 	.type			= USB,
194 	.devs.dev		= devs_developerbox_spi,
195 	.init			= developerbox_spi_init,
196 };
197