blob: 8482dea17f31cf991f864765e9e1eeccbfed0b3f [file] [log] [blame]
Daniel Thompson45e91a22018-06-04 13:46:29 +01001/*
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 "platform.h"
35
36#include <stdlib.h>
37#include <string.h>
38#include <libusb.h>
39#include "programmer.h"
40#include "spi.h"
41
42/* Bit positions for each pin. */
43#define DEVELOPERBOX_SPI_SCK 0
44#define DEVELOPERBOX_SPI_CS 1
45#define DEVELOPERBOX_SPI_MISO 2
46#define DEVELOPERBOX_SPI_MOSI 3
47
48/* Config request types */
49#define REQTYPE_HOST_TO_DEVICE 0x40
50#define REQTYPE_DEVICE_TO_HOST 0xc0
51
52/* Config request codes */
53#define CP210X_VENDOR_SPECIFIC 0xff
54
55/* CP210X_VENDOR_SPECIFIC */
56#define CP210X_WRITE_LATCH 0x37e1
57#define CP210X_READ_LATCH 0x00c2
58
59const struct dev_entry devs_developerbox_spi[] = {
60 {0x10c4, 0xea60, OK, "Silicon Labs", "CP2102N USB to UART Bridge Controller"},
61 {0},
62};
63
64struct libusb_context *usb_ctx;
65static libusb_device_handle *cp210x_handle;
66
67static int cp210x_gpio_get(void)
68{
69 int res;
70 uint8_t gpio;
71
72 res = libusb_control_transfer(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
83static void cp210x_gpio_set(uint8_t val, uint8_t mask)
84{
85 int res;
86 uint16_t gpio;
87
88 gpio = ((val & 0xf) << 8) | (mask & 0xf);
89
90 /* Set relay state on the card */
91 res = libusb_control_transfer(cp210x_handle, REQTYPE_HOST_TO_DEVICE,
92 CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH,
93 gpio, NULL, 0, 0);
94 if (res < 0)
95 msg_perr("Failed to read GPIO pins (%s)\n", libusb_error_name(res));
96}
97
98static void cp210x_bitbang_set_cs(int val)
99{
100 cp210x_gpio_set(val << DEVELOPERBOX_SPI_CS, 1 << DEVELOPERBOX_SPI_CS);
101}
102
103static void cp210x_bitbang_set_sck(int val)
104{
105 cp210x_gpio_set(val << DEVELOPERBOX_SPI_SCK, 1 << DEVELOPERBOX_SPI_SCK);
106}
107
108static void cp210x_bitbang_set_mosi(int val)
109{
110 cp210x_gpio_set(val << DEVELOPERBOX_SPI_MOSI, 1 << DEVELOPERBOX_SPI_MOSI);
111}
112
113static int cp210x_bitbang_get_miso(void)
114{
115 return !!(cp210x_gpio_get() & (1 << DEVELOPERBOX_SPI_MISO));
116}
117
118static void cp210x_bitbang_set_sck_set_mosi(int sck, int mosi)
119{
120 cp210x_gpio_set(sck << DEVELOPERBOX_SPI_SCK | mosi << DEVELOPERBOX_SPI_MOSI,
121 1 << DEVELOPERBOX_SPI_SCK | 1 << DEVELOPERBOX_SPI_MOSI);
122}
123
124static const struct bitbang_spi_master bitbang_spi_master_cp210x = {
125 .type = BITBANG_SPI_MASTER_DEVELOPERBOX,
126 .set_cs = cp210x_bitbang_set_cs,
127 .set_sck = cp210x_bitbang_set_sck,
128 .set_mosi = cp210x_bitbang_set_mosi,
129 .get_miso = cp210x_bitbang_get_miso,
130 .set_sck_set_mosi = cp210x_bitbang_set_sck_set_mosi,
131};
132
133static struct libusb_device_handle *get_device_by_vid_pid_serial(uint16_t vid, uint16_t pid,
134 const char *serialno)
135{
136 struct libusb_device **list;
137 ssize_t count = libusb_get_device_list(usb_ctx, &list);
138 if (count < 0) {
139 msg_perr("Getting the USB device list failed (%s)!\n", libusb_error_name(count));
140 return NULL;
141 }
142
143 ssize_t i = 0;
144 for (i = 0; i < count; i++) {
145 struct libusb_device *dev = list[i];
146 struct libusb_device_descriptor desc;
147 struct libusb_device_handle *handle;
148
149 int res = libusb_get_device_descriptor(dev, &desc);
150 if (res != 0) {
151 msg_perr("Reading the USB device descriptor failed (%s)!\n", libusb_error_name(res));
152 continue;
153 }
154
155 if ((desc.idVendor != vid) && (desc.idProduct != pid))
156 continue;
157
158 msg_pdbg("Found USB device %04"PRIx16":%04"PRIx16" at address %d-%d.\n",
159 desc.idVendor, desc.idProduct,
160 libusb_get_bus_number(dev), libusb_get_device_address(dev));
161
162 res = libusb_open(dev, &handle);
163 if (res != 0) {
164 msg_perr("Opening the USB device failed (%s)!\n", libusb_error_name(res));
165 continue;
166 }
167
168 if (serialno) {
169 unsigned char myserial[64];
170 res = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, myserial,
171 sizeof(myserial));
172 if (res < 0) {
173 msg_perr("Reading the USB serialno failed (%s)!\n", libusb_error_name(res));
174 libusb_close(handle);
175 continue;
176 }
177 msg_pdbg("Serial number is %s\n", myserial);
178
179 /* Filter out any serial number that does not commence with serialno */
180 if (0 != strncmp(serialno, (char *) myserial, strlen(serialno))) {
181 libusb_close(handle);
182 continue;
183 }
184 }
185
186 libusb_free_device_list(list, 1);
187 return handle;
188 }
189
190 libusb_free_device_list(list, 1);
191 return NULL;
192}
193
194static int developerbox_spi_shutdown(void *data)
195{
196 libusb_close(cp210x_handle);
197 libusb_exit(usb_ctx);
198
199 return 0;
200}
201
202int developerbox_spi_init(void)
203{
204 libusb_init(&usb_ctx);
205 if (!usb_ctx) {
206 msg_perr("Could not initialize libusb!\n");
207 return 1;
208 }
209
210 char *serialno = extract_programmer_param("serial");
211 if (serialno)
212 msg_pdbg("Looking for serial number commencing %s\n", serialno);
213 cp210x_handle = get_device_by_vid_pid_serial(
214 devs_developerbox_spi[0].vendor_id, devs_developerbox_spi[0].device_id, serialno);
215 free(serialno);
216 if (!cp210x_handle) {
217 msg_perr("Could not find a Developerbox programmer on USB.\n");
218 goto err_exit;
219 }
220
221 if (register_shutdown(developerbox_spi_shutdown, NULL))
222 goto err_exit;
223
224 if (register_spi_bitbang_master(&bitbang_spi_master_cp210x))
225 goto err_exit;
226
227 return 0;
228
229err_exit:
230 libusb_exit(usb_ctx);
231 return 1;
232}